diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint index 07be4573270d..9cf1c1b209fb 100644 --- a/usr/src/Makefile.lint +++ b/usr/src/Makefile.lint @@ -246,6 +246,7 @@ COMMON_SUBDIRS = \ cmd/sdpadm \ cmd/setpgrp \ cmd/smbios \ + cmd/smbsrv \ cmd/smserverd \ cmd/sort \ cmd/split \ @@ -400,6 +401,7 @@ COMMON_SUBDIRS = \ lib/pkcs11 \ lib/print \ lib/raidcfg_plugins \ + lib/smbsrv \ lib/fm \ lib/udapl \ lib/watchmalloc \ diff --git a/usr/src/Targetdirs b/usr/src/Targetdirs index b7d74fa7f741..1e2d094be225 100644 --- a/usr/src/Targetdirs +++ b/usr/src/Targetdirs @@ -153,6 +153,7 @@ ROOT.SYS= \ /var/sadm \ /var/opt \ /var/run \ + /var/smb \ /var/svc \ /var/svc/log \ /var/svc/manifest \ @@ -282,6 +283,7 @@ ROOT.BIN= \ /usr/lib/sasl \ /usr/lib/secure \ /usr/lib/security \ + /usr/lib/smbsrv \ /usr/lib/term \ /usr/lib/zones \ /usr/old \ @@ -363,6 +365,7 @@ ROOT.SYS2= \ /usr/lib/fs \ /usr/lib/fs/nfs \ /usr/lib/fs/proc \ + /usr/lib/fs/smb \ /usr/lib/fs/zfs \ /usr/lib/mdb \ /usr/lib/mdb/kvm \ @@ -390,7 +393,8 @@ ROOT.SYS64.2 = \ $($(MACH64)_ROOT.SYS64.2) \ /usr/lib/mdb/kvm/$(MACH64) \ /usr/lib/mdb/proc/$(MACH64) \ - /usr/lib/fs/nfs/$(MACH64) + /usr/lib/fs/nfs/$(MACH64) \ + /usr/lib/fs/smb/$(MACH64) UUCP.UUCP= \ /var/spool/uucp \ diff --git a/usr/src/cmd/Adm/sun/Makefile b/usr/src/cmd/Adm/sun/Makefile index 017f4ff8d4df..817aafbf613f 100644 --- a/usr/src/cmd/Adm/sun/Makefile +++ b/usr/src/cmd/Adm/sun/Makefile @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,31 +19,34 @@ # CDDL HEADER END # # -#ident "%Z%%M% %I% %E% SMI" +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. # -# Copyright (c) 1989, 1995, 1997 by Sun Microsystems, Inc. -# All Rights Reserved +# ident "%Z%%M% %I% %E% SMI" # ETCFILES= ioctl.syscon passwd shadow motd KVMFILES= README +SMBFILES= smbpasswd include ../../Makefile.cmd ROOTETCFILES= $(ETCFILES:%=$(ROOTETC)/%) ROOTUSRKVMFILES= $(KVMFILES:%=$(ROOTUSRKVM)/%) +ROOTVARSMBFILES= $(SMBFILES:%=$(ROOTVARSMB)/%) FILEMODE= 0644 OWNER= root GROUP= sys $(ROOTETC)/shadow := FILEMODE = 400 +$(ROOTVARSMB)/smbpasswd := FILEMODE = 0400 .KEEP_STATE: -all: $(ETCFILES) $(KVMFILES) +all: $(ETCFILES) $(KVMFILES) $(SMBFILES) -install: all $(ROOTETCFILES) $(ROOTUSRKVMFILES) +install: all $(ROOTETCFILES) $(ROOTUSRKVMFILES) $(ROOTVARSMBFILES) clean: @@ -59,11 +61,14 @@ motd: FRC @$(NOT_RELEASE_BUILD)-$(ECHO) $(DEV_CM) | sed -e "s/@(#)//" >> motd @-$(CAT) release_info >> motd +smbpasswd: + $(TOUCH) smbpasswd + clean: lint: clobber: - $(RM) motd + $(RM) motd smbpasswd FRC: diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index b3173b0190d6..f6c720e4f737 100644 --- a/usr/src/cmd/Makefile +++ b/usr/src/cmd/Makefile @@ -349,6 +349,7 @@ COMMON_SUBDIRS= \ sh \ sleep \ smbios \ + smbsrv \ smserverd \ soelim \ sort \ diff --git a/usr/src/cmd/Makefile.cmd b/usr/src/cmd/Makefile.cmd index 89d085da4d69..09e3e1d378ca 100644 --- a/usr/src/cmd/Makefile.cmd +++ b/usr/src/cmd/Makefile.cmd @@ -92,6 +92,7 @@ ROOTUSRSBIN64= $(ROOTUSRSBIN)/$(MACH64) ROOTMAN1= $(ROOT)/usr/share/man/man1 ROOTMAN1M= $(ROOT)/usr/share/man/man1m ROOTMAN3= $(ROOT)/usr/share/man/man3 +ROOTVARSMB= $(ROOT)/var/smb # @@ -246,6 +247,7 @@ ROOTSVCNETWORKSECURITY= $(ROOTSVCNETWORK)/security ROOTSVCNETWORKSSL= $(ROOTSVCNETWORK)/ssl ROOTSVCNETWORKIPSEC= $(ROOTSVCNETWORK)/ipsec ROOTSVCNETWORKSHARES= $(ROOTSVCNETWORK)/shares +ROOTSVCSMB= $(ROOTSVCNETWORK)/smb ROOTSVCPLATFORM= $(ROOTVARSVCMANIFEST)/platform ROOTSVCPLATFORMSUN4U= $(ROOTSVCPLATFORM)/sun4u ROOTSVCPLATFORMSUN4V= $(ROOTSVCPLATFORM)/sun4v @@ -460,6 +462,9 @@ $(ROOTMAN1M)/%: %.sunman $(ROOTMAN3)/%: %.sunman $(INS.rename) +$(ROOTVARSMB)/%: % + $(INS.file) + # build rule for statically linked programs with single source file. %.static: %.c $(LINK.c) -o $@ $< $(LDLIBS) diff --git a/usr/src/cmd/bnu/in.uucpd.c b/usr/src/cmd/bnu/in.uucpd.c index d8ae4b02c6fb..bc0b372f3e1f 100644 --- a/usr/src/cmd/bnu/in.uucpd.c +++ b/usr/src/cmd/bnu/in.uucpd.c @@ -466,7 +466,7 @@ struct sockaddr_in *sin; * there is no way to do the "pam_close_session()". * * Processes like "init" can do a pam_close_session() - * because they can use the utmp entry to retrive + * because they can use the utmp entry to retrieve * the proper username, ttyname, etc. -- * uucpd only writes to the wtmp file. * diff --git a/usr/src/cmd/chmod/Makefile b/usr/src/cmd/chmod/Makefile index fad270c1b074..5e6b3fe6c41a 100644 --- a/usr/src/cmd/chmod/Makefile +++ b/usr/src/cmd/chmod/Makefile @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -22,7 +21,7 @@ # #ident "%Z%%M% %I% %E% SMI" # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # cmd/chmod/Makefile @@ -42,7 +41,7 @@ CPPFLAGS += -D_FILE_OFFSET_BITS=64 LINTFLAGS += -erroff=E_NAME_DEF_NOT_USED2 -LDLIBS += -lsec +LDLIBS += -lsec -lnvpair -lcmdutils POFILE= chmod_cmd.po XGETFLAGS= -a -x chmod.xcl diff --git a/usr/src/cmd/chmod/chmod.c b/usr/src/cmd/chmod/chmod.c index 29813f7d093c..531c4bf3d26f 100644 --- a/usr/src/cmd/chmod/chmod.c +++ b/usr/src/cmd/chmod/chmod.c @@ -44,7 +44,8 @@ * where * mode is [ugoa][+-=][rwxXlstugo] or an octal number * mode is [<+|->A[# - * option is -R and -f + * mode is S + * option is -R, -f, and -@ */ /* @@ -58,6 +59,7 @@ #include #include #include +#include #include #include #include /* strerror() */ @@ -67,6 +69,10 @@ #include #include #include +#include +#include +#include +#include static int rflag; static int fflag; @@ -79,11 +85,37 @@ static char **mav; /* Alternate to argv (for parseargs) */ static char *ms; /* Points to the mode argument */ -#define ACL_ADD 1 -#define ACL_DELETE 2 -#define ACL_SLOT_DELETE 3 -#define ACL_REPLACE 4 -#define ACL_STRIP 5 +#define ACL_ADD 1 +#define ACL_DELETE 2 +#define ACL_SLOT_DELETE 3 +#define ACL_REPLACE 4 +#define ACL_STRIP 5 + +#define LEFTBRACE '{' +#define RIGHTBRACE '}' +#define A_SEP ',' +#define A_SEP_TOK "," + +#define A_COMPACT_TYPE 'c' +#define A_VERBOSE_TYPE 'v' +#define A_ALLATTRS_TYPE 'a' + +#define A_SET_OP '+' +#define A_INVERSE_OP '-' +#define A_REPLACE_OP '=' +#define A_UNDEF_OP '\0' + +#define A_SET_TEXT "set" +#define A_INVERSE_TEXT "clear" + +#define A_SET_VAL B_TRUE +#define A_CLEAR_VAL B_FALSE + +#define ATTR_OPTS 0 +#define ATTR_NAMES 1 + +#define sec_acls secptr.acls +#define sec_attrs secptr.attrs typedef struct acl_args { acl_t *acl_aclp; @@ -91,34 +123,55 @@ typedef struct acl_args { int acl_action; } acl_args_t; -extern mode_t -newmode_common(char *ms, mode_t new_mode, mode_t umsk, char *file, char *path, - o_mode_t *group_clear_bits, o_mode_t *group_set_bits); +typedef enum { + SEC_ACL, + SEC_ATTR +} chmod_sec_t; -static int -dochmod(char *name, char *path, mode_t umsk, acl_args_t *aclp), -chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp); -static int doacl(char *file, struct stat *st, acl_args_t *aclp); +typedef struct { + chmod_sec_t sec_type; + union { + acl_args_t *acls; + nvlist_t *attrs; + } secptr; +} sec_args_t; -static void handle_acl(char *name, o_mode_t group_clear_bits, - o_mode_t group_set_bits); +typedef struct attr_name { + char *name; + struct attr_name *next; +} attr_name_t; -static void usage(void); -void errmsg(int severity, int code, char *format, ...); +extern mode_t newmode_common(char *ms, mode_t new_mode, mode_t umsk, + char *file, char *path, o_mode_t *group_clear_bits, + o_mode_t *group_set_bits); +static int chmodr(char *dir, char *path, mode_t mode, mode_t umsk, + sec_args_t *secp, attr_name_t *attrname); +static int doacl(char *file, struct stat *st, acl_args_t *aclp); +static int dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp, + attr_name_t *attrnames); +static void handle_acl(char *name, o_mode_t group_clear_bits, + o_mode_t group_set_bits); +void errmsg(int severity, int code, char *format, ...); +static void free_attr_names(attr_name_t *attrnames); static void parseargs(int ac, char *av[]); - -int -parse_acl_args(char *arg, acl_args_t **acl_args); +static int parse_acl_args(char *arg, sec_args_t **sec_args); +static int parse_attr_args(char *arg, sec_args_t **sec_args); +static void print_attrs(int flag); +static int set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist); +static void usage(void); int main(int argc, char *argv[]) { - int i, c; - int status = 0; - mode_t umsk; - acl_args_t *acl_args = NULL; + int i, c; + int status = 0; + mode_t umsk; + sec_args_t *sec_args = NULL; + attr_name_t *attrnames = NULL; + attr_name_t *attrend = NULL; + attr_name_t *tattr; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ @@ -128,7 +181,7 @@ main(int argc, char *argv[]) parseargs(argc, argv); - while ((c = getopt(mac, mav, "Rf")) != EOF) { + while ((c = getopt(mac, mav, "Rf@:")) != EOF) { switch (c) { case 'R': rflag++; @@ -136,6 +189,20 @@ main(int argc, char *argv[]) case 'f': fflag++; break; + case '@': + if (((tattr = malloc(sizeof (attr_name_t))) == NULL) || + ((tattr->name = strdup(optarg)) == NULL)) { + perror("chmod"); + exit(2); + } + if (attrnames == NULL) { + attrnames = tattr; + attrnames->next = NULL; + } else { + attrend->next = tattr; + } + attrend = tattr; + break; case '?': usage(); exit(2); @@ -149,16 +216,33 @@ main(int argc, char *argv[]) mac -= optind; mav += optind; - if (mac >= 2 && (mav[0][0] == 'A')) { - if (parse_acl_args(*mav, &acl_args)) { + if ((mac >= 2) && (mav[0][0] == 'A')) { + if (attrnames != NULL) { + free_attr_names(attrnames); + attrnames = NULL; + } + if (parse_acl_args(*mav, &sec_args)) { usage(); exit(2); } + } else if ((mac >= 2) && (mav[0][0] == 'S')) { + if (parse_attr_args(*mav, &sec_args)) { + usage(); + exit(2); + + /* A no-op attribute operation was specified. */ + } else if (sec_args->sec_attrs == NULL) { + exit(0); + } } else { if (mac < 2) { usage(); exit(2); } + if (attrnames != NULL) { + free_attr_names(attrnames); + attrnames = NULL; + } } ms = mav[0]; @@ -167,14 +251,30 @@ main(int argc, char *argv[]) (void) umask(umsk); for (i = 1; i < mac; i++) { - status += dochmod(mav[i], mav[i], umsk, acl_args); + status += dochmod(mav[i], mav[i], umsk, sec_args, attrnames); } return (fflag ? 0 : status); } +static void +free_attr_names(attr_name_t *attrnames) +{ + attr_name_t *attrnamesptr = attrnames; + attr_name_t *tptr; + + while (attrnamesptr != NULL) { + tptr = attrnamesptr->next; + if (attrnamesptr->name != NULL) { + free(attrnamesptr->name); + } + attrnamesptr = tptr; + } +} + static int -dochmod(char *name, char *path, mode_t umsk, acl_args_t *aclp) +dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp, + attr_name_t *attrnames) { static struct stat st; int linkflg = 0; @@ -194,15 +294,24 @@ dochmod(char *name, char *path, mode_t umsk, acl_args_t *aclp) } /* Do not recurse if directory is object of symbolic link */ - if (rflag && ((st.st_mode & S_IFMT) == S_IFDIR) && !linkflg) - return (chmodr(name, path, st.st_mode, umsk, aclp)); + if (rflag && ((st.st_mode & S_IFMT) == S_IFDIR) && !linkflg) { + return (chmodr(name, path, st.st_mode, umsk, secp, attrnames)); + } - if (aclp) { - return (doacl(name, &st, aclp)); - } else if (chmod(name, newmode_common(ms, st.st_mode, umsk, name, path, - &group_clear_bits, &group_set_bits)) == -1) { - errmsg(2, 0, gettext("can't change %s\n"), path); - return (1); + if (secp != NULL) { + if (secp->sec_type == SEC_ACL) { + return (doacl(name, &st, secp->sec_acls)); + } else if (secp->sec_type == SEC_ATTR) { + return (set_attrs(name, attrnames, secp->sec_attrs)); + } else { + return (1); + } + } else { + if (chmod(name, newmode_common(ms, st.st_mode, umsk, name, path, + &group_clear_bits, &group_set_bits)) == -1) { + errmsg(2, 0, gettext("can't change %s\n"), path); + return (1); + } } /* @@ -218,9 +327,9 @@ dochmod(char *name, char *path, mode_t umsk, acl_args_t *aclp) return (0); } - static int -chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp) +chmodr(char *dir, char *path, mode_t mode, mode_t umsk, sec_args_t *secp, + attr_name_t *attrnames) { DIR *dirp; @@ -239,13 +348,21 @@ chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp) /* * Change what we are given before doing it's contents */ - if (aclp) { + if (secp != NULL) { if (lstat(dir, &st) < 0) { errmsg(2, 0, gettext("can't access %s\n"), path); return (1); } - if (doacl(dir, &st, aclp) != 0) + if (secp->sec_type == SEC_ACL) { + if (doacl(dir, &st, secp->sec_acls) != 0) + return (1); + } else if (secp->sec_type == SEC_ATTR) { + if (set_attrs(dir, attrnames, secp->sec_attrs) != 0) { + return (1); + } + } else { return (1); + } } else if (chmod(dir, newmode_common(ms, mode, umsk, dir, path, &group_clear_bits, &group_set_bits)) < 0) { errmsg(2, 0, gettext("can't change %s\n"), path); @@ -260,7 +377,8 @@ chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp) * general group permissions. */ - if (aclp == NULL) { /* only necessary when not setting ACL */ + if (secp != NULL) { + /* only necessary when not setting ACL or system attributes */ if (group_clear_bits || group_set_bits) handle_acl(dir, group_clear_bits, group_set_bits); } @@ -315,7 +433,7 @@ chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp) currdir, dp->d_name); return (1); } - ecode += dochmod(dp->d_name, currdir, umsk, aclp); + ecode += dochmod(dp->d_name, currdir, umsk, secp, attrnames); } (void) closedir(dirp); if (chdir(savedir) < 0) { @@ -344,7 +462,7 @@ errmsg(int severity, int code, char *format, ...) */ if (!fflag || (code != 0)) { (void) fprintf(stderr, - "chmod: %s: ", gettext(msg[severity])); + "chmod: %s: ", gettext(msg[severity])); (void) vfprintf(stderr, format, ap); } @@ -361,17 +479,36 @@ usage(void) "usage:\tchmod [-fR] file ...\n")); (void) fprintf(stderr, gettext( - "\tchmod [-fR] file ...\n")); + "\tchmod [-fR] [-@ attribute] ... " + "S file ...\n")); (void) fprintf(stderr, gettext( - "\tchmod [-fR] file ...\n")); + "\tchmod [-fR] file ...\n")); + (void) fprintf(stderr, gettext( + "\tchmod [-fR] file ...\n\n")); (void) fprintf(stderr, gettext( "where \t is a comma-separated list of\n")); + (void) fprintf(stderr, gettext( + "\t[ugoa]{+|-|=}[rwxXlstugo]\n\n")); (void) fprintf(stderr, gettext( - "\t[ugoa]{+|-|=}[rwxXlstugo]\n")); + "where \t is a comma-separated list of\n" + "\tone or more of the following\n")); + (void) fprintf(stderr, gettext( + "\t[+|-|=]c[|{}]\n" + "\t[+|-|=]v[|" + "\'{\'\'}\']\n" + "\t[+|-|=]a\n")); + (void) fprintf(stderr, gettext( + "where \t is a list of zero or more of\n")); + print_attrs(ATTR_OPTS); + (void) fprintf(stderr, gettext( + "where \t is one of\n")); + print_attrs(ATTR_NAMES); + (void) fprintf(stderr, gettext( + "\tand can be, optionally, immediately preceded by \"no\"\n\n")); (void) fprintf(stderr, gettext( "where \t is one of the following\n")); @@ -416,7 +553,7 @@ parseargs(int ac, char *av[]) for (fflag = i = 0; i < ac; i ++) { if (strcmp(av[i], "--") == 0) - fflag = 1; + fflag = 1; } /* process the arguments */ @@ -435,19 +572,26 @@ parseargs(int ac, char *av[]) */ if ((strchr(av[i], 'R') == NULL && - strchr(av[i], 'f') == NULL)) { - mav[mac++] = strdup("--"); + strchr(av[i], 'f') == NULL) && + strchr(av[i], '@') == NULL) { + if ((mav[mac++] = strdup("--")) == NULL) { + perror("chmod"); + exit(2); + } } } - mav[mac++] = strdup(av[i]); + if ((mav[mac++] = strdup(av[i])) == NULL) { + perror("chmod"); + exit(2); + } } mav[mac] = (char *)NULL; } -int -parse_acl_args(char *arg, acl_args_t **acl_args) +static int +parse_acl_args(char *arg, sec_args_t **sec_args) { acl_t *new_acl = NULL; int slot; @@ -512,7 +656,12 @@ parse_acl_args(char *arg, acl_args_t **acl_args) new_acl_args->acl_slot = slot; new_acl_args->acl_action = action; - *acl_args = new_acl_args; + if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) { + perror("chmod"); + exit(2); + } + (*sec_args)->sec_type = SEC_ACL; + (*sec_args)->sec_acls = new_acl_args; return (0); } @@ -593,10 +742,10 @@ doacl(char *file, struct stat *st, acl_args_t *acl_args) switch (acl_args->acl_action) { case ACL_ADD: if ((error = acl_addentries(aclp, - acl_args->acl_aclp, acl_args->acl_slot)) != 0) { - errmsg(1, 1, "%s\n", acl_strerror(error)); - acl_free(aclp); - return (1); + acl_args->acl_aclp, acl_args->acl_slot)) != 0) { + errmsg(1, 1, "%s\n", acl_strerror(error)); + acl_free(aclp); + return (1); } set_aclp = aclp; break; @@ -699,3 +848,630 @@ doacl(char *file, struct stat *st, acl_args_t *acl_args) acl_free(aclp); return (0); } + +/* + * Prints out the attributes in their verbose form: + * '{'[["no"]][,["no"]]...'}' + * similar to output of ls -/v. + */ +static void +print_nvlist(nvlist_t *attr_nvlist) +{ + int firsttime = 1; + boolean_t value; + nvlist_t *lptr = attr_nvlist; + nvpair_t *pair = NULL; + + (void) fprintf(stderr, "\t%c", LEFTBRACE); + while (pair = nvlist_next_nvpair(lptr, pair)) { + if (nvpair_value_boolean_value(pair, &value) == 0) { + (void) fprintf(stderr, "%s%s%s", + firsttime ? "" : A_SEP_TOK, + (value == A_SET_VAL) ? "" : "no", + nvpair_name(pair)); + firsttime = 0; + } else { + (void) fprintf(stderr, gettext( + ""), + strerror(errno)); + break; + } + } + (void) fprintf(stderr, "%c\n", RIGHTBRACE); +} + +/* + * Add an attribute name and boolean value to an nvlist if an action is to be + * performed for that attribute. The nvlist will be used later to set all the + * attributes in the nvlist in one operation through a call to setattrat(). + * + * If a set operation ('+') was specified, then a boolean representation of the + * attribute's value will be added to the nvlist for that attribute name. If an + * inverse operation ('-') was specified, then a boolean representation of the + * inverse of the attribute's value will be added to the nvlist for that + * attribute name. + * + * Returns an nvlist of attribute name and boolean value pairs if there are + * attribute actions to be performed, otherwise returns NULL. + */ +static nvlist_t * +set_attrs_nvlist(char *attractptr, int numofattrs) +{ + int attribute_set = 0; + f_attr_t i; + nvlist_t *attr_nvlist; + + if (nvlist_alloc(&attr_nvlist, NV_UNIQUE_NAME, 0) != 0) { + perror("chmod"); + exit(2); + } + + for (i = 0; i < numofattrs; i++) { + if (attractptr[i] != '\0') { + if ((nvlist_add_boolean_value(attr_nvlist, + attr_to_name(i), + (attractptr[i] == A_SET_OP))) != 0) { + errmsg(1, 2, gettext( + "unable to propagate attribute names and" + "values: %s\n"), strerror(errno)); + } else { + attribute_set = 1; + } + } + } + return (attribute_set ? attr_nvlist : NULL); +} + +/* + * Set the attributes of file, or if specified, of the named attribute file, + * attrname. Build an nvlist of attribute names and values and call setattrat() + * to set the attributes in one operation. + * + * Returns 0 if successful, otherwise returns 1. + */ +static int +set_file_attrs(char *file, char *attrname, nvlist_t *attr_nvlist) +{ + int rc; + char *filename; + + if (attrname != NULL) { + filename = attrname; + } else { + filename = basename(file); + } + + if ((rc = setattrat(AT_FDCWD, XATTR_VIEW_READWRITE, filename, + attr_nvlist)) != 0) { + char *emsg; + switch (errno) { + case EINVAL: + emsg = gettext("not supported"); + break; + case EPERM: + emsg = gettext("not privileged"); + break; + default: + emsg = strerror(rc); + } + errmsg(1, 0, gettext( + "cannot set the following attributes on " + "%s%s%s%s: %s\n"), + (attrname == NULL) ? "" : gettext("attribute "), + (attrname == NULL) ? "" : attrname, + (attrname == NULL) ? "" : gettext(" of "), + file, emsg); + print_nvlist(attr_nvlist); + } + + return (rc); +} + +static int +save_cwd(void) +{ + return (open(".", O_RDONLY)); +} + +static void +rest_cwd(int cwd) +{ + if (cwd != -1) { + if (fchdir(cwd) != 0) { + errmsg(1, 1, gettext( + "can't change to current working directory\n")); + } + (void) close(cwd); + } +} + +/* + * Returns 1 if filename is a system attribute file, otherwise + * returns 0. + */ +static int +is_sattr(char *filename) +{ + return (sysattr_type(filename) != _NOT_SATTR); +} + +/* + * Perform the action on the specified named attribute file for the file + * associated with the input file descriptor. If the named attribute file + * is "*", then the action is to be performed on all the named attribute files + * of the file associated with the input file descriptor. + */ +static int +set_named_attrs(char *file, int parentfd, char *attrname, nvlist_t *attr_nvlist) +{ + int dirfd; + int error = 0; + DIR *dirp = NULL; + struct dirent *dp; + struct stat st; + + if ((attrname == NULL) || (strcmp(attrname, "*") != 0)) { + /* + * Make sure the named attribute exists and extended system + * attributes are supported on the underlying file system. + */ + if (attrname != NULL) { + if (fstatat(parentfd, attrname, &st, + AT_SYMLINK_NOFOLLOW) < 0) { + errmsg(2, 0, gettext( + "can't access attribute %s of %s\n"), + attrname, file); + return (1); + } + if (sysattr_support(attrname, _PC_SATTR_ENABLED) != 1) { + errmsg(1, 0, gettext( + "extended system attributes not supported " + "for attribute %s of %s\n"), + attrname, file); + return (1); + } + } + + error = set_file_attrs(file, attrname, attr_nvlist); + + } else { + if (((dirfd = dup(parentfd)) == -1) || + ((dirp = fdopendir(dirfd)) == NULL)) { + errmsg(1, 0, gettext( + "cannot open dir pointer of file %s\n"), file); + if (dirfd > 0) { + (void) close(dirfd); + } + return (1); + } + + while (dp = readdir(dirp)) { + /* + * Process all extended attribute files except + * ".", "..", and extended system attribute files. + */ + if ((strcmp(dp->d_name, ".") == 0) || + (strcmp(dp->d_name, "..") == 0) || + is_sattr(dp->d_name)) { + continue; + } + + if (set_named_attrs(file, parentfd, dp->d_name, + attr_nvlist) != 0) { + error++; + } + } + if (dirp != NULL) { + (void) closedir(dirp); + } + } + + return ((error == 0) ? 0 : 1); +} + +/* + * Set the attributes of the specified file, or if specified with -@ on the + * command line, the specified named attributes of the specified file. + * + * Returns 0 if successful, otherwise returns 1. + */ +static int +set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist) +{ + char *parentd; + char *tpath = NULL; + int cwd; + int error = 0; + int parentfd; + attr_name_t *tattr = attrnames; + + if (attr_nvlist == NULL) { + return (0); + } + + if (sysattr_support(file, _PC_SATTR_ENABLED) != 1) { + errmsg(1, 0, gettext( + "extended system attributes not supported for %s\n"), file); + return (1); + } + + /* + * Open the parent directory and change into it before attempting + * to set the attributes of the file. + */ + if (attrnames == NULL) { + tpath = strdup(file); + parentd = dirname(tpath); + parentfd = open(parentd, O_RDONLY); + } else { + parentfd = attropen(file, ".", O_RDONLY); + } + if (parentfd == -1) { + errmsg(1, 0, gettext( + "cannot open attribute directory of %s\n"), file); + if (tpath != NULL) { + free(tpath); + } + return (1); + } + + if ((cwd = save_cwd()) < 0) { + errmsg(1, 1, gettext( + "can't get current working directory\n")); + } + if (fchdir(parentfd) != 0) { + errmsg(1, 0, gettext( + "can't change to parent %sdirectory of %s\n"), + (attrnames == NULL) ? "" : gettext("attribute "), file); + (void) close(cwd); + (void) close(parentfd); + if (tpath != NULL) { + free(tpath); + } + return (1); + } + + /* + * If no named attribute file names were provided on the command line + * then set the attributes of the base file, otherwise, set the + * attributes for each of the named attribute files specified. + */ + if (attrnames == NULL) { + error = set_named_attrs(file, parentfd, NULL, attr_nvlist); + free(tpath); + } else { + while (tattr != NULL) { + if (set_named_attrs(file, parentfd, tattr->name, + attr_nvlist) != 0) { + error++; + } + tattr = tattr->next; + } + } + (void) close(parentfd); + rest_cwd(cwd); + + return ((error == 0) ? 0 : 1); +} + +/* + * Prints the attributes in either the compact or verbose form indicated + * by flag. + */ +static void +print_attrs(int flag) +{ + f_attr_t i; + static int numofattrs; + int firsttime = 1; + + numofattrs = attr_count(); + + (void) fprintf(stderr, gettext("\t[")); + for (i = 0; i < numofattrs; i++) { + if ((attr_to_xattr_view(i) != XATTR_VIEW_READWRITE) || + (attr_to_data_type(i) != DATA_TYPE_BOOLEAN_VALUE)) { + continue; + } + (void) fprintf(stderr, "%s%s", + (firsttime == 1) ? "" : gettext("|"), + (flag == ATTR_OPTS) ? attr_to_option(i) : attr_to_name(i)); + firsttime = 0; + } + (void) fprintf(stderr, gettext("]\n")); +} + +/* + * Record what action should be taken on the specified attribute. Only boolean + * read-write attributes can be manipulated. + * + * Returns 0 if successful, otherwise returns 1. + */ +static int +set_attr_args(f_attr_t attr, char action, char *attractptr) +{ + if ((attr_to_xattr_view(attr) == XATTR_VIEW_READWRITE) && + (attr_to_data_type(attr) == DATA_TYPE_BOOLEAN_VALUE)) { + attractptr[attr] = action; + return (0); + } + return (1); +} + +/* + * Parses the entry and assigns the appropriate action (either '+' or '-' in + * attribute's position in the character array pointed to by attractptr, where + * upon exit, attractptr is positional and the value of each character specifies + * whether to set (a '+'), clear (a '-'), or leave untouched (a '\0') the + * attribute value. + * + * If the entry is an attribute name, then the A_SET_OP action is to be + * performed for this attribute. If the entry is an attribute name proceeded + * with "no", then the A_INVERSE_OP action is to be performed for this + * attribute. If the entry is one or more attribute option letters, then step + * through each of the option letters marking the action to be performed for + * each of the attributes associated with the letter as A_SET_OP. + * + * Returns 0 if the entry was a valid attribute(s) and the action to be + * performed on that attribute(s) has been recorded, otherwise returns 1. + */ +static int +parse_entry(char *entry, char action, char atype, int len, char *attractptr) +{ + char aopt[2] = {'\0', '\0'}; + char *aptr; + f_attr_t attr; + + if (atype == A_VERBOSE_TYPE) { + if ((attr = name_to_attr(entry)) != F_ATTR_INVAL) { + return (set_attr_args(attr, + (action == A_REPLACE_OP) ? A_SET_OP : action, + attractptr)); + } else if ((len > 2) && (strncmp(entry, "no", 2) == 0) && + ((attr = name_to_attr(entry + 2)) != F_ATTR_INVAL)) { + return (set_attr_args(attr, ((action == A_REPLACE_OP) || + (action == A_SET_OP)) ? A_INVERSE_OP : A_SET_OP, + attractptr)); + } else { + return (1); + } + } else if (atype == A_COMPACT_TYPE) { + for (aptr = entry; *aptr != '\0'; aptr++) { + *aopt = *aptr; + /* + * The output of 'ls' can be used as the attribute mode + * specification for chmod. This output can contain a + * hypen ('-') for each attribute that is not set. If + * so, ignore them. If a replace action is being + * performed, then all attributes that don't have an + * action set here, will be cleared down the line. + */ + if (*aptr == '-') { + continue; + } + if (set_attr_args(option_to_attr(aopt), + (action == A_REPLACE_OP) ? A_SET_OP : action, + attractptr) != 0) { + return (1); + } + } + return (0); + } + return (1); +} + +/* + * Parse the attribute specification, aoptsstr. Upon completion, attr_nvlist + * will point to an nvlist which contains pairs of attribute names and values + * to be set; attr_nvlist will be NULL if it is a no-op. + * + * The attribute specification format is + * S[oper]attr_type[attribute_list] + * where oper is + * + set operation of specified attributes in attribute list. + * This is the default operation. + * - inverse operation of specified attributes in attribute list + * = replace operation of all attributes. All attribute operations + * depend on those specified in the attribute list. Attributes + * not specified in the attribute list will be cleared. + * where attr_type is + * c compact type. Each entry in the attribute list is a character + * option representing an associated attribute name. + * v verbose type. Each entry in the attribute list is an + * an attribute name which can optionally be preceeded with "no" + * (to imply the attribute should be cleared). + * a all attributes type. The oper should be applied to all + * read-write boolean system attributes. No attribute list should + * be specified after an 'a' attribute type. + * + * Returns 0 if aoptsstr contained a valid attribute specification, + * otherwise, returns 1. + */ +static int +parse_attr_args(char *aoptsstr, sec_args_t **sec_args) +{ + char action; + char *attractptr; + char atype; + char *entry; + char *eptr; + char *nextattr; + char *nextentry; + char *subentry; + char *teptr; + char tok[] = {'\0', '\0'}; + int len; + f_attr_t i; + int numofattrs; + + if ((*aoptsstr != 'S') || (*(aoptsstr + 1) == '\0')) { + return (1); + } + + if ((eptr = strdup(aoptsstr + 1)) == NULL) { + perror("chmod"); + exit(2); + } + entry = eptr; + + /* + * Create a positional character array to determine a single attribute + * operation to be performed, where each index represents the system + * attribute affected, and it's value in the array represents the action + * to be performed, i.e., a value of '+' means to set the attribute, a + * value of '-' means to clear the attribute, and a value of '\0' means + * to leave the attribute untouched. Initially, this positional + * character array is all '\0's, representing a no-op. + */ + if ((numofattrs = attr_count()) < 1) { + errmsg(1, 1, gettext("system attributes not supported\n")); + } + + if ((attractptr = calloc(numofattrs, sizeof (char))) == NULL) { + perror("chmod"); + exit(2); + } + + if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) { + perror("chmod"); + exit(2); + } + (*sec_args)->sec_type = SEC_ATTR; + (*sec_args)->sec_attrs = NULL; + + /* Parse each attribute operation within the attribute specification. */ + while ((entry != NULL) && (*entry != '\0')) { + action = A_SET_OP; + atype = '\0'; + + /* Get the operator. */ + switch (*entry) { + case A_SET_OP: + case A_INVERSE_OP: + case A_REPLACE_OP: + action = *entry++; + break; + case A_COMPACT_TYPE: + case A_VERBOSE_TYPE: + case A_ALLATTRS_TYPE: + atype = *entry++; + action = A_SET_OP; + break; + default: + break; + } + + /* An attribute type must be specified. */ + if (atype == '\0') { + if ((*entry == A_COMPACT_TYPE) || + (*entry == A_VERBOSE_TYPE) || + (*entry == A_ALLATTRS_TYPE)) { + atype = *entry++; + } else { + return (1); + } + } + + /* Get the attribute specification separator. */ + if (*entry == LEFTBRACE) { + *tok = RIGHTBRACE; + entry++; + } else { + *tok = A_SEP; + } + + /* Get the attribute operation */ + if ((nextentry = strpbrk(entry, tok)) != NULL) { + *nextentry = '\0'; + nextentry++; + } + + /* Check for a no-op */ + if ((*entry == '\0') && (atype != A_ALLATTRS_TYPE) && + (action != A_REPLACE_OP)) { + entry = nextentry; + continue; + } + + /* + * Step through the attribute operation, setting the + * appropriate values for the specified attributes in the + * character array, attractptr. A value of '+' will mean the + * attribute is to be set, and a value of '-' will mean the + * attribute is to be cleared. If the value of an attribute + * remains '\0', then no action is to be taken on that + * attribute. As multiple operations specified are + * accumulated, a single attribute setting operation is + * represented in attractptr. + */ + len = strlen(entry); + if ((*tok == RIGHTBRACE) || (action == A_REPLACE_OP) || + (atype == A_ALLATTRS_TYPE)) { + + if ((action == A_REPLACE_OP) || + (atype == A_ALLATTRS_TYPE)) { + (void) memset(attractptr, '\0', numofattrs); + } + + if (len > 0) { + if ((teptr = strdup(entry)) == NULL) { + perror("chmod"); + exit(2); + } + subentry = teptr; + while (subentry != NULL) { + if ((nextattr = strpbrk(subentry, + A_SEP_TOK)) != NULL) { + *nextattr = '\0'; + nextattr++; + } + if (parse_entry(subentry, action, + atype, len, attractptr) != 0) { + return (1); + } + subentry = nextattr; + } + free(teptr); + } + + /* + * If performing the replace action, record the + * attributes and values for the rest of the + * attributes that have not already been recorded, + * otherwise record the specified action for all + * attributes. Note: set_attr_args() will only record + * the attribute and action if it is a boolean + * read-write attribute so we don't need to worry + * about checking it here. + */ + if ((action == A_REPLACE_OP) || + (atype == A_ALLATTRS_TYPE)) { + for (i = 0; i < numofattrs; i++) { + if (attractptr[i] == A_UNDEF_OP) { + (void) set_attr_args(i, + (action == A_SET_OP) ? + A_SET_OP : A_INVERSE_OP, + attractptr); + } + } + } + + } else { + if (parse_entry(entry, action, atype, len, + attractptr) != 0) { + return (1); + } + } + entry = nextentry; + } + + /* + * Populate an nvlist with attribute name and boolean value pairs + * using the single attribute operation. + */ + (*sec_args)->sec_attrs = set_attrs_nvlist(attractptr, numofattrs); + free(attractptr); + free(eptr); + + return (0); +} diff --git a/usr/src/cmd/cmd-inet/etc/services b/usr/src/cmd/cmd-inet/etc/services index a4862b5bbeff..edb0bb6c5d44 100644 --- a/usr/src/cmd/cmd-inet/etc/services +++ b/usr/src/cmd/cmd-inet/etc/services @@ -93,6 +93,8 @@ slp 427/tcp slp # Service Location Protocol, V2 slp 427/udp slp # Service Location Protocol, V2 mobile-ip 434/udp mobile-ip # Mobile-IP cvc_hostd 442/tcp # Network Console +microsoft-ds 445/tcp # Microsoft Directory Services +microsoft-ds 445/udp # Microsoft Directory Services ike 500/udp ike # Internet Key Exchange uuidgen 697/tcp # UUID Generator uuidgen 697/udp # UUID Generator diff --git a/usr/src/cmd/compress/Makefile b/usr/src/cmd/compress/Makefile index 5fc1e35e4217..ea306d8cc278 100644 --- a/usr/src/cmd/compress/Makefile +++ b/usr/src/cmd/compress/Makefile @@ -39,7 +39,7 @@ CFLAGS += $(CCVERBOSE) CPPFLAGS += -D_FILE_OFFSET_BITS=64 -I $(SRC)/common/util LINTFLAGS += -u -LDLIBS += -lsec +LDLIBS += -lcmdutils -lsec all: $(PROG) diff --git a/usr/src/cmd/compress/compress.c b/usr/src/cmd/compress/compress.c index fd08c406b9a9..de8e62b704fe 100644 --- a/usr/src/cmd/compress/compress.c +++ b/usr/src/cmd/compress/compress.c @@ -116,30 +116,20 @@ static char_type magic_header[] = { "\037\235" }; /* 1F 9D */ static char rcs_ident[] = "$Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $"; -#include #include #include -#include -#include -#include #include -#include /* XCU4 */ -#include -#include #include #include -#include #include #include #include #include -#include -#include -#include #include -#include +#include #include "getresponse.h" + static int n_bits; /* number of bits/code */ static int maxbits = BITS; /* user settable max # bits/code */ static code_int maxcode; /* maximum code, given n_bits */ @@ -246,7 +236,6 @@ static void oops(); static void output(code_int); static void prratio(FILE *, count_long, count_long); static void version(void); -static int mv_xattrs(char *, char *, int); #ifdef DEBUG static int in_stack(int, int); @@ -287,6 +276,10 @@ static count_long bytes_out; /* length of compressed output */ code_int sorttab[1<st_mode & 07777; - if (chmod(ofname, mode)) /* Copy modes */ + if (chmod(ofname, mode)) { /* Copy modes */ + if (errno == EPERM) { + (void) fprintf(stderr, + gettext("failed to chmod %s" + "- permisssion denied\n"), ofname); + } perror(ofname); - + } error = acl_get(ifname, ACL_NO_TRIVIAL, &aclp); if (error != 0) { (void) fprintf(stderr, gettext( @@ -1640,7 +1661,8 @@ copystat(char *ifname, struct stat *ifstat, char *ofname) perm_stat = 1; } if (aclp && (acl_set(ofname, aclp) < 0)) { - (void) fprintf(stderr, gettext("%s: failed to set acl " + (void) fprintf(stderr, + gettext("%s: failed to set acl " "entries\n"), ofname); perm_stat = 1; } @@ -1655,8 +1677,14 @@ copystat(char *ifname, struct stat *ifstat, char *ofname) timep.modtime = ifstat->st_mtime; /* Update last accessed and modified times */ (void) utime(ofname, &timep); - if (unlink(ifname)) /* Remove input file */ + if (unlink(ifname)) { /* Remove input file */ + if (errno == EPERM) { + (void) fprintf(stderr, + gettext("failed to remove %s" + "- permisssion denied\n"), ifname); + } perror(ifname); + } if (!quiet) { (void) fprintf(stderr, gettext( " -- replaced with %s"), ofname); @@ -1668,6 +1696,11 @@ copystat(char *ifname, struct stat *ifstat, char *ofname) /* Unsuccessful return -- one of the tests failed */ if (ofname[0] != '\0') { if (unlink(ofname)) { + if (errno == EPERM) { + (void) fprintf(stderr, + gettext("failed to remove %s" + "- permisssion denied\n"), ifname); + } perror(ofname); } @@ -1804,16 +1837,16 @@ Usage() { #ifdef DEBUG (void) fprintf(stderr, - "Usage: compress [-dDVfc] [-b maxbits] [file ...]\n"); + "Usage: compress [-dDVfc/] [-b maxbits] [file ...]\n"); #else if (strcmp(progname, "compress") == 0) { (void) fprintf(stderr, gettext( - "Usage: compress [-fv] [-b maxbits] [file ...]\n"\ - " compress [-cfv] [-b maxbits] [file]\n")); + "Usage: compress [-fv/] [-b maxbits] [file ...]\n"\ + " compress c [-fv] [-b maxbits] [file]\n")); } else if (strcmp(progname, "uncompress") == 0) (void) fprintf(stderr, gettext( - "Usage: uncompress [-cfv] [file ...]\n")); + "Usage: uncompress [-fv] [-c || -/] [file ...]\n")); else if (strcmp(progname, "zcat") == 0) (void) fprintf(stderr, gettext("Usage: zcat [file ...]\n")); @@ -1872,73 +1905,3 @@ addDotZ(char *fn, size_t fnsize) return (0); } - -/* - * mv_xattrs - move (via renameat) all of the extended attributes - * associated with the file infile to the file outfile. - * This function returns 0 on success and -1 on error. - */ -static int -mv_xattrs(char *infile, char *outfile, int silent) -{ - int indfd, outdfd, tmpfd; - DIR *dirp = NULL; - struct dirent *dp = NULL; - int error = 0; - char *etext; - - indfd = outdfd = tmpfd = -1; - - if ((indfd = attropen(infile, ".", O_RDONLY)) == -1) { - etext = gettext("cannot open source"); - error = -1; - goto out; - } - - if ((outdfd = attropen(outfile, ".", O_RDONLY)) == -1) { - etext = gettext("cannot open target"); - error = -1; - goto out; - } - - if ((tmpfd = dup(indfd)) == -1) { - etext = gettext("cannot dup descriptor"); - error = -1; - goto out; - - } - if ((dirp = fdopendir(tmpfd)) == NULL) { - etext = gettext("cannot access source"); - error = -1; - goto out; - } - - while (dp = readdir(dirp)) { - if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') || - (dp->d_name[0] == '.' && dp->d_name[1] == '.' && - dp->d_name[2] == '\0')) - continue; - if ((renameat(indfd, dp->d_name, outdfd, dp->d_name)) == -1) { - etext = dp->d_name; - error = -1; - goto out; - } - } -out: - if (error == -1 && silent == 0) { - if (quiet) { - (void) fprintf(stderr, "%s: ", infile); - } else { - (void) fprintf(stderr, ", "); - } - (void) fprintf(stderr, gettext("extended attribute error: ")); - perror(etext); - } - if (dirp) - (void) closedir(dirp); - if (indfd != -1) - (void) close(indfd); - if (outdfd != -1) - (void) close(outdfd); - return (error); -} diff --git a/usr/src/cmd/cpio/cpio.c b/usr/src/cmd/cpio/cpio.c index 7e12c2ef29ce..2a3dade1b6be 100644 --- a/usr/src/cmd/cpio/cpio.c +++ b/usr/src/cmd/cpio/cpio.c @@ -418,7 +418,7 @@ char Adir, /* Flags object as a directory */ static int Append = 0, /* Flag set while searching to end of archive */ Archive, /* File descriptor of the archive */ - Buf_error = 0, /* I/O error occured during buffer fill */ + Buf_error = 0, /* I/O error occurred during buffer fill */ Def_mode = 0777, /* Default file/directory protection modes */ Device, /* Device type being accessed (used with libgenIO) */ Error_cnt = 0, /* Cumulative count of I/O errors */ @@ -1986,7 +1986,7 @@ creat_tmp(char *nam_p) /* * If it's a regular file, write to the temporary file, and then rename - * in order to accomodate potential executables. + * in order to accommodate potential executables. * * Note: g_typeflag is only defined (set) for USTAR archive types. It * defaults to 0 in the cpio-format-regular file case, so this test @@ -3269,6 +3269,15 @@ flush_lnks(void) } /* l_p != &Lnk_hd */ } +#if defined(O_XATTR) +static int +is_sysattr(char *name) +{ + return ((strcmp(name, VIEW_READONLY) == 0) || + (strcmp(name, VIEW_READWRITE) == 0)); +} +#endif + /* * gethdr: Get a header from the archive, validate it and check for the trailer. * Any user specified Hdr_type is ignored (set to NONE in main). Hdr_type is @@ -3548,6 +3557,8 @@ gethdr(void) if (((Gen.g_mode & S_IFMT) == _XATTR_CPIO_MODE) || ((Hdr_type == USTAR || Hdr_type == TAR) && Thdr_p->tbuf.t_typeflag == _XATTR_HDRTYPE)) { + char *tapath; + if (xattrp != (struct xattr_buf *)NULL) { if (xattrbadhead) { free(xattrhead); @@ -3556,7 +3567,18 @@ gethdr(void) xattrhead = NULL; return (1); } - if (Atflag == 0 && ((Args & OCt) == 0)) { + + /* + * Skip processing of an attribute if -@ wasn't + * specified, or if -@ was specified and the attribute + * is either an extended system attribute or if the + * attribute name is not a file name, but a path name + * (-@ should only process extended attributes). + */ + tapath = xattrp->h_names + strlen(xattrp->h_names) + 1; + if (((Args & OCt) == 0) && ((Atflag == 0) || + (is_sysattr(tapath) || + (strpbrk(tapath, "/") != NULL)))) { data_in(P_SKIP); free(xattrhead); xattrhead = NULL; @@ -3581,8 +3603,7 @@ gethdr(void) xattrp->h_names); } - Gen.g_attrnam_p = e_strdup(E_EXIT, - xattrp->h_names + strlen(xattrp->h_names) + 1); + Gen.g_attrnam_p = e_strdup(E_EXIT, tapath); if (Hdr_type != USTAR && Hdr_type != TAR) { Gen.g_mode = Gen.g_mode & (~_XATTR_CPIO_MODE); @@ -3602,8 +3623,7 @@ gethdr(void) Gen.g_linktoattrfnam_p = e_strdup(E_EXIT, xattr_linkp->h_names); Gen.g_linktoattrnam_p = e_strdup(E_EXIT, - xattr_linkp->h_names + - strlen(xattr_linkp->h_names) + 1); + tapath); xattr_linkp = NULL; } ftype = Gen.g_mode & Ftype; @@ -3738,7 +3758,7 @@ gethdr(void) * getname: Get file names for inclusion in the archive. When end of file * on the input stream of file names is reached, flush the link buffer out. * For each filename, remove leading "./"s and multiple "/"s, and remove - * any trailing newline "\n". Finally, verify the existance of the file, + * any trailing newline "\n". Finally, verify the existence of the file, * and call creat_hdr() to fill in the gen_hdr structure. */ @@ -4065,7 +4085,7 @@ matched(void) if ((result == 0 && ! negatep) || (result == FNM_NOMATCH && negatep)) { - /* match occured */ + /* match occurred */ return (!(Args & OCf)); } } @@ -4760,7 +4780,7 @@ setpasswd(char *nam) * * Note that if Do_rename is set, then the roles of original and temporary * file are reversed. If all went well, we will rename() the temporary file - * over the original in order to accomodate potentially executing files. + * over the original in order to accommodate potentially executing files. */ static void rstfiles(int over, int dirfd) @@ -5567,7 +5587,12 @@ verbose(char *nam_p) * Translation note: * 'attribute' is a noun. */ - name_fmt = gettext("%s attribute %s"); + + if (is_sysattr(basename(Gen.g_attrnam_p))) { + name_fmt = gettext("%s system attribute %s"); + } else { + name_fmt = gettext("%s attribute %s"); + } name = (Args & OCp) ? nam_p : Gen.g_attrfnam_p; attribute = Gen.g_attrnam_p; @@ -6890,7 +6915,8 @@ xattrs_out(int (*func)()) } while ((dp = readdir(dirp)) != (struct dirent *)NULL) { - if (strcmp(dp->d_name, "..") == 0) { + if ((strcmp(dp->d_name, "..") == 0) || + is_sysattr(dp->d_name)) { continue; } @@ -7200,9 +7226,12 @@ read_xattr_hdr() off_t bytes; int comp_len, link_len; int namelen; + int asz; int cnt; char *tp; + char *xattrapath; int pad; + int parentfilelen; /* * Include any padding in the read. We need to be positioned @@ -7259,6 +7288,32 @@ read_xattr_hdr() xattr_linkp = NULL; } + /* + * Gather the attribute path from the filename and attrnames section. + * The filename and attrnames section can be composed of two or more + * path segments separated by a null character. The first segment + * is the path to the parent file that roots the entire sequence in + * the normal name space. The remaining segments describes a path + * rooted at the hidden extended attribute directory of the leaf file of + * the previous segment, making it possible to name attributes on + * attributes. + */ + parentfilelen = strlen(xattrp->h_names); + xattrapath = xattrp->h_names + parentfilelen + 1; + asz = strlen(xattrapath); + if ((asz + parentfilelen + 2) < namelen) { + /* + * The attrnames section contains a system attribute on an + * attribute. Save the name of the attribute for use later, + * and replace the null separating the attribute name from + * the system attribute name with a '/' so that xattrapath can + * be used to display messages with the full attribute path name + * rooted at the hidden attribute directory of the base file + * in normal name space. + */ + xattrapath[asz] = '/'; + } + return (0); } #endif @@ -7885,7 +7940,8 @@ sl_preview_synonyms(void) dp->d_name[1] == '\0') || (dp->d_name[0] == '.' && dp->d_name[1] == '.' && - dp->d_name[2] == '\0')) + dp->d_name[2] == '\0') || + is_sysattr(dp->d_name)) continue; if (fstatat(dirfd, dp->d_name, &sb, diff --git a/usr/src/cmd/cpio/cpio.h b/usr/src/cmd/cpio/cpio.h index e4ad5912ba98..d34ee049a48f 100644 --- a/usr/src/cmd/cpio/cpio.h +++ b/usr/src/cmd/cpio/cpio.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -19,15 +18,14 @@ * * CDDL HEADER END */ -/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ -/* All Rights Reserved */ - - /* - * Copyright 1988-2002 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + #ifndef _CPIO_H #define _CPIO_H @@ -152,6 +150,15 @@ extern "C" { #define FORMAT "%b %e %H:%M %Y" +/* Extended system attributes */ +#ifndef VIEW_READONLY +#define VIEW_READONLY "SUNWattr_ro" +#endif + +#ifndef VIEW_READWRITE +#define VIEW_READWRITE "SUNWattr_rw" +#endif + /* Values used in typeflag field */ #define REGTYPE '0' /* Regular File */ #define LNKTYPE '1' /* Link */ diff --git a/usr/src/cmd/dfs.cmds/sharemgr/commands.c b/usr/src/cmd/dfs.cmds/sharemgr/commands.c index 6140b03e1eee..0f75ef7ebf06 100644 --- a/usr/src/cmd/dfs.cmds/sharemgr/commands.c +++ b/usr/src/cmd/dfs.cmds/sharemgr/commands.c @@ -48,6 +48,10 @@ #include #include #include +#include +#include +#include +#include static char *sa_get_usage(sa_usage_t); @@ -75,13 +79,175 @@ has_protocol(sa_group_t group, char *protocol) } /* - * add_list(list, item) - * Adds a new list member that points to item to the list. + * validresource(name) + * + * Check that name only has valid characters in it. The current valid + * set are the printable characters but not including: + * " / \ [ ] : | < > + ; , ? * = \t + * Note that space is included and there is a maximum length. + */ +static int +validresource(const char *name) +{ + const char *cp; + size_t len; + + if (name == NULL) + return (B_FALSE); + + len = strlen(name); + if (len == 0 || len > SA_MAX_RESOURCE_NAME) + return (B_FALSE); + + if (strpbrk(name, "\"/\\[]:|<>+;,?*=\t") != NULL) { + return (B_FALSE); + } + + for (cp = name; *cp != '\0'; cp++) + if (iscntrl(*cp)) + return (B_FALSE); + + return (B_TRUE); +} + +/* + * conv_to_utf8(input) + * + * Convert the input string to utf8 from the current locale. If the + * conversion fails, use the current locale, it is likely close + * enough. For example, the "C" locale is a subset of utf-8. The + * return value may be a new string or the original input string. + */ + +static char * +conv_to_utf8(char *input) +{ + iconv_t cd; + char *output = input; + char *outleft; + char *curlocale; + size_t bytesleft; + size_t size; + size_t osize; + static int warned = 0; + + curlocale = nl_langinfo(CODESET); + if (curlocale == NULL) + curlocale = "C"; + cd = iconv_open("UTF-8", curlocale); + if (cd != NULL && cd != (iconv_t)-1) { + size = strlen(input); + /* Assume worst case of characters expanding to 4 bytes. */ + bytesleft = size * 4; + output = calloc(bytesleft, 1); + if (output != NULL) { + outleft = output; + osize = iconv(cd, (const char **)&input, &size, + &outleft, &bytesleft); + if (osize == (size_t)-1 || size != 0) { + free(output); + output = input; + } + } + (void) iconv_close(cd); + } else { + if (!warned) + (void) fprintf(stderr, + gettext("Cannot convert to UTF-8 from %s\n"), + curlocale ? curlocale : gettext("unknown")); + warned = 1; + } + return (output); +} + +/* + * conv_from(input) + * + * Convert the input string from utf8 to current locale. If the + * conversion isn't supported, just use as is. The return value may be + * a new string or the original input string. + */ + +static char * +conv_from_utf8(char *input) +{ + iconv_t cd; + char *output = input; + char *outleft; + char *curlocale; + size_t bytesleft; + size_t size; + size_t osize; + static int warned = 0; + + curlocale = nl_langinfo(CODESET); + if (curlocale == NULL) + curlocale = "C"; + cd = iconv_open(curlocale, "UTF-8"); + if (cd != NULL && cd != (iconv_t)-1) { + size = strlen(input); + /* Assume worst case of characters expanding to 4 bytes. */ + bytesleft = size * 4; + output = calloc(bytesleft, 1); + if (output != NULL) { + outleft = output; + osize = iconv(cd, (const char **)&input, &size, + &outleft, &bytesleft); + if (osize == (size_t)-1 || size != 0) { + free(output); + output = input; + } + } + (void) iconv_close(cd); + } else { + if (!warned) + (void) fprintf(stderr, + gettext("Cannot convert to %s from UTF-8\n"), + curlocale ? curlocale : gettext("unknown")); + warned = 1; + } + return (output); +} + +static void +print_rsrc_desc(char *resource) +{ + char *description; + char *desc; + + description = sa_get_resource_description(resource); + if (description != NULL) { + desc = conv_from_utf8(description); + if (desc != description) { + sa_free_share_description(description); + description = desc; + } + (void) printf("\t\"%s\"", description); + sa_free_share_description(description); + } +} + +static int +set_share_desc(sa_share_t share, char *description) +{ + char *desc; + int ret; + + desc = conv_to_utf8(description); + ret = sa_set_share_description(share, desc); + if (description != desc) + sa_free_share_description(desc); + return (ret); +} + +/* + * add_list(list, item, data, proto) + * Adds a new list member that points holds item in the list. * If list is NULL, it starts a new list. The function returns * the first member of the list. */ struct list * -add_list(struct list *listp, void *item, void *data) +add_list(struct list *listp, void *item, void *data, char *proto) { struct list *new, *tmp; @@ -90,6 +256,7 @@ add_list(struct list *listp, void *item, void *data) new->next = NULL; new->item = item; new->itemdata = data; + new->proto = proto; } else { return (listp); } @@ -229,15 +396,92 @@ check_authorizations(char *instname, int flags) } /* - * enable_group(group, updateproto) + * notify_or_enable_share(share, protocol) + * + * Since some protocols don't want an "enable" when properties change, + * this function will use the protocol specific notify function + * first. If that fails, it will then attempt to use the + * sa_enable_share(). "protocol" is the protocol that was specified + * on the command line. + */ +static void +notify_or_enable_share(sa_share_t share, char *protocol) +{ + sa_group_t group; + sa_optionset_t opt; + int ret = SA_OK; + char *path; + char *groupproto; + sa_share_t parent = share; + + /* If really a resource, get parent share */ + if (!sa_is_share(share)) { + parent = sa_get_resource_parent((sa_resource_t)share); + } + + /* + * Now that we've got a share in "parent", make sure it has a path. + */ + path = sa_get_share_attr(parent, "path"); + if (path == NULL) + return; + + group = sa_get_parent_group(parent); + + if (group == NULL) { + sa_free_attr_string(path); + return; + } + for (opt = sa_get_optionset(group, NULL); + opt != NULL; + opt = sa_get_next_optionset(opt)) { + groupproto = sa_get_optionset_attr(opt, "type"); + if (groupproto == NULL || + (protocol != NULL && strcmp(groupproto, protocol) != 0)) { + sa_free_attr_string(groupproto); + continue; + } + if (sa_is_share(share)) { + if ((ret = sa_proto_change_notify(share, + groupproto)) != SA_OK) { + ret = sa_enable_share(share, groupproto); + if (ret != SA_OK) { + (void) printf( + gettext("Could not reenable" + " share %s: %s\n"), + path, sa_errorstr(ret)); + } + } + } else { + /* Must be a resource */ + if ((ret = sa_proto_notify_resource(share, + groupproto)) != SA_OK) { + ret = sa_enable_resource(share, groupproto); + if (ret != SA_OK) { + (void) printf( + gettext("Could not " + "reenable resource %s: " + "%s\n"), path, + sa_errorstr(ret)); + } + } + } + sa_free_attr_string(groupproto); + } + sa_free_attr_string(path); +} + +/* + * enable_group(group, updateproto, notify, proto) * * enable all the shares in the specified group. This is a helper for * enable_all_groups in order to simplify regular and subgroup (zfs) - * disabling. Group has already been checked for non-NULL. + * enabling. Group has already been checked for non-NULL. If notify + * is non-zero, attempt to use the notify interface rather than + * enable. */ - static void -enable_group(sa_group_t group, char *updateproto) +enable_group(sa_group_t group, char *updateproto, int notify, char *proto) { sa_share_t share; @@ -246,7 +490,10 @@ enable_group(sa_group_t group, char *updateproto) share = sa_get_next_share(share)) { if (updateproto != NULL) (void) sa_update_legacy(share, updateproto); - (void) sa_enable_share(share, NULL); + if (notify) + notify_or_enable_share(share, proto); + else + (void) sa_enable_share(share, proto); } } @@ -266,6 +513,7 @@ isenabled(sa_group_t group) if (group != NULL) { state = sa_get_group_attr(group, "state"); if (state != NULL) { + if (strcmp(state, "enabled") == 0) ret = B_TRUE; sa_free_attr_string(state); @@ -276,13 +524,15 @@ isenabled(sa_group_t group) /* * enable_all_groups(list, setstate, online, updateproto) - * Given a list of groups, enable each one found. If updateproto - * is not NULL, then update all the shares for the protocol that - * was passed in. + * + * Given a list of groups, enable each one found. If updateproto is + * not NULL, then update all the shares for the protocol that was + * passed in. If enable is non-zero, tell enable_group to try the + * notify interface since this is a property change. */ static int enable_all_groups(sa_handle_t handle, struct list *work, int setstate, - int online, char *updateproto) + int online, char *updateproto, int enable) { int ret; char instance[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1]; @@ -319,7 +569,23 @@ enable_all_groups(sa_handle_t handle, struct list *work, int setstate, /* if itemdata != NULL then a single share */ if (work->itemdata != NULL) { - ret = sa_enable_share((sa_share_t)work->itemdata, NULL); + if (enable) { + if (work->itemdata != NULL) + notify_or_enable_share(work->itemdata, + updateproto); + else + ret = SA_CONFIG_ERR; + } else { + if (sa_is_share(work->itemdata)) { + ret = sa_enable_share( + (sa_share_t)work->itemdata, + updateproto); + } else { + ret = sa_enable_resource( + (sa_resource_t)work->itemdata, + updateproto); + } + } } if (ret != SA_OK) break; @@ -328,18 +594,20 @@ enable_all_groups(sa_handle_t handle, struct list *work, int setstate, if (work->itemdata == NULL) { zfs = sa_get_group_attr(group, "zfs"); /* - * if the share is managed by ZFS, don't + * If the share is managed by ZFS, don't * update any of the protocols since ZFS is - * handling this. updateproto will contain + * handling this. Updateproto will contain * the name of the protocol that we want to * update legacy files for. */ - enable_group(group, zfs == NULL ? updateproto : NULL); + enable_group(group, zfs == NULL ? updateproto : NULL, + enable, work->proto); for (subgroup = sa_get_sub_group(group); subgroup != NULL; subgroup = sa_get_next_group(subgroup)) { /* never update legacy for ZFS subgroups */ - enable_group(subgroup, NULL); + enable_group(subgroup, NULL, enable, + work->proto); } } if (online) { @@ -497,12 +765,14 @@ add_optionset(sa_group_t group, struct options *optlist, char *proto, int *err) { sa_optionset_t optionset; int ret = SA_OK; - int result = 0; + int result = B_FALSE; optionset = sa_get_optionset(group, proto); if (optionset == NULL) { optionset = sa_create_optionset(group, proto); - result = 1; /* adding a protocol is a change */ + if (optionset == NULL) + ret = SA_NO_MEMORY; + result = B_TRUE; /* adding a protocol is a change */ } if (optionset == NULL) { ret = SA_NO_MEMORY; @@ -541,7 +811,7 @@ add_optionset(sa_group_t group, struct options *optlist, char *proto, int *err) sa_errorstr(ret)); } else { /* there was a change */ - result = 1; + result = B_TRUE; } } } @@ -553,7 +823,7 @@ add_optionset(sa_group_t group, struct options *optlist, char *proto, int *err) "property %s: %s\n"), optlist->optname, sa_errorstr(ret)); } else { - result = 1; + result = B_TRUE; } } optlist = optlist->next; @@ -566,6 +836,160 @@ add_optionset(sa_group_t group, struct options *optlist, char *proto, int *err) return (result); } +/* + * resource_compliant(group) + * + * Go through all the shares in the group. Assume compliant, but if + * any share doesn't have at least one resource name, it isn't + * compliant. + */ +static int +resource_compliant(sa_group_t group) +{ + sa_share_t share; + + for (share = sa_get_share(group, NULL); share != NULL; + share = sa_get_next_share(share)) { + if (sa_get_share_resource(share, NULL) == NULL) { + return (B_FALSE); + } + } + return (B_TRUE); +} + +/* + * fix_path(path) + * + * change all illegal characters to something else. For now, all get + * converted to '_' and the leading '/' is stripped off. This is used + * to construct an resource name (SMB share name) that is valid. + * Caller must pass a valid path. + */ +static void +fix_path(char *path) +{ + char *cp; + size_t len; + + assert(path != NULL); + + /* make sure we are appropriate length */ + cp = path + 1; /* skip leading slash */ + while (cp != NULL && strlen(cp) > SA_MAX_RESOURCE_NAME) { + cp = strchr(cp, '/'); + if (cp != NULL) + cp++; + } + /* two cases - cp == NULL and cp is substring of path */ + if (cp == NULL) { + /* just take last SA_MAX_RESOURCE_NAME chars */ + len = 1 + strlen(path) - SA_MAX_RESOURCE_NAME; + (void) memmove(path, path + len, SA_MAX_RESOURCE_NAME); + path[SA_MAX_RESOURCE_NAME] = '\0'; + } else { + len = strlen(cp) + 1; + (void) memmove(path, cp, len); + } + + /* + * Don't want any of the characters that are not allowed + * in and SMB share name. Replace them with '_'. + */ + while (*path) { + switch (*path) { + case '/': + case '"': + case '\\': + case '[': + case ']': + case ':': + case '|': + case '<': + case '>': + case '+': + case ';': + case ',': + case '?': + case '*': + case '=': + case '\t': + *path = '_'; + break; + } + path++; + } +} + +/* + * name_adjust(path, count) + * + * Add a ~ in place of last few characters. The total number of + * characters is dependent on count. + */ +#define MAX_MANGLE_NUMBER 10000 + +static int +name_adjust(char *path, int count) +{ + size_t len; + + len = strlen(path) - 2; + if (count > 10) + len--; + if (count > 100) + len--; + if (count > 1000) + len--; + if (len > 0) + (void) sprintf(path + len, "~%d", count); + else + return (SA_BAD_VALUE); + + return (SA_OK); +} + +/* + * make_resources(group) + * + * Go through all the shares in the group and make them have resource + * names. + */ +static void +make_resources(sa_group_t group) +{ + sa_share_t share; + int count; + int err = SA_OK; + + for (share = sa_get_share(group, NULL); share != NULL; + share = sa_get_next_share(share)) { + /* Skip those with resources */ + if (sa_get_share_resource(share, NULL) == NULL) { + char *path; + path = sa_get_share_attr(share, "path"); + if (path == NULL) + continue; + fix_path(path); + count = 0; /* reset for next resource */ + while (sa_add_resource(share, path, + SA_SHARE_PERMANENT, &err) == NULL && + err == SA_DUPLICATE_NAME) { + int ret; + ret = name_adjust(path, count); + count++; + if (ret != SA_OK || + count >= MAX_MANGLE_NUMBER) { + (void) printf(gettext( + "Cannot create resource name for" + " path: %s\n"), path); + break; + } + } + sa_free_attr_string(path); + } + } +} + /* * sa_create(flags, argc, argv) * create a new group @@ -578,6 +1002,7 @@ sa_create(sa_handle_t handle, int flags, int argc, char *argv[]) char *groupname; sa_group_t group; + int force = 0; int verbose = 0; int dryrun = 0; int c; @@ -587,8 +1012,11 @@ sa_create(sa_handle_t handle, int flags, int argc, char *argv[]) int err = 0; int auth; - while ((c = getopt(argc, argv, "?hvnP:p:")) != EOF) { + while ((c = getopt(argc, argv, "?fhvnP:p:")) != EOF) { switch (c) { + case 'f': + force++; + break; case 'v': verbose++; break; @@ -596,6 +1024,12 @@ sa_create(sa_handle_t handle, int flags, int argc, char *argv[]) dryrun++; break; case 'P': + if (protocol != NULL) { + (void) printf(gettext("Specifying " + "multiple protocols " + "not supported: %s\n"), protocol); + return (SA_SYNTAX_ERR); + } protocol = optarg; if (sa_valid_protocol(protocol)) break; @@ -714,6 +1148,31 @@ sa_create(sa_handle_t handle, int flags, int argc, char *argv[]) } if (group != NULL) { sa_optionset_t optionset; + /* + * First check to see if the new protocol is one that + * requires resource names and make sure we are + * compliant before proceeding. + */ + if (protocol != NULL) { + uint64_t features; + + features = sa_proto_get_featureset(protocol); + if ((features & SA_FEATURE_RESOURCE) && + !resource_compliant(group)) { + if (force) { + make_resources(group); + } else { + ret = SA_RESOURCE_REQUIRED; + (void) printf( + gettext("Protocol " + "requires resource " + "names to be " + "set: %s\n"), + protocol); + goto err; + } + } + } if (optlist != NULL) { (void) add_optionset(group, optlist, protocol, &ret); @@ -760,6 +1219,7 @@ sa_create(sa_handle_t handle, int flags, int argc, char *argv[]) sa_errorstr(SA_NO_PERMISSION)); ret = SA_NO_PERMISSION; } +err: free_opt(optlist); return (ret); } @@ -816,14 +1276,26 @@ sa_delete(sa_handle_t handle, int flags, int argc, char *argv[]) dryrun++; break; case 'P': + if (protocol != NULL) { + (void) printf(gettext("Specifying " + "multiple protocols " + "not supported: %s\n"), protocol); + return (SA_SYNTAX_ERR); + } protocol = optarg; if (!sa_valid_protocol(protocol)) { (void) printf(gettext("Invalid protocol " - "specified: %s\n"), protocol); + "specified: %s\n"), protocol); return (SA_INVALID_PROTOCOL); } break; case 'S': + if (sectype != NULL) { + (void) printf(gettext("Specifying " + "multiple property " + "spaces not supported: %s\n"), sectype); + return (SA_SYNTAX_ERR); + } sectype = optarg; break; case 'f': @@ -909,7 +1381,7 @@ sa_delete(sa_handle_t handle, int flags, int argc, char *argv[]) /* a protocol delete */ sa_optionset_t optionset; sa_security_t security; - if (sectype != NULL) { + if (sectype != NULL) { /* only delete specified security */ security = sa_get_security(group, sectype, protocol); if (security != NULL && !dryrun) @@ -949,6 +1421,16 @@ sa_delete(sa_handle_t handle, int flags, int argc, char *argv[]) ret = SA_INVALID_PROTOCOL; } } + /* + * With the protocol items removed, make sure that all + * the shares are updated in the legacy files, if + * necessary. + */ + for (share = sa_get_share(group, NULL); + share != NULL; + share = sa_get_next_share(share)) { + (void) sa_delete_legacy(share, protocol); + } } done: @@ -1045,7 +1527,6 @@ group_proto(sa_group_t group) * their state and protocols. */ -/*ARGSUSED*/ static int sa_list(sa_handle_t handle, int flags, int argc, char *argv[]) { @@ -1053,6 +1534,9 @@ sa_list(sa_handle_t handle, int flags, int argc, char *argv[]) int verbose = 0; int c; char *protocol = NULL; +#ifdef lint + flags = flags; +#endif while ((c = getopt(argc, argv, "?hvP:")) != EOF) { switch (c) { @@ -1060,6 +1544,13 @@ sa_list(sa_handle_t handle, int flags, int argc, char *argv[]) verbose++; break; case 'P': + if (protocol != NULL) { + (void) printf(gettext( + "Specifying multiple protocols " + "not supported: %s\n"), + protocol); + return (SA_SYNTAX_ERR); + } protocol = optarg; if (!sa_valid_protocol(protocol)) { (void) printf(gettext( @@ -1226,6 +1717,229 @@ show_properties(sa_group_t group, char *protocol, char *prefix) } } +/* + * get_resource(share) + * + * Get the first resource name, if any, and fix string to be in + * current locale and have quotes if it has embedded spaces. Return + * an attr string that must be freed. + */ + +static char * +get_resource(sa_share_t share) +{ + sa_resource_t resource; + char *resstring = NULL; + char *retstring; + + if ((resource = sa_get_share_resource(share, NULL)) != NULL) { + resstring = sa_get_resource_attr(resource, "name"); + if (resstring != NULL) { + char *cp; + int len; + + retstring = conv_from_utf8(resstring); + if (retstring != resstring) { + sa_free_attr_string(resstring); + resstring = retstring; + } + if (strpbrk(resstring, " ") != NULL) { + /* account for quotes */ + len = strlen(resstring) + 3; + cp = calloc(len, sizeof (char)); + if (cp != NULL) { + (void) snprintf(cp, len, + "\"%s\"", resstring); + sa_free_attr_string(resstring); + resstring = cp; + } else { + sa_free_attr_string(resstring); + resstring = NULL; + } + } + } + } + return (resstring); +} + +/* + * has_resource_with_opt(share) + * + * Check to see if the share has any resource names with optionsets + * set. Also indicate if multiple resource names since the syntax + * would be about the same. + */ +static int +has_resource_with_opt(sa_share_t share) +{ + sa_resource_t resource; + int ret = B_FALSE; + + for (resource = sa_get_share_resource(share, NULL); + resource != NULL; + resource = sa_get_next_resource(resource)) { + + if (sa_get_optionset(resource, NULL) != NULL) { + ret = B_TRUE; + break; + } + } + return (ret); +} + +/* + * has_multiple_resource(share) + * + * Check to see if the share has any resource names with optionsets + * set. Also indicate if multiple resource names since the syntax + * would be about the same. + */ +static int +has_multiple_resource(sa_share_t share) +{ + sa_resource_t resource; + int num; + + for (num = 0, resource = sa_get_share_resource(share, NULL); + resource != NULL; + resource = sa_get_next_resource(resource)) { + num++; + if (num > 1) + return (B_TRUE); + } + return (B_FALSE); +} + +/* + * show_share(share, verbose, properties, proto, iszfs, sharepath) + * + * print out the share information. With the addition of resource as a + * full object that can have multiple instances below the share, we + * need to display that as well. + */ + +static void +show_share(sa_share_t share, int verbose, int properties, char *proto, + int iszfs, char *sharepath) +{ + char *drive; + char *exclude; + sa_resource_t resource = NULL; + char *description; + char *desc; + char *rsrcname; + int rsrcwithopt; + int multiple; + char *type; + + rsrcwithopt = has_resource_with_opt(share); + + if (verbose || (properties && rsrcwithopt)) { + /* First, indicate if transient */ + type = sa_get_share_attr(share, "type"); + if (type != NULL && !iszfs && verbose && + strcmp(type, "transient") == 0) + (void) printf("\t* "); + else + (void) printf("\t "); + + if (type != NULL) + sa_free_attr_string(type); + + /* + * If we came in with verbose, we want to handle the case of + * multiple resources as though they had properties set. + */ + multiple = has_multiple_resource(share); + + /* Next, if not multiple follow old model */ + if (!multiple && !rsrcwithopt) { + rsrcname = get_resource(share); + if (rsrcname != NULL && strlen(rsrcname) > 0) { + (void) printf("%s=%s", rsrcname, sharepath); + } else { + (void) printf("%s", sharepath); + } + if (rsrcname != NULL) + sa_free_attr_string(rsrcname); + } else { + /* Treat as simple and then resources come later */ + (void) printf("%s", sharepath); + } + drive = sa_get_share_attr(share, "drive-letter"); + if (drive != NULL) { + if (strlen(drive) > 0) + (void) printf(gettext("\tdrive-letter=\"%s:\""), + drive); + sa_free_attr_string(drive); + } + if (properties) + show_properties(share, proto, "\t"); + exclude = sa_get_share_attr(share, "exclude"); + if (exclude != NULL) { + (void) printf(gettext("\tnot-shared-with=[%s]"), + exclude); + sa_free_attr_string(exclude); + } + description = sa_get_share_description(share); + if (description != NULL) { + if (strlen(description) > 0) { + desc = conv_from_utf8(description); + if (desc != description) { + sa_free_share_description(description); + description = desc; + } + (void) printf("\t\"%s\"", description); + } + sa_free_share_description(description); + } + + /* + * If there are resource names with options, show them + * here, with one line per resource. Resource specific + * options are at the end of the line followed by + * description, if any. + */ + if (rsrcwithopt || multiple) { + for (resource = sa_get_share_resource(share, NULL); + resource != NULL; + resource = sa_get_next_resource(resource)) { + int has_space; + char *rsrc; + + (void) printf("\n\t\t "); + rsrcname = sa_get_resource_attr(resource, + "name"); + if (rsrcname == NULL) + continue; + + rsrc = conv_from_utf8(rsrcname); + has_space = strpbrk(rsrc, " ") != NULL; + + if (has_space) + (void) printf("\"%s\"=%s", rsrc, + sharepath); + else + (void) printf("%s=%s", rsrc, + sharepath); + if (rsrc != rsrcname) + sa_free_attr_string(rsrc); + sa_free_attr_string(rsrcname); + if (properties || rsrcwithopt) + show_properties(resource, proto, "\t"); + + /* Get description string if any */ + print_rsrc_desc(resource); + } + } + } else { + (void) printf("\t %s", sharepath); + if (properties) + show_properties(share, proto, "\t"); + } + (void) printf("\n"); +} + /* * show_group(group, verbose, properties, proto, subgroup) * @@ -1234,16 +1948,13 @@ show_properties(sa_group_t group, char *protocol, char *prefix) static void show_group(sa_group_t group, int verbose, int properties, char *proto, - char *subgroup) + char *subgroup) { sa_share_t share; char *groupname; - char *sharepath; - char *resource; - char *description; - char *type; char *zfs = NULL; int iszfs = 0; + char *sharepath; groupname = sa_get_group_attr(group, "name"); if (groupname != NULL) { @@ -1292,48 +2003,8 @@ show_group(sa_group_t group, int verbose, int properties, char *proto, share = sa_get_next_share(share)) { sharepath = sa_get_share_attr(share, "path"); if (sharepath != NULL) { - if (verbose) { - resource = sa_get_share_attr(share, - "resource"); - description = - sa_get_share_description(share); - type = sa_get_share_attr(share, - "type"); - if (type != NULL && !iszfs && - strcmp(type, "transient") == 0) - (void) printf("\t* "); - else - (void) printf("\t "); - if (resource != NULL && - strlen(resource) > 0) { - (void) printf("%s=%s", - resource, sharepath); - } else { - (void) printf("%s", sharepath); - } - if (resource != NULL) - sa_free_attr_string(resource); - if (properties) - show_properties(share, NULL, - "\t"); - if (description != NULL) { - if (strlen(description) > 0) { - (void) printf( - "\t\"%s\"", - description); - } - sa_free_share_description( - description); - } - if (type != NULL) - sa_free_attr_string(type); - } else { - (void) printf("\t%s", sharepath); - if (properties) - show_properties(share, NULL, - "\t"); - } - (void) printf("\n"); + show_share(share, verbose, properties, proto, + iszfs, sharepath); sa_free_attr_string(sharepath); } } @@ -1395,7 +2066,6 @@ show_group_xml(xmlDocPtr doc, sa_group_t group) * Implements the show subcommand. */ -/*ARGSUSED*/ int sa_show(sa_handle_t handle, int flags, int argc, char *argv[]) { @@ -1407,6 +2077,9 @@ sa_show(sa_handle_t handle, int flags, int argc, char *argv[]) char *protocol = NULL; int xml = 0; xmlDocPtr doc; +#ifdef lint + flags = flags; +#endif while ((c = getopt(argc, argv, "?hvP:px")) != EOF) { switch (c) { @@ -1417,6 +2090,13 @@ sa_show(sa_handle_t handle, int flags, int argc, char *argv[]) properties++; break; case 'P': + if (protocol != NULL) { + (void) printf(gettext( + "Specifying multiple protocols " + "not supported: %s\n"), + protocol); + return (SA_SYNTAX_ERR); + } protocol = optarg; if (!sa_valid_protocol(protocol)) { (void) printf(gettext( @@ -1491,14 +2171,16 @@ sa_show(sa_handle_t handle, int flags, int argc, char *argv[]) static int enable_share(sa_handle_t handle, sa_group_t group, sa_share_t share, - int update_legacy) + int update_legacy) { char *value; int enabled; sa_optionset_t optionset; + int err; int ret = SA_OK; char *zfs = NULL; int iszfs = 0; + int isshare; /* * need to enable this share if the group is enabled but not @@ -1511,7 +2193,7 @@ enable_share(sa_handle_t handle, sa_group_t group, sa_share_t share, sa_free_attr_string(value); /* remove legacy config if necessary */ if (update_legacy) - ret = sa_delete_legacy(share); + ret = sa_delete_legacy(share, NULL); zfs = sa_get_group_attr(group, "zfs"); if (zfs != NULL) { iszfs++; @@ -1524,15 +2206,47 @@ enable_share(sa_handle_t handle, sa_group_t group, sa_share_t share, * works because protocols must be set on the group * for the protocol to be enabled. */ + isshare = sa_is_share(share); for (optionset = sa_get_optionset(group, NULL); optionset != NULL && ret == SA_OK; optionset = sa_get_next_optionset(optionset)) { value = sa_get_optionset_attr(optionset, "type"); if (value != NULL) { - if (enabled) - ret = sa_enable_share(share, value); - if (update_legacy && !iszfs) - (void) sa_update_legacy(share, value); + if (enabled) { + if (isshare) { + err = sa_enable_share(share, value); + } else { + err = sa_enable_resource(share, value); + if (err == SA_NOT_SUPPORTED) { + sa_share_t parent; + parent = sa_get_resource_parent( + share); + if (parent != NULL) + err = sa_enable_share( + parent, value); + } + } + if (err != SA_OK) { + ret = err; + (void) printf(gettext( + "Failed to enable share for " + "\"%s\": %s\n"), + value, sa_errorstr(ret)); + } + } + /* + * If we want to update the legacy, use a copy of + * share so we can avoid breaking the loop we are in + * since we might also need to go up the tree to the + * parent. + */ + if (update_legacy && !iszfs) { + sa_share_t update = share; + if (!sa_is_share(share)) { + update = sa_get_resource_parent(share); + } + (void) sa_update_legacy(update, value); + } sa_free_attr_string(value); } } @@ -1541,13 +2255,45 @@ enable_share(sa_handle_t handle, sa_group_t group, sa_share_t share, return (ret); } +/* + * sa_require_resource(group) + * + * if any of the defined protocols on the group require resource + * names, then all shares must have them. + */ + +static int +sa_require_resource(sa_group_t group) +{ + sa_optionset_t optionset; + + for (optionset = sa_get_optionset(group, NULL); + optionset != NULL; + optionset = sa_get_next_optionset(optionset)) { + char *proto; + + proto = sa_get_optionset_attr(optionset, "type"); + if (proto != NULL) { + uint64_t features; + + features = sa_proto_get_featureset(proto); + if (features & SA_FEATURE_RESOURCE) { + sa_free_attr_string(proto); + return (B_TRUE); + } + sa_free_attr_string(proto); + } + } + return (B_FALSE); +} + /* * sa_addshare(flags, argc, argv) * * implements add-share subcommand. */ -int +static int sa_addshare(sa_handle_t handle, int flags, int argc, char *argv[]) { int verbose = 0; @@ -1556,9 +2302,11 @@ sa_addshare(sa_handle_t handle, int flags, int argc, char *argv[]) int ret = SA_OK; sa_group_t group; sa_share_t share; + sa_resource_t resource = NULL; char *sharepath = NULL; char *description = NULL; - char *resource = NULL; + char *rsrcname = NULL; + char *rsrc = NULL; int persist = SA_SHARE_PERMANENT; /* default to persist */ int auth; char dir[MAXPATHLEN]; @@ -1575,7 +2323,13 @@ sa_addshare(sa_handle_t handle, int flags, int argc, char *argv[]) description = optarg; break; case 'r': - resource = optarg; + if (rsrcname != NULL) { + (void) printf(gettext("Adding multiple " + "resource names not" + " supported\n")); + return (SA_SYNTAX_ERR); + } + rsrcname = optarg; break; case 's': /* @@ -1585,7 +2339,7 @@ sa_addshare(sa_handle_t handle, int flags, int argc, char *argv[]) if (sharepath != NULL) { (void) printf(gettext( "Adding multiple shares not supported\n")); - return (1); + return (SA_SYNTAX_ERR); } sharepath = optarg; break; @@ -1605,7 +2359,7 @@ sa_addshare(sa_handle_t handle, int flags, int argc, char *argv[]) (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_ADD_SHARE)); if (dryrun || sharepath != NULL || description != NULL || - resource != NULL || verbose || persist) { + rsrcname != NULL || verbose || persist) { (void) printf(gettext("\tgroup must be specified\n")); ret = SA_NO_SUCH_GROUP; } else { @@ -1617,116 +2371,174 @@ sa_addshare(sa_handle_t handle, int flags, int argc, char *argv[]) sa_get_usage(USAGE_ADD_SHARE)); (void) printf(gettext( "\t-s sharepath must be specified\n")); - return (SA_BAD_PATH); + ret = SA_BAD_PATH; } - if (realpath(sharepath, dir) == NULL) { - (void) printf(gettext( - "Path is not valid: %s\n"), sharepath); - return (SA_BAD_PATH); - } else { - sharepath = dir; + if (ret == SA_OK) { + if (realpath(sharepath, dir) == NULL) { + ret = SA_BAD_PATH; + (void) printf(gettext("Path " + "is not valid: %s\n"), + sharepath); + } else { + sharepath = dir; + } } - - /* Check for valid syntax */ - if (resource != NULL && strpbrk(resource, " \t/") != NULL) { - (void) printf(gettext("usage: %s\n"), - sa_get_usage(USAGE_ADD_SHARE)); - (void) printf(gettext( - "\tresource must not contain white" - "space or '/' characters\n")); - return (SA_BAD_PATH); + if (ret == SA_OK && rsrcname != NULL) { + /* check for valid syntax */ + if (validresource(rsrcname)) { + rsrc = conv_to_utf8(rsrcname); + resource = sa_find_resource(handle, rsrc); + if (resource != NULL) { + /* + * Resource names must be + * unique in the system + */ + ret = SA_DUPLICATE_NAME; + (void) printf(gettext("usage: %s\n"), + sa_get_usage(USAGE_ADD_SHARE)); + (void) printf(gettext( + "\tresource names must be unique " + "in the system\n")); + } + } else { + (void) printf(gettext("usage: %s\n"), + sa_get_usage(USAGE_ADD_SHARE)); + (void) printf(gettext( + "\tresource names use restricted " + "character set\n")); + ret = SA_INVALID_NAME; + } } - group = sa_get_group(handle, argv[optind]); - if (group == NULL) { - (void) printf(gettext("Group \"%s\" not found\n"), - argv[optind]); - return (SA_NO_SUCH_GROUP); + + if (ret != SA_OK) { + if (rsrc != NULL && rsrcname != rsrc) + sa_free_attr_string(rsrc); + return (ret); } - auth = check_authorizations(argv[optind], flags); + share = sa_find_share(handle, sharepath); if (share != NULL) { - group = sa_get_parent_group(share); - if (group != NULL) { - char *groupname; - groupname = sa_get_group_attr( - group, "name"); - if (groupname != NULL) { - (void) printf(gettext( - "Share path already " - "shared in group " - "\"%s\": %s\n"), - groupname, sharepath); - sa_free_attr_string(groupname); - } else { - (void) printf(gettext( - "Share path already" - "shared: %s\n"), - groupname, sharepath); - } - } else { + if (rsrcname == NULL) { + /* + * Can only have a duplicate share if a new + * resource name is being added. + */ + ret = SA_DUPLICATE_NAME; + (void) printf(gettext("Share path already " + "shared: %s\n"), sharepath); + } + } + if (ret != SA_OK) + return (ret); + + group = sa_get_group(handle, argv[optind]); + if (group != NULL) { + if (sa_require_resource(group) == B_TRUE && + rsrcname == NULL) { (void) printf(gettext( - "Share path %s already shared\n"), - sharepath); + "Resource name is required " + "by at least one enabled protocol " + "in group\n")); + return (SA_RESOURCE_REQUIRED); + } + if (share == NULL && ret == SA_OK) { + if (dryrun) + ret = sa_check_path(group, sharepath, + SA_CHECK_NORMAL); + else + share = sa_add_share(group, sharepath, + persist, &ret); } - return (SA_DUPLICATE_NAME); - } else { /* - * Need to check that resource name is - * unique at some point. Path checking - * should use the "normal" rules which - * don't check the repository. + * Make sure this isn't an attempt to put a resourced + * share into a different group than it already is in. */ - if (dryrun) - ret = sa_check_path(group, sharepath, - SA_CHECK_NORMAL); - else - share = sa_add_share(group, sharepath, - persist, &ret); + if (share != NULL) { + sa_group_t parent; + parent = sa_get_parent_group(share); + if (parent != group) { + ret = SA_DUPLICATE_NAME; + (void) printf(gettext( + "Share path already " + "shared: %s\n"), sharepath); + } + } if (!dryrun && share == NULL) { (void) printf(gettext( "Could not add share: %s\n"), sa_errorstr(ret)); } else { + auth = check_authorizations(argv[optind], + flags); if (!dryrun && ret == SA_OK) { - if (resource != NULL && - strpbrk(resource, " \t/") == NULL) { - ret = sa_set_share_attr(share, - "resource", resource); + if (rsrcname != NULL) { + resource = sa_add_resource( + share, + rsrc, + SA_SHARE_PERMANENT, + &ret); } if (ret == SA_OK && description != NULL) { - ret = sa_set_share_description( - share, description); + if (description != NULL) { + ret = + set_share_desc( + share, + description); + } } if (ret == SA_OK) { - /* Now enable the share(s) */ - ret = enable_share(handle, - group, share, 1); + /* now enable the share(s) */ + if (resource != NULL) { + ret = enable_share( + handle, + group, + resource, + 1); + } else { + ret = enable_share( + handle, + group, + share, + 1); + } ret = sa_update_config(handle); } switch (ret) { case SA_DUPLICATE_NAME: (void) printf(gettext( "Resource name in" - "use: %s\n"), resource); + "use: %s\n"), + rsrcname); break; default: - (void) printf( - gettext("Could not set " + (void) printf(gettext( + "Could not set " "attribute: %s\n"), sa_errorstr(ret)); break; case SA_OK: break; } - } else if (dryrun && ret == SA_OK && !auth && - verbose) { + } else if (dryrun && ret == SA_OK && + !auth && verbose) { (void) printf(gettext( "Command would fail: %s\n"), sa_errorstr(SA_NO_PERMISSION)); ret = SA_NO_PERMISSION; } } + } else { + switch (ret) { + default: + (void) printf(gettext( + "Group \"%s\" not found\n"), argv[optind]); + ret = SA_NO_SUCH_GROUP; + break; + case SA_BAD_PATH: + case SA_DUPLICATE_NAME: + break; + } } } return (ret); @@ -1747,10 +2559,11 @@ sa_moveshare(sa_handle_t handle, int flags, int argc, char *argv[]) int ret = SA_OK; sa_group_t group; sa_share_t share; + char *rsrcname = NULL; char *sharepath = NULL; int authsrc = 0, authdst = 0; - while ((c = getopt(argc, argv, "?hvns:")) != EOF) { + while ((c = getopt(argc, argv, "?hvnr:s:")) != EOF) { switch (c) { case 'n': dryrun++; @@ -1758,6 +2571,15 @@ sa_moveshare(sa_handle_t handle, int flags, int argc, char *argv[]) case 'v': verbose++; break; + case 'r': + if (rsrcname != NULL) { + (void) printf(gettext( + "Moving multiple resource names not" + " supported\n")); + return (SA_SYNTAX_ERR); + } + rsrcname = optarg; + break; case 's': /* * Remove share path from group. Currently limit @@ -1765,8 +2587,8 @@ sa_moveshare(sa_handle_t handle, int flags, int argc, char *argv[]) */ if (sharepath != NULL) { (void) printf(gettext("Moving multiple shares" - "not supported\n")); - return (SA_BAD_PATH); + " not supported\n")); + return (SA_SYNTAX_ERR); } sharepath = optarg; break; @@ -1780,21 +2602,20 @@ sa_moveshare(sa_handle_t handle, int flags, int argc, char *argv[]) } if (optind >= argc || sharepath == NULL) { - (void) printf(gettext("usage: %s\n"), - sa_get_usage(USAGE_MOVE_SHARE)); - if (dryrun || verbose || sharepath != NULL) { + (void) printf(gettext("usage: %s\n"), + sa_get_usage(USAGE_MOVE_SHARE)); + if (dryrun || verbose || sharepath != NULL) { + (void) printf(gettext("\tgroup must be specified\n")); + ret = SA_NO_SUCH_GROUP; + } else { + if (sharepath == NULL) { + ret = SA_SYNTAX_ERR; (void) printf(gettext( - "\tgroup must be specified\n")); - ret = SA_NO_SUCH_GROUP; + "\tsharepath must be specified\n")); } else { - if (sharepath == NULL) { - ret = SA_SYNTAX_ERR; - (void) printf(gettext( - "\tsharepath must be specified\n")); - } else { - ret = SA_OK; - } + ret = SA_OK; } + } } else { sa_group_t parent; char *zfsold; @@ -1839,30 +2660,41 @@ sa_moveshare(sa_handle_t handle, int flags, int argc, char *argv[]) if (zfsnew != NULL) sa_free_attr_string(zfsnew); } - if (!dryrun && ret == SA_OK) - ret = sa_move_share(group, share); if (ret == SA_OK && parent != group && !dryrun) { char *oldstate; - ret = sa_update_config(handle); /* * Note that the share may need to be - * "unshared" if the new group is - * disabled and the old was enabled or - * it may need to be share to update - * if the new group is enabled. + * "unshared" if the new group is disabled and + * the old was enabled or it may need to be + * share to update if the new group is + * enabled. We disable before the move and + * will have to enable after the move in order + * to cleanup entries for protocols that + * aren't in the new group. */ oldstate = sa_get_group_attr(parent, "state"); /* enable_share determines what to do */ - if (strcmp(oldstate, "enabled") == 0) { + if (strcmp(oldstate, "enabled") == 0) (void) sa_disable_share(share, NULL); - } - (void) enable_share(handle, group, share, 1); + if (oldstate != NULL) sa_free_attr_string(oldstate); } + if (!dryrun && ret == SA_OK) + ret = sa_move_share(group, share); + + /* + * Reenable and update any config information. + */ + if (ret == SA_OK && parent != group && !dryrun) { + ret = sa_update_config(handle); + + (void) enable_share(handle, group, share, 1); + } + if (ret != SA_OK) (void) printf(gettext("Could not move share: %s\n"), sa_errorstr(ret)); @@ -1891,12 +2723,14 @@ sa_removeshare(sa_handle_t handle, int flags, int argc, char *argv[]) int c; int ret = SA_OK; sa_group_t group; - sa_share_t share; + sa_resource_t resource = NULL; + sa_share_t share = NULL; + char *rsrcname = NULL; char *sharepath = NULL; char dir[MAXPATHLEN]; int auth; - while ((c = getopt(argc, argv, "?hfns:v")) != EOF) { + while ((c = getopt(argc, argv, "?hfnr:s:v")) != EOF) { switch (c) { case 'n': dryrun++; @@ -1920,6 +2754,19 @@ sa_removeshare(sa_handle_t handle, int flags, int argc, char *argv[]) } sharepath = optarg; break; + case 'r': + /* + * Remove share from group if last resource or remove + * resource from share if multiple resources. + */ + if (rsrcname != NULL) { + (void) printf(gettext( + "Removing multiple resource names not " + "supported\n")); + return (SA_SYNTAX_ERR); + } + rsrcname = optarg; + break; default: case 'h': case '?': @@ -1929,12 +2776,12 @@ sa_removeshare(sa_handle_t handle, int flags, int argc, char *argv[]) } } - if (optind >= argc || sharepath == NULL) { - if (sharepath == NULL) { + if (optind >= argc || (rsrcname == NULL && sharepath == NULL)) { + if (sharepath == NULL && rsrcname == NULL) { (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_REMOVE_SHARE)); - (void) printf(gettext( - "\t-s sharepath must be specified\n")); + (void) printf(gettext("\t-s sharepath or -r resource" + " must be specified\n")); ret = SA_BAD_PATH; } else { ret = SA_OK; @@ -1961,6 +2808,16 @@ sa_removeshare(sa_handle_t handle, int flags, int argc, char *argv[]) group = NULL; } + if (rsrcname != NULL) { + resource = sa_find_resource(handle, rsrcname); + if (resource == NULL) { + ret = SA_NO_SUCH_RESOURCE; + (void) printf(gettext( + "Resource name not found for share: %s\n"), + rsrcname); + } + } + /* * Lookup the path in the internal configuration. Care * must be taken to handle the case where the @@ -1968,10 +2825,27 @@ sa_removeshare(sa_handle_t handle, int flags, int argc, char *argv[]) * be able to deal with that as well. */ if (ret == SA_OK) { - if (group != NULL) - share = sa_get_share(group, sharepath); - else - share = sa_find_share(handle, sharepath); + if (sharepath != NULL) { + if (group != NULL) + share = sa_get_share(group, sharepath); + else + share = sa_find_share(handle, sharepath); + } + + if (resource != NULL) { + sa_share_t rsrcshare; + rsrcshare = sa_get_resource_parent(resource); + if (share == NULL) + share = rsrcshare; + else if (share != rsrcshare) { + ret = SA_NO_SUCH_RESOURCE; + (void) printf(gettext( + "Bad resource name for share: %s\n"), + rsrcname); + share = NULL; + } + } + /* * If we didn't find the share with the provided path, * it may be a symlink so attempt to resolve it using @@ -2013,13 +2887,17 @@ sa_removeshare(sa_handle_t handle, int flags, int argc, char *argv[]) else (void) printf(gettext("Share not found: %s\n"), sharepath); - ret = SA_NO_SUCH_PATH; + ret = SA_NO_SUCH_PATH; } else { if (group == NULL) group = sa_get_parent_group(share); if (!dryrun) { if (ret == SA_OK) { - ret = sa_disable_share(share, NULL); + if (resource != NULL) + ret = sa_disable_resource(resource, + NULL); + else + ret = sa_disable_share(share, NULL); /* * We don't care if it fails since it * could be disabled already. Some @@ -2027,19 +2905,37 @@ sa_removeshare(sa_handle_t handle, int flags, int argc, char *argv[]) * prevent removal, so also check for * force being set. */ - if (ret == SA_OK || ret == SA_NO_SUCH_PATH || + if ((ret == SA_OK || ret == SA_NO_SUCH_PATH || ret == SA_NOT_SUPPORTED || - ret == SA_SYSTEM_ERR || force) { + ret == SA_SYSTEM_ERR || force) && + resource == NULL) ret = sa_remove_share(share); + + if ((ret == SA_OK || ret == SA_NO_SUCH_PATH || + ret == SA_NOT_SUPPORTED || + ret == SA_SYSTEM_ERR || force) && + resource != NULL) { + ret = sa_remove_resource(resource); + if (ret == SA_OK) { + /* + * If this was the + * last one, remove + * the share as well. + */ + resource = + sa_get_share_resource( + share, NULL); + if (resource == NULL) + ret = sa_remove_share( + share); + } } if (ret == SA_OK) ret = sa_update_config(handle); } if (ret != SA_OK) - (void) printf(gettext( - "Could not remove share: %s\n"), - sa_errorstr(ret)); - + (void) printf(gettext("Could not remove share:" + " %s\n"), sa_errorstr(ret)); } else if (ret == SA_OK) { char *pname; pname = sa_get_group_attr(group, "name"); @@ -2071,12 +2967,17 @@ sa_set_share(sa_handle_t handle, int flags, int argc, char *argv[]) int ret = SA_OK; sa_group_t group, sharegroup; sa_share_t share; + sa_resource_t resource = NULL; char *sharepath = NULL; char *description = NULL; - char *resource = NULL; + char *desc; + char *rsrcname = NULL; + char *rsrc = NULL; + char *newname = NULL; + char *newrsrc; + char *groupname = NULL; int auth; int verbose = 0; - char *groupname; while ((c = getopt(argc, argv, "?hnd:r:s:")) != EOF) { switch (c) { @@ -2086,12 +2987,21 @@ sa_set_share(sa_handle_t handle, int flags, int argc, char *argv[]) case 'd': description = optarg; break; - case 'r': - resource = optarg; - break; case 'v': verbose++; break; + case 'r': + /* + * Update share by resource name + */ + if (rsrcname != NULL) { + (void) printf(gettext( + "Updating multiple resource names not " + "supported\n")); + return (SA_SYNTAX_ERR); + } + rsrcname = optarg; + break; case 's': /* * Save share path into group. Currently limit @@ -2101,7 +3011,7 @@ sa_set_share(sa_handle_t handle, int flags, int argc, char *argv[]) (void) printf(gettext( "Updating multiple shares not " "supported\n")); - return (SA_BAD_PATH); + return (SA_SYNTAX_ERR); } sharepath = optarg; break; @@ -2114,7 +3024,7 @@ sa_set_share(sa_handle_t handle, int flags, int argc, char *argv[]) } } - if (optind >= argc || sharepath == NULL) { + if (optind >= argc && sharepath == NULL && rsrcname == NULL) { if (sharepath == NULL) { (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_SET_SHARE)); @@ -2127,7 +3037,17 @@ sa_set_share(sa_handle_t handle, int flags, int argc, char *argv[]) if ((optind + 1) < argc) { (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_SET_SHARE)); - (void) printf(gettext("\tExtraneous group(s) at end\n")); + (void) printf(gettext("\tExtraneous group(s) at end\n")); + ret = SA_SYNTAX_ERR; + } + + /* + * Must have at least one of sharepath and rsrcrname. + * It is a syntax error to be missing both. + */ + if (sharepath == NULL && rsrcname == NULL) { + (void) printf(gettext("usage: %s\n"), + sa_get_usage(USAGE_SET_SHARE)); ret = SA_SYNTAX_ERR; } @@ -2141,70 +3061,132 @@ sa_set_share(sa_handle_t handle, int flags, int argc, char *argv[]) group = NULL; groupname = NULL; } - share = sa_find_share(handle, sharepath); - if (share == NULL) { - (void) printf(gettext("Share path \"%s\" not found\n"), - sharepath); - return (SA_NO_SUCH_PATH); - } - sharegroup = sa_get_parent_group(share); - if (group != NULL && group != sharegroup) { - (void) printf(gettext("Group \"%s\" does not contain " - "share %s\n"), argv[optind], sharepath); - ret = SA_BAD_PATH; - } else { - int delgroupname = 0; - if (groupname == NULL) { - groupname = sa_get_group_attr(sharegroup, "name"); - delgroupname = 1; - } - if (groupname != NULL) { - auth = check_authorizations(groupname, flags); - if (delgroupname) { - sa_free_attr_string(groupname); - groupname = NULL; - } + if (rsrcname != NULL) { + /* + * If rsrcname exists, split rename syntax and then + * convert to utf 8 if no errors. + */ + newname = strchr(rsrcname, '='); + if (newname != NULL) { + *newname++ = '\0'; + } + if (!validresource(rsrcname)) { + ret = SA_INVALID_NAME; + (void) printf(gettext("Invalid resource name: " + "\"%s\"\n"), rsrcname); } else { - ret = SA_NO_MEMORY; + rsrc = conv_to_utf8(rsrcname); } - if (resource != NULL) { - if (strpbrk(resource, " \t/") == NULL) { - if (!dryrun) { - ret = sa_set_share_attr(share, - "resource", resource); - } else { - sa_share_t resshare; - resshare = sa_get_resource(sharegroup, - resource); - if (resshare != NULL && - resshare != share) - ret = SA_DUPLICATE_NAME; - } + if (newname != NULL) { + if (!validresource(newname)) { + ret = SA_INVALID_NAME; + (void) printf(gettext("Invalid resource name: " + "%s\n"), newname); } else { - ret = SA_BAD_PATH; - (void) printf(gettext("Resource must not " - "contain white space or '/'\n")); + newrsrc = conv_to_utf8(newname); } } - if (ret == SA_OK && description != NULL) - ret = sa_set_share_description(share, description); } - if (!dryrun && ret == SA_OK) - ret = sa_update_config(handle); - switch (ret) { - case SA_DUPLICATE_NAME: - (void) printf(gettext("Resource name in use: %s\n"), resource); - break; - default: - (void) printf(gettext("Could not set attribute: %s\n"), - sa_errorstr(ret)); - break; - case SA_OK: - if (dryrun && !auth && verbose) - (void) printf(gettext("Command would fail: %s\n"), - sa_errorstr(SA_NO_PERMISSION)); - break; + if (ret != SA_OK) { + if (rsrcname != NULL && rsrcname != rsrc) + sa_free_attr_string(rsrc); + if (newname != NULL && newname != newrsrc) + sa_free_attr_string(newrsrc); + return (ret); + } + + if (sharepath != NULL) { + share = sa_find_share(handle, sharepath); + } else if (rsrcname != NULL) { + resource = sa_find_resource(handle, rsrc); + if (resource != NULL) { + share = sa_get_resource_parent(resource); + } + } + if (share != NULL) { + sharegroup = sa_get_parent_group(share); + if (group != NULL && group != sharegroup) { + (void) printf(gettext("Group \"%s\" does not contain " + "share %s\n"), + argv[optind], sharepath); + ret = SA_BAD_PATH; + } else { + int delgroupname = 0; + if (groupname == NULL) { + groupname = sa_get_group_attr(sharegroup, + "name"); + delgroupname = 1; + } + if (groupname != NULL) { + auth = check_authorizations(groupname, flags); + if (delgroupname) { + sa_free_attr_string(groupname); + groupname = NULL; + } + } else { + ret = SA_NO_MEMORY; + } + if (rsrcname != NULL) { + resource = sa_find_resource(handle, rsrc); + if (!dryrun) { + if (newname != NULL && + resource != NULL) + ret = sa_rename_resource( + resource, newrsrc); + else if (newname != NULL) + ret = SA_NO_SUCH_RESOURCE; + if (newname != NULL && + newname != newrsrc) + sa_free_attr_string(newrsrc); + } + if (rsrc != rsrcname) + sa_free_attr_string(rsrc); + } + + /* + * If the user has set a description, it will be + * on the resource if -r was used otherwise it + * must be on the share. + */ + if (ret == SA_OK && description != NULL) { + desc = conv_to_utf8(description); + if (resource != NULL) + ret = sa_set_resource_description( + resource, desc); + else + ret = sa_set_share_description(share, + desc); + if (desc != description) + sa_free_share_description(desc); + } + } + if (!dryrun && ret == SA_OK) { + if (resource != NULL) + (void) sa_enable_resource(resource, NULL); + ret = sa_update_config(handle); + } + switch (ret) { + case SA_DUPLICATE_NAME: + (void) printf(gettext("Resource name in use: %s\n"), + rsrcname); + break; + default: + (void) printf(gettext("Could not set: %s\n"), + sa_errorstr(ret)); + break; + case SA_OK: + if (dryrun && !auth && verbose) { + (void) printf(gettext( + "Command would fail: %s\n"), + sa_errorstr(SA_NO_PERMISSION)); + } + break; + } + } else { + (void) printf(gettext("Share path \"%s\" not found\n"), + sharepath); + ret = SA_NO_SUCH_PATH; } return (ret); @@ -2219,7 +3201,7 @@ sa_set_share(sa_handle_t handle, int flags, int argc, char *argv[]) static int add_security(sa_group_t group, char *sectype, - struct options *optlist, char *proto, int *err) + struct options *optlist, char *proto, int *err) { sa_security_t security; int ret = SA_OK; @@ -2248,8 +3230,8 @@ add_security(sa_group_t group, char *sectype, prop = sa_create_property(optlist->optname, optlist->optvalue); if (prop != NULL) { - ret = sa_valid_property(security, proto, - prop); + ret = sa_valid_property(security, + proto, prop); if (ret != SA_OK) { (void) sa_remove_property(prop); (void) printf(gettext( @@ -2264,8 +3246,8 @@ add_security(sa_group_t group, char *sectype, if (ret != SA_OK) { (void) printf(gettext( "Could not add " - "property (%s=%s): " - "%s\n"), + "property (%s=%s):" + " %s\n"), optlist->optname, optlist->optvalue, sa_errorstr(ret)); @@ -2334,16 +3316,24 @@ zfscheck(sa_group_t group, sa_share_t share) } /* - * basic_set(groupname, optlist, protocol, sharepath, dryrun) + * basic_set(groupname, optlist, protocol, sharepath, rsrcname, dryrun) * * This function implements "set" when a name space (-S) is not * specified. It is a basic set. Options and other CLI parsing has * already been done. + * + * "rsrcname" is a "resource name". If it is non-NULL, it must match + * the sharepath if present or group if present, otherwise it is used + * to set options. + * + * Resource names may take options if the protocol supports it. If the + * protocol doesn't support resource level options, rsrcname is just + * an alias for the share. */ static int basic_set(sa_handle_t handle, char *groupname, struct options *optlist, - char *protocol, char *sharepath, int dryrun) + char *protocol, char *sharepath, char *rsrcname, int dryrun) { sa_group_t group; int ret = SA_OK; @@ -2353,6 +3343,12 @@ basic_set(sa_handle_t handle, char *groupname, struct options *optlist, group = sa_get_group(handle, groupname); if (group != NULL) { sa_share_t share = NULL; + sa_resource_t resource = NULL; + + /* + * If there is a sharepath, make sure it belongs to + * the group. + */ if (sharepath != NULL) { share = sa_get_share(group, sharepath); if (share == NULL) { @@ -2372,6 +3368,41 @@ basic_set(sa_handle_t handle, char *groupname, struct options *optlist, "not supported: %s\n"), sharepath); } } + + /* + * If a resource name exists, make sure it belongs to + * the share if present else it belongs to the + * group. Also check the protocol to see if it + * supports resource level properties or not. If not, + * use share only. + */ + if (rsrcname != NULL) { + if (share != NULL) { + resource = sa_get_share_resource(share, + rsrcname); + if (resource == NULL) + ret = SA_NO_SUCH_RESOURCE; + } else { + resource = sa_get_resource(group, rsrcname); + if (resource != NULL) + share = sa_get_resource_parent( + resource); + else + ret = SA_NO_SUCH_RESOURCE; + } + if (ret == SA_OK && resource != NULL) { + uint64_t features; + /* + * Check to see if the resource can take + * properties. If so, stick the resource into + * "share" so it will all just work. + */ + features = sa_proto_get_featureset(protocol); + if (features & SA_FEATURE_RESOURCE) + share = (sa_share_t)resource; + } + } + if (ret == SA_OK) { /* group must exist */ ret = valid_options(optlist, protocol, @@ -2385,7 +3416,7 @@ basic_set(sa_handle_t handle, char *groupname, struct options *optlist, protocol, &ret); if (ret == SA_OK && change) worklist = add_list(worklist, group, - share); + share, protocol); } } free_opt(optlist); @@ -2403,7 +3434,8 @@ basic_set(sa_handle_t handle, char *groupname, struct options *optlist, */ if (!dryrun && ret == SA_OK && change && worklist != NULL) /* properties changed, so update all shares */ - (void) enable_all_groups(handle, worklist, 0, 0, protocol); + (void) enable_all_groups(handle, worklist, 0, 0, protocol, + B_TRUE); if (worklist != NULL) free_list(worklist); @@ -2420,7 +3452,7 @@ basic_set(sa_handle_t handle, char *groupname, struct options *optlist, static int space_set(sa_handle_t handle, char *groupname, struct options *optlist, - char *protocol, char *sharepath, int dryrun, char *sectype) + char *protocol, char *sharepath, int dryrun, char *sectype) { sa_group_t group; int ret = SA_OK; @@ -2476,15 +3508,17 @@ space_set(sa_handle_t handle, char *groupname, struct options *optlist, sa_errorstr(ret)); } if (ret == SA_OK && change) - worklist = add_list(worklist, group, share); + worklist = add_list(worklist, group, share, + protocol); } free_opt(optlist); } else { (void) printf(gettext("Group \"%s\" not found\n"), groupname); ret = SA_NO_SUCH_GROUP; } + /* - * we have a group and potentially legal additions + * We have a group and potentially legal additions. */ /* Commit to configuration if not a dryrun */ @@ -2492,7 +3526,7 @@ space_set(sa_handle_t handle, char *groupname, struct options *optlist, if (change && worklist != NULL) { /* properties changed, so update all shares */ (void) enable_all_groups(handle, worklist, 0, 0, - protocol); + protocol, B_TRUE); } ret = sa_update_config(handle); } @@ -2518,11 +3552,12 @@ sa_set(sa_handle_t handle, int flags, int argc, char *argv[]) char *protocol = NULL; int ret = SA_OK; struct options *optlist = NULL; + char *rsrcname = NULL; char *sharepath = NULL; char *optset = NULL; int auth; - while ((c = getopt(argc, argv, "?hvnP:p:s:S:")) != EOF) { + while ((c = getopt(argc, argv, "?hvnP:p:r:s:S:")) != EOF) { switch (c) { case 'v': verbose++; @@ -2531,6 +3566,12 @@ sa_set(sa_handle_t handle, int flags, int argc, char *argv[]) dryrun++; break; case 'P': + if (protocol != NULL) { + (void) printf(gettext( + "Specifying multiple protocols " + "not supported: %s\n"), protocol); + return (SA_SYNTAX_ERR); + } protocol = optarg; if (!sa_valid_protocol(protocol)) { (void) printf(gettext( @@ -2554,10 +3595,30 @@ sa_set(sa_handle_t handle, int flags, int argc, char *argv[]) break; } break; + case 'r': + if (rsrcname != NULL) { + (void) printf(gettext( + "Setting multiple resource names not" + " supported\n")); + return (SA_SYNTAX_ERR); + } + rsrcname = optarg; + break; case 's': + if (sharepath != NULL) { + (void) printf(gettext( + "Setting multiple shares not supported\n")); + return (SA_SYNTAX_ERR); + } sharepath = optarg; break; case 'S': + if (optset != NULL) { + (void) printf(gettext( + "Specifying multiple property " + "spaces not supported: %s\n"), optset); + return (SA_SYNTAX_ERR); + } optset = optarg; break; default: @@ -2610,7 +3671,7 @@ sa_set(sa_handle_t handle, int flags, int argc, char *argv[]) auth = check_authorizations(groupname, flags); if (optset == NULL) ret = basic_set(handle, groupname, optlist, protocol, - sharepath, dryrun); + sharepath, rsrcname, dryrun); else ret = space_set(handle, groupname, optlist, protocol, sharepath, dryrun, optset); @@ -2631,7 +3692,7 @@ sa_set(sa_handle_t handle, int flags, int argc, char *argv[]) static int remove_options(sa_group_t group, struct options *optlist, - char *proto, int *err) + char *proto, int *err) { struct options *cur; sa_optionset_t optionset; @@ -2698,7 +3759,7 @@ valid_unset(sa_group_t group, struct options *optlist, char *proto) static int valid_unset_security(sa_group_t group, struct options *optlist, char *proto, - char *sectype) + char *sectype) { struct options *cur; sa_security_t security; @@ -2736,7 +3797,7 @@ valid_unset_security(sa_group_t group, struct options *optlist, char *proto, static int remove_security(sa_group_t group, char *sectype, - struct options *optlist, char *proto, int *err) + struct options *optlist, char *proto, int *err) { sa_security_t security; int ret = SA_OK; @@ -2775,25 +3836,30 @@ remove_security(sa_group_t group, char *sectype, } /* - * basic_unset(groupname, optlist, protocol, sharepath, dryrun) + * basic_unset(groupname, optlist, protocol, sharepath, rsrcname, dryrun) * * Unset non-named optionset properties. */ static int basic_unset(sa_handle_t handle, char *groupname, struct options *optlist, - char *protocol, char *sharepath, int dryrun) + char *protocol, char *sharepath, char *rsrcname, int dryrun) { sa_group_t group; int ret = SA_OK; int change = 0; struct list *worklist = NULL; sa_share_t share = NULL; + sa_resource_t resource = NULL; group = sa_get_group(handle, groupname); if (group == NULL) return (ret); + /* + * If there is a sharepath, make sure it belongs to + * the group. + */ if (sharepath != NULL) { share = sa_get_share(group, sharepath); if (share == NULL) { @@ -2803,6 +3869,39 @@ basic_unset(sa_handle_t handle, char *groupname, struct options *optlist, ret = SA_NO_SUCH_PATH; } } + /* + * If a resource name exists, make sure it belongs to + * the share if present else it belongs to the + * group. Also check the protocol to see if it + * supports resource level properties or not. If not, + * use share only. + */ + if (rsrcname != NULL) { + if (share != NULL) { + resource = sa_get_share_resource(share, rsrcname); + if (resource == NULL) + ret = SA_NO_SUCH_RESOURCE; + } else { + resource = sa_get_resource(group, rsrcname); + if (resource != NULL) { + share = sa_get_resource_parent(resource); + } else { + ret = SA_NO_SUCH_RESOURCE; + } + } + if (ret == SA_OK && resource != NULL) { + uint64_t features; + /* + * Check to see if the resource can take + * properties. If so, stick the resource into + * "share" so it will all just work. + */ + features = sa_proto_get_featureset(protocol); + if (features & SA_FEATURE_RESOURCE) + share = (sa_share_t)resource; + } + } + if (ret == SA_OK) { /* group must exist */ ret = valid_unset(share != NULL ? share : group, @@ -2830,16 +3929,15 @@ basic_unset(sa_handle_t handle, char *groupname, struct options *optlist, optlist, protocol, &ret); } if (ret == SA_OK && change) - worklist = add_list(worklist, group, - share); + worklist = add_list(worklist, group, share, + protocol); if (ret != SA_OK) (void) printf(gettext( "Could not remove properties: " "%s\n"), sa_errorstr(ret)); } } else { - (void) printf(gettext("Group \"%s\" not found\n"), - groupname); + (void) printf(gettext("Group \"%s\" not found\n"), groupname); ret = SA_NO_SUCH_GROUP; } free_opt(optlist); @@ -2853,7 +3951,7 @@ basic_unset(sa_handle_t handle, char *groupname, struct options *optlist, if (change && worklist != NULL) { /* properties changed, so update all shares */ (void) enable_all_groups(handle, worklist, 0, 0, - protocol); + protocol, B_TRUE); } } if (worklist != NULL) @@ -2868,7 +3966,7 @@ basic_unset(sa_handle_t handle, char *groupname, struct options *optlist, */ static int space_unset(sa_handle_t handle, char *groupname, struct options *optlist, - char *protocol, char *sharepath, int dryrun, char *sectype) + char *protocol, char *sharepath, int dryrun, char *sectype) { sa_group_t group; int ret = SA_OK; @@ -2890,8 +3988,8 @@ space_unset(sa_handle_t handle, char *groupname, struct options *optlist, return (SA_NO_SUCH_PATH); } } - ret = valid_unset_security(share != NULL ? share : group, optlist, - protocol, sectype); + ret = valid_unset_security(share != NULL ? share : group, + optlist, protocol, sectype); if (ret == SA_OK && !dryrun) { if (optlist != NULL) { @@ -2936,7 +4034,7 @@ space_unset(sa_handle_t handle, char *groupname, struct options *optlist, } if (ret == SA_OK && change) - worklist = add_list(worklist, group, 0); + worklist = add_list(worklist, group, 0, protocol); free_opt(optlist); /* @@ -2948,7 +4046,7 @@ space_unset(sa_handle_t handle, char *groupname, struct options *optlist, /* properties changed, so update all shares */ if (change && worklist != NULL) (void) enable_all_groups(handle, worklist, 0, 0, - protocol); + protocol, B_TRUE); ret = sa_update_config(handle); } if (worklist != NULL) @@ -2973,11 +4071,12 @@ sa_unset(sa_handle_t handle, int flags, int argc, char *argv[]) char *protocol = NULL; int ret = SA_OK; struct options *optlist = NULL; + char *rsrcname = NULL; char *sharepath = NULL; char *optset = NULL; int auth; - while ((c = getopt(argc, argv, "?hvnP:p:s:S:")) != EOF) { + while ((c = getopt(argc, argv, "?hvnP:p:r:s:S:")) != EOF) { switch (c) { case 'v': verbose++; @@ -2986,6 +4085,12 @@ sa_unset(sa_handle_t handle, int flags, int argc, char *argv[]) dryrun++; break; case 'P': + if (protocol != NULL) { + (void) printf(gettext( + "Specifying multiple protocols " + "not supported: %s\n"), protocol); + return (SA_SYNTAX_ERR); + } protocol = optarg; if (!sa_valid_protocol(protocol)) { (void) printf(gettext( @@ -3011,10 +4116,35 @@ sa_unset(sa_handle_t handle, int flags, int argc, char *argv[]) break; } break; + case 'r': + /* + * Unset properties on resource if applicable or on + * share if resource for this protocol doesn't use + * resources. + */ + if (rsrcname != NULL) { + (void) printf(gettext( + "Unsetting multiple resource " + "names not supported\n")); + return (SA_SYNTAX_ERR); + } + rsrcname = optarg; + break; case 's': + if (sharepath != NULL) { + (void) printf(gettext( + "Adding multiple shares not supported\n")); + return (SA_SYNTAX_ERR); + } sharepath = optarg; break; case 'S': + if (optset != NULL) { + (void) printf(gettext( + "Specifying multiple property " + "spaces not supported: %s\n"), optset); + return (SA_SYNTAX_ERR); + } optset = optarg; break; default: @@ -3063,7 +4193,7 @@ sa_unset(sa_handle_t handle, int flags, int argc, char *argv[]) auth = check_authorizations(groupname, flags); if (optset == NULL) ret = basic_unset(handle, groupname, optlist, protocol, - sharepath, dryrun); + sharepath, rsrcname, dryrun); else ret = space_unset(handle, groupname, optlist, protocol, sharepath, dryrun, optset); @@ -3104,6 +4234,12 @@ sa_enable_group(sa_handle_t handle, int flags, int argc, char *argv[]) dryrun++; break; case 'P': + if (protocol != NULL) { + (void) printf(gettext( + "Specifying multiple protocols " + "not supported: %s\n"), protocol); + return (SA_SYNTAX_ERR); + } protocol = optarg; if (!sa_valid_protocol(protocol)) { (void) printf(gettext( @@ -3148,7 +4284,7 @@ sa_enable_group(sa_handle_t handle, int flags, int argc, char *argv[]) ret = SA_BUSY; /* already enabled */ } else { worklist = add_list(worklist, group, - 0); + 0, protocol); if (verbose) (void) printf(gettext( "Enabling group \"%s\"\n"), @@ -3165,11 +4301,11 @@ sa_enable_group(sa_handle_t handle, int flags, int argc, char *argv[]) for (group = sa_get_group(handle, NULL); group != NULL; group = sa_get_next_group(group)) { - worklist = add_list(worklist, group, 0); + worklist = add_list(worklist, group, 0, protocol); } } if (!dryrun && ret == SA_OK) - ret = enable_all_groups(handle, worklist, 1, 0, NULL); + ret = enable_all_groups(handle, worklist, 1, 0, NULL, B_FALSE); if (ret != SA_OK && ret != SA_BUSY) (void) printf(gettext("Could not enable group: %s\n"), @@ -3187,24 +4323,30 @@ sa_enable_group(sa_handle_t handle, int flags, int argc, char *argv[]) } /* - * disable_group(group, setstate) + * disable_group(group, proto) * - * Disable all the shares in the specified group honoring the setstate - * argument. This is a helper for disable_all_groups in order to - * simplify regular and subgroup (zfs) disabling. Group has already - * been checked for non-NULL. + * Disable all the shares in the specified group.. This is a helper + * for disable_all_groups in order to simplify regular and subgroup + * (zfs) disabling. Group has already been checked for non-NULL. */ static int -disable_group(sa_group_t group) +disable_group(sa_group_t group, char *proto) { sa_share_t share; int ret = SA_OK; + /* + * If the protocol isn't enabled, skip it and treat as + * successful. + */ + if (!has_protocol(group, proto)) + return (ret); + for (share = sa_get_share(group, NULL); share != NULL && ret == SA_OK; share = sa_get_next_share(share)) { - ret = sa_disable_share(share, NULL); + ret = sa_disable_share(share, proto); if (ret == SA_NO_SUCH_PATH) { /* * this is OK since the path is gone. we can't @@ -3216,7 +4358,6 @@ disable_group(sa_group_t group) return (ret); } - /* * disable_all_groups(work, setstate) * @@ -3243,10 +4384,11 @@ disable_all_groups(sa_handle_t handle, struct list *work, int setstate) for (subgroup = sa_get_sub_group(group); subgroup != NULL; subgroup = sa_get_next_group(subgroup)) { - ret = disable_group(subgroup); + ret = disable_group(subgroup, + work->proto); } } else { - ret = disable_group(group); + ret = disable_group(group, work->proto); } /* * We don't want to "disable" since it won't come @@ -3276,7 +4418,7 @@ sa_disable_group(sa_handle_t handle, int flags, int argc, char *argv[]) int all = 0; int c; int ret = SA_OK; - char *protocol; + char *protocol = NULL; char *state; struct list *worklist = NULL; sa_group_t group; @@ -3291,6 +4433,12 @@ sa_disable_group(sa_handle_t handle, int flags, int argc, char *argv[]) dryrun++; break; case 'P': + if (protocol != NULL) { + (void) printf(gettext( + "Specifying multiple protocols " + "not supported: %s\n"), protocol); + return (SA_SYNTAX_ERR); + } protocol = optarg; if (!sa_valid_protocol(protocol)) { (void) printf(gettext( @@ -3332,9 +4480,10 @@ sa_disable_group(sa_handle_t handle, int flags, int argc, char *argv[]) "Group \"%s\" is " "already disabled\n"), argv[optind]); - ret = SA_BUSY; /* already disable */ + ret = SA_BUSY; /* already disabled */ } else { - worklist = add_list(worklist, group, 0); + worklist = add_list(worklist, group, 0, + protocol); if (verbose) (void) printf(gettext( "Disabling group " @@ -3351,7 +4500,7 @@ sa_disable_group(sa_handle_t handle, int flags, int argc, char *argv[]) for (group = sa_get_group(handle, NULL); group != NULL; group = sa_get_next_group(group)) - worklist = add_list(worklist, group, 0); + worklist = add_list(worklist, group, 0, protocol); } if (ret == SA_OK && !dryrun) @@ -3377,7 +4526,7 @@ sa_disable_group(sa_handle_t handle, int flags, int argc, char *argv[]) * of the group(s) and only enables shares if the group is already * enabled. */ -/*ARGSUSED*/ + int sa_start_group(sa_handle_t handle, int flags, int argc, char *argv[]) { @@ -3389,6 +4538,9 @@ sa_start_group(sa_handle_t handle, int flags, int argc, char *argv[]) char *state; struct list *worklist = NULL; sa_group_t group; +#ifdef lint + flags = flags; +#endif while ((c = getopt(argc, argv, "?havP:")) != EOF) { switch (c) { @@ -3396,6 +4548,12 @@ sa_start_group(sa_handle_t handle, int flags, int argc, char *argv[]) all = 1; break; case 'P': + if (protocol != NULL) { + (void) printf(gettext( + "Specifying multiple protocols " + "not supported: %s\n"), protocol); + return (SA_SYNTAX_ERR); + } protocol = optarg; if (!sa_valid_protocol(protocol)) { (void) printf(gettext( @@ -3429,7 +4587,8 @@ sa_start_group(sa_handle_t handle, int flags, int argc, char *argv[]) state = sa_get_group_attr(group, "state"); if (state == NULL || strcmp(state, "enabled") == 0) { - worklist = add_list(worklist, group, 0); + worklist = add_list(worklist, group, 0, + protocol); if (verbose) (void) printf(gettext( "Starting group \"%s\"\n"), @@ -3437,7 +4596,7 @@ sa_start_group(sa_handle_t handle, int flags, int argc, char *argv[]) } else { /* * Determine if there are any - * protocols. if there aren't any, + * protocols. If there aren't any, * then there isn't anything to do in * any case so no error. */ @@ -3452,17 +4611,19 @@ sa_start_group(sa_handle_t handle, int flags, int argc, char *argv[]) optind++; } } else { - for (group = sa_get_group(handle, NULL); group != NULL; + for (group = sa_get_group(handle, NULL); + group != NULL; group = sa_get_next_group(group)) { state = sa_get_group_attr(group, "state"); if (state == NULL || strcmp(state, "enabled") == 0) - worklist = add_list(worklist, group, 0); + worklist = add_list(worklist, group, 0, + protocol); if (state != NULL) sa_free_attr_string(state); } } - (void) enable_all_groups(handle, worklist, 0, 1, NULL); + (void) enable_all_groups(handle, worklist, 0, 1, protocol, B_FALSE); if (worklist != NULL) free_list(worklist); @@ -3477,7 +4638,6 @@ sa_start_group(sa_handle_t handle, int flags, int argc, char *argv[]) * of the group(s) and only disables shares if the group is already * enabled. */ -/*ARGSUSED*/ int sa_stop_group(sa_handle_t handle, int flags, int argc, char *argv[]) { @@ -3489,6 +4649,9 @@ sa_stop_group(sa_handle_t handle, int flags, int argc, char *argv[]) char *state; struct list *worklist = NULL; sa_group_t group; +#ifdef lint + flags = flags; +#endif while ((c = getopt(argc, argv, "?havP:")) != EOF) { switch (c) { @@ -3496,6 +4659,12 @@ sa_stop_group(sa_handle_t handle, int flags, int argc, char *argv[]) all = 1; break; case 'P': + if (protocol != NULL) { + (void) printf(gettext( + "Specifying multiple protocols " + "not supported: %s\n"), protocol); + return (SA_SYNTAX_ERR); + } protocol = optarg; if (!sa_valid_protocol(protocol)) { (void) printf(gettext( @@ -3527,7 +4696,8 @@ sa_stop_group(sa_handle_t handle, int flags, int argc, char *argv[]) state = sa_get_group_attr(group, "state"); if (state == NULL || strcmp(state, "enabled") == 0) { - worklist = add_list(worklist, group, 0); + worklist = add_list(worklist, group, 0, + protocol); if (verbose) (void) printf(gettext( "Stopping group \"%s\"\n"), @@ -3541,16 +4711,17 @@ sa_stop_group(sa_handle_t handle, int flags, int argc, char *argv[]) optind++; } } else { - for (group = sa_get_group(handle, NULL); group != NULL; + for (group = sa_get_group(handle, NULL); + group != NULL; group = sa_get_next_group(group)) { state = sa_get_group_attr(group, "state"); if (state == NULL || strcmp(state, "enabled") == 0) - worklist = add_list(worklist, group, 0); + worklist = add_list(worklist, group, 0, + protocol); if (state != NULL) sa_free_attr_string(state); } } - (void) disable_all_groups(handle, worklist, 0); ret = sa_update_config(handle); @@ -3686,6 +4857,27 @@ out_share(FILE *out, sa_group_t group, char *proto) { sa_share_t share; char resfmt[128]; + char *defprop; + + /* + * The original share command defaulted to displaying NFS + * shares or allowed a protocol to be specified. We want to + * skip those shares that are not the specified protocol. + */ + if (proto != NULL && sa_get_optionset(group, proto) == NULL) + return; + + if (proto == NULL) + proto = "nfs"; + + /* + * get the default property string. NFS uses "rw" but + * everything else will use "". + */ + if (proto != NULL && strcmp(proto, "nfs") != 0) + defprop = "\"\""; + else + defprop = "rw"; for (share = sa_get_share(group, NULL); share != NULL; @@ -3698,11 +4890,12 @@ out_share(FILE *out, sa_group_t group, char *proto) char *sharedstate; int shared = 1; char *soptions; + char shareopts[MAXNAMLEN]; sharedstate = sa_get_share_attr(share, "shared"); path = sa_get_share_attr(share, "path"); type = sa_get_share_attr(share, "type"); - resource = sa_get_share_attr(share, "resource"); + resource = get_resource(share); groupname = sa_get_group_attr(group, "name"); if (groupname != NULL && strcmp(groupname, "default") == 0) { @@ -3711,8 +4904,12 @@ out_share(FILE *out, sa_group_t group, char *proto) } description = sa_get_share_description(share); - /* Want the sharetab version if it exists */ - soptions = sa_get_share_attr(share, "shareopts"); + /* + * Want the sharetab version if it exists, defaulting + * to NFS if no protocol specified. + */ + (void) snprintf(shareopts, MAXNAMLEN, "shareopts-%s", proto); + soptions = sa_get_share_attr(share, shareopts); if (sharedstate == NULL) shared = 0; @@ -3729,7 +4926,7 @@ out_share(FILE *out, sa_group_t group, char *proto) (void) fprintf(out, "%-14.14s %s %s \"%s\" \n", resfmt, path, (soptions != NULL && strlen(soptions) > 0) ? - soptions : "rw", + soptions : defprop, (description != NULL) ? description : ""); } @@ -3763,22 +4960,21 @@ output_legacy_file(FILE *out, char *proto, sa_handle_t handle) { sa_group_t group; - for (group = sa_get_group(handle, NULL); group != NULL; + for (group = sa_get_group(handle, NULL); + group != NULL; group = sa_get_next_group(group)) { - char *options; char *zfs; /* - * Get default options preformated, being careful to - * handle legacy shares differently from new style - * shares. Legacy share have options on the share. + * Go through all the groups and ZFS + * sub-groups. out_share() will format the shares in + * the group appropriately. */ zfs = sa_get_group_attr(group, "zfs"); if (zfs != NULL) { sa_group_t zgroup; sa_free_attr_string(zfs); - options = sa_proto_legacy_format(proto, group, 1); for (zgroup = sa_get_sub_group(group); zgroup != NULL; zgroup = sa_get_next_group(zgroup)) { @@ -3787,15 +4983,11 @@ output_legacy_file(FILE *out, char *proto, sa_handle_t handle) out_share(out, zgroup, proto); } } else { - options = sa_proto_legacy_format(proto, group, 1); out_share(out, group, proto); } - if (options != NULL) - free(options); } } -/*ARGSUSED*/ int sa_legacy_share(sa_handle_t handle, int flags, int argc, char *argv[]) { @@ -3815,8 +5007,13 @@ sa_legacy_share(sa_handle_t handle, int flags, int argc, char *argv[]) int curtype = SA_SHARE_TRANSIENT; char cmd[MAXPATHLEN]; sa_group_t group = NULL; + sa_resource_t rsrc = NULL; sa_share_t share; char dir[MAXPATHLEN]; + uint64_t features; +#ifdef lint + flags = flags; +#endif while ((c = getopt(argc, argv, "?hF:d:o:p")) != EOF) { switch (c) { @@ -3859,7 +5056,7 @@ sa_legacy_share(sa_handle_t handle, int flags, int argc, char *argv[]) /* Have the info so construct what is needed */ if (!argsused && optind == argc) { /* display current info in share format */ - (void) output_legacy_file(stdout, "nfs", handle); + (void) output_legacy_file(stdout, protocol, handle); return (ret); } @@ -3891,79 +5088,130 @@ sa_legacy_share(sa_handle_t handle, int flags, int argc, char *argv[]) else share = NULL; + features = sa_proto_get_featureset(protocol); + if (groupname != NULL) { ret = SA_NOT_ALLOWED; } else if (ret == SA_OK) { - char *legacygroup = "default"; + char *legacygroup; /* * The legacy group is always present and zfs groups * come and go. zfs shares may be in sub-groups and * the zfs share will already be in that group so it - * isn't an error. + * isn't an error. If the protocol is "smb", the group + * "smb" is used when "default" would otherwise be + * used. "default" is NFS only and "smb" is SMB only. */ + if (strcmp(protocol, "smb") == 0) + legacygroup = "smb"; + else + legacygroup = "default"; + /* * If the share exists (not NULL), then make sure it * is one we want to handle by getting the parent * group. */ - if (share != NULL) + if (share != NULL) { group = sa_get_parent_group(share); - else + } else { group = sa_get_group(handle, legacygroup); + if (group == NULL && strcmp(legacygroup, "smb") == 0) { + /* + * This group may not exist, so create + * as necessary. It only contains the + * "smb" protocol. + */ + group = sa_create_group(handle, legacygroup, + &ret); + if (group != NULL) + (void) sa_create_optionset(group, + protocol); + } + } - if (group != NULL) { - groupstatus = group_status(group); - if (share == NULL) { - share = sa_add_share(group, sharepath, - persist, &ret); - if (share == NULL && - ret == SA_DUPLICATE_NAME) { - /* - * Could be a ZFS path being started - */ - if (sa_zfs_is_shared(handle, - sharepath)) { - ret = SA_OK; - group = sa_get_group(handle, - "zfs"); - if (group == NULL) { - /* - * This shouldn't - * happen. - */ - ret = SA_CONFIG_ERR; - } else { - share = sa_add_share( - group, sharepath, - persist, &ret); - } - } - } - } else { - char *type; + if (group == NULL) { + ret = SA_SYSTEM_ERR; + goto err; + } + + groupstatus = group_status(group); + if (share == NULL) { + share = sa_add_share(group, sharepath, + persist, &ret); + if (share == NULL && + ret == SA_DUPLICATE_NAME) { /* - * May want to change persist state, but the - * important thing is to change options. We - * need to change them regardless of the - * source. + * Could be a ZFS path being started */ - if (sa_zfs_is_shared(handle, sharepath)) { - zfs = 1; - } - remove_all_options(share, protocol); - type = sa_get_share_attr(share, "type"); - if (type != NULL && - strcmp(type, "transient") != 0) { - curtype = SA_SHARE_PERMANENT; + if (sa_zfs_is_shared(handle, + sharepath)) { + ret = SA_OK; + group = sa_get_group(handle, + "zfs"); + if (group == NULL) { + /* + * This shouldn't + * happen. + */ + ret = SA_CONFIG_ERR; + } else { + share = sa_add_share( + group, sharepath, + persist, &ret); + } } - if (type != NULL) - sa_free_attr_string(type); - if (curtype != persist) { - (void) sa_set_share_attr(share, "type", - persist == SA_SHARE_PERMANENT ? - "persist" : "transient"); + } + } else { + char *type; + /* + * May want to change persist state, but the + * important thing is to change options. We + * need to change them regardless of the + * source. + */ + + if (sa_zfs_is_shared(handle, sharepath)) { + zfs = 1; + } + remove_all_options(share, protocol); + type = sa_get_share_attr(share, "type"); + if (type != NULL && + strcmp(type, "transient") != 0) { + curtype = SA_SHARE_PERMANENT; + } + if (type != NULL) + sa_free_attr_string(type); + if (curtype != persist) { + (void) sa_set_share_attr(share, "type", + persist == SA_SHARE_PERMANENT ? + "persist" : "transient"); + } + } + + /* + * If there is a resource name, we may + * actually care about it if this is share for + * a protocol that uses resource level sharing + * (SMB). We need to find the resource and, if + * it exists, make sure it belongs to the + * current share. If it doesn't exist, attempt + * to create it. + */ + + if (ret == SA_OK && resource != NULL) { + rsrc = sa_find_resource(handle, resource); + if (rsrc != NULL) { + if (share != sa_get_resource_parent(rsrc)) + ret = SA_DUPLICATE_NAME; + } else { + rsrc = sa_add_resource(share, resource, + persist, &ret); } + if (features & SA_FEATURE_RESOURCE) + share = rsrc; } + /* Have a group to hold this share path */ if (ret == SA_OK && options != NULL && strlen(options) > 0) { @@ -3973,20 +5221,21 @@ sa_legacy_share(sa_handle_t handle, int flags, int argc, char *argv[]) } if (!zfs) { /* - * ZFS shares never have resource or - * description and we can't store the values - * so don't try. + * ZFS shares never have a description + * and we can't store the values so + * don't try. */ if (ret == SA_OK && description != NULL) ret = sa_set_share_description(share, description); - if (ret == SA_OK && resource != NULL) - ret = sa_set_share_attr(share, - "resource", resource); } - if (ret == SA_OK) { - if (strcmp(groupstatus, "enabled") == 0) + if (ret == SA_OK && + strcmp(groupstatus, "enabled") == 0) { + if (rsrc != share) ret = sa_enable_share(share, protocol); + else + ret = sa_enable_resource(rsrc, + protocol); if (ret == SA_OK && persist == SA_SHARE_PERMANENT) { (void) sa_update_legacy(share, @@ -3995,15 +5244,12 @@ sa_legacy_share(sa_handle_t handle, int flags, int argc, char *argv[]) if (ret == SA_OK) ret = sa_update_config(handle); } - } else { - ret = SA_SYSTEM_ERR; - } } +err: if (ret != SA_OK) { (void) fprintf(stderr, gettext("Could not share: %s: %s\n"), sharepath, sa_errorstr(ret)); ret = SA_LEGACY_ERR; - } return (ret); } @@ -4013,7 +5259,6 @@ sa_legacy_share(sa_handle_t handle, int flags, int argc, char *argv[]) * * Implements the original unshare command. */ -/*ARGSUSED*/ int sa_legacy_unshare(sa_handle_t handle, int flags, int argc, char *argv[]) { @@ -4025,7 +5270,13 @@ sa_legacy_unshare(sa_handle_t handle, int flags, int argc, char *argv[]) int c; int ret = SA_OK; int true_legacy = 0; + uint64_t features = 0; + sa_resource_t resource = NULL; char cmd[MAXPATHLEN]; +#ifdef lint + flags = flags; + options = options; +#endif while ((c = getopt(argc, argv, "?hF:o:p")) != EOF) { switch (c) { @@ -4086,7 +5337,37 @@ sa_legacy_unshare(sa_handle_t handle, int flags, int argc, char *argv[]) share = sa_find_share(handle, dir); } } - if (share != NULL) { + if (share == NULL) { + /* Could be a resource name so check that next */ + features = sa_proto_get_featureset(protocol); + resource = sa_find_resource(handle, sharepath); + if (resource != NULL) { + share = sa_get_resource_parent(resource); + if (features & SA_FEATURE_RESOURCE) + (void) sa_disable_resource(resource, + protocol); + if (persist == SA_SHARE_PERMANENT) { + ret = sa_remove_resource(resource); + if (ret == SA_OK) + ret = sa_update_config(handle); + } + /* + * If we still have a resource on the + * share, we don't disable the share + * itself. IF there aren't anymore, we + * need to remove the share. The + * removal will be done in the next + * section if appropriate. + */ + resource = sa_get_share_resource(share, NULL); + if (resource != NULL) + share = NULL; + } else if (ret == SA_OK) { + /* Didn't find path and no resource */ + ret = SA_BAD_PATH; + } + } + if (share != NULL && resource == NULL) { ret = sa_disable_share(share, protocol); /* * Errors are ok and removal should still occur. The @@ -4101,7 +5382,13 @@ sa_legacy_unshare(sa_handle_t handle, int flags, int argc, char *argv[]) if (ret == SA_OK) ret = sa_update_config(handle); } - } else { + } else if (ret == SA_OK && share == NULL && resource == NULL) { + /* + * If both share and resource are NULL, then + * share not found. If one or the other was + * found or there was an earlier error, we + * assume it was handled earlier. + */ ret = SA_NOT_SHARED; } } @@ -4122,7 +5409,7 @@ sa_legacy_unshare(sa_handle_t handle, int flags, int argc, char *argv[]) /* * Common commands that implement the sub-commands used by all - * protcols. The entries are found via the lookup command + * protocols. The entries are found via the lookup command */ static sa_command_t commands[] = { @@ -4139,7 +5426,7 @@ static sa_command_t commands[] = { {"show", 0, sa_show, USAGE_SHOW}, {"share", 0, sa_legacy_share, USAGE_SHARE, SVC_SET|SVC_ACTION}, {"start", CMD_NODISPLAY, sa_start_group, USAGE_START, - SVC_SET|SVC_ACTION}, + SVC_SET|SVC_ACTION}, {"stop", CMD_NODISPLAY, sa_stop_group, USAGE_STOP, SVC_SET|SVC_ACTION}, {"unset", 0, sa_unset, USAGE_UNSET, SVC_SET}, {"unshare", 0, sa_legacy_unshare, USAGE_UNSHARE, SVC_SET|SVC_ACTION}, @@ -4176,11 +5463,14 @@ sa_get_usage(sa_usage_t index) "move-share [-nvh] -s sharepath destination-group"); break; case USAGE_REMOVE_SHARE: - ret = gettext("remove-share [-fnvh] -s sharepath group"); + ret = gettext( + "remove-share [-fnvh] {-s sharepath | -r resource} " + "group"); break; case USAGE_SET: ret = gettext("set [-nvh] -P proto [-S optspace] " - "[-p property=value]* [-s sharepath] group"); + "[-p property=value]* [-s sharepath] [-r resource]] " + "group"); break; case USAGE_SET_SECURITY: ret = gettext("set-security [-nvh] -P proto -S security-type " @@ -4208,12 +5498,12 @@ sa_get_usage(sa_usage_t index) "[-p property]* group"); break; case USAGE_UNSET_SECURITY: - ret = gettext("unset-security [-nvh] -P proto -S security-type" - " [-p property]* group"); + ret = gettext("unset-security [-nvh] -P proto " + "-S security-type [-p property]* group"); break; case USAGE_UNSHARE: ret = gettext( - "unshare [-F fstype] [-p] sharepath"); + "unshare [-F fstype] [-p] [-o optionlist] sharepath"); break; } return (ret); @@ -4225,12 +5515,14 @@ sa_get_usage(sa_usage_t index) * Lookup the sub-command. proto isn't currently used, but it may * eventually provide a way to provide protocol specific sub-commands. */ -/*ARGSUSED*/ sa_command_t * sa_lookup(char *cmd, char *proto) { int i; size_t len; +#ifdef lint + proto = proto; +#endif len = strlen(cmd); for (i = 0; commands[i].cmdname != NULL; i++) { @@ -4240,11 +5532,13 @@ sa_lookup(char *cmd, char *proto) return (NULL); } -/*ARGSUSED*/ void sub_command_help(char *proto) { int i; +#ifdef lint + proto = proto; +#endif (void) printf(gettext("\tsub-commands:\n")); for (i = 0; commands[i].cmdname != NULL; i++) { diff --git a/usr/src/cmd/dfs.cmds/sharemgr/group.xml b/usr/src/cmd/dfs.cmds/sharemgr/group.xml index 628338f8960c..3b6a4eb2105f 100644 --- a/usr/src/cmd/dfs.cmds/sharemgr/group.xml +++ b/usr/src/cmd/dfs.cmds/sharemgr/group.xml @@ -56,6 +56,17 @@ type='service'> + + + + SMB Server Node Trace Started"); + printf("\n\n"); +} + +END +{ + printf("<--SMB Server Node Trace Ended"); + printf("\n\n"); +} + +sdt:smbsrv:smb_node_lookup:smb_node_lookup_hit +/((smb_node_t *)arg0)->n_state == SMB_NODE_STATE_AVAILABLE/ +{ + printf("\nSMB Node lookup hit/SMB_NODE_STATE_AVAILABLE"); + printf("\n\tNode: %p", arg0); + printf("\n\tRefCnt: %d", ((smb_node_t *)arg0)->n_refcnt); + printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg0)->vp)->v_path); + stack(); +} + +sdt:smbsrv:smb_node_lookup:smb_node_lookup_hit +/((smb_node_t *)arg0)->n_state == SMB_NODE_STATE_DESTROYING/ +{ + printf("\nSMB Node lookup hit/SMB_NODE_STATE_DESTROYING"); + printf("\n\tNode: %p", arg0); + printf("\n\tRefCnt: %d", ((smb_node_t *)arg0)->n_refcnt); + printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg0)->vp)->v_path); + stack(); +} + +sdt:smbsrv:smb_node_lookup:smb_node_lookup_hit +/(((smb_node_t *)arg0)->n_state != SMB_NODE_STATE_DESTROYING) && + (((smb_node_t *)arg0)->n_state != SMB_NODE_STATE_AVAILABLE)/ +{ + printf("\nSMB Node lookup hit/Unknown State"); + printf("\n\tNode: %p", arg0); + printf("\n\tRefCnt: %d", ((smb_node_t *)arg0)->n_refcnt); + printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg0)->vp)->v_path); + stack(); +} + +sdt:smbsrv:smb_node_lookup:smb_node_lookup_miss +{ + printf("\nSMB Node lookup miss"); + printf("\n\tNode: %p", arg0); + printf("\n\tRefCnt: %d", ((smb_node_t *)arg0)->n_refcnt); + printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg0)->vp)->v_path); + stack(); +} + +sdt:smbsrv:smb_node_ref:smb_node_ref_exit +{ + printf("\nSMB Node reference taken"); + printf("\n\tNode: %p", arg0); + printf("\n\tRefCnt: %d", ((smb_node_t *)arg0)->n_refcnt); + printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg0)->vp)->v_path); + stack(); +} + +sdt:smbsrv:smb_node_release:smb_node_release +/((smb_node_t *)arg0)->n_refcnt == 1/ +{ + printf("\nSMB Node release(will be destroyed)"); + printf("\n\tNode: %p", arg0); + printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg0)->vp)->v_path); + stack(); +} + +sdt:smbsrv:smb_node_release:smb_node_release +/((smb_node_t *)arg0)->n_refcnt > 1/ +{ + printf("\nSMB Node release"); + printf("\n\tNode: %p", arg0); + printf("\n\tRefCnt: %d", ((smb_node_t *)arg0)->n_refcnt); + printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg0)->vp)->v_path); + stack(); +} + +sdt:smbsrv:smb_node_delete_on_close:smb_node_delete_on_close +/(int)arg0 == 0/ +{ + printf("\nSMB Node delete on close successful"); + printf("\n\tNode: %p", arg1); + printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg1)->vp)->v_path); + stack(); +} + +sdt:smbsrv:smb_node_delete_on_close:smb_node_delete_on_close +/(int)arg0 == 0/ +{ + printf("\nSMB Node delete on close failed (%d)", (int)arg0); + printf("\n\tNode: %p", arg1); + printf("\n\tName: %s", (string)((vnode_t *)((smb_node_t *)arg1)->vp)->v_path); + stack(); +} + + diff --git a/usr/src/cmd/smbsrv/dtrace/smbvfs.d b/usr/src/cmd/smbsrv/dtrace/smbvfs.d new file mode 100644 index 000000000000..23fa58910972 --- /dev/null +++ b/usr/src/cmd/smbsrv/dtrace/smbvfs.d @@ -0,0 +1,77 @@ +#!/usr/sbin/dtrace -qs +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +BEGIN +{ + printf("\n-->SMB Server VFS Trace Started"); + printf("\n\n"); +} + +END +{ + printf("\n<--SMB Server VFS Trace Ended"); + printf("\n\n"); +} + +sdt:smbsrv:smb_vfs_hold:smb_vfs_hold_hit +{ + printf("\nSMB VFS lookup hit"); + printf("\n Path: %s", (string)((smb_vfs_t *)arg0)->sv_rootvp->v_path); + printf("\n RefCount: %d", ((smb_vfs_t *)arg0)->sv_refcnt); +} + +sdt:smbsrv:smb_vfs_hold:smb_vfs_hold_miss +{ + printf("\nSMB VFS lookup miss"); + printf("\n Path: %s", (string)((smb_vfs_t *)arg0)->sv_rootvp->v_path); + printf("\n RefCount: %d", ((smb_vfs_t *)arg0)->sv_refcnt); +} + +sdt:smbsrv:smb_vfs_rele:smb_vfs_release +/(smb_vfs_t *)arg0 != 0/ +{ + printf("\nSMB VFS release hit"); + printf("\n Path: %s", (string)((smb_vfs_t *)arg0)->sv_rootvp->v_path); + printf("\n RefCount: %d", ((smb_vfs_t *)arg0)->sv_refcnt - 2); +} + +sdt:smbsrv:smb_vfs_rele:smb_vfs_release +/(smb_vfs_t *)arg0 == 0/ +{ + printf("\nSMB VFS release miss"); + printf("\n Path: %s", (string)((vnode_t *)arg1)->v_path); +} + +sdt:smbsrv:smb_vfs_rele_all:smb_vfs_rele_all_hit +{ + printf("\nSMB VFS free"); + printf("\n Path: %s", (string)((smb_vfs_t *)arg0)->sv_rootvp->v_path); + printf("\n RefCount: %d", ((smb_vfs_t *)arg0)->sv_refcnt); +} + + diff --git a/usr/src/cmd/smbsrv/dtrace/stype.d b/usr/src/cmd/smbsrv/dtrace/stype.d new file mode 100755 index 000000000000..19fef52dbe34 --- /dev/null +++ b/usr/src/cmd/smbsrv/dtrace/stype.d @@ -0,0 +1,151 @@ +#!/usr/sbin/dtrace -s + +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#pragma D option flowindent + +/* + * SmbSessionSetupX, SmbLogoffX and dispatcher + */ +smb_com_session_setup_andx:entry, +smb_com_logoff_andx:entry, +smb_com_session_setup_andx:return +{ +} + +sdt:smbsrv::smb-dispatch-com +{ + printf("command=%d", ((smb_request_t *)arg0)->smb_com); +} + +sdt:smbsrv::smb-sessionsetup-clntinfo +{ + clnt = (netr_client_t *)arg0; + + printf("domain=%s\n\n", stringof(clnt->domain)); + printf("username=%s\n\n", stringof(clnt->username)); +} + +smb_com_logoff_andx:return +{ + exit(0); +} + +/* + * Raise error functions (no return). + */ +smbsr_raise_error:entry +{ + printf("class=%d code=%d", arg1, arg2); +} + +smbsr_raise_cifs_error:entry +{ + printf("status=0x%08x class=%d, code=%d", arg1, arg2, arg3); +} + +smbsr_raise_nt_error:entry +{ + printf("error=0x%08x", arg1); +} + +smbsr_raise_errno:entry +{ + printf("errno=%d", arg1); +} + +/* + * Share/tree connect. + */ +smbsr_setup_share:entry +{ + printf("sharename=%s stype=%d", stringof(arg1), arg2); + self->stype = arg2; +} + +smbsr_setup_share:return +{ + self->stype = 0; +} + +smbsr_connect_tree:entry +{ +} + +smbsr_share_report:entry +{ + printf("%s: %s %s", stringof(arg1), stringof(arg2), stringof(arg3)); +} + +smbsr_connect_tree:return, +smbsr_share_report:return, +smb_pathname_reduce:return +{ + printf("rc=%d", arg1); +} + +smb_get_stype:entry +{ + printf("share=%s service=%s", stringof(arg0), stringof(arg1)); +} + +smb_get_stype:return +{ + printf("%d", arg1); +} + +smb_tree_connect:entry +/self->stype == 0/ +{ + printf("share=%s service=%s volname=%s", + stringof(arg3), + stringof(arg4), + stringof(((fsvol_attr_t *)arg7)->name)); +} + +smb_tree_connect:return +/self->stype == 0/ +{ + printf("FS=%s", stringof(((smb_tree_t *)arg1)->t_typename)); +} + +smb_pathname_reduce:entry +{ + printf("path=%s", stringof(arg2)); +} + +sdt:smbsrv::smb-vfs-volume +{ + printf("mntpnt=%s volname=%s", stringof(arg0), stringof(arg1)); +} + +sdt:smbsrv::smb-vfs-getflags +{ + printf("flags=0x%08x", arg0); +} diff --git a/usr/src/cmd/smbsrv/smbadm/Makefile b/usr/src/cmd/smbsrv/smbadm/Makefile new file mode 100644 index 000000000000..9bf78a75c2e0 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbadm/Makefile @@ -0,0 +1,47 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +PROG= smbadm +SRCS= smbadm.c + +include ../../Makefile.cmd +include ../Makefile.smbsrv.defs + +all: $(PROG) + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +include ../../Makefile.targ + +install: all .WAIT $(ROOTUSRSBINPROG) diff --git a/usr/src/cmd/smbsrv/smbadm/smbadm.c b/usr/src/cmd/smbsrv/smbadm/smbadm.c new file mode 100644 index 000000000000..98f61b2b9b60 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbadm/smbadm.c @@ -0,0 +1,1374 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module contains smbadm CLI which offers smb configuration + * functionalities. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +typedef enum { + HELP_ADD_MEMBER, + HELP_CREATE, + HELP_DELETE, + HELP_DEL_MEMBER, + HELP_GET, + HELP_JOIN, + HELP_LIST, + HELP_RENAME, + HELP_SET, + HELP_SHOW, + HELP_UDISABLE, + HELP_UENABLE +} smbadm_help_t; + +typedef struct smbadm_cmdinfo { + char *name; + int (*func)(int, char **); + smbadm_help_t usage; +} smbadm_cmdinfo_t; + +smbadm_cmdinfo_t *curcmd; +static char *progname; + +static int smbadm_join(int, char **); +static int smbadm_list(int, char **); +static int smbadm_group_create(int, char **); +static int smbadm_group_delete(int, char **); +static int smbadm_group_rename(int, char **); +static int smbadm_group_show(int, char **); +static int smbadm_group_getprop(int, char **); +static int smbadm_group_setprop(int, char **); +static int smbadm_group_addmember(int, char **); +static int smbadm_group_delmember(int, char **); +static int smbadm_user_disable(int, char **); +static int smbadm_user_enable(int, char **); + +static smbadm_cmdinfo_t smbadm_cmdtable[] = +{ + { "add-member", smbadm_group_addmember, HELP_ADD_MEMBER }, + { "create", smbadm_group_create, HELP_CREATE }, + { "delete", smbadm_group_delete, HELP_DELETE }, + { "disable-user", smbadm_user_disable, HELP_UDISABLE }, + { "enable-user", smbadm_user_enable, HELP_UENABLE }, + { "get", smbadm_group_getprop, HELP_GET }, + { "join", smbadm_join, HELP_JOIN }, + { "list", smbadm_list, HELP_LIST }, + { "remove-member", smbadm_group_delmember, HELP_DEL_MEMBER }, + { "rename", smbadm_group_rename, HELP_RENAME }, + { "set", smbadm_group_setprop, HELP_SET }, + { "show", smbadm_group_show, HELP_SHOW }, +}; + +#define SMBADM_NCMD (sizeof (smbadm_cmdtable) / sizeof (smbadm_cmdtable[0])) + +typedef struct smbadm_prop { + char *p_name; + char *p_value; +} smbadm_prop_t; + +typedef struct smbadm_prop_handle { + char *p_name; + char *p_dispvalue; + int (*p_setfn)(char *, smbadm_prop_t *); + int (*p_getfn)(char *, smbadm_prop_t *); + boolean_t (*p_chkfn)(smbadm_prop_t *); +} smbadm_prop_handle_t; + +static boolean_t smbadm_prop_validate(smbadm_prop_t *prop, boolean_t chkval); +static int smbadm_prop_parse(char *arg, smbadm_prop_t *prop); +static smbadm_prop_handle_t *smbadm_prop_gethandle(char *pname); + +static boolean_t smbadm_chkprop_priv(smbadm_prop_t *prop); +static int smbadm_setprop_tkowner(char *gname, smbadm_prop_t *prop); +static int smbadm_getprop_tkowner(char *gname, smbadm_prop_t *prop); +static int smbadm_setprop_backup(char *gname, smbadm_prop_t *prop); +static int smbadm_getprop_backup(char *gname, smbadm_prop_t *prop); +static int smbadm_setprop_restore(char *gname, smbadm_prop_t *prop); +static int smbadm_getprop_restore(char *gname, smbadm_prop_t *prop); +static int smbadm_setprop_desc(char *gname, smbadm_prop_t *prop); +static int smbadm_getprop_desc(char *gname, smbadm_prop_t *prop); + +static smbadm_prop_handle_t smbadm_ptable[] = { + {"backup", "on | off", smbadm_setprop_backup, + smbadm_getprop_backup, smbadm_chkprop_priv }, + {"restore", "on | off", smbadm_setprop_restore, + smbadm_getprop_restore, smbadm_chkprop_priv }, + {"take-ownership", "on | off", smbadm_setprop_tkowner, + smbadm_getprop_tkowner, smbadm_chkprop_priv }, + {"description", "", smbadm_setprop_desc, + smbadm_getprop_desc, NULL }, +}; + +static const char *smbadm_pwd_strerror(int error); + +/* + * Number of supported properties + */ +#define SMBADM_NPROP (sizeof (smbadm_ptable) / sizeof (smbadm_ptable[0])) + +static void +smbadm_cmdusage(FILE *fp, smbadm_cmdinfo_t *cmd) +{ + switch (cmd->usage) { + case HELP_ADD_MEMBER: + (void) fprintf(fp, + gettext("\t%s -m member [[-m member] ...] group\n"), + cmd->name); + return; + + case HELP_CREATE: + (void) fprintf(fp, gettext("\t%s [-d description] group\n"), + cmd->name); + return; + + case HELP_DELETE: + (void) fprintf(fp, gettext("\t%s group\n"), cmd->name); + return; + + case HELP_UDISABLE: + case HELP_UENABLE: + (void) fprintf(fp, gettext("\t%s user\n"), cmd->name); + return; + + case HELP_GET: + (void) fprintf(fp, gettext("\t%s [[-p property] ...] group\n"), + cmd->name); + return; + + case HELP_JOIN: + (void) fprintf(fp, gettext("\t%s -u username domain\n" + "\t%s -w workgroup\n"), cmd->name, cmd->name); + return; + + case HELP_LIST: + (void) fprintf(fp, gettext("\t%s\n"), cmd->name); + return; + + case HELP_DEL_MEMBER: + (void) fprintf(fp, + gettext("\t%s -m member [[-m member] ...] group\n"), + cmd->name); + return; + + case HELP_RENAME: + (void) fprintf(fp, gettext("\t%s group new-group\n"), + cmd->name); + return; + + case HELP_SET: + (void) fprintf(fp, gettext("\t%s -p property=value " + "[[-p property=value] ...] group\n"), cmd->name); + return; + + case HELP_SHOW: + (void) fprintf(fp, gettext("\t%s [-m] [-p] [group]\n"), + cmd->name); + return; + + } + + abort(); + /* NOTREACHED */ +} + +static void +smbadm_usage(boolean_t requested) +{ + FILE *fp = requested ? stdout : stderr; + boolean_t show_props = B_FALSE; + int i; + + if (curcmd == NULL) { + (void) fprintf(fp, + gettext("usage: %s [-h | [options]]\n"), + progname); + (void) fprintf(fp, + gettext("where 'command' is one of the following:\n\n")); + + for (i = 0; i < SMBADM_NCMD; i++) + smbadm_cmdusage(fp, &smbadm_cmdtable[i]); + + (void) fprintf(fp, + gettext("\nFor property list, run %s %s|%s\n"), + progname, "get", "set"); + + exit(requested ? 0 : 2); + } + + (void) fprintf(fp, gettext("usage:\n")); + smbadm_cmdusage(fp, curcmd); + + if (strcmp(curcmd->name, "get") == 0 || + strcmp(curcmd->name, "set") == 0) + show_props = B_TRUE; + + if (show_props) { + (void) fprintf(fp, + gettext("\nThe following properties are supported:\n")); + + (void) fprintf(fp, "\n\t%-16s %s\n\n", + "PROPERTY", "VALUES"); + + for (i = 0; i < SMBADM_NPROP; i++) { + (void) fprintf(fp, "\t%-16s %s\n", + smbadm_ptable[i].p_name, + smbadm_ptable[i].p_dispvalue); + } + } + + exit(requested ? 0 : 2); +} + +/* + * smbadm_join + * + * Join the given domain/workgroup + */ +static int +smbadm_join(int argc, char **argv) +{ + char option; + smb_joininfo_t jdi; + boolean_t join_w = B_FALSE; + boolean_t join_d = B_FALSE; + uint32_t status; + + bzero(&jdi, sizeof (jdi)); + + while ((option = getopt(argc, argv, "u:w:")) != -1) { + switch (option) { + case 'w': + (void) strlcpy(jdi.domain_name, optarg, + sizeof (jdi.domain_name)); + jdi.mode = SMB_SECMODE_WORKGRP; + join_w = B_TRUE; + break; + + case 'u': + /* admin username */ + (void) strlcpy(jdi.domain_username, optarg, + sizeof (jdi.domain_username)); + jdi.mode = SMB_SECMODE_DOMAIN; + join_d = B_TRUE; + break; + + default: + smbadm_usage(B_FALSE); + } + } + + if (join_w && join_d) { + (void) fprintf(stderr, + gettext("domain and workgroup " + "can not be specified together\n")); + smbadm_usage(B_FALSE); + } + + if (join_d && (argv[optind] != NULL)) { + (void) strlcpy(jdi.domain_name, argv[optind], + sizeof (jdi.domain_name)); + } + + if (*jdi.domain_name == '\0') { + (void) fprintf(stderr, gettext("missing %s name\n"), + (join_d) ? "domain" : "workgroup"); + smbadm_usage(B_FALSE); + } + + if (join_d && *jdi.domain_username == '\0') { + (void) fprintf(stderr, gettext("missing username\n")); + smbadm_usage(B_FALSE); + } + + if (join_w) { + status = smb_join(&jdi); + if (status == NT_STATUS_SUCCESS) { + (void) printf( + gettext("Successfully joined workgroup '%s'\n"), + jdi.domain_name); + return (0); + } + + (void) fprintf(stderr, + gettext("failed to join workgroup '%s' (%s)\n"), + jdi.domain_name, xlate_nt_status(status)); + + return (1); + } + + /* Join the domain */ + if (*jdi.domain_passwd == '\0') { + char *p = NULL; + char *prompt = gettext("Enter domain password: "); + p = getpassphrase(prompt); + if (!p) { + (void) fprintf(stderr, gettext("missing password\n")); + smbadm_usage(B_FALSE); + } + + (void) strlcpy(jdi.domain_passwd, p, + sizeof (jdi.domain_passwd)); + } + + (void) printf(gettext("Joining '%s' ... this may take a minute ...\n"), + jdi.domain_name); + + status = smb_join(&jdi); + + switch (status) { + case NT_STATUS_SUCCESS: + (void) printf(gettext("Successfully joined domain '%s'\n"), + jdi.domain_name); + return (0); + + case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND: + (void) fprintf(stderr, gettext("failed to find " + "any domain controllers for '%s'\n"), + jdi.domain_name); + break; + + default: + (void) fprintf(stderr, + gettext("failed to join domain '%s' (%s)\n"), + jdi.domain_name, xlate_nt_status(status)); + } + + return (1); +} + +/* + * smbadm_list + * + * Displays current security mode and domain/workgroup name. + */ +/*ARGSUSED*/ +static int +smbadm_list(int argc, char **argv) +{ + char resource_domain[SMB_PI_MAX_DOMAIN]; + int sec_mode; + char *modename; + + if (smbd_get_security_mode(&sec_mode)) { + (void) fprintf(stderr, + gettext("failed to get the security mode\n")); + return (1); + } + + modename = (sec_mode == SMB_SECMODE_DOMAIN) ? "domain" : "workgroup"; + + (void) printf(gettext("security mode: %s\n"), + smb_config_secmode_tostr(sec_mode)); + + if (smbd_get_param(SMB_CI_DOMAIN_NAME, resource_domain) != 0) { + (void) fprintf(stderr, + gettext("failed to get the %s name\n"), modename); + return (1); + } + + (void) printf(gettext("%s name: %s\n"), + modename, resource_domain); + + return (0); +} + +/* + * smbadm_group_create + * + * Creates a local SMB group + */ +static int +smbadm_group_create(int argc, char **argv) +{ + char *gname = NULL; + char *desc = NULL; + char option; + uint32_t status; + + while ((option = getopt(argc, argv, "d:")) != -1) { + switch (option) { + case 'd': + desc = optarg; + break; + + default: + smbadm_usage(B_FALSE); + } + } + + gname = argv[optind]; + if (optind >= argc || gname == NULL || *gname == '\0') { + (void) fprintf(stderr, gettext("missing group name\n")); + smbadm_usage(B_FALSE); + } + + if (getgrnam(gname) == NULL) { + (void) fprintf(stderr, + gettext("failed to get the Solaris group\n")); + (void) fprintf(stderr, + gettext("use 'groupadd' to add the Solaris group\n")); + return (1); + } + + status = smb_group_add(gname, desc); + if (status != NT_STATUS_SUCCESS) { + (void) fprintf(stderr, + gettext("failed to create the group (%s)\n"), + xlate_nt_status(status)); + } else { + (void) printf(gettext("Successfully created group '%s'\n"), + gname); + } + + return (status); +} + +/* + * smbadm_group_dump_members + * + * Dump group members details. + */ +static void +smbadm_group_dump_members(char *gname) +{ + ntgrp_member_list_t *members = NULL; + int mem_cnt = 0; + int offset = 0; + uint32_t status; + int i; + + status = smb_group_member_count(gname, &mem_cnt); + if (mem_cnt < 0) { + (void) fprintf(stderr, + gettext("failed to get the group members (%s)\n"), + xlate_nt_status(status)); + } + + if (mem_cnt == 0) { + (void) printf(gettext("\tNo members\n")); + return; + } + + (void) printf(gettext("\tMembers:\n")); + while (smb_group_member_list(gname, offset, &members) == 0) { + if (members == NULL) + break; + + for (i = 0; i < members->cnt; i++) + (void) printf(gettext("\t\t%s\n"), members->members[i]); + + offset += members->cnt; + smb_group_free_memberlist(members, 0); + if (offset >= mem_cnt) + break; + } +} + +/* + * smbadm_group_dump_privs + * + * Dump group privilege details. + */ +static void +smbadm_group_dump_privs(char *gname, ntpriv_list_t *privs) +{ + int privcnt = 0; + uint32_t privval; + char *name = NULL; + int i; + + (void) printf(gettext("\tPrivileges: \n")); + + for (i = 0; i < privs->cnt; i++) { + name = privs->privs[i]->name; + if (name == NULL) + continue; + + if (smb_group_priv_get(gname, privs->privs[i]->id, + &privval) != 0) { + continue; + } + + if (privval == SE_PRIVILEGE_ENABLED) { + (void) printf(gettext("\t\t%s: On\n"), name); + } else if (privval == SE_PRIVILEGE_DISABLED) { + (void) printf(gettext("\t\t%s: Off\n"), name); + } else { + (void) printf(gettext("\t\t%s: %d\n"), + name, privval); + } + + name = NULL; + privcnt++; + } + + if (privcnt == 0) + (void) printf(gettext("\t\tNo privileges\n")); +} + +/* + * smbadm_group_dump + * + * Dump group details. + */ +static int +smbadm_group_dump(ntgrp_list_t *list, boolean_t show_mem, boolean_t show_privs) +{ + ntpriv_list_t *privs = NULL; + char *gname; + uint32_t status; + int i; + + if (show_privs) { + if ((status = smb_group_priv_list(&privs)) != 0) { + (void) fprintf(stderr, + gettext("failed to get privileges (%s)\n"), + xlate_nt_status(status)); + return (1); + } + } + + for (i = 0; i < list->cnt; i++) { + gname = list->groups[i].name; + + (void) printf(gettext("%s (%s)\n"), gname, + list->groups[i].desc); + (void) printf(gettext("\tType: %s, Attr: %X\n"), + list->groups[i].type, list->groups[i].attr); + (void) printf(gettext("\tSID: %s\n"), + list->groups[i].sid); + + if (show_privs) + smbadm_group_dump_privs(gname, privs); + + if (show_mem) + smbadm_group_dump_members(list->groups[i].name); + } + + return (0); +} + +/* + * smbadm_group_show + * + */ +static int +smbadm_group_show(int argc, char **argv) +{ + char *gname = NULL; + int cnt = 0; + int offset = 0; + boolean_t show_privs; + boolean_t show_members; + char option; + uint32_t status; + ntgrp_list_t *list = NULL; + int ret = 0; + + show_privs = show_members = B_FALSE; + + while ((option = getopt(argc, argv, "mp")) != -1) { + switch (option) { + case 'm': + show_members = B_TRUE; + break; + case 'p': + show_privs = B_TRUE; + break; + + default: + smbadm_usage(B_FALSE); + } + } + + gname = argv[optind]; + if (optind >= argc || gname == NULL || *gname == '\0') + gname = "*"; + + status = smb_group_count(&cnt); + if ((status != NT_STATUS_SUCCESS) || (cnt < 0)) { + (void) fprintf(stderr, + gettext("failed to get the number of group(s) (%s)\n"), + xlate_nt_status(status)); + return (1); + } + + while ((offset < cnt)) { + status = smb_group_list(offset, &list, gname, 0); + if (status != NT_STATUS_SUCCESS) { + (void) fprintf(stderr, + gettext("failed to get the group(s) (%s)\n"), + xlate_nt_status(status)); + return (1); + } + + if ((list == NULL) || (list->cnt <= 0)) + break; + + ret = smbadm_group_dump(list, show_members, show_privs); + if (ret) + break; + + offset += list->cnt; + smb_group_free_list(list, 0); + list = NULL; + } + + return (ret); +} + +/* + * smbadm_group_delete + * + */ +static int +smbadm_group_delete(int argc, char **argv) +{ + uint32_t status; + char *gname = NULL; + + gname = argv[optind]; + if (optind >= argc || gname == NULL || *gname == '\0') { + (void) fprintf(stderr, gettext("missing group name\n")); + smbadm_usage(B_FALSE); + } + + status = smb_group_delete(gname); + if (status != NT_STATUS_SUCCESS) { + (void) fprintf(stderr, + gettext("failed to delete the group (%s)\n"), + xlate_nt_status(status)); + } else { + (void) printf(gettext("Successfully deleted group '%s'\n"), + gname); + } + + return (status); +} + +/* + * smbadm_group_rename + * + */ +static int +smbadm_group_rename(int argc, char **argv) +{ + char *gname = NULL; + char *ngname = NULL; + uint32_t status; + + gname = argv[optind]; + if (optind++ >= argc || gname == NULL || *gname == '\0') { + (void) fprintf(stderr, gettext("missing group name\n")); + smbadm_usage(B_FALSE); + } + + ngname = argv[optind]; + if (optind >= argc || ngname == NULL || *ngname == '\0') { + (void) fprintf(stderr, gettext("missing new group name\n")); + smbadm_usage(B_FALSE); + } + + if (getgrnam(gname) == NULL) { + (void) fprintf(stderr, + gettext("failed to get the Solaris group\n")); + (void) fprintf(stderr, + gettext("use 'groupadd' to add the Solaris group\n")); + return (1); + } + + status = smb_group_modify(gname, ngname, NULL); + if (status != NT_STATUS_SUCCESS) { + (void) fprintf(stderr, + gettext("failed to modify the group (%s)\n"), + xlate_nt_status(status)); + } else { + (void) printf(gettext("Successfully modified " + "group '%s'\n"), gname); + } + + return (status); +} + +/* + * smbadm_group_setprop + * + * Set the group properties. + */ +static int +smbadm_group_setprop(int argc, char **argv) +{ + char *gname = NULL; + smbadm_prop_t props[SMBADM_NPROP]; + smbadm_prop_handle_t *phandle; + char option; + int pcnt = 0; + int ret; + int p; + + bzero(props, SMBADM_NPROP * sizeof (smbadm_prop_t)); + + while ((option = getopt(argc, argv, "p:")) != -1) { + switch (option) { + case 'p': + if (pcnt >= SMBADM_NPROP) { + (void) fprintf(stderr, + gettext("exceeded number of supported" + " properties\n")); + smbadm_usage(B_FALSE); + } + + ret = smbadm_prop_parse(optarg, &props[pcnt++]); + if (ret) { + if (ret == 1) + exit(1); + + if (ret == 2) + smbadm_usage(B_FALSE); + } + break; + + default: + smbadm_usage(B_FALSE); + } + } + + if (pcnt == 0) { + (void) fprintf(stderr, + gettext("missing property=value argument\n")); + smbadm_usage(B_FALSE); + } + + gname = argv[optind]; + if (optind >= argc || gname == NULL || *gname == '\0') { + (void) fprintf(stderr, gettext("missing group name\n")); + smbadm_usage(B_FALSE); + } + + for (p = 0; p < pcnt; p++) { + phandle = smbadm_prop_gethandle(props[p].p_name); + if (phandle) { + if (phandle->p_setfn(gname, &props[p]) != 0) + ret = 1; + } + } + + return (ret); +} + +/* + * smbadm_group_getprop + * + * Get the group properties. + */ +static int +smbadm_group_getprop(int argc, char **argv) +{ + char *gname = NULL; + smbadm_prop_t props[SMBADM_NPROP]; + smbadm_prop_handle_t *phandle; + char option; + int pcnt = 0; + int ret; + int p; + + bzero(props, SMBADM_NPROP * sizeof (smbadm_prop_t)); + + while ((option = getopt(argc, argv, "p:")) != -1) { + switch (option) { + case 'p': + if (pcnt >= SMBADM_NPROP) { + (void) fprintf(stderr, + gettext("exceeded number of supported" + " properties\n")); + smbadm_usage(B_FALSE); + } + + ret = smbadm_prop_parse(optarg, &props[pcnt++]); + if (ret) { + if (ret == 1) + exit(1); + + if (ret == 2) + smbadm_usage(B_FALSE); + } + break; + + default: + smbadm_usage(B_FALSE); + } + } + + gname = argv[optind]; + if (optind >= argc || gname == NULL || *gname == '\0') { + (void) fprintf(stderr, gettext("missing group name\n")); + smbadm_usage(B_FALSE); + } + + if (pcnt == 0) { + /* + * If no property has be specified then get + * all the properties. + */ + pcnt = SMBADM_NPROP; + for (p = 0; p < pcnt; p++) + props[p].p_name = smbadm_ptable[p].p_name; + } + + for (p = 0; p < pcnt; p++) { + phandle = smbadm_prop_gethandle(props[p].p_name); + if (phandle) { + if (phandle->p_getfn(gname, &props[p]) != 0) + ret = 1; + } + } + + return (ret); +} + +/* + * smbadm_group_addmember + * + */ +static int +smbadm_group_addmember(int argc, char **argv) +{ + char *gname = NULL; + char **mname; + char option; + uint32_t status; + int mcnt = 0; + int ret = 0; + int i; + + + mname = (char **)malloc(argc * sizeof (char *)); + if (mname == NULL) { + (void) fprintf(stderr, gettext("out of memory\n")); + return (1); + } + bzero(mname, argc * sizeof (char *)); + + while ((option = getopt(argc, argv, "m:")) != -1) { + switch (option) { + case 'm': + mname[mcnt++] = optarg; + break; + + default: + free(mname); + smbadm_usage(B_FALSE); + } + } + + if (mcnt == 0) { + (void) fprintf(stderr, gettext("missing member name\n")); + free(mname); + smbadm_usage(B_FALSE); + } + + gname = argv[optind]; + if (optind >= argc || gname == NULL || *gname == 0) { + (void) fprintf(stderr, gettext("missing group name\n")); + free(mname); + smbadm_usage(B_FALSE); + } + + + for (i = 0; i < mcnt; i++) { + if (mname[i] == NULL) + continue; + + status = smb_group_member_add(gname, mname[i]); + if (status != NT_STATUS_SUCCESS) { + (void) fprintf(stderr, + gettext("failed to add %s (%s)\n"), + mname[i], xlate_nt_status(status)); + ret = 1; + } + else + (void) printf(gettext("Successfully added %s to %s\n"), + mname[i], gname); + } + + free(mname); + return (ret); +} + +/* + * smbadm_group_delmember + */ +static int +smbadm_group_delmember(int argc, char **argv) +{ + char *gname = NULL; + char **mname; + char option; + uint32_t status; + int mcnt = 0; + int ret = 0; + int i; + + mname = (char **)malloc(argc * sizeof (char *)); + if (mname == NULL) { + (void) fprintf(stderr, gettext("out of memory\n")); + return (1); + } + bzero(mname, argc * sizeof (char *)); + + while ((option = getopt(argc, argv, "m:")) != -1) { + switch (option) { + case 'm': + mname[mcnt++] = optarg; + break; + + default: + free(mname); + smbadm_usage(B_FALSE); + } + } + + if (mcnt == 0) { + (void) fprintf(stderr, gettext("missing member name\n")); + free(mname); + smbadm_usage(B_FALSE); + } + + gname = argv[optind]; + if (optind >= argc || gname == NULL || *gname == 0) { + (void) fprintf(stderr, gettext("missing group name\n")); + free(mname); + smbadm_usage(B_FALSE); + } + + + for (i = 0; i < mcnt; i++) { + if (mname[i] == NULL) + continue; + + status = smb_group_member_remove(gname, mname[i]); + if (status != NT_STATUS_SUCCESS) { + (void) fprintf(stderr, + gettext("failed to remove %s (%s)\n"), + mname[i], xlate_nt_status(status)); + ret = 1; + } else { + (void) printf( + gettext("Successfully removed %s from %s\n"), + mname[i], gname); + } + } + + return (ret); +} + +static int +smbadm_user_disable(int argc, char **argv) +{ + int error; + char *user = NULL; + + user = argv[optind]; + if (optind >= argc || user == NULL || *user == '\0') { + (void) fprintf(stderr, gettext("missing user name\n")); + smbadm_usage(B_FALSE); + } + + error = smb_pwd_setcntl(user, SMB_PWC_DISABLE); + if (error == SMB_PWE_SUCCESS) + (void) printf(gettext("%s is disabled.\n"), user); + else + (void) fprintf(stderr, "%s\n", smbadm_pwd_strerror(error)); + + return (error); +} + +static int +smbadm_user_enable(int argc, char **argv) +{ + int error; + char *user = NULL; + + user = argv[optind]; + if (optind >= argc || user == NULL || *user == '\0') { + (void) fprintf(stderr, gettext("missing user name\n")); + smbadm_usage(B_FALSE); + } + + error = smb_pwd_setcntl(user, SMB_PWC_ENABLE); + if (error == SMB_PWE_SUCCESS) + (void) printf(gettext("%s is enabled.\n"), user); + else + (void) fprintf(stderr, "%s\n", smbadm_pwd_strerror(error)); + + return (error); +} + + +int +main(int argc, char **argv) +{ + int i; + + (void) malloc(0); /* satisfy libumem dependency */ + + progname = basename(argv[0]); + + if (getzoneid() != GLOBAL_ZONEID) { + (void) fprintf(stderr, + gettext("cannot execute in non-global zone\n")); + return (0); + } + + if (is_system_labeled()) { + (void) fprintf(stderr, + gettext("Trusted Extensions not supported\n")); + return (0); + } + + if (argc < 2) { + (void) fprintf(stderr, gettext("missing command\n")); + smbadm_usage(B_FALSE); + } + + /* + * Special case "cmd --help/-?" + */ + if (strcmp(argv[1], "-?") == 0 || + strcmp(argv[1], "--help") == 0 || + strcmp(argv[1], "-h") == 0) + smbadm_usage(B_TRUE); + + for (i = 0; i < SMBADM_NCMD; ++i) { + curcmd = &smbadm_cmdtable[i]; + if (strcasecmp(argv[1], curcmd->name) == 0) { + if (argc > 2) { + /* cmd subcmd --help/-? */ + if (strcmp(argv[2], "-?") == 0 || + strcmp(argv[2], "--help") == 0 || + strcmp(argv[2], "-h") == 0) + smbadm_usage(B_TRUE); + } + + return (curcmd->func(argc - 1, &argv[1])); + } + } + + curcmd = NULL; + (void) fprintf(stderr, gettext("unknown subcommand (%s)\n"), argv[1]); + smbadm_usage(B_FALSE); + return (2); +} + +static boolean_t +smbadm_prop_validate(smbadm_prop_t *prop, boolean_t chkval) +{ + smbadm_prop_handle_t *pinfo; + int i; + + for (i = 0; i < SMBADM_NPROP; i++) { + pinfo = &smbadm_ptable[i]; + if (strcmp(pinfo->p_name, prop->p_name) == 0) { + if (pinfo->p_chkfn && chkval) + return (pinfo->p_chkfn(prop)); + + return (B_TRUE); + } + } + + (void) fprintf(stderr, + gettext("unrecognized property '%s'\n"), prop->p_name); + + return (B_FALSE); +} + +static int +smbadm_prop_parse(char *arg, smbadm_prop_t *prop) +{ + boolean_t parse_value; + char *equal; + + if (arg == NULL) + return (2); + + prop->p_name = prop->p_value = NULL; + + if (strcmp(curcmd->name, "set") == 0) + parse_value = B_TRUE; + else + parse_value = B_FALSE; + + prop->p_name = arg; + + if (parse_value) { + equal = strchr(arg, '='); + if (equal == NULL) + return (2); + + *equal++ = '\0'; + prop->p_value = equal; + } + + if (smbadm_prop_validate(prop, parse_value) == B_FALSE) + return (2); + + return (0); +} + +static smbadm_prop_handle_t * +smbadm_prop_gethandle(char *pname) +{ + int i; + + for (i = 0; i < SMBADM_NPROP; i++) + if (strcmp(pname, smbadm_ptable[i].p_name) == 0) + return (&smbadm_ptable[i]); + + return (NULL); +} + +static int +smbadm_setprop_desc(char *gname, smbadm_prop_t *prop) +{ + uint32_t status; + + status = smb_group_modify(gname, gname, prop->p_value); + if (status != NT_STATUS_SUCCESS) { + (void) fprintf(stderr, + gettext("failed to modify the group description (%s)\n"), + xlate_nt_status(status)); + return (1); + } + + (void) printf(gettext("Successfully modified " + "'%s' description\n"), gname); + + return (0); +} + +static int +smbadm_getprop_desc(char *gname, smbadm_prop_t *prop) +{ + uint32_t status; + ntgrp_list_t *list = NULL; + + status = smb_group_list(0, &list, gname, 0); + if (status != NT_STATUS_SUCCESS) { + (void) fprintf(stderr, + gettext("failed to get the %s (%s)\n"), + prop->p_name, xlate_nt_status(status)); + return (1); + } + + if ((list == NULL) || (list->cnt <= 0)) { + (void) fprintf(stderr, gettext("%s: no such group\n"), gname); + return (1); + } + + (void) printf(gettext("\t%s: %s\n"), prop->p_name, + list->groups[0].desc); + smb_group_free_list(list, 0); + return (0); +} + +static int +smbadm_group_setpriv(char *gname, uint32_t priv_id, smbadm_prop_t *prop) +{ + uint32_t priv_attr; + uint32_t status; + int ret; + + if (strcasecmp(prop->p_value, "on") == 0) { + (void) printf(gettext("Enabling %s privilege "), prop->p_name); + priv_attr = SE_PRIVILEGE_ENABLED; + } else { + (void) printf(gettext("Disabling %s privilege "), prop->p_name); + priv_attr = SE_PRIVILEGE_DISABLED; + } + + status = smb_group_priv_set(gname, priv_id, priv_attr); + + if (status == NT_STATUS_SUCCESS) { + (void) printf(gettext("succeeded\n")); + ret = 0; + } else { + (void) printf(gettext("failed: %s\n"), xlate_nt_status(status)); + ret = 1; + } + + return (ret); +} + +static int +smbadm_group_getpriv(char *gname, uint32_t priv_id, smbadm_prop_t *prop) +{ + uint32_t priv_attr; + uint32_t status; + + status = smb_group_priv_get(gname, priv_id, &priv_attr); + if (status != NT_STATUS_SUCCESS) { + (void) fprintf(stderr, gettext("failed to get %s (%s)\n"), + prop->p_name, xlate_nt_status(status)); + return (1); + } + + if (priv_attr == SE_PRIVILEGE_ENABLED) + (void) printf(gettext("\t%s: %s\n"), prop->p_name, "On"); + else if (priv_attr == SE_PRIVILEGE_DISABLED) + (void) printf(gettext("\t%s: %s\n"), prop->p_name, "Off"); + else + (void) printf(gettext("\t%s: %s\n"), prop->p_name, "Unknown"); + + return (0); +} + +static int +smbadm_setprop_tkowner(char *gname, smbadm_prop_t *prop) +{ + return (smbadm_group_setpriv(gname, SE_TAKE_OWNERSHIP_LUID, prop)); +} + +static int +smbadm_getprop_tkowner(char *gname, smbadm_prop_t *prop) +{ + return (smbadm_group_getpriv(gname, SE_TAKE_OWNERSHIP_LUID, prop)); +} + +static int +smbadm_setprop_backup(char *gname, smbadm_prop_t *prop) +{ + return (smbadm_group_setpriv(gname, SE_BACKUP_LUID, prop)); +} + +static int +smbadm_getprop_backup(char *gname, smbadm_prop_t *prop) +{ + return (smbadm_group_getpriv(gname, SE_BACKUP_LUID, prop)); +} + +static int +smbadm_setprop_restore(char *gname, smbadm_prop_t *prop) +{ + return (smbadm_group_setpriv(gname, SE_RESTORE_LUID, prop)); +} + +static int +smbadm_getprop_restore(char *gname, smbadm_prop_t *prop) +{ + return (smbadm_group_getpriv(gname, SE_RESTORE_LUID, prop)); +} + +static boolean_t +smbadm_chkprop_priv(smbadm_prop_t *prop) +{ + if (prop->p_value == NULL || *prop->p_value == '\0') { + (void) fprintf(stderr, + gettext("missing value for '%s'\n"), prop->p_name); + return (B_FALSE); + } + + if (strcasecmp(prop->p_value, "on") == 0) + return (B_TRUE); + + if (strcasecmp(prop->p_value, "off") == 0) + return (B_TRUE); + + (void) fprintf(stderr, + gettext("%s: unrecognized value for '%s' property\n"), + prop->p_value, prop->p_name); + + return (B_FALSE); +} + +static const char * +smbadm_pwd_strerror(int error) +{ + switch (error) { + case SMB_PWE_SUCCESS: + return (gettext("Success.")); + + case SMB_PWE_USER_UNKNOWN: + return (gettext("User does not exist.")); + + case SMB_PWE_USER_DISABLE: + return (gettext("User is disable.")); + + case SMB_PWE_CLOSE_FAILED: + case SMB_PWE_OPEN_FAILED: + case SMB_PWE_WRITE_FAILED: + case SMB_PWE_UPDATE_FAILED: + return (gettext("Unexpected failure. " + "SMB password database unchanged.")); + + case SMB_PWE_STAT_FAILED: + return (gettext("stat of SMB password file failed.")); + + case SMB_PWE_BUSY: + return (gettext("SMB password database busy. " + "Try again later.")); + + case SMB_PWE_DENIED: + return (gettext("Operation not permitted.")); + + case SMB_PWE_SYSTEM_ERROR: + return (gettext("System error.")); + } + + return (gettext("Unknown error code.")); +} + +/* + * Enable libumem debugging by default on DEBUG builds. + */ +#ifdef DEBUG +/* LINTED - external libumem symbol */ +const char * +_umem_debug_init(void) +{ + return ("default,verbose"); /* $UMEM_DEBUG setting */ +} + +/* LINTED - external libumem symbol */ +const char * +_umem_logging_init(void) +{ + return ("fail,contents"); /* $UMEM_LOGGING setting */ +} +#endif diff --git a/usr/src/cmd/smbsrv/smbd/Makefile b/usr/src/cmd/smbsrv/smbd/Makefile new file mode 100644 index 000000000000..725ebf7a63dd --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/Makefile @@ -0,0 +1,78 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +PROG= smbd +SRCS= \ + smbd_door_ops.c \ + smbd_door_server.c \ + smbd_doorsvc.c \ + smbd_join.c \ + smbd_logon.c \ + smbd_main.c \ + smbd_mlsvc_doorsvc.c \ + smbd_nicmon.c \ + smbd_share_doorsvc.c + +include ../../Makefile.cmd + +MANIFEST = server.xml + +ROOTMANIFESTDIR = $(ROOTSVCSMB) +$(ROOTMANIFEST):= FILEMODE = 0444 + +include ../Makefile.smbsrv.defs + +LDLIBS += -lmlsvc -lmlrpc -lsmbrdr -lsmbns -lbsm -lnsl -lsocket + +ROOTSMBDDIR = $(ROOTLIB)/smbsrv +ROOTSMBDFILE = $(PROG:%=$(ROOTSMBDDIR)/%) + +FILEMODE = 0444 +$(ROOTSMBDFILE):= FILEMODE = 0555 + +$(ROOTSMBDDIR)/%: % + $(INS.file) + +all: $(PROG) + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +check: $(CHKMANIFEST) + +_msg: + +include ../../Makefile.targ + +install: all .WAIT $(ROOTETCDEFAULTFILES) $(ROOTMANIFEST) \ + $(ROOTSMBDFILE) diff --git a/usr/src/cmd/smbsrv/smbd/server.xml b/usr/src/cmd/smbsrv/smbd/server.xml new file mode 100644 index 000000000000..879ca260a8a5 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/server.xml @@ -0,0 +1,250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/usr/src/cmd/smbsrv/smbd/smbd.h b/usr/src/cmd/smbsrv/smbd/smbd.h new file mode 100644 index 000000000000..27cd6f02e607 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd.h @@ -0,0 +1,75 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBD_H +#define _SMBD_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +extern int smb_nicmon_start(void); +extern void smb_nicmon_stop(void); +extern int smb_mlsvc_srv_start(void); +extern void smb_mlsvc_srv_stop(void); +extern int smb_lmshrd_srv_start(void); +extern void smb_lmshrd_srv_stop(void); + +extern int smb_doorsrv_start(void); +extern void smb_doorsrv_stop(void); +extern int smb_ntgroup_doorsrv_start(void); +extern void smb_ntgroup_doorsrv_stop(void); + +extern int smb_netlogon_init(void); + +extern smb_token_t *smbd_user_auth_logon(netr_client_t *); +extern void smbd_user_nonauth_logon(uint32_t); +extern void smbd_user_auth_logoff(uint32_t); + +typedef struct smbd { + const char *s_version; /* smbd version string */ + const char *s_pname; /* basename to use for messages */ + pid_t s_pid; /* process-ID of current daemon */ + uid_t s_uid; /* UID of current daemon */ + gid_t s_gid; /* GID of current daemon */ + int s_fg; /* Run in foreground */ + int s_drv_fd; /* Handle for SMB kernel driver */ + int s_shutdown_flag; /* Fields for shutdown control */ + int s_sigval; +} smbd_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBD_H */ diff --git a/usr/src/cmd/smbsrv/smbd/smbd_door_ops.c b/usr/src/cmd/smbsrv/smbd/smbd_door_ops.c new file mode 100644 index 000000000000..5b1634a719a1 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_door_ops.c @@ -0,0 +1,916 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMBd door operations + */ +#include +#include +#include +#include +#include +#include "smbd.h" + +static int smb_set_downcall_desc(int desc); +static int smb_get_downcall_desc(void); + +static char *smb_dop_set_dwncall_desc(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_user_auth_logon(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_user_nonauth_logon(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_user_auth_logoff(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); + +static char *smb_dop_user_list(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); + +static char *smb_dop_group_add(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_group_delete(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_group_member_add(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_group_member_remove(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_group_count(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_group_cachesize(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_group_modify(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_group_priv_num(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_group_priv_list(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_group_priv_get(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_group_priv_set(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_group_list(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_group_member_list(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); +static char *smb_dop_group_member_count(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err); + +/* SMB daemon's door operation table */ +smb_dr_op_t smb_doorsrv_optab[] = +{ + smb_dop_user_auth_logon, + smb_dop_set_dwncall_desc, + smb_dop_user_nonauth_logon, + smb_dop_user_auth_logoff, + smb_dop_user_list, + smb_dop_group_add, + smb_dop_group_delete, + smb_dop_group_member_add, + smb_dop_group_member_remove, + smb_dop_group_count, + smb_dop_group_cachesize, + smb_dop_group_modify, + smb_dop_group_priv_num, + smb_dop_group_priv_list, + smb_dop_group_priv_get, + smb_dop_group_priv_set, + smb_dop_group_list, + smb_dop_group_member_list, + smb_dop_group_member_count +}; + +/*ARGSUSED*/ +static char * +smb_dop_user_nonauth_logon(char *argp, size_t arg_size, door_desc_t *dp, + uint_t n_desc, size_t *rbufsize, int *err) +{ + char *buf; + uint32_t sid; + + if (smb_dr_decode_common(argp, arg_size, xdr_uint32_t, &sid) != 0) { + *rbufsize = 0; + *err = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + smbd_user_nonauth_logon(sid); + + if ((buf = smb_dr_set_res_stat(SMB_DR_OP_SUCCESS, rbufsize)) == NULL) { + *rbufsize = 0; + *err = SMB_DR_OP_ERR_ENCODE; + return (NULL); + } + + *err = SMB_DR_OP_SUCCESS; + return (buf); +} + +/*ARGSUSED*/ +static char * +smb_dop_user_auth_logoff(char *argp, size_t arg_size, door_desc_t *dp, + uint_t n_desc, size_t *rbufsize, int *err) +{ + char *buf; + uint32_t sid; + + if (smb_dr_decode_common(argp, arg_size, xdr_uint32_t, &sid) != 0) { + *rbufsize = 0; + *err = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + smbd_user_auth_logoff(sid); + + if ((buf = smb_dr_set_res_stat(SMB_DR_OP_SUCCESS, rbufsize)) == NULL) { + *rbufsize = 0; + *err = SMB_DR_OP_ERR_ENCODE; + return (NULL); + } + + *err = SMB_DR_OP_SUCCESS; + return (buf); +} + +/* + * smb_downcall_desc + * + * This downcall descriptor will be initialized when the SMB Kmod + * makes a upcall for the SMBD_DOOR_SET_DOWNCALL_DESC. + * This descriptor should be passed as the 1st argument to the + * door_call() whenever the SMBD is making a downcall to SMB Kmod. + */ +static int smb_downcall_desc = -1; +static mutex_t smb_downcall_mutex; + +/* + * Get and set the smb downcall descriptor. + */ +static int +smb_set_downcall_desc(int desc) +{ + (void) mutex_lock(&smb_downcall_mutex); + smb_downcall_desc = desc; + (void) mutex_unlock(&smb_downcall_mutex); + return (0); +} + +/* + * smb_get_downcall_desc + * + * Returns the downcall descriptor. + */ +static int +smb_get_downcall_desc(void) +{ + int rc; + + (void) mutex_lock(&smb_downcall_mutex); + rc = smb_downcall_desc; + (void) mutex_unlock(&smb_downcall_mutex); + return (rc); +} + +/* + * smb_dr_is_valid_opcode + * + * Validates the given door opcode. + */ +int +smb_dr_is_valid_opcode(int opcode) +{ + if (opcode < 0 || + opcode > (sizeof (smb_doorsrv_optab) / sizeof (smb_dr_op_t))) + return (-1); + else + return (0); +} + +/* + * Obtains an access token on successful user authentication. + */ +/*ARGSUSED*/ +static char * +smb_dop_user_auth_logon(char *argp, size_t arg_size, door_desc_t *dp, + uint_t n_desc, size_t *rbufsize, int *err) +{ + netr_client_t *clnt_info; + smb_token_t *token; + char *buf; + + *rbufsize = 0; + *err = 0; + clnt_info = smb_dr_decode_arg_get_token(argp, arg_size); + if (clnt_info == NULL) { + syslog(LOG_ERR, "smbd: clnt_info is NULL"); + *err = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + token = smbd_user_auth_logon(clnt_info); + + free(clnt_info); + + if (!token) { + *err = SMB_DR_OP_ERR_EMPTYBUF; + return (NULL); + } + + if ((buf = smb_dr_encode_res_token(token, rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + } + + smb_token_destroy(token); + return (buf); +} + +/* + * smb_dop_set_dwncall_desc + * + * Set the downcall descriptor. + */ +/*ARGSUSED*/ +static char * +smb_dop_set_dwncall_desc(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *buf = NULL; + uint32_t stat; + + *rbufsize = 0; + *err = 0; + + if (n_desc != 1 || + smb_set_downcall_desc(dp->d_data.d_desc.d_descriptor) != 0) { + stat = SMB_DR_OP_ERR; + } else { + /* install get downcall descriptor callback */ + (void) smb_dwncall_install_callback(smb_get_downcall_desc); + stat = SMB_DR_OP_SUCCESS; + } + if ((buf = smb_dr_set_res_stat(stat, rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + + return (buf); +} + +/* + * smb_dr_op_users + * + * This function will obtain information on the connected users + * starting at the given offset by making a door down-call. The + * information will then be returned to the user-space door client. + * + * At most 50 users (i.e. SMB_DR_MAX_USER) will be returned via this + * function. The user-space door client might need to make multiple + * calls to retrieve information on all connected users. + */ +/*ARGSUSED*/ +char * +smb_dop_user_list(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + smb_dr_ulist_t *ulist; + uint32_t offset; + char *rbuf = NULL; + int cnt = 0; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + if (smb_dr_decode_common(argp, arg_size, xdr_uint32_t, &offset) != 0) { + *err = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + ulist = malloc(sizeof (smb_dr_ulist_t)); + if (!ulist) { + *err = SMB_DR_OP_ERR_EMPTYBUF; + return (NULL); + } + + cnt = smb_dwncall_get_users(offset, ulist); + if (cnt < 0) { + *err = SMB_DR_OP_ERR_EMPTYBUF; + free(ulist); + return (NULL); + } + + if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, ulist, + xdr_smb_dr_ulist_t, rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + + smb_dr_ulist_free(ulist); + return (rbuf); +} + +/* NT Group door operations start from here */ +/*ARGSUSED*/ +static char * +smb_dop_group_add(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *rbuf = NULL; + ntgrp_dr_arg_t *args; + uint32_t ntstatus = NT_STATUS_UNSUCCESSFUL; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + /* Decode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, + "smb_dop_group_add: cannot allocate memory"); + *err = SMB_DR_OP_ERR; + return (NULL); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + if (smb_dr_decode_common(argp, arg_size, + xdr_ntgrp_dr_arg_t, args) != 0) { + free(args); + *err = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + ntstatus = nt_group_add(args->gname, args->desc); + + /* Encode the result and return */ + if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &ntstatus, + xdr_uint32_t, rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + + free(args); + return (rbuf); +} + +/*ARGSUSED*/ +static char * +smb_dop_group_delete(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *gname = NULL; + char *rbuf = NULL; + uint32_t ntstatus = NT_STATUS_UNSUCCESSFUL; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + /* Decode */ + if ((gname = smb_dr_decode_string(argp, arg_size)) == 0) { + *err = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + ntstatus = nt_group_delete(gname); + + /* Encode the result and return */ + if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &ntstatus, + xdr_uint32_t, rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + return (rbuf); +} + +/*ARGSUSED*/ +static char * +smb_dop_group_member_add(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *rbuf = NULL; + ntgrp_dr_arg_t *args; + nt_group_t *grp = NULL; + uint32_t ntstatus = NT_STATUS_UNSUCCESSFUL; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + /* Decode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, + "smb_dop_group_member_add: cannot allocate memory"); + *err = SMB_DR_OP_ERR; + return (NULL); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + if (smb_dr_decode_common(argp, arg_size, + xdr_ntgrp_dr_arg_t, args) != 0) { + free(args); + *err = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + grp = nt_group_getinfo(args->gname, RWLOCK_WRITER); + if (grp) { + ntstatus = nt_group_add_member_byname(args->gname, + args->member); + } else { + ntstatus = NT_STATUS_NO_SUCH_GROUP; + } + nt_group_putinfo(grp); + + /* Encode the result and return */ + if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &ntstatus, + xdr_uint32_t, rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + free(args); + return (rbuf); +} + +/*ARGSUSED*/ +static char * +smb_dop_group_member_remove(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *rbuf = NULL; + ntgrp_dr_arg_t *args; + nt_group_t *grp = NULL; + uint32_t ntstatus = NT_STATUS_UNSUCCESSFUL; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + /* Decode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, + "smb_dop_group_member_add: cannot allocate memory"); + *err = SMB_DR_OP_ERR; + return (NULL); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + if (smb_dr_decode_common(argp, arg_size, + xdr_ntgrp_dr_arg_t, args) != 0) { + free(args); + *err = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + grp = nt_group_getinfo(args->gname, RWLOCK_WRITER); + if (grp) { + ntstatus = nt_group_del_member_byname(grp, args->member); + } else { + ntstatus = NT_STATUS_NO_SUCH_GROUP; + } + nt_group_putinfo(grp); + + /* Encode the result and return */ + if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &ntstatus, + xdr_uint32_t, rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + + free(args); + return (rbuf); +} + +/*ARGSUSED*/ +static char * +smb_dop_group_count(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *rbuf = NULL; + int num = 0; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + num = nt_group_num_groups(); + + /* Encode the result and return */ + if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &num, + xdr_uint32_t, rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + return (rbuf); +} + +/*ARGSUSED*/ +static char * +smb_dop_group_cachesize(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *rbuf = NULL; + int num = 0; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + num = nt_group_cache_size(); + + /* Encode the result and return */ + if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &num, + xdr_uint32_t, rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + return (rbuf); +} + +/*ARGSUSED*/ +static char * +smb_dop_group_modify(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *rbuf = NULL; + ntgrp_dr_arg_t *args; + nt_group_t *grp = NULL; + uint32_t ntstatus = NT_STATUS_UNSUCCESSFUL; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + /* Decode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, + "smb_dop_group_modify: cannot allocate memory"); + *err = SMB_DR_OP_ERR; + return (NULL); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + if (smb_dr_decode_common(argp, arg_size, + xdr_ntgrp_dr_arg_t, args) != 0) { + free(args); + *err = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + grp = nt_group_getinfo(args->gname, RWLOCK_WRITER); + if (grp) { + if (!args->desc) + args->desc = grp->comment; + ntstatus = nt_group_modify(args->gname, + args->newgname, args->desc); + } else { + ntstatus = NT_STATUS_NO_SUCH_GROUP; + } + nt_group_putinfo(grp); + + /* Encode the result and return */ + if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &ntstatus, + xdr_uint32_t, rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + free(args); + return (rbuf); +} + +/*ARGSUSED*/ +static char * +smb_dop_group_priv_num(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *rbuf = NULL; + int num = 0; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + num = smb_priv_presentable_num(); + + /* Encode the result and return */ + if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &num, + xdr_uint32_t, rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + + return (rbuf); +} + +/*ARGSUSED*/ +static char * +smb_dop_group_priv_list(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *rbuf = NULL; + int num = 0, i, len = 0; + uint32_t *ids = NULL; + smb_privinfo_t *priv = NULL; + ntpriv_list_t *list; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + num = smb_priv_presentable_num(); + if (num > 0) { + len = sizeof (int) + (num * sizeof (privs_t *)); + if ((ids = malloc(num * sizeof (uint32_t))) == 0) { + syslog(LOG_ERR, "smb_dop_group_priv_list:" + "cannot allocate memory"); + *err = SMB_DR_OP_ERR; + return (NULL); + } + + if ((list = (ntpriv_list_t *)malloc(len)) == 0) { + syslog(LOG_ERR, "smb_dop_group_priv_list:" + "cannot allocate memory"); + *err = SMB_DR_OP_ERR; + free(ids); + return (NULL); + } + + list->cnt = num; + (void) smb_priv_presentable_ids(ids, num); + for (i = 0; i < num; i++) { + if ((list->privs[i] = malloc(sizeof (ntpriv_t))) == 0) { + *err = SMB_DR_OP_ERR; + free(ids); + smb_group_free_privlist(list, 1); + return (NULL); + } + bzero(list->privs[i], sizeof (ntpriv_t)); + priv = smb_priv_getbyvalue(ids[i]); + list->privs[i]->id = priv->id; + list->privs[i]->name = strdup(priv->name); + } + free(ids); + } + + if ((rbuf = smb_dr_encode_grp_privlist(SMB_DR_OP_SUCCESS, list, + rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + + smb_group_free_privlist(list, 1); + + return (rbuf); +} + +/*ARGSUSED*/ +static char * +smb_dop_group_priv_get(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *rbuf = NULL; + ntgrp_dr_arg_t *args; + uint32_t priv_attr; + nt_group_t *grp; + uint32_t retval; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + /* Decode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, + "smb_dop_group_priv_get: cannot allocate memory"); + *err = SMB_DR_OP_ERR; + return (NULL); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + if (smb_dr_decode_common(argp, arg_size, + xdr_ntgrp_dr_arg_t, args) != 0) { + free(args); + *err = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + grp = nt_group_getinfo(args->gname, RWLOCK_READER); + if (grp) { + priv_attr = nt_group_getpriv(grp, args->privid); + retval = priv_attr; + } else { + retval = NT_STATUS_NO_SUCH_GROUP; + *err = SMB_DR_OP_ERR; + } + nt_group_putinfo(grp); + + /* Encode the result and return */ + if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &retval, + xdr_uint32_t, rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + free(args); + return (rbuf); +} + +/*ARGSUSED*/ +static char * +smb_dop_group_priv_set(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *rbuf = NULL; + ntgrp_dr_arg_t *args; + uint32_t ntstatus = NT_STATUS_UNSUCCESSFUL; + nt_group_t *grp; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + /* Decode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, + "smb_dop_group_priv_set: cannot allocate memory"); + *err = SMB_DR_OP_ERR; + return (NULL); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + if (smb_dr_decode_common(argp, arg_size, + xdr_ntgrp_dr_arg_t, args) != 0) { + free(args); + *err = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + grp = nt_group_getinfo(args->gname, RWLOCK_WRITER); + if (grp) { + ntstatus = nt_group_setpriv(grp, + args->privid, args->priv_attr); + } else { + ntstatus = NT_STATUS_NO_SUCH_GROUP; + *err = SMB_DR_OP_ERR; + } + nt_group_putinfo(grp); + + /* Encode the result and return */ + if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &ntstatus, + xdr_uint32_t, rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + + free(args); + return (rbuf); +} + +/*ARGSUSED*/ +static char * +smb_dop_group_list(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *rbuf = NULL, *scope = NULL; + ntgrp_dr_arg_t *args; + ntgrp_list_t list; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + /* Decode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, + "smb_dop_group_list: cannot allocate memory"); + *err = SMB_DR_OP_ERR; + return (NULL); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + if (smb_dr_decode_common(argp, arg_size, + xdr_ntgrp_dr_arg_t, args) != 0) { + free(args); + *err = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + bzero(&list, sizeof (ntgrp_list_t)); + scope = args->scope; + if (scope == NULL) + scope = "*"; + nt_group_list(args->offset, scope, &list); + + /* Encode the result and return */ + if ((rbuf = smb_dr_encode_grp_list(SMB_DR_OP_SUCCESS, &list, + rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + + smb_group_free_list(&list, 1); + free(args); + return (rbuf); +} + +/*ARGSUSED*/ +static char * +smb_dop_group_member_list(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *rbuf = NULL; + ntgrp_dr_arg_t *args; + nt_group_t *grp = NULL; + ntgrp_member_list_t members; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + /* Decode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, + "smb_group_dr_listmember: cannot allocate memory"); + *err = SMB_DR_OP_ERR; + return (NULL); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + if (smb_dr_decode_common(argp, arg_size, + xdr_ntgrp_dr_arg_t, args) != 0) { + free(args); + *err = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + bzero(&members, sizeof (ntgrp_member_list_t)); + bzero(&members.members, SMB_GROUP_PER_LIST * sizeof (members_list)); + if ((!args->gname) || (strlen(args->gname) == 0)) { + free(args); + *err = SMB_DR_OP_ERR; + return (NULL); + } + grp = nt_group_getinfo(args->gname, RWLOCK_READER); + if (grp) { + (void) nt_group_member_list(args->offset, grp, &members); + } + nt_group_putinfo(grp); + + /* Encode the result and return */ + if ((rbuf = smb_dr_encode_grp_memberlist(SMB_DR_OP_SUCCESS, &members, + rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + + smb_group_free_memberlist(&members, 1); + free(args); + return (rbuf); +} + +/*ARGSUSED*/ +static char * +smb_dop_group_member_count(char *argp, size_t arg_size, + door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err) +{ + char *rbuf = NULL, *gname = NULL; + ntgrp_dr_arg_t *enc_args; + nt_group_t *grp = NULL; + int num = 0; + uint32_t ntstatus = NT_STATUS_UNSUCCESSFUL; + + *err = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + /* Decode */ + if ((gname = smb_dr_decode_string(argp, arg_size)) == 0) { + *err = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + grp = nt_group_getinfo(gname, RWLOCK_READER); + if (grp) { + num = nt_group_num_members(grp); + ntstatus = NT_STATUS_SUCCESS; + } else { + ntstatus = NT_STATUS_NO_SUCH_GROUP; + } + nt_group_putinfo(grp); + + /* Encode the result and return */ + if ((enc_args = (ntgrp_dr_arg_t *) + malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, + "smb_dop_group_member_count: cannot allocate memory"); + *err = SMB_DR_OP_ERR; + return (NULL); + } + bzero(enc_args, sizeof (ntgrp_dr_arg_t)); + enc_args->count = num; + enc_args->ntstatus = ntstatus; + if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, enc_args, + xdr_ntgrp_dr_arg_t, rbufsize)) == NULL) { + *err = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + free(enc_args); + return (rbuf); +} diff --git a/usr/src/cmd/smbsrv/smbd/smbd_door_server.c b/usr/src/cmd/smbsrv/smbd/smbd_door_server.c new file mode 100644 index 000000000000..30f3e91e13f0 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_door_server.c @@ -0,0 +1,290 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMBd door server + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +static int smb_door_fildes = -1; +static mutex_t smb_doorsrv_mutex; + +void smb_srv_door(void *, char *, size_t, door_desc_t *, uint_t); + +extern uint32_t smbd_join(smb_joininfo_t *); + +/* + * smb_doorsrv_start + * + * Start the SMBd door service. + * Returns 0 on success. Otherwise, -1. + */ +int +smb_doorsrv_start() +{ + int newfd; + + (void) mutex_lock(&smb_doorsrv_mutex); + + if (smb_door_fildes != -1) { + syslog(LOG_ERR, "smb_doorsrv_start: duplicate"); + (void) mutex_unlock(&smb_doorsrv_mutex); + return (0); + } + + if ((smb_door_fildes = door_create(smb_srv_door, + SMBD_DOOR_COOKIE, DOOR_UNREF)) < 0) { + syslog(LOG_ERR, "smb_doorsrv_start: door_create failed %s", + strerror(errno)); + (void) mutex_unlock(&smb_doorsrv_mutex); + return (-1); + } + + (void) unlink(SMBD_DOOR_NAME); + + if ((newfd = creat(SMBD_DOOR_NAME, 0644)) < 0) { + syslog(LOG_ERR, "smb_doorsrv_start: open failed %s", + strerror(errno)); + (void) door_revoke(smb_door_fildes); + smb_door_fildes = -1; + (void) mutex_unlock(&smb_doorsrv_mutex); + return (-1); + } + + (void) close(newfd); + (void) fdetach(SMBD_DOOR_NAME); + + if (fattach(smb_door_fildes, SMBD_DOOR_NAME) < 0) { + syslog(LOG_ERR, "smb_doorsrv_start: fattach failed %s", + strerror(errno)); + (void) door_revoke(smb_door_fildes); + smb_door_fildes = -1; + (void) mutex_unlock(&smb_doorsrv_mutex); + return (-1); + } + + (void) mutex_unlock(&smb_doorsrv_mutex); + return (0); +} + + +/* + * smb_doorsrv_stop + * + * Stop the smbd door service. + */ +void +smb_doorsrv_stop(void) +{ + (void) mutex_lock(&smb_doorsrv_mutex); + + if (smb_door_fildes != -1) { + (void) fdetach(SMBD_DOOR_NAME); + (void) door_revoke(smb_door_fildes); + smb_door_fildes = -1; + } + + (void) mutex_unlock(&smb_doorsrv_mutex); +} + + +/* + * smb_srv_door + * + */ +/*ARGSUSED*/ +void +smb_srv_door(void *cookie, char *ptr, size_t size, door_desc_t *dp, + uint_t n_desc) +{ + int req_type, rc; + char *buf; + int buflen; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + unsigned int dec_status; + unsigned int enc_status; + char *domain; + char *user; + char *passwd; + smb_joininfo_t jdi; + + + dec_ctx = smb_dr_decode_start(ptr, size); + + if (dec_ctx == 0) + return; + + req_type = smb_dr_get_uint32(dec_ctx); + buflen = SMBD_DOOR_SIZE; + + if ((buf = alloca(buflen)) == NULL) { + syslog(LOG_ERR, "SmbdDoorSrv: resource shortage"); + (void) smb_dr_decode_finish(dec_ctx); + return; + } + + enc_ctx = smb_dr_encode_start(buf, buflen); + if (enc_ctx == 0) { + syslog(LOG_ERR, "SmbdDoorSrv: encode start failed"); + (void) smb_dr_decode_finish(dec_ctx); + return; + } + + switch (req_type) { + case SMBD_DOOR_PARAM_GET: { + smb_cfg_id_t id; + char *value = NULL; + char *empty = ""; + + id = smb_dr_get_uint32(dec_ctx); + + dec_status = smb_dr_decode_finish(dec_ctx); + if (dec_status != 0) { + goto decode_error; + } + + smb_config_rdlock(); + value = smb_config_getstr(id); + smb_dr_put_int32(enc_ctx, SMBD_DOOR_SRV_SUCCESS); + + if (value) + smb_dr_put_string(enc_ctx, value); + else + smb_dr_put_string(enc_ctx, empty); + smb_config_unlock(); + break; + } + + case SMBD_DOOR_PARAM_SET: { + smb_cfg_id_t id; + char *value = NULL; + + id = smb_dr_get_uint32(dec_ctx); + value = smb_dr_get_string(dec_ctx); + + dec_status = smb_dr_decode_finish(dec_ctx); + if (dec_status != 0) { + smb_dr_free_string(value); + goto decode_error; + } + + smb_config_wrlock(); + if (smb_config_set(id, value) == 0) { + smb_dr_put_int32(enc_ctx, SMBD_DOOR_SRV_SUCCESS); + } else { + smb_dr_put_int32(enc_ctx, SMBD_DOOR_SRV_ERROR); + } + smb_config_unlock(); + smb_dr_free_string(value); + break; + } + + case SMBD_DOOR_NETBIOS_RECONFIG: { + smb_netbios_name_reconfig(); + smb_dr_put_int32(enc_ctx, SMBD_DOOR_SRV_SUCCESS); + break; + } + + case SMBD_DOOR_JOIN: + jdi.mode = smb_dr_get_uint32(dec_ctx); + domain = smb_dr_get_string(dec_ctx); + user = smb_dr_get_string(dec_ctx); + passwd = smb_dr_get_string(dec_ctx); + + dec_status = smb_dr_decode_finish(dec_ctx); + if (dec_status != 0 || + domain == 0 || user == 0 || passwd == 0) { + smb_dr_free_string(domain); + smb_dr_free_string(user); + smb_dr_free_string(passwd); + goto decode_error; + } + + (void) strlcpy(jdi.domain_name, domain, + sizeof (jdi.domain_name)); + (void) strlcpy(jdi.domain_username, user, + sizeof (jdi.domain_username)); + (void) strlcpy(jdi.domain_passwd, passwd, + sizeof (jdi.domain_passwd)); + + smb_dr_free_string(domain); + smb_dr_free_string(user); + smb_dr_free_string(passwd); + + rc = smbd_join(&jdi); + smb_dr_put_int32(enc_ctx, SMBD_DOOR_SRV_SUCCESS); + smb_dr_put_int32(enc_ctx, rc); + break; + + default: + goto decode_error; + } + + if ((enc_status = smb_dr_encode_finish(enc_ctx, &used)) != 0) + goto encode_error; + + (void) door_return(buf, used, NULL, 0); + + return; + +decode_error: + (void) smb_dr_put_int32(enc_ctx, SMBD_DOOR_SRV_ERROR); + (void) smb_dr_put_uint32(enc_ctx, dec_status); + (void) smb_dr_encode_finish(enc_ctx, &used); + (void) door_return(buf, used, NULL, 0); + return; + +encode_error: + enc_ctx = smb_dr_encode_start(buf, buflen); + (void) smb_dr_put_int32(enc_ctx, SMBD_DOOR_SRV_ERROR); + (void) smb_dr_put_uint32(enc_ctx, enc_status); + (void) smb_dr_encode_finish(enc_ctx, &used); + + (void) door_return(buf, used, NULL, 0); +} diff --git a/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c b/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c new file mode 100644 index 000000000000..d564c789b564 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c @@ -0,0 +1,200 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMBd door server + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static int smb_doorsrv_fildes = -1; +static mutex_t smb_doorsrv_mutex; + +static void smb_door_srv_func(void *cookie, char *ptr, size_t size, + door_desc_t *dp, uint_t n_odesc); + +/* + * smb_door_srv_start + * + * Start the smbd door service. Create and bind to a door. + * Returns 0 on success. Otherwise, -1. + */ +int +smb_door_srv_start() +{ + int newfd; + + (void) mutex_lock(&smb_doorsrv_mutex); + + if (smb_doorsrv_fildes != -1) { + (void) fprintf(stderr, "smb_doorsrv_start: already started"); + (void) mutex_unlock(&smb_doorsrv_mutex); + return (-1); + } + + if ((smb_doorsrv_fildes = door_create(smb_door_srv_func, + SMB_DR_SVC_COOKIE, DOOR_UNREF)) < 0) { + (void) fprintf(stderr, "smb_doorsrv_start: door_create: %s", + strerror(errno)); + smb_doorsrv_fildes = -1; + (void) mutex_unlock(&smb_doorsrv_mutex); + return (-1); + } + + (void) unlink(SMB_DR_SVC_NAME); + + if ((newfd = creat(SMB_DR_SVC_NAME, 0644)) < 0) { + (void) fprintf(stderr, "smb_doorsrv_start: open: %s", + strerror(errno)); + (void) door_revoke(smb_doorsrv_fildes); + smb_doorsrv_fildes = -1; + (void) mutex_unlock(&smb_doorsrv_mutex); + return (-1); + } + + (void) close(newfd); + (void) fdetach(SMB_DR_SVC_NAME); + + if (fattach(smb_doorsrv_fildes, SMB_DR_SVC_NAME) < 0) { + (void) fprintf(stderr, "smb_doorsrv_start: fattach: %s", + strerror(errno)); + (void) door_revoke(smb_doorsrv_fildes); + smb_doorsrv_fildes = -1; + (void) mutex_unlock(&smb_doorsrv_mutex); + return (-1); + } + + (void) mutex_unlock(&smb_doorsrv_mutex); + return (0); +} + + +/* + * smb_door_srv_stop + * + * Stop the smbd door service. + */ +void +smb_door_srv_stop(void) +{ + (void) mutex_lock(&smb_doorsrv_mutex); + + if (smb_doorsrv_fildes != -1) { + (void) fdetach(SMB_DR_SVC_NAME); + (void) door_revoke(smb_doorsrv_fildes); + smb_doorsrv_fildes = -1; + } + + (void) mutex_unlock(&smb_doorsrv_mutex); +} + +/* + * smb_door_err_hdlr + * + * Encode the appropriate error code to the first 4-byte of the result + * buffer upon any door operation failure. + */ +static char * +smb_door_srv_err_hdlr(int stat, size_t *rbufsize) +{ + char *rbuf; + + if ((rbuf = smb_dr_set_res_stat(stat, rbufsize)) == NULL) { + *rbufsize = 0; + return (NULL); + } + + return (rbuf); +} + +/* + * smb_door_srv_func + * + * This function will determine the opcode by decoding the first 4-byte of + * the argument buffer passed by a door client. The corresponding door + * operation will be looked up from the optab and get invoked. + * Basically, any door operation will takes the argument buffer as its + * parameter, and generates the result buffer. + */ +/*ARGSUSED*/ +void +smb_door_srv_func(void *cookie, char *argp, size_t arg_size, door_desc_t *dp, + uint_t n_desc) +{ + char *resbuf = NULL, *tmpbuf = NULL; + size_t rbufsize = 0; + int opcode; + int err; + smb_dr_op_t smbop; + + if ((opcode = smb_dr_get_opcode(argp, arg_size)) < 0) { + tmpbuf = smb_door_srv_err_hdlr(SMB_DR_OP_ERR_DECODE, + &rbufsize); + goto door_return; + } + + syslog(LOG_DEBUG, "smb_door_srv_func: execute server routine" + "(opcode=%d)", opcode); + if (smb_dr_is_valid_opcode(opcode) != 0) { + tmpbuf = smb_door_srv_err_hdlr(SMB_DR_OP_ERR_INVALID_OPCODE, + &rbufsize); + } else { + smbop = smb_doorsrv_optab[opcode]; + if ((tmpbuf = smbop(argp + sizeof (opcode), + arg_size - sizeof (opcode), dp, n_desc, + &rbufsize, &err)) == NULL) + tmpbuf = smb_door_srv_err_hdlr(err, &rbufsize); + } + +door_return: + if (tmpbuf) { + if ((resbuf = (char *)alloca(rbufsize)) == NULL) + rbufsize = 0; + else + (void) memcpy(resbuf, tmpbuf, rbufsize); + free(tmpbuf); + } + + (void) door_return(resbuf, rbufsize, NULL, 0); + /*NOTREACHED*/ +} diff --git a/usr/src/cmd/smbsrv/smbd/smbd_join.c b/usr/src/cmd/smbsrv/smbd/smbd_join.c new file mode 100644 index 000000000000..425df10df80b --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_join.c @@ -0,0 +1,395 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +/* + * Local protocol flags used to indicate which version of the + * netlogon protocol to use when attempting to find the PDC. + */ +#define NETLOGON_PROTO_NETLOGON 0x01 +#define NETLOGON_PROTO_SAMLOGON 0x02 + +/* + * Maximum time to wait for a domain controller (30 seconds). + */ +#define SMB_NETLOGON_TIMEOUT 30 + +/* + * Flags used in conjunction with the location and query condition + * variables. + */ +#define SMB_NETLF_LOCATE_DC 0x00000001 +#define SMB_NETLF_LSA_QUERY 0x00000002 + +typedef struct smb_netlogon_info { + char snli_domain[SMB_PI_MAX_DOMAIN]; + unsigned snli_flags; + mutex_t snli_locate_mtx; + cond_t snli_locate_cv; + mutex_t snli_query_mtx; + cond_t snli_query_cv; + uint32_t snli_status; +} smb_netlogon_info_t; + +static smb_netlogon_info_t smb_netlogon_info; + +static pthread_t lsa_monitor_thr; +static pthread_t dc_browser_thr; + +static void *smb_netlogon_lsa_monitor(void *arg); +static void *smb_netlogon_dc_browser(void *arg); + +/* + * Inline convenience function to find out if the domain information is + * valid. The caller can decide whether or not to wait. + */ +static boolean_t +smb_ntdomain_is_valid(uint32_t timeout) +{ + smb_ntdomain_t *info; + + if ((info = smb_getdomaininfo(timeout)) != 0) { + if (info->ipaddr != 0) + return (B_TRUE); + } + + return (B_FALSE); +} + +/* + * smbd_join + * + * Joins the specified domain/workgroup + */ +uint32_t +smbd_join(smb_joininfo_t *info) +{ + smb_ntdomain_t *pi; + uint32_t status; + unsigned char passwd_hash[SMBAUTH_HASH_SZ]; + char plain_passwd[PASS_LEN + 1]; + char plain_user[PASS_LEN + 1]; + + if (info->mode == SMB_SECMODE_WORKGRP) { + smb_config_wrlock(); + (void) smb_config_set_secmode(info->mode); + (void) smb_config_set(SMB_CI_DOMAIN_NAME, info->domain_name); + smb_config_unlock(); + return (NT_STATUS_SUCCESS); + } + + /* + * Ensure that any previous membership of this domain has + * been cleared from the environment before we start. This + * will ensure that we don't attempt a NETLOGON_SAMLOGON + * when attempting to find the PDC. + */ + smb_set_domain_member(0); + + (void) strcpy(plain_user, info->domain_username); + (void) strcpy(plain_passwd, info->domain_passwd); + + if (smb_auth_ntlm_hash(plain_passwd, passwd_hash) != SMBAUTH_SUCCESS) { + status = NT_STATUS_INTERNAL_ERROR; + syslog(LOG_ERR, "smbd: could not compute ntlm hash for '%s'", + plain_user); + return (status); + } + + smbrdr_ipc_set(plain_user, passwd_hash); + + if (locate_resource_pdc(info->domain_name)) { + if ((pi = smb_getdomaininfo(0)) == 0) { + status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + syslog(LOG_ERR, "smbd: could not get domain controller" + "information for '%s'", info->domain_name); + return (status); + } + + /* + * Temporary delay before creating + * the workstation trust account. + */ + (void) sleep(2); + status = mlsvc_validate_user(pi->server, pi->domain, + plain_user, plain_passwd); + + if (status == NT_STATUS_SUCCESS) { + smb_config_wrlock(); + (void) smb_config_set_secmode(SMB_SECMODE_DOMAIN); + (void) smb_config_set(SMB_CI_DOMAIN_NAME, + info->domain_name); + smb_config_unlock(); + smbrdr_ipc_commit(); + return (status); + } + + smbrdr_ipc_rollback(); + syslog(LOG_ERR, "smbd: failed joining %s (%s)", + info->domain_name, xlate_nt_status(status)); + return (status); + } + + smbrdr_ipc_rollback(); + syslog(LOG_ERR, "smbd: failed locating domain controller for %s", + info->domain_name); + return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND); +} + +/* + * locate_resource_pdc + * + * This is the entry point for discovering a domain controller for the + * specified domain. The caller may block here for around 30 seconds if + * the system has to go to the network and find a domain controller. + * Sometime it would be good to change this to smb_locate_pdc and allow + * the caller to specify whether or not he wants to wait for a response. + * + * The actual work of discovering a DC is handled by other threads. + * All we do here is signal the request and wait for a DC or a timeout. + * + * Returns B_TRUE if a domain controller is available. + */ +boolean_t +locate_resource_pdc(char *domain) +{ + int rc; + timestruc_t to; + + if (domain == NULL || *domain == '\0') + return (B_FALSE); + + (void) mutex_lock(&smb_netlogon_info.snli_locate_mtx); + + if ((smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) == 0) { + smb_netlogon_info.snli_flags |= SMB_NETLF_LOCATE_DC; + (void) strlcpy(smb_netlogon_info.snli_domain, domain, + SMB_PI_MAX_DOMAIN); + (void) cond_broadcast(&smb_netlogon_info.snli_locate_cv); + } + + while (smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) { + to.tv_sec = SMB_NETLOGON_TIMEOUT; + to.tv_nsec = 0; + rc = cond_reltimedwait(&smb_netlogon_info.snli_locate_cv, + &smb_netlogon_info.snli_locate_mtx, &to); + + if (rc == ETIME) + break; + } + + (void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx); + + return (smb_ntdomain_is_valid(0)); +} + +/* + * smb_netlogon_init + * + * Initialization of the DC browser and LSA monitor threads. + * Returns 0 on success, an error number if thread creation fails. + */ +int +smb_netlogon_init(void) +{ + pthread_attr_t tattr; + int rc; + + (void) pthread_attr_init(&tattr); + (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&lsa_monitor_thr, &tattr, + smb_netlogon_lsa_monitor, 0); + if (rc != 0) + goto nli_exit; + rc = pthread_create(&dc_browser_thr, &tattr, + smb_netlogon_dc_browser, 0); + if (rc != 0) { + (void) pthread_cancel(lsa_monitor_thr); + (void) pthread_join(lsa_monitor_thr, NULL); + } + +nli_exit: + (void) pthread_attr_destroy(&tattr); + return (rc); +} + +/* + * smb_netlogon_dc_browser + * + * This is the DC browser thread: it gets woken up whenever someone + * wants to locate a domain controller. + * + * With the introduction of Windows 2000, NetBIOS is no longer a + * requirement for NT domains. If NetBIOS has been disabled on the + * network there will be no browsers and we won't get any response + * to netlogon requests. So we try to find a DC controller via ADS + * first. If ADS is disabled or the DNS query fails, we drop back + * to the netlogon protocol. + * + * This function will block for up to 30 seconds waiting for the PDC + * to be discovered. Sometime it would be good to change this to + * smb_locate_pdc and allow the caller to specify whether or not he + * wants to wait for a response. + * + */ +/*ARGSUSED*/ +static void * +smb_netlogon_dc_browser(void *arg) +{ + boolean_t rc; + char resource_domain[SMB_PI_MAX_DOMAIN]; + int net, smb_nc_cnt; + int protocol; + + for (;;) { + (void) mutex_lock(&smb_netlogon_info.snli_locate_mtx); + + while ((smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) == + 0) { + (void) cond_wait(&smb_netlogon_info.snli_locate_cv, + &smb_netlogon_info.snli_locate_mtx); + } + + (void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx); + + (void) strlcpy(resource_domain, smb_netlogon_info.snli_domain, + SMB_PI_MAX_DOMAIN); + + smb_setdomaininfo(NULL, NULL, 0); + if (msdcs_lookup_ads() == 0) { + if (smb_is_domain_member()) + protocol = NETLOGON_PROTO_SAMLOGON; + else + protocol = NETLOGON_PROTO_NETLOGON; + + smb_nc_cnt = smb_nic_get_num(); + for (net = 0; net < smb_nc_cnt; net++) { + smb_netlogon_request(net, protocol, + resource_domain); + } + } + + rc = smb_ntdomain_is_valid(SMB_NETLOGON_TIMEOUT); + + (void) mutex_lock(&smb_netlogon_info.snli_locate_mtx); + smb_netlogon_info.snli_flags &= ~SMB_NETLF_LOCATE_DC; + (void) cond_broadcast(&smb_netlogon_info.snli_locate_cv); + (void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx); + + if (rc != B_TRUE) { + /* + * Notify the LSA monitor to update the + * primary and trusted domain information. + */ + (void) mutex_lock(&smb_netlogon_info.snli_query_mtx); + smb_netlogon_info.snli_flags |= SMB_NETLF_LSA_QUERY; + (void) cond_broadcast(&smb_netlogon_info.snli_query_cv); + (void) mutex_unlock(&smb_netlogon_info.snli_query_mtx); + } + } + + /*NOTREACHED*/ + return (NULL); +} + +/* + * smb_netlogon_lsa_monitor + * + * This monitor should run as a separate thread. It waits on a condition + * variable until someone indicates that the LSA domain information needs + * to be refreshed. It then queries the DC for the NT domain information: + * primary, account and trusted domains. The condition variable should be + * signaled whenever a DC is selected. + * + * Note that the LSA query calls require the DC information and this task + * may end up blocked on the DC location protocol, which is why this + * monitor is run as a separate thread. This should only happen if the DC + * goes down immediately after we located it. + */ +/*ARGSUSED*/ +static void * +smb_netlogon_lsa_monitor(void *arg) +{ + uint32_t status; + + for (;;) { + (void) mutex_lock(&smb_netlogon_info.snli_query_mtx); + + while ((smb_netlogon_info.snli_flags & SMB_NETLF_LSA_QUERY) == + 0) { + (void) cond_wait(&smb_netlogon_info.snli_query_cv, + &smb_netlogon_info.snli_query_mtx); + } + + smb_netlogon_info.snli_flags &= ~SMB_NETLF_LSA_QUERY; + (void) mutex_unlock(&smb_netlogon_info.snli_query_mtx); + + /* + * Skip the LSA query if Authenticated IPC is supported + * and the credential is not yet set. + */ + if (smbrdr_ipc_skip_lsa_query() == 0) { + status = lsa_query_primary_domain_info(); + if (status == NT_STATUS_SUCCESS) { + if (lsa_query_account_domain_info() + != NT_STATUS_SUCCESS) { + syslog(LOG_DEBUG, + "NetlogonLSAMonitor: query " + "account info failed"); + } + if (lsa_enum_trusted_domains() + != NT_STATUS_SUCCESS) { + syslog(LOG_DEBUG, + "NetlogonLSAMonitor: enum " + "trusted domain failed"); + } + } else { + syslog(LOG_DEBUG, + "NetlogonLSAMonitor: update failed"); + } + } + } + + /*NOTREACHED*/ + return (NULL); +} diff --git a/usr/src/cmd/smbsrv/smbd/smbd_logon.c b/usr/src/cmd/smbsrv/smbd/smbd_logon.c new file mode 100644 index 000000000000..3fa0443789bb --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_logon.c @@ -0,0 +1,289 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "smbd.h" + + +/* + * An audit session is established at user logon and terminated at user + * logoff. + * + * SMB audit handles are allocated when users logon (SmbSessionSetupX) + * and deallocted when a user logs off (SmbLogoffX). Each time an SMB + * audit handle is allocated it is added to a global list. + */ +typedef struct smb_audit { + struct smb_audit *sa_next; + adt_session_data_t *sa_handle; + uid_t sa_uid; + gid_t sa_gid; + uint32_t sa_audit_sid; + uint32_t sa_refcnt; + char *sa_domain; + char *sa_username; +} smb_audit_t; + +static smb_audit_t *smbd_audit_list; +static mutex_t smbd_audit_lock; + +/* + * Unique identifier for audit sessions in the audit list. + * Used to lookup an audit session on logoff. + */ +static uint32_t smbd_audit_sid; + +static void smbd_audit_link(smb_audit_t *); +static smb_audit_t *smbd_audit_unlink(uint32_t); + + +/* + * Invoked at user logon due to SmbSessionSetupX. Authenticate the + * user, start an audit session and audit the event. + */ +smb_token_t * +smbd_user_auth_logon(netr_client_t *clnt) +{ + smb_token_t *token; + smb_audit_t *entry; + adt_session_data_t *ah; + adt_event_data_t *event; + au_tid_addr_t termid; + uid_t uid; + gid_t gid; + char *sid; + int status; + int retval; + + if ((token = smb_logon(clnt)) == NULL) { + uid = ADT_NO_ATTRIB; + gid = ADT_NO_ATTRIB; + sid = strdup(NT_NULL_SIDSTR); + status = ADT_FAILURE; + retval = ADT_FAIL_VALUE_AUTH; + } else { + uid = token->tkn_user->i_id; + gid = token->tkn_primary_grp->i_id; + sid = nt_sid_format(token->tkn_user->i_sidattr.sid); + status = ADT_SUCCESS; + retval = ADT_SUCCESS; + } + + if (adt_start_session(&ah, NULL, 0)) { + syslog(LOG_AUTH | LOG_ALERT, "adt_start_session: %m"); + free(sid); + smb_token_destroy(token); + return (NULL); + } + + if ((event = adt_alloc_event(ah, ADT_smbd_session)) == NULL) { + syslog(LOG_AUTH | LOG_ALERT, + "adt_alloc_event(ADT_smbd_session): %m"); + (void) adt_end_session(ah); + free(sid); + smb_token_destroy(token); + return (NULL); + } + + (void) memset(&termid, 0, sizeof (au_tid_addr_t)); + termid.at_port = clnt->local_port; + termid.at_type = AU_IPv4; + termid.at_addr[0] = clnt->ipaddr; + adt_set_termid(ah, &termid); + + if (adt_set_user(ah, uid, gid, uid, gid, NULL, ADT_NEW)) { + syslog(LOG_AUTH | LOG_ALERT, "adt_set_user: %m"); + adt_free_event(event); + (void) adt_end_session(ah); + free(sid); + smb_token_destroy(token); + return (NULL); + } + + event->adt_smbd_session.domain = clnt->domain; + event->adt_smbd_session.username = clnt->username; + event->adt_smbd_session.sid = sid; + + if (adt_put_event(event, status, retval)) + syslog(LOG_AUTH | LOG_ALERT, "adt_put_event: %m"); + + adt_free_event(event); + free(sid); + + if (token) { + if ((entry = malloc(sizeof (smb_audit_t))) == NULL) { + syslog(LOG_ERR, "smbd_user_auth_logon: %m"); + (void) adt_end_session(ah); + free(sid); + smb_token_destroy(token); + return (NULL); + } + + entry->sa_handle = ah; + entry->sa_uid = uid; + entry->sa_gid = gid; + entry->sa_username = strdup(clnt->username); + entry->sa_domain = strdup(clnt->domain); + + (void) smb_autohome_add(entry->sa_username); + smbd_audit_link(entry); + token->tkn_audit_sid = entry->sa_audit_sid; + } + + return (token); +} + +/* + * Logon due to a subsequent SmbSessionSetupX on an existing session. + * The user was authenticated during the initial session setup. + */ +void +smbd_user_nonauth_logon(uint32_t audit_sid) +{ + smb_audit_t *entry; + + (void) mutex_lock(&smbd_audit_lock); + entry = smbd_audit_list; + + while (entry) { + if (entry->sa_audit_sid == audit_sid) { + ++entry->sa_refcnt; + break; + } + + entry = entry->sa_next; + } + + (void) mutex_unlock(&smbd_audit_lock); +} + +/* + * Invoked at user logoff due to SmbLogoffX. If this is the final + * logoff for this user on the session, audit the event and terminate + * the audit session. + */ +void +smbd_user_auth_logoff(uint32_t audit_sid) +{ + smb_audit_t *entry; + adt_session_data_t *ah; + adt_event_data_t *event; + + if ((entry = smbd_audit_unlink(audit_sid)) == NULL) + return; + + (void) smb_autohome_remove(entry->sa_username); + + ah = entry->sa_handle; + + if ((event = adt_alloc_event(ah, ADT_smbd_logoff)) == NULL) { + syslog(LOG_AUTH | LOG_ALERT, + "adt_alloc_event(ADT_smbd_logoff): %m"); + } else { + event->adt_smbd_logoff.domain = entry->sa_domain; + event->adt_smbd_logoff.username = entry->sa_username; + + if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS)) + syslog(LOG_AUTH | LOG_ALERT, "adt_put_event: %m"); + + adt_free_event(event); + } + + (void) adt_end_session(ah); + + free(entry->sa_username); + free(entry->sa_domain); + free(entry); +} + +/* + * Allocate an id and link an audit handle onto the global list. + */ +static void +smbd_audit_link(smb_audit_t *entry) +{ + (void) mutex_lock(&smbd_audit_lock); + + do { + ++smbd_audit_sid; + } while ((smbd_audit_sid == 0) || (smbd_audit_sid == (uint32_t)-1)); + + entry->sa_audit_sid = smbd_audit_sid; + entry->sa_refcnt = 1; + entry->sa_next = smbd_audit_list; + smbd_audit_list = entry; + + (void) mutex_unlock(&smbd_audit_lock); +} + +/* + * Unlink an audit handle. If the reference count reaches 0, the entry + * is removed from the list and returned. Otherwise the entry remains + * on the list and a null pointer is returned. + */ +static smb_audit_t * +smbd_audit_unlink(uint32_t audit_sid) +{ + smb_audit_t *entry; + smb_audit_t **ppe; + + (void) mutex_lock(&smbd_audit_lock); + ppe = &smbd_audit_list; + + while (*ppe) { + entry = *ppe; + + if (entry->sa_audit_sid == audit_sid) { + if (entry->sa_refcnt == 0) + break; + + if ((--entry->sa_refcnt) != 0) + break; + + *ppe = entry->sa_next; + (void) mutex_unlock(&smbd_audit_lock); + return (entry); + } + + ppe = &(*ppe)->sa_next; + } + + (void) mutex_unlock(&smbd_audit_lock); + return (NULL); +} diff --git a/usr/src/cmd/smbsrv/smbd/smbd_main.c b/usr/src/cmd/smbsrv/smbd/smbd_main.c new file mode 100644 index 000000000000..740cc7383784 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_main.c @@ -0,0 +1,758 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "smbd.h" + +#define DRV_DEVICE_PATH "/devices/pseudo/smbsrv@0:smbsrv" +#define SMB_CACHEDIR "/var/run/smb" +#define SMB_DBDIR "/var/smb" + +extern void smb_netbios_name_reconfig(); +extern void smb_browser_config(); + +static int smbd_daemonize_init(void); +static void smbd_daemonize_fini(int, int); + +static int smbd_kernel_bind(void); +static void smbd_kernel_unbind(void); +static int smbd_already_running(void); + +static int smbd_service_init(void); +static void smbd_service_fini(void); + +static int smbd_setup_options(int argc, char *argv[]); +static void smbd_usage(FILE *fp); +static void smbd_report(const char *fmt, ...); + +static void smbd_sig_handler(int sig); + +static int smbd_localtime_init(void); +static void *smbd_localtime_monitor(void *arg); + +extern time_t altzone; + +static pthread_t localtime_thr; + +static int smbd_refresh_init(void); +static void smbd_refresh_fini(void); +static void *smbd_refresh_monitor(void *); +static pthread_t refresh_thr; +static pthread_cond_t refresh_cond; +static pthread_mutex_t refresh_mutex; + +static smbd_t smbd; + +/* + * smbd user land daemon + * + * Use SMF error codes only on return or exit. + */ +int +main(int argc, char *argv[]) +{ + struct sigaction act; + sigset_t set; + uid_t uid; + int pfd = -1; + + smbd.s_pname = basename(argv[0]); + openlog(smbd.s_pname, LOG_PID | LOG_NOWAIT, LOG_DAEMON); + + if (smbd_setup_options(argc, argv) != 0) + return (SMF_EXIT_ERR_FATAL); + + if ((uid = getuid()) != smbd.s_uid) { + smbd_report("user %d: %s", uid, strerror(EPERM)); + return (SMF_EXIT_ERR_FATAL); + } + + if (getzoneid() != GLOBAL_ZONEID) { + smbd_report("non-global zones are not supported"); + return (SMF_EXIT_ERR_FATAL); + } + + if (is_system_labeled()) { + smbd_report("Trusted Extensions not supported"); + return (SMF_EXIT_ERR_FATAL); + } + + if (smbd_already_running()) + return (SMF_EXIT_OK); + + (void) sigfillset(&set); + (void) sigdelset(&set, SIGABRT); + + (void) sigfillset(&act.sa_mask); + act.sa_handler = smbd_sig_handler; + act.sa_flags = 0; + + (void) sigaction(SIGTERM, &act, NULL); + (void) sigaction(SIGHUP, &act, NULL); + (void) sigaction(SIGINT, &act, NULL); + (void) sigaction(SIGPIPE, &act, NULL); + + (void) sigdelset(&set, SIGTERM); + (void) sigdelset(&set, SIGHUP); + (void) sigdelset(&set, SIGINT); + (void) sigdelset(&set, SIGPIPE); + + if (smbd.s_fg) { + (void) sigdelset(&set, SIGTSTP); + (void) sigdelset(&set, SIGTTIN); + (void) sigdelset(&set, SIGTTOU); + + if (smbd_service_init() != 0) { + smbd_report("service initialization failed"); + exit(SMF_EXIT_ERR_FATAL); + } + } else { + /* + * "pfd" is a pipe descriptor -- any fatal errors + * during subsequent initialization of the child + * process should be written to this pipe and the + * parent will report this error as the exit status. + */ + pfd = smbd_daemonize_init(); + + if (smbd_service_init() != 0) { + smbd_report("daemon initialization failed"); + exit(SMF_EXIT_ERR_FATAL); + } + + smbd_daemonize_fini(pfd, SMF_EXIT_OK); + } + + (void) atexit(smbd_service_fini); + + while (!smbd.s_shutdown_flag) { + (void) sigsuspend(&set); + + switch (smbd.s_sigval) { + case 0: + break; + + case SIGPIPE: + break; + + case SIGHUP: + /* Refresh config was triggered */ + if (smbd.s_fg) + smbd_report("reconfiguration requested"); + (void) pthread_cond_signal(&refresh_cond); + break; + + default: + /* + * Typically SIGINT or SIGTERM. + */ + smbd.s_shutdown_flag = 1; + break; + } + + smbd.s_sigval = 0; + } + + smbd_service_fini(); + closelog(); + return (SMF_EXIT_OK); +} + +/* + * This function will fork off a child process, + * from which only the child will return. + * + * Use SMF error codes only on exit. + */ +static int +smbd_daemonize_init(void) +{ + int status, pfds[2]; + sigset_t set, oset; + pid_t pid; + int rc; + + /* + * Reset privileges to the minimum set required. We continue + * to run as root to create and access files in /var. + */ + rc = __init_daemon_priv(PU_RESETGROUPS | PU_LIMITPRIVS, + smbd.s_uid, smbd.s_gid, + PRIV_NET_MAC_AWARE, PRIV_NET_PRIVADDR, PRIV_PROC_AUDIT, + PRIV_SYS_DEVICES, PRIV_SYS_SMB, NULL); + + if (rc != 0) { + smbd_report("insufficient privileges"); + exit(SMF_EXIT_ERR_FATAL); + } + + /* + * Block all signals prior to the fork and leave them blocked in the + * parent so we don't get in a situation where the parent gets SIGINT + * and returns non-zero exit status and the child is actually running. + * In the child, restore the signal mask once we've done our setsid(). + */ + (void) sigfillset(&set); + (void) sigdelset(&set, SIGABRT); + (void) sigprocmask(SIG_BLOCK, &set, &oset); + + if (pipe(pfds) == -1) { + smbd_report("unable to create pipe"); + exit(SMF_EXIT_ERR_FATAL); + } + + closelog(); + + if ((pid = fork()) == -1) { + openlog(smbd.s_pname, LOG_PID | LOG_NOWAIT, LOG_DAEMON); + smbd_report("unable to fork"); + closelog(); + exit(SMF_EXIT_ERR_FATAL); + } + + /* + * If we're the parent process, wait for either the child to send us + * the appropriate exit status over the pipe or for the read to fail + * (presumably with 0 for EOF if our child terminated abnormally). + * If the read fails, exit with either the child's exit status if it + * exited or with SMF_EXIT_ERR_FATAL if it died from a fatal signal. + */ + if (pid != 0) { + (void) close(pfds[1]); + + if (read(pfds[0], &status, sizeof (status)) == sizeof (status)) + _exit(status); + + if (waitpid(pid, &status, 0) == pid && WIFEXITED(status)) + _exit(WEXITSTATUS(status)); + + _exit(SMF_EXIT_ERR_FATAL); + } + + openlog(smbd.s_pname, LOG_PID | LOG_NOWAIT, LOG_DAEMON); + smbd.s_pid = getpid(); + (void) setsid(); + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + (void) chdir("/"); + (void) umask(022); + (void) close(pfds[0]); + + return (pfds[1]); +} + +static void +smbd_daemonize_fini(int fd, int exit_status) +{ + /* + * Now that we're running, if a pipe fd was specified, write an exit + * status to it to indicate that our parent process can safely detach. + * Then proceed to loading the remaining non-built-in modules. + */ + if (fd >= 0) + (void) write(fd, &exit_status, sizeof (exit_status)); + + (void) close(fd); + + if ((fd = open("/dev/null", O_RDWR)) >= 0) { + (void) fcntl(fd, F_DUP2FD, STDIN_FILENO); + (void) fcntl(fd, F_DUP2FD, STDOUT_FILENO); + (void) fcntl(fd, F_DUP2FD, STDERR_FILENO); + (void) close(fd); + } + + __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION, + PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, NULL); +} + +static int +smbd_service_init(void) +{ + static char *dir[] = { + SMB_DBDIR, /* smbpasswd */ + SMB_CACHEDIR /* KRB credential cache */ + }; + + int rc; + int ddns_enabled; + uint32_t mode; + char resource_domain[SMB_PI_MAX_DOMAIN]; + int i; + + smbd.s_drv_fd = -1; + + for (i = 0; i < (sizeof (dir)/sizeof (dir[0])); ++i) { + errno = 0; + + if ((mkdir(dir[i], 0700) < 0) && (errno != EEXIST)) { + smbd_report("mkdir %s: %s", dir[i], strerror(errno)); + return (1); + } + } + + /* + * Set KRB5CCNAME (for the SMB credential cache) in the environment. + */ + if (putenv("KRB5CCNAME=" SMB_CACHEDIR "/ccache") != 0) { + smbd_report("unable to set KRB5CCNAME"); + return (1); + } + + (void) oem_language_set("english"); + + if (!nt_builtin_init()) { + smbd_report("out of memory"); + return (1); + } + + /* + * Need to load the configuration data prior to getting the + * interface information. + */ + if (smb_config_load() != 0) { + smbd_report("failed to load configuration data"); + return (1); + } + + smb_resolver_init(); + (void) smb_nicmon_start(); + + smbrdr_init(); + + smb_netbios_start(); + if (smb_netlogon_init() != 0) { + smbd_report("netlogon initialization failed"); + return (1); + } + + smb_config_rdlock(); + resource_domain[0] = '\0'; + (void) strlcpy(resource_domain, + smb_config_getstr(SMB_CI_DOMAIN_NAME), SMB_PI_MAX_DOMAIN); + (void) utf8_strupr(resource_domain); + smb_config_unlock(); + + mode = smb_get_security_mode(); + + if (mode == SMB_SECMODE_DOMAIN) + (void) locate_resource_pdc(resource_domain); + + /* Get the ID map client handle */ + if ((rc = smb_idmap_start()) != 0) { + smbd_report("no idmap handle"); + return (rc); + } + + if ((rc = nt_domain_init(resource_domain, mode)) != 0) { + if (rc == SMB_DOMAIN_NOMACHINE_SID) + smbd_report("No Security Identifier (SID) has been " + "generated for this system.\n" + "Check idmap service configuration."); + + if (rc == SMB_DOMAIN_NODOMAIN_SID) { + /* Get the domain sid from domain controller */ + if ((rc = lsa_query_primary_domain_info()) != 0) + smbd_report("Failed to get the Security " + "Identifier (SID) of domain %s", + resource_domain); + } + return (rc); + } + + if ((rc = mlsvc_init()) != 0) { + smbd_report("msrpc initialization failed"); + return (rc); + } + + if (rc = smb_mlsvc_srv_start()) { + smbd_report("msrpc door initialization failed: %d", rc); + return (rc); + } + + if (smb_lmshrd_srv_start() != 0) { + smbd_report("share initialization failed"); + } + + /* XXX following will get removed */ + (void) smb_doorsrv_start(); + if ((rc = smb_door_srv_start()) != 0) + return (rc); + + if ((rc = smbd_refresh_init()) != 0) + return (rc); + + /* Call dyndns update - Just in case its configured to refresh DNS */ + smb_config_rdlock(); + ddns_enabled = smb_config_getyorn(SMB_CI_DYNDNS_ENABLE); + smb_config_unlock(); + if (ddns_enabled) { + (void) dyndns_update(); + } + + if ((rc = smbd_kernel_bind()) != 0) + smbd_report("kernel bind error: %s", strerror(errno)); + + (void) smbd_localtime_init(); + + return (lmshare_start()); +} + +/* + * Close the kernel service and shutdown smbd services. + * This function is registered with atexit(): ensure that anything + * called from here is safe to be called multiple times. + */ +static void +smbd_service_fini(void) +{ + nt_builtin_fini(); + smbd_refresh_fini(); + smbd_kernel_unbind(); + smb_door_srv_stop(); + smb_doorsrv_stop(); + smb_lmshrd_srv_stop(); + lmshare_stop(); + smb_mlsvc_srv_stop(); + smb_nicmon_stop(); + smb_resolver_close(); + smb_idmap_stop(); +} + +/* + * smbd_refresh_init() + * + * SMB service refresh thread initialization. This thread waits for a + * refresh event and updates the daemon's view of the configuration + * before going back to sleep. + */ +static int +smbd_refresh_init() +{ + pthread_attr_t tattr; + pthread_condattr_t cattr; + int rc; + + (void) pthread_condattr_init(&cattr); + (void) pthread_cond_init(&refresh_cond, &cattr); + (void) pthread_condattr_destroy(&cattr); + + (void) pthread_mutex_init(&refresh_mutex, NULL); + + (void) pthread_attr_init(&tattr); + (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&refresh_thr, &tattr, smbd_refresh_monitor, 0); + (void) pthread_attr_destroy(&tattr); + return (rc); + +} + +/* + * smbd_refresh_fini() + * + * Stop the refresh thread. + */ +static void +smbd_refresh_fini() +{ + (void) pthread_cancel(refresh_thr); + + (void) pthread_cond_destroy(&refresh_cond); + (void) pthread_mutex_destroy(&refresh_mutex); +} + +/* + * smbd_refresh_monitor() + * + * Wait for a refresh event. When this thread wakes up, update the + * smbd configuration from the SMF config information then go back to + * wait for the next refresh. + */ +/*ARGSUSED*/ +static void * +smbd_refresh_monitor(void *arg) +{ + int dummy = 0; + + (void) pthread_mutex_lock(&refresh_mutex); + while (pthread_cond_wait(&refresh_cond, &refresh_mutex) == 0) { + /* + * We've been woken up by a refresh event so go do + * what is necessary. + */ + (void) smb_config_load(); + smb_nic_build_info(); + (void) smb_netbios_name_reconfig(); + (void) smb_browser_config(); + if (ioctl(smbd.s_drv_fd, SMB_IOC_CONFIG_REFRESH, &dummy) < 0) { + smbd_report("configuration update ioctl: %s", + strerror(errno)); + } + } + return (NULL); +} + + +/* + * If the door has already been opened by another process (non-zero pid + * in target), we assume that another smbd is already running. If there + * is a race here, it will be caught later when smbsrv is opened because + * only one process is allowed to open the device at a time. + */ +static int +smbd_already_running(void) +{ + door_info_t info; + int door; + + if ((door = open(SMBD_DOOR_NAME, O_RDONLY)) < 0) + return (0); + + if (door_info(door, &info) < 0) + return (0); + + if (info.di_target > 0) { + smbd_report("already running: pid %ld\n", info.di_target); + (void) close(door); + return (1); + } + + (void) close(door); + return (0); +} + +static int +smbd_kernel_bind(void) +{ + if (smbd.s_drv_fd != -1) + (void) close(smbd.s_drv_fd); + + if ((smbd.s_drv_fd = open(DRV_DEVICE_PATH, 0)) < 0) { + smbd.s_drv_fd = -1; + return (1); + } + return (0); +} + + +/* + * Initialization of the localtime thread. + * Returns 0 on success, an error number if thread creation fails. + */ + +int +smbd_localtime_init(void) +{ + pthread_attr_t tattr; + int rc; + + (void) pthread_attr_init(&tattr); + (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&localtime_thr, &tattr, smbd_localtime_monitor, 0); + (void) pthread_attr_destroy(&tattr); + return (rc); +} + +/* + * Local time thread to kernel land. + * Send local gmtoff to kernel module one time at startup + * and each time it changes (up to twice a year). + * Local gmtoff is checked once every 15 minutes and + * since some timezones are aligned on half and qtr hour boundaries, + * once an hour would likely suffice. + */ + +/*ARGSUSED*/ +static void * +smbd_localtime_monitor(void *arg) +{ + struct tm local_tm; + time_t secs, gmtoff; + time_t last_gmtoff = -1; + int timeout; + + for (;;) { + gmtoff = -altzone; + + if ((last_gmtoff != gmtoff) && (smbd.s_drv_fd != -1)) { + if (ioctl(smbd.s_drv_fd, SMB_IOC_GMTOFF, &gmtoff) < 0) { + smbd_report("localtime ioctl: %s", + strerror(errno)); + } + } + + /* + * Align the next iteration on a fifteen minute boundary. + */ + secs = time(0); + (void) localtime_r(&secs, &local_tm); + timeout = ((15 - (local_tm.tm_min % 15)) * SECSPERMIN); + (void) sleep(timeout); + + last_gmtoff = gmtoff; + } + + /*NOTREACHED*/ + return (NULL); +} + + +static void +smbd_kernel_unbind(void) +{ + if (smbd.s_drv_fd != -1) { + (void) close(smbd.s_drv_fd); + smbd.s_drv_fd = -1; + } +} + +static void +smbd_sig_handler(int sigval) +{ + if (smbd.s_sigval == 0) + smbd.s_sigval = sigval; +} + +/* + * Set up configuration options and parse the command line. + * This function will determine if we will run as a daemon + * or in the foreground. + * + * Failure to find a uid or gid results in using the default (0). + */ +static int +smbd_setup_options(int argc, char *argv[]) +{ + struct passwd *pwd; + struct group *grp; + int c; + + if ((pwd = getpwnam("root")) != NULL) + smbd.s_uid = pwd->pw_uid; + + if ((grp = getgrnam("sys")) != NULL) + smbd.s_gid = grp->gr_gid; + + smbd.s_fg = smb_get_fg_flag(); + + while ((c = getopt(argc, argv, ":f")) != -1) { + switch (c) { + case 'f': + smbd.s_fg = 1; + break; + + case ':': + case '?': + default: + smbd_usage(stderr); + return (-1); + } + } + + return (0); +} + +static void +smbd_usage(FILE *fp) +{ + static char *help[] = { + "-f run program in foreground" + }; + + int i; + + (void) fprintf(fp, "Usage: %s [-f]\n", smbd.s_pname); + + for (i = 0; i < sizeof (help)/sizeof (help[0]); ++i) + (void) fprintf(fp, " %s\n", help[i]); +} + +static void +smbd_report(const char *fmt, ...) +{ + char buf[128]; + va_list ap; + + if (fmt == NULL) + return; + + va_start(ap, fmt); + (void) vsnprintf(buf, 128, fmt, ap); + va_end(ap); + + (void) fprintf(stderr, "smbd: %s\n", buf); +} + +/* + * Enable libumem debugging by default on DEBUG builds. + */ +#ifdef DEBUG +/* LINTED - external libumem symbol */ +const char * +_umem_debug_init(void) +{ + return ("default,verbose"); /* $UMEM_DEBUG setting */ +} + +/* LINTED - external libumem symbol */ +const char * +_umem_logging_init(void) +{ + return ("fail,contents"); /* $UMEM_LOGGING setting */ +} +#endif diff --git a/usr/src/cmd/smbsrv/smbd/smbd_mlsvc_doorsvc.c b/usr/src/cmd/smbsrv/smbd/smbd_mlsvc_doorsvc.c new file mode 100755 index 000000000000..da39ac33c70d --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_mlsvc_doorsvc.c @@ -0,0 +1,423 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +static int smb_updoor_id = -1; +static pthread_mutex_t smb_winpipe_user_mutex; + +#define START_OUTDOOR_SIZE 65536 +#define MAX_INPIPE_LEN 65536 + +static smb_dr_user_ctx_t * +smb_user_ctx_mkabsolute(uint8_t *buf, uint32_t len) +{ + smb_dr_user_ctx_t *obj; + XDR xdrs; + + xdrmem_create(&xdrs, (const caddr_t)buf, len, XDR_DECODE); + obj = (smb_dr_user_ctx_t *)malloc(sizeof (smb_dr_user_ctx_t)); + if (!obj) { + xdr_destroy(&xdrs); + syslog(LOG_ERR, "smb_user_ctx_mkabsolute: resource shortage"); + return (NULL); + } + + bzero(obj, sizeof (smb_dr_user_ctx_t)); + if (!xdr_smb_dr_user_ctx_t(&xdrs, obj)) { + syslog(LOG_ERR, "smb_user_ctx_mkabsolute: XDR decode error"); + free(obj); + obj = NULL; + } + + xdr_destroy(&xdrs); + return (obj); +} + +static void +smb_user_ctx_free(smb_dr_user_ctx_t *user_ctx) +{ + if (user_ctx) { + xdr_free(xdr_smb_dr_user_ctx_t, (char *)user_ctx); + free(user_ctx); + } +} + +/* + * Function: smb_mlsvc_door_server + * + * This is the userland winpipe door entry point. + * Door arg data is a previously marshalled in to flat buffer + * that contains no pointers. This data is first unmarshalled into + * common structures. The data from the door *argp contains a header structure, + * mlsvc_door_hdr_t. Following are its members. + * thread_id - kernel thread id + * version number - possible use at a leter point + * call_type - rpc_transact, rpc_read or rpc_write + * length - max number of bytes that can be returned + * rpc_ctx - some rpc context info such as domain, user account, ... + * smb_pipe_t - pipeid, pipename, pipelen and data + * + * After converting the data, mlsvc_rpc_process is called + * The returning outpipe contains the relevant data to be + * returned to the windows client. + * + * Outgoing data must be marshalled again before returning. + */ + +/*ARGSUSED*/ +void +smb_mlsvc_door_server(void *cookie, char *argp, size_t arg_size, + door_desc_t *dd, uint_t n_desc) +{ + smb_pipe_t *inpipe = NULL; + smb_pipe_t *outpipe = NULL; + smb_pipe_t tmp_pipe; + smb_dr_user_ctx_t *user_ctx = NULL; + uchar_t *bufp; + int bplen = 0; + int total_pipelen; + mlsvc_door_hdr_t mdhin, mdhout; + int tbytes = 0; + int bytes_off = 0; + struct mlsvc_rpc_context *context; + uchar_t *obuf = NULL; + int current_out_len; + uint32_t adj_len; + char lfp[START_OUTDOOR_SIZE]; + + bufp = (uchar_t *)argp; + bcopy(bufp, &mdhin.md_tid, sizeof (uint64_t)); + bytes_off = sizeof (uint64_t); + bcopy(bufp+bytes_off, &mdhin.md_version, sizeof (uint16_t)); + bytes_off += sizeof (uint16_t); + bcopy(bufp+bytes_off, &mdhin.md_call_type, sizeof (uint16_t)); + bytes_off += sizeof (uint16_t); + bcopy(bufp+bytes_off, &mdhin.md_length, sizeof (uint32_t)); + bytes_off += sizeof (uint32_t); + bcopy(bufp+bytes_off, &mdhin.md_reserved, sizeof (uint64_t)); + bytes_off += sizeof (uint64_t); + + /* flush is a special case, just free the buffers and return */ + if (mdhin.md_call_type == SMB_RPC_FLUSH) { + bcopy(bufp + bytes_off, &tmp_pipe.sp_pipeid, sizeof (uint32_t)); + context = mlsvc_lookup_context(tmp_pipe.sp_pipeid); + if (!context) { + syslog(LOG_ERR, "mds: Cannot lookup mlsvc context"); + goto zero_exit; + } + if (context->inpipe) { + free(context->inpipe); + context->inpipe = NULL; + } + if (context->outpipe) { + free(context->outpipe); + context->outpipe = NULL; + } + mlsvc_rpc_release(tmp_pipe.sp_pipeid); + goto zero_exit; + } + + user_ctx = smb_user_ctx_mkabsolute(bufp + bytes_off, + arg_size - bytes_off); + if (!user_ctx) { + syslog(LOG_ERR, "mds: user_ctx_mkabsolute failed"); + goto zero_exit; + } + + tbytes += xdr_sizeof(xdr_smb_dr_user_ctx_t, user_ctx) + bytes_off; + + bzero(tmp_pipe.sp_pipename, SMB_MAX_PIPENAMELEN); + bcopy(bufp + tbytes, tmp_pipe.sp_pipename, SMB_MAX_PIPENAMELEN); + bplen = SMB_MAX_PIPENAMELEN; + bcopy(bufp + tbytes + bplen, &tmp_pipe.sp_pipeid, sizeof (uint32_t)); + bplen += sizeof (uint32_t); + bcopy(bufp + tbytes + bplen, &tmp_pipe.sp_datalen, sizeof (uint32_t)); + bplen += sizeof (uint32_t); + + total_pipelen = bplen + tmp_pipe.sp_datalen; + inpipe = malloc(total_pipelen); + if (! inpipe) { + syslog(LOG_ERR, "mds: resource shortage"); + goto zero_exit; + } + (void) strlcpy(inpipe->sp_pipename, tmp_pipe.sp_pipename, + SMB_MAX_PIPENAMELEN); + inpipe->sp_pipeid = tmp_pipe.sp_pipeid; + inpipe->sp_datalen = tmp_pipe.sp_datalen; + bcopy(bufp + tbytes + bplen, inpipe->sp_data, inpipe->sp_datalen); + + context = mlsvc_lookup_context(inpipe->sp_pipeid); + if (!context) { + syslog(LOG_ERR, "mds: Cannot lookup mlsvc context"); + goto zero_exit; + } + adj_len = mdhin.md_length; + /* + * If RPC_TRANSACT, save len, set cookie to 0 and store + * outpipe pointer into rpc_context. This will be used later + * by RPC_READ. See if we have pending writes. + * Clear the in context if we do. + */ + if ((mdhin.md_call_type == SMB_RPC_READ) && (context->inlen)) { + context->inpipe->sp_datalen = context->inlen; + if (context->inpipe->sp_pipeid != inpipe->sp_pipeid) { + syslog(LOG_DEBUG, "mds: RPC_READ pipeid mismatch !!"); + goto zero_exit; + } + (void) mlsvc_rpc_process(context->inpipe, &outpipe, user_ctx); + context->inlen = 0; + free(context->inpipe); + context->inpipe = NULL; + /* + * if no outpipe yet, initialize it + */ + if (!context->outpipe) { + context->outpipe = outpipe; + context->outcookie = 0; + context->outlen = outpipe->sp_datalen; + } + } + if (mdhin.md_call_type == SMB_RPC_TRANSACT) { + (void) mlsvc_rpc_process(inpipe, &outpipe, user_ctx); + /* + * init pipe context for subsequent calls + */ + context->outpipe = outpipe; + context->outcookie = 0; + context->outlen = outpipe->sp_datalen; + if (outpipe->sp_datalen < mdhin.md_length) + adj_len = outpipe->sp_datalen; + } + if (mdhin.md_call_type == SMB_RPC_WRITE) { + /* + * the first write we need to allocate + * the maximum inpipe len + */ + if (context->inlen == 0) { + context->inpipe = malloc(MAX_INPIPE_LEN); + } + if (! context->inpipe) { + syslog(LOG_ERR, "mds: ctx resource shortage"); + goto zero_exit; + } + bcopy(inpipe->sp_data, context->inpipe->sp_data + + context->inlen, inpipe->sp_datalen); + /* + * if we get another RPC_WRITE then we need to append + */ + context->inlen += inpipe->sp_datalen; + context->inpipe->sp_pipeid = inpipe->sp_pipeid; + context->inpipe->sp_datalen = context->inlen; + (void) strlcpy(context->inpipe->sp_pipename, + inpipe->sp_pipename, SMB_MAX_PIPENAMELEN); + goto zero_exit; + } + obuf = malloc(START_OUTDOOR_SIZE); + if (! obuf) { + syslog(LOG_ERR, "mds: obuf resource shortage"); + goto zero_exit; + } + /* + * check if this is the first transfer + * pipe and cookie management + */ + if (context->outcookie > 0) { + if (context->outcookie >= context->outlen) { + goto zero_exit; + } else { + if (adj_len < (context->outlen - context->outcookie)) { + bcopy(context->outpipe->sp_data + + context->outcookie, obuf, adj_len); + context->outcookie += adj_len; + current_out_len = adj_len; + } else { + bcopy(context->outpipe->sp_data + + context->outcookie, obuf, (context->outlen - + context->outcookie)); + current_out_len = context->outlen - + context->outcookie; + context->outcookie = 0; + free(context->outpipe); + context->outpipe = NULL; + } + } + outpipe = malloc(START_OUTDOOR_SIZE); + if (! outpipe) { + syslog(LOG_ERR, "mds: outpipe resource shortage"); + goto zero_exit; + } + } else { + if (adj_len < context->outlen) { + bcopy(context->outpipe->sp_data, obuf, adj_len); + context->outcookie += adj_len; + current_out_len = adj_len; + } else { + bcopy(context->outpipe->sp_data, obuf, context->outlen); + current_out_len = context->outlen; + context->outcookie = 0; + free(context->outpipe); + context->outpipe = NULL; + } + } + mdhout = mdhin; + mdhout.md_tid = (uint64_t)getpid(); /* user process pid */ + bytes_off = 0; + bcopy(&mdhout.md_tid, lfp, sizeof (uint64_t)); + bytes_off = sizeof (uint64_t); + bcopy(&mdhout.md_version, lfp + bytes_off, sizeof (uint16_t)); + bytes_off += sizeof (uint16_t); + bcopy(&mdhout.md_call_type, lfp + bytes_off, sizeof (uint16_t)); + bytes_off += sizeof (uint16_t); + bcopy(&adj_len, lfp + bytes_off, sizeof (uint32_t)); + bytes_off += sizeof (uint32_t); + bcopy(&mdhout.md_reserved, lfp + bytes_off, sizeof (uint64_t)); + bytes_off += sizeof (uint64_t); + tbytes = 0; + tbytes = bytes_off; + bzero(outpipe->sp_pipename, SMB_MAX_PIPENAMELEN); + (void) strcpy(outpipe->sp_pipename, "OUTPIPE"); + outpipe->sp_pipeid = inpipe->sp_pipeid; + bcopy(outpipe->sp_pipename, lfp + tbytes, SMB_MAX_PIPENAMELEN); + bplen = SMB_MAX_PIPENAMELEN; + bcopy(&(outpipe->sp_pipeid), lfp + tbytes + bplen, sizeof (uint32_t)); + bplen += sizeof (uint32_t); + + bcopy(&(current_out_len), lfp + tbytes + bplen, sizeof (uint32_t)); + bplen += sizeof (uint32_t); + + bcopy(obuf, lfp + tbytes + bplen, current_out_len); + tbytes += bplen + current_out_len; + smb_user_ctx_free(user_ctx); + free(obuf); + free(inpipe); + (void) door_return((char *)lfp, tbytes, NULL, 0); + /*NOTREACHED*/ + +zero_exit: + smb_user_ctx_free(user_ctx); + + if (obuf) + free(obuf); + if (inpipe) + free(inpipe); + + (void) door_return(0, 0, NULL, 0); +} + +/* + * smb_mlsvc_srv_start + * + * Start the mlsvc door service. + */ +int +smb_mlsvc_srv_start() +{ + int newfd; + + (void) pthread_mutex_lock(&smb_winpipe_user_mutex); + + if (smb_updoor_id != -1) { + (void) fprintf(stderr, "smb_mlsvc_srv_start: duplicate"); + (void) pthread_mutex_unlock(&smb_winpipe_user_mutex); + return (-1); + } + + errno = 0; + if ((smb_updoor_id = door_create(smb_mlsvc_door_server, 0, 0)) < 0) { + (void) fprintf(stderr, "smb_mlsvc_srv_start: door_create: %s", + strerror(errno)); + smb_updoor_id = -1; + (void) pthread_mutex_unlock(&smb_winpipe_user_mutex); + return (-1); + } + + (void) unlink(SMB_WINPIPE_DOOR_UP_PATH); + + if ((newfd = creat(SMB_WINPIPE_DOOR_UP_PATH, 0644)) < 0) { + (void) fprintf(stderr, "smb_mlsvc_srv_start: open: %s", + strerror(errno)); + (void) door_revoke(smb_updoor_id); + smb_updoor_id = -1; + (void) pthread_mutex_unlock(&smb_winpipe_user_mutex); + return (-1); + } + + (void) close(newfd); + (void) fdetach(SMB_WINPIPE_DOOR_UP_PATH); + + if (fattach(smb_updoor_id, SMB_WINPIPE_DOOR_UP_PATH) < 0) { + (void) fprintf(stderr, "smb_mlsvc_srv_start: fattach: %s", + strerror(errno)); + (void) door_revoke(smb_updoor_id); + smb_updoor_id = -1; + (void) pthread_mutex_unlock(&smb_winpipe_user_mutex); + return (-1); + } + + (void) pthread_mutex_unlock(&smb_winpipe_user_mutex); + return (0); +} + +/* + * smb_mlsvc_srv_stop + * + * Stop the mlsvc door service. + * We will eventually call this based on some signals + * No one calls this just yet, if the process dies all this stuff happens + * by default + */ +void +smb_mlsvc_srv_stop() +{ + (void) pthread_mutex_lock(&smb_winpipe_user_mutex); + + if (smb_updoor_id != -1) { + (void) fdetach(SMB_WINPIPE_DOOR_UP_PATH); + (void) door_revoke(smb_updoor_id); + smb_updoor_id = -1; + } + + (void) pthread_mutex_unlock(&smb_winpipe_user_mutex); +} diff --git a/usr/src/cmd/smbsrv/smbd/smbd_nicmon.c b/usr/src/cmd/smbsrv/smbd/smbd_nicmon.c new file mode 100644 index 000000000000..e13f7c457d76 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_nicmon.c @@ -0,0 +1,275 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This is the SMB NIC monitoring module. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static pthread_t smb_nicmon_thread; + +static void smb_setup_rtsock(int, int *); +static int smb_nics_changed(int); +static void *smb_nicmonitor(void *); +static int smb_setup_eventpipe(int *, int *); +static void smb_process_nic_change(); + +/* Use this to stop monitoring */ +static int eventpipe_write = -1; + +/* + * Start the nic monitor thread. + */ +int +smb_nicmon_start(void) +{ + int rc = 0; + + smb_nic_build_info(); + rc = pthread_create(&smb_nicmon_thread, NULL, smb_nicmonitor, 0); + if (rc != 0) { + syslog(LOG_ERR, "smb_nicmonitor: " + "NIC monitoring failed to start: %s", strerror(errno)); + return (rc); + } + return (rc); +} + +/* + * Stop the nic monitor. + */ +void +smb_nicmon_stop(void) +{ + uchar_t buf = 1; + + if (eventpipe_write < 0) + return; + + (void) write(eventpipe_write, &buf, sizeof (buf)); +} + +/* + * Setup routing socket for getting RTM messages. + */ +static void +smb_setup_rtsock(int af, int *s) +{ + int flags; + + *s = socket(PF_ROUTE, SOCK_RAW, af); + if (*s == -1) { + syslog(LOG_ERR, "smb_setup_rtsock: failed to " + "create routing socket"); + return; + } + if ((flags = fcntl(*s, F_GETFL, 0)) < 0) { + syslog(LOG_ERR, "smb_setup_rtsock: failed to fcntl F_GETFL"); + (void) close(*s); + *s = -1; + return; + } + if ((fcntl(*s, F_SETFL, flags | O_NONBLOCK)) < 0) { + syslog(LOG_ERR, "smb_setup_rtsock: failed to fcntl F_SETFL"); + (void) close(*s); + *s = -1; + return; + } +} + +static int +smb_nics_changed(int sock) +{ + int nbytes; + int64_t msg[2048 / 8]; + struct rt_msghdr *rtm; + int need_if_scan = 0; + + /* Read as many messages as possible and try to empty the sockets */ + for (;;) { + nbytes = read(sock, msg, sizeof (msg)); + if (nbytes <= 0) { + break; + } + rtm = (struct rt_msghdr *)msg; + if (rtm->rtm_version != RTM_VERSION) { + continue; + } + if (nbytes < rtm->rtm_msglen) { + syslog(LOG_DEBUG, "smb_nicmonitor: short read: %d " + "of %d", nbytes, rtm->rtm_msglen); + continue; + } + + switch (rtm->rtm_type) { + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_IFINFO: + need_if_scan = 1; + break; + default: + break; + } + } + + return (need_if_scan); +} + +/* + * Create pipe for signal delivery and set up signal handlers. + */ +static int +smb_setup_eventpipe(int *read_pipe, int *write_pipe) +{ + int fds[2]; + + if ((pipe(fds)) < 0) { + syslog(LOG_ERR, "smb_nicmonitor: failed to open pipe"); + return (1); + } + *read_pipe = fds[0]; + *write_pipe = fds[1]; + return (0); +} + +/* + * Call this to do stuff after ifs changed. + */ +static void +smb_process_nic_change() +{ + int ddns_enabled; + + smb_config_rdlock(); + ddns_enabled = smb_config_getyorn(SMB_CI_DYNDNS_ENABLE); + smb_config_unlock(); + + /* Clear rev zone before creating if list */ + if (ddns_enabled) { + if (dyndns_clear_rev_zone() != 0) { + syslog(LOG_ERR, "smb_nicmonitor: " + "failed to clear DNS reverse lookup zone"); + } + } + + /* re-initialize NIC table */ + smb_nic_build_info(); + + smb_netbios_name_reconfig(); + + if (ddns_enabled) { + if (dyndns_update() != 0) { + syslog(LOG_ERR, "smb_nicmonitor: " + "failed to update dynamic DNS"); + } + } +} + +/*ARGSUSED*/ +static void * +smb_nicmonitor(void *args) +{ + struct pollfd pollfds[2]; + int pollfd_num = 2; + int i, nic_changed; + /* AF_INET routing socket add AF_INET6 when we support IPv6 */ + static int rtsock_v4; + static int eventpipe_read = -1; + + /* + * Create the global routing socket. We use this to + * monitor changes in NIC interfaces. We are only interested + * in new inerface addition/deletion and change in UP/DOWN status. + */ + smb_setup_rtsock(AF_INET, &rtsock_v4); + if (rtsock_v4 == -1) { + syslog(LOG_ERR, "smb_nicmonitor: cannot open routing socket"); + return (NULL); + } + if (smb_setup_eventpipe(&eventpipe_read, &eventpipe_write) != 0) { + syslog(LOG_ERR, "smb_nicmonitor: cannot open event pipes"); + return (NULL); + } + + /* + * Keep listening for activity on any of the sockets. + */ + for (;;) { + nic_changed = 0; + pollfds[0].fd = rtsock_v4; + pollfds[0].events = POLLIN; + pollfds[1].fd = eventpipe_read; + pollfds[1].events = POLLIN; + if (poll(pollfds, pollfd_num, -1) < 0) { + if (errno == EINTR) + continue; + syslog(LOG_ERR, "smb_nicmonitor: " + "poll failed with errno %d", errno); + break; + } + for (i = 0; i < pollfd_num; i++) { + if ((pollfds[i].fd < 0) || + !(pollfds[i].revents & POLLIN)) + continue; + if (pollfds[i].fd == rtsock_v4) + nic_changed = smb_nics_changed(rtsock_v4); + if (pollfds[i].fd == eventpipe_read) + goto done; + } + + /* + * If anything changed do refresh our + * nic list and other configs. + */ + if (nic_changed) + smb_process_nic_change(); + } +done: + /* Close sockets */ + (void) close(rtsock_v4); + (void) close(eventpipe_read); + (void) close(eventpipe_write); + eventpipe_write = -1; + return (NULL); +} diff --git a/usr/src/cmd/smbsrv/smbd/smbd_share_doorsvc.c b/usr/src/cmd/smbsrv/smbd/smbd_share_doorsvc.c new file mode 100644 index 000000000000..342ac7b4e5c5 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_share_doorsvc.c @@ -0,0 +1,371 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * LanMan share door server + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +static smb_kmod_cfg_t smb_kcfg; + +static int smb_lmshrd_fildes = -1; +static pthread_mutex_t smb_lmshrd_srv_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* forward declaration */ +static void smb_lmshrd_srv_door(void *cookie, char *ptr, size_t size, + door_desc_t *dp, uint_t n_desc); +static int smb_lmshrd_srv_check(int opcode, char *sharename); + +/* + * smb_lmshrd_srv_start + * + * Start the LanMan share door service. + * Returns 0 on success. Otherwise, -1. + */ +int +smb_lmshrd_srv_start() +{ + int newfd; + + (void) pthread_mutex_lock(&smb_lmshrd_srv_mutex); + + if (smb_lmshrd_fildes != -1) { + syslog(LOG_ERR, "smb_lmshrd_srv_start: duplicate"); + (void) pthread_mutex_unlock(&smb_lmshrd_srv_mutex); + return (0); + } + + if ((smb_lmshrd_fildes = door_create(smb_lmshrd_srv_door, + LMSHR_DOOR_COOKIE, (DOOR_UNREF | DOOR_REFUSE_DESC))) < 0) { + syslog(LOG_ERR, "smb_lmshrd_srv_start: door_create: %s", + strerror(errno)); + (void) pthread_mutex_unlock(&smb_lmshrd_srv_mutex); + return (-1); + } + + (void) unlink(LMSHR_DOOR_NAME); + + if ((newfd = creat(LMSHR_DOOR_NAME, 0644)) < 0) { + syslog(LOG_ERR, "smb_lmshrd_srv_start: open: %s", + strerror(errno)); + (void) door_revoke(smb_lmshrd_fildes); + smb_lmshrd_fildes = -1; + (void) pthread_mutex_unlock(&smb_lmshrd_srv_mutex); + return (-1); + } + + (void) close(newfd); + (void) fdetach(LMSHR_DOOR_NAME); + + if (fattach(smb_lmshrd_fildes, LMSHR_DOOR_NAME) < 0) { + syslog(LOG_ERR, "smb_lmshrd_srv_start: fattach: %s", + strerror(errno)); + (void) door_revoke(smb_lmshrd_fildes); + smb_lmshrd_fildes = -1; + (void) pthread_mutex_unlock(&smb_lmshrd_srv_mutex); + return (-1); + } + + (void) pthread_mutex_unlock(&smb_lmshrd_srv_mutex); + return (0); +} + + +/* + * smb_lmshrd_srv_stop + * + * Stop the LanMan share door service. + */ +void +smb_lmshrd_srv_stop(void) +{ + (void) pthread_mutex_lock(&smb_lmshrd_srv_mutex); + + if (smb_lmshrd_fildes != -1) { + (void) fdetach(LMSHR_DOOR_NAME); + (void) door_revoke(smb_lmshrd_fildes); + smb_lmshrd_fildes = -1; + } + + (void) pthread_mutex_unlock(&smb_lmshrd_srv_mutex); +} + + +/* + * smb_lmshrd_srv_door + * + * This function with which the LMSHARE door is associated + * will invoke the appropriate CIFS share management function + * based on the request type of the door call. + */ +/*ARGSUSED*/ +void +smb_lmshrd_srv_door(void *cookie, char *ptr, size_t size, door_desc_t *dp, + uint_t n_desc) +{ + DWORD rc; + int req_type, mode, rc2; + char buf[LMSHR_DOOR_SIZE]; + unsigned int used; + smb_dr_ctx_t *dec_ctx = smb_dr_decode_start(ptr, size); + smb_dr_ctx_t *enc_ctx = smb_dr_encode_start(buf, sizeof (buf)); + unsigned int dec_status; + unsigned int enc_status; + char *sharename, *sharename2; + lmshare_info_t lmshr_info; + lmshare_info_t *lmshr_infop; + lmshare_iterator_t *lmshr_iter; + int offset; + lmshare_list_t lmshr_list; + + req_type = smb_dr_get_uint32(dec_ctx); + + switch (req_type) { + case LMSHR_DOOR_OPEN_ITERATOR: + mode = smb_dr_get_int32(dec_ctx); + + if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) + goto decode_error; + + lmshr_iter = lmshare_open_iterator(mode); + smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS); + smb_dr_put_lmshr_iterator(enc_ctx, + (uint64_t)(uintptr_t)lmshr_iter); + + break; + + case LMSHR_DOOR_CLOSE_ITERATOR: + lmshr_iter = (lmshare_iterator_t *)(uintptr_t) + smb_dr_get_lmshr_iterator(dec_ctx); + if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) + goto decode_error; + + lmshare_close_iterator(lmshr_iter); + smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS); + break; + + case LMSHR_DOOR_ITERATE: + lmshr_iter = (lmshare_iterator_t *)(uintptr_t) + smb_dr_get_lmshr_iterator(dec_ctx); + if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) + goto decode_error; + + lmshr_infop = lmshare_iterate(lmshr_iter); + smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS); + smb_dr_put_lmshare(enc_ctx, lmshr_infop); + break; + + case LMSHR_DOOR_NUM_SHARES: + if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) + goto decode_error; + + rc = lmshare_num_shares(); + smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS); + smb_dr_put_uint32(enc_ctx, rc); + break; + + case LMSHR_DOOR_DELETE: + sharename = smb_dr_get_string(dec_ctx); + + if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) { + smb_dr_free_string(sharename); + goto decode_error; + } + + rc = lmshare_delete(sharename, 0); + smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS); + smb_dr_put_uint32(enc_ctx, rc); + smb_dr_free_string(sharename); + break; + + case LMSHR_DOOR_RENAME: + sharename = smb_dr_get_string(dec_ctx); + sharename2 = smb_dr_get_string(dec_ctx); + + if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) { + smb_dr_free_string(sharename); + smb_dr_free_string(sharename2); + goto decode_error; + } + + rc = lmshare_rename(sharename, sharename2, 0); + smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS); + smb_dr_put_uint32(enc_ctx, rc); + smb_dr_free_string(sharename); + smb_dr_free_string(sharename2); + break; + + case LMSHR_DOOR_GETINFO: + sharename = smb_dr_get_string(dec_ctx); + if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) { + smb_dr_free_string(sharename); + goto decode_error; + } + + rc = lmshare_getinfo(sharename, &lmshr_info); + smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS); + smb_dr_put_uint32(enc_ctx, rc); + smb_dr_put_lmshare(enc_ctx, &lmshr_info); + smb_dr_free_string(sharename); + break; + + case LMSHR_DOOR_ADD: + smb_dr_get_lmshare(dec_ctx, &lmshr_info); + if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) + goto decode_error; + + rc = lmshare_add(&lmshr_info, 0); + smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS); + smb_dr_put_uint32(enc_ctx, rc); + smb_dr_put_lmshare(enc_ctx, &lmshr_info); + break; + + case LMSHR_DOOR_SETINFO: + smb_dr_get_lmshare(dec_ctx, &lmshr_info); + if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) + goto decode_error; + + rc = lmshare_setinfo(&lmshr_info, 0); + smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS); + smb_dr_put_uint32(enc_ctx, rc); + break; + + case LMSHR_DOOR_EXISTS: + case LMSHR_DOOR_IS_SPECIAL: + case LMSHR_DOOR_IS_RESTRICTED: + case LMSHR_DOOR_IS_ADMIN: + case LMSHR_DOOR_IS_VALID: + case LMSHR_DOOR_IS_DIR: + sharename = smb_dr_get_string(dec_ctx); + if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) { + smb_dr_free_string(sharename); + goto decode_error; + } + + rc2 = smb_lmshrd_srv_check(req_type, sharename); + smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS); + smb_dr_put_uint32(enc_ctx, rc2); + smb_dr_free_string(sharename); + break; + + case LMSHR_DOOR_LIST: + offset = smb_dr_get_int32(dec_ctx); + if ((dec_status = smb_dr_decode_finish(dec_ctx)) != 0) + goto decode_error; + + rc = lmshare_list(offset, &lmshr_list); + smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS); + smb_dr_put_uint32(enc_ctx, rc); + smb_dr_put_lmshr_list(enc_ctx, &lmshr_list); + break; + + case SMB_GET_KCONFIG: + smb_load_kconfig(&smb_kcfg); + smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_SUCCESS); + smb_dr_put_kconfig(enc_ctx, &smb_kcfg); + break; + + default: + goto decode_error; + } + + if ((enc_status = smb_dr_encode_finish(enc_ctx, &used)) != 0) + goto encode_error; + + (void) door_return(buf, used, NULL, 0); + + return; + +decode_error: + smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_ERROR); + smb_dr_put_uint32(enc_ctx, dec_status); + (void) smb_dr_encode_finish(enc_ctx, &used); + (void) door_return(buf, used, NULL, 0); + return; + +encode_error: + enc_ctx = smb_dr_encode_start(buf, sizeof (buf)); + smb_dr_put_int32(enc_ctx, LMSHR_DOOR_SRV_ERROR); + smb_dr_put_uint32(enc_ctx, enc_status); + (void) smb_dr_encode_finish(enc_ctx, &used); + (void) door_return(buf, used, NULL, 0); +} + +/* + * smb_lmshrd_srv_check + * + * Depending upon the opcode, this function will + * either check the existence of a share/dir or + * the the type of the specified share. + */ +static int +smb_lmshrd_srv_check(int opcode, char *sharename) +{ + int rc; + + switch (opcode) { + case LMSHR_DOOR_EXISTS: + rc = lmshare_exists(sharename); + break; + + case LMSHR_DOOR_IS_SPECIAL: + rc = lmshare_is_special(sharename); + break; + + case LMSHR_DOOR_IS_RESTRICTED: + rc = lmshare_is_restricted(sharename); + break; + + case LMSHR_DOOR_IS_ADMIN: + rc = lmshare_is_admin(sharename); + break; + + case LMSHR_DOOR_IS_VALID: + rc = lmshare_is_valid(sharename); + break; + + case LMSHR_DOOR_IS_DIR: + rc = lmshare_is_dir(sharename); + } + + return (rc); +} diff --git a/usr/src/cmd/smbsrv/smbstat/Makefile b/usr/src/cmd/smbsrv/smbstat/Makefile new file mode 100644 index 000000000000..1bb8b73737c0 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbstat/Makefile @@ -0,0 +1,39 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +PROG= smbstat +SRCS= smbstat.c + +include ../../Makefile.cmd + +OBJS= $(SRCS:%.c=%.o) + +include ../Makefile.smbsrv.defs + +LDLIBS = -lumem -lkstat +include ../Makefile.smbsrv.targ +include ../../Makefile.targ diff --git a/usr/src/cmd/smbsrv/smbstat/smbstat.c b/usr/src/cmd/smbsrv/smbstat/smbstat.c new file mode 100644 index 000000000000..b2c4bab4a7a7 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbstat/smbstat.c @@ -0,0 +1,283 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * smbstat: Server Message Block File System statistics + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static kstat_ctl_t *kc; /* libkstat cookie */ +static kstat_t *smb_info; +static kstat_t *smb_dispatch; +static kstat_t *ksmb_kstat; + +static int get_smbinfo_stat(void); +static int get_smbdispatch_stat(void); +static void smbstat_setup(void); +static void smbstat_smb_info_print(); +static void smbstat_smb_dispatch_print(); +static void smbstat_print(const char *, kstat_t *, int); +static int smbstat_width(kstat_t *, int); +static void smbstat_fail(int, char *, ...); +static kid_t smbstat_kstat_read(kstat_ctl_t *, kstat_t *, void *); +static void smbstat_usage(void); + +#define MAX_COLUMNS 80 + +int +main(int argc, char *argv[]) +{ + int c; + int iflag = 0; /* smb_info stats */ + int dflag = 0; /* smb_dispatch_all stats */ + + if (getzoneid() != GLOBAL_ZONEID) { + (void) fprintf(stderr, + gettext("%s: Cannot execute in non-global zone.\n"), + argv[0]); + return (0); + } + + if (is_system_labeled()) { + (void) fprintf(stderr, + gettext("%s: Trusted Extensions not supported.\n"), + argv[0]); + return (0); + } + + while ((c = getopt(argc, argv, "id")) != EOF) { + switch (c) { + case 'i': + iflag++; + break; + case 'd': + dflag++; + break; + case '?': + default: + smbstat_usage(); + } + } + + if ((argc - optind) > 0) { + smbstat_usage(); + } + + smbstat_setup(); + + if (iflag) { + smbstat_smb_info_print(); + } else if (dflag) { + smbstat_smb_dispatch_print(); + } else { + smbstat_smb_info_print(); + smbstat_smb_dispatch_print(); + } + + (void) kstat_close(kc); + free(ksmb_kstat); + return (0); +} + + +static int +get_smbinfo_stat(void) +{ + (void) smbstat_kstat_read(kc, smb_info, NULL); + return (smbstat_width(smb_info, 0)); +} + +static int +get_smbdispatch_stat(void) +{ + (void) smbstat_kstat_read(kc, smb_dispatch, NULL); + return (smbstat_width(smb_dispatch, 0)); +} + +static void +smbstat_smb_info_print() +{ + int field_width; + + field_width = get_smbinfo_stat(); + if (field_width == 0) + return; + + smbstat_print(gettext("\nSMB Info:\n"), smb_info, field_width); +} + +static void +smbstat_smb_dispatch_print() +{ + int field_width; + + field_width = get_smbdispatch_stat(); + if (field_width == 0) + return; + + smbstat_print(gettext("\nAll dispatched SMB requests statistics:\n"), + smb_dispatch, field_width); +} + +static void +smbstat_setup(void) +{ + if ((kc = kstat_open()) == NULL) + smbstat_fail(1, gettext("kstat_open(): can't open /dev/kstat")); + + if ((ksmb_kstat = malloc(sizeof (kstat_t))) == NULL) { + (void) kstat_close(kc); + smbstat_fail(1, gettext("Out of memory")); + } + + smb_info = kstat_lookup(kc, "smb", 0, "smb_info"); + smb_dispatch = kstat_lookup(kc, "smb", 0, "smb_dispatch_all"); + if ((smb_info == NULL) || (smb_dispatch == NULL)) + smbstat_fail(0, gettext("kstat lookups failed for smb. " + "Your kernel module may not be loaded\n")); +} + +static int +smbstat_width(kstat_t *req, int field_width) +{ + int i, nreq, len; + char fixlen[128]; + kstat_named_t *knp; + + knp = KSTAT_NAMED_PTR(req); + nreq = req->ks_ndata; + + for (i = 0; i < nreq; i++) { + len = strlen(knp[i].name) + 1; + if (field_width < len) + field_width = len; + (void) sprintf(fixlen, "%" PRIu64, knp[i].value.ui64); + len = strlen(fixlen) + 1; + if (field_width < len) + field_width = len; + } + return (field_width); +} + +static void +smbstat_print(const char *title_string, kstat_t *req, int field_width) +{ + int i, j, nreq, ncolumns; + char fixlen[128]; + kstat_named_t *knp; + + if (req == NULL) + return; + + if (field_width == 0) + return; + + (void) printf("%s\n", title_string); + ncolumns = (MAX_COLUMNS -1)/field_width; + + knp = KSTAT_NAMED_PTR(req); + nreq = req->ks_ndata; + + for (i = 0; i < nreq; i += ncolumns) { + /* prints out the titles of the columns */ + for (j = i; j < MIN(i + ncolumns, nreq); j++) { + (void) printf("%-*s", field_width, knp[j].name); + } + (void) printf("\n"); + /* prints out the stat numbers */ + for (j = i; j < MIN(i + ncolumns, nreq); j++) { + (void) sprintf(fixlen, "%" PRIu64 " ", + knp[j].value.ui64); + (void) printf("%-*s", field_width, fixlen); + } + (void) printf("\n"); + + } +} + +static void +smbstat_usage(void) +{ + (void) fprintf(stderr, gettext("Usage: smbstat [-id]\n")); + exit(1); +} + +static void +smbstat_fail(int do_perror, char *message, ...) +{ + va_list args; + + va_start(args, message); + (void) fprintf(stderr, gettext("smbstat: ")); + /* LINTED E_SEC_PRINTF_VAR_FMT */ + (void) vfprintf(stderr, message, args); + va_end(args); + if (do_perror) + (void) fprintf(stderr, ": %s", strerror(errno)); + (void) fprintf(stderr, "\n"); + exit(1); +} + +static kid_t +smbstat_kstat_read(kstat_ctl_t *kc, kstat_t *ksp, void *data) +{ + kid_t kstat_chain_id = kstat_read(kc, ksp, data); + + if (kstat_chain_id == -1) + smbstat_fail(1, gettext("kstat_read('%s') failed"), + ksp->ks_name); + return (kstat_chain_id); +} + +/* + * Enable libumem debugging by default on DEBUG builds. + */ +#ifdef DEBUG +/* LINTED - external libumem symbol */ +const char * +_umem_debug_init(void) +{ + return ("default,verbose"); /* $UMEM_DEBUG setting */ +} + +/* LINTED - external libumem symbol */ +const char * +_umem_logging_init(void) +{ + return ("fail,contents"); /* $UMEM_LOGGING setting */ +} +#endif diff --git a/usr/src/cmd/svc/configd/rc_node.c b/usr/src/cmd/svc/configd/rc_node.c index 4ef49a0b051f..bed6c9ed3f91 100644 --- a/usr/src/cmd/svc/configd/rc_node.c +++ b/usr/src/cmd/svc/configd/rc_node.c @@ -900,7 +900,7 @@ perm_add_enabling(permcheck_t *pcp, const char *auth) * perm_granted() returns 1 if the current door caller has one of the enabling * authorizations in pcp, 0 if it doesn't, and -1 if an error (usually lack of * memory) occurs. check_auth_list() checks an RBAC_AUTH_SEP-separated list - * of authorizations for existance in pcp, and check_prof_list() checks the + * of authorizations for existence in pcp, and check_prof_list() checks the * authorizations granted to an RBAC_AUTH_SEP-separated list of profiles. */ static int diff --git a/usr/src/cmd/tar/Makefile b/usr/src/cmd/tar/Makefile index a8053fb33d71..8d8157c5c589 100644 --- a/usr/src/cmd/tar/Makefile +++ b/usr/src/cmd/tar/Makefile @@ -40,7 +40,7 @@ DCFILE= $(PROG).dc LINTFLAGS += -u LAZYLIBS = $(ZLAZYLOAD) -ltsol $(ZNOLAZYLOAD) lint := LAZYLIBS = -ltsol -LDLIBS += -lsec $(LAZYLIBS) +LDLIBS += -lsec -lcmdutils -lnvpair $(LAZYLIBS) CFLAGS += $(CCVERBOSE) diff --git a/usr/src/cmd/tar/tar.c b/usr/src/cmd/tar/tar.c index d68c4e0e347a..80e72adb3534 100644 --- a/usr/src/cmd/tar/tar.c +++ b/usr/src/cmd/tar/tar.c @@ -61,26 +61,32 @@ #include #include #include -#include #include #include #include #include #include #include +#include +#include #include -#include "getresponse.h" +#include +#include #if defined(__SunOS_5_6) || defined(__SunOS_5_7) extern int defcntl(); #endif -#include +#if defined(_PC_SATTR_ENABLED) +#include +#include +#endif /* Trusted Extensions */ #include #include #include +#include "getresponse.h" /* * Source compatibility */ @@ -180,6 +186,14 @@ int utimes(const char *path, const struct timeval timeval_ptr[]); #define PUT_AS_LINK 1 #define PUT_NOTAS_LINK 0 +#ifndef VIEW_READONLY +#define VIEW_READONLY "SUNWattr_ro" +#endif + +#ifndef VIEW_READWRITE +#define VIEW_READWRITE "SUNWattr_rw" +#endif + #if _FILE_OFFSET_BITS == 64 #define FMT_off_t "lld" #define FMT_off_t_o "llo" @@ -199,6 +213,31 @@ struct sec_attr { char attr_info[1]; } *attr; +#if defined(O_XATTR) +typedef enum { + ATTR_OK, + ATTR_SKIP, + ATTR_CHDIR_ERR, + ATTR_OPEN_ERR, + ATTR_XATTR_ERR, + ATTR_SATTR_ERR +} attr_status_t; +#endif + +#if defined(O_XATTR) +typedef enum { + ARC_CREATE, + ARC_RESTORE +} arc_action_t; +#endif + +typedef struct attr_data { + char *attr_parent; + char *attr_path; + int attr_parentfd; + int attr_rw_sysattr; +} attr_data_t; + /* * * Tar has been changed to support extended attributes. @@ -306,7 +345,7 @@ struct sec_attr { static struct xattr_hdr *xattrhead; static struct xattr_buf *xattrp; static struct xattr_buf *xattr_linkp; /* pointer to link info, if any */ -static char *xattraname; /* attribute name */ +static char *xattrapath; /* attribute name */ static char *xattr_linkaname; /* attribute attribute is linked to */ static char Hiddendir; /* are we processing hidden xattr dir */ static char xattrbadhead; @@ -394,7 +433,7 @@ static void assert_string(char *s, char *msg); static int istape(int fd, int type); static void backtape(void); static void build_table(struct file_list *table[], char *file); -static void check_prefix(char **namep, char **dirp, char **compp); +static int check_prefix(char **namep, char **dirp, char **compp); static void closevol(void); static void copy(void *dst, void *src); static int convtoreg(off_t); @@ -421,7 +460,7 @@ static void newvol(void); static void passtape(void); static void putempty(blkcnt_t n); static int putfile(char *longname, char *shortname, char *parent, - int filetype, int lev, int symlink_lev); + attr_data_t *attrinfo, int filetype, int lev, int symlink_lev); static void readtape(char *buffer); static void seekdisk(blkcnt_t blocks); static void setPathTimes(int dirfd, char *path, timestruc_t modTime); @@ -429,8 +468,8 @@ static void splitfile(char *longname, int ifd, char *name, char *prefix, int filetype); static void tomodes(struct stat *sp); static void usage(void); -static void xblocks(off_t bytes, int ofile); -static void xsfile(int ofd); +static int xblocks(int issysattr, off_t bytes, int ofile); +static int xsfile(int issysattr, int ofd); static void resugname(int dirfd, char *name, int symflag); static int bcheck(char *bstr); static int checkdir(char *name); @@ -466,7 +505,8 @@ static char *getname(gid_t); static char *getgroup(gid_t); static int checkf(char *name, int mode, int howmuch); static int writetbuf(char *buffer, int n); -static int wantit(char *argv[], char **namep, char **dirp, char **comp); +static int wantit(char *argv[], char **namep, char **dirp, char **comp, + attr_data_t **attrinfo); static void append_ext_attr(char *shortname, char **secinfo, int *len); static int get_xdata(void); static void gen_num(const char *keyword, const u_longlong_t number); @@ -481,17 +521,18 @@ static int utf8_local(char *option, char **Xhdr_ptrptr, char *target, static int local_utf8(char **Xhdr_ptrptr, char *target, const char *src, iconv_t iconv_cd, int xhdrflg, int max_val); static int c_utf8(char *target, const char *source); -static int getstat(int dirfd, char *longname, char *shortname); -static void xattrs_put(char *, char *, char *); +static int getstat(int dirfd, char *longname, char *shortname, + char *attrparent); +static void xattrs_put(char *, char *, char *, char *); static void prepare_xattr(char **, char *, char *, char, struct linkbuf *, int *); -static int put_link(char *name, char *longname, char *component, char *prefix, - int filetype, char typeflag); +static int put_link(char *name, char *longname, char *component, + char *longattrname, char *prefix, int filetype, char typeflag); static int put_extra_attributes(char *longname, char *shortname, - char *prefix, int filetype, char typeflag); -static int put_xattr_hdr(char *longname, char *shortname, char *prefix, - int typeflag, int filetype, struct linkbuf *lp); -static int read_xattr_hdr(); + char *longattrname, char *prefix, int filetype, char typeflag); +static int put_xattr_hdr(char *longname, char *shortname, char *longattrname, + char *prefix, int typeflag, int filetype, struct linkbuf *lp); +static int read_xattr_hdr(attr_data_t **attrinfo); /* Trusted Extensions */ #define AUTO_ZONE "/zone" @@ -503,12 +544,14 @@ static int rebuild_lk_comp_path(char *str, char **namep); static void get_parent(char *path, char *dir); static char *get_component(char *path); -static int retry_attrdir_open(char *name); +static int retry_open_attr(int pdirfd, int cwd, char *dirp, char *pattr, + char *name, int oflag, mode_t mode); static char *skipslashes(char *string, char *start); static void chop_endslashes(char *path); static struct stat stbuf; +static char *myname; static int checkflag = 0; #ifdef _iBCS2 static int Fileflag; @@ -523,6 +566,7 @@ static int bflag, kflag, Aflag; static int Pflag; /* POSIX conformant archive */ static int Eflag; /* Allow files greater than 8GB */ static int atflag; /* traverse extended attributes */ +static int saflag; /* traverse extended sys attributes */ static int Dflag; /* Data change flag */ /* Trusted Extensions */ static int Tflag; /* Trusted Extensions attr flags */ @@ -657,6 +701,11 @@ main(int argc, char *argv[]) usage(); tfile = NULL; + if ((myname = strdup(argv[0])) == NULL) { + (void) fprintf(stderr, gettext( + "tar: cannot allocate program name\n")); + exit(1); + } if (init_yes() < 0) { (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES), @@ -737,7 +786,12 @@ main(int argc, char *argv[]) case '@': atflag++; break; -#endif +#endif /* O_XATTR */ +#if defined(_PC_SATTR_ENABLED) + case '/': + saflag++; + break; +#endif /* _PC_SATTR_ENABLED */ case 'u': uflag++; /* moved code after signals caught */ rflag++; @@ -1047,10 +1101,14 @@ usage(void) if (sysv3_env) { (void) fprintf(stderr, gettext( #if defined(O_XATTR) +#if defined(_PC_SATTR_ENABLED) + "Usage: tar {c|r|t|u|x}[BDeEhilmnopPqTvw@/[0-7]][bfFk][X...] " +#else "Usage: tar {c|r|t|u|x}[BDeEhilmnopPqTvw@[0-7]][bfFk][X...] " +#endif /* _PC_SATTR_ENABLED */ #else "Usage: tar {c|r|t|u|x}[BDeEhilmnopPqTvw[0-7]][bfFk][X...] " -#endif +#endif /* O_XATTR */ "[blocksize] [tarfile] [filename] [size] [exclude-file...] " "{file | -I include-file | -C directory file}...\n")); } else @@ -1058,10 +1116,14 @@ usage(void) { (void) fprintf(stderr, gettext( #if defined(O_XATTR) +#if defined(_PC_SATTR_ENABLED) + "Usage: tar {c|r|t|u|x}[BDeEFhilmnopPqTvw@/[0-7]][bfk][X...] " +#else "Usage: tar {c|r|t|u|x}[BDeEFhilmnopPqTvw@[0-7]][bfk][X...] " +#endif /* _PC_SATTR_ENABLED */ #else "Usage: tar {c|r|t|u|x}[BDeEFhilmnopPqTvw[0-7]][bfk][X...] " -#endif +#endif /* O_XATTR */ "[blocksize] [tarfile] [size] [exclude-file...] " "{file | -I include-file | -C directory file}...\n")); } @@ -1196,7 +1258,7 @@ dorep(char *argv[]) if (chdir(*++argv) < 0) vperror(0, gettext( - "can't change directories to %s"), *argv); + "can't change directories to %s"), *argv); else (void) getcwd(wdir, (sizeof (wdir))); argv++; @@ -1235,7 +1297,7 @@ dorep(char *argv[]) *cp2 = '\0'; if (chdir(file) < 0) { vperror(0, gettext( - "can't change directories to %s"), file); + "can't change directories to %s"), file); continue; } *cp2 = '/'; @@ -1243,13 +1305,15 @@ dorep(char *argv[]) } parent = getcwd(tempdir, (sizeof (tempdir))); - archtype = putfile(file, cp2, parent, NORMAL_FILE, + + archtype = putfile(file, cp2, parent, NULL, NORMAL_FILE, LEV0, SYMLINK_LEV0); #if defined(O_XATTR) if (!exitflag) { - if (atflag && archtype == PUT_NOTAS_LINK) { - xattrs_put(file, cp2, parent); + if ((atflag || saflag) && + (archtype == PUT_NOTAS_LINK)) { + xattrs_put(file, cp2, parent, NULL); } } #endif @@ -1447,7 +1511,11 @@ getdir(void) xattr_linkp = NULL; xattrhead = NULL; } else { - if (xattraname[0] == '.' && xattraname[1] == '\0' && + char *aname = basename(xattrapath); + size_t xindex = aname - xattrapath; + + if (xattrapath[xindex] == '.' && + xattrapath[xindex + 1] == '\0' && xattrp->h_typeflag == '5') { Hiddendir = 1; sp->st_mode = @@ -1495,9 +1563,102 @@ passtape(void) readtape(buf); } +#if defined(O_XATTR) +static int +is_sysattr(char *name) +{ + return ((strcmp(name, VIEW_READONLY) == 0) || + (strcmp(name, VIEW_READWRITE) == 0)); +} +#endif + +#if defined(O_XATTR) +/* + * Verify the attribute, attrname, is an attribute we want to restore. + * Never restore read-only system attribute files. Only restore read-write + * system attributes files when -/ was specified, and only traverse into + * the 2nd level attribute directory containing only system attributes if + * -@ was specified. This keeps us from archiving + * / + * when -/ was specified without -@. + * + * attrname - attribute file name + * attrparent - attribute's parent name within the base file's attribute + * directory hierarchy + */ +static attr_status_t +verify_attr(char *attrname, char *attrparent, int arc_rwsysattr, + int *rw_sysattr) +{ +#if defined(_PC_SATTR_ENABLED) + int attr_supported; + + /* Never restore read-only system attribute files */ + if ((attr_supported = sysattr_type(attrname)) == _RO_SATTR) { + *rw_sysattr = 0; + return (ATTR_SKIP); + } else { + *rw_sysattr = (attr_supported == _RW_SATTR); + } +#else + /* + * Only need to check if this attribute is an extended system + * attribute. + */ + if (*rw_sysattr = is_sysattr(attrname)) { + return (ATTR_SKIP); + } else { + return (ATTR_OK); + } +#endif /* _PC_SATTR_ENABLED */ + + /* + * If the extended system attribute file is specified with the + * arc_rwsysattr flag, as being transient (default extended + * attributes), then don't archive it. + */ + if (*rw_sysattr && !arc_rwsysattr) { + return (ATTR_SKIP); + } + + /* + * Only restore read-write system attribute files + * when -/ was specified. Only restore extended + * attributes when -@ was specified. + */ + if (atflag) { + if (!saflag) { + /* + * Only archive/restore the hidden directory "." if + * we're processing the top level hidden attribute + * directory. We don't want to process the + * hidden attribute directory of the attribute + * directory that contains only extended system + * attributes. + */ + if (*rw_sysattr || (Hiddendir && + (attrparent != NULL))) { + return (ATTR_SKIP); + } + } + } else if (saflag) { + /* + * Only archive/restore read-write extended system attribute + * files of the base file. + */ + if (!*rw_sysattr || (attrparent != NULL)) { + return (ATTR_SKIP); + } + } else { + return (ATTR_SKIP); + } + + return (ATTR_OK); +} +#endif static int -putfile(char *longname, char *shortname, char *parent, +putfile(char *longname, char *shortname, char *parent, attr_data_t *attrinfo, int filetype, int lev, int symlink_lev) { int infile = -1; /* deliberately invalid */ @@ -1509,6 +1670,8 @@ putfile(char *longname, char *shortname, char *parent, char filetmp[PATH_MAX + 1]; char *cp; char *name; + char *attrparent = NULL; + char *longattrname = NULL; struct dirent *dp; DIR *dirp; int i; @@ -1517,6 +1680,7 @@ putfile(char *longname, char *shortname, char *parent, int dirfd = -1; int rc = PUT_NOTAS_LINK; int archtype = 0; + int rw_sysattr = 0; char newparent[PATH_MAX + MAXNAMLEN + 1]; char *prefix = ""; char *tmpbuf; @@ -1533,28 +1697,24 @@ putfile(char *longname, char *shortname, char *parent, xhdr_flgs = 0; if (filetype == XATTR_FILE) { - dirfd = attropen(get_component(longname), ".", O_RDONLY); + attrparent = attrinfo->attr_parent; + longattrname = attrinfo->attr_path; + dirfd = attrinfo->attr_parentfd; } else { dirfd = open(".", O_RDONLY); } if (dirfd == -1) { (void) fprintf(stderr, gettext( - "tar: unable to open%sdirectory %s\n"), + "tar: unable to open%sdirectory %s%s%s%s\n"), (filetype == XATTR_FILE) ? gettext(" attribute ") : " ", + (attrparent == NULL) ? "" : gettext("of attribute "), + (attrparent == NULL) ? "" : attrparent, + (attrparent == NULL) ? "" : gettext(" of "), (filetype == XATTR_FILE) ? longname : parent); goto out; } - if (filetype == XATTR_FILE) { - if (fchdir(dirfd) < 0) { - (void) fprintf(stderr, gettext( - "tar: unable to fchdir into attribute directory" - " of file %s\n"), longname); - goto out; - } - } - if (lev > MAXLEV) { (void) fprintf(stderr, gettext("tar: directory nesting too deep, %s not dumped\n"), @@ -1562,7 +1722,7 @@ putfile(char *longname, char *shortname, char *parent, goto out; } - if (getstat(dirfd, longname, shortname)) + if (getstat(dirfd, longname, shortname, attrparent)) goto out; if (hflag) { @@ -1588,7 +1748,12 @@ putfile(char *longname, char *shortname, char *parent, */ if ((mt_ino == stbuf.st_ino) && (mt_dev == stbuf.st_dev)) { (void) fprintf(stderr, gettext( - "tar: %s same as archive file\n"), longname); + "tar: %s%s%s%s%s same as archive file\n"), + rw_sysattr ? gettext("system ") : "", + (longattrname == NULL) ? "" : gettext("attribute "), + (longattrname == NULL) ? "" : longattrname, + (longattrname == NULL) ? "" : gettext(" of "), + longname); Errflg = 1; goto out; } @@ -1605,8 +1770,13 @@ putfile(char *longname, char *shortname, char *parent, !S_ISBLK(stbuf.st_mode) && (Eflag == 0)) { (void) fprintf(stderr, gettext( - "tar: %s too large to archive. " - "Use E function modifier.\n"), longname); + "tar: %s%s%s%s%s too large to archive. " + "Use E function modifier.\n"), + rw_sysattr ? gettext("system ") : "", + (longattrname == NULL) ? "" : gettext("attribute "), + (longattrname == NULL) ? "" : longattrname, + (longattrname == NULL) ? "" : gettext(" of "), + longname); if (errflag) exitflag = 1; Errflg = 1; @@ -1787,8 +1957,8 @@ putfile(char *longname, char *shortname, char *parent, } } - if (put_extra_attributes(longname, shortname, prefix, - filetype, '5') != 0) + if (put_extra_attributes(longname, shortname, + longattrname, prefix, filetype, '5') != 0) goto out; #if defined(O_XATTR) @@ -1817,8 +1987,8 @@ putfile(char *longname, char *shortname, char *parent, 0); #endif if (filetype == XATTR_FILE && Hiddendir) { - (void) fprintf(vfile, "a %s attribute . ", - longname); + (void) fprintf(vfile, "a %s attribute %s ", + longname, longattrname); } else { (void) fprintf(vfile, "a %s/ ", longname); @@ -1835,9 +2005,9 @@ putfile(char *longname, char *shortname, char *parent, * If hidden dir then break now since xattrs_put() will do * the iterating of the directory. * - * At the moment, there can't be attributes on attributes - * or directories within the attributes hidden directory - * hierarchy. + * At the moment, there can only be system attributes on + * attributes. There can be no attributes on attributes or + * directories within the attributes hidden directory hierarchy. */ if (filetype == XATTR_FILE) break; @@ -1854,7 +2024,7 @@ putfile(char *longname, char *shortname, char *parent, if ((dirp = opendir(".")) == NULL) { vperror(0, gettext( - "can't open directory %s"), longname); + "can't open directory %s"), longname); if (chdir(parent) < 0) vperror(0, gettext("cannot change back?: %s"), parent); @@ -1873,12 +2043,13 @@ putfile(char *longname, char *shortname, char *parent, } else l = -1; - archtype = putfile(buf, cp, newparent, + archtype = putfile(buf, cp, newparent, NULL, NORMAL_FILE, lev + 1, symlink_lev); if (!exitflag) { - if (atflag && archtype == PUT_NOTAS_LINK) { - xattrs_put(buf, cp, newparent); + if ((atflag || saflag) && + (archtype == PUT_NOTAS_LINK)) { + xattrs_put(buf, cp, newparent, NULL); } } if (exitflag) @@ -1962,17 +2133,18 @@ putfile(char *longname, char *shortname, char *parent, break; case S_IFREG: if ((infile = openat(dirfd, shortname, 0)) < 0) { - vperror(0, "%s%s%s", longname, + vperror(0, "unable to open %s%s%s%s", longname, + rw_sysattr ? gettext(" system") : "", (filetype == XATTR_FILE) ? gettext(" attribute ") : "", - (filetype == XATTR_FILE) ? - shortname : ""); + (filetype == XATTR_FILE) ? (longattrname == NULL) ? + shortname : longattrname : ""); goto out; } blocks = TBLOCKS(stbuf.st_size); - if (put_link(name, longname, shortname, + if (put_link(name, longname, shortname, longattrname, prefix, filetype, '1') == 0) { (void) close(infile); rc = PUT_AS_LINK; @@ -2018,11 +2190,12 @@ putfile(char *longname, char *shortname, char *parent, DEBUG("seek = %" FMT_blkcnt_t "K\t", K(tapepos), 0); #endif - (void) fprintf(vfile, "a %s%s%s ", longname, - (filetype == XATTR_FILE) ? - gettext(" attribute ") : "", + (void) fprintf(vfile, "a %s%s%s%s ", longname, + rw_sysattr ? gettext(" system") : "", + (filetype == XATTR_FILE) ? gettext( + " attribute ") : "", (filetype == XATTR_FILE) ? - shortname : ""); + longattrname : ""); if (NotTape) (void) fprintf(vfile, "%" FMT_blkcnt_t "K\n", K(blocks)); @@ -2032,8 +2205,8 @@ putfile(char *longname, char *shortname, char *parent, blocks); } - if (put_extra_attributes(longname, shortname, prefix, - filetype, '0') != 0) + if (put_extra_attributes(longname, shortname, longattrname, + prefix, filetype, '0') != 0) goto out; /* @@ -2082,7 +2255,7 @@ putfile(char *longname, char *shortname, char *parent, blocks = TBLOCKS(stbuf.st_size); stbuf.st_size = (off_t)0; - if (put_link(name, longname, shortname, + if (put_link(name, longname, shortname, longattrname, prefix, filetype, '6') == 0) { rc = PUT_AS_LINK; goto out; @@ -2132,8 +2305,8 @@ putfile(char *longname, char *shortname, char *parent, &stbuf, stbuf.st_dev, prefix) != 0) goto out; - if (put_extra_attributes(longname, shortname, prefix, - filetype, '6') != 0) + if (put_extra_attributes(longname, shortname, longattrname, + prefix, filetype, '6') != 0) goto out; (void) sprintf(dblock.dbuf.chksum, "%07o", checksum(&dblock)); @@ -2144,8 +2317,8 @@ putfile(char *longname, char *shortname, char *parent, case S_IFCHR: stbuf.st_size = (off_t)0; blocks = TBLOCKS(stbuf.st_size); - if (put_link(name, longname, - shortname, prefix, filetype, '3') == 0) { + if (put_link(name, longname, shortname, longattrname, + prefix, filetype, '3') == 0) { rc = PUT_AS_LINK; goto out; } @@ -2193,7 +2366,7 @@ putfile(char *longname, char *shortname, char *parent, filetype, &stbuf, stbuf.st_rdev, prefix) != 0) goto out; - if (put_extra_attributes(longname, shortname, + if (put_extra_attributes(longname, shortname, longattrname, prefix, filetype, '3') != 0) goto out; @@ -2205,8 +2378,8 @@ putfile(char *longname, char *shortname, char *parent, case S_IFBLK: stbuf.st_size = (off_t)0; blocks = TBLOCKS(stbuf.st_size); - if (put_link(name, longname, - shortname, prefix, filetype, '4') == 0) { + if (put_link(name, longname, shortname, longattrname, + prefix, filetype, '4') == 0) { rc = PUT_AS_LINK; goto out; } @@ -2254,7 +2427,7 @@ putfile(char *longname, char *shortname, char *parent, filetype, &stbuf, stbuf.st_rdev, prefix) != 0) goto out; - if (put_extra_attributes(longname, shortname, + if (put_extra_attributes(longname, shortname, longattrname, prefix, filetype, '4') != 0) goto out; @@ -2273,9 +2446,7 @@ putfile(char *longname, char *shortname, char *parent, } out: - if (dirfd != -1) { - if (filetype == XATTR_FILE) - (void) chdir(parent); + if ((dirfd != -1) && (filetype != XATTR_FILE)) { (void) close(dirfd); } return (rc); @@ -2417,6 +2588,210 @@ convtoreg(off_t size) return (0); } +#if defined(O_XATTR) +static int +save_cwd(void) +{ + return (open(".", O_RDONLY)); +} +#endif + +#if defined(O_XATTR) +static void +rest_cwd(int *cwd) +{ + if (*cwd != -1) { + if (fchdir(*cwd) < 0) { + vperror(0, gettext( + "Cannot fchdir to attribute directory")); + exit(1); + } + (void) close(*cwd); + *cwd = -1; + } +} +#endif + +/* + * Verify the underlying file system supports the attribute type. + * Only archive extended attribute files when '-@' was specified. + * Only archive system extended attribute files if '-/' was specified. + */ +#if defined(O_XATTR) +static attr_status_t +verify_attr_support(char *filename, arc_action_t actflag) +{ + /* + */ + if (atflag) { + /* Verify extended attributes are supported */ + if (pathconf(filename, (actflag == ARC_CREATE) ? + _PC_XATTR_EXISTS : _PC_XATTR_ENABLED) != 1) { +#if defined(_PC_SATTR_ENABLED) + if (saflag) { + /* Verify system attributes are supported */ + if (sysattr_support(filename, + (actflag == ARC_CREATE) ? _PC_SATTR_EXISTS : + _PC_SATTR_ENABLED) != 1) { + return (ATTR_SATTR_ERR); + } + } else + return (ATTR_XATTR_ERR); +#else + return (ATTR_XATTR_ERR); +#endif /* _PC_SATTR_ENABLED */ + } + +#if defined(_PC_SATTR_ENABLED) + } else if (saflag) { + /* Verify system attributes are supported */ + if (sysattr_support(filename, (actflag == ARC_CREATE) ? + _PC_SATTR_EXISTS : _PC_SATTR_ENABLED) != 1) { + return (ATTR_SATTR_ERR); + } +#endif /* _PC_SATTR_ENABLED */ + } else { + return (ATTR_SKIP); + } + + return (ATTR_OK); +} +#endif + +#if defined(O_XATTR) +/* + * Recursively open attribute directories until the attribute directory + * containing the specified attribute, attrname, is opened. + * + * Currently, only 2 directory levels of attributes are supported, (i.e., + * extended system attributes on extended attributes). The following are + * the possible input combinations: + * 1. Open the attribute directory of the base file (don't change + * into it). + * attrinfo->parent = NULL + * attrname = '.' + * 2. Open the attribute directory of the base file and change into it. + * attrinfo->parent = NULL + * attrname = | + * 3. Open the attribute directory of the base file, change into it, + * then recursively call open_attr_dir() to open the attribute's + * parent directory (don't change into it). + * attrinfo->parent = + * attrname = '.' + * 4. Open the attribute directory of the base file, change into it, + * then recursively call open_attr_dir() to open the attribute's + * parent directory and change into it. + * attrinfo->parent = + * attrname = | + * + * An attribute directory will be opened only if the underlying file system + * supports the attribute type, and if the command line specifications (atflag + * and saflag) enable the processing of the attribute type. + * + * On succesful return, attrinfo->parentfd will be the file descriptor of the + * opened attribute directory. In addition, if the attribute is a read-write + * extended system attribute, attrinfo->rw_sysattr will be set to 1, otherwise + * it will be set to 0. + * + * Possible return values: + * ATTR_OK Successfully opened and, if needed, changed into the + * attribute directory containing attrname. + * ATTR_SKIP The command line specifications don't enable the + * processing of the attribute type. + * ATTR_CHDIR_ERR An error occurred while trying to change into an + * attribute directory. + * ATTR_OPEN_ERR An error occurred while trying to open an + * attribute directory. + * ATTR_XATTR_ERR The underlying file system doesn't support extended + * attributes. + * ATTR_SATTR_ERR The underlying file system doesn't support extended + * system attributes. + */ +static int +open_attr_dir(char *attrname, char *dirp, int cwd, attr_data_t *attrinfo) +{ + attr_status_t rc; + int firsttime = (attrinfo->attr_parentfd == -1); + int saveerrno; + + /* + * open_attr_dir() was recursively called (input combination number 4), + * close the previously opened file descriptor as we've already changed + * into it. + */ + if (!firsttime) { + (void) close(attrinfo->attr_parentfd); + attrinfo->attr_parentfd = -1; + } + + /* + * Verify that the underlying file system supports the restoration + * of the attribute. + */ + if ((rc = verify_attr_support(dirp, ARC_RESTORE)) != ATTR_OK) { + return (rc); + } + + /* Open the base file's attribute directory */ + if ((attrinfo->attr_parentfd = attropen(dirp, ".", O_RDONLY)) == -1) { + /* + * Save the errno from the attropen so it can be reported + * if the retry of the attropen fails. + */ + saveerrno = errno; + if ((attrinfo->attr_parentfd = retry_open_attr(-1, cwd, dirp, + NULL, ".", O_RDONLY, 0)) == -1) { + /* + * Reset typeflag back to real value so passtape + * will skip ahead correctly. + */ + dblock.dbuf.typeflag = _XATTR_HDRTYPE; + (void) close(attrinfo->attr_parentfd); + attrinfo->attr_parentfd = -1; + errno = saveerrno; + return (ATTR_OPEN_ERR); + } + } + + /* + * Change into the parent attribute's directory unless we are + * processing the hidden attribute directory of the base file itself. + */ + if ((Hiddendir == 0) || (firsttime && attrinfo->attr_parent != NULL)) { + if (fchdir(attrinfo->attr_parentfd) != 0) { + saveerrno = errno; + (void) close(attrinfo->attr_parentfd); + attrinfo->attr_parentfd = -1; + errno = saveerrno; + return (ATTR_CHDIR_ERR); + } + } + + /* Determine if the attribute should be processed */ + if ((rc = verify_attr(attrname, attrinfo->attr_parent, 1, + &attrinfo->attr_rw_sysattr)) != ATTR_OK) { + saveerrno = errno; + (void) close(attrinfo->attr_parentfd); + attrinfo->attr_parentfd = -1; + errno = saveerrno; + return (rc); + } + + /* + * If the attribute is an extended attribute, or extended system + * attribute, of an attribute (i.e., /), then + * recursively call open_attr_dir() to open the attribute directory + * of the parent attribute. + */ + if (firsttime && (attrinfo->attr_parent != NULL)) { + return (open_attr_dir(attrname, attrinfo->attr_parent, + attrinfo->attr_parentfd, attrinfo)); + } + + return (ATTR_OK); +} +#endif + static void #ifdef _iBCS2 doxtract(char *argv[], int tbl_cnt) @@ -2433,15 +2808,18 @@ doxtract(char *argv[]) int fcnt = 0; /* count # files in argv list */ int dir; int dirfd = -1; + int cwd = -1; + int rw_sysattr = 0; + int saveerrno; uid_t Uid; char *namep, *dirp, *comp, *linkp; /* for removing absolute paths */ char dirname[PATH_MAX+1]; char templink[PATH_MAX+1]; /* temp link with terminating NULL */ - char origdir[PATH_MAX+1]; int once = 1; int error; int symflag; int want; + attr_data_t *attrinfo = NULL; /* attribute info */ acl_t *aclp = NULL; /* acl info */ char dot[] = "."; /* dirp for using realpath */ timestruc_t time_zero; /* used for call to doDirTimes */ @@ -2480,10 +2858,26 @@ doxtract(char *argv[]) dir = 0; ofile = -1; + if (dirfd != -1) { + (void) close(dirfd); + dirfd = -1; + } + if (ofile != -1) { + if (close(ofile) != 0) + vperror(2, gettext("close error")); + } + +#if defined(O_XATTR) + if (cwd != -1) { + rest_cwd(&cwd); + } +#endif + /* namep is set by wantit to point to the full name */ - if ((want = wantit(argv, &namep, &dirp, &comp)) == 0) { + if ((want = wantit(argv, &namep, &dirp, &comp, + &attrinfo)) == 0) { #if defined(O_XATTR) - if (xattrp != (struct xattr_buf *)NULL) { + if (xattrp != NULL) { free(xattrhead); xattrp = NULL; xattr_linkp = NULL; @@ -2522,51 +2916,49 @@ doxtract(char *argv[]) dircreate = checkdir(&dirname[0]); #if defined(O_XATTR) - if (xattrp != (struct xattr_buf *)NULL) { - dirfd = attropen(dirp, ".", O_RDONLY); - } else { - dirfd = open(dirp, O_RDONLY); - } -#else - dirfd = open(dirp, O_RDONLY); -#endif + if (xattrp != NULL) { + int rc; - if (dirfd == -1) { -#if defined(O_XATTR) - if (xattrp) { - dirfd = retry_attrdir_open(dirp); - } -#endif - if (dirfd == -1) { -#if defined(O_XATTR) - if (xattrp) { + if (((cwd = save_cwd()) == -1) || + ((rc = open_attr_dir(comp, dirp, cwd, + attrinfo)) != ATTR_OK)) { + if (cwd == -1) { + vperror(0, gettext( + "unable to save current working " + "directory while processing " + "attribute %s of %s"), + dirp, attrinfo->attr_path); + } else if (rc != ATTR_SKIP) { (void) fprintf(vfile, gettext("tar: cannot open " - "attribute %s of file %s: %s\n"), - xattraname, dirp, strerror(errno)); - /* - * Reset typeflag back to real - * value so passtape will skip - * ahead correctly. - */ - dblock.dbuf.typeflag = _XATTR_HDRTYPE; - free(xattrhead); - xattrp = NULL; - xattr_linkp = NULL; - xattrhead = NULL; - } else { - (void) fprintf(vfile, - gettext("tar: cannot open %s %s\n"), - dirp, strerror(errno)); + "%sattribute %s of file %s: %s\n"), + attrinfo->attr_rw_sysattr ? gettext( + "system ") : "", + comp, dirp, strerror(errno)); } -#else - (void) fprintf(vfile, - gettext("tar: cannot open %s %s\n"), - dirp, strerror(errno)); -#endif + free(xattrhead); + xattrp = NULL; + xattr_linkp = NULL; + xattrhead = NULL; + passtape(); continue; + } else { + dirfd = attrinfo->attr_parentfd; + rw_sysattr = attrinfo->attr_rw_sysattr; } + } else { + dirfd = open(dirp, O_RDONLY); + } +#else + dirfd = open(dirp, O_RDONLY); +#endif + if (dirfd == -1) { + (void) fprintf(vfile, gettext( + "tar: cannot open %s: %s\n"), + dirp, strerror(errno)); + passtape(); + continue; } if (xhdr_flgs & _X_LINKPATH) @@ -2635,18 +3027,51 @@ doxtract(char *argv[]) * Dir is automatically created, we only * need to update mode and perm's. */ - if ((xattrp != (struct xattr_buf *)NULL) && Hiddendir == 1) { - if (fchownat(dirfd, ".", stbuf.st_uid, - stbuf.st_gid, 0) != 0) { - vperror(0, gettext( - "%s: failed to set ownership of attribute" - " directory"), namep); + if ((xattrp != NULL) && Hiddendir == 1) { + bytes = stbuf.st_size; + blocks = TBLOCKS(bytes); + if (vflag) { + (void) fprintf(vfile, + "x %s%s%s, %" FMT_off_t " bytes, ", namep, + gettext(" attribute "), + xattrapath, bytes); + if (NotTape) + (void) fprintf(vfile, + "%" FMT_blkcnt_t "K\n", K(blocks)); + else + (void) fprintf(vfile, gettext("%" + FMT_blkcnt_t " tape blocks\n"), + blocks); } - if (fchmod(dirfd, stbuf.st_mode) != 0) { - vperror(0, gettext( - "%s: failed to set permissions of" - " attribute directory"), namep); + /* + * Set the permissions and mode of the attribute + * unless the attribute is a system attribute (can't + * successfully do this) or the hidden attribute + * directory (".") of an attribute (when the attribute + * is restored, the hidden attribute directory of an + * attribute is transient). Note: when the permissions + * and mode are set for the hidden attribute directory + * of a file on a system supporting extended system + * attributes, even though it returns successfully, it + * will not have any affect since the attribute + * directory is transient. + */ + if (attrinfo->attr_parent == NULL) { + if (fchownat(dirfd, ".", stbuf.st_uid, + stbuf.st_gid, 0) != 0) { + vperror(0, gettext( + "%s%s%s: failed to set ownership " + "of attribute directory"), namep, + gettext(" attribute "), xattrapath); + } + + if (fchmod(dirfd, stbuf.st_mode) != 0) { + vperror(0, gettext( + "%s%s%s: failed to set permissions " + "of attribute directory"), namep, + gettext(" attribute "), xattrapath); + } } goto filedone; } @@ -2683,7 +3108,8 @@ doxtract(char *argv[]) } if (vflag) (void) fprintf(vfile, gettext( - "%s linked to %s\n"), namep, linkp); + "x %s linked to %s\n"), namep, + linkp); xcnt++; /* increment # files extracted */ continue; } @@ -2723,14 +3149,15 @@ doxtract(char *argv[]) } if (vflag) (void) fprintf(vfile, gettext( - "%s linked to %s\n"), namep, linkp); + "x %s linked to %s\n"), namep, + linkp); xcnt++; /* increment # files extracted */ continue; } if (mknod(namep, (int)(Gen.g_mode|S_IFCHR), (int)makedev(Gen.g_devmajor, Gen.g_devminor)) < 0) { vperror(0, gettext( - "%s: mknod failed"), namep); + "%s: mknod failed"), namep); continue; } bytes = stbuf.st_size; @@ -2771,7 +3198,8 @@ doxtract(char *argv[]) } if (vflag) (void) fprintf(vfile, gettext( - "%s linked to %s\n"), namep, linkp); + "x %s linked to %s\n"), namep, + linkp); xcnt++; /* increment # files extracted */ continue; } @@ -2833,28 +3261,16 @@ doxtract(char *argv[]) } #if defined(O_XATTR) if (xattrp && xattr_linkp) { - if (getcwd(origdir, (PATH_MAX+1)) == - (char *)NULL) { - vperror(0, gettext( - "A parent directory cannot" - " be read")); - exit(1); - } - if (fchdir(dirfd) < 0) { vperror(0, gettext( "Cannot fchdir to attribute " - "directory")); + "directory %s"), + (attrinfo->attr_parent == NULL) ? + dirp : attrinfo->attr_parent); exit(1); } - error = link(xattr_linkaname, xattraname); - if (chdir(origdir) < 0) { - vperror(0, gettext( - "Cannot chdir out of attribute " - "directory")); - exit(1); - } + error = link(xattr_linkaname, xattrapath); } else { error = link(linkp, namep); } @@ -2868,23 +3284,23 @@ doxtract(char *argv[]) namep, (xattr_linkp != NULL) ? gettext(" attribute ") : "", (xattr_linkp != NULL) ? - xattraname : ""); + xattrapath : ""); continue; } if (vflag) (void) fprintf(vfile, gettext( - "%s%s%s linked to %s%s%s\n"), namep, + "x %s%s%s linked to %s%s%s\n"), namep, (xattr_linkp != NULL) ? gettext(" attribute ") : "", (xattr_linkp != NULL) ? xattr_linkaname : "", - linkp, (xattr_linkp != NULL) ? - gettext(" attribute ") : "", + linkp, (xattr_linkp != NULL) ? - xattraname : ""); + gettext(" attribute ") : "", + (xattr_linkp != NULL) ? xattrapath : ""); xcnt++; /* increment # files extracted */ #if defined(O_XATTR) - if (xattrp != (struct xattr_buf *)NULL) { + if (xattrp != NULL) { free(xattrhead); xattrp = NULL; xattr_linkp = NULL; @@ -2925,20 +3341,58 @@ doxtract(char *argv[]) } if (vflag) (void) fprintf(vfile, gettext( - "%s linked to %s\n"), comp, linkp); + "x %s linked to %s\n"), comp, + linkp); xcnt++; /* increment # files extracted */ +#if defined(O_XATTR) + if (xattrp != NULL) { + free(xattrhead); + xattrp = NULL; + xattr_linkp = NULL; + xattrhead = NULL; + } +#endif continue; } newfile = ((fstatat(dirfd, comp, &xtractbuf, 0) == -1) ? TRUE : FALSE); - if ((ofile = openat(dirfd, comp, O_RDWR|O_CREAT|O_TRUNC, - stbuf.st_mode & MODEMASK)) < 0) { + ofile = openat(dirfd, comp, O_RDWR|O_CREAT|O_TRUNC, + stbuf.st_mode & MODEMASK); + saveerrno = errno; + +#if defined(O_XATTR) + if (xattrp != NULL) { + if (ofile < 0) { + ofile = retry_open_attr(dirfd, cwd, + dirp, attrinfo->attr_parent, comp, + O_RDWR|O_CREAT|O_TRUNC, + stbuf.st_mode & MODEMASK); + } + } +#endif + if (ofile < 0) { + errno = saveerrno; (void) fprintf(stderr, gettext( - "tar: %s - cannot create\n"), comp); + "tar: %s%s%s%s - cannot create\n"), + (xattrp == NULL) ? "" : (rw_sysattr ? + gettext("system attribure ") : + gettext("attribute ")), + (xattrp == NULL) ? "" : xattrapath, + (xattrp == NULL) ? "" : gettext(" of "), + (xattrp == NULL) ? comp : namep); if (errflag) done(1); else Errflg = 1; +#if defined(O_XATTR) + if (xattrp != NULL) { + dblock.dbuf.typeflag = _XATTR_HDRTYPE; + free(xattrhead); + xattrp = NULL; + xattr_linkp = NULL; + xattrhead = NULL; + } +#endif passtape(); continue; } @@ -2955,11 +3409,17 @@ doxtract(char *argv[]) if (extno != 0) { /* file is in pieces */ if (extotal < 1 || extotal > MAXEXT) (void) fprintf(stderr, gettext( - "tar: ignoring bad extent info for %s\n"), - comp); + "tar: ignoring bad extent info for " + "%s%s%s%s\n"), + (xattrp == NULL) ? "" : (rw_sysattr ? + gettext("system attribute ") : + gettext("attribute ")), + (xattrp == NULL) ? "" : xattrapath, + (xattrp == NULL) ? "" : gettext(" of "), + (xattrp == NULL) ? comp : namep); else { - xsfile(ofile); /* extract it */ - goto filedone; + /* extract it */ + (void) xsfile(rw_sysattr, ofile); } } extno = 0; /* let everyone know file is not split */ @@ -2969,8 +3429,10 @@ doxtract(char *argv[]) (void) fprintf(vfile, "x %s%s%s, %" FMT_off_t " bytes, ", (xattrp == NULL) ? "" : dirp, - (xattrp == NULL) ? "" : gettext(" attribute "), - (xattrp == NULL) ? namep : comp, bytes); + (xattrp == NULL) ? "" : (rw_sysattr ? + gettext(" system attribute ") : + gettext(" attribute ")), + (xattrp == NULL) ? namep : xattrapath, bytes); if (NotTape) (void) fprintf(vfile, "%" FMT_blkcnt_t "K\n", K(blocks)); @@ -2979,18 +3441,50 @@ doxtract(char *argv[]) FMT_blkcnt_t " tape blocks\n"), blocks); } - xblocks(bytes, ofile); + if (xblocks(rw_sysattr, bytes, ofile) != 0) { +#if defined(O_XATTR) + if (xattrp != NULL) { + free(xattrhead); + xattrp = NULL; + xattr_linkp = NULL; + xattrhead = NULL; + } +#endif + continue; + } filedone: if (mflag == 0 && !symflag) { if (dir) doDirTimes(namep, stbuf.st_mtim); + else +#if defined(O_XATTR) + if (xattrp != NULL) { + /* + * Set the time on the attribute unless + * the attribute is a system attribute + * (can't successfully do this) or the + * hidden attribute directory, "." (the + * time on the hidden attribute + * directory will be updated when + * attributes are restored, otherwise + * it's transient). + */ + if (!rw_sysattr && (Hiddendir == 0)) { + setPathTimes(dirfd, comp, + stbuf.st_mtim); + } + } else + setPathTimes(dirfd, comp, + stbuf.st_mtim); +#else setPathTimes(dirfd, comp, stbuf.st_mtim); +#endif } /* moved this code from above */ if (pflag && !symflag && Hiddendir == 0) { - if (xattrp != (struct xattr_buf *)NULL) + if (xattrp != NULL) (void) fchmod(ofile, stbuf.st_mode & MODEMASK); else (void) chmod(namep, stbuf.st_mode & MODEMASK); @@ -3007,7 +3501,7 @@ doxtract(char *argv[]) int ret; #if defined(O_XATTR) - if (xattrp != (struct xattr_buf *)NULL) { + if (xattrp != NULL) { if (Hiddendir) ret = facl_set(dirfd, aclp); else @@ -3021,8 +3515,14 @@ doxtract(char *argv[]) if (ret < 0) { if (pflag) { (void) fprintf(stderr, gettext( - "%s: failed to set acl entries\n"), - namep); + "%s%s%s%s: failed to set acl " + "entries\n"), namep, + (xattrp == NULL) ? "" : + (rw_sysattr ? gettext( + " system attribute ") : + gettext(" attribute ")), + (xattrp == NULL) ? "" : + xattrapath); } /* else: silent and continue */ } @@ -3031,7 +3531,7 @@ doxtract(char *argv[]) } #if defined(O_XATTR) - if (xattrp != (struct xattr_buf *)NULL) { + if (xattrp != NULL) { free(xattrhead); xattrp = NULL; xattr_linkp = NULL; @@ -3048,15 +3548,29 @@ doxtract(char *argv[]) convflag || dblock.dbuf.typeflag == '1')) { if (fstat(ofile, &xtractbuf) == -1) (void) fprintf(stderr, gettext( - "tar: cannot stat extracted file %s\n"), - namep); + "tar: cannot stat extracted file " + "%s%s%s%s\n"), + (xattrp == NULL) ? "" : (rw_sysattr ? + gettext("system attribute ") : + gettext("attribute ")), + (xattrp == NULL) ? "" : xattrapath, + (xattrp == NULL) ? "" : + gettext(" of "), namep); + else if ((xtractbuf.st_mode & (MODEMASK & ~S_IFMT)) != (stbuf.st_mode & (MODEMASK & ~S_IFMT))) { (void) fprintf(stderr, gettext( "tar: warning - file permissions have " - "changed for %s (are 0%o, should be " + "changed for %s%s%s%s (are 0%o, should be " "0%o)\n"), - namep, xtractbuf.st_mode, stbuf.st_mode); + (xattrp == NULL) ? "" : (rw_sysattr ? + gettext("system attribute ") : + gettext("attribute ")), + (xattrp == NULL) ? "" : xattrapath, + (xattrp == NULL) ? "" : + gettext(" of "), namep, + xtractbuf.st_mode, stbuf.st_mode); + } } if (ofile != -1) { @@ -3064,6 +3578,7 @@ doxtract(char *argv[]) dirfd = -1; if (close(ofile) != 0) vperror(2, gettext("close error")); + ofile = -1; } xcnt++; /* increment # files extracted */ } @@ -3188,8 +3703,9 @@ doxtract(char *argv[]) tp += attrsize; } while (bytes != 0); free(secp); - } else + } else { passtape(); + } } /* acl */ } /* for */ @@ -3202,6 +3718,15 @@ doxtract(char *argv[]) doDirTimes(NULL, time_zero); +#if defined(O_XATTR) + if (xattrp != NULL) { + free(xattrhead); + xattrp = NULL; + xattr_linkp = NULL; + xattrhead = NULL; + } +#endif + /* * Check if the number of files extracted is different from the * number of files listed on the command line @@ -3223,8 +3748,8 @@ doxtract(char *argv[]) * called by doxtract() and xsfile() */ -static void -xblocks(off_t bytes, int ofile) +static int +xblocks(int issysattr, off_t bytes, int ofile) { blkcnt_t blocks; char buf[TBLOCK]; @@ -3239,17 +3764,36 @@ xblocks(off_t bytes, int ofile) else write_count = bytes; if (write(ofile, buf, write_count) < 0) { + int saveerrno = errno; + if (xhdr_flgs & _X_PATH) (void) strcpy(tempname, Xtarhdr.x_path); else (void) sprintf(tempname, "%.*s", NAMSIZ, dblock.dbuf.name); - (void) fprintf(stderr, gettext( - "tar: %s: HELP - extract write error\n"), tempname); - done(2); + /* + * If the extended system attribute being extracted + * contains attributes that the user needs privileges + * for, then just display a warning message, skip + * the extraction of this file, and return. + */ + if ((saveerrno == EPERM) && issysattr) { + (void) fprintf(stderr, gettext( + "tar: unable to extract system attribute " + "%s: insufficient privileges\n"), tempname); + Errflg = 1; + return (1); + } else { + (void) fprintf(stderr, gettext( + "tar: %s: HELP - extract write error\n"), + tempname); + done(2); + } } bytes -= TBLOCK; } + + return (0); } @@ -3265,10 +3809,11 @@ xblocks(off_t bytes, int ofile) static union hblock savedblock; /* to ensure same file across volumes */ -static void -xsfile(int ofd) +static int +xsfile(int issysattr, int ofd) { int i, c; + int sysattrerr = 0; char name[PATH_MAX+1]; /* holds name for diagnostics */ int extents, totalext; off_t bytes, totalbytes; @@ -3295,7 +3840,11 @@ xsfile(int ofd) passtape(); if (close(ofd) != 0) vperror(2, gettext("close error")); - return; + if (sysattrerr) { + return (1); + } else { + return (0); + } } } extents = extotal; @@ -3312,7 +3861,10 @@ xsfile(int ofd) (void) fprintf(vfile, "+++ x %s [extent #%d], %" FMT_off_t " bytes, %ldK\n", name, extno, bytes, (long)K(TBLOCKS(bytes))); - xblocks(bytes, ofd); + if (xblocks(issysattr, bytes, ofd) != 0) { + sysattrerr = 1; + goto canit; + } totalbytes += bytes; totalext++; @@ -3361,6 +3913,8 @@ xsfile(int ofd) (void) fprintf(vfile, gettext( "x %s (in %d extents), %" FMT_off_t " bytes, %ldK\n"), name, totalext, totalbytes, (long)K(TBLOCKS(totalbytes))); + + return (0); } @@ -3394,7 +3948,6 @@ notsame(void) (strcmp(savedblock.dbuf.efsize, dblock.dbuf.efsize))); } - static void #ifdef _iBCS2 dotable(char *argv[], int tbl_cnt) @@ -3409,7 +3962,7 @@ dotable(char *argv[]) int want; char aclchar = ' '; /* either blank or '+' */ char templink[PATH_MAX+1]; - char *np; + attr_data_t *attrinfo = NULL; dumping = 0; @@ -3436,7 +3989,7 @@ dotable(char *argv[]) for (;;) { /* namep is set by wantit to point to the full name */ - if ((want = wantit(argv, &namep, &dirp, &comp)) == 0) + if ((want = wantit(argv, &namep, &dirp, &comp, &attrinfo)) == 0) continue; if (want == -1) break; @@ -3460,11 +4013,27 @@ dotable(char *argv[]) #if defined(O_XATTR) - if (xattrp != (struct xattr_buf *)NULL) { - np = xattrp->h_names + strlen(xattrp->h_names) + 1; - (void) printf(gettext("%s attribute %s"), - xattrp->h_names, np); + if (xattrp != NULL) { + int issysattr; + char *bn = basename(attrinfo->attr_path); + + /* + * We could use sysattr_type() to test whether or not + * the attribute we are processing is really an + * extended system attribute, which as of this writing + * just does a strcmp(), however, sysattr_type() may + * be changed to issue a pathconf() call instead, which + * would require being changed into the parent attribute + * directory. So instead, just do simple string + * comparisons to see if we are processing an extended + * system attribute. + */ + issysattr = is_sysattr(bn); + (void) printf(gettext("%s %sattribute %s"), + xattrp->h_names, + issysattr ? gettext("system ") : "", + attrinfo->attr_path); } else { (void) printf("%s", namep); } @@ -3486,7 +4055,7 @@ dotable(char *argv[]) (void) strcpy(templink, Xtarhdr.x_linkpath); } else { #if defined(O_XATTR) - if (xattrp != (struct xattr_buf *)NULL) { + if (xattrp != NULL) { (void) sprintf(templink, "file %.*s", NAMSIZ, xattrp->h_names); } else { @@ -3507,7 +4076,7 @@ dotable(char *argv[]) * linked to %s */ #if defined(O_XATTR) - if (xattrp != (struct xattr_buf *)NULL) { + if (xattrp != NULL) { (void) printf( gettext(" linked to attribute %s"), xattr_linkp->h_names + @@ -3533,7 +4102,7 @@ dotable(char *argv[]) " symbolic link to %s"), templink); (void) printf("\n"); #if defined(O_XATTR) - if (xattrp != (struct xattr_buf *)NULL) { + if (xattrp != NULL) { free(xattrhead); xattrp = NULL; xattrhead = NULL; @@ -5027,7 +5596,8 @@ mterr(char *operation, int i, int exitcode) } static int -wantit(char *argv[], char **namep, char **dirp, char **component) +wantit(char *argv[], char **namep, char **dirp, char **component, + attr_data_t **attrinfo) { char **cp; int gotit; /* true if we've found a match */ @@ -5044,17 +5614,29 @@ wantit(char *argv[], char **namep, char **dirp, char **component) #if defined(O_XATTR) if (dblock.dbuf.typeflag == _XATTR_HDRTYPE && xattrbadhead == 0) { - if (atflag || tflag) { - (void) read_xattr_hdr(); - } else { - passtape(); - } + /* + * Always needs to read the extended header. If atflag, saflag, + * or tflag isn't set, then we'll have the correct info for + * passtape() later. + */ + (void) read_xattr_hdr(attrinfo); goto top; } + /* + * Now that we've read the extended header, call passtape() if we aren't + * processing extended attributes. + */ + if ((xattrp != NULL) && !atflag && !saflag && !tflag) { + passtape(); + return (0); + } #endif /* sets *namep to point at the proper name */ - check_prefix(namep, dirp, component); + if (check_prefix(namep, dirp, component) != 0) { + passtape(); + return (0); + } if (endtape()) { if (Bflag) { @@ -5104,13 +5686,96 @@ wantit(char *argv[], char **namep, char **dirp, char **component) return (1); } +static int +fill_in_attr_info(char *attr, char *longname, char *attrparent, int atparentfd, + int rw_sysattr, attr_data_t **attrinfo) +{ + size_t pathlen; + char *tpath; + char *tparent; + + /* parent info */ + if (attrparent != NULL) { + if ((tparent = strdup(attrparent)) == NULL) { + vperror(0, gettext( + "unable to allocate memory for attribute parent " + "name for %sattribute %s/%s of %s"), + rw_sysattr ? gettext("system ") : "", + attrparent, attr, longname); + return (1); + } + } else { + tparent = NULL; + } + + /* path info */ + pathlen = strlen(attr) + 1; + if (attrparent != NULL) { + pathlen += strlen(attrparent) + 1; /* add 1 for '/' */ + } + if ((tpath = calloc(1, pathlen)) == NULL) { + vperror(0, gettext( + "unable to allocate memory for full " + "attribute path name for %sattribute %s%s%s of %s"), + rw_sysattr ? gettext("system ") : "", + (attrparent == NULL) ? "" : attrparent, + (attrparent == NULL) ? "" : "/", + attr, longname); + if (tparent != NULL) { + free(tparent); + } + return (1); + } + (void) snprintf(tpath, pathlen, "%s%s%s", + (attrparent == NULL) ? "" : attrparent, + (attrparent == NULL) ? "" : "/", + attr); + + /* fill in the attribute info */ + if (*attrinfo == NULL) { + if ((*attrinfo = malloc(sizeof (attr_data_t))) == NULL) { + vperror(0, gettext( + "unable to allocate memory for attribute " + "information for %sattribute %s%s%s of %s"), + rw_sysattr ? gettext("system ") : "", + (attrparent == NULL) ? "" : attrparent, + (attrparent == NULL) ? "" : gettext("/"), + attr, longname); + if (tparent != NULL) { + free(tparent); + } + free(tpath); + return (1); + } + } else { + if ((*attrinfo)->attr_parent != NULL) { + free((*attrinfo)->attr_parent); + } + if ((*attrinfo)->attr_path != NULL) { + free((*attrinfo)->attr_path); + } + /* + * The parent file descriptor is passed in, so don't + * close it here as it should be closed by the function + * that opened it. + */ + } + (*attrinfo)->attr_parent = tparent; + (*attrinfo)->attr_path = tpath; + (*attrinfo)->attr_rw_sysattr = rw_sysattr; + (*attrinfo)->attr_parentfd = atparentfd; + + return (0); +} /* * Return through *namep a pointer to the proper fullname (i.e " | * /"), as represented in the header entry dblock.dbuf. + * + * Returns 0 if successful, otherwise returns 1. */ -static void +static int check_prefix(char **namep, char **dirp, char **compp) { static char fullname[PATH_MAX + 1]; @@ -5140,7 +5805,7 @@ check_prefix(char **namep, char **dirp, char **compp) get_parent(fullname, dir); #if defined(O_XATTR) - if (xattrp == (struct xattr_buf *)NULL) { + if (xattrp == NULL) { #endif /* * Save of real name since were going to chop off the @@ -5158,13 +5823,15 @@ check_prefix(char **namep, char **dirp, char **compp) } else { (void) strcpy(fullname, xattrp->h_names); (void) strcpy(dir, fullname); - (void) strcpy(component, xattrp->h_names + - strlen(xattrp->h_names) + 1); + (void) strcpy(component, basename(xattrp->h_names + + strlen(xattrp->h_names) + 1)); } #endif *namep = fullname; *dirp = dir; *compp = component; + + return (0); } /* @@ -6540,12 +7207,13 @@ static void prepare_xattr( char **attrbuf, char *filename, - char *attrname, + char *attrpath, char typeflag, struct linkbuf *linkinfo, int *rlen) { char *bufhead; /* ptr to full buffer */ + char *aptr; struct xattr_hdr *hptr; /* ptr to header in bufhead */ struct xattr_buf *tptr; /* ptr to pathing pieces */ int totalen; /* total buffer length */ @@ -6558,6 +7226,7 @@ prepare_xattr( int linkstringlen; int complen; /* length of pathing section */ int linklen; /* length of link section */ + int attrnames_index; /* attrnames starting index */ /* * Release previous buffer @@ -6576,7 +7245,7 @@ prepare_xattr( /* * Add space for two nulls */ - stringlen = strlen(attrname) + strlen(filename) + 2; + stringlen = strlen(attrpath) + strlen(filename) + 2; complen = stringlen + sizeof (struct xattr_buf); len += stringlen; @@ -6591,7 +7260,10 @@ prepare_xattr( */ linkstringlen = strlen(linkinfo->pathname) + strlen(linkinfo->attrname) + 2; - len += linkstringlen; + linklen = linkstringlen + sizeof (struct xattr_buf); + len += linklen; + } else { + linklen = 0; } /* @@ -6611,12 +7283,6 @@ prepare_xattr( * Now we can fill in the necessary pieces */ - if (linkinfo != (struct linkbuf *)NULL) { - linklen = linkstringlen + (sizeof (struct xattr_buf)); - } else { - linklen = 0; - } - /* * first fill in the fixed header */ @@ -6630,15 +7296,38 @@ prepare_xattr( /* * Now fill in the filename + attrnames section + * The filename and attrnames section can be composed of two or more + * path segments separated by a null character. The first segment + * is the path to the parent file that roots the entire sequence in + * the normal name space. The remaining segments describes a path + * rooted at the hidden extended attribute directory of the leaf file of + * the previous segment, making it possible to name attributes on + * attributes. Thus, if we are just archiving an extended attribute, + * the second segment will contain the attribute name. If we are + * archiving a system attribute of an extended attribute, then the + * second segment will contain the attribute name, and a third segment + * will contain the system attribute name. The attribute pathing + * information is obtained from 'attrpath'. */ tptr = (struct xattr_buf *)(bufhead + sizeof (struct xattr_hdr)); (void) sprintf(tptr->h_namesz, "%0*d", sizeof (tptr->h_namesz) - 1, stringlen); (void) strcpy(tptr->h_names, filename); - (void) strcpy(&tptr->h_names[strlen(filename) + 1], attrname); + attrnames_index = strlen(filename) + 1; + (void) strcpy(&tptr->h_names[attrnames_index], attrpath); tptr->h_typeflag = typeflag; + /* + * Split the attrnames section into two segments if 'attrpath' + * contains pathing information for a system attribute of an + * extended attribute. We split them by replacing the '/' with + * a '\0'. + */ + if ((aptr = strpbrk(&tptr->h_names[attrnames_index], "/")) != NULL) { + *aptr = '\0'; + } + /* * Now fill in the optional link section if we have one */ @@ -6675,7 +7364,7 @@ prepare_xattr( #endif int -getstat(int dirfd, char *longname, char *shortname) +getstat(int dirfd, char *longname, char *shortname, char *attrparent) { int i, j; @@ -6722,7 +7411,11 @@ getstat(int dirfd, char *longname, char *shortname) if (printerr) { (void) fprintf(stderr, gettext( - "tar: %s: %s\n"), longname, strerror(errno)); + "tar: %s%s%s%s: %s\n"), + (attrparent == NULL) ? "" : gettext("attribute "), + (attrparent == NULL) ? "" : attrparent, + (attrparent == NULL) ? "" : gettext(" of "), + longname, strerror(errno)); Errflg = 1; } return (1); @@ -6730,60 +7423,208 @@ getstat(int dirfd, char *longname, char *shortname) return (0); } +/* + * Recursively archive the extended attributes and/or extended system attributes + * of the base file, longname. Note: extended system attribute files will be + * archived only if the extended system attributes are not transient (i.e. the + * extended system attributes are other than the default values). + * + * If -@ was specified and the underlying file system supports it, archive the + * extended attributes, and if there is a system attribute associated with the + * extended attribute, then recursively call xattrs_put() to archive the + * hidden attribute directory and the extended system attribute. If -/ was + * specified and the underlying file system supports it, archive the extended + * system attributes. Read-only extended system attributes are never archived. + * + * Currently, there cannot be attributes on attributes; only system + * attributes on attributes. In addition, there cannot be attributes on + * system attributes. A file and it's attribute directory hierarchy looks as + * follows: + * longname ----> . ("." is the hidden attribute directory) + * | + * ---------------------------- + * | | + * ----> . + * | + * + * + */ #if defined(O_XATTR) static void -xattrs_put(char *longname, char *shortname, char *parent) +xattrs_put(char *longname, char *shortname, char *parent, char *attrparent) { + char *filename = (attrparent == NULL) ? shortname : attrparent; + int arc_rwsysattr = 0; int dirfd; + int fd = -1; + int rw_sysattr = 0; + int rc; DIR *dirp; struct dirent *dp; + attr_data_t *attrinfo = NULL; - if (pathconf(shortname, _PC_XATTR_EXISTS) != 1) { + /* + * If the underlying file system supports it, then archive the extended + * attributes if -@ was specified, and the extended system attributes + * if -/ was specified. + */ + if (verify_attr_support(filename, ARC_CREATE) != ATTR_OK) { return; } - if ((dirfd = attropen(shortname, ".", O_RDONLY)) < 0) { - (void) fprintf(stderr, gettext( - "tar: unable to open attribute directory for file %s\n"), + /* + * Only want to archive a read-write extended system attribute file + * if it contains extended system attribute settings that are not the + * default values. + */ +#if defined(_PC_SATTR_ENABLED) + if (saflag) { + int filefd; + nvlist_t *slist = NULL; + + /* Determine if there are non-transient system attributes */ + errno = 0; + if ((filefd = open(filename, O_RDONLY)) == -1) { + if (attrparent == NULL) { + vperror(0, gettext( + "unable to open file %s"), longname); + } + return; + } + if (((slist = sysattr_list(basename(myname), filefd, + filename)) != NULL) || (errno != 0)) { + arc_rwsysattr = 1; + } + if (slist != NULL) { + (void) nvlist_free(slist); + slist = NULL; + } + (void) close(filefd); + } +#endif /* _PC_SATTR_ENABLED */ + + /* open the parent attribute directory */ + fd = attropen(filename, ".", O_RDONLY); + if (fd < 0) { + vperror(0, gettext( + "unable to open attribute directory for %s%s%sfile %s"), + (attrparent == NULL) ? "" : gettext("attribute "), + (attrparent == NULL) ? "" : attrparent, + (attrparent == NULL) ? "" : gettext(" of "), longname); return; } - if ((dirp = fdopendir(dirfd)) == NULL) { + /* + * We need to change into the parent's attribute directory to determine + * if each of the attributes should be archived. + */ + if (fchdir(fd) < 0) { + vperror(0, gettext( + "cannot change to attribute directory of %s%s%sfile %s"), + (attrparent == NULL) ? "" : gettext("attribute "), + (attrparent == NULL) ? "" : attrparent, + (attrparent == NULL) ? "" : gettext(" of "), + longname); + (void) close(fd); + return; + } + + if (((dirfd = dup(fd)) == -1) || + ((dirp = fdopendir(dirfd)) == NULL)) { (void) fprintf(stderr, gettext( - "tar: unable to open dir pointer for file %s\n"), longname); + "tar: unable to open dir pointer for %s%s%sfile %s\n"), + (attrparent == NULL) ? "" : gettext("attribute "), + (attrparent == NULL) ? "" : attrparent, + (attrparent == NULL) ? "" : gettext(" of "), + longname); + if (fd > 0) { + (void) close(fd); + } return; } while (dp = readdir(dirp)) { - if (dp->d_name[0] == '.' && dp->d_name[1] == '.' && - dp->d_name[2] == '\0') + if (strcmp(dp->d_name, "..") == 0) { continue; - - if (dp->d_name[0] == '.' && dp->d_name[1] == '\0') + } else if (strcmp(dp->d_name, ".") == 0) { Hiddendir = 1; - else + } else { Hiddendir = 0; + } + + /* Determine if this attribute should be archived */ + if (verify_attr(dp->d_name, attrparent, arc_rwsysattr, + &rw_sysattr) != ATTR_OK) { + continue; + } + + /* gather the attribute's information to pass to putfile() */ + if ((fill_in_attr_info(dp->d_name, longname, attrparent, + fd, rw_sysattr, &attrinfo)) == 1) { + continue; + } - (void) putfile(longname, dp->d_name, parent, + /* add the attribute to the archive */ + rc = putfile(longname, dp->d_name, parent, attrinfo, XATTR_FILE, LEV0, SYMLINK_LEV0); - if (exitflag) + if (exitflag) { break; + } + +#if defined(_PC_SATTR_ENABLED) + /* + * If both -/ and -@ were specified, then archive the + * attribute's extended system attributes and hidden directory + * by making a recursive call to xattrs_put(). + */ + if (!rw_sysattr && saflag && atflag && (rc != PUT_AS_LINK) && + (Hiddendir == 0)) { + + xattrs_put(longname, shortname, parent, dp->d_name); + + /* + * Change back to the parent's attribute directory + * to process any further attributes. + */ + if (fchdir(fd) < 0) { + vperror(0, gettext( + "cannot change back to attribute directory " + "of file %s"), longname); + break; + } + } +#endif /* _PC_SATTR_ENABLED */ } + if (attrinfo != NULL) { + if (attrinfo->attr_parent != NULL) { + free(attrinfo->attr_parent); + } + free(attrinfo->attr_path); + free(attrinfo); + } (void) closedir(dirp); + if (fd != -1) { + (void) close(fd); + } + + /* Change back to the parent directory of the base file */ + if (attrparent == NULL) { + (void) chdir(parent); + } } #else static void -xattrs_put(char *longname, char *shortname, char *parent) +xattrs_put(char *longname, char *shortname, char *parent, char *attrppath) { } #endif /* O_XATTR */ static int -put_link(char *name, char *longname, char *component, - char *prefix, int filetype, char type) +put_link(char *name, char *longname, char *component, char *longattrname, + char *prefix, int filetype, char type) { if (stbuf.st_nlink > 1) { @@ -6799,8 +7640,8 @@ put_link(char *name, char *longname, char *component, if (found) { #if defined(O_XATTR) if (filetype == XATTR_FILE) - if (put_xattr_hdr(longname, component, prefix, - type, filetype, lp)) { + if (put_xattr_hdr(longname, component, + longattrname, prefix, type, filetype, lp)) { goto out; } #endif @@ -6833,8 +7674,9 @@ put_link(char *name, char *longname, char *component, if (filetype == XATTR_FILE) { (void) fprintf(vfile, gettext( "a %s attribute %s link to " - "attribute %s\n"), - name, component, lp->attrname); + "%s attribute %s\n"), + name, component, name, + lp->attrname); } else { (void) fprintf(vfile, gettext( "a %s link to %s\n"), @@ -6853,7 +7695,8 @@ put_link(char *name, char *longname, char *component, lp->count = stbuf.st_nlink - 1; if (filetype == XATTR_FILE) { (void) strcpy(lp->pathname, longname); - (void) strcpy(lp->attrname, component); + (void) strcpy(lp->attrname, + component); } else { (void) strcpy(lp->pathname, longname); (void) strcpy(lp->attrname, ""); @@ -6867,8 +7710,8 @@ put_link(char *name, char *longname, char *component, } static int -put_extra_attributes(char *longname, char *shortname, char *prefix, - int filetype, char typeflag) +put_extra_attributes(char *longname, char *shortname, char *longattrname, + char *prefix, int filetype, char typeflag) { static acl_t *aclp = NULL; int error; @@ -6878,8 +7721,8 @@ put_extra_attributes(char *longname, char *shortname, char *prefix, aclp = NULL; } #if defined(O_XATTR) - if (atflag && filetype == XATTR_FILE) { - if (put_xattr_hdr(longname, shortname, prefix, + if ((atflag || saflag) && (filetype == XATTR_FILE)) { + if (put_xattr_hdr(longname, shortname, longattrname, prefix, typeflag, filetype, NULL)) { return (1); } @@ -6927,7 +7770,7 @@ put_extra_attributes(char *longname, char *shortname, char *prefix, #if defined(O_XATTR) static int -put_xattr_hdr(char *longname, char *shortname, char *prefix, +put_xattr_hdr(char *longname, char *shortname, char *longattrname, char *prefix, int typeflag, int filetype, struct linkbuf *lp) { char *lname = NULL; @@ -6943,7 +7786,7 @@ put_xattr_hdr(char *longname, char *shortname, char *prefix, fatal(gettext("Out of Memory.")); } sname = malloc(sizeof (char) * strlen(shortname) + - strlen(".hdr")); + strlen(".hdr") + 1); if (sname == NULL) { fatal(gettext("Out of Memory.")); } @@ -6960,7 +7803,7 @@ put_xattr_hdr(char *longname, char *shortname, char *prefix, /* * dump extended attr lookup info */ - prepare_xattr(&attrbuf, longname, shortname, typeflag, lp, &attrlen); + prepare_xattr(&attrbuf, longname, longattrname, typeflag, lp, &attrlen); write_ancillary(&dblock, attrbuf, attrlen, _XATTR_HDRTYPE); (void) sprintf(lname, "/dev/null/%s", shortname); @@ -6981,15 +7824,17 @@ put_xattr_hdr(char *longname, char *shortname, char *prefix, #if defined(O_XATTR) static int -read_xattr_hdr() +read_xattr_hdr(attr_data_t **attrinfo) { char buf[TBLOCK]; + char *attrparent = NULL; blkcnt_t blocks; char *tp; off_t bytes; int comp_len, link_len; int namelen; - + int attrparentlen; + int parentfilelen; if (dblock.dbuf.typeflag != _XATTR_HDRTYPE) return (1); @@ -7037,7 +7882,40 @@ read_xattr_hdr() else xattr_linkp = NULL; - xattraname = xattrp->h_names + strlen(xattrp->h_names) + 1; + /* + * Gather the attribute path from the filename and attrnames section. + * The filename and attrnames section can be composed of two or more + * path segments separated by a null character. The first segment + * is the path to the parent file that roots the entire sequence in + * the normal name space. The remaining segments describes a path + * rooted at the hidden extended attribute directory of the leaf file of + * the previous segment, making it possible to name attributes on + * attributes. + */ + parentfilelen = strlen(xattrp->h_names); + xattrapath = xattrp->h_names + parentfilelen + 1; + if ((strlen(xattrapath) + parentfilelen + 2) < namelen) { + /* + * The attrnames section contains a system attribute on an + * attribute. Save the name of the attribute for use later, + * and replace the null separating the attribute name from + * the system attribute name with a '/' so that xattrapath can + * be used to display messages with the full attribute path name + * rooted at the hidden attribute directory of the base file + * in normal name space. + */ + attrparent = strdup(xattrapath); + attrparentlen = strlen(attrparent); + xattrapath[attrparentlen] = '/'; + } + if ((fill_in_attr_info((attrparent == NULL) ? xattrapath : + xattrapath + attrparentlen + 1, xattrapath, attrparent, + -1, 0, attrinfo)) == 1) { + free(attrparent); + return (1); + } + + /* Gather link info */ if (xattr_linkp) { xattr_linkaname = xattr_linkp->h_names + strlen(xattr_linkp->h_names) + 1; @@ -7049,7 +7927,7 @@ read_xattr_hdr() } #else static int -read_xattr_hdr() +read_xattr_hdr(attr_data_t **attrinfo) { return (0); } @@ -7138,10 +8016,13 @@ get_component(char *path) } #endif +#if defined(O_XATTR) static int -retry_attrdir_open(char *name) +retry_open_attr(int pdirfd, int cwd, char *dirp, char *pattr, char *name, + int oflag, mode_t mode) { - int dirfd = -1; + int dirfd; + int ofilefd = -1; struct timeval times[2]; mode_t newmode; struct stat parentstat; @@ -7152,57 +8033,82 @@ retry_attrdir_open(char *name) * We couldn't get to attrdir. See if its * just a mode problem on the parent file. * for example: a mode such as r-xr--r-- - * won't let us create an attribute dir - * if it doesn't already exist. + * on a ufs file system without extended + * system attribute support won't let us + * create an attribute dir if it doesn't + * already exist, and on a ufs file system + * with extended system attribute support + * won't let us open the attribute for + * write. * * If file has a non-trivial ACL, then save it * off so that we can place it back on after doing * chmod's. */ - - if (stat(name, &parentstat) == -1) { - (void) fprintf(stderr, gettext("tar: cannot stat file %s %s\n"), - name, strerror(errno)); + if ((dirfd = openat(cwd, (pattr == NULL) ? dirp : pattr, + O_RDONLY)) == -1) { + return (-1); + } + if (fstat(dirfd, &parentstat) == -1) { + (void) fprintf(stderr, gettext( + "tar: cannot stat %sfile %s: %s\n"), + (pdirfd == -1) ? "" : gettext("parent of "), + (pdirfd == -1) ? dirp : name, strerror(errno)); return (-1); } - if ((error = acl_get(name, ACL_NO_TRIVIAL, &aclp)) != 0) { - (void) fprintf(stderr, gettext("tar: failed to retrieve ACL on" - " %s %s\n"), name, strerror(errno)); + if ((error = facl_get(dirfd, ACL_NO_TRIVIAL, &aclp)) != 0) { + (void) fprintf(stderr, gettext( + "tar: failed to retrieve ACL on %sfile %s: %s\n"), + (pdirfd == -1) ? "" : gettext("parent of "), + (pdirfd == -1) ? dirp : name, strerror(errno)); return (-1); } newmode = S_IWUSR | parentstat.st_mode; - if (chmod(name, newmode) == -1) { + if (fchmod(dirfd, newmode) == -1) { (void) fprintf(stderr, - gettext("tar: cannot chmod file %s to %o %s\n"), - name, newmode, strerror(errno)); + gettext( + "tar: cannot fchmod %sfile %s to %o: %s\n"), + (pdirfd == -1) ? "" : gettext("parent of "), + (pdirfd == -1) ? dirp : name, newmode, strerror(errno)); if (aclp) acl_free(aclp); return (-1); } - dirfd = attropen(name, ".", O_RDONLY); - /* - * Don't print error message if attropen() failed, - * caller will print message. - */ + if (pdirfd == -1) { + /* + * We weren't able to create the attribute directory before. + * Now try again. + */ + ofilefd = attropen(dirp, ".", oflag); + } else { + /* + * We weren't able to create open the attribute before. + * Now try again. + */ + ofilefd = openat(pdirfd, name, oflag, mode); + } /* * Put mode back to original */ - if (chmod(name, parentstat.st_mode) == -1) { + if (fchmod(dirfd, parentstat.st_mode) == -1) { (void) fprintf(stderr, - gettext("tar: cannot chmod file %s to %o %s\n"), - name, newmode, strerror(errno)); + gettext("tar: cannot chmod %sfile %s to %o: %s\n"), + (pdirfd == -1) ? "" : gettext("parent of "), + (pdirfd == -1) ? dirp : name, newmode, strerror(errno)); } if (aclp) { - error = acl_set(name, aclp); + error = facl_set(dirfd, aclp); if (error) { (void) fprintf(stderr, - gettext("tar: %s: failed to set acl entries\n"), - name); + gettext("tar: failed to set acl entries on " + "%sfile %s\n"), + (pdirfd == -1) ? "" : gettext("parent of "), + (pdirfd == -1) ? dirp : name); } acl_free(aclp); } @@ -7215,10 +8121,14 @@ retry_attrdir_open(char *name) times[0].tv_usec = 0; times[1].tv_sec = parentstat.st_mtime; times[1].tv_usec = 0; - (void) utimes(name, times); - return (dirfd); + (void) futimesat(cwd, (pattr == NULL) ? dirp : pattr, times); + + (void) close(dirfd); + + return (ofilefd); } +#endif #if !defined(O_XATTR) static int diff --git a/usr/src/cmd/truss/systable.c b/usr/src/cmd/truss/systable.c index 58b8065178f5..623a367e385d 100644 --- a/usr/src/cmd/truss/systable.c +++ b/usr/src/cmd/truss/systable.c @@ -720,6 +720,7 @@ static const struct systable fsatsystable[] = { {"futimesat", 4, DEC, NOV, HID, ATC, STG, HEX}, /* 6 */ {"renameat", 5, DEC, NOV, HID, ATC, STG, DEC, STG}, /* 7 */ {"__accessat", 5, DEC, NOV, HID, ATC, STG, ACC}, /* 8 */ +{"__openattrdirat", 3, DEC, NOV, HID, ATC, STG}, /* 9 */ {"openat", 4, DEC, NOV, HID, ATC, STG, OPN}, /* N - 2 */ {"openat64", 4, DEC, NOV, HID, ATC, STG, OPN}, /* N - 1 */ }; @@ -955,6 +956,7 @@ const struct sysalias sysalias[] = { { "futimesat", SYS_fsat }, { "renameat", SYS_fsat }, { "__accessat", SYS_fsat }, + { "__openattrdirat", SYS_fsat }, { "lgrpsys", SYS_lgrpsys }, { "getrusage", SYS_rusagesys }, { "getrusage_chld", SYS_rusagesys }, @@ -1248,6 +1250,7 @@ getsubcode(private_t *pri) case 6: case 7: case 8: + case 9: subcode = arg0; } break; diff --git a/usr/src/cmd/unpack/Makefile b/usr/src/cmd/unpack/Makefile index 076a2013e8a6..31e0e2455693 100644 --- a/usr/src/cmd/unpack/Makefile +++ b/usr/src/cmd/unpack/Makefile @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -22,7 +21,7 @@ # #ident "%Z%%M% %I% %E% SMI" # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -30,7 +29,9 @@ PROG= unpack include ../Makefile.cmd CFLAGS += $(CCVERBOSE) -LDLIBS += -lsec +CPPFLAGS += -D_FILE_OFFSET_BITS=64 + +LDLIBS += -lcmdutils -lsec XGETFLAGS += -a -x unpack.xcl .KEEP_STATE: diff --git a/usr/src/cmd/unpack/unpack.c b/usr/src/cmd/unpack/unpack.c index 44f70ac51ba8..b95909d4a399 100644 --- a/usr/src/cmd/unpack/unpack.c +++ b/usr/src/cmd/unpack/unpack.c @@ -23,11 +23,11 @@ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.21 */ +#pragma ident "%Z%%M% %I% %E% SMI" /* * Huffman decompressor @@ -35,24 +35,16 @@ * or unpack filename... */ -#include -#include #include #include -#include -#include -#include #include #include -#include -#include #include -#include #include #include +#include static struct utimbuf u_times; - static jmp_buf env; static struct stat status; static char *argv0, *argvk; @@ -91,7 +83,11 @@ static int decode(); static int getwdsize(); static int getch(); static int getdict(); -static int mv_xattrs(); + +/* Extended system attribute support */ + +static int saflg = 0; + /* read in the dictionary portion and build decoding structures */ /* return 1 if successful, 0 otherwise */ @@ -113,7 +109,7 @@ getdict() inleft = read(infile, &inbuff[0], BUFSIZ); if (inleft < 0) { (void) fprintf(stderr, gettext( - "%s: %s: read error: "), argv0, filename); + "%s: %s: read error: "), argv0, filename); perror(""); return (0); } @@ -135,7 +131,7 @@ getdict() maxlev = *inp++ & 0377; if (maxlev > 24) { goof: (void) fprintf(stderr, gettext( - "%s: %s: not in packed format\n"), argv0, filename); + "%s: %s: not in packed format\n"), argv0, filename); return (0); } for (i = 1; i <= maxlev; i++) @@ -185,16 +181,16 @@ decode() inleft = read(infile, inp = &inbuff[0], BUFSIZ); if (inleft < 0) { (void) fprintf(stderr, gettext( - "%s: %s: read error: "), - argv0, filename); + "%s: %s: read error: "), + argv0, filename); perror(""); return (0); } } if (--inleft < 0) { uggh: (void) fprintf(stderr, gettext( - "%s: %s: unpacking error\n"), - argv0, filename); + "%s: %s: unpacking error\n"), + argv0, filename); return (0); } c = *inp++; @@ -208,10 +204,12 @@ uggh: (void) fprintf(stderr, gettext( p = &tree[lev][j]; if (p == eof) { c = outp - &outbuff[0]; - if (write(outfile, &outbuff[0], c) != c) { -wrerr: (void) fprintf(stderr, gettext( - "%s: %s: write error: "), - argv0, argvk); + if (write(outfile, &outbuff[0], c) + != c) { +wrerr: (void) fprintf(stderr, + gettext( + "%s: %s: write error: "), + argv0, argvk); perror(""); return (0); } @@ -223,7 +221,7 @@ wrerr: (void) fprintf(stderr, gettext( *outp++ = *p; if (outp == &outbuff[BUFSIZ]) { if (write(outfile, outp = &outbuff[0], - BUFSIZ) != BUFSIZ) + BUFSIZ) != BUFSIZ) goto wrerr; origsize -= BUFSIZ; } @@ -248,25 +246,28 @@ main(int argc, char *argv[]) int max_name; void onsig(int); acl_t *aclp = NULL; - + int c; + char *progname; + int sattr_exist = 0; + int xattr_exist = 0; if (signal(SIGHUP, SIG_IGN) != SIG_IGN) #ifdef __STDC__ - signal((int)SIGHUP, onsig); + (void) signal((int)SIGHUP, onsig); #else - signal((int)SIGHUP, onsig); + (void) signal((int)SIGHUP, onsig); #endif if (signal(SIGINT, SIG_IGN) != SIG_IGN) #ifdef __STDC__ - signal((int)SIGINT, onsig); + (void) signal((int)SIGINT, onsig); #else - signal((int)SIGINT, onsig); + (void) signal((int)SIGINT, onsig); #endif if (signal(SIGTERM, SIG_IGN) != SIG_IGN) #ifdef __STDC__ - signal((int)SIGTERM, onsig); + (void) signal((int)SIGTERM, onsig); #else - signal(SIGTERM, onsig); + (void) signal(SIGTERM, onsig); #endif (void) setlocale(LC_ALL, ""); @@ -275,16 +276,28 @@ main(int argc, char *argv[]) #endif (void) textdomain(TEXT_DOMAIN); + if (progname = strrchr(argv[0], '/')) + ++progname; + else + progname = argv[0]; + p1 = *argv; - while (*p1++); /* Point p1 to end of argv[0] string */ + while (*p1++) { }; /* Point p1 to end of argv[0] string */ while (--p1 >= *argv) if (*p1 == '/')break; *argv = p1 + 1; argv0 = argv[0]; if (**argv == 'p')pcat++; /* User entered pcat (or /xx/xx/pcat) */ - while (getopt(argc, argv, "") != EOF) - ++errflg; + while ((c = getopt(argc, argv, "/")) != EOF) { + if (c == '/') { + if (pcat) + ++errflg; + else + saflg++; + } else + ++errflg; + } /* * Check for invalid option. Also check for missing * file operand, ie: "unpack" or "pcat". @@ -292,7 +305,13 @@ main(int argc, char *argv[]) argc -= optind; argv = &argv[optind]; if (errflg || argc < 1) { - (void) fprintf(stderr, gettext("usage: %s file...\n"), argv0); + if (!pcat) + (void) fprintf(stderr, + gettext("usage: %s [-/] file...\n"), argv0); + else + (void) fprintf(stderr, gettext("usage: %s file...\n"), + argv0); + if (argc < 1) { /* * return 1 for usage error when no file was specified @@ -329,8 +348,8 @@ main(int argc, char *argv[]) *cp = '\0'; if ((infile = open(filename, O_RDONLY)) == -1) { (void) fprintf(stderr, gettext( - "%s: %s: cannot open: "), - argv0, filename); + "%s: %s: cannot open: "), + argv0, filename); perror(""); goto done; } @@ -342,7 +361,7 @@ main(int argc, char *argv[]) if (error != 0) { (void) printf(gettext( "%s: %s: cannot retrieve ACL : %s\n"), - argv0, filename, acl_strerror(error)); + argv0, filename, acl_strerror(error)); } max_name = pathconf(filename, _PC_NAME_MAX); @@ -352,38 +371,69 @@ main(int argc, char *argv[]) } if (i >= (MAXPATHLEN-1) || (i - sep - 1) > max_name) { (void) fprintf(stderr, gettext( - "%s: %s: file name too long\n"), - argv0, argvk); + "%s: %s: file name too long\n"), + argv0, argvk); goto done; } if (stat(argvk, &status) != -1) { (void) fprintf(stderr, gettext( - "%s: %s: already exists\n"), - argv0, argvk); + "%s: %s: already exists\n"), + argv0, argvk); goto done; } (void) fstat(infile, &status); if (status.st_nlink != 1) { (void) printf(gettext( - "%s: %s: Warning: file has links\n"), - argv0, filename); + "%s: %s: Warning: file has links\n"), + argv0, filename); } if ((outfile = creat(argvk, status.st_mode)) == -1) { (void) fprintf(stderr, gettext( - "%s: %s: cannot create: "), - argv0, argvk); + "%s: %s: cannot create: "), + argv0, argvk); perror(""); goto done; } rmflg = 1; } - if (getdict() && /* unpack */ - (pcat || - (pathconf(filename, _PC_XATTR_EXISTS) != 1) || - (mv_xattrs(infile, outfile, - filename, 0) == 0))) { + if (getdict()) { /* unpack */ + if (pathconf(filename, _PC_XATTR_EXISTS) == 1) + xattr_exist = 1; + if (saflg && sysattr_support(filename, + _PC_SATTR_EXISTS) == 1) + sattr_exist = 1; + if (pcat || xattr_exist || sattr_exist) { + if (mv_xattrs(progname, filename, argv[k], + sattr_exist, 0) + != 0) { + /* Move attributes back ... */ + xattr_exist = 0; + sattr_exist = 0; + if (pathconf(argvk, _PC_XATTR_EXISTS) + == 1) + xattr_exist = 1; + if (saflg && sysattr_support(argvk, + _PC_SATTR_EXISTS) == 1) + sattr_exist = 1; + if (!pcat && (xattr_exist || + sattr_exist)) { + (void) mv_xattrs(progname, + argv[k], filename, + sattr_exist, 1); + (void) unlink(argvk); + goto done; + } + } else { + if (!pcat) + (void) unlink(filename); + } + } else if (!pcat) + (void) unlink(filename); + if (!pcat) { + (void) printf(gettext("%s: %s: unpacked\n"), + argv0, argvk); /* * preserve acc & mod dates */ @@ -392,8 +442,8 @@ main(int argc, char *argv[]) if (utime(argvk, &u_times) != 0) { errflg++; (void) fprintf(stderr, gettext( - "%s: cannot change times on %s: "), - argv0, argvk); + "%s: cannot change times on %s: "), + argv0, argvk); perror(""); } if (chmod(argvk, status.st_mode) != 0) { @@ -405,7 +455,7 @@ main(int argc, char *argv[]) perror(""); } (void) chown(argvk, - status.st_uid, status.st_gid); + status.st_uid, status.st_gid); if (aclp && (facl_set(outfile, aclp) < 0)) { (void) printf(gettext("%s: cannot " "set ACL on %s: "), argv0, argvk); @@ -413,22 +463,10 @@ main(int argc, char *argv[]) } rmflg = 0; - (void) printf(gettext("%s: %s: unpacked\n"), - argv0, argvk); - (void) unlink(filename); - } if (!errflg) fcount--; /* success after all */ } - else - if (!pcat) { - if (pathconf(argvk, _PC_XATTR_EXISTS) == 1) { - (void) mv_xattrs(outfile, infile, - argvk, 1); - } - (void) unlink(argvk); - } done: (void) close(infile); if (!pcat) (void) close(outfile); @@ -465,8 +503,8 @@ expand() origsize += (unsigned)getwdsize(); if (origsize == 0 || is_eof) { (void) fprintf(stderr, gettext( - "%s: %s: not in packed format\n"), - argv0, filename); + "%s: %s: not in packed format\n"), + argv0, filename); return (0); } t = Tree; @@ -479,8 +517,8 @@ expand() */ if (is_eof) { (void) fprintf(stderr, gettext( - "%s: %s: not in packed format\n"), - argv0, filename); + "%s: %s: not in packed format\n"), + argv0, filename); return (0); } *t++ = i & 0377; @@ -491,8 +529,8 @@ expand() */ if (is_eof) { (void) fprintf(stderr, gettext( - "%s: %s: not in packed format\n"), - argv0, filename); + "%s: %s: not in packed format\n"), + argv0, filename); return (0); } @@ -506,8 +544,8 @@ expand() */ if (word == 0 && is_eof && origsize > 0) { (void) fprintf(stderr, gettext( - "%s: %s: not in packed format\n"), - argv0, filename); + "%s: %s: not in packed format\n"), + argv0, filename); return (0); } bit = 16; @@ -533,8 +571,8 @@ getch() inleft = read(infile, inp = inbuff, BUFSIZ); if (inleft < 0) { (void) fprintf(stderr, gettext( - "%s: %s: read error: "), - argv0, filename); + "%s: %s: read error: "), + argv0, filename); perror(""); longjmp(env, 1); } else { /* reached EOF, report it */ @@ -572,7 +610,9 @@ onsig(int sig) /* created by unpack and not yet done */ if (rmflg == 1) (void) unlink(argvk); - exit(1); + /* To quiet lint noise */ + if (sig == SIGTERM || sig == SIGHUP || sig == SIGINT) + exit(1); } void @@ -585,79 +625,10 @@ putch(char c) n = write(outfile, outp = outbuff, BUFSIZ); if (n < BUFSIZ) { (void) fprintf(stderr, gettext( - "%s: %s: write error: "), - argv0, argvk); + "%s: %s: write error: "), + argv0, argvk); perror(""); longjmp(env, 2); } } } - -/* - * mv_xattrs - move (via renameat) all of the extended attributes - * associated with the file referenced by infd to the file - * referenced by outfd. The infile and silent arguments are - * provided for error message processing. This function - * returns 0 on success and -1 on error. - */ -static int -mv_xattrs(int infd, int outfd, char *infile, int silent) -{ - int indfd, outdfd, tmpfd; - DIR *dirp = NULL; - struct dirent *dp = NULL; - int error = 0; - char *etext; - - indfd = outdfd = tmpfd = -1; - - if ((indfd = openat(infd, ".", O_RDONLY|O_XATTR)) == -1) { - etext = gettext("cannot open source"); - error = -1; - goto out; - } - - if ((outdfd = openat(outfd, ".", O_RDONLY|O_XATTR)) == -1) { - etext = gettext("cannot open target"); - error = -1; - goto out; - } - - if ((tmpfd = dup(indfd)) == -1) { - etext = gettext("cannot dup descriptor"); - error = -1; - goto out; - - } - if ((dirp = fdopendir(tmpfd)) == NULL) { - etext = gettext("cannot access source"); - error = -1; - goto out; - } - - while (dp = readdir(dirp)) { - if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') || - (dp->d_name[0] == '.' && dp->d_name[1] == '.' && - dp->d_name[2] == '\0')) - continue; - if ((renameat(indfd, dp->d_name, outdfd, dp->d_name)) == -1) { - etext = dp->d_name; - error = -1; - goto out; - } - } -out: - if (error == -1 && silent == 0) { - fprintf(stderr, gettext( - "unpack: %s: cannot move extended attributes, "), - infile); - perror(etext); - } - if (dirp) - closedir(dirp); - if (indfd != -1) - close(indfd); - if (outdfd != -1) - close(outdfd); - return (error); -} diff --git a/usr/src/cmd/wbem/provider/c/wbem_disk/common/disk_descriptors.c b/usr/src/cmd/wbem/provider/c/wbem_disk/common/disk_descriptors.c index d962c94291a1..c59d9f95438a 100644 --- a/usr/src/cmd/wbem/provider/c/wbem_disk/common/disk_descriptors.c +++ b/usr/src/cmd/wbem/provider/c/wbem_disk/common/disk_descriptors.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -285,7 +284,7 @@ disk_descriptor_toCCIMInstance(char *hostname, dm_descriptor_t desc, return ((CCIMInstance *)NULL); } - nvlist_lookup_uint32(nvlp, DM_BLOCKSIZE, &blocksize); + (void) nvlist_lookup_uint32(nvlp, DM_BLOCKSIZE, &blocksize); error = snprintf(buf, sizeof (buf), "%llu", ui64 * blocksize); if (error < 0) { diff --git a/usr/src/cmd/wbem/provider/c/wbem_disk/common/drive_descriptors.c b/usr/src/cmd/wbem/provider/c/wbem_disk/common/drive_descriptors.c index 37c38e528204..200aa2ed4ed5 100644 --- a/usr/src/cmd/wbem/provider/c/wbem_disk/common/drive_descriptors.c +++ b/usr/src/cmd/wbem/provider/c/wbem_disk/common/drive_descriptors.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2002 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -724,7 +723,7 @@ drive_descriptors_toCCIMObjPathInstList(char *providerName, dm_descriptor_t *dp, } drvtype = DM_DT_UNKNOWN; - nvlist_lookup_uint32(attrs, DM_DRVTYPE, &drvtype); + (void) nvlist_lookup_uint32(attrs, DM_DRVTYPE, &drvtype); nvlist_free(attrs); switch (drvtype) { diff --git a/usr/src/cmd/wbem/provider/c/wbem_disk/common/methods.c b/usr/src/cmd/wbem/provider/c/wbem_disk/common/methods.c index 9f94fc99c173..72489a3dbe28 100644 --- a/usr/src/cmd/wbem/provider/c/wbem_disk/common/methods.c +++ b/usr/src/cmd/wbem/provider/c/wbem_disk/common/methods.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2002 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -824,23 +823,23 @@ disk_geometry(char *media_name, ulong_t *geometry) geometry[0] = val32; val32 = 0; - nvlist_lookup_uint32(attrs, DM_NHEADS, &val32); + (void) nvlist_lookup_uint32(attrs, DM_NHEADS, &val32); geometry[1] = val32; val32 = 0; - nvlist_lookup_uint32(attrs, DM_BLOCKSIZE, &val32); + (void) nvlist_lookup_uint32(attrs, DM_BLOCKSIZE, &val32); geometry[2] = (geometry[1] * geometry[0]) * val32; val32 = 0; - nvlist_lookup_uint32(attrs, DM_NPHYSCYLINDERS, &val32); + (void) nvlist_lookup_uint32(attrs, DM_NPHYSCYLINDERS, &val32); geometry[3] = val32; val32 = 0; - nvlist_lookup_uint32(attrs, DM_NCYLINDERS, &val32); + (void) nvlist_lookup_uint32(attrs, DM_NCYLINDERS, &val32); geometry[4] = val32; val32 = 0; - nvlist_lookup_uint32(attrs, DM_NALTCYLINDERS, &val32); + (void) nvlist_lookup_uint32(attrs, DM_NALTCYLINDERS, &val32); geometry[5] = val32; val32 = 0; /* This one is probably there only in x86 machines. */ - nvlist_lookup_uint32(attrs, DM_NACTUALCYLINDERS, &val32); + (void) nvlist_lookup_uint32(attrs, DM_NACTUALCYLINDERS, &val32); geometry[6] = val32; nvlist_free(attrs); diff --git a/usr/src/cmd/zdb/zdb.c b/usr/src/cmd/zdb/zdb.c index 86278f1d8ba1..055c283c1fc0 100644 --- a/usr/src/cmd/zdb/zdb.c +++ b/usr/src/cmd/zdb/zdb.c @@ -720,7 +720,7 @@ dump_dsl_dir(objset_t *os, uint64_t object, void *data, size_t size) if (dd == NULL) return; - ASSERT(size == sizeof (*dd)); + ASSERT3U(size, >=, sizeof (dsl_dir_phys_t)); crtime = dd->dd_creation_time; nicenum(dd->dd_used_bytes, used); @@ -925,7 +925,7 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES] = { dump_zap, /* DSL props */ dump_dsl_dataset, /* DSL dataset */ dump_znode, /* ZFS znode */ - dump_acl, /* ZFS ACL */ + dump_acl, /* ZFS V0 ACL */ dump_uint8, /* ZFS plain file */ dump_zpldir, /* ZFS directory */ dump_zap, /* ZFS master node */ @@ -940,6 +940,10 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES] = { dump_uint64, /* SPA history offsets */ dump_zap, /* Pool properties */ dump_zap, /* DSL permissions */ + dump_acl, /* ZFS ACL */ + dump_uint8, /* ZFS SYSACL */ + dump_none, /* FUID nvlist */ + dump_packed_nvlist, /* FUID nvlist size */ }; static void diff --git a/usr/src/cmd/zdb/zdb_il.c b/usr/src/cmd/zdb/zdb_il.c index 10dfe20edaa6..02d35a050332 100644 --- a/usr/src/cmd/zdb/zdb_il.c +++ b/usr/src/cmd/zdb/zdb_il.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -233,19 +233,26 @@ typedef struct zil_rec_info { } zil_rec_info_t; static zil_rec_info_t zil_rec_info[TX_MAX_TYPE] = { - { NULL, "Total " }, - { zil_prt_rec_create, "TX_CREATE " }, - { zil_prt_rec_create, "TX_MKDIR " }, - { zil_prt_rec_create, "TX_MKXATTR " }, - { zil_prt_rec_create, "TX_SYMLINK " }, - { zil_prt_rec_remove, "TX_REMOVE " }, - { zil_prt_rec_remove, "TX_RMDIR " }, - { zil_prt_rec_link, "TX_LINK " }, - { zil_prt_rec_rename, "TX_RENAME " }, - { zil_prt_rec_write, "TX_WRITE " }, - { zil_prt_rec_truncate, "TX_TRUNCATE" }, - { zil_prt_rec_setattr, "TX_SETATTR " }, - { zil_prt_rec_acl, "TX_ACL " }, + { NULL, "Total " }, + { zil_prt_rec_create, "TX_CREATE " }, + { zil_prt_rec_create, "TX_MKDIR " }, + { zil_prt_rec_create, "TX_MKXATTR " }, + { zil_prt_rec_create, "TX_SYMLINK " }, + { zil_prt_rec_remove, "TX_REMOVE " }, + { zil_prt_rec_remove, "TX_RMDIR " }, + { zil_prt_rec_link, "TX_LINK " }, + { zil_prt_rec_rename, "TX_RENAME " }, + { zil_prt_rec_write, "TX_WRITE " }, + { zil_prt_rec_truncate, "TX_TRUNCATE " }, + { zil_prt_rec_setattr, "TX_SETATTR " }, + { zil_prt_rec_acl, "TX_ACL_V0 " }, + { zil_prt_rec_acl, "TX_ACL_ACL " }, + { zil_prt_rec_create, "TX_CREATE_ACL " }, + { zil_prt_rec_create, "TX_CREATE_ATTR " }, + { zil_prt_rec_create, "TX_CREATE_ACL_ATTR " }, + { zil_prt_rec_create, "TX_MKDIR_ACL " }, + { zil_prt_rec_create, "TX_MKDIR_ATTR " }, + { zil_prt_rec_create, "TX_MKDIR_ACL_ATTR " }, }; /* ARGSUSED */ @@ -255,12 +262,14 @@ print_log_record(zilog_t *zilog, lr_t *lr, void *arg, uint64_t claim_txg) int txtype; int verbose = MAX(dump_opt['d'], dump_opt['i']); + /* reduce size of txtype to strip off TX_CI bit */ txtype = lr->lrc_txtype; ASSERT(txtype != 0 && (uint_t)txtype < TX_MAX_TYPE); ASSERT(lr->lrc_txg); - (void) printf("\t\t%s len %6llu, txg %llu, seq %llu\n", + (void) printf("\t\t%s%s len %6llu, txg %llu, seq %llu\n", + (lr->lrc_txtype & TX_CI) ? "CI-" : "", zil_rec_info[txtype].zri_name, (u_longlong_t)lr->lrc_reclen, (u_longlong_t)lr->lrc_txg, diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c index 3fe2cd1705c7..e7ea903595d1 100644 --- a/usr/src/cmd/zfs/zfs_main.c +++ b/usr/src/cmd/zfs/zfs_main.c @@ -283,7 +283,9 @@ usage_prop_cb(int prop, void *cb) (void) fprintf(fp, "\t%-13s ", zfs_prop_to_name(prop)); - if (zfs_prop_readonly(prop)) + if (prop == ZFS_PROP_CASE) + (void) fprintf(fp, "NO "); + else if (zfs_prop_readonly(prop)) (void) fprintf(fp, " NO "); else (void) fprintf(fp, " YES "); @@ -1348,6 +1350,35 @@ upgrade_set_callback(zfs_handle_t *zhp, void *data) upgrade_cbdata_t *cb = data; int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); + if (cb->cb_version >= ZPL_VERSION_FUID) { + char pool_name[MAXPATHLEN]; + zpool_handle_t *zpool_handle; + int spa_version; + char *p; + + if (zfs_prop_get(zhp, ZFS_PROP_NAME, pool_name, + sizeof (pool_name), NULL, NULL, 0, B_FALSE) != 0) + return (-1); + + if (p = strchr(pool_name, '/')) + *p = '\0'; + if ((zpool_handle = zpool_open(g_zfs, pool_name)) == NULL) + return (-1); + + spa_version = zpool_get_prop_int(zpool_handle, + ZPOOL_PROP_VERSION, NULL); + zpool_close(zpool_handle); + if (spa_version < SPA_VERSION_FUID) { + /* can't upgrade */ + (void) printf(gettext("%s: can not be upgraded; " + "the pool version needs to first be upgraded\nto " + "version %d\n\n"), + zfs_get_name(zhp), SPA_VERSION_FUID); + cb->cb_numfailed++; + return (0); + } + } + /* upgrade */ if (version < cb->cb_version) { char verstr[16]; @@ -1442,6 +1473,8 @@ zfs_do_upgrade(int argc, char **argv) "---------------\n"); (void) printf(gettext(" 1 Initial ZFS filesystem version\n")); (void) printf(gettext(" 2 Enhanced directory entries\n")); + (void) printf(gettext(" 3 Case insensitive and File system " + "unique identifer (FUID)\n")); (void) printf(gettext("\nFor more information on a particular " "version, including supported releases, see:\n\n")); (void) printf("http://www.opensolaris.org/os/community/zfs/" @@ -1494,7 +1527,7 @@ zfs_do_upgrade(int argc, char **argv) * ... * * -r Recurse over all children - * -H Scripted mode; elide headers and separate colums by tabs + * -H Scripted mode; elide headers and separate columns by tabs * -o Control which fields to display. * -t Control which object types to display. * -s Specify sort columns, descending order. @@ -2121,7 +2154,7 @@ zfs_do_set(int argc, char **argv) * zfs snapshot [-r] * * Creates a snapshot with the given name. While functionally equivalent to - * 'zfs create', it is a separate command to diffferentiate intent. + * 'zfs create', it is a separate command to differentiate intent. */ static int zfs_do_snapshot(int argc, char **argv) @@ -2828,15 +2861,17 @@ dataset_cmp(const void *a, const void *b) * Share or mount a dataset. */ static int -share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit, - const char *options) +share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol, + boolean_t explicit, const char *options) { char mountpoint[ZFS_MAXPROPLEN]; char shareopts[ZFS_MAXPROPLEN]; + char smbshareopts[ZFS_MAXPROPLEN]; const char *cmdname = op == OP_SHARE ? "share" : "mount"; struct mnttab mnt; uint64_t zoned, canmount; zfs_type_t type = zfs_get_type(zhp); + boolean_t shared_nfs, shared_smb; assert(type & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)); @@ -2877,9 +2912,12 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit, sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); + verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts, + sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0); canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); - if (op == OP_SHARE && strcmp(shareopts, "off") == 0) { + if (op == OP_SHARE && strcmp(shareopts, "off") == 0 && + strcmp(smbshareopts, "off") == 0) { if (!explicit) return (0); @@ -2935,7 +2973,15 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit, */ switch (op) { case OP_SHARE: - if (zfs_is_shared_nfs(zhp, NULL)) { + + shared_nfs = zfs_is_shared_nfs(zhp, NULL); + shared_smb = zfs_is_shared_smb(zhp, NULL); + + if (shared_nfs && shared_smb || + (shared_nfs && strcmp(shareopts, "on") == 0 && + strcmp(smbshareopts, "off") == 0) || + (shared_smb && strcmp(smbshareopts, "on") == 0 && + strcmp(shareopts, "off") == 0)) { if (!explicit) return (0); @@ -2949,8 +2995,23 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit, zfs_mount(zhp, NULL, 0) != 0) return (1); - if (zfs_share_nfs(zhp) != 0) + if (protocol == NULL) { + if (zfs_shareall(zhp) != 0) + return (1); + } else if (strcmp(protocol, "nfs") == 0) { + if (zfs_share_nfs(zhp)) + return (1); + } else if (strcmp(protocol, "smb") == 0) { + if (zfs_share_smb(zhp)) + return (1); + } else { + (void) fprintf(stderr, gettext("cannot share " + "'%s': invalid share type '%s' " + "specified\n"), + zfs_get_name(zhp), protocol); return (1); + } + break; case OP_MOUNT: @@ -3098,20 +3159,22 @@ share_mount(int op, int argc, char **argv) if (do_all) { zfs_handle_t **dslist = NULL; size_t i, count = 0; + char *protocol = NULL; if (op == OP_MOUNT) { types = ZFS_TYPE_FILESYSTEM; } else if (argc > 0) { - if (strcmp(argv[0], "nfs") == 0) { + if (strcmp(argv[0], "nfs") == 0 || + strcmp(argv[0], "smb") == 0) { types = ZFS_TYPE_FILESYSTEM; } else if (strcmp(argv[0], "iscsi") == 0) { types = ZFS_TYPE_VOLUME; } else { (void) fprintf(stderr, gettext("share type " - "must be 'nfs' or 'iscsi'\n")); + "must be 'nfs', 'smb' or 'iscsi'\n")); usage(B_FALSE); } - + protocol = argv[0]; argc--; argv++; } else { @@ -3134,8 +3197,8 @@ share_mount(int op, int argc, char **argv) if (verbose) report_mount_progress(i, count); - if (share_mount_one(dslist[i], op, flags, B_FALSE, - options) != 0) + if (share_mount_one(dslist[i], op, flags, protocol, + B_FALSE, options) != 0) ret = 1; zfs_close(dslist[i]); } @@ -3181,7 +3244,7 @@ share_mount(int op, int argc, char **argv) if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) { ret = 1; } else { - ret = share_mount_one(zhp, op, flags, B_TRUE, + ret = share_mount_one(zhp, op, flags, NULL, B_TRUE, options); zfs_close(zhp); } @@ -3203,7 +3266,7 @@ zfs_do_mount(int argc, char **argv) } /* - * zfs share -a [nfs | iscsi] + * zfs share -a [nfs | iscsi | smb] * zfs share filesystem * * Share all filesystems, or share the given filesystem. @@ -3243,7 +3306,8 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) struct stat64 statbuf; struct extmnttab entry; const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount"; - char property[ZFS_MAXPROPLEN]; + char nfs_mnt_prop[ZFS_MAXPROPLEN]; + char smbshare_prop[ZFS_MAXPROPLEN]; /* * Search for the path in /etc/mnttab. Rather than looking for the @@ -3283,27 +3347,31 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) return (1); verify(zfs_prop_get(zhp, op == OP_SHARE ? - ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, - sizeof (property), NULL, NULL, 0, B_FALSE) == 0); + ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, nfs_mnt_prop, + sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0); + verify(zfs_prop_get(zhp, op == OP_SHARE ? + ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, smbshare_prop, + sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0); if (op == OP_SHARE) { - if (strcmp(property, "off") == 0) { + if (strcmp(nfs_mnt_prop, "off") == 0 && + strcmp(smbshare_prop, "off") == 0) { (void) fprintf(stderr, gettext("cannot unshare " "'%s': legacy share\n"), path); (void) fprintf(stderr, gettext("use " "unshare(1M) to unshare this filesystem\n")); ret = 1; - } else if (!zfs_is_shared_nfs(zhp, NULL)) { + } else if (!zfs_is_shared(zhp)) { (void) fprintf(stderr, gettext("cannot unshare '%s': " "not currently shared\n"), path); ret = 1; } else { - ret = zfs_unshareall_nfs(zhp); + ret = zfs_unshareall_bypath(zhp, path); } } else { if (is_manual) { ret = zfs_unmount(zhp, NULL, flags); - } else if (strcmp(property, "legacy") == 0) { + } else if (strcmp(nfs_mnt_prop, "legacy") == 0) { (void) fprintf(stderr, gettext("cannot unmount " "'%s': legacy mountpoint\n"), zfs_get_name(zhp)); @@ -3331,7 +3399,8 @@ unshare_unmount(int op, int argc, char **argv) int ret = 0; int types, c; zfs_handle_t *zhp; - char property[ZFS_MAXPROPLEN]; + char nfsiscsi_mnt_prop[ZFS_MAXPROPLEN]; + char sharesmb[ZFS_MAXPROPLEN]; /* check options */ while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) { @@ -3412,18 +3481,31 @@ unshare_unmount(int op, int argc, char **argv) continue; } - verify(zfs_prop_get(zhp, op == OP_SHARE ? - ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, - property, sizeof (property), NULL, NULL, - 0, B_FALSE) == 0); - - /* Ignore legacy mounts and shares */ - if ((op == OP_SHARE && - strcmp(property, "off") == 0) || - (op == OP_MOUNT && - strcmp(property, "legacy") == 0)) { - zfs_close(zhp); - continue; + switch (op) { + case OP_SHARE: + verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, + nfsiscsi_mnt_prop, + sizeof (nfsiscsi_mnt_prop), + NULL, NULL, 0, B_FALSE) == 0); + if (strcmp(nfsiscsi_mnt_prop, "off") != 0) + break; + verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, + nfsiscsi_mnt_prop, + sizeof (nfsiscsi_mnt_prop), + NULL, NULL, 0, B_FALSE) == 0); + if (strcmp(nfsiscsi_mnt_prop, "off") == 0) + continue; + break; + case OP_MOUNT: + /* Ignore legacy mounts */ + verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, + nfsiscsi_mnt_prop, + sizeof (nfsiscsi_mnt_prop), + NULL, NULL, 0, B_FALSE) == 0); + if (strcmp(nfsiscsi_mnt_prop, "legacy") == 0) + continue; + default: + break; } node = safe_malloc(sizeof (unshare_unmount_node_t)); @@ -3463,7 +3545,7 @@ unshare_unmount(int op, int argc, char **argv) switch (op) { case OP_SHARE: - if (zfs_unshare_nfs(node->un_zhp, + if (zfs_unshareall_bypath(node->un_zhp, node->un_mountp) != 0) ret = 1; break; @@ -3537,12 +3619,22 @@ unshare_unmount(int op, int argc, char **argv) if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { verify(zfs_prop_get(zhp, op == OP_SHARE ? - ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, - sizeof (property), NULL, NULL, 0, B_FALSE) == 0); + ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, + nfsiscsi_mnt_prop, sizeof (nfsiscsi_mnt_prop), NULL, + NULL, 0, B_FALSE) == 0); switch (op) { case OP_SHARE: - if (strcmp(property, "off") == 0) { + verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, + nfsiscsi_mnt_prop, + sizeof (nfsiscsi_mnt_prop), + NULL, NULL, 0, B_FALSE) == 0); + verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, + sharesmb, sizeof (sharesmb), NULL, NULL, + 0, B_FALSE) == 0); + + if (strcmp(nfsiscsi_mnt_prop, "off") == 0 && + strcmp(sharesmb, "off") == 0) { (void) fprintf(stderr, gettext("cannot " "unshare '%s': legacy share\n"), zfs_get_name(zhp)); @@ -3550,18 +3642,18 @@ unshare_unmount(int op, int argc, char **argv) "unshare(1M) to unshare this " "filesystem\n")); ret = 1; - } else if (!zfs_is_shared_nfs(zhp, NULL)) { + } else if (!zfs_is_shared(zhp)) { (void) fprintf(stderr, gettext("cannot " "unshare '%s': not currently " "shared\n"), zfs_get_name(zhp)); ret = 1; - } else if (zfs_unshareall_nfs(zhp) != 0) { + } else if (zfs_unshareall(zhp) != 0) { ret = 1; } break; case OP_MOUNT: - if (strcmp(property, "legacy") == 0) { + if (strcmp(nfsiscsi_mnt_prop, "legacy") == 0) { (void) fprintf(stderr, gettext("cannot " "unmount '%s': legacy " "mountpoint\n"), zfs_get_name(zhp)); @@ -3583,10 +3675,11 @@ unshare_unmount(int op, int argc, char **argv) } else { assert(op == OP_SHARE); - verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, property, - sizeof (property), NULL, NULL, 0, B_FALSE) == 0); + verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, + nfsiscsi_mnt_prop, sizeof (nfsiscsi_mnt_prop), + NULL, NULL, 0, B_FALSE) == 0); - if (strcmp(property, "off") == 0) { + if (strcmp(nfsiscsi_mnt_prop, "off") == 0) { (void) fprintf(stderr, gettext("cannot unshare " "'%s': 'shareiscsi' property not set\n"), zfs_get_name(zhp)); diff --git a/usr/src/cmd/zpool/zpool_main.c b/usr/src/cmd/zpool/zpool_main.c index 7b4d0f6f0696..0025c3a81c7b 100644 --- a/usr/src/cmd/zpool/zpool_main.c +++ b/usr/src/cmd/zpool/zpool_main.c @@ -731,7 +731,7 @@ zpool_do_create(int argc, char **argv) ZFS_PROP_MOUNTPOINT), mountpoint) == 0); if (zfs_mount(pool, NULL, 0) == 0) - ret = zfs_share_nfs(pool); + ret = zfs_shareall(pool); zfs_close(pool); } } else if (libzfs_errno(g_zfs) == EZFS_INVALIDNAME) { @@ -3277,6 +3277,8 @@ zpool_do_upgrade(int argc, char **argv) (void) printf(gettext(" 6 bootfs pool property\n")); (void) printf(gettext(" 7 Separate intent log devices\n")); (void) printf(gettext(" 8 Delegated administration\n")); + (void) printf(gettext(" 9 Case insensitive support and " + "File system unique identifiers (FUID)\n")); (void) printf(gettext("For more information on a particular " "version, including supported releases, see:\n\n")); (void) printf("http://www.opensolaris.org/os/community/zfs/" diff --git a/usr/src/common/acl/acl_common.c b/usr/src/common/acl/acl_common.c index f93446045f9e..2f8f6bd409a7 100644 --- a/usr/src/common/acl/acl_common.c +++ b/usr/src/common/acl/acl_common.c @@ -26,18 +26,128 @@ #pragma ident "%Z%%M% %I% %E% SMI" #include -#include #include +#include #if defined(_KERNEL) #include +#include +#include #else #include #include +#include #include +#include #include +#include +#include +#include #define ASSERT assert #endif +#define ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \ + ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \ + ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL) + + +#define ACL_SYNCHRONIZE_SET_DENY 0x0000001 +#define ACL_SYNCHRONIZE_SET_ALLOW 0x0000002 +#define ACL_SYNCHRONIZE_ERR_DENY 0x0000004 +#define ACL_SYNCHRONIZE_ERR_ALLOW 0x0000008 + +#define ACL_WRITE_OWNER_SET_DENY 0x0000010 +#define ACL_WRITE_OWNER_SET_ALLOW 0x0000020 +#define ACL_WRITE_OWNER_ERR_DENY 0x0000040 +#define ACL_WRITE_OWNER_ERR_ALLOW 0x0000080 + +#define ACL_DELETE_SET_DENY 0x0000100 +#define ACL_DELETE_SET_ALLOW 0x0000200 +#define ACL_DELETE_ERR_DENY 0x0000400 +#define ACL_DELETE_ERR_ALLOW 0x0000800 + +#define ACL_WRITE_ATTRS_OWNER_SET_DENY 0x0001000 +#define ACL_WRITE_ATTRS_OWNER_SET_ALLOW 0x0002000 +#define ACL_WRITE_ATTRS_OWNER_ERR_DENY 0x0004000 +#define ACL_WRITE_ATTRS_OWNER_ERR_ALLOW 0x0008000 + +#define ACL_WRITE_ATTRS_WRITER_SET_DENY 0x0010000 +#define ACL_WRITE_ATTRS_WRITER_SET_ALLOW 0x0020000 +#define ACL_WRITE_ATTRS_WRITER_ERR_DENY 0x0040000 +#define ACL_WRITE_ATTRS_WRITER_ERR_ALLOW 0x0080000 + +#define ACL_WRITE_NAMED_WRITER_SET_DENY 0x0100000 +#define ACL_WRITE_NAMED_WRITER_SET_ALLOW 0x0200000 +#define ACL_WRITE_NAMED_WRITER_ERR_DENY 0x0400000 +#define ACL_WRITE_NAMED_WRITER_ERR_ALLOW 0x0800000 + +#define ACL_READ_NAMED_READER_SET_DENY 0x1000000 +#define ACL_READ_NAMED_READER_SET_ALLOW 0x2000000 +#define ACL_READ_NAMED_READER_ERR_DENY 0x4000000 +#define ACL_READ_NAMED_READER_ERR_ALLOW 0x8000000 + + +#define ACE_VALID_MASK_BITS (\ + ACE_READ_DATA | \ + ACE_LIST_DIRECTORY | \ + ACE_WRITE_DATA | \ + ACE_ADD_FILE | \ + ACE_APPEND_DATA | \ + ACE_ADD_SUBDIRECTORY | \ + ACE_READ_NAMED_ATTRS | \ + ACE_WRITE_NAMED_ATTRS | \ + ACE_EXECUTE | \ + ACE_DELETE_CHILD | \ + ACE_READ_ATTRIBUTES | \ + ACE_WRITE_ATTRIBUTES | \ + ACE_DELETE | \ + ACE_READ_ACL | \ + ACE_WRITE_ACL | \ + ACE_WRITE_OWNER | \ + ACE_SYNCHRONIZE) + +#define ACE_MASK_UNDEFINED 0x80000000 + +#define ACE_VALID_FLAG_BITS (ACE_FILE_INHERIT_ACE | \ + ACE_DIRECTORY_INHERIT_ACE | \ + ACE_NO_PROPAGATE_INHERIT_ACE | ACE_INHERIT_ONLY_ACE | \ + ACE_SUCCESSFUL_ACCESS_ACE_FLAG | ACE_FAILED_ACCESS_ACE_FLAG | \ + ACE_IDENTIFIER_GROUP | ACE_OWNER | ACE_GROUP | ACE_EVERYONE) + +/* + * ACL conversion helpers + */ + +typedef enum { + ace_unused, + ace_user_obj, + ace_user, + ace_group, /* includes GROUP and GROUP_OBJ */ + ace_other_obj +} ace_to_aent_state_t; + +typedef struct acevals { + uid_t key; + avl_node_t avl; + uint32_t mask; + uint32_t allowed; + uint32_t denied; + int aent_type; +} acevals_t; + +typedef struct ace_list { + acevals_t user_obj; + avl_tree_t user; + int numusers; + acevals_t group_obj; + avl_tree_t group; + int numgroups; + acevals_t other_obj; + uint32_t acl_mask; + int hasmask; + int dfacl_flag; + ace_to_aent_state_t state; + int seen; /* bitmask of all aclent_t a_type values seen */ +} ace_list_t; ace_t trivial_acl[] = { {(uid_t)-1, 0, ACE_OWNER, ACE_ACCESS_DENIED_ACE_TYPE}, @@ -55,43 +165,58 @@ ace_t trivial_acl[] = { void -adjust_ace_pair(ace_t *pair, mode_t mode) +adjust_ace_pair_common(void *pair, size_t access_off, + size_t pairsize, mode_t mode) { + char *datap = (char *)pair; + uint32_t *amask0 = (uint32_t *)(uintptr_t)(datap + access_off); + uint32_t *amask1 = (uint32_t *)(uintptr_t)(datap + pairsize + + access_off); if (mode & S_IROTH) - pair[1].a_access_mask |= ACE_READ_DATA; + *amask1 |= ACE_READ_DATA; else - pair[0].a_access_mask |= ACE_READ_DATA; + *amask0 |= ACE_READ_DATA; if (mode & S_IWOTH) - pair[1].a_access_mask |= - ACE_WRITE_DATA|ACE_APPEND_DATA; + *amask1 |= ACE_WRITE_DATA|ACE_APPEND_DATA; else - pair[0].a_access_mask |= - ACE_WRITE_DATA|ACE_APPEND_DATA; + *amask0 |= ACE_WRITE_DATA|ACE_APPEND_DATA; if (mode & S_IXOTH) - pair[1].a_access_mask |= ACE_EXECUTE; + *amask1 |= ACE_EXECUTE; else - pair[0].a_access_mask |= ACE_EXECUTE; + *amask0 |= ACE_EXECUTE; +} + +void +adjust_ace_pair(ace_t *pair, mode_t mode) +{ + adjust_ace_pair_common(pair, offsetof(ace_t, a_access_mask), + sizeof (ace_t), mode); } /* * ace_trivial: * determine whether an ace_t acl is trivial * - * Trivialness implys that the acl is composed of only + * Trivialness implies that the acl is composed of only * owner, group, everyone entries. ACL can't * have read_acl denied, and write_owner/write_acl/write_attributes * can only be owner@ entry. */ int -ace_trivial(ace_t *acep, int aclcnt) +ace_trivial_common(void *acep, int aclcnt, + uint64_t (*walk)(void *, uint64_t, int aclcnt, + uint16_t *, uint16_t *, uint32_t *)) { - int i; int owner_seen = 0; int group_seen = 0; int everyone_seen = 0; + uint16_t flags; + uint32_t mask; + uint16_t type; + uint64_t cookie = 0; - for (i = 0; i != aclcnt; i++) { - switch (acep[i].a_flags & 0xf040) { + while (cookie = walk(acep, cookie, aclcnt, &flags, &type, &mask)) { + switch (flags & ACE_TYPE_FLAGS) { case ACE_OWNER: if (group_seen || everyone_seen) return (1); @@ -113,7 +238,7 @@ ace_trivial(ace_t *acep, int aclcnt) } - if (acep[i].a_flags & (ACE_FILE_INHERIT_ACE| + if (flags & (ACE_FILE_INHERIT_ACE| ACE_DIRECTORY_INHERIT_ACE|ACE_NO_PROPAGATE_INHERIT_ACE| ACE_INHERIT_ONLY_ACE)) return (1); @@ -124,27 +249,48 @@ ace_trivial(ace_t *acep, int aclcnt) * Don't allow anybody to deny reading basic * attributes or a files ACL. */ - if ((acep[i].a_access_mask & - (ACE_READ_ACL|ACE_READ_ATTRIBUTES)) && - (acep[i].a_type == ACE_ACCESS_DENIED_ACE_TYPE)) + if ((mask & (ACE_READ_ACL|ACE_READ_ATTRIBUTES)) && + (type == ACE_ACCESS_DENIED_ACE_TYPE)) return (1); /* * Allow on owner@ to allow * write_acl/write_owner/write_attributes */ - if (acep[i].a_type == ACE_ACCESS_ALLOWED_ACE_TYPE && - (!(acep[i].a_flags & ACE_OWNER) && (acep[i].a_access_mask & + if (type == ACE_ACCESS_ALLOWED_ACE_TYPE && + (!(flags & ACE_OWNER) && (mask & (ACE_WRITE_OWNER|ACE_WRITE_ACL|ACE_WRITE_ATTRIBUTES)))) return (1); + } if ((owner_seen == 0) || (group_seen == 0) || (everyone_seen == 0)) - return (1); + return (1); return (0); } +uint64_t +ace_walk(void *datap, uint64_t cookie, int aclcnt, uint16_t *flags, + uint16_t *type, uint32_t *mask) +{ + ace_t *acep = datap; + + *flags = acep[cookie].a_flags; + *type = acep[cookie].a_type; + *mask = acep[cookie++].a_access_mask; + + if (cookie > aclcnt) + return (0); + else + return (cookie); +} + +int +ace_trivial(ace_t *acep, int aclcnt) +{ + return (ace_trivial_common(acep, aclcnt, ace_walk)); +} /* * Generic shellsort, from K&R (1st ed, p 58.), somewhat modified. @@ -171,8 +317,8 @@ ksort(caddr_t v, int n, int s, int (*f)()) for (g = n / 2; g > 0; g /= 2) { for (i = g; i < n; i++) { for (j = i - g; j >= 0 && - (*f)(v + j * s, v + (j + g) * s) == 1; - j -= g) { + (*f)(v + j * s, v + (j + g) * s) == 1; + j -= g) { p1 = (void *)(v + j * s); p2 = (void *)(v + (j + g) * s); for (ii = 0; ii < s / 4; ii++) { @@ -215,3 +361,1347 @@ cmp2acls(void *a, void *b) /* Totally equal */ return (0); } + +/*ARGSUSED*/ +static void * +cacl_realloc(void *ptr, size_t size, size_t new_size) +{ +#if defined(_KERNEL) + void *tmp; + + tmp = kmem_alloc(new_size, KM_SLEEP); + (void) memcpy(tmp, ptr, (size < new_size) ? size : new_size); + kmem_free(ptr, size); + return (tmp); +#else + return (realloc(ptr, new_size)); +#endif +} + +static int +cacl_malloc(void **ptr, size_t size) +{ +#if defined(_KERNEL) + *ptr = kmem_zalloc(size, KM_SLEEP); + return (0); +#else + *ptr = calloc(1, size); + if (*ptr == NULL) + return (errno); + + return (0); +#endif +} + +/*ARGSUSED*/ +static void +cacl_free(void *ptr, size_t size) +{ +#if defined(_KERNEL) + kmem_free(ptr, size); +#else + free(ptr); +#endif +} + +acl_t * +acl_alloc(enum acl_type type) +{ + acl_t *aclp; + + if (cacl_malloc((void **)&aclp, sizeof (acl_t)) != 0) + return (NULL); + + aclp->acl_aclp = NULL; + aclp->acl_cnt = 0; + + switch (type) { + case ACE_T: + aclp->acl_type = ACE_T; + aclp->acl_entry_size = sizeof (ace_t); + break; + case ACLENT_T: + aclp->acl_type = ACLENT_T; + aclp->acl_entry_size = sizeof (aclent_t); + break; + default: + acl_free(aclp); + aclp = NULL; + } + return (aclp); +} + +/* + * Free acl_t structure + */ +void +acl_free(acl_t *aclp) +{ + int acl_size; + + if (aclp == NULL) + return; + + if (aclp->acl_aclp) { + acl_size = aclp->acl_cnt * aclp->acl_entry_size; + cacl_free(aclp->acl_aclp, acl_size); + } + + cacl_free(aclp, sizeof (acl_t)); +} + +static uint32_t +access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow) +{ + uint32_t access_mask = 0; + int acl_produce; + int synchronize_set = 0, write_owner_set = 0; + int delete_set = 0, write_attrs_set = 0; + int read_named_set = 0, write_named_set = 0; + + acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW | + ACL_WRITE_ATTRS_OWNER_SET_ALLOW | + ACL_WRITE_ATTRS_WRITER_SET_DENY); + + if (isallow) { + synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW; + write_owner_set = ACL_WRITE_OWNER_SET_ALLOW; + delete_set = ACL_DELETE_SET_ALLOW; + if (hasreadperm) + read_named_set = ACL_READ_NAMED_READER_SET_ALLOW; + if (haswriteperm) + write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW; + if (isowner) + write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW; + else if (haswriteperm) + write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW; + } else { + + synchronize_set = ACL_SYNCHRONIZE_SET_DENY; + write_owner_set = ACL_WRITE_OWNER_SET_DENY; + delete_set = ACL_DELETE_SET_DENY; + if (hasreadperm) + read_named_set = ACL_READ_NAMED_READER_SET_DENY; + if (haswriteperm) + write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY; + if (isowner) + write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY; + else if (haswriteperm) + write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY; + else + /* + * If the entity is not the owner and does not + * have write permissions ACE_WRITE_ATTRIBUTES will + * always go in the DENY ACE. + */ + access_mask |= ACE_WRITE_ATTRIBUTES; + } + + if (acl_produce & synchronize_set) + access_mask |= ACE_SYNCHRONIZE; + if (acl_produce & write_owner_set) + access_mask |= ACE_WRITE_OWNER; + if (acl_produce & delete_set) + access_mask |= ACE_DELETE; + if (acl_produce & write_attrs_set) + access_mask |= ACE_WRITE_ATTRIBUTES; + if (acl_produce & read_named_set) + access_mask |= ACE_READ_NAMED_ATTRS; + if (acl_produce & write_named_set) + access_mask |= ACE_WRITE_NAMED_ATTRS; + + return (access_mask); +} + +/* + * Given an mode_t, convert it into an access_mask as used + * by nfsace, assuming aclent_t -> nfsace semantics. + */ +static uint32_t +mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow) +{ + uint32_t access = 0; + int haswriteperm = 0; + int hasreadperm = 0; + + if (isallow) { + haswriteperm = (mode & S_IWOTH); + hasreadperm = (mode & S_IROTH); + } else { + haswriteperm = !(mode & S_IWOTH); + hasreadperm = !(mode & S_IROTH); + } + + /* + * The following call takes care of correctly setting the following + * mask bits in the access_mask: + * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE, + * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS + */ + access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow); + + if (isallow) { + access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES; + if (isowner) + access |= ACE_WRITE_ACL; + } else { + if (! isowner) + access |= ACE_WRITE_ACL; + } + + /* read */ + if (mode & S_IROTH) { + access |= ACE_READ_DATA; + } + /* write */ + if (mode & S_IWOTH) { + access |= ACE_WRITE_DATA | + ACE_APPEND_DATA; + if (isdir) + access |= ACE_DELETE_CHILD; + } + /* exec */ + if (mode & 01) { + access |= ACE_EXECUTE; + } + + return (access); +} + +/* + * Given an nfsace (presumably an ALLOW entry), make a + * corresponding DENY entry at the address given. + */ +static void +ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner) +{ + (void) memcpy(deny, allow, sizeof (ace_t)); + + deny->a_who = allow->a_who; + + deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE; + deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS; + if (isdir) + deny->a_access_mask ^= ACE_DELETE_CHILD; + + deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER | + ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS | + ACE_WRITE_NAMED_ATTRS); + deny->a_access_mask |= access_mask_set((allow->a_access_mask & + ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner, + B_FALSE); +} +/* + * Make an initial pass over an array of aclent_t's. Gather + * information such as an ACL_MASK (if any), number of users, + * number of groups, and whether the array needs to be sorted. + */ +static int +ln_aent_preprocess(aclent_t *aclent, int n, + int *hasmask, mode_t *mask, + int *numuser, int *numgroup, int *needsort) +{ + int error = 0; + int i; + int curtype = 0; + + *hasmask = 0; + *mask = 07; + *needsort = 0; + *numuser = 0; + *numgroup = 0; + + for (i = 0; i < n; i++) { + if (aclent[i].a_type < curtype) + *needsort = 1; + else if (aclent[i].a_type > curtype) + curtype = aclent[i].a_type; + if (aclent[i].a_type & USER) + (*numuser)++; + if (aclent[i].a_type & (GROUP | GROUP_OBJ)) + (*numgroup)++; + if (aclent[i].a_type & CLASS_OBJ) { + if (*hasmask) { + error = EINVAL; + goto out; + } else { + *hasmask = 1; + *mask = aclent[i].a_perm; + } + } + } + + if ((! *hasmask) && (*numuser + *numgroup > 1)) { + error = EINVAL; + goto out; + } + +out: + return (error); +} + +/* + * Convert an array of aclent_t into an array of nfsace entries, + * following POSIX draft -> nfsv4 conversion semantics as outlined in + * the IETF draft. + */ +static int +ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir) +{ + int error = 0; + mode_t mask; + int numuser, numgroup, needsort; + int resultsize = 0; + int i, groupi = 0, skip; + ace_t *acep, *result = NULL; + int hasmask; + + error = ln_aent_preprocess(aclent, n, &hasmask, &mask, + &numuser, &numgroup, &needsort); + if (error != 0) + goto out; + + /* allow + deny for each aclent */ + resultsize = n * 2; + if (hasmask) { + /* + * stick extra deny on the group_obj and on each + * user|group for the mask (the group_obj was added + * into the count for numgroup) + */ + resultsize += numuser + numgroup; + /* ... and don't count the mask itself */ + resultsize -= 2; + } + + /* sort the source if necessary */ + if (needsort) + ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls); + + if (cacl_malloc((void **)&result, resultsize * sizeof (ace_t)) != 0) + goto out; + + acep = result; + + for (i = 0; i < n; i++) { + /* + * don't process CLASS_OBJ (mask); mask was grabbed in + * ln_aent_preprocess() + */ + if (aclent[i].a_type & CLASS_OBJ) + continue; + + /* If we need an ACL_MASK emulator, prepend it now */ + if ((hasmask) && + (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) { + acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE; + acep->a_flags = 0; + if (aclent[i].a_type & GROUP_OBJ) { + acep->a_who = (uid_t)-1; + acep->a_flags |= + (ACE_IDENTIFIER_GROUP|ACE_GROUP); + } else if (aclent[i].a_type & USER) { + acep->a_who = aclent[i].a_id; + } else { + acep->a_who = aclent[i].a_id; + acep->a_flags |= ACE_IDENTIFIER_GROUP; + } + if (aclent[i].a_type & ACL_DEFAULT) { + acep->a_flags |= ACE_INHERIT_ONLY_ACE | + ACE_FILE_INHERIT_ACE | + ACE_DIRECTORY_INHERIT_ACE; + } + /* + * Set the access mask for the prepended deny + * ace. To do this, we invert the mask (found + * in ln_aent_preprocess()) then convert it to an + * DENY ace access_mask. + */ + acep->a_access_mask = mode_to_ace_access((mask ^ 07), + isdir, 0, 0); + acep += 1; + } + + /* handle a_perm -> access_mask */ + acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm, + isdir, aclent[i].a_type & USER_OBJ, 1); + + /* emulate a default aclent */ + if (aclent[i].a_type & ACL_DEFAULT) { + acep->a_flags |= ACE_INHERIT_ONLY_ACE | + ACE_FILE_INHERIT_ACE | + ACE_DIRECTORY_INHERIT_ACE; + } + + /* + * handle a_perm and a_id + * + * this must be done last, since it involves the + * corresponding deny aces, which are handled + * differently for each different a_type. + */ + if (aclent[i].a_type & USER_OBJ) { + acep->a_who = (uid_t)-1; + acep->a_flags |= ACE_OWNER; + ace_make_deny(acep, acep + 1, isdir, B_TRUE); + acep += 2; + } else if (aclent[i].a_type & USER) { + acep->a_who = aclent[i].a_id; + ace_make_deny(acep, acep + 1, isdir, B_FALSE); + acep += 2; + } else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) { + if (aclent[i].a_type & GROUP_OBJ) { + acep->a_who = (uid_t)-1; + acep->a_flags |= ACE_GROUP; + } else { + acep->a_who = aclent[i].a_id; + } + acep->a_flags |= ACE_IDENTIFIER_GROUP; + /* + * Set the corresponding deny for the group ace. + * + * The deny aces go after all of the groups, unlike + * everything else, where they immediately follow + * the allow ace. + * + * We calculate "skip", the number of slots to + * skip ahead for the deny ace, here. + * + * The pattern is: + * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3 + * thus, skip is + * (2 * numgroup) - 1 - groupi + * (2 * numgroup) to account for MD + A + * - 1 to account for the fact that we're on the + * access (A), not the mask (MD) + * - groupi to account for the fact that we have + * passed up groupi number of MD's. + */ + skip = (2 * numgroup) - 1 - groupi; + ace_make_deny(acep, acep + skip, isdir, B_FALSE); + /* + * If we just did the last group, skip acep past + * all of the denies; else, just move ahead one. + */ + if (++groupi >= numgroup) + acep += numgroup + 1; + else + acep += 1; + } else if (aclent[i].a_type & OTHER_OBJ) { + acep->a_who = (uid_t)-1; + acep->a_flags |= ACE_EVERYONE; + ace_make_deny(acep, acep + 1, isdir, B_FALSE); + acep += 2; + } else { + error = EINVAL; + goto out; + } + } + + *acepp = result; + *rescount = resultsize; + +out: + if (error != 0) { + if ((result != NULL) && (resultsize > 0)) { + cacl_free(result, resultsize * sizeof (ace_t)); + } + } + + return (error); +} + +static int +convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir, + ace_t **retacep, int *retacecnt) +{ + ace_t *acep; + ace_t *dfacep; + int acecnt = 0; + int dfacecnt = 0; + int dfaclstart = 0; + int dfaclcnt = 0; + aclent_t *aclp; + int i; + int error; + int acesz, dfacesz; + + ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls); + + for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) { + if (aclp->a_type & ACL_DEFAULT) + break; + } + + if (i < aclcnt) { + dfaclstart = i; + dfaclcnt = aclcnt - i; + } + + if (dfaclcnt && isdir == 0) { + return (EINVAL); + } + + error = ln_aent_to_ace(aclentp, i, &acep, &acecnt, isdir); + if (error) + return (error); + + if (dfaclcnt) { + error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt, + &dfacep, &dfacecnt, isdir); + if (error) { + if (acep) { + cacl_free(acep, acecnt * sizeof (ace_t)); + } + return (error); + } + } + + if (dfacecnt != 0) { + acesz = sizeof (ace_t) * acecnt; + dfacesz = sizeof (ace_t) * dfacecnt; + acep = cacl_realloc(acep, acesz, acesz + dfacesz); + if (acep == NULL) + return (ENOMEM); + if (dfaclcnt) { + (void) memcpy(acep + acecnt, dfacep, dfacesz); + } + } + if (dfaclcnt) + cacl_free(dfacep, dfacecnt * sizeof (ace_t)); + + *retacecnt = acecnt + dfacecnt; + *retacep = acep; + return (0); +} + +static int +ace_mask_to_mode(uint32_t mask, o_mode_t *modep, int isdir) +{ + int error = 0; + o_mode_t mode = 0; + uint32_t bits, wantbits; + + /* read */ + if (mask & ACE_READ_DATA) + mode |= S_IROTH; + + /* write */ + wantbits = (ACE_WRITE_DATA | ACE_APPEND_DATA); + if (isdir) + wantbits |= ACE_DELETE_CHILD; + bits = mask & wantbits; + if (bits != 0) { + if (bits != wantbits) { + error = ENOTSUP; + goto out; + } + mode |= S_IWOTH; + } + + /* exec */ + if (mask & ACE_EXECUTE) { + mode |= S_IXOTH; + } + + *modep = mode; + +out: + return (error); +} + +static void +acevals_init(acevals_t *vals, uid_t key) +{ + bzero(vals, sizeof (*vals)); + vals->allowed = ACE_MASK_UNDEFINED; + vals->denied = ACE_MASK_UNDEFINED; + vals->mask = ACE_MASK_UNDEFINED; + vals->key = key; +} + +static void +ace_list_init(ace_list_t *al, int dfacl_flag) +{ + acevals_init(&al->user_obj, NULL); + acevals_init(&al->group_obj, NULL); + acevals_init(&al->other_obj, NULL); + al->numusers = 0; + al->numgroups = 0; + al->acl_mask = 0; + al->hasmask = 0; + al->state = ace_unused; + al->seen = 0; + al->dfacl_flag = dfacl_flag; +} + +/* + * Find or create an acevals holder for a given id and avl tree. + * + * Note that only one thread will ever touch these avl trees, so + * there is no need for locking. + */ +static acevals_t * +acevals_find(ace_t *ace, avl_tree_t *avl, int *num) +{ + acevals_t key, *rc; + avl_index_t where; + + key.key = ace->a_who; + rc = avl_find(avl, &key, &where); + if (rc != NULL) + return (rc); + + /* this memory is freed by ln_ace_to_aent()->ace_list_free() */ + if (cacl_malloc((void **)&rc, sizeof (acevals_t)) != 0) + return (NULL); + + acevals_init(rc, ace->a_who); + avl_insert(avl, rc, where); + (*num)++; + + return (rc); +} + +static int +access_mask_check(ace_t *acep, int mask_bit, int isowner) +{ + int set_deny, err_deny; + int set_allow, err_allow; + int acl_consume; + int haswriteperm, hasreadperm; + + if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) { + haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 0 : 1; + hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 0 : 1; + } else { + haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 1 : 0; + hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 1 : 0; + } + + acl_consume = (ACL_SYNCHRONIZE_ERR_DENY | + ACL_DELETE_ERR_DENY | + ACL_WRITE_OWNER_ERR_DENY | + ACL_WRITE_OWNER_ERR_ALLOW | + ACL_WRITE_ATTRS_OWNER_SET_ALLOW | + ACL_WRITE_ATTRS_OWNER_ERR_DENY | + ACL_WRITE_ATTRS_WRITER_SET_DENY | + ACL_WRITE_ATTRS_WRITER_ERR_ALLOW | + ACL_WRITE_NAMED_WRITER_ERR_DENY | + ACL_READ_NAMED_READER_ERR_DENY); + + if (mask_bit == ACE_SYNCHRONIZE) { + set_deny = ACL_SYNCHRONIZE_SET_DENY; + err_deny = ACL_SYNCHRONIZE_ERR_DENY; + set_allow = ACL_SYNCHRONIZE_SET_ALLOW; + err_allow = ACL_SYNCHRONIZE_ERR_ALLOW; + } else if (mask_bit == ACE_WRITE_OWNER) { + set_deny = ACL_WRITE_OWNER_SET_DENY; + err_deny = ACL_WRITE_OWNER_ERR_DENY; + set_allow = ACL_WRITE_OWNER_SET_ALLOW; + err_allow = ACL_WRITE_OWNER_ERR_ALLOW; + } else if (mask_bit == ACE_DELETE) { + set_deny = ACL_DELETE_SET_DENY; + err_deny = ACL_DELETE_ERR_DENY; + set_allow = ACL_DELETE_SET_ALLOW; + err_allow = ACL_DELETE_ERR_ALLOW; + } else if (mask_bit == ACE_WRITE_ATTRIBUTES) { + if (isowner) { + set_deny = ACL_WRITE_ATTRS_OWNER_SET_DENY; + err_deny = ACL_WRITE_ATTRS_OWNER_ERR_DENY; + set_allow = ACL_WRITE_ATTRS_OWNER_SET_ALLOW; + err_allow = ACL_WRITE_ATTRS_OWNER_ERR_ALLOW; + } else if (haswriteperm) { + set_deny = ACL_WRITE_ATTRS_WRITER_SET_DENY; + err_deny = ACL_WRITE_ATTRS_WRITER_ERR_DENY; + set_allow = ACL_WRITE_ATTRS_WRITER_SET_ALLOW; + err_allow = ACL_WRITE_ATTRS_WRITER_ERR_ALLOW; + } else { + if ((acep->a_access_mask & mask_bit) && + (acep->a_type & ACE_ACCESS_ALLOWED_ACE_TYPE)) { + return (ENOTSUP); + } + return (0); + } + } else if (mask_bit == ACE_READ_NAMED_ATTRS) { + if (!hasreadperm) + return (0); + + set_deny = ACL_READ_NAMED_READER_SET_DENY; + err_deny = ACL_READ_NAMED_READER_ERR_DENY; + set_allow = ACL_READ_NAMED_READER_SET_ALLOW; + err_allow = ACL_READ_NAMED_READER_ERR_ALLOW; + } else if (mask_bit == ACE_WRITE_NAMED_ATTRS) { + if (!haswriteperm) + return (0); + + set_deny = ACL_WRITE_NAMED_WRITER_SET_DENY; + err_deny = ACL_WRITE_NAMED_WRITER_ERR_DENY; + set_allow = ACL_WRITE_NAMED_WRITER_SET_ALLOW; + err_allow = ACL_WRITE_NAMED_WRITER_ERR_ALLOW; + } else { + return (EINVAL); + } + + if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) { + if (acl_consume & set_deny) { + if (!(acep->a_access_mask & mask_bit)) { + return (ENOTSUP); + } + } else if (acl_consume & err_deny) { + if (acep->a_access_mask & mask_bit) { + return (ENOTSUP); + } + } + } else { + /* ACE_ACCESS_ALLOWED_ACE_TYPE */ + if (acl_consume & set_allow) { + if (!(acep->a_access_mask & mask_bit)) { + return (ENOTSUP); + } + } else if (acl_consume & err_allow) { + if (acep->a_access_mask & mask_bit) { + return (ENOTSUP); + } + } + } + return (0); +} + +static int +ace_to_aent_legal(ace_t *acep) +{ + int error = 0; + int isowner; + + /* only ALLOW or DENY */ + if ((acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE) && + (acep->a_type != ACE_ACCESS_DENIED_ACE_TYPE)) { + error = ENOTSUP; + goto out; + } + + /* check for invalid flags */ + if (acep->a_flags & ~(ACE_VALID_FLAG_BITS)) { + error = EINVAL; + goto out; + } + + /* some flags are illegal */ + if (acep->a_flags & (ACE_SUCCESSFUL_ACCESS_ACE_FLAG | + ACE_FAILED_ACCESS_ACE_FLAG | + ACE_NO_PROPAGATE_INHERIT_ACE)) { + error = ENOTSUP; + goto out; + } + + /* check for invalid masks */ + if (acep->a_access_mask & ~(ACE_VALID_MASK_BITS)) { + error = EINVAL; + goto out; + } + + if ((acep->a_flags & ACE_OWNER)) { + isowner = 1; + } else { + isowner = 0; + } + + error = access_mask_check(acep, ACE_SYNCHRONIZE, isowner); + if (error) + goto out; + + error = access_mask_check(acep, ACE_WRITE_OWNER, isowner); + if (error) + goto out; + + error = access_mask_check(acep, ACE_DELETE, isowner); + if (error) + goto out; + + error = access_mask_check(acep, ACE_WRITE_ATTRIBUTES, isowner); + if (error) + goto out; + + error = access_mask_check(acep, ACE_READ_NAMED_ATTRS, isowner); + if (error) + goto out; + + error = access_mask_check(acep, ACE_WRITE_NAMED_ATTRS, isowner); + if (error) + goto out; + + /* more detailed checking of masks */ + if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) { + if (! (acep->a_access_mask & ACE_READ_ATTRIBUTES)) { + error = ENOTSUP; + goto out; + } + if ((acep->a_access_mask & ACE_WRITE_DATA) && + (! (acep->a_access_mask & ACE_APPEND_DATA))) { + error = ENOTSUP; + goto out; + } + if ((! (acep->a_access_mask & ACE_WRITE_DATA)) && + (acep->a_access_mask & ACE_APPEND_DATA)) { + error = ENOTSUP; + goto out; + } + } + + /* ACL enforcement */ + if ((acep->a_access_mask & ACE_READ_ACL) && + (acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE)) { + error = ENOTSUP; + goto out; + } + if (acep->a_access_mask & ACE_WRITE_ACL) { + if ((acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) && + (isowner)) { + error = ENOTSUP; + goto out; + } + if ((acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) && + (! isowner)) { + error = ENOTSUP; + goto out; + } + } + +out: + return (error); +} + +static int +ace_allow_to_mode(uint32_t mask, o_mode_t *modep, int isdir) +{ + /* ACE_READ_ACL and ACE_READ_ATTRIBUTES must both be set */ + if ((mask & (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) != + (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) { + return (ENOTSUP); + } + + return (ace_mask_to_mode(mask, modep, isdir)); +} + +static int +acevals_to_aent(acevals_t *vals, aclent_t *dest, ace_list_t *list, + uid_t owner, gid_t group, int isdir) +{ + int error; + uint32_t flips = ACE_POSIX_SUPPORTED_BITS; + + if (isdir) + flips |= ACE_DELETE_CHILD; + if (vals->allowed != (vals->denied ^ flips)) { + error = ENOTSUP; + goto out; + } + if ((list->hasmask) && (list->acl_mask != vals->mask) && + (vals->aent_type & (USER | GROUP | GROUP_OBJ))) { + error = ENOTSUP; + goto out; + } + error = ace_allow_to_mode(vals->allowed, &dest->a_perm, isdir); + if (error != 0) + goto out; + dest->a_type = vals->aent_type; + if (dest->a_type & (USER | GROUP)) { + dest->a_id = vals->key; + } else if (dest->a_type & USER_OBJ) { + dest->a_id = owner; + } else if (dest->a_type & GROUP_OBJ) { + dest->a_id = group; + } else if (dest->a_type & OTHER_OBJ) { + dest->a_id = 0; + } else { + error = EINVAL; + goto out; + } + +out: + return (error); +} + + +static int +ace_list_to_aent(ace_list_t *list, aclent_t **aclentp, int *aclcnt, + uid_t owner, gid_t group, int isdir) +{ + int error = 0; + aclent_t *aent, *result = NULL; + acevals_t *vals; + int resultcount; + + if ((list->seen & (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) != + (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) { + error = ENOTSUP; + goto out; + } + if ((! list->hasmask) && (list->numusers + list->numgroups > 0)) { + error = ENOTSUP; + goto out; + } + + resultcount = 3 + list->numusers + list->numgroups; + /* + * This must be the same condition as below, when we add the CLASS_OBJ + * (aka ACL mask) + */ + if ((list->hasmask) || (! list->dfacl_flag)) + resultcount += 1; + + if (cacl_malloc((void **)&result, + resultcount * sizeof (aclent_t)) != 0) { + error = ENOMEM; + goto out; + } + aent = result; + + /* USER_OBJ */ + if (!(list->user_obj.aent_type & USER_OBJ)) { + error = EINVAL; + goto out; + } + + error = acevals_to_aent(&list->user_obj, aent, list, owner, group, + isdir); + + if (error != 0) + goto out; + ++aent; + /* USER */ + vals = NULL; + for (vals = avl_first(&list->user); vals != NULL; + vals = AVL_NEXT(&list->user, vals)) { + if (!(vals->aent_type & USER)) { + error = EINVAL; + goto out; + } + error = acevals_to_aent(vals, aent, list, owner, group, + isdir); + if (error != 0) + goto out; + ++aent; + } + /* GROUP_OBJ */ + if (!(list->group_obj.aent_type & GROUP_OBJ)) { + error = EINVAL; + goto out; + } + error = acevals_to_aent(&list->group_obj, aent, list, owner, group, + isdir); + if (error != 0) + goto out; + ++aent; + /* GROUP */ + vals = NULL; + for (vals = avl_first(&list->group); vals != NULL; + vals = AVL_NEXT(&list->group, vals)) { + if (!(vals->aent_type & GROUP)) { + error = EINVAL; + goto out; + } + error = acevals_to_aent(vals, aent, list, owner, group, + isdir); + if (error != 0) + goto out; + ++aent; + } + /* + * CLASS_OBJ (aka ACL_MASK) + * + * An ACL_MASK is not fabricated if the ACL is a default ACL. + * This is to follow UFS's behavior. + */ + if ((list->hasmask) || (! list->dfacl_flag)) { + if (list->hasmask) { + uint32_t flips = ACE_POSIX_SUPPORTED_BITS; + if (isdir) + flips |= ACE_DELETE_CHILD; + error = ace_mask_to_mode(list->acl_mask ^ flips, + &aent->a_perm, isdir); + if (error != 0) + goto out; + } else { + /* fabricate the ACL_MASK from the group permissions */ + error = ace_mask_to_mode(list->group_obj.allowed, + &aent->a_perm, isdir); + if (error != 0) + goto out; + } + aent->a_id = 0; + aent->a_type = CLASS_OBJ | list->dfacl_flag; + ++aent; + } + /* OTHER_OBJ */ + if (!(list->other_obj.aent_type & OTHER_OBJ)) { + error = EINVAL; + goto out; + } + error = acevals_to_aent(&list->other_obj, aent, list, owner, group, + isdir); + if (error != 0) + goto out; + ++aent; + + *aclentp = result; + *aclcnt = resultcount; + +out: + if (error != 0) { + if (result != NULL) + cacl_free(result, resultcount * sizeof (aclent_t)); + } + + return (error); +} + + +/* + * free all data associated with an ace_list + */ +static void +ace_list_free(ace_list_t *al) +{ + acevals_t *node; + void *cookie; + + if (al == NULL) + return; + + cookie = NULL; + while ((node = avl_destroy_nodes(&al->user, &cookie)) != NULL) + cacl_free(node, sizeof (acevals_t)); + cookie = NULL; + while ((node = avl_destroy_nodes(&al->group, &cookie)) != NULL) + cacl_free(node, sizeof (acevals_t)); + + avl_destroy(&al->user); + avl_destroy(&al->group); + + /* free the container itself */ + cacl_free(al, sizeof (ace_list_t)); +} + +static int +acevals_compare(const void *va, const void *vb) +{ + const acevals_t *a = va, *b = vb; + + if (a->key == b->key) + return (0); + + if (a->key > b->key) + return (1); + + else + return (-1); +} + +/* + * Convert a list of ace_t entries to equivalent regular and default + * aclent_t lists. Return error (ENOTSUP) when conversion is not possible. + */ +static int +ln_ace_to_aent(ace_t *ace, int n, uid_t owner, gid_t group, + aclent_t **aclentp, int *aclcnt, aclent_t **dfaclentp, int *dfaclcnt, + int isdir) +{ + int error = 0; + ace_t *acep; + uint32_t bits; + int i; + ace_list_t *normacl = NULL, *dfacl = NULL, *acl; + acevals_t *vals; + + *aclentp = NULL; + *aclcnt = 0; + *dfaclentp = NULL; + *dfaclcnt = 0; + + /* we need at least user_obj, group_obj, and other_obj */ + if (n < 6) { + error = ENOTSUP; + goto out; + } + if (ace == NULL) { + error = EINVAL; + goto out; + } + + error = cacl_malloc((void **)&normacl, sizeof (ace_list_t)); + if (error != 0) + goto out; + + avl_create(&normacl->user, acevals_compare, sizeof (acevals_t), + offsetof(acevals_t, avl)); + avl_create(&normacl->group, acevals_compare, sizeof (acevals_t), + offsetof(acevals_t, avl)); + + ace_list_init(normacl, 0); + + error = cacl_malloc((void **)&dfacl, sizeof (ace_list_t)); + if (error != 0) + goto out; + + avl_create(&dfacl->user, acevals_compare, sizeof (acevals_t), + offsetof(acevals_t, avl)); + avl_create(&dfacl->group, acevals_compare, sizeof (acevals_t), + offsetof(acevals_t, avl)); + ace_list_init(dfacl, ACL_DEFAULT); + + /* process every ace_t... */ + for (i = 0; i < n; i++) { + acep = &ace[i]; + + /* rule out certain cases quickly */ + error = ace_to_aent_legal(acep); + if (error != 0) + goto out; + + /* + * Turn off these bits in order to not have to worry about + * them when doing the checks for compliments. + */ + acep->a_access_mask &= ~(ACE_WRITE_OWNER | ACE_DELETE | + ACE_SYNCHRONIZE | ACE_WRITE_ATTRIBUTES | + ACE_READ_NAMED_ATTRS | ACE_WRITE_NAMED_ATTRS); + + /* see if this should be a regular or default acl */ + bits = acep->a_flags & + (ACE_INHERIT_ONLY_ACE | + ACE_FILE_INHERIT_ACE | + ACE_DIRECTORY_INHERIT_ACE); + if (bits != 0) { + /* all or nothing on these inherit bits */ + if (bits != (ACE_INHERIT_ONLY_ACE | + ACE_FILE_INHERIT_ACE | + ACE_DIRECTORY_INHERIT_ACE)) { + error = ENOTSUP; + goto out; + } + acl = dfacl; + } else { + acl = normacl; + } + + if ((acep->a_flags & ACE_OWNER)) { + if (acl->state > ace_user_obj) { + error = ENOTSUP; + goto out; + } + acl->state = ace_user_obj; + acl->seen |= USER_OBJ; + vals = &acl->user_obj; + vals->aent_type = USER_OBJ | acl->dfacl_flag; + } else if ((acep->a_flags & ACE_EVERYONE)) { + acl->state = ace_other_obj; + acl->seen |= OTHER_OBJ; + vals = &acl->other_obj; + vals->aent_type = OTHER_OBJ | acl->dfacl_flag; + } else if (acep->a_flags & ACE_IDENTIFIER_GROUP) { + if (acl->state > ace_group) { + error = ENOTSUP; + goto out; + } + if ((acep->a_flags & ACE_GROUP)) { + acl->seen |= GROUP_OBJ; + vals = &acl->group_obj; + vals->aent_type = GROUP_OBJ | acl->dfacl_flag; + } else { + acl->seen |= GROUP; + vals = acevals_find(acep, &acl->group, + &acl->numgroups); + if (vals == NULL) { + error = ENOMEM; + goto out; + } + vals->aent_type = GROUP | acl->dfacl_flag; + } + acl->state = ace_group; + } else { + if (acl->state > ace_user) { + error = ENOTSUP; + goto out; + } + acl->state = ace_user; + acl->seen |= USER; + vals = acevals_find(acep, &acl->user, + &acl->numusers); + if (vals == NULL) { + error = ENOMEM; + goto out; + } + vals->aent_type = USER | acl->dfacl_flag; + } + + if (!(acl->state > ace_unused)) { + error = EINVAL; + goto out; + } + + if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) { + /* no more than one allowed per aclent_t */ + if (vals->allowed != ACE_MASK_UNDEFINED) { + error = ENOTSUP; + goto out; + } + vals->allowed = acep->a_access_mask; + } else { + /* + * it's a DENY; if there was a previous DENY, it + * must have been an ACL_MASK. + */ + if (vals->denied != ACE_MASK_UNDEFINED) { + /* ACL_MASK is for USER and GROUP only */ + if ((acl->state != ace_user) && + (acl->state != ace_group)) { + error = ENOTSUP; + goto out; + } + + if (! acl->hasmask) { + acl->hasmask = 1; + acl->acl_mask = vals->denied; + /* check for mismatched ACL_MASK emulations */ + } else if (acl->acl_mask != vals->denied) { + error = ENOTSUP; + goto out; + } + vals->mask = vals->denied; + } + vals->denied = acep->a_access_mask; + } + } + + /* done collating; produce the aclent_t lists */ + if (normacl->state != ace_unused) { + error = ace_list_to_aent(normacl, aclentp, aclcnt, + owner, group, isdir); + if (error != 0) { + goto out; + } + } + if (dfacl->state != ace_unused) { + error = ace_list_to_aent(dfacl, dfaclentp, dfaclcnt, + owner, group, isdir); + if (error != 0) { + goto out; + } + } + +out: + if (normacl != NULL) + ace_list_free(normacl); + if (dfacl != NULL) + ace_list_free(dfacl); + + return (error); +} + +static int +convert_ace_to_aent(ace_t *acebufp, int acecnt, int isdir, + uid_t owner, gid_t group, aclent_t **retaclentp, int *retaclcnt) +{ + int error = 0; + aclent_t *aclentp, *dfaclentp; + int aclcnt, dfaclcnt; + int aclsz, dfaclsz; + + error = ln_ace_to_aent(acebufp, acecnt, owner, group, + &aclentp, &aclcnt, &dfaclentp, &dfaclcnt, isdir); + + if (error) + return (error); + + + if (dfaclcnt != 0) { + /* + * Slap aclentp and dfaclentp into a single array. + */ + aclsz = sizeof (aclent_t) * aclcnt; + dfaclsz = sizeof (aclent_t) * dfaclcnt; + aclentp = cacl_realloc(aclentp, aclsz, aclsz + dfaclsz); + if (aclentp != NULL) { + (void) memcpy(aclentp + aclcnt, dfaclentp, dfaclsz); + } else { + error = ENOMEM; + } + } + + if (aclentp) { + *retaclentp = aclentp; + *retaclcnt = aclcnt + dfaclcnt; + } + + if (dfaclentp) + cacl_free(dfaclentp, dfaclsz); + + return (error); +} + + +int +acl_translate(acl_t *aclp, int target_flavor, int isdir, uid_t owner, + gid_t group) +{ + int aclcnt; + void *acldata; + int error; + + /* + * See if we need to translate + */ + if ((target_flavor == _ACL_ACE_ENABLED && aclp->acl_type == ACE_T) || + (target_flavor == _ACL_ACLENT_ENABLED && + aclp->acl_type == ACLENT_T)) + return (0); + + if (target_flavor == -1) { + error = EINVAL; + goto out; + } + + if (target_flavor == _ACL_ACE_ENABLED && + aclp->acl_type == ACLENT_T) { + error = convert_aent_to_ace(aclp->acl_aclp, + aclp->acl_cnt, isdir, (ace_t **)&acldata, &aclcnt); + if (error) + goto out; + + } else if (target_flavor == _ACL_ACLENT_ENABLED && + aclp->acl_type == ACE_T) { + error = convert_ace_to_aent(aclp->acl_aclp, aclp->acl_cnt, + isdir, owner, group, (aclent_t **)&acldata, &aclcnt); + if (error) + goto out; + } else { + error = ENOTSUP; + goto out; + } + + /* + * replace old acl with newly translated acl + */ + cacl_free(aclp->acl_aclp, aclp->acl_cnt * aclp->acl_entry_size); + aclp->acl_aclp = acldata; + aclp->acl_cnt = aclcnt; + if (target_flavor == _ACL_ACE_ENABLED) { + aclp->acl_type = ACE_T; + aclp->acl_entry_size = sizeof (ace_t); + } else { + aclp->acl_type = ACLENT_T; + aclp->acl_entry_size = sizeof (aclent_t); + } + return (0); + +out: + +#if !defined(_KERNEL) + errno = error; + return (-1); +#else + return (error); +#endif +} diff --git a/usr/src/common/acl/acl_common.h b/usr/src/common/acl/acl_common.h index 2227ad77ea93..84bd04f52fd6 100644 --- a/usr/src/common/acl/acl_common.h +++ b/usr/src/common/acl/acl_common.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,12 +19,12 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#ifndef _ACL_ACL_UTILS_H -#define _ACL_ACL_UTILS_H +#ifndef _ACL_COMMON_H +#define _ACL_COMMON_H #pragma ident "%Z%%M% %I% %E% SMI" @@ -34,7 +33,7 @@ #include #include -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif @@ -42,15 +41,21 @@ extern ace_t trivial_acl[6]; extern int acltrivial(const char *); extern void adjust_ace_pair(ace_t *pair, mode_t mode); +extern void adjust_ace_pair_common(void *, size_t, size_t, mode_t); extern int ace_trivial(ace_t *acep, int aclcnt); +extern int ace_trivial_common(void *, int, + uint64_t (*walk)(void *, uint64_t, int aclcnt, uint16_t *, uint16_t *, + uint32_t *mask)); +extern acl_t *acl_alloc(acl_type_t); +extern void acl_free(acl_t *aclp); +extern int acl_translate(acl_t *aclp, int target_flavor, + int isdir, uid_t owner, gid_t group); void ksort(caddr_t v, int n, int s, int (*f)()); int cmp2acls(void *a, void *b); - - -#ifdef __cplusplus +#ifdef __cplusplus } #endif -#endif /* _ACL_ACL_UTILS_H */ +#endif /* _ACL_COMMON_H */ diff --git a/usr/src/common/smbsrv/smb_common_door_decode.c b/usr/src/common/smbsrv/smb_common_door_decode.c new file mode 100644 index 000000000000..907c29bd2382 --- /dev/null +++ b/usr/src/common/smbsrv/smb_common_door_decode.c @@ -0,0 +1,387 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Provides encode/decode routines for all door servers/clients. + */ + +#ifdef _KERNEL +#include +#include +#else +#include +#endif +#include +#ifndef _KERNEL +#include +#endif + +#include +#include +#include + + +smb_dr_ctx_t * +smb_dr_decode_start(char *ptr, int size) +{ + smb_dr_ctx_t *ctx = MEM_MALLOC("CommonDoor", sizeof (smb_dr_ctx_t)); + if (ctx) { + ctx->start_ptr = ctx->ptr = ptr; + ctx->end_ptr = ptr + size; + ctx->status = 0; + } + return (ctx); +} + +int +smb_dr_decode_finish(smb_dr_ctx_t *ctx) +{ + int status = ctx->status; + if (status == 0 && ctx->ptr != ctx->end_ptr) { + status = ENOTEMPTY; + } + MEM_FREE("CommonDoor", ctx); + return (status); +} + +smb_dr_ctx_t * +smb_dr_encode_start(char *ptr, int size) +{ + smb_dr_ctx_t *ctx = MEM_MALLOC("CommonDoor", sizeof (smb_dr_ctx_t)); + if (ctx) { + ctx->start_ptr = ctx->ptr = ptr; + ctx->end_ptr = ptr + size; + ctx->status = 0; + } + return (ctx); +} + +int +smb_dr_encode_finish(smb_dr_ctx_t *ctx, unsigned int *used) +{ + int status = ctx->status; + if (status == 0) { + if (ctx->ptr < ctx->end_ptr) { + /*LINTED E_PTRDIFF_OVERFLOW*/ + *used = ctx->ptr - ctx->start_ptr; + } + else + status = ENOSPC; + } + + MEM_FREE("CommonDoor", ctx); + return (status); +} + +DWORD +smb_dr_get_dword(smb_dr_ctx_t *ctx) +{ + DWORD num = 0; + if (ctx->status == 0) { + if (ctx->ptr + sizeof (DWORD) <= ctx->end_ptr) { + (void) memcpy(&num, ctx->ptr, sizeof (DWORD)); + ctx->ptr += sizeof (DWORD); + } + else + ctx->status = ENOSPC; + } + return (num); +} + +int32_t +smb_dr_get_int32(smb_dr_ctx_t *ctx) +{ + int32_t num = 0; + if (ctx->status == 0) { + if (ctx->ptr + sizeof (int32_t) <= ctx->end_ptr) { + (void) memcpy(&num, ctx->ptr, sizeof (int32_t)); + ctx->ptr += sizeof (int32_t); + } + else + ctx->status = ENOSPC; + } + return (num); +} + +uint32_t +smb_dr_get_uint32(smb_dr_ctx_t *ctx) +{ + return ((uint32_t)smb_dr_get_int32(ctx)); +} + +char * +smb_dr_get_string(smb_dr_ctx_t *ctx) +{ + char *buf = NULL; + int len = smb_dr_get_int32(ctx); + + if (ctx->status == 0) { + if (len == -1) + return (buf); + + if (ctx->ptr + len <= ctx->end_ptr) { + buf = MEM_MALLOC("CommonDoor", len +1); + if (buf) { + if (len == 0) + (void) strcpy(buf, ""); + else { + (void) memcpy(buf, ctx->ptr, len); + ctx->ptr += len; + *(buf + len) = '\0'; + } + } + else +#ifndef _KERNEL + ctx->status = errno; +#else + ctx->status = ENOMEM; +#endif + } + else + ctx->status = ENOSPC; + } + return (buf); +} + +void +smb_dr_put_dword(smb_dr_ctx_t *ctx, DWORD num) +{ + if (ctx->status == 0) { + if (ctx->ptr + sizeof (DWORD) <= ctx->end_ptr) { + (void) memcpy(ctx->ptr, &num, sizeof (DWORD)); + ctx->ptr += sizeof (DWORD); + } else + ctx->status = ENOSPC; + } +} + +void +smb_dr_put_int32(smb_dr_ctx_t *ctx, int32_t num) +{ + if (ctx->status == 0) { + if (ctx->ptr + sizeof (int32_t) <= ctx->end_ptr) { + (void) memcpy(ctx->ptr, &num, sizeof (int32_t)); + ctx->ptr += sizeof (int32_t); + } else + ctx->status = ENOSPC; + } +} + +void +smb_dr_put_uint32(smb_dr_ctx_t *ctx, uint32_t num) +{ + smb_dr_put_int32(ctx, (int32_t)num); +} + +void +smb_dr_put_string(smb_dr_ctx_t *ctx, char *buf) +{ + int len; + + if (!buf) + len = -1; + else + len = strlen(buf); + + if (ctx->status == 0) { + smb_dr_put_int32(ctx, len); + if (len <= 0) + return; + + if (ctx->ptr + len <= ctx->end_ptr) { + (void) memcpy(ctx->ptr, buf, len); + ctx->ptr += len; + } + else + ctx->status = ENOSPC; + } +} + +void +smb_dr_free_string(char *buf) +{ + if (buf) + MEM_FREE("CommonDoor", buf); +} + +int64_t +smb_dr_get_int64(smb_dr_ctx_t *ctx) +{ + int64_t num = 0; + if (ctx->status == 0) { + if (ctx->ptr + sizeof (int64_t) <= ctx->end_ptr) { + (void) memcpy(&num, ctx->ptr, sizeof (int64_t)); + ctx->ptr += sizeof (int64_t); + } + else + ctx->status = ENOSPC; + } + return (num); +} + +uint64_t +smb_dr_get_uint64(smb_dr_ctx_t *ctx) +{ + return ((uint64_t)smb_dr_get_int64(ctx)); +} + + +void +smb_dr_put_int64(smb_dr_ctx_t *ctx, int64_t num) +{ + if (ctx->status == 0) { + if (ctx->ptr + sizeof (int64_t) <= ctx->end_ptr) { + (void) memcpy(ctx->ptr, &num, sizeof (int64_t)); + ctx->ptr += sizeof (int64_t); + } else + ctx->status = ENOSPC; + } +} + +void +smb_dr_put_uint64(smb_dr_ctx_t *ctx, uint64_t num) +{ + smb_dr_put_int64(ctx, (int64_t)num); +} + +void +smb_dr_put_short(smb_dr_ctx_t *ctx, short num) +{ + if (ctx->status == 0) { + if (ctx->ptr + sizeof (short) <= ctx->end_ptr) { + (void) memcpy(ctx->ptr, &num, sizeof (short)); + ctx->ptr += sizeof (short); + } else + ctx->status = ENOSPC; + } +} + +short +smb_dr_get_short(smb_dr_ctx_t *ctx) +{ + short num = 0; + if (ctx->status == 0) { + if (ctx->ptr + sizeof (short) <= ctx->end_ptr) { + (void) memcpy(&num, ctx->ptr, sizeof (short)); + ctx->ptr += sizeof (short); + } + else + ctx->status = ENOSPC; + } + return (num); +} + +void +smb_dr_put_ushort(smb_dr_ctx_t *ctx, unsigned short num) +{ + smb_dr_put_short(ctx, (short)num); +} + +unsigned short +smb_dr_get_ushort(smb_dr_ctx_t *ctx) +{ + return ((unsigned short)smb_dr_get_short(ctx)); +} + +void +smb_dr_put_word(smb_dr_ctx_t *ctx, WORD num) +{ + smb_dr_put_ushort(ctx, num); +} + +WORD +smb_dr_get_word(smb_dr_ctx_t *ctx) +{ + return (smb_dr_get_ushort(ctx)); +} + +void +smb_dr_put_BYTE(smb_dr_ctx_t *ctx, BYTE byte) +{ + if (ctx->status == 0) { + if (ctx->ptr + sizeof (BYTE) <= ctx->end_ptr) { + (void) memcpy(ctx->ptr, &byte, sizeof (BYTE)); + ctx->ptr += sizeof (BYTE); + } else + ctx->status = ENOSPC; + } +} + +BYTE +smb_dr_get_BYTE(smb_dr_ctx_t *ctx) +{ + BYTE byte = 0; + if (ctx->status == 0) { + if (ctx->ptr + sizeof (BYTE) <= ctx->end_ptr) { + (void) memcpy(&byte, ctx->ptr, sizeof (BYTE)); + ctx->ptr += sizeof (BYTE); + } + else + ctx->status = ENOSPC; + } + return (byte); +} + +void +smb_dr_put_buf(smb_dr_ctx_t *ctx, unsigned char *start, int len) +{ + smb_dr_put_int32(ctx, len); + if (ctx->status == 0) { + if (ctx->ptr + len <= ctx->end_ptr) { + (void) memcpy(ctx->ptr, start, len); + ctx->ptr += len; + } else + ctx->status = ENOSPC; + } +} + +int +smb_dr_get_buf(smb_dr_ctx_t *ctx, unsigned char *buf, int bufsize) +{ + int len = -1; + + if (!buf) + return (-1); + + len = smb_dr_get_int32(ctx); + if (ctx->status == 0) { + if (bufsize < len) { + ctx->status = ENOSPC; + return (-2); + } + + if (ctx->ptr + len <= ctx->end_ptr) { + (void) memcpy(buf, ctx->ptr, len); + ctx->ptr += len; + } else { + ctx->status = ENOSPC; + return (-3); + } + } + + return (len); +} diff --git a/usr/src/common/smbsrv/smb_match.c b/usr/src/common/smbsrv/smb_match.c new file mode 100644 index 000000000000..8edf565e1b98 --- /dev/null +++ b/usr/src/common/smbsrv/smb_match.c @@ -0,0 +1,241 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef _KERNEL +#include +#include +#else +#include +#include +#endif +#include + + +/* + * c Any non-special character matches itslef + * ? Match any character + * ab character 'a' followed by character 'b' + * S Any string of non-special characters + * AB String 'A' followed by string 'B' + * * Any String, including the empty string + */ +int +smb_match(char *patn, char *str) +{ + for (;;) { + switch (*patn) { + case 0: + return (*str == 0); + + case '?': + if (*str != 0) { + str++; + patn++; + continue; + } else { + return (0); + } + /*NOTREACHED*/ + +#if 0 + case '[': + int invert = 0, clower, cupper; + + patn++; + if (*patn == '!') { + invert = 1; + patn++; + } + for (;;) { + clower = *patn; + if (clower == 0) + break; + if (clower == ']') { + patn++; + break; + } + patn++; + if (*patn == '-') { + /* range */ + patn++; + cupper = *patn; + if (cupper == 0) + break; + patn++; + } else { + cupper = clower; + } + if (*str < clower || cupper < *str) + continue; + + /* match */ + if (invert) + return (0); + + while (*patn && *patn++ != ']') + ; + str++; + continue; /* THIS WON`T WORK */ + } + if (invert) { + str++; + continue; + } + return (0); + +#endif + + case '*': + patn++; + if (*patn == 0) + return (1); + +#if 0 + if (*patn != '?' && *patn != '*' && *patn != '[') { + /* accelerate */ + while (*str) { + if (*str == *patn && + match(patn+1, str+1)) + return (1); + str++; + } + return (0); + } +#endif + + while (*str) { + if (smb_match(patn, str)) + return (1); + str++; + } + return (0); + + default: + if (*str != *patn) + return (0); + str++; + patn++; + continue; + } + } +} + +int +smb_match83(char *patn, char *str83) +{ + int avail; + char *ptr; + char name83[14]; + + ptr = name83; + for (avail = 8; (avail > 0) && (*patn != '.') && (*patn != 0); + avail--) { + *(ptr++) = *(patn++); + } + while (avail--) + *(ptr++) = ' '; + *(ptr++) = '.'; + + if (*patn == '.') + patn++; + else if (*patn != 0) + return (0); + + for (avail = 3; (avail > 0) && (*patn != 0); avail--) { + *(ptr++) = *(patn++); + } + if (*patn != 0) + return (0); + + while (avail--) + *(ptr++) = ' '; + *ptr = 0; + + return (smb_match_ci(name83, str83)); +} + + + +int +smb_match_ci(char *patn, char *str) +{ + /* + * "<" is a special pattern that matches only those names that do + * NOT have an extension. "." and ".." are ok. + */ + if (strcmp(patn, "<") == 0) { + if ((strcmp(str, ".") == 0) || (strcmp(str, "..") == 0)) + return (1); + if (strchr(str, '.') == 0) + return (1); + return (0); + } + for (;;) { + switch (*patn) { + case 0: + return (*str == 0); + + case '?': + if (*str != 0) { + str++; + patn++; + continue; + } else { + return (0); + } + /*NOTREACHED*/ + + + case '*': + patn++; + if (*patn == 0) + return (1); + + while (*str) { + if (smb_match_ci(patn, str)) + return (1); + str++; + } + return (0); + + default: + if (*str != *patn) { + int c1 = *str; + int c2 = *patn; + + c1 = mts_tolower(c1); + c2 = mts_tolower(c2); + if (c1 != c2) + return (0); + } + str++; + patn++; + continue; + } + } + /* NOT REACHED */ +} diff --git a/usr/src/common/smbsrv/smb_msgbuf.c b/usr/src/common/smbsrv/smb_msgbuf.c new file mode 100644 index 000000000000..d456dac68307 --- /dev/null +++ b/usr/src/common/smbsrv/smb_msgbuf.c @@ -0,0 +1,709 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Msgbuf buffer management implementation. The smb_msgbuf interface is + * typically used to encode or decode SMB data using sprintf/scanf + * style operations. It contains special handling for the SMB header. + * It can also be used for general purpose encoding and decoding. + */ + +#include +#include +#include +#ifndef _KERNEL +#include +#include +#include +#include +#else +#include +#include +#endif +#include +#include +#include + +static int buf_decode(smb_msgbuf_t *, char *, va_list ap); +static int buf_encode(smb_msgbuf_t *, char *, va_list ap); +static void *smb_msgbuf_malloc(smb_msgbuf_t *, size_t); +static int smb_msgbuf_chkerc(char *text, int erc); +static void buf_decode_wcs(mts_wchar_t *, mts_wchar_t *, int wcstrlen); + +/* + * Returns the offset or number of bytes used within the buffer. + */ +size_t +smb_msgbuf_used(smb_msgbuf_t *mb) +{ + /*LINTED E_PTRDIFF_OVERFLOW*/ + return (mb->scan - mb->base); +} + +/* + * Returns the actual buffer size. + */ +size_t +smb_msgbuf_size(smb_msgbuf_t *mb) +{ + return (mb->max); +} + +uint8_t * +smb_msgbuf_base(smb_msgbuf_t *mb) +{ + return (mb->base); +} + +/* + * Ensure that the scan is aligned on a word (16-bit) boundary. + */ +void +smb_msgbuf_word_align(smb_msgbuf_t *mb) +{ + mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 1) & ~1); +} + +/* + * Ensure that the scan is aligned on a dword (32-bit) boundary. + */ +void +smb_msgbuf_dword_align(smb_msgbuf_t *mb) +{ + mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 3) & ~3); +} + +/* + * Checks whether or not the buffer has space for the amount of data + * specified. Returns 1 if there is space, otherwise returns 0. + */ +int +smb_msgbuf_has_space(smb_msgbuf_t *mb, size_t size) +{ + if (size > mb->max || (mb->scan + size) > mb->end) + return (0); + + return (1); +} + +/* + * Set flags the smb_msgbuf. + */ +void +smb_msgbuf_fset(smb_msgbuf_t *mb, uint32_t flags) +{ + mb->flags |= flags; +} + +/* + * Clear flags the smb_msgbuf. + */ +void +smb_msgbuf_fclear(smb_msgbuf_t *mb, uint32_t flags) +{ + mb->flags &= ~flags; +} + +/* + * smb_msgbuf_init + * + * Initialize a smb_msgbuf_t structure based on the buffer and size + * specified. Both scan and base initially point to the beginning + * of the buffer and end points to the limit of the buffer. As + * data is added scan should be incremented to point to the next + * offset at which data will be written. Max and count are set + * to the actual buffer size. + */ +void +smb_msgbuf_init(smb_msgbuf_t *mb, uint8_t *buf, size_t size, uint32_t flags) +{ + mb->scan = mb->base = buf; + mb->max = mb->count = size; + mb->end = &buf[size]; + mb->flags = flags; + mb->mlist.next = 0; +} + + +/* + * smb_msgbuf_term + * + * Destruct a smb_msgbuf_t. Free any memory hanging off the mlist. + */ +void +smb_msgbuf_term(smb_msgbuf_t *mb) +{ + smb_msgbuf_mlist_t *item = mb->mlist.next; + smb_msgbuf_mlist_t *tmp; + + while (item) { + tmp = item; + item = item->next; +#ifndef _KERNEL + free(tmp); +#else + kmem_free(tmp, tmp->size); +#endif + } +} + + +/* + * smb_msgbuf_decode + * + * Decode a smb_msgbuf buffer as indicated by the format string into + * the variable arg list. This is similar to a scanf operation. + * + * On success, returns the number of bytes encoded. Otherwise + * returns a -ve error code. + */ +int +smb_msgbuf_decode(smb_msgbuf_t *mb, char *fmt, ...) +{ + int rc; + uint8_t *orig_scan; + va_list ap; + + va_start(ap, fmt); + orig_scan = mb->scan; + rc = buf_decode(mb, fmt, ap); + va_end(ap); + + if (rc != SMB_MSGBUF_SUCCESS) { + (void) smb_msgbuf_chkerc("smb_msgbuf_decode", rc); + mb->scan = orig_scan; + return (rc); + } + + /*LINTED E_PTRDIFF_OVERFLOW*/ + return (mb->scan - orig_scan); +} + + +/* + * buf_decode + * + * Private decode function, where the real work of decoding the smb_msgbuf + * is done. This function should only be called via smb_msgbuf_decode to + * ensure correct behaviour and error handling. + */ +static int +buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap) +{ + uint32_t ival; + uint8_t c; + uint8_t *cvalp; + uint8_t **cvalpp; + uint16_t *wvalp; + uint32_t *lvalp; + uint64_t *llvalp; + mts_wchar_t *wcs; + int repc; + int rc; + + while ((c = *fmt++) != 0) { + repc = 1; + + if (c == ' ' || c == '\t') + continue; + + if (c == '(') { + while (((c = *fmt++) != 0) && c != ')') + ; + + if (!c) + return (SMB_MSGBUF_SUCCESS); + + continue; + } + + if ('0' <= c && c <= '9') { + repc = 0; + do { + repc = repc * 10 + c - '0'; + c = *fmt++; + } while ('0' <= c && c <= '9'); + } else if (c == '#') { + repc = va_arg(ap, int); + c = *fmt++; + } + + switch (c) { + case '.': + if (smb_msgbuf_has_space(mb, repc) == 0) + return (SMB_MSGBUF_UNDERFLOW); + + mb->scan += repc; + break; + + case 'c': + if (smb_msgbuf_has_space(mb, repc) == 0) + return (SMB_MSGBUF_UNDERFLOW); + + cvalp = va_arg(ap, uint8_t *); + bcopy(mb->scan, cvalp, repc); + mb->scan += repc; + break; + + case 'b': + if (smb_msgbuf_has_space(mb, repc) == 0) + return (SMB_MSGBUF_UNDERFLOW); + + cvalp = va_arg(ap, uint8_t *); + while (repc-- > 0) { + *cvalp++ = *mb->scan++; + } + break; + + case 'w': + rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t)); + if (rc == 0) + return (SMB_MSGBUF_UNDERFLOW); + + wvalp = va_arg(ap, uint16_t *); + while (repc-- > 0) { + *wvalp++ = LE_IN16(mb->scan); + mb->scan += sizeof (uint16_t); + } + break; + + case 'l': + rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t)); + if (rc == 0) + return (SMB_MSGBUF_UNDERFLOW); + + lvalp = va_arg(ap, uint32_t *); + while (repc-- > 0) { + *lvalp++ = LE_IN32(mb->scan); + mb->scan += sizeof (int32_t); + } + break; + + case 'q': + rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t)); + if (rc == 0) + return (SMB_MSGBUF_UNDERFLOW); + + llvalp = va_arg(ap, uint64_t *); + while (repc-- > 0) { + *llvalp++ = LE_IN64(mb->scan); + mb->scan += sizeof (int64_t); + } + break; + + case 'u': /* Convert from unicode if flags are set */ + if (mb->flags & SMB_MSGBUF_UNICODE) + goto unicode_translation; + /*FALLTHROUGH*/ + + case 's': + ival = strlen((const char *)mb->scan) + 1; + if (smb_msgbuf_has_space(mb, ival) == 0) + return (SMB_MSGBUF_UNDERFLOW); + + if ((cvalp = smb_msgbuf_malloc(mb, ival * 2)) == 0) + return (SMB_MSGBUF_UNDERFLOW); + + if ((ival = mts_stombs((char *)cvalp, + (char *)mb->scan, ival * 2)) == + (uint32_t)-1) { + return (SMB_MSGBUF_DATA_ERROR); + } + + cvalpp = va_arg(ap, uint8_t **); + *cvalpp = cvalp; + mb->scan += (ival+1); + break; + + case 'U': /* Convert from unicode */ +unicode_translation: + /* + * Unicode strings are always word aligned. + * The malloc'd area is larger than the + * original string because the UTF-8 chars + * may be longer than the wide-chars. + */ + smb_msgbuf_word_align(mb); + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + wcs = (mts_wchar_t *)mb->scan; + + /* count the null wchar */ + repc = sizeof (mts_wchar_t); + while (*wcs++) + repc += sizeof (mts_wchar_t); + + if (smb_msgbuf_has_space(mb, repc) == 0) + return (SMB_MSGBUF_UNDERFLOW); + + /* Decode wchar string into host byte-order */ + if ((wcs = smb_msgbuf_malloc(mb, repc)) == 0) + return (SMB_MSGBUF_UNDERFLOW); + + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + buf_decode_wcs(wcs, (mts_wchar_t *)mb->scan, + repc / sizeof (mts_wchar_t)); + + /* Get space for translated string */ + if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0) + return (SMB_MSGBUF_UNDERFLOW); + + /* Translate string */ + (void) mts_wcstombs((char *)cvalp, wcs, repc * 2); + + cvalpp = va_arg(ap, uint8_t **); + *cvalpp = cvalp; + mb->scan += repc; + break; + + case 'M': + if (smb_msgbuf_has_space(mb, 4) == 0) + return (SMB_MSGBUF_UNDERFLOW); + + if (mb->scan[0] != 0xFF || + mb->scan[1] != 'S' || + mb->scan[2] != 'M' || + mb->scan[3] != 'B') { + return (SMB_MSGBUF_INVALID_HEADER); + } + mb->scan += 4; + break; + + default: + return (SMB_MSGBUF_INVALID_FORMAT); + } + } + + return (SMB_MSGBUF_SUCCESS); +} + + +/* + * smb_msgbuf_encode + * + * Encode a smb_msgbuf buffer as indicated by the format string using + * the variable arg list. This is similar to a sprintf operation. + * + * On success, returns the number of bytes encoded. Otherwise + * returns a -ve error code. + */ +int +smb_msgbuf_encode(smb_msgbuf_t *mb, char *fmt, ...) +{ + int rc; + uint8_t *orig_scan; + va_list ap; + + va_start(ap, fmt); + orig_scan = mb->scan; + rc = buf_encode(mb, fmt, ap); + va_end(ap); + + if (rc != SMB_MSGBUF_SUCCESS) { + (void) smb_msgbuf_chkerc("smb_msgbuf_encode", rc); + mb->scan = orig_scan; + return (rc); + } + + /*LINTED E_PTRDIFF_OVERFLOW*/ + return (mb->scan - orig_scan); +} + + +/* + * buf_encode + * + * Private encode function, where the real work of encoding the smb_msgbuf + * is done. This function should only be called via smb_msgbuf_encode to + * ensure correct behaviour and error handling. + */ +static int +buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap) +{ + uint8_t cval; + uint16_t wval; + uint32_t lval; + uint64_t llval; + uint32_t ival; + uint8_t *cvalp; + uint8_t c; + mts_wchar_t wcval; + int count; + int repc = 1; + int rc; + + while ((c = *fmt++) != 0) { + repc = 1; + + if (c == ' ' || c == '\t') + continue; + + if (c == '(') { + while (((c = *fmt++) != 0) && c != ')') + ; + + if (!c) + return (SMB_MSGBUF_SUCCESS); + + continue; + } + + if ('0' <= c && c <= '9') { + repc = 0; + do { + repc = repc * 10 + c - '0'; + c = *fmt++; + } while ('0' <= c && c <= '9'); + } else if (c == '#') { + repc = va_arg(ap, int); + c = *fmt++; + } + + switch (c) { + case '.': + if (smb_msgbuf_has_space(mb, repc) == 0) + return (SMB_MSGBUF_OVERFLOW); + + while (repc-- > 0) + *mb->scan++ = 0; + break; + + case 'c': + if (smb_msgbuf_has_space(mb, repc) == 0) + return (SMB_MSGBUF_OVERFLOW); + + cvalp = va_arg(ap, uint8_t *); + bcopy(cvalp, mb->scan, repc); + mb->scan += repc; + break; + + case 'b': + if (smb_msgbuf_has_space(mb, repc) == 0) + return (SMB_MSGBUF_OVERFLOW); + + while (repc-- > 0) { + cval = va_arg(ap, int); + *mb->scan++ = cval; + } + break; + + case 'w': + rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t)); + if (rc == 0) + return (SMB_MSGBUF_OVERFLOW); + + while (repc-- > 0) { + wval = va_arg(ap, int); + LE_OUT16(mb->scan, wval); + mb->scan += sizeof (uint16_t); + } + break; + + case 'l': + rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t)); + if (rc == 0) + return (SMB_MSGBUF_OVERFLOW); + + while (repc-- > 0) { + lval = va_arg(ap, uint32_t); + LE_OUT32(mb->scan, lval); + mb->scan += sizeof (int32_t); + } + break; + + case 'q': + rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t)); + if (rc == 0) + return (SMB_MSGBUF_OVERFLOW); + + while (repc-- > 0) { + llval = va_arg(ap, uint64_t); + LE_OUT64(mb->scan, llval); + mb->scan += sizeof (uint64_t); + } + break; + + case 'u': /* conditional unicode */ + if (mb->flags & SMB_MSGBUF_UNICODE) + goto unicode_translation; + /* FALLTHROUGH */ + + case 's': + cvalp = va_arg(ap, uint8_t *); + ival = strlen((const char *)cvalp) + 1; + + if (smb_msgbuf_has_space(mb, ival) == 0) + return (SMB_MSGBUF_OVERFLOW); + + ival = + mts_mbstos((char *)mb->scan, (const char *)cvalp); + mb->scan += ival + 1; + break; + + case 'U': /* unicode */ +unicode_translation: + /* + * Unicode strings are always word aligned. + */ + smb_msgbuf_word_align(mb); + cvalp = va_arg(ap, uint8_t *); + + for (;;) { + rc = smb_msgbuf_has_space(mb, + sizeof (mts_wchar_t)); + if (rc == 0) + return (SMB_MSGBUF_OVERFLOW); + + count = mts_mbtowc(&wcval, (const char *)cvalp, + MTS_MB_CHAR_MAX); + + if (count < 0) { + return (SMB_MSGBUF_DATA_ERROR); + } else if (count == 0) { + /* + * No longer need to do this now that + * mbtowc correctly writes the null + * before returning zero but paranoia + * wins. + */ + wcval = 0; + count = 1; + } + + /* Write wchar in wire-format */ + LE_OUT16(mb->scan, wcval); + + if (*cvalp == 0) { + /* + * End of string. Check to see whether + * or not to include the null + * terminator. + */ + if ((mb->flags & SMB_MSGBUF_NOTERM) == + 0) + mb->scan += + sizeof (mts_wchar_t); + break; + } + + mb->scan += sizeof (mts_wchar_t); + cvalp += count; + } + break; + + case 'M': + if (smb_msgbuf_has_space(mb, 4) == 0) + return (SMB_MSGBUF_OVERFLOW); + + *mb->scan++ = 0xFF; + *mb->scan++ = 'S'; + *mb->scan++ = 'M'; + *mb->scan++ = 'B'; + break; + + default: + return (SMB_MSGBUF_INVALID_FORMAT); + } + } + + return (SMB_MSGBUF_SUCCESS); +} + + +/* + * smb_msgbuf_malloc + * + * Allocate some memory for use with this smb_msgbuf. We increase the + * requested size to hold the list pointer and return a pointer + * to the area for use by the caller. + */ +static void * +smb_msgbuf_malloc(smb_msgbuf_t *mb, size_t size) +{ + smb_msgbuf_mlist_t *item; + + size += sizeof (smb_msgbuf_mlist_t); + +#ifndef _KERNEL + if ((item = malloc(size)) == NULL) + return (NULL); +#else + item = kmem_alloc(size, KM_SLEEP); +#endif + item->next = mb->mlist.next; + item->size = size; + mb->mlist.next = item; + + /* + * The caller gets a pointer to the address + * immediately after the smb_msgbuf_mlist_t. + */ + return ((void *)(item + 1)); +} + + +/* + * smb_msgbuf_chkerc + * + * Diagnostic function to write an appropriate message to the system log. + */ +static int +smb_msgbuf_chkerc(char *text, int erc) +{ + static struct { + int erc; + char *name; + } etable[] = { + { SMB_MSGBUF_SUCCESS, "success" }, + { SMB_MSGBUF_UNDERFLOW, "overflow/underflow" }, + { SMB_MSGBUF_INVALID_FORMAT, "invalid format" }, + { SMB_MSGBUF_INVALID_HEADER, "invalid header" }, + { SMB_MSGBUF_DATA_ERROR, "data error" } + }; + + int i; + + for (i = 0; i < sizeof (etable)/sizeof (etable[0]); ++i) { + if (etable[i].erc == erc) { + if (text == 0) + text = "smb_msgbuf_chkerc"; + break; + } + } + return (erc); +} + +static void +buf_decode_wcs(mts_wchar_t *dst_wcstr, mts_wchar_t *src_wcstr, int wcstrlen) +{ + int i; + + for (i = 0; i < wcstrlen; i++) { + *dst_wcstr = LE_IN16(src_wcstr); + dst_wcstr++; + src_wcstr++; + } +} diff --git a/usr/src/common/smbsrv/smb_native.c b/usr/src/common/smbsrv/smb_native.c new file mode 100644 index 000000000000..bd88cc542720 --- /dev/null +++ b/usr/src/common/smbsrv/smb_native.c @@ -0,0 +1,231 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module defines generic functions to map Native OS and Native + * LanMan names to values. + */ + +#ifdef _KERNEL +#include +#include +#else +#include +#endif +#include +#include + +/* + * smbnative_os_value + * + * Return the appropriate native OS value for the specified native OS name. + * + * Windows 2000 server: "Windows 2000 2195" + * Windows XP Professional client: "Windows 2002 2543" + * Windows XP PDC server: "Windows 5.1" + * Windows .Net: "Windows .NET 3621" + * Windows .Net: "Windows .NET 3718" + * + * DAVE (Thursby Software: CIFS for MacOS) uses "MacOS", sometimes with a + * version number appended, i.e. "MacOS 8.5.1". We treat DAVE like NT 4.0 + * except for the cases that DAVE clients set 'watch tree' flag in notify + * change requests. + * + * Samba reports UNIX as its Native OS, which we can map to NT 4.0. + */ +int +smbnative_os_value(char *native_os) +{ + typedef struct native_os_table { + int os_value; + char *os_name; + } native_os_table_t; + + static native_os_table_t os_table[] = { + { NATIVE_OS_WINNT, "Windows NT 4.0" }, + { NATIVE_OS_WINNT, "Windows NT" }, + { NATIVE_OS_WIN95, "Windows 4.0" }, + { NATIVE_OS_WIN2000, "Windows 5.0" }, + { NATIVE_OS_WIN2000, "Windows 5.1" }, + { NATIVE_OS_WIN2000, "Windows 2000 5.0" }, + { NATIVE_OS_NT5_1, "Windows 2000 5.1" }, + { NATIVE_OS_WIN2000, "Windows 2000" }, + { NATIVE_OS_WIN2000, "Windows 2002" }, + { NATIVE_OS_WIN2000, "Windows .NET" }, + { NATIVE_OS_WIN2000, "Windows Server 2003" }, + { NATIVE_OS_WIN2000, "Windows XP" }, + { NATIVE_OS_WINNT, "UNIX" }, + { NATIVE_OS_MACOS, "MacOS" } + }; + + int i; + int len; + char *os_name; + + if (native_os == NULL) { + return (NATIVE_OS_UNKNOWN); + } + + for (i = 0; i < sizeof (os_table)/sizeof (os_table[0]); ++i) { + os_name = os_table[i].os_name; + len = strlen(os_name); + + if (utf8_strncasecmp(os_name, native_os, len) == 0) { + return (os_table[i].os_value); + } + } + return (NATIVE_OS_UNKNOWN); +} + + +/* + * smbnative_lm_value + * + * Return the appropriate native LanMan value for the specified native + * LanMan name. There's an alignment problem in some packets from some + * clients that means we can miss the first character, so we do an + * additional check starting from the second character. + * + * DAVE (Thursby Software: CIFS for MacOS) sometimes uses a Unicode + * character in the LanMan name. Variations seen so far are: + * + * 44 00 41 00 56 00 45 00 00 00 D.A.V.E... + * + * 44 00 41 00 56 00 45 00 22 21 20 00 56 00 32 00 + * 2E 00 35 00 2E 00 31 00 00 00 D.A.V.E."!..V.2...5...1... + * + * Samba reports its own name (Samba) as its Native LM, which we can + * map to NT LM 4.0. + */ +int +smbnative_lm_value(char *native_lm) +{ + typedef struct native_lm_table { + int lm_value; + char *lm_name; + } native_lm_table_t; + + static native_lm_table_t lm_table[] = { + { NATIVE_LM_NT, "NT LAN Manager 4.0" }, + { NATIVE_LM_NT, "Windows NT 4.0" }, + { NATIVE_LM_NT, "Windows NT" }, + { NATIVE_LM_NT, "Windows 4.0" }, + { NATIVE_LM_WIN2000, "Windows 2000 LAN Manager" }, + { NATIVE_LM_WIN2000, "Windows 2000 5.0" }, + { NATIVE_LM_WIN2000, "Windows 2000 5.1" }, + { NATIVE_LM_WIN2000, "Windows 2000", }, + { NATIVE_LM_WIN2000, "Windows 2002 5.1" }, + { NATIVE_LM_WIN2000, "Windows 2002" }, + { NATIVE_LM_WIN2000, "Windows .NET 5.2" }, + { NATIVE_LM_WIN2000, "Windows .NET" }, + { NATIVE_LM_WIN2000, "Windows Server 2003" }, + { NATIVE_LM_WIN2000, "Windows XP" }, + { NATIVE_LM_NT, "Samba" }, + { NATIVE_LM_NT, "DAVE" } + }; + + int i; + int len; + char *lm_name; + + if (native_lm == NULL) { + return (NATIVE_LM_NONE); + } + + for (i = 0; i < sizeof (lm_table)/sizeof (lm_table[0]); ++i) { + lm_name = lm_table[i].lm_name; + len = strlen(lm_name); + + if ((utf8_strncasecmp(lm_name, native_lm, len) == 0) || + (utf8_strncasecmp(&lm_name[1], native_lm, len - 1) == 0)) { + return (lm_table[i].lm_value); + } + } + return (NATIVE_LM_NONE); +} + +/* + * smbnative_pdc_value + * + * This function is used when NetFORCE contacting a PDC + * to authenticate a connected user to determine and keep + * the PDC type. + * + * The reason for adding this functionality is that NetFORCE + * doesn't support Samba PDC but code didn't check the PDC type + * and do authentication agains any PDC. This behaviour could + * cause problem in some circumstances. + * Now that we determine the PDC type the authentication code + * can be configured (by smb.samba.pdc env var) to return access + * denied to authentication attempts when PDC is Samba. + */ +int +smbnative_pdc_value(char *native_lm) +{ + typedef struct pdc_table { + int pdc_value; + char *pdc_lmname; + } pdc_table_t; + + static pdc_table_t pdc_table[] = { + { PDC_WINNT, "NT LAN Manager 4.0" }, + { PDC_WINNT, "Windows NT 4.0" }, + { PDC_WINNT, "Windows NT" }, + { PDC_WINNT, "Windows 4.0" }, + { PDC_WIN2000, "Windows 2000 LAN Manager" }, + { PDC_WIN2000, "Windows 2000 5.0" }, + { PDC_WIN2000, "Windows 2000 5.1" }, + { PDC_WIN2000, "Windows 2000", }, + { PDC_WIN2000, "Windows 2002 5.1" }, + { PDC_WIN2000, "Windows 2002" }, + { PDC_WIN2000, "Windows .NET 5.2" }, + { PDC_WIN2000, "Windows .NET" }, + { PDC_SAMBA, "Samba" }, + { PDC_WINNT, "DAVE" } + }; + + int i; + int len; + char *pdc_lmname; + + if (native_lm == 0) { + return (PDC_UNKNOWN); + } + + for (i = 0; i < sizeof (pdc_table)/sizeof (pdc_table[0]); ++i) { + pdc_lmname = pdc_table[i].pdc_lmname; + len = strlen(pdc_lmname); + + if ((utf8_strncasecmp(pdc_lmname, native_lm, len) == 0) || + (utf8_strncasecmp(&pdc_lmname[1], native_lm, len - 1) + == 0)) { + return (pdc_table[i].pdc_value); + } + } + + return (PDC_UNKNOWN); +} diff --git a/usr/src/common/smbsrv/smb_netbios_util.c b/usr/src/common/smbsrv/smb_netbios_util.c new file mode 100644 index 000000000000..1bd47f80bcf5 --- /dev/null +++ b/usr/src/common/smbsrv/smb_netbios_util.c @@ -0,0 +1,394 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef _KERNEL +#include +#include +#else +#include +#endif +#include +#include +#include + +static int domainname_is_valid(char *domain_name); + +/* + * Routines than support name compression. + * + * The NetBIOS name representation in all NetBIOS packets (for NAME, + * SESSION, and DATAGRAM services) is defined in the Domain Name + * Service RFC 883[3] as "compressed" name messages. This format is + * called "second-level encoding" in the section entitled + * "Representation of NetBIOS Names" in the Concepts and Methods + * document. + * + * For ease of description, the first two paragraphs from page 31, + * the section titled "Domain name representation and compression", + * of RFC 883 are replicated here: + * + * Domain names messages are expressed in terms of a sequence + * of labels. Each label is represented as a one octet length + * field followed by that number of octets. Since every domain + * name ends with the null label of the root, a compressed + * domain name is terminated by a length byte of zero. The + * high order two bits of the length field must be zero, and + * the remaining six bits of the length field limit the label + * to 63 octets or less. + * + * To simplify implementations, the total length of label + * octets and label length octets that make up a domain name is + * restricted to 255 octets or less. + * + * The following is the uncompressed representation of the NetBIOS name + * "FRED ", which is the 4 ASCII characters, F, R, E, D, followed by 12 + * space characters (0x20). This name has the SCOPE_ID: "NETBIOS.COM" + * + * EGFCEFEECACACACACACACACACACACACA.NETBIOS.COM + * + * This uncompressed representation of names is called "first-level + * encoding" in the section entitled "Representation of NetBIOS Names" + * in the Concepts and Methods document. + * + * The following is a pictographic representation of the compressed + * representation of the previous uncompressed Domain Name + * representation. + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x20 | E (0x45) | G (0x47) | F (0x46) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | C (0x43) | E (0x45) | F (0x46) | E (0x45) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | E (0x45) | C (0x43) | A (0x41) | C (0x43) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | A (0x41) | C (0x43) | A (0x41) | C (0x43) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | A (0x41) | C (0x43) | A (0x41) | C (0x43) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | A (0x41) | C (0x43) | A (0x41) | C (0x43) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | A (0x41) | C (0x43) | A (0x41) | C (0x43) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | A (0x41) | C (0x43) | A (0x41) | C (0x43) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | A (0X41) | 0x07 | N (0x4E) | E (0x45) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | T (0x54) | B (0x42) | I (0x49) | O (0x4F) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | S (0x53) | 0x03 | C (0x43) | O (0x4F) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | M (0x4D) | 0x00 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Each section of a domain name is called a label [7 (page 31)]. A + * label can be a maximum of 63 bytes. The first byte of a label in + * compressed representation is the number of bytes in the label. For + * the above example, the first 0x20 is the number of bytes in the + * left-most label, EGFCEFEECACACACACACACACACACACACA, of the domain + * name. The bytes following the label length count are the characters + * of the label. The following labels are in sequence after the first + * label, which is the encoded NetBIOS name, until a zero (0x00) length + * count. The zero length count represents the root label, which is + * always null. + * + * A label length count is actually a 6-bit field in the label length + * field. The most significant 2 bits of the field, bits 7 and 6, are + * flags allowing an escape from the above compressed representation. + * If bits 7 and 6 are both set (11), the following 14 bits are an + * offset pointer into the full message to the actual label string from + * another domain name that belongs in this name. This label pointer + * allows for a further compression of a domain name in a packet. + * + * NetBIOS implementations can only use label string pointers in Name + * Service packets. They cannot be used in Session or Datagram Service + * packets. + * + * The other two possible values for bits 7 and 6 (01 and 10) of a label + * length field are reserved for future use by RFC 883[2 (page 32)]. + * + * Note that the first octet of a compressed name must contain one of + * the following bit patterns. (An "x" indicates a bit whose value may + * be either 0 or 1.): + * + * 00100000 - Netbios name, length must be 32 (decimal) + * 11xxxxxx - Label string pointer + * 10xxxxxx - Reserved + * 01xxxxxx - Reserved + */ + +/* + * netbios_first_level_name_encode + * + * Put test description here. + * + * Inputs: + * char * in -> Name to encode + * char * out -> Buffer to encode into. + * int length -> # of bytes to encode. + * + * Returns: + * Nothing + */ +int +netbios_first_level_name_encode(unsigned char *name, unsigned char *scope, + unsigned char *out, int max_out) +{ + unsigned char ch, len; + unsigned char *in; + unsigned char *lp; + unsigned char *op = out; + + if (max_out < 0x21) + return (-1); + + in = name; + *op++ = 0x20; + for (len = 0; len < NETBIOS_NAME_SZ; len++) { + ch = *in++; + *op++ = 'A' + ((ch >> 4) & 0xF); + *op++ = 'A' + ((ch) & 0xF); + } + + max_out -= 0x21; + + in = scope; + len = 0; + lp = op++; + while (((ch = *in++) != 0) && (max_out-- > 1)) { + if (ch == 0) { + if ((*lp = len) != 0) + *op++ = 0; + break; + } + if (ch == '.') { + *lp = len; + lp = op++; + len = 0; + } else { + *op++ = ch; + len++; + } + } + *lp = len; + if (len != 0) + *op = 0; + + /*LINTED E_PTRDIFF_OVERFLOW*/ + return (op - out); +} + +/* + * smb_first_level_name_decode + * + * The null terminated string "in" is the name to decode. The output + * is placed in the name_entry structure "name". + * + * The scope field is a series of length designated labels as described + * in the "Domain name representation and compression" section of RFC883. + * The two high order two bits of the length field must be zero, the + * remaining six bits contain the field length. The total length of the + * domain name is restricted to 255 octets but note that the trailing + * root label and its dot are not printed. When converting the labels, + * the length fields are replaced by dots. + * + * Returns the number of bytes scanned or -1 to indicate an error. + */ +int +netbios_first_level_name_decode(char *in, char *name, char *scope) +{ + unsigned int length, bytes; + char c1, c2; + char *cp; + char *out; + + cp = in; + + if ((length = *cp++) != 0x20) { + return (-1); + } + + out = name; + while (length > 0) { + c1 = *cp++; + c2 = *cp++; + + if ('A' <= c1 && c1 <= 'P' && 'A' <= c2 && c2 <= 'P') { + c1 -= 'A'; + c2 -= 'A'; + *out++ = (c1 << 4) | (c2); + } else { + return (-1); /* conversion error */ + } + length -= 2; + } + + out = scope; + bytes = 0; + for (length = *cp++; length != 0; length = *cp++) { + if ((length & 0xc0) != 0x00) { + /* + * This is a pointer or a reserved field. If it's + * a pointer (16-bits) we have to skip the next byte. + */ + if ((length & 0xc0) == 0xc0) { + cp++; + continue; + } + } + + /* + * Replace the length with a '.', except for the first one. + */ + if (out != scope) { + *out++ = '.'; + bytes++; + } + + while (length-- > 0) { + if (bytes++ >= (NETBIOS_DOMAIN_NAME_MAX - 1)) { + return (-1); + } + *out++ = *cp++; + } + } + *out = 0; + + /* + * We are supposed to preserve all 8-bits of the domain name + * but due to the single byte representation in the name cache + * and UTF-8 encoding everywhere else, we restrict domain names + * to Appendix 1 - Domain Name Syntax Specification in RFC883. + */ + if (domainname_is_valid(scope)) { + (void) utf8_strupr(scope); + /*LINTED E_PTRDIFF_OVERFLOW*/ + return (cp - in); + } + + scope[0] = '\0'; + return (-1); +} + +/* + * smb_netbios_name_isvalid + * + * This function is provided to be used by session service + * which runs in kernel in order to hide name_entry definition. + * + * It returns the decoded name in the provided buffer as 'out' + * if it's not null. + * + * Returns 0 if decode fails, 1 if it succeeds. + */ +int +netbios_name_isvalid(char *in, char *out) +{ + char name[NETBIOS_NAME_SZ]; + char scope[NETBIOS_DOMAIN_NAME_MAX]; + + if (netbios_first_level_name_decode(in, name, scope) < 0) + return (0); + + if (out) + (void) strlcpy(out, name, NETBIOS_NAME_SZ); + + return (1); +} + +/* + * Characters that we allow in DNS domain names, in addition to + * alphanumeric characters. This is not quite consistent with + * RFC883. This is global so that it can be patched if there is + * a need to change the valid characters in the field. + */ +unsigned char *dns_allowed = (unsigned char *)"-_"; + +/* + * dns_is_allowed + * + * Check the dns_allowed characters and return true (1) if the character + * is in the table. Otherwise return false (0). + */ +static int +dns_is_allowed(unsigned char c) +{ + unsigned char *p = dns_allowed; + + while (*p) { + if (c == *p++) + return (1); + } + + return (0); +} + + +/* + * domainname_is_valid + * + * Check the specified domain name for mostly compliance with RFC883 + * Appendix 1. Names may contain alphanumeric characters, hyphens, + * underscores and dots. The first character after a dot must be an + * alphabetic character. RFC883 doesn't mention underscores but we + * allow it due to common use, and we don't check that labels end + * with an alphanumeric character. + * + * Returns true (1) if the name is valid. Otherwise returns false (0). + */ +static int +domainname_is_valid(char *domain_name) +{ + char *name; + int first_char = 1; + + if (domain_name == 0) + return (0); + + for (name = domain_name; *name != 0; ++name) { + if (*name == '.') { + first_char = 1; + continue; + } + + if (first_char) { + if (mts_isalpha_ascii(*name) == 0) + return (0); + + first_char = 0; + continue; + } + + if (mts_isalnum_ascii(*name) || dns_is_allowed(*name)) + continue; + + return (0); + } + + return (1); +} diff --git a/usr/src/common/smbsrv/smb_oem.c b/usr/src/common/smbsrv/smb_oem.c new file mode 100644 index 000000000000..4dca80402ba9 --- /dev/null +++ b/usr/src/common/smbsrv/smb_oem.c @@ -0,0 +1,762 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Support for oem <-> unicode translations. + */ + +#ifndef _KERNEL +#include +#include +#include +#include +#include +#endif /* _KERNEL */ +#include +#include +#include +#include +/* + * name: Name used to show on the telnet/GUI. + * filename: The actual filename contains the codepage. + * doublebytes: The codepage is double or single byte. + * oempage: The oempage is used to convert Unicode to OEM chars. + * Memory needs to be allocated for value field of oempage + * to store the entire table. + * unipage: The unipage is used to convert OEM to Unicode chars. + * Memory needs to be allocated for value field of unipage + * to store the entire table. + * valid: This field indicates if the page is valid or not. + * ref: This ref count is used to keep track of the usage of BOTH + * oempage and unipage. + * Note: If the cpid of the table is changed, please change the + * codepage_id in oem.h as well. + */ +typedef struct oem_codepage { + char *filename; + unsigned int bytesperchar; + oempage_t oempage; + oempage_t unicodepage; + unsigned int valid; + unsigned int ref; +} oem_codepage_t; + +static oem_codepage_t oemcp_table[] = { + {"850.cpg", 1, {0, 0}, {0, 0}, 0, 0}, /* Multilingual Latin1 */ + {"950.cpg", 2, {1, 0}, {1, 0}, 0, 0}, /* Chinese Traditional */ + {"1252.cpg", 1, {2, 0}, {2, 0}, 0, 0}, /* MS Latin1 */ + {"949.cpg", 2, {3, 0}, {3, 0}, 0, 0}, /* Korean */ + {"936.cpg", 2, {4, 0}, {4, 0}, 0, 0}, /* Chinese Simplified */ + {"932.cpg", 2, {5, 0}, {5, 0}, 0, 0}, /* Japanese */ + {"852.cpg", 1, {6, 0}, {6, 0}, 0, 0}, /* Multilingual Latin2 */ + {"1250.cpg", 1, {7, 0}, {7, 0}, 0, 0}, /* MS Latin2 */ + {"1253.cpg", 1, {8, 0}, {8, 0}, 0, 0}, /* MS Greek */ + {"737.cpg", 1, {9, 0}, {9, 0}, 0, 0}, /* Greek */ + {"1254.cpg", 1, {10, 0}, {10, 0}, 0, 0}, /* MS Turkish */ + {"857.cpg", 1, {11, 0}, {11, 0}, 0, 0}, /* Multilingual Latin5 */ + {"1251.cpg", 1, {12, 0}, {12, 0}, 0, 0}, /* MS Cyrillic */ + {"866.cpg", 1, {13, 0}, {13, 0}, 0, 0}, /* Cyrillic II */ + {"1255.cpg", 1, {14, 0}, {14, 0}, 0, 0}, /* MS Hebrew */ + {"862.cpg", 1, {15, 0}, {15, 0}, 0, 0}, /* Hebrew */ + {"1256.cpg", 1, {16, 0}, {16, 0}, 0, 0}, /* MS Arabic */ + {"720.cpg", 1, {17, 0}, {17, 0}, 0, 0} /* Arabic */ +}; + +static language lang_table[] = { + {"Arabic", OEM_CP_IND_720, OEM_CP_IND_1256}, + {"Brazilian", OEM_CP_IND_850, OEM_CP_IND_1252}, + {"Chinese Traditional", OEM_CP_IND_950, OEM_CP_IND_950}, + {"Chinese Simplified", OEM_CP_IND_936, OEM_CP_IND_936}, + {"Czech", OEM_CP_IND_852, OEM_CP_IND_1250}, + {"Danish", OEM_CP_IND_850, OEM_CP_IND_1252}, + {"Dutch", OEM_CP_IND_850, OEM_CP_IND_1252}, + {"English", OEM_CP_IND_850, OEM_CP_IND_1252}, + {"Finnish", OEM_CP_IND_850, OEM_CP_IND_1252}, + {"French", OEM_CP_IND_850, OEM_CP_IND_1252}, + {"German", OEM_CP_IND_850, OEM_CP_IND_1252}, + {"Greek", OEM_CP_IND_737, OEM_CP_IND_1253}, + {"Hebrew", OEM_CP_IND_862, OEM_CP_IND_1255}, + {"Hungarian", OEM_CP_IND_852, OEM_CP_IND_1250}, + {"Italian", OEM_CP_IND_850, OEM_CP_IND_1252}, + {"Japanese", OEM_CP_IND_932, OEM_CP_IND_932}, + {"Korean", OEM_CP_IND_949, OEM_CP_IND_949}, + {"Norwegian", OEM_CP_IND_850, OEM_CP_IND_1252}, + {"Polish", OEM_CP_IND_852, OEM_CP_IND_1250}, + {"Russian", OEM_CP_IND_866, OEM_CP_IND_1251}, + {"Slovak", OEM_CP_IND_852, OEM_CP_IND_1250}, + {"Slovenian", OEM_CP_IND_852, OEM_CP_IND_1250}, + {"Spanish", OEM_CP_IND_850, OEM_CP_IND_1252}, + {"Swedish", OEM_CP_IND_850, OEM_CP_IND_1252}, + {"Turkish", OEM_CP_IND_857, OEM_CP_IND_1254} +}; + + + +/* + * The oem_default_smb_cp is the default smb codepage for English. + * It is actually codepage 850. + */ +mts_wchar_t oem_default_smb_cp[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, + 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, + 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, + 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + + + +/* + * The oem_default_telnet_cp is the default telnet codepage for English. + * It is actually codepage 1252. + */ +mts_wchar_t oem_default_telnet_cp[256] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, + 0x9, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x40, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x60, + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x70, + 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, 0x20AC, + 0x81, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, + 0x2030, 0x160, 0x2039, 0x152, 0x8D, 0x017D, 0x8F, 0x90, + 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, + 0x2122, 0x161, 0x203A, 0x153, 0x9D, 0x017E, 0x178, 0x00A0, + 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, + 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, + 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, + 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, + 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, + 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, + 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, + 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, + 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, + 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, + 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, + 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF +}; + + +#define MAX_OEMPAGES (sizeof (oemcp_table) / sizeof (oemcp_table[0])) +#define MAX_UNI_IDX 65536 + + + +/* + * oem_codepage_bytesperchar + * + * This function returns the max bytes per oem char for the specified + * oem table. This basically shows if the oem codepage is single or + * double bytes. + */ +static unsigned int +oem_codepage_bytesperchar(unsigned int cpid) +{ + if (cpid >= MAX_OEMPAGES) + return (0); + else + return (oemcp_table[cpid].bytesperchar); +} + + + +/* + * oem_get_codepage_path + * + * This function will get the codepage path. + */ +const char * +oem_get_codepage_path(void) +{ +#ifdef PBSHORTCUT /* */ + const char *path = getenv("codepage.oem.directory"); + if (path == 0) + return ("/"); + else + return (path); +#else /* PBSHORTCUT */ + return ("/"); +#endif /* PBSHORTCUT */ +} + +/* + * oem_codepage_init + * + * This function will init oem page via the cpid of the oem table. + * The function oem_codepage_free must be called when the oempage is + * no longer needed to free up the allocated memory. If the codepage is + * successfully initialized, zero will be the return value; otherwise + * -1 will be the return value. + */ +int +oem_codepage_init(unsigned int cpid) +{ +#ifndef _KERNEL + FILE *fp; + static mutex_t mutex; + char buf[32]; + char filePath[100]; +#endif /* _KERNEL */ + unsigned int max_oem_index; + const char *codepagePath = oem_get_codepage_path(); + mts_wchar_t *default_oem_cp = 0; + oem_codepage_t *oemcp; + + /* + * The OEM codepages 850 and 1252 are stored in kernel; therefore, + * no need for codepagePath to be defined to work. + */ + if (cpid >= MAX_OEMPAGES || + (codepagePath == 0 && + cpid != oem_default_smb_cpid && cpid != oem_default_telnet_cpid)) + return (-1); + + max_oem_index = 1 << oem_codepage_bytesperchar(cpid) * 8; + /* + * Use mutex so no two same index can be initialize + * at the same time. + */ +#ifndef _KERNEL + (void) mutex_lock(&mutex); +#endif /* _KERNEL */ + + oemcp = &oemcp_table[cpid]; + if (oemcp->valid) { + oemcp->valid++; +#ifndef _KERNEL + (void) mutex_unlock(&mutex); +#endif /* _KERNEL */ + return (0); + } + + oemcp->oempage.value = + MEM_ZALLOC("oem", max_oem_index * sizeof (mts_wchar_t)); + if (oemcp->oempage.value == 0) { +#ifndef _KERNEL + (void) mutex_unlock(&mutex); +#endif /* _KERNEL */ + return (-1); + } + + oemcp->unicodepage.value = + MEM_ZALLOC("oem", MAX_UNI_IDX * sizeof (mts_wchar_t)); + if (oemcp->unicodepage.value == 0) { + MEM_FREE("oem", oemcp->oempage.value); + oemcp->oempage.value = 0; +#ifndef _KERNEL + (void) mutex_unlock(&mutex); +#endif /* _KERNEL */ + return (-1); + } + + /* + * The default English page is stored in kernel. + * Therefore, no need to go to codepage files. + */ +#ifndef _KERNEL + if (cpid == oem_default_smb_cpid) + default_oem_cp = oem_default_smb_cp; + else if (cpid == oem_default_telnet_cpid) + default_oem_cp = oem_default_telnet_cp; + else + default_oem_cp = 0; +#else /* _KERNEL */ + default_oem_cp = oem_default_smb_cp; +#endif /* _KERNEL */ + + if (default_oem_cp) { + int i; + + for (i = 0; i < max_oem_index; i++) { + oemcp->oempage.value[i] = default_oem_cp[i]; + oemcp->unicodepage.value[default_oem_cp[i]] = + (mts_wchar_t)i; + } +#ifdef _KERNEL + } + /* + * XXX This doesn't seem right. How do we handle the situation + * where default_oem_cp == 0 in the kernel? + * Is this a PBSHORTCUT? + */ +#else + } else { + + /* + * The codepage is not one of the default that stores + * in the include + * file; therefore, we need to read from the file. + */ + (void) snprintf(filePath, sizeof (filePath), + "%s/%s", codepagePath, oemcp->filename); + fp = fopen(filePath, "r"); + + if (fp == 0) { + MEM_FREE("oem", oemcp->oempage.value); + MEM_FREE("oem", oemcp->unicodepage.value); +#ifndef _KERNEL + (void) mutex_unlock(&mutex); +#endif /* _KERNEL */ + return (-1); + } + + while (fgets(buf, 32, fp) != 0) { + char *endptr; + unsigned int oemval, unival; + + endptr = (char *)strchr(buf, ' '); + if (endptr == 0) { + continue; + } + + oemval = strtol(buf, &endptr, 0); + unival = strtol(endptr+1, 0, 0); + + if (oemval >= max_oem_index || unival >= MAX_UNI_IDX) { + continue; + } + + oemcp->oempage.value[oemval] = unival; + oemcp->unicodepage.value[unival] = oemval; + } + (void) fclose(fp); + } +#endif /* _KERNEL */ + + oemcp->valid = 1; +#ifndef _KERNEL + (void) mutex_unlock(&mutex); +#endif /* _KERNEL */ + return (0); +} + + + + +/* + * oem_codepage_free + * + * This function will clear the valid bit and free the memory + * allocated to the oem/unipage by oem_codepage_init if the ref count + * is zero. + */ +void +oem_codepage_free(unsigned int cpid) +{ + oem_codepage_t *oemcp; + + if (cpid >= MAX_OEMPAGES || !oemcp_table[cpid].valid) + return; + + oemcp = &oemcp_table[cpid]; + oemcp->valid--; + + if (oemcp->ref != 0 || oemcp->valid != 0) + return; + + if (oemcp->oempage.value != 0) { + MEM_FREE("oem", oemcp->oempage.value); + oemcp->oempage.value = 0; + } + + if (oemcp->unicodepage.value != 0) { + MEM_FREE("oem", oemcp->unicodepage.value); + oemcp->unicodepage.value = 0; + } +} + + + +/* + * oem_get_oempage + * + * This function will return the current oempage and increment + * the ref count. The function oem_release_page should always + * be called when finish using the oempage to decrement the + * ref count. + */ +static oempage_t * +oem_get_oempage(unsigned int cpid) +{ + if (cpid >= MAX_OEMPAGES) + return (0); + + if (oemcp_table[cpid].valid) { + oemcp_table[cpid].ref++; + return (&oemcp_table[cpid].oempage); + } + return (0); +} + + + +/* + * oem_get_unipage + * + * This function will return the current unipage and increment + * the ref count. The function oem_release_page should always + * be called when finish using the unipage to decrement the + * ref count. + */ +static oempage_t * +oem_get_unipage(unsigned int cpid) +{ + if (cpid >= MAX_OEMPAGES) + return (0); + + if (oemcp_table[cpid].valid) { + oemcp_table[cpid].ref++; + return (&oemcp_table[cpid].unicodepage); + } + return (0); +} + + + +/* + * oem_release_page + * + * This function will decrement the ref count and check the valid + * bit. It will free the memory allocated for the pages + * if the + * valid bit is not set, ref count is zero and the page is not + * already freed. + */ +static void +oem_release_page(oempage_t *page) +{ + oem_codepage_t *oemcp = &oemcp_table[page->cpid]; + + page = 0; + + if (oemcp->ref > 0) + oemcp->ref--; + + if (oemcp->ref != 0 || oemcp->valid) + return; + + if (oemcp->oempage.value != 0) { + MEM_FREE("oem", oemcp->oempage.value); + oemcp->oempage.value = 0; + } + + if (oemcp->unicodepage.value != 0) { + MEM_FREE("oem", oemcp->unicodepage.value); + oemcp->unicodepage.value = 0; + } +} + + + +/* + * unicodestooems + * + * Convert unicode string to oem string. The function will stop + * converting the unicode string when size nbytes - 1 is reached + * or when there is not enough room to store another unicode. + * If the function is called when the codepage is not initialized + * or when the codepage initialize failed, it will return 0. + * Otherwise, the total # of the converted unicode is returned. + */ +size_t +unicodestooems( + char *oemstring, + const mts_wchar_t *unicodestring, + size_t nbytes, + unsigned int cpid) +{ + oempage_t *unipage; + unsigned int count = 0; + mts_wchar_t oemchar; + + if (cpid >= MAX_OEMPAGES) + return (0); + + if (unicodestring == 0 || oemstring == 0) + return (0); + + if ((unipage = oem_get_unipage(cpid)) == 0) + return (0); + + while ((oemchar = unipage->value[*unicodestring]) != 0) { + if (oemchar & 0xff00 && nbytes >= MTS_MB_CHAR_MAX) { + *oemstring++ = oemchar >> 8; + *oemstring++ = (char)oemchar; + nbytes -= 2; + } else if (nbytes > 1) { + *oemstring++ = (char)oemchar; + nbytes--; + } else + break; + + count++; + unicodestring++; + } + + *oemstring = 0; + + oem_release_page(unipage); + + return (count); +} + + + +/* + * oemstounicodes + * + * Convert oem string to unicode string. The function will stop + * converting the oem string when unicodestring len reaches nwchars - 1. + * or when there is not enough room to store another oem char. + * If the function is called when the codepage is not initialized + * or when the codepage initialize failed, it will return 0. + * Otherwise, the total # of the converted oem chars is returned. + * The oem char can be either 1 or 2 bytes. + */ +size_t +oemstounicodes( + mts_wchar_t *unicodestring, + const char *oemstring, + size_t nwchars, + unsigned int cpid) +{ + oempage_t *oempage; + size_t count = nwchars; + mts_wchar_t oemchar; + + if (cpid >= MAX_OEMPAGES) + return (0); + + if (unicodestring == 0 || oemstring == 0) + return (0); + + if ((oempage = oem_get_oempage(cpid)) == 0) + return (0); + + while ((oemchar = (mts_wchar_t)*oemstring++ & 0xff) != 0) { + /* + * Cannot find one byte oemchar in table. Must be + * a lead byte. Try two bytes. + */ + + if ((oempage->value[oemchar] == 0) && (oemchar != 0)) { + oemchar = oemchar << 8 | (*oemstring++ & 0xff); + if (oempage->value[oemchar] == 0) { + *unicodestring = 0; + break; + } + } +#ifdef _BIG_ENDIAN + *unicodestring = LE_IN16(&oempage->value[oemchar]); +#else + *unicodestring = oempage->value[oemchar]; +#endif + count--; + unicodestring++; + } + + *unicodestring = 0; + + oem_release_page(oempage); + + return (nwchars - count); +} + +/* + * oem_get_lang_table + * + * This function returns a pointer to the language table. + */ +language * +oem_get_lang_table(void) +{ + return (lang_table); +} + +/* + * oem_no_of_languages + * + * This function returns total languages support in the system. + */ +int +oem_no_of_languages(void) +{ + return (sizeof (lang_table)/sizeof (lang_table[0])); +} + + +#ifndef _KERNEL +#if 1 +/* + * TESTING Functions + */ +void +oemcp_print(unsigned int cpid) +{ + unsigned int bytesperchar, max_index, i; + oempage_t *oempage, *unipage; + unsigned int counter = 0; + + if (cpid >= MAX_OEMPAGES) { + (void) printf("oemcp cpid %d is invalid\n", cpid); + return; + } + + if ((oempage = oem_get_oempage(cpid)) == 0) { + (void) printf("oemcp of cpid %d is invalid\n", cpid); + return; + } + + if ((unipage = oem_get_unipage(cpid)) == 0) { + (void) printf("unicp of cpid %d is invalid\n", cpid); + return; + } + + if ((bytesperchar = oem_codepage_bytesperchar(cpid)) == 0) { + (void) printf("bytesperchar of cpid %d is not correct\n", cpid); + return; + } + + max_index = 1 << bytesperchar * 8; + + (void) printf("OEMPAGE:\n"); + for (i = 0; i < max_index; i++) { + if ((counter + 1) % 4 == 0 && + (oempage->value[i] != 0 || i == 0)) { + (void) printf("%x %x\n", i, oempage->value[i]); + counter++; + } else if (oempage->value[i] != 0 || i == 0) { + (void) printf("%x %x, ", i, oempage->value[i]); + counter++; + } + } + counter = 0; + (void) printf("\n\nUNIPAGE:\n"); + for (i = 0; i < 65536; i++) { + if ((counter + 1) % 8 == 0 && + (unipage->value[i] != 0 || i == 0)) { + (void) printf("%x %x\n", i, unipage->value[i]); + counter++; + } else if (unipage->value[i] != 0 || i == 0) { + (void) printf("%x %x, ", i, unipage->value[i]); + counter++; + } + } + (void) printf("\n"); + oem_release_page(oempage); + oem_release_page(unipage); +} + + + +void +oemstringtest(unsigned int cpid) +{ + unsigned char *c, *cbuf; + unsigned char cbuf1[100] = {0xfe, 0xfd, 0xf2, 0xe9, + 0x63, 0xce, 0xdb, 0x8c, 0x9c, 0x21, 0}; + unsigned char cbuf2[100] = {0xfe, 0xfc, 0x63, 0x81, 0x42, + 0x91, 0x40, 0x24, 0xff, 0x49}; + mts_wchar_t buf[100], *wc; + + if (cpid == 1) + cbuf = cbuf1; + else if (cpid == 2) + cbuf = cbuf2; + + /* + * Before oem->uni conversion. + */ + (void) printf("Before oem->uni conversion: "); + for (c = cbuf; *c != 0; c++) + (void) printf("%x ", *c); + (void) printf("\n"); + + /* + * oem->uni conversion + */ + (void) oemstounicodes(buf, (const char *)cbuf, 100, cpid); + + /* + * After oem->uni conversion. + */ + (void) printf("After oem->uni conversion: "); + for (wc = buf; *wc != 0; wc++) + (void) printf("%x ", *wc); + (void) printf("\n"); + + /* + * uni->oem conversion + */ + (void) unicodestooems((char *)cbuf, buf, 100, cpid); + + /* + * After uni->oem conversion. + */ + (void) printf("After uni->oem conversion: "); + for (c = cbuf; *c != 0; c++) + (void) printf("%x ", *c); + (void) printf("\n"); +} +#endif +#endif /* _KERNEL */ diff --git a/usr/src/common/smbsrv/smb_opmlang.c b/usr/src/common/smbsrv/smb_opmlang.c new file mode 100644 index 000000000000..af7ee73cb1e9 --- /dev/null +++ b/usr/src/common/smbsrv/smb_opmlang.c @@ -0,0 +1,127 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include + +static unsigned int smb_cpid = NO_OF_OEM_CP_INDS; +static unsigned int telnet_cpid = NO_OF_OEM_CP_INDS; + +/* + * oem_get_smb_cpid + * + * This function returns the cpid for current smb codepage. + */ +unsigned int +oem_get_smb_cpid(void) +{ + return (smb_cpid); +} + +/* + * oem_get_telnet_cpid + * + * This function returns the cpid for current telnet codepage. + */ +unsigned int +oem_get_telnet_cpid(void) +{ + return (telnet_cpid); +} + +/* + * oem_current_language + * + * This function will return the current language setting. + * The current language is stored in env "codepage.oem.language". + * If the env does not exist, "None Selected" will be returned. + */ +char * +oem_current_language() +{ +#ifdef PBSHORTCUT + char *p = getenv("codepage.oem.language"); + + if (p) + return (p); +#endif + return ("None Selected"); +} + + +/* + * oem_language_set + * + * This function will set the oem language and correct + * env variables. + */ +int +oem_language_set(char *lang_name) +{ + int i; + language *lang_table = oem_get_lang_table(); + + for (i = 0; i < NO_OF_LANGUAGES; i++) { + if (utf8_strcasecmp(lang_name, lang_table[i].language) == 0) { + unsigned int oldSmbIndex = smb_cpid; + unsigned int oldTelnetIndex = telnet_cpid; + if (oem_codepage_init(lang_table[i].smbIndex) < 0 || + oem_codepage_init(lang_table[i].telnetIndex) < 0) { + oem_codepage_free(lang_table[i].smbIndex); + oem_codepage_free(lang_table[i].telnetIndex); + (void) oem_codepage_init(oem_default_smb_cpid); + (void) oem_codepage_init( + oem_default_telnet_cpid); + smb_cpid = oem_default_smb_cpid; + telnet_cpid = oem_default_telnet_cpid; +#ifdef PBSHORTCUT + setenv("codepage.oem.language", + oem_default_language); +#endif + } else { + smb_cpid = lang_table[i].smbIndex; + telnet_cpid = lang_table[i].telnetIndex; +#ifdef PBSHORTCUT + setenv("codepage.oem.language", + lang_table[i].language); +#endif + } +#ifdef PBSHORTCUT + saveenv(); +#endif + + if (oldSmbIndex < NO_OF_OEM_CP_INDS) + oem_codepage_free(oldSmbIndex); + if (oldTelnetIndex < NO_OF_OEM_CP_INDS) + oem_codepage_free(oldTelnetIndex); + return (0); + } + } + + return (-1); +} diff --git a/usr/src/common/smbsrv/smb_share_door_decode.c b/usr/src/common/smbsrv/smb_share_door_decode.c new file mode 100644 index 000000000000..cc3f2884e8b5 --- /dev/null +++ b/usr/src/common/smbsrv/smb_share_door_decode.c @@ -0,0 +1,156 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Encode/decode functions used by both lmshare door server and client. + */ + +#ifndef _KERNEL +#include +#include +#include +#else +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +void +smb_dr_get_kconfig(smb_dr_ctx_t *ctx, smb_kmod_cfg_t *cfg) +{ + if (ctx->status == 0) { + (void) memcpy(cfg, ctx->ptr, sizeof (smb_kmod_cfg_t)); + ctx->ptr += sizeof (smb_kmod_cfg_t); + } + else + bzero(cfg, sizeof (smb_kmod_cfg_t)); +} + +void +smb_dr_put_kconfig(smb_dr_ctx_t *ctx, smb_kmod_cfg_t *cfg) +{ + if (ctx->ptr + sizeof (smb_kmod_cfg_t) <= ctx->end_ptr) { + (void) memcpy(ctx->ptr, cfg, sizeof (smb_kmod_cfg_t)); + ctx->ptr += sizeof (smb_kmod_cfg_t); + } + else + ctx->status = ENOSPC; +} + +void +smb_dr_get_lmshare(smb_dr_ctx_t *ctx, lmshare_info_t *si) +{ + if (ctx->status == 0) { + if (smb_dr_get_int32(ctx)) { + (void) memcpy(si, ctx->ptr, sizeof (lmshare_info_t)); + ctx->ptr += sizeof (lmshare_info_t); + } + else + bzero(si, sizeof (lmshare_info_t)); + } + else + bzero(si, sizeof (lmshare_info_t)); +} + +void +smb_dr_put_lmshare(smb_dr_ctx_t *ctx, lmshare_info_t *si) +{ + if (si) { + smb_dr_put_int32(ctx, 1); + if (ctx->ptr + sizeof (lmshare_info_t) <= ctx->end_ptr) { + (void) memcpy(ctx->ptr, si, sizeof (lmshare_info_t)); + ctx->ptr += sizeof (lmshare_info_t); + } + else + ctx->status = ENOSPC; + } + else + smb_dr_put_int32(ctx, 0); +} + +uint64_t +smb_dr_get_lmshr_iterator(smb_dr_ctx_t *ctx) +{ + uint64_t lmshr_iter; + + if (smb_dr_get_int32(ctx)) + lmshr_iter = smb_dr_get_uint64(ctx); + else + lmshr_iter = 0; + + return (lmshr_iter); +} + +void +smb_dr_put_lmshr_iterator(smb_dr_ctx_t *ctx, uint64_t lmshr_iter) +{ + if (lmshr_iter) { + smb_dr_put_int32(ctx, 1); + smb_dr_put_uint64(ctx, lmshr_iter); + } + else + smb_dr_put_int32(ctx, 0); +} + +void +smb_dr_get_lmshr_list(smb_dr_ctx_t *ctx, lmshare_list_t *shrlist) +{ + if (ctx->status == 0) { + if (smb_dr_get_int32(ctx)) { + (void) memcpy(shrlist, + ctx->ptr, sizeof (lmshare_list_t)); + ctx->ptr += sizeof (lmshare_list_t); + } + else + bzero(shrlist, sizeof (lmshare_list_t)); + } + else + bzero(shrlist, sizeof (lmshare_list_t)); +} + +void +smb_dr_put_lmshr_list(smb_dr_ctx_t *ctx, lmshare_list_t *shrlist) +{ + if (shrlist) { + smb_dr_put_int32(ctx, 1); + if (ctx->ptr + sizeof (lmshare_list_t) <= ctx->end_ptr) { + (void) memcpy(ctx->ptr, + shrlist, sizeof (lmshare_list_t)); + ctx->ptr += sizeof (lmshare_list_t); + } + else + ctx->status = ENOSPC; + } + else + smb_dr_put_int32(ctx, 0); +} diff --git a/usr/src/common/smbsrv/smb_sid.c b/usr/src/common/smbsrv/smb_sid.c new file mode 100644 index 000000000000..ffbb3048ed82 --- /dev/null +++ b/usr/src/common/smbsrv/smb_sid.c @@ -0,0 +1,587 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * NT Security Identifier (SID) library functions. + */ + +#ifndef _KERNEL +#include +#include +#include +#include +#include +#else /* _KERNEL */ +#include +#include +#endif /* _KERNEL */ + +#include +#include +#include +#include + + +/* + * nt_sid_is_valid + * + * Check that a sid is valid. The checking is minimal: check the pointer + * is valid and that the revision and sub-authority count is legal. + * Returns 1 if the sid appears to be valid. Otherwise 0. + */ +int +nt_sid_is_valid(nt_sid_t *sid) +{ + if (sid == 0) + return (0); + + return (sid->Revision == NT_SID_REVISION && + sid->SubAuthCount < NT_SID_SUBAUTH_MAX) ? 1 : 0; +} + + +/* + * nt_sid_length + * + * Returns the number of bytes required to hold the sid. + */ +int +nt_sid_length(nt_sid_t *sid) +{ + if (sid == 0) + return (0); + + return (sizeof (nt_sid_t) - sizeof (DWORD) + + (sid->SubAuthCount * sizeof (DWORD))); +} + + +/* + * nt_sid_dup + * + * Make a duplicate of the specified sid. The memory for the new sid is + * allocated using malloc so the caller should call free when it is no + * longer required. A pointer to the new sid is returned. + */ +nt_sid_t * +nt_sid_dup(nt_sid_t *sid) +{ + nt_sid_t *new_sid; + int size; + int i; + + if (sid == 0) + return (0); + + size = sizeof (nt_sid_t) + + (sid->SubAuthCount * sizeof (DWORD)) + + sizeof (DWORD); + + if ((new_sid = MEM_MALLOC("libnt", size)) == 0) + return (0); + + (void) memcpy(new_sid, sid, sizeof (nt_sid_t)); + + for (i = 0; i < sid->SubAuthCount && i < NT_SID_SUBAUTH_MAX; ++i) + new_sid->SubAuthority[i] = sid->SubAuthority[i]; + + return (new_sid); +} + + +/* + * nt_sid_splice + * + * Make a full user sid from the domain sid and the user relative id + * (rid). The memory for the new sid is allocated using malloc so the + * caller should call free when it is no longer required. A pointer + * to the new sid is returned. + */ +nt_sid_t * +nt_sid_splice(nt_sid_t *domain_sid, DWORD rid) +{ + nt_sid_t *sid; + int size; + int i; + + if (domain_sid == 0) + return (0); + + size = sizeof (nt_sid_t) + + (domain_sid->SubAuthCount * sizeof (DWORD)) + + sizeof (DWORD); + + if ((sid = MEM_MALLOC("libnt", size)) == 0) + return (0); + + (void) memcpy(sid, domain_sid, sizeof (nt_sid_t)); + + for (i = 0; i < sid->SubAuthCount && i < NT_SID_SUBAUTH_MAX; ++i) + sid->SubAuthority[i] = domain_sid->SubAuthority[i]; + + sid->SubAuthority[i] = rid; + ++sid->SubAuthCount; + return (sid); +} + + +/* + * nt_sid_get_rid + * + * Return the Relative Id (RID) from the specified SID. It is the + * caller's responsibility to ensure that this is an appropriate SID. + * All we do here is return the last sub-authority from the SID. + */ +int +nt_sid_get_rid(nt_sid_t *sid, DWORD *rid) +{ + if (!nt_sid_is_valid(sid)) + return (-1); + + if (sid->SubAuthCount == 0) { + return (-1); + } + + if (rid) + *rid = sid->SubAuthority[sid->SubAuthCount - 1]; + return (0); +} + + +/* + * nt_sid_split + * + * Take a full user sid and split it into the domain sid and the user + * relative id (rid). The original sid is modified in place - use + * nt_sid_dup before calling this function to preserve the original SID. + */ +int +nt_sid_split(nt_sid_t *sid, DWORD *rid) +{ + if (!nt_sid_is_valid(sid)) { + return (-1); + } + + if (sid->SubAuthCount == 0) { + return (-1); + } + + --sid->SubAuthCount; + if (rid) + *rid = sid->SubAuthority[sid->SubAuthCount]; + return (0); +} + + +/* + * nt_sid_gen_null_sid + * + * This function allocates a SID structure and initializes it as the + * well-known Null SID (S-1-0-0). A pointer to the SID is returned. + * As the memory for this structure is obtained via malloc, it is the + * caller's responsibility to free the memory when it is no longer + * required. If malloc fails, a null pointer is returned. + */ +nt_sid_t * +nt_sid_gen_null_sid(void) +{ + nt_sid_t *sid; + int size; + + size = sizeof (nt_sid_t) + sizeof (DWORD); + + if ((sid = MEM_MALLOC("libnt", size)) == 0) { + return (0); + } + + sid->Revision = 1; + sid->SubAuthCount = 1; + return (sid); +} + + +/* + * nt_sid_is_equal + * + * Compare two SIDs and return a boolean result. The checks are ordered + * such that components that are more likely to differ are checked + * first. For example, after checking that the SIDs contain the same + * SubAuthCount, we check the sub-authorities in reverse order because + * the RID is the most likely differentiator between two SIDs, i.e. + * they are probably going to be in the same domain. + * + * Returns 1 if the SIDs are equal. Otherwise returns 0. + */ +int +nt_sid_is_equal(nt_sid_t *sid1, nt_sid_t *sid2) +{ + int i; + + if (sid1 == 0 || sid2 == 0) + return (0); + + if (sid1->SubAuthCount != sid2->SubAuthCount || + sid1->Revision != sid2->Revision) + return (0); + + for (i = sid1->SubAuthCount - 1; i >= 0; --i) + if (sid1->SubAuthority[i] != sid2->SubAuthority[i]) + return (0); + + if (bcmp(&sid1->Authority, &sid2->Authority, NT_SID_AUTH_MAX)) + return (0); + + return (1); +} + +/* + * nt_sid_is_indomain + * + * Check if given SID is in given domain. + * Returns 1 on success. Otherwise returns 0. + */ +int +nt_sid_is_indomain(nt_sid_t *domain_sid, nt_sid_t *sid) +{ + int i; + + if (sid == 0 || domain_sid == 0) { + return (0); + } + + if (domain_sid->Revision != sid->Revision || + sid->SubAuthCount < domain_sid->SubAuthCount) + return (0); + + for (i = domain_sid->SubAuthCount - 1; i >= 0; --i) + if (domain_sid->SubAuthority[i] != sid->SubAuthority[i]) + return (0); + + if (bcmp(&domain_sid->Authority, &sid->Authority, NT_SID_AUTH_MAX)) + return (0); + + return (1); +} + +#ifndef _KERNEL +/* + * nt_sid_is_local + * + * Check a SID to see if it belongs to the local domain. This is almost + * the same as checking that two SIDs are equal except that we don't + * care if the specified SID contains extra sub-authorities. We're only + * interested in the domain part. + * + * Returns 1 if the SIDs are equal. Otherwise returns 0. + */ +int +nt_sid_is_local(nt_sid_t *sid) +{ + nt_sid_t *local_sid; + + local_sid = nt_domain_local_sid(); + return (nt_sid_is_indomain(local_sid, sid)); +} + +/* + * nt_sid_is_builtin + * + * Check a SID to see if it belongs to the builtin domain. + * Returns 1 if the SID is a builtin SID. Otherwise returns 0. + */ +int +nt_sid_is_builtin(nt_sid_t *sid) +{ + nt_domain_t *domain; + + domain = nt_domain_lookupbytype(NT_DOMAIN_BUILTIN); + if (domain == 0) + return (0); + return (nt_sid_is_indomain(domain->sid, sid)); +} +#endif /* _KERNEL */ + +/* + * nt_sid_is_domain_equal + * + * Compare two SIDs's domain and return a boolean result. + * + * Returns 1 if the domain SID are the same. Otherwise returns 0. + */ +int +nt_sid_is_domain_equal(nt_sid_t *pSid1, nt_sid_t *pSid2) +{ + int i, n; + + if (pSid1->Revision != pSid2->Revision) + return (0); + + if (pSid1->SubAuthCount != pSid2->SubAuthCount) + return (0); + + if (bcmp(pSid1->Authority, pSid2->Authority, NT_SID_AUTH_MAX) != 0) + return (0); + + n = pSid1->SubAuthCount; + + n -= 1; /* don't compare last SubAuthority[] (aka RID) */ + + for (i = 0; i < n; i++) + if (pSid1->SubAuthority[i] != pSid2->SubAuthority[i]) + return (0); + + return (1); +} + +/* + * nt_sid_logf + * + * Format a sid and write it to the system log. See nt_sid_format + * for format information. + */ +void +nt_sid_logf(nt_sid_t *sid) +{ + char *s; + + if ((s = nt_sid_format(sid)) == 0) + return; + + MEM_FREE("libnt", s); +} + + +/* + * nt_sid_format + * + * Format a sid and return it as a string. The memory for the string is + * allocated using malloc so the caller should call free when it is no + * longer required. A pointer to the string is returned. + */ +char * +nt_sid_format(nt_sid_t *sid) +{ + int i; + char *fmtbuf; + char *p; + + if (sid == 0) + return (0); + + if ((fmtbuf = MEM_MALLOC("libnt", NT_SID_FMTBUF_SIZE)) == 0) + return (0); + + p = fmtbuf; + (void) sprintf(p, "S-%d-", sid->Revision); + while (*p) + ++p; + + for (i = 0; i < NT_SID_AUTH_MAX; ++i) { + if (sid->Authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) { + (void) sprintf(p, "%d", sid->Authority[i]); + while (*p) + ++p; + } + } + + for (i = 0; i < sid->SubAuthCount && i < NT_SID_SUBAUTH_MAX; ++i) { + (void) sprintf(p, "-%u", sid->SubAuthority[i]); + while (*p) + ++p; + } + + return (fmtbuf); +} + +/* + * nt_sid_format2 + * + * Format a sid and return it in the passed buffer. + */ +void +nt_sid_format2(nt_sid_t *sid, char *fmtbuf) +{ + int i; + char *p; + + if (sid == 0 || fmtbuf == 0) + return; + + p = fmtbuf; + (void) sprintf(p, "S-%d-", sid->Revision); + while (*p) + ++p; + + for (i = 0; i < NT_SID_AUTH_MAX; ++i) { + if (sid->Authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) { + (void) sprintf(p, "%d", sid->Authority[i]); + while (*p) + ++p; + } + } + + for (i = 0; i < sid->SubAuthCount && i < NT_SID_SUBAUTH_MAX; ++i) { + (void) sprintf(p, "-%u", sid->SubAuthority[i]); + while (*p) + ++p; + } +} + +/* + * nt_sid_strtosid + * + * Converts a SID in string form to a SID structure. There are lots of + * simplifying assumptions in here. The memory for the SID is allocated + * as if it was the largest possible SID; the caller is responsible for + * freeing the memory when it is no longer required. We assume that the + * string starts with "S-1-" and that the authority is held in the last + * byte, which should be okay for most situations. It also assumes the + * sub-authorities are in decimal format. + * + * On success, a pointer to a SID is returned. Otherwise a null pointer + * is returned. + * + * XXX this function may have endian issues + */ +nt_sid_t * +nt_sid_strtosid(char *sidstr) +{ + nt_sid_t *sid; + char *p; + int size; + BYTE i; +#ifdef _KERNEL + long sua; +#endif /* _KERNEL */ + + if (sidstr == 0) { + return (0); + } + + if (strncmp(sidstr, "S-1-", 4) != 0) { + return (0); + } + + size = sizeof (nt_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (DWORD)); + + if ((sid = MEM_MALLOC("libnt", size)) == 0) { + return (0); + } + + bzero(sid, size); + sid->Revision = NT_SID_REVISION; +#ifndef _KERNEL + sid->Authority[5] = atoi(&sidstr[4]); +#else /* _KERNEL */ + sua = 0; + /* XXX Why are we treating sua as a long/unsigned long? */ + (void) ddi_strtoul(&sidstr[4], 0, 10, (unsigned long *)&sua); + sid->Authority[5] = (BYTE)sua; +#endif /* _KERNEL */ + + for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { + while (*p && *p == '-') + ++p; + + if (*p < '0' || *p > '9') { + MEM_FREE("libnt", sid); + return (0); + } + +#ifndef _KERNEL + sid->SubAuthority[i] = strtoul(p, 0, 10); +#else /* _KERNEL */ + sua = 0; + (void) ddi_strtoul(p, 0, 10, (unsigned long *)&sua); + sid->SubAuthority[i] = (DWORD)sua; +#endif /* _KERNEL */ + + while (*p && *p != '-') + ++p; + } + + sid->SubAuthCount = i; + return (sid); +} + + +/* + * nt_sid_name_use + * + * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE + * provides the context for a SID, i.e. the type of resource to which + * it refers. + */ +char * +nt_sid_name_use(unsigned int snu_id) +{ + static char *snu_name[] = { + "SidTypeSidPrefix", + "SidTypeUser", + "SidTypeGroup", + "SidTypeDomain", + "SidTypeAlias", + "SidTypeWellKnownGroup", + "SidTypeDeletedAccount", + "SidTypeInvalid", + "SidTypeUnknown" + }; + + if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0])))) + return (snu_name[snu_id]); + else { + return (snu_name[SidTypeUnknown]); + } +} + + +/* + * nt_sid_copy + * + * Copy information of srcsid to dessid. The buffer should be allocated + * for dessid before passing to this function. The size of buffer for + * dessid should be specified in the buflen. + * + * Returns total bytes of information copied. If there is an error, 0 + * will be returned. + */ +int +nt_sid_copy(nt_sid_t *dessid, nt_sid_t *srcsid, unsigned buflen) +{ + unsigned n_bytes; + + if (!dessid || !srcsid) + return (0); + + n_bytes = nt_sid_length(srcsid); + if (n_bytes > buflen) + return (0); + + bcopy((char *)srcsid, (char *)dessid, n_bytes); + + return (n_bytes); +} diff --git a/usr/src/common/smbsrv/smb_status_xlat.c b/usr/src/common/smbsrv/smb_status_xlat.c new file mode 100644 index 000000000000..987a1544030f --- /dev/null +++ b/usr/src/common/smbsrv/smb_status_xlat.c @@ -0,0 +1,597 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file provides a text translation service for NT status codes. + */ + +#ifdef _KERNEL +#include +#include +#else +#include +#include +#endif /* _KERNEL */ +#include + +typedef struct xlate_table { + DWORD value; + char *name; +} xlate_table_t; + + +static xlate_table_t ntx_table[] = { + { NT_STATUS_SUCCESS, "SUCCESS" }, + { NT_STATUS_UNSUCCESSFUL, "UNSUCCESSFUL" }, + { NT_STATUS_NOT_IMPLEMENTED, "NOT_IMPLEMENTED" }, + { NT_STATUS_INVALID_INFO_CLASS, "INVALID_INFO_CLASS" }, + { NT_STATUS_INFO_LENGTH_MISMATCH, "INFO_LENGTH_MISMATCH" }, + { NT_STATUS_BUFFER_OVERFLOW, "BUFFER_OVERFLOW" }, + /* + * There seems to be some overloading of status codes. + * NT_STATUS_IN_PAGE_ERROR is NT_STATUS_NO_MORE_FILES + */ + { NT_STATUS_NO_MORE_FILES, "NO MORE FILES" }, + { NT_STATUS_PAGEFILE_QUOTA, "PAGEFILE_QUOTA" }, + { NT_STATUS_INVALID_HANDLE, "INVALID_HANDLE" }, + { NT_STATUS_BAD_INITIAL_STACK, "BAD_INITIAL_STACK" }, + { NT_STATUS_BAD_INITIAL_PC, "BAD_INITIAL_PC" }, + { NT_STATUS_INVALID_CID, "INVALID_CID" }, + { NT_STATUS_TIMER_NOT_CANCELED, "TIMER_NOT_CANCELED" }, + { NT_STATUS_INVALID_PARAMETER, "INVALID_PARAMETER" }, + { NT_STATUS_NO_SUCH_DEVICE, "NO_SUCH_DEVICE" }, + { NT_STATUS_NO_SUCH_FILE, "NO_SUCH_FILE" }, + { NT_STATUS_INVALID_DEVICE_REQUEST, "INVALID_DEVICE_REQUEST" }, + { NT_STATUS_END_OF_FILE, "END_OF_FILE" }, + { NT_STATUS_WRONG_VOLUME, "WRONG_VOLUME" }, + { NT_STATUS_NO_MEDIA_IN_DEVICE, "NO_MEDIA_IN_DEVICE" }, + { NT_STATUS_UNRECOGNIZED_MEDIA, "UNRECOGNIZED_MEDIA" }, + { NT_STATUS_NONEXISTENT_SECTOR, "NONEXISTENT_SECTOR" }, + { NT_STATUS_MORE_PROCESSING_REQUIRED, "MORE_PROCESSING_REQUIRED" }, + { NT_STATUS_NO_MEMORY, "NO_MEMORY" }, + { NT_STATUS_CONFLICTING_ADDRESSES, "CONFLICTING_ADDRESSES" }, + { NT_STATUS_NOT_MAPPED_VIEW, "NOT_MAPPED_VIEW" }, + + /* + * There seems to be some overloading of status codes. + * When we get NT_STATUS_UNABLE_TO_FREE_VM it really + * means NT_STATUS_NO_MORE_DATA. + */ + { NT_STATUS_UNABLE_TO_FREE_VM, "NO_MORE_DATA" }, + + { NT_STATUS_UNABLE_TO_DELETE_SECTION, "UNABLE_TO_DELETE_SECTION" }, + { NT_STATUS_INVALID_SYSTEM_SERVICE, "INVALID_SYSTEM_SERVICE" }, + { NT_STATUS_ILLEGAL_INSTRUCTION, "ILLEGAL_INSTRUCTION" }, + { NT_STATUS_INVALID_LOCK_SEQUENCE, "INVALID_LOCK_SEQUENCE" }, + { NT_STATUS_INVALID_VIEW_SIZE, "INVALID_VIEW_SIZE" }, + { NT_STATUS_INVALID_FILE_FOR_SECTION, "INVALID_FILE_FOR_SECTION" }, + { NT_STATUS_ALREADY_COMMITTED, "ALREADY_COMMITTED" }, + { NT_STATUS_ACCESS_DENIED, "ACCESS_DENIED" }, + { NT_STATUS_BUFFER_TOO_SMALL, "BUFFER_TOO_SMALL" }, + { NT_STATUS_OBJECT_TYPE_MISMATCH, "OBJECT_TYPE_MISMATCH" }, + { NT_STATUS_NONCONTINUABLE_EXCEPTION, "NONCONTINUABLE_EXCEPTION" }, + { NT_STATUS_INVALID_DISPOSITION, "INVALID_DISPOSITION" }, + { NT_STATUS_UNWIND, "UNWIND" }, + { NT_STATUS_BAD_STACK, "BAD_STACK" }, + { NT_STATUS_INVALID_UNWIND_TARGET, "INVALID_UNWIND_TARGET" }, + { NT_STATUS_NOT_LOCKED, "NOT_LOCKED" }, + { NT_STATUS_PARITY_ERROR, "PARITY_ERROR" }, + { NT_STATUS_UNABLE_TO_DECOMMIT_VM, "UNABLE_TO_DECOMMIT_VM" }, + { NT_STATUS_NOT_COMMITTED, "NOT_COMMITTED" }, + { NT_STATUS_INVALID_PORT_ATTRIBUTES, "INVALID_PORT_ATTRIBUTES" }, + { NT_STATUS_PORT_MESSAGE_TOO_LONG, "PORT_MESSAGE_TOO_LONG" }, + { NT_STATUS_INVALID_PARAMETER_MIX, "INVALID_PARAMETER_MIX" }, + { NT_STATUS_INVALID_QUOTA_LOWER, "INVALID_QUOTA_LOWER" }, + { NT_STATUS_DISK_CORRUPT_ERROR, "DISK_CORRUPT_ERROR" }, + { NT_STATUS_OBJECT_NAME_INVALID, "OBJECT_NAME_INVALID" }, + { NT_STATUS_OBJECT_NAME_NOT_FOUND, "OBJECT_NAME_NOT_FOUND" }, + { NT_STATUS_OBJECT_NAME_COLLISION, "OBJECT_NAME_COLLISION" }, + { NT_STATUS_HANDLE_NOT_WAITABLE, "HANDLE_NOT_WAITABLE" }, + { NT_STATUS_PORT_DISCONNECTED, "PORT_DISCONNECTED" }, + { NT_STATUS_DEVICE_ALREADY_ATTACHED, "DEVICE_ALREADY_ATTACHED" }, + { NT_STATUS_OBJECT_PATH_INVALID, "OBJECT_PATH_INVALID" }, + { NT_STATUS_OBJECT_PATH_NOT_FOUND, "OBJECT_PATH_NOT_FOUND" }, + { NT_STATUS_OBJECT_PATH_SYNTAX_BAD, "OBJECT_PATH_SYNTAX_BAD" }, + { NT_STATUS_DATA_OVERRUN, "DATA_OVERRUN" }, + { NT_STATUS_DATA_LATE_ERROR, "DATA_LATE_ERROR" }, + { NT_STATUS_DATA_ERROR, "DATA_ERROR" }, + { NT_STATUS_CRC_ERROR, "CRC_ERROR" }, + { NT_STATUS_SECTION_TOO_BIG, "SECTION_TOO_BIG" }, + { NT_STATUS_PORT_CONNECTION_REFUSED, "PORT_CONNECTION_REFUSED" }, + { NT_STATUS_INVALID_PORT_HANDLE, "INVALID_PORT_HANDLE" }, + { NT_STATUS_SHARING_VIOLATION, "SHARING_VIOLATION" }, + { NT_STATUS_QUOTA_EXCEEDED, "QUOTA_EXCEEDED" }, + { NT_STATUS_INVALID_PAGE_PROTECTION, "INVALID_PAGE_PROTECTION" }, + { NT_STATUS_MUTANT_NOT_OWNED, "MUTANT_NOT_OWNED" }, + { NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED, "SEMAPHORE_LIMIT_EXCEEDED" }, + { NT_STATUS_PORT_ALREADY_SET, "PORT_ALREADY_SET" }, + { NT_STATUS_SECTION_NOT_IMAGE, "SECTION_NOT_IMAGE" }, + { NT_STATUS_SUSPEND_COUNT_EXCEEDED, "SUSPEND_COUNT_EXCEEDED" }, + { NT_STATUS_THREAD_IS_TERMINATING, "THREAD_IS_TERMINATING" }, + { NT_STATUS_BAD_WORKING_SET_LIMIT, "BAD_WORKING_SET_LIMIT" }, + { NT_STATUS_INCOMPATIBLE_FILE_MAP, "INCOMPATIBLE_FILE_MAP" }, + { NT_STATUS_SECTION_PROTECTION, "SECTION_PROTECTION" }, + { NT_STATUS_EAS_NOT_SUPPORTED, "EAS_NOT_SUPPORTED" }, + { NT_STATUS_EA_TOO_LARGE, "EA_TOO_LARGE" }, + { NT_STATUS_NONEXISTENT_EA_ENTRY, "NONEXISTENT_EA_ENTRY" }, + { NT_STATUS_NO_EAS_ON_FILE, "NO_EAS_ON_FILE" }, + { NT_STATUS_EA_CORRUPT_ERROR, "EA_CORRUPT_ERROR" }, + { NT_STATUS_FILE_LOCK_CONFLICT, "FILE_LOCK_CONFLICT" }, + { NT_STATUS_LOCK_NOT_GRANTED, "LOCK_NOT_GRANTED" }, + { NT_STATUS_DELETE_PENDING, "DELETE_PENDING" }, + { NT_STATUS_CTL_FILE_NOT_SUPPORTED, "CTL_FILE_NOT_SUPPORTED" }, + { NT_STATUS_UNKNOWN_REVISION, "UNKNOWN_REVISION" }, + { NT_STATUS_REVISION_MISMATCH, "REVISION_MISMATCH" }, + { NT_STATUS_INVALID_OWNER, "INVALID_OWNER" }, + { NT_STATUS_INVALID_PRIMARY_GROUP, "INVALID_PRIMARY_GROUP" }, + { NT_STATUS_NO_IMPERSONATION_TOKEN, "NO_IMPERSONATION_TOKEN" }, + { NT_STATUS_CANT_DISABLE_MANDATORY, "CANT_DISABLE_MANDATORY" }, + { NT_STATUS_NO_LOGON_SERVERS, "NO_LOGON_SERVERS" }, + { NT_STATUS_NO_SUCH_LOGON_SESSION, "NO_SUCH_LOGON_SESSION" }, + { NT_STATUS_NO_SUCH_PRIVILEGE, "NO_SUCH_PRIVILEGE" }, + { NT_STATUS_PRIVILEGE_NOT_HELD, "PRIVILEGE_NOT_HELD" }, + { NT_STATUS_INVALID_ACCOUNT_NAME, "INVALID_ACCOUNT_NAME" }, + { NT_STATUS_USER_EXISTS, "USER_EXISTS" }, + { NT_STATUS_NO_SUCH_USER, "NO_SUCH_USER" }, + { NT_STATUS_GROUP_EXISTS, "GROUP_EXISTS" }, + { NT_STATUS_NO_SUCH_GROUP, "NO_SUCH_GROUP" }, + { NT_STATUS_MEMBER_IN_GROUP, "MEMBER_IN_GROUP" }, + { NT_STATUS_MEMBER_NOT_IN_GROUP, "MEMBER_NOT_IN_GROUP" }, + { NT_STATUS_LAST_ADMIN, "LAST_ADMIN" }, + { NT_STATUS_WRONG_PASSWORD, "WRONG_PASSWORD" }, + { NT_STATUS_ILL_FORMED_PASSWORD, "ILL_FORMED_PASSWORD" }, + { NT_STATUS_PASSWORD_RESTRICTION, "PASSWORD_RESTRICTION" }, + { NT_STATUS_LOGON_FAILURE, "LOGON_FAILURE" }, + { NT_STATUS_ACCOUNT_RESTRICTION, "ACCOUNT_RESTRICTION" }, + { NT_STATUS_INVALID_LOGON_HOURS, "INVALID_LOGON_HOURS" }, + { NT_STATUS_INVALID_WORKSTATION, "INVALID_WORKSTATION" }, + { NT_STATUS_PASSWORD_EXPIRED, "PASSWORD_EXPIRED" }, + { NT_STATUS_ACCOUNT_DISABLED, "ACCOUNT_DISABLED" }, + { NT_STATUS_NONE_MAPPED, "NONE_MAPPED" }, + { NT_STATUS_TOO_MANY_LUIDS_REQUESTED, "TOO_MANY_LUIDS_REQUESTED" }, + { NT_STATUS_LUIDS_EXHAUSTED, "LUIDS_EXHAUSTED" }, + { NT_STATUS_INVALID_SUB_AUTHORITY, "INVALID_SUB_AUTHORITY" }, + { NT_STATUS_INVALID_ACL, "INVALID_ACL" }, + { NT_STATUS_INVALID_SID, "INVALID_SID" }, + { NT_STATUS_INVALID_SECURITY_DESCR, "INVALID_SECURITY_DESCR" }, + { NT_STATUS_PROCEDURE_NOT_FOUND, "PROCEDURE_NOT_FOUND" }, + { NT_STATUS_INVALID_IMAGE_FORMAT, "INVALID_IMAGE_FORMAT" }, + { NT_STATUS_NO_TOKEN, "NO_TOKEN" }, + { NT_STATUS_BAD_INHERITANCE_ACL, "BAD_INHERITANCE_ACL" }, + { NT_STATUS_RANGE_NOT_LOCKED, "RANGE_NOT_LOCKED" }, + { NT_STATUS_DISK_FULL, "DISK_FULL" }, + { NT_STATUS_SERVER_DISABLED, "SERVER_DISABLED" }, + { NT_STATUS_SERVER_NOT_DISABLED, "SERVER_NOT_DISABLED" }, + { NT_STATUS_TOO_MANY_GUIDS_REQUESTED, "TOO_MANY_GUIDS_REQUESTED" }, + { NT_STATUS_GUIDS_EXHAUSTED, "GUIDS_EXHAUSTED" }, + { NT_STATUS_INVALID_ID_AUTHORITY, "INVALID_ID_AUTHORITY" }, + { NT_STATUS_AGENTS_EXHAUSTED, "AGENTS_EXHAUSTED" }, + { NT_STATUS_INVALID_VOLUME_LABEL, "INVALID_VOLUME_LABEL" }, + { NT_STATUS_SECTION_NOT_EXTENDED, "SECTION_NOT_EXTENDED" }, + { NT_STATUS_NOT_MAPPED_DATA, "NOT_MAPPED_DATA" }, + { NT_STATUS_RESOURCE_DATA_NOT_FOUND, "RESOURCE_DATA_NOT_FOUND" }, + { NT_STATUS_RESOURCE_TYPE_NOT_FOUND, "RESOURCE_TYPE_NOT_FOUND" }, + { NT_STATUS_RESOURCE_NAME_NOT_FOUND, "RESOURCE_NAME_NOT_FOUND" }, + { NT_STATUS_ARRAY_BOUNDS_EXCEEDED, "ARRAY_BOUNDS_EXCEEDED" }, + { NT_STATUS_FLOAT_DENORMAL_OPERAND, "FLOAT_DENORMAL_OPERAND" }, + { NT_STATUS_FLOAT_DIVIDE_BY_ZERO, "FLOAT_DIVIDE_BY_ZERO" }, + { NT_STATUS_FLOAT_INEXACT_RESULT, "FLOAT_INEXACT_RESULT" }, + { NT_STATUS_FLOAT_INVALID_OPERATION, "FLOAT_INVALID_OPERATION" }, + { NT_STATUS_FLOAT_OVERFLOW, "FLOAT_OVERFLOW" }, + { NT_STATUS_FLOAT_STACK_CHECK, "FLOAT_STACK_CHECK" }, + { NT_STATUS_FLOAT_UNDERFLOW, "FLOAT_UNDERFLOW" }, + { NT_STATUS_INTEGER_DIVIDE_BY_ZERO, "INTEGER_DIVIDE_BY_ZERO" }, + { NT_STATUS_INTEGER_OVERFLOW, "INTEGER_OVERFLOW" }, + { NT_STATUS_PRIVILEGED_INSTRUCTION, "PRIVILEGED_INSTRUCTION" }, + { NT_STATUS_TOO_MANY_PAGING_FILES, "TOO_MANY_PAGING_FILES" }, + { NT_STATUS_FILE_INVALID, "FILE_INVALID" }, + { NT_STATUS_ALLOTTED_SPACE_EXCEEDED, "ALLOTTED_SPACE_EXCEEDED" }, + { NT_STATUS_INSUFFICIENT_RESOURCES, "INSUFFICIENT_RESOURCES" }, + { NT_STATUS_DFS_EXIT_PATH_FOUND, "DFS_EXIT_PATH_FOUND" }, + { NT_STATUS_DEVICE_DATA_ERROR, "DEVICE_DATA_ERROR" }, + { NT_STATUS_DEVICE_NOT_CONNECTED, "DEVICE_NOT_CONNECTED" }, + { NT_STATUS_DEVICE_POWER_FAILURE, "DEVICE_POWER_FAILURE" }, + { NT_STATUS_FREE_VM_NOT_AT_BASE, "FREE_VM_NOT_AT_BASE" }, + { NT_STATUS_MEMORY_NOT_ALLOCATED, "MEMORY_NOT_ALLOCATED" }, + { NT_STATUS_WORKING_SET_QUOTA, "WORKING_SET_QUOTA" }, + { NT_STATUS_MEDIA_WRITE_PROTECTED, "MEDIA_WRITE_PROTECTED" }, + { NT_STATUS_DEVICE_NOT_READY, "DEVICE_NOT_READY" }, + { NT_STATUS_INVALID_GROUP_ATTRIBUTES, "INVALID_GROUP_ATTRIBUTES" }, + { NT_STATUS_BAD_IMPERSONATION_LEVEL, "BAD_IMPERSONATION_LEVEL" }, + { NT_STATUS_CANT_OPEN_ANONYMOUS, "CANT_OPEN_ANONYMOUS" }, + { NT_STATUS_BAD_VALIDATION_CLASS, "BAD_VALIDATION_CLASS" }, + { NT_STATUS_BAD_TOKEN_TYPE, "BAD_TOKEN_TYPE" }, + { NT_STATUS_BAD_MASTER_BOOT_RECORD, "BAD_MASTER_BOOT_RECORD" }, + { NT_STATUS_INSTRUCTION_MISALIGNMENT, "INSTRUCTION_MISALIGNMENT" }, + { NT_STATUS_INSTANCE_NOT_AVAILABLE, "INSTANCE_NOT_AVAILABLE" }, + { NT_STATUS_PIPE_NOT_AVAILABLE, "PIPE_NOT_AVAILABLE" }, + { NT_STATUS_INVALID_PIPE_STATE, "INVALID_PIPE_STATE" }, + { NT_STATUS_PIPE_BUSY, "PIPE_BUSY" }, + { NT_STATUS_ILLEGAL_FUNCTION, "ILLEGAL_FUNCTION" }, + { NT_STATUS_PIPE_DISCONNECTED, "PIPE_DISCONNECTED" }, + { NT_STATUS_PIPE_CLOSING, "PIPE_CLOSING" }, + { NT_STATUS_PIPE_CONNECTED, "PIPE_CONNECTED" }, + { NT_STATUS_PIPE_LISTENING, "PIPE_LISTENING" }, + { NT_STATUS_INVALID_READ_MODE, "INVALID_READ_MODE" }, + { NT_STATUS_IO_TIMEOUT, "IO_TIMEOUT" }, + { NT_STATUS_FILE_FORCED_CLOSED, "FILE_FORCED_CLOSED" }, + { NT_STATUS_PROFILING_NOT_STARTED, "PROFILING_NOT_STARTED" }, + { NT_STATUS_PROFILING_NOT_STOPPED, "PROFILING_NOT_STOPPED" }, + { NT_STATUS_COULD_NOT_INTERPRET, "COULD_NOT_INTERPRET" }, + { NT_STATUS_FILE_IS_A_DIRECTORY, "FILE_IS_A_DIRECTORY" }, + { NT_STATUS_NOT_SUPPORTED, "NOT_SUPPORTED" }, + { NT_STATUS_REMOTE_NOT_LISTENING, "REMOTE_NOT_LISTENING" }, + { NT_STATUS_DUPLICATE_NAME, "DUPLICATE_NAME" }, + { NT_STATUS_BAD_NETWORK_PATH, "BAD_NETWORK_PATH" }, + { NT_STATUS_NETWORK_BUSY, "NETWORK_BUSY" }, + { NT_STATUS_DEVICE_DOES_NOT_EXIST, "DEVICE_DOES_NOT_EXIST" }, + { NT_STATUS_TOO_MANY_COMMANDS, "TOO_MANY_COMMANDS" }, + { NT_STATUS_ADAPTER_HARDWARE_ERROR, "ADAPTER_HARDWARE_ERROR" }, + { NT_STATUS_INVALID_NETWORK_RESPONSE, "INVALID_NETWORK_RESPONSE" }, + { NT_STATUS_UNEXPECTED_NETWORK_ERROR, "UNEXPECTED_NETWORK_ERROR" }, + { NT_STATUS_BAD_REMOTE_ADAPTER, "BAD_REMOTE_ADAPTER" }, + { NT_STATUS_PRINT_QUEUE_FULL, "PRINT_QUEUE_FULL" }, + { NT_STATUS_NO_SPOOL_SPACE, "NO_SPOOL_SPACE" }, + { NT_STATUS_PRINT_CANCELLED, "PRINT_CANCELLED" }, + { NT_STATUS_NETWORK_NAME_DELETED, "NETWORK_NAME_DELETED" }, + { NT_STATUS_NETWORK_ACCESS_DENIED, "NETWORK_ACCESS_DENIED" }, + { NT_STATUS_BAD_DEVICE_TYPE, "BAD_DEVICE_TYPE" }, + { NT_STATUS_BAD_NETWORK_NAME, "BAD_NETWORK_NAME" }, + { NT_STATUS_TOO_MANY_NAMES, "TOO_MANY_NAMES" }, + { NT_STATUS_TOO_MANY_SESSIONS, "TOO_MANY_SESSIONS" }, + { NT_STATUS_SHARING_PAUSED, "SHARING_PAUSED" }, + { NT_STATUS_REQUEST_NOT_ACCEPTED, "REQUEST_NOT_ACCEPTED" }, + { NT_STATUS_REDIRECTOR_PAUSED, "REDIRECTOR_PAUSED" }, + { NT_STATUS_NET_WRITE_FAULT, "NET_WRITE_FAULT" }, + { NT_STATUS_PROFILING_AT_LIMIT, "PROFILING_AT_LIMIT" }, + { NT_STATUS_NOT_SAME_DEVICE, "NOT_SAME_DEVICE" }, + { NT_STATUS_FILE_RENAMED, "FILE_RENAMED" }, + { NT_STATUS_VIRTUAL_CIRCUIT_CLOSED, "VIRTUAL_CIRCUIT_CLOSED" }, + { NT_STATUS_NO_SECURITY_ON_OBJECT, "NO_SECURITY_ON_OBJECT" }, + { NT_STATUS_CANT_WAIT, "CANT_WAIT" }, + { NT_STATUS_PIPE_EMPTY, "PIPE_EMPTY" }, + { NT_STATUS_CANT_ACCESS_DOMAIN_INFO, "CANT_ACCESS_DOMAIN_INFO" }, + { NT_STATUS_CANT_TERMINATE_SELF, "CANT_TERMINATE_SELF" }, + { NT_STATUS_INVALID_SERVER_STATE, "INVALID_SERVER_STATE" }, + { NT_STATUS_INVALID_DOMAIN_STATE, "INVALID_DOMAIN_STATE" }, + { NT_STATUS_INVALID_DOMAIN_ROLE, "INVALID_DOMAIN_ROLE" }, + { NT_STATUS_NO_SUCH_DOMAIN, "NO_SUCH_DOMAIN" }, + { NT_STATUS_DOMAIN_EXISTS, "DOMAIN_EXISTS" }, + { NT_STATUS_DOMAIN_LIMIT_EXCEEDED, "DOMAIN_LIMIT_EXCEEDED" }, + { NT_STATUS_OPLOCK_NOT_GRANTED, "OPLOCK_NOT_GRANTED" }, + { NT_STATUS_INVALID_OPLOCK_PROTOCOL, "INVALID_OPLOCK_PROTOCOL" }, + { NT_STATUS_INTERNAL_DB_CORRUPTION, "INTERNAL_DB_CORRUPTION" }, + { NT_STATUS_INTERNAL_ERROR, "INTERNAL_ERROR" }, + { NT_STATUS_GENERIC_NOT_MAPPED, "GENERIC_NOT_MAPPED" }, + { NT_STATUS_BAD_DESCRIPTOR_FORMAT, "BAD_DESCRIPTOR_FORMAT" }, + { NT_STATUS_INVALID_USER_BUFFER, "INVALID_USER_BUFFER" }, + { NT_STATUS_UNEXPECTED_IO_ERROR, "UNEXPECTED_IO_ERROR" }, + { NT_STATUS_UNEXPECTED_MM_CREATE_ERR, "UNEXPECTED_MM_CREATE_ERR" }, + { NT_STATUS_UNEXPECTED_MM_MAP_ERROR, "UNEXPECTED_MM_MAP_ERROR" }, + { NT_STATUS_UNEXPECTED_MM_EXTEND_ERR, "UNEXPECTED_MM_EXTEND_ERR" }, + { NT_STATUS_NOT_LOGON_PROCESS, "NOT_LOGON_PROCESS" }, + { NT_STATUS_LOGON_SESSION_EXISTS, "LOGON_SESSION_EXISTS" }, + { NT_STATUS_INVALID_PARAMETER_1, "INVALID_PARAMETER_1" }, + { NT_STATUS_INVALID_PARAMETER_2, "INVALID_PARAMETER_2" }, + { NT_STATUS_INVALID_PARAMETER_3, "INVALID_PARAMETER_3" }, + { NT_STATUS_INVALID_PARAMETER_4, "INVALID_PARAMETER_4" }, + { NT_STATUS_INVALID_PARAMETER_5, "INVALID_PARAMETER_5" }, + { NT_STATUS_INVALID_PARAMETER_6, "INVALID_PARAMETER_6" }, + { NT_STATUS_INVALID_PARAMETER_7, "INVALID_PARAMETER_7" }, + { NT_STATUS_INVALID_PARAMETER_8, "INVALID_PARAMETER_8" }, + { NT_STATUS_INVALID_PARAMETER_9, "INVALID_PARAMETER_9" }, + { NT_STATUS_INVALID_PARAMETER_10, "INVALID_PARAMETER_10" }, + { NT_STATUS_INVALID_PARAMETER_11, "INVALID_PARAMETER_11" }, + { NT_STATUS_INVALID_PARAMETER_12, "INVALID_PARAMETER_12" }, + { NT_STATUS_REDIRECTOR_NOT_STARTED, "REDIRECTOR_NOT_STARTED" }, + { NT_STATUS_REDIRECTOR_STARTED, "REDIRECTOR_STARTED" }, + { NT_STATUS_STACK_OVERFLOW, "STACK_OVERFLOW" }, + { NT_STATUS_NO_SUCH_PACKAGE, "NO_SUCH_PACKAGE" }, + { NT_STATUS_BAD_FUNCTION_TABLE, "BAD_FUNCTION_TABLE" }, + { NT_STATUS_DIRECTORY_NOT_EMPTY, "DIRECTORY_NOT_EMPTY" }, + { NT_STATUS_FILE_CORRUPT_ERROR, "FILE_CORRUPT_ERROR" }, + { NT_STATUS_NOT_A_DIRECTORY, "NOT_A_DIRECTORY" }, + { NT_STATUS_BAD_LOGON_SESSION_STATE, "BAD_LOGON_SESSION_STATE" }, + { NT_STATUS_LOGON_SESSION_COLLISION, "LOGON_SESSION_COLLISION" }, + { NT_STATUS_NAME_TOO_LONG, "NAME_TOO_LONG" }, + { NT_STATUS_FILES_OPEN, "FILES_OPEN" }, + { NT_STATUS_CONNECTION_IN_USE, "CONNECTION_IN_USE" }, + { NT_STATUS_MESSAGE_NOT_FOUND, "MESSAGE_NOT_FOUND" }, + { NT_STATUS_PROCESS_IS_TERMINATING, "PROCESS_IS_TERMINATING" }, + { NT_STATUS_INVALID_LOGON_TYPE, "INVALID_LOGON_TYPE" }, + { NT_STATUS_NO_GUID_TRANSLATION, "NO_GUID_TRANSLATION" }, + { NT_STATUS_CANNOT_IMPERSONATE, "CANNOT_IMPERSONATE" }, + { NT_STATUS_IMAGE_ALREADY_LOADED, "IMAGE_ALREADY_LOADED" }, + { NT_STATUS_ABIOS_NOT_PRESENT, "ABIOS_NOT_PRESENT" }, + { NT_STATUS_ABIOS_LID_NOT_EXIST, "ABIOS_LID_NOT_EXIST" }, + { NT_STATUS_ABIOS_LID_ALREADY_OWNED, "ABIOS_LID_ALREADY_OWNED" }, + { NT_STATUS_ABIOS_NOT_LID_OWNER, "ABIOS_NOT_LID_OWNER" }, + { NT_STATUS_ABIOS_INVALID_COMMAND, "ABIOS_INVALID_COMMAND" }, + { NT_STATUS_ABIOS_INVALID_LID, "ABIOS_INVALID_LID" }, + { NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE, + "ABIOS_SELECTOR_NOT_AVAILABLE" }, + { NT_STATUS_ABIOS_INVALID_SELECTOR, "ABIOS_INVALID_SELECTOR" }, + { NT_STATUS_NO_LDT, "NO_LDT" }, + { NT_STATUS_INVALID_LDT_SIZE, "INVALID_LDT_SIZE" }, + { NT_STATUS_INVALID_LDT_OFFSET, "INVALID_LDT_OFFSET" }, + { NT_STATUS_INVALID_LDT_DESCRIPTOR, "INVALID_LDT_DESCRIPTOR" }, + { NT_STATUS_INVALID_IMAGE_NE_FORMAT, "INVALID_IMAGE_NE_FORMAT" }, + { NT_STATUS_RXACT_INVALID_STATE, "RXACT_INVALID_STATE" }, + { NT_STATUS_RXACT_COMMIT_FAILURE, "RXACT_COMMIT_FAILURE" }, + { NT_STATUS_MAPPED_FILE_SIZE_ZERO, "MAPPED_FILE_SIZE_ZERO" }, + { NT_STATUS_TOO_MANY_OPENED_FILES, "TOO_MANY_OPENED_FILES" }, + { NT_STATUS_CANCELLED, "CANCELLED" }, + { NT_STATUS_CANNOT_DELETE, "CANNOT_DELETE" }, + { NT_STATUS_INVALID_COMPUTER_NAME, "INVALID_COMPUTER_NAME" }, + { NT_STATUS_FILE_DELETED, "FILE_DELETED" }, + { NT_STATUS_SPECIAL_ACCOUNT, "SPECIAL_ACCOUNT" }, + { NT_STATUS_SPECIAL_GROUP, "SPECIAL_GROUP" }, + { NT_STATUS_SPECIAL_USER, "SPECIAL_USER" }, + { NT_STATUS_MEMBERS_PRIMARY_GROUP, "MEMBERS_PRIMARY_GROUP" }, + { NT_STATUS_FILE_CLOSED, "FILE_CLOSED" }, + { NT_STATUS_TOO_MANY_THREADS, "TOO_MANY_THREADS" }, + { NT_STATUS_THREAD_NOT_IN_PROCESS, "THREAD_NOT_IN_PROCESS" }, + { NT_STATUS_TOKEN_ALREADY_IN_USE, "TOKEN_ALREADY_IN_USE" }, + { NT_STATUS_PAGEFILE_QUOTA_EXCEEDED, "PAGEFILE_QUOTA_EXCEEDED" }, + { NT_STATUS_COMMITMENT_LIMIT, "COMMITMENT_LIMIT" }, + { NT_STATUS_INVALID_IMAGE_LE_FORMAT, "INVALID_IMAGE_LE_FORMAT" }, + { NT_STATUS_INVALID_IMAGE_NOT_MZ, "INVALID_IMAGE_NOT_MZ" }, + { NT_STATUS_INVALID_IMAGE_PROTECT, "INVALID_IMAGE_PROTECT" }, + { NT_STATUS_INVALID_IMAGE_WIN_16, "INVALID_IMAGE_WIN_16" }, + { NT_STATUS_LOGON_SERVER_CONFLICT, "LOGON_SERVER_CONFLICT" }, + { NT_STATUS_TIME_DIFFERENCE_AT_DC, "TIME_DIFFERENCE_AT_DC" }, + { NT_STATUS_SYNCHRONIZATION_REQUIRED, "SYNCHRONIZATION_REQUIRED" }, + { NT_STATUS_DLL_NOT_FOUND, "DLL_NOT_FOUND" }, + { NT_STATUS_OPEN_FAILED, "OPEN_FAILED" }, + { NT_STATUS_IO_PRIVILEGE_FAILED, "IO_PRIVILEGE_FAILED" }, + { NT_STATUS_ORDINAL_NOT_FOUND, "ORDINAL_NOT_FOUND" }, + { NT_STATUS_ENTRYPOINT_NOT_FOUND, "ENTRYPOINT_NOT_FOUND" }, + { NT_STATUS_CONTROL_C_EXIT, "CONTROL_C_EXIT" }, + { NT_STATUS_LOCAL_DISCONNECT, "LOCAL_DISCONNECT" }, + { NT_STATUS_REMOTE_DISCONNECT, "REMOTE_DISCONNECT" }, + { NT_STATUS_REMOTE_RESOURCES, "REMOTE_RESOURCES" }, + { NT_STATUS_LINK_FAILED, "LINK_FAILED" }, + { NT_STATUS_LINK_TIMEOUT, "LINK_TIMEOUT" }, + { NT_STATUS_INVALID_CONNECTION, "INVALID_CONNECTION" }, + { NT_STATUS_INVALID_ADDRESS, "INVALID_ADDRESS" }, + { NT_STATUS_DLL_INIT_FAILED, "DLL_INIT_FAILED" }, + { NT_STATUS_MISSING_SYSTEMFILE, "MISSING_SYSTEMFILE" }, + { NT_STATUS_UNHANDLED_EXCEPTION, "UNHANDLED_EXCEPTION" }, + { NT_STATUS_APP_INIT_FAILURE, "APP_INIT_FAILURE" }, + { NT_STATUS_PAGEFILE_CREATE_FAILED, "PAGEFILE_CREATE_FAILED" }, + { NT_STATUS_NO_PAGEFILE, "NO_PAGEFILE" }, + { NT_STATUS_INVALID_LEVEL, "INVALID_LEVEL" }, + { NT_STATUS_WRONG_PASSWORD_CORE, "WRONG_PASSWORD_CORE" }, + { NT_STATUS_ILLEGAL_FLOAT_CONTEXT, "ILLEGAL_FLOAT_CONTEXT" }, + { NT_STATUS_PIPE_BROKEN, "PIPE_BROKEN" }, + { NT_STATUS_REGISTRY_CORRUPT, "REGISTRY_CORRUPT" }, + { NT_STATUS_REGISTRY_IO_FAILED, "REGISTRY_IO_FAILED" }, + { NT_STATUS_NO_EVENT_PAIR, "NO_EVENT_PAIR" }, + { NT_STATUS_UNRECOGNIZED_VOLUME, "UNRECOGNIZED_VOLUME" }, + { NT_STATUS_SERIAL_NO_DEVICE_INITED, "SERIAL_NO_DEVICE_INITED" }, + { NT_STATUS_NO_SUCH_ALIAS, "NO SUCH ALIAS" }, + { NT_STATUS_MEMBER_NOT_IN_ALIAS, "MEMBER NOT IN ALIAS" }, + { NT_STATUS_MEMBER_IN_ALIAS, "MEMBER IN ALIAS" }, + { NT_STATUS_ALIAS_EXISTS, "ALIAS EXISTS" }, + { NT_STATUS_LOGON_NOT_GRANTED, "LOGON_NOT_GRANTED" }, + { NT_STATUS_TOO_MANY_SECRETS, "TOO_MANY_SECRETS" }, + { NT_STATUS_SECRET_TOO_LONG, "SECRET_TOO_LONG" }, + { NT_STATUS_INTERNAL_DB_ERROR, "INTERNAL_DB_ERROR" }, + { NT_STATUS_FULLSCREEN_MODE, "FULLSCREEN_MODE" }, + { NT_STATUS_TOO_MANY_CONTEXT_IDS, "TOO_MANY_CONTEXT_IDS" }, + { NT_STATUS_LOGON_TYPE_NOT_GRANTED, "LOGON_TYPE_NOT_GRANTED" }, + { NT_STATUS_NOT_REGISTRY_FILE, "NOT_REGISTRY_FILE" }, + { NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED, + "NT_CROSS_ENCRYPTION_REQUIRED" }, + { NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR, "DOMAIN_CTRLR_CONFIG_ERROR" }, + { NT_STATUS_FT_MISSING_MEMBER, "FT_MISSING_MEMBER" }, + { NT_STATUS_ILL_FORMED_SERVICE_ENTRY, "ILL_FORMED_SERVICE_ENTRY" }, + { NT_STATUS_ILLEGAL_CHARACTER, "ILLEGAL_CHARACTER" }, + { NT_STATUS_UNMAPPABLE_CHARACTER, "UNMAPPABLE_CHARACTER" }, + { NT_STATUS_UNDEFINED_CHARACTER, "UNDEFINED_CHARACTER" }, + { NT_STATUS_FLOPPY_VOLUME, "FLOPPY_VOLUME" }, + { NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND, "FLOPPY_ID_MARK_NOT_FOUND" }, + { NT_STATUS_FLOPPY_WRONG_CYLINDER, "FLOPPY_WRONG_CYLINDER" }, + { NT_STATUS_FLOPPY_UNKNOWN_ERROR, "FLOPPY_UNKNOWN_ERROR" }, + { NT_STATUS_FLOPPY_BAD_REGISTERS, "FLOPPY_BAD_REGISTERS" }, + { NT_STATUS_DISK_RECALIBRATE_FAILED, "DISK_RECALIBRATE_FAILED" }, + { NT_STATUS_DISK_OPERATION_FAILED, "DISK_OPERATION_FAILED" }, + { NT_STATUS_DISK_RESET_FAILED, "DISK_RESET_FAILED" }, + { NT_STATUS_SHARED_IRQ_BUSY, "SHARED_IRQ_BUSY" }, + { NT_STATUS_FT_ORPHANING, "FT_ORPHANING" }, + { NT_STATUS_PARTITION_FAILURE, "PARTITION_FAILURE" }, + { NT_STATUS_INVALID_BLOCK_LENGTH, "INVALID_BLOCK_LENGTH" }, + { NT_STATUS_DEVICE_NOT_PARTITIONED, "DEVICE_NOT_PARTITIONED" }, + { NT_STATUS_UNABLE_TO_LOCK_MEDIA, "UNABLE_TO_LOCK_MEDIA" }, + { NT_STATUS_UNABLE_TO_UNLOAD_MEDIA, "UNABLE_TO_UNLOAD_MEDIA" }, + { NT_STATUS_EOM_OVERFLOW, "EOM_OVERFLOW" }, + { NT_STATUS_NO_MEDIA, "NO_MEDIA" }, + { NT_STATUS_NO_SUCH_MEMBER, "NO SUCH MEMBER" }, + { NT_STATUS_INVALID_MEMBER, "INVALID MEMBER" }, + { NT_STATUS_KEY_DELETED, "KEY_DELETED" }, + { NT_STATUS_NO_LOG_SPACE, "NO_LOG_SPACE" }, + { NT_STATUS_TOO_MANY_SIDS, "TOO MANY SIDS" }, + { NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED, + "LM_CROSS_ENCRYPTION_REQUIRED" }, + { NT_STATUS_KEY_HAS_CHILDREN, "KEY_HAS_CHILDREN" }, + { NT_STATUS_CHILD_MUST_BE_VOLATILE, "CHILD_MUST_BE_VOLATILE" }, + { NT_STATUS_DEVICE_CONFIGURATION_ERROR, "DEVICE_CONFIGURATION_ERROR" }, + { NT_STATUS_DRIVER_INTERNAL_ERROR, "DRIVER_INTERNAL_ERROR" }, + { NT_STATUS_INVALID_DEVICE_STATE, "INVALID_DEVICE_STATE" }, + { NT_STATUS_IO_DEVICE_ERROR, "IO_DEVICE_ERROR" }, + { NT_STATUS_DEVICE_PROTOCOL_ERROR, "DEVICE_PROTOCOL_ERROR" }, + { NT_STATUS_BACKUP_CONTROLLER, "BACKUP_CONTROLLER" }, + { NT_STATUS_LOG_FILE_FULL, "LOG_FILE_FULL" }, + { NT_STATUS_TOO_LATE, "TOO_LATE" }, + { NT_STATUS_NO_TRUST_LSA_SECRET, "NO_TRUST_LSA_SECRET" }, + { NT_STATUS_NO_TRUST_SAM_ACCOUNT, "NO_TRUST_SAM_ACCOUNT" }, + { NT_STATUS_TRUSTED_DOMAIN_FAILURE, "TRUSTED_DOMAIN_FAILURE" }, + { NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE, + "TRUSTED_RELATIONSHIP_FAILURE" }, + { NT_STATUS_EVENTLOG_FILE_CORRUPT, "EVENTLOG_FILE_CORRUPT" }, + { NT_STATUS_EVENTLOG_CANT_START, "EVENTLOG_CANT_START" }, + { NT_STATUS_TRUST_FAILURE, "TRUST_FAILURE" }, + { NT_STATUS_MUTANT_LIMIT_EXCEEDED, "MUTANT_LIMIT_EXCEEDED" }, + { NT_STATUS_NETLOGON_NOT_STARTED, "NETLOGON_NOT_STARTED" }, + { NT_STATUS_ACCOUNT_EXPIRED, "ACCOUNT_EXPIRED" }, + { NT_STATUS_POSSIBLE_DEADLOCK, "POSSIBLE_DEADLOCK" }, + { NT_STATUS_NETWORK_CREDENTIAL_CONFLICT, + "NETWORK_CREDENTIAL_CONFLICT" }, + { NT_STATUS_REMOTE_SESSION_LIMIT, "REMOTE_SESSION_LIMIT" }, + { NT_STATUS_EVENTLOG_FILE_CHANGED, "EVENTLOG_FILE_CHANGED" }, + { NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT, + "NOLOGON_INTERDOMAIN_TRUST_ACCOUNT" }, + { NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, + "NOLOGON_WORKSTATION_TRUST_ACCOUNT" }, + { NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT, + "NOLOGON_SERVER_TRUST_ACCOUNT" }, + { NT_STATUS_DOMAIN_TRUST_INCONSISTENT, "DOMAIN_TRUST_INCONSISTENT" }, + { NT_STATUS_FS_DRIVER_REQUIRED, "FS_DRIVER_REQUIRED" }, + { NT_STATUS_NO_USER_SESSION_KEY, "NO_USER_SESSION_KEY" }, + { NT_STATUS_USER_SESSION_DELETED, "USER_SESSION_DELETED" }, + { NT_STATUS_RESOURCE_LANG_NOT_FOUND, "RESOURCE_LANG_NOT_FOUND" }, + { NT_STATUS_INSUFF_SERVER_RESOURCES, "INSUFF_SERVER_RESOURCES" }, + { NT_STATUS_INVALID_BUFFER_SIZE, "INVALID_BUFFER_SIZE" }, + { NT_STATUS_INVALID_ADDRESS_COMPONENT, "INVALID_ADDRESS_COMPONENT" }, + { NT_STATUS_INVALID_ADDRESS_WILDCARD, "INVALID_ADDRESS_WILDCARD" }, + { NT_STATUS_TOO_MANY_ADDRESSES, "TOO_MANY_ADDRESSES" }, + { NT_STATUS_ADDRESS_ALREADY_EXISTS, "ADDRESS_ALREADY_EXISTS" }, + { NT_STATUS_ADDRESS_CLOSED, "ADDRESS_CLOSED" }, + { NT_STATUS_CONNECTION_DISCONNECTED, "CONNECTION_DISCONNECTED" }, + { NT_STATUS_CONNECTION_RESET, "CONNECTION_RESET" }, + { NT_STATUS_TOO_MANY_NODES, "TOO_MANY_NODES" }, + { NT_STATUS_TRANSACTION_ABORTED, "TRANSACTION_ABORTED" }, + { NT_STATUS_TRANSACTION_TIMED_OUT, "TRANSACTION_TIMED_OUT" }, + { NT_STATUS_TRANSACTION_NO_RELEASE, "TRANSACTION_NO_RELEASE" }, + { NT_STATUS_TRANSACTION_NO_MATCH, "TRANSACTION_NO_MATCH" }, + { NT_STATUS_TRANSACTION_RESPONDED, "TRANSACTION_RESPONDED" }, + { NT_STATUS_TRANSACTION_INVALID_ID, "TRANSACTION_INVALID_ID" }, + { NT_STATUS_TRANSACTION_INVALID_TYPE, "TRANSACTION_INVALID_TYPE" }, + { NT_STATUS_NOT_SERVER_SESSION, "NOT_SERVER_SESSION" }, + { NT_STATUS_NOT_CLIENT_SESSION, "NOT_CLIENT_SESSION" }, + { NT_STATUS_CANNOT_LOAD_REGISTRY_FILE, "CANNOT_LOAD_REGISTRY_FILE" }, + { NT_STATUS_DEBUG_ATTACH_FAILED, "DEBUG_ATTACH_FAILED" }, + { NT_STATUS_SYSTEM_PROCESS_TERMINATED, "SYSTEM_PROCESS_TERMINATED" }, + { NT_STATUS_DATA_NOT_ACCEPTED, "DATA_NOT_ACCEPTED" }, + { NT_STATUS_NO_BROWSER_SERVERS_FOUND, "NO_BROWSER_SERVERS_FOUND" }, + { NT_STATUS_VDM_HARD_ERROR, "VDM_HARD_ERROR" }, + { NT_STATUS_DRIVER_CANCEL_TIMEOUT, "DRIVER_CANCEL_TIMEOUT" }, + { NT_STATUS_REPLY_MESSAGE_MISMATCH, "REPLY_MESSAGE_MISMATCH" }, + { NT_STATUS_MAPPED_ALIGNMENT, "MAPPED_ALIGNMENT" }, + { NT_STATUS_IMAGE_CHECKSUM_MISMATCH, "IMAGE_CHECKSUM_MISMATCH" }, + { NT_STATUS_LOST_WRITEBEHIND_DATA, "LOST_WRITEBEHIND_DATA" }, + { NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID, + "CLIENT_SERVER_PARAMETERS_INVALID" }, + { NT_STATUS_PASSWORD_MUST_CHANGE, "PASSWORD_MUST_CHANGE" }, + { NT_STATUS_NOT_FOUND, "NOT_FOUND" }, + { NT_STATUS_NOT_TINY_STREAM, "NOT_TINY_STREAM" }, + { NT_STATUS_RECOVERY_FAILURE, "RECOVERY_FAILURE" }, + { NT_STATUS_STACK_OVERFLOW_READ, "STACK_OVERFLOW_READ" }, + { NT_STATUS_FAIL_CHECK, "FAIL_CHECK" }, + { NT_STATUS_DUPLICATE_OBJECTID, "DUPLICATE_OBJECTID" }, + { NT_STATUS_OBJECTID_EXISTS, "OBJECTID_EXISTS" }, + { NT_STATUS_CONVERT_TO_LARGE, "CONVERT_TO_LARGE" }, + { NT_STATUS_RETRY, "RETRY" }, + { NT_STATUS_FOUND_OUT_OF_SCOPE, "FOUND_OUT_OF_SCOPE" }, + { NT_STATUS_ALLOCATE_BUCKET, "ALLOCATE_BUCKET" }, + { NT_STATUS_PROPSET_NOT_FOUND, "PROPSET_NOT_FOUND" }, + { NT_STATUS_MARSHALL_OVERFLOW, "MARSHALL_OVERFLOW" }, + { NT_STATUS_INVALID_VARIANT, "INVALID_VARIANT" }, + { NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND, + "DOMAIN_CONTROLLER_NOT_FOUND" }, + { NT_STATUS_ACCOUNT_LOCKED_OUT, "ACCOUNT_LOCKED_OUT" }, + { NT_STATUS_HANDLE_NOT_CLOSABLE, "HANDLE_NOT_CLOSABLE" }, + { NT_STATUS_CONNECTION_REFUSED, "CONNECTION_REFUSED" }, + { NT_STATUS_GRACEFUL_DISCONNECT, "GRACEFUL_DISCONNECT" }, + { NT_STATUS_ADDRESS_ALREADY_ASSOCIATED, "ADDRESS_ALREADY_ASSOCIATED" }, + { NT_STATUS_ADDRESS_NOT_ASSOCIATED, "ADDRESS_NOT_ASSOCIATED" }, + { NT_STATUS_CONNECTION_INVALID, "CONNECTION_INVALID" }, + { NT_STATUS_CONNECTION_ACTIVE, "CONNECTION_ACTIVE" }, + { NT_STATUS_NETWORK_UNREACHABLE, "NETWORK_UNREACHABLE" }, + { NT_STATUS_HOST_UNREACHABLE, "HOST/PARTNER UNREACHABLE" }, + { NT_STATUS_PROTOCOL_UNREACHABLE, "PROTOCOL_UNREACHABLE" }, + { NT_STATUS_PORT_UNREACHABLE, "PORT_UNREACHABLE" }, + { NT_STATUS_REQUEST_ABORTED, "REQUEST_ABORTED" }, + { NT_STATUS_CONNECTION_ABORTED, "CONNECTION_ABORTED" }, + { NT_STATUS_BAD_COMPRESSION_BUFFER, "BAD_COMPRESSION_BUFFER" }, + { NT_STATUS_USER_MAPPED_FILE, "USER_MAPPED_FILE" }, + { NT_STATUS_AUDIT_FAILED, "AUDIT_FAILED" }, + { NT_STATUS_TIMER_RESOLUTION_NOT_SET, "TIMER_RESOLUTION_NOT_SET" }, + { NT_STATUS_CONNECTION_COUNT_LIMIT, "CONNECTION_COUNT_LIMIT" }, + { NT_STATUS_LOGIN_TIME_RESTRICTION, "LOGIN_TIME_RESTRICTION" }, + { NT_STATUS_LOGIN_WKSTA_RESTRICTION, "LOGIN_WKSTA_RESTRICTION" }, + { NT_STATUS_IMAGE_MP_UP_MISMATCH, "IMAGE_MP_UP_MISMATCH" }, + { NT_STATUS_INSUFFICIENT_LOGON_INFO, "INSUFFICIENT_LOGON_INFO" }, + { NT_STATUS_BAD_DLL_ENTRYPOINT, "BAD_DLL_ENTRYPOINT" }, + { NT_STATUS_BAD_SERVICE_ENTRYPOINT, "BAD_SERVICE_ENTRYPOINT" }, + { NT_STATUS_LPC_REPLY_LOST, "LPC_REPLY_LOST" }, + { NT_STATUS_IP_ADDRESS_CONFLICT1, "IP_ADDRESS_CONFLICT1" }, + { NT_STATUS_IP_ADDRESS_CONFLICT2, "IP_ADDRESS_CONFLICT2" }, + { NT_STATUS_REGISTRY_QUOTA_LIMIT, "REGISTRY_QUOTA_LIMIT" }, + { NT_STATUS_PATH_NOT_COVERED, "PATH_NOT_COVERED" }, + { NT_STATUS_NO_CALLBACK_ACTIVE, "NO_CALLBACK_ACTIVE" }, + { NT_STATUS_LICENSE_QUOTA_EXCEEDED, "LICENSE_QUOTA_EXCEEDED" }, + { NT_STATUS_PWD_TOO_SHORT, "PWD_TOO_SHORT" }, + { NT_STATUS_PWD_TOO_RECENT, "PWD_TOO_RECENT" }, + { NT_STATUS_PWD_HISTORY_CONFLICT, "PWD_HISTORY_CONFLICT" }, + { NT_STATUS_PLUGPLAY_NO_DEVICE, "PLUGPLAY_NO_DEVICE" }, + { NT_STATUS_UNSUPPORTED_COMPRESSION, "UNSUPPORTED_COMPRESSION" }, + { NT_STATUS_INVALID_HW_PROFILE, "INVALID_HW_PROFILE" }, + { NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH, + "INVALID_PLUGPLAY_DEVICE_PATH" }, + { NT_STATUS_DRIVER_ORDINAL_NOT_FOUND, "DRIVER_ORDINAL_NOT_FOUND" }, + { NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND, + "DRIVER_ENTRYPOINT_NOT_FOUND" }, + { NT_STATUS_RESOURCE_NOT_OWNED, "RESOURCE_NOT_OWNED" }, + { NT_STATUS_TOO_MANY_LINKS, "TOO_MANY_LINKS" }, + { NT_STATUS_QUOTA_LIST_INCONSISTENT, "QUOTA_LIST_INCONSISTENT" }, + { NT_STATUS_FILE_IS_OFFLINE, "FILE_IS_OFFLINE" }, +}; + + +/* + * Translate an ntstatus value to a meaningful text string. If there isn't + * a corresponding text string in the table, the text representation of the + * status value is returned. This uses a static buffer so there is a + * possible concurrency issue if the caller hangs on to this pointer for a + * while but it should be harmless and really remote since the value will + * almost always be found in the table. + */ +char * +xlate_nt_status(DWORD ntstatus) +{ + static char unknown[16]; + int i; + + for (i = 0; i < sizeof (ntx_table)/sizeof (ntx_table[0]); ++i) { + if (ntx_table[i].value == NT_SC_VALUE(ntstatus)) + return (ntx_table[i].name); + } + + (void) sprintf(unknown, "0x%08x", ntstatus); + return (unknown); +} diff --git a/usr/src/common/smbsrv/smb_strcase.c b/usr/src/common/smbsrv/smb_strcase.c new file mode 100644 index 000000000000..e1de2cdd32cd --- /dev/null +++ b/usr/src/common/smbsrv/smb_strcase.c @@ -0,0 +1,388 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Case conversion functions for strings. Originally this module only + * dealt with ASCII strings. It has been updated to support European + * character set characters. The current implementation is based on + * code page table lookup rather than simple character range checks. + */ + +#ifdef _KERNEL +#include +#include +#else +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Global pointer to the current code page. This is + * defaulted to a standard ASCII table. + */ +static codepage_t *current_codepage = usascii_codepage; + +/* + * A flag indicating whether the codepage being used is ASCII + * When this flag is set, string opeartions can go faster. + */ +static int is_unicode = 0; + +/* + * codepage_isupper + * + * Determine whether or not a character is an uppercase character. + * This function operates on the current codepage table. Returns + * non-zero if the character is uppercase. Otherwise returns zero. + */ +int +codepage_isupper(int c) +{ + unsigned short mask = is_unicode ? 0xffff : 0xff; + + return (current_codepage[c & mask].ctype & CODEPAGE_ISUPPER); +} + + +/* + * codepage_islower + * + * Determine whether or not a character is an lowercase character. + * This function operates on the current codepage table. Returns + * non-zero if the character is lowercase. Otherwise returns zero. + */ +int +codepage_islower(int c) +{ + unsigned short mask = is_unicode ? 0xffff : 0xff; + + return (current_codepage[c & mask].ctype & CODEPAGE_ISLOWER); +} + + +/* + * codepage_toupper + * + * Convert individual characters to their uppercase equivalent value. + * If the specified character is lowercase, the uppercase value will + * be returned. Otherwise the original value will be returned. + */ +int +codepage_toupper(int c) +{ + unsigned short mask = is_unicode ? 0xffff : 0xff; + + return (current_codepage[c & mask].upper); +} + + +/* + * codepage_tolower + * + * Convert individual characters to their lowercase equivalent value. + * If the specified character is uppercase, the lowercase value will + * be returned. Otherwise the original value will be returned. + */ +int +codepage_tolower(int c) +{ + unsigned short mask = is_unicode ? 0xffff : 0xff; + + return (current_codepage[c & mask].lower); +} + + +/* + * strupr + * + * Convert a string to uppercase using the appropriate codepage. The + * string is converted in place. A pointer to the string is returned. + * There is an assumption here that uppercase and lowercase values + * always result encode to the same length. + */ +char * +utf8_strupr(char *s) +{ + mts_wchar_t c; + char *p = s; + + while (*p) { + if (mts_isascii(*p)) { + *p = codepage_toupper(*p); + p++; + } else { + if (mts_mbtowc(&c, p, MTS_MB_CHAR_MAX) < 0) + return (0); + + if (c == 0) + break; + + c = codepage_toupper(c); + p += mts_wctomb(p, c); + } + } + + return (s); +} + + +/* + * strlwr + * + * Convert a string to lowercase using the appropriate codepage. The + * string is converted in place. A pointer to the string is returned. + * There is an assumption here that uppercase and lowercase values + * always result encode to the same length. + */ +char * +utf8_strlwr(char *s) +{ + mts_wchar_t c; + char *p = s; + + while (*p) { + if (mts_isascii(*p)) { + *p = codepage_tolower(*p); + p++; + } else { + if (mts_mbtowc(&c, p, MTS_MB_CHAR_MAX) < 0) + return (0); + + if (c == 0) + break; + + c = codepage_tolower(c); + p += mts_wctomb(p, c); + } + } + + return (s); +} + + +/* + * isstrlwr + * + * Returns 1 if string contains NO uppercase chars 0 otherwise. However, + * -1 is returned if "s" is not a valid multi-byte string. + */ +int +utf8_isstrlwr(const char *s) +{ + mts_wchar_t c; + int n; + const char *p = s; + + while (*p) { + if (mts_isascii(*p) && codepage_isupper(*p)) + return (0); + else { + if ((n = mts_mbtowc(&c, p, MTS_MB_CHAR_MAX)) < 0) + return (-1); + + if (c == 0) + break; + + if (codepage_isupper(c)) + return (0); + + p += n; + } + } + + return (1); +} + + +/* + * isstrupr + * + * Returns 1 if string contains NO lowercase chars 0 otherwise. However, + * -1 is returned if "s" is not a valid multi-byte string. + */ +int +utf8_isstrupr(const char *s) +{ + mts_wchar_t c; + int n; + const char *p = s; + + while (*p) { + if (mts_isascii(*p) && codepage_islower(*p)) + return (0); + else { + if ((n = mts_mbtowc(&c, p, MTS_MB_CHAR_MAX)) < 0) + return (-1); + + if (c == 0) + break; + + if (codepage_islower(c)) + return (0); + + p += n; + } + } + + return (1); +} + + +/* + * strcasecmp + * + * Compare the null-terminated strings s1 and s2 and return an integer + * greater than, equal to, or less than 0, according as s1 is lexico + * graphically greater than, equal to, or less than s2 after translation + * of each corresponding character to lowercase. The strings themselves + * are not modified. + * + * Out: 0 if strings are equal + * < 0 if first string < second string + * > 0 if first string > second string + */ +int +utf8_strcasecmp(const char *s1, const char *s2) +{ + mts_wchar_t c1, c2; + int n1, n2; + const char *p1 = s1; + const char *p2 = s2; + + for (;;) { + if (mts_isascii(*p1)) + c1 = *p1++; + else { + if ((n1 = mts_mbtowc(&c1, p1, MTS_MB_CHAR_MAX)) < 0) + return (-1); + p1 += n1; + } + + if (mts_isascii(*p2)) + c2 = *p2++; + else { + if ((n2 = mts_mbtowc(&c2, p2, MTS_MB_CHAR_MAX)) < 0) + return (1); + p2 += n2; + } + + if (c1 == 0 || c2 == 0) + break; + + if (c1 == c2) + continue; + + c1 = codepage_tolower(c1); + c2 = codepage_tolower(c2); + + if (c1 != c2) + break; + } + + return ((int)c1 - (int)c2); +} + + +/* + * strncasecmp + * + * Compare two null-terminated strings, s1 and s2, of at most len + * characters and return an int greater than, equal to, or less than 0, + * dependent on whether s1 is lexicographically greater than, equal to, + * or less than s2 after translation of each corresponding character to + * lowercase. The original strings are not modified. + * + * Out: 0 if strings are equal + * < 0 if first string < second string + * > 0 if first string > second string + */ +int +utf8_strncasecmp(const char *s1, const char *s2, int len) +{ + mts_wchar_t c1, c2; + int n1, n2; + const char *p1 = s1; + const char *p2 = s2; + + if (len <= 0) + return (0); + + while (len--) { + if (mts_isascii(*p1)) + c1 = *p1++; + else { + if ((n1 = mts_mbtowc(&c1, p1, MTS_MB_CHAR_MAX)) < 0) + return (-1); + p1 += n1; + } + + if (mts_isascii(*p2)) + c2 = *p2++; + else { + if ((n2 = mts_mbtowc(&c2, p2, MTS_MB_CHAR_MAX)) < 0) + return (1); + p2 += n2; + } + + if (c1 == 0 || c2 == 0) + break; + + if (c1 == c2) + continue; + + c1 = codepage_tolower(c1); + c2 = codepage_tolower(c2); + + if (c1 != c2) + break; + } + + return ((int)c1 - (int)c2); +} + + + +int +utf8_isstrascii(const char *s) +{ + while (*s) { + if (mts_isascii(*s) == 0) + return (0); + s++; + } + return (1); +} diff --git a/usr/src/common/smbsrv/smb_string.c b/usr/src/common/smbsrv/smb_string.c new file mode 100644 index 000000000000..0ff4f1e8a642 --- /dev/null +++ b/usr/src/common/smbsrv/smb_string.c @@ -0,0 +1,103 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Implementation of some of the string functions. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef _KERNEL +#include +#include +#else +#include +#include +#include +#endif +#include +#include +#include + + +/* + * strsubst + * + * Scan a string replacing all occurrences of orgchar with newchar. + * Returns a pointer to s, or null of s is null. + */ +char * +strsubst(char *s, char orgchar, char newchar) +{ + char *p = s; + + if (p == 0) + return (0); + + while (*p) { + if (*p == orgchar) + *p = newchar; + ++p; + } + + return (s); +} + +/* + * strcanon + * + * Normalize a string by reducing all the repeated characters in + * buf as defined by class. For example; + * + * char *buf = strdup("/d1//d2//d3\\\\d4\\\\f1.txt"); + * strcanon(buf, "/\\"); + * + * Would result in buf containing the following string: + * + * /d1/d2/d3\d4\f1.txt + * + * This function modifies the contents of buf in place and returns + * a pointer to buf. + */ +char * +strcanon(char *buf, const char *class) +{ + char *p = buf; + char *q = buf; + char *r; + + while (*p) { + *q++ = *p; + + if ((r = strchr(class, *p)) != 0) { + while (*p == *r) + ++p; + } else + ++p; + } + + *q = '\0'; + return (buf); +} diff --git a/usr/src/common/smbsrv/smb_token.c b/usr/src/common/smbsrv/smb_token.c new file mode 100644 index 000000000000..1e71bf673fdf --- /dev/null +++ b/usr/src/common/smbsrv/smb_token.c @@ -0,0 +1,383 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * NT Token library (kernel/user) + */ + +#ifdef _KERNEL +#include +#include +#include +#include +#include +#include +#include +#else /* _KERNEL */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* _KERNEL */ + +#include +#include + +#include +#include +#include +#include +#include + +#ifndef _KERNEL +#include +#define ASSERT assert +void (*smb_token_logfunc)(int, const char *, ...) = syslog; +int smb_token_errlog = LOG_ERR; +int smb_token_infolog = LOG_INFO; +#else /* _KERNEL */ +void (*smb_token_logfunc)(int, const char *, ...) = cmn_err; +int smb_token_errlog = CE_WARN; +int smb_token_infolog = CE_NOTE; +#endif /* _KERNEL */ + +int smb_token_debug = 0; + +#ifdef _KERNEL +extern char *inet_ntop(int, const void *, char *, int); +#endif /* _KERNEL */ + +/* + * Returns -1 on error. Otherwise, returns 0. + */ +int +smb_token_tobuf(smb_dr_user_ctx_t *usr, char *buf, int len) +{ + char ipaddr_buf[INET_ADDRSTRLEN]; + + if (!usr) { + (void) strcpy(buf, "N/A"); + return (-1); + } + + (void) inet_ntop(AF_INET, (char *)&usr->du_ipaddr, ipaddr_buf, + sizeof (ipaddr_buf)); + (void) snprintf(buf, len, "%s\\%s %s (%s)", + usr->du_domain ? usr->du_domain : "", + usr->du_account ? usr->du_account : "", + usr->du_workstation ? usr->du_workstation : "", + ipaddr_buf); + + return (0); +} + +/*PRINTFLIKE3*/ +void +smb_token_log(int level, smb_dr_user_ctx_t *usr, char *fmt, ...) +{ + va_list ap; + char *msg; + int len; + char tokenbuf[NTTOKEN_BASIC_INFO_MAXLEN]; + + msg = MEM_MALLOC("nttoken", 1024); + if (!msg) { + smb_token_logfunc(smb_token_errlog, "smb_token_log: " + "resource shortage"); + return; + } + + if (usr) + (void) smb_token_tobuf(usr, tokenbuf, sizeof (tokenbuf)); + else + (void) strcpy(tokenbuf, "UNKNOWN"); + + va_start(ap, fmt); + (void) snprintf(msg, 1024, "Token[%s]: ", tokenbuf); + len = strlen(msg); + (void) vsnprintf(msg + len, 1024 - len, fmt, ap); + va_end(ap); +#ifdef _KERNEL + cmn_err(level, "%s", msg); +#else + syslog(level, "%s", msg); +#endif /* _KERNEL */ + + MEM_FREE("nttoken", msg); +} + +#ifndef _KERNEL +/* + * smb_token_print + * + * Diagnostic routine to write the contents of a token to the log. + */ +void +smb_token_print(smb_token_t *token) +{ + smb_win_grps_t *w_grps; + smb_posix_grps_t *x_grps; + smb_sid_attrs_t *grp; + char sidstr[128]; + int i; + + if (token == NULL) + return; + + smb_token_logfunc(smb_token_infolog, "Token for %s\\%s", + (token->tkn_domain_name) ? token->tkn_domain_name : "-NULL-", + (token->tkn_account_name) ? token->tkn_account_name : "-NULL-"); + + smb_token_logfunc(smb_token_infolog, " User->Attr: %d", + token->tkn_user->i_sidattr.attrs); + nt_sid_format2((nt_sid_t *)token->tkn_user->i_sidattr.sid, sidstr); + smb_token_logfunc(smb_token_infolog, " User->Sid: %s (id=%u)", + sidstr, token->tkn_user->i_id); + + nt_sid_format2((nt_sid_t *)token->tkn_owner->i_sidattr.sid, sidstr); + smb_token_logfunc(smb_token_infolog, " Ownr->Sid: %s (id=%u)", + sidstr, token->tkn_owner->i_id); + + nt_sid_format2((nt_sid_t *)token->tkn_primary_grp->i_sidattr.sid, + sidstr); + smb_token_logfunc(smb_token_infolog, " PGrp->Sid: %s (id=%u)", + sidstr, token->tkn_primary_grp->i_id); + + w_grps = token->tkn_win_grps; + if (w_grps) { + smb_token_logfunc(smb_token_infolog, " Windows groups: %d", + w_grps->wg_count); + + for (i = 0; i < w_grps->wg_count; ++i) { + grp = &w_grps->wg_groups[i].i_sidattr; + smb_token_logfunc(smb_token_infolog, + " Grp[%d].Attr:%d", i, grp->attrs); + if (w_grps->wg_groups[i].i_sidattr.sid) { + nt_sid_format2((nt_sid_t *)grp->sid, sidstr); + smb_token_logfunc(smb_token_infolog, + " Grp[%d].Sid: %s (id=%u)", i, sidstr, + w_grps->wg_groups[i].i_id); + } + } + } + else + smb_token_logfunc(smb_token_infolog, " No Windows groups"); + + x_grps = token->tkn_posix_grps; + if (x_grps) { + smb_token_logfunc(smb_token_infolog, " Solaris groups: %d", + x_grps->pg_ngrps); + for (i = 0; i < x_grps->pg_ngrps; i++) + smb_token_logfunc(smb_token_infolog, " %u", + x_grps->pg_grps[i]); + } + else + smb_token_logfunc(smb_token_infolog, " No Solaris groups"); + + if (token->tkn_privileges) + smb_privset_log(token->tkn_privileges); + else + smb_token_logfunc(smb_token_infolog, " No privileges"); +} +#endif /* _KERNEL */ + +/* + * smb_token_query_privilege + * + * Find out if the specified privilege is enable in the given + * access token. + */ +int +smb_token_query_privilege(smb_token_t *token, int priv_id) +{ + smb_privset_t *privset; + int i; + + if ((token == NULL) || (token->tkn_privileges == NULL)) + return (0); + + privset = token->tkn_privileges; + for (i = 0; privset->priv_cnt; i++) { + if (privset->priv[i].luid.lo_part == priv_id) { + if (privset->priv[i].attrs == SE_PRIVILEGE_ENABLED) + return (1); + else + return (0); + } + } + + return (0); +} + +#ifndef _KERNEL +/* + * smb_token_mkselfrel + * + * encode: structure -> flat buffer (buffer size) + * Pre-condition: obj is non-null. + */ +uint8_t * +smb_token_mkselfrel(smb_token_t *obj, uint32_t *len) +{ + uint8_t *buf; + XDR xdrs; + + if (!obj) { + smb_token_logfunc(smb_token_errlog, + "smb_token_mkselfrel: invalid parameter"); + return (NULL); + } + + *len = xdr_sizeof(xdr_smb_token_t, obj); + buf = (uint8_t *)malloc(*len); + if (!buf) { + smb_token_logfunc(smb_token_errlog, + "smb_token_mkselfrel: resource shortage"); + return (NULL); + } + + xdrmem_create(&xdrs, (const caddr_t)buf, *len, XDR_ENCODE); + + if (!xdr_smb_token_t(&xdrs, obj)) { + smb_token_logfunc(smb_token_errlog, + "smb_token_mkselfrel: XDR encode error"); + *len = 0; + free(buf); + buf = NULL; + } + + xdr_destroy(&xdrs); + return (buf); +} + +/* + * netr_client_mkabsolute + * + * decode: flat buffer -> structure + */ +netr_client_t * +netr_client_mkabsolute(uint8_t *buf, uint32_t len) +{ + netr_client_t *obj; + XDR xdrs; + + xdrmem_create(&xdrs, (const caddr_t)buf, len, XDR_DECODE); + obj = (netr_client_t *)malloc(sizeof (netr_client_t)); + if (!obj) { + smb_token_logfunc(smb_token_errlog, "netr_client_mkabsolute: " + "resource shortage"); + xdr_destroy(&xdrs); + return (NULL); + } + + bzero(obj, sizeof (netr_client_t)); + if (!xdr_netr_client_t(&xdrs, obj)) { + smb_token_logfunc(smb_token_errlog, "netr_client_mkabsolute: " + "XDR decode error"); + free(obj); + obj = NULL; + } + + xdr_destroy(&xdrs); + return (obj); +} +#else /* _KERNEL */ +/* + * smb_token_mkabsolute + * + * decode: flat buffer -> structure + */ +smb_token_t * +smb_token_mkabsolute(uint8_t *buf, uint32_t len) +{ + smb_token_t *obj; + XDR xdrs; + + xdrmem_create(&xdrs, (const caddr_t)buf, len, XDR_DECODE); + obj = kmem_zalloc(sizeof (smb_token_t), KM_SLEEP); + + if (!xdr_smb_token_t(&xdrs, obj)) { + smb_token_logfunc(smb_token_errlog, "smb_token_mkabsolute: XDR " + "decode error"); + kmem_free(obj, sizeof (smb_token_t)); + obj = NULL; + } + + xdr_destroy(&xdrs); + return (obj); +} + +/* + * netr_client_mkselfrel + * + * encode: structure -> flat buffer (buffer size) + * Pre-condition: obj is non-null. + */ +uint8_t * +netr_client_mkselfrel(netr_client_t *obj, uint32_t *len) +{ + uint8_t *buf; + XDR xdrs; + + *len = xdr_sizeof(xdr_netr_client_t, obj); + buf = kmem_alloc(*len, KM_SLEEP); + + xdrmem_create(&xdrs, (const caddr_t)buf, *len, XDR_ENCODE); + + if (!xdr_netr_client_t(&xdrs, obj)) { + smb_token_logfunc(smb_token_errlog, "netr_client_mkselfrel: " + "XDR encode error"); + kmem_free(buf, *len); + *len = 0; + buf = NULL; + } + + xdr_destroy(&xdrs); + return (buf); +} + +void +smb_token_free(smb_token_t *token) +{ + if (!token) + return; + + /* + * deallocate any pointer field of an access token object + * using xdr_free since they are created by the XDR decode + * operation. + */ + xdr_free(xdr_smb_token_t, (char *)token); + kmem_free(token, sizeof (smb_token_t)); +} +#endif /* _KERNEL */ diff --git a/usr/src/common/smbsrv/smb_token_xdr.c b/usr/src/common/smbsrv/smb_token_xdr.c new file mode 100644 index 000000000000..14870b29c6de --- /dev/null +++ b/usr/src/common/smbsrv/smb_token_xdr.c @@ -0,0 +1,395 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file was originally generated using rpcgen. + */ + +#ifndef _KERNEL +#include +#endif /* !_KERNEL */ +#include +#include +#include +#include +#include + +bool_t +xdr_ntsid_helper(xdrs, sid) + XDR *xdrs; + char **sid; +{ + uint32_t pos, len; + uint8_t dummy, cnt; + bool_t rc; + + switch (xdrs->x_op) { + case XDR_DECODE: + /* + * chicken-and-egg: Can't use nt_sid_length() since it takes + * SID as its parameter while sid is yet to be decoded. + */ + pos = xdr_getpos(xdrs); + + if (!xdr_bool(xdrs, &rc)) + return (FALSE); + + if (!xdr_uint8_t(xdrs, &dummy)) + return (FALSE); + + if (!xdr_uint8_t(xdrs, &cnt)) + return (FALSE); + + rc = xdr_setpos(xdrs, pos); + + if (rc == FALSE) + return (FALSE); + + len = sizeof (nt_sid_t) - sizeof (uint32_t) + + (cnt * sizeof (uint32_t)); + + if (!xdr_pointer(xdrs, sid, len, (xdrproc_t)xdr_nt_sid_t)) + return (FALSE); + break; + + case XDR_ENCODE: + case XDR_FREE: + if (*sid == NULL) + return (FALSE); + + len = nt_sid_length((nt_sid_t *)(uintptr_t)*sid); + if (!xdr_pointer(xdrs, sid, len, (xdrproc_t)xdr_nt_sid_t)) + return (FALSE); + break; + } + + return (TRUE); +} + +bool_t +xdr_smb_privset_helper(xdrs, privs) + XDR *xdrs; + char **privs; +{ + uint32_t pos, len; + uint32_t cnt; + bool_t rc; + smb_privset_t *p; + + if (xdrs->x_op == XDR_DECODE) { + pos = xdr_getpos(xdrs); + + if (!xdr_bool(xdrs, &rc)) + return (FALSE); + + if (!xdr_uint32_t(xdrs, &cnt)) + return (FALSE); + + rc = xdr_setpos(xdrs, pos); + + if (rc == FALSE) + return (FALSE); + } else { + if (*privs == NULL) + return (FALSE); + + p = (smb_privset_t *)(uintptr_t)*privs; + cnt = p->priv_cnt; + } + + len = sizeof (smb_privset_t) + - sizeof (smb_luid_attrs_t) + + (cnt * sizeof (smb_luid_attrs_t)); + + if (!xdr_pointer(xdrs, privs, len, (xdrproc_t)xdr_smb_privset_t)) + return (FALSE); + + return (TRUE); +} + +bool_t +xdr_smb_win_grps_helper(xdrs, grps) + XDR *xdrs; + char **grps; +{ + uint32_t pos, len; + uint16_t cnt; + bool_t rc; + + if (xdrs->x_op == XDR_DECODE) { + pos = xdr_getpos(xdrs); + + if (!xdr_bool(xdrs, &rc)) + return (FALSE); + + if (!xdr_uint16_t(xdrs, &cnt)) + return (FALSE); + + rc = xdr_setpos(xdrs, pos); + if (rc == FALSE) + return (FALSE); + } else { + if (*grps == NULL) + return (FALSE); + + cnt = ((smb_win_grps_t *)(uintptr_t)*grps)->wg_count; + } + + len = cnt * sizeof (smb_id_t) + sizeof (smb_win_grps_t); + + if (!xdr_pointer(xdrs, grps, len, (xdrproc_t)xdr_smb_win_grps_t)) + return (FALSE); + + return (TRUE); +} + +bool_t +xdr_smb_id_t(xdrs, objp) + XDR *xdrs; + smb_id_t *objp; +{ + if (!xdr_smb_sid_attrs_t(xdrs, &objp->i_sidattr)) + return (FALSE); + if (!xdr_uint32_t(xdrs, (uint32_t *)&objp->i_id)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_smb_win_grps_t(xdrs, objp) + XDR *xdrs; + smb_win_grps_t *objp; +{ + if (!xdr_uint16_t(xdrs, &objp->wg_count)) + return (FALSE); + if (!xdr_vector(xdrs, (char *)objp->wg_groups, objp->wg_count, + sizeof (smb_id_t), (xdrproc_t)xdr_smb_id_t)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_smb_posix_grps_t(xdrs, objp) + XDR *xdrs; + smb_posix_grps_t *objp; +{ + if (!xdr_uint32_t(xdrs, &objp->pg_ngrps)) + return (FALSE); + if (!xdr_vector(xdrs, (char *)objp->pg_grps, objp->pg_ngrps, + sizeof (uint32_t), (xdrproc_t)xdr_uint32_t)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_smb_posix_grps_helper(xdrs, identity) + XDR *xdrs; + char **identity; +{ + uint32_t pos, len; + uint32_t cnt; + bool_t rc; + + if (xdrs->x_op == XDR_DECODE) { + pos = xdr_getpos(xdrs); + + if (!xdr_bool(xdrs, &rc)) + return (FALSE); + + if (!xdr_uint32_t(xdrs, &cnt)) + return (FALSE); + + rc = xdr_setpos(xdrs, pos); + if (rc == FALSE) + return (FALSE); + } else { + if (*identity == NULL) + return (FALSE); + cnt = ((smb_posix_grps_t *)(uintptr_t)*identity)->pg_ngrps; + } + + len = SMB_POSIX_GRPS_SIZE(cnt); + + if (!xdr_pointer(xdrs, identity, len, (xdrproc_t)xdr_smb_posix_grps_t)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_smb_session_key_t(xdrs, objp) + XDR *xdrs; + smb_session_key_t *objp; +{ + if (!xdr_vector(xdrs, (char *)objp->data, 16, + sizeof (uint8_t), (xdrproc_t)xdr_uint8_t)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_netr_client_t(xdrs, objp) + XDR *xdrs; + netr_client_t *objp; +{ + if (!xdr_uint16_t(xdrs, &objp->logon_level)) + return (FALSE); + if (!xdr_string(xdrs, &objp->username, ~0)) + return (FALSE); + if (!xdr_string(xdrs, &objp->domain, ~0)) + return (FALSE); + if (!xdr_string(xdrs, &objp->workstation, ~0)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->ipaddr)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->challenge_key.challenge_key_val, + (uint32_t *)&objp->challenge_key.challenge_key_len, ~0, + sizeof (uint8_t), (xdrproc_t)xdr_uint8_t)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->nt_password.nt_password_val, + (uint32_t *)&objp->nt_password.nt_password_len, ~0, + sizeof (uint8_t), (xdrproc_t)xdr_uint8_t)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->lm_password.lm_password_val, + (uint32_t *)&objp->lm_password.lm_password_len, ~0, + sizeof (uint8_t), (xdrproc_t)xdr_uint8_t)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->logon_id)) + return (FALSE); + if (!xdr_int(xdrs, &objp->native_os)) + return (FALSE); + if (!xdr_int(xdrs, &objp->native_lm)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->local_ipaddr)) + return (FALSE); + if (!xdr_uint16_t(xdrs, &objp->local_port)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->flags)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nt_sid_t(xdrs, objp) + XDR *xdrs; + nt_sid_t *objp; +{ + if (!xdr_uint8_t(xdrs, &objp->Revision)) + return (FALSE); + if (!xdr_uint8_t(xdrs, &objp->SubAuthCount)) + return (FALSE); + if (!xdr_vector(xdrs, (char *)objp->Authority, NT_SID_AUTH_MAX, + sizeof (uint8_t), (xdrproc_t)xdr_uint8_t)) + return (FALSE); + if (!xdr_vector(xdrs, (char *)objp->SubAuthority, objp->SubAuthCount, + sizeof (uint32_t), (xdrproc_t)xdr_uint32_t)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_smb_luid_t(xdrs, objp) + XDR *xdrs; + smb_luid_t *objp; +{ + if (!xdr_uint32_t(xdrs, &objp->lo_part)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->hi_part)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_smb_luid_attrs_t(xdrs, objp) + XDR *xdrs; + smb_luid_attrs_t *objp; +{ + if (!xdr_smb_luid_t(xdrs, &objp->luid)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->attrs)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_smb_privset_t(xdrs, objp) + XDR *xdrs; + smb_privset_t *objp; +{ + if (!xdr_uint32_t(xdrs, &objp->priv_cnt)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->control)) + return (FALSE); + if (!xdr_vector(xdrs, (char *)objp->priv, objp->priv_cnt, + sizeof (smb_luid_attrs_t), + (xdrproc_t)xdr_smb_luid_attrs_t)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_smb_sid_attrs_t(xdrs, objp) + XDR *xdrs; + smb_sid_attrs_t *objp; +{ + if (!xdr_uint32_t(xdrs, &objp->attrs)) + return (FALSE); + return (xdr_ntsid_helper(xdrs, (char **)&objp->sid)); +} + +bool_t +xdr_smb_token_t(xdrs, objp) + XDR *xdrs; + smb_token_t *objp; +{ + if (!xdr_pointer(xdrs, (char **)&objp->tkn_user, + sizeof (smb_id_t), (xdrproc_t)xdr_smb_id_t)) + return (FALSE); + if (!xdr_pointer(xdrs, (char **)&objp->tkn_owner, + sizeof (smb_id_t), (xdrproc_t)xdr_smb_id_t)) + return (FALSE); + if (!xdr_pointer(xdrs, (char **)&objp->tkn_primary_grp, + sizeof (smb_id_t), (xdrproc_t)xdr_smb_id_t)) + return (FALSE); + if (!xdr_smb_win_grps_helper(xdrs, (char **)&objp->tkn_win_grps)) + return (FALSE); + if (!xdr_smb_privset_helper(xdrs, (char **)&objp->tkn_privileges)) + return (FALSE); + if (!xdr_string(xdrs, &objp->tkn_account_name, ~0)) + return (FALSE); + if (!xdr_string(xdrs, &objp->tkn_domain_name, ~0)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->tkn_flags)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->tkn_audit_sid)) + return (FALSE); + if (!xdr_pointer(xdrs, (char **)&objp->tkn_session_key, + sizeof (smb_session_key_t), (xdrproc_t)xdr_smb_session_key_t)) + return (FALSE); + if (!xdr_smb_posix_grps_helper(xdrs, (char **)&objp->tkn_posix_grps)) + return (FALSE); + return (TRUE); +} diff --git a/usr/src/common/smbsrv/smb_utf8.c b/usr/src/common/smbsrv/smb_utf8.c new file mode 100644 index 000000000000..704f01877e42 --- /dev/null +++ b/usr/src/common/smbsrv/smb_utf8.c @@ -0,0 +1,418 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Multibyte/wide-char conversion routines. Wide-char encoding provides + * a fixed size character encoding that maps to the Unicode 16-bit + * (UCS-2) character set standard. Multibyte or UCS transformation + * format (UTF) encoding is a variable length character encoding scheme + * that s compatible with existing ASCII characters and guarantees that + * the resultant strings do not contain embedded null characters. Both + * types of encoding provide a null terminator: single byte for UTF-8 + * and a wide-char null for Unicode. See RFC 2044. + * + * The table below illustrates the UTF-8 encoding scheme. The letter x + * indicates bits available for encoding the character value. + * + * UCS-2 UTF-8 octet sequence (binary) + * 0x0000-0x007F 0xxxxxxx + * 0x0080-0x07FF 110xxxxx 10xxxxxx + * 0x0800-0xFFFF 1110xxxx 10xxxxxx 10xxxxxx + * + * RFC 2044 + * UTF-8,a transformation format of UNICODE and ISO 10646 + * F. Yergeau + * Alis Technologies + * October 1996 + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef _KERNEL +#include +#include +#else +#include +#include +#include +#include +#endif +#include +#include + +int mbtowc_verbose = 0; +int mbtowc_announce = 0; + +/* + * mbstowcs + * + * The mbstowcs() function converts a multibyte character string + * mbstring into a wide character string wcstring. No more than + * nwchars wide characters are stored. A terminating null wide + * character is appended if there is room. + * + * Returns the number of wide characters converted, not counting + * any terminating null wide character. Returns -1 if an invalid + * multibyte character is encountered. + */ +size_t +mts_mbstowcs(mts_wchar_t *wcstring, const char *mbstring, size_t nwchars) +{ + int len; + mts_wchar_t *start = wcstring; + + while (nwchars--) { + len = mts_mbtowc(wcstring, mbstring, MTS_MB_CHAR_MAX); + if (len < 0) { + *wcstring = 0; + return ((size_t)-1); + } + + if (*mbstring == 0) + break; + + ++wcstring; + mbstring += len; + } + + return (wcstring - start); +} + + +/* + * mbtowc + * + * The mbtowc() function converts a multibyte character mbchar into + * a wide character and stores the result in the object pointed to + * by wcharp. Up to nbytes bytes are examined. + * + * If mbchar is NULL, mbtowc() returns zero to indicate that shift + * states are not supported. If mbchar is valid, returns the number + * of bytes processed in mbchar. If mbchar is invalid, returns -1. + */ +int /*ARGSUSED*/ +mts_mbtowc(mts_wchar_t *wcharp, const char *mbchar, size_t nbytes) +{ + unsigned char mbyte; + mts_wchar_t wide_char; + int count; + int bytes_left; + + if (mbchar == 0) + return (0); /* shift states not supported */ + + /* 0xxxxxxx -> 1 byte ASCII encoding */ + if (((mbyte = *mbchar++) & 0x80) == 0) { + if (wcharp) + *wcharp = (mts_wchar_t)mbyte; + + return (mbyte ? 1 : 0); + } + + /* 10xxxxxx -> invalid first byte */ + if ((mbyte & 0x40) == 0) { + if (mbtowc_verbose || mbtowc_announce == 0) { + mbtowc_announce = 1; + } + return (-1); + } + + wide_char = mbyte; + if ((mbyte & 0x20) == 0) { + wide_char &= 0x1f; + bytes_left = 1; + } else if ((mbyte & 0x10) == 0) { + wide_char &= 0x0f; + bytes_left = 2; + } else { + if (mbtowc_verbose || mbtowc_announce == 0) { + mbtowc_announce = 1; + } + return (-1); + } + + count = 1; + while (bytes_left--) { + if (((mbyte = *mbchar++) & 0xc0) != 0x80) { + if (mbtowc_verbose || mbtowc_announce == 0) { + mbtowc_announce = 1; + } + return (-1); + } + + count++; + wide_char = (wide_char << 6) | (mbyte & 0x3f); + } + + if (wcharp) + *wcharp = wide_char; + + return (count); +} + + +/* + * wctomb + * + * The wctomb() function converts a wide character wchar into a multibyte + * character and stores the result in mbchar. The object pointed to by + * mbchar must be large enough to accommodate the multibyte character. + * + * Returns the numberof bytes written to mbchar. + */ +int +mts_wctomb(char *mbchar, mts_wchar_t wchar) +{ +#ifdef UTF8_DEBUG + char *start = mbchar; +#endif + + if ((wchar & ~0x7f) == 0) { + *mbchar = (char)wchar; + return (1); + } + + if ((wchar & ~0x7ff) == 0) { + *mbchar++ = (wchar >> 6) | 0xc0; + *mbchar = (wchar & 0x3f) | 0x80; + return (2); + } + + *mbchar++ = (wchar >> 12) | 0xe0; + *mbchar++ = ((wchar >> 6) & 0x3f) | 0x80; + *mbchar = (wchar & 0x3f) | 0x80; + return (3); +} + + +/* + * wcstombs + * + * The wcstombs() function converts a wide character string wcstring + * into a multibyte character string mbstring. Up to nbytes bytes are + * stored in mbstring. Partial multibyte characters at the end of the + * string are not stored. The multibyte character string is null + * terminated if there is room. + * + * Returns the number of bytes converted, not counting the terminating + * null byte. + */ +size_t +mts_wcstombs(char *mbstring, const mts_wchar_t *wcstring, size_t nbytes) +{ + char *start = mbstring; + const mts_wchar_t *wcp = wcstring; + mts_wchar_t wide_char; + char buf[4]; + size_t len; + + if ((mbstring == 0) || (wcstring == 0)) + return (0); + + while (nbytes > MTS_MB_CHAR_MAX) { + wide_char = *wcp++; + len = mts_wctomb(mbstring, wide_char); + + if (wide_char == 0) + /*LINTED E_PTRDIFF_OVERFLOW*/ + return (mbstring - start); + + mbstring += len; + nbytes -= len; + } + + while (wide_char && nbytes) { + wide_char = *wcp++; + if ((len = mts_wctomb(buf, wide_char)) > nbytes) { + *mbstring = 0; + break; + } + + bcopy(buf, mbstring, len); + mbstring += len; + nbytes -= len; + } + + /*LINTED E_PTRDIFF_OVERFLOW*/ + return (mbstring - start); +} + + +/* + * Returns the number of bytes that would be written if the multi- + * byte string mbs was converted to a wide character string, not + * counting the terminating null wide character. + */ +size_t +mts_wcequiv_strlen(const char *mbs) +{ + mts_wchar_t wide_char; + size_t bytes; + size_t len = 0; + + while (*mbs) { + bytes = mts_mbtowc(&wide_char, mbs, MTS_MB_CHAR_MAX); + if (bytes == ((size_t)-1)) + return ((size_t)-1); + + len += sizeof (mts_wchar_t); + mbs += bytes; + } + + return (len); +} + + +/* + * Returns the number of bytes that would be written if the multi- + * byte string mbs was converted to a single byte character string, + * not counting the terminating null character. + */ +size_t +mts_sbequiv_strlen(const char *mbs) +{ + mts_wchar_t wide_char; + size_t nbytes; + size_t len = 0; + + while (*mbs) { + nbytes = mts_mbtowc(&wide_char, mbs, MTS_MB_CHAR_MAX); + if (nbytes == ((size_t)-1)) + return ((size_t)-1); + + if (wide_char & 0xFF00) + len += sizeof (mts_wchar_t); + else + ++len; + + mbs += nbytes; + } + + return (len); +} + + +/* + * stombs + * + * Convert a regular null terminated string 'string' to a UTF-8 encoded + * null terminated multi-byte string 'mbstring'. Only full converted + * UTF-8 characters will be written 'mbstring'. If a character will not + * fit within the remaining buffer space or 'mbstring' will overflow + * max_mblen, the conversion process will be terminated and 'mbstring' + * will be null terminated. + * + * Returns the number of bytes written to 'mbstring', excluding the + * terminating null character. + * + * If either mbstring or string is a null pointer, -1 is returned. + */ +int +mts_stombs(char *mbstring, char *string, int max_mblen) +{ + char *start = mbstring; + unsigned char *p = (unsigned char *)string; + int space_left = max_mblen; + int len; + mts_wchar_t wide_char; + char buf[4]; + + if (!mbstring || !string) + return (-1); + + while (*p && space_left > 2) { + wide_char = *p++; + len = mts_wctomb(mbstring, wide_char); + mbstring += len; + space_left -= len; + } + + if (*p) { + wide_char = *p; + if ((len = mts_wctomb(buf, wide_char)) < 2) { + *mbstring = *buf; + mbstring += len; + space_left -= len; + } + } + + *mbstring = '\0'; + + /*LINTED E_PTRDIFF_OVERFLOW*/ + return (mbstring - start); +} + + +/* + * mbstos + * + * Convert a null terminated multi-byte string 'mbstring' to a regular + * null terminated string 'string'. A 1-byte character in 'mbstring' + * maps to a 1-byte character in 'string'. A 2-byte character in + * 'mbstring' will be mapped to 2-bytes, if the upper byte is non-null. + * Otherwise the upper byte null will be discarded to ensure that the + * output stream does not contain embedded null characters. + * + * If the input stream contains invalid multi-byte characters, a value + * of -1 will be returned. Otherwise the length of 'string', excluding + * the terminating null character, is returned. + * + * If either mbstring or string is a null pointer, -1 is returned. + */ +int +mts_mbstos(char *string, const char *mbstring) +{ + mts_wchar_t wc; + unsigned char *start = (unsigned char *)string; + int len; + + if (string == 0 || mbstring == 0) + return (-1); + + while (*mbstring) { + if ((len = mts_mbtowc(&wc, mbstring, MTS_MB_CHAR_MAX)) < 0) { + *string = 0; + return (-1); + } + + if (wc & 0xFF00) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + *((mts_wchar_t *)string) = wc; + string += sizeof (mts_wchar_t); + } + else + { + *string = (unsigned char)wc; + string++; + } + + mbstring += len; + } + + *string = 0; + + /*LINTED E_PTRDIFF_OVERFLOW*/ + return ((unsigned char *)string - start); +} diff --git a/usr/src/common/smbsrv/smb_xdr_utils.c b/usr/src/common/smbsrv/smb_xdr_utils.c new file mode 100644 index 000000000000..1293eb34c1ac --- /dev/null +++ b/usr/src/common/smbsrv/smb_xdr_utils.c @@ -0,0 +1,165 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef _KERNEL +#include +#endif /* _KERNEL */ +#include + +#ifdef _KERNEL +/* + * xdr_vector(): + * + * XDR a fixed length array. Unlike variable-length arrays, + * the storage of fixed length arrays is static and unfreeable. + * > basep: base of the array + * > size: size of the array + * > elemsize: size of each element + * > xdr_elem: routine to XDR each element + */ +#define LASTUNSIGNED ((uint_t)0-1) +bool_t +xdr_vector(XDR *xdrs, char *basep, uint_t nelem, + uint_t elemsize, xdrproc_t xdr_elem) +{ + uint_t i; + char *elptr; + + elptr = basep; + for (i = 0; i < nelem; i++) { + if (!(*xdr_elem)(xdrs, elptr, LASTUNSIGNED)) + return (FALSE); + elptr += elemsize; + } + return (TRUE); +} + +/* + * XDR an unsigned char + */ +bool_t +xdr_u_char(XDR *xdrs, uchar_t *cp) +{ + int i; + + switch (xdrs->x_op) { + case XDR_ENCODE: + i = (*cp); + return (XDR_PUTINT32(xdrs, &i)); + case XDR_DECODE: + if (!XDR_GETINT32(xdrs, &i)) + return (FALSE); + *cp = (uchar_t)i; + return (TRUE); + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} +#endif /* _KERNEL */ + +bool_t +xdr_smb_dr_string_t(xdrs, objp) + XDR *xdrs; + smb_dr_string_t *objp; +{ + if (!xdr_string(xdrs, &objp->buf, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_smb_dr_bytes_t(xdrs, objp) + XDR *xdrs; + smb_dr_bytes_t *objp; +{ + if (!xdr_array(xdrs, (char **)&objp->bytes_val, + (uint32_t *)&objp->bytes_len, ~0, sizeof (uint8_t), + (xdrproc_t)xdr_uint8_t)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_smb_dr_user_ctx_t(xdrs, objp) + XDR *xdrs; + smb_dr_user_ctx_t *objp; +{ + if (!xdr_uint64_t(xdrs, &objp->du_session_id)) + return (FALSE); + if (!xdr_uint16_t(xdrs, &objp->du_uid)) + return (FALSE); + if (!xdr_uint16_t(xdrs, &objp->du_domain_len)) + return (FALSE); + if (!xdr_string(xdrs, &objp->du_domain, ~0)) + return (FALSE); + if (!xdr_uint16_t(xdrs, &objp->du_account_len)) + return (FALSE); + if (!xdr_string(xdrs, &objp->du_account, ~0)) + return (FALSE); + if (!xdr_uint16_t(xdrs, &objp->du_workstation_len)) + return (FALSE); + if (!xdr_string(xdrs, &objp->du_workstation, ~0)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->du_ipaddr)) + return (FALSE); + if (!xdr_int32_t(xdrs, &objp->du_native_os)) + return (FALSE); + if (!xdr_int64_t(xdrs, &objp->du_logon_time)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->du_flags)) + return (FALSE); + return (TRUE); +} + + +bool_t +xdr_smb_dr_ulist_t(xdrs, objp) + XDR *xdrs; + smb_dr_ulist_t *objp; +{ + if (!xdr_uint32_t(xdrs, &objp->dul_cnt)) + return (FALSE); + if (!xdr_vector(xdrs, (char *)objp->dul_users, SMB_DR_MAX_USERS, + sizeof (smb_dr_user_ctx_t), (xdrproc_t)xdr_smb_dr_user_ctx_t)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_smb_dr_kshare_t(xdrs, objp) + XDR *xdrs; + smb_dr_kshare_t *objp; +{ + if (!xdr_int32_t(xdrs, &objp->k_op)) + return (FALSE); + if (!xdr_string(xdrs, &objp->k_path, MAXPATHLEN)) + return (FALSE); + if (!xdr_string(xdrs, &objp->k_sharename, MAXNAMELEN)) + return (FALSE); + return (TRUE); +} diff --git a/usr/src/common/util/string.c b/usr/src/common/util/string.c index 94153d2db2ad..73063ead6176 100644 --- a/usr/src/common/util/string.c +++ b/usr/src/common/util/string.c @@ -622,6 +622,62 @@ strspn(const char *string, const char *charset) return ((size_t)(q - string)); } +size_t +strcspn(const char *string, const char *charset) +{ + const char *p, *q; + + for (q = string; *q != '\0'; ++q) { + for (p = charset; *p != '\0' && *p != *q; ++p) + ; + if (*p != '\0') + break; + } + + /*LINTED E_PTRDIFF_OVERFLOW*/ + return ((size_t)(q - string)); +} + +/* + * strsep + * + * The strsep() function locates, in the string referenced by *stringp, the + * first occurrence of any character in the string delim (or the terminating + * `\0' character) and replaces it with a `\0'. The location of the next + * character after the delimiter character (or NULL, if the end of the + * string was reached) is stored in *stringp. The original value of + * *stringp is returned. + * + * If *stringp is initially NULL, strsep() returns NULL. + */ +char * +strsep(char **stringp, const char *delim) +{ + char *s; + const char *spanp; + int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + + for (tok = s; ; ) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + /* * Unless mentioned otherwise, all of the routines below should be added to * the Solaris DDI as necessary. For now, only provide them to standalone. diff --git a/usr/src/common/xattr/xattr_common.c b/usr/src/common/xattr/xattr_common.c new file mode 100644 index 000000000000..55902162eade --- /dev/null +++ b/usr/src/common/xattr/xattr_common.c @@ -0,0 +1,135 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#if defined(_KERNEL) +#include +#else +#include +#endif + +/* + * This table maps each system attribute to its option and its view. + * All new system attrs must be added to this table. To add a new view, + * add another entry to xattr_dirents[] and update xattr_view_t in sys/attr.h. + * Also, xattr_file_pathconf() and sys/unistd.h should be updated to add + * return values for the new view. + */ + +static xattr_entry_t xattrs[F_ATTR_ALL] = { + { A_ARCHIVE, O_ARCHIVE, XATTR_VIEW_READWRITE, DATA_TYPE_BOOLEAN_VALUE }, + { A_HIDDEN, O_HIDDEN, XATTR_VIEW_READWRITE, DATA_TYPE_BOOLEAN_VALUE }, + { A_READONLY, O_READONLY, XATTR_VIEW_READWRITE, + DATA_TYPE_BOOLEAN_VALUE }, + { A_SYSTEM, O_SYSTEM, XATTR_VIEW_READWRITE, DATA_TYPE_BOOLEAN_VALUE }, + { A_APPENDONLY, O_APPENDONLY, XATTR_VIEW_READWRITE, + DATA_TYPE_BOOLEAN_VALUE }, + { A_NODUMP, O_NODUMP, XATTR_VIEW_READWRITE, DATA_TYPE_BOOLEAN_VALUE }, + { A_IMMUTABLE, O_IMMUTABLE, XATTR_VIEW_READWRITE, + DATA_TYPE_BOOLEAN_VALUE }, + { A_AV_MODIFIED, O_AV_MODIFIED, XATTR_VIEW_READWRITE, + DATA_TYPE_BOOLEAN_VALUE }, + { A_OPAQUE, O_NONE, XATTR_VIEW_READONLY, DATA_TYPE_BOOLEAN_VALUE }, + { A_AV_SCANSTAMP, O_NONE, XATTR_VIEW_READONLY, DATA_TYPE_UINT8_ARRAY }, + { A_AV_QUARANTINED, O_AV_QUARANTINED, XATTR_VIEW_READWRITE, + DATA_TYPE_BOOLEAN_VALUE }, + { A_NOUNLINK, O_NOUNLINK, XATTR_VIEW_READWRITE, + DATA_TYPE_BOOLEAN_VALUE }, + { A_CRTIME, O_NONE, XATTR_VIEW_READWRITE, DATA_TYPE_UINT64_ARRAY }, + { A_OWNERSID, O_NONE, XATTR_VIEW_READWRITE, DATA_TYPE_NVLIST }, + { A_GROUPSID, O_NONE, XATTR_VIEW_READWRITE, DATA_TYPE_NVLIST }, + { A_FSID, O_NONE, XATTR_VIEW_READONLY, DATA_TYPE_UINT64 }, + { A_MDEV, O_NONE, XATTR_VIEW_READONLY, DATA_TYPE_UINT16 }, +}; + +const char * +attr_to_name(f_attr_t attr) +{ + if (attr >= F_ATTR_ALL || attr < 0) + return (NULL); + + return (xattrs[attr].x_name); +} + +const char * +attr_to_option(f_attr_t attr) +{ + if (attr >= F_ATTR_ALL || attr < 0) + return (NULL); + + return (xattrs[attr].x_option); +} + +f_attr_t +name_to_attr(const char *name) +{ + int i; + + for (i = 0; i < F_ATTR_ALL; i++) { + if (strcmp(name, xattrs[i].x_name) == 0) + return (i); + } + + return (F_ATTR_INVAL); +} + +f_attr_t +option_to_attr(const char *option) +{ + int i; + + for (i = 0; i < F_ATTR_ALL; i++) { + if (strcmp(option, xattrs[i].x_option) == 0) + return (i); + } + + return (F_ATTR_INVAL); +} + +xattr_view_t +attr_to_xattr_view(f_attr_t attr) +{ + if (attr >= F_ATTR_ALL || attr < 0) + return (NULL); + + return (xattrs[attr].x_xattr_view); +} + +int +attr_count(void) +{ + return (F_ATTR_ALL); +} + +data_type_t +attr_to_data_type(f_attr_t attr) +{ + if (attr >= F_ATTR_ALL || attr < 0) + return (DATA_TYPE_UNKNOWN); + + return (xattrs[attr].x_data_type); +} diff --git a/usr/src/common/zfs/zfs_prop.c b/usr/src/common/zfs/zfs_prop.c index 19b6774b6eb3..2d04ce5fb0d4 100644 --- a/usr/src/common/zfs/zfs_prop.c +++ b/usr/src/common/zfs/zfs_prop.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "zfs_prop.h" #include "zfs_deleg.h" @@ -100,6 +101,13 @@ zfs_prop_init(void) { NULL } }; + static zprop_index_t case_table[] = { + { "sensitive", ZFS_CASE_SENSITIVE }, + { "insensitive", ZFS_CASE_INSENSITIVE }, + { "mixed", ZFS_CASE_MIXED }, + { NULL } + }; + static zprop_index_t copies_table[] = { { "1", 1 }, { "2", 2 }, @@ -107,9 +115,19 @@ zfs_prop_init(void) { NULL } }; + static zprop_index_t normalize_table[] = { + { "none", ZFS_NORMALIZE_NONE }, + { "formD", ZFS_NORMALIZE_D }, + { "formKC", ZFS_NORMALIZE_KC }, + { "formC", ZFS_NORMALIZE_C }, + { "formKD", ZFS_NORMALIZE_KD }, + { NULL } + }; + static zprop_index_t version_table[] = { { "1", 1 }, { "2", 2 }, + { "3", 3 }, { "current", ZPL_VERSION }, { NULL } }; @@ -163,11 +181,17 @@ zfs_prop_init(void) register_index(ZFS_PROP_XATTR, "xattr", 1, PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "XATTR", boolean_table); + register_index(ZFS_PROP_VSCAN, "vscan", 0, PROP_INHERIT, + ZFS_TYPE_FILESYSTEM, "on | off", "VSCAN", + boolean_table); + register_index(ZFS_PROP_NBMAND, "nbmand", 0, PROP_INHERIT, + ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off", "NBMAND", + boolean_table); /* default index properties */ register_index(ZFS_PROP_VERSION, "version", 0, PROP_DEFAULT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, - "1 | 2 | current", "VERSION", version_table); + "1 | 2 | 3 | current", "VERSION", version_table); /* default index (boolean) properties */ register_index(ZFS_PROP_CANMOUNT, "canmount", 1, PROP_DEFAULT, @@ -177,6 +201,20 @@ zfs_prop_init(void) register_index(ZFS_PROP_MOUNTED, "mounted", 0, PROP_READONLY, ZFS_TYPE_FILESYSTEM, "yes | no", "MOUNTED", boolean_table); + /* set once index properties */ + register_index(ZFS_PROP_NORMALIZE, "normalization", ZFS_NORMALIZE_NONE, + PROP_ONETIME, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, + "none | formC | formD | formKC | formKD", "NORMALIZATION", + normalize_table); + register_index(ZFS_PROP_CASE, "casesensitivity", ZFS_CASE_SENSITIVE, + PROP_ONETIME, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, + "sensitive | insensitive | mixed", "CASE", case_table); + + /* set once index (boolean) properties */ + register_index(ZFS_PROP_UTF8ONLY, "utf8only", 0, PROP_ONETIME, + ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, + "on | off", "UTF8ONLY", boolean_table); + /* string properties */ register_string(ZFS_PROP_ORIGIN, "origin", NULL, PROP_READONLY, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "", "ORIGIN"); @@ -188,6 +226,8 @@ zfs_prop_init(void) ZFS_TYPE_DATASET, "on | off | type=", "SHAREISCSI"); register_string(ZFS_PROP_TYPE, "type", NULL, PROP_READONLY, ZFS_TYPE_DATASET, "filesystem | volume | snapshot", "TYPE"); + register_string(ZFS_PROP_SHARESMB, "sharesmb", "off", PROP_INHERIT, + ZFS_TYPE_FILESYSTEM, "on | off | sharemgr(1M) options", "SHARESMB"); /* readonly number properties */ register_number(ZFS_PROP_USED, "used", 0, PROP_READONLY, @@ -200,8 +240,8 @@ zfs_prop_init(void) PROP_READONLY, ZFS_TYPE_DATASET, "<1.00x or higher if compressed>", "RATIO"); register_number(ZFS_PROP_VOLBLOCKSIZE, "volblocksize", 8192, - PROP_READONLY, ZFS_TYPE_VOLUME, - "512 to 128k, power of 2", "VOLBLOCK"); + PROP_ONETIME, + ZFS_TYPE_VOLUME, "512 to 128k, power of 2", "VOLBLOCK"); /* default number properties */ register_number(ZFS_PROP_QUOTA, "quota", 0, PROP_DEFAULT, @@ -322,7 +362,17 @@ zfs_prop_get_type(zfs_prop_t prop) boolean_t zfs_prop_readonly(zfs_prop_t prop) { - return (zfs_prop_table[prop].pd_attr == PROP_READONLY); + return (zfs_prop_table[prop].pd_attr == PROP_READONLY || + zfs_prop_table[prop].pd_attr == PROP_ONETIME); +} + +/* + * Returns TRUE if the property is only allowed to be set once. + */ +boolean_t +zfs_prop_setonce(zfs_prop_t prop) +{ + return (zfs_prop_table[prop].pd_attr == PROP_ONETIME); } const char * @@ -353,7 +403,8 @@ zfs_prop_to_name(zfs_prop_t prop) boolean_t zfs_prop_inheritable(zfs_prop_t prop) { - return (zfs_prop_table[prop].pd_attr == PROP_INHERIT); + return (zfs_prop_table[prop].pd_attr == PROP_INHERIT || + zfs_prop_table[prop].pd_attr == PROP_ONETIME); } #ifndef _KERNEL @@ -399,4 +450,5 @@ zfs_prop_align_right(zfs_prop_t prop) { return (zfs_prop_table[prop].pd_rightalign); } + #endif diff --git a/usr/src/common/zfs/zfs_prop.h b/usr/src/common/zfs/zfs_prop.h index e2f1186285cc..08af3f4090a8 100644 --- a/usr/src/common/zfs/zfs_prop.h +++ b/usr/src/common/zfs/zfs_prop.h @@ -48,7 +48,14 @@ typedef enum { typedef enum { PROP_DEFAULT, PROP_READONLY, - PROP_INHERIT + PROP_INHERIT, + /* + * ONETIME properties are a sort of conglomeration of READONLY + * and INHERIT. They can be set only during object creation, + * after that they are READONLY. If not explicitly set during + * creation, they can be inherited. + */ + PROP_ONETIME } zprop_attr_t; typedef struct zfs_index { diff --git a/usr/src/grub/grub-0.95/stage2/zfs-include/zfs.h b/usr/src/grub/grub-0.95/stage2/zfs-include/zfs.h index 8af94bc497d1..9619d5bfc28a 100644 --- a/usr/src/grub/grub-0.95/stage2/zfs-include/zfs.h +++ b/usr/src/grub/grub-0.95/stage2/zfs-include/zfs.h @@ -37,7 +37,8 @@ #define SPA_VERSION_6 6ULL #define SPA_VERSION_7 7ULL #define SPA_VERSION_8 8ULL -#define SPA_VERSION SPA_VERSION_8 +#define SPA_VERSION_9 9ULL +#define SPA_VERSION SPA_VERSION_9 /* * The following are configuration names used in the nvlist describing a pool's diff --git a/usr/src/grub/grub-0.95/stage2/zfs-include/zfs_acl.h b/usr/src/grub/grub-0.95/stage2/zfs-include/zfs_acl.h index eb51b9baa89f..77ebb8dc77d1 100644 --- a/usr/src/grub/grub-0.95/stage2/zfs-include/zfs_acl.h +++ b/usr/src/grub/grub-0.95/stage2/zfs-include/zfs_acl.h @@ -31,21 +31,32 @@ typedef unsigned int uid_t; /* UID type */ #endif /* _UID_T */ -typedef struct ace { - uid_t a_who; /* uid or gid */ - uint32_t a_access_mask; /* read,write,... */ - uint16_t a_flags; /* see below */ - uint16_t a_type; /* allow or deny */ -} ace_t; +typedef struct zfs_oldace { + uint32_t z_fuid; /* "who" */ + uint32_t z_access_mask; /* access mask */ + uint16_t z_flags; /* flags, i.e inheritance */ + uint16_t z_type; /* type of entry allow/deny */ +} zfs_oldace_t; #define ACE_SLOT_CNT 6 -typedef struct zfs_znode_acl { +typedef struct zfs_znode_acl_v0 { uint64_t z_acl_extern_obj; /* ext acl pieces */ uint32_t z_acl_count; /* Number of ACEs */ uint16_t z_acl_version; /* acl version */ uint16_t z_acl_pad; /* pad */ - ace_t z_ace_data[ACE_SLOT_CNT]; /* 6 standard ACEs */ + zfs_oldace_t z_ace_data[ACE_SLOT_CNT]; /* 6 standard ACEs */ +} zfs_znode_acl_v0_t; + +#define ZFS_ACE_SPACE (sizeof (zfs_oldace_t) * ACE_SLOT_CNT) + +typedef struct zfs_znode_acl { + uint64_t z_acl_extern_obj; /* ext acl pieces */ + uint32_t z_acl_size; /* Number of bytes in ACL */ + uint16_t z_acl_version; /* acl version */ + uint16_t z_acl_count; /* ace count */ + uint8_t z_ace_data[ZFS_ACE_SPACE]; /* space for embedded ACEs */ } zfs_znode_acl_t; + #endif /* _SYS_FS_ZFS_ACL_H */ diff --git a/usr/src/head/Makefile b/usr/src/head/Makefile index 5183a405139e..d63931a8a430 100644 --- a/usr/src/head/Makefile +++ b/usr/src/head/Makefile @@ -48,6 +48,7 @@ HDRS= $($(MACH)_HDRS) $(ATTRDB_HDRS) \ archives.h \ assert.h \ atomic.h \ + attr.h \ config_admin.h \ cpio.h \ crypt.h \ diff --git a/usr/src/head/attr.h b/usr/src/head/attr.h new file mode 100644 index 000000000000..9c394412a151 --- /dev/null +++ b/usr/src/head/attr.h @@ -0,0 +1,59 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _ATTR_H +#define _ATTR_H + +#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.6.1.7 */ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__STDC__) + +extern int getattrat(int, xattr_view_t, const char *, nvlist_t **); +extern int fgetattr(int, xattr_view_t, nvlist_t **); +extern int setattrat(int, xattr_view_t, const char *, nvlist_t *); +extern int fsetattr(int, xattr_view_t, nvlist_t *); + +#else /* defined(__STDC__) */ + +extern int getattrat(); +extern int fgetattr(); +extern int setattrat(); +extern int fsetattr(); + +#endif /* defined(__STDC__) */ + +#ifdef __cplusplus +} +#endif + +#endif /* _ATTR_H */ diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index 68613f94127b..ab96c6624e31 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -234,6 +234,7 @@ SUBDIRS += \ libidmap \ libipmi \ libexacct/demo \ + smbsrv \ $($(MACH)_SUBDIRS) sparc_SUBDIRS= .WAIT \ @@ -421,6 +422,7 @@ HDRSUBDIRS= \ libkrb5 \ libshare \ libidmap \ + smbsrv \ $($(MACH)_HDRSUBDIRS) $(CLOSED_BUILD)HDRSUBDIRS += \ @@ -506,7 +508,7 @@ libinetcfg: libnsl libsocket libdevinfo libkmf: libcryptoutil pkcs11 openssl libnsl: libmd5 libscf libmapid: libresolv -libuuid: libdlpi libdladm +libuuid: libdlpi libdladm libinetutil: libsocket libsecdb: libnsl libsasl: libgss libsocket pkcs11 libmd @@ -528,7 +530,7 @@ libwrap: libnsl libsocket libwanboot: libnvpair libresolv libnsl libsocket libdevinfo libinetutil \ libdhcputil openssl libwanbootutil: libnsl -pam_modules: libproject passwdutil $(SMARTCARD) +pam_modules: libproject passwdutil $(SMARTCARD) smbsrv libscf: libuutil libmd libgen libinetsvc: libscf librestart: libuutil libscf @@ -545,6 +547,8 @@ brand: libc libsocket libshare: libscf libzfs libuuid libfsmgt libsecdb libexacct/demo: libexacct libproject libsocket libnsl libtsalarm: libpcp +smbsrv: libsocket libnsl libmd libxnet libpthread librt \ + libshare libidmap pkcs11 # # The reason this rule checks for the existence of the diff --git a/usr/src/lib/common/inc/c_synonyms.h b/usr/src/lib/common/inc/c_synonyms.h index 50bb091a589d..8b8a61053193 100644 --- a/usr/src/lib/common/inc/c_synonyms.h +++ b/usr/src/lib/common/inc/c_synonyms.h @@ -744,6 +744,7 @@ extern "C" { #define readv _readv #define realpath _realpath #define remque _remque +#define renameat _renameat #define resolvepath _resolvepath #define rmdir _rmdir #define rwlock_destroy _rwlock_destroy diff --git a/usr/src/lib/libbc/inc/include/tzfile.h b/usr/src/lib/libbc/inc/include/tzfile.h index d6e43f03c2ce..b094236256b4 100644 --- a/usr/src/lib/libbc/inc/include/tzfile.h +++ b/usr/src/lib/libbc/inc/include/tzfile.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,139 +19,17 @@ * CDDL HEADER END */ /* - * Copyright 1989 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* from Arthur Olson's 6.1 */ -#pragma ident "%Z%%M% %I% %E% SMI" - -#ifndef TZFILE_H - -#define TZFILE_H - -/* -** Information about time zone files. -*/ - -#define TZDIR "/usr/share/lib/zoneinfo" /* Time zone object file directory */ - -#define TZDEFAULT (getenv("TZ")) - -#define TZDEFRULES "posixrules" - -/* -** Each file begins with. . . -*/ - -struct tzhead { - char tzh_reserved[24]; /* reserved for future use */ - char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ - char tzh_leapcnt[4]; /* coded number of leap seconds */ - char tzh_timecnt[4]; /* coded number of transition times */ - char tzh_typecnt[4]; /* coded number of local time types */ - char tzh_charcnt[4]; /* coded number of abbr. chars */ -}; - -/* -** . . .followed by. . . -** -** tzh_timecnt (char [4])s coded transition times a la time(2) -** tzh_timecnt (unsigned char)s types of local time starting at above -** tzh_typecnt repetitions of -** one (char [4]) coded GMT offset in seconds -** one (unsigned char) used to set tm_isdst -** one (unsigned char) that's an abbreviation list index -** tzh_charcnt (char)s '\0'-terminated zone abbreviations -** tzh_leapcnt repetitions of -** one (char [4]) coded leap second transition times -** one (char [4]) total correction after above -** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition -** time is standard time, if FALSE, -** transition time is wall clock time -** if absent, transition times are -** assumed to be wall clock time -*/ - -/* -** In the current implementation, "tzset()" refuses to deal with files that -** exceed any of the limits below. -*/ +#ifndef _TZFILE_H +#define _TZFILE_H -/* -** The TZ_MAX_TIMES value below is enough to handle a bit more than a -** year's worth of solar time (corrected daily to the nearest second) or -** 138 years of Pacific Presidential Election time -** (where there are three time zone transitions every fourth year). -*/ -#define TZ_MAX_TIMES 370 - -#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ - -#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ - -#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ - -#define SECSPERMIN 60 -#define MINSPERHOUR 60 -#define HOURSPERDAY 24 -#define DAYSPERWEEK 7 -#define DAYSPERNYEAR 365 -#define DAYSPERLYEAR 366 -#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) -#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) -#define MONSPERYEAR 12 - -#define TM_SUNDAY 0 -#define TM_MONDAY 1 -#define TM_TUESDAY 2 -#define TM_WEDNESDAY 3 -#define TM_THURSDAY 4 -#define TM_FRIDAY 5 -#define TM_SATURDAY 6 - -#define TM_JANUARY 0 -#define TM_FEBRUARY 1 -#define TM_MARCH 2 -#define TM_APRIL 3 -#define TM_MAY 4 -#define TM_JUNE 5 -#define TM_JULY 6 -#define TM_AUGUST 7 -#define TM_SEPTEMBER 8 -#define TM_OCTOBER 9 -#define TM_NOVEMBER 10 -#define TM_DECEMBER 11 - -#define TM_YEAR_BASE 1900 - -#define EPOCH_YEAR 1970 -#define EPOCH_WDAY TM_THURSDAY - -/* -** Accurate only for the past couple of centuries; -** that will probably do. -*/ - -#define isleap(y) (((y) % 4) == 0 && ((y) % 100) != 0 || ((y) % 400) == 0) - -/* -** Use of the underscored variants may cause problems if you move your code to -** certain System-V-based systems; for maximum portability, use the -** underscore-free variants. The underscored variants are provided for -** backward compatibility only; they may disappear from future versions of -** this file. -*/ +#pragma ident "%Z%%M% %I% %E% SMI" -#define SECS_PER_MIN SECSPERMIN -#define MINS_PER_HOUR MINSPERHOUR -#define HOURS_PER_DAY HOURSPERDAY -#define DAYS_PER_WEEK DAYSPERWEEK -#define DAYS_PER_NYEAR DAYSPERNYEAR -#define DAYS_PER_LYEAR DAYSPERLYEAR -#define SECS_PER_HOUR SECSPERHOUR -#define SECS_PER_DAY SECSPERDAY -#define MONS_PER_YEAR MONSPERYEAR +#include -#endif /* !defined TZFILE_H */ +#endif /* _TZFILE_H */ diff --git a/usr/src/lib/libbsm/audit_event.txt b/usr/src/lib/libbsm/audit_event.txt index 8192080da338..125ef5017489 100644 --- a/usr/src/lib/libbsm/audit_event.txt +++ b/usr/src/lib/libbsm/audit_event.txt @@ -444,6 +444,8 @@ 6241:AUE_uadmin_remount:uadmin(1m) - remount:ss 6242:AUE_uadmin_ftrace:uadmin(1m) - ftrace:ss 6243:AUE_uadmin_swapctl:uadmin(1m) - swapctl:ss +6237:AUE_smbd_session:smbd(1m) session setup:lo +6238:AUE_smbd_logoff:smbd(1m) session logoff:lo # # Trusted Extensions events: # diff --git a/usr/src/lib/libbsm/common/adt.xml b/usr/src/lib/libbsm/common/adt.xml index 3ae2957188b9..a91361e4024b 100644 --- a/usr/src/lib/libbsm/common/adt.xml +++ b/usr/src/lib/libbsm/common/adt.xml @@ -1231,8 +1231,61 @@ Use is subject to license terms. uadmin(1M) + + + smbd + /usr/lib/smbsrv/smbd + + + + + + + + domain + + + + + username + + + + + sid + + + + + + + + + + smbd + /usr/lib/smbsrv/smbd + + + + + + + + domain + + + + + username + + + + + + + - + diff --git a/usr/src/lib/libc/Makefile.targ b/usr/src/lib/libc/Makefile.targ index b8237246a4a5..95a0a3b0e3b4 100644 --- a/usr/src/lib/libc/Makefile.targ +++ b/usr/src/lib/libc/Makefile.targ @@ -272,6 +272,10 @@ $(COMOBJS:%=pics/%): $(SRC)/common/util/$$(@F:.o=.c) $(COMPILE.c) -o $@ $(SRC)/common/util/$(@F:.o=.c) $(POST_PROCESS_O) +$(XATTROBJS:%=pics/%): $(SRC)/common/xattr/$$(@F:.o=.c) + $(COMPILE.c) -o $@ $(SRC)/common/xattr/$(@F:.o=.c) + $(POST_PROCESS_O) + $(DTRACEOBJS:%=pics/%): $(SRC)/common/dtrace/$$(@F:.o=.c) $(COMPILE.c) -o $@ $(SRC)/common/dtrace/$(@F:.o=.c) $(POST_PROCESS_O) diff --git a/usr/src/lib/libc/amd64/Makefile b/usr/src/lib/libc/amd64/Makefile index 0e2d924805bc..0539645b34b9 100644 --- a/usr/src/lib/libc/amd64/Makefile +++ b/usr/src/lib/libc/amd64/Makefile @@ -85,6 +85,8 @@ FPASMOBJS= \ ATOMICOBJS= \ atomic.o +XATTROBJS= \ + xattr_common.o COMOBJS= \ bcmp.o \ bcopy.o \ @@ -99,6 +101,7 @@ GENOBJS= \ _getsp.o \ abs.o \ alloca.o \ + attrat.o \ byteorder.o \ cache.o \ cuexit.o \ @@ -849,6 +852,7 @@ MOSTOBJS= \ $(I386FPOBJS) \ $(FPASMOBJS) \ $(ATOMICOBJS) \ + $(XATTROBJS) \ $(COMOBJS) \ $(GENOBJS) \ $(PORTFP) \ @@ -966,6 +970,7 @@ CLOBBERFILES += $(LIB_PIC) # list of C source for lint SRCS= \ $(ATOMICOBJS:%.o=$(SRC)/common/atomic/%.c) \ + $(XATTROBJS:%.o=$(SRC)/common/xattr/%.c) \ $(COMOBJS:%.o=$(SRC)/common/util/%.c) \ $(PORTFP:%.o=../port/fp/%.c) \ $(PORTGEN:%.o=../port/gen/%.c) \ diff --git a/usr/src/lib/libc/i386/Makefile.com b/usr/src/lib/libc/i386/Makefile.com index 6fe1a1c5cc55..5fb4ffea8462 100644 --- a/usr/src/lib/libc/i386/Makefile.com +++ b/usr/src/lib/libc/i386/Makefile.com @@ -83,6 +83,9 @@ FPASMOBJS= \ ATOMICOBJS= \ atomic.o +XATTROBJS= \ + xattr_common.o + COMOBJS= \ bcmp.o \ bcopy.o \ @@ -389,6 +392,7 @@ PORTGEN= \ atoi.o \ atol.o \ atoll.o \ + attrat.o \ attropen.o \ atexit.o \ atfork.o \ @@ -884,6 +888,7 @@ MOSTOBJS= \ $(FPOBJS) \ $(FPASMOBJS) \ $(ATOMICOBJS) \ + $(XATTROBJS) \ $(COMOBJS) \ $(DTRACEOBJS) \ $(GENOBJS) \ @@ -1026,6 +1031,7 @@ CLOBBERFILES += $(LIB_PIC) # list of C source for lint SRCS= \ $(ATOMICOBJS:%.o=$(SRC)/common/atomic/%.c) \ + $(XATTROBJS:%.o=$(SRC)/common/xattr/%.c) \ $(COMOBJS:%.o=$(SRC)/common/util/%.c) \ $(DTRACEOBJS:%.o=$(SRC)/common/dtrace/%.c) \ $(PORTFP:%.o=../port/fp/%.c) \ diff --git a/usr/src/lib/libc/port/gen/attrat.c b/usr/src/lib/libc/port/gen/attrat.c new file mode 100644 index 000000000000..f2a26d17cdc8 --- /dev/null +++ b/usr/src/lib/libc/port/gen/attrat.c @@ -0,0 +1,273 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "synonyms.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int (*nvpacker)(nvlist_t *, char **, size_t *, int, int); +static int (*nvsize)(nvlist_t *, size_t *, int); +static int (*nvunpacker)(char *, size_t, nvlist_t **); +static mutex_t attrlock = DEFAULTMUTEX; +static int initialized; +extern int __openattrdirat(int basefd, const char *name); + +static char *xattr_view_name[XATTR_VIEW_LAST] = { + VIEW_READONLY, + VIEW_READWRITE +}; + +static int +attrat_init() +{ + if (initialized == 0) { + lmutex_lock(&attrlock); + if (initialized == 1) { + lmutex_unlock(&attrlock); + return (0); + } + + void *libnvhandle = dlopen("libnvpair.so.1", RTLD_LAZY); + if (libnvhandle == NULL || (nvpacker = (int (*)(nvlist_t *, + char **, size_t *, int, int)) dlsym(libnvhandle, + "nvlist_pack")) == NULL || + (nvsize = (int (*)(nvlist_t *, + size_t *, int)) dlsym(libnvhandle, + "nvlist_size")) == NULL || + (nvunpacker = (int (*)(char *, size_t, + nvlist_t **)) dlsym(libnvhandle, + "nvlist_unpack")) == NULL) { + if (libnvhandle) + dlclose(libnvhandle); + lmutex_unlock(&attrlock); + return (EINVAL); + } + + initialized = 1; + lmutex_unlock(&attrlock); + } + return (0); +} + +static int +attr_nv_pack(nvlist_t *request, void **nv_request, size_t *nv_requestlen) +{ + size_t bufsize; + char *packbuf = NULL; + + if (nvsize(request, &bufsize, NV_ENCODE_XDR) != 0) { + return (EINVAL); + } + + packbuf = malloc(bufsize); + if (packbuf == NULL) + return (EINVAL); + if (nvpacker(request, &packbuf, &bufsize, NV_ENCODE_XDR, 0) != 0) { + free(packbuf); + } else { + *nv_request = (void *)packbuf; + *nv_requestlen = bufsize; + } + return (0); +} + +static const char * +view_to_name(xattr_view_t view) +{ + if (view >= XATTR_VIEW_LAST || view < 0) + return (NULL); + return (xattr_view_name[view]); +} + +static int +xattr_openat(int basefd, xattr_view_t view, int mode) +{ + const char *xattrname; + int xattrfd; + int oflag; + + switch (view) { + case XATTR_VIEW_READONLY: + oflag = O_RDONLY; + break; + case XATTR_VIEW_READWRITE: + oflag = mode & O_RDWR; + break; + default: + (void) __set_errno(EINVAL); + return (-1); + } + if (mode & O_XATTR) + oflag |= O_XATTR; + + xattrname = view_to_name(view); + xattrfd = openat(basefd, xattrname, oflag); + if (xattrfd < 0) + return (xattrfd); + /* Don't cache sysattr info (advisory) */ + (void) directio(xattrfd, DIRECTIO_ON); + return (xattrfd); +} + +static int +cgetattr(int fd, nvlist_t **response) +{ + int error; + int bytesread; + void *nv_response; + size_t nv_responselen; + struct stat buf; + + if (error = attrat_init()) + return (__set_errno(error)); + if ((error = fstat(fd, &buf)) != 0) + return (__set_errno(error)); + nv_responselen = buf.st_size; + + if ((nv_response = malloc(nv_responselen)) == NULL) + return (__set_errno(ENOMEM)); + bytesread = read(fd, nv_response, nv_responselen); + if (bytesread != nv_responselen) + return (__set_errno(EFAULT)); + + error = nvunpacker(nv_response, nv_responselen, response); + free(nv_response); + return (error); +} + +static int +csetattr(int fd, nvlist_t *request) +{ + int error, saveerrno; + int byteswritten; + void *nv_request; + size_t nv_requestlen; + + if (error = attrat_init()) + return (__set_errno(error)); + + if ((error = attr_nv_pack(request, &nv_request, &nv_requestlen)) != 0) + return (__set_errno(error)); + + (void) __set_errno(0); + byteswritten = write(fd, nv_request, nv_requestlen); + if (byteswritten != nv_requestlen) { + saveerrno = errno; + free(nv_request); + errno = saveerrno; + return (__set_errno(errno)); + } + + free(nv_request); + return (0); +} + +int +fgetattr(int basefd, xattr_view_t view, nvlist_t **response) +{ + int error, saveerrno, xattrfd; + + if ((xattrfd = xattr_openat(basefd, view, O_XATTR)) < 0) + return (xattrfd); + + error = cgetattr(xattrfd, response); + saveerrno = errno; + (void) close(xattrfd); + errno = saveerrno; + return (error); +} + +int +fsetattr(int basefd, xattr_view_t view, nvlist_t *request) +{ + int error, saveerrno, xattrfd; + + if ((xattrfd = xattr_openat(basefd, view, O_RDWR | O_XATTR)) < 0) + return (xattrfd); + error = csetattr(xattrfd, request); + saveerrno = errno; + (void) close(xattrfd); + errno = saveerrno; + return (error); +} + +int +getattrat(int basefd, xattr_view_t view, const char *name, nvlist_t **response) +{ + int error, saveerrno, namefd, xattrfd; + + if ((namefd = __openattrdirat(basefd, name)) < 0) + return (namefd); + + if ((xattrfd = xattr_openat(namefd, view, 0)) < 0) { + saveerrno = errno; + (void) close(namefd); + errno = saveerrno; + return (xattrfd); + } + + error = cgetattr(xattrfd, response); + saveerrno = errno; + (void) close(namefd); + (void) close(xattrfd); + errno = saveerrno; + return (error); +} + +int +setattrat(int basefd, xattr_view_t view, const char *name, nvlist_t *request) +{ + int error, saveerrno, namefd, xattrfd; + + if ((namefd = __openattrdirat(basefd, name)) < 0) + return (namefd); + + if ((xattrfd = xattr_openat(namefd, view, O_RDWR)) < 0) { + saveerrno = errno; + (void) close(namefd); + errno = saveerrno; + return (xattrfd); + } + + error = csetattr(xattrfd, request); + saveerrno = errno; + (void) close(namefd); + (void) close(xattrfd); + errno = saveerrno; + return (error); +} diff --git a/usr/src/lib/libc/port/gen/privlib.c b/usr/src/lib/libc/port/gen/privlib.c index 1968f7eaa436..57218876bcf3 100644 --- a/usr/src/lib/libc/port/gen/privlib.c +++ b/usr/src/lib/libc/port/gen/privlib.c @@ -442,7 +442,7 @@ priv_set(priv_op_t op, priv_ptype_t setname, ...) /* * priv_ineffect(privilege). - * tests the existance of a privilege against the effective set. + * tests the existence of a privilege against the effective set. */ boolean_t priv_ineffect(const char *priv) diff --git a/usr/src/lib/libc/port/llib-lc b/usr/src/lib/libc/port/llib-lc index 3e3bba59ca31..e74313a424f8 100644 --- a/usr/src/lib/libc/port/llib-lc +++ b/usr/src/lib/libc/port/llib-lc @@ -31,6 +31,7 @@ #include #include +#include #include #include #include diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers index 3e658f68e676..717c4d91861d 100644 --- a/usr/src/lib/libc/port/mapfile-vers +++ b/usr/src/lib/libc/port/mapfile-vers @@ -64,8 +64,11 @@ SUNW_1.23 { # SunOS 5.11 (Solaris 11) err; errx; fdatasync; + fgetattr; forkallx; forkx; + fsetattr; + getattrat; htonl; htons; lio_listio; @@ -111,6 +114,7 @@ SUNW_1.23 { # SunOS 5.11 (Solaris 11) sem_trywait; sem_unlink; sem_wait; + setattrat; _sharefs; shm_open; shm_unlink; @@ -1408,6 +1412,11 @@ SUNWprivate_1.1 { _atomic_swap_uint = NODYNSORT; _atomic_swap_ulong = NODYNSORT; _atomic_swap_ushort = NODYNSORT; + attr_count; + attr_to_data_type; + attr_to_name; + attr_to_option; + attr_to_xattr_view; _autofssys; _brk; __btowc_dense; @@ -1762,6 +1771,7 @@ SUNWprivate_1.1 { __mutex_trylock; _mutex_unlock = NODYNSORT; __mutex_unlock; + name_to_attr; _nanosleep; __nan_read; __nan_written; @@ -1807,6 +1817,8 @@ SUNWprivate_1.1 { __nsw_getconfig_v1; __nthreads; __numeric_init; + __openattrdirat; + option_to_attr; _openlog; _plock; _port_alert; diff --git a/usr/src/lib/libc/port/sys/fsmisc.c b/usr/src/lib/libc/port/sys/fsmisc.c index 88415c53630d..491cc73732bd 100644 --- a/usr/src/lib/libc/port/sys/fsmisc.c +++ b/usr/src/lib/libc/port/sys/fsmisc.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -60,3 +59,9 @@ renameat(int fromfd, const char *fromname, int tofd, const char *toname) { return (syscall(SYS_fsat, 7, fromfd, fromname, tofd, toname)); } + +int +__openattrdirat(int fd, const char *name) +{ + return (syscall(SYS_fsat, 9, fd, name)); +} diff --git a/usr/src/lib/libc/sparc/Makefile b/usr/src/lib/libc/sparc/Makefile index c67463652b96..37e0818dc623 100644 --- a/usr/src/lib/libc/sparc/Makefile +++ b/usr/src/lib/libc/sparc/Makefile @@ -102,6 +102,9 @@ FPASMOBJS= \ ATOMICOBJS= \ atomic.o +XATTROBJS= \ + xattr_common.o + COMOBJS= \ bcmp.o \ bcopy.o \ @@ -413,6 +416,7 @@ PORTGEN= \ atoi.o \ atol.o \ atoll.o \ + attrat.o \ attropen.o \ atexit.o \ atfork.o \ @@ -912,6 +916,7 @@ MOSTOBJS= \ $(FPOBJS) \ $(FPASMOBJS) \ $(ATOMICOBJS) \ + $(XATTROBJS) \ $(COMOBJS) \ $(DTRACEOBJS) \ $(GENOBJS) \ @@ -1044,6 +1049,7 @@ CLOBBERFILES += $(LIB_PIC) # list of C source for lint SRCS= \ $(ATOMICOBJS:%.o=$(SRC)/common/atomic/%.c) \ + $(XATTROBJS:%.o=$(SRC)/common/xattr/%.c) \ $(COMOBJS:%.o=$(SRC)/common/util/%.c) \ $(DTRACEOBJS:%.o=$(SRC)/common/dtrace/%.c) \ $(PORTFP:%.o=../port/fp/%.c) \ diff --git a/usr/src/lib/libc/sparcv9/Makefile b/usr/src/lib/libc/sparcv9/Makefile index c114126bfe34..c837e3936154 100644 --- a/usr/src/lib/libc/sparcv9/Makefile +++ b/usr/src/lib/libc/sparcv9/Makefile @@ -107,6 +107,9 @@ $(__GNUC)FPASMOBJS += \ ATOMICOBJS= \ atomic.o +XATTROBJS= \ + xattr_common.o + COMOBJS= \ bcmp.o \ bcopy.o \ @@ -371,6 +374,7 @@ PORTGEN= \ abort.o \ addsev.o \ assert.o \ + attrat.o \ atof.o \ atoi.o \ atol.o \ @@ -858,6 +862,7 @@ MOSTOBJS= \ $(FPOBJS64) \ $(FPASMOBJS) \ $(ATOMICOBJS) \ + $(XATTROBJS) \ $(COMOBJS) \ $(GENOBJS) \ $(PORTFP) \ @@ -978,6 +983,7 @@ CLOBBERFILES += $(LIB_PIC) # list of C source for lint SRCS= \ $(ATOMICOBJS:%.o=$(SRC)/common/atomic/%.c) \ + $(XATTROBJS:%.o=$(SRC)/common/xattr/%.c) \ $(COMOBJS:%.o=$(SRC)/common/util/%.c) \ $(PORTFP:%.o=../port/fp/%.c) \ $(PORTGEN:%.o=../port/gen/%.c) \ diff --git a/usr/src/lib/libcmdutils/Makefile.com b/usr/src/lib/libcmdutils/Makefile.com index 50ca57cedfff..e12e5b07c5f2 100644 --- a/usr/src/lib/libcmdutils/Makefile.com +++ b/usr/src/lib/libcmdutils/Makefile.com @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -27,14 +27,14 @@ LIBRARY= libcmdutils.a VERS= .1 -OBJECTS= avltree.o +OBJECTS= avltree.o sysattrs.o writefile.o process_xattrs.o include ../../Makefile.lib include ../../Makefile.rootfs LIBS = $(DYNLIB) $(LINTLIB) -LDLIBS += -lc -lavl +LDLIBS += -lc -lavl -lnvpair SRCDIR = ../common $(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) @@ -43,7 +43,7 @@ CFLAGS += $(CCVERBOSE) # All commands using the common avltree interfaces must # be largefile aware. -CPPFLAGS += -I.. -D_REENTRANT -D_FILE_OFFSET_BITS=64 +CPPFLAGS += -I.. -I../../common/inc -D_REENTRANT -D_FILE_OFFSET_BITS=64 .KEEP_STATE: diff --git a/usr/src/lib/libcmdutils/common/mapfile-vers b/usr/src/lib/libcmdutils/common/mapfile-vers index 48d26ca9afb1..295493845bc8 100644 --- a/usr/src/lib/libcmdutils/common/mapfile-vers +++ b/usr/src/lib/libcmdutils/common/mapfile-vers @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -30,6 +30,12 @@ SUNWprivate_1.1 { add_tnode; destroy_tree; tnode_compare; + sysattr_type; + sysattr_support; + writefile; + get_attrdirs; + mv_xattrs; + sysattr_list; local: *; }; diff --git a/usr/src/lib/libcmdutils/common/process_xattrs.c b/usr/src/lib/libcmdutils/common/process_xattrs.c new file mode 100644 index 000000000000..5c8df946a178 --- /dev/null +++ b/usr/src/lib/libcmdutils/common/process_xattrs.c @@ -0,0 +1,334 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include "libcmdutils.h" + + +/* + * Gets file descriptors of attribute directories for source and target + * attribute files + */ +int +get_attrdirs(int indfd, int outdfd, char *attrfile, int *sfd, int *tfd) +{ + int pwdfd; + int fd1; + int fd2; + + pwdfd = open(".", O_RDONLY); + if ((pwdfd != -1) && (fchdir(indfd) == 0)) { + if ((fd1 = attropen(attrfile, ".", O_RDONLY)) == -1) { + (void) fchdir(pwdfd); + (void) close(pwdfd); + return (1); + } + *sfd = fd1; + } else { + (void) fchdir(pwdfd); + (void) close(pwdfd); + return (1); + } + if (fchdir(outdfd) == 0) { + if ((fd2 = attropen(attrfile, ".", O_RDONLY)) == -1) { + (void) fchdir(pwdfd); + (void) close(pwdfd); + return (1); + } + *tfd = fd2; + } else { + (void) fchdir(pwdfd); + (void) close(pwdfd); + return (1); + } + (void) fchdir(pwdfd); + return (0); +} + +/* + * mv_xattrs - Copies the content of the extended attribute files. Then + * moves the extended system attributes from the input attribute files + * to the target attribute files. Moves the extended system attributes + * from source to the target file. This function returns 0 on success + * and nonzero on error. + */ +int +mv_xattrs(char *cmd, char *infile, char *outfile, int sattr, int silent) +{ + int srcfd = -1; + int indfd = -1; + int outdfd = -1; + int tmpfd = -1; + int sattrfd = -1; + int tattrfd = -1; + int asfd = -1; + int atfd = -1; + DIR *dirp = NULL; + struct dirent *dp = NULL; + char *etext = NULL; + struct stat st1; + struct stat st2; + nvlist_t *response = NULL; + nvlist_t *res = NULL; + + if ((srcfd = open(infile, O_RDONLY)) == -1) { + etext = dgettext(TEXT_DOMAIN, "cannot open source"); + goto error; + } + if (sattr) + response = sysattr_list(cmd, srcfd, infile); + + if ((indfd = openat(srcfd, ".", O_RDONLY|O_XATTR)) == -1) { + etext = dgettext(TEXT_DOMAIN, "cannot openat source"); + goto error; + } + if ((outdfd = attropen(outfile, ".", O_RDONLY)) == -1) { + etext = dgettext(TEXT_DOMAIN, "cannot attropen target"); + goto error; + } + if ((tmpfd = dup(indfd)) == -1) { + etext = dgettext(TEXT_DOMAIN, "cannot dup descriptor"); + goto error; + + } + if ((dirp = fdopendir(tmpfd)) == NULL) { + etext = dgettext(TEXT_DOMAIN, "cannot access source"); + goto error; + } + while ((dp = readdir(dirp)) != NULL) { + if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') || + (dp->d_name[0] == '.' && dp->d_name[1] == '.' && + dp->d_name[2] == '\0') || + (sysattr_type(dp->d_name) == _RO_SATTR) || + (sysattr_type(dp->d_name) == _RW_SATTR)) + continue; + + if ((sattrfd = openat(indfd, dp->d_name, + O_RDONLY)) == -1) { + etext = dgettext(TEXT_DOMAIN, + "cannot open src attribute file"); + goto error; + } + if (fstat(sattrfd, &st1) < 0) { + etext = dgettext(TEXT_DOMAIN, + "could not stat attribute file"); + goto error; + } + if ((tattrfd = openat(outdfd, dp->d_name, + O_RDWR|O_CREAT|O_TRUNC, st1.st_mode)) == -1) { + etext = dgettext(TEXT_DOMAIN, + "cannot open target attribute file"); + goto error; + } + if (fstat(tattrfd, &st2) < 0) { + etext = dgettext(TEXT_DOMAIN, + "could not stat attribute file"); + goto error; + } + if (writefile(sattrfd, tattrfd, infile, outfile, dp->d_name, + dp->d_name, &st1, &st2) != 0) { + etext = dgettext(TEXT_DOMAIN, + "failed to copy extended attribute " + "from source to target"); + goto error; + } + + errno = 0; + if (sattr) { + /* + * Gets non default extended system attributes from + * source to copy to target. + */ + if (dp->d_name != NULL) + res = sysattr_list(cmd, sattrfd, dp->d_name); + + if (res != NULL && + get_attrdirs(indfd, outdfd, dp->d_name, &asfd, + &atfd) != 0) { + etext = dgettext(TEXT_DOMAIN, + "Failed to open attribute files"); + goto error; + } + /* + * Copy extended system attribute from source + * attribute file to target attribute file + */ + if (res != NULL && + (renameat(asfd, VIEW_READWRITE, atfd, + VIEW_READWRITE) != 0)) { + if (errno == EPERM) + etext = dgettext(TEXT_DOMAIN, + "Permission denied -" + "failed to move system attribute"); + else + etext = dgettext(TEXT_DOMAIN, + "failed to move extended " + "system attribute"); + goto error; + } + } + if (sattrfd != -1) + (void) close(sattrfd); + if (tattrfd != -1) + (void) close(tattrfd); + if (asfd != -1) + (void) close(asfd); + if (atfd != -1) + (void) close(atfd); + if (res != NULL) { + nvlist_free(res); + res = NULL; + } + } + errno = 0; + /* Copy extended system attribute from source to target */ + + if (response != NULL) { + if (renameat(indfd, VIEW_READWRITE, outdfd, + VIEW_READWRITE) == 0) + goto done; + + if (errno == EPERM) + etext = dgettext(TEXT_DOMAIN, "Permission denied"); + else + etext = dgettext(TEXT_DOMAIN, + "failed to move system attribute"); + } +error: + if (res != NULL) + nvlist_free(res); + if (silent == 0 && etext != NULL) { + if (!sattr) + (void) fprintf(stderr, dgettext(TEXT_DOMAIN, + "%s: %s: cannot move extended attributes, "), + cmd, infile); + else + (void) fprintf(stderr, dgettext(TEXT_DOMAIN, + "%s: %s: cannot move extended system " + "attributes, "), cmd, infile); + perror(etext); + } +done: + if (dirp) + (void) closedir(dirp); + if (sattrfd != -1) + (void) close(sattrfd); + if (tattrfd != -1) + (void) close(tattrfd); + if (asfd != -1) + (void) close(asfd); + if (atfd != -1) + (void) close(atfd); + if (indfd != -1) + (void) close(indfd); + if (outdfd != -1) + (void) close(outdfd); + if (response != NULL) + nvlist_free(response); + if (etext != NULL) + return (1); + else + return (0); +} + +/* + * The function returns non default extended system attribute list + * associated with 'fname' and returns NULL when an error has occured + * or when only extended system attributes other than archive, + * av_modified or crtime are set. + * + * The function returns system attribute list for the following cases: + * + * - any extended system attribute other than the default attributes + * ('archive', 'av_modified' and 'crtime') is set + * - nvlist has NULL name string + * - nvpair has data type of 'nvlist' + * - default data type. + */ + +nvlist_t * +sysattr_list(char *cmd, int fd, char *fname) +{ + boolean_t value; + data_type_t type; + nvlist_t *response; + nvpair_t *pair; + f_attr_t fattr; + char *name; + + if (nvlist_alloc(&response, NV_UNIQUE_NAME, 0) != 0) { + (void) fprintf(stderr, dgettext(TEXT_DOMAIN, + "%s: %s: nvlist_alloc failed\n"), + cmd, fname); + return (NULL); + } + if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) { + (void) fprintf(stderr, dgettext(TEXT_DOMAIN, + "%s: %s: fgetattr failed\n"), + cmd, fname); + nvlist_free(response); + return (NULL); + } + pair = NULL; + while ((pair = nvlist_next_nvpair(response, pair)) != NULL) { + + name = nvpair_name(pair); + + if (name != NULL) + fattr = name_to_attr(name); + else + return (response); + + type = nvpair_type(pair); + switch (type) { + case DATA_TYPE_BOOLEAN_VALUE: + if (nvpair_value_boolean_value(pair, + &value) != 0) { + (void) fprintf(stderr, + dgettext(TEXT_DOMAIN, "%s " + "nvpair_value_boolean_value " + "failed\n"), cmd); + continue; + } + if (value && fattr != F_ARCHIVE && + fattr != F_AV_MODIFIED) + return (response); + break; + case DATA_TYPE_UINT64_ARRAY: + if (fattr != F_CRTIME) + return (response); + break; + case DATA_TYPE_NVLIST: + default: + return (response); + break; + } + } + if (response != NULL) + nvlist_free(response); + return (NULL); +} diff --git a/usr/src/lib/libcmdutils/common/sysattrs.c b/usr/src/lib/libcmdutils/common/sysattrs.c new file mode 100644 index 000000000000..a22e8e5e21bc --- /dev/null +++ b/usr/src/lib/libcmdutils/common/sysattrs.c @@ -0,0 +1,128 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "libcmdutils.h" + +/* + * Returns the status of attempting to obtain the extended system + * attributes in the specified view. + * + * Note: If obtaining status for an extended attribute file, the caller must + * chdir into the hidden directory prior to calling sysattr_status(). + * + * Returns 1 if the extended system attributes were obtained, otherwise + * returns 0. + */ +int +sysattr_status(char *file, xattr_view_t view) +{ + nvlist_t *response; + int saveerrno; + int status; + + if (nvlist_alloc(&response, NV_UNIQUE_NAME, 0) != 0) { + return (0); + } + + status = getattrat(AT_FDCWD, view, file, &response); + + saveerrno = errno; + (void) nvlist_free(response); + errno = saveerrno; + + return (status == 0); +} + +/* + * Returns the type of the specified in file. If the file name matches + * the name of either a read-only or read-write extended system attribute + * file then sysattr_type() returns the type of file: + * return value file type + * ------------ --------- + * _RO_SATTR read-only extended system attribute file + * _RW_SATTR read-write extended system attribute file + * _NOT_SATTR neither a read-only or read-write extended system + * attribute file. + */ +int +sysattr_type(char *file) +{ + if (file == NULL) { + errno = ENOENT; + return (_NOT_SATTR); + } + + if (strcmp(basename(file), file) != 0) { + errno = EINVAL; + return (_NOT_SATTR); + } + + errno = 0; + if (strcmp(file, VIEW_READONLY) == 0) { + return (_RO_SATTR); + } else if (strcmp(file, VIEW_READWRITE) == 0) { + return (_RW_SATTR); + } else { + return (_NOT_SATTR); + } +} + +/* + * Call sysattr_support() instead of pathconf(file, _PC_SATTR_ENABLED) or + * pathconf(file, _PC_SATTR_EXISTS) so that if pathconf() fails over NFS, we + * can still try to figure out if extended system attributes are supported by + * testing for a valid extended system attribute file. + * + * 'name' can have the values _PC_SATTR_ENABLED or _PC_SATTR_EXISTS. + * + * Returns 1 if the underlying file system supports extended system attributes, + * otherwise, returns -1. + */ +int +sysattr_support(char *file, int name) +{ + int rc; + + errno = 0; + if ((name != _PC_SATTR_ENABLED) && + (name != _PC_SATTR_EXISTS)) { + errno = EINVAL; + return (-1); + } + if (((rc = pathconf(file, name)) == 1) || (errno != EINVAL)) { + return (rc); + } + return (sysattr_status(file, XATTR_VIEW_READONLY)); +} diff --git a/usr/src/lib/libcmdutils/common/writefile.c b/usr/src/lib/libcmdutils/common/writefile.c new file mode 100644 index 000000000000..3e36f90c610c --- /dev/null +++ b/usr/src/lib/libcmdutils/common/writefile.c @@ -0,0 +1,230 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * University Copyright- Copyright (c) 1982, 1986, 1988 + * The Regents of the University of California + * All Rights Reserved + * + * University Acknowledgment- Portions of this document are derived from + * software developed by the University of California, Berkeley, and its + * contributors. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include "libcmdutils.h" + + +int +writefile(int fi, int fo, char *infile, char *outfile, char *asfile, + char *atfile, struct stat *s1p, struct stat *s2p) +{ + int mapsize, munmapsize; + caddr_t cp; + off_t filesize = s1p->st_size; + off_t offset; + int nbytes; + int remains; + int n; + size_t src_size; + size_t targ_size; + char *srcbuf; + char *targbuf; + + if (asfile != NULL) { + src_size = strlen(infile) + strlen(asfile) + + strlen(dgettext(TEXT_DOMAIN, " attribute ")) + 1; + } else { + src_size = strlen(infile) + 1; + } + srcbuf = malloc(src_size); + if (srcbuf == NULL) { + (void) fprintf(stderr, + dgettext(TEXT_DOMAIN, "could not allocate memory" + " for path buffer: ")); + return (1); + } + if (asfile != NULL) { + (void) snprintf(srcbuf, src_size, "%s%s%s", + infile, dgettext(TEXT_DOMAIN, " attribute "), asfile); + } else { + (void) snprintf(srcbuf, src_size, "%s", infile); + } + + if (atfile != NULL) { + targ_size = strlen(outfile) + strlen(atfile) + + strlen(dgettext(TEXT_DOMAIN, " attribute ")) + 1; + } else { + targ_size = strlen(outfile) + 1; + } + targbuf = malloc(targ_size); + if (targbuf == NULL) { + (void) fprintf(stderr, + dgettext(TEXT_DOMAIN, "could not allocate memory" + " for path buffer: ")); + return (1); + } + if (atfile != NULL) { + (void) snprintf(targbuf, targ_size, "%s%s%s", + outfile, dgettext(TEXT_DOMAIN, " attribute "), atfile); + } else { + (void) snprintf(targbuf, targ_size, "%s", outfile); + } + + if (ISREG(*s1p) && s1p->st_size > SMALLFILESIZE) { + /* + * Determine size of initial mapping. This will determine the + * size of the address space chunk we work with. This initial + * mapping size will be used to perform munmap() in the future. + */ + mapsize = MAXMAPSIZE; + if (s1p->st_size < mapsize) mapsize = s1p->st_size; + munmapsize = mapsize; + + /* + * Mmap time! + */ + if ((cp = mmap((caddr_t)NULL, mapsize, PROT_READ, + MAP_SHARED, fi, (off_t)0)) == MAP_FAILED) + mapsize = 0; /* can't mmap today */ + } else + mapsize = 0; + + if (mapsize != 0) { + offset = 0; + + for (;;) { + nbytes = write(fo, cp, mapsize); + /* + * if we write less than the mmaped size it's due to a + * media error on the input file or out of space on + * the output file. So, try again, and look for errno. + */ + if ((nbytes >= 0) && (nbytes != (int)mapsize)) { + remains = mapsize - nbytes; + while (remains > 0) { + nbytes = write(fo, + cp + mapsize - remains, remains); + if (nbytes < 0) { + if (errno == ENOSPC) + perror(targbuf); + else + perror(srcbuf); + (void) close(fi); + (void) close(fo); + (void) munmap(cp, munmapsize); + if (ISREG(*s2p)) + (void) unlink(targbuf); + return (1); + } + remains -= nbytes; + if (remains == 0) + nbytes = mapsize; + } + } + /* + * although the write manual page doesn't specify this + * as a possible errno, it is set when the nfs read + * via the mmap'ed file is accessed, so report the + * problem as a source access problem, not a target file + * problem + */ + if (nbytes < 0) { + if (errno == EACCES) + perror(srcbuf); + else + perror(targbuf); + (void) close(fi); + (void) close(fo); + (void) munmap(cp, munmapsize); + if (ISREG(*s2p)) + (void) unlink(targbuf); + if (srcbuf != NULL) + free(srcbuf); + if (targbuf != NULL) + free(targbuf); + return (1); + } + filesize -= nbytes; + if (filesize == 0) + break; + offset += nbytes; + if (filesize < mapsize) + mapsize = filesize; + if (mmap(cp, mapsize, PROT_READ, MAP_SHARED | + MAP_FIXED, fi, offset) == MAP_FAILED) { + perror(srcbuf); + (void) close(fi); + (void) close(fo); + (void) munmap(cp, munmapsize); + if (ISREG(*s2p)) + (void) unlink(targbuf); + if (srcbuf != NULL) + free(srcbuf); + if (targbuf != NULL) + free(targbuf); + return (1); + } + } + (void) munmap(cp, munmapsize); + } else { + char buf[SMALLFILESIZE]; + for (;;) { + n = read(fi, buf, sizeof (buf)); + if (n == 0) { + return (0); + } else if (n < 0) { + (void) close(fi); + (void) close(fo); + if (ISREG(*s2p)) + (void) unlink(targbuf); + if (srcbuf != NULL) + free(srcbuf); + if (targbuf != NULL) + free(targbuf); + return (1); + } else if (write(fo, buf, n) != n) { + (void) close(fi); + (void) close(fo); + if (ISREG(*s2p)) + (void) unlink(targbuf); + if (srcbuf != NULL) + free(srcbuf); + if (targbuf != NULL) + free(targbuf); + return (1); + } + } + } + if (srcbuf != NULL) + free(srcbuf); + if (targbuf != NULL) + free(targbuf); + return (0); +} diff --git a/usr/src/lib/libcmdutils/libcmdutils.h b/usr/src/lib/libcmdutils/libcmdutils.h index 28a9e996d421..d1c3a0b19355 100644 --- a/usr/src/lib/libcmdutils/libcmdutils.h +++ b/usr/src/lib/libcmdutils/libcmdutils.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -38,14 +37,35 @@ * this file. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include +#include +#include #ifdef __cplusplus extern "C" { #endif +/* extended system attribute support */ +#define _NOT_SATTR 0 +#define _RO_SATTR 1 +#define _RW_SATTR 2 + +#define MAXMAPSIZE (1024*1024*8) /* map at most 8MB */ +#define SMALLFILESIZE (32*1024) /* don't use mmap on little file */ +#define ISREG(A) (((A).st_mode & S_IFMT) == S_IFREG) + /* avltree */ #define OFFSETOF(s, m) ((size_t)(&(((s *)0)->m))) @@ -56,11 +76,38 @@ typedef struct tree_node { avl_node_t avl_link; } tree_node_t; + + /* extended system attribute support */ + +/* Determine if a file is the name of an extended system attribute file */ +extern int sysattr_type(char *); + +/* Determine if the underlying file system supports system attributes */ +extern int sysattr_support(char *, int); + +/* Copies the content of the source file to the target file */ +extern int writefile(int, int, char *, char *, char *, char *, +struct stat *, struct stat *); + +/* Gets file descriptors of the source and target attribute files */ +extern int get_attrdirs(int, int, char *, int *, int *); + +/* Move extended attribute and extended system attribute */ +extern int mv_xattrs(char *, char *, char *, int, int); + +/* Returns non default extended system attribute list */ +extern nvlist_t *sysattr_list(char *, int, char *); + + + + /* avltree */ + /* * Used to compare two nodes. We are attempting to match the 1st * argument (node) against the 2nd argument (a node which * is already in the search tree). */ + extern int tnode_compare(const void *, const void *); /* diff --git a/usr/src/lib/libsec/common/acl.y b/usr/src/lib/libsec/common/acl.y index 684ab0ba3df0..8b5d29509e98 100644 --- a/usr/src/lib/libsec/common/acl.y +++ b/usr/src/lib/libsec/common/acl.y @@ -19,13 +19,13 @@ * * CDDL HEADER END * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" -#include +#include #include extern int yyinteractive; diff --git a/usr/src/lib/libsec/common/acl_lex.l b/usr/src/lib/libsec/common/acl_lex.l index ce08a1da2854..890c063cc740 100644 --- a/usr/src/lib/libsec/common/acl_lex.l +++ b/usr/src/lib/libsec/common/acl_lex.l @@ -59,6 +59,7 @@ int yybufpos; %} +%e 1500 %s TS NS PS AIS AS US ES /* * TS = type state @@ -73,7 +74,7 @@ int yybufpos; ID [0-9]+ LOGNAME [^:]+: PERM_STR [rRwWxpdDaAcCos-]+ -INHERIT_STR [fdinFS-]+ +INHERIT_STR [fdinFSI-]+ %% @@ -398,6 +399,32 @@ INHERIT_STR [fdinFS-]+ yylval.val = ACE_ACCESS_DENIED_ACE_TYPE; return (ACCESS_TYPE); } +audit/[:,\n] { + int c; + + c = input(); + unput(c); + if (c == ',' || c == '\n') + BEGIN ES; + else + BEGIN US; + + yylval.val = ACE_SYSTEM_AUDIT_ACE_TYPE; + return (ACCESS_TYPE); + } +alarm/[:,\n] { + int c; + + c = input(); + unput(c); + if (c == ',' || c == '\n') + BEGIN ES; + else + BEGIN US; + + yylval.val = ACE_SYSTEM_ALARM_ACE_TYPE; + return (ACCESS_TYPE); + } : { acl_error(dgettext(TEXT_DOMAIN, @@ -466,6 +493,33 @@ INHERIT_STR [fdinFS-]+ yylval.val = ACE_ACCESS_DENIED_ACE_TYPE; return (ACCESS_TYPE); } +audit/[:,\n] { + int c; + + c = input(); + unput(c); + if (c == ',' || c == '\n') + BEGIN ES; + else + BEGIN US; + + yylval.val = ACE_SYSTEM_AUDIT_ACE_TYPE; + return (ACCESS_TYPE); + } +alarm/[:,\n] { + + int c; + + c = input(); + unput(c); + if (c == ',' || c == '\n') + BEGIN ES; + else + BEGIN US; + + yylval.val = ACE_SYSTEM_ALARM_ACE_TYPE; + return (ACCESS_TYPE); + } file_inherit/[:/,] { yylval.val = ACE_FILE_INHERIT_ACE; return (ACE_INHERIT); @@ -482,6 +536,19 @@ INHERIT_STR [fdinFS-]+ yylval.val = ACE_INHERIT_ONLY_ACE; return (ACE_INHERIT); } + +successful_access/[/:,] { + yylval.val = ACE_SUCCESSFUL_ACCESS_ACE_FLAG; + return (ACE_INHERIT); + } +failed_access/[/:,] { + yylval.val = ACE_FAILED_ACCESS_ACE_FLAG; + return (ACE_INHERIT); + } +inherited/[/:,] { + yylval.val = ACE_INHERITED_ACE; + return (ACE_INHERIT); + } {INHERIT_STR}/[:] { yylval.str = strdup(yytext); if (yylval.str == NULL) { @@ -625,7 +692,7 @@ INHERIT_STR [fdinFS-]+ * used for retrieving illegal data in ACL specification. * * The first set of characters is retrieved from yytext. - * subseequent characters are pulled from the input stream, + * subsequent characters are pulled from the input stream, * until either EOF or one of the requested terminators is scene. * Result is returned in yylval.str which is malloced. */ @@ -783,7 +850,7 @@ acl_str_to_id(char *str, int *id) uid_t value; errno = 0; - value = strtol(str, &end, 10); + value = strtoul(str, &end, 10); if (errno != 0 || *end != '\0') return (EACL_INVALID_USER_GROUP); diff --git a/usr/src/lib/libsec/common/acltext.c b/usr/src/lib/libsec/common/acltext.c index cdfd171c8290..c0e1bb1e58b7 100644 --- a/usr/src/lib/libsec/common/acltext.c +++ b/usr/src/lib/libsec/common/acltext.c @@ -540,8 +540,12 @@ ace_inherit_txt(char *buf, char **endp, uint32_t iflags, int flags) buf[5] = 'F'; else buf[5] = '-'; - buf[6] = '\0'; - *endp = buf + 6; + if (iflags & ACE_INHERITED_ACE) + buf[6] = 'I'; + else + buf[6] = '-'; + buf[7] = '\0'; + *endp = buf + 7; } else { if (iflags & ACE_FILE_INHERIT_ACE) { strcpy(lend, "file_inherit/"); @@ -559,6 +563,18 @@ ace_inherit_txt(char *buf, char **endp, uint32_t iflags, int flags) strcpy(lend, "inherit_only/"); lend += sizeof ("inherit_only/") - 1; } + if (iflags & ACE_SUCCESSFUL_ACCESS_ACE_FLAG) { + strcpy(lend, "successful_access/"); + lend += sizeof ("successful_access/") - 1; + } + if (iflags & ACE_FAILED_ACCESS_ACE_FLAG) { + strcpy(lend, "failed_access/"); + lend += sizeof ("failed_access/") - 1; + } + if (iflags & ACE_INHERITED_ACE) { + strcpy(lend, "inherited/"); + lend += sizeof ("inherited/") - 1; + } if (*(lend - 1) == '/') *--lend = '\0'; @@ -829,16 +845,19 @@ increase_length(struct dynaclstr *dacl, size_t increase) * The length of a perms entry is 144 i.e read_data/write_data... * to each acl entry. * - * iflags: file_inherit/dir_inherit/inherit_only/no_propagate + * iflags: file_inherit/dir_inherit/inherit_only/no_propagate/successful_access + * /failed_access * */ #define ACE_ENTRYTYPLEN 6 -#define IFLAGS_SIZE 51 +#define IFLAGS_STR "file_inherit/dir_inherit/inherit_only/no_propagate/" \ + "successful_access/failed_access/inherited" +#define IFLAGS_SIZE (sizeof (IFLAGS_STR) - 1) #define ACCESS_TYPE_SIZE 7 /* if unknown */ #define COLON_CNT 3 #define PERMS_LEN 216 -#define ACE_ENTRY_SIZE (ACE_ENTRYTYPLEN + ID_STR_MAX + PERMS_LEN +\ +#define ACE_ENTRY_SIZE (ACE_ENTRYTYPLEN + ID_STR_MAX + PERMS_LEN + \ ACCESS_TYPE_SIZE + IFLAGS_SIZE + COLON_CNT + APPENDED_ID_MAX) static char * @@ -871,7 +890,9 @@ ace_acltotext(acl_t *aceaclp, int flags) (void) ace_inherit_txt(endp, &endp, aclp->a_flags, flags); if (flags & ACL_COMPACT_FMT || aclp->a_flags & (ACE_FILE_INHERIT_ACE | ACE_DIRECTORY_INHERIT_ACE | - (ACE_INHERIT_ONLY_ACE | ACE_NO_PROPAGATE_INHERIT_ACE))) { + (ACE_INHERIT_ONLY_ACE | ACE_NO_PROPAGATE_INHERIT_ACE | + ACE_INHERITED_ACE | ACE_SUCCESSFUL_ACCESS_ACE_FLAG | + ACE_FAILED_ACCESS_ACE_FLAG))) { *endp++ = ':'; *endp = '\0'; } @@ -972,7 +993,7 @@ ace_compact_printacl(acl_t *aclp) aclp->acl_flags & ACL_IS_DIR, ACL_COMPACT_FMT)); (void) printf("%s:", ace_inherit_txt(endp, &endp, acep->a_flags, - ACL_COMPACT_FMT)); + ACL_COMPACT_FMT)); (void) printf("%s\n", ace_access_txt(endp, &endp, acep->a_type)); } @@ -1038,16 +1059,14 @@ typedef struct value_table { uint32_t p_value; /* value for perm when pletter found */ } value_table_t; -#define ACE_PERM_COUNT 14 - /* - * The permission tables are layed out in positional order + * The permission tables are laid out in positional order * a '-' character will indicate a permission at a given * position is not specified. The '-' is not part of the * table, but will be checked for in the permission computation * routine. */ -value_table_t ace_perm_table[ACE_PERM_COUNT] = { +value_table_t ace_perm_table[] = { { 'r', ACE_READ_DATA}, { 'w', ACE_WRITE_DATA}, { 'x', ACE_EXECUTE}, @@ -1064,24 +1083,28 @@ value_table_t ace_perm_table[ACE_PERM_COUNT] = { { 's', ACE_SYNCHRONIZE} }; -#define ACLENT_PERM_COUNT 3 +#define ACE_PERM_COUNT (sizeof (ace_perm_table) / sizeof (value_table_t)) -value_table_t aclent_perm_table[ACLENT_PERM_COUNT] = { +value_table_t aclent_perm_table[] = { { 'r', S_IROTH}, { 'w', S_IWOTH}, { 'x', S_IXOTH} }; -#define IFLAG_COUNT 6 -value_table_t inherit_table[IFLAG_COUNT] = { +#define ACLENT_PERM_COUNT (sizeof (aclent_perm_table) / sizeof (value_table_t)) + +value_table_t inherit_table[] = { {'f', ACE_FILE_INHERIT_ACE}, {'d', ACE_DIRECTORY_INHERIT_ACE}, {'i', ACE_INHERIT_ONLY_ACE}, {'n', ACE_NO_PROPAGATE_INHERIT_ACE}, {'S', ACE_SUCCESSFUL_ACCESS_ACE_FLAG}, - {'F', ACE_FAILED_ACCESS_ACE_FLAG} + {'F', ACE_FAILED_ACCESS_ACE_FLAG}, + {'I', ACE_INHERITED_ACE} }; +#define IFLAG_COUNT (sizeof (inherit_table) / sizeof (value_table_t)) + /* * compute value from a permission table or inheritance table * based on string passed in. If positional is set then diff --git a/usr/src/lib/libsec/common/aclutils.c b/usr/src/lib/libsec/common/aclutils.c index d90ad4b17185..0200db6c6621 100644 --- a/usr/src/lib/libsec/common/aclutils.c +++ b/usr/src/lib/libsec/common/aclutils.c @@ -33,171 +33,23 @@ #include #include #include -#include #include #include #include #include #include -#include #include - -#define offsetof(s, m) ((size_t)(&(((s *)0)->m))) +#include #define ACL_PATH 0 #define ACL_FD 1 -#define ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \ - ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \ - ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL) - - -#define ACL_SYNCHRONIZE_SET_DENY 0x0000001 -#define ACL_SYNCHRONIZE_SET_ALLOW 0x0000002 -#define ACL_SYNCHRONIZE_ERR_DENY 0x0000004 -#define ACL_SYNCHRONIZE_ERR_ALLOW 0x0000008 - -#define ACL_WRITE_OWNER_SET_DENY 0x0000010 -#define ACL_WRITE_OWNER_SET_ALLOW 0x0000020 -#define ACL_WRITE_OWNER_ERR_DENY 0x0000040 -#define ACL_WRITE_OWNER_ERR_ALLOW 0x0000080 - -#define ACL_DELETE_SET_DENY 0x0000100 -#define ACL_DELETE_SET_ALLOW 0x0000200 -#define ACL_DELETE_ERR_DENY 0x0000400 -#define ACL_DELETE_ERR_ALLOW 0x0000800 - -#define ACL_WRITE_ATTRS_OWNER_SET_DENY 0x0001000 -#define ACL_WRITE_ATTRS_OWNER_SET_ALLOW 0x0002000 -#define ACL_WRITE_ATTRS_OWNER_ERR_DENY 0x0004000 -#define ACL_WRITE_ATTRS_OWNER_ERR_ALLOW 0x0008000 - -#define ACL_WRITE_ATTRS_WRITER_SET_DENY 0x0010000 -#define ACL_WRITE_ATTRS_WRITER_SET_ALLOW 0x0020000 -#define ACL_WRITE_ATTRS_WRITER_ERR_DENY 0x0040000 -#define ACL_WRITE_ATTRS_WRITER_ERR_ALLOW 0x0080000 - -#define ACL_WRITE_NAMED_WRITER_SET_DENY 0x0100000 -#define ACL_WRITE_NAMED_WRITER_SET_ALLOW 0x0200000 -#define ACL_WRITE_NAMED_WRITER_ERR_DENY 0x0400000 -#define ACL_WRITE_NAMED_WRITER_ERR_ALLOW 0x0800000 - -#define ACL_READ_NAMED_READER_SET_DENY 0x1000000 -#define ACL_READ_NAMED_READER_SET_ALLOW 0x2000000 -#define ACL_READ_NAMED_READER_ERR_DENY 0x4000000 -#define ACL_READ_NAMED_READER_ERR_ALLOW 0x8000000 - - -#define ACE_VALID_MASK_BITS (\ - ACE_READ_DATA | \ - ACE_LIST_DIRECTORY | \ - ACE_WRITE_DATA | \ - ACE_ADD_FILE | \ - ACE_APPEND_DATA | \ - ACE_ADD_SUBDIRECTORY | \ - ACE_READ_NAMED_ATTRS | \ - ACE_WRITE_NAMED_ATTRS | \ - ACE_EXECUTE | \ - ACE_DELETE_CHILD | \ - ACE_READ_ATTRIBUTES | \ - ACE_WRITE_ATTRIBUTES | \ - ACE_DELETE | \ - ACE_READ_ACL | \ - ACE_WRITE_ACL | \ - ACE_WRITE_OWNER | \ - ACE_SYNCHRONIZE) - -#define ACE_MASK_UNDEFINED 0x80000000 - -#define ACE_VALID_FLAG_BITS (ACE_FILE_INHERIT_ACE | \ - ACE_DIRECTORY_INHERIT_ACE | \ - ACE_NO_PROPAGATE_INHERIT_ACE | ACE_INHERIT_ONLY_ACE | \ - ACE_SUCCESSFUL_ACCESS_ACE_FLAG | ACE_FAILED_ACCESS_ACE_FLAG | \ - ACE_IDENTIFIER_GROUP | ACE_OWNER | ACE_GROUP | ACE_EVERYONE) - -/* - * ACL conversion helpers - */ - -typedef enum { - ace_unused, - ace_user_obj, - ace_user, - ace_group, /* includes GROUP and GROUP_OBJ */ - ace_other_obj -} ace_to_aent_state_t; - -typedef struct acevals { - uid_t key; - avl_node_t avl; - uint32_t mask; - uint32_t allowed; - uint32_t denied; - int aent_type; -} acevals_t; - -typedef struct ace_list { - acevals_t user_obj; - avl_tree_t user; - int numusers; - acevals_t group_obj; - avl_tree_t group; - int numgroups; - acevals_t other_obj; - uint32_t acl_mask; - int hasmask; - int dfacl_flag; - ace_to_aent_state_t state; - int seen; /* bitmask of all aclent_t a_type values seen */ -} ace_list_t; typedef union { const char *file; int fd; } acl_inp; -acl_t * -acl_alloc(enum acl_type type) -{ - acl_t *aclp; - - aclp = malloc(sizeof (acl_t)); - - if (aclp == NULL) - return (NULL); - - aclp->acl_aclp = NULL; - aclp->acl_cnt = 0; - - switch (type) { - case ACE_T: - aclp->acl_type = ACE_T; - aclp->acl_entry_size = sizeof (ace_t); - break; - case ACLENT_T: - aclp->acl_type = ACLENT_T; - aclp->acl_entry_size = sizeof (aclent_t); - break; - default: - acl_free(aclp); - aclp = NULL; - } - return (aclp); -} - -/* - * Free acl_t structure - */ -void -acl_free(acl_t *aclp) -{ - if (aclp == NULL) - return; - - if (aclp->acl_aclp) - free(aclp->acl_aclp); - free(aclp); -} /* * Determine whether a file has a trivial ACL @@ -242,1194 +94,6 @@ acl_trivial(const char *filename) return (val); } -static uint32_t -access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow) -{ - uint32_t access_mask = 0; - int acl_produce; - int synchronize_set = 0, write_owner_set = 0; - int delete_set = 0, write_attrs_set = 0; - int read_named_set = 0, write_named_set = 0; - - acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW | - ACL_WRITE_ATTRS_OWNER_SET_ALLOW | - ACL_WRITE_ATTRS_WRITER_SET_DENY); - - if (isallow) { - synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW; - write_owner_set = ACL_WRITE_OWNER_SET_ALLOW; - delete_set = ACL_DELETE_SET_ALLOW; - if (hasreadperm) - read_named_set = ACL_READ_NAMED_READER_SET_ALLOW; - if (haswriteperm) - write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW; - if (isowner) - write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW; - else if (haswriteperm) - write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW; - } else { - - synchronize_set = ACL_SYNCHRONIZE_SET_DENY; - write_owner_set = ACL_WRITE_OWNER_SET_DENY; - delete_set = ACL_DELETE_SET_DENY; - if (hasreadperm) - read_named_set = ACL_READ_NAMED_READER_SET_DENY; - if (haswriteperm) - write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY; - if (isowner) - write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY; - else if (haswriteperm) - write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY; - else - /* - * If the entity is not the owner and does not - * have write permissions ACE_WRITE_ATTRIBUTES will - * always go in the DENY ACE. - */ - access_mask |= ACE_WRITE_ATTRIBUTES; - } - - if (acl_produce & synchronize_set) - access_mask |= ACE_SYNCHRONIZE; - if (acl_produce & write_owner_set) - access_mask |= ACE_WRITE_OWNER; - if (acl_produce & delete_set) - access_mask |= ACE_DELETE; - if (acl_produce & write_attrs_set) - access_mask |= ACE_WRITE_ATTRIBUTES; - if (acl_produce & read_named_set) - access_mask |= ACE_READ_NAMED_ATTRS; - if (acl_produce & write_named_set) - access_mask |= ACE_WRITE_NAMED_ATTRS; - - return (access_mask); -} - -/* - * Given an mode_t, convert it into an access_mask as used - * by nfsace, assuming aclent_t -> nfsace semantics. - */ -static uint32_t -mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow) -{ - uint32_t access = 0; - int haswriteperm = 0; - int hasreadperm = 0; - - if (isallow) { - haswriteperm = (mode & 02); - hasreadperm = (mode & 04); - } else { - haswriteperm = !(mode & 02); - hasreadperm = !(mode & 04); - } - - /* - * The following call takes care of correctly setting the following - * mask bits in the access_mask: - * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE, - * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS - */ - access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow); - - if (isallow) { - access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES; - if (isowner) - access |= ACE_WRITE_ACL; - } else { - if (! isowner) - access |= ACE_WRITE_ACL; - } - - /* read */ - if (mode & 04) { - access |= ACE_READ_DATA; - } - /* write */ - if (mode & 02) { - access |= ACE_WRITE_DATA | - ACE_APPEND_DATA; - if (isdir) - access |= ACE_DELETE_CHILD; - } - /* exec */ - if (mode & 01) { - access |= ACE_EXECUTE; - } - - return (access); -} - -/* - * Given an nfsace (presumably an ALLOW entry), make a - * corresponding DENY entry at the address given. - */ -static void -ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner) -{ - (void) memcpy(deny, allow, sizeof (ace_t)); - - deny->a_who = allow->a_who; - - deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE; - deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS; - if (isdir) - deny->a_access_mask ^= ACE_DELETE_CHILD; - - deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER | - ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS | - ACE_WRITE_NAMED_ATTRS); - deny->a_access_mask |= access_mask_set((allow->a_access_mask & - ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner, - B_FALSE); -} -/* - * Make an initial pass over an array of aclent_t's. Gather - * information such as an ACL_MASK (if any), number of users, - * number of groups, and whether the array needs to be sorted. - */ -static int -ln_aent_preprocess(aclent_t *aclent, int n, - int *hasmask, mode_t *mask, - int *numuser, int *numgroup, int *needsort) -{ - int error = 0; - int i; - int curtype = 0; - - *hasmask = 0; - *mask = 07; - *needsort = 0; - *numuser = 0; - *numgroup = 0; - - for (i = 0; i < n; i++) { - if (aclent[i].a_type < curtype) - *needsort = 1; - else if (aclent[i].a_type > curtype) - curtype = aclent[i].a_type; - if (aclent[i].a_type & USER) - (*numuser)++; - if (aclent[i].a_type & (GROUP | GROUP_OBJ)) - (*numgroup)++; - if (aclent[i].a_type & CLASS_OBJ) { - if (*hasmask) { - error = EINVAL; - goto out; - } else { - *hasmask = 1; - *mask = aclent[i].a_perm; - } - } - } - - if ((! *hasmask) && (*numuser + *numgroup > 1)) { - error = EINVAL; - goto out; - } - -out: - return (error); -} - -/* - * Convert an array of aclent_t into an array of nfsace entries, - * following POSIX draft -> nfsv4 conversion semantics as outlined in - * the IETF draft. - */ -static int -ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir) -{ - int error = 0; - mode_t mask; - int numuser, numgroup, needsort; - int resultsize = 0; - int i, groupi = 0, skip; - ace_t *acep, *result = NULL; - int hasmask; - - error = ln_aent_preprocess(aclent, n, &hasmask, &mask, - &numuser, &numgroup, &needsort); - if (error != 0) - goto out; - - /* allow + deny for each aclent */ - resultsize = n * 2; - if (hasmask) { - /* - * stick extra deny on the group_obj and on each - * user|group for the mask (the group_obj was added - * into the count for numgroup) - */ - resultsize += numuser + numgroup; - /* ... and don't count the mask itself */ - resultsize -= 2; - } - - /* sort the source if necessary */ - if (needsort) - ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls); - - result = acep = calloc(1, resultsize * sizeof (ace_t)); - if (result == NULL) - goto out; - - for (i = 0; i < n; i++) { - /* - * don't process CLASS_OBJ (mask); mask was grabbed in - * ln_aent_preprocess() - */ - if (aclent[i].a_type & CLASS_OBJ) - continue; - - /* If we need an ACL_MASK emulator, prepend it now */ - if ((hasmask) && - (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) { - acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE; - acep->a_flags = 0; - if (aclent[i].a_type & GROUP_OBJ) { - acep->a_who = (uid_t)-1; - acep->a_flags |= - (ACE_IDENTIFIER_GROUP|ACE_GROUP); - } else if (aclent[i].a_type & USER) { - acep->a_who = aclent[i].a_id; - } else { - acep->a_who = aclent[i].a_id; - acep->a_flags |= ACE_IDENTIFIER_GROUP; - } - if (aclent[i].a_type & ACL_DEFAULT) { - acep->a_flags |= ACE_INHERIT_ONLY_ACE | - ACE_FILE_INHERIT_ACE | - ACE_DIRECTORY_INHERIT_ACE; - } - /* - * Set the access mask for the prepended deny - * ace. To do this, we invert the mask (found - * in ln_aent_preprocess()) then convert it to an - * DENY ace access_mask. - */ - acep->a_access_mask = mode_to_ace_access((mask ^ 07), - isdir, 0, 0); - acep += 1; - } - - /* handle a_perm -> access_mask */ - acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm, - isdir, aclent[i].a_type & USER_OBJ, 1); - - /* emulate a default aclent */ - if (aclent[i].a_type & ACL_DEFAULT) { - acep->a_flags |= ACE_INHERIT_ONLY_ACE | - ACE_FILE_INHERIT_ACE | - ACE_DIRECTORY_INHERIT_ACE; - } - - /* - * handle a_perm and a_id - * - * this must be done last, since it involves the - * corresponding deny aces, which are handled - * differently for each different a_type. - */ - if (aclent[i].a_type & USER_OBJ) { - acep->a_who = (uid_t)-1; - acep->a_flags |= ACE_OWNER; - ace_make_deny(acep, acep + 1, isdir, B_TRUE); - acep += 2; - } else if (aclent[i].a_type & USER) { - acep->a_who = aclent[i].a_id; - ace_make_deny(acep, acep + 1, isdir, B_FALSE); - acep += 2; - } else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) { - if (aclent[i].a_type & GROUP_OBJ) { - acep->a_who = (uid_t)-1; - acep->a_flags |= ACE_GROUP; - } else { - acep->a_who = aclent[i].a_id; - } - acep->a_flags |= ACE_IDENTIFIER_GROUP; - /* - * Set the corresponding deny for the group ace. - * - * The deny aces go after all of the groups, unlike - * everything else, where they immediately follow - * the allow ace. - * - * We calculate "skip", the number of slots to - * skip ahead for the deny ace, here. - * - * The pattern is: - * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3 - * thus, skip is - * (2 * numgroup) - 1 - groupi - * (2 * numgroup) to account for MD + A - * - 1 to account for the fact that we're on the - * access (A), not the mask (MD) - * - groupi to account for the fact that we have - * passed up groupi number of MD's. - */ - skip = (2 * numgroup) - 1 - groupi; - ace_make_deny(acep, acep + skip, isdir, B_FALSE); - /* - * If we just did the last group, skip acep past - * all of the denies; else, just move ahead one. - */ - if (++groupi >= numgroup) - acep += numgroup + 1; - else - acep += 1; - } else if (aclent[i].a_type & OTHER_OBJ) { - acep->a_who = (uid_t)-1; - acep->a_flags |= ACE_EVERYONE; - ace_make_deny(acep, acep + 1, isdir, B_FALSE); - acep += 2; - } else { - error = EINVAL; - goto out; - } - } - - *acepp = result; - *rescount = resultsize; - -out: - if (error != 0) { - if ((result != NULL) && (resultsize > 0)) { - free(result); - } - } - - return (error); -} - -static int -convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir, - ace_t **retacep, int *retacecnt) -{ - ace_t *acep; - ace_t *dfacep; - int acecnt = 0; - int dfacecnt = 0; - int dfaclstart = 0; - int dfaclcnt = 0; - aclent_t *aclp; - int i; - int error; - - ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls); - - for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) { - if (aclp->a_type & ACL_DEFAULT) - break; - } - - if (i < aclcnt) { - dfaclstart = i; - dfaclcnt = aclcnt - i; - } - - if (dfaclcnt && isdir == 0) { - return (-1); - } - - error = ln_aent_to_ace(aclentp, i, &acep, &acecnt, isdir); - if (error) - return (-1); - - if (dfaclcnt) { - error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt, - &dfacep, &dfacecnt, isdir); - if (error) { - if (acep) { - free(acep); - } - return (-1); - } - } - - if (dfacecnt != 0) { - acep = realloc(acep, sizeof (ace_t) * (acecnt + dfacecnt)); - if (acep == NULL) - return (-1); - if (dfaclcnt) { - (void) memcpy(acep + acecnt, dfacep, - sizeof (ace_t) * dfacecnt); - } - } - if (dfaclcnt) - free(dfacep); - - *retacecnt = acecnt + dfacecnt; - *retacep = acep; - return (0); -} - -static void -acevals_init(acevals_t *vals, uid_t key) -{ - bzero(vals, sizeof (*vals)); - vals->allowed = ACE_MASK_UNDEFINED; - vals->denied = ACE_MASK_UNDEFINED; - vals->mask = ACE_MASK_UNDEFINED; - vals->key = key; -} - -static void -ace_list_init(ace_list_t *al, int dfacl_flag) -{ - acevals_init(&al->user_obj, NULL); - acevals_init(&al->group_obj, NULL); - acevals_init(&al->other_obj, NULL); - al->numusers = 0; - al->numgroups = 0; - al->acl_mask = 0; - al->hasmask = 0; - al->state = ace_unused; - al->seen = 0; - al->dfacl_flag = dfacl_flag; -} - -/* - * Find or create an acevals holder for a given id and avl tree. - * - * Note that only one thread will ever touch these avl trees, so - * there is no need for locking. - */ -static acevals_t * -acevals_find(ace_t *ace, avl_tree_t *avl, int *num) -{ - acevals_t key, *rc; - avl_index_t where; - - key.key = ace->a_who; - rc = avl_find(avl, &key, &where); - if (rc != NULL) - return (rc); - - /* this memory is freed by ln_ace_to_aent()->ace_list_free() */ - rc = calloc(1, sizeof (acevals_t)); - if (rc == NULL) - return (rc); - acevals_init(rc, ace->a_who); - avl_insert(avl, rc, where); - (*num)++; - - return (rc); -} - -static int -access_mask_check(ace_t *acep, int mask_bit, int isowner) -{ - int set_deny, err_deny; - int set_allow, err_allow; - int acl_consume; - int haswriteperm, hasreadperm; - - if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) { - haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 0 : 1; - hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 0 : 1; - } else { - haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 1 : 0; - hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 1 : 0; - } - - acl_consume = (ACL_SYNCHRONIZE_ERR_DENY | - ACL_DELETE_ERR_DENY | - ACL_WRITE_OWNER_ERR_DENY | - ACL_WRITE_OWNER_ERR_ALLOW | - ACL_WRITE_ATTRS_OWNER_SET_ALLOW | - ACL_WRITE_ATTRS_OWNER_ERR_DENY | - ACL_WRITE_ATTRS_WRITER_SET_DENY | - ACL_WRITE_ATTRS_WRITER_ERR_ALLOW | - ACL_WRITE_NAMED_WRITER_ERR_DENY | - ACL_READ_NAMED_READER_ERR_DENY); - - if (mask_bit == ACE_SYNCHRONIZE) { - set_deny = ACL_SYNCHRONIZE_SET_DENY; - err_deny = ACL_SYNCHRONIZE_ERR_DENY; - set_allow = ACL_SYNCHRONIZE_SET_ALLOW; - err_allow = ACL_SYNCHRONIZE_ERR_ALLOW; - } else if (mask_bit == ACE_WRITE_OWNER) { - set_deny = ACL_WRITE_OWNER_SET_DENY; - err_deny = ACL_WRITE_OWNER_ERR_DENY; - set_allow = ACL_WRITE_OWNER_SET_ALLOW; - err_allow = ACL_WRITE_OWNER_ERR_ALLOW; - } else if (mask_bit == ACE_DELETE) { - set_deny = ACL_DELETE_SET_DENY; - err_deny = ACL_DELETE_ERR_DENY; - set_allow = ACL_DELETE_SET_ALLOW; - err_allow = ACL_DELETE_ERR_ALLOW; - } else if (mask_bit == ACE_WRITE_ATTRIBUTES) { - if (isowner) { - set_deny = ACL_WRITE_ATTRS_OWNER_SET_DENY; - err_deny = ACL_WRITE_ATTRS_OWNER_ERR_DENY; - set_allow = ACL_WRITE_ATTRS_OWNER_SET_ALLOW; - err_allow = ACL_WRITE_ATTRS_OWNER_ERR_ALLOW; - } else if (haswriteperm) { - set_deny = ACL_WRITE_ATTRS_WRITER_SET_DENY; - err_deny = ACL_WRITE_ATTRS_WRITER_ERR_DENY; - set_allow = ACL_WRITE_ATTRS_WRITER_SET_ALLOW; - err_allow = ACL_WRITE_ATTRS_WRITER_ERR_ALLOW; - } else { - if ((acep->a_access_mask & mask_bit) && - (acep->a_type & ACE_ACCESS_ALLOWED_ACE_TYPE)) { - return (ENOTSUP); - } - return (0); - } - } else if (mask_bit == ACE_READ_NAMED_ATTRS) { - if (!hasreadperm) - return (0); - - set_deny = ACL_READ_NAMED_READER_SET_DENY; - err_deny = ACL_READ_NAMED_READER_ERR_DENY; - set_allow = ACL_READ_NAMED_READER_SET_ALLOW; - err_allow = ACL_READ_NAMED_READER_ERR_ALLOW; - } else if (mask_bit == ACE_WRITE_NAMED_ATTRS) { - if (!haswriteperm) - return (0); - - set_deny = ACL_WRITE_NAMED_WRITER_SET_DENY; - err_deny = ACL_WRITE_NAMED_WRITER_ERR_DENY; - set_allow = ACL_WRITE_NAMED_WRITER_SET_ALLOW; - err_allow = ACL_WRITE_NAMED_WRITER_ERR_ALLOW; - } else { - return (EINVAL); - } - - if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) { - if (acl_consume & set_deny) { - if (!(acep->a_access_mask & mask_bit)) { - return (ENOTSUP); - } - } else if (acl_consume & err_deny) { - if (acep->a_access_mask & mask_bit) { - return (ENOTSUP); - } - } - } else { - /* ACE_ACCESS_ALLOWED_ACE_TYPE */ - if (acl_consume & set_allow) { - if (!(acep->a_access_mask & mask_bit)) { - return (ENOTSUP); - } - } else if (acl_consume & err_allow) { - if (acep->a_access_mask & mask_bit) { - return (ENOTSUP); - } - } - } - return (0); -} - -static int -ace_to_aent_legal(ace_t *acep) -{ - int error = 0; - int isowner; - - /* only ALLOW or DENY */ - if ((acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE) && - (acep->a_type != ACE_ACCESS_DENIED_ACE_TYPE)) { - error = ENOTSUP; - goto out; - } - - /* check for invalid flags */ - if (acep->a_flags & ~(ACE_VALID_FLAG_BITS)) { - error = EINVAL; - goto out; - } - - /* some flags are illegal */ - if (acep->a_flags & (ACE_SUCCESSFUL_ACCESS_ACE_FLAG | - ACE_FAILED_ACCESS_ACE_FLAG | - ACE_NO_PROPAGATE_INHERIT_ACE)) { - error = ENOTSUP; - goto out; - } - - /* check for invalid masks */ - if (acep->a_access_mask & ~(ACE_VALID_MASK_BITS)) { - error = EINVAL; - goto out; - } - - if ((acep->a_flags & ACE_OWNER)) { - isowner = 1; - } else { - isowner = 0; - } - - error = access_mask_check(acep, ACE_SYNCHRONIZE, isowner); - if (error) - goto out; - - error = access_mask_check(acep, ACE_WRITE_OWNER, isowner); - if (error) - goto out; - - error = access_mask_check(acep, ACE_DELETE, isowner); - if (error) - goto out; - - error = access_mask_check(acep, ACE_WRITE_ATTRIBUTES, isowner); - if (error) - goto out; - - error = access_mask_check(acep, ACE_READ_NAMED_ATTRS, isowner); - if (error) - goto out; - - error = access_mask_check(acep, ACE_WRITE_NAMED_ATTRS, isowner); - if (error) - goto out; - - /* more detailed checking of masks */ - if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) { - if (! (acep->a_access_mask & ACE_READ_ATTRIBUTES)) { - error = ENOTSUP; - goto out; - } - if ((acep->a_access_mask & ACE_WRITE_DATA) && - (! (acep->a_access_mask & ACE_APPEND_DATA))) { - error = ENOTSUP; - goto out; - } - if ((! (acep->a_access_mask & ACE_WRITE_DATA)) && - (acep->a_access_mask & ACE_APPEND_DATA)) { - error = ENOTSUP; - goto out; - } - } - - /* ACL enforcement */ - if ((acep->a_access_mask & ACE_READ_ACL) && - (acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE)) { - error = ENOTSUP; - goto out; - } - if (acep->a_access_mask & ACE_WRITE_ACL) { - if ((acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) && - (isowner)) { - error = ENOTSUP; - goto out; - } - if ((acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) && - (! isowner)) { - error = ENOTSUP; - goto out; - } - } - -out: - return (error); -} - -static int -ace_mask_to_mode(uint32_t mask, o_mode_t *modep, int isdir) -{ - int error = 0; - o_mode_t mode = 0; - uint32_t bits, wantbits; - - /* read */ - if (mask & ACE_READ_DATA) - mode |= 04; - - /* write */ - wantbits = (ACE_WRITE_DATA | ACE_APPEND_DATA); - if (isdir) - wantbits |= ACE_DELETE_CHILD; - bits = mask & wantbits; - if (bits != 0) { - if (bits != wantbits) { - error = ENOTSUP; - goto out; - } - mode |= 02; - } - - /* exec */ - if (mask & ACE_EXECUTE) { - mode |= 01; - } - - *modep = mode; - -out: - return (error); -} - -static int -ace_allow_to_mode(uint32_t mask, o_mode_t *modep, int isdir) -{ - /* ACE_READ_ACL and ACE_READ_ATTRIBUTES must both be set */ - if ((mask & (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) != - (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) { - return (ENOTSUP); - } - - return (ace_mask_to_mode(mask, modep, isdir)); -} - -static int -acevals_to_aent(acevals_t *vals, aclent_t *dest, ace_list_t *list, - uid_t owner, gid_t group, int isdir) -{ - int error; - uint32_t flips = ACE_POSIX_SUPPORTED_BITS; - - if (isdir) - flips |= ACE_DELETE_CHILD; - if (vals->allowed != (vals->denied ^ flips)) { - error = ENOTSUP; - goto out; - } - if ((list->hasmask) && (list->acl_mask != vals->mask) && - (vals->aent_type & (USER | GROUP | GROUP_OBJ))) { - error = ENOTSUP; - goto out; - } - error = ace_allow_to_mode(vals->allowed, &dest->a_perm, isdir); - if (error != 0) - goto out; - dest->a_type = vals->aent_type; - if (dest->a_type & (USER | GROUP)) { - dest->a_id = vals->key; - } else if (dest->a_type & USER_OBJ) { - dest->a_id = owner; - } else if (dest->a_type & GROUP_OBJ) { - dest->a_id = group; - } else if (dest->a_type & OTHER_OBJ) { - dest->a_id = 0; - } else { - error = EINVAL; - goto out; - } - -out: - return (error); -} - -static int -ace_list_to_aent(ace_list_t *list, aclent_t **aclentp, int *aclcnt, - uid_t owner, gid_t group, int isdir) -{ - int error = 0; - aclent_t *aent, *result = NULL; - acevals_t *vals; - int resultcount; - - if ((list->seen & (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) != - (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) { - error = ENOTSUP; - goto out; - } - if ((! list->hasmask) && (list->numusers + list->numgroups > 0)) { - error = ENOTSUP; - goto out; - } - - resultcount = 3 + list->numusers + list->numgroups; - /* - * This must be the same condition as below, when we add the CLASS_OBJ - * (aka ACL mask) - */ - if ((list->hasmask) || (! list->dfacl_flag)) - resultcount += 1; - - result = aent = calloc(1, resultcount * sizeof (aclent_t)); - - if (result == NULL) { - error = ENOMEM; - goto out; - } - - /* USER_OBJ */ - if (!(list->user_obj.aent_type & USER_OBJ)) { - error = EINVAL; - goto out; - } - - error = acevals_to_aent(&list->user_obj, aent, list, owner, group, - isdir); - - if (error != 0) - goto out; - ++aent; - /* USER */ - vals = NULL; - for (vals = avl_first(&list->user); vals != NULL; - vals = AVL_NEXT(&list->user, vals)) { - if (!(vals->aent_type & USER)) { - error = EINVAL; - goto out; - } - error = acevals_to_aent(vals, aent, list, owner, group, - isdir); - if (error != 0) - goto out; - ++aent; - } - /* GROUP_OBJ */ - if (!(list->group_obj.aent_type & GROUP_OBJ)) { - error = EINVAL; - goto out; - } - error = acevals_to_aent(&list->group_obj, aent, list, owner, group, - isdir); - if (error != 0) - goto out; - ++aent; - /* GROUP */ - vals = NULL; - for (vals = avl_first(&list->group); vals != NULL; - vals = AVL_NEXT(&list->group, vals)) { - if (!(vals->aent_type & GROUP)) { - error = EINVAL; - goto out; - } - error = acevals_to_aent(vals, aent, list, owner, group, - isdir); - if (error != 0) - goto out; - ++aent; - } - /* - * CLASS_OBJ (aka ACL_MASK) - * - * An ACL_MASK is not fabricated if the ACL is a default ACL. - * This is to follow UFS's behavior. - */ - if ((list->hasmask) || (! list->dfacl_flag)) { - if (list->hasmask) { - uint32_t flips = ACE_POSIX_SUPPORTED_BITS; - if (isdir) - flips |= ACE_DELETE_CHILD; - error = ace_mask_to_mode(list->acl_mask ^ flips, - &aent->a_perm, isdir); - if (error != 0) - goto out; - } else { - /* fabricate the ACL_MASK from the group permissions */ - error = ace_mask_to_mode(list->group_obj.allowed, - &aent->a_perm, isdir); - if (error != 0) - goto out; - } - aent->a_id = 0; - aent->a_type = CLASS_OBJ | list->dfacl_flag; - ++aent; - } - /* OTHER_OBJ */ - if (!(list->other_obj.aent_type & OTHER_OBJ)) { - error = EINVAL; - goto out; - } - error = acevals_to_aent(&list->other_obj, aent, list, owner, group, - isdir); - if (error != 0) - goto out; - ++aent; - - *aclentp = result; - *aclcnt = resultcount; - -out: - if (error != 0) { - if (result != NULL) - free(result); - } - - return (error); -} - -/* - * free all data associated with an ace_list - */ -static void -ace_list_free(ace_list_t *al) -{ - acevals_t *node; - void *cookie; - - if (al == NULL) - return; - - cookie = NULL; - while ((node = avl_destroy_nodes(&al->user, &cookie)) != NULL) - free(node); - cookie = NULL; - while ((node = avl_destroy_nodes(&al->group, &cookie)) != NULL) - free(node); - - avl_destroy(&al->user); - avl_destroy(&al->group); - - /* free the container itself */ - free(al); -} - -static int -acevals_compare(const void *va, const void *vb) -{ - const acevals_t *a = va, *b = vb; - - if (a->key == b->key) - return (0); - - if (a->key > b->key) - return (1); - - else - return (-1); -} - -/* - * Convert a list of ace_t entries to equivalent regular and default - * aclent_t lists. Return error (ENOTSUP) when conversion is not possible. - */ -static int -ln_ace_to_aent(ace_t *ace, int n, uid_t owner, gid_t group, - aclent_t **aclentp, int *aclcnt, aclent_t **dfaclentp, int *dfaclcnt, - int isdir) -{ - int error = 0; - ace_t *acep; - uint32_t bits; - int i; - ace_list_t *normacl = NULL, *dfacl = NULL, *acl; - acevals_t *vals; - - *aclentp = NULL; - *aclcnt = 0; - *dfaclentp = NULL; - *dfaclcnt = 0; - - /* we need at least user_obj, group_obj, and other_obj */ - if (n < 6) { - error = ENOTSUP; - goto out; - } - if (ace == NULL) { - error = EINVAL; - goto out; - } - - normacl = calloc(1, sizeof (ace_list_t)); - - if (normacl == NULL) { - error = errno; - goto out; - } - - avl_create(&normacl->user, acevals_compare, sizeof (acevals_t), - offsetof(acevals_t, avl)); - avl_create(&normacl->group, acevals_compare, sizeof (acevals_t), - offsetof(acevals_t, avl)); - - ace_list_init(normacl, 0); - - dfacl = calloc(1, sizeof (ace_list_t)); - if (dfacl == NULL) { - error = errno; - goto out; - } - avl_create(&dfacl->user, acevals_compare, sizeof (acevals_t), - offsetof(acevals_t, avl)); - avl_create(&dfacl->group, acevals_compare, sizeof (acevals_t), - offsetof(acevals_t, avl)); - ace_list_init(dfacl, ACL_DEFAULT); - - /* process every ace_t... */ - for (i = 0; i < n; i++) { - acep = &ace[i]; - - /* rule out certain cases quickly */ - error = ace_to_aent_legal(acep); - if (error != 0) - goto out; - - /* - * Turn off these bits in order to not have to worry about - * them when doing the checks for compliments. - */ - acep->a_access_mask &= ~(ACE_WRITE_OWNER | ACE_DELETE | - ACE_SYNCHRONIZE | ACE_WRITE_ATTRIBUTES | - ACE_READ_NAMED_ATTRS | ACE_WRITE_NAMED_ATTRS); - - /* see if this should be a regular or default acl */ - bits = acep->a_flags & - (ACE_INHERIT_ONLY_ACE | - ACE_FILE_INHERIT_ACE | - ACE_DIRECTORY_INHERIT_ACE); - if (bits != 0) { - /* all or nothing on these inherit bits */ - if (bits != (ACE_INHERIT_ONLY_ACE | - ACE_FILE_INHERIT_ACE | - ACE_DIRECTORY_INHERIT_ACE)) { - error = ENOTSUP; - goto out; - } - acl = dfacl; - } else { - acl = normacl; - } - - if ((acep->a_flags & ACE_OWNER)) { - if (acl->state > ace_user_obj) { - error = ENOTSUP; - goto out; - } - acl->state = ace_user_obj; - acl->seen |= USER_OBJ; - vals = &acl->user_obj; - vals->aent_type = USER_OBJ | acl->dfacl_flag; - } else if ((acep->a_flags & ACE_EVERYONE)) { - acl->state = ace_other_obj; - acl->seen |= OTHER_OBJ; - vals = &acl->other_obj; - vals->aent_type = OTHER_OBJ | acl->dfacl_flag; - } else if (acep->a_flags & ACE_IDENTIFIER_GROUP) { - if (acl->state > ace_group) { - error = ENOTSUP; - goto out; - } - if ((acep->a_flags & ACE_GROUP)) { - acl->seen |= GROUP_OBJ; - vals = &acl->group_obj; - vals->aent_type = GROUP_OBJ | acl->dfacl_flag; - } else { - acl->seen |= GROUP; - vals = acevals_find(acep, &acl->group, - &acl->numgroups); - if (vals == NULL) { - error = ENOMEM; - goto out; - } - vals->aent_type = GROUP | acl->dfacl_flag; - } - acl->state = ace_group; - } else { - if (acl->state > ace_user) { - error = ENOTSUP; - goto out; - } - acl->state = ace_user; - acl->seen |= USER; - vals = acevals_find(acep, &acl->user, - &acl->numusers); - if (vals == NULL) { - error = ENOMEM; - goto out; - } - vals->aent_type = USER | acl->dfacl_flag; - } - - if (!(acl->state > ace_unused)) { - error = EINVAL; - goto out; - } - - if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) { - /* no more than one allowed per aclent_t */ - if (vals->allowed != ACE_MASK_UNDEFINED) { - error = ENOTSUP; - goto out; - } - vals->allowed = acep->a_access_mask; - } else { - /* - * it's a DENY; if there was a previous DENY, it - * must have been an ACL_MASK. - */ - if (vals->denied != ACE_MASK_UNDEFINED) { - /* ACL_MASK is for USER and GROUP only */ - if ((acl->state != ace_user) && - (acl->state != ace_group)) { - error = ENOTSUP; - goto out; - } - - if (! acl->hasmask) { - acl->hasmask = 1; - acl->acl_mask = vals->denied; - /* check for mismatched ACL_MASK emulations */ - } else if (acl->acl_mask != vals->denied) { - error = ENOTSUP; - goto out; - } - vals->mask = vals->denied; - } - vals->denied = acep->a_access_mask; - } - } - - /* done collating; produce the aclent_t lists */ - if (normacl->state != ace_unused) { - error = ace_list_to_aent(normacl, aclentp, aclcnt, - owner, group, isdir); - if (error != 0) { - goto out; - } - } - if (dfacl->state != ace_unused) { - error = ace_list_to_aent(dfacl, dfaclentp, dfaclcnt, - owner, group, isdir); - if (error != 0) { - goto out; - } - } - -out: - if (normacl != NULL) - ace_list_free(normacl); - if (dfacl != NULL) - ace_list_free(dfacl); - - return (error); -} - -static int -convert_ace_to_aent(ace_t *acebufp, int acecnt, int isdir, - uid_t owner, gid_t group, aclent_t **retaclentp, int *retaclcnt) -{ - int error; - aclent_t *aclentp, *dfaclentp; - int aclcnt, dfaclcnt; - - error = ln_ace_to_aent(acebufp, acecnt, owner, group, - &aclentp, &aclcnt, &dfaclentp, &dfaclcnt, isdir); - - if (error) - return (error); - - - if (dfaclcnt != 0) { - /* - * Slap aclentp and dfaclentp into a single array. - */ - aclentp = realloc(aclentp, (sizeof (aclent_t) * aclcnt) + - (sizeof (aclent_t) * dfaclcnt)); - if (aclentp != NULL) { - (void) memcpy(aclentp + aclcnt, - dfaclentp, sizeof (aclent_t) * dfaclcnt); - } else { - error = -1; - } - } - - if (aclentp) { - *retaclentp = aclentp; - *retaclcnt = aclcnt + dfaclcnt; - } - - if (dfaclentp) - free(dfaclentp); - - return (error); -} static int cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp) @@ -1576,56 +240,6 @@ facl_get(int fd, int get_flag, acl_t **aclp) return (cacl_get(acl_inp, get_flag, ACL_FD, aclp)); } -static int -acl_translate(acl_t *aclp, int target_flavor, int isdir, uid_t owner, - gid_t group) -{ - int aclcnt; - void *acldata; - int error; - - /* - * See if we need to translate - */ - if ((target_flavor == _ACL_ACE_ENABLED && aclp->acl_type == ACE_T) || - (target_flavor == _ACL_ACLENT_ENABLED && - aclp->acl_type == ACLENT_T)) - return (0); - - if (target_flavor == -1) - return (-1); - - if (target_flavor == _ACL_ACE_ENABLED && - aclp->acl_type == ACLENT_T) { - error = convert_aent_to_ace(aclp->acl_aclp, - aclp->acl_cnt, isdir, (ace_t **)&acldata, &aclcnt); - if (error) { - errno = error; - return (-1); - } - } else if (target_flavor == _ACL_ACLENT_ENABLED && - aclp->acl_type == ACE_T) { - error = convert_ace_to_aent(aclp->acl_aclp, aclp->acl_cnt, - isdir, owner, group, (aclent_t **)&acldata, &aclcnt); - if (error) { - errno = error; - return (-1); - } - } else { - errno = ENOTSUP; - return (-1); - } - - /* - * replace old acl with newly translated acl - */ - free(aclp->acl_aclp); - aclp->acl_aclp = acldata; - aclp->acl_cnt = aclcnt; - aclp->acl_type = (target_flavor == _ACL_ACE_ENABLED) ? ACE_T : ACLENT_T; - return (0); -} - /* * Set an ACL, translates acl to ace_t when appropriate. */ diff --git a/usr/src/lib/libsec/common/aclutils.h b/usr/src/lib/libsec/common/aclutils.h index e5c34347bfa5..1db0aa4752c5 100644 --- a/usr/src/lib/libsec/common/aclutils.h +++ b/usr/src/lib/libsec/common/aclutils.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -29,6 +29,7 @@ #pragma ident "%Z%%M% %I% %E% SMI" #include +#include #include #include #include @@ -50,26 +51,6 @@ extern "C" { * append_data/add_subdirectory * when object of ACL is known. */ -#define ACL_IS_DIR 0x2 - -typedef enum acl_type { - ACLENT_T = 0, - ACE_T = 1 -} acl_type_t; - -/* - * acl flags - */ -#define ACL_IS_TRIVIAL 0x1 - -struct acl_info { - acl_type_t acl_type; /* style of acl */ - int acl_cnt; /* number of acl entries */ - int acl_entry_size; /* sizeof acl entry */ - int acl_flags; /* special flags about acl */ - void *acl_aclp; /* the acl */ -}; - #define PERM_TYPE_ACE 0x1 /* permissions are of ACE type */ #define PERM_TYPE_UNKNOWN 0x2 /* permission type not yet known */ @@ -105,7 +86,6 @@ extern void acl_error(const char *, ...); extern int acl_parse(const char *, acl_t **); extern int yyparse(void); extern void yyreset(void); -extern acl_t *acl_alloc(enum acl_type); extern acl_t *acl_to_aclp(enum acl_type, void *, int); #ifdef __cplusplus diff --git a/usr/src/lib/libsecdb/auth_attr.txt b/usr/src/lib/libsecdb/auth_attr.txt index afb8349e40f7..0b81cd20f014 100644 --- a/usr/src/lib/libsecdb/auth_attr.txt +++ b/usr/src/lib/libsecdb/auth_attr.txt @@ -121,6 +121,7 @@ solaris.smf.manage.mdns:::Manage Multicast DNS Service States::help=SmfMDNSState solaris.smf.manage.name-service-cache:::Manage Name Service Cache Daemon Service States::help=SmfNscdStates.html solaris.smf.manage.nwam:::Manage Network Auto-Magic Service States::help=SmfNWAMStates.html solaris.smf.manage.power:::Manage Power Management Service States::help=SmfPowerStates.html +solaris.smf.manage.smb:::Manage SMB Service States::help=SmfSMBStates.html solaris.smf.manage.rmvolmgr:::Manage Rmvolmgr Service States::help=SmfRmvolmgrStates.html solaris.smf.manage.routing:::Manage Routing Service States::help=SmfRoutingStates.html solaris.smf.manage.rpc.bind:::Manage RPC Program number mapper::help=SmfRPCBind.html @@ -137,6 +138,8 @@ solaris.smf.value.inetd:::Change values of SMF Inetd configuration paramaters::h solaris.smf.value.ipsec:::Change Values of SMF IPsec Properties::help=SmfValueIPsec.html solaris.smf.value.mdns:::Change Values of MDNS Service Properties::help=SmfValueMDNS.html solaris.smf.value.nwam:::Change Values of SMF Network Auto-Magic Properties::help=SmfValueNWAM.html +solaris.smf.value.smb:::Change Values of SMB Service Properties::help=SmfValueSMB.html +solaris.smf.read.smb:::Read permission for protected SMF SMB Service Properties::help=AuthReadSMB.html solaris.smf.value.routing:::Change Values of SMF Routing Properties::help=SmfValueRouting.html solaris.smf.value.tnd:::Change Trusted Network Daemon Service Property Values::help=ValueTND.html # diff --git a/usr/src/lib/libsecdb/help/auths/AuthReadSMB.html b/usr/src/lib/libsecdb/help/auths/AuthReadSMB.html new file mode 100755 index 000000000000..aec2ed97e818 --- /dev/null +++ b/usr/src/lib/libsecdb/help/auths/AuthReadSMB.html @@ -0,0 +1,38 @@ + + + + + + + + + + +When View Rights is in the Authorizations Included column, it grants the authorization to list and read rights, in the Users tools of the Solaris Management Console. +

+If View Rights is grayed, then you are not entitled to Add or Remove this authorization. +

+ + diff --git a/usr/src/lib/libsecdb/help/auths/Makefile b/usr/src/lib/libsecdb/help/auths/Makefile index 1bdd12e0cea3..d28be1d4c517 100644 --- a/usr/src/lib/libsecdb/help/auths/Makefile +++ b/usr/src/lib/libsecdb/help/auths/Makefile @@ -93,6 +93,9 @@ HTMLENTS = \ SmfValueNADD.html \ SmfValueNWAM.html \ SmfValueRouting.html \ + SmfValueSMB.html \ + AuthReadSMB.html \ + SmfSMBStates.html \ SmfWpaStates.html \ NetworkHeader.html \ WifiConfig.html \ diff --git a/usr/src/lib/libsecdb/help/auths/SmfSMBStates.html b/usr/src/lib/libsecdb/help/auths/SmfSMBStates.html new file mode 100644 index 000000000000..baa85f002d93 --- /dev/null +++ b/usr/src/lib/libsecdb/help/auths/SmfSMBStates.html @@ -0,0 +1,40 @@ + + + + + +When Manage SMB Service States is in the Authorizations Include +column, it grants the authorization to enable, disable, or restart +the SMB service. +

+If Manage SMB Service States is grayed, then you are not entitled +to Add or Remove this authorization. +
  + + diff --git a/usr/src/lib/libsecdb/help/auths/SmfValueSMB.html b/usr/src/lib/libsecdb/help/auths/SmfValueSMB.html new file mode 100644 index 000000000000..0e5c3bc27764 --- /dev/null +++ b/usr/src/lib/libsecdb/help/auths/SmfValueSMB.html @@ -0,0 +1,40 @@ + + + + + +When Value SMB Properties is in the Authorizations Include +column, it grants the authorization to change SMB service +property values. +

+If Value SMB Properties is grayed, then you are not entitled to +Add or Remove this authorization. +
  + + diff --git a/usr/src/lib/libsecdb/help/profiles/Makefile b/usr/src/lib/libsecdb/help/profiles/Makefile index 9ad6ede80312..e0300d339bed 100644 --- a/usr/src/lib/libsecdb/help/profiles/Makefile +++ b/usr/src/lib/libsecdb/help/profiles/Makefile @@ -62,6 +62,7 @@ HTMLENTS = \ RtPrntAdmin.html \ RtProcManagement.html \ RtRightsDelegate.html \ + RtSMBMngmnt.html \ RtSoftwareInstall.html \ RtSysEvMngmnt.html \ RtUserMngmnt.html \ diff --git a/usr/src/lib/libsecdb/help/profiles/RtSMBMngmnt.html b/usr/src/lib/libsecdb/help/profiles/RtSMBMngmnt.html new file mode 100644 index 000000000000..bbfef648abd9 --- /dev/null +++ b/usr/src/lib/libsecdb/help/profiles/RtSMBMngmnt.html @@ -0,0 +1,40 @@ + + + + + + + + + + +When SMB Service Management is in the Rights Included column, it grants +the right to administer the SMB service. +

+If SMB Service Management is grayed, then you are not entitled to Add or +Remove this right. +

+ + diff --git a/usr/src/lib/libsecdb/prof_attr.txt b/usr/src/lib/libsecdb/prof_attr.txt index 96107e01f46c..1c171d427bd3 100644 --- a/usr/src/lib/libsecdb/prof_attr.txt +++ b/usr/src/lib/libsecdb/prof_attr.txt @@ -42,7 +42,7 @@ Log Management:::Manage log files:help=RtLogMngmnt.html Basic Solaris User:::Automatically assigned rights:auths=solaris.profmgr.read,solaris.jobs.user,solaris.mail.mailq,solaris.device.mount.removable;profiles=All;help=RtDefault.html Device Security:::Manage devices and Volume Manager:auths=solaris.device.*;help=RtDeviceSecurity.html DHCP Management:::Manage the DHCP service:auths=solaris.dhcpmgr.*;help=RtDHCPMngmnt.html -File System Management:::Manage, mount, share file systems:auths=solaris.smf.manage.autofs,solaris.smf.manage.shares.*,solaris.smf.value.shares.*;help=RtFileSysMngmnt.html +File System Management:::Manage, mount, share file systems:profiles=SMB Management;auths=solaris.smf.manage.autofs,solaris.smf.manage.shares.*,solaris.smf.value.shares.*;help=RtFileSysMngmnt.html File System Security:::Manage file system security attributes:help=RtFileSysSecurity.html HAL Management:::Manage HAL SMF service:auths=solaris.smf.manage.hal;help=RtHALMngmnt.html Idmap Name Mapping Management:::Manage Name-based Mapping Rules of Identity Mapping Service:auths=solaris.admin.idmap.rules;help=RtIdmapNameRulesMngmnt.html @@ -75,6 +75,7 @@ Crypto Management:::Cryptographic Framework Administration:help=RtCryptoMngmnt.h Kerberos Client Management:::Maintain and Administer Kerberos excluding the servers:help=RtKerberosClntMngmnt.html Kerberos Server Management:::Maintain and Administer Kerberos Servers:profiles=Kerberos Client Management;help=RtKerberosSrvrMngmnt.html DAT Administration:::Manage the DAT configuration:help=RtDatAdmin.html +SMB Management:::Manage the SMB service:auths=solaris.smf.manage.smb,solaris.smf.value.smb,solaris.smf.read.smb;help=RtSMBMngmnt.html ZFS File System Management:::Create and Manage ZFS File Systems:help=RtZFSFileSysMngmnt.html ZFS Storage Management:::Create and Manage ZFS Storage Pools:help=RtZFSStorageMngmnt.html Zone Management:::Zones Virtual Application Environment Administration:help=RtZoneMngmnt.html diff --git a/usr/src/lib/libshare/Makefile b/usr/src/lib/libshare/Makefile index fb191600658d..0c333e44597a 100644 --- a/usr/src/lib/libshare/Makefile +++ b/usr/src/lib/libshare/Makefile @@ -35,7 +35,7 @@ $(BUILD64)MACHS += $(MACH64) # Add plugin module directories here. They need to build after the libshare # objects are built. -PLUGINS = nfs +PLUGINS = smb nfs $(PLUGINS): $(MACHS) SUBDIRS = $(MACHS) $(PLUGINS) diff --git a/usr/src/lib/libshare/common/libshare.c b/usr/src/lib/libshare/common/libshare.c index 25213e97a458..49104b2abe01 100644 --- a/usr/src/lib/libshare/common/libshare.c +++ b/usr/src/lib/libshare/common/libshare.c @@ -57,6 +57,16 @@ #define DFS_LOCK_FILE "/etc/dfs/fstypes" #define SA_STRSIZE 256 /* max string size for names */ +/* + * internal object type values returned by sa_get_object_type() + */ +#define SA_TYPE_UNKNOWN 0 +#define SA_TYPE_GROUP 1 +#define SA_TYPE_SHARE 2 +#define SA_TYPE_RESOURCE 3 +#define SA_TYPE_OPTIONSET 4 +#define SA_TYPE_ALTSPACE 5 + /* * internal data structures */ @@ -69,16 +79,20 @@ extern int gettransients(sa_handle_impl_t, xmlNodePtr *); extern int sa_valid_property(void *, char *, sa_property_t); extern char *sa_fstype(char *); extern int sa_is_share(void *); +extern int sa_is_resource(void *); extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */ extern int sa_group_is_zfs(sa_group_t); extern int sa_path_is_zfs(char *); extern int sa_zfs_set_sharenfs(sa_group_t, char *, int); +extern int sa_zfs_set_sharesmb(sa_group_t, char *, int); extern void update_legacy_config(sa_handle_t); extern int issubdir(char *, char *); extern int sa_zfs_init(sa_handle_impl_t); extern void sa_zfs_fini(sa_handle_impl_t); extern void sablocksigs(sigset_t *); extern void saunblocksigs(sigset_t *); +static sa_group_t sa_get_optionset_parent(sa_optionset_t); +static char *get_node_attr(void *, char *); /* * Data structures for finding/managing the document root to access @@ -188,6 +202,21 @@ sa_errorstr(int err) case SA_NOT_SHARED: ret = dgettext(TEXT_DOMAIN, "not shared"); break; + case SA_NO_SUCH_RESOURCE: + ret = dgettext(TEXT_DOMAIN, "no such resource"); + break; + case SA_RESOURCE_REQUIRED: + ret = dgettext(TEXT_DOMAIN, "resource name required"); + break; + case SA_MULTIPLE_ERROR: + ret = dgettext(TEXT_DOMAIN, "errors from multiple protocols"); + break; + case SA_PATH_IS_SUBDIR: + ret = dgettext(TEXT_DOMAIN, "path is a subpath of share"); + break; + case SA_PATH_IS_PARENTDIR: + ret = dgettext(TEXT_DOMAIN, "path is parent of a share"); + break; default: (void) snprintf(errstr, sizeof (errstr), dgettext(TEXT_DOMAIN, "unknown %d"), err); @@ -375,6 +404,36 @@ is_shared(sa_share_t share) return (result); } +/* + * excluded_protocol(share, proto) + * + * Returns B_TRUE if the specified protocol appears in the "exclude" + * property. This is used to prevent sharing special case shares + * (e.g. subdirs when SMB wants a subdir and NFS doesn't. B_FALSE is + * returned if the protocol isn't in the list. + */ +static boolean_t +excluded_protocol(sa_share_t share, char *proto) +{ + char *protolist; + char *str; + char *token; + + protolist = sa_get_share_attr(share, "exclude"); + if (protolist != NULL) { + str = protolist; + while ((token = strtok(str, ",")) != NULL) { + if (strcmp(token, proto) == 0) { + sa_free_attr_string(protolist); + return (B_TRUE); + } + str = NULL; + } + sa_free_attr_string(protolist); + } + return (B_FALSE); +} + /* * checksubdirgroup(group, newpath, strictness) * @@ -392,6 +451,11 @@ checksubdirgroup(sa_group_t group, char *newpath, int strictness) sa_share_t share; char *path; int issub = SA_OK; + int subdir; + int parent; + + if (newpath == NULL) + return (SA_INVALID_PATH); for (share = sa_get_share(group, NULL); share != NULL; share = sa_get_next_share(share)) { @@ -417,13 +481,18 @@ checksubdirgroup(sa_group_t group, char *newpath, int strictness) */ if (path == NULL) continue; - if (newpath != NULL && - (strcmp(path, newpath) == 0 || issubdir(newpath, path) || - issubdir(path, newpath))) { - sa_free_attr_string(path); - path = NULL; + + if (strcmp(path, newpath) == 0) { issub = SA_INVALID_PATH; - break; + } else { + subdir = issubdir(newpath, path); + parent = issubdir(path, newpath); + if (subdir || parent) { + sa_free_attr_string(path); + path = NULL; + return (subdir ? + SA_PATH_IS_SUBDIR : SA_PATH_IS_PARENTDIR); + } } sa_free_attr_string(path); path = NULL; @@ -446,15 +515,16 @@ static int checksubdir(sa_handle_t handle, char *newpath, int strictness) { sa_group_t group; - int issub; + int issub = SA_OK; char *path = NULL; - for (issub = 0, group = sa_get_group(handle, NULL); - group != NULL && !issub; group = sa_get_next_group(group)) { + for (group = sa_get_group(handle, NULL); + group != NULL && issub == SA_OK; + group = sa_get_next_group(group)) { if (sa_group_is_zfs(group)) { sa_group_t subgroup; for (subgroup = sa_get_sub_group(group); - subgroup != NULL && !issub; + subgroup != NULL && issub == SA_OK; subgroup = sa_get_next_group(subgroup)) issub = checksubdirgroup(subgroup, newpath, strictness); @@ -517,14 +587,17 @@ validpath(sa_handle_t handle, char *path, int strictness) /* * check to see if group/share is persistent. + * + * "group" can be either an sa_group_t or an sa_share_t. (void *) + * works since both thse types are also void *. */ -static int -is_persistent(sa_group_t group) +int +sa_is_persistent(void *group) { char *type; int persist = 1; - type = sa_get_group_attr(group, "type"); + type = sa_get_group_attr((sa_group_t)group, "type"); if (type != NULL && strcmp(type, "transient") == 0) persist = 0; if (type != NULL) @@ -589,6 +662,35 @@ is_zfs_group(sa_group_t group) return (ret); } +/* + * sa_get_object_type(object) + * + * This function returns a numeric value representing the object + * type. This allows using simpler checks when doing type specific + * operations. + */ + +static int +sa_get_object_type(void *object) +{ + xmlNodePtr node = (xmlNodePtr)object; + int type; + + if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) + type = SA_TYPE_GROUP; + else if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) + type = SA_TYPE_SHARE; + else if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) + type = SA_TYPE_RESOURCE; + else if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) + type = SA_TYPE_OPTIONSET; + else if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) + type = SA_TYPE_ALTSPACE; + else + assert(0); + return (type); +} + /* * sa_optionset_name(optionset, oname, len, id) * return the SMF name for the optionset. If id is not NULL, it @@ -605,15 +707,34 @@ static int sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id) { char *proto; + void *parent; + int ptype; if (id == NULL) id = "optionset"; - proto = sa_get_optionset_attr(optionset, "type"); - len = snprintf(oname, len, "%s_%s", id, proto ? proto : "default"); + parent = sa_get_optionset_parent(optionset); + if (parent != NULL) { + ptype = sa_get_object_type(parent); + proto = sa_get_optionset_attr(optionset, "type"); + if (ptype != SA_TYPE_RESOURCE) { + len = snprintf(oname, len, "%s_%s", id, + proto ? proto : "default"); + } else { + char *index; + index = get_node_attr((void *)parent, "id"); + if (index != NULL) + len = snprintf(oname, len, "%s_%s_%s", id, + proto ? proto : "default", index); + else + len = 0; + } - if (proto != NULL) - sa_free_attr_string(proto); + if (proto != NULL) + sa_free_attr_string(proto); + } else { + len = 0; + } return (len); } @@ -660,6 +781,7 @@ verifydefgroupopts(sa_handle_t handle) { sa_group_t defgrp; sa_optionset_t opt; + defgrp = sa_get_group(handle, "default"); if (defgrp != NULL) { opt = sa_get_optionset(defgrp, NULL); @@ -1187,7 +1309,7 @@ sa_find_share(sa_handle_t handle, char *sharepath) /* * sa_check_path(group, path, strictness) * - * check that path is a valid path relative to the group. Currently, + * Check that path is a valid path relative to the group. Currently, * we are ignoring the group and checking only the NFS rules. Later, * we may want to use the group to then check against the protocols * enabled on the group. The strictness values mean: @@ -1206,16 +1328,84 @@ sa_check_path(sa_group_t group, char *path, int strictness) } /* - * _sa_add_share(group, sharepath, persist, *error) + * mark_excluded_protos(group, share, flags) + * + * Walk through all the protocols enabled for the group and check to + * see if the share has any of them should be in the exclude list + * based on the featureset of the protocol. If there are any, add the + * "exclude" property to the share. + */ +static void +mark_excluded_protos(sa_group_t group, xmlNodePtr share, uint64_t flags) +{ + sa_optionset_t optionset; + char exclude_list[SA_STRSIZE]; + char *sep = ""; + + exclude_list[0] = '\0'; + for (optionset = sa_get_optionset(group, NULL); + optionset != NULL; + optionset = sa_get_next_optionset(optionset)) { + char *value; + uint64_t features; + value = sa_get_optionset_attr(optionset, "type"); + if (value == NULL) + continue; + features = sa_proto_get_featureset(value); + sa_free_attr_string(value); + if (!(features & flags)) { + (void) strlcat(exclude_list, sep, + sizeof (exclude_list)); + (void) strlcat(exclude_list, value, + sizeof (exclude_list)); + sep = ","; + } + } + if (exclude_list[0] != '\0') + xmlSetProp(share, (xmlChar *)"exclude", + (xmlChar *)exclude_list); +} + +/* + * get_all_features(group) + * + * Walk through all the protocols on the group and collect all + * possible enabled features. This is the OR of all the featuresets. + */ +static uint64_t +get_all_features(sa_group_t group) +{ + sa_optionset_t optionset; + uint64_t features = 0; + + for (optionset = sa_get_optionset(group, NULL); + optionset != NULL; + optionset = sa_get_next_optionset(optionset)) { + char *value; + value = sa_get_optionset_attr(optionset, "type"); + if (value == NULL) + continue; + features |= sa_proto_get_featureset(value); + sa_free_attr_string(value); + } + return (features); +} + + +/* + * _sa_add_share(group, sharepath, persist, *error, flags) * - * common code for all types of add_share. sa_add_share() is the + * Common code for all types of add_share. sa_add_share() is the * public API, we also need to be able to do this when parsing legacy * files and construction of the internal configuration while - * extracting config info from SMF. + * extracting config info from SMF. "flags" indicates if some + * protocols need relaxed rules while other don't. These values are + * the featureset values defined in libshare.h. */ sa_share_t -_sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) +_sa_add_share(sa_group_t group, char *sharepath, int persist, int *error, + uint64_t flags) { xmlNodePtr node = NULL; int err; @@ -1223,52 +1413,60 @@ _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) err = SA_OK; /* assume success */ node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL); - if (node != NULL) { - xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath); - xmlSetProp(node, (xmlChar *)"type", - persist ? (xmlChar *)"persist" : (xmlChar *)"transient"); - if (persist != SA_SHARE_TRANSIENT) { - /* - * persistent shares come in two flavors: SMF and - * ZFS. Sort this one out based on target group and - * path type. Currently, only NFS is supported in the - * ZFS group and it is always on. - */ - if (sa_group_is_zfs(group) && - sa_path_is_zfs(sharepath)) { + if (node == NULL) { + if (error != NULL) + *error = SA_NO_MEMORY; + return (node); + } + + xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath); + xmlSetProp(node, (xmlChar *)"type", + persist ? (xmlChar *)"persist" : (xmlChar *)"transient"); + if (flags != 0) + mark_excluded_protos(group, node, flags); + if (persist != SA_SHARE_TRANSIENT) { + /* + * persistent shares come in two flavors: SMF and + * ZFS. Sort this one out based on target group and + * path type. Both NFS and SMB are supported. First, + * check to see if the protocol is enabled on the + * subgroup and then setup the share appropriately. + */ + if (sa_group_is_zfs(group) && + sa_path_is_zfs(sharepath)) { + if (sa_get_optionset(group, "nfs") != NULL) err = sa_zfs_set_sharenfs(group, sharepath, 1); + else if (sa_get_optionset(group, "smb") != NULL) + err = sa_zfs_set_sharesmb(group, sharepath, 1); + } else { + sa_handle_impl_t impl_handle; + impl_handle = + (sa_handle_impl_t)sa_find_group_handle(group); + if (impl_handle != NULL) { + err = sa_commit_share(impl_handle->scfhandle, + group, (sa_share_t)node); } else { - sa_handle_impl_t impl_handle; - impl_handle = - (sa_handle_impl_t)sa_find_group_handle( - group); - if (impl_handle != NULL) { - err = sa_commit_share( - impl_handle->scfhandle, group, - (sa_share_t)node); - } else { - err = SA_SYSTEM_ERR; - } + err = SA_SYSTEM_ERR; } } - if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) { - /* called by the dfstab parser so could be a show */ - err = SA_OK; - } - if (err != SA_OK) { - /* - * we couldn't commit to the repository so undo - * our internal state to reflect reality. - */ - xmlUnlinkNode(node); - xmlFreeNode(node); - node = NULL; - } - } else { - err = SA_NO_MEMORY; } + if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) + /* called by the dfstab parser so could be a show */ + err = SA_OK; + + if (err != SA_OK) { + /* + * we couldn't commit to the repository so undo + * our internal state to reflect reality. + */ + xmlUnlinkNode(node); + xmlFreeNode(node); + node = NULL; + } + if (error != NULL) *error = err; + return (node); } @@ -1285,9 +1483,10 @@ sa_share_t sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) { xmlNodePtr node = NULL; - sa_share_t dup; int strictness = SA_CHECK_NORMAL; sa_handle_t handle; + uint64_t special = 0; + uint64_t features; /* * If the share is to be permanent, use strict checking so a @@ -1304,12 +1503,33 @@ sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) handle = sa_find_group_handle(group); - if ((dup = sa_find_share(handle, sharepath)) == NULL && - (*error = sa_check_path(group, sharepath, strictness)) == SA_OK) { - node = _sa_add_share(group, sharepath, persist, error); - } - if (dup != NULL) + /* + * need to determine if the share is valid. The rules are: + * - The path must not already exist + * - The path must not be a subdir or parent dir of an + * existing path unless at least one protocol allows it. + * The sub/parent check is done in sa_check_path(). + */ + + if (sa_find_share(handle, sharepath) == NULL) { + *error = sa_check_path(group, sharepath, strictness); + features = get_all_features(group); + switch (*error) { + case SA_PATH_IS_SUBDIR: + if (features & SA_FEATURE_ALLOWSUBDIRS) + special |= SA_FEATURE_ALLOWSUBDIRS; + break; + case SA_PATH_IS_PARENTDIR: + if (features & SA_FEATURE_ALLOWPARDIRS) + special |= SA_FEATURE_ALLOWPARDIRS; + break; + } + if (*error == SA_OK || special != SA_FEATURE_NONE) + node = _sa_add_share(group, sharepath, persist, + error, special); + } else { *error = SA_DUPLICATE_NAME; + } return ((sa_share_t)node); } @@ -1324,28 +1544,52 @@ sa_enable_share(sa_share_t share, char *protocol) { char *sharepath; struct stat st; - int err = 0; + int err = SA_OK; + int ret; sharepath = sa_get_share_attr(share, "path"); + if (sharepath == NULL) + return (SA_NO_MEMORY); if (stat(sharepath, &st) < 0) { err = SA_NO_SUCH_PATH; } else { /* tell the server about the share */ if (protocol != NULL) { + if (excluded_protocol(share, protocol)) + goto done; + /* lookup protocol specific handler */ err = sa_proto_share(protocol, share); if (err == SA_OK) - (void) sa_set_share_attr(share, "shared", - "true"); + (void) sa_set_share_attr(share, + "shared", "true"); } else { - /* - * Tell all protocols. Only NFS for now but - * SMB is coming. - */ - err = sa_proto_share("nfs", share); + /* Tell all protocols about the share */ + sa_group_t group; + sa_optionset_t optionset; + + group = sa_get_parent_group(share); + + for (optionset = sa_get_optionset(group, NULL); + optionset != NULL; + optionset = sa_get_next_optionset(optionset)) { + char *proto; + proto = sa_get_optionset_attr(optionset, + "type"); + if (proto != NULL) { + if (!excluded_protocol(share, proto)) { + ret = sa_proto_share(proto, + share); + if (ret != SA_OK) + err = ret; + } + sa_free_attr_string(proto); + } + } (void) sa_set_share_attr(share, "shared", "true"); } } +done: if (sharepath != NULL) sa_free_attr_string(sharepath); return (err); @@ -1353,31 +1597,47 @@ sa_enable_share(sa_share_t share, char *protocol) /* * sa_disable_share(share, protocol) - * Disable the specified share to the specified protocol. - * If protocol is NULL, then all protocols. + * Disable the specified share to the specified protocol. If + * protocol is NULL, then all protocols that are enabled for the + * share should be disabled. */ int sa_disable_share(sa_share_t share, char *protocol) { char *path; - char *shared; + int err = SA_OK; int ret = SA_OK; path = sa_get_share_attr(share, "path"); - shared = sa_get_share_attr(share, "shared"); if (protocol != NULL) { ret = sa_proto_unshare(share, protocol, path); } else { /* need to do all protocols */ - ret = sa_proto_unshare(share, "nfs", path); + sa_group_t group; + sa_optionset_t optionset; + + group = sa_get_parent_group(share); + + /* Tell all protocols about the share */ + for (optionset = sa_get_optionset(group, NULL); + optionset != NULL; + optionset = sa_get_next_optionset(optionset)) { + char *proto; + + proto = sa_get_optionset_attr(optionset, "type"); + if (proto != NULL) { + err = sa_proto_unshare(share, proto, path); + if (err != SA_OK) + ret = err; + sa_free_attr_string(proto); + } + } } if (ret == SA_OK) (void) sa_set_share_attr(share, "shared", NULL); if (path != NULL) sa_free_attr_string(path); - if (shared != NULL) - sa_free_attr_string(shared); return (ret); } @@ -1415,7 +1675,7 @@ sa_remove_share(sa_share_t share) /* only do SMF action if permanent */ if (!transient || zfs != NULL) { /* remove from legacy dfstab as well as possible SMF */ - ret = sa_delete_legacy(share); + ret = sa_delete_legacy(share, NULL); if (ret == SA_OK) { if (!sa_group_is_zfs(group)) { sa_handle_impl_t impl_handle; @@ -1497,7 +1757,7 @@ sa_move_share(sa_group_t group, sa_share_t share) /* * sa_get_parent_group(share) * - * Return the containg group for the share. If a group was actually + * Return the containing group for the share. If a group was actually * passed in, we don't want a parent so return NULL. */ @@ -1728,7 +1988,7 @@ sa_update_config(sa_handle_t handle) /* * get_node_attr(node, tag) * - * Get the speficied tag(attribute) if it exists on the node. This is + * Get the specified tag(attribute) if it exists on the node. This is * used internally by a number of attribute oriented functions. */ @@ -1746,7 +2006,7 @@ get_node_attr(void *nodehdl, char *tag) /* * get_node_attr(node, tag) * - * Set the speficied tag(attribute) to the specified value This is + * Set the specified tag(attribute) to the specified value This is * used internally by a number of attribute oriented functions. It * doesn't update the repository, only the internal document state. */ @@ -1792,6 +2052,14 @@ sa_set_group_attr(sa_group_t group, char *tag, char *value) char *groupname; sa_handle_impl_t impl_handle; + /* + * ZFS group/subgroup doesn't need the handle so shortcut. + */ + if (sa_group_is_zfs(group)) { + set_node_attr((void *)group, tag, value); + return (SA_OK); + } + impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); if (impl_handle != NULL) { groupname = sa_get_group_attr(group, "name"); @@ -1832,48 +2100,16 @@ sa_get_share_attr(sa_share_t share, char *tag) return (get_node_attr((void *)share, tag)); } -/* - * sa_get_resource(group, resource) - * - * Search all the shares in the speified group for a share with a - * resource name matching the one specified. - * - * In the future, it may be advantageous to allow group to be NULL and - * search all groups but that isn't needed at present. - */ - -sa_share_t -sa_get_resource(sa_group_t group, char *resource) -{ - sa_share_t share = NULL; - char *name = NULL; - - if (resource != NULL) { - for (share = sa_get_share(group, NULL); share != NULL; - share = sa_get_next_share(share)) { - name = sa_get_share_attr(share, "resource"); - if (name != NULL) { - if (strcmp(name, resource) == 0) - break; - sa_free_attr_string(name); - name = NULL; - } - } - if (name != NULL) - sa_free_attr_string(name); - } - return ((sa_share_t)share); -} - /* * _sa_set_share_description(share, description) * - * Add a description tag with text contents to the specified share. - * A separate XML tag is used rather than a property. + * Add a description tag with text contents to the specified share. A + * separate XML tag is used rather than a property. This can also be + * used with resources. */ xmlNodePtr -_sa_set_share_description(sa_share_t share, char *content) +_sa_set_share_description(void *share, char *content) { xmlNodePtr node; node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description", @@ -2205,7 +2441,6 @@ sa_set_share_description(sa_share_t share, char *content) break; } } - group = sa_get_parent_group(share); /* no existing description but want to add */ if (node == NULL && content != NULL) { /* add a description */ @@ -2218,7 +2453,8 @@ sa_set_share_description(sa_share_t share, char *content) xmlUnlinkNode(node); xmlFreeNode(node); } - if (group != NULL && is_persistent((sa_group_t)share)) { + group = sa_get_parent_group(share); + if (group != NULL && sa_is_persistent(share)) { sa_handle_impl_t impl_handle; impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); if (impl_handle != NULL) { @@ -2269,7 +2505,7 @@ sa_get_share_description(sa_share_t share) } } if (node != NULL) { - description = xmlNodeGetContent((xmlNodePtr)share); + description = xmlNodeGetContent(node); fixproblemchars((char *)description); } return ((char *)description); @@ -2299,12 +2535,44 @@ sa_create_optionset(sa_group_t group, char *proto) { sa_optionset_t optionset; sa_group_t parent = group; + sa_share_t share = NULL; + int err = SA_OK; + char *id = NULL; optionset = sa_get_optionset(group, proto); if (optionset != NULL) { /* can't have a duplicate protocol */ optionset = NULL; } else { + /* + * Account for resource names being slightly + * different. + */ + if (sa_is_share(group)) { + /* + * Transient shares do not have an "id" so not an + * error to not find one. + */ + id = sa_get_share_attr((sa_share_t)group, "id"); + } else if (sa_is_resource(group)) { + share = sa_get_resource_parent( + (sa_resource_t)group); + id = sa_get_resource_attr(share, "id"); + + /* id can be NULL if the group is transient (ZFS) */ + if (id == NULL && sa_is_persistent(group)) + err = SA_NO_MEMORY; + } + if (err == SA_NO_MEMORY) { + /* + * Couldn't get the id for the share or + * resource. While this could be a + * configuration issue, it is most likely an + * out of memory. In any case, fail the create. + */ + return (NULL); + } + optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"optionset", NULL); /* @@ -2314,38 +2582,44 @@ sa_create_optionset(sa_group_t group, char *proto) if (optionset != NULL) { char oname[SA_STRSIZE]; char *groupname; - char *id = NULL; - if (sa_is_share(group)) + /* + * Need to get parent group in all cases, but also get + * the share if this is a resource. + */ + if (sa_is_share(group)) { parent = sa_get_parent_group((sa_share_t)group); + } else if (sa_is_resource(group)) { + share = sa_get_resource_parent( + (sa_resource_t)group); + parent = sa_get_parent_group(share); + } sa_set_optionset_attr(optionset, "type", proto); - if (sa_is_share(group)) { - id = sa_get_share_attr((sa_share_t)group, "id"); - } (void) sa_optionset_name(optionset, oname, sizeof (oname), id); groupname = sa_get_group_attr(parent, "name"); - if (groupname != NULL && is_persistent(group)) { + if (groupname != NULL && sa_is_persistent(group)) { sa_handle_impl_t impl_handle; - impl_handle = (sa_handle_impl_t) - sa_find_group_handle(group); + impl_handle = + (sa_handle_impl_t)sa_find_group_handle( + group); assert(impl_handle != NULL); if (impl_handle != NULL) { (void) sa_get_instance( - impl_handle->scfhandle, - groupname); + impl_handle->scfhandle, groupname); (void) sa_create_pgroup( impl_handle->scfhandle, oname); } } if (groupname != NULL) sa_free_attr_string(groupname); - if (id != NULL) - sa_free_attr_string(id); } } + + if (id != NULL) + sa_free_attr_string(id); return (optionset); } @@ -2388,7 +2662,7 @@ sa_get_optionset_parent(sa_optionset_t optionset) * * In order to avoid making multiple updates to a ZFS share when * setting properties, the share attribute "changed" will be set to - * true when a property is added or modifed. When done adding + * true when a property is added or modified. When done adding * properties, we can then detect that an update is needed. We then * clear the state here to detect additional changes. */ @@ -2470,7 +2744,7 @@ sa_commit_properties(sa_optionset_t optionset, int clear) /* * sa_destroy_optionset(optionset) * - * Remove the optionset from its group. Update the repostory to + * Remove the optionset from its group. Update the repository to * reflect this change. */ @@ -2486,9 +2760,16 @@ sa_destroy_optionset(sa_optionset_t optionset) /* now delete the prop group */ group = sa_get_optionset_parent(optionset); - if (group != NULL && sa_is_share(group)) { - ispersist = is_persistent(group); - id = sa_get_share_attr((sa_share_t)group, "id"); + if (group != NULL) { + if (sa_is_resource(group)) { + sa_resource_t resource = group; + sa_share_t share = sa_get_resource_parent(resource); + group = sa_get_parent_group(share); + id = sa_get_share_attr(share, "id"); + } else if (sa_is_share(group)) { + id = sa_get_share_attr((sa_share_t)group, "id"); + } + ispersist = sa_is_persistent(group); } if (ispersist) { sa_handle_impl_t impl_handle; @@ -2559,7 +2840,7 @@ sa_create_security(sa_group_t group, char *sectype, char *proto) sa_set_security_attr(security, "sectype", sectype); (void) sa_security_name(security, oname, sizeof (oname), id); - if (groupname != NULL && is_persistent(group)) { + if (groupname != NULL && sa_is_persistent(group)) { sa_handle_impl_t impl_handle; impl_handle = (sa_handle_impl_t)sa_find_group_handle( @@ -2603,7 +2884,7 @@ sa_destroy_security(sa_security_t security) if (group != NULL && !iszfs) { if (sa_is_share(group)) - ispersist = is_persistent(group); + ispersist = sa_is_persistent(group); id = sa_get_share_attr((sa_share_t)group, "id"); } if (ispersist) { @@ -2666,7 +2947,6 @@ is_nodetype(void *node, char *type) return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0); } - /* * add_or_update() * @@ -2721,12 +3001,12 @@ sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group, int opttype; /* 1 == optionset, 0 == security */ char *id = NULL; int iszfs = 0; - int isshare = 0; sa_group_t parent = NULL; + sa_share_t share = NULL; sa_handle_impl_t impl_handle; scfutilhandle_t *scf_handle; - if (!is_persistent(group)) { + if (!sa_is_persistent(group)) { /* * if the group/share is not persistent we don't need * to do anything here @@ -2742,12 +3022,20 @@ sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group, entry = scf_entry_create(scf_handle->handle); opttype = is_nodetype((void *)optionset, "optionset"); + /* + * Check for share vs. resource since they need slightly + * different treatment given the hierarchy. + */ if (valstr != NULL && entry != NULL) { if (sa_is_share(group)) { - isshare = 1; parent = sa_get_parent_group(group); + share = (sa_share_t)group; if (parent != NULL) iszfs = is_zfs_group(parent); + } else if (sa_is_resource(group)) { + share = sa_get_parent_group(group); + if (share != NULL) + parent = sa_get_parent_group(share); } else { iszfs = is_zfs_group(group); } @@ -2755,15 +3043,13 @@ sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group, if (scf_handle->trans == NULL) { char oname[SA_STRSIZE]; char *groupname = NULL; - if (isshare) { - if (parent != NULL) { + if (share != NULL) { + if (parent != NULL) groupname = sa_get_group_attr(parent, "name"); - } - id = - sa_get_share_attr((sa_share_t)group, - "id"); + id = sa_get_share_attr( + (sa_share_t)share, "id"); } else { groupname = sa_get_group_attr(group, "name"); @@ -2870,14 +3156,22 @@ sa_add_property(void *object, sa_property_t property) sa_free_attr_string(proto); parent = sa_get_parent_group(object); - if (!is_persistent(parent)) { + if (!sa_is_persistent(parent)) return (ret); - } - if (sa_is_share(parent)) + if (sa_is_resource(parent)) { + /* + * Resources are children of share. Need to go up two + * levels to find the group but the parent needs to be + * the share at this point in order to get the "id". + */ + parent = sa_get_parent_group(parent); group = sa_get_parent_group(parent); - else + } else if (sa_is_share(parent)) { + group = sa_get_parent_group(parent); + } else { group = parent; + } if (property == NULL) { ret = SA_NO_MEMORY; @@ -3110,7 +3404,7 @@ sa_set_protocol_property(sa_property_t prop, char *value) /* * sa_add_protocol_property(propset, prop) * - * Add a new property to the protocol sepcific property set. + * Add a new property to the protocol specific property set. */ int @@ -3128,7 +3422,7 @@ sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop) /* * sa_create_protocol_properties(proto) * - * Create a protocol specifity property set. + * Create a protocol specific property set. */ sa_protocol_properties_t @@ -3141,3 +3435,583 @@ sa_create_protocol_properties(char *proto) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); return (node); } + +/* + * sa_get_share_resource(share, resource) + * + * Get the named resource from the share, if it exists. If resource is + * NULL, get the first resource. + */ + +sa_resource_t +sa_get_share_resource(sa_share_t share, char *resource) +{ + xmlNodePtr node = NULL; + xmlChar *name; + + if (share != NULL) { + for (node = ((xmlNodePtr)share)->children; node != NULL; + node = node->next) { + if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) { + if (resource == NULL) { + /* + * We are looking for the first + * resource node and not a names + * resource. + */ + break; + } else { + /* is it the correct share? */ + name = xmlGetProp(node, + (xmlChar *)"name"); + if (name != NULL && + xmlStrcasecmp(name, + (xmlChar *)resource) == 0) { + xmlFree(name); + break; + } + xmlFree(name); + } + } + } + } + return ((sa_resource_t)node); +} + +/* + * sa_get_next_resource(resource) + * Return the next share following the specified share + * from the internal list of shares. Returns NULL if there + * are no more shares. The list is relative to the same + * group. + */ +sa_share_t +sa_get_next_resource(sa_resource_t resource) +{ + xmlNodePtr node = NULL; + + if (resource != NULL) { + for (node = ((xmlNodePtr)resource)->next; node != NULL; + node = node->next) { + if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) + break; + } + } + return ((sa_share_t)node); +} + +/* + * _sa_get_next_resource_index(share) + * + * get the next resource index number (one greater then current largest) + */ + +static int +_sa_get_next_resource_index(sa_share_t share) +{ + sa_resource_t resource; + int index = 0; + char *id; + + for (resource = sa_get_share_resource(share, NULL); + resource != NULL; + resource = sa_get_next_resource(resource)) { + id = get_node_attr((void *)resource, "id"); + if (id != NULL) { + int val; + val = atoi(id); + if (val > index) + index = val; + sa_free_attr_string(id); + } + } + return (index + 1); +} + + +/* + * sa_add_resource(share, resource, persist, &err) + * + * Adds a new resource name associated with share. The resource name + * must be unique in the system and will be case insensitive (eventually). + */ + +sa_resource_t +sa_add_resource(sa_share_t share, char *resource, int persist, int *error) +{ + xmlNodePtr node; + int err = SA_OK; + sa_resource_t res; + sa_group_t group; + sa_handle_t handle; + char istring[8]; /* just big enough for an integer value */ + int index; + + group = sa_get_parent_group(share); + handle = sa_find_group_handle(group); + res = sa_find_resource(handle, resource); + if (res != NULL) { + err = SA_DUPLICATE_NAME; + res = NULL; + } else { + node = xmlNewChild((xmlNodePtr)share, NULL, + (xmlChar *)"resource", NULL); + if (node != NULL) { + xmlSetProp(node, (xmlChar *)"name", + (xmlChar *)resource); + xmlSetProp(node, (xmlChar *)"type", persist ? + (xmlChar *)"persist" : (xmlChar *)"transient"); + if (persist != SA_SHARE_TRANSIENT) { + index = _sa_get_next_resource_index(share); + (void) snprintf(istring, sizeof (istring), "%d", + index); + xmlSetProp(node, (xmlChar *)"id", + (xmlChar *)istring); + if (!sa_group_is_zfs(group) && + sa_is_persistent((sa_group_t)share)) { + /* ZFS doesn't use resource names */ + sa_handle_impl_t ihandle; + ihandle = (sa_handle_impl_t) + sa_find_group_handle( + group); + if (ihandle != NULL) + err = sa_commit_share( + ihandle->scfhandle, group, + share); + else + err = SA_SYSTEM_ERR; + } + } + } + } + if (error != NULL) + *error = err; + return ((sa_resource_t)node); +} + +/* + * sa_remove_resource(resource) + * + * Remove the resource name from the share (and the system) + */ + +int +sa_remove_resource(sa_resource_t resource) +{ + sa_share_t share; + sa_group_t group; + char *type; + int ret = SA_OK; + int transient = 0; + + share = sa_get_resource_parent(resource); + type = sa_get_share_attr(share, "type"); + group = sa_get_parent_group(share); + + + if (type != NULL) { + if (strcmp(type, "persist") != 0) + transient = 1; + sa_free_attr_string(type); + } + + /* Remove from the share */ + xmlUnlinkNode((xmlNode *)resource); + xmlFreeNode((xmlNode *)resource); + + /* only do SMF action if permanent and not ZFS */ + if (!transient && !sa_group_is_zfs(group)) { + sa_handle_impl_t ihandle; + ihandle = (sa_handle_impl_t)sa_find_group_handle(group); + if (ihandle != NULL) + ret = sa_commit_share(ihandle->scfhandle, group, share); + else + ret = SA_SYSTEM_ERR; + } + return (ret); +} + +/* + * proto_resource_rename(handle, group, resource, newname) + * + * Helper function for sa_rename_resource that notifies the protocol + * of a resource name change prior to a config repository update. + */ +static int +proto_rename_resource(sa_handle_t handle, sa_group_t group, + sa_resource_t resource, char *newname) +{ + sa_optionset_t optionset; + int ret = SA_OK; + int err; + + for (optionset = sa_get_optionset(group, NULL); + optionset != NULL; + optionset = sa_get_next_optionset(optionset)) { + char *type; + type = sa_get_optionset_attr(optionset, "type"); + if (type != NULL) { + err = sa_proto_rename_resource(handle, type, resource, + newname); + if (err != SA_OK) + ret = err; + sa_free_attr_string(type); + } + } + return (ret); +} + +/* + * sa_rename_resource(resource, newname) + * + * Rename the resource to the new name, if it is unique. + */ + +int +sa_rename_resource(sa_resource_t resource, char *newname) +{ + sa_share_t share; + sa_group_t group = NULL; + sa_resource_t target; + int ret = SA_CONFIG_ERR; + sa_handle_t handle = NULL; + + share = sa_get_resource_parent(resource); + if (share == NULL) + return (ret); + + group = sa_get_parent_group(share); + if (group == NULL) + return (ret); + + handle = (sa_handle_impl_t)sa_find_group_handle(group); + if (handle == NULL) + return (ret); + + target = sa_find_resource(handle, newname); + if (target != NULL) { + ret = SA_DUPLICATE_NAME; + } else { + /* + * Everything appears to be valid at this + * point. Change the name of the active share and then + * update the share in the appropriate repository. + */ + ret = proto_rename_resource(handle, group, resource, newname); + set_node_attr(resource, "name", newname); + if (!sa_group_is_zfs(group) && + sa_is_persistent((sa_group_t)share)) { + sa_handle_impl_t ihandle = (sa_handle_impl_t)handle; + ret = sa_commit_share(ihandle->scfhandle, group, + share); + } + } + return (ret); +} + +/* + * sa_get_resource_attr(resource, tag) + * + * Get the named attribute of the resource. "name" and "id" are + * currently defined. NULL if tag not defined. + */ + +char * +sa_get_resource_attr(sa_resource_t resource, char *tag) +{ + return (get_node_attr((void *)resource, tag)); +} + +/* + * sa_set_resource_attr(resource, tag, value) + * + * Get the named attribute of the resource. "name" and "id" are + * currently defined. NULL if tag not defined. Currently we don't do + * much, but additional checking may be needed in the future. + */ + +int +sa_set_resource_attr(sa_resource_t resource, char *tag, char *value) +{ + set_node_attr((void *)resource, tag, value); + return (SA_OK); +} + +/* + * sa_get_resource_parent(resource_t) + * + * Returns the share associated with the resource. + */ + +sa_share_t +sa_get_resource_parent(sa_resource_t resource) +{ + sa_share_t share = NULL; + + if (resource != NULL) + share = (sa_share_t)((xmlNodePtr)resource)->parent; + return (share); +} + +/* + * find_resource(group, name) + * + * Find the resource within the group. + */ + +static sa_resource_t +find_resource(sa_group_t group, char *resname) +{ + sa_share_t share; + sa_resource_t resource = NULL; + char *name; + + /* Iterate over all the shares and resources in the group. */ + for (share = sa_get_share(group, NULL); + share != NULL && resource == NULL; + share = sa_get_next_share(share)) { + for (resource = sa_get_share_resource(share, NULL); + resource != NULL; + resource = sa_get_next_resource(resource)) { + name = sa_get_resource_attr(resource, "name"); + if (name != NULL && xmlStrcasecmp((xmlChar*)name, + (xmlChar*)resname) == 0) { + sa_free_attr_string(name); + break; + } + if (name != NULL) { + sa_free_attr_string(name); + } + } + } + return (resource); +} + +/* + * sa_find_resource(name) + * + * Find the named resource in the system. + */ + +sa_resource_t +sa_find_resource(sa_handle_t handle, char *name) +{ + sa_group_t group; + sa_group_t zgroup; + sa_resource_t resource = NULL; + + /* + * Iterate over all groups and zfs subgroups and check for + * resource name in them. + */ + for (group = sa_get_group(handle, NULL); group != NULL; + group = sa_get_next_group(group)) { + + if (is_zfs_group(group)) { + for (zgroup = + (sa_group_t)_sa_get_child_node((xmlNodePtr)group, + (xmlChar *)"group"); + zgroup != NULL && resource == NULL; + zgroup = sa_get_next_group(zgroup)) { + resource = find_resource(zgroup, name); + } + } else { + resource = find_resource(group, name); + } + if (resource != NULL) + break; + } + return (resource); +} + +/* + * sa_get_resource(group, resource) + * + * Search all the shares in the specified group for a share with a + * resource name matching the one specified. + * + * In the future, it may be advantageous to allow group to be NULL and + * search all groups but that isn't needed at present. + */ + +sa_resource_t +sa_get_resource(sa_group_t group, char *resource) +{ + sa_share_t share = NULL; + sa_resource_t res = NULL; + + if (resource != NULL) { + for (share = sa_get_share(group, NULL); + share != NULL && res == NULL; + share = sa_get_next_share(share)) { + res = sa_get_share_resource(share, resource); + } + } + return (res); +} + +/* + * sa_enable_resource, protocol) + * Disable the specified share to the specified protocol. + * If protocol is NULL, then all protocols. + */ +int +sa_enable_resource(sa_resource_t resource, char *protocol) +{ + int ret = SA_OK; + char **protocols; + int numproto; + + if (protocol != NULL) { + ret = sa_proto_share_resource(protocol, resource); + } else { + /* need to do all protocols */ + if ((numproto = sa_get_protocols(&protocols)) >= 0) { + int i, err; + for (i = 0; i < numproto; i++) { + err = sa_proto_share_resource( + protocols[i], resource); + if (err != SA_OK) + ret = err; + } + free(protocols); + } + } + if (ret == SA_OK) + (void) sa_set_resource_attr(resource, "shared", NULL); + + return (ret); +} + +/* + * sa_disable_resource(resource, protocol) + * + * Disable the specified share for the specified protocol. If + * protocol is NULL, then all protocols. If the underlying + * protocol doesn't implement disable at the resource level, we + * disable at the share level. + */ +int +sa_disable_resource(sa_resource_t resource, char *protocol) +{ + int ret = SA_OK; + char **protocols; + int numproto; + + if (protocol != NULL) { + ret = sa_proto_unshare_resource(protocol, resource); + if (ret == SA_NOT_IMPLEMENTED) { + sa_share_t parent; + /* + * The protocol doesn't implement unshare + * resource. That implies that resource names are + * simple aliases for this protocol so we need to + * unshare the share. + */ + parent = sa_get_resource_parent(resource); + if (parent != NULL) + ret = sa_disable_share(parent, protocol); + else + ret = SA_CONFIG_ERR; + } + } else { + /* need to do all protocols */ + if ((numproto = sa_get_protocols(&protocols)) >= 0) { + int i, err; + for (i = 0; i < numproto; i++) { + err = sa_proto_unshare_resource(protocols[i], + resource); + if (err == SA_NOT_SUPPORTED) { + sa_share_t parent; + parent = sa_get_resource_parent( + resource); + if (parent != NULL) + err = sa_disable_share(parent, + protocols[i]); + else + err = SA_CONFIG_ERR; + } + if (err != SA_OK) + ret = err; + } + free(protocols); + } + } + if (ret == SA_OK) + (void) sa_set_resource_attr(resource, "shared", NULL); + + return (ret); +} + +/* + * sa_set_resource_description(resource, content) + * + * Set the description of share to content. + */ + +int +sa_set_resource_description(sa_resource_t resource, char *content) +{ + xmlNodePtr node; + sa_group_t group; + sa_share_t share; + int ret = SA_OK; + + for (node = ((xmlNodePtr)resource)->children; + node != NULL; + node = node->next) { + if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { + break; + } + } + + /* no existing description but want to add */ + if (node == NULL && content != NULL) { + /* add a description */ + node = _sa_set_share_description(resource, content); + } else if (node != NULL && content != NULL) { + /* update a description */ + xmlNodeSetContent(node, (xmlChar *)content); + } else if (node != NULL && content == NULL) { + /* remove an existing description */ + xmlUnlinkNode(node); + xmlFreeNode(node); + } + share = sa_get_resource_parent(resource); + group = sa_get_parent_group(share); + if (group != NULL && sa_is_persistent(share)) { + sa_handle_impl_t impl_handle; + impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); + if (impl_handle != NULL) + ret = sa_commit_share(impl_handle->scfhandle, + group, share); + else + ret = SA_SYSTEM_ERR; + } + return (ret); +} + +/* + * sa_get_resource_description(share) + * + * Return the description text for the specified share if it + * exists. NULL if no description exists. + */ + +char * +sa_get_resource_description(sa_resource_t resource) +{ + xmlChar *description = NULL; + xmlNodePtr node; + + for (node = ((xmlNodePtr)resource)->children; node != NULL; + node = node->next) { + if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) + break; + } + if (node != NULL) { + description = xmlNodeGetContent(node); + fixproblemchars((char *)description); + } + return ((char *)description); +} diff --git a/usr/src/lib/libshare/common/libshare.h b/usr/src/lib/libshare/common/libshare.h index c78b0822c6c1..bdac6a6d645c 100644 --- a/usr/src/lib/libshare/common/libshare.h +++ b/usr/src/lib/libshare/common/libshare.h @@ -37,6 +37,8 @@ extern "C" { #endif +#include + /* * Basic datatypes for most functions */ @@ -46,6 +48,7 @@ typedef void *sa_property_t; typedef void *sa_optionset_t; typedef void *sa_security_t; typedef void *sa_protocol_properties_t; +typedef void *sa_resource_t; typedef void *sa_handle_t; /* opaque handle to access core functions */ @@ -77,6 +80,11 @@ typedef void *sa_handle_t; /* opaque handle to access core functions */ #define SA_NOT_SUPPORTED 21 /* operation not supported for proto */ #define SA_PROP_SHARE_ONLY 22 /* property valid on share only */ #define SA_NOT_SHARED 23 /* path is not shared */ +#define SA_NO_SUCH_RESOURCE 24 /* resource not found */ +#define SA_RESOURCE_REQUIRED 25 /* resource name is required */ +#define SA_MULTIPLE_ERROR 26 /* multiple protocols reported error */ +#define SA_PATH_IS_SUBDIR 27 /* check_path found path is subdir */ +#define SA_PATH_IS_PARENTDIR 28 /* check_path found path is parent */ /* API Initialization */ #define SA_INIT_SHARE_API 0x0001 /* init share specific interface */ @@ -90,8 +98,9 @@ typedef void *sa_handle_t; /* opaque handle to access core functions */ */ #define SA_MAX_NAME_LEN 100 /* must fit service instance name */ +#define SA_MAX_RESOURCE_NAME 255 /* Maximum length of resource name */ -/* Used in calls to sa_add_share() */ +/* Used in calls to sa_add_share() and sa_add_resource() */ #define SA_SHARE_TRANSIENT 0 /* shared but not across reboot */ #define SA_SHARE_LEGACY 1 /* share is in dfstab only */ #define SA_SHARE_PERMANENT 2 /* share goes to repository */ @@ -104,6 +113,16 @@ typedef void *sa_handle_t; /* opaque handle to access core functions */ #define SA_RBAC_MANAGE "solaris.smf.manage.shares" #define SA_RBAC_VALUE "solaris.smf.value.shares" +/* + * Feature set bit definitions + */ + +#define SA_FEATURE_NONE 0x0000 /* no feature flags set */ +#define SA_FEATURE_RESOURCE 0x0001 /* resource names are required */ +#define SA_FEATURE_DFSTAB 0x0002 /* need to manage in dfstab */ +#define SA_FEATURE_ALLOWSUBDIRS 0x0004 /* allow subdirs to be shared */ +#define SA_FEATURE_ALLOWPARDIRS 0x0008 /* allow parent dirs to be shared */ + /* * legacy files */ @@ -143,7 +162,6 @@ extern int sa_check_path(sa_group_t, char *, int); extern int sa_move_share(sa_group_t, sa_share_t); extern int sa_remove_share(sa_share_t); extern sa_share_t sa_get_share(sa_group_t, char *); -extern sa_share_t sa_get_resource(sa_group_t, char *); extern sa_share_t sa_find_share(sa_handle_t, char *); extern sa_share_t sa_get_next_share(sa_share_t); extern char *sa_get_share_attr(sa_share_t, char *); @@ -152,9 +170,26 @@ extern sa_group_t sa_get_parent_group(sa_share_t); extern int sa_set_share_attr(sa_share_t, char *, char *); extern int sa_set_share_description(sa_share_t, char *); extern int sa_enable_share(sa_group_t, char *); -extern int sa_disable_share(sa_group_t, char *); +extern int sa_disable_share(sa_share_t, char *); extern int sa_is_share(void *); +/* resource name related */ +extern sa_resource_t sa_find_resource(sa_handle_t, char *); +extern sa_resource_t sa_get_resource(sa_group_t, char *); +extern sa_resource_t sa_get_next_resource(sa_resource_t); +extern sa_share_t sa_get_resource_parent(sa_resource_t); +extern sa_resource_t sa_get_share_resource(sa_share_t, char *); +extern sa_resource_t sa_add_resource(sa_share_t, char *, int, int *); +extern int sa_remove_resource(sa_resource_t); +extern char *sa_get_resource_attr(sa_resource_t, char *); +extern int sa_set_resource_attr(sa_resource_t, char *, char *); +extern int sa_set_resource_description(sa_resource_t, char *); +extern char *sa_get_resource_description(sa_resource_t); +extern int sa_enable_resource(sa_resource_t, char *); +extern int sa_disable_resource(sa_resource_t, char *); +extern int sa_rename_resource(sa_resource_t, char *); +extern void sa_fix_resource_name(char *); + /* data structure free calls */ extern void sa_free_attr_string(char *); extern void sa_free_share_description(char *); @@ -179,6 +214,7 @@ extern int sa_update_property(sa_property_t, char *); extern int sa_remove_property(sa_property_t); extern int sa_commit_properties(sa_optionset_t, int); extern int sa_valid_property(void *, char *, sa_property_t); +extern int sa_is_persistent(void *); /* security control */ extern sa_security_t sa_get_security(sa_group_t, char *, char *); @@ -196,6 +232,7 @@ extern int sa_parse_legacy_options(sa_group_t, char *, char *); extern char *sa_proto_legacy_format(char *, sa_group_t, int); extern int sa_is_security(char *, char *); extern sa_protocol_properties_t sa_proto_get_properties(char *); +extern uint64_t sa_proto_get_featureset(char *); extern sa_property_t sa_get_protocol_property(sa_protocol_properties_t, char *); extern sa_property_t sa_get_next_protocol_property(sa_property_t); extern int sa_set_protocol_property(sa_property_t, char *); @@ -206,9 +243,12 @@ extern int sa_add_protocol_property(sa_protocol_properties_t, sa_property_t); extern int sa_proto_valid_prop(char *, sa_property_t, sa_optionset_t); extern int sa_proto_valid_space(char *, char *); extern char *sa_proto_space_alias(char *, char *); +extern int sa_proto_get_transients(sa_handle_t, char *); +extern int sa_proto_notify_resource(sa_resource_t, char *); +extern int sa_proto_change_notify(sa_share_t, char *); /* handle legacy (dfstab/sharetab) files */ -extern int sa_delete_legacy(sa_share_t); +extern int sa_delete_legacy(sa_share_t, char *); extern int sa_update_legacy(sa_share_t, char *); extern int sa_update_sharetab(sa_share_t, char *); extern int sa_delete_sharetab(char *, char *); diff --git a/usr/src/lib/libshare/common/libshare_impl.h b/usr/src/lib/libshare/common/libshare_impl.h index a454e3ef7506..ab661e432f71 100644 --- a/usr/src/lib/libshare/common/libshare_impl.h +++ b/usr/src/lib/libshare/common/libshare_impl.h @@ -72,6 +72,13 @@ struct sa_plugin_ops { char *(*sa_space_alias)(char *); int (*sa_update_legacy)(sa_share_t); int (*sa_delete_legacy)(sa_share_t); + int (*sa_change_notify)(sa_share_t); + int (*sa_enable_resource)(sa_resource_t); + int (*sa_disable_resource)(sa_resource_t); + uint64_t (*sa_features)(void); + int (*sa_get_transient_shares)(sa_handle_t); /* add transients */ + int (*sa_notify_resource)(sa_resource_t); + int (*sa_rename_resource)(sa_handle_t, sa_resource_t, char *); int (*sa_run_command)(int, int, char **); /* proto specific */ int (*sa_command_help)(); }; @@ -97,6 +104,8 @@ extern int sa_proto_unshare(sa_share_t, char *, char *); extern int sa_proto_valid_prop(char *, sa_property_t, sa_optionset_t); extern int sa_proto_security_prop(char *, char *); extern int sa_proto_legacy_opts(char *, sa_group_t, char *); +extern int sa_proto_share_resource(char *, sa_resource_t); +extern int sa_proto_unshare_resource(char *, sa_resource_t); /* internal utility functions */ extern sa_optionset_t sa_get_derived_optionset(sa_group_t, char *, int); @@ -121,7 +130,7 @@ extern void sa_emptyshare(struct share *sh); /* ZFS functions */ extern int sa_get_zfs_shares(sa_handle_t, char *); extern int sa_zfs_update(sa_share_t); -extern int sa_share_zfs(sa_share_t, char *, share_t *, void *, boolean_t); +extern int sa_share_zfs(sa_share_t, char *, share_t *, void *, zfs_share_op_t); extern int sa_sharetab_fill_zfs(sa_share_t share, struct share *sh, char *proto); @@ -131,6 +140,8 @@ extern void proto_plugin_fini(); extern int sa_proto_set_property(char *, sa_property_t); extern int sa_proto_delete_legacy(char *, sa_share_t); extern int sa_proto_update_legacy(char *, sa_share_t); +extern int sa_proto_rename_resource(sa_handle_t, char *, + sa_resource_t, char *); #define PL_TYPE_PROPERTY 0 #define PL_TYPE_SECURITY 1 diff --git a/usr/src/lib/libshare/common/libshare_zfs.c b/usr/src/lib/libshare/common/libshare_zfs.c index 1cfbcbe72a4c..e0ee84ef8a93 100644 --- a/usr/src/lib/libshare/common/libshare_zfs.c +++ b/usr/src/lib/libshare/common/libshare_zfs.c @@ -36,7 +36,7 @@ #include #include -extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *); +extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *, uint64_t); extern sa_group_t _sa_create_zfs_group(sa_group_t, char *); extern char *sa_fstype(char *); extern void set_node_attr(void *, char *, char *); @@ -109,7 +109,7 @@ sa_zfs_fini(sa_handle_impl_t impl_handle) /* * get_one_filesystem(zfs_handle_t, data) * - * an interator function called while iterating through the ZFS + * an iterator function called while iterating through the ZFS * root. It accumulates into an array of file system handles that can * be used to derive info about those file systems. * @@ -390,7 +390,7 @@ sa_zfs_is_shared(sa_handle_t sahandle, char *path) } /* - * find_or_create_group(groupname, proto, *err) + * find_or_create_group(handle, groupname, proto, *err) * * While walking the ZFS tree, we need to add shares to a defined * group. If the group doesn't exist, create it first, making sure it @@ -424,19 +424,8 @@ find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err) if (group != NULL) { if (proto != NULL) { optionset = sa_get_optionset(group, proto); - if (optionset == NULL) { + if (optionset == NULL) optionset = sa_create_optionset(group, proto); - } else { - char **protolist; - int numprotos, i; - numprotos = sa_get_protocols(&protolist); - for (i = 0; i < numprotos; i++) { - optionset = sa_create_optionset(group, - protolist[i]); - } - if (protolist != NULL) - free(protolist); - } } } if (err != NULL) @@ -457,8 +446,8 @@ find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err) */ static sa_group_t -find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname, - char *optstring, int *err) +find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname, char *proto, + char *optstring, int *err) { sa_group_t group = NULL; sa_group_t zfs; @@ -494,30 +483,58 @@ find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname, options = strdup(optstring); if (options != NULL) { *err = sa_parse_legacy_options(group, - options, "nfs"); + options, proto); + + /* If no optionset, add one */ + if (sa_get_optionset(group, proto) == + NULL) + (void) sa_create_optionset( + group, proto); free(options); } else { *err = SA_NO_MEMORY; } } + } else if (proto != NULL && strcmp(proto, "smb") == 0) { + *err = SA_PROP_SHARE_ONLY; } } return (group); } +/* + * zfs_construct_resource(share, name, base, dataset) + * + * Add a resource to the share using name as a template. If name == + * NULL, then construct a name based on the dataset value. + * name. + */ +static void +zfs_construct_resource(sa_share_t share, char *dataset) +{ + char buff[SA_MAX_RESOURCE_NAME + 1]; + int ret = SA_OK; + + (void) snprintf(buff, SA_MAX_RESOURCE_NAME, "%s", dataset); + sa_fix_resource_name(buff); + (void) sa_add_resource(share, buff, SA_SHARE_TRANSIENT, &ret); +} + /* * zfs_inherited(handle, source, sourcestr) * - * handle case of inherited sharenfs. Pulled out of sa_get_zfs_shares + * handle case of inherited share{nfs,smb}. Pulled out of sa_get_zfs_shares * for readability. */ static int zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr, - char *shareopts, char *mountpoint) + char *shareopts, char *mountpoint, char *proto, char *dataset) { int doshopt = 0; int err = SA_OK; sa_group_t group; + sa_resource_t resource; + uint64_t features; /* * Need to find the "real" parent sub-group. It may not be @@ -525,11 +542,18 @@ zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr, * variable. The real parent not mounted can occur if * "canmount=off and sharenfs=on". */ - group = find_or_create_zfs_subgroup(handle, sourcestr, shareopts, - &doshopt); + group = find_or_create_zfs_subgroup(handle, sourcestr, proto, + shareopts, &doshopt); if (group != NULL) { - share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT, - &err); + /* + * We may need the first share for resource + * prototype. We only care about it if it has a + * resource that sets a prefix value. + */ + if (share == NULL) + share = _sa_add_share(group, mountpoint, + SA_SHARE_TRANSIENT, &err, + (uint64_t)SA_FEATURE_NONE); /* * some options may only be on shares. If the opt * string contains one of those, we put it just on the @@ -539,10 +563,27 @@ zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr, char *options; options = strdup(shareopts); if (options != NULL) { + set_node_attr(share, "dataset", dataset); err = sa_parse_legacy_options(share, options, - "nfs"); + proto); + set_node_attr(share, "dataset", NULL); free(options); } + if (sa_get_optionset(group, proto) == NULL) + (void) sa_create_optionset(group, proto); + } + features = sa_proto_get_featureset(proto); + if (share != NULL && features & SA_FEATURE_RESOURCE) { + /* + * We have a share and the protocol requires + * that at least one resource exist (probably + * SMB). We need to make sure that there is at + * least one. + */ + resource = sa_get_share_resource(share, NULL); + if (resource == NULL) { + zfs_construct_resource(share, dataset); + } } } else { err = SA_NO_MEMORY; @@ -557,45 +598,60 @@ zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr, * of sa_get_zfs_shares for readability. */ static int -zfs_notinherited(sa_group_t group, char *mountpoint, char *shareopts) +zfs_notinherited(sa_group_t group, sa_share_t share, char *mountpoint, + char *shareopts, char *proto, char *dataset) { int err = SA_OK; - sa_share_t share; - char *options; + sa_resource_t resource; + uint64_t features; set_node_attr(group, "zfs", "true"); - share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT, &err); + if (share == NULL) + share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT, + &err, (uint64_t)SA_FEATURE_NONE); if (err == SA_OK) { if (strcmp(shareopts, "on") == 0) - shareopts = "rw"; - - options = strdup(shareopts); - if (options != NULL) { - err = sa_parse_legacy_options(group, options, - "nfs"); - free(options); - } - if (err == SA_PROP_SHARE_ONLY) { - /* - * Same as above, some properties may - * only be on shares, but due to the - * ZFS sub-groups being artificial, we - * sometimes get this and have to deal - * with it. We do it by attempting to - * put it on the share. - */ + shareopts = ""; + if (shareopts != NULL) { + char *options; options = strdup(shareopts); if (options != NULL) { - err = sa_parse_legacy_options(share, - options, "nfs"); + err = sa_parse_legacy_options(group, options, + proto); free(options); } + if (err == SA_PROP_SHARE_ONLY) { + /* + * Same as above, some properties may + * only be on shares, but due to the + * ZFS sub-groups being artificial, we + * sometimes get this and have to deal + * with it. We do it by attempting to + * put it on the share. + */ + options = strdup(shareopts); + if (options != NULL) { + err = sa_parse_legacy_options(share, + options, proto); + free(options); + } + } + /* unmark the share's changed state */ + set_node_attr(share, "changed", NULL); + } + features = sa_proto_get_featureset(proto); + if (share != NULL && features & SA_FEATURE_RESOURCE) { + /* + * We have a share and the protocol requires + * that at least one resource exist (probably + * SMB). We need to make sure that there is at + * least one. + */ + resource = sa_get_share_resource(share, NULL); + if (resource == NULL) { + zfs_construct_resource(share, dataset); + } } - /* Mark as the defining node of the subgroup */ - set_node_attr(share, "subgroup", "true"); - - /* unmark the share's changed state */ - set_node_attr(share, "changed", NULL); } return (err); } @@ -618,6 +674,46 @@ zfs_grp_error(int err) } } +/* + * zfs_process_share(handle, share, mountpoint, proto, source, + * shareopts, sourcestr) + * + * Creates the subgroup, if necessary and adds shares and adds shares + * and properties. + */ +static int +zfs_process_share(sa_handle_t handle, sa_group_t group, sa_share_t share, + char *mountpoint, char *proto, zprop_source_t source, char *shareopts, + char *sourcestr, char *dataset) +{ + int err = SA_OK; + + if (source & ZPROP_SRC_INHERITED) { + err = zfs_inherited(handle, share, sourcestr, shareopts, + mountpoint, proto, dataset); + } else { + group = find_or_create_zfs_subgroup(handle, dataset, proto, + shareopts, &err); + if (group == NULL) { + static int err = 0; + /* + * there is a problem, but we can't do + * anything about it at this point so we issue + * a warning an move on. + */ + zfs_grp_error(err); + err = 1; + } + set_node_attr(group, "zfs", "true"); + /* + * Add share with local opts via zfs_notinherited. + */ + err = zfs_notinherited(group, share, mountpoint, shareopts, + proto, dataset); + } + return (err); +} + /* * sa_get_zfs_shares(handle, groupname) * @@ -652,7 +748,7 @@ sa_get_zfs_shares(sa_handle_t handle, char *groupname) if (zfs_libhandle == NULL) return (SA_SYSTEM_ERR); - zfsgroup = find_or_create_group(handle, groupname, "nfs", &err); + zfsgroup = find_or_create_group(handle, groupname, NULL, &err); if (zfsgroup == NULL) return (legacy); @@ -666,6 +762,7 @@ sa_get_zfs_shares(sa_handle_t handle, char *groupname) group = zfsgroup; for (i = 0; i < count; i++) { char *dataset; + int foundnfs = 0; source = ZPROP_SRC_ALL; /* If no mountpoint, skip. */ @@ -694,8 +791,9 @@ sa_get_zfs_shares(sa_handle_t handle, char *groupname) ZFS_MAXPROPLEN, B_FALSE) == 0 && strcmp(shareopts, "off") != 0) { /* it is shared so add to list */ - share = sa_find_share(handle, mountpoint); err = SA_OK; + foundnfs = 1; + share = sa_find_share(handle, mountpoint); if (share != NULL) { /* * A zfs file system had been shared @@ -711,36 +809,36 @@ sa_get_zfs_shares(sa_handle_t handle, char *groupname) share = NULL; } if (err == SA_OK) { - if (source & ZPROP_SRC_INHERITED) { - err = zfs_inherited(handle, - share, sourcestr, - shareopts, mountpoint); - } else { - group = _sa_create_zfs_group( - zfsgroup, dataset); - if (group == NULL) { - static int err = 0; - /* - * there is a problem, - * but we can't do - * anything about it - * at this point so we - * issue a warning an - * move on. - */ - zfs_grp_error(err); - err = 1; - continue; - } - set_node_attr(group, "zfs", - "true"); - /* - * Add share with local opts via - * zfs_notinherited. - */ - err = zfs_notinherited(group, - mountpoint, shareopts); - } + err = zfs_process_share(handle, group, + share, mountpoint, "nfs", source, + shareopts, sourcestr, dataset); + } + } + if (zfs_prop_get(zlist[i], ZFS_PROP_SHARESMB, shareopts, + sizeof (shareopts), &source, sourcestr, + ZFS_MAXPROPLEN, B_FALSE) == 0 && + strcmp(shareopts, "off") != 0) { + /* it is shared so add to list */ + err = SA_OK; + share = sa_find_share(handle, mountpoint); + if (share != NULL && !foundnfs) { + /* + * A zfs file system had been shared + * through traditional methods + * (share/dfstab or added to a non-zfs + * group. Now it has been added to a + * ZFS group via the zfs + * command. Remove from previous + * config and setup with current + * options. + */ + err = sa_remove_share(share); + share = NULL; + } + if (err == SA_OK) { + err = zfs_process_share(handle, group, + share, mountpoint, "smb", source, + shareopts, sourcestr, dataset); } } } @@ -810,6 +908,122 @@ sa_zfs_set_sharenfs(sa_group_t group, char *path, int on) return (ret); } +/* + * add_resources(share, opt) + * + * Add resource properties to those in "opt". Resources are prefixed + * with name=resourcename. + */ +static char * +add_resources(sa_share_t share, char *opt) +{ + char *newopt = NULL; + char *propstr; + sa_resource_t resource; + + newopt = strdup(opt); + if (newopt == NULL) + return (newopt); + + for (resource = sa_get_share_resource(share, NULL); + resource != NULL; + resource = sa_get_next_resource(resource)) { + char *name; + size_t size; + + name = sa_get_resource_attr(resource, "name"); + if (name == NULL) { + free(newopt); + return (NULL); + } + size = strlen(name) + strlen(opt) + sizeof ("name=") + 1; + newopt = calloc(1, size); + if (newopt != NULL) + (void) snprintf(newopt, size, "%s,name=%s", opt, name); + free(opt); + opt = newopt; + propstr = sa_proto_legacy_format("smb", resource, 0); + if (propstr == NULL) { + free(opt); + return (NULL); + } + size = strlen(propstr) + strlen(opt) + 2; + newopt = calloc(1, size); + if (newopt != NULL) + (void) snprintf(newopt, size, "%s,%s", opt, propstr); + free(opt); + opt = newopt; + } + return (opt); +} + +/* + * sa_zfs_set_sharesmb(group, path, on) + * + * Update the "sharesmb" property on the path. If on is true, then set + * to the properties on the group or "on" if no properties are + * defined. Set to "off" if on is false. + */ + +int +sa_zfs_set_sharesmb(sa_group_t group, char *path, int on) +{ + int ret = SA_NOT_IMPLEMENTED; + char *command; + sa_share_t share; + + /* In case SMB not enabled */ + if (sa_get_optionset(group, "smb") == NULL) + return (SA_NOT_SUPPORTED); + + command = malloc(ZFS_MAXPROPLEN * 2); + if (command != NULL) { + char *opts = NULL; + char *dataset = NULL; + FILE *pfile; + sa_handle_impl_t impl_handle; + + if (on) { + char *newopt; + + share = sa_get_share(group, NULL); + opts = sa_proto_legacy_format("smb", share, 1); + if (opts != NULL && strlen(opts) == 0) { + free(opts); + opts = strdup("on"); + } + newopt = add_resources(opts, share); + free(opts); + opts = newopt; + } + + impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); + assert(impl_handle != NULL); + if (impl_handle != NULL) + dataset = get_zfs_dataset(impl_handle, path, B_FALSE); + else + ret = SA_SYSTEM_ERR; + + if (dataset != NULL) { + (void) snprintf(command, ZFS_MAXPROPLEN * 2, + "echo %s set sharesmb=\"%s\" %s", COMMAND, + opts != NULL ? opts : "off", dataset); + pfile = popen(command, "r"); + if (pfile != NULL) { + ret = pclose(pfile); + if (ret != 0) + ret = SA_SYSTEM_ERR; + } + } + if (opts != NULL) + free(opts); + if (dataset != NULL) + free(dataset); + free(command); + } + return (ret); +} + /* * sa_zfs_update(group) * @@ -986,7 +1200,7 @@ sa_sharetab_fill_zfs(sa_share_t share, share_t *sh, char *proto) int sa_share_zfs(sa_share_t share, char *path, share_t *sh, - void *exportdata, boolean_t on) + void *exportdata, zfs_share_op_t operation) { libzfs_handle_t *libhandle; sa_group_t group; @@ -1057,7 +1271,7 @@ sa_share_zfs(sa_share_t share, char *path, share_t *sh, sh->sh_size += j; SMAX(i, j); err = zfs_deleg_share_nfs(libhandle, dataset, path, - exportdata, sh, i, on); + exportdata, sh, i, operation); libzfs_fini(libhandle); } free(dataset); diff --git a/usr/src/lib/libshare/common/libsharecore.c b/usr/src/lib/libshare/common/libsharecore.c index 503a424cc68a..2e39594fc31e 100644 --- a/usr/src/lib/libshare/common/libsharecore.c +++ b/usr/src/lib/libshare/common/libsharecore.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include "sharetab.h" @@ -88,7 +89,7 @@ extern char *get_token(char *); static void dfs_free_list(xfs_sharelist_t *); /* prototypes */ void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *); -extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *); +extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *, uint64_t); extern sa_group_t _sa_create_group(sa_handle_impl_t, char *); static void outdfstab(FILE *, xfs_sharelist_t *); extern int _sa_remove_optionset(sa_optionset_t); @@ -563,13 +564,13 @@ sa_comment_line(char *line, char *err) } /* - * sa_delete_legacy(share) + * sa_delete_legacy(share, protocol) * * Delete the specified share from the legacy config file. */ int -sa_delete_legacy(sa_share_t share) +sa_delete_legacy(sa_share_t share, char *protocol) { FILE *dfstab; int err; @@ -580,39 +581,54 @@ sa_delete_legacy(sa_share_t share) sa_group_t parent; sigset_t old; + /* + * Protect against shares that don't have paths. This is not + * really an error at this point. + */ + path = sa_get_share_attr(share, "path"); + if (path == NULL) + return (ret); + dfstab = open_dfstab(SA_LEGACY_DFSTAB); if (dfstab != NULL) { (void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8); sablocksigs(&old); - path = sa_get_share_attr(share, "path"); parent = sa_get_parent_group(share); if (parent != NULL) { (void) lockf(fileno(dfstab), F_LOCK, 0); list = getdfstab(dfstab); rewind(dfstab); - for (optionset = sa_get_optionset(parent, NULL); - optionset != NULL; - optionset = sa_get_next_optionset(optionset)) { - char *proto = sa_get_optionset_attr(optionset, - "type"); - if (list != NULL && proto != NULL) + if (protocol != NULL) { + if (list != NULL) list = remdfsentry(list, path, - proto); - if (proto == NULL) - ret = SA_NO_MEMORY; - /* - * May want to only do the dfstab if - * this call returns NOT IMPLEMENTED - * but it shouldn't hurt. - */ - if (ret == SA_OK) { - err = sa_proto_delete_legacy(proto, - share); - if (err != SA_NOT_IMPLEMENTED) - ret = err; + protocol); + } else { + for (optionset = sa_get_optionset(parent, NULL); + optionset != NULL; + optionset = + sa_get_next_optionset(optionset)) { + char *proto = sa_get_optionset_attr( + optionset, "type"); + + if (list != NULL && proto != NULL) + list = remdfsentry(list, path, + proto); + if (proto == NULL) + ret = SA_NO_MEMORY; + /* + * may want to only do the dfstab if + * this call returns NOT IMPLEMENTED + * but it shouldn't hurt. + */ + if (ret == SA_OK) { + err = sa_proto_delete_legacy( + proto, share); + if (err != SA_NOT_IMPLEMENTED) + ret = err; + } + if (proto != NULL) + sa_free_attr_string(proto); } - if (proto != NULL) - sa_free_attr_string(proto); } outdfstab(dfstab, list); if (list != NULL) @@ -623,13 +639,16 @@ sa_delete_legacy(sa_share_t share) (void) fsync(fileno(dfstab)); saunblocksigs(&old); (void) fclose(dfstab); - sa_free_attr_string(path); } else { if (errno == EACCES || errno == EPERM) ret = SA_NO_PERMISSION; else ret = SA_CONFIG_ERR; } + + if (path != NULL) + sa_free_attr_string(path); + return (ret); } @@ -639,7 +658,7 @@ sa_delete_legacy(sa_share_t share) * There is an assumption that dfstab will be the most common form of * legacy configuration file for shares, but not the only one. Because * of that, dfstab handling is done in the main code with calls to - * this function and protocol specific calls to deal with formating + * this function and protocol specific calls to deal with formatting * options into dfstab/share compatible syntax. Since not everything * will be dfstab, there is a provision for calling a protocol * specific plugin interface that allows the protocol plugin to do its @@ -655,10 +674,16 @@ sa_update_legacy(sa_share_t share, char *proto) char *path; sigset_t old; char *persist; + uint64_t features; ret = sa_proto_update_legacy(proto, share); if (ret != SA_NOT_IMPLEMENTED) return (ret); + + features = sa_proto_get_featureset(proto); + if (!(features & SA_FEATURE_DFSTAB)) + return (ret); + /* do the dfstab format */ persist = sa_get_share_attr(share, "type"); /* @@ -748,6 +773,21 @@ sa_is_share(void *object) } return (0); } +/* + * sa_is_resource(object) + * + * returns true of the object is of type "share". + */ + +int +sa_is_resource(void *object) +{ + if (object != NULL) { + if (strcmp((char *)((xmlNodePtr)object)->name, "resource") == 0) + return (1); + } + return (0); +} /* * _sa_remove_property(property) @@ -1392,6 +1432,7 @@ parse_sharetab(sa_handle_t handle) sa_group_t lgroup; char *groupname; int legacy = 0; + char shareopts[MAXNAMLEN]; list = get_share_list(&err); if (list == NULL) @@ -1410,7 +1451,9 @@ parse_sharetab(sa_handle_t handle) * share with no arguments. */ set_node_attr(share, "shared", "true"); - set_node_attr(share, "shareopts", tmplist->options); + (void) snprintf(shareopts, MAXNAMLEN, "shareopts-%s", + tmplist->fstype); + set_node_attr(share, shareopts, tmplist->options); continue; } @@ -1426,9 +1469,9 @@ parse_sharetab(sa_handle_t handle) *groupname++ = '\0'; group = sa_get_group(handle, groupname); if (group != NULL) { - share = _sa_add_share(group, - tmplist->path, - SA_SHARE_TRANSIENT, &err); + share = _sa_add_share(group, tmplist->path, + SA_SHARE_TRANSIENT, &err, + (uint64_t)SA_FEATURE_NONE); } else { /* * While this case shouldn't @@ -1447,7 +1490,7 @@ parse_sharetab(sa_handle_t handle) */ share = _sa_add_share(lgroup, tmplist->path, SA_SHARE_TRANSIENT, - &err); + &err, (uint64_t)SA_FEATURE_NONE); } } else { if (sa_zfs_is_shared(handle, tmplist->path)) { @@ -1472,11 +1515,12 @@ parse_sharetab(sa_handle_t handle) if (group != NULL) { share = _sa_add_share(group, tmplist->path, SA_SHARE_TRANSIENT, - &err); + &err, (uint64_t)SA_FEATURE_NONE); } } else { share = _sa_add_share(lgroup, tmplist->path, - SA_SHARE_TRANSIENT, &err); + SA_SHARE_TRANSIENT, &err, + (uint64_t)SA_FEATURE_NONE); } } if (share == NULL) @@ -1517,12 +1561,22 @@ int gettransients(sa_handle_impl_t ihandle, xmlNodePtr *root) { int legacy = 0; + int numproto; + char **protocols = NULL; + int i; if (root != NULL) { if (*root == NULL) *root = xmlNewNode(NULL, (xmlChar *)"sharecfg"); - if (*root != NULL) + if (*root != NULL) { legacy = parse_sharetab(ihandle); + numproto = sa_get_protocols(&protocols); + for (i = 0; i < numproto; i++) + legacy |= sa_proto_get_transients( + (sa_handle_t)ihandle, protocols[i]); + if (protocols != NULL) + free(protocols); + } } return (legacy); } @@ -1630,7 +1684,7 @@ sa_free_fstype(char *type) * Work backward to the top of the share object tree and start * copying protocol specific optionsets into a newly created * optionset that doesn't have a parent (it will be freed - * later). This provides for the property inheritence model. That + * later). This provides for the property inheritance model. That * is, properties closer to the share take precedence over group * level. This also provides for groups of groups in the future. */ @@ -1719,7 +1773,7 @@ sa_free_derived_optionset(sa_optionset_t optionset) * Find all the security types set for this object. This is * preliminary to getting a derived security set. The return value is an * optionset containg properties which are the sectype values found by - * walking up the XML document struture. The returned optionset + * walking up the XML document structure. The returned optionset * is a derived optionset. * * If hier is 0, only look at object. If non-zero, walk up the tree. @@ -1885,6 +1939,19 @@ sa_fillshare(sa_share_t share, char *proto, struct share *sh) sa_group_t group; char *buff; char *zfs; + sa_resource_t resource; + char *rsrcname = NULL; + char *defprop; + + /* + * We only want to deal with the path level shares for the + * sharetab file. If a resource, get the parent. + */ + if (sa_is_resource(share)) { + resource = (sa_resource_t)share; + share = sa_get_resource_parent(resource); + rsrcname = sa_get_resource_attr(resource, "name"); + } group = sa_get_parent_group(share); if (group != NULL) { @@ -1912,41 +1979,48 @@ sa_fillshare(sa_share_t share, char *proto, struct share *sh) sa_free_attr_string(value); } - value = sa_get_share_attr(share, "resource"); - if (value != NULL || groupname != NULL) { + if (rsrcname != NULL || groupname != NULL) { int len = 0; - if (value != NULL) - len += strlen(value); + if (rsrcname != NULL) + len += strlen(rsrcname); if (groupname != NULL) len += strlen(groupname); len += 3; /* worst case */ buff = malloc(len); (void) snprintf(buff, len, "%s%s%s", - (value != NULL && strlen(value) > 0) ? value : "-", + (rsrcname != NULL && + strlen(rsrcname) > 0) ? rsrcname : "-", groupname != NULL ? "@" : "", groupname != NULL ? groupname : ""); sh->sh_res = buff; - if (value != NULL) - sa_free_attr_string(value); - if (groupname != NULL) { + if (rsrcname != NULL) + sa_free_attr_string(rsrcname); + if (groupname != NULL) sa_free_attr_string(groupname); - groupname = NULL; - } } else { sh->sh_res = strdup("-"); } + /* + * Get correct default prop string. NFS uses "rw", others use + * "". + */ + if (strcmp(proto, "nfs") != 0) + defprop = "\"\""; + else + defprop = "rw"; + sh->sh_fstype = strdup(proto); value = sa_proto_legacy_format(proto, share, 1); if (value != NULL) { if (strlen(value) > 0) sh->sh_opts = strdup(value); else - sh->sh_opts = strdup("rw"); + sh->sh_opts = strdup(defprop); free(value); } else { - sh->sh_opts = strdup("rw"); + sh->sh_opts = strdup(defprop); } value = sa_get_share_description(share); @@ -2041,3 +2115,67 @@ sa_delete_sharetab(char *path, char *proto) return (ret); } +/* + * sa_fix_resource_name(path) + * + * change all illegal characters to something else. For now, all get + * converted to '_' and the leading '/' is stripped off. This is used + * to construct an resource name (SMB share name) that is valid. + * Caller must pass a valid path. + */ +void +sa_fix_resource_name(char *path) +{ + char *cp; + size_t len; + + assert(path != NULL); + + /* make sure we are appropriate length */ + cp = path; + if (*cp == '/') + cp++; /* skip leading slash */ + while (cp != NULL && strlen(cp) > SA_MAX_RESOURCE_NAME) { + cp = strchr(cp, '/'); + if (cp != NULL) + cp++; + } + /* two cases - cp == NULL and cp is substring of path */ + if (cp == NULL) { + /* just take last SA_MAX_RESOURCE_NAME chars */ + len = 1 + strlen(path) - SA_MAX_RESOURCE_NAME; + (void) memmove(path, path + len, SA_MAX_RESOURCE_NAME); + path[SA_MAX_RESOURCE_NAME] = '\0'; + } else { + len = strlen(cp) + 1; + (void) memmove(path, cp, len); + } + + /* + * Don't want any of the characters that are not allowed + * in an SMB share name. Replace them with '_'. + */ + while (*path) { + switch (*path) { + case '/': + case '"': + case '\\': + case '[': + case ']': + case ':': + case '|': + case '<': + case '>': + case '+': + case ';': + case ',': + case '?': + case '*': + case '=': + case '\t': + *path = '_'; + break; + } + path++; + } +} diff --git a/usr/src/lib/libshare/common/mapfile-vers b/usr/src/lib/libshare/common/mapfile-vers index 317623b3b497..f7dfde5730d2 100644 --- a/usr/src/lib/libshare/common/mapfile-vers +++ b/usr/src/lib/libshare/common/mapfile-vers @@ -106,9 +106,28 @@ SUNWprivate { sa_delete_legacy; sa_free_derived_security; sa_enable_share; + sa_enable_resource; sa_create_group; sa_valid_protocol; sa_find_group_handle; + sa_add_resource; + sa_remove_resource; + sa_rename_resource; + sa_get_resource; + sa_get_next_resource; + sa_get_resource_attr; + sa_set_resource_attr; + sa_get_share_resource; + sa_get_resource_parent; + sa_find_resource; + sa_proto_change_notify; + sa_proto_notify_resource; + sa_disable_resource; + sa_proto_get_featureset; + sa_is_persistent; + sa_set_resource_description; + sa_get_resource_description; + sa_fix_resource_name; local: *; }; diff --git a/usr/src/lib/libshare/common/plugin.c b/usr/src/lib/libshare/common/plugin.c index 4079e24ff700..08856a895170 100644 --- a/usr/src/lib/libshare/common/plugin.c +++ b/usr/src/lib/libshare/common/plugin.c @@ -64,7 +64,7 @@ void proto_plugin_fini(); * /usr/lib/fs/nfs/libshare_nfs.so. The protocol specific directory * would have a modules with name libshare_.so. If one is * found, initialize it and add to the internal list of - * protocols. These are used for protocol specifici operations. + * protocols. These are used for protocol specific operations. */ int @@ -242,9 +242,9 @@ sa_proto_share(char *proto, sa_share_t share) } /* - * sa_proto_unshare(proto, path) + * sa_proto_unshare(proto, share) * - * Deactivate (unshare) the path for this protocol. + * Deactivate (unshare) the share for this protocol. */ int @@ -258,6 +258,52 @@ sa_proto_unshare(sa_share_t share, char *proto, char *path) return (ret); } +/* + * sa_proto_share_resource(char *proto, sa_resource_t resource) + * + * For protocols that actually enable at the resource level, do the + * protocol specific resource enable. If it doesn't, return an error. + * Note that the resource functions are optional so can return + * SA_NOT_SUPPORTED. + */ + +int +sa_proto_share_resource(char *proto, sa_resource_t resource) +{ + struct sa_plugin_ops *ops = find_protocol(proto); + int ret = SA_INVALID_PROTOCOL; + + if (ops != NULL) { + if (ops->sa_enable_resource != NULL) + ret = ops->sa_enable_resource(resource); + else + ret = SA_NOT_SUPPORTED; + } + return (ret); +} + +/* + * sa_proto_unshare_resource(char *proto, sa_resource_t resource) + * + * For protocols that actually disable at the resource level, do the + * protocol specific resource disable. If it doesn't, return an error. + */ + +int +sa_proto_unshare_resource(char *proto, sa_resource_t resource) +{ + struct sa_plugin_ops *ops = find_protocol(proto); + int ret = SA_INVALID_PROTOCOL; + + if (ops != NULL) { + if (ops->sa_disable_resource != NULL) + ret = ops->sa_disable_resource(resource); + else + ret = SA_NOT_SUPPORTED; + } + return (ret); +} + /* * sa_proto_valid_prop(proto, prop, opt) * @@ -395,7 +441,7 @@ sa_proto_get_properties(char *proto) /* * sa_proto_set_property(proto, prop) * - * Update the protocol specifiec property. + * Update the protocol specific property. */ int @@ -467,16 +513,127 @@ int sa_proto_delete_legacy(char *proto, sa_share_t share) { struct sa_plugin_ops *ops = find_protocol(proto); - int ret = SA_OK; + int ret = SA_NOT_IMPLEMENTED; if (ops != NULL) { if (ops->sa_delete_legacy != NULL) ret = ops->sa_delete_legacy(share); - } else { - if (proto != NULL) - ret = SA_NOT_IMPLEMENTED; - else + } else if (proto == NULL) { + ret = SA_INVALID_PROTOCOL; + } + return (ret); +} + +/* + * sa_proto_change_notify(share, char *protocol) + * + * Notify the protocol that a change has been made to the share + */ + +int +sa_proto_change_notify(sa_share_t share, char *proto) +{ + struct sa_plugin_ops *ops = find_protocol(proto); + int ret = SA_NOT_IMPLEMENTED; + + if (ops != NULL) { + if (ops->sa_change_notify != NULL) + ret = ops->sa_change_notify(share); + } else if (proto == NULL) { ret = SA_INVALID_PROTOCOL; } return (ret); } + +/* + * sa_proto_notify_resource(resource, char *protocol) + * + * Notify the protocol that a change has been made to the share + */ + +int +sa_proto_notify_resource(sa_resource_t resource, char *proto) +{ + struct sa_plugin_ops *ops = find_protocol(proto); + int ret = SA_NOT_IMPLEMENTED; + + if (ops != NULL) { + if (ops->sa_notify_resource != NULL) + ret = ops->sa_notify_resource(resource); + } else if (proto == NULL) { + ret = SA_INVALID_PROTOCOL; + } + return (ret); +} + +/* + * sa_proto_get_featureset(protocol) + * + * Get bitmask of defined features of the protocol. These are + * primarily things like SA_FEATURE_RESOURCE (shares are by resource + * name rather than path) and other operational features that affect + * behavior. + */ + +uint64_t +sa_proto_get_featureset(char *proto) +{ + struct sa_plugin_ops *ops = find_protocol(proto); + uint64_t ret = 0; + + if (ops != NULL) { + if (ops->sa_features != NULL) + ret = ops->sa_features(); + } + /* if not implemented, zero is valid */ + return (ret); +} + +/* + * sa_proto_get_transients(sa_handle_t) + * + * Called to get any protocol specific transient shares. NFS doesn't + * use this since the info is in sharetab which is processed as a + * common transient store. + * + * The protocol plugin should verify that the share isn't in the + * repository and then add it as a transient. + * + * Not having an entry is not a problem. It returns 0 in that case. + */ + +int +sa_proto_get_transients(sa_handle_t handle, char *proto) +{ + struct sa_plugin_ops *ops = find_protocol(proto); + int ret = 0; + + if (ops != NULL) { + if (ops->sa_get_transient_shares != NULL) + ret = ops->sa_get_transient_shares(handle); + } + return (ret); +} + +/* + * sa_proto_rename_resource(sa_handle_t, proto, sa_resource_t, newname) + * + * Protocols may need to know when a resource has changed names in + * order to notify clients. This must be done "before" the name in the + * resource has been changed. Not being implemented is not a problem. + */ + +int +sa_proto_rename_resource(sa_handle_t handle, char *proto, + sa_resource_t resource, char *newname) +{ + struct sa_plugin_ops *ops = find_protocol(proto); + int ret = SA_OK; + + if (ops != NULL) { + if (ops->sa_rename_resource != NULL) + ret = ops->sa_rename_resource(handle, resource, + newname); + } + return (ret); +} diff --git a/usr/src/lib/libshare/common/scfutil.c b/usr/src/lib/libshare/common/scfutil.c index 05421d605ba5..344d3729bbf4 100644 --- a/usr/src/lib/libshare/common/scfutil.c +++ b/usr/src/lib/libshare/common/scfutil.c @@ -35,6 +35,7 @@ #include "libshare_impl.h" #include "scfutil.h" #include +#include #include #include #include @@ -396,7 +397,8 @@ sa_extract_attrs(xmlNodePtr root, scfutilhandle_t *handle, static char *share_attr[] = { "path", "id", - "resource", + "drive-letter", + "exclude", NULL, }; @@ -410,6 +412,52 @@ is_share_attr(char *name) return (0); } +/* + * _sa_make_resource(node, valuestr) + * + * Make a resource node on the share node. The valusestr will either + * be old format (SMF acceptable string) or new format (pretty much an + * arbitrary string with "nnn:" prefixing in order to persist + * mapping). The input valuestr will get modified in place. This is + * only used in SMF repository parsing. A possible third field will be + * a "description" string. + */ + +static void +_sa_make_resource(xmlNodePtr node, char *valuestr) +{ + char *idx; + char *name; + char *description = NULL; + + idx = valuestr; + name = strchr(valuestr, ':'); + if (name == NULL) { + /* this is old form so give an index of "0" */ + idx = "0"; + name = valuestr; + } else { + /* NUL the ':' and move past it */ + *name++ = '\0'; + /* There could also be a description string */ + description = strchr(name, ':'); + if (description != NULL) + *description++ = '\0'; + } + node = xmlNewChild(node, NULL, (xmlChar *)"resource", NULL); + if (node != NULL) { + xmlSetProp(node, (xmlChar *)"name", (xmlChar *)name); + xmlSetProp(node, (xmlChar *)"id", (xmlChar *)idx); + /* SMF values are always persistent */ + xmlSetProp(node, (xmlChar *)"type", (xmlChar *)"persist"); + if (description != NULL && strlen(description) > 0) { + (void) xmlNewChild(node, NULL, (xmlChar *)"description", + (xmlChar *)description); + } + } +} + + /* * sa_share_from_pgroup * @@ -490,34 +538,70 @@ sa_share_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle, vallen) >= 0) { ret = SA_OK; } + } else if (strcmp(name, "resource") == 0) { + ret = SA_OK; } } - if (ret == SA_OK) { + if (ret != SA_OK) + continue; + /* + * Check that we have the "path" property in + * name. The string in name will always be nul + * terminated if scf_property_get_name() + * succeeded. + */ + if (strcmp(name, "path") == 0) + have_path = 1; + if (is_share_attr(name)) { /* - * Check that we have the "path" property in - * name. The string in name will always be nul - * terminated if scf_property_get_name() - * succeeded. + * If a share attr, then simple - + * usually path and id name */ - if (strcmp(name, "path") == 0) - have_path = 1; - if (is_share_attr(name)) { - /* - * If a share attr, then simple - - * usually path and resource name - */ - xmlSetProp(node, (xmlChar *)name, - (xmlChar *)valuestr); - } else { - if (strcmp(name, "description") == 0) { - /* We have a description node */ - xmlNodePtr desc; - desc = xmlNewChild(node, NULL, - (xmlChar *)"description", NULL); - if (desc != NULL) - xmlNodeSetContent(desc, - (xmlChar *)valuestr); + xmlSetProp(node, (xmlChar *)name, + (xmlChar *)valuestr); + } else if (strcmp(name, "resource") == 0) { + /* + * Resource names handled differently since + * there can be multiple on each share. The + * "resource" id must be preserved since this + * will be used by some protocols in mapping + * "property spaces" to names and is always + * used to create SMF property groups specific + * to resources. CIFS needs this. The first + * value is present so add and then loop for + * any additional. Since this is new and + * previous values may exist, handle + * conversions. + */ + scf_iter_t *viter; + viter = scf_iter_create(handle->handle); + if (viter != NULL && + scf_iter_property_values(viter, prop) == 0) { + while (scf_iter_next_value(viter, value) > 0) { + /* Have a value so process it */ + if (scf_value_get_ustring(value, + valuestr, vallen) >= 0) { + /* have a ustring */ + _sa_make_resource(node, + valuestr); + } else if (scf_value_get_astring(value, + valuestr, vallen) >= 0) { + /* have an astring */ + _sa_make_resource(node, + valuestr); + } } + scf_iter_destroy(viter); + } + } else { + if (strcmp(name, "description") == 0) { + /* We have a description node */ + xmlNodePtr desc; + desc = xmlNewChild(node, NULL, + (xmlChar *)"description", NULL); + if (desc != NULL) + xmlNodeSetContent(desc, + (xmlChar *)valuestr); } } } @@ -583,7 +667,34 @@ find_share_by_id(sa_handle_t handle, char *shareid) } /* - * sa_share_props_from_pgroup(root, handle, pg, id) + * find_resource_by_index(share, index) + * + * Search the resource records on the share for the id index. + */ +static sa_resource_t +find_resource_by_index(sa_share_t share, char *index) +{ + sa_resource_t resource; + sa_resource_t found = NULL; + char *id; + + for (resource = sa_get_share_resource(share, NULL); + resource != NULL && found == NULL; + resource = sa_get_next_resource(resource)) { + id = (char *)xmlGetProp((xmlNodePtr)resource, (xmlChar *)"id"); + if (id != NULL) { + if (strcmp(id, index) == 0) { + /* found it so save in "found" */ + found = resource; + } + sa_free_attr_string(id); + } + } + return (found); +} + +/* + * sa_share_props_from_pgroup(root, handle, pg, id, sahandle) * * Extract share properties from the SMF property group. More sanity * checks are done and the share object is created. We ignore some @@ -639,6 +750,7 @@ sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle, /* not a valid proto (null) */ return (ret); } + sectype = strchr(proto, '_'); if (sectype != NULL) *sectype++ = '\0'; @@ -664,10 +776,35 @@ sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle, if (sectype == NULL) node = xmlNewChild(root, NULL, (xmlChar *)"optionset", NULL); else { - node = xmlNewChild(root, NULL, (xmlChar *)"security", NULL); - if (node != NULL) - xmlSetProp(node, (xmlChar *)"sectype", - (xmlChar *)sectype); + if (isdigit((int)*sectype)) { + sa_resource_t resource; + /* + * If sectype[0] is a digit, then it is an index into + * the resource names. We need to find a resource + * record and then get the properties into an + * optionset. The optionset becomes the "node" and the + * rest is hung off of the share. + */ + resource = find_resource_by_index(share, sectype); + if (resource != NULL) { + node = xmlNewChild(resource, NULL, + (xmlChar *)"optionset", NULL); + } else { + /* this shouldn't happen */ + ret = SA_SYSTEM_ERR; + } + } else { + /* + * If not a digit, then it is a security type + * (alternate option space). Security types start with + * an alphabetic. + */ + node = xmlNewChild(root, NULL, (xmlChar *)"security", + NULL); + if (node != NULL) + xmlSetProp(node, (xmlChar *)"sectype", + (xmlChar *)sectype); + } } if (node == NULL) { ret = SA_NO_MEMORY; @@ -685,7 +822,7 @@ sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle, if (iter == NULL || value == NULL || prop == NULL || name == NULL) goto out; - /* Iterate over the share pg properties */ + /* iterate over the share pg properties */ if (scf_iter_pg_properties(iter, pg) == 0) { while (scf_iter_next_property(iter, prop) > 0) { ret = SA_SYSTEM_ERR; /* assume the worst */ @@ -897,7 +1034,7 @@ sa_extract_group(xmlNodePtr root, scfutilhandle_t *handle, * sa_extract_defaults(root, handle, instance) * * Local function to find the default properties that live in the - * default instance's "operation" proprerty group. + * default instance's "operation" property group. */ static void @@ -946,7 +1083,7 @@ sa_extract_defaults(xmlNodePtr root, scfutilhandle_t *handle, /* - * sa_get_config(handle, root, doc, sahandlec) + * sa_get_config(handle, root, doc, sahandle) * * Walk the SMF repository for /network/shares/group and find all the * instances. These become group names. Then add the XML structure @@ -1275,6 +1412,147 @@ sa_set_property(scfutilhandle_t *handle, char *propname, char *valstr) return (ret); } +/* + * check_resource(share) + * + * Check to see if share has any persistent resources. We don't want + * to save if they are all transient. + */ +static int +check_resource(sa_share_t share) +{ + sa_resource_t resource; + int ret = B_FALSE; + + for (resource = sa_get_share_resource(share, NULL); + resource != NULL && ret == B_FALSE; + resource = sa_get_next_resource(resource)) { + char *type; + type = sa_get_resource_attr(resource, "type"); + if (type != NULL) { + if (strcmp(type, "transient") != 0) { + ret = B_TRUE; + } + sa_free_attr_string(type); + } + } + return (ret); +} + +/* + * sa_set_resource_property(handle, prop, value) + * + * set a property transaction entry into the pending SMF + * transaction. We don't want to include any transient resources + */ + +static int +sa_set_resource_property(scfutilhandle_t *handle, sa_share_t share) +{ + int ret = SA_OK; + scf_value_t *value; + scf_transaction_entry_t *entry; + sa_resource_t resource; + char *valstr; + char *idstr; + char *description; + char *propstr = NULL; + size_t strsize; + + /* don't bother if no persistent resources */ + if (check_resource(share) == B_FALSE) + return (ret); + + /* + * properties must be set in transactions and don't take + * effect until the transaction has been ended/committed. + */ + entry = scf_entry_create(handle->handle); + if (entry == NULL) + return (SA_SYSTEM_ERR); + + if (scf_transaction_property_change(handle->trans, entry, + "resource", SCF_TYPE_ASTRING) != 0 && + scf_transaction_property_new(handle->trans, entry, + "resource", SCF_TYPE_ASTRING) != 0) { + scf_entry_destroy(entry); + return (SA_SYSTEM_ERR); + + } + for (resource = sa_get_share_resource(share, NULL); + resource != NULL; + resource = sa_get_next_resource(resource)) { + value = scf_value_create(handle->handle); + if (value == NULL) { + ret = SA_NO_MEMORY; + break; + } + /* Get size of complete string */ + valstr = sa_get_resource_attr(resource, "name"); + idstr = sa_get_resource_attr(resource, "id"); + description = sa_get_resource_description(resource); + strsize = (valstr != NULL) ? strlen(valstr) : 0; + strsize += (idstr != NULL) ? strlen(idstr) : 0; + strsize += (description != NULL) ? strlen(description) : 0; + if (strsize > 0) { + strsize += 3; /* add nul and ':' */ + propstr = (char *)malloc(strsize); + if (propstr == NULL) { + scf_value_destroy(value); + ret = SA_NO_MEMORY; + goto err; + } + if (idstr == NULL) + (void) snprintf(propstr, strsize, "%s", + valstr ? valstr : ""); + else + (void) snprintf(propstr, strsize, "%s:%s:%s", + idstr ? idstr : "", valstr ? valstr : "", + description ? description : ""); + if (scf_value_set_astring(value, propstr) != 0) { + ret = SA_SYSTEM_ERR; + free(propstr); + scf_value_destroy(value); + break; + } + if (scf_entry_add_value(entry, value) != 0) { + ret = SA_SYSTEM_ERR; + free(propstr); + scf_value_destroy(value); + break; + } + /* the value is in the transaction */ + value = NULL; + free(propstr); + } +err: + if (valstr != NULL) + sa_free_attr_string(valstr); + if (idstr != NULL) + sa_free_attr_string(idstr); + if (description != NULL) + sa_free_share_description(description); + } + /* the entry is in the transaction */ + entry = NULL; + + if (ret == SA_SYSTEM_ERR) { + switch (scf_error()) { + case SCF_ERROR_PERMISSION_DENIED: + ret = SA_NO_PERMISSION; + break; + } + } + /* + * cleanup if there were any errors that didn't leave + * these values where they would be cleaned up later. + */ + if (entry != NULL) + scf_entry_destroy(entry); + + return (ret); +} + /* * sa_commit_share(handle, group, share) * @@ -1288,7 +1566,6 @@ sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share) int ret = SA_OK; char *groupname; char *name; - char *resource; char *description; char *sharename; ssize_t proplen; @@ -1371,14 +1648,34 @@ sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share) } } if (ret == SA_OK) { - resource = sa_get_share_attr(share, - "resource"); - if (resource != NULL) { + name = sa_get_share_attr(share, "drive-letter"); + if (name != NULL) { + /* A drive letter may exist for SMB */ + ret = sa_set_property(handle, + "drive-letter", name); + sa_free_attr_string(name); + } + } + if (ret == SA_OK) { + name = sa_get_share_attr(share, "exclude"); + if (name != NULL) { + /* + * In special cases need to + * exclude proto enable. + */ ret = sa_set_property(handle, - "resource", resource); - sa_free_attr_string(resource); + "exclude", name); + sa_free_attr_string(name); } } + if (ret == SA_OK) { + /* + * If there are resource names, bundle them up + * and save appropriately. + */ + ret = sa_set_resource_property(handle, share); + } + if (ret == SA_OK) { description = sa_get_share_description(share); if (description != NULL) { @@ -1413,6 +1710,49 @@ sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share) return (ret); } +/* + * remove_resources(handle, share, shareid) + * + * If the share has resources, remove all of them and their + * optionsets. + */ +static int +remove_resources(scfutilhandle_t *handle, sa_share_t share, char *shareid) +{ + sa_resource_t resource; + sa_optionset_t opt; + char *proto; + char *id; + ssize_t proplen; + char *propstring; + int ret = SA_OK; + + proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); + propstring = malloc(proplen); + if (propstring == NULL) + return (SA_NO_MEMORY); + + for (resource = sa_get_share_resource(share, NULL); + resource != NULL; resource = sa_get_next_resource(resource)) { + id = sa_get_resource_attr(resource, "id"); + if (id == NULL) + continue; + for (opt = sa_get_optionset(resource, NULL); + opt != NULL; opt = sa_get_next_optionset(resource)) { + proto = sa_get_optionset_attr(opt, "type"); + if (proto != NULL) { + (void) snprintf(propstring, proplen, + "%s_%s_%s", shareid, proto, id); + ret = sa_delete_pgroup(handle, propstring); + sa_free_attr_string(proto); + } + } + sa_free_attr_string(id); + } + free(propstring); + return (ret); +} + /* * sa_delete_share(handle, group, share) * @@ -1444,6 +1784,8 @@ sa_delete_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share) } ret = sa_get_instance(handle, groupname); if (ret == SA_OK) { + /* If a share has resources, remove them */ + ret = remove_resources(handle, share, shareid); /* If a share has properties, remove them */ ret = sa_delete_pgroup(handle, shareid); for (opt = sa_get_optionset(share, NULL); diff --git a/usr/src/lib/libshare/nfs/libshare_nfs.c b/usr/src/lib/libshare/nfs/libshare_nfs.c index a7426ab841f2..0a3175de7044 100644 --- a/usr/src/lib/libshare/nfs/libshare_nfs.c +++ b/usr/src/lib/libshare/nfs/libshare_nfs.c @@ -73,6 +73,7 @@ static int nfs_set_proto_prop(sa_property_t); static sa_protocol_properties_t nfs_get_proto_set(); static char *nfs_get_status(); static char *nfs_space_alias(char *); +static uint64_t nfs_features(); /* * ops vector that provides the protocol specific info and operations @@ -95,7 +96,14 @@ struct sa_plugin_ops sa_plugin_ops = { nfs_get_proto_set, nfs_get_status, nfs_space_alias, - NULL, + NULL, /* update_legacy */ + NULL, /* delete_legacy */ + NULL, /* change_notify */ + NULL, /* enable_resource */ + NULL, /* disable_resource */ + nfs_features, + NULL, /* transient shares */ + NULL, /* notify resource */ NULL }; @@ -543,13 +551,16 @@ nfs_parse_legacy_options(sa_group_t group, char *options) optionset = sa_create_optionset(group, "nfs"); } else { /* - * have an existing optionset so we need to compare - * options in order to detect errors. For now, we - * assume that the first optionset is the correct one - * and the others will be the same. This needs to be - * fixed before the final code is ready. + * Have an existing optionset . Ideally, we would need + * to compare options in order to detect errors. For + * now, we assume that the first optionset is the + * correct one and the others will be the same. An + * empty optionset is the same as no optionset so we + * don't want to exit in that case. Getting an empty + * optionset can occur with ZFS property checking. */ - return (ret); + if (sa_get_property(optionset, NULL) != NULL) + return (ret); } if (strcmp(options, SHOPT_RW) == 0) { @@ -839,7 +850,7 @@ fill_export_from_optionset(struct exportdata *export, sa_optionset_t optionset) /* * since options may be set/reset multiple times, always do an * explicit set or clear of the option. This allows defaults - * to be set and then the protocol specifici to override. + * to be set and then the protocol specific to override. */ name = sa_get_property_attr(option, "type"); @@ -1804,7 +1815,8 @@ nfs_enable_share(sa_share_t share) ea.uex = &export; sa_sharetab_fill_zfs(share, &sh, "nfs"); - err = sa_share_zfs(share, path, &sh, &ea, B_TRUE); + err = sa_share_zfs(share, path, &sh, + &ea, ZFS_SHARE_NFS); sa_emptyshare(&sh); } } else { @@ -1909,7 +1921,8 @@ nfs_disable_share(sa_share_t share, char *path) sh.sh_path = path; sh.sh_fstype = "nfs"; - err = sa_share_zfs(share, path, &sh, &ea, B_FALSE); + err = sa_share_zfs(share, path, &sh, + &ea, ZFS_UNSHARE_NFS); } else err = exportfs(path, NULL); if (err < 0) { @@ -2802,7 +2815,7 @@ nfs_minmax_check(int index, int value) /* * nfs_validate_proto_prop(index, name, value) * - * Verify that the property specifed by name can take the new + * Verify that the property specified by name can take the new * value. This is a sanity check to prevent bad values getting into * the default files. All values need to be checked against what is * allowed by their defined type. If a type isn't explicitly defined @@ -2967,3 +2980,15 @@ nfs_space_alias(char *space) } return (strdup(name)); } + +/* + * nfs_features() + * + * Return a mask of the features required. + */ + +static uint64_t +nfs_features() +{ + return ((uint64_t)SA_FEATURE_DFSTAB); +} diff --git a/usr/src/lib/libshare/smb/Makefile b/usr/src/lib/libshare/smb/Makefile new file mode 100644 index 000000000000..d5f3df523314 --- /dev/null +++ b/usr/src/lib/libshare/smb/Makefile @@ -0,0 +1,51 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../../Makefile.lib + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +MSGFILES = libshare_smb.c + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../../Makefile.targ +include ../../../Makefile.msg.targ diff --git a/usr/src/lib/libshare/smb/Makefile.com b/usr/src/lib/libshare/smb/Makefile.com new file mode 100644 index 000000000000..997f47daefdf --- /dev/null +++ b/usr/src/lib/libshare/smb/Makefile.com @@ -0,0 +1,88 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +LIBRARY = libshare_smb.a +VERS = .1 +SMBMLSVC_DIR = $(SRC)/lib/smbsrv/libmlsvc/common +SMBBASE_DIR = $(SRC)/lib/smbsrv/libsmb/common +SMBCOMMON_DIR = $(SRC)/common/smbsrv + +LIBOBJS = libshare_smb.o smb_share_doorclnt.o +SMBCOMMON_OBJ = smb_share_door_decode.o smb_common_door_decode.o +SMBBASE_OBJ = smb_cfg.o smb_scfutil.o smb_door_client.o +SMBMLSVC_OBJ = smb_share_util.o +OBJECTS = $(LIBOBJS) $(SMBCOMMON_OBJ) $(SMBBASE_OBJ) $(SMBMLSVC_OBJ) + +include ../../../Makefile.lib + +ROOTLIBDIR = $(ROOT)/usr/lib/fs/smb +ROOTLIBDIR64 = $(ROOT)/usr/lib/fs/smb/$(MACH64) + +LIBSRCS = $(LIBOBJS:%.o=$(SRCDIR)/%.c) +lintcheck := SRCS = $(LIBSRCS) + +LIBS = $(DYNLIB) +LDLIBS += -lshare -lnsl -lscf -lumem -lc +all install := LDLIBS += -lxml2 + +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -D_REENTRANT -I/usr/include/libxml2 \ + -I$(SRCDIR)/../common + +.KEEP_STATE: + +all: $(LIBS) + +install: all + +lint: lintcheck + +pics/smb_door_client.o: $(SMBBASE_DIR)/smb_door_client.c + $(COMPILE.c) -o $@ $(SMBBASE_DIR)/smb_door_client.c + $(POST_PROCESS_O) + +pics/smb_share_door_decode.o: $(SMBCOMMON_DIR)/smb_share_door_decode.c + $(COMPILE.c) -o $@ $(SMBCOMMON_DIR)/smb_share_door_decode.c + $(POST_PROCESS_O) + +pics/smb_common_door_decode.o: $(SMBCOMMON_DIR)/smb_common_door_decode.c + $(COMPILE.c) -o $@ $(SMBCOMMON_DIR)/smb_common_door_decode.c + $(POST_PROCESS_O) + +pics/smb_cfg.o: $(SMBBASE_DIR)/smb_cfg.c + $(COMPILE.c) -o $@ $(SMBBASE_DIR)/smb_cfg.c + $(POST_PROCESS_O) + +pics/smb_scfutil.o: $(SMBBASE_DIR)/smb_scfutil.c + $(COMPILE.c) -o $@ $(SMBBASE_DIR)/smb_scfutil.c + $(POST_PROCESS_O) + +pics/smb_share_util.o: $(SMBMLSVC_DIR)/smb_share_util.c + $(COMPILE.c) -o $@ $(SMBMLSVC_DIR)/smb_share_util.c + $(POST_PROCESS_O) + +include ../../../Makefile.targ diff --git a/usr/src/lib/libshare/smb/amd64/Makefile b/usr/src/lib/libshare/smb/amd64/Makefile new file mode 100644 index 000000000000..90b1730d235a --- /dev/null +++ b/usr/src/lib/libshare/smb/amd64/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libshare/smb/i386/Makefile b/usr/src/lib/libshare/smb/i386/Makefile new file mode 100644 index 000000000000..543238473c55 --- /dev/null +++ b/usr/src/lib/libshare/smb/i386/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/libshare/smb/libshare_smb.c b/usr/src/lib/libshare/smb/libshare_smb.c new file mode 100644 index 000000000000..7d705e036922 --- /dev/null +++ b/usr/src/lib/libshare/smb/libshare_smb.c @@ -0,0 +1,1641 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB specific functions + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libshare.h" +#include "libshare_impl.h" +#include +#include +#include +#include +#include "libshare_smb.h" +#include +#include +#include +#include +#include + +/* internal functions */ +static int smb_share_init(void); +static void smb_share_fini(void); +static int smb_enable_share(sa_share_t); +static int smb_share_changed(sa_share_t); +static int smb_resource_changed(sa_resource_t); +static int smb_rename_resource(sa_handle_t, sa_resource_t, char *); +static int smb_disable_share(sa_share_t share, char *); +static int smb_validate_property(sa_property_t, sa_optionset_t); +static int smb_set_proto_prop(sa_property_t); +static sa_protocol_properties_t smb_get_proto_set(void); +static char *smb_get_status(void); +static int smb_parse_optstring(sa_group_t, char *); +static char *smb_format_options(sa_group_t, int); + +static int smb_enable_service(void); + +static int range_check_validator(int, char *); +static int range_check_validator_zero_ok(int, char *); +static int string_length_check_validator(int, char *); +static int true_false_validator(int, char *); +static int ip_address_validator_empty_ok(int, char *); +static int ip_address_csv_list_validator_empty_ok(int, char *); +static int ipc_mode_validator(int, char *); +static int path_validator(int, char *); + +static int smb_enable_resource(sa_resource_t); +static int smb_disable_resource(sa_resource_t); +static uint64_t smb_share_features(void); +static int smb_list_transient(sa_handle_t); + +/* size of basic format allocation */ +#define OPT_CHUNK 1024 + +/* + * Indexes of entries in smb_proto_options table. + * Changes to smb_proto_options table may require + * an update to these values. + */ +#define PROTO_OPT_WINS1 6 +#define PROTO_OPT_WINS_EXCLUDE 8 + + +/* + * ops vector that provides the protocol specific info and operations + * for share management. + */ + +struct sa_plugin_ops sa_plugin_ops = { + SA_PLUGIN_VERSION, + SMB_PROTOCOL_NAME, + smb_share_init, + smb_share_fini, + smb_enable_share, + smb_disable_share, + smb_validate_property, + NULL, + NULL, + smb_parse_optstring, + smb_format_options, + smb_set_proto_prop, + smb_get_proto_set, + smb_get_status, + NULL, + NULL, + NULL, + smb_share_changed, + smb_enable_resource, + smb_disable_resource, + smb_share_features, + smb_list_transient, + smb_resource_changed, + smb_rename_resource, + NULL, + NULL +}; + +/* + * option definitions. Make sure to keep the #define for the option + * index just before the entry it is the index for. Changing the order + * can cause breakage. + */ + +struct option_defs optdefs[] = { + {SHOPT_AD_CONTAINER, OPT_TYPE_STRING}, + {SHOPT_NAME, OPT_TYPE_NAME}, + {NULL, NULL}, +}; + +/* + * findopt(name) + * + * Lookup option "name" in the option table and return the table + * index. + */ + +static int +findopt(char *name) +{ + int i; + if (name != NULL) { + for (i = 0; optdefs[i].tag != NULL; i++) { + if (strcmp(optdefs[i].tag, name) == 0) + return (i); + } + } + return (-1); +} + +/* + * is_a_number(number) + * + * is the string a number in one of the forms we want to use? + */ + +static int +is_a_number(char *number) +{ + int ret = 1; + int hex = 0; + + if (strncmp(number, "0x", 2) == 0) { + number += 2; + hex = 1; + } else if (*number == '-') { + number++; /* skip the minus */ + } + + while (ret == 1 && *number != '\0') { + if (hex) { + ret = isxdigit(*number++); + } else { + ret = isdigit(*number++); + } + } + return (ret); +} + +/* + * validresource(name) + * + * Check that name only has valid characters in it. The current valid + * set are the printable characters but not including: + * " / \ [ ] : | < > + ; , ? * = \t + * Note that space is included and there is a maximum length. + */ +static int +validresource(const char *name) +{ + const char *cp; + size_t len; + + if (name == NULL) + return (B_FALSE); + + len = strlen(name); + if (len == 0 || len > SA_MAX_RESOURCE_NAME) + return (B_FALSE); + + if (strpbrk(name, "\"/\\[]:|<>+;,?*=\t") != NULL) { + return (B_FALSE); + } + + for (cp = name; *cp != '\0'; cp++) + if (iscntrl(*cp)) + return (B_FALSE); + + return (B_TRUE); +} + +/* + * smb_isonline() + * + * Determine if the SMF service instance is in the online state or + * not. A number of operations depend on this state. + */ +static boolean_t +smb_isonline(void) +{ + char *str; + boolean_t ret = B_FALSE; + + if ((str = smf_get_state(SMBD_DEFAULT_INSTANCE_FMRI)) != NULL) { + ret = (strcmp(str, SCF_STATE_STRING_ONLINE) == 0); + free(str); + } + return (ret); +} + +/* + * smb_enable_share tells the implementation that it is to enable the share. + * This entails converting the path and options into the appropriate ioctl + * calls. It is assumed that all error checking of paths, etc. were + * done earlier. + */ +static int +smb_enable_share(sa_share_t share) +{ + char *path; + char *rname; + lmshare_info_t si; + sa_resource_t resource; + boolean_t iszfs; + boolean_t privileged; + int err = SA_OK; + priv_set_t *priv_effective; + boolean_t online; + + priv_effective = priv_allocset(); + (void) getppriv(PRIV_EFFECTIVE, priv_effective); + privileged = (priv_isfullset(priv_effective) == B_TRUE); + priv_freeset(priv_effective); + + /* get the path since it is important in several places */ + path = sa_get_share_attr(share, "path"); + if (path == NULL) + return (SA_NO_SUCH_PATH); + + online = smb_isonline(); + + iszfs = sa_path_is_zfs(path); + + if (iszfs) { + + if (privileged == B_FALSE && !online) { + + if (!online) { + (void) printf(dgettext(TEXT_DOMAIN, + "SMB: Cannot share remove " + "file system: %s\n"), path); + (void) printf(dgettext(TEXT_DOMAIN, + "SMB: Service needs to be enabled " + "by a privileged user\n")); + err = SA_NO_PERMISSION; + errno = EPERM; + } + if (err) { + sa_free_attr_string(path); + return (err); + } + + } + } + + if (privileged == B_TRUE && !online) { + err = smb_enable_service(); + if (err != SA_OK) { + (void) printf(dgettext(TEXT_DOMAIN, + "SMB: Unable to enable service\n")); + /* + * For now, it is OK to not be able to enable + * the service. + */ + if (err == SA_BUSY) + err = SA_OK; + } else { + online = B_TRUE; + } + } + + /* + * Don't bother trying to start shares if the service isn't + * running. + */ + if (!online) + goto done; + + /* Each share can have multiple resources */ + for (resource = sa_get_share_resource(share, NULL); + resource != NULL; + resource = sa_get_next_resource(resource)) { + sa_optionset_t opts; + bzero(&si, sizeof (lmshare_info_t)); + rname = sa_get_resource_attr(resource, "name"); + if (rname == NULL) { + sa_free_attr_string(path); + return (SA_NO_SUCH_RESOURCE); + } + + opts = sa_get_derived_optionset(resource, SMB_PROTOCOL_NAME, 1); + smb_build_lmshare_info(rname, path, opts, &si); + sa_free_attr_string(rname); + + sa_free_derived_optionset(opts); + if (!iszfs) { + err = lmshrd_add(&si); + } else { + share_t sh; + + sa_sharetab_fill_zfs(share, &sh, "smb"); + err = sa_share_zfs(share, (char *)path, &sh, + &si, ZFS_SHARE_SMB); + + sa_emptyshare(&sh); + } + } + if (!iszfs) + (void) sa_update_sharetab(share, "smb"); +done: + sa_free_attr_string(path); + + return (err == NERR_DuplicateShare ? 0 : err); +} + +/* + * This is the share for CIFS all shares have resource names. + * Enable tells the smb server to update its hash. If it fails + * because smb server is down, we just ignore as smb server loads + * the resources from sharemanager at startup. + */ + +static int +smb_enable_resource(sa_resource_t resource) +{ + char *path; + char *rname; + sa_optionset_t opts; + sa_share_t share; + lmshare_info_t si; + int ret; + + share = sa_get_resource_parent(resource); + if (share == NULL) + return (SA_NO_SUCH_PATH); + path = sa_get_share_attr(share, "path"); + if (path == NULL) + return (SA_SYSTEM_ERR); + rname = sa_get_resource_attr(resource, "name"); + if (rname == NULL) { + sa_free_attr_string(path); + return (SA_NO_SUCH_RESOURCE); + } + + ret = smb_enable_service(); + + if (!smb_isonline()) { + ret = SA_OK; + goto done; + } + + opts = sa_get_derived_optionset(resource, SMB_PROTOCOL_NAME, 1); + smb_build_lmshare_info(rname, path, opts, &si); + sa_free_attr_string(path); + sa_free_attr_string(rname); + sa_free_derived_optionset(opts); + if (lmshrd_add(&si) != NERR_Success) + return (SA_NOT_SHARED); + (void) sa_update_sharetab(share, "smb"); + +done: + return (ret); +} + +/* + * Remove it from smb server hash. + */ +static int +smb_disable_resource(sa_resource_t resource) +{ + char *rname; + DWORD res; + sa_share_t share; + + rname = sa_get_resource_attr(resource, "name"); + if (rname == NULL) + return (SA_NO_SUCH_RESOURCE); + + if (smb_isonline()) { + res = lmshrd_delete(rname); + if (res != NERR_Success) { + sa_free_attr_string(rname); + return (SA_CONFIG_ERR); + } + sa_free_attr_string(rname); + rname = NULL; + } + share = sa_get_resource_parent(resource); + if (share != NULL) { + rname = sa_get_share_attr(share, "path"); + if (rname != NULL) { + (void) sa_delete_sharetab(rname, "smb"); + sa_free_attr_string(rname); + rname = NULL; + } + } + if (rname != NULL) + sa_free_attr_string(rname); + /* + * Always return OK as smb/server may be down and + * Shares will be picked up when loaded. + */ + return (SA_OK); +} + +/* + * smb_share_changed(sa_share_t share) + * + * The specified share has changed. + */ +static int +smb_share_changed(sa_share_t share) +{ + char *path; + sa_resource_t resource; + + /* get the path since it is important in several places */ + path = sa_get_share_attr(share, "path"); + if (path == NULL) + return (SA_NO_SUCH_PATH); + for (resource = sa_get_share_resource(share, NULL); + resource != NULL; + resource = sa_get_next_resource(resource)) + (void) smb_resource_changed(resource); + + sa_free_attr_string(path); + + return (SA_OK); +} + +/* + * smb_resource_changed(sa_resource_t resource) + * + * The specified resource has changed. + */ +static int +smb_resource_changed(sa_resource_t resource) +{ + DWORD res; + lmshare_info_t si; + lmshare_info_t new_si; + char *rname, *path; + sa_optionset_t opts; + sa_share_t share; + + rname = sa_get_resource_attr(resource, "name"); + if (rname == NULL) + return (SA_NO_SUCH_RESOURCE); + + share = sa_get_resource_parent(resource); + if (share == NULL) { + sa_free_attr_string(rname); + return (SA_CONFIG_ERR); + } + + path = sa_get_share_attr(share, "path"); + if (path == NULL) { + sa_free_attr_string(rname); + return (SA_NO_SUCH_PATH); + } + + if (!smb_isonline()) { + sa_free_attr_string(rname); + return (SA_OK); + } + + /* Update the share cache in smb/server */ + res = lmshrd_getinfo(rname, &si); + if (res != NERR_Success) { + sa_free_attr_string(path); + sa_free_attr_string(rname); + return (SA_CONFIG_ERR); + } + + opts = sa_get_derived_optionset(resource, SMB_PROTOCOL_NAME, 1); + smb_build_lmshare_info(rname, path, opts, &new_si); + sa_free_derived_optionset(opts); + sa_free_attr_string(path); + sa_free_attr_string(rname); + + /* + * Update all fields from sa_share_t + * Get derived values. + */ + if (lmshrd_setinfo(&new_si) != LMSHR_DOOR_SRV_SUCCESS) + return (SA_CONFIG_ERR); + return (smb_enable_service()); +} + +/* + * smb_disable_share(sa_share_t share) + * + * Unshare the specified share. + */ +static int +smb_disable_share(sa_share_t share, char *path) +{ + char *rname; + sa_resource_t resource; + boolean_t iszfs; + int err = SA_OK; + + iszfs = sa_path_is_zfs(path); + if (!smb_isonline()) + goto done; + + for (resource = sa_get_share_resource(share, NULL); + resource != NULL; + resource = sa_get_next_resource(resource)) { + rname = sa_get_resource_attr(resource, "name"); + if (rname == NULL) { + continue; + } + if (!iszfs) { + err = lmshrd_delete(rname); + switch (err) { + case NERR_NetNameNotFound: + case NERR_Success: + err = SA_OK; + break; + default: + err = SA_CONFIG_ERR; + break; + } + } else { + share_t sh; + + sa_sharetab_fill_zfs(share, &sh, "smb"); + err = sa_share_zfs(share, (char *)path, &sh, + rname, ZFS_UNSHARE_SMB); + sa_emptyshare(&sh); + } + sa_free_attr_string(rname); + } +done: + if (!iszfs) + (void) sa_delete_sharetab(path, "smb"); + return (err); +} + +/* + * smb_validate_property(property, parent) + * + * Check that the property has a legitimate value for its type. + */ + +static int +smb_validate_property(sa_property_t property, sa_optionset_t parent) +{ + int ret = SA_OK; + char *propname; + int optindex; + sa_group_t parent_group; + char *value; + + propname = sa_get_property_attr(property, "type"); + + if ((optindex = findopt(propname)) < 0) + ret = SA_NO_SUCH_PROP; + + /* need to validate value range here as well */ + if (ret == SA_OK) { + parent_group = sa_get_parent_group((sa_share_t)parent); + if (optdefs[optindex].share && !sa_is_share(parent_group)) + ret = SA_PROP_SHARE_ONLY; + } + if (ret != SA_OK) { + if (propname != NULL) + sa_free_attr_string(propname); + return (ret); + } + + value = sa_get_property_attr(property, "value"); + if (value != NULL) { + /* first basic type checking */ + switch (optdefs[optindex].type) { + case OPT_TYPE_NUMBER: + /* check that the value is all digits */ + if (!is_a_number(value)) + ret = SA_BAD_VALUE; + break; + case OPT_TYPE_BOOLEAN: + if (strlen(value) == 0 || + strcasecmp(value, "true") == 0 || + strcmp(value, "1") == 0 || + strcasecmp(value, "false") == 0 || + strcmp(value, "0") == 0) { + ret = SA_OK; + } else { + ret = SA_BAD_VALUE; + } + break; + case OPT_TYPE_NAME: + /* + * Make sure no invalid characters + */ + if (validresource(value) == B_FALSE) + ret = SA_BAD_VALUE; + break; + case OPT_TYPE_STRING: + /* whatever is here should be ok */ + break; + default: + break; + } + } + + if (value != NULL) + sa_free_attr_string(value); + if (ret == SA_OK && optdefs[optindex].check != NULL) + /* do the property specific check */ + ret = optdefs[optindex].check(property); + + if (propname != NULL) + sa_free_attr_string(propname); + return (ret); +} + +/* + * Protocol management functions + * + * properties defined in the default files are defined in + * proto_option_defs for parsing and validation. + */ + +struct smb_proto_option_defs { + char *name; /* display name -- remove protocol identifier */ + int smb_index; + int32_t minval; + int32_t maxval; /* In case of length of string this should be max */ + int (*validator)(int, char *); + int32_t refresh; +} smb_proto_options[] = { + { SMB_CD_SYS_CMNT, + SMB_CI_SYS_CMNT, 0, MAX_VALUE_BUFLEN, + string_length_check_validator, SMB_REFRESH_REFRESH}, + { SMB_CD_MAX_WORKERS, + SMB_CI_MAX_WORKERS, 64, 1024, range_check_validator, + SMB_REFRESH_REFRESH}, + { SMB_CD_NBSCOPE, + SMB_CI_NBSCOPE, 0, MAX_VALUE_BUFLEN, + string_length_check_validator, SMB_REFRESH_REFRESH}, + { SMB_CD_RDR_IPCMODE, + SMB_CI_RDR_IPCMODE, 0, 0, ipc_mode_validator, SMB_REFRESH_REFRESH}, + { SMB_CD_LM_LEVEL, + SMB_CI_LM_LEVEL, 2, 5, range_check_validator, SMB_REFRESH_REFRESH}, + { SMB_CD_KEEPALIVE, + SMB_CI_KEEPALIVE, 20, 5400, range_check_validator_zero_ok, + SMB_REFRESH_REFRESH}, + { SMB_CD_WINS_SRV1, + SMB_CI_WINS_SRV1, 0, MAX_VALUE_BUFLEN, + ip_address_validator_empty_ok, SMB_REFRESH_REFRESH}, + { SMB_CD_WINS_SRV2, + SMB_CI_WINS_SRV2, 0, MAX_VALUE_BUFLEN, + ip_address_validator_empty_ok, SMB_REFRESH_REFRESH}, + { SMB_CD_WINS_EXCL, + SMB_CI_WINS_EXCL, 0, MAX_VALUE_BUFLEN, + ip_address_csv_list_validator_empty_ok, SMB_REFRESH_REFRESH}, + { SMB_CD_SIGNING_ENABLE, + SMB_CI_SIGNING_ENABLE, 0, 0, true_false_validator, + SMB_REFRESH_REFRESH}, + { SMB_CD_SIGNING_REQD, + SMB_CI_SIGNING_REQD, 0, 0, true_false_validator, + SMB_REFRESH_REFRESH}, + { SMB_CD_RESTRICT_ANON, + SMB_CI_RESTRICT_ANON, 0, 0, true_false_validator, + SMB_REFRESH_REFRESH}, + { SMB_CD_DOMAIN_SRV, + SMB_CI_DOMAIN_SRV, 0, MAX_VALUE_BUFLEN, + ip_address_validator_empty_ok, SMB_REFRESH_REFRESH}, + { SMB_CD_ADS_ENABLE, + SMB_CI_ADS_ENABLE, 0, 0, true_false_validator, SMB_REFRESH_REFRESH}, + { SMB_CD_ADS_USER, + SMB_CI_ADS_USER, 0, MAX_VALUE_BUFLEN, + string_length_check_validator, SMB_REFRESH_REFRESH}, + { SMB_CD_ADS_USER_CONTAINER, + SMB_CI_ADS_USER_CONTAINER, 0, MAX_VALUE_BUFLEN, + string_length_check_validator, SMB_REFRESH_REFRESH}, + { SMB_CD_ADS_DOMAIN, + SMB_CI_ADS_DOMAIN, 0, MAX_VALUE_BUFLEN, + string_length_check_validator, SMB_REFRESH_REFRESH}, + { SMB_CD_ADS_PASSWD, + SMB_CI_ADS_PASSWD, 0, MAX_VALUE_BUFLEN, + string_length_check_validator, SMB_REFRESH_REFRESH}, + { SMB_CD_ADS_IPLOOKUP, + SMB_CI_ADS_IPLOOKUP, 0, 0, true_false_validator, + SMB_REFRESH_REFRESH}, + { SMB_CD_ADS_SITE, + SMB_CI_ADS_SITE, 0, MAX_VALUE_BUFLEN, + string_length_check_validator, SMB_REFRESH_REFRESH}, + { SMB_CD_DYNDNS_ENABLE, + SMB_CI_DYNDNS_ENABLE, 0, 0, true_false_validator, + SMB_REFRESH_REFRESH}, + { SMB_CD_DYNDNS_RETRY_SEC, + SMB_CI_DYNDNS_RETRY_SEC, 0, 20, range_check_validator, + SMB_REFRESH_REFRESH}, + { SMB_CD_DYNDNS_RETRY_COUNT, + SMB_CI_DYNDNS_RETRY_COUNT, 3, 5, range_check_validator, + SMB_REFRESH_REFRESH}, + { SMB_CD_AUTOHOME_MAP, + SMB_CI_AUTOHOME_MAP, 0, MAX_VALUE_BUFLEN, + path_validator}, + {NULL, -1, 0, 0, NULL} +}; + +/* + * Check the range of value as int range. + */ +static int +range_check_validator(int index, char *value) +{ + int ret = SA_OK; + + if (!is_a_number(value)) { + ret = SA_BAD_VALUE; + } else { + int val; + val = strtoul(value, NULL, 0); + if (val < smb_proto_options[index].minval || + val > smb_proto_options[index].maxval) + ret = SA_BAD_VALUE; + } + return (ret); +} + +/* + * Check the range of value as int range. + */ +static int +range_check_validator_zero_ok(int index, char *value) +{ + int ret = SA_OK; + + if (!is_a_number(value)) { + ret = SA_BAD_VALUE; + } else { + int val; + val = strtoul(value, NULL, 0); + if (val == 0) + ret = SA_OK; + else { + if (val < smb_proto_options[index].minval || + val > smb_proto_options[index].maxval) + ret = SA_BAD_VALUE; + } + } + return (ret); +} + +/* + * Check the length of the string + */ +static int +string_length_check_validator(int index, char *value) +{ + int ret = SA_OK; + + if (value == NULL) + return (SA_BAD_VALUE); + if (strlen(value) > smb_proto_options[index].maxval) + ret = SA_BAD_VALUE; + return (ret); +} + +/* + * Check yes/no + */ +/*ARGSUSED*/ +static int +true_false_validator(int index, char *value) +{ + if (value == NULL) + return (SA_BAD_VALUE); + if ((strcasecmp(value, "true") == 0) || + (strcasecmp(value, "false") == 0)) + return (SA_OK); + return (SA_BAD_VALUE); +} + +/* + * Check IP address. + */ +/*ARGSUSED*/ +static int +ip_address_validator_empty_ok(int index, char *value) +{ + char sbytes[16]; + int len; + + if (value == NULL) + return (SA_OK); + len = strlen(value); + if (len == 0) + return (SA_OK); + if (inet_pton(AF_INET, value, (void *)sbytes) != 1) + return (SA_BAD_VALUE); + + return (SA_OK); +} + +/* + * Check IP address list + */ +/*ARGSUSED*/ +static int +ip_address_csv_list_validator_empty_ok(int index, char *value) +{ + char sbytes[16]; + char *ip, *tmp, *ctx; + + if (value == NULL || *value == '\0') + return (SA_OK); + + if (strlen(value) > MAX_VALUE_BUFLEN) + return (SA_BAD_VALUE); + + if ((tmp = strdup(value)) == NULL) + return (SA_NO_MEMORY); + + ip = strtok_r(tmp, ",", &ctx); + while (ip) { + if (strlen(ip) == 0) { + free(tmp); + return (SA_BAD_VALUE); + } + if (*ip != 0) { + if (inet_pton(AF_INET, ip, + (void *)sbytes) != 1) { + free(tmp); + return (SA_BAD_VALUE); + } + } + ip = strtok_r(0, ",", &ctx); + } + + free(tmp); + return (SA_OK); +} + +/* + * Check IPC mode + */ +/*ARGSUSED*/ +static int +ipc_mode_validator(int index, char *value) +{ + if (value == NULL) + return (SA_BAD_VALUE); + if (strcasecmp(value, "anon") == 0) + return (SA_OK); + if (strcasecmp(value, "auth") == 0) + return (SA_OK); + return (SA_BAD_VALUE); +} + +/* + * Check path + */ +/*ARGSUSED*/ +static int +path_validator(int index, char *value) +{ + struct stat buffer; + int fd, status; + + if (value == NULL) + return (SA_BAD_VALUE); + + fd = open(value, O_RDONLY); + if (fd < 0) + return (SA_BAD_VALUE); + + status = fstat(fd, &buffer); + (void) close(fd); + + if (status < 0) + return (SA_BAD_VALUE); + + if (buffer.st_mode & S_IFDIR) + return (SA_OK); + return (SA_BAD_VALUE); +} + +/* + * the protoset holds the defined options so we don't have to read + * them multiple times + */ +static sa_protocol_properties_t protoset; + +static int +findprotoopt(char *name) +{ + int i; + for (i = 0; smb_proto_options[i].name != NULL; i++) { + if (strcasecmp(smb_proto_options[i].name, name) == 0) + return (i); + } + return (-1); +} + +/* + * smb_load_proto_properties() + * + * read the smb config values from SMF. + */ + +static int +smb_load_proto_properties() +{ + sa_property_t prop; + int index; + char *value; + + protoset = sa_create_protocol_properties(SMB_PROTOCOL_NAME); + if (protoset == NULL) + return (SA_NO_MEMORY); + + if (smb_config_load() != 0) + return (SA_CONFIG_ERR); + for (index = 0; smb_proto_options[index].name != NULL; index++) { + value = smb_config_getenv(smb_proto_options[index].smb_index); + prop = sa_create_property( + smb_proto_options[index].name, value); + (void) sa_add_protocol_property(protoset, prop); + } + return (SA_OK); +} + +/* + * smb_share_init() + * + * Initialize the smb plugin. + */ + +static int +smb_share_init(void) +{ + int ret = SA_OK; + + if (sa_plugin_ops.sa_init != smb_share_init) + return (SA_SYSTEM_ERR); + + if (smb_load_proto_properties() != SA_OK) + return (SA_SYSTEM_ERR); + + return (ret); +} + +/* + * smb_share_fini() + * + */ +static void +smb_share_fini(void) +{ + xmlFreeNode(protoset); + protoset = NULL; +} + +/* + * smb_get_proto_set() + * + * Return an optionset with all the protocol specific properties in + * it. + */ +static sa_protocol_properties_t +smb_get_proto_set(void) +{ + return (protoset); +} + +/* + * How long to wait for service to come online + */ +#define WAIT_FOR_SERVICE 15 + +/* + * smb_enable_service() + * + */ +static int +smb_enable_service(void) +{ + int i; + int ret = SA_OK; + + if (!smb_isonline()) { + if (smf_enable_instance(SMBD_DEFAULT_INSTANCE_FMRI, 0) != 0) { + (void) fprintf(stderr, + dgettext(TEXT_DOMAIN, + "%s failed to restart: %s\n"), + scf_strerror(scf_error())); + return (SA_CONFIG_ERR); + } + + /* Wait for service to come online */ + for (i = 0; i < WAIT_FOR_SERVICE; i++) { + if (smb_isonline()) { + ret = SA_OK; + break; + } else { + ret = SA_BUSY; + (void) sleep(1); + } + } + } + return (ret); +} + +/* + * smb_validate_proto_prop(index, name, value) + * + * Verify that the property specified by name can take the new + * value. This is a sanity check to prevent bad values getting into + * the default files. + */ +static int +smb_validate_proto_prop(int index, char *name, char *value) +{ + if ((name == NULL) || (index < 0)) + return (SA_BAD_VALUE); + + if (smb_proto_options[index].validator == NULL) + return (SA_OK); + + if (smb_proto_options[index].validator(index, value) == SA_OK) + return (SA_OK); + return (SA_BAD_VALUE); +} + +/* + * smb_set_proto_prop(prop) + * + * check that prop is valid. + */ +/*ARGSUSED*/ +static int +smb_set_proto_prop(sa_property_t prop) +{ + int ret = SA_OK; + char *name; + char *value; + int index = -1; + + name = sa_get_property_attr(prop, "type"); + value = sa_get_property_attr(prop, "value"); + if (name != NULL && value != NULL) { + index = findprotoopt(name); + if (index >= 0) { + /* should test for valid value */ + ret = smb_validate_proto_prop(index, name, value); + if (ret == SA_OK) { + /* Save to SMF */ + smb_config_setenv( + smb_proto_options[index].smb_index, value); + /* + * Specialized refresh mechanisms can + * be flagged in the proto_options and + * processed here. + */ + if (smb_proto_options[index].refresh & + SMB_REFRESH_REFRESH) + (void) smf_refresh_instance( + SMBD_DEFAULT_INSTANCE_FMRI); + else if (smb_proto_options[index].refresh & + SMB_REFRESH_RESTART) + (void) smf_restart_instance( + SMBD_DEFAULT_INSTANCE_FMRI); + } + } + } + if (name != NULL) + sa_free_attr_string(name); + if (value != NULL) + sa_free_attr_string(value); + + return (ret); +} + +/* + * smb_get_status() + * + * What is the current status of the smbd? We use the SMF state here. + * Caller must free the returned value. + */ + +static char * +smb_get_status(void) +{ + char *state = NULL; + state = smf_get_state(SMBD_DEFAULT_INSTANCE_FMRI); + return (state != NULL ? state : "-"); +} + +/* + * This protocol plugin require resource names + */ +static uint64_t +smb_share_features(void) +{ + return (SA_FEATURE_RESOURCE | SA_FEATURE_ALLOWSUBDIRS | + SA_FEATURE_ALLOWPARDIRS); +} + +/* + * This should be used to convert lmshare_info to sa_resource_t + * Should only be needed to build temp shares/resources to be + * supplied to sharemanager to display temp shares. + */ +static int +smb_build_tmp_sa_resource(sa_handle_t handle, lmshare_info_t *si) +{ + int err; + sa_share_t share; + sa_group_t group; + sa_resource_t resource; + + if (si == NULL) + return (SA_INVALID_NAME); + + /* + * First determine if the "share path" is already shared + * somewhere. If it is, we have to use it as the authority on + * where the transient share lives so will use it's parent + * group. If it doesn't exist, it needs to land in "smb". + */ + + share = sa_find_share(handle, si->directory); + if (share != NULL) { + group = sa_get_parent_group(share); + } else { + group = smb_get_smb_share_group(handle); + if (group == NULL) + return (SA_NO_SUCH_GROUP); + share = sa_get_share(group, si->directory); + if (share == NULL) { + share = sa_add_share(group, si->directory, + SA_SHARE_TRANSIENT, &err); + if (share == NULL) + return (SA_NO_SUCH_PATH); + } + } + + /* + * Now handle the resource. Make sure that the resource is + * transient and added to the share. + */ + resource = sa_get_share_resource(share, si->share_name); + if (resource == NULL) { + resource = sa_add_resource(share, + si->share_name, SA_SHARE_TRANSIENT, &err); + if (resource == NULL) + return (SA_NO_SUCH_RESOURCE); + } + + /* set resource attributes now */ + (void) sa_set_resource_attr(resource, "description", si->comment); + (void) sa_set_resource_attr(resource, SHOPT_AD_CONTAINER, + si->container); + + return (SA_OK); +} + +/* + * Return smb transient shares. Note that we really want to look at + * all current shares from SMB in order to determine this. Transient + * shares should be those that don't appear in either the SMF or ZFS + * configurations. Those that are in the repositories will be + * filtered out by smb_build_tmp_sa_resource. + */ +static int +smb_list_transient(sa_handle_t handle) +{ + int i, offset, num; + lmshare_list_t list; + int res; + + num = lmshrd_num_shares(); + if (num <= 0) + return (SA_OK); + offset = 0; + while (lmshrd_list(offset, &list) != NERR_InternalError) { + if (list.no == 0) + break; + for (i = 0; i < list.no; i++) { + res = smb_build_tmp_sa_resource(handle, + &(list.smbshr[i])); + if (res != SA_OK) + return (res); + } + offset += list.no; + } + + return (SA_OK); +} + +/* + * fix_resource_name(share, name, prefix) + * + * Construct a name where the ZFS dataset has the prefix replaced with "name". + */ +static char * +fix_resource_name(sa_share_t share, char *name, char *prefix) +{ + char *dataset = NULL; + char *newname = NULL; + size_t psize; + size_t nsize; + + dataset = sa_get_share_attr(share, "dataset"); + + if (dataset != NULL && strcmp(dataset, prefix) != 0) { + psize = strlen(prefix); + if (strncmp(dataset, prefix, psize) == 0) { + /* need string plus ',' and NULL */ + nsize = (strlen(dataset) - psize) + strlen(name) + 2; + newname = calloc(nsize, 1); + if (newname != NULL) { + (void) snprintf(newname, nsize, "%s%s", name, + dataset + psize); + sa_fix_resource_name(newname); + } + sa_free_attr_string(dataset); + return (newname); + } + } + if (dataset != NULL) + sa_free_attr_string(dataset); + return (strdup(name)); +} + +/* + * smb_parse_optstring(group, options) + * + * parse a compact option string into individual options. This allows + * ZFS sharesmb and sharemgr "share" command to work. group can be a + * group, a share or a resource. + */ +static int +smb_parse_optstring(sa_group_t group, char *options) +{ + char *dup; + char *base; + char *lasts; + char *token; + sa_optionset_t optionset; + sa_group_t parent = NULL; + sa_resource_t resource = NULL; + int iszfs = 0; + int persist = 0; + int need_optionset = 0; + int ret = SA_OK; + sa_property_t prop; + + /* + * In order to not attempt to change ZFS properties unless + * absolutely necessary, we never do it in the legacy parsing + * so we need to keep track of this. + */ + if (sa_is_share(group)) { + char *zfs; + + parent = sa_get_parent_group(group); + if (parent != NULL) { + zfs = sa_get_group_attr(parent, "zfs"); + if (zfs != NULL) { + sa_free_attr_string(zfs); + iszfs = 1; + } + } + } else { + iszfs = sa_group_is_zfs(group); + /* + * If a ZFS group, then we need to see if a resource + * name is being set. If so, bail with + * SA_PROP_SHARE_ONLY, so we come back in with a share + * instead of a group. + */ + if (strncmp(options, "name=", sizeof ("name=") - 1) == 0 || + strstr(options, ",name=") != NULL) { + return (SA_PROP_SHARE_ONLY); + } + } + + /* do we have an existing optionset? */ + optionset = sa_get_optionset(group, "smb"); + if (optionset == NULL) { + /* didn't find existing optionset so create one */ + optionset = sa_create_optionset(group, "smb"); + if (optionset == NULL) + return (SA_NO_MEMORY); + } else { + /* + * If an optionset already exists, we've come through + * twice so ignore the second time. + */ + return (ret); + } + + /* We need a copy of options for the next part. */ + dup = strdup(options); + if (dup == NULL) + return (SA_NO_MEMORY); + + /* + * SMB properties are straightforward and are strings, + * integers or booleans. Properties are separated by + * commas. It will be necessary to parse quotes due to some + * strings not having a restricted characters set. + * + * Note that names will create a resource. For now, if there + * is a set of properties "before" the first name="", those + * properties will be placed on the group. + */ + persist = sa_is_persistent(group); + base = dup; + token = dup; + lasts = NULL; + while (token != NULL && ret == SA_OK) { + ret = SA_OK; + token = strtok_r(base, ",", &lasts); + base = NULL; + if (token != NULL) { + char *value; + /* + * All SMB properties have values so there + * MUST be an '=' character. If it doesn't, + * it is a syntax error. + */ + value = strchr(token, '='); + if (value != NULL) { + *value++ = '\0'; + } else { + ret = SA_SYNTAX_ERR; + break; + } + /* + * We may need to handle a "name" property + * that is a ZFS imposed resource name. Each + * name would trigger getting a new "resource" + * to put properties on. For now, assume no + * "name" property for special handling. + */ + + if (strcmp(token, "name") == 0) { + char *prefix; + char *name = NULL; + /* + * We have a name, so now work on the + * resource level. We have a "share" + * in "group" due to the caller having + * added it. If we are called with a + * group, the check for group/share + * at the beginning of this function + * will bail out the parse if there is a + * "name" but no share. + */ + if (!iszfs) { + ret = SA_SYNTAX_ERR; + break; + } + /* + * Make sure the parent group has the + * "prefix" property since we will + * need to use this for constructing + * inherited name= values. + */ + prefix = sa_get_group_attr(parent, "prefix"); + if (prefix == NULL) { + prefix = sa_get_group_attr(parent, + "name"); + if (prefix != NULL) { + (void) sa_set_group_attr(parent, + "prefix", prefix); + } + } + name = fix_resource_name((sa_share_t)group, + value, prefix); + if (name != NULL) { + resource = sa_add_resource( + (sa_share_t)group, name, + SA_SHARE_TRANSIENT, &ret); + sa_free_attr_string(name); + } else { + ret = SA_NO_MEMORY; + } + if (prefix != NULL) + sa_free_attr_string(prefix); + + /* A resource level optionset is needed */ + + need_optionset = 1; + if (resource == NULL) { + ret = SA_NO_MEMORY; + break; + } + continue; + } + + if (need_optionset) { + optionset = sa_create_optionset(resource, + "smb"); + need_optionset = 0; + } + + prop = sa_create_property(token, value); + if (prop == NULL) + ret = SA_NO_MEMORY; + else + ret = sa_add_property(optionset, prop); + if (ret != SA_OK) + break; + if (!iszfs) + ret = sa_commit_properties(optionset, !persist); + } + } + free(dup); + return (ret); +} + +/* + * smb_sprint_option(rbuff, rbuffsize, incr, prop, sep) + * + * provides a mechanism to format SMB properties into legacy output + * format. If the buffer would overflow, it is reallocated and grown + * as appropriate. Special cases of converting internal form of values + * to those used by "share" are done. this function does one property + * at a time. + */ + +static void +smb_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr, + sa_property_t prop, int sep) +{ + char *name; + char *value; + int curlen; + char *buff = *rbuff; + size_t buffsize = *rbuffsize; + + name = sa_get_property_attr(prop, "type"); + value = sa_get_property_attr(prop, "value"); + if (buff != NULL) + curlen = strlen(buff); + else + curlen = 0; + if (name != NULL) { + int len; + len = strlen(name) + sep; + + /* + * A future RFE would be to replace this with more + * generic code and to possibly handle more types. + * + * For now, everything else is treated as a string. If + * we get any properties that aren't exactly + * name/value pairs, we may need to + * interpret/transform. + */ + if (value != NULL) + len += 1 + strlen(value); + + while (buffsize <= (curlen + len)) { + /* need more room */ + buffsize += incr; + buff = realloc(buff, buffsize); + *rbuff = buff; + *rbuffsize = buffsize; + if (buff == NULL) { + /* realloc failed so free everything */ + if (*rbuff != NULL) + free(*rbuff); + goto err; + } + } + if (buff == NULL) + goto err; + (void) snprintf(buff + curlen, buffsize - curlen, + "%s%s=%s", sep ? "," : "", + name, value != NULL ? value : "\"\""); + + } +err: + if (name != NULL) + sa_free_attr_string(name); + if (value != NULL) + sa_free_attr_string(value); +} + +/* + * smb_format_resource_options(resource, hier) + * + * format all the options on the group into a flattened option + * string. If hier is non-zero, walk up the tree to get inherited + * options. + */ + +static char * +smb_format_options(sa_group_t group, int hier) +{ + sa_optionset_t options = NULL; + sa_property_t prop; + int sep = 0; + char *buff; + size_t buffsize; + + + buff = malloc(OPT_CHUNK); + if (buff == NULL) + return (NULL); + + buff[0] = '\0'; + buffsize = OPT_CHUNK; + + /* + * We may have a an optionset relative to this item. format + * these if we find them and then add any security definitions. + */ + + options = sa_get_derived_optionset(group, "smb", hier); + + /* + * do the default set first but skip any option that is also + * in the protocol specific optionset. + */ + if (options != NULL) { + for (prop = sa_get_property(options, NULL); + prop != NULL; prop = sa_get_next_property(prop)) { + /* + * use this one since we skipped any + * of these that were also in + * optdefault + */ + smb_sprint_option(&buff, &buffsize, OPT_CHUNK, + prop, sep); + if (buff == NULL) { + /* + * buff could become NULL if there + * isn't enough memory for + * smb_sprint_option to realloc() + * as necessary. We can't really + * do anything about it at this + * point so we return NULL. The + * caller should handle the + * failure. + */ + if (options != NULL) + sa_free_derived_optionset( + options); + return (buff); + } + sep = 1; + } + } + + if (options != NULL) + sa_free_derived_optionset(options); + return (buff); +} + +/* + * smb_rename_resource(resource, newname) + * + * Change the current exported name of the resource to newname. + */ +/*ARGSUSED*/ +int +smb_rename_resource(sa_handle_t handle, sa_resource_t resource, char *newname) +{ + int ret = SA_OK; + int err; + char *oldname; + + oldname = sa_get_resource_attr(resource, "name"); + if (oldname == NULL) + return (SA_NO_SUCH_RESOURCE); + + err = lmshrd_rename(oldname, newname); + + /* improve error values somewhat */ + switch (err) { + case NERR_Success: + break; + case NERR_InternalError: + ret = SA_SYSTEM_ERR; + break; + case NERR_DuplicateShare: + ret = SA_DUPLICATE_NAME; + break; + default: + ret = SA_CONFIG_ERR; + break; + } + + return (ret); +} diff --git a/usr/src/lib/libshare/smb/libshare_smb.h b/usr/src/lib/libshare/smb/libshare_smb.h new file mode 100644 index 000000000000..2d86c9ed32b7 --- /dev/null +++ b/usr/src/lib/libshare/smb/libshare_smb.h @@ -0,0 +1,75 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * basic API declarations for share management + */ + +#ifndef _LIBSHARE_SMB_H +#define _LIBSHARE_SMB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + * defined options types. These should be in a file rather than + * compiled in. Until there is a plugin mechanism to add new types, + * this is sufficient. + */ +#define OPT_TYPE_ANY 0 +#define OPT_TYPE_STRING 1 +#define OPT_TYPE_BOOLEAN 2 +#define OPT_TYPE_NUMBER 3 +#define OPT_TYPE_PATH 4 +#define OPT_TYPE_PROTOCOL 5 +#define OPT_TYPE_NAME 6 + +struct option_defs { + char *tag; + int type; + int share; /* share only option */ + int (*check)(char *); +}; + +/* + * Sharectl property refresh types. Bit mask to indicate which type(s) + * of refresh might be needed on the service(s). + */ + +#define SMB_REFRESH_RESTART 0x0001 /* restart smb/server */ +#define SMB_REFRESH_REFRESH 0x0002 /* refresh smb/server */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBSHARE_SMB_H */ diff --git a/usr/src/lib/libshare/smb/mapfile-vers b/usr/src/lib/libshare/smb/mapfile-vers new file mode 100644 index 000000000000..18c216bf6bec --- /dev/null +++ b/usr/src/lib/libshare/smb/mapfile-vers @@ -0,0 +1,34 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +SUNWprivate_1.1 { + global: + sa_plugin_ops; + local: + *; +}; + diff --git a/usr/src/lib/libshare/smb/smb_share_doorclnt.c b/usr/src/lib/libshare/smb/smb_share_doorclnt.c new file mode 100644 index 000000000000..0ebf61b7a905 --- /dev/null +++ b/usr/src/lib/libshare/smb/smb_share_doorclnt.c @@ -0,0 +1,951 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * User-space door client for LanMan share management. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +int lmshrd_fildes = -1; + +char *lmshrd_desc[] = { + "", + "LmshrdOpenIter", + "LmshrdCloseIter", + "LmshrdIterate", + "LmshrdNumShares", + "LmshrdDelete", + "LmshrdRename", + "LmshrdGetinfo", + "LmshrdAdd", + "LmshrdSetinfo", + "LmshrdExists", + "LmshrdIsSpecial", + "LmshrdIsRestricted", + "LmshrdIsAdmin", + "LmshrdIsValid", + "LmshrdIsDir", + "LmshrdList", + "LmshrdListTrans", + "LmshrdNumTrans", + "N/A", + 0 +}; + +/* + * Returns 0 on success. Otherwise, -1. + */ +static int +lmshrd_door_open(int opcode) +{ + int rc = 0; + + if (lmshrd_fildes == -1 && + (lmshrd_fildes = open(LMSHR_DOOR_NAME, O_RDONLY)) < 0) { + syslog(LOG_DEBUG, "%s: open %s failed %s", lmshrd_desc[opcode], + LMSHR_DOOR_NAME, strerror(errno)); + rc = -1; + } + return (rc); +} + +/* + * Return 0 upon success. Otherwise, -1. + */ +static int +lmshrd_door_check_srv_status(int opcode, smb_dr_ctx_t *dec_ctx) +{ + int status = smb_dr_get_int32(dec_ctx); + int err; + int rc = -1; + + switch (status) { + case LMSHR_DOOR_SRV_SUCCESS: + rc = 0; + break; + + case LMSHR_DOOR_SRV_ERROR: + err = smb_dr_get_uint32(dec_ctx); + syslog(LOG_ERR, "%s: Encountered door server error %s", + lmshrd_desc[opcode], strerror(err)); + break; + + default: + syslog(LOG_ERR, "%s: Unknown door server status", + lmshrd_desc[opcode]); + } + + if (rc != 0) { + if ((err = smb_dr_decode_finish(dec_ctx)) != 0) + syslog(LOG_ERR, "%s: Decode error %s", + lmshrd_desc[opcode], strerror(err)); + } + + return (rc); +} + +uint64_t +lmshrd_open_iterator(int mode) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + unsigned int status = 0; + uint64_t lmshr_iter = 0; + int opcode = LMSHR_DOOR_OPEN_ITERATOR; + + if (lmshrd_door_open(opcode) == -1) + return (lmshr_iter); + + buf = malloc(LMSHR_DOOR_SIZE); + if (!buf) + return (lmshr_iter); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_int32(enc_ctx, mode); + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (lmshr_iter); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_call(lmshrd_fildes, &arg) < 0) { + syslog(LOG_DEBUG, "%s: Door call failed %s", + lmshrd_desc[opcode], strerror(errno)); + (void) free(buf); + lmshrd_fildes = -1; + return (lmshr_iter); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + (void) free(buf); + return (lmshr_iter); + } + + lmshr_iter = smb_dr_get_lmshr_iterator(dec_ctx); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (lmshr_iter); + } + + (void) free(buf); + return (lmshr_iter); +} + + +DWORD +lmshrd_close_iterator(uint64_t iterator) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + unsigned int status = 0; + int opcode = LMSHR_DOOR_CLOSE_ITERATOR; + + if (lmshrd_door_open(opcode) == -1) + return (NERR_InternalError); + + buf = malloc(LMSHR_DOOR_SIZE); + if (!buf) + return (NERR_InternalError); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_lmshr_iterator(enc_ctx, iterator); + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_call(lmshrd_fildes, &arg) < 0) { + syslog(LOG_DEBUG, "%s: Door call failed %s", + lmshrd_desc[opcode], strerror(errno)); + (void) free(buf); + lmshrd_fildes = -1; + return (NERR_InternalError); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + (void) free(buf); + return (NERR_InternalError); + } + + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + (void) free(buf); + return (NERR_Success); +} + +DWORD +lmshrd_iterate(uint64_t iterator, lmshare_info_t *si) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + unsigned int status = 0; + int opcode = LMSHR_DOOR_ITERATE; + + if (lmshrd_door_open(opcode) == -1) + return (NERR_InternalError); + + buf = malloc(LMSHR_DOOR_SIZE); + if (!buf) + return (NERR_InternalError); + + bzero(si, sizeof (lmshare_info_t)); + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_lmshr_iterator(enc_ctx, iterator); + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_call(lmshrd_fildes, &arg) < 0) { + syslog(LOG_DEBUG, "%s: Door call failed %s", + lmshrd_desc[opcode], strerror(errno)); + (void) free(buf); + lmshrd_fildes = -1; + return (NERR_InternalError); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + (void) free(buf); + return (NERR_InternalError); + } + + smb_dr_get_lmshare(dec_ctx, si); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + (void) free(buf); + return (NERR_Success); +} + +DWORD +lmshrd_list(int offset, lmshare_list_t *list) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + int status; + DWORD rc; + int opcode = LMSHR_DOOR_LIST; + + if (lmshrd_door_open(opcode) == -1) + return (NERR_InternalError); + + buf = malloc(LMSHR_DOOR_SIZE); + + if (!buf) + return (NERR_InternalError); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_int32(enc_ctx, offset); + + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_call(lmshrd_fildes, &arg) < 0) { + syslog(LOG_DEBUG, "%s: Door call failed %s", + lmshrd_desc[opcode], strerror(errno)); + (void) free(buf); + lmshrd_fildes = -1; + return (NERR_InternalError); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + (void) free(buf); + return (NERR_InternalError); + } + + rc = smb_dr_get_uint32(dec_ctx); + smb_dr_get_lmshr_list(dec_ctx, list); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + (void) free(buf); + + return (rc); +} + +int +lmshrd_num_shares(void) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + unsigned int status = 0; + DWORD num_shares; + int opcode = LMSHR_DOOR_NUM_SHARES; + + if (lmshrd_door_open(opcode) == -1) + return (-1); + + buf = malloc(LMSHR_DOOR_SIZE); + if (!buf) + return (-1); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, LMSHR_DOOR_NUM_SHARES); + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (-1); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_call(lmshrd_fildes, &arg) < 0) { + syslog(LOG_DEBUG, "%s: Door call failed %s", + lmshrd_desc[opcode], strerror(errno)); + (void) free(buf); + lmshrd_fildes = -1; + return (-1); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + (void) free(buf); + return (-1); + } + + num_shares = smb_dr_get_uint32(dec_ctx); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (-1); + } + + (void) free(buf); + return (num_shares); +} + +DWORD +lmshrd_delete(char *share_name) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + int status; + DWORD rc; + int opcode = LMSHR_DOOR_DELETE; + + if (lmshrd_door_open(opcode) == -1) + return (NERR_InternalError); + + buf = malloc(LMSHR_DOOR_SIZE); + if (!buf) + return (NERR_InternalError); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, LMSHR_DOOR_DELETE); + smb_dr_put_string(enc_ctx, share_name); + + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_call(lmshrd_fildes, &arg) < 0) { + syslog(LOG_DEBUG, "%s: Door call failed %s", + lmshrd_desc[opcode], strerror(errno)); + (void) free(buf); + lmshrd_fildes = -1; + return (NERR_InternalError); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + (void) free(buf); + return (NERR_InternalError); + } + + rc = smb_dr_get_uint32(dec_ctx); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + (void) free(buf); + return (rc); + +} + +DWORD +lmshrd_rename(char *from, char *to) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + int status; + DWORD rc; + int opcode = LMSHR_DOOR_RENAME; + + if (lmshrd_door_open(opcode) == -1) + return (NERR_InternalError); + + buf = malloc(LMSHR_DOOR_SIZE); + if (!buf) + return (NERR_InternalError); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, LMSHR_DOOR_RENAME); + smb_dr_put_string(enc_ctx, from); + smb_dr_put_string(enc_ctx, to); + + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_call(lmshrd_fildes, &arg) < 0) { + syslog(LOG_DEBUG, "%s: Door call failed %s", + lmshrd_desc[opcode], strerror(errno)); + (void) free(buf); + lmshrd_fildes = -1; + return (NERR_InternalError); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + (void) free(buf); + return (NERR_InternalError); + } + + rc = smb_dr_get_uint32(dec_ctx); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + (void) free(buf); + return (rc); +} + +DWORD +lmshrd_getinfo(char *share_name, lmshare_info_t *si) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + int status; + DWORD rc; + int opcode = LMSHR_DOOR_GETINFO; + + if (lmshrd_door_open(opcode) == -1) + return (NERR_InternalError); + + buf = malloc(LMSHR_DOOR_SIZE); + if (!buf) + return (NERR_InternalError); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, LMSHR_DOOR_GETINFO); + smb_dr_put_string(enc_ctx, share_name); + + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_call(lmshrd_fildes, &arg) < 0) { + syslog(LOG_DEBUG, "%s: Door call failed %s", + lmshrd_desc[opcode], strerror(errno)); + (void) free(buf); + lmshrd_fildes = -1; + return (NERR_InternalError); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + (void) free(buf); + return (NERR_InternalError); + } + + rc = smb_dr_get_uint32(dec_ctx); + smb_dr_get_lmshare(dec_ctx, si); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + (void) free(buf); + return (rc); +} + +DWORD +lmshrd_add(lmshare_info_t *si) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + int status; + DWORD rc; + int opcode = LMSHR_DOOR_ADD; + + if (lmshrd_door_open(opcode) == -1) + return (NERR_InternalError); + + buf = malloc(LMSHR_DOOR_SIZE); + if (!buf) + return (NERR_InternalError); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_lmshare(enc_ctx, si); + + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_call(lmshrd_fildes, &arg) < 0) { + syslog(LOG_DEBUG, "%s: Door call failed %s", + lmshrd_desc[opcode], strerror(errno)); + (void) free(buf); + lmshrd_fildes = -1; + return (NERR_InternalError); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + (void) free(buf); + return (NERR_InternalError); + } + + rc = smb_dr_get_uint32(dec_ctx); + smb_dr_get_lmshare(dec_ctx, si); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + (void) free(buf); + return (rc); +} + +DWORD +lmshrd_setinfo(lmshare_info_t *si) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + int status; + DWORD rc; + int opcode = LMSHR_DOOR_SETINFO; + + if (lmshrd_door_open(opcode) == -1) + return (NERR_InternalError); + + buf = malloc(LMSHR_DOOR_SIZE); + if (!buf) + return (NERR_InternalError); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_lmshare(enc_ctx, si); + + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_call(lmshrd_fildes, &arg) < 0) { + syslog(LOG_DEBUG, "%s: Door call failed %s", + lmshrd_desc[opcode], strerror(errno)); + (void) free(buf); + lmshrd_fildes = -1; + return (NERR_InternalError); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + (void) free(buf); + return (NERR_InternalError); + } + + rc = smb_dr_get_uint32(dec_ctx); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + (void) free(buf); + return (rc); +} + +static int +lmshrd_check(char *share_name, int opcode) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + int status, rc; + + if (lmshrd_door_open(opcode) == -1) + return (NERR_InternalError); + + buf = malloc(LMSHR_DOOR_SIZE); + if (!buf) + return (NERR_InternalError); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_string(enc_ctx, share_name); + + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_call(lmshrd_fildes, &arg) < 0) { + syslog(LOG_DEBUG, "%s: Door call failed %s", + lmshrd_desc[opcode], strerror(errno)); + (void) free(buf); + lmshrd_fildes = -1; + return (NERR_InternalError); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + (void) free(buf); + return (NERR_InternalError); + } + + rc = smb_dr_get_int32(dec_ctx); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + lmshrd_desc[opcode], strerror(status)); + (void) free(buf); + return (NERR_InternalError); + } + + (void) free(buf); + return (rc); +} + +int +lmshrd_exists(char *share_name) +{ + return (lmshrd_check(share_name, LMSHR_DOOR_EXISTS)); +} + +int +lmshrd_is_special(char *share_name) +{ + return (lmshrd_check(share_name, LMSHR_DOOR_IS_SPECIAL)); +} + +int +lmshrd_is_restricted(char *share_name) +{ + return (lmshrd_check(share_name, LMSHR_DOOR_IS_RESTRICTED)); +} + +int +lmshrd_is_admin(char *share_name) +{ + return (lmshrd_check(share_name, LMSHR_DOOR_IS_ADMIN)); +} + +int +lmshrd_is_valid(char *share_name) +{ + return (lmshrd_check(share_name, LMSHR_DOOR_IS_VALID)); +} + +int +lmshrd_is_dir(char *path) +{ + return (lmshrd_check(path, LMSHR_DOOR_IS_DIR)); +} + +static char * +lmshare_decode_type(unsigned int stype) +{ + switch (stype) { + case STYPE_DISKTREE: + return ("Disk"); + case STYPE_PRINTQ: + return ("Print Queue"); + case STYPE_DEVICE: + return ("Device"); + case STYPE_IPC: + return ("IPC"); + case STYPE_DFS: + return ("DFS"); + case STYPE_SPECIAL: + return ("Special"); + default: + return ("Unknown"); + }; +} + + +static void +lmshare_loginfo(FILE *fp, lmshare_info_t *si) +{ + if (!si) { + return; + } + + (void) fprintf(fp, "\n%s Information:\n", si->share_name); + (void) fprintf(fp, "\tFolder: %s\n", si->directory); + (void) fprintf(fp, "\tType: %s\n", lmshare_decode_type(si->stype)); + (void) fprintf(fp, "\tComment: %s\n", si->comment); + + (void) fprintf(fp, "\tStatus: %s\n", + ((si->mode & LMSHRM_TRANS) ? "Transient" : "Permanent")); + + (void) fprintf(fp, "\tContainer: %s\n", si->container); +} + +int +lmshrd_dump_hash(char *logfname) +{ + lmshare_info_t si; + uint64_t it; + FILE *fp; + + if ((logfname == 0) || (*logfname == 0)) + fp = stdout; + else { + fp = fopen(logfname, "w"); + if (fp == 0) { + syslog(LOG_WARNING, "LmshareDump [%s]:" + " cannot create logfile", logfname); + syslog(LOG_WARNING, "LmshareDump:" + " output will be written on screen"); + } + } + + it = lmshrd_open_iterator(LMSHRM_PERM); + if (it == NULL) { + syslog(LOG_ERR, "LmshareDump: resource shortage"); + if (fp && fp != stdout) { + (void) fclose(fp); + } + return (1); + } + + if (lmshrd_iterate(it, &si) != NERR_Success) { + syslog(LOG_ERR, "LmshareDump: Iterator iterate failed"); + if (fp && fp != stdout) { + (void) fclose(fp); + } + return (1); + } + while (*si.share_name != 0) { + lmshare_loginfo(fp, &si); + if (lmshrd_iterate(it, &si) != NERR_Success) { + syslog(LOG_ERR, "LmshareDump: Iterator iterate failed"); + if (fp && fp != stdout) { + (void) fclose(fp); + } + return (1); + } + } + + if (lmshrd_close_iterator(it) != NERR_Success) { + syslog(LOG_ERR, "LmshareDump: Iterator close failed"); + if (fp && fp != stdout) { + (void) fclose(fp); + } + return (1); + } + if (fp && fp != stdout) { + (void) fclose(fp); + } + return (0); +} diff --git a/usr/src/lib/libshare/smb/sparc/Makefile b/usr/src/lib/libshare/smb/sparc/Makefile new file mode 100644 index 000000000000..543238473c55 --- /dev/null +++ b/usr/src/lib/libshare/smb/sparc/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/libshare/smb/sparcv9/Makefile b/usr/src/lib/libshare/smb/sparcv9/Makefile new file mode 100644 index 000000000000..90b1730d235a --- /dev/null +++ b/usr/src/lib/libshare/smb/sparcv9/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libsldap/common/ns_writes.c b/usr/src/lib/libsldap/common/ns_writes.c index 6c39272eba18..4ac8c16c132d 100644 --- a/usr/src/lib/libsldap/common/ns_writes.c +++ b/usr/src/lib/libsldap/common/ns_writes.c @@ -86,7 +86,7 @@ replace_mapped_attr_in_dn( *new_dn = NULL; /* - * seperate dn into individual componets + * separate dn into individual componets * e.g. * "automountKey=user_01" , "automountMapName_test=auto_home", ... */ diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h index 73bd80d201b6..265d22e66b01 100644 --- a/usr/src/lib/libzfs/common/libzfs.h +++ b/usr/src/lib/libzfs/common/libzfs.h @@ -110,6 +110,8 @@ enum { EZFS_BADPERMSET, /* invalid permission set name */ EZFS_NODELEGATION, /* delegated administration is disabled */ EZFS_PERMRDONLY, /* pemissions are readonly */ + EZFS_UNSHARESMBFAILED, /* failed to unshare over smb */ + EZFS_SHARESMBFAILED, /* failed to share over smb */ EZFS_UNKNOWN }; @@ -320,7 +322,6 @@ extern const char *zfs_get_name(const zfs_handle_t *); /* * zfs dataset property management */ -extern int zfs_prop_valid_for_type(zfs_prop_t, int); extern const char *zfs_prop_default_string(zfs_prop_t); extern uint64_t zfs_prop_default_numeric(zfs_prop_t); extern const char *zfs_prop_column_name(zfs_prop_t); @@ -459,15 +460,22 @@ extern int zfs_unshare(zfs_handle_t *); * Protocol-specific share support functions. */ extern boolean_t zfs_is_shared_nfs(zfs_handle_t *, char **); +extern boolean_t zfs_is_shared_smb(zfs_handle_t *, char **); extern int zfs_share_nfs(zfs_handle_t *); +extern int zfs_share_smb(zfs_handle_t *); +extern int zfs_shareall(zfs_handle_t *); extern int zfs_unshare_nfs(zfs_handle_t *, const char *); +extern int zfs_unshare_smb(zfs_handle_t *, const char *); extern int zfs_unshareall_nfs(zfs_handle_t *); +extern int zfs_unshareall_smb(zfs_handle_t *); +extern int zfs_unshareall_bypath(zfs_handle_t *, const char *); +extern int zfs_unshareall(zfs_handle_t *); extern boolean_t zfs_is_shared_iscsi(zfs_handle_t *); extern int zfs_share_iscsi(zfs_handle_t *); extern int zfs_unshare_iscsi(zfs_handle_t *); extern int zfs_iscsi_perm_check(libzfs_handle_t *, char *, ucred_t *); extern int zfs_deleg_share_nfs(libzfs_handle_t *, char *, char *, - void *, void *, int, boolean_t); + void *, void *, int, zfs_share_op_t); /* * When dealing with nvlists, verify() is extremely useful diff --git a/usr/src/lib/libzfs/common/libzfs_changelist.c b/usr/src/lib/libzfs/common/libzfs_changelist.c index 9ceefdc3b101..2b53f7d9834d 100644 --- a/usr/src/lib/libzfs/common/libzfs_changelist.c +++ b/usr/src/lib/libzfs/common/libzfs_changelist.c @@ -71,6 +71,7 @@ typedef struct prop_changenode { struct prop_changelist { zfs_prop_t cl_prop; zfs_prop_t cl_realprop; + zfs_prop_t cl_shareprop; /* used with sharenfs/sharesmb */ uu_list_pool_t *cl_pool; uu_list_t *cl_list; boolean_t cl_waslegacy; @@ -185,6 +186,7 @@ changelist_postfix(prop_changelist_t *clp) cn = uu_list_prev(clp->cl_list, cn)) { boolean_t sharenfs; + boolean_t sharesmb; /* * If we are in the global zone, but this dataset is exported @@ -222,14 +224,18 @@ changelist_postfix(prop_changelist_t *clp) /* * Remount if previously mounted or mountpoint was legacy, - * or sharenfs property is set. + * or sharenfs or sharesmb property is set. */ sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0) && (strcmp(shareopts, "off") != 0)); - if ((cn->cn_mounted || clp->cl_waslegacy || sharenfs) && - !zfs_is_mounted(cn->cn_handle, NULL) && + sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB, + shareopts, sizeof (shareopts), NULL, NULL, 0, + B_FALSE) == 0) && (strcmp(shareopts, "off") != 0)); + + if ((cn->cn_mounted || clp->cl_waslegacy || sharenfs || + sharesmb) && !zfs_is_mounted(cn->cn_handle, NULL) && zfs_mount(cn->cn_handle, NULL, 0) != 0) ret = -1; @@ -237,11 +243,16 @@ changelist_postfix(prop_changelist_t *clp) * We always re-share even if the filesystem is currently * shared, so that we can adopt any new options. */ - if (cn->cn_shared || clp->cl_waslegacy || sharenfs) { + if (cn->cn_shared || clp->cl_waslegacy || + sharenfs || sharesmb) { if (sharenfs) ret = zfs_share_nfs(cn->cn_handle); else ret = zfs_unshare_nfs(cn->cn_handle, NULL); + if (sharesmb) + ret = zfs_share_smb(cn->cn_handle); + else + ret = zfs_unshare_smb(cn->cn_handle, NULL); } } @@ -302,21 +313,22 @@ changelist_rename(prop_changelist_t *clp, const char *src, const char *dst) } /* - * Given a gathered changelist for the 'sharenfs' property, unshare all the - * datasets in the list. + * Given a gathered changelist for the 'sharenfs' or 'sharesmb' property, + * unshare all the datasets in the list. */ int -changelist_unshare(prop_changelist_t *clp) +changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto) { prop_changenode_t *cn; int ret = 0; - if (clp->cl_prop != ZFS_PROP_SHARENFS) + if (clp->cl_prop != ZFS_PROP_SHARENFS && + clp->cl_prop != ZFS_PROP_SHARESMB) return (0); for (cn = uu_list_first(clp->cl_list); cn != NULL; cn = uu_list_next(clp->cl_list, cn)) { - if (zfs_unshare_nfs(cn->cn_handle, NULL) != 0) + if (zfs_unshare_proto(cn->cn_handle, NULL, proto) != 0) ret = -1; } @@ -386,6 +398,7 @@ change_one(zfs_handle_t *zhp, void *data) char where[64]; prop_changenode_t *cn; zprop_source_t sourcetype; + zprop_source_t share_sourcetype; /* * We only want to unmount/unshare those filesystems that may inherit @@ -405,9 +418,25 @@ change_one(zfs_handle_t *zhp, void *data) return (0); } + /* + * If we are "watching" sharenfs or sharesmb + * then check out the companion property which is tracked + * in cl_shareprop + */ + if (clp->cl_shareprop != ZPROP_INVAL && + zfs_prop_get(zhp, clp->cl_shareprop, property, + sizeof (property), &share_sourcetype, where, sizeof (where), + B_FALSE) != 0) { + zfs_close(zhp); + return (0); + } + if (clp->cl_alldependents || clp->cl_allchildren || sourcetype == ZPROP_SRC_DEFAULT || - sourcetype == ZPROP_SRC_INHERITED) { + sourcetype == ZPROP_SRC_INHERITED || + (clp->cl_shareprop != ZPROP_INVAL && + (share_sourcetype == ZPROP_SRC_DEFAULT || + share_sourcetype == ZPROP_SRC_INHERITED))) { if ((cn = zfs_alloc(zfs_get_handle(zhp), sizeof (prop_changenode_t))) == NULL) { zfs_close(zhp); @@ -507,7 +536,8 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags) * order, regardless of their position in the hierarchy. */ if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED || - prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) { + prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS || + prop == ZFS_PROP_SHARESMB) { compare = compare_mountpoints; clp->cl_sorted = B_TRUE; } @@ -561,9 +591,19 @@ changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags) if (clp->cl_prop != ZFS_PROP_MOUNTPOINT && clp->cl_prop != ZFS_PROP_SHARENFS && + clp->cl_prop != ZFS_PROP_SHARESMB && clp->cl_prop != ZFS_PROP_SHAREISCSI) return (clp); + /* + * If watching SHARENFS or SHARESMB then + * also watch its companion property. + */ + if (clp->cl_prop == ZFS_PROP_SHARENFS) + clp->cl_shareprop = ZFS_PROP_SHARESMB; + else if (clp->cl_prop == ZFS_PROP_SHARESMB) + clp->cl_shareprop = ZFS_PROP_SHARENFS; + if (clp->cl_alldependents) { if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) { changelist_free(clp); diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c index 04a37032eaae..db912e7f20b5 100644 --- a/usr/src/lib/libzfs/common/libzfs_dataset.c +++ b/usr/src/lib/libzfs/common/libzfs_dataset.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include "zfs_namecheck.h" @@ -376,7 +377,7 @@ make_dataset_handle(libzfs_handle_t *hdl, const char *path) if (ioctl(hdl->libzfs_fd, ZFS_IOC_ROLLBACK, &zc) == 0) goto top; /* - * If we can sucessfully destroy it, pretend that it + * If we can successfully destroy it, pretend that it * never existed. */ if (ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc) == 0) { @@ -481,6 +482,8 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, char *strval; zfs_prop_t prop; nvlist_t *ret; + int chosen_normal = -1; + int chosen_utf = -1; if (type == ZFS_TYPE_SNAPSHOT) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, @@ -545,7 +548,7 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, } if (zfs_prop_readonly(prop) && - (prop != ZFS_PROP_VOLBLOCKSIZE || zhp != NULL)) { + (!zfs_prop_setonce(prop) || zhp != NULL)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' is readonly"), propname); @@ -636,19 +639,23 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, /*FALLTHRU*/ + case ZFS_PROP_SHARESMB: case ZFS_PROP_SHARENFS: /* - * For the mountpoint and sharenfs properties, check if - * it can be set in a global/non-global zone based on + * For the mountpoint and sharenfs or sharesmb + * properties, check if it can be set in a + * global/non-global zone based on * the zoned property value: * * global zone non-global zone * -------------------------------------------------- * zoned=on mountpoint (no) mountpoint (yes) * sharenfs (no) sharenfs (no) + * sharesmb (no) sharesmb (no) * * zoned=off mountpoint (yes) N/A * sharenfs (yes) + * sharesmb (yes) */ if (zoned) { if (getzoneid() == GLOBAL_ZONEID) { @@ -659,7 +666,8 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, (void) zfs_error(hdl, EZFS_ZONED, errbuf); goto error; - } else if (prop == ZFS_PROP_SHARENFS) { + } else if (prop == ZFS_PROP_SHARENFS || + prop == ZFS_PROP_SHARESMB) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' cannot be set in " "a non-global zone"), propname); @@ -684,17 +692,24 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, * property. Now we want to make sure that the * property value is valid if it is sharenfs. */ - if (prop == ZFS_PROP_SHARENFS && + if ((prop == ZFS_PROP_SHARENFS || + prop == ZFS_PROP_SHARESMB) && strcmp(strval, "on") != 0 && strcmp(strval, "off") != 0) { + zfs_share_proto_t proto; + + if (prop == ZFS_PROP_SHARESMB) + proto = PROTO_SMB; + else + proto = PROTO_NFS; /* - * Must be an NFS option string so - * init the libshare in order to - * enable the parser and then parse - * the options. We use the control API - * since we don't care about the - * current configuration and don't + * Must be an valid sharing protocol + * option string so init the libshare + * in order to enable the parser and + * then parse the options. We use the + * control API since we don't care about + * the current configuration and don't * want the overhead of loading it * until we actually do something. */ @@ -714,7 +729,7 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, goto error; } - if (zfs_parse_options(strval, "nfs") != SA_OK) { + if (zfs_parse_options(strval, proto) != SA_OK) { /* * There was an error in parsing so * deal with it by issuing an error @@ -733,6 +748,12 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, zfs_uninit_libshare(hdl); } + break; + case ZFS_PROP_UTF8ONLY: + chosen_utf = (int)intval; + break; + case ZFS_PROP_NORMALIZE: + chosen_normal = (int)intval; break; } @@ -785,6 +806,27 @@ zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, } } + /* + * If normalization was chosen, but no UTF8 choice was made, + * enforce rejection of non-UTF8 names. + * + * If normalization was chosen, but rejecting non-UTF8 names + * was explicitly not chosen, it is an error. + */ + if (chosen_normal > ZFS_NORMALIZE_NONE && chosen_utf < 0) { + if (nvlist_add_uint64(ret, + zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) { + (void) no_memory(hdl); + goto error; + } + } else if (chosen_normal > ZFS_NORMALIZE_NONE && chosen_utf == 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' must be set 'on' if normalization chosen"), + zfs_prop_to_name(ZFS_PROP_UTF8ONLY)); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } + /* * If this is an existing volume, and someone is setting the volsize, * make sure that it matches the reservation, or add it if necessary. @@ -941,7 +983,7 @@ zfs_perms_add_who_nvlist(nvlist_t *who_nvp, uint64_t whoid, void *whostr, * whostr may be null for everyone or create perms. * who_type: is the type of entry in whostr. Typically this will be * ZFS_DELEG_WHO_UNKNOWN. - * perms: comman separated list of permissions. May be null if user + * perms: common separated list of permissions. May be null if user * is requested to remove permissions by who. * inherit: Specifies the inheritance of the permissions. Will be either * ZFS_DELEG_PERM_LOCAL and/or ZFS_DELEG_PERM_DESCENDENT. @@ -1833,6 +1875,11 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, mntopt_on = MNTOPT_XATTR; mntopt_off = MNTOPT_NOXATTR; break; + + case ZFS_PROP_NBMAND: + mntopt_on = MNTOPT_NBMAND; + mntopt_off = MNTOPT_NONBMAND; + break; } /* @@ -1871,6 +1918,7 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, case ZFS_PROP_READONLY: case ZFS_PROP_SETUID: case ZFS_PROP_XATTR: + case ZFS_PROP_NBMAND: *val = getprop_uint64(zhp, prop, source); if (hasmntopt(&mnt, mntopt_on) && !*val) { @@ -4177,7 +4225,7 @@ zfs_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred) int zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, - void *export, void *sharetab, int sharemax, boolean_t share_on) + void *export, void *sharetab, int sharemax, zfs_share_op_t operation) { zfs_cmd_t zc = { 0 }; int error; @@ -4186,7 +4234,7 @@ zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path, (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value)); zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab; zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export; - zc.zc_share.z_sharetype = share_on; + zc.zc_share.z_sharetype = operation; zc.zc_share.z_sharemax = sharemax; error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc); diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h index 19a7590cee30..cfc03791dd20 100644 --- a/usr/src/lib/libzfs/common/libzfs_impl.h +++ b/usr/src/lib/libzfs/common/libzfs_impl.h @@ -90,6 +90,23 @@ struct zpool_handle { diskaddr_t zpool_start_block; }; +typedef enum { + PROTO_NFS = 0, + PROTO_SMB = 1, + PROTO_END = 2 +} zfs_share_proto_t; + +/* + * The following can be used as a bitmask and any new values + * added must preserve that capability. + */ +typedef enum { + SHARED_NOT_SHARED = 0x0, + SHARED_ISCSI = 0x1, + SHARED_NFS = 0x2, + SHARED_SMB = 0x4 +} zfs_share_type_t; + int zfs_error(libzfs_handle_t *, int, const char *); int zfs_error_fmt(libzfs_handle_t *, int, const char *, ...); void zfs_error_aux(libzfs_handle_t *, const char *, ...); @@ -127,7 +144,7 @@ void changelist_rename(prop_changelist_t *, const char *, const char *); void changelist_remove(zfs_handle_t *, prop_changelist_t *); void changelist_free(prop_changelist_t *); prop_changelist_t *changelist_gather(zfs_handle_t *, zfs_prop_t, int); -int changelist_unshare(prop_changelist_t *); +int changelist_unshare(prop_changelist_t *, zfs_share_proto_t *); int changelist_haszonedchild(prop_changelist_t *); void remove_mountpoint(zfs_handle_t *); @@ -148,8 +165,10 @@ void namespace_clear(libzfs_handle_t *); extern int zfs_init_libshare(libzfs_handle_t *, int); extern void zfs_uninit_libshare(libzfs_handle_t *); -extern int zfs_parse_options(char *, char *); +extern int zfs_parse_options(char *, zfs_share_proto_t); +extern int zfs_unshare_proto(zfs_handle_t *zhp, + const char *, zfs_share_proto_t *); #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libzfs/common/libzfs_mount.c b/usr/src/lib/libzfs/common/libzfs_mount.c index d7dd227bc8ce..6810f7efdc2c 100644 --- a/usr/src/lib/libzfs/common/libzfs_mount.c +++ b/usr/src/lib/libzfs/common/libzfs_mount.c @@ -45,11 +45,17 @@ * zfs_unshare() * * zfs_is_shared_nfs() - * zfs_share_nfs() - * zfs_unshare_nfs() - * zfs_unshareall_nfs() + * zfs_is_shared_smb() * zfs_is_shared_iscsi() + * zfs_share_proto() + * zfs_shareall(); * zfs_share_iscsi() + * zfs_unshare_nfs() + * zfs_unshare_smb() + * zfs_unshareall_nfs() + * zfs_unshareall_smb() + * zfs_unshareall() + * zfs_unshareall_bypath() * zfs_unshare_iscsi() * * The following functions are available for pool consumers, and will @@ -82,11 +88,46 @@ #include #define MAXISALEN 257 /* based on sysinfo(2) man page */ +static int zfs_share_proto(zfs_handle_t *, zfs_share_proto_t *); +zfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **, + zfs_share_proto_t); + static int (*iscsitgt_zfs_share)(const char *); static int (*iscsitgt_zfs_unshare)(const char *); static int (*iscsitgt_zfs_is_shared)(const char *); static int (*iscsitgt_svc_online)(); +/* + * The share protocols table must be in the same order as the zfs_share_prot_t + * enum in libzfs_impl.h + */ +typedef struct { + zfs_prop_t p_prop; + char *p_name; + int p_share_err; + int p_unshare_err; +} proto_table_t; + +proto_table_t proto_table[PROTO_END] = { + {ZFS_PROP_SHARENFS, "nfs", EZFS_SHARENFSFAILED, EZFS_UNSHARENFSFAILED}, + {ZFS_PROP_SHARESMB, "smb", EZFS_SHARESMBFAILED, EZFS_UNSHARESMBFAILED}, +}; + +zfs_share_proto_t nfs_only[] = { + PROTO_NFS, + PROTO_END +}; + +zfs_share_proto_t smb_only[] = { + PROTO_SMB, + PROTO_END +}; +zfs_share_proto_t share_all_proto[] = { + PROTO_NFS, + PROTO_SMB, + PROTO_END +}; + #pragma init(zfs_iscsi_init) static void zfs_iscsi_init(void) @@ -111,29 +152,54 @@ zfs_iscsi_init(void) } /* - * Search the sharetab for the given mountpoint, returning true if it is found. + * Search the sharetab for the given mountpoint and protocol, returning + * a zfs_share_type_t value. */ -static boolean_t -is_shared(libzfs_handle_t *hdl, const char *mountpoint) +static zfs_share_type_t +is_shared(libzfs_handle_t *hdl, const char *mountpoint, zfs_share_proto_t proto) { char buf[MAXPATHLEN], *tab; + char *ptr; if (hdl->libzfs_sharetab == NULL) - return (0); + return (SHARED_NOT_SHARED); (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET); while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) { /* the mountpoint is the first entry on each line */ - if ((tab = strchr(buf, '\t')) != NULL) { + if ((tab = strchr(buf, '\t')) == NULL) + continue; + + *tab = '\0'; + if (strcmp(buf, mountpoint) == 0) { + /* + * the protocol field is the third field + * skip over second field + */ + ptr = ++tab; + if ((tab = strchr(ptr, '\t')) == NULL) + continue; + ptr = ++tab; + if ((tab = strchr(ptr, '\t')) == NULL) + continue; *tab = '\0'; - if (strcmp(buf, mountpoint) == 0) - return (B_TRUE); + if (strcmp(ptr, + proto_table[proto].p_name) == 0) { + switch (proto) { + case PROTO_NFS: + return (SHARED_NFS); + case PROTO_SMB: + return (SHARED_SMB); + default: + return (0); + } + } } } - return (B_FALSE); + return (SHARED_NOT_SHARED); } /* @@ -349,12 +415,12 @@ zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags) /* * Unshare and unmount the filesystem */ - if (zfs_unshare_nfs(zhp, mntpt) != 0) + if (zfs_unshare_proto(zhp, mntpt, share_all_proto) != 0) return (-1); if (unmount_one(zhp->zfs_hdl, mntpt, flags) != 0) { free(mntpt); - (void) zfs_share_nfs(zhp); + (void) zfs_shareall(zhp); return (-1); } free(mntpt); @@ -387,10 +453,17 @@ zfs_unmountall(zfs_handle_t *zhp, int flags) boolean_t zfs_is_shared(zfs_handle_t *zhp) { + zfs_share_type_t rc = 0; + zfs_share_proto_t *curr_proto; + if (ZFS_IS_VOLUME(zhp)) return (zfs_is_shared_iscsi(zhp)); - return (zfs_is_shared_nfs(zhp, NULL)); + for (curr_proto = share_all_proto; *curr_proto != PROTO_END; + curr_proto++) + rc |= zfs_is_shared_proto(zhp, NULL, *curr_proto); + + return (rc ? B_TRUE : B_FALSE); } int @@ -399,7 +472,7 @@ zfs_share(zfs_handle_t *zhp) if (ZFS_IS_VOLUME(zhp)) return (zfs_share_iscsi(zhp)); - return (zfs_share_nfs(zhp)); + return (zfs_share_proto(zhp, share_all_proto)); } int @@ -408,32 +481,47 @@ zfs_unshare(zfs_handle_t *zhp) if (ZFS_IS_VOLUME(zhp)) return (zfs_unshare_iscsi(zhp)); - return (zfs_unshare_nfs(zhp, NULL)); + return (zfs_unshareall(zhp)); } /* * Check to see if the filesystem is currently shared. */ -boolean_t -zfs_is_shared_nfs(zfs_handle_t *zhp, char **where) +zfs_share_type_t +zfs_is_shared_proto(zfs_handle_t *zhp, char **where, zfs_share_proto_t proto) { char *mountpoint; + zfs_share_type_t rc; if (!zfs_is_mounted(zhp, &mountpoint)) - return (B_FALSE); + return (SHARED_NOT_SHARED); - if (is_shared(zhp->zfs_hdl, mountpoint)) { + if (rc = is_shared(zhp->zfs_hdl, mountpoint, proto)) { if (where != NULL) *where = mountpoint; else free(mountpoint); - return (B_TRUE); + return (rc); } else { free(mountpoint); - return (B_FALSE); + return (SHARED_NOT_SHARED); } } +boolean_t +zfs_is_shared_nfs(zfs_handle_t *zhp, char **where) +{ + return (zfs_is_shared_proto(zhp, where, + PROTO_NFS) != SHARED_NOT_SHARED); +} + +boolean_t +zfs_is_shared_smb(zfs_handle_t *zhp, char **where) +{ + return (zfs_is_shared_proto(zhp, where, + PROTO_SMB) != SHARED_NOT_SHARED); +} + /* * Make sure things will work if libshare isn't installed by using * wrapper functions that check to see that the pointers to functions @@ -552,12 +640,13 @@ zfs_uninit_libshare(libzfs_handle_t *zhandle) */ int -zfs_parse_options(char *options, char *proto) +zfs_parse_options(char *options, zfs_share_proto_t proto) { int ret; if (_sa_parse_legacy_options != NULL) - ret = _sa_parse_legacy_options(NULL, options, proto); + ret = _sa_parse_legacy_options(NULL, options, + proto_table[proto].p_name); else ret = SA_CONFIG_ERR; return (ret); @@ -609,74 +698,102 @@ zfs_sa_disable_share(sa_share_t share, char *proto) } /* - * Share the given filesystem according to the options in 'sharenfs'. We rely + * Share the given filesystem according to the options in the specified + * protocol specific properties (sharenfs, sharesmb). We rely * on "libshare" to the dirty work for us. */ -int -zfs_share_nfs(zfs_handle_t *zhp) +static int +zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto) { char mountpoint[ZFS_MAXPROPLEN]; char shareopts[ZFS_MAXPROPLEN]; libzfs_handle_t *hdl = zhp->zfs_hdl; sa_share_t share; + zfs_share_proto_t *curr_proto; int ret; if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL)) return (0); - /* - * Return success if there are no share options. - */ - if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts), - NULL, NULL, 0, B_FALSE) != 0 || - strcmp(shareopts, "off") == 0) - return (0); - - /* - * If the 'zoned' property is set, then zfs_is_mountable() will have - * already bailed out if we are in the global zone. But local - * zones cannot be NFS servers, so we ignore it for local zones as well. - */ - if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) - return (0); - if ((ret = zfs_init_libshare(hdl, SA_INIT_SHARE_API)) != SA_OK) { (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, dgettext(TEXT_DOMAIN, "cannot share '%s': %s"), zfs_get_name(zhp), _sa_errorstr(ret)); return (-1); } - share = zfs_sa_find_share(hdl->libzfs_sharehdl, mountpoint); - if (share != NULL) { - int err; - err = zfs_sa_enable_share(share, "nfs"); - if (err != SA_OK) { - (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, + + for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) { + /* + * Return success if there are no share options. + */ + if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop, + shareopts, sizeof (shareopts), NULL, NULL, + 0, B_FALSE) != 0 || strcmp(shareopts, "off") == 0) + continue; + + /* + * If the 'zoned' property is set, then zfs_is_mountable() + * will have already bailed out if we are in the global zone. + * But local zones cannot be NFS servers, so we ignore it for + * local zones as well. + */ + if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) + continue; + + share = zfs_sa_find_share(hdl->libzfs_sharehdl, mountpoint); + if (share != NULL) { + int err; + err = zfs_sa_enable_share(share, + proto_table[*curr_proto].p_name); + if (err != SA_OK) { + (void) zfs_error_fmt(hdl, + proto_table[*curr_proto].p_share_err, + dgettext(TEXT_DOMAIN, "cannot share '%s'"), + zfs_get_name(zhp)); + return (-1); + } + } else { + (void) zfs_error_fmt(hdl, + proto_table[*curr_proto].p_share_err, dgettext(TEXT_DOMAIN, "cannot share '%s'"), zfs_get_name(zhp)); return (-1); } - } else { - (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED, - dgettext(TEXT_DOMAIN, "cannot share '%s'"), - zfs_get_name(zhp)); - return (-1); - } + } return (0); } + +int +zfs_share_nfs(zfs_handle_t *zhp) +{ + return (zfs_share_proto(zhp, nfs_only)); +} + +int +zfs_share_smb(zfs_handle_t *zhp) +{ + return (zfs_share_proto(zhp, smb_only)); +} + +int +zfs_shareall(zfs_handle_t *zhp) +{ + return (zfs_share_proto(zhp, share_all_proto)); +} + /* * Unshare a filesystem by mountpoint. */ static int -unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint) +unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint, + zfs_share_proto_t proto) { sa_share_t share; int err; char *mntpt; - /* * Mountpoint could get trashed if libshare calls getmntany * which id does during API initialization, so strdup the @@ -696,7 +813,7 @@ unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint) free(mntpt); /* don't need the copy anymore */ if (share != NULL) { - err = zfs_sa_disable_share(share, "nfs"); + err = zfs_sa_disable_share(share, proto_table[proto].p_name); if (err != SA_OK) { return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED, dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"), @@ -714,7 +831,8 @@ unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint) * Unshare the given filesystem. */ int -zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint) +zfs_unshare_proto(zfs_handle_t *zhp, const char *mountpoint, + zfs_share_proto_t *proto) { struct mnttab search = { 0 }, entry; char *mntpt = NULL; @@ -724,19 +842,25 @@ zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint) search.mnt_fstype = MNTTYPE_ZFS; rewind(zhp->zfs_hdl->libzfs_mnttab); if (mountpoint != NULL) - mountpoint = mntpt = zfs_strdup(zhp->zfs_hdl, mountpoint); + mntpt = zfs_strdup(zhp->zfs_hdl, mountpoint); if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) && getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry, &search) == 0)) { + zfs_share_proto_t *curr_proto; if (mountpoint == NULL) - mountpoint = entry.mnt_mountp; + mntpt = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp); - if (is_shared(zhp->zfs_hdl, mountpoint) && - unshare_one(zhp->zfs_hdl, zhp->zfs_name, mountpoint) != 0) { - if (mntpt != NULL) - free(mntpt); - return (-1); + for (curr_proto = proto; *curr_proto != PROTO_END; + curr_proto++) { + + if (is_shared(zhp->zfs_hdl, mntpt, *curr_proto) && + unshare_one(zhp->zfs_hdl, zhp->zfs_name, + mntpt, *curr_proto) != 0) { + if (mntpt != NULL) + free(mntpt); + return (-1); + } } } if (mntpt != NULL) @@ -745,11 +869,23 @@ zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint) return (0); } +int +zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint) +{ + return (zfs_unshare_proto(zhp, mountpoint, nfs_only)); +} + +int +zfs_unshare_smb(zfs_handle_t *zhp, const char *mountpoint) +{ + return (zfs_unshare_proto(zhp, mountpoint, smb_only)); +} + /* - * Same as zfs_unmountall(), but for NFS unshares. + * Same as zfs_unmountall(), but for NFS and SMB unshares. */ int -zfs_unshareall_nfs(zfs_handle_t *zhp) +zfs_unshareall_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto) { prop_changelist_t *clp; int ret; @@ -758,12 +894,36 @@ zfs_unshareall_nfs(zfs_handle_t *zhp) if (clp == NULL) return (-1); - ret = changelist_unshare(clp); + ret = changelist_unshare(clp, proto); changelist_free(clp); return (ret); } +int +zfs_unshareall_nfs(zfs_handle_t *zhp) +{ + return (zfs_unshareall_proto(zhp, nfs_only)); +} + +int +zfs_unshareall_smb(zfs_handle_t *zhp) +{ + return (zfs_unshareall_proto(zhp, smb_only)); +} + +int +zfs_unshareall(zfs_handle_t *zhp) +{ + return (zfs_unshareall_proto(zhp, share_all_proto)); +} + +int +zfs_unshareall_bypath(zfs_handle_t *zhp, const char *mountpoint) +{ + return (zfs_unshare_proto(zhp, mountpoint, share_all_proto)); +} + /* * Remove the mountpoint associated with the current dataset, if necessary. * We only remove the underlying directory if: @@ -805,7 +965,7 @@ zfs_is_shared_iscsi(zfs_handle_t *zhp) * If iscsi deamon isn't running then we aren't shared */ if (iscsitgt_svc_online && iscsitgt_svc_online() == 1) - return (0); + return (B_FALSE); else return (iscsitgt_zfs_is_shared != NULL && iscsitgt_zfs_is_shared(zhp->zfs_name) != 0); @@ -853,7 +1013,7 @@ zfs_unshare_iscsi(zfs_handle_t *zhp) /* * Return if the volume is not shared */ - if (!zfs_is_shared_iscsi(zhp)) + if (zfs_is_shared_iscsi(zhp) != SHARED_ISCSI) return (0); /* @@ -1143,9 +1303,14 @@ zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force) * Walk through and first unshare everything. */ for (i = 0; i < used; i++) { - if (is_shared(hdl, mountpoints[i]) && - unshare_one(hdl, mountpoints[i], mountpoints[i]) != 0) - goto out; + zfs_share_proto_t *curr_proto; + for (curr_proto = share_all_proto; *curr_proto != PROTO_END; + curr_proto++) { + if (is_shared(hdl, mountpoints[i], *curr_proto) && + unshare_one(hdl, mountpoints[i], + mountpoints[i], *curr_proto) != 0) + goto out; + } } /* diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c index d446b3209168..8a1ad6d656b2 100644 --- a/usr/src/lib/libzfs/common/libzfs_util.c +++ b/usr/src/lib/libzfs/common/libzfs_util.c @@ -136,6 +136,10 @@ libzfs_error_description(libzfs_handle_t *hdl) return (dgettext(TEXT_DOMAIN, "unshare(1M) failed")); case EZFS_SHARENFSFAILED: return (dgettext(TEXT_DOMAIN, "share(1M) failed")); + case EZFS_UNSHARESMBFAILED: + return (dgettext(TEXT_DOMAIN, "smb remove share failed")); + case EZFS_SHARESMBFAILED: + return (dgettext(TEXT_DOMAIN, "smb add share failed")); case EZFS_ISCSISVCUNAVAIL: return (dgettext(TEXT_DOMAIN, "iscsitgt service need to be enabled by " diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers index 1ca0da3453fe..d7d68f8708c4 100644 --- a/usr/src/lib/libzfs/common/mapfile-vers +++ b/usr/src/lib/libzfs/common/mapfile-vers @@ -53,6 +53,7 @@ SUNWprivate_1.1 { zfs_is_shared; zfs_is_shared_iscsi; zfs_is_shared_nfs; + zfs_is_shared_smb; zfs_iter_children; zfs_iter_dependents; zfs_iter_filesystems; @@ -92,7 +93,9 @@ SUNWprivate_1.1 { zfs_rollback; zfs_send; zfs_share; + zfs_shareall; zfs_share_nfs; + zfs_share_smb; zfs_share_iscsi; zfs_snapshot; zfs_type_to_name; @@ -101,7 +104,11 @@ SUNWprivate_1.1 { zfs_unshare; zfs_unshare_iscsi; zfs_unshare_nfs; + zfs_unshare_smb; + zfs_unshareall; + zfs_unshareall_bypath; zfs_unshareall_nfs; + zfs_unshareall_smb; zpool_add; zpool_clear; zpool_close; diff --git a/usr/src/lib/libzpool/common/kernel.c b/usr/src/lib/libzpool/common/kernel.c index 12219b72a99f..e8ce11b2a5ca 100644 --- a/usr/src/lib/libzpool/common/kernel.c +++ b/usr/src/lib/libzpool/common/kernel.c @@ -389,9 +389,10 @@ vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3) return (0); } +/*ARGSUSED*/ int vn_openat(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, - int x3, vnode_t *startvp) + int x3, vnode_t *startvp, int fd) { char *realpath = umem_alloc(strlen(path) + 2, UMEM_NOFAIL); int ret; @@ -399,6 +400,7 @@ vn_openat(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, ASSERT(startvp == rootdir); (void) sprintf(realpath, "/%s", path); + /* fd ignored for now, need if want to simulate nbmand support */ ret = vn_open(realpath, x1, flags, mode, vpp, x2, x3); umem_free(realpath, strlen(path) + 2); @@ -622,7 +624,8 @@ kobj_open_file(char *name) vnode_t *vp; /* set vp as the _fd field of the file */ - if (vn_openat(name, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0, rootdir) != 0) + if (vn_openat(name, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0, rootdir, + -1) != 0) return ((void *)-1UL); file = umem_zalloc(sizeof (struct _buf), UMEM_NOFAIL); @@ -814,6 +817,14 @@ z_compress_level(void *dst, size_t *dstlen, const void *src, size_t srclen, return (ret); } +/*ARGSUSED*/ +size_t u8_textprep_str(char *i, size_t *il, char *o, size_t *ol, int nf, + size_t vers, int *err) +{ + *err = EINVAL; + return ((size_t)-1); +} + uid_t crgetuid(cred_t *cr) { diff --git a/usr/src/lib/libzpool/common/sys/zfs_context.h b/usr/src/lib/libzpool/common/sys/zfs_context.h index 628a9c25a463..7825bfcb080a 100644 --- a/usr/src/lib/libzpool/common/sys/zfs_context.h +++ b/usr/src/lib/libzpool/common/sys/zfs_context.h @@ -322,6 +322,9 @@ extern void taskq_destroy(taskq_t *); extern void taskq_wait(taskq_t *); extern int taskq_member(taskq_t *, void *); +#define XVA_MAPSIZE 3 +#define XVA_MAGIC 0x78766174 + /* * vnodes */ @@ -331,41 +334,79 @@ typedef struct vnode { char *v_path; } vnode_t; + +typedef struct xoptattr { + timestruc_t xoa_createtime; /* Create time of file */ + uint8_t xoa_archive; + uint8_t xoa_system; + uint8_t xoa_readonly; + uint8_t xoa_hidden; + uint8_t xoa_nounlink; + uint8_t xoa_immutable; + uint8_t xoa_appendonly; + uint8_t xoa_nodump; + uint8_t xoa_settable; + uint8_t xoa_opaque; + uint8_t xoa_av_quarantined; + uint8_t xoa_av_modified; +} xoptattr_t; + typedef struct vattr { uint_t va_mask; /* bit-mask of attributes */ u_offset_t va_size; /* file size in bytes */ } vattr_t; -#define AT_TYPE 0x0001 -#define AT_MODE 0x0002 -#define AT_UID 0x0004 -#define AT_GID 0x0008 -#define AT_FSID 0x0010 -#define AT_NODEID 0x0020 -#define AT_NLINK 0x0040 -#define AT_SIZE 0x0080 -#define AT_ATIME 0x0100 -#define AT_MTIME 0x0200 -#define AT_CTIME 0x0400 -#define AT_RDEV 0x0800 -#define AT_BLKSIZE 0x1000 -#define AT_NBLOCKS 0x2000 -#define AT_SEQ 0x8000 + +typedef struct xvattr { + vattr_t xva_vattr; /* Embedded vattr structure */ + uint32_t xva_magic; /* Magic Number */ + uint32_t xva_mapsize; /* Size of attr bitmap (32-bit words) */ + uint32_t *xva_rtnattrmapp; /* Ptr to xva_rtnattrmap[] */ + uint32_t xva_reqattrmap[XVA_MAPSIZE]; /* Requested attrs */ + uint32_t xva_rtnattrmap[XVA_MAPSIZE]; /* Returned attrs */ + xoptattr_t xva_xoptattrs; /* Optional attributes */ +} xvattr_t; + +typedef struct vsecattr { + uint_t vsa_mask; /* See below */ + int vsa_aclcnt; /* ACL entry count */ + void *vsa_aclentp; /* pointer to ACL entries */ + int vsa_dfaclcnt; /* default ACL entry count */ + void *vsa_dfaclentp; /* pointer to default ACL entries */ + size_t vsa_aclentsz; /* ACE size in bytes of vsa_aclentp */ +} vsecattr_t; + +#define AT_TYPE 0x00001 +#define AT_MODE 0x00002 +#define AT_UID 0x00004 +#define AT_GID 0x00008 +#define AT_FSID 0x00010 +#define AT_NODEID 0x00020 +#define AT_NLINK 0x00040 +#define AT_SIZE 0x00080 +#define AT_ATIME 0x00100 +#define AT_MTIME 0x00200 +#define AT_CTIME 0x00400 +#define AT_RDEV 0x00800 +#define AT_BLKSIZE 0x01000 +#define AT_NBLOCKS 0x02000 +#define AT_SEQ 0x08000 +#define AT_XVATTR 0x10000 #define CRCREAT 0 -#define VOP_CLOSE(vp, f, c, o, cr) 0 -#define VOP_PUTPAGE(vp, of, sz, fl, cr) 0 -#define VOP_GETATTR(vp, vap, fl, cr) ((vap)->va_size = (vp)->v_size, 0) +#define VOP_CLOSE(vp, f, c, o, cr, ct) 0 +#define VOP_PUTPAGE(vp, of, sz, fl, cr, ct) 0 +#define VOP_GETATTR(vp, vap, fl, cr, ct) ((vap)->va_size = (vp)->v_size, 0) -#define VOP_FSYNC(vp, f, cr) fsync((vp)->v_fd) +#define VOP_FSYNC(vp, f, cr, ct) fsync((vp)->v_fd) #define VN_RELE(vp) vn_close(vp) extern int vn_open(char *path, int x1, int oflags, int mode, vnode_t **vpp, int x2, int x3); extern int vn_openat(char *path, int x1, int oflags, int mode, vnode_t **vpp, - int x2, int x3, vnode_t *vp); + int x2, int x3, vnode_t *vp, int fd); extern int vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len, offset_t offset, int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp); extern void vn_close(vnode_t *vp); @@ -453,6 +494,21 @@ struct bootstat { uint64_t st_size; }; +typedef struct ace_object { + uid_t a_who; + uint32_t a_access_mask; + uint16_t a_flags; + uint16_t a_type; + uint8_t a_obj_type[16]; + uint8_t a_inherit_obj_type[16]; +} ace_object_t; + + +#define ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 +#define ACE_ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 +#define ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 +#define ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 + extern struct _buf *kobj_open_file(char *name); extern int kobj_read_file(struct _buf *file, char *buf, unsigned size, unsigned off); @@ -464,6 +520,25 @@ extern int zfs_secpolicy_rename_perms(const char *from, const char *to, extern int zfs_secpolicy_destroy_perms(const char *name, cred_t *cr); extern zoneid_t getzoneid(void); +/* + * UTF-8 text preparation functions and their macros. + * (sunddi.h) + */ +#define U8_STRCMP_CS 0x00000001 +#define U8_STRCMP_CI_UPPER 0x00000002 +#define U8_STRCMP_CI_LOWER 0x00000004 + +#define U8_TEXTPREP_TOUPPER U8_STRCMP_CI_UPPER +#define U8_TEXTPREP_TOLOWER U8_STRCMP_CI_LOWER +#define U8_TEXTPREP_IGNORE_NULL 0x00010000 + +#define U8_UNICODE_320 (0) +#define U8_UNICODE_500 (1) +#define U8_UNICODE_LATEST U8_UNICODE_500 + +extern size_t u8_textprep_str(char *, size_t *, char *, size_t *, int, size_t, + int *); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/pam_modules/Makefile b/usr/src/lib/pam_modules/Makefile index c5692283bea5..0dce8eb7b10e 100644 --- a/usr/src/lib/pam_modules/Makefile +++ b/usr/src/lib/pam_modules/Makefile @@ -49,7 +49,8 @@ SUBDIRS = \ unix_auth \ unix_account \ unix_cred \ - unix_session + unix_session \ + smb $(CLOSED_BUILD)SUBDIRS += \ $(CLOSED)/lib/pam_modules/smartcard diff --git a/usr/src/lib/pam_modules/smb/Makefile b/usr/src/lib/pam_modules/smb/Makefile new file mode 100644 index 000000000000..ea50c3ae1f27 --- /dev/null +++ b/usr/src/lib/pam_modules/smb/Makefile @@ -0,0 +1,56 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../../Makefile.lib + +TEXT_DOMAIN= SUNW_OST_SYSOSPAM +POFILE= smb_passwd.po +MSGFILES= smb_passwd.c + +SUBDIRS= $(MACH) +#$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +_msg: $(MSGDOMAINPOFILE) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include $(SRC)/Makefile.msg.targ +include ../../Makefile.targ diff --git a/usr/src/lib/pam_modules/smb/Makefile.com b/usr/src/lib/pam_modules/smb/Makefile.com new file mode 100644 index 000000000000..8afe5b8232a9 --- /dev/null +++ b/usr/src/lib/pam_modules/smb/Makefile.com @@ -0,0 +1,42 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +LIBRARY= pam_smb_passwd.a +VERS= .1 +OBJECTS= smb_passwd.o + +include ../../Makefile.pam_modules + +LDLIBS += -lpam -lc +LDLIBS += -L$(ROOT)/usr/lib/smbsrv -lsmb + +all: $(LIBS) + +lint: lintcheck + +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/pam_modules/smb/amd64/Makefile b/usr/src/lib/pam_modules/smb/amd64/Makefile new file mode 100644 index 000000000000..8b0fed8b9f4e --- /dev/null +++ b/usr/src/lib/pam_modules/smb/amd64/Makefile @@ -0,0 +1,35 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +DYNFLAGS += -R/usr/lib/smbsrv/$(MACH64) +DYNFLAGS += $(ROOT)/usr/lib/$(MACH64)/passwdutil.so.1 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/pam_modules/smb/i386/Makefile b/usr/src/lib/pam_modules/smb/i386/Makefile new file mode 100644 index 000000000000..5be2ca90dfeb --- /dev/null +++ b/usr/src/lib/pam_modules/smb/i386/Makefile @@ -0,0 +1,34 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +DYNFLAGS += -R/usr/lib/smbsrv +DYNFLAGS += $(ROOT)/usr/lib/passwdutil.so.1 + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/pam_modules/smb/mapfile-vers b/usr/src/lib/pam_modules/smb/mapfile-vers new file mode 100644 index 000000000000..2a7c980a33dd --- /dev/null +++ b/usr/src/lib/pam_modules/smb/mapfile-vers @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +SUNW_1.1 { + global: + pam_sm_chauthtok; + local: + *; +}; diff --git a/usr/src/lib/pam_modules/smb/smb_passwd.c b/usr/src/lib/pam_modules/smb/smb_passwd.c new file mode 100644 index 000000000000..9167d093a623 --- /dev/null +++ b/usr/src/lib/pam_modules/smb/smb_passwd.c @@ -0,0 +1,202 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +/*PRINTFLIKE3*/ +static void +error(boolean_t nowarn, pam_handle_t *pamh, char *fmt, ...) +{ + va_list ap; + char message[PAM_MAX_MSG_SIZE]; + + if (nowarn) + return; + + va_start(ap, fmt); + (void) vsnprintf(message, sizeof (message), fmt, ap); + (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, &message, + NULL); + va_end(ap); +} + +/*PRINTFLIKE3*/ +static void +info(boolean_t nowarn, pam_handle_t *pamh, char *fmt, ...) +{ + va_list ap; + char message[PAM_MAX_MSG_SIZE]; + + if (nowarn) + return; + + va_start(ap, fmt); + (void) vsnprintf(message, sizeof (message), fmt, ap); + (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, &message, + NULL); + va_end(ap); +} + +int +pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + boolean_t debug = B_FALSE; + boolean_t nowarn = B_FALSE; + pwu_repository_t files_rep; + char *user, *local_user; + char *newpw; + char *service; + int privileged; + int res; + int i; + + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) + debug = B_TRUE; + else if (strcmp(argv[i], "nowarn") == 0) + nowarn = B_TRUE; + } + + if ((flags & PAM_PRELIM_CHECK) != 0) + return (PAM_IGNORE); + + if ((flags & PAM_UPDATE_AUTHTOK) == 0) + return (PAM_SYSTEM_ERR); + + if ((flags & PAM_SILENT) != 0) + nowarn = B_TRUE; + + if (debug) + __pam_log(LOG_AUTH | LOG_DEBUG, + "pam_smb_passwd: storing authtok"); + + (void) pam_get_item(pamh, PAM_SERVICE, (void **)&service); + (void) pam_get_item(pamh, PAM_USER, (void **)&user); + + if (user == NULL || *user == '\0') { + __pam_log(LOG_AUTH | LOG_ERR, + "pam_smb_passwd: username is empty"); + return (PAM_USER_UNKNOWN); + } + + (void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&newpw); + if (newpw == NULL) { + /* + * A module on the stack has removed PAM_AUTHTOK. We fail + */ + return (PAM_AUTHTOK_ERR); + } + + /* Check to see if this is a local user */ + files_rep.type = "files"; + files_rep.scope = NULL; + files_rep.scope_len = 0; + res = __user_to_authenticate(user, &files_rep, &local_user, + &privileged); + if (res != PWU_SUCCESS) { + switch (res) { + case PWU_NOT_FOUND: + /* if not a local user, ignore */ + if (debug) { + __pam_log(LOG_AUTH | LOG_DEBUG, + "pam_smb_passwd: %s is not local", user); + } + return (PAM_IGNORE); + case PWU_DENIED: + return (PAM_PERM_DENIED); + } + return (PAM_SYSTEM_ERR); + } + + res = smb_pwd_setpasswd(user, newpw); + + /* + * now map the various return states to user messages + * and PAM return codes. + */ + switch (res) { + case SMB_PWE_SUCCESS: + info(nowarn, pamh, dgettext(TEXT_DOMAIN, + "%s: SMB password successfully changed for %s"), + service, user); + return (PAM_SUCCESS); + + case SMB_PWE_STAT_FAILED: + __pam_log(LOG_AUTH | LOG_ERR, + "%s: stat of SMB password file failed", service); + return (PAM_SYSTEM_ERR); + + case SMB_PWE_OPEN_FAILED: + case SMB_PWE_WRITE_FAILED: + case SMB_PWE_CLOSE_FAILED: + case SMB_PWE_UPDATE_FAILED: + error(nowarn, pamh, dgettext(TEXT_DOMAIN, + "%s: Unexpected failure. SMB password database unchanged."), + service); + return (PAM_SYSTEM_ERR); + + case SMB_PWE_BUSY: + error(nowarn, pamh, dgettext(TEXT_DOMAIN, + "%s: SMB password database busy. Try again later."), + service); + + return (PAM_AUTHTOK_LOCK_BUSY); + + case SMB_PWE_USER_UNKNOWN: + error(nowarn, pamh, dgettext(TEXT_DOMAIN, + "%s: %s does not exist."), service, user); + return (PAM_USER_UNKNOWN); + + case SMB_PWE_USER_DISABLE: + error(nowarn, pamh, dgettext(TEXT_DOMAIN, + "%s: %s is disable. SMB password database unchanged."), + service, user); + return (PAM_IGNORE); + + case SMB_PWE_DENIED: + return (PAM_PERM_DENIED); + + default: + res = PAM_SYSTEM_ERR; + break; + } + + return (res); +} diff --git a/usr/src/lib/pam_modules/smb/sparc/Makefile b/usr/src/lib/pam_modules/smb/sparc/Makefile new file mode 100644 index 000000000000..5be2ca90dfeb --- /dev/null +++ b/usr/src/lib/pam_modules/smb/sparc/Makefile @@ -0,0 +1,34 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +DYNFLAGS += -R/usr/lib/smbsrv +DYNFLAGS += $(ROOT)/usr/lib/passwdutil.so.1 + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/pam_modules/smb/sparcv9/Makefile b/usr/src/lib/pam_modules/smb/sparcv9/Makefile new file mode 100644 index 000000000000..8b0fed8b9f4e --- /dev/null +++ b/usr/src/lib/pam_modules/smb/sparcv9/Makefile @@ -0,0 +1,35 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +DYNFLAGS += -R/usr/lib/smbsrv/$(MACH64) +DYNFLAGS += $(ROOT)/usr/lib/$(MACH64)/passwdutil.so.1 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/smbsrv/Makefile b/usr/src/lib/smbsrv/Makefile new file mode 100644 index 000000000000..a57e621ffe0f --- /dev/null +++ b/usr/src/lib/smbsrv/Makefile @@ -0,0 +1,43 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.lib + +SUBDIRS = \ + libmlsvc \ + libmlrpc \ + libsmb \ + libsmbns \ + libsmbrdr + +libmlrpc: libsmb +libsmbrdr: libsmb +libsmbns: libsmb +libmlsvc: libsmb libmlrpc libsmbrdr libsmbns + +include ./Makefile.subdirs diff --git a/usr/src/lib/smbsrv/Makefile.lib b/usr/src/lib/smbsrv/Makefile.lib new file mode 100644 index 000000000000..abd5776bc905 --- /dev/null +++ b/usr/src/lib/smbsrv/Makefile.lib @@ -0,0 +1,50 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +# +# Common Makefile definitions for smbsrv. +# + +# We reset the Makefile.lib macros ROOTLIBDIR to refer to usr/lib/smbsrv +# We also install the userland library header files under /usr/include/smbsrv +ROOTSMBHDRDIR= $(ROOTHDRDIR)/smbsrv +ROOTSMBHDRS= $(HDRS:%=$(ROOTSMBHDRDIR)/%) + +ROOTLIBDIR = $(ROOT)/usr/lib/smbsrv + +SRCDIR= ../common +NDLDIR= $(ROOT)/usr/include/smbsrv/ndl +LIBS= $(DYNLIB) $(LINTLIB) +C99MODE = -xc99=%all +C99LMODE = -Xc99=%all +CPPFLAGS += -I$(SRCDIR) -I. +DYNFLAGS += -R/usr/lib/smbsrv +DYNFLAGS64 += -R/usr/lib/smbsrv/$(MACH64) +LDLIBS32 += -L$(ROOT)/usr/lib/smbsrv +LDLIBS64 += -L$(ROOT)/usr/lib/smbsrv/$(MACH64) +$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) + +CLEANFILES += $(OBJECTS:%_ndr.o=%_ndr.c) diff --git a/usr/src/lib/smbsrv/Makefile.smbsrv b/usr/src/lib/smbsrv/Makefile.smbsrv new file mode 100644 index 000000000000..16d6ddfed1af --- /dev/null +++ b/usr/src/lib/smbsrv/Makefile.smbsrv @@ -0,0 +1,65 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +# +# Toplevel Makefile included by each subdirectory. Responsible for the 'check' +# and 'install_h' targets, as well as descending into the architecture directory +# to actually build the library. +# + +include ../../Makefile.lib +include ../Makefile.lib + +SUBDIRS= $(MACH) +#$(BUILD64)SUBDIRS += $(MACH64) + +HDRDIR= common + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber lint: $(SUBDIRS) + +install: install_h $(SUBDIRS) + +check: $(CHECKHDRS) + +install_h: $(ROOTSMBHDRS) + +$(SUBDIRS): FRC + @cd $@; pwd; VERSION='$(VERSION)' $(MAKE) $(TARGET) + +FRC: + +$(ROOTSMBHDRDIR)/%: common/% + $(INS.file) + +include ../../Makefile.targ diff --git a/usr/src/lib/smbsrv/Makefile.subdirs b/usr/src/lib/smbsrv/Makefile.subdirs new file mode 100644 index 000000000000..928a4e21676b --- /dev/null +++ b/usr/src/lib/smbsrv/Makefile.subdirs @@ -0,0 +1,42 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +.KEEP_STATE: + +all := TARGET = all +check := TARGET = check +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +install_h := TARGET = install_h +lint := TARGET = lint + +all check clean clobber install install_h lint: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; VERSION='$(VERSION)' $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/smbsrv/Makefile.targ b/usr/src/lib/smbsrv/Makefile.targ new file mode 100644 index 000000000000..92a05ef2436f --- /dev/null +++ b/usr/src/lib/smbsrv/Makefile.targ @@ -0,0 +1,42 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +# +# Common targets for smbsrv Makefiles +# + +%_ndr.c: $(NDLDIR)/%.ndl + $(NDRGEN) -Y $(CC) $< + +pics/%.o: $(SRC)/common/smbsrv/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck diff --git a/usr/src/lib/smbsrv/libmlrpc/Makefile b/usr/src/lib/smbsrv/libmlrpc/Makefile new file mode 100644 index 000000000000..c5a61203cd58 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +HDRS= libmlrpc.h + +include ../Makefile.smbsrv diff --git a/usr/src/lib/smbsrv/libmlrpc/Makefile.com b/usr/src/lib/smbsrv/libmlrpc/Makefile.com new file mode 100644 index 000000000000..86c767254680 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/Makefile.com @@ -0,0 +1,57 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +LIBRARY = libmlrpc.a +VERS = .1 + +OBJS_COMMON = \ + mlndo.o \ + mlndr.o \ + mlrpc_client.o \ + mlrpc_encdec.o \ + mlrpc_heap.o \ + mlrpc_server.o \ + mlrpc_svc.o + +NDLLIST = rpcpdu + +OBJECTS= $(OBJS_COMMON) $(OBJS_SHARED) $(NDLLIST:%=%_ndr.o) + +include ../../../Makefile.lib +include ../../Makefile.lib + +INCS += -I$(SRC)/common/smbsrv + +LDLIBS += -lsmb -lc + +CPPFLAGS += $(INCS) -D_REENTRANT + +SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \ + $(OBJS_SHARED:%.o=$(SRC)/common/smbsrv/%.c) + +include ../../Makefile.targ +include ../../../Makefile.targ diff --git a/usr/src/lib/smbsrv/libmlrpc/amd64/Makefile b/usr/src/lib/smbsrv/libmlrpc/amd64/Makefile new file mode 100644 index 000000000000..a2f97019c815 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/amd64/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h b/usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h new file mode 100644 index 000000000000..9a20054d8fa0 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h @@ -0,0 +1,41 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBMLRPC_H +#define _LIBMLRPC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBMLRPC_H */ diff --git a/usr/src/lib/smbsrv/libmlrpc/common/llib-lmlrpc b/usr/src/lib/smbsrv/libmlrpc/common/llib-lmlrpc new file mode 100644 index 000000000000..1621e3c2f6f7 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/common/llib-lmlrpc @@ -0,0 +1,31 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/*LINTLIBRARY*/ +/*PROTOLIB1*/ + +#include diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers b/usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers new file mode 100644 index 000000000000..6858b4cea0ee --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers @@ -0,0 +1,57 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +SUNWprivate { + global: + mlndr_inner; + mlndr_params; + mlndr_topmost; + mlnds_destruct; + mlnds_initialize; + mlrpc_binding_pool_initialize; + mlrpc_c_bind; + mlrpc_c_call; + mlrpc_c_free_heap; + mlrpc_heap_avail; + mlrpc_heap_create; + mlrpc_heap_destroy; + mlrpc_heap_malloc; + mlrpc_heap_mkvcs; + mlrpc_heap_strsave; + mlrpc_heap_used; + mlrpc_register_service; + mlsvc_lookup_context; + mlsvc_rpc_process; + mlsvc_rpc_release; + ndt__char; + ndt_s_wchar; + ndt__uchar; + ndt__ulong; + ndt__ushort; + local: + *; +}; diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mlndo.c b/usr/src/lib/smbsrv/libmlrpc/common/mlndo.c new file mode 100644 index 000000000000..c469223c4d2c --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/common/mlndo.c @@ -0,0 +1,534 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * MLRPC server-side NDR stream (PDU) operations. Stream operations + * should return TRUE (non-zero) on success or FALSE (zero or a null + * pointer) on failure. When an operation returns FALSE, including + * mlndo_malloc() returning NULL, it should set the mlnds->error to + * indicate what went wrong. + * + * When available, the relevant ndr_reference is passed to the + * operation but keep in mind that it may be a null pointer. + * + * Functions mlndo_get_pdu(), mlndo_put_pdu(), and mlndo_pad_pdu() + * must never grow the PDU data. A request for out-of-bounds data is + * an error. The swap_bytes flag is 1 if NDR knows that the byte- + * order in the PDU is different from the local system. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define NDOBUFSZ 128 + +#define NDR_PDU_BLOCK_SIZE (4*1024) +#define NDR_PDU_BLOCK_MASK (NDR_PDU_BLOCK_SIZE - 1) +#define NDR_PDU_ALIGN(N) \ + (((N) + NDR_PDU_BLOCK_SIZE) & ~NDR_PDU_BLOCK_MASK) +#define NDR_PDU_MAX_SIZE (64*1024*1024) + +static char *mlndo_malloc(struct mlndr_stream *, unsigned, + struct ndr_reference *); +static int mlndo_free(struct mlndr_stream *, char *, struct ndr_reference *); +static int mlndo_grow_pdu(struct mlndr_stream *, unsigned long, + struct ndr_reference *); +static int mlndo_pad_pdu(struct mlndr_stream *, unsigned long, unsigned long, + struct ndr_reference *); +static int mlndo_get_pdu(struct mlndr_stream *, unsigned long, unsigned long, + char *, int, struct ndr_reference *); +static int mlndo_put_pdu(struct mlndr_stream *, unsigned long, unsigned long, + char *, int, struct ndr_reference *); +static void mlndo_tattle(struct mlndr_stream *, char *, struct ndr_reference *); +static void mlndo_tattle_error(struct mlndr_stream *, struct ndr_reference *); +static int mlndo_reset(struct mlndr_stream *); +static void mlndo_destruct(struct mlndr_stream *); +static void mlndo_hexfmt(uint8_t *, int, int, char *, int); + +/* + * The mlndr stream operations table. + */ +static struct mlndr_stream_ops mlnds_ops = { + mlndo_malloc, + mlndo_free, + mlndo_grow_pdu, + mlndo_pad_pdu, + mlndo_get_pdu, + mlndo_put_pdu, + mlndo_tattle, + mlndo_tattle_error, + mlndo_reset, + mlndo_destruct +}; + +/* + * mlnds_bswap + * + * Copies len bytes from src to dst such that dst contains the bytes + * from src in reverse order. + * + * We expect to be dealing with bytes, words, dwords etc. So the + * length must be non-zero and a power of 2. + */ +void +mlnds_bswap(void *srcbuf, void *dstbuf, size_t len) +{ + uint8_t *src = (uint8_t *)srcbuf; + uint8_t *dst = (uint8_t *)dstbuf; + + if ((len != 0) && ((len & (len - 1)) == 0)) { + src += len; + + while (len--) + *dst++ = *(--src); + } +} + +/* + * mlnds_initialize + * + * Initialize a stream. Sets up the PDU parameters and assigns the stream + * operations and the reference to the heap. An external heap is provided + * to the stream, rather than each stream creating its own heap. + */ +int +mlnds_initialize(struct mlndr_stream *mlnds, unsigned pdu_size_hint, + int composite_op, mlrpc_heap_t *heap) +{ + unsigned size; + + assert(mlnds); + assert(heap); + + bzero(mlnds, sizeof (*mlnds)); + + if (pdu_size_hint > NDR_PDU_MAX_SIZE) + return (0); + + size = (pdu_size_hint == 0) ? NDR_PDU_BLOCK_SIZE : pdu_size_hint; + mlnds->pdu_base_addr = malloc(size); + assert(mlnds->pdu_base_addr); + + mlnds->pdu_max_size = size; + mlnds->pdu_size = 0; + mlnds->pdu_base_offset = (unsigned long)mlnds->pdu_base_addr; + + mlnds->mlndo = &mlnds_ops; + mlnds->heap = (struct mlrpc_heap *)heap; + + mlnds->m_op = composite_op & 0x0F; + mlnds->dir = composite_op & 0xF0; + + mlnds->outer_queue_tailp = &mlnds->outer_queue_head; + return (1); +} + +/* + * mlnds_destruct + * + * Destroy a stream. This is an external interface to provide access to + * the stream's destruct operation. + */ +void +mlnds_destruct(struct mlndr_stream *mlnds) +{ + MLNDS_DESTRUCT(mlnds); +} + +/* + * mlndo_malloc + * + * Allocate memory from the stream heap. + */ +/*ARGSUSED*/ +static char * +mlndo_malloc(struct mlndr_stream *mlnds, unsigned len, + struct ndr_reference *ref) +{ + return (mlrpc_heap_malloc((mlrpc_heap_t *)mlnds->heap, len)); +} + +/* + * mlndo_free + * + * Always succeeds: cannot free individual stream allocations. + */ +/*ARGSUSED*/ +static int +mlndo_free(struct mlndr_stream *mlnds, char *p, struct ndr_reference *ref) +{ + return (1); +} + +/* + * mlndo_grow_pdu + * + * This is the only place that should change the size of the PDU. If the + * desired offset is beyond the current PDU size, we realloc the PDU + * buffer to accommodate the request. For efficiency, the PDU is always + * extended to a NDR_PDU_BLOCK_SIZE boundary. Requests to grow the PDU + * beyond NDR_PDU_MAX_SIZE are rejected. + * + * Returns 1 to indicate success. Otherwise 0 to indicate failure. + */ +static int +mlndo_grow_pdu(struct mlndr_stream *mlnds, unsigned long want_end_offset, + struct ndr_reference *ref) +{ + unsigned char *pdu_addr; + unsigned pdu_max_size; + + mlndo_printf(mlnds, ref, "grow %d", want_end_offset); + + pdu_max_size = mlnds->pdu_max_size; + + if (want_end_offset > pdu_max_size) { + pdu_max_size = NDR_PDU_ALIGN(want_end_offset); + + if (pdu_max_size >= NDR_PDU_MAX_SIZE) + return (0); + + pdu_addr = realloc(mlnds->pdu_base_addr, pdu_max_size); + if (pdu_addr == 0) + return (0); + + mlnds->pdu_max_size = pdu_max_size; + mlnds->pdu_base_addr = pdu_addr; + mlnds->pdu_base_offset = (unsigned long)pdu_addr; + } + + mlnds->pdu_size = want_end_offset; + return (1); +} + +static int +mlndo_pad_pdu(struct mlndr_stream *mlnds, unsigned long pdu_offset, + unsigned long n_bytes, struct ndr_reference *ref) +{ + unsigned char *data; + + data = (unsigned char *)mlnds->pdu_base_offset; + data += pdu_offset; + + mlndo_printf(mlnds, ref, "pad %d@%-3d", n_bytes, pdu_offset); + + bzero(data, n_bytes); + return (1); +} + +/* + * mlndo_get_pdu + * + * The swap flag is 1 if NDR knows that the byte-order in the PDU + * is different from the local system. + * + * Returns 1 on success or 0 to indicate failure. + */ +static int +mlndo_get_pdu(struct mlndr_stream *mlnds, unsigned long pdu_offset, + unsigned long n_bytes, char *buf, int swap_bytes, + struct ndr_reference *ref) +{ + unsigned char *data; + char hexbuf[NDOBUFSZ]; + + data = (unsigned char *)mlnds->pdu_base_offset; + data += pdu_offset; + + mlndo_hexfmt(data, n_bytes, swap_bytes, hexbuf, NDOBUFSZ); + + mlndo_printf(mlnds, ref, "get %d@%-3d = %s", + n_bytes, pdu_offset, hexbuf); + + if (!swap_bytes) + bcopy(data, buf, n_bytes); + else + mlnds_bswap(data, (unsigned char *)buf, n_bytes); + + return (1); +} + +/* + * mlndo_put_pdu + * + * This is a receiver makes right protocol. So we do not need + * to be concerned about the byte-order of an outgoing PDU. + */ +/*ARGSUSED*/ +static int +mlndo_put_pdu(struct mlndr_stream *mlnds, unsigned long pdu_offset, + unsigned long n_bytes, char *buf, int swap_bytes, + struct ndr_reference *ref) +{ + unsigned char *data; + char hexbuf[NDOBUFSZ]; + + data = (unsigned char *)mlnds->pdu_base_offset; + data += pdu_offset; + + mlndo_hexfmt((uint8_t *)buf, n_bytes, 0, hexbuf, NDOBUFSZ); + + mlndo_printf(mlnds, ref, "put %d@%-3d = %s", + n_bytes, pdu_offset, hexbuf); + + bcopy(buf, data, n_bytes); + return (1); +} + +static void +mlndo_tattle(struct mlndr_stream *mlnds, char *what, + struct ndr_reference *ref) +{ + mlndo_printf(mlnds, ref, what); +} + +static void +mlndo_tattle_error(struct mlndr_stream *mlnds, struct ndr_reference *ref) +{ + unsigned char *data; + char hexbuf[NDOBUFSZ]; + + data = (unsigned char *)mlnds->pdu_base_offset; + if (ref) + data += ref->pdu_offset; + else + data += mlnds->pdu_scan_offset; + + mlndo_hexfmt(data, 16, 0, hexbuf, NDOBUFSZ); + + mlndo_printf(mlnds, ref, "ERROR=%d REF=%d OFFSET=%d SIZE=%d/%d", + mlnds->error, mlnds->error_ref, mlnds->pdu_scan_offset, + mlnds->pdu_size, mlnds->pdu_max_size); + mlndo_printf(mlnds, ref, " %s", hexbuf); +} + +/* + * mlndo_reset + * + * Reset a stream: zap the outer_queue. We don't need to tamper + * with the stream heap: it's handled externally to the stream. + */ +static int +mlndo_reset(struct mlndr_stream *mlnds) +{ + mlndo_printf(mlnds, 0, "reset"); + + mlnds->pdu_size = 0; + mlnds->pdu_scan_offset = 0; + mlnds->outer_queue_head = 0; + mlnds->outer_current = 0; + mlnds->outer_queue_tailp = &mlnds->outer_queue_head; + + return (1); +} + +/* + * mlndo_destruct + * + * Destruct a stream: zap the outer_queue. Currently, this operation isn't + * required because the heap management (creation/destruction) is external + * to the stream but it provides a place-holder for stream cleanup. + */ +static void +mlndo_destruct(struct mlndr_stream *mlnds) +{ + mlndo_printf(mlnds, 0, "destruct"); + + if (mlnds->pdu_base_addr != 0) { + free(mlnds->pdu_base_addr); + mlnds->pdu_base_addr = 0; + mlnds->pdu_base_offset = 0; + } + + mlnds->outer_queue_head = 0; + mlnds->outer_current = 0; + mlnds->outer_queue_tailp = &mlnds->outer_queue_head; +} + +/* + * Printf style formatting for NDR operations. + */ +void +mlndo_printf(struct mlndr_stream *mlnds, struct ndr_reference *ref, + const char *fmt, ...) +{ + va_list ap; + char buf[NDOBUFSZ]; + + va_start(ap, fmt); + (void) vsnprintf(buf, NDOBUFSZ, fmt, ap); + va_end(ap); + + if (mlnds) + mlndo_fmt(mlnds, ref, buf); + else + mlndo_trace(buf); +} + +/* + * Main output formatter for NDR operations. + * + * UI 03 ... rpc_vers get 1@0 = 5 {05} + * UI 03 ... rpc_vers_minor get 1@1 = 0 {00} + * + * U Marshalling flag (M=marshal, U=unmarshal) + * I Direction flag (I=in, O=out) + * ... Field name + * get PDU operation (get or put) + * 1@0 Bytes @ offset (i.e. 1 byte at offset 0) + * {05} Value + */ +void +mlndo_fmt(struct mlndr_stream *mlnds, struct ndr_reference *ref, char *note) +{ + struct ndr_reference *p; + int indent; + char ref_name[NDOBUFSZ]; + char buf[NDOBUFSZ]; + int m_op_c = '?', dir_c = '?'; + + switch (mlnds->m_op) { + case 0: m_op_c = '-'; break; + case NDR_M_OP_MARSHALL: m_op_c = 'M'; break; + case NDR_M_OP_UNMARSHALL: m_op_c = 'U'; break; + default: m_op_c = '?'; break; + } + + switch (mlnds->dir) { + case 0: dir_c = '-'; break; + case NDR_DIR_IN: dir_c = 'I'; break; + case NDR_DIR_OUT: dir_c = 'O'; break; + default: dir_c = '?'; break; + } + + for (indent = 0, p = ref; p; p = p->enclosing) + indent++; + + if (ref && ref->name) { + if (*ref->name == '[' && ref->enclosing) { + indent--; + (void) snprintf(ref_name, NDOBUFSZ, "%s%s", + ref->enclosing->name, ref->name); + } else { + (void) strlcpy(ref_name, ref->name, NDOBUFSZ); + } + } else { + (void) strlcpy(ref_name, "----", NDOBUFSZ); + } + + (void) snprintf(buf, NDOBUFSZ, "%c%c %02d %-.*s %-*s %s", + m_op_c, dir_c, indent, indent, + "....+....+....+....+....+....", + 20 - indent, ref_name, note); + + mlndo_trace(buf); +} + +/*ARGSUSED*/ +void +mlndo_trace(const char *s) +{ + /* + * Temporary fbt for dtrace until user space sdt enabled. + */ +} + +/* + * Format data as hex bytes (limit is 10 bytes): + * + * 1188689424 {10 f6 d9 46} + * + * If the input data is greater than 10 bytes, an ellipsis will + * be inserted before the closing brace. + */ +static void +mlndo_hexfmt(uint8_t *data, int size, int swap_bytes, char *buf, int len) +{ + char *p = buf; + int interp = 1; + uint32_t c; + int n; + int i; + + n = (size > 10) ? 10 : size; + if (n > len-1) + n = len-1; + + switch (size) { + case 1: + c = *(uint8_t *)data; + break; + case 2: + if (swap_bytes == 0) /*LINTED E_BAD_PTR_CAST_ALIGN*/ + c = *(uint16_t *)data; + else + c = (data[0] << 8) | data[1]; + break; + case 4: + if (swap_bytes == 0) { /*LINTED E_BAD_PTR_CAST_ALIGN*/ + c = *(uint32_t *)data; + } else { + c = (data[0] << 24) | (data[1] << 16) + | (data[2] << 8) | data[3]; + } + break; + default: + c = 0; + interp = 0; + break; + } + + if (interp) + p += sprintf(p, "%4u {", c); + else + p += sprintf(p, " {"); + + p += sprintf(p, "%02x", data[0]); + for (i = 1; i < n; i++) + p += sprintf(p, " %02x", data[i]); + if (size > 10) + p += sprintf(p, " ...}"); + else + p += sprintf(p, "}"); + + /* + * Show c if it's a printable character or wide-char. + */ + if (size < 4 && isprint((uint8_t)c)) + (void) sprintf(p, " %c", (uint8_t)c); +} diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mlndr.c b/usr/src/lib/smbsrv/libmlrpc/common/mlndr.c new file mode 100644 index 000000000000..6d13d459b36e --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/common/mlndr.c @@ -0,0 +1,1965 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Network Data Representation (NDR) is a compatible subset of the DCE RPC + * and MSRPC NDR. NDR is used to move parameters consisting of + * complicated trees of data constructs between an RPC client and server. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#define NDR_STRING_MAX 256 + +#define NDR_IS_UNION(T) \ + (((T)->type_flags & NDR_F_TYPEOP_MASK) == NDR_F_UNION) +#define NDR_IS_STRING(T) \ + (((T)->type_flags & NDR_F_TYPEOP_MASK) == NDR_F_STRING) + +extern struct ndr_typeinfo ndt_s_wchar; + +/* + * The following synopsis describes the terms TOP-MOST, OUTER and INNER. + * + * Each parameter (call arguments and return values) is a TOP-MOST item. + * A TOP-MOST item consists of one or more OUTER items. An OUTER item + * consists of one or more INNER items. There are important differences + * between each kind, which, primarily, have to do with the allocation + * of memory to contain data structures and the order of processing. + * + * This is most easily demonstrated with a short example. + * Consider these structures: + * + * struct top_param { + * long level; + * struct list * head; + * long count; + * }; + * + * struct list { + * struct list * next; + * char * str; // a string + * }; + * + * Now, consider an instance tree like this: + * + * +---------+ +-------+ +-------+ + * |top_param| +--->|list #1| +--->|list #2| + * +---------+ | +-------+ | +-------+ + * | level | | | next ----+ | next --->(NULL) + * | head ----+ | str -->"foo" | str -->"bar" + * | count | | flag | | flag | + * +---------+ +-------+ +-------+ + * + * The DCE(MS)/RPC Stub Data encoding for the tree is the following. + * The vertical bars (|) indicate OUTER construct boundaries. + * + * +-----+----------------------+----------------------+-----+-----+-----+ + * |level|#1.next #1.str #1.flag|#2.next #2.str #2.flag|"bar"|"foo"|count| + * +-----+----------------------+----------------------+-----+-----+-----+ + * level |<----------------------- head -------------------------->|count + * TOP TOP TOP + * + * Here's what to notice: + * + * - The members of the TOP-MOST construct are scattered through the Stub + * Data in the order they occur. This example shows a TOP-MOST construct + * consisting of atomic types (pointers and integers). A construct + * (struct) within the TOP-MOST construct would be contiguous and not + * scattered. + * + * - The members of OUTER constructs are contiguous, which allows for + * non-copied relocated (fixed-up) data structures at the packet's + * destination. We don't do fix-ups here. The pointers within the + * OUTER constructs are processed depth-first in the order that they + * occur. If they were processed breadth first, the sequence would + * be #1,"foo",#2,"bar". This is tricky because OUTER constructs may + * be variable length, and pointers are often encountered before the + * size(s) is known. + * + * - The INNER constructs are simply the members of an OUTER construct. + * + * For comparison, consider how ONC RPC would handle the same tree of + * data. ONC requires very little buffering, while DCE requires enough + * buffer space for the entire message. ONC does atom-by-atom depth-first + * (de)serialization and copy, while DCE allows for constructs to be + * "fixed-up" (relocated) in place at the destination. The packet data + * for the same tree processed by ONC RPC would look like this: + * + * +---------------------------------------------------------------------+ + * |level #1.next #2.next #2.str "bar" #2.flag #1.str "foo" #1.flag count| + * +---------------------------------------------------------------------+ + * TOP #1 #2 #2 bar #2 #1 foo #1 TOP + * + * More details about each TOP-MOST, OUTER, and INNER constructs appear + * throughout this source file near where such constructs are processed. + * + * NDR_REFERENCE + * + * The primary object for NDR is the struct ndr_reference. + * + * An ndr_reference indicates the local datum (i.e. native "C" data + * format), and the element within the Stub Data (contained within the + * RPC PDU (protocol data unit). An ndr_reference also indicates, + * largely as a debugging aid, something about the type of the + * element/datum, and the enclosing construct for the element. The + * ndr_reference's are typically allocated on the stack as locals, + * and the chain of ndr_reference.enclosing references is in reverse + * order of the call graph. + * + * The ndr_reference.datum is a pointer to the local memory that + * contains/receives the value. The ndr_reference.pdu_offset indicates + * where in the Stub Data the value is to be stored/retrieved. + * + * The ndr_reference also contains various parameters to the NDR + * process, such as ndr_reference.size_is, which indicates the size + * of variable length data, or ndr_reference.switch_is, which + * indicates the arm of a union to use. + * + * QUEUE OF OUTER REFERENCES + * + * Some OUTER constructs are variable size. Sometimes (often) we don't + * know the size of the OUTER construct until after pointers have been + * encountered. Hence, we can not begin processing the referent of the + * pointer until after the referring OUTER construct is completely + * processed, i.e. we don't know where to find/put the referent in the + * Stub Data until we know the size of all its predecessors. + * + * This is managed using the queue of OUTER references. The queue is + * anchored in mlndr_stream.outer_queue_head. At any time, + * mlndr_stream.outer_queue_tailp indicates where to put the + * ndr_reference for the next encountered pointer. + * + * Refer to the example above as we illustrate the queue here. In these + * illustrations, the queue entries are not the data structures themselves. + * Rather, they are ndr_reference entries which **refer** to the data + * structures in both the PDU and local memory. + * + * During some point in the processing, the queue looks like this: + * + * outer_current -------v + * outer_queue_head --> list#1 --0 + * outer_queue_tailp ---------& + * + * When the pointer #1.next is encountered, and entry is added to the + * queue, + * + * outer_current -------v + * outer_queue_head --> list#1 --> list#2 --0 + * outer_queue_tailp --------------------& + * + * and the members of #1 continue to be processed, which encounters + * #1.str: + * + * outer_current -------v + * outer_queue_head --> list#1 --> list#2 --> "foo" --0 + * outer_queue_tailp ------------------------------& + * + * Upon the completion of list#1, the processing continues by moving + * to mlndr_stream.outer_current->next, and the tail is set to this + * outer member: + * + * outer_current ------------------v + * outer_queue_head --> list#1 --> list#2 --> "foo" --0 + * outer_queue_tailp --------------------& + * + * Space for list#2 is allocated, either in the Stub Data or of local + * memory. When #2.next is encountered, it is found to be the null + * pointer and no reference is added to the queue. When #2.str is + * encountered, it is found to be valid, and a reference is added: + * + * outer_current ------------------v + * outer_queue_head --> list#1 --> list#2 --> "bar" --> "foo" --0 + * outer_queue_tailp ------------------------------& + * + * Processing continues in a similar fashion with the string "bar", + * which is variable-length. At this point, memory for "bar" may be + * malloc()ed during NDR_M_OP_UNMARSHALL: + * + * outer_current -----------------------------v + * outer_queue_head --> list#1 --> list#2 --> "bar" --> "foo" --0 + * outer_queue_tailp ------------------------------& + * + * And finishes on string "foo". Notice that because "bar" is a + * variable length string, and we don't know the PDU offset for "foo" + * until we reach this point. + * + * When the queue is drained (current->next==0), processing continues + * with the next TOP-MOST member. + * + * The queue of OUTER constructs manages the variable-length semantics + * of OUTER constructs and satisfies the depth-first requirement. + * We allow the queue to linger until the entire TOP-MOST structure is + * processed as an aid to debugging. + */ + +static struct ndr_reference *mlndr_enter_outer_queue(struct ndr_reference *); +extern int mlndr__ulong(struct ndr_reference *); + +/* + * TOP-MOST ELEMENTS + * + * This is fundamentally the first OUTER construct of the parameter, + * possibly followed by more OUTER constructs due to pointers. The + * datum (local memory) for TOP-MOST constructs (structs) is allocated + * by the caller of NDR. + * + * After the element is transferred, the outer_queue is drained. + * + * All we have to do is add an entry to the outer_queue for this + * top-most member, and commence the outer_queue processing. + */ +int +mlndo_process(struct mlndr_stream *mlnds, struct ndr_typeinfo *ti, + char *datum) +{ + struct ndr_reference myref; + + bzero(&myref, sizeof (myref)); + myref.stream = mlnds; + myref.datum = datum; + myref.name = "PROCESS"; + myref.ti = ti; + + return (mlndr_topmost(&myref)); +} + +int +mlndo_operation(struct mlndr_stream *mlnds, struct ndr_typeinfo *ti, + int opnum, char *datum) +{ + struct ndr_reference myref; + + bzero(&myref, sizeof (myref)); + myref.stream = mlnds; + myref.datum = datum; + myref.name = "OPERATION"; + myref.ti = ti; + myref.inner_flags = NDR_F_SWITCH_IS; + myref.switch_is = opnum; + + if (ti->type_flags != NDR_F_INTERFACE) { + NDR_SET_ERROR(&myref, NDR_ERR_NOT_AN_INTERFACE); + return (0); + } + + return ((*ti->ndr_func)(&myref)); +} + +int +mlndr_params(struct ndr_reference *params_ref) +{ + struct ndr_typeinfo *ti = params_ref->ti; + + if (ti->type_flags == NDR_F_OPERATION) + return (*ti->ndr_func) (params_ref); + else + return (mlndr_topmost(params_ref)); +} + +int +mlndr_topmost(struct ndr_reference *top_ref) +{ + struct mlndr_stream *mlnds; + struct ndr_typeinfo *ti; + struct ndr_reference *outer_ref = 0; + int is_varlen; + int is_string; + int error; + int rc; + unsigned n_fixed; + int params; + + assert(top_ref); + assert(top_ref->stream); + assert(top_ref->ti); + + mlnds = top_ref->stream; + ti = top_ref->ti; + + is_varlen = ti->pdu_size_variable_part; + is_string = NDR_IS_STRING(ti); + + assert(mlnds->outer_queue_tailp && !*mlnds->outer_queue_tailp); + assert(!mlnds->outer_current); + + params = top_ref->inner_flags & NDR_F_PARAMS_MASK; + + switch (params) { + case NDR_F_NONE: + case NDR_F_SWITCH_IS: + if (is_string || is_varlen) { + error = NDR_ERR_TOPMOST_VARLEN_ILLEGAL; + NDR_SET_ERROR(outer_ref, error); + return (0); + } + n_fixed = ti->pdu_size_fixed_part; + break; + + case NDR_F_SIZE_IS: + error = NDR_ERR_TOPMOST_VARLEN_ILLEGAL; + NDR_SET_ERROR(outer_ref, error); + return (0); + + case NDR_F_DIMENSION_IS: + if (is_varlen) { + error = NDR_ERR_ARRAY_VARLEN_ILLEGAL; + NDR_SET_ERROR(outer_ref, error); + return (0); + } + n_fixed = ti->pdu_size_fixed_part * top_ref->dimension_is; + break; + + case NDR_F_IS_POINTER: + case NDR_F_IS_POINTER+NDR_F_SIZE_IS: + n_fixed = 4; + break; + + case NDR_F_IS_REFERENCE: + case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS: + n_fixed = 0; + break; + + default: + error = NDR_ERR_OUTER_PARAMS_BAD; + NDR_SET_ERROR(outer_ref, error); + return (0); + } + + outer_ref = mlndr_enter_outer_queue(top_ref); + if (!outer_ref) + return (0); /* error already set */ + + /* + * Hand-craft the first OUTER construct and directly call + * mlndr_inner(). Then, run the outer_queue. We do this + * because mlndr_outer() wants to malloc() memory for + * the construct, and we already have the memory. + */ + + /* move the flags, etc, around again, undoes enter_outer_queue() */ + outer_ref->inner_flags = top_ref->inner_flags; + outer_ref->outer_flags = 0; + outer_ref->datum = top_ref->datum; + + /* All outer constructs start on a mod4 (longword) boundary */ + if (!mlndr_outer_align(outer_ref)) + return (0); /* error already set */ + + /* Regardless of what it is, this is where it starts */ + outer_ref->pdu_offset = mlnds->pdu_scan_offset; + + rc = mlndr_outer_grow(outer_ref, n_fixed); + if (!rc) + return (0); /* error already set */ + + outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_fixed; + + /* set-up outer_current, as though run_outer_queue() was doing it */ + mlnds->outer_current = outer_ref; + mlnds->outer_queue_tailp = &mlnds->outer_current->next; + mlnds->pdu_scan_offset = outer_ref->pdu_end_offset; + + /* do the topmost member */ + rc = mlndr_inner(outer_ref); + if (!rc) + return (0); /* error already set */ + + mlnds->pdu_scan_offset = outer_ref->pdu_end_offset; + + /* advance, as though run_outer_queue() was doing it */ + mlnds->outer_current = mlnds->outer_current->next; + return (mlndr_run_outer_queue(mlnds)); +} + +static struct ndr_reference * +mlndr_enter_outer_queue(struct ndr_reference *arg_ref) +{ + struct mlndr_stream *mlnds = arg_ref->stream; + struct ndr_reference *outer_ref; + + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + outer_ref = (struct ndr_reference *) + MLNDS_MALLOC(mlnds, sizeof (*outer_ref), arg_ref); + if (!outer_ref) { + NDR_SET_ERROR(arg_ref, NDR_ERR_MALLOC_FAILED); + return (0); + } + + *outer_ref = *arg_ref; + + /* move advice in inner_flags to outer_flags */ + outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK; + outer_ref->inner_flags = 0; + outer_ref->enclosing = mlnds->outer_current; + outer_ref->backptr = 0; + outer_ref->datum = 0; + + assert(mlnds->outer_queue_tailp); + + outer_ref->next = *mlnds->outer_queue_tailp; + *mlnds->outer_queue_tailp = outer_ref; + mlnds->outer_queue_tailp = &outer_ref->next; + return (outer_ref); +} + +int +mlndr_run_outer_queue(struct mlndr_stream *mlnds) +{ + while (mlnds->outer_current) { + mlnds->outer_queue_tailp = &mlnds->outer_current->next; + + if (!mlndr_outer(mlnds->outer_current)) + return (0); + + mlnds->outer_current = mlnds->outer_current->next; + } + + return (1); +} + +/* + * OUTER CONSTRUCTS + * + * OUTER constructs are where the real work is, which stems from the + * variable-length potential. + * + * DCE(MS)/RPC VARIABLE LENGTH -- CONFORMANT, VARYING, VARYING/CONFORMANT + * + * DCE(MS)/RPC provides for three forms of variable length: CONFORMANT, + * VARYING, and VARYING/CONFORMANT. + * + * What makes this so tough is that the variable-length array may be well + * encapsulated within the outer construct. Further, because DCE(MS)/RPC + * tries to keep the constructs contiguous in the data stream, the sizing + * information precedes the entire OUTER construct. The sizing information + * must be used at the appropriate time, which can be after many, many, + * many fixed-length elements. During IDL type analysis, we know in + * advance constructs that encapsulate variable-length constructs. So, + * we know when we have a sizing header and when we don't. The actual + * semantics of the header are largely deferred. + * + * Currently, VARYING constructs are not implemented but they are described + * here in case they have to be implemented in the future. Similarly, + * DCE(MS)/RPC provides for multi-dimensional arrays, which are currently + * not implemented. Only one-dimensional, variable-length arrays are + * supported. + * + * CONFORMANT CONSTRUCTS -- VARIABLE LENGTH ARRAYS START THE SHOW + * + * All variable-length values are arrays. These arrays may be embedded + * well within another construct. However, a variable-length construct + * may ONLY appear as the last member of an enclosing construct. Example: + * + * struct credentials { + * ulong uid, gid; + * ulong n_gids; + * [size_is(n_gids)] + * ulong gids[*]; // variable-length. + * }; + * + * CONFORMANT constructs have a dynamic size in local memory and in the + * PDU. The CONFORMANT quality is indicated by the [size_is()] advice. + * CONFORMANT constructs have the following header: + * + * struct conformant_header { + * ulong size_is; + * }; + * + * (Multi-dimensional CONFORMANT arrays have a similar header for each + * dimension - not implemented). + * + * Example CONFORMANT construct: + * + * struct user { + * char * name; + * struct credentials cred; // see above + * }; + * + * Consider the data tree: + * + * +--------+ + * | user | + * +--------+ + * | name ----> "fred" (the string is a different OUTER) + * | uid | + * | gid | + * | n_gids | for example, 3 + * | gids[0]| + * | gids[1]| + * | gids[2]| + * +--------+ + * + * The OUTER construct in the Stub Data would be: + * + * +---+---------+---------------------------------------------+ + * |pad|size_is=3 name uid gid n_gids=3 gids[0] gids[1] gids[2]| + * +---+---------+---------------------------------------------+ + * szing hdr|user |<-------------- user.cred ------------>| + * |<--- fixed-size ---->|<----- conformant ---->| + * + * The ndr_typeinfo for struct user will have: + * pdu_fixed_size_part = 16 four long words (name uid gid n_gids) + * pdu_variable_size_part = 4 per element, sizeof gids[0] + * + * VARYING CONSTRUCTS -- NOT IMPLEMENTED + * + * VARYING constructs have the following header: + * + * struct varying_header { + * ulong first_is; + * ulong length_is; + * }; + * + * This indicates which interval of an array is significant. + * Non-intersecting elements of the array are undefined and usually + * zero-filled. The first_is parameter for C arrays is always 0 for + * the first element. + * + * N.B. Constructs may contain one CONFORMANT element, which is always + * last, but may contain many VARYING elements, which can be anywhere. + * + * VARYING CONFORMANT constructs have the sizing headers arranged like + * this: + * + * struct conformant_header all_conformant[N_CONFORMANT_DIM]; + * struct varying_header all_varying[N_VARYING_ELEMS_AND_DIMS]; + * + * The sizing header is immediately followed by the values for the + * construct. Again, we don't support more than one dimension and + * we don't support VARYING constructs at this time. + * + * A good example of a VARYING/CONFORMANT data structure is the UNIX + * directory entry: + * + * struct dirent { + * ushort reclen; + * ushort namlen; + * ulong inum; + * [size_is(reclen-8) length_is(namlen+1)] // -(2+2+4), +1 for NUL + * uchar name[*]; + * }; + * + * + * STRINGS ARE A SPECIAL CASE + * + * Strings are handled specially. MS/RPC uses VARYING/CONFORMANT structures + * for strings. This is a simple one-dimensional variable-length array, + * typically with its last element all zeroes. We handle strings with the + * header: + * + * struct string_header { + * ulong size_is; + * ulong first_is; // always 0 + * ulong length_is; // always same as size_is + * }; + * + * If general support for VARYING and VARYING/CONFORMANT mechanisms is + * implemented, we probably won't need the strings special case. + */ +int +mlndr_outer(struct ndr_reference *outer_ref) +{ + struct mlndr_stream *mlnds = outer_ref->stream; + struct ndr_typeinfo *ti = outer_ref->ti; + int is_varlen = ti->pdu_size_variable_part; + int is_union = NDR_IS_UNION(ti); + int is_string = NDR_IS_STRING(ti); + int error = NDR_ERR_OUTER_PARAMS_BAD; + int params; + + params = outer_ref->outer_flags & NDR_F_PARAMS_MASK; + + NDR_TATTLE(outer_ref, "--OUTER--"); + + /* All outer constructs start on a mod4 (longword) boundary */ + if (!mlndr_outer_align(outer_ref)) + return (0); /* error already set */ + + /* Regardless of what it is, this is where it starts */ + outer_ref->pdu_offset = mlnds->pdu_scan_offset; + + if (is_union) { + error = NDR_ERR_OUTER_UNION_ILLEGAL; + NDR_SET_ERROR(outer_ref, error); + return (0); + } + + switch (params) { + case NDR_F_NONE: + if (is_string) + return (mlndr_outer_string(outer_ref)); + if (is_varlen) + return (mlndr_outer_conformant_construct(outer_ref)); + + return (mlndr_outer_fixed(outer_ref)); + break; + + case NDR_F_SIZE_IS: + case NDR_F_DIMENSION_IS: + if (is_varlen) { + error = NDR_ERR_ARRAY_VARLEN_ILLEGAL; + break; + } + + if (params == NDR_F_SIZE_IS) + return (mlndr_outer_conformant_array(outer_ref)); + else + return (mlndr_outer_fixed_array(outer_ref)); + break; + + default: + error = NDR_ERR_OUTER_PARAMS_BAD; + break; + } + + /* + * If we get here, something is wrong. Most likely, + * the params flags do not match. + */ + NDR_SET_ERROR(outer_ref, error); + return (0); +} + +int +mlndr_outer_fixed(struct ndr_reference *outer_ref) +{ + struct mlndr_stream *mlnds = outer_ref->stream; + struct ndr_typeinfo *ti = outer_ref->ti; + struct ndr_reference myref; + char *valp = NULL; + int is_varlen = ti->pdu_size_variable_part; + int is_union = NDR_IS_UNION(ti); + int is_string = NDR_IS_STRING(ti); + int rc; + unsigned n_hdr; + unsigned n_fixed; + unsigned n_variable; + unsigned n_alloc; + unsigned n_pdu_total; + int params; + + params = outer_ref->outer_flags & NDR_F_PARAMS_MASK; + + assert(!is_varlen && !is_string && !is_union); + assert(params == NDR_F_NONE); + + /* no header for this */ + n_hdr = 0; + + /* fixed part -- exactly one of these */ + n_fixed = ti->pdu_size_fixed_part; + assert(n_fixed > 0); + + /* variable part -- exactly none of these */ + n_variable = 0; + + /* sum them up to determine the PDU space required */ + n_pdu_total = n_hdr + n_fixed + n_variable; + + /* similar sum to determine how much local memory is required */ + n_alloc = n_fixed + n_variable; + + rc = mlndr_outer_grow(outer_ref, n_pdu_total); + if (!rc) + return (rc); /* error already set */ + + switch (mlnds->m_op) { + case NDR_M_OP_MARSHALL: + valp = outer_ref->datum; + assert(valp); + if (outer_ref->backptr) { + assert(valp == *outer_ref->backptr); + } + break; + + case NDR_M_OP_UNMARSHALL: + valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref); + if (!valp) { + NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED); + return (0); + } + if (outer_ref->backptr) + *outer_ref->backptr = valp; + outer_ref->datum = valp; + break; + + default: + NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); + return (0); + } + + bzero(&myref, sizeof (myref)); + myref.stream = mlnds; + myref.enclosing = outer_ref; + myref.ti = outer_ref->ti; + myref.datum = outer_ref->datum; + myref.name = "FIXED-VALUE"; + myref.outer_flags = NDR_F_NONE; + myref.inner_flags = NDR_F_NONE; + + myref.pdu_offset = outer_ref->pdu_offset; + outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total; + + rc = mlndr_inner(&myref); + if (!rc) + return (rc); /* error already set */ + + mlnds->pdu_scan_offset = outer_ref->pdu_end_offset; + return (1); +} + +int +mlndr_outer_fixed_array(struct ndr_reference *outer_ref) +{ + struct mlndr_stream *mlnds = outer_ref->stream; + struct ndr_typeinfo *ti = outer_ref->ti; + struct ndr_reference myref; + char *valp = NULL; + int is_varlen = ti->pdu_size_variable_part; + int is_union = NDR_IS_UNION(ti); + int is_string = NDR_IS_STRING(ti); + int rc; + unsigned n_hdr; + unsigned n_fixed; + unsigned n_variable; + unsigned n_alloc; + unsigned n_pdu_total; + int params; + + params = outer_ref->outer_flags & NDR_F_PARAMS_MASK; + + assert(!is_varlen && !is_string && !is_union); + assert(params == NDR_F_DIMENSION_IS); + + /* no header for this */ + n_hdr = 0; + + /* fixed part -- exactly dimension_is of these */ + n_fixed = ti->pdu_size_fixed_part * outer_ref->dimension_is; + assert(n_fixed > 0); + + /* variable part -- exactly none of these */ + n_variable = 0; + + /* sum them up to determine the PDU space required */ + n_pdu_total = n_hdr + n_fixed + n_variable; + + /* similar sum to determine how much local memory is required */ + n_alloc = n_fixed + n_variable; + + rc = mlndr_outer_grow(outer_ref, n_pdu_total); + if (!rc) + return (rc); /* error already set */ + + switch (mlnds->m_op) { + case NDR_M_OP_MARSHALL: + valp = outer_ref->datum; + assert(valp); + if (outer_ref->backptr) { + assert(valp == *outer_ref->backptr); + } + break; + + case NDR_M_OP_UNMARSHALL: + valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref); + if (!valp) { + NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED); + return (0); + } + if (outer_ref->backptr) + *outer_ref->backptr = valp; + outer_ref->datum = valp; + break; + + default: + NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); + return (0); + } + + bzero(&myref, sizeof (myref)); + myref.stream = mlnds; + myref.enclosing = outer_ref; + myref.ti = outer_ref->ti; + myref.datum = outer_ref->datum; + myref.name = "FIXED-ARRAY"; + myref.outer_flags = NDR_F_NONE; + myref.inner_flags = NDR_F_DIMENSION_IS; + myref.dimension_is = outer_ref->dimension_is; + + myref.pdu_offset = outer_ref->pdu_offset; + outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total; + + rc = mlndr_inner(&myref); + if (!rc) + return (rc); /* error already set */ + + mlnds->pdu_scan_offset = outer_ref->pdu_end_offset; + return (1); +} + +int +mlndr_outer_conformant_array(struct ndr_reference *outer_ref) +{ + struct mlndr_stream *mlnds = outer_ref->stream; + struct ndr_typeinfo *ti = outer_ref->ti; + struct ndr_reference myref; + char *valp = NULL; + int is_varlen = ti->pdu_size_variable_part; + int is_union = NDR_IS_UNION(ti); + int is_string = NDR_IS_STRING(ti); + unsigned long size_is; + int rc; + unsigned n_hdr; + unsigned n_fixed; + unsigned n_variable; + unsigned n_alloc; + unsigned n_pdu_total; + int params; + + params = outer_ref->outer_flags & NDR_F_PARAMS_MASK; + + assert(!is_varlen && !is_string && !is_union); + assert(params == NDR_F_SIZE_IS); + + /* conformant header for this */ + n_hdr = 4; + + /* fixed part -- exactly none of these */ + n_fixed = 0; + + /* variable part -- exactly size_of of these */ + /* notice that it is the **fixed** size of the ti */ + n_variable = ti->pdu_size_fixed_part * outer_ref->size_is; + + /* sum them up to determine the PDU space required */ + n_pdu_total = n_hdr + n_fixed + n_variable; + + /* similar sum to determine how much local memory is required */ + n_alloc = n_fixed + n_variable; + + rc = mlndr_outer_grow(outer_ref, n_pdu_total); + if (!rc) + return (rc); /* error already set */ + + switch (mlnds->m_op) { + case NDR_M_OP_MARSHALL: + size_is = outer_ref->size_is; + rc = mlndr_outer_poke_sizing(outer_ref, 0, &size_is); + if (!rc) + return (0); /* error already set */ + + valp = outer_ref->datum; + assert(valp); + if (outer_ref->backptr) { + assert(valp == *outer_ref->backptr); + } + break; + + case NDR_M_OP_UNMARSHALL: + rc = mlndr_outer_peek_sizing(outer_ref, 0, &size_is); + if (!rc) + return (0); /* error already set */ + + if (size_is != outer_ref->size_is) { + NDR_SET_ERROR(outer_ref, + NDR_ERR_SIZE_IS_MISMATCH_PDU); + return (0); + } + + valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref); + if (!valp) { + NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED); + return (0); + } + if (outer_ref->backptr) + *outer_ref->backptr = valp; + outer_ref->datum = valp; + break; + + default: + NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); + return (0); + } + + bzero(&myref, sizeof (myref)); + myref.stream = mlnds; + myref.enclosing = outer_ref; + myref.ti = outer_ref->ti; + myref.datum = outer_ref->datum; + myref.name = "CONFORMANT-ARRAY"; + myref.outer_flags = NDR_F_NONE; + myref.inner_flags = NDR_F_SIZE_IS; + myref.size_is = outer_ref->size_is; + + myref.inner_flags = NDR_F_DIMENSION_IS; /* convenient */ + myref.dimension_is = outer_ref->size_is; /* convenient */ + + myref.pdu_offset = outer_ref->pdu_offset + 4; + outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total; + + outer_ref->type_flags = NDR_F_NONE; + outer_ref->inner_flags = NDR_F_NONE; + + rc = mlndr_inner(&myref); + if (!rc) + return (rc); /* error already set */ + + mlnds->pdu_scan_offset = outer_ref->pdu_end_offset; + return (1); +} + +int +mlndr_outer_conformant_construct(struct ndr_reference *outer_ref) +{ + struct mlndr_stream *mlnds = outer_ref->stream; + struct ndr_typeinfo *ti = outer_ref->ti; + struct ndr_reference myref; + char *valp = NULL; + int is_varlen = ti->pdu_size_variable_part; + int is_union = NDR_IS_UNION(ti); + int is_string = NDR_IS_STRING(ti); + unsigned long size_is; + int rc; + unsigned n_hdr; + unsigned n_fixed; + unsigned n_variable; + unsigned n_alloc; + unsigned n_pdu_total; + int params; + + params = outer_ref->outer_flags & NDR_F_PARAMS_MASK; + + assert(is_varlen && !is_string && !is_union); + assert(params == NDR_F_NONE); + + /* conformant header for this */ + n_hdr = 4; + + /* fixed part -- exactly one of these */ + n_fixed = ti->pdu_size_fixed_part; + + /* variable part -- exactly size_of of these */ + n_variable = 0; /* 0 for the moment */ + + /* sum them up to determine the PDU space required */ + n_pdu_total = n_hdr + n_fixed + n_variable; + + /* similar sum to determine how much local memory is required */ + n_alloc = n_fixed + n_variable; + + /* For the moment, grow enough for the fixed-size part */ + rc = mlndr_outer_grow(outer_ref, n_pdu_total); + if (!rc) + return (rc); /* error already set */ + + switch (mlnds->m_op) { + case NDR_M_OP_MARSHALL: + /* + * We don't know the size yet. We have to wait for + * it. Proceed with the fixed-size part, and await + * the call to mlndr_size_is(). + */ + size_is = 0; + rc = mlndr_outer_poke_sizing(outer_ref, 0, &size_is); + if (!rc) + return (0); /* error already set */ + + valp = outer_ref->datum; + assert(valp); + if (outer_ref->backptr) { + assert(valp == *outer_ref->backptr); + } + break; + + case NDR_M_OP_UNMARSHALL: + /* + * We know the size of the variable part because + * of the CONFORMANT header. We will verify + * the header against the [size_is(X)] advice + * later when mlndr_size_is() is called. + */ + rc = mlndr_outer_peek_sizing(outer_ref, 0, &size_is); + if (!rc) + return (0); /* error already set */ + + /* recalculate metrics */ + n_variable = size_is * ti->pdu_size_variable_part; + n_pdu_total = n_hdr + n_fixed + n_variable; + n_alloc = n_fixed + n_variable; + + rc = mlndr_outer_grow(outer_ref, n_pdu_total); + if (!rc) + return (rc); /* error already set */ + + outer_ref->size_is = size_is; /* verified later */ + + valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref); + if (!valp) { + NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED); + return (0); + } + if (outer_ref->backptr) + *outer_ref->backptr = valp; + outer_ref->datum = valp; + break; + + default: + NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); + return (0); + } + + bzero(&myref, sizeof (myref)); + myref.stream = mlnds; + myref.enclosing = outer_ref; + myref.ti = outer_ref->ti; + myref.datum = outer_ref->datum; + myref.name = "CONFORMANT-CONSTRUCT"; + myref.outer_flags = NDR_F_NONE; + myref.inner_flags = NDR_F_NONE; + myref.size_is = outer_ref->size_is; + + myref.pdu_offset = outer_ref->pdu_offset + 4; + outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total; + + outer_ref->type_flags = NDR_F_SIZE_IS; /* indicate pending */ + outer_ref->inner_flags = NDR_F_NONE; /* indicate pending */ + + rc = mlndr_inner(&myref); + if (!rc) + return (rc); /* error already set */ + + mlnds->pdu_scan_offset = outer_ref->pdu_end_offset; + + if (outer_ref->inner_flags != NDR_F_SIZE_IS) { + NDR_SET_ERROR(&myref, NDR_ERR_SIZE_IS_MISMATCH_AFTER); + return (0); + } + + return (1); +} + +int +mlndr_size_is(struct ndr_reference *ref) +{ + struct mlndr_stream *mlnds = ref->stream; + struct ndr_reference *outer_ref = mlnds->outer_current; + struct ndr_typeinfo *ti = outer_ref->ti; + unsigned long size_is; + int rc; + unsigned n_hdr; + unsigned n_fixed; + unsigned n_variable; + unsigned n_pdu_total; + + assert(ref->inner_flags & NDR_F_SIZE_IS); + size_is = ref->size_is; + + if (outer_ref->type_flags != NDR_F_SIZE_IS) { + NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_UNEXPECTED); + return (0); + } + + if (outer_ref->inner_flags & NDR_F_SIZE_IS) { + NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_DUPLICATED); + return (0); + } + + /* repeat metrics, see mlndr_conformant_construct() above */ + n_hdr = 4; + n_fixed = ti->pdu_size_fixed_part; + n_variable = size_is * ti->pdu_size_variable_part; + n_pdu_total = n_hdr + n_fixed + n_variable; + + rc = mlndr_outer_grow(outer_ref, n_pdu_total); + if (!rc) + return (rc); /* error already set */ + + switch (mlnds->m_op) { + case NDR_M_OP_MARSHALL: + /* + * We have to set the sizing header and extend + * the size of the PDU (already done). + */ + rc = mlndr_outer_poke_sizing(outer_ref, 0, &size_is); + if (!rc) + return (0); /* error already set */ + break; + + case NDR_M_OP_UNMARSHALL: + /* + * Allocation done during mlndr_conformant_construct(). + * All we are doing here is verifying that the + * intended size (ref->size_is) matches the sizing header. + */ + if (size_is != outer_ref->size_is) { + NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_MISMATCH_PDU); + return (0); + } + break; + + default: + NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); + return (0); + } + + outer_ref->inner_flags |= NDR_F_SIZE_IS; + outer_ref->size_is = ref->size_is; + return (1); +} + +int +mlndr_outer_string(struct ndr_reference *outer_ref) +{ + struct mlndr_stream *mlnds = outer_ref->stream; + struct ndr_typeinfo *ti = outer_ref->ti; + struct ndr_reference myref; + char *valp = NULL; + unsigned is_varlen = ti->pdu_size_variable_part; + int is_union = NDR_IS_UNION(ti); + int is_string = NDR_IS_STRING(ti); + int rc; + unsigned n_zeroes; + unsigned ix; + unsigned long size_is; + unsigned long first_is; + unsigned long length_is; + unsigned n_hdr; + unsigned n_fixed; + unsigned n_variable; + unsigned n_alloc; + unsigned n_pdu_total; + int params; + + params = outer_ref->outer_flags & NDR_F_PARAMS_MASK; + + assert(is_varlen && is_string && !is_union); + assert(params == NDR_F_NONE); + + /* string header for this: size_is first_is length_is */ + n_hdr = 12; + + /* fixed part -- exactly none of these */ + n_fixed = 0; + + if (!mlndr_outer_grow(outer_ref, n_hdr)) + return (0); /* error already set */ + + switch (mlnds->m_op) { + case NDR_M_OP_MARSHALL: + valp = outer_ref->datum; + assert(valp); + + if (outer_ref->backptr) + assert(valp == *outer_ref->backptr); + + if (ti == &ndt_s_wchar) { + /* + * size_is is the number of characters in the string, + * including the null. We assume valp is UTF-8 encoded. + * We can use mts_wcequiv_strlen for ASCII, extended + * ASCII or Unicode (UCS-2). + */ + size_is = (mts_wcequiv_strlen(valp) / + sizeof (mts_wchar_t)) + 1; + + if (size_is > NDR_STRING_MAX) { + NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN); + return (0); + } + } else { + valp = outer_ref->datum; + n_zeroes = 0; + for (ix = 0; ix < 1024; ix++) { + if (valp[ix] == 0) { + n_zeroes++; + if (n_zeroes >= is_varlen && + ix % is_varlen == 0) { + break; + } + } else { + n_zeroes = 0; + } + } + if (ix >= 1024) { + NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN); + return (0); + } + size_is = ix+1; + } + + first_is = 0; + length_is = size_is; + + if (!mlndr_outer_poke_sizing(outer_ref, 0, &size_is) || + !mlndr_outer_poke_sizing(outer_ref, 4, &first_is) || + !mlndr_outer_poke_sizing(outer_ref, 8, &length_is)) + return (0); /* error already set */ + break; + + case NDR_M_OP_UNMARSHALL: + if (!mlndr_outer_peek_sizing(outer_ref, 0, &size_is) || + !mlndr_outer_peek_sizing(outer_ref, 4, &first_is) || + !mlndr_outer_peek_sizing(outer_ref, 8, &length_is)) + return (0); /* error already set */ + + /* + * In addition to the first_is check, we used to check that + * size_is or size_is-1 was equal to length_is but Windows95 + * doesn't conform to this "rule" (see variable part below). + * The srvmgr tool for Windows95 sent the following values + * for a path string: + * + * size_is = 261 (0x105) + * first_is = 0 + * length_is = 53 (0x35) + * + * The length_is was correct (for the given path) but the + * size_is was the maximum path length rather than being + * related to length_is. + */ + if (first_is != 0) { + NDR_SET_ERROR(outer_ref, NDR_ERR_STRING_SIZING); + return (0); + } + + if (ti == &ndt_s_wchar) { + /* + * Decoding Unicode to UTF-8; we need to allow + * for the maximum possible char size. It would + * be nice to use mbequiv_strlen but the string + * may not be null terminated. + */ + n_alloc = (size_is + 1) * MTS_MB_CHAR_MAX; + } else { + n_alloc = (size_is + 1) * is_varlen; + } + + valp = MLNDS_MALLOC(mlnds, n_alloc, outer_ref); + if (!valp) { + NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED); + return (0); + } + + bzero(valp, (size_is+1) * is_varlen); + + if (outer_ref->backptr) + *outer_ref->backptr = valp; + outer_ref->datum = valp; + break; + + default: + NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); + return (0); + } + + /* + * Variable part - exactly length_is of these. + * + * Usually, length_is is same as size_is and includes nul. + * Some protocols use length_is = size_is-1, and length_is does + * not include the nul (which is more consistent with DCE spec). + * If the length_is is 0, there is no data following the + * sizing header, regardless of size_is. + */ + n_variable = length_is * is_varlen; + + /* sum them up to determine the PDU space required */ + n_pdu_total = n_hdr + n_fixed + n_variable; + + /* similar sum to determine how much local memory is required */ + n_alloc = n_fixed + n_variable; + + rc = mlndr_outer_grow(outer_ref, n_pdu_total); + if (!rc) + return (rc); /* error already set */ + + if (length_is > 0) { + bzero(&myref, sizeof (myref)); + myref.stream = mlnds; + myref.enclosing = outer_ref; + myref.ti = outer_ref->ti; + myref.datum = outer_ref->datum; + myref.name = "OUTER-STRING"; + myref.outer_flags = NDR_F_IS_STRING; + myref.inner_flags = NDR_F_NONE; + + /* + * Set up strlen_is so that we know what to + * expect later (see mlndr_s_wchar). + */ + myref.strlen_is = length_is; + } + + myref.pdu_offset = outer_ref->pdu_offset + 12; + + /* + * Don't try to decode empty strings. + */ + if ((size_is == 0) && (first_is == 0) && (length_is == 0)) { + mlnds->pdu_scan_offset = outer_ref->pdu_end_offset; + return (1); + } + + if ((size_is != 0) && (length_is != 0)) { + rc = mlndr_inner(&myref); + if (!rc) + return (rc); /* error already set */ + } + + mlnds->pdu_scan_offset = outer_ref->pdu_end_offset; + return (1); +} + +int +mlndr_outer_peek_sizing(struct ndr_reference *outer_ref, unsigned offset, + unsigned long *sizing_p) +{ + struct mlndr_stream *mlnds = outer_ref->stream; + unsigned long pdu_offset; + int rc; + + pdu_offset = outer_ref->pdu_offset + offset; + + if (pdu_offset < mlnds->outer_current->pdu_offset || + pdu_offset > mlnds->outer_current->pdu_end_offset || + pdu_offset+4 > mlnds->outer_current->pdu_end_offset) { + NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK); + return (0); + } + + switch (mlnds->m_op) { + case NDR_M_OP_MARSHALL: + NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED); + return (0); + + case NDR_M_OP_UNMARSHALL: + rc = MLNDS_GET_PDU(mlnds, pdu_offset, 4, (char *)sizing_p, + mlnds->swap, outer_ref); + break; + + default: + NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); + return (0); + } + + return (rc); +} + +int +mlndr_outer_poke_sizing(struct ndr_reference *outer_ref, unsigned offset, + unsigned long *sizing_p) +{ + struct mlndr_stream *mlnds = outer_ref->stream; + unsigned long pdu_offset; + int rc; + + pdu_offset = outer_ref->pdu_offset + offset; + + if (pdu_offset < mlnds->outer_current->pdu_offset || + pdu_offset > mlnds->outer_current->pdu_end_offset || + pdu_offset+4 > mlnds->outer_current->pdu_end_offset) { + NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK); + return (0); + } + + switch (mlnds->m_op) { + case NDR_M_OP_MARSHALL: + rc = MLNDS_PUT_PDU(mlnds, pdu_offset, 4, (char *)sizing_p, + mlnds->swap, outer_ref); + break; + + case NDR_M_OP_UNMARSHALL: + NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED); + return (0); + + default: + NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); + return (0); + } + + return (rc); +} + +/* + * All OUTER constructs begin on a mod4 (dword) boundary - except + * for the ones that don't: some MSRPC calls appear to use word or + * packed alignment. Strings appear to be dword aligned. + */ +int +mlndr_outer_align(struct ndr_reference *outer_ref) +{ + struct mlndr_stream *mlnds = outer_ref->stream; + int rc; + unsigned n_pad; + unsigned align; + + if (outer_ref->packed_alignment && outer_ref->ti != &ndt_s_wchar) { + align = outer_ref->ti->alignment; + n_pad = ((align + 1) - mlnds->pdu_scan_offset) & align; + } else { + n_pad = (4 - mlnds->pdu_scan_offset) & 3; + } + + if (n_pad == 0) + return (1); /* already aligned, often the case */ + + if (!mlndr_outer_grow(outer_ref, n_pad)) + return (0); /* error already set */ + + switch (mlnds->m_op) { + case NDR_M_OP_MARSHALL: + rc = MLNDS_PAD_PDU(mlnds, + mlnds->pdu_scan_offset, n_pad, outer_ref); + if (!rc) { + NDR_SET_ERROR(outer_ref, NDR_ERR_PAD_FAILED); + return (0); + } + break; + + case NDR_M_OP_UNMARSHALL: + break; + + default: + NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); + return (0); + } + + mlnds->pdu_scan_offset += n_pad; + return (1); +} + +int +mlndr_outer_grow(struct ndr_reference *outer_ref, unsigned n_total) +{ + struct mlndr_stream *mlnds = outer_ref->stream; + unsigned long pdu_want_size; + int rc, is_ok = 0; + + pdu_want_size = mlnds->pdu_scan_offset + n_total; + + if (pdu_want_size <= mlnds->pdu_max_size) { + is_ok = 1; + } + + switch (mlnds->m_op) { + case NDR_M_OP_MARSHALL: + if (is_ok) + break; + rc = MLNDS_GROW_PDU(mlnds, pdu_want_size, outer_ref); + if (!rc) { + NDR_SET_ERROR(outer_ref, NDR_ERR_GROW_FAILED); + return (0); + } + break; + + case NDR_M_OP_UNMARSHALL: + if (is_ok) + break; + NDR_SET_ERROR(outer_ref, NDR_ERR_UNDERFLOW); + return (0); + + default: + NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); + return (0); + } + + if (mlnds->pdu_size < pdu_want_size) + mlnds->pdu_size = pdu_want_size; + + outer_ref->pdu_end_offset = pdu_want_size; + return (1); +} + +/* + * INNER ELEMENTS + * + * The local datum (arg_ref->datum) already exists, there is no need to + * malloc() it. The datum should point at a member of a structure. + * + * For the most part, mlndr_inner() and its helpers are just a sanity + * check. The underlying ti->ndr_func() could be called immediately + * for non-pointer elements. For the sake of robustness, we detect + * run-time errors here. Most of the situations this protects against + * have already been checked by the IDL compiler. This is also a + * common point for processing of all data, and so is a convenient + * place to work from for debugging. + */ +int +mlndr_inner(struct ndr_reference *arg_ref) +{ + struct ndr_typeinfo *ti = arg_ref->ti; + int is_varlen = ti->pdu_size_variable_part; + int is_union = NDR_IS_UNION(ti); + int error = NDR_ERR_INNER_PARAMS_BAD; + int params; + + params = arg_ref->inner_flags & NDR_F_PARAMS_MASK; + + switch (params) { + case NDR_F_NONE: + if (is_union) { + error = NDR_ERR_SWITCH_VALUE_MISSING; + break; + } + return (*ti->ndr_func)(arg_ref); + break; + + case NDR_F_SIZE_IS: + case NDR_F_DIMENSION_IS: + case NDR_F_IS_POINTER+NDR_F_SIZE_IS: /* pointer to something */ + case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS: /* pointer to something */ + if (is_varlen) { + error = NDR_ERR_ARRAY_VARLEN_ILLEGAL; + break; + } + if (is_union) { + error = NDR_ERR_ARRAY_UNION_ILLEGAL; + break; + } + if (params & NDR_F_IS_POINTER) + return (mlndr_inner_pointer(arg_ref)); + else if (params & NDR_F_IS_REFERENCE) + return (mlndr_inner_reference(arg_ref)); + else + return (mlndr_inner_array(arg_ref)); + break; + + case NDR_F_IS_POINTER: /* type is pointer to one something */ + if (is_union) { + error = NDR_ERR_ARRAY_UNION_ILLEGAL; + break; + } + return (mlndr_inner_pointer(arg_ref)); + break; + + case NDR_F_IS_REFERENCE: /* type is pointer to one something */ + if (is_union) { + error = NDR_ERR_ARRAY_UNION_ILLEGAL; + break; + } + return (mlndr_inner_reference(arg_ref)); + break; + + case NDR_F_SWITCH_IS: + if (!is_union) { + error = NDR_ERR_SWITCH_VALUE_ILLEGAL; + break; + } + return (*ti->ndr_func)(arg_ref); + break; + + default: + error = NDR_ERR_INNER_PARAMS_BAD; + break; + } + + /* + * If we get here, something is wrong. Most likely, + * the params flags do not match + */ + NDR_SET_ERROR(arg_ref, error); + return (0); +} + +int +mlndr_inner_pointer(struct ndr_reference *arg_ref) +{ + struct mlndr_stream *mlnds = arg_ref->stream; + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + char **valpp = (char **)arg_ref->datum; + struct ndr_reference *outer_ref; + + if (!mlndr__ulong(arg_ref)) + return (0); /* error */ + if (!*valpp) + return (1); /* NULL pointer */ + + outer_ref = mlndr_enter_outer_queue(arg_ref); + if (!outer_ref) + return (0); /* error already set */ + + /* move advice in inner_flags to outer_flags sans pointer */ + outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK; + outer_ref->outer_flags &= ~NDR_F_IS_POINTER; +#ifdef NDR_INNER_NOT_YET + outer_ref->outer_flags |= NDR_F_BACKPTR; + if (outer_ref->outer_flags & NDR_F_SIZE_IS) { + outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT; + } +#endif /* NDR_INNER_NOT_YET */ + + outer_ref->backptr = valpp; + + switch (mlnds->m_op) { + case NDR_M_OP_MARSHALL: + outer_ref->datum = *valpp; + break; + + case NDR_M_OP_UNMARSHALL: + /* + * This is probably wrong if the application allocated + * memory in advance. Indicate no value for now. + * ONC RPC handles this case. + */ + *valpp = 0; + outer_ref->datum = 0; + break; + } + + return (1); /* pointer dereference scheduled */ +} + +int +mlndr_inner_reference(struct ndr_reference *arg_ref) +{ + struct mlndr_stream *mlnds = arg_ref->stream; + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + char **valpp = (char **)arg_ref->datum; + struct ndr_reference *outer_ref; + + outer_ref = mlndr_enter_outer_queue(arg_ref); + if (!outer_ref) + return (0); /* error already set */ + + /* move advice in inner_flags to outer_flags sans pointer */ + outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK; + outer_ref->outer_flags &= ~NDR_F_IS_REFERENCE; +#ifdef NDR_INNER_REF_NOT_YET + outer_ref->outer_flags |= NDR_F_BACKPTR; + if (outer_ref->outer_flags & NDR_F_SIZE_IS) { + outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT; + } +#endif /* NDR_INNER_REF_NOT_YET */ + + outer_ref->backptr = valpp; + + switch (mlnds->m_op) { + case NDR_M_OP_MARSHALL: + outer_ref->datum = *valpp; + break; + + case NDR_M_OP_UNMARSHALL: + /* + * This is probably wrong if the application allocated + * memory in advance. Indicate no value for now. + * ONC RPC handles this case. + */ + *valpp = 0; + outer_ref->datum = 0; + break; + } + + return (1); /* pointer dereference scheduled */ +} + +int +mlndr_inner_array(struct ndr_reference *encl_ref) +{ + struct ndr_typeinfo *ti = encl_ref->ti; + struct ndr_reference myref; + unsigned long pdu_offset = encl_ref->pdu_offset; + unsigned long n_elem; + unsigned long i; + char name[30]; + + if (encl_ref->inner_flags & NDR_F_SIZE_IS) { + /* now is the time to check/set size */ + if (!mlndr_size_is(encl_ref)) + return (0); /* error already set */ + n_elem = encl_ref->size_is; + } else { + assert(encl_ref->inner_flags & NDR_F_DIMENSION_IS); + n_elem = encl_ref->dimension_is; + } + + bzero(&myref, sizeof (myref)); + myref.enclosing = encl_ref; + myref.stream = encl_ref->stream; + myref.packed_alignment = 0; + myref.ti = ti; + myref.inner_flags = NDR_F_NONE; + + for (i = 0; i < n_elem; i++) { + (void) sprintf(name, "[%lu]", i); + myref.name = name; + myref.pdu_offset = pdu_offset + i * ti->pdu_size_fixed_part; + myref.datum = encl_ref->datum + i * ti->c_size_fixed_part; + + if (!mlndr_inner(&myref)) + return (0); + } + + return (1); +} + + +/* + * BASIC TYPES + */ +#define MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \ + extern int mlndr_##TYPE(struct ndr_reference *encl_ref); \ + struct ndr_typeinfo ndt_##TYPE = { \ + 1, /* NDR version */ \ + (SIZE)-1, /* alignment */ \ + NDR_F_NONE, /* flags */ \ + mlndr_##TYPE, /* ndr_func */ \ + SIZE, /* pdu_size_fixed_part */ \ + 0, /* pdu_size_variable_part */ \ + SIZE, /* c_size_fixed_part */ \ + 0, /* c_size_variable_part */ \ + }; \ + int mlndr_##TYPE(struct ndr_reference *ref) { \ + return (mlndr_basic_integer(ref, SIZE)); \ +} + +#define MAKE_BASIC_TYPE_STRING(TYPE, SIZE) \ + extern int mlndr_s##TYPE(struct ndr_reference *encl_ref); \ + struct ndr_typeinfo ndt_s##TYPE = { \ + 1, /* NDR version */ \ + (SIZE)-1, /* alignment */ \ + NDR_F_STRING, /* flags */ \ + mlndr_s##TYPE, /* ndr_func */ \ + 0, /* pdu_size_fixed_part */ \ + SIZE, /* pdu_size_variable_part */ \ + 0, /* c_size_fixed_part */ \ + SIZE, /* c_size_variable_part */ \ + }; \ + int mlndr_s##TYPE(struct ndr_reference *ref) { \ + return (mlndr_string_basic_integer(ref, &ndt_##TYPE)); \ +} + +#define MAKE_BASIC_TYPE(TYPE, SIZE) \ + MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \ + MAKE_BASIC_TYPE_STRING(TYPE, SIZE) + +extern int +mlndr_basic_integer(struct ndr_reference *ref, unsigned size); + +extern int +mlndr_string_basic_integer(struct ndr_reference *encl_ref, + struct ndr_typeinfo *type_under); + + +MAKE_BASIC_TYPE(_char, 1) +MAKE_BASIC_TYPE(_uchar, 1) +MAKE_BASIC_TYPE(_short, 2) +MAKE_BASIC_TYPE(_ushort, 2) +MAKE_BASIC_TYPE(_long, 4) +MAKE_BASIC_TYPE(_ulong, 4) + +MAKE_BASIC_TYPE_BASE(_wchar, 2) + +int +mlndr_basic_integer(struct ndr_reference *ref, unsigned size) +{ + struct mlndr_stream *mlnds = ref->stream; + char *valp = (char *)ref->datum; + int rc; + + switch (mlnds->m_op) { + case NDR_M_OP_MARSHALL: + rc = MLNDS_PUT_PDU(mlnds, ref->pdu_offset, size, + valp, mlnds->swap, ref); + break; + + case NDR_M_OP_UNMARSHALL: + rc = MLNDS_GET_PDU(mlnds, ref->pdu_offset, size, + valp, mlnds->swap, ref); + break; + + default: + NDR_SET_ERROR(ref, NDR_ERR_M_OP_INVALID); + return (0); + } + + return (rc); +} + +int +mlndr_string_basic_integer(struct ndr_reference *encl_ref, + struct ndr_typeinfo *type_under) +{ + unsigned long pdu_offset = encl_ref->pdu_offset; + unsigned size = type_under->pdu_size_fixed_part; + char *valp; + struct ndr_reference myref; + unsigned long i; + long sense = 0; + char name[30]; + + assert(size != 0); + + bzero(&myref, sizeof (myref)); + myref.enclosing = encl_ref; + myref.stream = encl_ref->stream; + myref.packed_alignment = 0; + myref.ti = type_under; + myref.inner_flags = NDR_F_NONE; + myref.name = name; + + for (i = 0; i < NDR_STRING_MAX; i++) { + (void) sprintf(name, "[%lu]", i); + myref.pdu_offset = pdu_offset + i * size; + valp = encl_ref->datum + i * size; + myref.datum = valp; + + if (!mlndr_inner(&myref)) + return (0); + + switch (size) { + case 1: sense = *valp; break; + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + case 2: sense = *(short *)valp; break; + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + case 4: sense = *(long *)valp; break; + } + + if (!sense) + break; + } + + return (1); +} + + +extern int mlndr_s_wchar(struct ndr_reference *encl_ref); +struct ndr_typeinfo ndt_s_wchar = { + 1, /* NDR version */ + 2-1, /* alignment */ + NDR_F_STRING, /* flags */ + mlndr_s_wchar, /* ndr_func */ + 0, /* pdu_size_fixed_part */ + 2, /* pdu_size_variable_part */ + 0, /* c_size_fixed_part */ + 1, /* c_size_variable_part */ +}; + + +/* + * Hand coded wchar function because all strings are transported + * as wide characters. During NDR_M_OP_MARSHALL, we convert from + * multi-byte to wide characters. During NDR_M_OP_UNMARSHALL, we + * convert from wide characters to multi-byte. + * + * It appeared that NT would sometimes leave a spurious character + * in the data stream before the null wide_char, which would get + * included in the string decode because we processed until the + * null character. It now looks like NT does not always terminate + * RPC Unicode strings and the terminating null is a side effect + * of field alignment. So now we rely on the strlen_is (set up in + * mlndr_outer_string) of the enclosing reference. This may or may + * not include the null but it doesn't matter, the algorithm will + * get it right. + */ +int +mlndr_s_wchar(struct ndr_reference *encl_ref) +{ + struct mlndr_stream *mlnds = encl_ref->stream; + unsigned short wide_char; + char *valp; + struct ndr_reference myref; + unsigned long i; + char name[30]; + int count; + int char_count = 0; + + if (mlnds->m_op == NDR_M_OP_UNMARSHALL) { + /* + * To avoid problems with zero length strings + * we can just null terminate here and be done. + */ + if (encl_ref->strlen_is == 0) { + encl_ref->datum[0] = '\0'; + return (1); + } + } + + bzero(&myref, sizeof (myref)); + myref.enclosing = encl_ref; + myref.stream = encl_ref->stream; + myref.packed_alignment = 0; + myref.ti = &ndt__wchar; + myref.inner_flags = NDR_F_NONE; + myref.datum = (char *)&wide_char; + myref.name = name; + myref.pdu_offset = encl_ref->pdu_offset; + + valp = encl_ref->datum; + count = 0; + + for (i = 0; i < NDR_STRING_MAX; i++) { + (void) sprintf(name, "[%lu]", i); + + if (mlnds->m_op == NDR_M_OP_MARSHALL) { + count = mts_mbtowc((mts_wchar_t *)&wide_char, valp, + MTS_MB_CHAR_MAX); + if (count < 0) { + return (0); + } else if (count == 0) { + /* + * If the input char is 0, mts_mbtowc + * returns 0 without setting wide_char. + * I'm not sure if this is the correct + * behaviour for mts_mbtowc but for + * now we need to set wide_char to 0 + * and assume a count of 1. + */ + wide_char = *valp; + count = 1; + } + } + + if (!mlndr_inner(&myref)) + return (0); + + if (mlnds->m_op == NDR_M_OP_UNMARSHALL) { + count = mts_wctomb(valp, wide_char); + + if ((++char_count) == encl_ref->strlen_is) { + valp += count; + *valp = '\0'; + break; + } + } + + if (!wide_char) + break; + + myref.pdu_offset += sizeof (wide_char); + valp += count; + } + + return (1); +} diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_client.c b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_client.c new file mode 100644 index 000000000000..9bce9469ea43 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_client.c @@ -0,0 +1,369 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include + +#include +#include +#include + +#define MLRPC_IS_LAST_FRAG(F) ((F) & MLRPC_PFC_LAST_FRAG) +#define MLRPC_DEFAULT_FRAGSZ 8192 + +static void mlrpc_c_init_hdr(struct mlrpc_client *, struct mlrpc_xaction *); +static int mlrpc_c_get_frags(struct mlrpc_client *, struct mlrpc_xaction *); +static void mlrpc_c_remove_hdr(struct mlndr_stream *, int *); + +int +mlrpc_c_bind(struct mlrpc_client *mcli, char *service_name, + struct mlrpc_binding **ret_binding_p) +{ + struct mlrpc_service *msvc; + struct mlrpc_binding *mbind; + struct mlrpc_xaction mxa; + mlrpcconn_bind_hdr_t *bhdr; + mlrpc_p_cont_elem_t *pce; + mlrpcconn_bind_ack_hdr_t *bahdr; + mlrpc_p_result_t *pre; + int rc; + + bzero(&mxa, sizeof (mxa)); + + msvc = mlrpc_find_service_by_name(service_name); + if (msvc == NULL) + return (MLRPC_DRC_FAULT_API_SERVICE_INVALID); + + mxa.binding_list = mcli->binding_list; + if ((mbind = mlrpc_new_binding(&mxa)) == NULL) + return (MLRPC_DRC_FAULT_API_BIND_NO_SLOTS); + + mlrpc_c_init_hdr(mcli, &mxa); + + bhdr = &mxa.send_hdr.bind_hdr; + bhdr->common_hdr.ptype = MLRPC_PTYPE_BIND; + bhdr->common_hdr.frag_length = sizeof (*bhdr); + bhdr->max_xmit_frag = MLRPC_DEFAULT_FRAGSZ; + bhdr->max_recv_frag = MLRPC_DEFAULT_FRAGSZ; + bhdr->assoc_group_id = 0; + bhdr->p_context_elem.n_context_elem = 1; + + /* Assign presentation context id */ + pce = &bhdr->p_context_elem.p_cont_elem[0]; + pce->p_cont_id = mcli->next_p_cont_id++; + pce->n_transfer_syn = 1; + + /* Set up UUIDs and versions from the service */ + pce->abstract_syntax.if_version = msvc->abstract_syntax_version; + rc = mlrpc_str_to_uuid(msvc->abstract_syntax_uuid, + &pce->abstract_syntax.if_uuid); + if (!rc) + return (MLRPC_DRC_FAULT_API_SERVICE_INVALID); + + pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version; + rc = mlrpc_str_to_uuid(msvc->transfer_syntax_uuid, + &pce->transfer_syntaxes[0].if_uuid); + if (!rc) + return (MLRPC_DRC_FAULT_API_SERVICE_INVALID); + + /* Format and exchange the PDU */ + + rc = (*mcli->xa_init)(mcli, &mxa, 0); + if (MLRPC_DRC_IS_FAULT(rc)) + return (rc); + + rc = mlrpc_encode_pdu_hdr(&mxa); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + rc = (*mcli->xa_exchange)(mcli, &mxa); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + rc = mlrpc_decode_pdu_hdr(&mxa); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + /* done with buffers */ + (*mcli->xa_destruct)(mcli, &mxa); + + bahdr = &mxa.recv_hdr.bind_ack_hdr; + + if (mxa.ptype != MLRPC_PTYPE_BIND_ACK) + return (MLRPC_DRC_FAULT_RECEIVED_MALFORMED); + + if (bahdr->p_result_list.n_results != 1) + return (MLRPC_DRC_FAULT_RECEIVED_MALFORMED); + + pre = &bahdr->p_result_list.p_results[0]; + + if (pre->result != MLRPC_PCDR_ACCEPTANCE) + return (MLRPC_DRC_FAULT_RECEIVED_MALFORMED); + + mbind->p_cont_id = pce->p_cont_id; + mbind->which_side = MLRPC_BIND_SIDE_CLIENT; + mbind->context = mcli; + mbind->service = msvc; + mbind->instance_specific = 0; + + *ret_binding_p = mbind; + return (MLRPC_DRC_OK); + +fault_exit: + (*mcli->xa_destruct)(mcli, &mxa); + return (rc); +} + +int +mlrpc_c_call(struct mlrpc_binding *mbind, int opnum, void *params, + mlrpc_heapref_t *heapref) +{ + struct mlrpc_client *mcli = mbind->context; + struct mlrpc_service *msvc = mbind->service; + struct mlrpc_xaction mxa; + mlrpcconn_request_hdr_t *reqhdr; + mlrpcconn_common_header_t *rsphdr; + unsigned long recv_pdu_scan_offset; + int rc; + + if (mlrpc_find_stub_in_svc(msvc, opnum) == NULL) + return (MLRPC_DRC_FAULT_API_OPNUM_INVALID); + + bzero(&mxa, sizeof (mxa)); + mxa.ptype = MLRPC_PTYPE_REQUEST; + mxa.opnum = opnum; + mxa.binding = mbind; + + mlrpc_c_init_hdr(mcli, &mxa); + + reqhdr = &mxa.send_hdr.request_hdr; + reqhdr->common_hdr.ptype = MLRPC_PTYPE_REQUEST; + reqhdr->p_cont_id = mbind->p_cont_id; + reqhdr->opnum = opnum; + + rc = (*mcli->xa_init)(mcli, &mxa, heapref->heap); + if (MLRPC_DRC_IS_FAULT(rc)) + return (rc); + + /* Reserve room for hdr */ + mxa.send_mlnds.pdu_scan_offset = sizeof (*reqhdr); + + rc = mlrpc_encode_call(&mxa, params); + if (!MLRPC_DRC_IS_OK(rc)) + goto fault_exit; + + mxa.send_mlnds.pdu_scan_offset = 0; + + /* + * Now we have the PDU size, we need to set up the + * frag_length and calculate the alloc_hint. + */ + mxa.send_hdr.common_hdr.frag_length = mxa.send_mlnds.pdu_size; + reqhdr->alloc_hint = mxa.send_mlnds.pdu_size - + sizeof (mlrpcconn_request_hdr_t); + + rc = mlrpc_encode_pdu_hdr(&mxa); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + rc = (*mcli->xa_exchange)(mcli, &mxa); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + rc = mlrpc_decode_pdu_hdr(&mxa); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + if (mxa.ptype != MLRPC_PTYPE_RESPONSE) { + rc = MLRPC_DRC_FAULT_RECEIVED_MALFORMED; + goto fault_exit; + } + + rsphdr = &mxa.recv_hdr.common_hdr; + + if (!MLRPC_IS_LAST_FRAG(rsphdr->pfc_flags)) { + /* + * This is a multi-fragment response. + * Preserve the current scan offset while getting + * fragments so that we can continue afterward + * as if we had received the entire response as + * a single PDU. + */ + recv_pdu_scan_offset = mxa.recv_mlnds.pdu_scan_offset; + + if (mlrpc_c_get_frags(mcli, &mxa) < 0) { + rc = MLRPC_DRC_FAULT_RECEIVED_MALFORMED; + goto fault_exit; + } + + mxa.recv_mlnds.pdu_scan_offset = recv_pdu_scan_offset; + } + + rc = mlrpc_decode_return(&mxa, params); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + rc = (*mcli->xa_preserve)(mcli, &mxa, heapref); + if (MLRPC_DRC_IS_FAULT(rc)) + goto fault_exit; + + (*mcli->xa_destruct)(mcli, &mxa); + return (MLRPC_DRC_OK); + +fault_exit: + (*mcli->xa_destruct)(mcli, &mxa); + return (rc); +} + +int +mlrpc_c_free_heap(struct mlrpc_binding *mbind, mlrpc_heapref_t *heapref) +{ + struct mlrpc_client *mcli = mbind->context; + + (*mcli->xa_release)(mcli, heapref); + return (0); +} + +static void +mlrpc_c_init_hdr(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa) +{ + mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr; + + hdr->rpc_vers = 5; + hdr->rpc_vers_minor = 0; + hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG + MLRPC_PFC_LAST_FRAG; + hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII; +#ifndef _BIG_ENDIAN + hdr->packed_drep.intg_char_rep |= MLRPC_REPLAB_INTG_LITTLE_ENDIAN; +#endif + /* hdr->frag_length */ + hdr->auth_length = 0; + hdr->call_id = mcli->next_call_id++; +} + +/* + * mlrpc_c_remove_hdr + * + * Remove an RPC fragment header from the received data stream. + * + * Original RPC receive buffer: + * |- frag1 -| |-frag M(partial)-| + * +==================+=============+----+=================+ + * | SmbTransact Rsp1 | SmbTransact | | SmbReadX RspN | + * | (with RPC hdr) | Rsp2 | .. | (with RPC hdr) | + * +-----+------------+-------------+ +-----+-----------+ + * | hdr | data | data | .. | hdr | data | + * +=====+============+=============+----+=====+===========+ + * <------ + * ^ ^ ^ + * | | | + * base_offset hdr data + * + * |-------------------------------------|-----------------| + * offset len + * + * RPC receive buffer (after this call): + * +==================+=============+----+===========+ + * | SmbTransact Rsp1 | SmbTransact | | SmbReadX | + * | (with RPC hdr) | Rsp2 | .. | RspN | + * +-----+------------+-------------+ +-----------+ + * | hdr | data | data | .. | data | + * +=====+============+=============+----+===========+ + */ +static void +mlrpc_c_remove_hdr(struct mlndr_stream *mlnds, int *nbytes) +{ + char *hdr; + char *data; + + hdr = (char *)mlnds->pdu_base_offset + mlnds->pdu_scan_offset; + data = hdr + MLRPC_RSP_HDR_SIZE; + *nbytes -= MLRPC_RSP_HDR_SIZE; + + bcopy(data, hdr, *nbytes); + mlnds->pdu_size -= MLRPC_RSP_HDR_SIZE; +} + +/* + * mlrpc_c_get_frags + * + * A DCE RPC message that is larger than a single fragment is transmitted + * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for + * both Windows 2000 and 2003. + * + * Collect RPC fragments and append them to the receive stream buffer. + * Each received fragment has a header, which we need to remove as we + * build the full RPC PDU. + * + * The xa_read() calls will translate to SmbReadX requests. Note that + * there is no correspondence between SmbReadX buffering and DCE RPC + * fragment alignment. + * + * Return -1 on error. Otherwise, return the total data count of the + * complete RPC response upon success. + */ +static int +mlrpc_c_get_frags(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa) +{ + struct mlndr_stream *mlnds = &mxa->recv_mlnds; + mlrpcconn_common_header_t hdr; + int frag_rcvd; + int frag_size; + int last_frag; + int nbytes; + + /* + * The scan offest will be used to locate the frag header. + */ + mlnds->pdu_scan_offset = mlnds->pdu_base_offset + mlnds->pdu_size; + + do { + frag_rcvd = 0; + + do { + if ((nbytes = (*mcli->xa_read)(mcli, mxa)) < 0) + return (-1); + + if (frag_rcvd == 0) { + mlrpc_decode_frag_hdr(mlnds, &hdr); + + last_frag = MLRPC_IS_LAST_FRAG(hdr.pfc_flags); + frag_size = hdr.frag_length + - MLRPC_RSP_HDR_SIZE; + + mlrpc_c_remove_hdr(mlnds, &nbytes); + mlnds->pdu_scan_offset += frag_size; + } + + frag_rcvd += nbytes; + + } while (frag_rcvd < frag_size); + } while (!last_frag); + + return (0); +} diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_encdec.c b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_encdec.c new file mode 100644 index 000000000000..884683dfe45c --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_encdec.c @@ -0,0 +1,349 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include + +#include +#include +#include + +#ifdef _BIG_ENDIAN +static const int mlrpc_native_byte_order = MLRPC_REPLAB_INTG_BIG_ENDIAN; +#else +static const int mlrpc_native_byte_order = MLRPC_REPLAB_INTG_LITTLE_ENDIAN; +#endif + +int +mlrpc_encode_decode_common(struct mlrpc_xaction *mxa, int mode, unsigned opnum, + struct ndr_typeinfo *ti, void *datum) +{ + struct mlndr_stream *mlnds; + int m_op = NDR_MODE_TO_M_OP(mode); + int rc; + + if (m_op == NDR_M_OP_MARSHALL) + mlnds = &mxa->send_mlnds; + else + mlnds = &mxa->recv_mlnds; + + /* + * Make sure that mlnds is in the correct mode + */ + if (!NDR_MODE_MATCH(mlnds, mode)) + return (MLRPC_DRC_FAULT_MODE_MISMATCH); + + /* + * Perform the (un)marshalling + */ + if (mlndo_operation(mlnds, ti, opnum, datum)) + return (MLRPC_DRC_OK); + + switch (mlnds->error) { + case NDR_ERR_MALLOC_FAILED: + rc = MLRPC_DRC_FAULT_OUT_OF_MEMORY; + break; + + case NDR_ERR_SWITCH_VALUE_INVALID: + rc = MLRPC_DRC_FAULT_PARAM_0_INVALID; + break; + + case NDR_ERR_UNDERFLOW: + rc = MLRPC_DRC_FAULT_RECEIVED_RUNT; + break; + + case NDR_ERR_GROW_FAILED: + rc = MLRPC_DRC_FAULT_ENCODE_TOO_BIG; + break; + + default: + if (m_op == NDR_M_OP_MARSHALL) + rc = MLRPC_DRC_FAULT_ENCODE_FAILED; + else + rc = MLRPC_DRC_FAULT_DECODE_FAILED; + break; + } + + return (rc); +} + +int +mlrpc_decode_call(struct mlrpc_xaction *mxa, void *params) +{ + int rc; + + rc = mlrpc_encode_decode_common(mxa, NDR_MODE_CALL_RECV, + mxa->opnum, mxa->binding->service->interface_ti, params); + + return (rc + MLRPC_PTYPE_REQUEST); +} + +int +mlrpc_encode_return(struct mlrpc_xaction *mxa, void *params) +{ + int rc; + + rc = mlrpc_encode_decode_common(mxa, NDR_MODE_RETURN_SEND, + mxa->opnum, mxa->binding->service->interface_ti, params); + + return (rc + MLRPC_PTYPE_RESPONSE); +} + +int +mlrpc_encode_call(struct mlrpc_xaction *mxa, void *params) +{ + int rc; + + rc = mlrpc_encode_decode_common(mxa, NDR_MODE_CALL_SEND, + mxa->opnum, mxa->binding->service->interface_ti, params); + + return (rc + MLRPC_PTYPE_REQUEST); +} + +int +mlrpc_decode_return(struct mlrpc_xaction *mxa, void *params) +{ + int rc; + + rc = mlrpc_encode_decode_common(mxa, NDR_MODE_RETURN_RECV, + mxa->opnum, mxa->binding->service->interface_ti, params); + + return (rc + MLRPC_PTYPE_RESPONSE); +} + +int +mlrpc_decode_pdu_hdr(struct mlrpc_xaction *mxa) +{ + mlrpcconn_common_header_t *hdr = &mxa->recv_hdr.common_hdr; + struct mlndr_stream *mlnds = &mxa->recv_mlnds; + int ptype; + int rc; + int charset; + int byte_order; + + if (mlnds->m_op != NDR_M_OP_UNMARSHALL) + return (MLRPC_DRC_FAULT_MODE_MISMATCH + 0xFF); + + /* + * All PDU headers are at least this big + */ + rc = MLNDS_GROW_PDU(mlnds, sizeof (mlrpcconn_common_header_t), 0); + if (!rc) + return (MLRPC_DRC_FAULT_RECEIVED_RUNT + 0xFF); + + /* + * Peek at the first eight bytes to figure out what we're doing. + */ + rc = MLNDS_GET_PDU(mlnds, 0, 8, (char *)hdr, 0, 0); + if (!rc) + return (MLRPC_DRC_FAULT_DECODE_FAILED + 0xFF); + + /* + * Verify the protocol version. + */ + if ((hdr->rpc_vers != 5) || (hdr->rpc_vers_minor != 0)) + return (MLRPC_DRC_FAULT_DECODE_FAILED + 0xFF); + + /* + * Check for ASCII as the character set. This is an ASCII + * versus EBCDIC option and has nothing to do with Unicode. + */ + charset = hdr->packed_drep.intg_char_rep & MLRPC_REPLAB_CHAR_MASK; + if (charset != MLRPC_REPLAB_CHAR_ASCII) + return (MLRPC_DRC_FAULT_DECODE_FAILED + 0xFF); + + /* + * Set the byte swap flag if the PDU byte-order + * is different from the local byte-order. + */ + byte_order = hdr->packed_drep.intg_char_rep & MLRPC_REPLAB_INTG_MASK; + mlnds->swap = (byte_order != mlrpc_native_byte_order) ? 1 : 0; + + ptype = hdr->ptype; + if (ptype == MLRPC_PTYPE_REQUEST && + (hdr->pfc_flags & MLRPC_PFC_OBJECT_UUID) != 0) { + ptype = MLRPC_PTYPE_REQUEST_WITH; /* fake for sizing */ + } + + mxa->ptype = hdr->ptype; + + rc = mlrpc_encode_decode_common(mxa, + NDR_M_OP_AND_DIR_TO_MODE(mlnds->m_op, mlnds->dir), + ptype, &TYPEINFO(mlrpcconn_hdr), hdr); + + return (rc + 0xFF); +} + +/* + * Decode an RPC fragment header. Use mlrpc_decode_pdu_hdr() to process + * the first fragment header then this function to process additional + * fragment headers. + */ +void +mlrpc_decode_frag_hdr(struct mlndr_stream *mlnds, + mlrpcconn_common_header_t *hdr) +{ + mlrpcconn_common_header_t *tmp; + uint8_t *pdu; + int byte_order; + + pdu = (uint8_t *)mlnds->pdu_base_offset + mlnds->pdu_scan_offset; + bcopy(pdu, hdr, MLRPC_RSP_HDR_SIZE); + + /* + * Swap non-byte fields if the PDU byte-order + * is different from the local byte-order. + */ + byte_order = hdr->packed_drep.intg_char_rep & MLRPC_REPLAB_INTG_MASK; + + if (byte_order != mlrpc_native_byte_order) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + tmp = (mlrpcconn_common_header_t *)pdu; + + mlnds_bswap(&tmp->frag_length, &hdr->frag_length, + sizeof (WORD)); + mlnds_bswap(&tmp->auth_length, &hdr->auth_length, + sizeof (WORD)); + mlnds_bswap(&tmp->call_id, &hdr->call_id, sizeof (DWORD)); + } +} + +int +mlrpc_encode_pdu_hdr(struct mlrpc_xaction *mxa) +{ + mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr; + struct mlndr_stream *mlnds = &mxa->send_mlnds; + int ptype; + int rc; + + if (mlnds->m_op != NDR_M_OP_MARSHALL) + return (MLRPC_DRC_FAULT_MODE_MISMATCH + 0xFF); + + ptype = hdr->ptype; + if (ptype == MLRPC_PTYPE_REQUEST && + (hdr->pfc_flags & MLRPC_PFC_OBJECT_UUID) != 0) { + ptype = MLRPC_PTYPE_REQUEST_WITH; /* fake for sizing */ + } + + rc = mlrpc_encode_decode_common(mxa, + NDR_M_OP_AND_DIR_TO_MODE(mlnds->m_op, mlnds->dir), + ptype, &TYPEINFO(mlrpcconn_hdr), hdr); + + return (rc + 0xFF); +} + +/* + * This is a hand-coded derivative of the automatically generated + * (un)marshalling routine for bind_ack headers. bind_ack headers + * have an interior conformant array, which is inconsistent with + * IDL/NDR rules. + */ +extern struct ndr_typeinfo ndt__uchar; +extern struct ndr_typeinfo ndt__ushort; +extern struct ndr_typeinfo ndt__ulong; + +int mlndr__mlrpcconn_bind_ack_hdr(struct ndr_reference *encl_ref); +struct ndr_typeinfo ndt__mlrpcconn_bind_ack_hdr = { + 1, /* NDR version */ + 3, /* alignment */ + NDR_F_STRUCT, /* flags */ + mlndr__mlrpcconn_bind_ack_hdr, /* ndr_func */ + 68, /* pdu_size_fixed_part */ + 0, /* pdu_size_variable_part */ + 68, /* c_size_fixed_part */ + 0, /* c_size_variable_part */ +}; + +/* + * [_no_reorder] + */ +int +mlndr__mlrpcconn_bind_ack_hdr(struct ndr_reference *encl_ref) +{ + struct mlndr_stream *mlnds = encl_ref->stream; + struct mlrpcconn_bind_ack_hdr *val = /*LINTED E_BAD_PTR_CAST_ALIGN*/ + (struct mlrpcconn_bind_ack_hdr *)encl_ref->datum; + struct ndr_reference myref; + unsigned long offset; + + bzero(&myref, sizeof (myref)); + myref.enclosing = encl_ref; + myref.stream = encl_ref->stream; + myref.packed_alignment = 0; + + /* do all members in order */ + NDR_MEMBER(_mlrpcconn_common_header, common_hdr, 0UL); + NDR_MEMBER(_ushort, max_xmit_frag, 16UL); + NDR_MEMBER(_ushort, max_recv_frag, 18UL); + NDR_MEMBER(_ulong, assoc_group_id, 20UL); + + /* port any is the conformant culprit */ + offset = 24UL; + + switch (mlnds->m_op) { + case NDR_M_OP_MARSHALL: + val->sec_addr.length = + strlen((char *)val->sec_addr.port_spec) + 1; + break; + + case NDR_M_OP_UNMARSHALL: + break; + + default: + NDR_SET_ERROR(encl_ref, NDR_ERR_M_OP_INVALID); + return (0); + } + + NDR_MEMBER(_ushort, sec_addr.length, offset); + NDR_MEMBER_ARR_WITH_DIMENSION(_uchar, sec_addr.port_spec, + offset+2UL, val->sec_addr.length); + + offset += 2; + offset += val->sec_addr.length; + offset += (4 - offset) & 3; + + NDR_MEMBER(_mlrpc_p_result_list, p_result_list, offset); + return (1); +} + +unsigned +mlrpc_bind_ack_hdr_size(struct mlrpcconn_bind_ack_hdr *bahdr) +{ + unsigned offset; + unsigned length; + + /* port any is the conformant culprit */ + offset = 24UL; + + length = strlen((char *)bahdr->sec_addr.port_spec) + 1; + + offset += 2; + offset += length; + offset += (4 - offset) & 3; + offset += sizeof (bahdr->p_result_list); + return (offset); +} diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_heap.c b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_heap.c new file mode 100644 index 000000000000..97a16ea010cd --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_heap.c @@ -0,0 +1,236 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * MLRPC heap management. The heap is used for temporary storage by + * both the client and server side library routines. In order to + * support the different requirements of the various RPCs, the heap + * can grow dynamically if required. We start with a single block + * and perform sub-allocations from it. If an RPC requires more space + * we will continue to add it a block at a time. This means that we + * don't hog lots of memory on every call to support the few times + * that we actually need a lot heap space. + * + * Note that there is no individual free function. Once space has been + * allocated, it remains allocated until the heap is destroyed. This + * shouldn't be an issue because the heap is being filled with data to + * be marshalled or unmarshalled and we need it all to be there until + * the point that the entire heap is no longer required. + */ + +#include +#include +#include +#include +#include + +#include +#include + +/* + * Allocate a heap structure and the first heap block. For many RPC + * operations this will be the only time we need to malloc memory + * in this instance of the heap. The only point of note here is that + * we put the heap management data in the first block to avoid a + * second malloc. Make sure that sizeof(mlrpc_heap_t) is smaller + * than MLRPC_HEAP_BLKSZ. + * + * Note that the heap management data is at the start of the first block. + * + * Returns a pointer to the newly created heap, which is used like an + * opaque handle with the rest of the heap management interface.. + */ +mlrpc_heap_t * +mlrpc_heap_create(void) +{ + mlrpc_heap_t *heap; + char *base; + + if ((base = (char *)malloc(MLRPC_HEAP_BLKSZ)) == NULL) + return (NULL); + + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + heap = (mlrpc_heap_t *)base; + bzero(heap, sizeof (mlrpc_heap_t)); + + heap->iovcnt = MLRPC_HEAP_MAXIOV; + heap->iov = heap->iovec; + heap->iov->iov_base = base; + heap->iov->iov_len = sizeof (mlrpc_heap_t); + heap->top = base + MLRPC_HEAP_BLKSZ; + heap->next = base + sizeof (mlrpc_heap_t); + + return (heap); +} + +/* + * Deallocate all of the memory associated with a heap. This is the + * only way to deallocate heap memory, it isn't possible to free the + * space obtained by individual malloc calls. + * + * Note that the first block contains the heap management data, which + * is deleted last. + */ +void +mlrpc_heap_destroy(mlrpc_heap_t *heap) +{ + int i; + char *p; + + if (heap) { + for (i = 1; i < MLRPC_HEAP_MAXIOV; ++i) { + if ((p = heap->iovec[i].iov_base) != NULL) + free(p); + } + + free(heap); + } +} + +/* + * Allocate space in the specified heap. All requests are padded, if + * required, to ensure dword alignment. If the current iov will be + * exceeded, we allocate a new block and setup the next iov. Otherwise + * all we have to do is move the next pointer and update the current + * iov length. + * + * On success, a pointer to the allocated (dword aligned) area is + * returned. Otherwise a null pointer is returned. + */ +void * +mlrpc_heap_malloc(mlrpc_heap_t *heap, unsigned size) +{ + char *p; + int align; + int incr_size; + + align = (4 - size) & 3; + size += align; + + if (heap == NULL || size == 0) + return (NULL); + + p = heap->next; + + if (p + size > heap->top) { + if ((heap->iovcnt == 0) || ((--heap->iovcnt) == 0)) + return (NULL); + + incr_size = (size < MLRPC_HEAP_BLKSZ) ? MLRPC_HEAP_BLKSZ : size; + + if ((p = (char *)malloc(incr_size)) == NULL) + return (NULL); + + ++heap->iov; + heap->iov->iov_base = p; + heap->iov->iov_len = 0; + heap->top = p + incr_size; + } + + heap->next = p + size; + heap->iov->iov_len += size; + return ((void *)p); +} + +/* + * Convenience function to do heap strdup. + */ +void * +mlrpc_heap_strsave(mlrpc_heap_t *heap, char *s) +{ + int len; + void *p; + + if (s == NULL) + return (NULL); + + /* + * We don't need to clutter the heap with empty strings. + */ + if ((len = strlen(s)) == 0) + return (""); + + if ((p = mlrpc_heap_malloc(heap, len+1)) != NULL) + (void) strcpy((char *)p, s); + + return (p); +} + +/* + * Our regular string marshalling always creates null terminated strings + * but some Windows clients and servers are pedantic about the string + * formats they will accept and require non-null terminated strings. + * This function can be used to build a wide-char, non-null terminated + * string in the heap as a varying/conformant array. We need to do the + * wide-char conversion here because the marshalling code won't be + * aware that this is really a string. + */ +void +mlrpc_heap_mkvcs(mlrpc_heap_t *heap, char *s, mlrpc_vcbuf_t *vcs) +{ + int mlen; + + vcs->wclen = mts_wcequiv_strlen(s); + vcs->wcsize = vcs->wclen; + + mlen = sizeof (struct mlrpc_vcb) + vcs->wcsize + sizeof (mts_wchar_t); + + vcs->vcb = (struct mlrpc_vcb *)mlrpc_heap_malloc(heap, mlen); + + if (vcs->vcb) { + vcs->vcb->vc_first_is = 0; + vcs->vcb->vc_length_is = vcs->wclen / sizeof (mts_wchar_t); + (void) mts_mbstowcs((mts_wchar_t *)vcs->vcb->buffer, s, + vcs->vcb->vc_length_is); + } +} + +int +mlrpc_heap_used(mlrpc_heap_t *heap) +{ + int used = 0; + int i; + + for (i = 0; i < MLRPC_HEAP_MAXIOV; ++i) + used += heap->iovec[i].iov_len; + + return (used); +} + +int +mlrpc_heap_avail(mlrpc_heap_t *heap) +{ + int avail; + int count; + + count = (heap->iovcnt == 0) ? 0 : (heap->iovcnt - 1); + + avail = count * MLRPC_HEAP_BLKSZ; + avail += (heap->top - heap->next); + + return (avail); +} diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_server.c b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_server.c new file mode 100644 index 000000000000..a5bf41a6174b --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_server.c @@ -0,0 +1,836 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Server side RPC handler. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * Fragment size (5680: NT style). + */ +#define MLRPC_FRAG_SZ 5680 +static unsigned long mlrpc_frag_size = MLRPC_FRAG_SZ; + +/* + * Context table. + */ +#define CTXT_TABLE_ENTRIES 128 +static struct mlsvc_rpc_context context_table[CTXT_TABLE_ENTRIES]; +static mutex_t mlsvc_context_lock; + +static int mlrpc_s_process(struct mlrpc_xaction *); +static int mlrpc_s_bind(struct mlrpc_xaction *); +static int mlrpc_s_request(struct mlrpc_xaction *); +static int mlrpc_generic_call_stub(struct mlrpc_xaction *); +static void mlrpc_reply_prepare_hdr(struct mlrpc_xaction *); +static int mlrpc_s_alter_context(struct mlrpc_xaction *); +static void mlrpc_reply_bind_ack(struct mlrpc_xaction *); +static void mlrpc_reply_fault(struct mlrpc_xaction *, unsigned long); +static int mlrpc_build_reply(struct mlrpc_xaction *); + +/* + * The is the RPC service server-side entry point. All MSRPC encoded + * messages should be passed through here. We use the same context + * structure as the client side but we don't need to set up the client + * side info. + */ +int +mlsvc_rpc_process(smb_pipe_t *inpipe, smb_pipe_t **outpipe, + smb_dr_user_ctx_t *user_ctx) +{ + struct mlsvc_rpc_context *context; + struct mlrpc_xaction *mxa; + struct mlndr_stream *recv_mlnds; + struct mlndr_stream *send_mlnds; + unsigned char *pdu_base_addr; + int datalen; + + if (inpipe == NULL || user_ctx == NULL) + return (-1); + + context = mlsvc_lookup_context(inpipe->sp_pipeid); + if (context == NULL) + return (-1); + + context->user_ctx = user_ctx; + + mxa = (struct mlrpc_xaction *)malloc(sizeof (struct mlrpc_xaction)); + if (mxa == NULL) + return (-1); + + bzero(mxa, sizeof (struct mlrpc_xaction)); + mxa->context = context; + mxa->binding_list = context->binding; + + if ((mxa->heap = mlrpc_heap_create()) == NULL) { + free(mxa); + return (-1); + } + + recv_mlnds = &mxa->recv_mlnds; + + (void) mlnds_initialize(recv_mlnds, inpipe->sp_datalen, + NDR_MODE_CALL_RECV, mxa->heap); + + bcopy(inpipe->sp_data, recv_mlnds->pdu_base_addr, inpipe->sp_datalen); + + send_mlnds = &mxa->send_mlnds; + (void) mlnds_initialize(send_mlnds, 0, + NDR_MODE_RETURN_SEND, mxa->heap); + + (void) mlrpc_s_process(mxa); + + /* + * copy into outpipe + */ + datalen = send_mlnds->pdu_size_with_rpc_hdrs; + *outpipe = calloc(1, sizeof (smb_pipe_t) + datalen); + (*outpipe)->sp_datalen = datalen; + + /* + * Different pointers for single frag vs multi frag responses. + */ + if (send_mlnds->pdu_base_addr_with_rpc_hdrs) + pdu_base_addr = send_mlnds->pdu_base_addr_with_rpc_hdrs; + else + pdu_base_addr = send_mlnds->pdu_base_addr; + + bcopy((char *)pdu_base_addr, (*outpipe)->sp_data, datalen); + mlnds_destruct(&mxa->recv_mlnds); + mlnds_destruct(&mxa->send_mlnds); + mlrpc_heap_destroy(mxa->heap); + free(mxa); + return (datalen); +} + +/* + * Lookup the context for pipeid. If one exists, return a pointer to it. + * Otherwise attempt to allocate a new context and return it. If the + * context table is full, return a null pointer. + */ +struct mlsvc_rpc_context * +mlsvc_lookup_context(int fid) +{ + struct mlsvc_rpc_context *context; + struct mlsvc_rpc_context *available = NULL; + int i; + + (void) mutex_lock(&mlsvc_context_lock); + + for (i = 0; i < CTXT_TABLE_ENTRIES; ++i) { + context = &context_table[i]; + + if (available == NULL && context->fid == 0) { + available = context; + continue; + } + + if (context->fid == fid) { + (void) mutex_unlock(&mlsvc_context_lock); + return (context); + } + } + + if (available) { + bzero(available, sizeof (struct mlsvc_rpc_context)); + available->fid = fid; + + mlrpc_binding_pool_initialize(&available->binding, + available->binding_pool, CTXT_N_BINDING_POOL); + } + + (void) mutex_unlock(&mlsvc_context_lock); + return (available); +} + +/* + * This function should be called to release the context associated + * with a fid when the client performs a close file. + */ +void +mlsvc_rpc_release(int fid) +{ + struct mlsvc_rpc_context *context; + int i; + + (void) mutex_lock(&mlsvc_context_lock); + + for (i = 0; i < CTXT_TABLE_ENTRIES; ++i) { + context = &context_table[i]; + + if (context->fid == fid) { + bzero(context, sizeof (struct mlsvc_rpc_context)); + break; + } + } + + (void) mutex_unlock(&mlsvc_context_lock); +} + +/* + * This is the entry point for all server-side RPC processing. + * It is assumed that the PDU has already been received. + */ +static int +mlrpc_s_process(struct mlrpc_xaction *mxa) +{ + int rc; + + rc = mlrpc_decode_pdu_hdr(mxa); + if (!MLRPC_DRC_IS_OK(rc)) + return (-1); + + (void) mlrpc_reply_prepare_hdr(mxa); + + switch (mxa->ptype) { + case MLRPC_PTYPE_BIND: + rc = mlrpc_s_bind(mxa); + break; + + case MLRPC_PTYPE_REQUEST: + rc = mlrpc_s_request(mxa); + break; + + case MLRPC_PTYPE_ALTER_CONTEXT: + rc = mlrpc_s_alter_context(mxa); + break; + + default: + rc = MLRPC_DRC_FAULT_RPCHDR_PTYPE_INVALID; + break; + } + + if (MLRPC_DRC_IS_FAULT(rc)) + mlrpc_reply_fault(mxa, rc); + + (void) mlrpc_build_reply(mxa); + return (rc); +} + +/* + * Multiple p_cont_elem[]s, multiple transfer_syntaxes[] and multiple + * p_results[] not supported. + */ +static int +mlrpc_s_bind(struct mlrpc_xaction *mxa) +{ + mlrpc_p_cont_list_t *cont_list; + mlrpc_p_result_list_t *result_list; + mlrpc_p_result_t *result; + unsigned p_cont_id; + struct mlrpc_binding *mbind; + mlrpc_uuid_t *as_uuid; + mlrpc_uuid_t *ts_uuid; + char as_buf[64]; + char ts_buf[64]; + int as_vers; + int ts_vers; + struct mlndr_stream *send_mlnds; + struct mlrpc_service *msvc; + int rc; + mlrpc_port_any_t *sec_addr; + + /* acquire targets */ + cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem; + result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list; + result = &result_list->p_results[0]; + + /* + * Set up temporary secondary address port. + * We will correct this later (below). + */ + send_mlnds = &mxa->send_mlnds; + sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr; + sec_addr->length = 13; + (void) strcpy((char *)sec_addr->port_spec, "\\PIPE\\ntsvcs"); + + result_list->n_results = 1; + result_list->reserved = 0; + result_list->reserved2 = 0; + result->result = MLRPC_PCDR_ACCEPTANCE; + result->reason = 0; + bzero(&result->transfer_syntax, sizeof (result->transfer_syntax)); + + /* sanity check */ + if (cont_list->n_context_elem != 1 || + cont_list->p_cont_elem[0].n_transfer_syn != 1) { + mlndo_trace("mlrpc_s_bind: warning: multiple p_cont_elem"); + } + + p_cont_id = cont_list->p_cont_elem[0].p_cont_id; + + if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) != NULL) { + /* + * Duplicate p_cont_id. + * Send a bind_ack with a better error. + */ + mlndo_trace("mlrpc_s_bind: duplicate binding"); + return (MLRPC_DRC_FAULT_BIND_PCONT_BUSY); + } + + if ((mbind = mlrpc_new_binding(mxa)) == NULL) { + /* + * No free binding slot + */ + result->result = MLRPC_PCDR_PROVIDER_REJECTION; + result->reason = MLRPC_PPR_LOCAL_LIMIT_EXCEEDED; + mlndo_trace("mlrpc_s_bind: no resources"); + return (MLRPC_DRC_OK); + } + + as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid; + as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version; + + ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid; + ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version; + + msvc = mlrpc_find_service_by_uuids(as_uuid, as_vers, ts_uuid, ts_vers); + if (!msvc) { + mlrpc_uuid_to_str(as_uuid, as_buf); + mlrpc_uuid_to_str(ts_uuid, ts_buf); + + mlndo_printf(send_mlnds, 0, "mlrpc_s_bind: unknown service"); + mlndo_printf(send_mlnds, 0, "abs=%s v%d, xfer=%s v%d", + as_buf, as_vers, ts_buf, ts_vers); + + result->result = MLRPC_PCDR_PROVIDER_REJECTION; + result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED; + return (MLRPC_DRC_OK); + } + + /* + * We can now use the correct secondary address port. + */ + sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr; + sec_addr->length = strlen(msvc->sec_addr_port) + 1; + (void) strlcpy((char *)sec_addr->port_spec, msvc->sec_addr_port, + MLRPC_PORT_ANY_MAX_PORT_SPEC); + + mbind->p_cont_id = p_cont_id; + mbind->which_side = MLRPC_BIND_SIDE_SERVER; + /* mbind->context set by app */ + mbind->service = msvc; + mbind->instance_specific = 0; + + mxa->binding = mbind; + + if (msvc->bind_req) { + /* + * Call the service-specific bind() handler. If + * this fails, we shouild send a specific error + * on the bind ack. + */ + rc = (msvc->bind_req)(mxa); + if (MLRPC_DRC_IS_FAULT(rc)) { + mbind->service = 0; /* free binding slot */ + mbind->which_side = 0; + mbind->p_cont_id = 0; + mbind->instance_specific = 0; + return (rc); + } + } + + result->transfer_syntax = + cont_list->p_cont_elem[0].transfer_syntaxes[0]; + + /* + * Special rejection of Windows 2000 DSSETUP interface. + * This interface was introduced in Windows 2000 but has + * been subsequently deprecated due to problems. + */ + if (strcmp(msvc->name, "DSSETUP") == 0) { + result->result = MLRPC_PCDR_PROVIDER_REJECTION; + result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED; + } + + return (MLRPC_DRC_BINDING_MADE); +} + +/* + * mlrpc_s_alter_context + * + * The alter context request is used to request additional presentation + * context for another interface and/or version. It's very similar to a + * bind request. + * + * We don't fully support multiple contexts so, for now, we reject this + * request. Windows 2000 clients attempt to use an alternate LSA context + * when ACLs are modified. + */ +static int +mlrpc_s_alter_context(struct mlrpc_xaction *mxa) +{ + mlrpc_p_result_list_t *result_list; + mlrpc_p_result_t *result; + mlrpc_p_cont_list_t *cont_list; + struct mlrpc_binding *mbind; + struct mlrpc_service *msvc; + unsigned p_cont_id; + mlrpc_uuid_t *as_uuid; + mlrpc_uuid_t *ts_uuid; + int as_vers; + int ts_vers; + mlrpc_port_any_t *sec_addr; + + result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list; + result_list->n_results = 1; + result_list->reserved = 0; + result_list->reserved2 = 0; + + result = &result_list->p_results[0]; + result->result = MLRPC_PCDR_ACCEPTANCE; + result->reason = 0; + bzero(&result->transfer_syntax, sizeof (result->transfer_syntax)); + + if (mxa != NULL) { + result->result = MLRPC_PCDR_PROVIDER_REJECTION; + result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED; + return (MLRPC_DRC_OK); + } + + cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem; + p_cont_id = cont_list->p_cont_elem[0].p_cont_id; + + if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) != NULL) + return (MLRPC_DRC_FAULT_BIND_PCONT_BUSY); + + if ((mbind = mlrpc_new_binding(mxa)) == NULL) { + result->result = MLRPC_PCDR_PROVIDER_REJECTION; + result->reason = MLRPC_PPR_LOCAL_LIMIT_EXCEEDED; + return (MLRPC_DRC_OK); + } + + as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid; + as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version; + + ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid; + ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version; + + msvc = mlrpc_find_service_by_uuids(as_uuid, as_vers, ts_uuid, ts_vers); + if (msvc == 0) { + result->result = MLRPC_PCDR_PROVIDER_REJECTION; + result->reason = MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED; + return (MLRPC_DRC_OK); + } + + mbind->p_cont_id = p_cont_id; + mbind->which_side = MLRPC_BIND_SIDE_SERVER; + /* mbind->context set by app */ + mbind->service = msvc; + mbind->instance_specific = 0; + mxa->binding = mbind; + + sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr; + sec_addr->length = 0; + bzero(sec_addr->port_spec, MLRPC_PORT_ANY_MAX_PORT_SPEC); + + result->transfer_syntax = + cont_list->p_cont_elem[0].transfer_syntaxes[0]; + + return (MLRPC_DRC_BINDING_MADE); +} + +static int +mlrpc_s_request(struct mlrpc_xaction *mxa) +{ + struct mlrpc_binding *mbind; + struct mlrpc_service *msvc; + unsigned p_cont_id; + int rc; + + mxa->opnum = mxa->recv_hdr.request_hdr.opnum; + p_cont_id = mxa->recv_hdr.request_hdr.p_cont_id; + + if ((mbind = mlrpc_find_binding(mxa, p_cont_id)) == NULL) + return (MLRPC_DRC_FAULT_REQUEST_PCONT_INVALID); + + mxa->binding = mbind; + msvc = mbind->service; + + /* + * Make room for the response hdr. + */ + mxa->send_mlnds.pdu_scan_offset = MLRPC_RSP_HDR_SIZE; + + if (msvc->call_stub) + rc = (*msvc->call_stub)(mxa); + else + rc = mlrpc_generic_call_stub(mxa); + + if (MLRPC_DRC_IS_FAULT(rc)) { + mlndo_printf(0, 0, "%s[0x%02x]: 0x%04x", + msvc->name, mxa->opnum, rc); + } + + return (rc); +} + +/* + * The transaction and the two mlnds streams use the same heap, which + * should already exist at this point. The heap will also be available + * to the stub. + */ +static int +mlrpc_generic_call_stub(struct mlrpc_xaction *mxa) +{ + struct mlrpc_binding *mbind = mxa->binding; + struct mlrpc_service *msvc = mbind->service; + struct ndr_typeinfo *intf_ti = msvc->interface_ti; + struct mlrpc_stub_table *ste; + int opnum = mxa->opnum; + unsigned p_len = intf_ti->c_size_fixed_part; + char *param; + int rc; + + if (mxa->heap == NULL) { + mlndo_printf(0, 0, "%s[0x%02x]: no heap", msvc->name, opnum); + return (MLRPC_DRC_FAULT_OUT_OF_MEMORY); + } + + if ((ste = mlrpc_find_stub_in_svc(msvc, opnum)) == NULL) { + mlndo_printf(0, 0, "%s[0x%02x]: invalid opnum", + msvc->name, opnum); + return (MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID); + } + + if ((param = mlrpc_heap_malloc(mxa->heap, p_len)) == NULL) + return (MLRPC_DRC_FAULT_OUT_OF_MEMORY); + + bzero(param, p_len); + + rc = mlrpc_decode_call(mxa, param); + if (!MLRPC_DRC_IS_OK(rc)) + return (rc); + + rc = (*ste->func)(param, mxa); + if (rc == MLRPC_DRC_OK) + rc = mlrpc_encode_return(mxa, param); + + return (rc); +} + +/* + * We can perform some initial setup of the response header here. + * We also need to cache some of the information from the bind + * negotiation for use during subsequent RPC calls. + */ +static void +mlrpc_reply_prepare_hdr(struct mlrpc_xaction *mxa) +{ + mlrpcconn_common_header_t *rhdr = &mxa->recv_hdr.common_hdr; + mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr; + + hdr->rpc_vers = 5; + hdr->rpc_vers_minor = 0; + hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG + MLRPC_PFC_LAST_FRAG; + hdr->packed_drep = rhdr->packed_drep; + hdr->frag_length = 0; + hdr->auth_length = 0; + hdr->call_id = rhdr->call_id; +#ifdef _BIG_ENDIAN + hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII + | MLRPC_REPLAB_INTG_BIG_ENDIAN; +#else + hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII + | MLRPC_REPLAB_INTG_LITTLE_ENDIAN; +#endif + + switch (mxa->ptype) { + case MLRPC_PTYPE_BIND: + hdr->ptype = MLRPC_PTYPE_BIND_ACK; + mxa->send_hdr.bind_ack_hdr.max_xmit_frag = + mxa->recv_hdr.bind_hdr.max_xmit_frag; + mxa->send_hdr.bind_ack_hdr.max_recv_frag = + mxa->recv_hdr.bind_hdr.max_recv_frag; + mxa->send_hdr.bind_ack_hdr.assoc_group_id = + mxa->recv_hdr.bind_hdr.assoc_group_id; + + if (mxa->send_hdr.bind_ack_hdr.assoc_group_id == 0) + mxa->send_hdr.bind_ack_hdr.assoc_group_id = time(0); + + /* + * Save the maximum fragment sizes + * for use with subsequent requests. + */ + mxa->context->max_xmit_frag = + mxa->recv_hdr.bind_hdr.max_xmit_frag; + + mxa->context->max_recv_frag = + mxa->recv_hdr.bind_hdr.max_recv_frag; + + break; + + case MLRPC_PTYPE_REQUEST: + hdr->ptype = MLRPC_PTYPE_RESPONSE; + /* mxa->send_hdr.response_hdr.alloc_hint */ + mxa->send_hdr.response_hdr.p_cont_id = + mxa->recv_hdr.request_hdr.p_cont_id; + mxa->send_hdr.response_hdr.cancel_count = 0; + mxa->send_hdr.response_hdr.reserved = 0; + break; + + case MLRPC_PTYPE_ALTER_CONTEXT: + hdr->ptype = MLRPC_PTYPE_ALTER_CONTEXT_RESP; + /* + * The max_xmit_frag, max_recv_frag + * and assoc_group_id are ignored. + */ + break; + + default: + hdr->ptype = 0xFF; + } +} + +/* + * Finish and encode the bind acknowledge (MLRPC_PTYPE_BIND_ACK) header. + * The frag_length is different from a regular RPC response. + */ +static void +mlrpc_reply_bind_ack(struct mlrpc_xaction *mxa) +{ + mlrpcconn_common_header_t *hdr; + mlrpcconn_bind_ack_hdr_t *bahdr; + + hdr = &mxa->send_hdr.common_hdr; + bahdr = &mxa->send_hdr.bind_ack_hdr; + hdr->frag_length = mlrpc_bind_ack_hdr_size(bahdr); +} + +/* + * Signal an RPC fault. The stream is reset and we overwrite whatever + * was in the response header with the fault information. + */ +static void +mlrpc_reply_fault(struct mlrpc_xaction *mxa, unsigned long drc) +{ + mlrpcconn_common_header_t *rhdr = &mxa->recv_hdr.common_hdr; + mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr; + struct mlndr_stream *mlnds = &mxa->send_mlnds; + unsigned long fault_status; + + MLNDS_RESET(mlnds); + + hdr->rpc_vers = 5; + hdr->rpc_vers_minor = 0; + hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG + MLRPC_PFC_LAST_FRAG; + hdr->packed_drep = rhdr->packed_drep; + hdr->frag_length = sizeof (mxa->send_hdr.fault_hdr); + hdr->auth_length = 0; + hdr->call_id = rhdr->call_id; +#ifdef _BIG_ENDIAN + hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII + | MLRPC_REPLAB_INTG_BIG_ENDIAN; +#else + hdr->packed_drep.intg_char_rep = MLRPC_REPLAB_CHAR_ASCII + | MLRPC_REPLAB_INTG_LITTLE_ENDIAN; +#endif + + switch (drc & MLRPC_DRC_MASK_SPECIFIER) { + case MLRPC_DRC_FAULT_OUT_OF_MEMORY: + case MLRPC_DRC_FAULT_ENCODE_TOO_BIG: + fault_status = MLRPC_FAULT_NCA_OUT_ARGS_TOO_BIG; + break; + + case MLRPC_DRC_FAULT_REQUEST_PCONT_INVALID: + fault_status = MLRPC_FAULT_NCA_INVALID_PRES_CONTEXT_ID; + break; + + case MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID: + fault_status = MLRPC_FAULT_NCA_OP_RNG_ERROR; + break; + + case MLRPC_DRC_FAULT_DECODE_FAILED: + case MLRPC_DRC_FAULT_ENCODE_FAILED: + fault_status = MLRPC_FAULT_NCA_PROTO_ERROR; + break; + + default: + fault_status = MLRPC_FAULT_NCA_UNSPEC_REJECT; + break; + } + + mxa->send_hdr.fault_hdr.common_hdr.ptype = MLRPC_PTYPE_FAULT; + mxa->send_hdr.fault_hdr.status = fault_status; + mxa->send_hdr.response_hdr.alloc_hint = hdr->frag_length; +} + +static int +mlrpc_build_reply(struct mlrpc_xaction *mxa) +{ + mlrpcconn_common_header_t *hdr = &mxa->send_hdr.common_hdr; + struct mlndr_stream *mlnds = &mxa->send_mlnds; + unsigned long pdu_size; + unsigned long frag_size; + unsigned long pdu_data_size; + unsigned long frag_data_size; + uint32_t rem_dlen; + uint32_t save_rem_dlen; + uint32_t bytesoff; + uint32_t cnt; + uint32_t obytes; + uint32_t num_ext_frags; + uint16_t last_frag = 0; + uchar_t *frag_startp; + mlrpcconn_common_header_t *rpc_hdr; + + hdr = &mxa->send_hdr.common_hdr; + + frag_size = mlrpc_frag_size; + pdu_size = mlnds->pdu_size; + + if (pdu_size <= frag_size) { + /* + * Single fragment response. The PDU size may be zero + * here (i.e. bind or fault response). So don't make + * any assumptions about it until after the header is + * encoded. + */ + switch (hdr->ptype) { + case MLRPC_PTYPE_BIND_ACK: + mlrpc_reply_bind_ack(mxa); + break; + + case MLRPC_PTYPE_FAULT: + /* already setup */ + break; + + case MLRPC_PTYPE_RESPONSE: + hdr->frag_length = pdu_size; + mxa->send_hdr.response_hdr.alloc_hint = + hdr->frag_length; + break; + + default: + hdr->frag_length = pdu_size; + break; + } + + mlnds->pdu_scan_offset = 0; + (void) mlrpc_encode_pdu_hdr(mxa); + + mlnds->pdu_size_with_rpc_hdrs = mlnds->pdu_size; + mlnds->pdu_base_addr_with_rpc_hdrs = 0; + return (0); + } + + /* + * Multiple fragment response. + */ + hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG; + hdr->frag_length = frag_size; + mxa->send_hdr.response_hdr.alloc_hint = pdu_size - MLRPC_RSP_HDR_SIZE; + mlnds->pdu_scan_offset = 0; + + (void) mlrpc_encode_pdu_hdr(mxa); + + /* + * We need to update the 24-byte header in subsequent fragments. + * + * pdu_data_size: total data remaining to be handled + * frag_size: total fragment size including header + * frag_data_size: data in fragment + * (i.e. frag_size - MLRPC_RSP_HDR_SIZE) + */ + pdu_data_size = pdu_size - MLRPC_RSP_HDR_SIZE; + frag_data_size = frag_size - MLRPC_RSP_HDR_SIZE; + + num_ext_frags = pdu_data_size / frag_data_size; + /* + * if the outpipe is bigger than a frag_size, we need + * to stretch the pipe and insert an RPC header at each + * frag boundary. This outpipe gets chunked out in xdrlen + * sizes for each trans request + */ + mlnds->pdu_base_addr_with_rpc_hdrs + = malloc(pdu_size + (num_ext_frags * MLRPC_RSP_HDR_SIZE)); + mlnds->pdu_size_with_rpc_hdrs = + mlnds->pdu_size + (num_ext_frags * MLRPC_RSP_HDR_SIZE); + + /* + * Start stretching loop. + */ + bcopy(mlnds->pdu_base_addr, + mlnds->pdu_base_addr_with_rpc_hdrs, frag_size); + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + rpc_hdr = (mlrpcconn_common_header_t *) + mlnds->pdu_base_addr_with_rpc_hdrs; + rpc_hdr->pfc_flags = MLRPC_PFC_FIRST_FRAG; + rem_dlen = pdu_data_size - frag_size; + bytesoff = frag_size; + cnt = 1; + while (num_ext_frags--) { + /* first copy the RPC header to the front of the frag */ + bcopy(mlnds->pdu_base_addr, mlnds->pdu_base_addr_with_rpc_hdrs + + (cnt * frag_size), MLRPC_RSP_HDR_SIZE); + + /* then copy the data portion of the frag */ + save_rem_dlen = rem_dlen; + if (rem_dlen >= (frag_size - MLRPC_RSP_HDR_SIZE)) { + rem_dlen = rem_dlen - frag_size + MLRPC_RSP_HDR_SIZE; + obytes = frag_size - MLRPC_RSP_HDR_SIZE; + } else { + last_frag = 1; /* this is the last one */ + obytes = rem_dlen; + } + + frag_startp = mlnds->pdu_base_addr_with_rpc_hdrs + + (cnt * frag_size); + bcopy(mlnds->pdu_base_addr + bytesoff, + frag_startp + MLRPC_RSP_HDR_SIZE, obytes); + + /* set the FRAG FLAGS in the frag header spot */ + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + rpc_hdr = (mlrpcconn_common_header_t *)frag_startp; + if (last_frag) { + rpc_hdr->frag_length = save_rem_dlen; + rpc_hdr->pfc_flags = MLRPC_PFC_LAST_FRAG; + } else { + rpc_hdr->pfc_flags = 0; + } + + bytesoff += (frag_size - MLRPC_RSP_HDR_SIZE); + cnt++; + } + + return (0); +} diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_svc.c b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_svc.c new file mode 100644 index 000000000000..771e54b7e415 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/common/mlrpc_svc.c @@ -0,0 +1,286 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +#include +#include +#include + +#include +#include +#include + +#define NDL_MAX_SERVICES 32 +static struct mlrpc_service *mlrpc_services[NDL_MAX_SERVICES]; + +struct mlrpc_stub_table * +mlrpc_find_stub_in_svc(struct mlrpc_service *msvc, int opnum) +{ + struct mlrpc_stub_table *ste; + + for (ste = msvc->stub_table; ste->func; ste++) { + if (ste->opnum == opnum) + return (ste); + } + + return (NULL); +} + +struct mlrpc_service * +mlrpc_find_service_by_name(const char *name) +{ + struct mlrpc_service *msvc; + int i; + + for (i = 0; i < NDL_MAX_SERVICES; i++) { + if ((msvc = mlrpc_services[i]) == NULL) + continue; + + if (strcasecmp(name, msvc->name) != 0) + continue; + + mlndo_printf(0, 0, "%s %s", msvc->name, msvc->desc); + return (msvc); + } + + return (NULL); +} + +struct mlrpc_service * +mlrpc_find_service_by_uuids(mlrpc_uuid_t *as_uuid, int as_vers, + mlrpc_uuid_t *ts_uuid, int ts_vers) +{ + struct mlrpc_service *msvc; + char abstract_syntax[128]; + char transfer_syntax[128]; + int i; + + if (as_uuid) + mlrpc_uuid_to_str(as_uuid, abstract_syntax); + + if (ts_uuid) + mlrpc_uuid_to_str(ts_uuid, transfer_syntax); + + for (i = 0; i < NDL_MAX_SERVICES; i++) { + if ((msvc = mlrpc_services[i]) == NULL) + continue; + + if (as_uuid) { + if (msvc->abstract_syntax_uuid == 0) + continue; + + if (msvc->abstract_syntax_version != as_vers) + continue; + + if (strcasecmp(abstract_syntax, + msvc->abstract_syntax_uuid)) + continue; + } + + if (ts_uuid) { + if (msvc->transfer_syntax_uuid == 0) + continue; + + if (msvc->transfer_syntax_version != ts_vers) + continue; + + if (strcasecmp(transfer_syntax, + msvc->transfer_syntax_uuid)) + continue; + } + + mlndo_printf(0, 0, "%s %s", msvc->name, msvc->desc); + return (msvc); + } + + return (NULL); +} + +/* + * Register a service. + * + * Returns: + * 0 Success + * -1 Duplicate service + * -2 Duplicate name + * -3 Table overflow + */ +int +mlrpc_register_service(struct mlrpc_service *msvc) +{ + struct mlrpc_service *p; + int free_slot = -1; + int i; + + for (i = 0; i < NDL_MAX_SERVICES; i++) { + if ((p = mlrpc_services[i]) == NULL) { + if (free_slot < 0) + free_slot = i; + continue; + } + + if (p == msvc) + return (-1); + + if (strcasecmp(p->name, msvc->name) == 0) + return (-2); + } + + if (free_slot < 0) + return (-3); + + mlrpc_services[free_slot] = msvc; + return (0); +} + +void +mlrpc_unregister_service(struct mlrpc_service *msvc) +{ + int i; + + for (i = 0; i < NDL_MAX_SERVICES; i++) { + if (mlrpc_services[i] == msvc) + mlrpc_services[i] = NULL; + } +} + +int +mlrpc_list_services(char *buffer, int bufsize) +{ + struct mlrpc_service *msvc; + smb_ctxbuf_t ctx; + int i; + + (void) smb_ctxbuf_init(&ctx, (uint8_t *)buffer, bufsize); + + for (i = 0; i < NDL_MAX_SERVICES; i++) { + if ((msvc = mlrpc_services[i]) != 0) { + (void) smb_ctxbuf_printf(&ctx, "%-16s %s\n", + msvc->name, msvc->desc); + } + } + + return (smb_ctxbuf_len(&ctx)); +} + +void +mlrpc_uuid_to_str(mlrpc_uuid_t *uuid, char *str) +{ + (void) sprintf(str, "%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x", + uuid->data1, uuid->data2, uuid->data3, + uuid->data4[0], uuid->data4[1], + uuid->data4[2], uuid->data4[3], + uuid->data4[4], uuid->data4[5], + uuid->data4[6], uuid->data4[7]); +} + +int +mlrpc_str_to_uuid(char *str, mlrpc_uuid_t *uuid) +{ + char *p = str; + char *q; + char buf[4]; + int i; + + uuid->data1 = strtoul(p, &p, 16); + if (*p != '-') + return (0); + p++; + + uuid->data2 = strtol(p, &p, 16); + if (*p != '-') + return (0); + p++; + + uuid->data3 = strtol(p, &p, 16); + if (*p != '-') + return (0); + p++; + + for (i = 0; i < 8; i++) { + if (p[0] == 0 || p[1] == 0) + return (0); + + buf[0] = *p++; + buf[1] = *p++; + buf[2] = 0; + uuid->data4[i] = strtol(buf, &q, 16); + if (*q != 0) + return (0); + } + + if (*p != 0) + return (0); + + return (1); +} + +void +mlrpc_binding_pool_initialize(struct mlrpc_binding **headpp, + struct mlrpc_binding pool[], unsigned n_pool) +{ + struct mlrpc_binding *head = NULL; + int ix; + + for (ix = n_pool - 1; ix >= 0; ix--) { + pool[ix].next = head; + pool[ix].service = NULL; + pool[ix].p_cont_id = 0xffff; + pool[ix].instance_specific = 0; + head = &pool[ix]; + } + + *headpp = head; +} + +struct mlrpc_binding * +mlrpc_find_binding(struct mlrpc_xaction *mxa, mlrpc_p_context_id_t p_cont_id) +{ + struct mlrpc_binding *mbind; + + for (mbind = mxa->binding_list; mbind; mbind = mbind->next) { + if (mbind->service != NULL && + mbind->which_side == MLRPC_BIND_SIDE_SERVER && + mbind->p_cont_id == p_cont_id) + break; + } + + return (mbind); +} + +struct mlrpc_binding * +mlrpc_new_binding(struct mlrpc_xaction *mxa) +{ + struct mlrpc_binding *mbind; + + for (mbind = mxa->binding_list; mbind; mbind = mbind->next) { + if (mbind->service == NULL) + break; + } + + return (mbind); +} diff --git a/usr/src/lib/smbsrv/libmlrpc/i386/Makefile b/usr/src/lib/smbsrv/libmlrpc/i386/Makefile new file mode 100644 index 000000000000..f91f0270e9ad --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/i386/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/smbsrv/libmlrpc/sparc/Makefile b/usr/src/lib/smbsrv/libmlrpc/sparc/Makefile new file mode 100644 index 000000000000..f91f0270e9ad --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/sparc/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/smbsrv/libmlrpc/sparcv9/Makefile b/usr/src/lib/smbsrv/libmlrpc/sparcv9/Makefile new file mode 100644 index 000000000000..a2f97019c815 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlrpc/sparcv9/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/smbsrv/libmlsvc/Makefile b/usr/src/lib/smbsrv/libmlsvc/Makefile new file mode 100644 index 000000000000..21f844a2c0d1 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +HDRS= libmlsvc.h + +include ../Makefile.smbsrv diff --git a/usr/src/lib/smbsrv/libmlsvc/Makefile.com b/usr/src/lib/smbsrv/libmlsvc/Makefile.com new file mode 100644 index 000000000000..e0c1ee5c4c43 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/Makefile.com @@ -0,0 +1,86 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#pragma ident "%Z%%M% %I% %E% SMI" +# + +LIBRARY = libmlsvc.a +VERS = .1 + +OBJS_COMMON = \ + lsalib.o \ + lsar_lookup.o \ + lsar_open.o \ + mlsvc_client.o \ + mlsvc_dssetup.o \ + mlsvc_handle.o \ + mlsvc_init.o \ + mlsvc_logr.o \ + mlsvc_lsa.o \ + mlsvc_netr.o \ + mlsvc_sam.o \ + mlsvc_srvsvc.o \ + mlsvc_svcctl.o \ + mlsvc_util.o \ + mlsvc_winreg.o \ + mlsvc_wkssvc.o \ + netdfs.o \ + netr_auth.o \ + netr_logon.o \ + samlib.o \ + samr_open.o \ + samr_lookup.o \ + secdb.o \ + srvsvc_client.o \ + lmshare.o \ + smb_share_util.o \ + smb_autohome.o + +# Automatically generated from .ndl files +NDLLIST = \ + dssetup \ + eventlog \ + lsarpc \ + netdfs \ + netlogon \ + samrpc \ + spoolss \ + srvsvc \ + svcctl \ + winreg + +OBJECTS= $(OBJS_COMMON) $(NDLLIST:%=%_ndr.o) + +include ../../../Makefile.lib +include ../../Makefile.lib + +INCS += -I$(SRC)/common/smbsrv + +LDLIBS += -lmlrpc -lsmbrdr -lsmb -lsmbns -lshare -lnsl -lc + +SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \ + $(OBJS_SHARED:%.o=$(SRC)/common/smbsrv/%.c) + +include ../../Makefile.targ +include ../../../Makefile.targ diff --git a/usr/src/lib/smbsrv/libmlsvc/amd64/Makefile b/usr/src/lib/smbsrv/libmlsvc/amd64/Makefile new file mode 100644 index 000000000000..a2f97019c815 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/amd64/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h new file mode 100644 index 000000000000..71cc37af4866 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h @@ -0,0 +1,237 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBMLSVC_H +#define _LIBMLSVC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int mlsvc_init(void); +extern int mlsvc_is_local_domain(const char *); +extern DWORD lsa_query_primary_domain_info(void); +extern DWORD lsa_query_account_domain_info(void); +extern DWORD lsa_enum_trusted_domains(void); + +extern boolean_t locate_resource_pdc(char *); + +#define SMB_AUTOHOME_FILE "smbautohome" +#define SMB_AUTOHOME_PATH "/etc" + +typedef struct smb_autohome { + struct smb_autohome *ah_next; + uint32_t ah_hits; + time_t ah_timestamp; + char *ah_name; /* User account name */ + char *ah_path; /* Home directory path */ + char *ah_container; /* ADS container distinguished name */ +} smb_autohome_t; + +extern int smb_autohome_add(const char *); +extern int smb_autohome_remove(const char *); +extern int smb_is_autohome(const lmshare_info_t *); +extern void smb_autohome_setent(void); +extern void smb_autohome_endent(void); +extern smb_autohome_t *smb_autohome_getent(const char *name); +extern smb_autohome_t *smb_autohome_lookup(const char *name); + +/* + * Local groups + */ +#define NT_GROUP_FMRI_PREFIX "network/smb/group" + +typedef enum { + RWLOCK_NONE, + RWLOCK_WRITER, + RWLOCK_READER +} krwmode_t; + +typedef struct nt_group_data { + void *data; + int size; +} nt_group_data_t; + +/* + * IMPORTANT NOTE: + * If you change nt_group_member_t, nt_group_members_t, or nt_group_t + * structures, you MIGHT have to change following functions accordingly: + * nt_group_setfields + * nt_group_init_size + * nt_group_init + */ +typedef struct nt_group_member { + uint16_t info_size; /* size of the whole structure */ + uint16_t sid_name_use; /* type of the specified SID */ + char *account; /* Pointer to account name of member */ + nt_sid_t sid; /* Variable length */ +} nt_group_member_t; + +typedef struct nt_group_members { + uint32_t size; /* in bytes */ + uint32_t count; + nt_group_member_t list[ANY_SIZE_ARRAY]; +} nt_group_members_t; + +typedef struct nt_group { + time_t age; + nt_group_data_t info; + /* + * following fields point to a contigous block + * of memory that is read and written from/to DB + */ + uint32_t *attr; + uint16_t *sid_name_use; + char *name; + char *comment; + nt_sid_t *sid; + smb_privset_t *privileges; + nt_group_members_t *members; +} nt_group_t; + +typedef struct nt_group_iterator { + HT_ITERATOR *iterator; + int iteration; +} nt_group_iterator_t; + +extern int nt_group_num_groups(void); +extern uint32_t nt_group_add(char *, char *); +extern uint32_t nt_group_modify(char *, char *, char *); +extern uint32_t nt_group_delete(char *); +extern nt_group_t *nt_group_getinfo(char *, krwmode_t); +extern void nt_group_putinfo(nt_group_t *); + +extern int nt_group_getpriv(nt_group_t *, uint32_t); +extern uint32_t nt_group_setpriv(nt_group_t *, uint32_t, uint32_t); + +/* Member manipulation functions */ +extern int nt_group_is_member(nt_group_t *, nt_sid_t *); +extern uint32_t nt_group_del_member(nt_group_t *, void *, int); +extern uint32_t nt_group_add_member(nt_group_t *, nt_sid_t *, uint16_t, char *); +extern int nt_group_num_members(nt_group_t *); + +extern void nt_group_ht_lock(krwmode_t); +extern void nt_group_ht_unlock(void); + +extern nt_group_iterator_t *nt_group_open_iterator(void); +extern void nt_group_close_iterator(nt_group_iterator_t *); +extern nt_group_t *nt_group_iterate(nt_group_iterator_t *); + +extern int nt_group_cache_size(void); + +extern int nt_group_member_list(int offset, nt_group_t *grp, + ntgrp_member_list_t *rmembers); +extern void nt_group_list(int offset, char *pattern, ntgrp_list_t *list); + +extern uint32_t sam_init(void); + +extern uint32_t nt_group_add_member_byname(char *, char *); +extern uint32_t nt_group_del_member_byname(nt_group_t *, char *); +extern void nt_group_add_groupprivs(nt_group_t *, smb_privset_t *); + +extern uint32_t nt_groups_member_privs(nt_sid_t *, smb_privset_t *); +extern int nt_groups_member_ngroups(nt_sid_t *); +extern uint32_t nt_groups_member_groups(nt_sid_t *, smb_id_t *, int); +extern nt_group_t *nt_groups_lookup_rid(uint32_t); +extern int nt_groups_count(int); + +/* + * source for account name size is MSDN + */ +#define NT_GROUP_NAME_CHAR_MAX 32 +#define NT_GROUP_NAME_MAX (NT_GROUP_NAME_CHAR_MAX * 3 + 1) +#define NT_GROUP_USER_NAME_MAX (NT_GROUP_NAME_CHAR_MAX * 3 + 1) +#define NT_GROUP_MEMBER_NAME_MAX (NT_GROUP_NAME_CHAR_MAX * 3 + 1) +#define NT_GROUP_COMMENT_MAX 256 + +/* + * flags for count operation + */ +#define NT_GROUP_CNT_BUILTIN 1 +#define NT_GROUP_CNT_LOCAL 2 +#define NT_GROUP_CNT_ALL 3 + +/* + * flag to distinguish between add and modify + * operations. + */ +#define NT_GROUP_OP_CHANGE 1 +#define NT_GROUP_OP_SYNC 2 + +/* + * specify key type for deleting a member i.e. + * whether it's member's name or member's SID. + */ +#define NT_GROUP_KEY_SID 1 +#define NT_GROUP_KEY_NAME 2 + +/* Macro for walking members */ +#define NEXT_MEMBER(m) (nt_group_member_t *)((char *)(m) + (m)->info_size) + +/* + * When NT requests the security descriptor for a local file that + * doesn't already have a one, we generate one on-the-fly. The SD + * contains both user and group SIDs. The problem is that we need a + * way to distinguish a user SID from a group SID when NT performs a + * subsequent SID lookup to obtain the appropriate name to display. + * The following macros are used to map to and from an external + * representation so that we can tell the difference between UIDs + * and GIDs. The local UID/GID is shifted left and the LSB is used + * to distinguish the id type before it is inserted into the SID. + * We can then use this type identifier during lookup operations. + */ +#define SAM_MIN_RID 1000 +#define SAM_RT_ERROR -1 +#define SAM_RT_UNIX_UID 0 +#define SAM_RT_UNIX_GID 1 +#define SAM_RT_NT_UID 2 +#define SAM_RT_NT_GID 3 +#define SAM_RT_MASK 0x3 +#define SAM_RT_EVERYONE 4 +#define SAM_RT_UNKNOWN 5 + +#define SAM_RID_TYPE(rid) ((rid) & SAM_RT_MASK) +#define SAM_DECODE_RID(rid) (((rid) - SAM_MIN_RID) >> 2) +#define SAM_ENCODE_RID(type, id) ((((id) << 2) | type) + SAM_MIN_RID) +#define SAM_ENCODE_UXUID(id) SAM_ENCODE_RID(SAM_RT_UNIX_UID, id) +#define SAM_ENCODE_UXGID(id) SAM_ENCODE_RID(SAM_RT_UNIX_GID, id) +#define SAM_ENCODE_NTUID(id) SAM_ENCODE_RID(SAM_RT_NT_UID, id) +#define SAM_ENCODE_NTGID(id) SAM_ENCODE_RID(SAM_RT_NT_GID, id) + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBMLSVC_H */ diff --git a/usr/src/lib/smbsrv/libmlsvc/common/llib-lmlsvc b/usr/src/lib/smbsrv/libmlsvc/common/llib-lmlsvc new file mode 100644 index 000000000000..412e13d740b5 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/llib-lmlsvc @@ -0,0 +1,31 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/*LINTLIBRARY*/ +/*PROTOLIB1*/ + +#include diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lmshare.c b/usr/src/lib/smbsrv/libmlsvc/common/lmshare.c new file mode 100644 index 000000000000..5fb02b71f491 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/lmshare.c @@ -0,0 +1,1233 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Lan Manager (SMB/CIFS) share interface implementation. This interface + * returns Win32 error codes, usually network error values (lmerr.h). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#define LMSHR_HASHTAB_SZ 1024 + +static HT_HANDLE *lmshare_handle = NULL; + +static rwlock_t lmshare_lock; +static pthread_t lmshare_load_thread; +static void *lmshare_load(void *); +static DWORD lmshare_create_table(); +static int lmshare_delete_shmgr(struct lmshare_info *); +static int lmshare_setinfo_shmgr(struct lmshare_info *); +static DWORD lmshare_set_refcnt(char *share_name, int refcnt); + +typedef struct lmshare_ad_item { + TAILQ_ENTRY(lmshare_ad_item) next; + char name[MAXNAMELEN]; + char container[MAXPATHLEN]; + char flag; +} lmshare_ad_item_t; + +typedef struct lmshare_ad_queue { + int nentries; + TAILQ_HEAD(adqueue, lmshare_ad_item) adlist; +} lmshare_ad_queue_t; + +static lmshare_ad_queue_t ad_queue; +static int publish_on = 0; + +static pthread_t lmshare_publish_thread; +static mutex_t lmshare_publish_mutex = PTHREAD_MUTEX_INITIALIZER; +static cond_t lmshare_publish_cv = DEFAULTCV; + +static void *lmshare_publisher(void *); +static void lmshare_stop_publish(); + +/* + * Start loading lmshare information from sharemanager + * and create the cache. + */ +int +lmshare_start() +{ + int rc; + + rc = pthread_create(&lmshare_publish_thread, NULL, + lmshare_publisher, 0); + if (rc != 0) { + syslog(LOG_ERR, "Failed to start publisher thread, " + "share publishing is disabled"); + } + + rc = pthread_create(&lmshare_load_thread, NULL, + lmshare_load, 0); + if (rc != 0) { + syslog(LOG_ERR, "Failed to start share loading, " + "existing shares will not be available"); + } + + return (rc); +} + +void +lmshare_stop() +{ + lmshare_stop_publish(); +} + +/* + * Load shares from sharemanager. + */ +/*ARGSUSED*/ +static void * +lmshare_load(void *args) +{ + lmshare_info_t si; + sa_handle_t handle; + sa_group_t group; + sa_share_t share; + sa_resource_t resource; + sa_optionset_t opts; + char *gstate, *path, *rname; + + if (lmshare_create_table() != NERR_Success) { + syslog(LOG_ERR, "Failed to create share hash table"); + return (NULL); + } + + handle = sa_init(SA_INIT_SHARE_API); + if (handle == NULL) { + syslog(LOG_ERR, "Failed to load share " + "information: no libshare handle"); + return (NULL); + } + + for (group = sa_get_group(handle, NULL); + group != NULL; group = sa_get_next_group(group)) { + gstate = sa_get_group_attr(group, "state"); + if ((gstate == NULL) || + (strcasecmp(gstate, "disabled") == 0)) { + /* Skip disabled or unknown state group */ + continue; + } + /* Check and see if smb protocol is available */ + if (sa_get_optionset(group, SMB_PROTOCOL_NAME) == NULL) + continue; + for (share = sa_get_share(group, NULL); + share != NULL; share = sa_get_next_share(share)) { + path = sa_get_share_attr(share, "path"); + if (path == NULL) { + syslog(LOG_ERR, "Invalid share NO path"); + continue; + } + for (resource = sa_get_share_resource(share, NULL); + resource != NULL; + resource = sa_get_next_resource(resource)) { + rname = sa_get_resource_attr(resource, "name"); + if (rname == NULL) { + syslog(LOG_ERR, "Invalid share " + "resource for path: %s", path); + continue; + } + opts = sa_get_derived_optionset(resource, + SMB_PROTOCOL_NAME, 1); + smb_build_lmshare_info(rname, path, opts, &si); + sa_free_derived_optionset(opts); + (void) free(rname); + if (lmshare_add(&si, 0) != NERR_Success) { + syslog(LOG_ERR, "Failed to load " + "share %s", si.share_name); + } + } + /* We are done with all shares for same path */ + (void) free(path); + } + } + + sa_fini(handle); + + return (NULL); +} + +/* + * lmshare_callback + * + * Call back to free share structures stored + * in shares' hash table. + */ +static void +lmshare_callback(HT_ITEM *item) +{ + if (item && item->hi_data) + (void) free(item->hi_data); +} + +/* + * lmshare_create_table + * + * Create the share hash table. + */ +static DWORD +lmshare_create_table(void) +{ + if (lmshare_handle == NULL) { + (void) rwlock_init(&lmshare_lock, USYNC_THREAD, 0); + (void) rw_wrlock(&lmshare_lock); + + lmshare_handle = ht_create_table(LMSHR_HASHTAB_SZ, + MAXNAMELEN, 0); + if (lmshare_handle == NULL) { + syslog(LOG_ERR, "lmshare_create_table:" + " unable to create share table"); + (void) rw_unlock(&lmshare_lock); + return (NERR_InternalError); + } + (void) ht_register_callback(lmshare_handle, lmshare_callback); + (void) rw_unlock(&lmshare_lock); + } + + return (NERR_Success); +} + +/* + * lmshare_add_adminshare + * + * add the admin share for the volume when the share database + * for that volume is going to be loaded. + */ +DWORD +lmshare_add_adminshare(char *volname, unsigned char drive) +{ + struct lmshare_info si; + DWORD rc; + + if (drive == 0) + return (NERR_InvalidDevice); + + bzero(&si, sizeof (lmshare_info_t)); + (void) strcpy(si.directory, volname); + si.mode = LMSHRM_TRANS; + (void) snprintf(si.share_name, sizeof (si.share_name), "%c$", drive); + rc = lmshare_add(&si, 0); + + return (rc); +} + +/* + * lmshare_num_shares + * + * Return the total number of shares, which should be the same value + * that would be returned from a share enum request. + */ +int +lmshare_num_shares(void) +{ + int n_shares; + + n_shares = ht_get_total_items(lmshare_handle); + + /* If we don't store IPC$ in hash table we should do this */ + n_shares++; + + return (n_shares); +} + +/* + * lmshare_open_iterator + * + * Create and initialize an iterator for traversing hash table. + * It gets a mode that can be LMSHR_IM_ALL to iterate through all + * the shares stored in table or LMSHR_IM_PRES to iterate through + * only presentable shares. + * + * It also accepts a local IP address. This is used in dual head + * systems to only return the shares that belong to the head which + * is specified by the 'ipaddr'. If ipaddr is 0 it'll return shares + * of both heads. + * + * On success return pointer to the new iterator. + * On failure return (NULL). + */ +lmshare_iterator_t * +lmshare_open_iterator(int mode) +{ + lmshare_iterator_t *shi; + int sz = sizeof (lmshare_iterator_t) + sizeof (HT_ITERATOR); + + shi = malloc(sz); + if (shi != NULL) { + bzero(shi, sz); + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + shi->iterator = (HT_ITERATOR *) + ((char *)shi + sizeof (lmshare_iterator_t)); + shi->mode = mode; + } else { + syslog(LOG_DEBUG, "Failed to create share iterator handle"); + } + return (shi); +} + +/* + * lmshare_close_iterator + * + * Free memory allocated by the given iterator. + */ +void +lmshare_close_iterator(lmshare_iterator_t *shi) +{ + (void) free(shi); +} + +/* + * lmshare_iterate + * + * Iterate on the shares in the hash table. The iterator must be opened + * before the first iteration. On subsequent calls, the iterator must be + * passed unchanged. + * + * Returns NULL on failure or when all shares are visited, otherwise + * returns information of visited share. + * + * Note that there are some special shares, i.e. IPC$, that must also + * be processed. + */ +lmshare_info_t * +lmshare_iterate(lmshare_iterator_t *shi) +{ + HT_ITEM *item; + lmshare_info_t *si; + + if (lmshare_handle == NULL || shi == NULL) + return (NULL); + + if (shi->iteration == 0) { + /* + * IPC$ is always first. + */ + (void) strcpy(shi->si.share_name, "IPC$"); + shi->si.mode = LMSHRM_TRANS; + shi->si.stype = (int)(STYPE_IPC | STYPE_SPECIAL); + shi->iteration = 1; + return (&(shi->si)); + } + + if (shi->iteration == 1) { + if ((item = ht_findfirst( + lmshare_handle, shi->iterator)) == NULL) { + return (NULL); + } + + si = (lmshare_info_t *)(item->hi_data); + ++shi->iteration; + + if (si->mode & shi->mode) { + (void) memcpy(&(shi->si), si, + sizeof (lmshare_info_t)); + return (&(shi->si)); + } + } + + while ((item = ht_findnext(shi->iterator)) != NULL) { + si = (lmshare_info_t *)(item->hi_data); + ++shi->iteration; + if (si->mode & shi->mode) { + (void) memcpy(&(shi->si), si, sizeof (lmshare_info_t)); + + return (&(shi->si)); + } + } + + return (NULL); +} + +/* + * lmshare_add + * + * Add a share. This is a wrapper round lmshare_setinfo that checks + * whether or not the share already exists. If the share exists, an + * error is returned. + * + * Don't check lmshare_is_dir here: it causes rootfs to recurse. + */ +DWORD +lmshare_add(lmshare_info_t *si, int doshm) +{ + DWORD status = NERR_Success; + + if (si == 0 || lmshare_is_valid(si->share_name) == 0) + return (NERR_InvalidDevice); + + (void) utf8_strlwr(si->share_name); + + if (lmshare_exists(si->share_name)) { + if ((si->mode & LMSHRM_TRANS) == 0) + return (NERR_DuplicateShare); + } + + if (si->refcnt == 0) { + status = lmshare_setinfo(si, doshm); + lmshare_do_publish(si, LMSHR_PUBLISH, 1); + } + + if ((si->mode & LMSHRM_TRANS) && (status == NERR_Success)) { + si->refcnt++; + status = lmshare_set_refcnt(si->share_name, si->refcnt); + } + + if (status) + return (status); + + return (smb_dwncall_share(LMSHR_ADD, si->directory, si->share_name)); +} + +/* + * lmshare_delete + * + * Remove a share. Ensure that all SMB trees associated with this share + * are disconnected. If the share does not exist, an error is returned. + */ +DWORD +lmshare_delete(char *share_name, int doshm) +{ + lmshare_info_t *si; + HT_ITEM *item; + DWORD status; + char path[MAXPATHLEN]; + + if (share_name) + (void) utf8_strlwr(share_name); + + if (lmshare_is_valid(share_name) == 0 || + lmshare_exists(share_name) == 0) { + return (NERR_NetNameNotFound); + } + + (void) rw_wrlock(&lmshare_lock); + item = ht_find_item(lmshare_handle, share_name); + + if (item == NULL) { + (void) rw_unlock(&lmshare_lock); + return (NERR_ItemNotFound); + } + + si = (lmshare_info_t *)item->hi_data; + if (si == NULL) { + (void) rw_unlock(&lmshare_lock); + return (NERR_InternalError); + } + + if ((si->mode & LMSHRM_TRANS) != 0) { + si->refcnt--; + if (si->refcnt > 0) { + status = lmshare_set_refcnt(si->share_name, si->refcnt); + (void) rw_unlock(&lmshare_lock); + return (status); + } + } + + if (doshm && (lmshare_delete_shmgr(si) != 0)) { + (void) rw_unlock(&lmshare_lock); + return (NERR_InternalError); + } + + lmshare_do_publish(si, LMSHR_UNPUBLISH, 1); + + /* + * Copy the path before the entry is removed from the hash table + */ + + (void) strlcpy(path, si->directory, MAXPATHLEN); + + /* Delete from hash table */ + + (void) ht_remove_item(lmshare_handle, share_name); + (void) rw_unlock(&lmshare_lock); + + return (smb_dwncall_share(LMSHR_DELETE, path, share_name)); +} + +/* + * lmshare_set_refcnt + * + * sets the autohome refcnt for a share + */ +static DWORD +lmshare_set_refcnt(char *share_name, int refcnt) +{ + lmshare_info_t *si; + HT_ITEM *item; + + if (share_name) { + (void) utf8_strlwr(share_name); + } + (void) rw_wrlock(&lmshare_lock); + item = ht_find_item(lmshare_handle, share_name); + if (item == NULL) { + (void) rw_unlock(&lmshare_lock); + return (NERR_ItemNotFound); + } + + si = (lmshare_info_t *)item->hi_data; + if (si == NULL) { + (void) rw_unlock(&lmshare_lock); + return (NERR_InternalError); + } + si->refcnt = refcnt; + (void) rw_unlock(&lmshare_lock); + return (NERR_Success); +} + +/* + * lmshare_rename + * + * Rename a share. Check that the current name exists and the new name + * doesn't exist. The rename is performed by deleting the current share + * definition and creating a new share with the new name. + */ +DWORD +lmshare_rename(char *from_name, char *to_name, int doshm) +{ + struct lmshare_info si; + DWORD nerr; + + if (lmshare_is_valid(from_name) == 0 || + lmshare_is_valid(to_name) == 0) + return (NERR_InvalidDevice); + + (void) utf8_strlwr(from_name); + (void) utf8_strlwr(to_name); + + if (lmshare_exists(from_name) == 0) + return (NERR_NetNameNotFound); + + if (lmshare_exists(to_name)) + return (NERR_DuplicateShare); + + if ((nerr = lmshare_getinfo(from_name, &si)) != NERR_Success) + return (nerr); + + if ((nerr = lmshare_delete(from_name, doshm)) != NERR_Success) + return (nerr); + + (void) strlcpy(si.share_name, to_name, MAXNAMELEN); + return (lmshare_add(&si, 1)); +} + +/* + * lmshare_exists + * + * Returns 1 if the share exists. Otherwise returns 0. + */ +int +lmshare_exists(char *share_name) +{ + if (share_name == 0 || *share_name == 0) + return (0); + + if (ht_find_item(lmshare_handle, share_name) == NULL) + return (0); + else + return (1); +} + +/* + * lmshare_is_special + * + * Simple check to determine if share name represents a special share, + * i.e. the last character of the name is a '$'. Returns STYPE_SPECIAL + * if the name is special. Otherwise returns 0. + */ +int +lmshare_is_special(char *share_name) +{ + int len; + + if (share_name == 0) + return (0); + + if ((len = strlen(share_name)) == 0) + return (0); + + if (share_name[len - 1] == '$') + return (STYPE_SPECIAL); + else + return (0); +} + + +/* + * lmshare_is_restricted + * + * Check whether or not there is a restriction on a share. Restricted + * shares are generally STYPE_SPECIAL, for example, IPC$. All the + * administration share names are restricted: C$, D$ etc. Returns 1 + * if the share is restricted. Otherwise 0 is returned to indicate + * that there are no restrictions. + */ +int +lmshare_is_restricted(char *share_name) +{ + static char *restricted[] = { + "IPC$" + }; + + int i; + + for (i = 0; i < sizeof (restricted)/sizeof (restricted[0]); i++) { + if (strcasecmp(restricted[i], share_name) == 0) + return (1); + } + + if (lmshare_is_admin(share_name)) + return (1); + + return (0); +} + + +/* + * lmshare_is_admin + * + * Check whether or not access to the share should be restricted to + * administrators. This is a bit of a hack because what we're doing + * is checking for the default admin shares: C$, D$ etc.. There are + * other shares that have restrictions: see lmshare_is_restricted(). + * + * Returns 1 if the shares is an admin share. Otherwise 0 is returned + * to indicate that there are no restrictions. + */ +int +lmshare_is_admin(char *share_name) +{ + if (share_name == 0) + return (0); + + if (strlen(share_name) == 2 && + mts_isalpha(share_name[0]) && share_name[1] == '$') { + return (1); + } + + return (0); +} + + +/* + * lmshare_is_valid + * + * Check if any invalid char is present in share name. According to + * MSDN article #236388: "Err Msg: The Share Name Contains Invalid + * Characters", the list of invalid character is: + * + * " / \ [ ] : | < > + ; , ? * = + * + * Also rejects if control characters are embedded. + * + * If the sharename is valid, return (1). Otherwise return (0). + */ +int +lmshare_is_valid(char *share_name) +{ + char *invalid = "\"/\\[]:|<>+;,?*="; + char *cp; + + if (share_name == 0) + return (0); + + if (strpbrk(share_name, invalid)) + return (0); + + for (cp = share_name; *cp != '\0'; cp++) + if (iscntrl(*cp)) + return (0); + + return (1); +} + +/* + * lmshare_is_dir + * + * Check to determine if a share object represents a directory. + * + * Returns 1 if the path leads to a directory. Otherwise returns 0. + */ +int +lmshare_is_dir(char *path) +{ + struct stat stat_info; + + if (stat(path, &stat_info) == 0) + if (S_ISDIR(stat_info.st_mode)) + return (1); + + return (0); + +} + +/* + * lmshare_getinfo + * + * Load the information for the specified share into the supplied share + * info structure. If the shared directory does not begin with a /, one + * will be inserted as a prefix. + */ +DWORD +lmshare_getinfo(char *share_name, struct lmshare_info *si) +{ + int i, endidx; + int dirlen; + HT_ITEM *item; + + (void) rw_rdlock(&lmshare_lock); + + (void) utf8_strlwr(share_name); + if ((item = ht_find_item(lmshare_handle, share_name)) == NULL) { + bzero(si, sizeof (lmshare_info_t)); + (void) rw_unlock(&lmshare_lock); + return (NERR_NetNameNotFound); + } + + (void) memcpy(si, item->hi_data, sizeof (lmshare_info_t)); + (void) rw_unlock(&lmshare_lock); + + if (si->directory[0] == '\0') + return (NERR_NetNameNotFound); + + if (si->directory[0] != '/') { + dirlen = strlen(si->directory) + 1; + endidx = (dirlen < MAXPATHLEN-1) ? + dirlen : MAXPATHLEN - 2; + for (i = endidx; i >= 0; i--) + si->directory[i+1] = si->directory[i]; + si->directory[MAXPATHLEN-1] = '\0'; + si->directory[0] = '/'; + } + + return (NERR_Success); +} + +/* + * Remove share from sharemanager repository. + */ +static int +lmshare_delete_shmgr(struct lmshare_info *si) +{ + sa_handle_t handle; + sa_share_t share; + sa_resource_t resource; + + handle = sa_init(SA_INIT_SHARE_API); + if (handle == NULL) { + syslog(LOG_ERR, "Failed to get handle to " + "share lib"); + return (1); + } + share = sa_find_share(handle, si->directory); + if (share == NULL) { + syslog(LOG_ERR, "Failed to get share to delete"); + sa_fini(handle); + return (1); + } + resource = sa_get_share_resource(share, si->share_name); + if (resource == NULL) { + syslog(LOG_ERR, "Failed to get share resource to delete"); + sa_fini(handle); + return (1); + } + if (sa_remove_resource(resource) != SA_OK) { + syslog(LOG_ERR, "Failed to remove resource"); + sa_fini(handle); + return (1); + } + sa_fini(handle); + return (0); +} + +static int +lmshare_setinfo_shmgr(struct lmshare_info *si) +{ + sa_handle_t handle; + sa_share_t share; + sa_group_t group; + sa_resource_t resource; + int share_created = 0; + int err; + + /* Add share to sharemanager */ + handle = sa_init(SA_INIT_SHARE_API); + if (handle == NULL) { + syslog(LOG_ERR, "Failed to get handle to share lib"); + return (1); + } + share = sa_find_share(handle, si->directory); + if (share == NULL) { + group = smb_get_smb_share_group(handle); + if (group == NULL) { + sa_fini(handle); + return (1); + } + share = sa_add_share(group, si->directory, 0, &err); + if (share == NULL) { + sa_fini(handle); + return (1); + } + share_created = 1; + } + resource = sa_get_share_resource(share, si->share_name); + if (resource == NULL) { + resource = sa_add_resource(share, si->share_name, + SA_SHARE_PERMANENT, &err); + if (resource == NULL) { + goto failure; + } + } + if (sa_set_resource_attr(resource, + "description", si->comment) != SA_OK) { + syslog(LOG_ERR, "Falied to set resource " + "description in sharemgr"); + goto failure; + } + if (sa_set_resource_attr(resource, + SHOPT_AD_CONTAINER, si->container) != SA_OK) { + syslog(LOG_ERR, "Falied to set ad-container in sharemgr"); + goto failure; + } + + sa_fini(handle); + return (0); +failure: + if (share_created && (share != NULL)) { + if (sa_remove_share(share) != SA_OK) { + syslog(LOG_ERR, "Failed to cleanup share"); + } + } + if (resource != NULL) { + if (sa_remove_resource(resource) != SA_OK) { + syslog(LOG_ERR, "Failed to cleanup share resource"); + } + } + sa_fini(handle); + return (1); +} + +/* + * del_fromhash + * + * Delete the given share only from hash table + */ +static DWORD +del_fromhash(char *share_name) +{ + if (share_name == 0) + return (NERR_NetNameNotFound); + + (void) utf8_strlwr(share_name); + + if (lmshare_is_valid(share_name) == 0 || + lmshare_exists(share_name) == 0) { + return (NERR_NetNameNotFound); + } + + (void) rw_wrlock(&lmshare_lock); + (void) ht_remove_item(lmshare_handle, share_name); + (void) rw_unlock(&lmshare_lock); + + return (NERR_Success); +} + +/* + * lmshare_setinfo + * + * Adds the specified share into the system hash table + * and also store its info in the corresponding disk + * structure if it is not a temporary (LMSHRM_TRANS) share. + * when the first share is going to be added, create shares + * hash table if it is not already created. + * If the share already exists, it will be replaced. If the + * new share directory name does not begin with a /, one will be + * inserted as a prefix. + */ +DWORD +lmshare_setinfo(lmshare_info_t *si, int doshm) +{ + int i, endidx; + int dirlen; + lmshare_info_t *add_si; + int res = NERR_Success; + lmshare_info_t old_si; + + if (si->directory[0] != '/') { + dirlen = strlen(si->directory) + 1; + endidx = (dirlen < MAXPATHLEN - 1) ? + dirlen : MAXPATHLEN - 2; + for (i = endidx; i >= 0; i--) + si->directory[i+1] = si->directory[i]; + si->directory[MAXPATHLEN-1] = '\0'; + si->directory[0] = '/'; + } + + /* XXX Do we need to translate the directory here? to real path */ + if (lmshare_is_dir(si->directory) == 0) + return (NERR_UnknownDevDir); + + /* + * We should allocate memory for new entry because we + * don't know anything about the passed pointer i.e. + * it maybe destroyed by caller of this function while + * we only store a pointer to the data in hash table. + * Hash table doesn't do any allocation for the data that + * is being added. + */ + add_si = malloc(sizeof (lmshare_info_t)); + if (add_si == NULL) { + syslog(LOG_ERR, "LmshareSetinfo: resource shortage"); + return (NERR_NoRoom); + } + + (void) memcpy(add_si, si, sizeof (lmshare_info_t)); + + /* + * If we can't find it, use the new one to get things in sync, + * but if there is an existing one, that is the one to + * unpublish. + */ + if (lmshare_getinfo(si->share_name, &old_si) != NERR_Success) + (void) memcpy(&old_si, si, sizeof (lmshare_info_t)); + + if (doshm) { + res = lmshare_delete(si->share_name, doshm); + if (res != NERR_Success) { + free(add_si); + syslog(LOG_ERR, "LmshareSetinfo: delete failed", res); + return (res); + } + } else { + /* Unpublish old share from AD */ + if ((si->mode & LMSHRM_TRANS) == 0) { + lmshare_do_publish(&old_si, LMSHR_UNPUBLISH, 1); + } + (void) del_fromhash(si->share_name); + } + /* if it's not transient it should be permanent */ + if ((add_si->mode & LMSHRM_TRANS) == 0) + add_si->mode |= LMSHRM_PERM; + + + add_si->stype = STYPE_DISKTREE; + add_si->stype |= lmshare_is_special(add_si->share_name); + + (void) rw_wrlock(&lmshare_lock); + if (ht_add_item(lmshare_handle, add_si->share_name, add_si) == NULL) { + syslog(LOG_ERR, "lmshare_setinfo[%s]: error in adding share", + add_si->share_name); + (void) rw_unlock(&lmshare_lock); + free(add_si); + return (NERR_InternalError); + } + (void) rw_unlock(&lmshare_lock); + + if ((add_si->mode & LMSHRM_TRANS) == 0) { + if (doshm && (lmshare_setinfo_shmgr(add_si) != 0)) { + syslog(LOG_ERR, "Update share %s in sharemgr failed", + add_si->share_name); + return (NERR_InternalError); + } + lmshare_do_publish(add_si, LMSHR_PUBLISH, 1); + } + + return (res); +} + +/* + * lmshare_decode_type + * + * Gets a SMB share type as an integer value and return + * a string name for it. + */ +static char * +lmshare_decode_type(uint_t stype) +{ + switch (stype) { + case STYPE_DISKTREE: + return ("Disk"); + case STYPE_PRINTQ: + return ("Print Queue"); + case STYPE_DEVICE: + return ("Device"); + case STYPE_IPC: + return ("IPC"); + case STYPE_DFS: + return ("DFS"); + case STYPE_SPECIAL: + return ("Special"); + default: + return ("Unknown"); + /* NOTREACHED */ + }; +} + +/* + * lmshare_loginfo + * + * Decodes and writes the information of the given + * share to the specified file. + */ +void +lmshare_loginfo(FILE *fp, lmshare_info_t *si) +{ + (void) fprintf(fp, "\n%s Information:\n", si->share_name); + (void) fprintf(fp, "\tFolder: %s\n", si->directory); + (void) fprintf(fp, "\tType: %s\n", + lmshare_decode_type((uint_t)si->stype)); + (void) fprintf(fp, "\tComment: %s\n", si->comment); + + (void) fprintf(fp, "\tStatus: %s\n", + ((si->mode & LMSHRM_TRANS) ? "Transient" : "Permanent")); + + (void) fprintf(fp, "\tContainer: %s\n", si->container); +} + +DWORD +lmshare_list(int offset, lmshare_list_t *list) +{ + lmshare_iterator_t *iterator; + lmshare_info_t *si; + int list_idx = 0; + int i = 0; + + bzero(list, sizeof (lmshare_list_t)); + if ((iterator = lmshare_open_iterator(LMSHRM_ALL)) == NULL) + return (NERR_InternalError); + + (void) lmshare_iterate(iterator); /* To skip IPC$ */ + + while ((si = lmshare_iterate(iterator)) != NULL) { + if (lmshare_is_special(si->share_name)) { + /* + * Don't return restricted shares. + */ + if (lmshare_is_restricted(si->share_name)) + continue; + } + + if (i++ < offset) + continue; + + (void) memcpy(&list->smbshr[list_idx], si, + sizeof (lmshare_info_t)); + if (++list_idx == LMSHARES_PER_REQUEST) + break; + } + lmshare_close_iterator(iterator); + + list->no = list_idx; + + return (NERR_Success); +} + +/* + * Put the share on publish queue. + */ +void +lmshare_do_publish(lmshare_info_t *si, char flag, int poke) +{ + lmshare_ad_item_t *item = NULL; + + if (publish_on == 0) + return; + if ((si == NULL) || (si->container[0] == '\0')) + return; + (void) mutex_lock(&lmshare_publish_mutex); + item = (lmshare_ad_item_t *)malloc(sizeof (lmshare_ad_item_t)); + if (item == NULL) { + syslog(LOG_ERR, "Failed to allocate share publish item"); + (void) mutex_unlock(&lmshare_publish_mutex); + return; + } + item->flag = flag; + (void) strlcpy(item->name, si->share_name, sizeof (item->name)); + (void) strlcpy(item->container, si->container, + sizeof (item->container)); + /*LINTED - E_CONSTANT_CONDITION*/ + TAILQ_INSERT_TAIL(&ad_queue.adlist, item, next); + ad_queue.nentries++; + if (poke) + (void) cond_signal(&lmshare_publish_cv); + (void) mutex_unlock(&lmshare_publish_mutex); +} + +void +lmshare_stop_publish() +{ + (void) mutex_lock(&lmshare_publish_mutex); + publish_on = 0; + (void) cond_signal(&lmshare_publish_cv); + (void) mutex_unlock(&lmshare_publish_mutex); +} + +/* + * This functions waits to be signaled and once running + * will publish/unpublish any items on list. + * lmshare_stop_publish when called will exit this thread. + */ +/*ARGSUSED*/ +static void * +lmshare_publisher(void *arg) +{ + ADS_HANDLE *ah; + lmshare_ad_item_t *item; + char hostname[MAXHOSTNAMELEN]; + char name[MAXNAMELEN]; + char container[MAXPATHLEN]; + char flag; + + /*LINTED - E_CONSTANT_CONDITION*/ + TAILQ_INIT(&ad_queue.adlist); + ad_queue.nentries = 0; + publish_on = 1; + hostname[0] = '\0'; + + for (;;) { + (void) cond_wait(&lmshare_publish_cv, + &lmshare_publish_mutex); + + if (hostname[0] == '\0') { + if (smb_gethostname(hostname, MAXHOSTNAMELEN, 0) != 0) + continue; + } + + if (publish_on == 0) { + syslog(LOG_DEBUG, "lmshare: publisher exit"); + if (ad_queue.nentries == 0) { + (void) mutex_unlock(&lmshare_publish_mutex); + break; + } + for (item = TAILQ_FIRST(&ad_queue.adlist); item; + item = TAILQ_FIRST(&ad_queue.adlist)) { + /*LINTED - E_CONSTANT_CONDITION*/ + TAILQ_REMOVE(&ad_queue.adlist, item, next); + (void) free(item); + } + ad_queue.nentries = 0; + (void) mutex_unlock(&lmshare_publish_mutex); + break; + } + if (ad_queue.nentries == 0) + continue; + ah = ads_open(); + if (ah == NULL) { + /* We mostly have no AD config so just clear the list */ + for (item = TAILQ_FIRST(&ad_queue.adlist); item; + item = TAILQ_FIRST(&ad_queue.adlist)) { + /*LINTED - E_CONSTANT_CONDITION*/ + TAILQ_REMOVE(&ad_queue.adlist, item, next); + (void) free(item); + } + ad_queue.nentries = 0; + continue; + } + TAILQ_FOREACH(item, &ad_queue.adlist, next) { + (void) strlcpy(name, item->name, sizeof (name)); + (void) strlcpy(container, item->container, + sizeof (container)); + flag = item->flag; + + if (flag == LMSHR_UNPUBLISH) + (void) ads_remove_share(ah, name, NULL, + container, hostname); + else + (void) ads_publish_share(ah, name, NULL, + container, hostname); + } + for (item = TAILQ_FIRST(&ad_queue.adlist); item; + item = TAILQ_FIRST(&ad_queue.adlist)) { + /*LINTED - E_CONSTANT_CONDITION*/ + TAILQ_REMOVE(&ad_queue.adlist, item, next); + (void) free(item); + } + ad_queue.nentries = 0; + if (ah != NULL) { + ads_close(ah); + ah = NULL; + } + } + + syslog(LOG_DEBUG, "lmshare: Stopping publisher"); + return (NULL); +} + +/* + * lmshare_get_realpath + * + * Derive the real path of a share from the path provided by a + * Windows client application during the share addition. + * + * For instance, the real path of C:\ is /cvol and the + * real path of F:\home is /vol1/home. + * + * clipath - path provided by the Windows client is in the + * format of :\

+ * realpath - path that will be stored as the directory field of + * the lmshare_info_t structure of the share. + * maxlen - maximum length fo the realpath buffer + * + * Return LAN Manager network error code. + */ +/*ARGSUSED*/ +DWORD +lmshare_get_realpath(const char *clipath, char *realpath, int maxlen) +{ + /* XXX do this translation */ + return (NERR_Success); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c b/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c new file mode 100644 index 000000000000..ca98eb8eabb2 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c @@ -0,0 +1,637 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module provides the high level interface to the LSA RPC functions. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static int lsa_list_accounts(mlsvc_handle_t *); + +/* + * lsa_query_primary_domain_info + * + * Obtains the primary domain SID and name from the specified server + * (domain controller). The information is stored in the NT domain + * database by the lower level lsar_query_info_policy call. The caller + * should query the database to obtain a reference to the primary + * domain information. + * + * Returns NT status codes. + */ +DWORD +lsa_query_primary_domain_info(void) +{ + mlsvc_handle_t domain_handle; + DWORD status; + + if ((lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle)) != 0) + return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + + status = lsar_query_info_policy(&domain_handle, + MSLSA_POLICY_PRIMARY_DOMAIN_INFO); + + (void) lsar_close(&domain_handle); + return (status); +} + +/* + * lsa_query_account_domain_info + * + * Obtains the account domain SID and name from the current server + * (domain controller). The information is stored in the NT domain + * database by the lower level lsar_query_info_policy call. The caller + * should query the database to obtain a reference to the account + * domain information. + * + * Returns NT status codes. + */ +DWORD +lsa_query_account_domain_info(void) +{ + mlsvc_handle_t domain_handle; + DWORD status; + + if ((lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle)) != 0) + return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + + status = lsar_query_info_policy(&domain_handle, + MSLSA_POLICY_ACCOUNT_DOMAIN_INFO); + + (void) lsar_close(&domain_handle); + return (status); +} + +/* + * lsa_enum_trusted_domains + * + * Enumerate the trusted domains in our primary domain. The information + * is stored in the NT domain database by the lower level + * lsar_enum_trusted_domains call. The caller should query the database + * to obtain a reference to the trusted domain information. + * + * Returns NT status codes. + */ +DWORD +lsa_enum_trusted_domains(void) +{ + mlsvc_handle_t domain_handle; + DWORD enum_context; + DWORD status; + + if ((lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle)) != 0) + return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + + enum_context = 0; + + status = lsar_enum_trusted_domains(&domain_handle, &enum_context); + if (status == MLSVC_NO_MORE_DATA) { + /* + * MLSVC_NO_MORE_DATA indicates that we + * have all of the available information. + */ + status = NT_STATUS_SUCCESS; + } + + (void) lsar_close(&domain_handle); + return (status); +} + +/* + * lsa_test_lookup + * + * Test routine for lsa_lookup_name and lsa_lookup_sid. + */ +void +lsa_test_lookup(char *name) +{ + smb_userinfo_t *user_info; + nt_sid_t *sid; + DWORD status; + smb_ntdomain_t *di; + + if ((di = smb_getdomaininfo(0)) == 0) + return; + + user_info = mlsvc_alloc_user_info(); + + if (lsa_lookup_builtin_name(name, user_info) != 0) { + status = lsa_lookup_name(di->server, di->domain, name, + user_info); + + if (status == 0) { + sid = nt_sid_splice(user_info->domain_sid, + user_info->rid); + + (void) lsa_lookup_sid(sid, user_info); + free(sid); + } + } + + mlsvc_free_user_info(user_info); +} + +/* + * lsa_lookup_builtin_name + * + * lookup builtin account table to see if account_name is + * there. If it is there, set sid_name_use, domain_sid, + * domain_name, and rid fields of the passed user_info + * structure and return 0. If lookup fails return 1. + */ +int +lsa_lookup_builtin_name(char *account_name, smb_userinfo_t *user_info) +{ + char *domain; + int res; + + user_info->domain_sid = nt_builtin_lookup_name(account_name, + &user_info->sid_name_use); + + if (user_info->domain_sid == 0) + return (1); + + res = nt_sid_split(user_info->domain_sid, &user_info->rid); + if (res < 0) + return (1); + + domain = nt_builtin_lookup_domain(account_name); + if (domain) { + user_info->domain_name = strdup(domain); + return (0); + } + + return (1); +} + +/* + * lsa_lookup_local_sam + * + * lookup for the given account name in the local SAM database. + * Returns 0 on success. If lookup fails return 1. + */ +int +lsa_lookup_local_sam(char *domain, char *account_name, + smb_userinfo_t *user_info) +{ + nt_group_t *grp; + + if (*domain == '\0' || *account_name == '\0') + return (1); + + grp = nt_group_getinfo(account_name, RWLOCK_READER); + if (grp == 0) + return (1); + + user_info->sid_name_use = *grp->sid_name_use; + user_info->domain_sid = nt_sid_dup(grp->sid); + nt_group_putinfo(grp); + + if (user_info->domain_sid == 0) + return (1); + + (void) nt_sid_split(user_info->domain_sid, &user_info->rid); + user_info->domain_name = strdup(domain); + + if (user_info->domain_name == 0) { + free(user_info->domain_sid); + user_info->domain_sid = 0; + return (1); + } + + return (0); +} + +/* + * lsa_lookup_local + * + * if given account name has domain part, check to see if + * it matches with host name or any of host's primary addresses. + * if any match found first lookup in builtin accounts table and + * then in local SAM table. + * + * if account name doesn't have domain part, first do local lookups + * if nothing is found return 1. This means that caller function should + * do domain lookup. + * if any error happened return -1, if name is found return 0. + */ +int +lsa_lookup_local(char *name, smb_userinfo_t *user_info) +{ + char hostname[MAXHOSTNAMELEN]; + int res = 0; + int local_lookup = 0; + char *tmp; + net_cfg_t cfg; + uint32_t addr; + + if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0) + return (-1); + + tmp = strchr(name, '\\'); + if (tmp != 0) { + *tmp = 0; + if (strcasecmp(name, hostname) == 0) + local_lookup = 1; + + if (!local_lookup) { + addr = inet_addr(name); + if (smb_nic_get_byip(addr, &cfg) != NULL) { + local_lookup = 1; + } + } + + if (!local_lookup) { + /* do domain lookup */ + *tmp = '\\'; + return (1); + } + + name = tmp + 1; + local_lookup = 1; + } + + res = lsa_lookup_builtin_name(name, user_info); + if (res != 0) + res = lsa_lookup_local_sam(hostname, name, user_info); + + if (res == 0) + return (0); + + if (local_lookup) + return (-1); + + return (1); +} + +/* + * lsa_lookup_name + * + * Lookup a name on the specified server (domain controller) and obtain + * the appropriate SID. The information is returned in the user_info + * structure. The caller is responsible for allocating and releasing + * this structure. On success sid_name_use will be set to indicate the + * type of SID. If the name is the domain name, this function will be + * identical to lsa_domain_info. Otherwise the rid and name fields will + * also be valid. On failure sid_name_use will be set to SidTypeUnknown. + * + * On success 0 is returned. Otherwise a -ve error code. + */ +int lsa_lookup_name(char *server, char *domain, char *account_name, + smb_userinfo_t *user_info) +{ + mlsvc_handle_t domain_handle; + int rc; + + rc = lsar_open(MLSVC_IPC_ANON, server, domain, 0, 0, &domain_handle); + if (rc != 0) + return (-1); + + rc = lsar_lookup_names(&domain_handle, account_name, user_info); + + (void) lsar_close(&domain_handle); + return (rc); +} + +/* + * lsa_lookup_name2 + * + * Returns NT status codes. + */ +DWORD lsa_lookup_name2(char *server, char *domain, char *account_name, + smb_userinfo_t *user_info) +{ + mlsvc_handle_t domain_handle; + DWORD status; + int rc; + + rc = lsar_open(MLSVC_IPC_ANON, server, domain, 0, 0, &domain_handle); + if (rc != 0) + return (NT_STATUS_INVALID_PARAMETER); + + status = lsar_lookup_names2(&domain_handle, account_name, user_info); + if (status == NT_STATUS_REVISION_MISMATCH) { + /* + * Not a Windows 2000 domain controller: + * use the NT compatible call. + */ + if (lsar_lookup_names(&domain_handle, account_name, + user_info) != 0) + status = NT_STATUS_NONE_MAPPED; + else + status = 0; + } + + (void) lsar_close(&domain_handle); + return (status); +} + +/* + * lsa_lookup_sid + * + * Lookup a SID on the specified server (domain controller) and obtain + * the appropriate name. The information is returned in the user_info + * structure. The caller is responsible for allocating and releasing + * this structure. On success sid_name_use will be set to indicate the + * type of SID. On failure sid_name_use will be set to SidTypeUnknown. + * + * On success 0 is returned. Otherwise a -ve error code. + */ +int +lsa_lookup_sid(nt_sid_t *sid, smb_userinfo_t *user_info) +{ + mlsvc_handle_t domain_handle; + int rc; + + rc = lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle); + if (rc != 0) + return (-1); + + rc = lsar_lookup_sids(&domain_handle, + (struct mslsa_sid *)sid, user_info); + + (void) lsar_close(&domain_handle); + return (rc); +} + +/* + * lsa_lookup_sid2 + * + * Returns NT status codes. + */ +DWORD +lsa_lookup_sid2(nt_sid_t *sid, smb_userinfo_t *user_info) +{ + mlsvc_handle_t domain_handle; + DWORD status; + int rc; + + rc = lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle); + if (rc != 0) + return (NT_STATUS_INVALID_PARAMETER); + + status = lsar_lookup_sids2(&domain_handle, + (struct mslsa_sid *)sid, user_info); + + if (status == NT_STATUS_REVISION_MISMATCH) { + /* + * Not a Windows 2000 domain controller: + * use the NT compatible call. + */ + if (lsar_lookup_sids(&domain_handle, (struct mslsa_sid *)sid, + user_info) != 0) + status = NT_STATUS_NONE_MAPPED; + else + status = 0; + } + + (void) lsar_close(&domain_handle); + return (status); +} + +/* + * lsa_test_lookup2 + * + * Test routine for lsa_lookup_name2 and lsa_lookup_sid2. + */ +void +lsa_test_lookup2(char *name) +{ + smb_userinfo_t *user_info; + nt_sid_t *sid; + DWORD status; + smb_ntdomain_t *di; + + if ((di = smb_getdomaininfo(0)) == 0) + return; + + user_info = mlsvc_alloc_user_info(); + + if (lsa_lookup_builtin_name(name, user_info) != 0) { + status = lsa_lookup_name2(di->server, di->domain, name, + user_info); + + if (status == 0) { + sid = nt_sid_splice(user_info->domain_sid, + user_info->rid); + + (void) lsa_lookup_sid2(sid, user_info); + free(sid); + } + } + + mlsvc_free_user_info(user_info); +} + +/* + * lsa_lookup_privs + * + * Request the privileges associated with the specified account. In + * order to get the privileges, we first have to lookup the name on + * the specified domain controller and obtain the appropriate SID. + * The SID can then be used to open the account and obtain the + * account privileges. The results from both the name lookup and the + * privileges are returned in the user_info structure. The caller is + * responsible for allocating and releasing this structure. + * + * On success 0 is returned. Otherwise a -ve error code. + */ +/*ARGSUSED*/ +int +lsa_lookup_privs(char *server, char *account_name, char *target_name, + smb_userinfo_t *user_info) +{ + mlsvc_handle_t domain_handle; + int rc; +#if 0 + mlsvc_handle_t account_handle; + struct mslsa_sid *sid; + + lsa_lookup_name(0, 0, target_name, user_info); + + sid = (struct mslsa_sid *) + nt_sid_splice(user_info->domain_sid, user_info->rid); + + lsa_lookup_sid(server, account_name, (nt_sid_t *)sid, user_info); +#endif + if ((lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle)) != 0) + return (-1); + + rc = lsa_list_accounts(&domain_handle); +#if 0 + rc = lsar_open_account(&domain_handle, sid, &account_handle); + if (rc == 0) { + (void) lsar_enum_privs_account(&account_handle, user_info); + (void) lsar_close(&account_handle); + } + + free(sid); +#endif + (void) lsar_close(&domain_handle); + return (rc); +} + +/* + * lsa_list_privs + * + * List the privileges supported by the specified server. + * This function is only intended for diagnostics. + * + * Returns NT status codes. + */ +DWORD +lsa_list_privs(char *server, char *domain) +{ + static char name[128]; + static struct ms_luid luid; + mlsvc_handle_t domain_handle; + int rc; + int i; + + rc = lsar_open(MLSVC_IPC_ANON, server, domain, 0, 0, &domain_handle); + if (rc != 0) + return (NT_STATUS_INVALID_PARAMETER); + + for (i = 0; i < 30; ++i) { + luid.low_part = i; + rc = lsar_lookup_priv_name(&domain_handle, &luid, name, 128); + if (rc != 0) + continue; + + (void) lsar_lookup_priv_value(&domain_handle, name, &luid); + (void) lsar_lookup_priv_display_name(&domain_handle, name, + name, 128); + } + + (void) lsar_close(&domain_handle); + return (NT_STATUS_SUCCESS); +} + +/* + * lsa_test + * + * LSA test routine: open and close the LSA interface. + * TBD: the parameters should be server and domain. + * + * On success 0 is returned. Otherwise a -ve error code. + */ +/*ARGSUSED*/ +int +lsa_test(char *server, char *account_name) +{ + mlsvc_handle_t domain_handle; + int rc; + + rc = lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle); + if (rc != 0) + return (-1); + + if (lsar_close(&domain_handle) != 0) + return (-1); + + return (0); +} + +/* + * lsa_list_accounts + * + * This function can be used to list the accounts in the specified + * domain. For now the SIDs are just listed in the system log. + * + * On success 0 is returned. Otherwise a -ve error code. + */ +static int +lsa_list_accounts(mlsvc_handle_t *domain_handle) +{ + mlsvc_handle_t account_handle; + struct mslsa_EnumAccountBuf accounts; + struct mslsa_sid *sid; + char *name; + WORD sid_name_use; + smb_userinfo_t *user_info; + DWORD enum_context = 0; + int rc; + int i; + + user_info = mlsvc_alloc_user_info(); + bzero(&accounts, sizeof (struct mslsa_EnumAccountBuf)); + + do { + rc = lsar_enum_accounts(domain_handle, &enum_context, + &accounts); + if (rc != 0) + return (rc); + + for (i = 0; i < accounts.entries_read; ++i) { + sid = accounts.info[i].sid; + + name = nt_builtin_lookup_sid((nt_sid_t *)sid, + &sid_name_use); + + if (name == 0) { + if (lsar_lookup_sids(domain_handle, sid, + user_info) == 0) { + name = user_info->name; + sid_name_use = user_info->sid_name_use; + } else { + name = "unknown"; + sid_name_use = SidTypeUnknown; + } + } + + nt_sid_logf((nt_sid_t *)sid); + + if (lsar_open_account(domain_handle, sid, + &account_handle) == 0) { + (void) lsar_enum_privs_account(&account_handle, + user_info); + (void) lsar_close(&account_handle); + } + + free(accounts.info[i].sid); + mlsvc_release_user_info(user_info); + } + + if (accounts.info) + free(accounts.info); + } while (rc == 0 && accounts.entries_read != 0); + + mlsvc_free_user_info(user_info); + return (0); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lsar_lookup.c b/usr/src/lib/smbsrv/libmlsvc/common/lsar_lookup.c new file mode 100644 index 000000000000..5216818cce76 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/lsar_lookup.c @@ -0,0 +1,875 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Local Security Authority RPC (LSARPC) library interface functions for + * query, lookup and enumeration calls. + */ + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * The maximum number of bytes we are prepared to deal with in a + * response. + */ +#define MLSVC_MAX_RESPONSE_LEN 1024 + +/* + * This structure is used when lookuping up names. We only lookup one + * name at a time but the structure will allow for more. + */ +typedef struct lookup_name_table { + DWORD n_entry; + mslsa_string_t name[8]; +} lookup_name_table_t; + +/* + * lsar_query_security_desc + * + * Don't use this call yet. It is just a place holder for now. + */ +int +lsar_query_security_desc(mlsvc_handle_t *lsa_handle) +{ + struct mslsa_QuerySecurityObject arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int rc; + int opnum; + + context = lsa_handle->context; + opnum = LSARPC_OPNUM_QuerySecurityObject; + + bzero(&arg, sizeof (struct mslsa_QuerySecurityObject)); + (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(context, opnum, &arg, &heap); + mlsvc_rpc_free(context, &heap); + return (rc); +} + +/* + * lsar_query_info_policy + * + * The general purpose of this function is to allow various pieces of + * information to be queried on the domain controller. The only + * information queries supported are MSLSA_POLICY_PRIMARY_DOMAIN_INFO + * and MSLSA_POLICY_ACCOUNT_DOMAIN_INFO. + * + * On success, the return code will be 0 and the user_info structure + * will be set up. The sid_name_use field will be set to SidTypeDomain + * indicating that the domain name and domain sid fields are vaild. If + * the infoClass returned from the server is not one of the supported + * values, the sid_name_use willbe set to SidTypeUnknown. If the RPC + * fails, a negative error code will be returned, in which case the + * user_info will not have been updated. + */ +DWORD +lsar_query_info_policy(mlsvc_handle_t *lsa_handle, WORD infoClass) +{ + struct mslsa_QueryInfoPolicy arg; + struct mlsvc_rpc_context *context; + struct mslsa_PrimaryDomainInfo *pd_info; + struct mslsa_AccountDomainInfo *ad_info; + mlrpc_heapref_t heap; + nt_domain_t *nt_new_dp; + int opnum; + DWORD status; + + if (lsa_handle == 0) + return (NT_STATUS_INVALID_PARAMETER); + + context = lsa_handle->context; + opnum = LSARPC_OPNUM_QueryInfoPolicy; + + bzero(&arg, sizeof (struct mslsa_QueryInfoPolicy)); + (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); + arg.info_class = infoClass; + + (void) mlsvc_rpc_init(&heap); + if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) { + status = NT_STATUS_INVALID_PARAMETER; + } else if (arg.status != 0) { + mlsvc_rpc_report_status(opnum, arg.status); + status = NT_SC_VALUE(arg.status); + } else { + switch (infoClass) { + case MSLSA_POLICY_PRIMARY_DOMAIN_INFO: + pd_info = &arg.info->ru.pd_info; + + nt_domain_flush(NT_DOMAIN_PRIMARY); + nt_new_dp = nt_domain_new(NT_DOMAIN_PRIMARY, + (char *)pd_info->name.str, + (nt_sid_t *)pd_info->sid); + (void) nt_domain_add(nt_new_dp); + status = NT_STATUS_SUCCESS; + break; + + case MSLSA_POLICY_ACCOUNT_DOMAIN_INFO: + ad_info = &arg.info->ru.ad_info; + + nt_domain_flush(NT_DOMAIN_ACCOUNT); + nt_new_dp = nt_domain_new(NT_DOMAIN_ACCOUNT, + (char *)ad_info->name.str, + (nt_sid_t *)ad_info->sid); + (void) nt_domain_add(nt_new_dp); + status = NT_STATUS_SUCCESS; + break; + + default: + status = NT_STATUS_INVALID_INFO_CLASS; + break; + } + } + + mlsvc_rpc_free(context, &heap); + return (status); +} + +/* + * lsar_lookup_names + * + * Lookup a name and obtain the domain and user rid. The RPC call will + * actually support lookup of multiple names but we probably don't + * need to do that. On the final system the lookup level should be + * level 2 but for now we want to restrict it to level 1 so that we + * don't crash the PDC when we get things wrong. + * + * If the lookup fails, the status will typically be + * NT_STATUS_NONE_MAPPED. + */ +int +lsar_lookup_names(mlsvc_handle_t *lsa_handle, char *name, + smb_userinfo_t *user_info) +{ + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + int rc; + int index; + struct mslsa_LookupNames arg; + size_t length; + lookup_name_table_t name_table; + struct mslsa_rid_entry *rid_entry; + struct mslsa_domain_entry *domain_entry; + char *p; + + if (lsa_handle == NULL || name == NULL || user_info == NULL) + return (-1); + + bzero(user_info, sizeof (smb_userinfo_t)); + user_info->sid_name_use = SidTypeUnknown; + + context = lsa_handle->context; + opnum = LSARPC_OPNUM_LookupNames; + + bzero(&arg, sizeof (struct mslsa_LookupNames)); + (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); + + arg.name_table = (struct mslsa_lup_name_table *)&name_table; + name_table.n_entry = 1; + + /* + * Windows NT expects the name length to exclude the terminating + * wchar null but doesn't care whether the allosize includes or + * excludes the null char. Windows 2000 insists that both the + * length and the allosize include the wchar null. + * + * Note: NT returns an error if the mapped_count is non-zero + * when the RPC is called. + */ + if (context->server_os == NATIVE_OS_WIN2000) { + /* + * Windows 2000 doesn't like an LSA lookup for + * DOMAIN\Administrator. + */ + if ((p = strchr(name, '\\')) != 0) { + ++p; + + if (strcasecmp(p, "administrator") == 0) + name = p; + } + + length = mts_wcequiv_strlen(name) + sizeof (mts_wchar_t); + arg.lookup_level = MSLSA_LOOKUP_LEVEL_1; + } else { + length = mts_wcequiv_strlen(name); + arg.lookup_level = MSLSA_LOOKUP_LEVEL_1; + } + + name_table.name[0].length = length; + name_table.name[0].allosize = length; + name_table.name[0].str = (unsigned char *)name; + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(context, opnum, &arg, &heap); + if (rc == 0) { + if (arg.status != 0) { + rc = -1; + } else if (arg.mapped_count == 0) { + rc = -1; + } else { + rid_entry = &arg.translated_sids.rids[0]; + user_info->sid_name_use = rid_entry->sid_name_use; + user_info->rid = rid_entry->rid; + user_info->name = MEM_STRDUP("mlrpc", name); + + if ((index = rid_entry->domain_index) == -1) { + user_info->domain_sid = 0; + user_info->domain_name = 0; + } else { + domain_entry = + &arg.domain_table->entries[index]; + user_info->domain_sid = nt_sid_dup( + (nt_sid_t *)domain_entry->domain_sid); + user_info->domain_name = MEM_STRDUP("mlrpc", + (const char *) + domain_entry->domain_name.str); + } + } + } + + mlsvc_rpc_free(context, &heap); + return (rc); +} + +/* + * lsar_lookup_sids + * + * Lookup a sid and obtain the domain sid and user name. The RPC call + * will actually support lookup of multiple sids but we probably don't + * need to do that. On the final system the lookup level should be + * level 2 but for now we want to restrict it to level 1 so that we + * don't crash the PDC when we get things wrong. + */ +int +lsar_lookup_sids(mlsvc_handle_t *lsa_handle, struct mslsa_sid *sid, + smb_userinfo_t *user_info) +{ + struct mslsa_LookupSids arg; + struct mslsa_lup_sid_entry sid_entry; + struct mslsa_name_entry *name_entry; + struct mslsa_domain_entry *domain_entry; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + int rc; + int index; + + if (lsa_handle == NULL || sid == NULL || user_info == NULL) + return (-1); + + context = lsa_handle->context; + opnum = LSARPC_OPNUM_LookupSids; + + bzero(&arg, sizeof (struct mslsa_LookupSids)); + (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); + arg.lookup_level = MSLSA_LOOKUP_LEVEL_2; + + sid_entry.psid = sid; + arg.lup_sid_table.n_entry = 1; + arg.lup_sid_table.entries = &sid_entry; + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(context, opnum, &arg, &heap); + if (rc == 0) { + if (arg.mapped_count == 0) { + user_info->sid_name_use = SidTypeInvalid; + rc = 1; + } else { + name_entry = &arg.name_table.entries[0]; + user_info->sid_name_use = name_entry->sid_name_use; + + if (user_info->sid_name_use == SidTypeUser || + user_info->sid_name_use == SidTypeGroup || + user_info->sid_name_use == SidTypeAlias) { + + user_info->rid = + sid->SubAuthority[sid->SubAuthCount - 1]; + + user_info->name = MEM_STRDUP("mlrpc", + (const char *)name_entry->name.str); + } + + if ((index = name_entry->domain_ix) == -1) { + user_info->domain_sid = 0; + user_info->domain_name = 0; + } else { + domain_entry = + &arg.domain_table->entries[index]; + + user_info->domain_sid = nt_sid_dup( + (nt_sid_t *)domain_entry->domain_sid); + + user_info->domain_name = MEM_STRDUP("mlrpc", + (const char *) + domain_entry->domain_name.str); + } + } + } + + mlsvc_rpc_free(context, &heap); + return (rc); +} + +/* + * lsar_enum_accounts + * + * Enumerate the list of accounts (i.e. SIDs). Use the handle returned + * from lsa_open_policy2. The enum_context is used to support multiple + * calls to this enumeration function. It should be set to 0 on the + * first call. It will be updated by the domain controller and should + * simply be passed unchanged to subsequent calls until there are no + * more accounts. A warning status of 0x1A indicates that no more data + * is available. The list of accounts will be returned in accounts. + * This list is dynamically allocated using malloc, it should be freed + * by the caller when it is no longer required. + */ +int +lsar_enum_accounts(mlsvc_handle_t *lsa_handle, DWORD *enum_context, + struct mslsa_EnumAccountBuf *accounts) +{ + struct mslsa_EnumerateAccounts arg; + struct mslsa_AccountInfo *info; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + int rc; + DWORD n_entries; + DWORD i; + int nbytes; + + if (lsa_handle == NULL || enum_context == NULL || accounts == NULL) + return (-1); + + accounts->entries_read = 0; + accounts->info = 0; + + context = lsa_handle->context; + opnum = LSARPC_OPNUM_EnumerateAccounts; + + bzero(&arg, sizeof (struct mslsa_EnumerateAccounts)); + (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); + arg.enum_context = *enum_context; + arg.max_length = MLSVC_MAX_RESPONSE_LEN; + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(context, opnum, &arg, &heap); + if (rc == 0) { + if (arg.status != 0) { + if ((arg.status & 0x00FFFFFF) == MLSVC_NO_MORE_DATA) { + *enum_context = arg.enum_context; + } else { + mlsvc_rpc_report_status(opnum, + (DWORD)arg.status); + rc = -1; + } + } else if (arg.enum_buf->entries_read != 0) { + n_entries = arg.enum_buf->entries_read; + nbytes = n_entries * sizeof (struct mslsa_AccountInfo); + + info = (struct mslsa_AccountInfo *)MEM_MALLOC("mlrpc", + nbytes); + if (info == NULL) { + mlsvc_rpc_free(context, &heap); + return (-1); + } + + for (i = 0; i < n_entries; ++i) + info[i].sid = (struct mslsa_sid *)nt_sid_dup( + (nt_sid_t *)arg.enum_buf->info[i].sid); + + accounts->entries_read = n_entries; + accounts->info = info; + *enum_context = arg.enum_context; + } + } + + mlsvc_rpc_free(context, &heap); + return (rc); +} + +/* + * lsar_enum_trusted_domains + * + * Enumerate the list of trusted domains. Use the handle returned from + * lsa_open_policy2. The enum_context is used to support multiple calls + * to this enumeration function. It should be set to 0 on the first + * call. It will be updated by the domain controller and should simply + * be passed unchanged to subsequent calls until there are no more + * domains. + * + * The trusted domains aren't actually returned here. They are added + * to the NT domain database. After all of the trusted domains have + * been discovered, the database can be interrogated to find all of + * the trusted domains. + */ +DWORD +lsar_enum_trusted_domains(mlsvc_handle_t *lsa_handle, DWORD *enum_context) +{ + struct mslsa_EnumTrustedDomain arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + nt_domain_t *nt_new_dp; + int opnum; + DWORD status; + DWORD n_entries; + DWORD i; + + context = lsa_handle->context; + opnum = LSARPC_OPNUM_EnumTrustedDomain; + + bzero(&arg, sizeof (struct mslsa_EnumTrustedDomain)); + (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); + arg.enum_context = *enum_context; + arg.max_length = MLSVC_MAX_RESPONSE_LEN; + + (void) mlsvc_rpc_init(&heap); + if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) { + status = NT_STATUS_INVALID_PARAMETER; + } else if (arg.status != 0) { + *enum_context = arg.enum_context; + status = NT_SC_VALUE(arg.status); + + /* + * status 0x8000001A means NO_MORE_DATA, + * which is not an error. + */ + if (status != MLSVC_NO_MORE_DATA) + mlsvc_rpc_report_status(opnum, arg.status); + } else if (arg.enum_buf->entries_read == 0) { + *enum_context = arg.enum_context; + status = 0; + } else { + nt_domain_flush(NT_DOMAIN_TRUSTED); + n_entries = arg.enum_buf->entries_read; + + for (i = 0; i < n_entries; ++i) { + nt_new_dp = nt_domain_new( + NT_DOMAIN_TRUSTED, + (char *)arg.enum_buf->info[i].name.str, + (nt_sid_t *)arg.enum_buf->info[i].sid); + + (void) nt_domain_add(nt_new_dp); + } + + *enum_context = arg.enum_context; + status = 0; + } + + mlsvc_rpc_free(context, &heap); + return (status); +} + +/* + * lsar_enum_privs_account + * + * Privileges enum? Need an account handle. + */ +/*ARGSUSED*/ +int +lsar_enum_privs_account(mlsvc_handle_t *account_handle, + smb_userinfo_t *user_info) +{ + struct mslsa_EnumPrivsAccount arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + int rc; + + context = account_handle->context; + opnum = LSARPC_OPNUM_EnumPrivsAccount; + + bzero(&arg, sizeof (struct mslsa_EnumPrivsAccount)); + (void) memcpy(&arg.account_handle, &account_handle->handle, + sizeof (mslsa_handle_t)); + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(context, opnum, &arg, &heap); + if ((rc == 0) && (arg.status != 0)) { + mlsvc_rpc_report_status(opnum, (DWORD)arg.status); + rc = -1; + } + mlsvc_rpc_free(context, &heap); + return (rc); +} + +/* + * lsar_lookup_priv_value + * + * Map a privilege name to a local unique id (LUID). Privilege names + * are consistent across the network. LUIDs are machine specific. + * This function provides the means to map a privilege name to the + * LUID used by a remote server to represent it. The handle here is + * a policy handle. + */ +int +lsar_lookup_priv_value(mlsvc_handle_t *lsa_handle, char *name, + struct ms_luid *luid) +{ + struct mslsa_LookupPrivValue arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + int rc; + size_t length; + + if (lsa_handle == NULL || name == NULL || luid == NULL) + return (-1); + + context = lsa_handle->context; + opnum = LSARPC_OPNUM_LookupPrivValue; + + bzero(&arg, sizeof (struct mslsa_LookupPrivValue)); + (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); + + length = mts_wcequiv_strlen(name); + if (context->server_os == NATIVE_OS_WIN2000) + length += sizeof (mts_wchar_t); + + arg.name.length = length; + arg.name.allosize = length; + arg.name.str = (unsigned char *)name; + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(context, opnum, &arg, &heap); + if (rc == 0) { + if (arg.status != 0) + rc = -1; + else + (void) memcpy(luid, &arg.luid, sizeof (struct ms_luid)); + } + + mlsvc_rpc_free(context, &heap); + return (rc); +} + +/* + * lsar_lookup_priv_name + * + * Map a local unique id (LUID) to a privilege name. Privilege names + * are consistent across the network. LUIDs are machine specific. + * This function the means to map the LUID used by a remote server to + * the appropriate privilege name. The handle here is a policy handle. + */ +int +lsar_lookup_priv_name(mlsvc_handle_t *lsa_handle, struct ms_luid *luid, + char *name, int namelen) +{ + struct mslsa_LookupPrivName arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + int rc; + + if (lsa_handle == NULL || luid == NULL || name == NULL) + return (-1); + + context = lsa_handle->context; + opnum = LSARPC_OPNUM_LookupPrivName; + + bzero(&arg, sizeof (struct mslsa_LookupPrivName)); + (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); + (void) memcpy(&arg.luid, luid, sizeof (struct ms_luid)); + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(context, opnum, &arg, &heap); + if (rc == 0) { + if (arg.status != 0) + rc = -1; + else + (void) strlcpy(name, (char const *)arg.name->str, + namelen); + } + + mlsvc_rpc_free(context, &heap); + return (rc); +} + +/* + * lsar_lookup_priv_display_name + * + * Map a privilege name to a privilege display name. The input handle + * should be an LSA policy handle and the name would normally be one + * of the privileges defined in smb_privilege.h + * + * There's something peculiar about the return status from NT servers, + * it's not always present. So for now, I'm ignoring the status in the + * RPC response. + * + * Returns NT status codes. + */ +DWORD +lsar_lookup_priv_display_name(mlsvc_handle_t *lsa_handle, char *name, + char *display_name, int display_len) +{ + struct mslsa_LookupPrivDisplayName arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + size_t length; + DWORD status; + + if (lsa_handle == NULL || name == NULL || display_name == NULL) + return (NT_STATUS_INVALID_PARAMETER); + + context = lsa_handle->context; + opnum = LSARPC_OPNUM_LookupPrivDisplayName; + + bzero(&arg, sizeof (struct mslsa_LookupPrivDisplayName)); + (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); + + length = mts_wcequiv_strlen(name); + arg.name.length = length; + arg.name.allosize = length; + arg.name.str = (unsigned char *)name; + + arg.client_language = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); + arg.default_language = MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL); + + (void) mlsvc_rpc_init(&heap); + + if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) + status = NT_STATUS_INVALID_PARAMETER; +#if 0 + else if (arg.status != 0) + status = NT_SC_VALUE(arg.status); +#endif + else { + (void) strlcpy(display_name, + (char const *)arg.display_name->str, display_len); + status = NT_STATUS_SUCCESS; + } + + mlsvc_rpc_free(context, &heap); + return (status); +} + +/* + * lsar_lookup_sids2 + */ +DWORD +lsar_lookup_sids2(mlsvc_handle_t *lsa_handle, struct mslsa_sid *sid, + smb_userinfo_t *user_info) +{ + struct lsar_lookup_sids2 arg; + struct lsar_name_entry2 *name_entry; + struct mslsa_lup_sid_entry sid_entry; + struct mslsa_domain_entry *domain_entry; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + int index; + DWORD status; + + if (lsa_handle == NULL || sid == NULL || user_info == NULL) + return (NT_STATUS_INVALID_PARAMETER); + + context = lsa_handle->context; + opnum = LSARPC_OPNUM_LookupSids2; + + if (context->server_os != NATIVE_OS_WIN2000) + return (NT_STATUS_REVISION_MISMATCH); + + bzero(&arg, sizeof (struct lsar_lookup_sids2)); + (void) memcpy(&arg.policy_handle, lsa_handle, sizeof (mslsa_handle_t)); + + sid_entry.psid = sid; + arg.lup_sid_table.n_entry = 1; + arg.lup_sid_table.entries = &sid_entry; + arg.lookup_level = MSLSA_LOOKUP_LEVEL_1; + arg.requested_count = arg.lup_sid_table.n_entry; + + (void) mlsvc_rpc_init(&heap); + + if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) { + status = NT_STATUS_INVALID_PARAMETER; + } else if (arg.mapped_count == 0) { + user_info->sid_name_use = SidTypeInvalid; + status = NT_STATUS_NONE_MAPPED; + } else if (arg.status != 0) { + mlsvc_rpc_report_status(opnum, arg.status); + status = NT_SC_VALUE(arg.status); + } else { + status = 0; + + name_entry = &arg.name_table.entries[0]; + user_info->sid_name_use = name_entry->sid_name_use; + + if (user_info->sid_name_use == SidTypeUser || + user_info->sid_name_use == SidTypeGroup || + user_info->sid_name_use == SidTypeAlias) { + + user_info->rid = + sid->SubAuthority[sid->SubAuthCount - 1]; + + user_info->name = MEM_STRDUP("mlrpc", + (char const *)name_entry->name.str); + + } + + if ((index = name_entry->domain_ix) == -1) { + user_info->domain_sid = 0; + user_info->domain_name = 0; + } else { + domain_entry = &arg.domain_table->entries[index]; + + user_info->domain_sid = nt_sid_dup( + (nt_sid_t *)domain_entry->domain_sid); + + user_info->domain_name = MEM_STRDUP("mlrpc", + (char const *)domain_entry->domain_name.str); + } + } + + mlsvc_rpc_free(context, &heap); + return (status); +} + + +/* + * lsar_lookup_names2 + * + * Windows NT expects the name length to exclude the terminating + * wchar null but Windows 2000 insists that both the length and + * the allosize include the wchar null. Windows NT doesn't care + * whether or not the allosize includes or excludes the null char. + * + * As a precaution, I set the lookup level to 1 on Windows 2000 + * until I can do some more testing. + * + * Note that NT returns an error if the mapped_count is non-zero + * when the RPC is called. + * + * It should be okay to lookup DOMAIN\Administrator in this function. + */ +DWORD +lsar_lookup_names2(mlsvc_handle_t *lsa_handle, char *name, + smb_userinfo_t *user_info) +{ + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + int index; + struct lsar_LookupNames2 arg; + size_t length; + lookup_name_table_t name_table; + struct lsar_rid_entry2 *rid_entry; + struct mslsa_domain_entry *domain_entry; + DWORD status; + + if (lsa_handle == NULL || name == NULL || user_info == NULL) + return (NT_STATUS_INVALID_PARAMETER); + + bzero(user_info, sizeof (smb_userinfo_t)); + user_info->sid_name_use = SidTypeUnknown; + + context = lsa_handle->context; + opnum = LSARPC_OPNUM_LookupNames2; + + if (context->server_os != NATIVE_OS_WIN2000) + return (NT_STATUS_REVISION_MISMATCH); + + bzero(&arg, sizeof (struct lsar_LookupNames2)); + (void) memcpy(&arg.policy_handle, lsa_handle, sizeof (mslsa_handle_t)); + arg.unknown_sb2 = 0x00000002; + arg.lookup_level = MSLSA_LOOKUP_LEVEL_1; + + arg.name_table = (struct mslsa_lup_name_table *)&name_table; + name_table.n_entry = 1; + + length = mts_wcequiv_strlen(name) + sizeof (mts_wchar_t); + name_table.name[0].length = length; + name_table.name[0].allosize = length; + name_table.name[0].str = (unsigned char *)name; + + (void) mlsvc_rpc_init(&heap); + + if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) { + status = NT_STATUS_INVALID_PARAMETER; + } else if (arg.status != 0) { + mlsvc_rpc_report_status(opnum, arg.status); + status = NT_SC_VALUE(arg.status); + } else if (arg.mapped_count == 0) { + user_info->sid_name_use = SidTypeInvalid; + status = NT_STATUS_NONE_MAPPED; + } else { + status = 0; + + rid_entry = &arg.translated_sids.rids[0]; + user_info->sid_name_use = rid_entry->sid_name_use; + user_info->rid = rid_entry->rid; + user_info->name = MEM_STRDUP("mlrpc", name); + + if ((index = rid_entry->domain_index) == -1) { + user_info->domain_sid = 0; + user_info->domain_name = 0; + } else { + domain_entry = &arg.domain_table->entries[index]; + + user_info->domain_sid = nt_sid_dup( + (nt_sid_t *)domain_entry->domain_sid); + + user_info->domain_name = MEM_STRDUP("mlrpc", + (char const *)domain_entry->domain_name.str); + } + } + + mlsvc_rpc_free(context, &heap); + return (status); +} + +void +mlsvc_rpc_report_status(int opnum, DWORD status) +{ + char *s = "unknown"; + + if (status == 0) + s = "success"; + else if (NT_SC_IS_ERROR(status)) + s = "error"; + else if (NT_SC_IS_WARNING(status)) + s = "warning"; + else if (NT_SC_IS_INFO(status)) + s = "info"; + + smb_tracef("mlrpc[0x%02x]: %s: %s (0x%08x)", + opnum, s, xlate_nt_status(status), status); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lsar_open.c b/usr/src/lib/smbsrv/libmlsvc/common/lsar_open.c new file mode 100644 index 000000000000..57b1b62a976b --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/lsar_open.c @@ -0,0 +1,298 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Local Security Authority RPC (LSARPC) library interface functions for + * open and close calls. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * lsar_open + * + * This is a wrapper round lsar_open_policy2 to ensure that we connect + * using the appropriate session and logon. We default to the resource + * domain information if the caller didn't supply a server name and a + * domain name. + * + * On success 0 is returned. Otherwise a -ve error code. + */ +int lsar_open(int ipc_mode, char *server, char *domain, char *username, + char *password, mlsvc_handle_t *domain_handle) +{ + smb_ntdomain_t *di; + int remote_os; + int remote_lm; + int rc; + + if ((di = smb_getdomaininfo(0)) == NULL) + return (-1); + + if (server == NULL || domain == NULL) { + server = di->server; + domain = di->domain; + } + + switch (ipc_mode) { + case MLSVC_IPC_USER: + /* + * Use the supplied credentials. + */ + rc = mlsvc_user_logon(server, domain, username, password); + break; + + case MLSVC_IPC_ADMIN: + /* + * Use the resource domain administrator credentials. + */ + server = di->server; + domain = di->domain; + username = smbrdr_ipc_get_user(); + + rc = mlsvc_admin_logon(server, domain); + break; + + case MLSVC_IPC_ANON: + default: + rc = mlsvc_anonymous_logon(server, domain, &username); + break; + } + + if (rc != 0) + return (-1); + + rc = lsar_open_policy2(server, domain, username, domain_handle); + if (rc == 0) { + if (mlsvc_session_native_values(domain_handle->context->fid, + &remote_os, &remote_lm, 0) != 0) + remote_os = NATIVE_OS_UNKNOWN; + + domain_handle->context->server_os = remote_os; + } + return (rc); +} + + +/* + * lsar_open_policy2 + * + * Obtain an LSA policy handle. A policy handle is required to access + * LSA resources on a remote server. The server name supplied here does + * not need the double backslash prefix; it is added here. Call this + * function via lsar_open to ensure that the appropriate connection is + * in place. + * + * I'm not sure if it makes a difference whether we use GENERIC_EXECUTE + * or STANDARD_RIGHTS_EXECUTE. For a long time I used the standard bit + * and then I added the generic bit while working on privileges because + * NT sets that bit. I don't think it matters. + * + * Returns 0 on success. Otherwise non-zero to indicate a failure. + */ +int lsar_open_policy2(char *server, char *domain, char *username, + mlsvc_handle_t *lsa_handle) +{ + struct mslsa_OpenPolicy2 arg; + mlrpc_heapref_t heap; + int rc; + int opnum; + int fid; + int remote_os; + int remote_lm; + int len; + + if (server == NULL || domain == NULL || + username == NULL || lsa_handle == NULL) + return (-1); + + fid = mlsvc_open_pipe(server, domain, username, "\\lsarpc"); + if (fid < 0) + return (-1); + + if ((rc = mlsvc_rpc_bind(lsa_handle, fid, "LSARPC")) < 0) { + (void) mlsvc_close_pipe(fid); + return (rc); + } + + opnum = LSARPC_OPNUM_OpenPolicy2; + bzero(&arg, sizeof (struct mslsa_OpenPolicy2)); + + len = strlen(server) + 4; + arg.servername = malloc(len); + if (arg.servername == NULL) { + (void) mlsvc_close_pipe(fid); + free(lsa_handle->context); + return (-1); + } + + (void) snprintf((char *)arg.servername, len, "\\\\%s", server); + arg.attributes.length = sizeof (struct mslsa_object_attributes); + + (void) mlsvc_session_native_values(fid, &remote_os, &remote_lm, 0); + + if (remote_os == NATIVE_OS_NT5_0) { + arg.desiredAccess = MAXIMUM_ALLOWED; + } else { + arg.desiredAccess = GENERIC_EXECUTE + | STANDARD_RIGHTS_EXECUTE + | POLICY_VIEW_LOCAL_INFORMATION + | POLICY_LOOKUP_NAMES; + } + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(lsa_handle->context, opnum, &arg, &heap); + if (rc == 0) { + if (arg.status != 0) { + rc = -1; + } else { + (void) memcpy(&lsa_handle->handle, &arg.domain_handle, + sizeof (mslsa_handle_t)); + + if (mlsvc_is_null_handle(lsa_handle)) + rc = -1; + } + } + + mlsvc_rpc_free(lsa_handle->context, &heap); + free(arg.servername); + + if (rc != 0) { + (void) mlsvc_close_pipe(fid); + free(lsa_handle->context); + } + + return (rc); +} + +/* + * lsar_open_account + * + * Obtain an LSA account handle. The lsa_handle must be a valid handle + * obtained via lsar_open_policy2. The main thing to remember here is + * to set up the context in the lsa_account_handle. I'm not sure what + * the requirements are for desired access. Some values require admin + * access. + * + * Returns 0 on success. Otherwise non-zero to indicate a failure. + */ +int +lsar_open_account(mlsvc_handle_t *lsa_handle, struct mslsa_sid *sid, + mlsvc_handle_t *lsa_account_handle) +{ + struct mslsa_OpenAccount arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int rc; + int opnum; + + if (mlsvc_is_null_handle(lsa_handle) || + sid == NULL || lsa_account_handle == NULL) + return (-1); + + context = lsa_handle->context; + opnum = LSARPC_OPNUM_OpenAccount; + bzero(&arg, sizeof (struct mslsa_OpenAccount)); + + (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); + arg.sid = sid; + arg.access_mask = STANDARD_RIGHTS_REQUIRED +#if 0 + | POLICY_VIEW_AUDIT_INFORMATION + | POLICY_GET_PRIVATE_INFORMATION + | POLICY_TRUST_ADMIN +#endif + | POLICY_VIEW_LOCAL_INFORMATION; + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(context, opnum, &arg, &heap); + if (rc == 0) { + if (arg.status != 0) { + rc = -1; + } else { + lsa_account_handle->context = context; + + (void) memcpy(&lsa_account_handle->handle, + &arg.account_handle, sizeof (mslsa_handle_t)); + + if (mlsvc_is_null_handle(lsa_account_handle)) + rc = -1; + } + } + + mlsvc_rpc_free(context, &heap); + return (rc); +} + +/* + * lsar_close + * + * Close the LSA connection associated with the handle. The lsa_handle + * must be a valid handle obtained via a call to lsar_open_policy2 or + * lsar_open_account. On success the handle will be zeroed out to + * ensure that it is not used again. If this is the top level handle + * (i.e. the one obtained via lsar_open_policy2) the pipe is closed + * and the context is freed. + * + * Returns 0 on success. Otherwise non-zero to indicate a failure. + */ +int +lsar_close(mlsvc_handle_t *lsa_handle) +{ + struct mslsa_CloseHandle arg; + mlrpc_heapref_t heap; + int rc; + int opnum; + + if (mlsvc_is_null_handle(lsa_handle)) + return (-1); + + opnum = LSARPC_OPNUM_CloseHandle; + bzero(&arg, sizeof (struct mslsa_CloseHandle)); + (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(lsa_handle->context, opnum, &arg, &heap); + mlsvc_rpc_free(lsa_handle->context, &heap); + + if (lsa_handle->context->handle == &lsa_handle->handle) { + (void) mlsvc_close_pipe(lsa_handle->context->fid); + free(lsa_handle->context); + } + + bzero(lsa_handle, sizeof (mlsvc_handle_t)); + return (rc); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers b/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers new file mode 100644 index 000000000000..e05f51b279d0 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers @@ -0,0 +1,97 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# +# ident "%Z%%M% %I% %E% SMI" + +SUNWprivate { + global: + lmshare_add; + lmshare_close_iterator; + lmshare_delete; + lmshare_exists; + lmshare_getinfo; + lmshare_get_realpath; + lmshare_is_admin; + lmshare_is_dir; + lmshare_is_restricted; + lmshare_is_special; + lmshare_is_valid; + lmshare_iterate; + lmshare_list; + lmshare_num_shares; + lmshare_open_iterator; + lmshare_rename; + lmshare_setinfo; + lmshare_start; + lmshare_stop; + lsa_lookup_name2; + lsa_lookup_sid2; + lsa_query_primary_domain_info; + lsa_query_account_domain_info; + lsa_enum_trusted_domains; + mlsvc_init; + mlsvc_validate_user; + mlsvc_is_local_domain; + nt_group_add; + nt_group_add_groupprivs; + nt_group_add_member_byname; + nt_group_cache_size; + nt_group_close_iterator; + nt_group_delete; + nt_group_del_member_byname; + nt_group_getinfo; + nt_group_getpriv; + nt_group_ht_lock; + nt_group_ht_unlock; + nt_group_is_member; + nt_group_iterate; + nt_group_modify; + nt_group_num_groups; + nt_group_num_members; + nt_group_open_iterator; + nt_group_putinfo; + nt_groups_count; + nt_group_setpriv; + nt_groups_lookup_rid; + nt_groups_member_groups; + nt_groups_member_ngroups; + nt_groups_member_privs; + nt_group_list; + nt_group_member_list; + sam_init; + smb_logon; + smb_token_destroy; + smb_build_lmshare_info; + smb_get_smb_share_group; + smb_autohome_add; + smb_autohome_endent; + smb_autohome_getent; + smb_autohome_lookup; + smb_autohome_remove; + smb_autohome_setent; + smb_is_autohome; + local: + *; +}; diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c new file mode 100644 index 000000000000..ac8cc3003c05 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c @@ -0,0 +1,306 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Context functions to support the RPC interface library. + */ + +#include +#include + +#include +#include +#include + +static int mlsvc_xa_init(struct mlrpc_client *, struct mlrpc_xaction *, + mlrpc_heap_t *); +static int mlsvc_xa_exchange(struct mlrpc_client *, struct mlrpc_xaction *); +static int mlsvc_xa_read(struct mlrpc_client *, struct mlrpc_xaction *); +static int mlsvc_xa_preserve(struct mlrpc_client *, struct mlrpc_xaction *, + mlrpc_heapref_t *); +static int mlsvc_xa_destruct(struct mlrpc_client *, struct mlrpc_xaction *); +static void mlsvc_xa_release(struct mlrpc_client *, mlrpc_heapref_t *heapref); + +/* + * mlsvc_rpc_bind + * + * This the entry point for all client RPC services. This call must be + * made to initialize an RPC context structure and bind to the remote + * service before any RPCs can be exchanged with that service. The + * descriptor is a wrapper that is used to associate an RPC handle with + * the context data for that specific instance of the interface. The + * handle is zeroed to ensure that it doesn't look like a valid handle. + * The context handle is assigned to point at the RPC handle so that we + * know when to free the context. As each handle is initialized it will + * include a pointer to this context but only when we close this initial + * RPC handle can the context be freed. + * + * On success, return a pointer to the descriptor. Otherwise return a + * null pointer. + */ +int +mlsvc_rpc_bind(mlsvc_handle_t *desc, int fid, char *service) +{ + struct mlsvc_rpc_context *context; + int rc; + + bzero(&desc->handle, sizeof (ms_handle_t)); + + context = malloc(sizeof (struct mlsvc_rpc_context)); + if ((desc->context = context) == NULL) + return (-1); + + bzero(context, sizeof (struct mlsvc_rpc_context)); + context->cli.context = context; + + mlrpc_binding_pool_initialize(&context->cli.binding_list, + context->binding_pool, CTXT_N_BINDING_POOL); + + context->fid = fid; + context->handle = &desc->handle; + context->cli.xa_init = mlsvc_xa_init; + context->cli.xa_exchange = mlsvc_xa_exchange; + context->cli.xa_read = mlsvc_xa_read; + context->cli.xa_preserve = mlsvc_xa_preserve; + context->cli.xa_destruct = mlsvc_xa_destruct; + context->cli.xa_release = mlsvc_xa_release; + + rc = mlrpc_c_bind(&context->cli, service, &context->binding); + if (MLRPC_DRC_IS_FAULT(rc)) { + free(context); + desc->context = NULL; + return (-1); + } + + return (rc); +} + +/* + * mlsvc_rpc_init + * + * This function must be called by client side applications before + * calling mlsvc_rpc_call to allocate a heap. The heap must be + * destroyed by either calling mlrpc_heap_destroy or mlsvc_rpc_free. + * Use mlrpc_heap_destroy if mlsvc_rpc_call has not yet been called. + * Otherwise use mlsvc_rpc_free. + * + * Returns 0 on success. Otherwise returns -1 to indicate an error. + */ +int +mlsvc_rpc_init(mlrpc_heapref_t *heapref) +{ + bzero(heapref, sizeof (mlrpc_heapref_t)); + + if ((heapref->heap = mlrpc_heap_create()) == NULL) + return (-1); + + return (0); +} + +/* + * mlsvc_rpc_call + * + * This function should be called by the client RPC interface functions + * to make an RPC call. The remote service is identified by the context + * handle, which should have been initialized with by mlsvc_rpc_bind. + */ +int +mlsvc_rpc_call(struct mlsvc_rpc_context *context, int opnum, void *params, + mlrpc_heapref_t *heapref) +{ + return (mlrpc_c_call(context->binding, opnum, params, heapref)); +} + +/* + * mlsvc_rpc_free + * + * This function should be called by the client RPC interface functions + * to free the heap after an RPC call returns. + */ +void +mlsvc_rpc_free(struct mlsvc_rpc_context *context, mlrpc_heapref_t *heapref) +{ + mlrpc_c_free_heap(context->binding, heapref); +} + +/* + * The following functions provide the callback interface in the + * context handle. + */ +/*ARGSUSED*/ +static int +mlsvc_xa_init(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa, + mlrpc_heap_t *heap) +{ + struct mlndr_stream *recv_mlnds = &mxa->recv_mlnds; + struct mlndr_stream *send_mlnds = &mxa->send_mlnds; + + /* + * If the caller hasn't provided a heap, create one here. + */ + if (heap == 0) { + if ((heap = mlrpc_heap_create()) == 0) + return (-1); + } + + mxa->heap = heap; + + mlnds_initialize(send_mlnds, 0, NDR_MODE_CALL_SEND, heap); + mlnds_initialize(recv_mlnds, 16 * 1024, NDR_MODE_RETURN_RECV, heap); + return (0); +} + +/* + * mlsvc_xa_exchange + * + * This is the entry pointy for an RPC client call exchange with + * a server, which will result in an smbrdr SmbTransact request. + * + * SmbTransact should return the number of bytes received, which + * we record as the PDU size, or a negative error code. + */ +static int +mlsvc_xa_exchange(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa) +{ + struct mlsvc_rpc_context *context = mcli->context; + struct mlndr_stream *recv_mlnds = &mxa->recv_mlnds; + struct mlndr_stream *send_mlnds = &mxa->send_mlnds; + int rc; + + rc = smbrdr_rpc_transact(context->fid, + (char *)send_mlnds->pdu_base_offset, send_mlnds->pdu_size, + (char *)recv_mlnds->pdu_base_offset, recv_mlnds->pdu_max_size); + + if (rc < 0) + recv_mlnds->pdu_size = 0; + else + recv_mlnds->pdu_size = rc; + + return (rc); +} + +/* + * mlsvc_xa_read + * + * This entry point will be invoked if the xa-exchange response contained + * only the first fragment of a multi-fragment response. The RPC client + * code will then make repeated xa-read requests to obtain the remaining + * fragments, which will result in smbrdr SmbReadX requests. + * + * SmbReadX should return the number of bytes received, in which case we + * expand the PDU size to include the received data, or a negative error + * code. + */ +static int +mlsvc_xa_read(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa) +{ + struct mlsvc_rpc_context *context = mcli->context; + struct mlndr_stream *mlnds = &mxa->recv_mlnds; + int len; + int rc; + + if ((len = (mlnds->pdu_max_size - mlnds->pdu_size)) < 0) + return (-1); + + rc = smbrdr_rpc_readx(context->fid, + (char *)mlnds->pdu_base_offset + mlnds->pdu_size, len); + + if (rc < 0) + return (-1); + + mlnds->pdu_size += rc; + + if (mlnds->pdu_size > mlnds->pdu_max_size) { + mlnds->pdu_size = mlnds->pdu_max_size; + return (-1); + } + + return (rc); +} + +/* + * mlsvc_xa_preserve + * + * This function is called to preserve the heap. We save a reference + * to the heap and set the mxa heap pointer to null so that the heap + * will not be discarded when mlsvc_xa_destruct is called. + */ +/*ARGSUSED*/ +static int +mlsvc_xa_preserve(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa, + mlrpc_heapref_t *heapref) +{ + heapref->state = MLRPC_HRST_PRESERVED; + heapref->heap = mxa->heap; + heapref->recv_pdu_buf = (char *)mxa->recv_mlnds.pdu_base_addr; + heapref->send_pdu_buf = (char *)mxa->send_mlnds.pdu_base_addr; + + mxa->heap = NULL; + return (0); +} + +/* + * mlsvc_xa_destruct + * + * This function is called to dispose of the heap. If the heap has + * been preserved via mlsvc_xa_preserve, the mxa heap pointer will + * be null and we assume that the heap will be released later via + * a call to mlsvc_xa_release. Otherwise we free the memory here. + */ +/*ARGSUSED*/ +static int +mlsvc_xa_destruct(struct mlrpc_client *mcli, struct mlrpc_xaction *mxa) +{ + if (mxa->heap) { + mlnds_destruct(&mxa->recv_mlnds); + mlnds_destruct(&mxa->send_mlnds); + mlrpc_heap_destroy(mxa->heap); + } + + return (0); +} + +/* + * mlsvc_xa_release + * + * This function is called, via some indirection, as a result of a + * call to mlsvc_rpc_free. This is where we free the heap memory + * that was preserved during an RPC call. + */ +/*ARGSUSED*/ +static void +mlsvc_xa_release(struct mlrpc_client *mcli, mlrpc_heapref_t *heapref) +{ + if (heapref == NULL) + return; + + if (heapref->state == MLRPC_HRST_PRESERVED) { + free(heapref->recv_pdu_buf); + free(heapref->send_pdu_buf); + mlrpc_heap_destroy(heapref->heap); + } +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_dssetup.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_dssetup.c new file mode 100644 index 000000000000..a20304afa8c2 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_dssetup.c @@ -0,0 +1,168 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Active Directory Setup RPC interface used by Windows2000. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static int dssetup_DsRoleGetPrimaryDomainInfo(void *, struct mlrpc_xaction *); + +static mlrpc_stub_table_t dssetup_stub_table[] = { + { dssetup_DsRoleGetPrimaryDomainInfo, + DSSETUP_OPNUM_DsRoleGetPrimaryDomainInfo }, + {0} +}; + +static mlrpc_service_t dssetup_service = { + "DSSETUP", /* name */ + "Active Directory Setup", /* desc */ + "\\lsarpc", /* endpoint */ + PIPE_LSASS, /* sec_addr_port */ + "3919286a-b10c-11d0-9ba800c04fd92ef5", 0, /* abstract */ + "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */ + 0, /* no bind_instance_size */ + 0, /* no bind_req() */ + 0, /* no unbind_and_close() */ + 0, /* use generic_call_stub() */ + &TYPEINFO(dssetup_interface), /* interface ti */ + dssetup_stub_table /* stub_table */ +}; + +/* + * dssetup_initialize + * + * This function registers the DSSETUP interface with the RPC runtime + * library. It must be called in order to use either the client side + * or the server side functions. + */ +void +dssetup_initialize(void) +{ + (void) mlrpc_register_service(&dssetup_service); +} + +/* + * Request for primary domain information and status. + */ +static int +dssetup_DsRoleGetPrimaryDomainInfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct dssetup_DsRoleGetPrimaryDomainInfo *param = arg; + char dns_domain[MAXHOSTNAMELEN]; + smb_ntdomain_t *di; + DWORD status; + + switch (param->level) { + case DS_ROLE_BASIC_INFORMATION: + break; + + case DS_ROLE_UPGRADE_STATUS: + case DS_ROLE_OP_STATUS: + default: + bzero(param, + sizeof (struct dssetup_DsRoleGetPrimaryDomainInfo)); + param->status = NT_SC_ERROR(NT_STATUS_INVALID_LEVEL); + return (MLRPC_DRC_OK); + } + + di = smb_getdomaininfo(0); + (void) smb_getdomainname(dns_domain, MAXHOSTNAMELEN); + + if (di == NULL) { + bzero(param, + sizeof (struct dssetup_DsRoleGetPrimaryDomainInfo)); + param->status = NT_SC_ERROR(NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + return (MLRPC_DRC_OK); + } + + (void) utf8_strlwr(dns_domain); + + param->ru.info1.role = DS_ROLE_MEMBER_SERVER; + param->ru.info1.flags = 0; + param->ru.info1.nt_domain = + (uint8_t *)MLRPC_HEAP_STRSAVE(mxa, di->domain); + param->ru.info1.dns_domain = + (uint8_t *)MLRPC_HEAP_STRSAVE(mxa, dns_domain); + param->ru.info1.forest = + (uint8_t *)MLRPC_HEAP_STRSAVE(mxa, dns_domain); + bzero(¶m->ru.info1.domain_guid, sizeof (mlrpc_uuid_t)); + + if (param->ru.info1.nt_domain == NULL || + param->ru.info1.dns_domain == NULL || + param->ru.info1.forest == NULL) { + bzero(param, + sizeof (struct dssetup_DsRoleGetPrimaryDomainInfo)); + status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + } else { + status = NT_STATUS_SUCCESS; + } + + param->status = status; + return (MLRPC_DRC_OK); +} + +DECL_FIXUP_STRUCT(dssetup_GetPrimaryDomainInfo_ru); +DECL_FIXUP_STRUCT(dssetup_GetPrimaryDomainInfoRes); +DECL_FIXUP_STRUCT(dssetup_DsRoleGetPrimaryDomainInfo); + +void +fixup_dssetup_DsRoleGetPrimaryDomainInfo( + struct dssetup_DsRoleGetPrimaryDomainInfo *val) +{ + unsigned short size1 = 0; + unsigned short size2 = 0; + unsigned short size3 = 0; + + switch (val->switch_value) { + CASE_INFO_ENT(dssetup_DsRolePrimaryDomInfo, 1); + CASE_INFO_ENT(dssetup_DsRolePrimaryDomInfo, 2); + CASE_INFO_ENT(dssetup_DsRolePrimaryDomInfo, 3); + + default: + return; + }; + + size2 = size1 + (2 * sizeof (DWORD)); + size3 = size2 + sizeof (mlrpcconn_request_hdr_t) + sizeof (DWORD); + + FIXUP_PDU_SIZE(dssetup_GetPrimaryDomainInfo_ru, size1); + FIXUP_PDU_SIZE(dssetup_GetPrimaryDomainInfoRes, size2); + FIXUP_PDU_SIZE(dssetup_DsRoleGetPrimaryDomainInfo, size3); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_handle.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_handle.c new file mode 100644 index 000000000000..a13b7346f529 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_handle.c @@ -0,0 +1,179 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module provides the handles used in the various server-side + * RPC functions. I don't think other systems care about the value in + * the handle. It should be treated as an opaque data block. Handles + * are issued when a service is opened and obsoleted when it is closed. + * We should check incoming RPC requests to ensure that the handle + * being used is associated with the particular service being accessed. + */ + +#include +#include +#include + +#include +#include +#include + +/* + * Each time a handle is allocated it is added to the global handle + * descriptor list because we need some way of identifying the + * interface and domain to which the handle was assigned when it is + * returned on a subsequent RPC. + */ +ms_handle_t mlsvc_handle; +ms_handle_desc_t *mlsvc_desc_list; + +/* + * mlsvc_get_handle + * + * This function returns a handle for use with the server-side RPC + * functions. Every time it is called it will increment the handle + * value and return a pointer to it. On NT, handle[0] always seems + * to be zero and handle[1] increments. The rest seems to be some + * sort of unique value so the local domain SID should do. + * + * The handle is added to the global handle descriptor list with the + * designated ifspec and key tag. + */ +ms_handle_t * +mlsvc_get_handle(ms_ifspec_t ifspec, char *key, DWORD discrim) +{ + ms_handle_desc_t *desc; + nt_sid_t *sid; + + if ((desc = malloc(sizeof (ms_handle_desc_t))) == NULL) + assert(desc); + + sid = nt_domain_local_sid(); + if (mlsvc_handle.handle[1] == 0) { + mlsvc_handle.handle[0] = 0; + mlsvc_handle.handle[1] = 0; + mlsvc_handle.handle[2] = sid->SubAuthority[1]; + mlsvc_handle.handle[3] = sid->SubAuthority[2]; + mlsvc_handle.handle[4] = sid->SubAuthority[3]; + } + + ++mlsvc_handle.handle[1]; + + bcopy(&mlsvc_handle, &desc->handle, sizeof (ms_handle_t)); + desc->ifspec = ifspec; + desc->discrim = discrim; + desc->next = mlsvc_desc_list; + mlsvc_desc_list = desc; + + if (key) + (void) strlcpy(desc->key, key, MLSVC_HANDLE_KEY_MAX); + else + desc->key[0] = '\0'; + + return (&mlsvc_handle); +} + + +/* + * mlsvc_put_handle + * + * Remove a handle from the global handle descriptor list and free the + * memory it was using. If the list contained the descriptor, a value + * of 0 is returned. Otherwise -1 is returned. + */ +int +mlsvc_put_handle(ms_handle_t *handle) +{ + ms_handle_desc_t *desc; + ms_handle_desc_t **ppdesc = &mlsvc_desc_list; + + assert(handle); + + while (*ppdesc) { + desc = *ppdesc; + + if (bcmp(&desc->handle, handle, sizeof (ms_handle_t)) == 0) { + *ppdesc = desc->next; + free(desc); + return (0); + } + + ppdesc = &(*ppdesc)->next; + } + + return (-1); +} + + +/* + * mlsvc_validate_handle + * + * Lookup a handle in the global handle descriptor list. If the handle + * is in the list, a pointer to the descriptor is returned. Otherwise + * a null pointer is returned. + */ +int +mlsvc_validate_handle(ms_handle_t *handle, char *key) +{ + ms_handle_desc_t *desc; + + assert(handle); + assert(key); + + if ((desc = mlsvc_lookup_handle(handle)) == 0) + return (NULL); + + if (strcmp(desc->key, key)) + return (NULL); + + return (1); +} + + +/* + * mlsvc_lookup_handle + * + * Lookup a handle in the global handle descriptor list. If the handle + * is in the list, a pointer to the descriptor is returned. Otherwise + * a null pointer is returned. + */ +ms_handle_desc_t * +mlsvc_lookup_handle(ms_handle_t *handle) +{ + ms_handle_desc_t *desc = mlsvc_desc_list; + + assert(handle); + + while (desc) { + if (bcmp(&desc->handle, handle, sizeof (ms_handle_t)) == 0) + return (desc); + + desc = desc->next; + } + + return (NULL); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c new file mode 100644 index 000000000000..fc175549499e --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c @@ -0,0 +1,65 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include + +#include +#include +#include +#include + +void dssetup_initialize(void); +void srvsvc_initialize(void); +void wkssvc_initialize(void); +void lsarpc_initialize(void); +void logr_initialize(void); +void netr_initialize(void); +void samr_initialize(void); +void svcctl_initialize(void); +void winreg_initialize(void); + +/* + * All mlrpc initialization is invoked from here. + * Returns 0 upon success. Otherwise, returns -1. + */ +int +mlsvc_init(void) +{ + srvsvc_initialize(); + wkssvc_initialize(); + lsarpc_initialize(); + netr_initialize(); + dssetup_initialize(); + samr_initialize(); + svcctl_initialize(); + winreg_initialize(); + logr_initialize(); + + (void) sam_init(); + return (0); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_logr.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_logr.c new file mode 100644 index 000000000000..14c13a031556 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_logr.c @@ -0,0 +1,574 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Event Log Service RPC (LOGR) interface definition. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define FWD +1 +#define REW -1 + +/* define the logging structs here - from syslog.h */ +#define NMSGMASK 1023 +#define MAXMSGLEN 223 +#define LOGBUFSIZE (MAXMSGLEN + 1) + +#define LOG_PRI(p) ((p) & LOG_PRIMASK) + +typedef struct log_entry { + struct timeval timestamp; /* time of log entry */ + int pri; /* message priority */ + char msg[LOGBUFSIZE]; /* log message text */ + int thread_id; /* calling function thread ID */ + char thread_name[12]; /* calling function thread name */ + unsigned long caller_adr; /* calling function address */ +} log_entry_t; + +typedef struct log_info { + log_entry_t entry[NMSGMASK+1]; + int ix; + int alarm_on; + int alarm_disable; + int timestamp_level; + int disp_msg_len; + int prefix_len; +} log_info_t; + +/* + * READ flags for EventLogRead + * + * EVENTLOG_SEEK_READ + * The read operation proceeds from the record specified by the + * dwRecordOffset parameter. This flag cannot be used with + * EVENTLOG_SEQUENTIAL_READ. + * + * EVENTLOG_SEQUENTIAL_READ + * The read operation proceeds sequentially from the last call to the + * ReadEventLog function using this handle. This flag cannot be used + * with EVENTLOG_SEEK_READ. + * + * If the buffer is large enough, more than one record can be read at + * the specified seek position; you must specify one of the following + * flags to indicate the direction for successive read operations. + * + * EVENTLOG_FORWARDS_READ + * The log is read in chronological order. This flag cannot be used + * with EVENTLOG_BACKWARDS_READ. + * + * EVENTLOG_BACKWARDS_READ + * The log is read in reverse chronological order. This flag cannot be + * used with EVENTLOG_FORWARDS_READ. + */ +#define EVENTLOG_SEQUENTIAL_READ 0x0001 +#define EVENTLOG_SEEK_READ 0x0002 +#define EVENTLOG_FORWARDS_READ 0x0004 +#define EVENTLOG_BACKWARDS_READ 0x0008 + +/* + * The types of events that can be logged. + */ +#define EVENTLOG_SUCCESS 0x0000 +#define EVENTLOG_ERROR_TYPE 0x0001 +#define EVENTLOG_WARNING_TYPE 0x0002 +#define EVENTLOG_INFORMATION_TYPE 0x0004 +#define EVENTLOG_AUDIT_SUCCESS 0x0008 +#define EVENTLOG_AUDIT_FAILURE 0x0010 + +/* + * Event Identifiers + * + * Event identifiers uniquely identify a particular event. Each event + * source can define its own numbered events and the description strings + * to which they are mapped. Event viewers can present these strings to + * the user. They should help the user understand what went wrong and + * suggest what actions to take. Direct the description at users solving + * their own problems, not at administrators or support technicians. + * Make the description clear and concise and avoid culture-specific + * phrases. + * + * The following diagram illustrates the format of an event identifier. + * + * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +---+-+-+-----------------------+-------------------------------+ + * |Sev|C|R| Facility | Code | + * +---+-+-+-----------------------+-------------------------------+ + * + * Sev + * Indicates the severity. This is one of the following values: + * 00 - Success + * 01 - Informational + * 10 - Warning + * 11 - Error + * + * C + * Indicates a customer code (1) or a system code (0). + * R + * Reserved bit. + * Facility + * Facility code. + * Code + * Status code for the facility. + */ +#define EVENTID_SEVERITY_SUCCESS 0x00000000 +#define EVENTID_SEVERITY_INFO 0x40000000 +#define EVENTID_SEVERITY_WARNING 0x80000000 +#define EVENTID_SEVERITY_ERROR 0xC0000000 + +#define EVENTID_SYSTEM_CODE 0x00000000 +#define EVENTID_CUSTOMER_CODE 0x20000000 + +#define MAX_SRCNAME_LEN 20 +#define LOGR_KEY "LogrOpen" + + +static int logr_s_EventLogClose(void *, struct mlrpc_xaction *); +static int logr_s_EventLogQueryCount(void *, struct mlrpc_xaction *); +static int logr_s_EventLogGetOldestRec(void *, struct mlrpc_xaction *); +static int logr_s_EventLogOpen(void *, struct mlrpc_xaction *); +static int logr_s_EventLogRead(void *, struct mlrpc_xaction *); + +static mlrpc_stub_table_t logr_stub_table[] = { + { logr_s_EventLogClose, LOGR_OPNUM_EventLogClose }, + { logr_s_EventLogQueryCount, LOGR_OPNUM_EventLogQueryCount }, + { logr_s_EventLogGetOldestRec, LOGR_OPNUM_EventLogGetOldestRec }, + { logr_s_EventLogOpen, LOGR_OPNUM_EventLogOpen }, + { logr_s_EventLogRead, LOGR_OPNUM_EventLogRead }, + {0} +}; + +static mlrpc_service_t logr_service = { + "LOGR", /* name */ + "Event Log Service", /* desc */ + "\\eventlog", /* endpoint */ + PIPE_NTSVCS, /* sec_addr_port */ + "82273fdc-e32a-18c3-3f78827929dc23ea", 0, /* abstract */ + "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */ + 0, /* no bind_instance_size */ + 0, /* no bind_req() */ + 0, /* no unbind_and_close() */ + 0, /* use generic_call_stub() */ + &TYPEINFO(logr_interface), /* interface ti */ + logr_stub_table /* stub_table */ +}; + +typedef struct { + DWORD tot_recnum; + DWORD last_sentrec; + char first_read; + struct log_info log; +} read_data_t; + +static char logr_sysname[SYS_NMLN]; +static char hostname[MAXHOSTNAMELEN]; +static mts_wchar_t wcs_hostname[MAXHOSTNAMELEN]; +static int hostname_len = 0; +static mts_wchar_t wcs_srcname[MAX_SRCNAME_LEN]; +static int srcname_len = 0; +static int str_offs, sh_len; + +/* + * logr_initialize + * + * This function registers the LOGR RPC interface with the RPC runtime + * library. It must be called in order to use either the client side + * or the server side functions. + */ +void +logr_initialize(void) +{ + struct utsname name; + char *sysname; + + if (uname(&name) < 0) + sysname = "Solaris"; + else + sysname = name.sysname; + + (void) strlcpy(logr_sysname, sysname, SYS_NMLN); + (void) mlrpc_register_service(&logr_service); +} + +/* + * logr_s_EventLogClose + * + * This is a request to close the LOGR interface specified by the + * handle. Free the handle and its associated resources and zero out + * the result handle for the client. + */ +/*ARGSUSED*/ +static int +logr_s_EventLogClose(void *arg, struct mlrpc_xaction *mxa) +{ + struct logr_EventLogClose *param = arg; + ms_handle_desc_t *desc; + read_data_t *data; + + if ((desc = mlsvc_lookup_handle((ms_handle_t *)¶m->handle)) == 0) { + param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); + return (MLRPC_DRC_OK); + } + + data = (read_data_t *)(desc->discrim); + free(data); + (void) mlsvc_put_handle((ms_handle_t *)¶m->handle); + + bzero(¶m->result_handle, sizeof (logr_handle_t)); + param->status = NT_STATUS_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * logr_s_EventLogOpen + * + * This is a request to open the event log. + * + * Return a handle for use with subsequent event log requests. + */ +/*ARGSUSED*/ +static int +logr_s_EventLogOpen(void *arg, struct mlrpc_xaction *mxa) +{ + struct logr_EventLogOpen *param = arg; + ms_handle_t *handle; + int log_enable = 0; + + smb_config_rdlock(); + log_enable = smb_config_getyorn(SMB_CI_LOGR_ENABLE); + smb_config_unlock(); + + if (log_enable == 0) { + bzero(¶m->handle, sizeof (logr_handle_t)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); + } + + handle = mlsvc_get_handle(MLSVC_IFSPEC_LOGR, LOGR_KEY, 0); + bcopy(handle, ¶m->handle, sizeof (logr_handle_t)); + + if (hostname_len == 0) { + if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0) { + bzero(¶m->handle, sizeof (logr_handle_t)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); + } + + hostname_len = (strlen(hostname) + 1) * 2; + (void) mts_mbstowcs(wcs_hostname, hostname, hostname_len / 2); + srcname_len = (strlen(logr_sysname) + 1) * 2; + (void) mts_mbstowcs(wcs_srcname, logr_sysname, srcname_len / 2); + sh_len = srcname_len + hostname_len; + str_offs = 12 * sizeof (DWORD) + 4 * sizeof (WORD) + sh_len; + } + + param->status = NT_STATUS_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * logr_get_snapshot + * + * Allocate memory and make a copy, as a snapshot, from system log. + */ +static read_data_t * +logr_get_snapshot(void) +{ + read_data_t *data = NULL; + return (data); +} + +/* + * logr_s_EventLogQueryCount + * + * take a snapshot from system log, assign it to the given handle. + * return number of log entries in the snapshot as result of RPC + * call. + */ +/*ARGSUSED*/ +static int +logr_s_EventLogQueryCount(void *arg, struct mlrpc_xaction *mxa) +{ + struct logr_EventLogQueryCount *param = arg; + ms_handle_desc_t *desc; + read_data_t *data; + + if ((desc = mlsvc_lookup_handle((ms_handle_t *)¶m->handle)) == 0) { + param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); + return (MLRPC_DRC_OK); + } + + if ((data = logr_get_snapshot()) == NULL) { + param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + return (MLRPC_DRC_OK); + } + + desc->discrim = (DWORD)data; + param->rec_num = data->tot_recnum; + param->status = NT_STATUS_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * logr_s_EventLogGetOldestRec + * + * Return oldest record number in the snapshot as result of RPC call. + */ +/*ARGSUSED*/ +static int +logr_s_EventLogGetOldestRec(void *arg, struct mlrpc_xaction *mxa) +{ + struct logr_EventLogGetOldestRec *param = arg; + ms_handle_desc_t *desc; + read_data_t *data; + + desc = mlsvc_lookup_handle((ms_handle_t *)¶m->handle); + if (desc == NULL) { + param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); + return (MLRPC_DRC_OK); + } + + data = (read_data_t *)desc->discrim; + param->oldest_rec = data->log.ix - data->tot_recnum; + param->status = NT_STATUS_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * set_event_typeid + * + * Map the local system log priority to the event type and event ID + * for Windows events. + */ +void +set_event_typeid(int le_pri, WORD *etype, DWORD *eid) +{ + switch (LOG_PRI(le_pri)) { + case LOG_EMERG: + case LOG_ALERT: + case LOG_CRIT: + case LOG_ERR: + *eid = EVENTID_SEVERITY_ERROR; + *etype = EVENTLOG_ERROR_TYPE; + break; + case LOG_WARNING: + *eid = EVENTID_SEVERITY_WARNING; + *etype = EVENTLOG_WARNING_TYPE; + break; + case LOG_NOTICE: + case LOG_INFO: + case LOG_DEBUG: + *eid = EVENTID_SEVERITY_INFO; + *etype = EVENTLOG_INFORMATION_TYPE; + break; + default: + *eid = EVENTID_SEVERITY_SUCCESS; + *etype = EVENTLOG_SUCCESS; + } +} + +static log_entry_t * +log_get_entry(struct log_info *linfo, int entno) +{ + return (&linfo->entry[entno]); +} + +/* + * Fill a Windows event record based on a local system log record. + */ +static void +set_logrec(log_entry_t *le, DWORD recno, logr_record_t *rec) +{ + int len; + + rec->Length1 = sizeof (logr_record_t); + rec->Reserved = 0x654C664C; + rec->RecordNumber = recno; + rec->TimeGenerated = le->timestamp.tv_sec; + rec->TimeWritten = le->timestamp.tv_sec; + set_event_typeid(le->pri, &rec->EventType, &rec->EventID); + rec->NumStrings = 1; + rec->EventCategory = 0; + rec->ReservedFlags = 0; + rec->ClosingRecordNumber = 0; + rec->StringOffset = str_offs; + rec->UserSidLength = 0; + rec->UserSidOffset = sizeof (logr_record_t) - sizeof (DWORD); + rec->DataLength = 0; + rec->DataOffset = sizeof (logr_record_t) - sizeof (DWORD); + bzero(rec->info, LOGR_INFOLEN); + (void) memcpy(rec->info, wcs_srcname, srcname_len); + (void) memcpy(rec->info + srcname_len, wcs_hostname, hostname_len); + + len = (LOGR_INFOLEN - sh_len) / 2; + + if ((strlen(le->msg) + 1) < len) + len = strlen(le->msg) + 1; + + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + (void) mts_mbstowcs((mts_wchar_t *)(rec->info+sh_len), le->msg, len); + rec->Length2 = sizeof (logr_record_t); +} + +/* + * logr_s_EventLogRead + * + * Reads a whole number of entries from system log. The function can + * read log entries in chronological or reverse chronological order. + */ +/*ARGSUSED*/ +static int +logr_s_EventLogRead(void *arg, struct mlrpc_xaction *mxa) +{ + struct logr_EventLogRead *param = arg; + ms_handle_desc_t *desc; + read_data_t *rdata; + log_entry_t *le; + DWORD ent_no, ent_num, ent_remain; + logr_record_t *rec; + BYTE *buf; + int dir, ent_per_req; + + desc = mlsvc_lookup_handle((ms_handle_t *)¶m->handle); + if (desc == NULL) { + param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); + return (MLRPC_DRC_OK); + } + + rdata = (read_data_t *)(desc->discrim); + if (rdata == 0) { + if ((rdata = logr_get_snapshot()) == NULL) { + param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + return (MLRPC_DRC_OK); + } + + desc->discrim = (DWORD)rdata; + } + + dir = (param->read_flags & EVENTLOG_FORWARDS_READ) ? FWD : REW; + + if (param->read_flags & EVENTLOG_SEEK_READ) { + rdata->last_sentrec = param->rec_offset; + } else if (rdata->first_read) { + /* + * set last record number which is read for + * the first iteration of sequential read. + */ + rdata->last_sentrec = (dir == FWD) + ? (rdata->log.ix - rdata->tot_recnum) : rdata->log.ix; + } + + ent_remain = (dir == FWD) + ? (rdata->tot_recnum - rdata->last_sentrec) : rdata->last_sentrec; + + /* + * function should return as many whole log entries as + * will fit in the buffer; it should not return partial + * entries, even if there is room in the buffer. + */ + ent_per_req = param->nbytes_to_read / sizeof (logr_record_t); + if (ent_remain > ent_per_req) + ent_remain = ent_per_req; + + if (ent_remain == 0) { + /* + * Send this error to Windows client so that it + * can figure out that there is no more record + * to read. + */ + param->sent_size = 0; + param->unknown = 0; + param->status = NT_SC_ERROR(NT_STATUS_END_OF_FILE); + return (MLRPC_DRC_OK); + } + + buf = (param->read_flags & EVENTLOG_SEEK_READ) + ? param->ru.rec : param->ru.recs; + + for (ent_num = 0, ent_no = rdata->last_sentrec; + ent_num < ent_remain; ent_num++, ent_no += dir) { + le = log_get_entry(&rdata->log, ent_no & NMSGMASK); + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + rec = (logr_record_t *)buf; + set_logrec(le, ent_no, rec); + buf += sizeof (logr_record_t); + } + rdata->last_sentrec = ent_no; + rdata->first_read = 0; + + param->sent_size = sizeof (logr_record_t) * ent_remain; + param->unknown = 0; + param->status = NT_STATUS_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * Declare extern references. + */ +DECL_FIXUP_STRUCT(logr_read_u); +DECL_FIXUP_STRUCT(logr_read_info); +DECL_FIXUP_STRUCT(logr_EventLogRead); + +/* + * Patch the logr_EventLogRead union. + * This function is called from mlsvc_logr_ndr.c + */ +void +fixup_logr_EventLogRead(void *xarg) +{ + struct logr_EventLogRead *arg = (struct logr_EventLogRead *)xarg; + unsigned short size1 = 0; + unsigned short size2 = 0; + unsigned short size3 = 0; + DWORD nbr = arg->nbytes_to_read; + + switch (nbr) { + case 0: + size1 = 0; + break; + default: + size1 = nbr; + break; + }; + + size2 = size1 + (2 * sizeof (DWORD)); + size3 = size2 + sizeof (mlrpcconn_request_hdr_t) + sizeof (DWORD); + + FIXUP_PDU_SIZE(logr_read_u, size1); + FIXUP_PDU_SIZE(logr_read_info, size2); + FIXUP_PDU_SIZE(logr_EventLogRead, size3); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_lsa.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_lsa.c new file mode 100644 index 000000000000..6aa4d716fea6 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_lsa.c @@ -0,0 +1,1385 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * Local Security Authority RPC (LSARPC) server-side interface definition. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct local_group_table { + WORD sid_name_use; + WORD domain_ix; + char *sid; + char *name; +}; + +static int lsarpc_s_CloseHandle(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_QuerySecurityObject(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_EnumAccounts(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_EnumTrustedDomain(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_OpenAccount(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_EnumPrivsAccount(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_LookupPrivValue(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_LookupPrivName(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_LookupPrivDisplayName(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_QueryInfoPolicy(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_OpenDomainHandle(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_OpenDomainHandle(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_LookupSids(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_LookupNames(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_GetConnectedUser(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_LookupSids2(void *arg, struct mlrpc_xaction *); +static int lsarpc_s_LookupNames2(void *arg, struct mlrpc_xaction *); + +static int lsarpc_s_PrimaryDomainInfo(struct mslsa_PrimaryDomainInfo *, + struct mlrpc_xaction *); +static int lsarpc_s_AccountDomainInfo(struct mslsa_AccountDomainInfo *, + struct mlrpc_xaction *); +static int lsarpc_s_LookupNtSid(struct mlrpc_xaction *, nt_sid_t *, + smb_userinfo_t *, struct mslsa_name_entry *, int); +static int lsarpc_s_LookupLocalSid(struct mlrpc_xaction *, nt_sid_t *, + smb_userinfo_t *, struct mslsa_name_entry *); +static int lsarpc_s_LookupBuiltinSid(struct mlrpc_xaction *, nt_sid_t *, + smb_userinfo_t *, struct mslsa_name_entry *); +static int lsarpc_s_UnknownSid(struct mlrpc_xaction *, nt_sid_t *, + smb_userinfo_t *, struct mslsa_name_entry *); +static int lsarpc_s_UpdateDomainTable(struct mlrpc_xaction *, + smb_userinfo_t *, struct mslsa_domain_table *, DWORD *); + +static int lsarpc_w2k_enable; + +static mlrpc_stub_table_t lsarpc_stub_table[] = { + { lsarpc_s_CloseHandle, LSARPC_OPNUM_CloseHandle }, + { lsarpc_s_QuerySecurityObject, LSARPC_OPNUM_QuerySecurityObject }, + { lsarpc_s_EnumAccounts, LSARPC_OPNUM_EnumerateAccounts }, + { lsarpc_s_EnumTrustedDomain, LSARPC_OPNUM_EnumTrustedDomain }, + { lsarpc_s_OpenAccount, LSARPC_OPNUM_OpenAccount }, + { lsarpc_s_EnumPrivsAccount, LSARPC_OPNUM_EnumPrivsAccount }, + { lsarpc_s_LookupPrivValue, LSARPC_OPNUM_LookupPrivValue }, + { lsarpc_s_LookupPrivName, LSARPC_OPNUM_LookupPrivName }, + { lsarpc_s_LookupPrivDisplayName, LSARPC_OPNUM_LookupPrivDisplayName }, + { lsarpc_s_QueryInfoPolicy, LSARPC_OPNUM_QueryInfoPolicy }, + { lsarpc_s_OpenDomainHandle, LSARPC_OPNUM_OpenPolicy }, + { lsarpc_s_OpenDomainHandle, LSARPC_OPNUM_OpenPolicy2 }, + { lsarpc_s_LookupSids, LSARPC_OPNUM_LookupSids }, + { lsarpc_s_LookupNames, LSARPC_OPNUM_LookupNames }, + { lsarpc_s_GetConnectedUser, LSARPC_OPNUM_GetConnectedUser }, + { lsarpc_s_LookupSids2, LSARPC_OPNUM_LookupSids2 }, + { lsarpc_s_LookupNames2, LSARPC_OPNUM_LookupNames2 }, + {0} +}; + +static mlrpc_service_t lsarpc_service = { + "LSARPC", /* name */ + "Local Security Authority", /* desc */ + "\\lsarpc", /* endpoint */ + PIPE_LSASS, /* sec_addr_port */ + "12345778-1234-abcd-ef000123456789ab", 0, /* abstract */ + "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */ + 0, /* no bind_instance_size */ + 0, /* no bind_req() */ + 0, /* no unbind_and_close() */ + 0, /* use generic_call_stub() */ + &TYPEINFO(lsarpc_interface), /* interface ti */ + lsarpc_stub_table /* stub_table */ +}; + +/* + * Windows 2000 interface. + */ +static mlrpc_service_t lsarpc_w2k_service = { + "LSARPC_W2K", /* name */ + "Local Security Authority", /* desc */ + "\\lsarpc", /* endpoint */ + PIPE_LSASS, /* sec_addr_port */ + "3919286a-b10c-11d0-9ba800c04fd92ef5", 0, /* abstract */ + "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */ + 0, /* no bind_instance_size */ + 0, /* no bind_req() */ + 0, /* no unbind_and_close() */ + 0, /* use generic_call_stub() */ + &TYPEINFO(lsarpc_interface), /* interface ti */ + lsarpc_stub_table /* stub_table */ +}; + +/* + * lsarpc_initialize + * + * This function registers the LSA RPC interface with the RPC runtime + * library. It must be called in order to use either the client side + * or the server side functions. + */ +void +lsarpc_initialize(void) +{ + (void) mlrpc_register_service(&lsarpc_service); + + if (lsarpc_w2k_enable) + (void) mlrpc_register_service(&lsarpc_w2k_service); +} + +/* + * lsarpc_s_OpenDomainHandle opnum=0x06 + * + * This is a request to open the LSA (OpenPolicy and OpenPolicy2). + * The client is looking for an LSA domain handle. Handles appear to + * be a 20 byte opaque object with the top 4 bytes all zero. As it is + * opaque to the client, we can put anything we like in it. Real handles + * do appear to contain a sequence number which is incremented when a + * new handle is issued. However, we don't really care about that + * (yet). Always return MLRPC_DRC_OK. + */ +/*ARGSUSED*/ +static int +lsarpc_s_OpenDomainHandle(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslsa_OpenPolicy2 *param = arg; + + bzero(¶m->domain_handle, sizeof (mslsa_handle_t)); + (void) strcpy((char *)¶m->domain_handle.hand2, "DomainHandle"); + param->status = 0; + + return (MLRPC_DRC_OK); +} + +/* + * lsarpc_s_CloseHandle opnum=0x00 + * + * This is a request to close the LSA interface specified by the handle. + * We don't track handles (yet), so just zero out the handle and return + * MLRPC_DRC_OK. Setting the handle to zero appears to be standard + * behaviour and someone may rely on it, i.e. we do on the client side. + */ +/*ARGSUSED*/ +static int +lsarpc_s_CloseHandle(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslsa_CloseHandle *param = arg; + + bzero(¶m->result_handle, sizeof (param->result_handle)); + param->status = 0; + + return (MLRPC_DRC_OK); +} + +/* + * lsarpc_s_QuerySecurityObject + */ +/*ARGSUSED*/ +static int +lsarpc_s_QuerySecurityObject(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslsa_QuerySecurityObject *param = arg; + + bzero(param, sizeof (struct mslsa_QuerySecurityObject)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + + return (MLRPC_DRC_OK); +} + +/* + * lsarpc_s_EnumAccounts + * + * Enumerate the list of local accounts SIDs. The client should supply + * a valid OpenPolicy2 handle. The enum_context is used to support + * multiple enumeration calls to obtain the complete list of SIDs. + * It should be set to 0 on the first call and passed unchanged on + * subsequent calls until there are no more accounts - the server will + * return NT_SC_WARNING(MLSVC_NO_MORE_DATA). + * + * For now just set the status to access-denied. Note that we still have + * to provide a valid address for enum_buf because it's a reference and + * the marshalling rules require that references must not be null. + * The enum_context is used to support multiple + */ +static int +lsarpc_s_EnumAccounts(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslsa_EnumerateAccounts *param = arg; + struct mslsa_EnumAccountBuf *enum_buf; + + bzero(param, sizeof (struct mslsa_EnumerateAccounts)); + + enum_buf = MLRPC_HEAP_NEW(mxa, struct mslsa_EnumAccountBuf); + if (enum_buf == NULL) { + param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + return (MLRPC_DRC_OK); + } + + bzero(enum_buf, sizeof (struct mslsa_EnumAccountBuf)); + param->enum_buf = enum_buf; + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); +} + + +/* + * lsarpc_s_EnumTrustedDomain + * + * This is the server side function for handling requests to enumerate + * the list of trusted domains: currently held in the NT domain database. + * This call requires an OpenPolicy2 handle. The enum_context is used to + * support multiple enumeration calls to obtain the complete list. + * It should be set to 0 on the first call and passed unchanged on + * subsequent calls until there are no more accounts - the server will + * return NT_SC_WARNING(MLSVC_NO_MORE_DATA). + * + * For now just set the status to access-denied. Note that we still have + * to provide a valid address for enum_buf because it's a reference and + * the marshalling rules require that references must not be null. + */ +static int +lsarpc_s_EnumTrustedDomain(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslsa_EnumTrustedDomain *param = arg; + struct mslsa_EnumTrustedDomainBuf *enum_buf; + + bzero(param, sizeof (struct mslsa_EnumTrustedDomain)); + + enum_buf = MLRPC_HEAP_NEW(mxa, struct mslsa_EnumTrustedDomainBuf); + if (enum_buf == NULL) { + param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + return (MLRPC_DRC_OK); + } + + bzero(enum_buf, sizeof (struct mslsa_EnumTrustedDomainBuf)); + param->enum_buf = enum_buf; + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); +} + + +/* + * lsarpc_s_OpenAccount + * + * This is a request to open an account handle. This function hasn't + * been tested. It is just a template in case some server somewhere + * makes this call. See lsarpc_s_OpenDomainHandle for more information. + */ +/*ARGSUSED*/ +static int +lsarpc_s_OpenAccount(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslsa_OpenAccount *param = arg; + + if (param->handle.hand1 != 0 || + strcmp("DomainHandle", (char *)¶m->handle.hand2)) { + param->status = NT_SC_ERROR(ERROR_NO_SUCH_DOMAIN); + } else { + (void) strcpy((char *)¶m->account_handle.hand2, + "AccountHandle"); + param->status = 0; + } + + return (MLRPC_DRC_OK); +} + + +/* + * lsarpc_s_EnumPrivsAccount + * + * This is the server side function for handling requests for account + * privileges. For now just set the status to not-supported status and + * return MLRPC_DRC_OK. Note that we still have to provide a valid + * address for enum_buf because it's a reference and the marshalling + * rules require that references must not be null. + */ +/*ARGSUSED*/ +static int +lsarpc_s_EnumPrivsAccount(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslsa_EnumPrivsAccount *param = arg; + + bzero(param, sizeof (struct mslsa_EnumPrivsAccount)); + param->status = NT_SC_ERROR(NT_STATUS_NOT_SUPPORTED); + return (MLRPC_DRC_OK); +} + +/* + * lsarpc_s_LookupPrivValue + * + * Server side function used to map a privilege name to a locally unique + * identifier (LUID). + */ +/*ARGSUSED*/ +static int +lsarpc_s_LookupPrivValue(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslsa_LookupPrivValue *param = arg; + smb_privinfo_t *pi; + + if ((pi = smb_priv_getbyname((char *)param->name.str)) == NULL) { + bzero(param, sizeof (struct mslsa_LookupPrivValue)); + param->status = NT_SC_ERROR(NT_STATUS_NO_SUCH_PRIVILEGE); + return (MLRPC_DRC_OK); + } + + param->luid.low_part = pi->id; + param->luid.high_part = 0; + param->status = NT_STATUS_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * lsarpc_s_LookupPrivName + * + * Server side function used to map a locally unique identifier (LUID) + * to the appropriate privilege name string. + */ +static int +lsarpc_s_LookupPrivName(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslsa_LookupPrivName *param = arg; + smb_privinfo_t *pi; + int rc; + + if ((pi = smb_priv_getbyvalue(param->luid.low_part)) == NULL) { + bzero(param, sizeof (struct mslsa_LookupPrivName)); + param->status = NT_SC_ERROR(NT_STATUS_NO_SUCH_PRIVILEGE); + return (MLRPC_DRC_OK); + } + + param->name = MLRPC_HEAP_NEW(mxa, mslsa_string_t); + if (param->name == NULL) { + bzero(param, sizeof (struct mslsa_LookupPrivName)); + param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + return (MLRPC_DRC_OK); + } + + rc = mlsvc_string_save((ms_string_t *)param->name, pi->name, mxa); + if (rc == 0) { + bzero(param, sizeof (struct mslsa_LookupPrivName)); + param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + return (MLRPC_DRC_OK); + } + + param->status = NT_STATUS_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * lsarpc_s_LookupPrivDisplayName + * + * This is the server side function for handling requests for account + * privileges. For now just set the status to not-supported status and + * return MLRPC_DRC_OK. + */ +static int +lsarpc_s_LookupPrivDisplayName(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslsa_LookupPrivDisplayName *param = arg; + smb_privinfo_t *pi; + int rc; + + if ((pi = smb_priv_getbyname((char *)param->name.str)) == NULL) { + bzero(param, sizeof (struct mslsa_LookupPrivDisplayName)); + param->status = NT_SC_ERROR(NT_STATUS_NO_SUCH_PRIVILEGE); + return (MLRPC_DRC_OK); + } + + param->display_name = MLRPC_HEAP_NEW(mxa, mslsa_string_t); + if (param->display_name == NULL) { + bzero(param, sizeof (struct mslsa_LookupPrivDisplayName)); + param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + return (MLRPC_DRC_OK); + } + + rc = mlsvc_string_save((ms_string_t *)param->display_name, + pi->display_name, mxa); + + if (rc == 0) { + bzero(param, sizeof (struct mslsa_LookupPrivDisplayName)); + param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + return (MLRPC_DRC_OK); + } + + param->language_ret = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); + param->status = NT_STATUS_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * lsarpc_s_GetConnectedUser + * + * This is still guesswork. Netmon doesn't know about this + * call and I'm not really sure what it is intended to achieve. + * Another packet capture application, Ethereal, calls this RPC as + * GetConnectedUser. + * We will receive our own hostname in the request and it appears + * we should respond with an account name and the domain name of connected + * user from the client that makes this call. + */ +static int +lsarpc_s_GetConnectedUser(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslsa_GetConnectedUser *param = arg; + smb_dr_user_ctx_t *user_ctx = mxa->context->user_ctx; + DWORD status = NT_STATUS_SUCCESS; + int rc1; + int rc2; + + if (user_ctx == NULL) { + bzero(param, sizeof (struct mslsa_GetConnectedUser)); + status = NT_SC_ERROR(NT_STATUS_NO_TOKEN); + param->status = status; + return (MLRPC_DRC_OK); + } + + if (smb_getdomaininfo(0) == NULL) { + bzero(param, sizeof (struct mslsa_GetConnectedUser)); + status = NT_SC_ERROR(NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + param->status = status; + return (MLRPC_DRC_OK); + } + + param->owner = MLRPC_HEAP_NEW(mxa, struct mslsa_string_desc); + param->domain = MLRPC_HEAP_NEW(mxa, struct mslsa_DomainName); + if (param->owner == NULL || param->domain == NULL) { + status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + param->status = status; + return (MLRPC_DRC_OK); + } + + param->domain->name = MLRPC_HEAP_NEW(mxa, struct mslsa_string_desc); + if (param->domain->name == NULL) { + status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + param->status = status; + return (MLRPC_DRC_OK); + } + + rc1 = mlsvc_string_save((ms_string_t *)param->owner, + user_ctx->du_account, mxa); + + rc2 = mlsvc_string_save((ms_string_t *)param->domain->name, + user_ctx->du_domain, mxa); + + if (rc1 == 0 || rc2 == 0) + status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + + param->status = status; + return (MLRPC_DRC_OK); +} + + +/* + * lsarpc_s_QueryInfoPolicy + * + * This is the server side function for handling LSA information policy + * queries. Currently, we only support primary domain and account + * domain queries. This is just a front end to switch on the request + * and hand it off to the appropriate function to actually deal with + * obtaining and building the response. + */ +static int +lsarpc_s_QueryInfoPolicy(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslsa_QueryInfoPolicy *param = arg; + struct mslsa_PolicyInfo *info; + int result; + + info = (struct mslsa_PolicyInfo *)MLRPC_HEAP_MALLOC( + mxa, sizeof (struct mslsa_PolicyInfo)); + + info->switch_value = param->info_class; + + switch (param->info_class) { + case MSLSA_POLICY_PRIMARY_DOMAIN_INFO: + result = lsarpc_s_PrimaryDomainInfo(&info->ru.pd_info, mxa); + break; + + case MSLSA_POLICY_ACCOUNT_DOMAIN_INFO: + result = lsarpc_s_AccountDomainInfo(&info->ru.ad_info, mxa); + break; + + default: + result = (MLRPC_DRC_FAULT_PARAM_0_UNIMPLEMENTED); + break; + } + + param->info = info; + param->status = NT_STATUS_SUCCESS; + return (result); +} + + +/* + * lsarpc_s_PrimaryDomainInfo + * + * This is the service side function for handling primary domain policy + * queries. This will return the primary domain name and sid. This is + * currently a pass through interface so all we do is act as a proxy + * between the client and the DC. If there is no session, fake up the + * response with default values - useful for share mode. + * + * If the server name matches the local hostname, we should return + * the local domain SID. + */ +static int +lsarpc_s_PrimaryDomainInfo(struct mslsa_PrimaryDomainInfo *pd_info, + struct mlrpc_xaction *mxa) +{ + int security_mode; + smb_ntdomain_t *di; + nt_domain_t *ntdp; + nt_sid_t *sid; + char domain_name[MLSVC_DOMAIN_NAME_MAX]; + char *name; + DWORD status; + int rc; + + status = NT_STATUS_SUCCESS; + + security_mode = smb_get_security_mode(); + + if (security_mode != SMB_SECMODE_DOMAIN) { + rc = smb_gethostname(domain_name, MLSVC_DOMAIN_NAME_MAX, 1); + if (rc != 0) { + status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + return (MLRPC_DRC_FAULT_OUT_OF_MEMORY); + } + + name = domain_name; + sid = nt_sid_dup(nt_domain_local_sid()); + } else { + if ((di = smb_getdomaininfo(0)) == 0) { + status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + return (MLRPC_DRC_FAULT_OUT_OF_MEMORY); + } + + ntdp = nt_domain_lookup_name(di->domain); + if (ntdp == 0) { + (void) lsa_query_primary_domain_info(); + ntdp = nt_domain_lookup_name(di->domain); + } + + if (ntdp == 0) { + sid = nt_sid_gen_null_sid(); + name = di->domain; + } else { + sid = nt_sid_dup(ntdp->sid); + name = ntdp->name; + } + } + + if (sid == 0) { + status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + return (MLRPC_DRC_FAULT_OUT_OF_MEMORY); + } + + if (mlsvc_string_save((ms_string_t *)&pd_info->name, name, mxa) == 0) + status = NT_STATUS_INSUFFICIENT_RESOURCES; + + if ((pd_info->sid = (struct mslsa_sid *)mlsvc_sid_save(sid, mxa)) == 0) + status = NT_STATUS_INSUFFICIENT_RESOURCES; + + free(sid); + + if (status != NT_STATUS_SUCCESS) + return (MLRPC_DRC_FAULT_OUT_OF_MEMORY); + + return (MLRPC_DRC_OK); +} + + +/* + * lsarpc_s_AccountDomainInfo + * + * This is the service side function for handling account domain policy + * queries. This is where we return our local domain information so that + * NT knows who to query for information on local names and SIDs. The + * domain name is the local hostname. + */ +static int +lsarpc_s_AccountDomainInfo(struct mslsa_AccountDomainInfo *ad_info, + struct mlrpc_xaction *mxa) +{ + char domain_name[MLSVC_DOMAIN_NAME_MAX]; + nt_sid_t *domain_sid; + int rc; + + if (smb_gethostname(domain_name, MLSVC_DOMAIN_NAME_MAX, 1) != 0) + return (MLRPC_DRC_FAULT_OUT_OF_MEMORY); + + if ((domain_sid = nt_domain_local_sid()) == NULL) + return (MLRPC_DRC_FAULT_OUT_OF_MEMORY); + + rc = mlsvc_string_save((ms_string_t *)&ad_info->name, + domain_name, mxa); + if (rc == 0) + return (MLRPC_DRC_FAULT_OUT_OF_MEMORY); + + ad_info->sid = (struct mslsa_sid *)mlsvc_sid_save(domain_sid, mxa); + if (ad_info->sid == NULL) + return (MLRPC_DRC_FAULT_OUT_OF_MEMORY); + + return (MLRPC_DRC_OK); +} + +/* + * lsarpc_s_LookupNames + * + * This is the service side function for handling name lookup requests. + * Currently, we only support lookups of a single name. This is also a + * pass through interface so all we do is act as a proxy between the + * client and the DC. + */ +static int +lsarpc_s_LookupNames(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslsa_LookupNames *param = arg; + struct mslsa_rid_entry *rids; + smb_userinfo_t *user_info = 0; + struct mslsa_domain_table *domain_table; + struct mslsa_domain_entry *domain_entry; + char *name = ""; + DWORD status = NT_STATUS_SUCCESS; + int rc = 0; + + if (param->name_table->n_entry != 1) + return (MLRPC_DRC_FAULT_PARAM_0_UNIMPLEMENTED); + + rids = MLRPC_HEAP_NEW(mxa, struct mslsa_rid_entry); + domain_table = MLRPC_HEAP_NEW(mxa, struct mslsa_domain_table); + domain_entry = MLRPC_HEAP_NEW(mxa, struct mslsa_domain_entry); + user_info = mlsvc_alloc_user_info(); + + if (rids == NULL || domain_table == NULL || + domain_entry == NULL || user_info == NULL) { + status = NT_STATUS_NO_MEMORY; + goto name_lookup_failed; + } + + name = (char *)param->name_table->names->str; + + rc = lsa_lookup_local(name, user_info); + if (rc < 0) { + status = NT_STATUS_NO_SUCH_USER; + goto name_lookup_failed; + } + + if (rc > 0) { + if (lsa_lookup_name(0, 0, name, user_info) != 0) { + status = NT_STATUS_NO_SUCH_USER; + goto name_lookup_failed; + } + } + + /* + * Set up the rid table. + */ + rids[0].sid_name_use = user_info->sid_name_use; + rids[0].rid = user_info->rid; + rids[0].domain_index = 0; + param->translated_sids.n_entry = 1; + param->translated_sids.rids = rids; + + /* + * Set up the domain table. + */ + domain_table->entries = domain_entry; + domain_table->n_entry = 1; + domain_table->max_n_entry = MLSVC_DOMAIN_MAX; + + rc = mlsvc_string_save((ms_string_t *)&domain_entry->domain_name, + user_info->domain_name, mxa); + + domain_entry->domain_sid = + (struct mslsa_sid *)mlsvc_sid_save(user_info->domain_sid, mxa); + + if (rc == 0 || domain_entry->domain_sid == NULL) { + status = NT_STATUS_NO_MEMORY; + goto name_lookup_failed; + } + + param->domain_table = domain_table; + param->mapped_count = 1; + param->status = 0; + + mlsvc_free_user_info(user_info); + return (MLRPC_DRC_OK); + +name_lookup_failed: + mlsvc_free_user_info(user_info); + bzero(param, sizeof (struct mslsa_LookupNames)); + param->status = NT_SC_ERROR(status); + return (MLRPC_DRC_OK); +} + +/* + * lsarpc_s_LookupSids + * + * This is the service side function for handling sid lookup requests. + * We have to set up both the name table and the domain table in the + * response. For each SID, we check for UNIX domain (local lookup) or + * NT domain (DC lookup) and call the appropriate lookup function. This + * should resolve the SID to a name. Then we need to update the domain + * table and make the name entry point at the appropriate domain table + * entry. + * + * On success return 0. Otherwise return an RPC specific error code. + */ +static int +lsarpc_s_LookupSids(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslsa_LookupSids *param = arg; + struct mslsa_domain_table *domain_table; + struct mslsa_domain_entry *domain_entry; + struct mslsa_name_entry *names; + smb_userinfo_t *user_info; + nt_sid_t *sid; + DWORD n_entry; + int result; + int i; + + user_info = mlsvc_alloc_user_info(); + + n_entry = param->lup_sid_table.n_entry; + names = MLRPC_HEAP_NEWN(mxa, struct mslsa_name_entry, n_entry); + domain_table = MLRPC_HEAP_NEW(mxa, struct mslsa_domain_table); + domain_entry = MLRPC_HEAP_NEWN(mxa, struct mslsa_domain_entry, + MLSVC_DOMAIN_MAX); + + if (names == NULL || domain_table == NULL || + domain_entry == NULL || user_info == NULL) { + bzero(param, sizeof (struct mslsa_LookupSids)); + param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + return (MLRPC_DRC_OK); + } + + domain_table->entries = domain_entry; + domain_table->n_entry = 0; + domain_table->max_n_entry = MLSVC_DOMAIN_MAX; + + for (i = 0; i < n_entry; ++i) { + bzero(&names[i], sizeof (struct mslsa_name_entry)); + sid = (nt_sid_t *)param->lup_sid_table.entries[i].psid; + + if (nt_sid_is_local(sid)) { + result = lsarpc_s_LookupLocalSid(mxa, sid, user_info, + &names[i]); + } else { + result = lsarpc_s_LookupBuiltinSid(mxa, sid, user_info, + &names[i]); + + if (result != 0) + result = lsarpc_s_LookupNtSid(mxa, sid, + user_info, &names[i], 1); + + if (result != 0) { + result = lsarpc_s_UnknownSid(mxa, sid, + user_info, &names[i]); + } + } + + if (result == -1) { + mlsvc_free_user_info(user_info); + param->domain_table = 0; + param->name_table.n_entry = 0; + param->name_table.entries = 0; + param->mapped_count = 0; + param->status = NT_SC_ERROR(NT_STATUS_INVALID_SID); + return (MLRPC_DRC_OK); + } + + result = lsarpc_s_UpdateDomainTable(mxa, user_info, + domain_table, &names[i].domain_ix); + + if (result == -1) { + mlsvc_free_user_info(user_info); + param->domain_table = 0; + param->name_table.n_entry = 0; + param->name_table.entries = 0; + param->mapped_count = 0; + param->status = NT_SC_ERROR(NT_STATUS_INVALID_SID); + return (MLRPC_DRC_OK); + } + + mlsvc_release_user_info(user_info); + } + + param->domain_table = domain_table; + param->name_table.n_entry = n_entry; + param->name_table.entries = names; + param->mapped_count = n_entry; + param->status = 0; + + mlsvc_free_user_info(user_info); + return (MLRPC_DRC_OK); +} + +/* + * lsarpc_s_LookupLocalSid + * + * This function handles local domain SID lookup. If the SID matches the + * local domain SID, we lookup the local files to map the RID to a name. + * We attempt to handle both users and groups. When the SID was supplied + * to the client, the ID type should have been encoded in the RID. We + * decode the RID and lookup it up in either the passwd file or the + * group file as appropriate. + * + * On success, 0 is returned. Otherwise -1 is returned. + */ +static int +lsarpc_s_LookupLocalSid(struct mlrpc_xaction *mxa, nt_sid_t *sid, + smb_userinfo_t *user_info, struct mslsa_name_entry *name) +{ + char buffer[MLSVC_DOMAIN_NAME_MAX]; + char namebuf[MLSVC_DOMAIN_NAME_MAX]; + nt_sid_t *lds; + nt_sid_t *tmp_sid; + nt_group_t *grp; + struct passwd *pw; + struct group *gr; + DWORD rid; + int unix_id; + + if (smb_gethostname(buffer, MLSVC_DOMAIN_NAME_MAX, 1) != 0) + return (-1); + + /* + * Only free tmp_sid in error paths. If it is assigned to the + * user_info, it will be freed later when that structure is + * released. + */ + if ((tmp_sid = nt_sid_dup(sid)) == NULL) + return (-1); + + rid = 0; + lds = nt_domain_local_sid(); + user_info->sid_name_use = SidTypeInvalid; + + if (nt_sid_is_equal(lds, tmp_sid)) { + user_info->sid_name_use = SidTypeDomain; + user_info->name = strdup(buffer); + } else { + (void) nt_sid_split(tmp_sid, &rid); + + switch (SAM_RID_TYPE(rid)) { + case SAM_RT_NT_UID: + break; + + case SAM_RT_NT_GID: + user_info->sid_name_use = SidTypeAlias; + grp = nt_groups_lookup_rid(rid); + if (grp) + user_info->name = strdup(grp->name); + else { + (void) snprintf(namebuf, sizeof (namebuf), + "%d (no name)", rid); + user_info->name = strdup(namebuf); + } + break; + + case SAM_RT_UNIX_UID: + /* + * It is always possible that the rid will not + * correspond to an entry in the local passwd or group + * file. In this case we can return the RID with a + * message to indicate the problem, which seems better + * than returning an invalid SID error. + */ + unix_id = SAM_DECODE_RID(rid); + (void) snprintf(namebuf, sizeof (namebuf), + "%d (no name)", unix_id); + user_info->sid_name_use = SidTypeUser; + pw = getpwuid(unix_id); + user_info->name = (pw) ? + strdup(pw->pw_name) : strdup(namebuf); + break; + + case SAM_RT_UNIX_GID: + unix_id = SAM_DECODE_RID(rid); + (void) snprintf(namebuf, sizeof (namebuf), + "%d (no name)", unix_id); + user_info->sid_name_use = SidTypeAlias; + gr = getgrgid(unix_id); + user_info->name = (gr) ? + strdup(gr->gr_name) : strdup(namebuf); + break; + } + } + + if (user_info->sid_name_use == SidTypeInvalid) { + free(tmp_sid); + return (-1); + } + + /* + * Set up the rest of user_info. + * Don't free tmp_sid after this. + */ + user_info->rid = rid; + user_info->domain_name = strdup(buffer); + user_info->domain_sid = tmp_sid; + + bzero(name, sizeof (struct mslsa_name_entry)); + name->sid_name_use = user_info->sid_name_use; + + if (!mlsvc_string_save( + (ms_string_t *)&name->name, user_info->name, mxa)) { + return (-1); + } + + return (0); +} + +/* + * lsarpc_s_LookupNtSid + * + * This function handles NT domain SID lookup on the domain controller. + * Most of the work is performed by lsa_lookup_sid. We just have to + * update the name data for the response. It is assumed that any SID + * passed to this function has already been checked and correctly + * identified as an NT domain SID. It shouldn't break anything if you + * get it wrong, the domain controller will just reject the SID. + * + * On success, 0 is returned. Otherwise -1 is returned. + */ +static int +lsarpc_s_LookupNtSid(struct mlrpc_xaction *mxa, nt_sid_t *sid, + smb_userinfo_t *user_info, struct mslsa_name_entry *name, int version) +{ + char *username; + DWORD status; + + if (smb_getdomaininfo(0) == 0) { + status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + return (-1); + } + + if (version == 2) + status = lsa_lookup_sid2(sid, user_info); + else + status = lsa_lookup_sid(sid, user_info); + + if (status != 0) + return (-1); + + switch (user_info->sid_name_use) { + case SidTypeDomain: + if ((username = user_info->domain_name) == 0) + user_info->sid_name_use = SidTypeUnknown; + break; + + case SidTypeUser: + case SidTypeGroup: + case SidTypeAlias: + case SidTypeDeletedAccount: + case SidTypeWellKnownGroup: + if ((username = user_info->name) == 0) + user_info->sid_name_use = SidTypeUnknown; + break; + + default: + return (-1); + } + + if (username == 0) + username = "unknown"; + bzero(name, sizeof (struct mslsa_name_entry)); + name->sid_name_use = user_info->sid_name_use; + + if (!mlsvc_string_save((ms_string_t *)&name->name, username, mxa)) + return (-1); + + return (0); +} + +/* + * lsarpc_s_LookupBuiltinSid + * + * This function handles predefined local groups and aliases in the NT + * AUTHORITY or BUILTIN domains, and some other miscellaneous bits. I + * don't think NT cares about the domain field of well-known groups or + * aliases but it seems sensible to set it up anyway. If we get a match, + * set up the name in the response heap. + * + * On success, 0 is returned. Otherwise non-zero is returned. A non-zero + * return value should not be automatically interpreted as an error. The + * caller should attempt to resolve the SID through alternative means. + */ +static int +lsarpc_s_LookupBuiltinSid(struct mlrpc_xaction *mxa, nt_sid_t *sid, + smb_userinfo_t *user_info, struct mslsa_name_entry *name) +{ + char *np; + WORD sid_name_use; + + if ((np = nt_builtin_lookup_sid(sid, &sid_name_use)) == NULL) + return (1); + + user_info->sid_name_use = sid_name_use; + user_info->name = strdup(np); + user_info->domain_sid = nt_sid_dup(sid); + + if (user_info->name == NULL || user_info->domain_sid == NULL) { + mlsvc_release_user_info(user_info); + return (-1); + } + + if (sid_name_use != SidTypeDomain && sid->SubAuthCount != 0) + user_info->rid = sid->SubAuthority[sid->SubAuthCount - 1]; + else + user_info->rid = 0; + + if ((np = nt_builtin_lookup_domain(user_info->name)) != NULL) + user_info->domain_name = strdup(np); + else + user_info->domain_name = strdup("UNKNOWN"); + + if (user_info->domain_name == NULL) { + mlsvc_release_user_info(user_info); + return (-1); + } + + if (sid_name_use == SidTypeAlias && + user_info->domain_sid->SubAuthCount != 0) { + --user_info->domain_sid->SubAuthCount; + } + + bzero(name, sizeof (struct mslsa_name_entry)); + name->sid_name_use = sid_name_use; + + if (sid_name_use == SidTypeUnknown) { + mlsvc_release_user_info(user_info); + return (1); + } + + if (!mlsvc_string_save( + (ms_string_t *)&name->name, user_info->name, mxa)) { + mlsvc_release_user_info(user_info); + return (-1); + } + + return (0); +} + +/* + * lsarpc_s_UnknownSid + * + * This function handles unknown SIDs. By the time this is called we + * know that this is not a local SID and that the PDC has no idea to + * whom this sid refers. It may be a remnant from a time when the + * server was in another domain. All we can do is turn into the SID + * into a string and return it in place of a user name. + * + * On success, 0 is returned. Otherwise -1 is returned. + */ +static int +lsarpc_s_UnknownSid(struct mlrpc_xaction *mxa, nt_sid_t *sid, + smb_userinfo_t *user_info, struct mslsa_name_entry *name) +{ + char domain_name[MLSVC_DOMAIN_NAME_MAX]; + char *sidbuf; + + if ((sidbuf = nt_sid_format(sid)) == NULL) + return (-1); + + if (smb_gethostname(domain_name, MLSVC_DOMAIN_NAME_MAX, 1) != 0) + return (-1); + + (void) utf8_strupr(domain_name); + mlsvc_release_user_info(user_info); + user_info->sid_name_use = SidTypeUnknown; + user_info->name = sidbuf; + user_info->domain_name = strdup(domain_name); + user_info->domain_sid = nt_sid_dup(nt_domain_local_sid()); + + bzero(name, sizeof (struct mslsa_name_entry)); + name->sid_name_use = user_info->sid_name_use; + + if (!mlsvc_string_save( + (ms_string_t *)&name->name, user_info->name, mxa)) { + return (-1); + } + + return (0); +} + +/* + * lsarpc_s_UpdateDomainTable + * + * This routine is responsible for maintaining the domain table which + * will be returned from a SID lookup. Whenever a name is added to the + * name table, this function should be called with the corresponding + * domain name. If the domain information is not already in the table, + * it is added. On success return 0; Otherwise -1 is returned. + */ +static int +lsarpc_s_UpdateDomainTable(struct mlrpc_xaction *mxa, + smb_userinfo_t *user_info, struct mslsa_domain_table *domain_table, + DWORD *domain_idx) +{ + struct mslsa_domain_entry *dentry; + DWORD n_entry; + DWORD i; + + if (user_info->sid_name_use == SidTypeWellKnownGroup || + user_info->sid_name_use == SidTypeUnknown || + user_info->sid_name_use == SidTypeInvalid) { + /* + * These types don't need to reference an entry in the + * domain table. So return -1. + */ + *domain_idx = (DWORD)-1; + return (0); + } + + if ((dentry = domain_table->entries) == NULL) + return (-1); + + if ((n_entry = domain_table->n_entry) >= MLSVC_DOMAIN_MAX) + return (-1); + + for (i = 0; i < n_entry; ++i) { + if (nt_sid_is_equal((nt_sid_t *)dentry[i].domain_sid, + user_info->domain_sid)) { + *domain_idx = i; + return (0); + } + } + + if (i == MLSVC_DOMAIN_MAX) + return (-1); + + if (!mlsvc_string_save((ms_string_t *)&dentry[i].domain_name, + user_info->domain_name, mxa)) + return (-1); + + dentry[i].domain_sid = + (struct mslsa_sid *)mlsvc_sid_save(user_info->domain_sid, mxa); + + if (dentry[i].domain_sid == NULL) + return (-1); + + ++domain_table->n_entry; + *domain_idx = i; + return (0); +} + +/* + * lsarpc_s_LookupSids2 + * + * Other than the use of lsar_lookup_sids2 and lsar_name_entry2, this + * is identical to lsarpc_s_LookupSids. + */ +static int +lsarpc_s_LookupSids2(void *arg, struct mlrpc_xaction *mxa) +{ + struct lsar_lookup_sids2 *param = arg; + struct lsar_name_entry2 *names; + struct mslsa_domain_table *domain_table; + struct mslsa_domain_entry *domain_entry; + smb_userinfo_t *user_info; + nt_sid_t *sid; + DWORD n_entry; + int result; + int i; + + user_info = mlsvc_alloc_user_info(); + + n_entry = param->lup_sid_table.n_entry; + names = MLRPC_HEAP_NEWN(mxa, struct lsar_name_entry2, n_entry); + domain_table = MLRPC_HEAP_NEW(mxa, struct mslsa_domain_table); + domain_entry = MLRPC_HEAP_NEWN(mxa, struct mslsa_domain_entry, + MLSVC_DOMAIN_MAX); + + if (names == NULL || domain_table == NULL || + domain_entry == NULL || user_info == NULL) { + bzero(param, sizeof (struct lsar_lookup_sids2)); + param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + return (MLRPC_DRC_OK); + } + + domain_table->entries = domain_entry; + domain_table->n_entry = 0; + domain_table->max_n_entry = MLSVC_DOMAIN_MAX; + + for (i = 0; i < n_entry; ++i) { + bzero(&names[i], sizeof (struct lsar_name_entry2)); + sid = (nt_sid_t *)param->lup_sid_table.entries[i].psid; + + if (nt_sid_is_local(sid)) { + result = lsarpc_s_LookupLocalSid(mxa, sid, user_info, + (struct mslsa_name_entry *)&names[i]); + } else { + result = lsarpc_s_LookupBuiltinSid(mxa, sid, user_info, + (struct mslsa_name_entry *)&names[i]); + + if (result != 0) + result = lsarpc_s_LookupNtSid(mxa, sid, + user_info, + (struct mslsa_name_entry *)&names[i], 2); + + if (result != 0) { + result = lsarpc_s_UnknownSid(mxa, sid, + user_info, + (struct mslsa_name_entry *)&names[i]); + } + } + + if (result == -1) { + mlsvc_free_user_info(user_info); + param->domain_table = 0; + param->name_table.n_entry = 0; + param->name_table.entries = 0; + param->mapped_count = 0; + param->status = NT_SC_ERROR(NT_STATUS_INVALID_SID); + return (MLRPC_DRC_OK); + } + + result = lsarpc_s_UpdateDomainTable(mxa, user_info, + domain_table, &names[i].domain_ix); + + if (result == -1) { + mlsvc_free_user_info(user_info); + param->domain_table = 0; + param->name_table.n_entry = 0; + param->name_table.entries = 0; + param->mapped_count = 0; + param->status = NT_SC_ERROR(NT_STATUS_INVALID_SID); + + return (MLRPC_DRC_OK); + } + + mlsvc_release_user_info(user_info); + } + + param->domain_table = domain_table; + param->name_table.n_entry = n_entry; + param->name_table.entries = names; + param->mapped_count = n_entry; + param->status = 0; + + mlsvc_free_user_info(user_info); + return (MLRPC_DRC_OK); +} + +/* + * lsarpc_s_LookupNames2 + * + * Other than the use of lsar_LookupNames2 and lsar_rid_entry2, this + * is identical to lsarpc_s_LookupNames. + */ +static int +lsarpc_s_LookupNames2(void *arg, struct mlrpc_xaction *mxa) +{ + struct lsar_LookupNames2 *param = arg; + struct lsar_rid_entry2 *rids; + smb_userinfo_t *user_info = 0; + struct mslsa_domain_table *domain_table; + struct mslsa_domain_entry *domain_entry; + char *name = ""; + DWORD status = NT_STATUS_SUCCESS; + int rc = 0; + + if (param->name_table->n_entry != 1) + return (MLRPC_DRC_FAULT_PARAM_0_UNIMPLEMENTED); + + rids = MLRPC_HEAP_NEW(mxa, struct lsar_rid_entry2); + domain_table = MLRPC_HEAP_NEW(mxa, struct mslsa_domain_table); + domain_entry = MLRPC_HEAP_NEW(mxa, struct mslsa_domain_entry); + user_info = mlsvc_alloc_user_info(); + + if (rids == 0 || domain_table == 0 || + domain_entry == 0 || user_info == 0) { + status = NT_STATUS_NO_MEMORY; + goto name_lookup2_failed; + } + + name = (char *)param->name_table->names->str; + + rc = lsa_lookup_local(name, user_info); + if (rc < 0) { + status = NT_STATUS_NONE_MAPPED; + goto name_lookup2_failed; + } + + if (rc > 0) { + if (lsa_lookup_name2(0, 0, name, user_info) != 0) { + status = NT_STATUS_NONE_MAPPED; + goto name_lookup2_failed; + } + } + + /* + * Set up the rid table. + */ + bzero(rids, sizeof (struct lsar_rid_entry2)); + rids[0].sid_name_use = user_info->sid_name_use; + rids[0].rid = user_info->rid; + rids[0].domain_index = 0; + param->translated_sids.n_entry = 1; + param->translated_sids.rids = rids; + + /* + * Set up the domain table. + */ + domain_table->entries = domain_entry; + domain_table->n_entry = 1; + domain_table->max_n_entry = MLSVC_DOMAIN_MAX; + + rc = mlsvc_string_save((ms_string_t *)&domain_entry->domain_name, + user_info->domain_name, mxa); + + domain_entry->domain_sid = + (struct mslsa_sid *)mlsvc_sid_save(user_info->domain_sid, mxa); + + if (rc == 0 || domain_entry->domain_sid == NULL) { + status = NT_STATUS_NO_MEMORY; + goto name_lookup2_failed; + } + + param->domain_table = domain_table; + param->mapped_count = 1; + param->status = 0; + + mlsvc_free_user_info(user_info); + return (MLRPC_DRC_OK); + +name_lookup2_failed: + mlsvc_free_user_info(user_info); + bzero(param, sizeof (struct lsar_LookupNames2)); + param->status = NT_SC_ERROR(status); + return (MLRPC_DRC_OK); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c new file mode 100644 index 000000000000..4edfcacd5154 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c @@ -0,0 +1,202 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * NetLogon RPC (NETR) interface definition. This module provides + * the server side NETR RPC interface and the interface registration + * function. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +static int netr_s_ServerReqChallenge(void *, struct mlrpc_xaction *); +static int netr_s_ServerAuthenticate2(void *, struct mlrpc_xaction *); +static int netr_s_ServerPasswordSet(void *, struct mlrpc_xaction *); +static int netr_s_SamLogon(void *, struct mlrpc_xaction *); +static int netr_s_SamLogoff(void *, struct mlrpc_xaction *); + +static mlrpc_stub_table_t netr_stub_table[] = { + { netr_s_ServerReqChallenge, NETR_OPNUM_ServerReqChallenge }, + { netr_s_ServerAuthenticate2, NETR_OPNUM_ServerAuthenticate2 }, + { netr_s_ServerPasswordSet, NETR_OPNUM_ServerPasswordSet }, + { netr_s_SamLogon, NETR_OPNUM_SamLogon }, + { netr_s_SamLogoff, NETR_OPNUM_SamLogoff }, + {0} +}; + +static mlrpc_service_t netr_service = { + "NETR", /* name */ + "NetLogon", /* desc */ + "\\netlogon", /* endpoint */ + PIPE_LSASS, /* sec_addr_port */ + "12345678-1234-abcd-ef0001234567cffb", 1, /* abstract */ + "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */ + 0, /* no bind_instance_size */ + 0, /* no bind_req() */ + 0, /* no unbind_and_close() */ + 0, /* use generic_call_stub() */ + &TYPEINFO(netr_interface), /* interface ti */ + netr_stub_table /* stub_table */ +}; + +/* + * netr_initialize + * + * This function registers the NETR RPC interface with the RPC runtime + * library. It must be called in order to use either the client side + * or the server side functions. + */ +void +netr_initialize(void) +{ + (void) mlrpc_register_service(&netr_service); +} + +/* + * netr_s_ServerReqChallenge + */ +/*ARGSUSED*/ +static int +netr_s_ServerReqChallenge(void *arg, struct mlrpc_xaction *mxa) +{ + struct netr_ServerReqChallenge *param = arg; + + bzero(param, sizeof (struct netr_ServerReqChallenge)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); +} + +/* + * netr_s_ServerAuthenticate2 + */ +/*ARGSUSED*/ +static int +netr_s_ServerAuthenticate2(void *arg, struct mlrpc_xaction *mxa) +{ + struct netr_ServerAuthenticate2 *param = arg; + + bzero(param, sizeof (struct netr_ServerAuthenticate2)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); +} + +/* + * netr_s_ServerPasswordSet + */ +/*ARGSUSED*/ +static int +netr_s_ServerPasswordSet(void *arg, struct mlrpc_xaction *mxa) +{ + struct netr_PasswordSet *param = arg; + + bzero(param, sizeof (struct netr_PasswordSet)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); +} + +/* + * netr_s_SamLogon + */ +/*ARGSUSED*/ +static int +netr_s_SamLogon(void *arg, struct mlrpc_xaction *mxa) +{ + struct netr_SamLogon *param = arg; + + bzero(param, sizeof (struct netr_SamLogon)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); +} + +/* + * netr_s_SamLogoff + */ +/*ARGSUSED*/ +static int +netr_s_SamLogoff(void *arg, struct mlrpc_xaction *mxa) +{ + struct netr_SamLogoff *param = arg; + + bzero(param, sizeof (struct netr_SamLogoff)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); +} + +/* + * Declare extern references. + */ +DECL_FIXUP_STRUCT(netr_validation_u); +DECL_FIXUP_STRUCT(netr_validation_info); +DECL_FIXUP_STRUCT(netr_SamLogon); + +/* + * Patch the netr_SamLogon union. + * This function is called from mlsvc_netr_ndr.c + */ +void +fixup_netr_SamLogon(struct netr_SamLogon *arg) +{ + unsigned short size1 = 0; + unsigned short size2 = 0; + unsigned short size3 = 0; + WORD level = (WORD)arg->validation_level; + + switch (level) { + case 3: + /* + * The netr_validation_u union contains a pointer, which + * is a DWORD in NDR. So we need to set size1 to ensure + * that we can correctly decode the remaining parameters. + */ + size1 = sizeof (DWORD); + break; + + default: + /* + * If the request is badly formed or the level is invalid, + * the server returns NT_STATUS_INVALID_INFO_CLASS. Size1 + * must be zero to correctly decode the status. + */ + size1 = 0; + break; + }; + + size2 = size1 + (2 * sizeof (DWORD)); + size3 = size2 + sizeof (mlrpcconn_request_hdr_t) + sizeof (DWORD); + + FIXUP_PDU_SIZE(netr_validation_u, size1); + FIXUP_PDU_SIZE(netr_validation_info, size2); + FIXUP_PDU_SIZE(netr_SamLogon, size3); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_sam.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_sam.c new file mode 100644 index 000000000000..3b51c05e718d --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_sam.c @@ -0,0 +1,1463 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Security Accounts Manager RPC (SAMR) interface definition. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The keys associated with the various handles dispensed + * by the SAMR server. These keys can be used to validate + * client activity. These values are never passed over + * the network so security shouldn't be an issue. + */ +#define SAMR_CONNECT_KEY "SamrConnect" +#define SAMR_DOMAIN_KEY "SamrDomain" +#define SAMR_USER_KEY "SamrUser" +#define SAMR_GROUP_KEY "SamrGroup" +#define SAMR_ALIAS_KEY "SamrAlias" + +/* + * Domain discriminator values. Set the top bit to try + * to distinguish these values from user and group ids. + */ +#define SAMR_DATABASE_DOMAIN 0x80000001 +#define SAMR_LOCAL_DOMAIN 0x80000002 +#define SAMR_BUILTIN_DOMAIN 0x80000003 +#define SAMR_PRIMARY_DOMAIN 0x80000004 + +static DWORD samr_s_enum_local_domains(struct samr_EnumLocalDomain *, + struct mlrpc_xaction *); + +static mlrpc_stub_table_t samr_stub_table[]; + +static mlrpc_service_t samr_service = { + "SAMR", /* name */ + "Security Accounts Manager", /* desc */ + "\\samr", /* endpoint */ + PIPE_LSASS, /* sec_addr_port */ + "12345778-1234-abcd-ef000123456789ac", 1, /* abstract */ + "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */ + 0, /* no bind_instance_size */ + 0, /* no bind_req() */ + 0, /* no unbind_and_close() */ + 0, /* use generic_call_stub() */ + &TYPEINFO(samr_interface), /* interface ti */ + samr_stub_table /* stub_table */ +}; + +/* + * samr_initialize + * + * This function registers the SAM RPC interface with the RPC runtime + * library. It must be called in order to use either the client side + * or the server side functions. + */ +void +samr_initialize(void) +{ + (void) mlrpc_register_service(&samr_service); +} + +/* + * samr_s_ConnectAnon + * + * This is a request to connect to the local SAM database. We don't + * support any form of update request and our database doesn't + * contain any private information, so there is little point in + * doing any access access checking here. + * + * Return a handle for use with subsequent SAM requests. + */ +/*ARGSUSED*/ +static int +samr_s_ConnectAnon(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_ConnectAnon *param = arg; + ms_handle_t *handle; + + handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR, SAMR_CONNECT_KEY, + SAMR_DATABASE_DOMAIN); + bcopy(handle, ¶m->handle, sizeof (samr_handle_t)); + + param->status = 0; + return (MLRPC_DRC_OK); +} + +/* + * samr_s_CloseHandle + * + * This is a request to close the SAM interface specified by the handle. + * Free the handle and zero out the result handle for the client. + * + * We could do some checking here but it probably doesn't matter. + */ +/*ARGSUSED*/ +static int +samr_s_CloseHandle(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_CloseHandle *param = arg; + +#ifdef SAMR_S_DEBUG + if (mlsvc_lookup_handle((ms_handle_t *)¶m->handle) == 0) { + bzero(¶m->result_handle, sizeof (samr_handle_t)); + param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); + return (MLRPC_DRC_OK); + } +#endif /* SAMR_S_DEBUG */ + + (void) mlsvc_put_handle((ms_handle_t *)¶m->handle); + bzero(¶m->result_handle, sizeof (samr_handle_t)); + param->status = 0; + return (MLRPC_DRC_OK); +} + +/* + * samr_s_LookupDomain + * + * This is a request to map a domain name to a domain SID. We can map + * the primary domain name, our local domain name (hostname) and the + * builtin domain names to the appropriate SID. Anything else will be + * rejected. + */ +static int +samr_s_LookupDomain(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_LookupDomain *param = arg; + char resource_domain[MAXHOSTNAMELEN]; + char *domain_name; + char *p; + nt_sid_t *sid = NULL; + + if ((domain_name = (char *)param->domain_name.str) == NULL) { + bzero(param, sizeof (struct samr_LookupDomain)); + param->status = NT_SC_ERROR(NT_STATUS_INVALID_PARAMETER); + return (MLRPC_DRC_OK); + } + + smb_config_rdlock(); + p = smb_config_getstr(SMB_CI_DOMAIN_NAME); + (void) strlcpy(resource_domain, p, MAXHOSTNAMELEN); + smb_config_unlock(); + + if (mlsvc_is_local_domain(domain_name) == 1) { + sid = nt_sid_dup(nt_domain_local_sid()); + } else if (strcasecmp(resource_domain, domain_name) == 0) { + /* + * We should not be asked to provide + * the domain SID for the primary domain. + */ + sid = NULL; + } else { + sid = nt_builtin_lookup_name(domain_name, 0); + } + + if (sid) { + param->sid = (struct samr_sid *)mlsvc_sid_save(sid, mxa); + free(sid); + + if (param->sid == NULL) { + bzero(param, sizeof (struct samr_LookupDomain)); + param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); + return (MLRPC_DRC_OK); + } + + param->status = NT_STATUS_SUCCESS; + } else { + param->sid = NULL; + param->status = NT_SC_ERROR(NT_STATUS_NO_SUCH_DOMAIN); + } + + return (MLRPC_DRC_OK); +} + +/* + * samr_s_EnumLocalDomains + * + * This is a request for the local domains supported by this server. + * All we do here is validate the handle and set the status. The real + * work is done in samr_s_enum_local_domains. + */ +static int +samr_s_EnumLocalDomains(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_EnumLocalDomain *param = arg; + ms_handle_t *handle; + DWORD status; + + handle = (ms_handle_t *)¶m->handle; + + if (mlsvc_validate_handle(handle, SAMR_CONNECT_KEY) == 0) + status = NT_STATUS_ACCESS_DENIED; + else + status = samr_s_enum_local_domains(param, mxa); + + if (status == NT_STATUS_SUCCESS) { + param->enum_context = param->info->entries_read; + param->total_entries = param->info->entries_read; + param->status = NT_STATUS_SUCCESS; + } else { + bzero(param, sizeof (struct samr_EnumLocalDomain)); + param->status = NT_SC_ERROR(status); + } + + return (MLRPC_DRC_OK); +} + + +/* + * samr_s_enum_local_domains + * + * This function should only be called via samr_s_EnumLocalDomains to + * ensure that the appropriate validation is performed. We will answer + * queries about two domains: the local domain, synonymous with the + * local hostname, and the BUILTIN domain. So we return these two + * strings. + * + * Returns NT status values. + */ +static DWORD +samr_s_enum_local_domains(struct samr_EnumLocalDomain *param, + struct mlrpc_xaction *mxa) +{ + struct samr_LocalDomainInfo *info; + struct samr_LocalDomainEntry *entry; + char *hostname; + + hostname = MLRPC_HEAP_MALLOC(mxa, MAXHOSTNAMELEN); + if (hostname == NULL) + return (NT_STATUS_NO_MEMORY); + + if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0) + return (NT_STATUS_NO_MEMORY); + + entry = MLRPC_HEAP_NEWN(mxa, struct samr_LocalDomainEntry, 2); + if (entry == NULL) + return (NT_STATUS_NO_MEMORY); + + bzero(entry, (sizeof (struct samr_LocalDomainEntry) * 2)); + (void) mlsvc_string_save((ms_string_t *)&entry[0].name, hostname, mxa); + (void) mlsvc_string_save((ms_string_t *)&entry[1].name, "Builtin", mxa); + + info = MLRPC_HEAP_NEW(mxa, struct samr_LocalDomainInfo); + if (info == NULL) + return (NT_STATUS_NO_MEMORY); + + info->entries_read = 2; + info->entry = entry; + param->info = info; + return (NT_STATUS_SUCCESS); +} + +/* + * samr_s_OpenDomain + * + * This is a request to open a domain within the local SAM database. + * The caller must supply a valid handle obtained via a successful + * connect. We return a handle to be used to access objects within + * this domain. + */ +/*ARGSUSED*/ +static int +samr_s_OpenDomain(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_OpenDomain *param = arg; + ms_handle_t *handle = 0; + nt_domain_t *domain; + + if (!mlsvc_validate_handle( + (ms_handle_t *)¶m->handle, SAMR_CONNECT_KEY)) { + bzero(¶m->domain_handle, sizeof (samr_handle_t)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); + } + + domain = nt_domain_lookup_sid((nt_sid_t *)param->sid); + if (domain == NULL) { + bzero(¶m->domain_handle, sizeof (samr_handle_t)); + param->status = NT_SC_ERROR(NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + return (MLRPC_DRC_OK); + } + + switch (domain->type) { + case NT_DOMAIN_BUILTIN: + handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR, + SAMR_DOMAIN_KEY, SAMR_BUILTIN_DOMAIN); + + bcopy(handle, ¶m->domain_handle, sizeof (samr_handle_t)); + param->status = 0; + break; + + case NT_DOMAIN_LOCAL: + handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR, + SAMR_DOMAIN_KEY, SAMR_LOCAL_DOMAIN); + + bcopy(handle, ¶m->domain_handle, sizeof (samr_handle_t)); + param->status = 0; + break; + + default: + bzero(¶m->domain_handle, sizeof (samr_handle_t)); + param->status = NT_SC_ERROR(NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + } + + return (MLRPC_DRC_OK); +} + +/* + * samr_s_QueryDomainInfo + * + * The caller should pass a domain handle. + * + * Windows 95 Server Manager sends requests for levels 6 and 7 when + * the services menu item is selected. Level 2 is basically for getting + * number of users, groups, and aliases in a domain. + * We have no information on what the various information levels mean. + */ +static int +samr_s_QueryDomainInfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_QueryDomainInfo *param = arg; + ms_handle_desc_t *desc; + char *hostname; + char *domain_str = ""; + int rc; + + desc = mlsvc_lookup_handle((ms_handle_t *)¶m->domain_handle); + if (desc == NULL || (strcmp(desc->key, SAMR_DOMAIN_KEY) != 0)) { + bzero(param, sizeof (struct samr_QueryDomainInfo)); + param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); + return (MLRPC_DRC_OK); + } + + switch (param->info_level) { + case SAMR_QUERY_DOMAIN_INFO_6: + param->ru.info6.unknown1 = 0x00000000; + param->ru.info6.unknown2 = 0x00147FB0; + param->ru.info6.unknown3 = 0x00000000; + param->ru.info6.unknown4 = 0x00000000; + param->ru.info6.unknown5 = 0x00000000; + param->status = NT_STATUS_SUCCESS; + break; + + case SAMR_QUERY_DOMAIN_INFO_7: + param->ru.info7.unknown1 = 0x00000003; + param->status = NT_STATUS_SUCCESS; + break; + + case SAMR_QUERY_DOMAIN_INFO_2: + if (desc->discrim == SAMR_LOCAL_DOMAIN) { + hostname = MLRPC_HEAP_MALLOC(mxa, MAXHOSTNAMELEN); + rc = smb_gethostname(hostname, MAXHOSTNAMELEN, 1); + if (rc != 0 || hostname == NULL) { + bzero(param, + sizeof (struct samr_QueryDomainInfo)); + param->status = + NT_SC_ERROR(NT_STATUS_NO_MEMORY); + return (MLRPC_DRC_OK); + } + + domain_str = hostname; + } else { + if (desc->discrim == SAMR_BUILTIN_DOMAIN) + domain_str = "Builtin"; + } + + param->ru.info2.unknown1 = 0x00000000; + param->ru.info2.unknown2 = 0x80000000; + + (void) mlsvc_string_save((ms_string_t *)&(param->ru.info2.s1), + "", mxa); + + (void) mlsvc_string_save( + (ms_string_t *)&(param->ru.info2.domain), domain_str, mxa); + + (void) mlsvc_string_save((ms_string_t *)&(param->ru.info2.s2), + "", mxa); + + param->ru.info2.sequence_num = 0x0000002B; + param->ru.info2.unknown3 = 0x00000000; + param->ru.info2.unknown4 = 0x00000001; + param->ru.info2.unknown5 = 0x00000003; + param->ru.info2.unknown6 = 0x00000001; + param->ru.info2.num_users = 0; + param->ru.info2.num_groups = 0; + param->ru.info2.num_aliases = + (desc->discrim == SAMR_BUILTIN_DOMAIN) ? + nt_groups_count(NT_GROUP_CNT_BUILTIN) : + nt_groups_count(NT_GROUP_CNT_LOCAL); + + param->status = NT_STATUS_SUCCESS; + break; + + default: + bzero(param, sizeof (struct samr_QueryDomainInfo)); + return (MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID); + }; + + param->address = (DWORD)¶m->ru; + param->switch_value = param->info_level; + return (MLRPC_DRC_OK); +} + +/* + * samr_s_LookupNames + * + * The definition for this interface is obviously wrong but I can't + * seem to get it to work the way I think it should. It should + * support multiple name lookup but I can only get one working for now. + */ +static int +samr_s_LookupNames(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_LookupNames *param = arg; + ms_handle_desc_t *desc; + struct passwd *pw; + struct group *gr; + nt_sid_t *sid; + nt_group_t *grp; + WORD rid_type; + + desc = mlsvc_lookup_handle((ms_handle_t *)¶m->handle); + if (desc == 0 || (strcmp(desc->key, SAMR_DOMAIN_KEY) != 0)) { + bzero(param, sizeof (struct samr_LookupNames)); + param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); + return (MLRPC_DRC_OK); + } + + if (param->n_entry != 1) { + bzero(param, sizeof (struct samr_LookupNames)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); + } + + if (param->name.str == NULL) { + bzero(param, sizeof (struct samr_LookupNames)); + /* + * Windows NT returns NT_STATUS_NONE_MAPPED when the + * name is NULL. + * Windows 2000 returns STATUS_INVALID_ACCOUNT_NAME. + */ + param->status = NT_SC_ERROR(NT_STATUS_NONE_MAPPED); + return (MLRPC_DRC_OK); + } + + param->rids.rid = MLRPC_HEAP_NEW(mxa, DWORD); + param->rid_types.rid_type = MLRPC_HEAP_NEW(mxa, DWORD); + + if (desc->discrim == SAMR_BUILTIN_DOMAIN) { + sid = nt_builtin_lookup_name((char *)param->name.str, + &rid_type); + + if (sid != 0) { + param->rids.n_entry = 1; + (void) nt_sid_get_rid(sid, ¶m->rids.rid[0]); + param->rid_types.n_entry = 1; + param->rid_types.rid_type[0] = rid_type; + param->status = NT_STATUS_SUCCESS; + free(sid); + return (MLRPC_DRC_OK); + } + } else if (desc->discrim == SAMR_LOCAL_DOMAIN) { + grp = nt_group_getinfo((char *)param->name.str, RWLOCK_READER); + + if (grp != NULL) { + param->rids.n_entry = 1; + (void) nt_sid_get_rid(grp->sid, ¶m->rids.rid[0]); + param->rid_types.n_entry = 1; + param->rid_types.rid_type[0] = *grp->sid_name_use; + param->status = NT_STATUS_SUCCESS; + nt_group_putinfo(grp); + return (MLRPC_DRC_OK); + } + + if ((pw = getpwnam((const char *)param->name.str)) != NULL) { + param->rids.n_entry = 1; + param->rids.rid[0] = SAM_ENCODE_UXUID(pw->pw_uid); + param->rid_types.n_entry = 1; + param->rid_types.rid_type[0] = SidTypeUser; + param->status = NT_STATUS_SUCCESS; + return (MLRPC_DRC_OK); + } + + if ((gr = getgrnam((const char *)param->name.str)) != NULL) { + param->rids.n_entry = 1; + param->rids.rid[0] = SAM_ENCODE_UXGID(gr->gr_gid); + param->rid_types.n_entry = 1; + param->rid_types.rid_type[0] = SidTypeAlias; + param->status = NT_STATUS_SUCCESS; + return (MLRPC_DRC_OK); + } + } + + param->rids.n_entry = 0; + param->rid_types.n_entry = 0; + param->status = NT_SC_ERROR(NT_STATUS_NONE_MAPPED); + return (MLRPC_DRC_OK); +} + +/* + * samr_s_OpenUser + * + * This is a request to open a user within a specified domain in the + * local SAM database. The caller must supply a valid domain handle, + * obtained via a successful domain open request. The user is + * specified by the rid in the request. + */ +/*ARGSUSED*/ +static int +samr_s_OpenUser(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_OpenUser *param = arg; + ms_handle_t *handle; + + if (!mlsvc_validate_handle( + (ms_handle_t *)¶m->handle, SAMR_DOMAIN_KEY)) { + bzero(¶m->user_handle, sizeof (samr_handle_t)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); + } + + handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR, SAMR_USER_KEY, + param->rid); + bcopy(handle, ¶m->user_handle, sizeof (samr_handle_t)); + + /* + * Need QueryUserInfo(level 21). + */ + bzero(¶m->user_handle, sizeof (samr_handle_t)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); +} + +/* + * samr_s_DeleteUser + * + * This is a request to delete a user within a specified domain in the + * local SAM database. The caller should supply a valid user handle but + * we deny access regardless. + */ +/*ARGSUSED*/ +static int +samr_s_DeleteUser(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_DeleteUser *param = arg; + + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); +} + +/* + * samr_s_QueryUserInfo + * + * Returns: + * NT_STATUS_SUCCESS + * NT_STATUS_ACCESS_DENIED + * NT_STATUS_INVALID_INFO_CLASS + */ +/*ARGSUSED*/ +static int +samr_s_QueryUserInfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_QueryUserInfo *param = arg; + + if (!mlsvc_validate_handle( + (ms_handle_t *)¶m->user_handle, SAMR_USER_KEY)) { + bzero(param, sizeof (struct samr_QueryUserInfo)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); + } + + bzero(param, sizeof (struct samr_QueryUserInfo)); + param->status = 0; + return (MLRPC_DRC_OK); +} + +/* + * samr_s_QueryUserGroups + * + * This is a request to obtain a list of groups of which a user is a + * member. The user is identified from the handle, which contains an + * encoded uid in the discriminator field. + * + * Get complete list of groups and check for builtin domain. + */ +static int +samr_s_QueryUserGroups(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_QueryUserGroups *param = arg; + struct samr_UserGroupInfo *info; + ms_handle_desc_t *desc; + struct passwd *pw; + DWORD uid; + + desc = mlsvc_lookup_handle((ms_handle_t *)¶m->user_handle); + if (desc == 0 || strcmp(desc->key, SAMR_USER_KEY)) { + bzero(param, sizeof (struct samr_QueryUserGroups)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); + } + + info = MLRPC_HEAP_NEW(mxa, struct samr_UserGroupInfo); + info->groups = MLRPC_HEAP_NEW(mxa, struct samr_UserGroups); + + uid = SAM_DECODE_RID(desc->discrim); + + if ((pw = getpwuid(uid)) != 0) { + info->n_entry = 1; + info->groups->rid = SAM_ENCODE_UXGID(pw->pw_gid); + info->groups->attr = SE_GROUP_MANDATORY + | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; + param->info = info; + param->status = 0; + } else { + bzero(param, sizeof (struct samr_QueryUserGroups)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + } + + return (MLRPC_DRC_OK); +} + +/* + * samr_s_OpenGroup + * + * This is a request to open a group within the specified domain in the + * local SAM database. The caller must supply a valid domain handle, + * obtained via a successful domain open request. The group is + * specified by the rid in the request. If this is a local RID it + * should already be encoded with type information. + * + * We return a handle to be used to access information about this group. + */ +/*ARGSUSED*/ +static int +samr_s_OpenGroup(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_OpenGroup *param = arg; + ms_handle_t *handle; + + if (!mlsvc_validate_handle( + (ms_handle_t *)¶m->handle, SAMR_DOMAIN_KEY)) { + bzero(¶m->group_handle, sizeof (samr_handle_t)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); + } + + handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR, SAMR_GROUP_KEY, + param->rid); + bcopy(handle, ¶m->group_handle, sizeof (samr_handle_t)); + + param->status = 0; + return (MLRPC_DRC_OK); +} + +/* + * samr_s_Connect + * + * This is a request to connect to the local SAM database. We don't + * support any form of update request and our database doesn't + * contain any private information, so there is little point in + * doing any access access checking here. + * + * Return a handle for use with subsequent SAM requests. + */ +/*ARGSUSED*/ +static int +samr_s_Connect(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_Connect *param = arg; + ms_handle_t *handle; + + handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR, + SAMR_CONNECT_KEY, SAMR_DATABASE_DOMAIN); + bcopy(handle, ¶m->handle, sizeof (samr_handle_t)); + + param->status = 0; + return (MLRPC_DRC_OK); +} + +/* + * samr_s_GetUserPwInfo + * + * This is a request to get a user's password. + */ +/*ARGSUSED*/ +static int +samr_s_GetUserPwInfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_GetUserPwInfo *param = arg; + ms_handle_t *handle; + DWORD status = 0; + + handle = (ms_handle_t *)¶m->user_handle; + + if (!mlsvc_validate_handle(handle, SAMR_USER_KEY)) + status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + + bzero(param, sizeof (struct samr_GetUserPwInfo)); + param->status = status; + return (MLRPC_DRC_OK); +} + +/* + * samr_s_CreateUser + * + * This is a request to create a user within a specified domain in the + * local SAM database. We always deny access. + */ +/*ARGSUSED*/ +static int +samr_s_CreateUser(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_CreateUser *param = arg; + + bzero(¶m->user_handle, sizeof (samr_handle_t)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); +} + +/* + * samr_s_ChangeUserPasswd + */ +/*ARGSUSED*/ +static int +samr_s_ChangeUserPasswd(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_ChangeUserPasswd *param = arg; + + bzero(param, sizeof (struct samr_ChangeUserPasswd)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); +} + +/* + * samr_s_GetDomainPwInfo + */ +/*ARGSUSED*/ +static int +samr_s_GetDomainPwInfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_GetDomainPwInfo *param = arg; + + bzero(param, sizeof (struct samr_GetDomainPwInfo)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); +} + +/* + * samr_s_SetUserInfo + */ +/*ARGSUSED*/ +static int +samr_s_SetUserInfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_SetUserInfo *param = arg; + + bzero(param, sizeof (struct samr_SetUserInfo)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); +} + +/* + * samr_s_QueryDispInfo + * + * This function is supposed to return local users' information. + * As we don't support local users, this function dosen't send + * back any information. + * + * I added a peice of code that returns information for Administrator + * and Guest builtin users. All information are hard-coded which I get + * from packet captures. Currently, this peice of code is opt-out. + */ +/*ARGSUSED*/ +static int +samr_s_QueryDispInfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_QueryDispInfo *param = arg; + ms_handle_desc_t *desc; + DWORD status = 0; + + desc = mlsvc_lookup_handle((ms_handle_t *)¶m->domain_handle); + if (desc == NULL || (strcmp(desc->key, SAMR_DOMAIN_KEY) != 0)) + status = NT_STATUS_INVALID_HANDLE; + +#ifdef SAMR_SUPPORT_USER + if ((desc->discrim != SAMR_LOCAL_DOMAIN) || (param->start_idx != 0)) { + param->total_size = 0; + param->returned_size = 0; + param->switch_value = 1; + param->count = 0; + param->users = 0; + } else { + param->total_size = 328; + param->returned_size = 328; + param->switch_value = 1; + param->count = 2; + param->users = (struct user_disp_info *)MLRPC_HEAP_MALLOC(mxa, + sizeof (struct user_disp_info)); + + param->users->count = 2; + param->users->acct[0].index = 1; + param->users->acct[0].rid = 500; + param->users->acct[0].ctrl = 0x210; + + (void) mlsvc_string_save( + (ms_string_t *)¶m->users->acct[0].name, + "Administrator", mxa); + + (void) mlsvc_string_save( + (ms_string_t *)¶m->users->acct[0].fullname, + "Built-in account for administering the computer/domain", + mxa); + + bzero(¶m->users->acct[0].desc, sizeof (samr_string_t)); + + param->users->acct[1].index = 2; + param->users->acct[1].rid = 501; + param->users->acct[1].ctrl = 0x211; + + (void) mlsvc_string_save( + (ms_string_t *)¶m->users->acct[1].name, + "Guest", mxa); + + (void) mlsvc_string_save( + (ms_string_t *)¶m->users->acct[1].fullname, + "Built-in account for guest access to the computer/domain", + mxa); + + bzero(¶m->users->acct[1].desc, sizeof (samr_string_t)); + } +#else + param->total_size = 0; + param->returned_size = 0; + param->switch_value = 1; + param->count = 0; + param->users = 0; +#endif + param->status = status; + return (MLRPC_DRC_OK); +} + +/* + * samr_s_EnumDomainGroups + * + * + * This function is supposed to return local users' information. + * As we don't support local users, this function dosen't send + * back any information. + * + * I added a peice of code that returns information for a + * domain group as None. All information are hard-coded which I get + * from packet captures. Currently, this peice of code is opt-out. + */ +/*ARGSUSED*/ +static int +samr_s_EnumDomainGroups(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_EnumDomainGroups *param = arg; + ms_handle_desc_t *desc; + DWORD status = NT_STATUS_SUCCESS; + + desc = mlsvc_lookup_handle((ms_handle_t *)¶m->domain_handle); + if (desc == NULL || (strcmp(desc->key, SAMR_DOMAIN_KEY) != 0)) + status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); + + param->total_size = 0; + param->returned_size = 0; + param->switch_value = 3; + param->count = 0; + param->groups = 0; + param->status = status; + return (MLRPC_DRC_OK); + +#ifdef SAMR_SUPPORT_GROUPS + if ((desc->discrim != SAMR_LOCAL_DOMAIN) || (param->start_idx != 0)) { + param->total_size = 0; + param->returned_size = 0; + param->switch_value = 3; + param->count = 0; + param->groups = 0; + } else { + param->total_size = 64; + param->returned_size = 64; + param->switch_value = 3; + param->count = 1; + param->groups = (struct group_disp_info *)MLRPC_HEAP_MALLOC( + mxa, sizeof (struct group_disp_info)); + + param->groups->count = 1; + param->groups->acct[0].index = 1; + param->groups->acct[0].rid = 513; + param->groups->acct[0].ctrl = 0x7; + (void) mlsvc_string_save( + (ms_string_t *)¶m->groups->acct[0].name, "None", mxa); + + (void) mlsvc_string_save( + (ms_string_t *)¶m->groups->acct[0].desc, + "Ordinary users", mxa); + } + + param->status = NT_STATUS_SUCCESS; + return (MLRPC_DRC_OK); +#endif +} + +/* + * samr_s_OpenAlias + * + * Lookup for requested alias, if it exists return a handle + * for that alias. The alias domain sid should match with + * the passed domain handle. + */ +/*ARGSUSED*/ +static int +samr_s_OpenAlias(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_OpenAlias *param = arg; + ms_handle_desc_t *desc = 0; + ms_handle_t *handle; + nt_group_t *grp; + DWORD status = NT_STATUS_SUCCESS; + + desc = mlsvc_lookup_handle((ms_handle_t *)¶m->domain_handle); + if (desc == 0 || (strcmp(desc->key, SAMR_DOMAIN_KEY) != 0)) { + status = NT_STATUS_INVALID_HANDLE; + goto open_alias_err; + } + + if (param->access_mask != SAMR_ALIAS_ACCESS_GET_INFO) { + status = NT_STATUS_ACCESS_DENIED; + goto open_alias_err; + } + + grp = nt_groups_lookup_rid(param->rid); + if (grp == 0) { + status = NT_STATUS_NO_SUCH_ALIAS; + goto open_alias_err; + } + + if (((desc->discrim == SAMR_LOCAL_DOMAIN) && + !nt_sid_is_local(grp->sid)) || + ((desc->discrim == SAMR_BUILTIN_DOMAIN) && + !nt_sid_is_builtin(grp->sid))) { + status = NT_STATUS_NO_SUCH_ALIAS; + goto open_alias_err; + } + + handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR, SAMR_ALIAS_KEY, + param->rid); + bcopy(handle, ¶m->alias_handle, sizeof (samr_handle_t)); + param->status = 0; + return (MLRPC_DRC_OK); + +open_alias_err: + bzero(¶m->alias_handle, sizeof (samr_handle_t)); + param->status = NT_SC_ERROR(status); + return (MLRPC_DRC_OK); +} + +/* + * samr_s_CreateDomainAlias + * + * Creates a local group in the security database, which is the + * security accounts manager (SAM) + * For more information you can look at MSDN page for NetLocalGroupAdd. + * This RPC is used by CMC and right now it returns access denied. + * The peice of code that creates a local group doesn't get compiled. + */ +/*ARGSUSED*/ +static int +samr_s_CreateDomainAlias(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_CreateDomainAlias *param = arg; + +#ifdef SAMR_SUPPORT_ADD_ALIAS + DWORD status = NT_STATUS_SUCCESS; + ms_handle_desc_t *desc = 0; + ms_handle_t *handle; + nt_group_t *grp; + char *alias_name; +#endif + bzero(¶m->alias_handle, sizeof (samr_handle_t)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); + +#ifdef SAMR_SUPPORT_ADD_ALIAS + alias_name = param->alias_name.str; + if (alias_name == 0) { + status = NT_STATUS_INVALID_PARAMETER; + goto create_alias_err; + } + + desc = mlsvc_lookup_handle((ms_handle_t *)¶m->domain_handle); + if (desc == 0 || + (desc->discrim != SAMR_LOCAL_DOMAIN) || + (strcmp(desc->key, SAMR_DOMAIN_KEY) != 0)) { + status = NT_STATUS_INVALID_HANDLE; + goto create_alias_err; + } + + /* + * Check access mask. User should be member of + * Administrators or Account Operators local group. + */ + status = nt_group_add(alias_name, 0, + NT_GROUP_AF_ADD | NT_GROUP_AF_LOCAL); + + if (status != NT_STATUS_SUCCESS) + goto create_alias_err; + + grp = nt_group_getinfo(alias_name, RWLOCK_READER); + if (grp == NULL) { + status = NT_STATUS_INTERNAL_ERROR; + goto create_alias_err; + } + + (void) nt_sid_get_rid(grp->sid, ¶m->rid); + nt_group_putinfo(grp); + handle = mlsvc_get_handle(MLSVC_IFSPEC_SAMR, SAMR_ALIAS_KEY, + param->rid); + bcopy(handle, ¶m->alias_handle, sizeof (samr_handle_t)); + + param->status = 0; + return (MLRPC_DRC_OK); + +create_alias_err: + bzero(¶m->alias_handle, sizeof (samr_handle_t)); + param->status = NT_SC_ERROR(status); + return (MLRPC_DRC_OK); +#endif +} + +/* + * samr_s_SetAliasInfo + * + * For more information you can look at MSDN page for NetLocalGroupSetInfo. + */ +/*ARGSUSED*/ +static int +samr_s_SetAliasInfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_SetAliasInfo *param = arg; + DWORD status = NT_STATUS_SUCCESS; + + if (!mlsvc_validate_handle( + (ms_handle_t *)¶m->alias_handle, SAMR_ALIAS_KEY)) { + status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); + } + + param->status = status; + return (MLRPC_DRC_OK); +} + +/* + * samr_s_QueryAliasInfo + * + * Retrieves information about the specified local group account + * by given handle. + */ +static int +samr_s_QueryAliasInfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_QueryAliasInfo *param = arg; + ms_handle_desc_t *desc; + nt_group_t *grp; + DWORD status; + + desc = mlsvc_lookup_handle((ms_handle_t *)¶m->alias_handle); + if (desc == NULL || (strcmp(desc->key, SAMR_ALIAS_KEY) != 0)) { + status = NT_STATUS_INVALID_HANDLE; + goto query_alias_err; + } + + grp = nt_groups_lookup_rid(desc->discrim); + if (grp == NULL) { + status = NT_STATUS_NO_SUCH_ALIAS; + goto query_alias_err; + } + + switch (param->level) { + case SAMR_QUERY_ALIAS_INFO_1: + param->ru.info1.level = param->level; + (void) mlsvc_string_save( + (ms_string_t *)¶m->ru.info1.name, grp->name, mxa); + + (void) mlsvc_string_save( + (ms_string_t *)¶m->ru.info1.desc, grp->comment, mxa); + + param->ru.info1.unknown = 1; + break; + + case SAMR_QUERY_ALIAS_INFO_3: + param->ru.info3.level = param->level; + (void) mlsvc_string_save( + (ms_string_t *)¶m->ru.info3.desc, grp->comment, mxa); + + break; + + default: + status = NT_STATUS_INVALID_INFO_CLASS; + goto query_alias_err; + }; + + param->address = (DWORD)¶m->ru; + param->status = 0; + return (MLRPC_DRC_OK); + +query_alias_err: + param->status = NT_SC_ERROR(status); + return (MLRPC_DRC_OK); +} + +/* + * samr_s_DeleteDomainAlias + * + * Deletes a local group account and all its members from the + * security database, which is the security accounts manager (SAM) database. + * Only members of the Administrators or Account Operators local group can + * execute this function. + * For more information you can look at MSDN page for NetLocalGroupSetInfo. + * + * This RPC is used by CMC and right now it returns access denied. + * The peice of code that removes a local group doesn't get compiled. + */ +/*ARGSUSED*/ +static int +samr_s_DeleteDomainAlias(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_DeleteDomainAlias *param = arg; + +#ifdef SAMR_SUPPORT_DEL_ALIAS + ms_handle_desc_t *desc = 0; + nt_group_t *grp; + char *alias_name; + DWORD status; +#endif + + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + return (MLRPC_DRC_OK); + +#ifdef SAMR_SUPPORT_DEL_ALIAS + desc = mlsvc_lookup_handle((ms_handle_t *)¶m->alias_handle); + if (desc == 0 || (strcmp(desc->key, SAMR_ALIAS_KEY) != 0)) { + status = NT_STATUS_INVALID_HANDLE; + goto delete_alias_err; + } + + grp = nt_groups_lookup_rid(desc->discrim); + if (grp == 0) { + status = NT_STATUS_NO_SUCH_ALIAS; + goto delete_alias_err; + } + + alias_name = strdup(grp->name); + if (alias_name == 0) { + status = NT_STATUS_NO_MEMORY; + goto delete_alias_err; + } + + status = nt_group_delete(alias_name); + free(alias_name); + if (status != NT_STATUS_SUCCESS) + goto delete_alias_err; + + param->status = 0; + return (MLRPC_DRC_OK); + +delete_alias_err: + param->status = NT_SC_ERROR(status); + return (MLRPC_DRC_OK); +#endif +} + +/* + * samr_s_EnumDomainAliases + * + * This function sends back a list which contains all local groups' name. + */ +static int +samr_s_EnumDomainAliases(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_EnumDomainAliases *param = arg; + ms_handle_desc_t *desc; + nt_group_t *grp = NULL; + DWORD status; + nt_group_iterator_t *gi; + nt_sid_t *local_sid; + nt_sid_t *builtin_sid; + nt_sid_t *sid; + DWORD cnt, skip; + struct name_rid *info; + + desc = mlsvc_lookup_handle((ms_handle_t *)¶m->domain_handle); + if (desc == NULL || (strcmp(desc->key, SAMR_DOMAIN_KEY) != 0)) { + status = NT_STATUS_INVALID_HANDLE; + goto enum_alias_err; + } + + local_sid = nt_domain_local_sid(); + builtin_sid = nt_builtin_lookup_name("BUILTIN", 0); + + if (desc->discrim == SAMR_LOCAL_DOMAIN) { + sid = local_sid; + } else if (desc->discrim == SAMR_BUILTIN_DOMAIN) { + sid = builtin_sid; + } else { + status = NT_STATUS_INVALID_HANDLE; + goto enum_alias_err; + } + + cnt = skip = 0; + gi = nt_group_open_iterator(); + nt_group_ht_lock(RWLOCK_READER); + while ((grp = nt_group_iterate(gi)) != 0) { + if (skip++ < param->resume_handle) + continue; + if (nt_sid_is_indomain(sid, grp->sid)) + cnt++; + } + nt_group_ht_unlock(); + nt_group_close_iterator(gi); + + param->aliases = (struct aliases_info *)MLRPC_HEAP_MALLOC(mxa, + sizeof (struct aliases_info) + (cnt-1) * sizeof (struct name_rid)); + + param->aliases->count = cnt; + param->aliases->address = cnt; + info = param->aliases->info; + + skip = 0; + gi = nt_group_open_iterator(); + nt_group_ht_lock(RWLOCK_READER); + while ((grp = nt_group_iterate(gi)) != NULL) { + if (skip++ < param->resume_handle) + continue; + if (nt_sid_is_indomain(sid, grp->sid)) { + (void) nt_sid_get_rid(grp->sid, &info->rid); + (void) mlsvc_string_save((ms_string_t *)&info->name, + grp->name, mxa); + + info++; + } + } + nt_group_ht_unlock(); + nt_group_close_iterator(gi); + + param->out_resume = cnt; + param->entries = cnt; + param->status = 0; + return (MLRPC_DRC_OK); + +enum_alias_err: + param->status = NT_SC_ERROR(status); + return (MLRPC_DRC_OK); +} + +/* + * samr_s_Connect3 + * + * This is the connect3 form of the connect request. It contains an + * extra parameter over samr_Connect. See samr_s_Connect for other + * details. NT returns an RPC fault - so we can do the same for now. + * Doing it this way should avoid the unsupported opnum error message + * appearing in the log. + */ +/*ARGSUSED*/ +static int +samr_s_Connect3(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_Connect3 *param = arg; + + bzero(param, sizeof (struct samr_Connect3)); + return (MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID); +} + + +/* + * samr_s_Connect4 + * + * This is the connect4 form of the connect request used by Windows XP. + * Returns an RPC fault for now. + */ +/*ARGSUSED*/ +static int +samr_s_Connect4(void *arg, struct mlrpc_xaction *mxa) +{ + struct samr_Connect4 *param = arg; + + bzero(param, sizeof (struct samr_Connect4)); + return (MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID); +} + +static mlrpc_stub_table_t samr_stub_table[] = { + { samr_s_ConnectAnon, SAMR_OPNUM_ConnectAnon }, + { samr_s_CloseHandle, SAMR_OPNUM_CloseHandle }, + { samr_s_LookupDomain, SAMR_OPNUM_LookupDomain }, + { samr_s_EnumLocalDomains, SAMR_OPNUM_EnumLocalDomains }, + { samr_s_OpenDomain, SAMR_OPNUM_OpenDomain }, + { samr_s_QueryDomainInfo, SAMR_OPNUM_QueryDomainInfo }, + { samr_s_LookupNames, SAMR_OPNUM_LookupNames }, + { samr_s_OpenUser, SAMR_OPNUM_OpenUser }, + { samr_s_DeleteUser, SAMR_OPNUM_DeleteUser }, + { samr_s_QueryUserInfo, SAMR_OPNUM_QueryUserInfo }, + { samr_s_QueryUserGroups, SAMR_OPNUM_QueryUserGroups }, + { samr_s_OpenGroup, SAMR_OPNUM_OpenGroup }, + { samr_s_Connect, SAMR_OPNUM_Connect }, + { samr_s_GetUserPwInfo, SAMR_OPNUM_GetUserPwInfo }, + { samr_s_CreateUser, SAMR_OPNUM_CreateUser }, + { samr_s_ChangeUserPasswd, SAMR_OPNUM_ChangeUserPasswd }, + { samr_s_GetDomainPwInfo, SAMR_OPNUM_GetDomainPwInfo }, + { samr_s_SetUserInfo, SAMR_OPNUM_SetUserInfo }, + { samr_s_Connect3, SAMR_OPNUM_Connect3 }, + { samr_s_Connect4, SAMR_OPNUM_Connect4 }, + { samr_s_QueryDispInfo, SAMR_OPNUM_QueryDispInfo }, + { samr_s_OpenAlias, SAMR_OPNUM_OpenAlias }, + { samr_s_CreateDomainAlias, SAMR_OPNUM_CreateDomainAlias }, + { samr_s_SetAliasInfo, SAMR_OPNUM_SetAliasInfo }, + { samr_s_QueryAliasInfo, SAMR_OPNUM_QueryAliasInfo }, + { samr_s_DeleteDomainAlias, SAMR_OPNUM_DeleteDomainAlias }, + { samr_s_EnumDomainAliases, SAMR_OPNUM_EnumDomainAliases }, + { samr_s_EnumDomainGroups, SAMR_OPNUM_EnumDomainGroups }, + {0} +}; + +/* + * There is a bug in the way that midl and the marshalling code handles + * unions so we need to fix some of the data offsets at runtime. The + * following macros and the fixup functions handle the corrections. + */ +DECL_FIXUP_STRUCT(samr_QueryDomainInfo_ru); +DECL_FIXUP_STRUCT(samr_QueryDomainInfoRes); +DECL_FIXUP_STRUCT(samr_QueryDomainInfo); + +DECL_FIXUP_STRUCT(samr_QueryAliasInfo_ru); +DECL_FIXUP_STRUCT(samr_QueryAliasInfoRes); +DECL_FIXUP_STRUCT(samr_QueryAliasInfo); + +DECL_FIXUP_STRUCT(QueryUserInfo_result_u); +DECL_FIXUP_STRUCT(QueryUserInfo_result); +DECL_FIXUP_STRUCT(samr_QueryUserInfo); + +void +fixup_samr_QueryDomainInfo(struct samr_QueryDomainInfo *val) +{ + unsigned short size1 = 0; + unsigned short size2 = 0; + unsigned short size3 = 0; + + switch (val->switch_value) { + CASE_INFO_ENT(samr_QueryDomainInfo, 2); + CASE_INFO_ENT(samr_QueryDomainInfo, 6); + CASE_INFO_ENT(samr_QueryDomainInfo, 7); + + default: + return; + }; + + size2 = size1 + (2 * sizeof (DWORD)); + size3 = size2 + sizeof (mlrpcconn_request_hdr_t) + sizeof (DWORD); + + FIXUP_PDU_SIZE(samr_QueryDomainInfo_ru, size1); + FIXUP_PDU_SIZE(samr_QueryDomainInfoRes, size2); + FIXUP_PDU_SIZE(samr_QueryDomainInfo, size3); +} + +void +fixup_samr_QueryAliasInfo(struct samr_QueryAliasInfo *val) +{ + unsigned short size1 = 0; + unsigned short size2 = 0; + unsigned short size3 = 0; + + switch (val->level) { + CASE_INFO_ENT(samr_QueryAliasInfo, 1); + CASE_INFO_ENT(samr_QueryAliasInfo, 3); + + default: + return; + }; + + size2 = size1 + (2 * sizeof (DWORD)); + size3 = size2 + sizeof (mlrpcconn_request_hdr_t) + sizeof (DWORD); + + FIXUP_PDU_SIZE(samr_QueryAliasInfo_ru, size1); + FIXUP_PDU_SIZE(samr_QueryAliasInfoRes, size2); + FIXUP_PDU_SIZE(samr_QueryAliasInfo, size3); +} + +void +fixup_samr_QueryUserInfo(struct samr_QueryUserInfo *val) +{ + unsigned short size1 = 0; + unsigned short size2 = 0; + unsigned short size3 = 0; + + switch (val->switch_index) { + CASE_INFO_ENT(samr_QueryUserInfo, 1); + CASE_INFO_ENT(samr_QueryUserInfo, 6); + CASE_INFO_ENT(samr_QueryUserInfo, 7); + CASE_INFO_ENT(samr_QueryUserInfo, 8); + CASE_INFO_ENT(samr_QueryUserInfo, 9); + CASE_INFO_ENT(samr_QueryUserInfo, 16); + + default: + return; + }; + + size2 = size1 + (2 * sizeof (DWORD)); + size3 = size2 + sizeof (mlrpcconn_request_hdr_t) + sizeof (DWORD); + + FIXUP_PDU_SIZE(QueryUserInfo_result_u, size1); + FIXUP_PDU_SIZE(QueryUserInfo_result, size2); + FIXUP_PDU_SIZE(samr_QueryUserInfo, size3); +} + +/* + * As long as there is only one entry in the union, there is no need + * to patch anything. + */ +/*ARGSUSED*/ +void +fixup_samr_QueryGroupInfo(struct samr_QueryGroupInfo *val) +{ +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_spoolss.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_spoolss.c new file mode 100644 index 000000000000..bac138cf091e --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_spoolss.c @@ -0,0 +1,139 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Printing and Spooling RPC interface definition. + * A stub to resolve RPC requests to this service. + */ + +#include +#include +#include +#include + +static mlrpc_stub_table_t spoolss_stub_table[]; + +static mlrpc_service_t spoolss_service = { + "SPOOLSS", /* name */ + "Print Spool Service", /* desc */ + "\\spoolss", /* endpoint */ + PIPE_SPOOLSS, /* sec_addr_port */ + "12345678-1234-abcd-ef000123456789ab", 1, /* abstract */ + "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */ + 0, /* no bind_instance_size */ + 0, /* no bind_req() */ + 0, /* no unbind_and_close() */ + 0, /* use generic_call_stub() */ + &TYPEINFO(spoolss_interface), /* interface ti */ + spoolss_stub_table /* stub_table */ +}; + +/* + * spoolss_initialize + * + * This function registers the SPOOLSS RPC interface with the RPC + * runtime library. It must be called in order to use either the + * client side or the server side functions. + */ +void +spoolss_initialize(void) +{ + (void) mlrpc_register_service(&spoolss_service); +} + +/* + * spoolss_s_OpenPrinter + * + * We don't offer print spooling support. It should be okay to + * set the status to access denied and return MLRPC_DRC_OK. + */ +static int +spoolss_s_OpenPrinter(void *arg, struct mlrpc_xaction *mxa) +{ + struct spoolss_OpenPrinter *param = arg; + + bzero(param, sizeof (struct spoolss_OpenPrinter)); + param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); + + return (MLRPC_DRC_OK); +} + + +/* + * spoolss_s_stub + */ +static int +spoolss_s_stub(void *arg, struct mlrpc_xaction *mxa) +{ + return (MLRPC_DRC_FAULT_PARAM_0_UNIMPLEMENTED); +} + +static mlrpc_stub_table_t spoolss_stub_table[] = { + { spoolss_s_OpenPrinter, SPOOLSS_OPNUM_OpenPrinter }, + { spoolss_s_stub, SPOOLSS_OPNUM_GetJob }, + { spoolss_s_stub, SPOOLSS_OPNUM_DeletePrinter }, + { spoolss_s_stub, SPOOLSS_OPNUM_GetPrinterDriver }, + { spoolss_s_stub, SPOOLSS_OPNUM_DeletePrinterDriver }, + { spoolss_s_stub, SPOOLSS_OPNUM_AddPrintProcessor }, + { spoolss_s_stub, SPOOLSS_OPNUM_GetPrintProcessorDirectory }, + { spoolss_s_stub, SPOOLSS_OPNUM_AbortPrinter }, + { spoolss_s_stub, SPOOLSS_OPNUM_ReadPrinter }, + { spoolss_s_stub, SPOOLSS_OPNUM_WaitForPrinterChange }, + { spoolss_s_stub, SPOOLSS_OPNUM_AddForm }, + { spoolss_s_stub, SPOOLSS_OPNUM_DeleteForm }, + { spoolss_s_stub, SPOOLSS_OPNUM_GetForm }, + { spoolss_s_stub, SPOOLSS_OPNUM_SetForm }, + { spoolss_s_stub, SPOOLSS_OPNUM_EnumMonitors }, + { spoolss_s_stub, SPOOLSS_OPNUM_AddPort }, + { spoolss_s_stub, SPOOLSS_OPNUM_ConfigurePort }, + { spoolss_s_stub, SPOOLSS_OPNUM_DeletePort }, + { spoolss_s_stub, SPOOLSS_OPNUM_CreatePrinterIc }, + { spoolss_s_stub, SPOOLSS_OPNUM_PlayDescriptionPrinterIc }, + { spoolss_s_stub, SPOOLSS_OPNUM_DeletePrinterIc }, + { spoolss_s_stub, SPOOLSS_OPNUM_AddPrinterConnection }, + { spoolss_s_stub, SPOOLSS_OPNUM_DeletePrinterConnection }, + { spoolss_s_stub, SPOOLSS_OPNUM_PrinterMessageBox }, + { spoolss_s_stub, SPOOLSS_OPNUM_AddMonitor }, + { spoolss_s_stub, SPOOLSS_OPNUM_DeleteMonitor }, + { spoolss_s_stub, SPOOLSS_OPNUM_DeletePrintProcessor }, + { spoolss_s_stub, SPOOLSS_OPNUM_AddPrintProvider }, + { spoolss_s_stub, SPOOLSS_OPNUM_DeletePrintProvider }, + { spoolss_s_stub, SPOOLSS_OPNUM_ResetPrinter }, + { spoolss_s_stub, SPOOLSS_OPNUM_FindFirstChangeNotify }, + { spoolss_s_stub, SPOOLSS_OPNUM_FindNextChangeNotify }, + { spoolss_s_stub, SPOOLSS_OPNUM_RouterFindFirstNotify }, + { spoolss_s_stub, SPOOLSS_OPNUM_ReplyOpenPrinter }, + { spoolss_s_stub, SPOOLSS_OPNUM_RouterReplyPrinter }, + { spoolss_s_stub, SPOOLSS_OPNUM_ReplyClosePrinter }, + { spoolss_s_stub, SPOOLSS_OPNUM_AddPortEx }, + { spoolss_s_stub, SPOOLSS_OPNUM_RemoteFindFirstChangeNotify }, + { spoolss_s_stub, SPOOLSS_OPNUM_SpoolerInitialize }, + { spoolss_s_stub, SPOOLSS_OPNUM_ResetPrinterEx }, + { spoolss_s_stub, SPOOLSS_OPNUM_RouterRefreshChangeNotify }, + { spoolss_s_OpenPrinter, SPOOLSS_OPNUM_OpenPrinter2 }, + {0} +}; diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_srvsvc.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_srvsvc.c new file mode 100644 index 000000000000..2a9a1f52d0ed --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_srvsvc.c @@ -0,0 +1,1684 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Server Service RPC (SRVSVC) server-side interface definition. + * The server service provides a remote administration interface. + * + * This service uses NERR/Win32 error codes rather than NT status + * values. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SV_TYPE_SENT_BY_ME (SV_TYPE_WORKSTATION | SV_TYPE_SERVER | SV_TYPE_NT) + +static DWORD mlsvc_NetSessionEnumLevel0(struct mslm_infonres *, DWORD, + struct mlrpc_xaction *); +static DWORD mlsvc_NetSessionEnumLevel1(struct mslm_infonres *, DWORD, + struct mlrpc_xaction *); +static DWORD mlsvc_NetShareEnumLevel0(struct mslm_infonres *, DWORD, + struct mlrpc_xaction *, char); +static DWORD mlsvc_NetShareEnumLevel1(struct mslm_infonres *, DWORD, + struct mlrpc_xaction *, char); +static DWORD mlsvc_NetShareEnumLevel2(struct mslm_infonres *, DWORD, + struct mlrpc_xaction *, char); +static DWORD mlsvc_NetShareEnumLevel502(struct mslm_infonres *, DWORD, + struct mlrpc_xaction *, char); +static DWORD mlsvc_NetShareEnumCommon(struct mlrpc_xaction *, DWORD, + int, lmshare_info_t *, void *); +static int srvsvc_is_poweruser(struct mlrpc_xaction *); + +static char empty_string[1]; + +static mlrpc_stub_table_t srvsvc_stub_table[]; + +static mlrpc_service_t srvsvc_service = { + "SRVSVC", /* name */ + "Server services", /* desc */ + "\\srvsvc", /* endpoint */ + PIPE_NTSVCS, /* sec_addr_port */ + "4b324fc8-1670-01d3-12785a47bf6ee188", 3, /* abstract */ + "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */ + 0, /* no bind_instance_size */ + 0, /* no bind_req() */ + 0, /* no unbind_and_close() */ + 0, /* use generic_call_stub() */ + &TYPEINFO(srvsvc_interface), /* interface ti */ + srvsvc_stub_table /* stub_table */ +}; + +/* + * srvsvc_fix_comment + * + * The parser sometimes has problems with empty strings so we + * need to ensure that the comment field has something in it. + */ +static inline char * +srvsvc_fix_comment(char *original, char *alternative) +{ + if (original == 0 || strlen(original) == 0) + return (alternative); + + return (original); +} + +/* + * srvsvc_share_mkpath + * + * Create the share path required by the share enum calls. This function + * creates the path in a MLRPC heap buffer ready for use by the caller. + * + * Some Windows over-the-wire backup applications do not work unless a + * drive letter is present in the share path. We don't care about the + * drive letter since the path is fully qualified with the volume name. + * We can try using drive B since by default that letter isn't assigned + * and even if it conflicts, we should still be okay with the fully + * qualified path. + * + * Windows clients seem to be mostly okay with the forward slash in + * share paths but they cannot handle one immediately after the drive + * letter, i.e. D:/. For consistency we convert all the slashes in + * the path. + * + * Returns a pointer to a heap buffer containing the share path, which + * could be a null pointer if the heap allocation fails. + */ +static char * +srvsvc_share_mkpath(struct mlrpc_xaction *mxa, char *path) +{ + char tmpbuf[MAXPATHLEN]; + char *p; + + if (strlen(path) == 0) + return (MLRPC_HEAP_STRSAVE(mxa, path)); + + /* strip the volume from the path (/vol1/home -> /home) */ + p = strchr(path[0] == '/' ? &path[1] : path, '/'); + + (void) snprintf(tmpbuf, MAXPATHLEN, "%c:%s", 'B' + /* vattr.drive_letter */, p == NULL ? "/": p); + (void) strsubst(tmpbuf, '/', '\\'); + + return (MLRPC_HEAP_STRSAVE(mxa, tmpbuf)); +} + +/* + * srvsvc_add_autohome + * + * Add the autohome share for the user to the shares' list + * if autohome is enabled the share is not a permanent share. + */ +static int +srvsvc_add_autohome(struct mlrpc_xaction *mxa, char *username, DWORD i, + int level, char *infop) +{ + lmshare_info_t si; + DWORD status; + + if ((lmshare_getinfo(username, &si) == NERR_Success) && + (si.mode & LMSHRM_TRANS)) { + status = mlsvc_NetShareEnumCommon(mxa, i, level, &si, + (void *)infop); + if (status == ERROR_SUCCESS) + i++; + } + + return (i); +} + +/* + * srvsvc_initialize + * + * This function registers the SRVSVC RPC interface with the RPC runtime + * library. It must be called in order to use either the client side + * or the server side functions. + */ +void +srvsvc_initialize(void) +{ + (void) mlrpc_register_service(&srvsvc_service); +} + +/* + * srvsvc_s_NetConnectEnum + * + * Under construction. This is just enough to get the interface working. + * Current level 0 and level 1 connection info are supported. + * + * Level 1 request is made by 'srvmgr' (Server Manager) + * utility of NT Server part of NT Domain to MLRPC server + * while double click of share info icon. These values + * are currectly virtual to MLRPC client and does't + * reflect the real state of server. + */ +static int +srvsvc_s_NetConnectEnum(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslm_NetConnectEnum *param = arg; + struct mslm_NetConnectInfoBuf0 *ci0; + struct mslm_NetConnectInfoBuf1 *ci1; + DWORD status; + + status = ERROR_SUCCESS; + switch (param->info.level) { + case 0: + ci0 = MLRPC_HEAP_NEW(mxa, struct mslm_NetConnectInfoBuf0); + if (ci0 == 0) { + status = ERROR_NOT_ENOUGH_MEMORY; + break; + } + ci0->coni0_id = 0x17; + + param->info.ru.info0 + = MLRPC_HEAP_NEW(mxa, struct mslm_NetConnectInfo0); + + if (param->info.ru.info0 == 0) { + status = ERROR_NOT_ENOUGH_MEMORY; + break; + } + param->info.ru.info0->ci0 = ci0; + param->info.ru.info0->entries_read = 1; + + param->total_entries = 1; + param->resume_handle = 0; + break; + + case 1: + ci1 = MLRPC_HEAP_NEW(mxa, struct mslm_NetConnectInfoBuf1); + if (ci1 == 0) { + status = ERROR_NOT_ENOUGH_MEMORY; + break; + } + ci1->coni1_id = 0x17; + ci1->coni1_type = STYPE_IPC; + ci1->coni1_num_opens = 1; + ci1->coni1_num_users = 1; + ci1->coni1_time = 16; + ci1->coni1_username = + (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, "Administrator"); + + ci1->coni1_netname = + (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, "IPC$"); + + param->info.ru.info1 = MLRPC_HEAP_NEW(mxa, + struct mslm_NetConnectInfo1); + + if (param->info.ru.info1 == 0) { + status = ERROR_NOT_ENOUGH_MEMORY; + break; + } + param->info.ru.info1->ci1 = ci1; + param->info.ru.info1->entries_read = 1; + + param->total_entries = 1; + param->resume_handle = 0; + break; + + default: + status = ERROR_ACCESS_DENIED; + break; + } + + if (status != ERROR_SUCCESS) + bzero(param, sizeof (struct mslm_NetConnectEnum)); + + param->status = status; + return (MLRPC_DRC_OK); +} + + +/* + * srvsvc_s_NetFileEnum + * + * Under construction. The values used here are fictional values and + * bear no relation to any real values, living or otherwise. I just + * made them up to get the interface working. + */ +static int +srvsvc_s_NetFileEnum(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslm_NetFileEnum *param = arg; + struct mslm_NetFileInfoBuf3 *fi3; + + if (param->info.switch_value != 3) { + bzero(param, sizeof (struct mslm_NetFileEnum)); + param->status = ERROR_INVALID_LEVEL; + return (MLRPC_DRC_OK); + } + + fi3 = MLRPC_HEAP_NEW(mxa, struct mslm_NetFileInfoBuf3); + if (fi3 == 0) { + bzero(param, sizeof (struct mslm_NetFileEnum)); + param->status = ERROR_NOT_ENOUGH_MEMORY; + return (MLRPC_DRC_OK); + } + + fi3->fi3_id = 0xF5; + fi3->fi3_permissions = 0x23; + fi3->fi3_num_locks = 0; + fi3->fi3_pathname = + (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, "\\PIPE\\srvsvc"); + + fi3->fi3_username = + (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, "Administrator"); + + param->info.ru.info3 = MLRPC_HEAP_NEW(mxa, struct mslm_NetFileInfo3); + if (param->info.ru.info3 == 0) { + bzero(param, sizeof (struct mslm_NetFileEnum)); + param->status = ERROR_NOT_ENOUGH_MEMORY; + return (MLRPC_DRC_OK); + } + + param->info.ru.info3->fi3 = fi3; + param->info.ru.info3->entries_read = 1; + param->total_entries = 1; + + if (param->resume_handle) + *param->resume_handle = 0x5F; + + param->status = ERROR_SUCCESS; + return (MLRPC_DRC_OK); +} + + +/* + * srvsvc_s_NetFileClose + * + * Under construction. This is just enough to get the interface working. + */ +/*ARGSUSED*/ +static int +srvsvc_s_NetFileClose(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslm_NetFileClose *param = arg; + + bzero(param, sizeof (struct mslm_NetFileClose)); + param->status = ERROR_SUCCESS; + return (MLRPC_DRC_OK); +} + + +/* + * srvsvc_s_NetShareGetInfo + * + * This call is made by Windows2000 to get share information. There are + * probably other information levels but these are the only ones I've + * seen so far. + * + * Returns Win32 error codes. + */ +static int +srvsvc_s_NetShareGetInfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct mlsm_NetShareGetInfo *param = arg; + struct mslm_NetShareGetInfo0 *info0; + struct mslm_NetShareGetInfo1 *info1; + struct mslm_NetShareGetInfo2 *info2; + struct mslm_NetShareGetInfo502 *info502; + struct mslm_NetShareGetInfo1005 *info1005; + struct lmshare_info si; + char shr_comment[LMSHR_COMMENT_MAX]; + DWORD status; + + status = lmshare_getinfo((char *)param->netname, &si); + if (status != NERR_Success) { + if (strcasecmp((const char *)param->netname, "IPC$") == 0) { + /* + * Windows clients don't send the \\PIPE path for IPC$. + */ + (void) memset(&si, 0, sizeof (lmshare_info_t)); + (void) strcpy(si.share_name, "IPC$"); + (void) strcpy(si.comment, "Remote IPC"); + si.stype = (int)(STYPE_IPC | STYPE_SPECIAL); + } else { + bzero(param, sizeof (struct mlsm_NetShareGetInfo)); + param->status = status; + return (MLRPC_DRC_OK); + } + } + + if (si.comment && strlen(si.comment)) + (void) snprintf(shr_comment, sizeof (shr_comment), "%s %s", + si.directory, si.comment); + else + (void) strcpy(shr_comment, si.directory); + + status = ERROR_SUCCESS; + + switch (param->level) { + case 0: + info0 = MLRPC_HEAP_NEW(mxa, struct mslm_NetShareGetInfo0); + if (info0 == 0) { + + status = ERROR_NOT_ENOUGH_MEMORY; + break; + } + info0->shi0_netname + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si.share_name); + + param->result.ru.info0 = info0; + break; + + case 1: + info1 = MLRPC_HEAP_NEW(mxa, struct mslm_NetShareGetInfo1); + if (info1 == 0) { + status = ERROR_NOT_ENOUGH_MEMORY; + break; + } + info1->shi1_netname = + (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si.share_name); + info1->shi1_comment = + (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, shr_comment); + info1->shi1_type = si.stype; + param->result.ru.info1 = info1; + break; + + case 2: + info2 = MLRPC_HEAP_NEW(mxa, struct mslm_NetShareGetInfo2); + if (info2 == 0) { + status = ERROR_NOT_ENOUGH_MEMORY; + break; + } + info2->shi2_netname = + (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si.share_name); + info2->shi2_comment = + (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, shr_comment); + info2->shi2_path = + (unsigned char *)srvsvc_share_mkpath(mxa, si.directory); + info2->shi2_passwd = 0; + info2->shi2_type = si.stype; + info2->shi2_permissions = 0; + info2->shi2_max_uses = SHI_USES_UNLIMITED; + info2->shi2_current_uses = 0; + param->result.ru.info2 = info2; + break; + + case 1005: + info1005 = MLRPC_HEAP_NEW(mxa, struct mslm_NetShareGetInfo1005); + if (info1005 == 0) { + + status = ERROR_NOT_ENOUGH_MEMORY; + break; + } + info1005->shi1005_flags = 0; + param->result.ru.info1005 = info1005; + break; + + case 502: + /* + * Level 502 provides level 2 information plus a + * security descriptor. We don't support security + * descriptors on shares yet. + */ + info502 = MLRPC_HEAP_NEW(mxa, struct mslm_NetShareGetInfo502); + if (info502 == 0) { + status = ERROR_NOT_ENOUGH_MEMORY; + break; + } + info502->shi502_netname = + (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si.share_name); + info502->shi502_comment = + (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, shr_comment); + info502->shi502_path = + (unsigned char *)srvsvc_share_mkpath(mxa, si.directory); + info502->shi502_passwd = 0; + info502->shi502_type = si.stype; + info502->shi502_permissions = 0; + info502->shi502_max_uses = SHI_USES_UNLIMITED; + info502->shi502_current_uses = 0; + info502->shi502_reserved = 0; + info502->shi502_security_descriptor = 0; + param->result.ru.info502 = info502; + break; + + default: + status = ERROR_ACCESS_DENIED; + break; + } + + if (status != ERROR_SUCCESS) + bzero(param, sizeof (struct mlsm_NetShareGetInfo)); + else + param->result.switch_value = param->level; + + param->status = status; + return (MLRPC_DRC_OK); +} + + +/* + * srvsvc_s_NetShareSetInfo + * + * This call is made by SrvMgr to set share information. + * Always returns ERROR_ACCESS_DENIED for now. + * + * Returns Win32 error codes. + */ +static int +srvsvc_s_NetShareSetInfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct mlsm_NetShareSetInfo *param = arg; + + (void) memset(param, 0, sizeof (struct mlsm_NetShareSetInfo)); + param->parm_err_ptr = (DWORD)MLRPC_HEAP_MALLOC(mxa, sizeof (DWORD)); + param->parm_err = 0; + + smb_config_rdlock(); + if (smb_config_getyorn(SMB_CI_SRVSVC_SHRSET_ENABLE) != 0) + param->status = ERROR_SUCCESS; + else + param->status = ERROR_ACCESS_DENIED; + smb_config_unlock(); + + return (MLRPC_DRC_OK); +} + +/* + * srvsvc_s_NetSessionEnum + * + * Level 1 request is made by the 'srvmgr' (Server Manager) utility on + * NT Server when the user info icon is selected. + * + * Return Values + * If the function succeeds, the return value is NERR_Success. + * If the function fails, the return value can be one of the following + * error codes: + * + * ERROR_ACCESS_DENIED The user does not have access to the requested + * information. + * ERROR_INVALID_LEVEL The value specified for the level parameter is + * invalid. + * ERROR_INVALID_PARAMETER The specified parameter is invalid. + * ERROR_MORE_DATA More entries are available. Specify a large + * enough buffer to receive all entries. + * ERROR_NOT_ENOUGH_MEMORY Insufficient memory is available. + * NERR_ClientNameNotFound A session does not exist with the computer + * name. + * NERR_InvalidComputer The computer name is invalid. + * NERR_UserNotFound The user name could not be found. + */ +static int +srvsvc_s_NetSessionEnum(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslm_NetSessionEnum *param = arg; + struct mslm_infonres *infonres; + DWORD status; + DWORD n_sessions; + + infonres = MLRPC_HEAP_NEW(mxa, struct mslm_infonres); + if (infonres == 0) { + bzero(param, sizeof (struct mslm_NetSessionEnum)); + param->status = ERROR_NOT_ENOUGH_MEMORY; + return (MLRPC_DRC_OK); + } + + infonres->entriesread = 0; + infonres->entries = 0; + param->result.level = param->level; + param->result.bufptr.p = infonres; + param->total_entries = 1; + param->status = ERROR_SUCCESS; + + n_sessions = (DWORD) smb_dwncall_user_num(); + + switch (param->level) { + case 0: + status = mlsvc_NetSessionEnumLevel0(infonres, n_sessions, mxa); + break; + + case 1: + status = mlsvc_NetSessionEnumLevel1(infonres, n_sessions, mxa); + break; + + default: + status = ERROR_INVALID_LEVEL; + break; + } + + if (status != 0) { + bzero(param, sizeof (struct mslm_NetSessionEnum)); + param->status = status; + return (MLRPC_DRC_OK); + } + + param->resume_handle = 0; + param->total_entries = infonres->entriesread; + param->status = status; + return (MLRPC_DRC_OK); +} + +/* + * mlsvc_NetSessionEnumLevel0 + * + * Build the level 0 session information. + */ +/*ARGSUSED*/ +static DWORD +mlsvc_NetSessionEnumLevel0(struct mslm_infonres *infonres, DWORD n_sessions, + struct mlrpc_xaction *mxa) +{ + struct mslm_SESSION_INFO_0 *info0; + smb_dr_ulist_t *ulist; + smb_dr_user_ctx_t *user; + char *workstation; + char ipaddr_buf[INET_ADDRSTRLEN]; + int i, offset, cnt, total; + + info0 = MLRPC_HEAP_NEWN(mxa, struct mslm_SESSION_INFO_0, n_sessions); + if (info0 == 0) + return (ERROR_NOT_ENOUGH_MEMORY); + + ulist = malloc(sizeof (smb_dr_ulist_t)); + if (!ulist) + return (ERROR_NOT_ENOUGH_MEMORY); + + for (total = 0, offset = 0; + (cnt = smb_dwncall_get_users(offset, ulist)) > 0; + offset += cnt) { + for (i = 0; i < cnt && total < n_sessions; i++, total++) { + user = &ulist->dul_users[i]; + /* + * Ignore local tokens (IP address is zero). + */ + if (user->du_ipaddr == 0) { + total--; + smb_dr_ulist_free(ulist); + ulist = malloc(sizeof (smb_dr_ulist_t)); + if (!ulist) + return (ERROR_NOT_ENOUGH_MEMORY); + continue; + } + + if ((workstation = user->du_workstation) == 0) { + (void) inet_ntop(AF_INET, + (char *)&user->du_ipaddr, ipaddr_buf, + sizeof (ipaddr_buf)); + workstation = ipaddr_buf; + } + + info0[total].sesi0_cname = MLRPC_HEAP_STRSAVE(mxa, + workstation); + if (info0[total].sesi0_cname == 0) { + smb_dr_ulist_free(ulist); + return (ERROR_NOT_ENOUGH_MEMORY); + } + + } + smb_dr_ulist_free(ulist); + ulist = malloc(sizeof (smb_dr_ulist_t)); + if (!ulist) + return (ERROR_NOT_ENOUGH_MEMORY); + + } + + infonres->entriesread = total; + infonres->entries = info0; + return (ERROR_SUCCESS); +} + + +/* + * mlsvc_NetSessionEnumLevel1 + * + * Build the level 1 session information. + */ +/*ARGSUSED*/ +static DWORD +mlsvc_NetSessionEnumLevel1(struct mslm_infonres *infonres, DWORD n_sessions, + struct mlrpc_xaction *mxa) +{ + struct mslm_SESSION_INFO_1 *info1; + smb_dr_ulist_t *ulist; + smb_dr_user_ctx_t *user; + char *workstation; + char *account; + char ipaddr_buf[INET_ADDRSTRLEN]; + int i, offset, cnt, total; + + info1 = MLRPC_HEAP_NEWN(mxa, struct mslm_SESSION_INFO_1, n_sessions); + if (info1 == 0) + return (ERROR_NOT_ENOUGH_MEMORY); + + ulist = malloc(sizeof (smb_dr_ulist_t)); + if (!ulist) + return (ERROR_NOT_ENOUGH_MEMORY); + + for (total = 0, offset = 0; + (cnt = smb_dwncall_get_users(offset, ulist)) > 0; + offset += cnt) { + for (i = 0; i < cnt && total < n_sessions; i++, total++) { + user = &ulist->dul_users[i]; + /* + * Ignore local user_ctxs (IP address is zero). + */ + if (user->du_ipaddr == 0) { + total--; + smb_dr_ulist_free(ulist); + ulist = malloc(sizeof (smb_dr_ulist_t)); + if (!ulist) + return (ERROR_NOT_ENOUGH_MEMORY); + continue; + } + + if ((workstation = user->du_workstation) == 0) { + (void) inet_ntop(AF_INET, + (char *)&user->du_ipaddr, + ipaddr_buf, sizeof (ipaddr_buf)); + workstation = ipaddr_buf; + } + + if ((account = user->du_account) == 0) + account = "Unknown"; + + info1[total].sesi1_cname = MLRPC_HEAP_STRSAVE(mxa, + workstation); + info1[total].sesi1_uname = MLRPC_HEAP_STRSAVE(mxa, + account); + + if (info1[total].sesi1_cname == 0 || + info1[total].sesi1_uname == 0) { + smb_dr_ulist_free(ulist); + return (ERROR_NOT_ENOUGH_MEMORY); + } + + info1[total].sesi1_nopens = 1; + info1[total].sesi1_time = time(0) - + user->du_logon_time; + info1[total].sesi1_itime = 0; + info1[total].sesi1_uflags = + (user->du_flags & SMB_ATF_GUEST) ? SESS_GUEST : 0; + } + smb_dr_ulist_free(ulist); + ulist = malloc(sizeof (smb_dr_ulist_t)); + if (!ulist) + return (ERROR_NOT_ENOUGH_MEMORY); + } + + infonres->entriesread = total; + infonres->entries = info1; + return (ERROR_SUCCESS); +} + +/* + * srvsvc_s_NetSessionDel + * + * Ends a network session between a server and a workstation. + * On NT only members of the Administrators or Account Operators + * local groups are permitted to use NetSessionDel. + * + * Return Values + * If the function succeeds, the return value is NERR_Success/ + * ERROR_SUCCESS. If the function fails, the return value can be + * one of the following error codes: + * + * ERROR_ACCESS_DENIED The user does not have access to the + * requested information. + * ERROR_INVALID_PARAMETER The specified parameter is invalid. + * ERROR_NOT_ENOUGH_MEMORY Insufficient memory is available. + * NERR_ClientNameNotFound A session does not exist with that + * computer name. + */ +static int +srvsvc_s_NetSessionDel(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslm_NetSessionDel *param = arg; + + if (srvsvc_is_poweruser(mxa) == 0) { + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); + } + + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +/* + * SRVSVC NetServerGetInfo + * + * IN LPTSTR servername, + * IN DWORD level, + * OUT union switch(level) { + * case 100: mslm_SERVER_INFO_100 *p100; + * case 101: mslm_SERVER_INFO_101 *p101; + * case 102: mslm_SERVER_INFO_102 *p102; + * default: char *nullptr; + * } bufptr, + * OUT DWORD status + */ +static int +srvsvc_s_NetServerGetInfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslm_NetServerGetInfo *param = arg; + struct mslm_SERVER_INFO_100 *info100; + struct mslm_SERVER_INFO_101 *info101; + struct mslm_SERVER_INFO_102 *info102; + char *sys_comment; + char hostname[MAXHOSTNAMELEN]; + + if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0) { +netservergetinfo_no_memory: + bzero(param, sizeof (struct mslm_NetServerGetInfo)); + return (ERROR_NOT_ENOUGH_MEMORY); + } + + smb_config_rdlock(); + sys_comment = smb_config_getstr(SMB_CI_SYS_CMNT); + sys_comment = srvsvc_fix_comment(sys_comment, " "); + smb_config_unlock(); + + switch (param->level) { + case 100: + info100 = MLRPC_HEAP_NEW(mxa, struct mslm_SERVER_INFO_100); + if (info100 == 0) + goto netservergetinfo_no_memory; + + bzero(info100, sizeof (struct mslm_SERVER_INFO_100)); + info100->sv100_platform_id = SV_PLATFORM_ID_NT; + info100->sv100_name + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, hostname); + + if (info100->sv100_name == 0) + goto netservergetinfo_no_memory; + + param->result.bufptr.bufptr100 = info100; + break; + + case 101: + info101 = MLRPC_HEAP_NEW(mxa, struct mslm_SERVER_INFO_101); + if (info101 == 0) + goto netservergetinfo_no_memory; + + bzero(info101, sizeof (struct mslm_SERVER_INFO_101)); + info101->sv101_platform_id = SV_PLATFORM_ID_NT; + info101->sv101_version_major = 4; + info101->sv101_version_minor = 0; + info101->sv101_type = SV_TYPE_SENT_BY_ME; + info101->sv101_name + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, hostname); + + info101->sv101_comment + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, sys_comment); + + if (info101->sv101_name == 0 || info101->sv101_comment == 0) + goto netservergetinfo_no_memory; + + param->result.bufptr.bufptr101 = info101; + break; + + case 102: + info102 = MLRPC_HEAP_NEW(mxa, struct mslm_SERVER_INFO_102); + if (info102 == 0) + goto netservergetinfo_no_memory; + + bzero(info102, sizeof (struct mslm_SERVER_INFO_102)); + info102->sv102_platform_id = SV_PLATFORM_ID_NT; + info102->sv102_version_major = 4; + info102->sv102_version_minor = 0; + info102->sv102_type = SV_TYPE_SENT_BY_ME; + info102->sv102_name + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, hostname); + + info102->sv102_comment + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, sys_comment); + + /* + * The following level 102 fields are defaulted to zero + * by virtue of the call to bzero above. + * + * sv102_users + * sv102_disc + * sv102_hidden + * sv102_announce + * sv102_anndelta + * sv102_licenses + * sv102_userpath + */ + if (info102->sv102_name == 0 || info102->sv102_comment == 0) + goto netservergetinfo_no_memory; + + param->result.bufptr.bufptr102 = info102; + break; + + default: + bzero(¶m->result, + sizeof (struct mslm_NetServerGetInfo_result)); + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); + } + + param->result.level = param->level; + param->status = (ERROR_SUCCESS); + return (MLRPC_DRC_OK); +} + +/* + * NetRemoteTOD + * + * Returns information about the time of day on this server. + * + * typedef struct _TIME_OF_DAY_INFO { + * DWORD tod_elapsedt; // seconds since 00:00:00 January 1 1970 GMT + * DWORD tod_msecs; // arbitrary milliseconds (since reset) + * DWORD tod_hours; // current hour [0-23] + * DWORD tod_mins; // current minute [0-59] + * DWORD tod_secs; // current second [0-59] + * DWORD tod_hunds; // current hundredth (0.01) second [0-99] + * LONG tod_timezone; // time zone of the server + * DWORD tod_tinterval; // clock tick time interval + * DWORD tod_day; // day of the month [1-31] + * DWORD tod_month; // month of the year [1-12] + * DWORD tod_year; // current year + * DWORD tod_weekday; // day of the week since sunday [0-6] + * } TIME_OF_DAY_INFO; + * + * The time zone of the server is calculated in minutes from Greenwich + * Mean Time (GMT). For time zones west of Greenwich, the value is + * positive; for time zones east of Greenwich, the value is negative. + * A value of -1 indicates that the time zone is undefined. + * + * The clock tick value represents a resolution of one ten-thousandth + * (0.0001) second. + */ +static int +srvsvc_s_NetRemoteTOD(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslm_NetRemoteTOD *param = arg; + struct mslm_TIME_OF_DAY_INFO *tod; + struct timeval time_val; + struct tm tm; + + (void) gettimeofday(&time_val, 0); + (void) gmtime_r(&time_val.tv_sec, &tm); + + tod = MLRPC_HEAP_NEW(mxa, struct mslm_TIME_OF_DAY_INFO); + if (tod == NULL) { + bzero(param, sizeof (struct mslm_NetRemoteTOD)); + return (ERROR_NOT_ENOUGH_MEMORY); + } + + tod->tod_elapsedt = time_val.tv_sec; + tod->tod_msecs = time_val.tv_usec; + tod->tod_hours = tm.tm_hour; + tod->tod_mins = tm.tm_min; + tod->tod_secs = tm.tm_sec; + tod->tod_hunds = 0; + tod->tod_tinterval = 1000; + tod->tod_day = tm.tm_mday; + tod->tod_month = tm.tm_mon+1; + tod->tod_year = tm.tm_year+1900; + tod->tod_weekday = tm.tm_wday; + + (void) localtime_r(&time_val.tv_sec, &tm); + + param->bufptr = tod; + param->status = ERROR_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * srvsvc_s_NetNameValidate + * + * Perform name validation. + * I've observed that the Computer Management Windows Application + * always send this request with type=0x09 and the flags=0 when + * attempting to validate a share name. + * + * The share name is consider invalid if it contains any of the + * following character (as mentioned in MSDN article #236388). + * + * " / \ [ ] : | < > + ; , ? * = + * + * + * For now, if the type is other than 0x09, return access denied. + * + * Returns Win32 error codes. + */ +/*ARGSUSED*/ +static int +srvsvc_s_NetNameValidate(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslm_NetNameValidate *param = arg; + + switch (param->type) { + case 0x09: + param->status = lmshare_is_valid((char *)param->pathname) ? + ERROR_SUCCESS : ERROR_INVALID_NAME; + break; + + default: + param->status = ERROR_ACCESS_DENIED; + break; + } + + return (MLRPC_DRC_OK); +} + +/* + * srvsvc_s_NetShareAdd + * + * Add a new share. We support info levels 2 and 502 but ignore the + * security descriptor in level 502 requests. Only the administrator, + * or a member of the domain administrators group, is allowed to add + * shares. + * + * This interface is used by the rmtshare command from the NT resource + * kit. Rmtshare allows a client to add or remove shares on a server + * from the client's command line. + * + * Note that we don't support security descriptors on a share. If the + * /grant is used, the share will be created but the subsequent attempt + * to manipulate the security descriptor (NetShareGetInfo) will fail. + * Similarly for the /remove option. + * + * Returns Win32 error codes. + */ +static int +srvsvc_s_NetShareAdd(void *arg, struct mlrpc_xaction *mxa) +{ + static DWORD parm_err = 0; + DWORD parm_stat; + struct mslm_NetShareAdd *param = arg; + smb_dr_user_ctx_t *user_ctx; + struct mslm_SHARE_INFO_2 *info2; + struct lmshare_info si; + char realpath[MAXPATHLEN]; + + user_ctx = mxa->context->user_ctx; + + if (srvsvc_is_poweruser(mxa) == 0) { + bzero(param, sizeof (struct mslm_NetShareAdd)); + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); + } + + switch (param->level) { + case 2: + info2 = param->info.un.info2; + break; + + case 502: + info2 = (struct mslm_SHARE_INFO_2 *)param->info.un.info502; + break; + + default: + bzero(param, sizeof (struct mslm_NetShareAdd)); + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); + } + + if (info2->shi2_netname == 0 || info2->shi2_path == 0) { + bzero(param, sizeof (struct mslm_NetShareAdd)); + param->status = NERR_NetNameNotFound; + return (MLRPC_DRC_OK); + } + + if (lmshare_is_restricted((char *)info2->shi2_netname)) { + bzero(param, sizeof (struct mslm_NetShareAdd)); + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); + } + + if (info2->shi2_remark == 0) + info2->shi2_remark = (unsigned char *)""; + + /* + * Derive the real path which will be stored in the + * directory field of the lmshare_info_t structure + * from the path field in this RPC request. + */ + parm_stat = lmshare_get_realpath((const char *)info2->shi2_path, + realpath, MAXPATHLEN); + + if (parm_stat != NERR_Success) { + bzero(param, sizeof (struct mslm_NetShareAdd)); + param->status = parm_stat; + param->parm_err + = (user_ctx->du_native_os == NATIVE_OS_WIN95) ? + 0 : &parm_err; + return (MLRPC_DRC_OK); + } + + (void) memset(&si, 0, sizeof (lmshare_info_t)); + (void) strlcpy(si.share_name, (const char *)info2->shi2_netname, + MAXNAMELEN); + + (void) strlcpy(si.directory, realpath, MAXPATHLEN); + (void) strlcpy(si.comment, (const char *)info2->shi2_remark, + LMSHR_COMMENT_MAX); + + si.mode = LMSHRM_PERM; + + param->status = lmshare_add(&si, 1); + param->parm_err = (user_ctx->du_native_os == NATIVE_OS_WIN95) ? + 0 : &parm_err; + return (MLRPC_DRC_OK); +} + +/* + * srvsvc_is_poweruser + * + * Check whether or not the specified user has power-user privileges, + * i.e. is a member of the Domain Admins, Administrators or Power + * Users groups. This is typically required for operations such as + * adding/deleting shares. + * + * Returns 1 if the user is a power user, otherwise returns 0. + */ +static int +srvsvc_is_poweruser(struct mlrpc_xaction *mxa) +{ + smb_dr_user_ctx_t *user = mxa->context->user_ctx; + + return ((user->du_flags & SMB_ATF_ADMIN) || + (user->du_flags & SMB_ATF_POWERUSER)); +} + +/* + * srvsvc_s_NetShareEnum + * + * Request for various levels of information about our shares. + * Level 0: just the share names. + * Level 1: the share name, the share type and the comment field. + * Level 2: everything that we know about the shares. + */ +static int +srvsvc_s_NetShareEnum(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslm_NetShareEnum *param = arg; + struct mslm_infonres *infonres; + DWORD status; + DWORD n_shares; + + infonres = MLRPC_HEAP_NEW(mxa, struct mslm_infonres); + if (infonres == 0) { + bzero(param, sizeof (struct mslm_NetShareEnum)); + param->status = ERROR_NOT_ENOUGH_MEMORY; + return (MLRPC_DRC_OK); + } + + infonres->entriesread = 0; + infonres->entries = 0; + param->result.level = param->level; + param->result.bufptr.p = infonres; + param->totalentries = 1; /* NT stream hint value: prefmaxlen? */ + param->status = ERROR_SUCCESS; + + n_shares = lmshare_num_shares(); + + switch (param->level) { + case 0: + status = mlsvc_NetShareEnumLevel0(infonres, n_shares, mxa, 0); + break; + + case 1: + status = mlsvc_NetShareEnumLevel1(infonres, n_shares, mxa, 0); + break; + + case 2: + status = mlsvc_NetShareEnumLevel2(infonres, n_shares, mxa, 0); + break; + + case 502: + status = mlsvc_NetShareEnumLevel502(infonres, n_shares, mxa, 0); + break; + + default: + status = ERROR_INVALID_PARAMETER; + break; + } + + if (status != 0) { + bzero(param, sizeof (struct mslm_NetShareEnum)); + param->status = status; + return (MLRPC_DRC_OK); + } + + param->resume_handle = 0; + param->totalentries = infonres->entriesread; + param->status = status; + return (MLRPC_DRC_OK); +} + + +/* + * srvsvc_s_NetShareEnumSticky + * + * Request for various levels of information about our shares. + * Level 0: just the share names. + * Level 1: the share name, the share type and the comment field. + * Level 2: everything that we know about the shares. + * + * NetShareEnumSticky is the same as NetShareEnum except that hidden + * shares are not returned. This call was apparently added due to a + * bug in the NT implementation of NetShareEnum - it didn't process + * the resume handle correctly so that attempts to enumerate large + * share lists resulted in an infinite loop. + */ +static int +srvsvc_s_NetShareEnumSticky(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslm_NetShareEnum *param = arg; + struct mslm_infonres *infonres; + DWORD resume_handle; + DWORD status; + DWORD n_shares; + + infonres = MLRPC_HEAP_NEW(mxa, struct mslm_infonres); + if (infonres == 0) { + bzero(param, sizeof (struct mslm_NetShareEnum)); + param->status = ERROR_NOT_ENOUGH_MEMORY; + return (MLRPC_DRC_OK); + } + + infonres->entriesread = 0; + infonres->entries = 0; + param->result.level = param->level; + param->result.bufptr.p = infonres; + param->totalentries = 1; /* NT stream hint value: prefmaxlen? */ + param->status = ERROR_SUCCESS; + + n_shares = lmshare_num_shares(); + + if (param->resume_handle) + resume_handle = *param->resume_handle; + else + resume_handle = 0; + + switch (param->level) { + case 0: + status = mlsvc_NetShareEnumLevel0(infonres, n_shares, mxa, 1); + break; + + case 1: + status = mlsvc_NetShareEnumLevel1(infonres, n_shares, mxa, 1); + break; + + case 2: + status = mlsvc_NetShareEnumLevel2(infonres, n_shares, mxa, 1); + break; + + case 502: + status = mlsvc_NetShareEnumLevel502(infonres, n_shares, mxa, 1); + break; + + default: + status = ERROR_INVALID_PARAMETER; + break; + } + + if (status != 0) { + bzero(param, sizeof (struct mslm_NetShareEnum)); + param->status = status; + return (MLRPC_DRC_OK); + } + + if (param->resume_handle) + *param->resume_handle = resume_handle; + param->totalentries = infonres->entriesread; + param->status = status; + return (MLRPC_DRC_OK); +} + + + +/* + * mlsvc_NetShareEnumLevel0 + * + * Build the level 0 share information. The list should have been built + * before we got here so all we have to do is copy the share names to + * the response heap and setup the infonres values. + */ +static DWORD +mlsvc_NetShareEnumLevel0(struct mslm_infonres *infonres, DWORD n_shares, + struct mlrpc_xaction *mxa, char sticky) +{ + struct mslm_SHARE_INFO_0 *info0; + lmshare_iterator_t *iterator; + lmshare_info_t *si; + DWORD i; + DWORD status; + smb_dr_user_ctx_t *user_ctx = mxa->context->user_ctx; + + info0 = MLRPC_HEAP_NEWN(mxa, struct mslm_SHARE_INFO_0, n_shares); + if (info0 == 0) { + status = ERROR_NOT_ENOUGH_MEMORY; + return (status); + } + + iterator = lmshare_open_iterator(LMSHRM_ALL); + if (iterator == NULL) { + status = ERROR_NOT_ENOUGH_MEMORY; + return (status); + } + + i = 0; + while ((si = lmshare_iterate(iterator)) != 0) { + if (sticky && (si->stype & STYPE_SPECIAL)) + continue; + + if (smb_is_autohome(si)) + continue; + + status = mlsvc_NetShareEnumCommon(mxa, i, 0, si, + (void *)info0); + + if (status != ERROR_SUCCESS) + break; + + i++; + } + + i = srvsvc_add_autohome(mxa, user_ctx->du_account, i, 0, (char *)info0); + + lmshare_close_iterator(iterator); + + infonres->entriesread = i; + infonres->entries = info0; + return (ERROR_SUCCESS); +} + + +/* + * mlsvc_NetShareEnumLevel1 + * + * Build the level 1 share information. The list should have been built + * before we arrived here so all we have to do is copy the share info + * to the response heap and setup the infonres values. The only thing + * to be aware of here is that there are minor difference between the + * various share types. + */ +static DWORD +mlsvc_NetShareEnumLevel1(struct mslm_infonres *infonres, DWORD n_shares, + struct mlrpc_xaction *mxa, char sticky) +{ + struct mslm_SHARE_INFO_1 *info1; + lmshare_iterator_t *iterator; + lmshare_info_t *si; + DWORD i; + smb_dr_user_ctx_t *user_ctx = mxa->context->user_ctx; + + info1 = MLRPC_HEAP_NEWN(mxa, struct mslm_SHARE_INFO_1, n_shares); + if (info1 == 0) + return (ERROR_NOT_ENOUGH_MEMORY); + + iterator = lmshare_open_iterator(LMSHRM_ALL); + if (iterator == NULL) + return (ERROR_NOT_ENOUGH_MEMORY); + + i = 0; + while ((si = lmshare_iterate(iterator)) != 0) { + if (sticky && (si->stype & STYPE_SPECIAL)) + continue; + + if (smb_is_autohome(si)) + continue; + + if (mlsvc_NetShareEnumCommon(mxa, i, 1, si, + (void *)info1) != ERROR_SUCCESS) + break; + i++; + } + + i = srvsvc_add_autohome(mxa, user_ctx->du_account, i, 1, (char *)info1); + + lmshare_close_iterator(iterator); + + infonres->entriesread = i; + infonres->entries = info1; + return (ERROR_SUCCESS); +} + +/* + * mlsvc_NetShareEnumLevel2 + * + * Build the level 2 share information. The list should have been built + * before we arrived here so all we have to do is copy the share info + * to the response heap and setup the infonres values. The only thing + * to be aware of here is that there are minor difference between the + * various share types. + */ +static DWORD +mlsvc_NetShareEnumLevel2(struct mslm_infonres *infonres, DWORD n_shares, + struct mlrpc_xaction *mxa, char sticky) +{ + struct mslm_SHARE_INFO_2 *info2; + lmshare_iterator_t *iterator; + lmshare_info_t *si; + DWORD i; + smb_dr_user_ctx_t *user_ctx = mxa->context->user_ctx; + + info2 = MLRPC_HEAP_NEWN(mxa, struct mslm_SHARE_INFO_2, n_shares); + if (info2 == 0) + return (ERROR_NOT_ENOUGH_MEMORY); + + iterator = lmshare_open_iterator(LMSHRM_ALL); + if (iterator == NULL) + return (ERROR_NOT_ENOUGH_MEMORY); + + i = 0; + while ((si = lmshare_iterate(iterator)) != 0) { + if (sticky && (si->stype & STYPE_SPECIAL)) + continue; + + if (smb_is_autohome(si)) + continue; + + if (mlsvc_NetShareEnumCommon(mxa, i, 2, si, + (void *)info2) != ERROR_SUCCESS) + break; + i++; + } + + i = srvsvc_add_autohome(mxa, user_ctx->du_account, i, 2, (char *)info2); + + lmshare_close_iterator(iterator); + infonres->entriesread = i; + infonres->entries = info2; + return (ERROR_SUCCESS); +} + +/* + * mlsvc_NetShareEnumLevel502 + * + * Build the level 502 share information. This is the same as level 2 + * but with a security descriptor in the share structure. We don't + * support SD's on shares so we can just set that field to zero. See + * mlsvc_NetShareEnumLevel2 for more information. + */ +static DWORD +mlsvc_NetShareEnumLevel502(struct mslm_infonres *infonres, DWORD n_shares, + struct mlrpc_xaction *mxa, char sticky) +{ + struct mslm_SHARE_INFO_502 *info502; + lmshare_iterator_t *iterator; + lmshare_info_t *si; + DWORD i; + smb_dr_user_ctx_t *user_ctx = mxa->context->user_ctx; + + info502 = MLRPC_HEAP_NEWN(mxa, struct mslm_SHARE_INFO_502, n_shares); + + if (info502 == 0) + return (ERROR_NOT_ENOUGH_MEMORY); + + iterator = lmshare_open_iterator(LMSHRM_ALL); + if (iterator == NULL) + return (ERROR_NOT_ENOUGH_MEMORY); + + i = 0; + while ((si = lmshare_iterate(iterator)) != 0) { + if (sticky && (si->stype & STYPE_SPECIAL)) + continue; + + if (smb_is_autohome(si)) + continue; + + if (mlsvc_NetShareEnumCommon( + mxa, i, 502, si, (void *)info502) != ERROR_SUCCESS) + break; + i++; + } + + i = srvsvc_add_autohome(mxa, user_ctx->du_account, i, 502, + (char *)info502); + + lmshare_close_iterator(iterator); + infonres->entriesread = i; + infonres->entries = info502; + return (ERROR_SUCCESS); +} + +/* + * mlsvc_NetShareEnumCommon + * + * Build the levels 0, 1, 2 and 502 share information. This function + * is called by the various NetShareEnum levels for each share. If + * we cannot build the share data for some reason, we return an error + * but the actual value of the error is not important to the caller. + * The caller just needs to know not to include this info in the RPC + * response. + * + * Returns: + * ERROR_SUCCESS + * ERROR_NOT_ENOUGH_MEMORY + * ERROR_INVALID_LEVEL + */ +static DWORD +mlsvc_NetShareEnumCommon(struct mlrpc_xaction *mxa, DWORD i, int level, + lmshare_info_t *si, void *infop) +{ + struct mslm_SHARE_INFO_0 *info0; + struct mslm_SHARE_INFO_1 *info1; + struct mslm_SHARE_INFO_2 *info2; + struct mslm_SHARE_INFO_502 *info502; + char shr_comment[LMSHR_COMMENT_MAX]; + + if ((si->stype & STYPE_MASK) == STYPE_IPC) { + /* + * Windows clients don't send the \\PIPE path for IPC$. + */ + si->directory[0] = '\0'; + (void) strcpy(si->comment, "Remote IPC"); + } + + if (si->comment && strlen(si->comment)) + (void) snprintf(shr_comment, sizeof (shr_comment), "%s (%s)", + si->directory, si->comment); + else + (void) strcpy(shr_comment, si->directory); + + switch (level) { + case 0: + info0 = (struct mslm_SHARE_INFO_0 *)infop; + info0[i].shi0_netname + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si->share_name); + + if (info0[i].shi0_netname == 0) + return (ERROR_NOT_ENOUGH_MEMORY); + break; + + case 1: + info1 = (struct mslm_SHARE_INFO_1 *)infop; + info1[i].shi1_netname + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si->share_name); + + info1[i].shi1_remark + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, shr_comment); + + info1[i].shi1_type = si->stype; + + if (!info1[i].shi1_netname || !info1[i].shi1_remark) + return (ERROR_NOT_ENOUGH_MEMORY); + break; + + case 2: + info2 = (struct mslm_SHARE_INFO_2 *)infop; + info2[i].shi2_netname + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si->share_name); + + info2[i].shi2_remark + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, shr_comment); + + info2[i].shi2_path + = (unsigned char *)srvsvc_share_mkpath(mxa, si->directory); + + info2[i].shi2_type = si->stype; + info2[i].shi2_permissions = 0; + info2[i].shi2_max_uses = SHI_USES_UNLIMITED; + info2[i].shi2_current_uses = 0; + info2[i].shi2_passwd + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, empty_string); + + if (!info2[i].shi2_netname || !info2[i].shi2_remark || + !info2[i].shi2_passwd || !info2[i].shi2_path) + return (ERROR_NOT_ENOUGH_MEMORY); + + break; + + case 502: + info502 = (struct mslm_SHARE_INFO_502 *)infop; + info502[i].shi502_netname + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, si->share_name); + + info502[i].shi502_remark + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, shr_comment); + + info502[i].shi502_path + = (unsigned char *)srvsvc_share_mkpath(mxa, si->directory); + + info502[i].shi502_type = si->stype; + info502[i].shi502_permissions = 0; + info502[i].shi502_max_uses = SHI_USES_UNLIMITED; + info502[i].shi502_current_uses = 0; + info502[i].shi502_passwd + = (unsigned char *)MLRPC_HEAP_STRSAVE(mxa, empty_string); + + info502[i].shi502_reserved = 0; + info502[i].shi502_security_descriptor = 0; + + if (!info502[i].shi502_netname || !info502[i].shi502_remark || + !info502[i].shi502_passwd || !info502[i].shi502_path) + return (ERROR_NOT_ENOUGH_MEMORY); + break; + + default: + return (ERROR_INVALID_LEVEL); + } + + return (ERROR_SUCCESS); +} + +/* + * srvsvc_s_NetShareDel + * + * Delete a share. Only the administrator, or a member of the domain + * administrators group, is allowed to delete shares. + * + * This interface is used by the rmtshare command from the NT resource + * kit. Rmtshare allows a client to add or remove shares on a server + * from the client's command line. + * + * Returns Win32 error codes. + */ +static int +srvsvc_s_NetShareDel(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslm_NetShareDel *param = arg; + + if (srvsvc_is_poweruser(mxa) == 0 || + lmshare_is_restricted((char *)param->netname)) { + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); + } + + param->status = lmshare_delete((char *)param->netname, 1); + return (MLRPC_DRC_OK); +} + +/* + * srvsvc_s_NetGetFileSecurity + * + * Get security descriptor of the requested file/folder + * + * Right now, just returns ERROR_ACCESS_DENIED, because we cannot + * get the requested SD here in MLRPC code. + */ +/*ARGSUSED*/ +static int +srvsvc_s_NetGetFileSecurity(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslm_NetGetFileSecurity *param = arg; + + param->length = 0; + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +/* + * srvsvc_s_NetSetFileSecurity + * + * Set the given security descriptor for the requested file/folder + * + * Right now, just returns ERROR_ACCESS_DENIED, because we cannot + * set the requested SD here in MLRPC code. + */ +/*ARGSUSED*/ +static int +srvsvc_s_NetSetFileSecurity(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslm_NetSetFileSecurity *param = arg; + + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +static mlrpc_stub_table_t srvsvc_stub_table[] = { + { srvsvc_s_NetConnectEnum, SRVSVC_OPNUM_NetConnectEnum }, + { srvsvc_s_NetFileEnum, SRVSVC_OPNUM_NetFileEnum }, + { srvsvc_s_NetFileClose, SRVSVC_OPNUM_NetFileClose }, + { srvsvc_s_NetShareGetInfo, SRVSVC_OPNUM_NetShareGetInfo }, + { srvsvc_s_NetShareSetInfo, SRVSVC_OPNUM_NetShareSetInfo }, + { srvsvc_s_NetSessionEnum, SRVSVC_OPNUM_NetSessionEnum }, + { srvsvc_s_NetSessionDel, SRVSVC_OPNUM_NetSessionDel }, + { srvsvc_s_NetServerGetInfo, SRVSVC_OPNUM_NetServerGetInfo }, + { srvsvc_s_NetRemoteTOD, SRVSVC_OPNUM_NetRemoteTOD }, + { srvsvc_s_NetNameValidate, SRVSVC_OPNUM_NetNameValidate }, + { srvsvc_s_NetShareAdd, SRVSVC_OPNUM_NetShareAdd }, + { srvsvc_s_NetShareDel, SRVSVC_OPNUM_NetShareDel }, + { srvsvc_s_NetShareEnum, SRVSVC_OPNUM_NetShareEnum }, + { srvsvc_s_NetShareEnumSticky, SRVSVC_OPNUM_NetShareEnumSticky }, + { srvsvc_s_NetGetFileSecurity, SRVSVC_OPNUM_NetGetFileSecurity }, + { srvsvc_s_NetSetFileSecurity, SRVSVC_OPNUM_NetSetFileSecurity }, + {0} +}; diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_svcctl.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_svcctl.c new file mode 100644 index 000000000000..eca831f8999e --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_svcctl.c @@ -0,0 +1,485 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * NT Service Control Services (SVCCTL) RPC interface definition. + * This interface provides remote access to add, remove, start and + * stop services. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * SVCCTL diagnostics flag: set to 1 to enable verbose logging. + */ +int svcctl_debug = 0; + +/* + * The handle keys for the various types of handles returned + * by this interface. + */ +#define SVCCTL_MANAGER_KEY "svcctlManager" +#define SVCCTL_SERVICE_KEY "svcctlService" + + +typedef struct { + char *svc_name; + char *display_name; + char *local_name; +} svc_info_t; + + +/* + * The list of service we report to Server Manager. Entries don't + * have to be alphabetically arranged here; Server Manager will + * sort the list. + * + * NOTE: The enumeration list is currently built in a fixed-size, + * 1024 byte buffer. Be careful not to over-run the buffer. + */ +static svc_info_t svc_info[] = { + { "Dhcp", "DHCP Client", "dhcpc" }, + { "EventLog", "EventLog", NULL }, + { "Netlogon", "Net Logon", NULL }, + { "WebAdmin", "Web Administration", "httpd" }, + { "RlgnSvr", "Remote Login", "rlogin" }, + { "RpcSs", "Remote Procedure Call (RPC) Service", NULL }, + { "RshSvr", "Remote Shell", "rsh" }, + { "SshSvr", "Secure Shell", "ssh" }, + { "TlntSvr", "Telnet", "telnet" }, + { "Dnscache", "DNS Client", "dns" }, + { "NisSvr", "Network Information Services", NULL }, + { "NtLmSsp", "NT LM Security Support Provider", NULL }, + { "Samss", "Security Accounts Manager", NULL }, + { "UPS", "Uninterruptible Power Supply", "ups" }, + { "TftpSvr", "TFTP", "tftp" } +}; + +#define SVCCTL_NUM_SVCS (sizeof (svc_info)/sizeof (svc_info[0])) + + +static DWORD svcctl_get_status(const char *); +static DWORD svcctl_validate_service(char *); +static DWORD svcctl_validate_handle(char *, ms_handle_t *, char *, + struct mlrpc_xaction *); +static int svcctl_is_admin(struct mlrpc_xaction *); + +static int svcctl_s_Close(void *, struct mlrpc_xaction *); +static int svcctl_s_OpenManager(void *, struct mlrpc_xaction *); +static int svcctl_s_OpenService(void *, struct mlrpc_xaction *); +static int svcctl_s_QueryServiceStatus(void *, struct mlrpc_xaction *); +static int svcctl_s_QueryServiceConfig(void *, struct mlrpc_xaction *); +static int svcctl_s_EnumServicesStatus(void *, struct mlrpc_xaction *); + +static mlrpc_stub_table_t svcctl_stub_table[] = { + { svcctl_s_Close, SVCCTL_OPNUM_Close }, + { svcctl_s_OpenManager, SVCCTL_OPNUM_OpenManager }, + { svcctl_s_OpenService, SVCCTL_OPNUM_OpenService }, + { svcctl_s_QueryServiceStatus, SVCCTL_OPNUM_QueryServiceStatus }, + { svcctl_s_QueryServiceConfig, SVCCTL_OPNUM_QueryServiceConfig }, + { svcctl_s_EnumServicesStatus, SVCCTL_OPNUM_EnumServicesStatus }, + {0} +}; + +static mlrpc_service_t svcctl_service = { + "SVCCTL", /* name */ + "Service Control Services", /* desc */ + "\\svcctl", /* endpoint */ + PIPE_NTSVCS, /* sec_addr_port */ + "367abb81-9844-35f1-ad3298f038001003", 2, /* abstract */ + "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */ + 0, /* no bind_instance_size */ + 0, /* no bind_req() */ + 0, /* no unbind_and_close() */ + 0, /* use generic_call_stub() */ + &TYPEINFO(svcctl_interface), /* interface ti */ + svcctl_stub_table /* stub_table */ +}; + +/* + * svcctl_initialize + * + * This function registers the SVCCTL RPC interface with the RPC runtime + * library. It must be called in order to use either the client side + * or the server side functions. + */ +void +svcctl_initialize(void) +{ + (void) mlrpc_register_service(&svcctl_service); +} + +/* + * svcctl_s_Close + * + * This is a request to close the SVCCTL interface specified by the + * handle. Free the handle and zero out the result handle for the + * client. + * + * Returns: + * ERROR_SUCCESS + * ERROR_INVALID_HANDLE + */ +static int +svcctl_s_Close(void *arg, struct mlrpc_xaction *mxa) +{ + struct svcctl_Close *param = arg; + smb_dr_user_ctx_t *user_ctx = mxa->context->user_ctx; + ms_handle_t *handle; + DWORD status; + + if (svcctl_debug) + smb_token_log(LOG_DEBUG, user_ctx, "(SvcctlClose)"); + + handle = (ms_handle_t *)¶m->handle; + status = svcctl_validate_handle("SvcctlClose", handle, 0, mxa); + + if (status == ERROR_SUCCESS) + (void) mlsvc_put_handle((ms_handle_t *)¶m->handle); + + bzero(¶m->result_handle, sizeof (svcctl_handle_t)); + param->status = status; + return (MLRPC_DRC_OK); +} + +/* + * svcctl_s_OpenManager + * + * This is a request to open the service control manager. Dependent + * on the desired access we either generate a handle to be used on + * subsequent requests or deny access. + * + * Returns: + * ERROR_SUCCESS + * ERROR_ACCESS_DENIED + * + * Return a handle for use with subsequent svcctl requests. + */ +static int +svcctl_s_OpenManager(void *arg, struct mlrpc_xaction *mxa) +{ + struct svcctl_OpenManager *param = arg; + ms_handle_t *handle; + int rc; + + rc = svcctl_is_admin(mxa); + + if ((rc == 0) || (param->desired_access & SC_MANAGER_LOCK) != 0) { + /* + * The user doesn't have Administrator rights + * or wants a write lock on the Services DB. + */ + bzero(¶m->handle, sizeof (svcctl_handle_t)); + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); + } + + handle = mlsvc_get_handle(MLSVC_IFSPEC_SVCCTL, SVCCTL_MANAGER_KEY, 0); + bcopy(handle, ¶m->handle, sizeof (svcctl_handle_t)); + + param->status = ERROR_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * svcctl_s_OpenService + * + * Return a handle for use with subsequent svcctl requests. + * + * Returns: + * ERROR_SUCCESS + * ERROR_INVALID_HANDLE + * ERROR_SERVICE_DOES_NOT_EXIST + */ +static int +svcctl_s_OpenService(void *arg, struct mlrpc_xaction *mxa) +{ + struct svcctl_OpenService *param = arg; + ms_handle_t *handle; + DWORD status; + + status = svcctl_validate_handle("SvcctlOpenService", + (ms_handle_t *)¶m->manager_handle, SVCCTL_MANAGER_KEY, mxa); + + if (status != ERROR_SUCCESS) { + bzero(¶m->service_handle, sizeof (svcctl_handle_t)); + param->status = status; + return (MLRPC_DRC_OK); + } + + status = svcctl_validate_service((char *)param->service_name); + if (status != ERROR_SUCCESS) { + bzero(¶m->service_handle, sizeof (svcctl_handle_t)); + param->status = status; + return (MLRPC_DRC_OK); + } + + handle = mlsvc_get_handle(MLSVC_IFSPEC_SVCCTL, SVCCTL_SERVICE_KEY, 0); + bcopy(handle, ¶m->service_handle, sizeof (svcctl_handle_t)); + + param->status = ERROR_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * svcctl_s_QueryServiceStatus + * + * Returns: + * ERROR_SUCCESS + * ERROR_INVALID_HANDLE + */ +static int +svcctl_s_QueryServiceStatus(void *arg, struct mlrpc_xaction *mxa) +{ + struct svcctl_QueryServiceStatus *param = arg; + DWORD status; + + status = svcctl_validate_handle("SvcctlQueryServiceStatus", + (ms_handle_t *)¶m->service_handle, SVCCTL_SERVICE_KEY, mxa); + + if (status != ERROR_SUCCESS) { + bzero(¶m, sizeof (struct svcctl_QueryServiceStatus)); + param->status = status; + return (MLRPC_DRC_OK); + } + + param->service_status.service_type = SERVICE_WIN32_SHARE_PROCESS; + param->service_status.cur_state = SERVICE_RUNNING; + param->service_status.ctrl_accepted = 0; + param->service_status.w32_exitcode = 0; + param->service_status.svc_specified_exitcode = 0; + param->service_status.check_point = 0; + param->service_status.wait_hint = 0; + + param->status = ERROR_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * svcctl_s_EnumServicesStatus + * + * Enumerate the list of services we support. Currently, this list + * is built in a fixed-size 1024 byte buffer - be careful not to + * over-run the buffer. + * + * Returns: + * ERROR_SUCCESS + * ERROR_INVALID_HANDLE + */ +static int +svcctl_s_EnumServicesStatus(void *arg, struct mlrpc_xaction *mxa) +{ + struct svcctl_EnumServicesStatus *param = arg; + svc_enum_status_t *service_table; + svc_enum_status_t *svc; + mts_wchar_t *wide_name; + char *name; + int i, namelen; + int offs; + DWORD status; + + status = svcctl_validate_handle("SvcctlEnumServicesStatus", + (ms_handle_t *)¶m->manager_handle, SVCCTL_MANAGER_KEY, mxa); + + if (status != ERROR_SUCCESS) { + param->status = status; + return (MLRPC_DRC_OK); + } + + if (param->buf_size < 1024) { + param->status = ERROR_MORE_DATA; + return (MLRPC_DRC_OK); + } + + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + service_table = (svc_enum_status_t *)param->services; + offs = SVCCTL_NUM_SVCS * sizeof (svc_enum_status_t); + + for (i = 0; i < SVCCTL_NUM_SVCS; i++) { + svc = &service_table[i]; + + svc->svc_name = offs; + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + wide_name = (mts_wchar_t *)¶m->services[offs]; + name = svc_info[i].svc_name; + namelen = strlen(name) + 1; + (void) mts_mbstowcs(wide_name, name, namelen); + + offs += namelen * 2; + + svc->display_name = offs; + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + wide_name = (mts_wchar_t *)¶m->services[offs]; + name = svc_info[i].display_name; + namelen = strlen(name) + 1; + (void) mts_mbstowcs(wide_name, name, namelen); + + offs += namelen * 2; + + name = svc_info[i].local_name; + if (name) + svc->svc_status.cur_state = svcctl_get_status(name); + else + svc->svc_status.cur_state = SERVICE_RUNNING; + + svc->svc_status.service_type = SERVICE_WIN32_SHARE_PROCESS; + svc->svc_status.ctrl_accepted = 0; + svc->svc_status.w32_exitcode = 0; + svc->svc_status.svc_specified_exitcode = 0; + svc->svc_status.check_point = 0; + svc->svc_status.wait_hint = 0; + } + + param->buf_size = 1024; + param->bytes_needed = 0; + param->svc_num = SVCCTL_NUM_SVCS; + param->resume_handle = 0; + param->status = ERROR_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * svcctl_s_QueryServiceConfig + * + * Returns: + * ERROR_SUCCESS + * ERROR_INVALID_HANDLE + */ +static int +svcctl_s_QueryServiceConfig(void *arg, struct mlrpc_xaction *mxa) +{ + struct svcctl_QueryServiceConfig *param = arg; + DWORD status; + + status = svcctl_validate_handle("SvcctlQueryServiceConfig", + (ms_handle_t *)¶m->service_handle, SVCCTL_SERVICE_KEY, mxa); + + if (status != ERROR_SUCCESS) { + bzero(¶m, sizeof (struct svcctl_QueryServiceConfig)); + param->status = status; + return (MLRPC_DRC_OK); + } + + param->service_cfg.service_type = SERVICE_WIN32_SHARE_PROCESS; + param->service_cfg.start_type = SERVICE_AUTO_START; + param->service_cfg.error_control = SERVICE_ERROR_IGNORE; + param->service_cfg.binary_pathname = 0; + param->service_cfg.loadorder_group = 0; + param->service_cfg.tag_id = 0; + param->service_cfg.dependencies = 0; + param->service_cfg.service_startname = 0; + param->service_cfg.display_name = 0; + + param->cfg_bytes = sizeof (svc_config_t); + param->status = ERROR_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * Check to see whether or not a service is supported. The check is + * case-insensitive to avoid any naming issues due to the different + * versions of Windows. + * + * Returns: + * ERROR_SUCCESS + * ERROR_SERVICE_DOES_NOT_EXIST + */ +static DWORD +svcctl_validate_service(char *svc_name) +{ + int i; + + if (svc_name == NULL) + return (ERROR_SERVICE_DOES_NOT_EXIST); + + for (i = 0; i < SVCCTL_NUM_SVCS; i++) { + if (strcasecmp(svc_name, svc_info[i].svc_name) == 0) + return (ERROR_SUCCESS); + } + + return (ERROR_SERVICE_DOES_NOT_EXIST); +} + +/* + * Check whether or not the svcctl module allocated this handle. + * + * Returns: + * ERROR_SUCCESS + * ERROR_INVALID_HANDLE. + */ +/*ARGSUSED*/ +static DWORD +svcctl_validate_handle(char *name, ms_handle_t *handle, char *key, + struct mlrpc_xaction *mxa) +{ + int mgr_erc; + int svc_erc; + + mgr_erc = mlsvc_validate_handle(handle, SVCCTL_MANAGER_KEY); + svc_erc = mlsvc_validate_handle(handle, SVCCTL_SERVICE_KEY); + + if (mgr_erc == 0 && svc_erc == 0) + return (ERROR_INVALID_HANDLE); + + return (ERROR_SUCCESS); +} + +/* + * Report the service status: SERVICE_PAUSED or SERVICE_RUNNING. + */ +/*ARGSUSED*/ +static DWORD +svcctl_get_status(const char *name) +{ + return (SERVICE_RUNNING); +} + +/* + * SVCCTL access is restricted to administrators: members of + * the Domain Admins or Administrators groups. + * + * Returns 1 if the user has admin rights. Otherwise returns 0. + */ +static int +svcctl_is_admin(struct mlrpc_xaction *mxa) +{ + smb_dr_user_ctx_t *user_ctx = mxa->context->user_ctx; + + if (user_ctx == NULL) + return (0); + + return (user_ctx->du_flags & SMB_ATF_ADMIN); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c new file mode 100644 index 000000000000..29cd3f058a08 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c @@ -0,0 +1,752 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Utility functions to support the RPC interface library. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +extern int netr_open(char *, char *, mlsvc_handle_t *); +extern int netr_close(mlsvc_handle_t *); +extern DWORD netlogon_auth(char *, mlsvc_handle_t *, DWORD); +extern int mlsvc_user_getauth(char *, char *, smb_auth_info_t *); + +static int mlsvc_lookup_local_name(char *name, nt_sid_t **sid); +static int mlsvc_lookup_nt_name(char *name, nt_sid_t **sid); +static int mlsvc_lookup_nt_sid(nt_sid_t *sid, char *buf, int bufsize); + +/* + * Compare the supplied domain name with the local hostname. + * We need to deal with both server names and fully-qualified + * domain names. + * + * Returns: + * 0 The specified domain is not the local domain, + * 1 The Specified domain is the local domain. + * -1 Invalid parameter or unable to get the local + * system information. + */ +int +mlsvc_is_local_domain(const char *domain) +{ + char hostname[MAXHOSTNAMELEN]; + uint32_t mode; + int rc; + + if (strchr(domain, '.') != NULL) + rc = smb_getfqhostname(hostname, MAXHOSTNAMELEN); + else + rc = smb_gethostname(hostname, MAXHOSTNAMELEN, 1); + + if (rc != 0) + return (-1); + + rc = strcasecmp(domain, hostname); + mode = smb_get_security_mode(); + + if ((rc == 0) || (mode == SMB_SECMODE_WORKGRP)) + return (1); + + return (0); +} + +/* + * mlsvc_lookup_name + * + * Lookup a name in the specified domain and translate it to a SID. + * If the name is in the NT domain, it may refer to a user, group or + * alias. Otherwise it must refer to a UNIX username. The memory for + * the sid is allocated using malloc so the caller should call free + * when it is no longer required. + * + * On success, 0 will be returned and sid will point to a local domain + * user SID. Otherwise -1 will be returned. + */ +int +mlsvc_lookup_name(char *domain, char *name, nt_sid_t **sid) +{ + if (domain == NULL || name == NULL || sid == NULL) + return (-1); + + if (mlsvc_is_local_domain(domain) == 1) + return (mlsvc_lookup_local_name(name, sid)); + else + return (mlsvc_lookup_nt_name(name, sid)); +} + +/* + * mlsvc_lookup_local_name + * + * Lookup a name in the local password file and translate it to a SID. + * The name must refer to a user. This is a private function intended + * to support mlsvc_lookup_name so it doesn't perform any parameter + * validation. The memory for the sid is allocated using malloc so the + * caller must call free when it is no longer required. + * + * On success, 0 will be returned and sid will point to a local domain + * user SID. Otherwise -1 will be returned. + */ +static int +mlsvc_lookup_local_name(char *name, nt_sid_t **sid) +{ + struct passwd *pw; + nt_sid_t *domain_sid; + + if ((pw = getpwnam(name)) == NULL) + return (-1); + + if ((domain_sid = nt_domain_local_sid()) == NULL) + return (-1); + + *sid = nt_sid_splice(domain_sid, pw->pw_uid); + return (0); +} + +/* + * mlsvc_lookup_nt_name + * + * Lookup a name in the specified NT domain and translate it to a SID. + * The name may refer to a user, group or alias. This is a private + * function intended to support mlsvc_lookup_name so it doesn't do any + * parameter validation. The memory for the sid is allocated using + * malloc so the caller should call free when it is no longer required. + * + * On success, 0 will be returned and sid will point to an NT domain + * user SID. Otherwise -1 will be returned. + */ +static int +mlsvc_lookup_nt_name(char *name, nt_sid_t **sid) +{ + smb_userinfo_t *user_info; + + if ((user_info = mlsvc_alloc_user_info()) == NULL) + return (-1); + + if (lsa_lookup_name(0, 0, name, user_info) != 0) + return (-1); + + *sid = nt_sid_splice(user_info->domain_sid, user_info->rid); + mlsvc_free_user_info(user_info); + return (0); +} + +/* + * mlsvc_lookup_sid + * + * Lookup a SID and translate it to a name. The name returned may refer + * to a domain, user, group or alias dependent on the SID. On success 0 + * will be returned. Otherwise -1 will be returned. + */ +int +mlsvc_lookup_sid(nt_sid_t *sid, char *buf, int bufsize) +{ + struct passwd *pw; + struct group *gr; + nt_group_t *grp; + DWORD rid; + + if (sid == NULL || buf == NULL) + return (-1); + + if (nt_sid_is_local(sid)) { + (void) nt_sid_get_rid(sid, &rid); + + switch (SAM_RID_TYPE(rid)) { + case SAM_RT_NT_UID: + break; + + case SAM_RT_NT_GID: + if ((grp = nt_groups_lookup_rid(rid)) == NULL) + return (-1); + + (void) strlcpy(buf, grp->name, bufsize); + break; + + case SAM_RT_UNIX_UID: + if ((pw = getpwuid(SAM_DECODE_RID(rid))) == NULL) + return (-1); + + (void) strlcpy(buf, pw->pw_name, bufsize); + break; + + case SAM_RT_UNIX_GID: + if ((gr = getgrgid(SAM_DECODE_RID(rid))) == NULL) + return (-1); + + (void) strlcpy(buf, gr->gr_name, bufsize); + break; + } + + return (0); + } + + return (mlsvc_lookup_nt_sid(sid, buf, bufsize)); +} + +/* + * mlsvc_lookup_nt_sid + * + * Lookup an NT SID and translate it to a name. This is a private + * function intended to support mlsvc_lookup_sid so it doesn't do any + * parameter validation. The input account_name specifies the logon/ + * session to be used for the lookup. It doesn't need to have any + * association with the SID being looked up. The name returned may + * refer to a domain, user, group or alias dependent on the SID. + * + * On success the name will be copied into buf and 0 will be returned. + * Otherwise -1 will be returned. + */ +static int +mlsvc_lookup_nt_sid(nt_sid_t *sid, char *buf, int bufsize) +{ + smb_userinfo_t *user_info; + int rc; + + if ((user_info = mlsvc_alloc_user_info()) == NULL) + return (-1); + + if ((rc = lsa_lookup_sid(sid, user_info)) == 0) + (void) strlcpy(buf, user_info->name, bufsize); + + mlsvc_free_user_info(user_info); + return (rc); +} + +/* + * mlsvc_alloc_user_info + * + * Allocate a user_info structure and set the contents to zero. A + * pointer to the user_info structure is returned. + */ +smb_userinfo_t * +mlsvc_alloc_user_info(void) +{ + smb_userinfo_t *user_info; + + user_info = (smb_userinfo_t *)malloc(sizeof (smb_userinfo_t)); + if (user_info == NULL) + return (NULL); + + bzero(user_info, sizeof (smb_userinfo_t)); + return (user_info); +} + +/* + * mlsvc_free_user_info + * + * Free a user_info structure. This function ensures that the contents + * of the user_info are freed as well as the user_info itself. + */ +void +mlsvc_free_user_info(smb_userinfo_t *user_info) +{ + if (user_info) { + mlsvc_release_user_info(user_info); + free(user_info); + } +} + +/* + * mlsvc_release_user_info + * + * Release the contents of a user_info structure and zero out the + * elements but do not free the user_info structure itself. This + * function cleans out the structure so that it can be reused without + * worrying about stale contents. + */ +void +mlsvc_release_user_info(smb_userinfo_t *user_info) +{ + int i; + + if (user_info == NULL) + return; + + free(user_info->name); + free(user_info->domain_sid); + free(user_info->domain_name); + free(user_info->groups); + + if (user_info->n_other_grps) { + for (i = 0; i < user_info->n_other_grps; i++) + free(user_info->other_grps[i].sid); + + free(user_info->other_grps); + } + + free(user_info->user_sid); + free(user_info->pgrp_sid); + bzero(user_info, sizeof (smb_userinfo_t)); +} + +/* + * mlsvc_setadmin_user_info + * + * Determines if the given user is the domain Administrator or a + * member of Domain Admins or Administrators group and set the + * user_info->flags accordingly. + */ +void +mlsvc_setadmin_user_info(smb_userinfo_t *user_info) +{ + nt_domain_t *domain; + nt_group_t *grp; + int i; + + if ((domain = nt_domain_lookupbytype(NT_DOMAIN_PRIMARY)) == NULL) + return; + + if (!nt_sid_is_equal((nt_sid_t *)user_info->domain_sid, domain->sid)) + return; + + if (user_info->rid == DOMAIN_USER_RID_ADMIN) + user_info->flags |= SMB_UINFO_FLAG_DADMIN; + else if (user_info->primary_group_rid == DOMAIN_GROUP_RID_ADMINS) + user_info->flags |= SMB_UINFO_FLAG_DADMIN; + else { + for (i = 0; i < user_info->n_groups; i++) + if (user_info->groups[i].rid == DOMAIN_GROUP_RID_ADMINS) + user_info->flags |= SMB_UINFO_FLAG_DADMIN; + } + + grp = nt_group_getinfo("Administrators", RWLOCK_READER); + if (grp) { + i = nt_group_is_member(grp, user_info->user_sid); + nt_group_putinfo(grp); + if (i) + user_info->flags |= SMB_UINFO_FLAG_LADMIN; + } +} + +/* + * mlsvc_string_save + * + * This is a convenience function to prepare strings for an RPC call. + * An ms_string_t is set up with the appropriate lengths and str is + * set up to point to a copy of the original string on the heap. The + * macro MLRPC_HEAP_STRSAVE is an alias for mlrpc_heap_strsave, which + * extends the heap and copies the string into the new area. + */ +int +mlsvc_string_save(ms_string_t *ms, char *str, struct mlrpc_xaction *mxa) +{ + int length; + char *p; + + if (ms == NULL || str == NULL || mxa == NULL) + return (0); + + /* + * Windows NT expects the name length to exclude the + * terminating wchar null but doesn't care whether or + * not the allosize includes it. Windows 2000 insists + * that both the length and the allosize include the + * wchar null. + */ + length = mts_wcequiv_strlen(str); + ms->allosize = length + sizeof (mts_wchar_t); + + if (mxa->context->user_ctx->du_native_os == NATIVE_OS_WIN2000) + ms->length = ms->allosize; + else + ms->length = length; + + if ((p = MLRPC_HEAP_STRSAVE(mxa, str)) == NULL) { + return (0); + } + + ms->str = (LPTSTR)p; + return (1); +} + +/* + * mlsvc_sid_save + * + * Expand the heap and copy the sid into the new area. + * Returns a pointer to the copy of the sid on the heap. + */ +nt_sid_t * +mlsvc_sid_save(nt_sid_t *sid, struct mlrpc_xaction *mxa) +{ + nt_sid_t *heap_sid; + unsigned size; + + if (sid == NULL) + return (NULL); + + size = nt_sid_length(sid); + + if ((heap_sid = (nt_sid_t *)MLRPC_HEAP_MALLOC(mxa, size)) == NULL) + return (0); + + bcopy(sid, heap_sid, size); + return (heap_sid); +} + +/* + * mlsvc_is_null_handle + * + * Check a handle against a null handle. Returns 1 if the handle is + * null. Otherwise returns 0. + */ +int +mlsvc_is_null_handle(mlsvc_handle_t *handle) +{ + static ms_handle_t zero_handle; + + if (handle == NULL || handle->context == NULL) + return (1); + + if (!memcmp(&handle->handle, &zero_handle, sizeof (ms_handle_t))) + return (1); + + return (0); +} + +/* + * mlsvc_validate_user + * + * Returns NT status codes. + */ +DWORD +mlsvc_validate_user(char *server, char *domain, char *plain_user, + char *plain_text) +{ + smb_auth_info_t auth; + smb_ntdomain_t *di; + int erc; + DWORD status; + mlsvc_handle_t netr_handle; + char machine_passwd[MLSVC_MACHINE_ACCT_PASSWD_MAX]; + + machine_passwd[0] = '\0'; + + /* + * Ensure that the domain name is uppercase. + */ + (void) utf8_strupr(domain); + + /* + * There is no point continuing if the domain information is + * not available. Wait for up to 10 seconds and then give up. + */ + if ((di = smb_getdomaininfo(10)) == 0) { + status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + return (status); + } + + if (strcasecmp(domain, di->domain) != 0) { + status = NT_STATUS_INVALID_PARAMETER; + return (status); + } + + erc = mlsvc_user_logon(server, domain, plain_user, plain_text); + + if (erc == AUTH_USER_GRANT) { + int isenabled; + + smb_config_rdlock(); + isenabled = smb_config_getyorn(SMB_CI_ADS_ENABLE); + smb_config_unlock(); + if (isenabled) { + if (adjoin(machine_passwd, + sizeof (machine_passwd)) == ADJOIN_SUCCESS) { + status = NT_STATUS_SUCCESS; + } else { + status = NT_STATUS_UNSUCCESSFUL; + } + } else { + /* + * Ensure that we don't have an old account in + * this domain. There's no need to check the + * return status. + */ + (void) sam_remove_trust_account(server, domain); + + if (mlsvc_user_getauth(server, plain_user, &auth) + != 0) { + status = NT_STATUS_INVALID_PARAMETER; + return (status); + } + + status = sam_create_trust_account(server, domain, + &auth); + if (status == NT_STATUS_SUCCESS) { + (void) smb_gethostname(machine_passwd, + sizeof (machine_passwd), 0); + (void) utf8_strlwr(machine_passwd); + } + } + + if (status == NT_STATUS_SUCCESS) { + smb_config_wrlock(); + if (smb_config_set(SMB_CI_MACHINE_PASSWD, + machine_passwd) != 0) { + smb_config_unlock(); + return (NT_STATUS_UNSUCCESSFUL); + } + smb_config_unlock(); + + /* + * If we successfully create a trust account, we mark + * ourselves as a domain member in the environment so + * that we use the SAMLOGON version of the NETLOGON + * PDC location protocol. + */ + smb_set_domain_member(1); + + if (netr_open(server, domain, &netr_handle) == 0) { + status = netlogon_auth(server, &netr_handle, + NETR_FLG_INIT); + (void) netr_close(&netr_handle); + } else { + status = NT_STATUS_OPEN_FAILED; + } + } + } else { + status = NT_STATUS_LOGON_FAILURE; + } + + return (status); +} + +/*ARGSUSED*/ +void +nt_group_ht_lock(krwmode_t locktype) +{ +} + +void +nt_group_ht_unlock(void) +{ +} + +int +nt_group_num_groups(void) +{ + return (0); +} + +/*ARGSUSED*/ +uint32_t +nt_group_add(char *gname, char *comment) +{ + return (NT_STATUS_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +uint32_t +nt_group_modify(char *gname, char *new_gname, char *comment) +{ + return (NT_STATUS_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +uint32_t +nt_group_delete(char *gname) +{ + return (NT_STATUS_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +nt_group_t * +nt_group_getinfo(char *gname, krwmode_t locktype) +{ + return (NULL); +} + +/*ARGSUSED*/ +void +nt_group_putinfo(nt_group_t *grp) +{ +} + +/*ARGSUSED*/ +int +nt_group_getpriv(nt_group_t *grp, uint32_t priv_id) +{ + return (SE_PRIVILEGE_DISABLED); +} + +/*ARGSUSED*/ +uint32_t +nt_group_setpriv(nt_group_t *grp, uint32_t priv_id, uint32_t new_attr) +{ + return (NT_STATUS_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +int +nt_group_is_member(nt_group_t *grp, nt_sid_t *sid) +{ + return (0); +} + +/*ARGSUSED*/ +uint32_t +nt_group_add_member(nt_group_t *grp, nt_sid_t *msid, uint16_t sid_name_use, + char *account) +{ + return (NT_STATUS_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +uint32_t +nt_group_del_member(nt_group_t *grp, void *key, int keytype) +{ + return (NT_STATUS_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +int +nt_group_num_members(nt_group_t *grp) +{ + return (0); +} + +nt_group_iterator_t * +nt_group_open_iterator(void) +{ + return (NULL); +} + +/*ARGSUSED*/ +void +nt_group_close_iterator(nt_group_iterator_t *gi) +{ +} + +/*ARGSUSED*/ +nt_group_t * +nt_group_iterate(nt_group_iterator_t *gi) +{ + return (NULL); +} + +int +nt_group_cache_size(void) +{ + return (0); +} + +uint32_t +sam_init(void) +{ + return (NT_STATUS_SUCCESS); +} + +/*ARGSUSED*/ +uint32_t +nt_group_add_member_byname(char *gname, char *account) +{ + return (NT_STATUS_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +uint32_t +nt_group_del_member_byname(nt_group_t *grp, char *member_name) +{ + return (NT_STATUS_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +void +nt_group_add_groupprivs(nt_group_t *grp, smb_privset_t *priv) +{ +} + +/*ARGSUSED*/ +uint32_t +nt_groups_member_privs(nt_sid_t *sid, smb_privset_t *priv) +{ + return (NT_STATUS_SUCCESS); +} + +/*ARGSUSED*/ +int +nt_groups_member_ngroups(nt_sid_t *sid) +{ + return (0); +} + +/*ARGSUSED*/ +uint32_t +nt_groups_member_groups(nt_sid_t *sid, smb_id_t *grps, int ngrps) +{ + return (NT_STATUS_SUCCESS); +} + +/*ARGSUSED*/ +nt_group_t * +nt_groups_lookup_rid(uint32_t rid) +{ + return (NULL); +} + +/*ARGSUSED*/ +int +nt_groups_count(int cnt_opt) +{ + return (0); +} + +/*ARGSUSED*/ +int +nt_group_member_list(int offset, nt_group_t *grp, + ntgrp_member_list_t *rmembers) +{ + return (0); +} + +/*ARGSUSED*/ +void +nt_group_list(int offset, char *pattern, ntgrp_list_t *list) +{ +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_winreg.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_winreg.c new file mode 100644 index 000000000000..bd2d5ee26f75 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_winreg.c @@ -0,0 +1,454 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Windows Registry RPC (WINREG) server-side interface. + * + * The WINREG RPC interface returns Win32 error codes. + * + * HKLM Hive Key Local Machine + * HKU Hive Key Users + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * List of mlsvc handle (local handle management) keys. + */ +#define WINREG_HKLM "WinregOpenHKLM" +#define WINREG_HKU "WinregOpenUser" +#define WINREG_KEY "WinregOpenKey" + +/* + * List of supported registry keys (case-insensitive). + * "System\\CurrentControlSet\\Services\\Alerter\\Parameters" + */ +static char *winreg_keys[] = { + "System\\CurrentControlSet\\Control\\ProductOptions", + "System\\CurrentControlSet\\Services\\Eventlog\\System" +}; + +static char *winreg_lookup_value(const char *); + +static int winreg_s_OpenHKLM(void *, struct mlrpc_xaction *); +static int winreg_s_OpenHKUsers(void *, struct mlrpc_xaction *); +static int winreg_s_Close(void *, struct mlrpc_xaction *); +static int winreg_s_CreateKey(void *, struct mlrpc_xaction *); +static int winreg_s_DeleteKey(void *, struct mlrpc_xaction *); +static int winreg_s_DeleteValue(void *, struct mlrpc_xaction *); +static int winreg_s_OpenKey(void *, struct mlrpc_xaction *); +static int winreg_s_QueryKey(void *, struct mlrpc_xaction *); +static int winreg_s_QueryValue(void *, struct mlrpc_xaction *); +static int winreg_s_CreateValue(void *, struct mlrpc_xaction *); +static int winreg_s_Shutdown(void *, struct mlrpc_xaction *); +static int winreg_s_GetVersion(void *, struct mlrpc_xaction *); + +static mlrpc_stub_table_t winreg_stub_table[] = { + { winreg_s_OpenHKLM, WINREG_OPNUM_OpenHKLM }, + { winreg_s_OpenHKUsers, WINREG_OPNUM_OpenHKUsers }, + { winreg_s_Close, WINREG_OPNUM_Close }, + { winreg_s_CreateKey, WINREG_OPNUM_CreateKey }, + { winreg_s_DeleteKey, WINREG_OPNUM_DeleteKey }, + { winreg_s_DeleteValue, WINREG_OPNUM_DeleteValue }, + { winreg_s_OpenKey, WINREG_OPNUM_OpenKey }, + { winreg_s_QueryKey, WINREG_OPNUM_QueryKey }, + { winreg_s_QueryValue, WINREG_OPNUM_QueryValue }, + { winreg_s_CreateValue, WINREG_OPNUM_CreateValue }, + { winreg_s_Shutdown, WINREG_OPNUM_Shutdown }, + { winreg_s_GetVersion, WINREG_OPNUM_GetVersion }, + {0} +}; + +static mlrpc_service_t winreg_service = { + "Winreg", /* name */ + "Windows Registry", /* desc */ + "\\winreg", /* endpoint */ + PIPE_WINREG, /* sec_addr_port */ + "338cd001-2244-31f1-aaaa900038001003", 1, /* abstract */ + "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */ + 0, /* no bind_instance_size */ + 0, /* no bind_req() */ + 0, /* no unbind_and_close() */ + 0, /* use generic_call_stub() */ + &TYPEINFO(winreg_interface), /* interface ti */ + winreg_stub_table /* stub_table */ +}; + +static char winreg_sysname[SYS_NMLN]; + +/* + * winreg_initialize + * + * This function registers the WINREG RPC interface with the RPC runtime + * library. It must be called in order to use either the client side + * or the server side functions. + */ +void +winreg_initialize(void) +{ + struct utsname name; + char *sysname; + + if (uname(&name) < 0) + sysname = "Solaris"; + else + sysname = name.sysname; + + (void) strlcpy(winreg_sysname, sysname, SYS_NMLN); + (void) mlrpc_register_service(&winreg_service); +} + +/* + * winreg_s_OpenHKLM + * + * This is a request to open the HKLM and get a handle. The client + * should treat the handle as an opaque object. + * + * Status: + * ERROR_SUCCESS Valid handle returned. + * ERROR_ACCESS_DENIED Unable to allocate a handle. + */ +/*ARGSUSED*/ +static int +winreg_s_OpenHKLM(void *arg, struct mlrpc_xaction *mxa) +{ + struct msreg_OpenHKLM *param = arg; + ms_handle_t *handle; + + handle = mlsvc_get_handle(MLSVC_IFSPEC_WINREG, WINREG_HKLM, 0); + if (handle == NULL) { + bzero(¶m->handle, sizeof (msreg_handle_t)); + param->status = ERROR_ACCESS_DENIED; + } else { + bcopy(handle, ¶m->handle, sizeof (msreg_handle_t)); + param->status = ERROR_SUCCESS; + } + + return (MLRPC_DRC_OK); +} + +/* + * winreg_s_OpenHKUsers + * + * This is a request to get a HKUsers handle. I'm not sure we are + * ready to fully support this interface yet, mostly due to the need + * to support subsequent requests, but we may support enough now. It + * seems okay with regedt32. + */ +/*ARGSUSED*/ +static int +winreg_s_OpenHKUsers(void *arg, struct mlrpc_xaction *mxa) +{ + struct msreg_OpenHKUsers *param = arg; + ms_handle_t *handle; + + handle = mlsvc_get_handle(MLSVC_IFSPEC_WINREG, WINREG_HKU, 0); + if (handle == NULL) { + bzero(¶m->handle, sizeof (msreg_handle_t)); + param->status = ERROR_ACCESS_DENIED; + } else { + bcopy(handle, ¶m->handle, sizeof (msreg_handle_t)); + param->status = ERROR_SUCCESS; + } + + return (MLRPC_DRC_OK); +} + +/* + * winreg_s_Close + * + * This is a request to close the WINREG interface specified by the + * handle. We don't track handles (yet), so just zero out the handle + * and return MLRPC_DRC_OK. Setting the handle to zero appears to be + * standard behaviour. + */ +/*ARGSUSED*/ +static int +winreg_s_Close(void *arg, struct mlrpc_xaction *mxa) +{ + struct msreg_Close *param = arg; + + (void) mlsvc_put_handle((ms_handle_t *)¶m->handle); + bzero(¶m->result_handle, sizeof (msreg_handle_t)); + param->status = ERROR_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * winreg_s_CreateKey + */ +/*ARGSUSED*/ +static int +winreg_s_CreateKey(void *arg, struct mlrpc_xaction *mxa) +{ + struct msreg_CreateKey *param = arg; + + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +/* + * winreg_s_DeleteKey + */ +/*ARGSUSED*/ +static int +winreg_s_DeleteKey(void *arg, struct mlrpc_xaction *mxa) +{ + struct msreg_DeleteKey *param = arg; + + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +/* + * winreg_s_DeleteValue + */ +/*ARGSUSED*/ +static int +winreg_s_DeleteValue(void *arg, struct mlrpc_xaction *mxa) +{ + struct msreg_DeleteValue *param = arg; + + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +/* + * winreg_s_OpenKey + * + * This is a request to open a windows registry key. The list + * of supported keys is listed in the winreg_keys table. If we + * recognize the key, we return a handle. + * + * Returns: + * ERROR_SUCCESS Valid handle returned. + * ERROR_FILE_NOT_FOUND No key or unable to allocate a handle. + */ +/*ARGSUSED*/ +static int +winreg_s_OpenKey(void *arg, struct mlrpc_xaction *mxa) +{ + struct msreg_OpenKey *param = arg; + ms_handle_t *handle; + char *key = (char *)param->name.str; + int i; + + for (i = 0; i < sizeof (winreg_keys)/sizeof (winreg_keys[0]); ++i) { + if (strcasecmp(key, winreg_keys[i]) == 0) { + handle = mlsvc_get_handle(MLSVC_IFSPEC_WINREG, + WINREG_KEY, 0); + + if (handle == NULL) + break; + + bcopy(handle, ¶m->result_handle, + sizeof (msreg_handle_t)); + + param->status = ERROR_SUCCESS; + return (MLRPC_DRC_OK); + } + } + + bzero(¶m->result_handle, sizeof (msreg_handle_t)); + param->status = ERROR_FILE_NOT_FOUND; + return (MLRPC_DRC_OK); +} + +/* + * winreg_s_QueryKey + */ +/*ARGSUSED*/ +static int +winreg_s_QueryKey(void *arg, struct mlrpc_xaction *mxa) +{ + static char nullstr[2] = { 0, 0 }; + struct msreg_QueryKey *param = arg; + + bzero(param, sizeof (struct msreg_QueryKey)); + + param->name.length = 2; + param->name.allosize = 0; + param->name.str = (unsigned char *)nullstr; + param->status = ERROR_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * winreg_s_QueryValue + * + * This is a request to get the value associated with a specified name. + * + * Returns: + * ERROR_SUCCESS Value returned. + * ERROR_FILE_NOT_FOUND PrimaryModule is not supported. + * ERROR_CANTREAD No such name or memory problem. + */ +static int +winreg_s_QueryValue(void *arg, struct mlrpc_xaction *mxa) +{ + struct msreg_QueryValue *param = arg; + struct msreg_value *pv; + char *name; + char *value; + DWORD slen; + DWORD msize; + + name = (char *)param->value_name.str; + + if (strcasecmp(name, "PrimaryModule") == 0) { + param->status = ERROR_FILE_NOT_FOUND; + return (MLRPC_DRC_OK); + } + + if ((value = winreg_lookup_value(name)) == NULL) { + param->status = ERROR_CANTREAD; + return (MLRPC_DRC_OK); + } + + slen = mts_wcequiv_strlen(value) + sizeof (mts_wchar_t); + msize = sizeof (struct msreg_value) + slen; + + param->value = (struct msreg_value *)MLRPC_HEAP_MALLOC(mxa, msize); + param->type = MLRPC_HEAP_NEW(mxa, DWORD); + param->value_size = MLRPC_HEAP_NEW(mxa, DWORD); + param->value_size_total = MLRPC_HEAP_NEW(mxa, DWORD); + + if (param->value == NULL || param->type == NULL || + param->value_size == NULL || param->value_size_total == NULL) { + param->status = ERROR_CANTREAD; + return (MLRPC_DRC_OK); + } + + bzero(param->value, msize); + pv = param->value; + pv->vc_first_is = 0; + pv->vc_length_is = slen; + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + (void) mts_mbstowcs((mts_wchar_t *)pv->value, value, slen); + + *param->type = 1; + *param->value_size = slen; + *param->value_size_total = slen; + + param->status = ERROR_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * Lookup a name in the registry and return the associated value. + * Our registry is a case-insensitive, name-value pair table. + * + * Windows ProductType: WinNT, ServerNT, LanmanNT. + * Windows NT4.0 workstation: WinNT + * Windows NT4.0 server: ServerNT + * + * If LanmanNT is used here, Windows 2000 sends LsarQueryInfoPolicy + * with info level 6, which we don't support. If we use ServerNT + * (as reported by NT4.0 Server) Windows 2000 send requests for + * levels 3 and 5, which are support. + * + * On success, returns a pointer to the value. Otherwise returns + * a null pointer. + */ +static char * +winreg_lookup_value(const char *name) +{ + static struct registry { + char *name; + char *value; + } registry[] = { + { "ProductType", "ServerNT" }, + { "Sources", NULL } /* product name */ + }; + + int i; + + for (i = 0; i < sizeof (registry)/sizeof (registry[0]); ++i) { + if (strcasecmp(registry[i].name, name) == 0) { + if (registry[i].value == NULL) + return (winreg_sysname); + else + return (registry[i].value); + } + } + + return (0); +} + +/* + * winreg_s_CreateValue + */ +/*ARGSUSED*/ +static int +winreg_s_CreateValue(void *arg, struct mlrpc_xaction *mxa) +{ + struct msreg_CreateValue *param = arg; + + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +/* + * winreg_s_Shutdown + * + * Attempt to shutdown or reboot the system: access denied. + */ +/*ARGSUSED*/ +static int +winreg_s_Shutdown(void *arg, struct mlrpc_xaction *mxa) +{ + struct msreg_Shutdown *param = arg; + + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +/* + * winreg_s_GetVersion + * + * Return the windows registry version. The current version is 5. + * This call is usually made prior to enumerating or querying registry + * keys or values. + */ +/*ARGSUSED*/ +static int +winreg_s_GetVersion(void *arg, struct mlrpc_xaction *mxa) +{ + struct msreg_GetVersion *param = arg; + + param->version = 5; + param->status = ERROR_SUCCESS; + return (MLRPC_DRC_OK); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_wkssvc.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_wkssvc.c new file mode 100644 index 000000000000..b7453b171e3e --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_wkssvc.c @@ -0,0 +1,142 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int wkssvc_s_NetWkstaGetInfo(void *, struct mlrpc_xaction *); + +static mlrpc_stub_table_t wkssvc_stub_table[] = { + { wkssvc_s_NetWkstaGetInfo, WKSSVC_OPNUM_NetWkstaGetInfo }, + {0} +}; + +static mlrpc_service_t wkssvc_service = { + "Workstation", /* name (WKSSVC or WKSTA) */ + "Workstation services", /* desc */ + "\\wkssvc", /* endpoint */ + PIPE_NTSVCS, /* sec_addr_port */ + "6bffd098-a112-3610-983346c3f87e345a", 1, /* abstract */ + "8a885d04-1ceb-11c9-9fe808002b104860", 2, /* transfer */ + 0, /* no bind_instance_size */ + 0, /* no bind_req() */ + 0, /* no unbind_and_close() */ + 0, /* use generic_call_stub() */ + &TYPEINFO(wkssvc_interface), /* interface ti */ + wkssvc_stub_table /* stub_table */ +}; + +void +wkssvc_initialize(void) +{ + (void) mlrpc_register_service(&wkssvc_service); +} + +/* + * WKSSVC NetWkstaGetInfo ( + * IN LPTSTR servername, + * IN DWORD level, + * OUT union switch(level) { + * case 100: _WKSTA_INFO_100 * p100; + * case 101: _WKSTA_INFO_101 * p101; + * case 102: _WKSTA_INFO_102 * p102; + * } bufptr, + * OUT DWORD status + * ) + */ +static int +wkssvc_s_NetWkstaGetInfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct mslm_NetWkstaGetInfo *param = arg; + mslm_NetWkstaGetInfo_rb *rb; + char hostname[MAXHOSTNAMELEN]; + char *resource_domain; + char *p; + DWORD status; + int rc; + + rc = smb_getnetbiosname(hostname, MAXHOSTNAMELEN); + rb = MLRPC_HEAP_NEW(mxa, mslm_NetWkstaGetInfo_rb); + + if ((rc != 0) || (rb == NULL)) { + bzero(param, sizeof (struct mslm_NetWkstaGetInfo)); + param->status = ERROR_NOT_ENOUGH_MEMORY; + return (MLRPC_DRC_OK); + } + + param->result.level = param->level; + param->result.bufptr.nullptr = (void *) rb; + + switch (param->level) { + case 100: + rb->buf100.wki100_platform_id = SV_PLATFORM_ID_NT; + rb->buf100.wki100_ver_major = 4; + rb->buf100.wki100_ver_minor = 0; + + if ((p = MLRPC_HEAP_STRSAVE(mxa, hostname)) == NULL) { + status = ERROR_NOT_ENOUGH_MEMORY; + break; + } + rb->buf100.wki100_computername = (unsigned char *)p; + + smb_config_rdlock(); + resource_domain = smb_config_getstr(SMB_CI_DOMAIN_NAME); + + if ((p = MLRPC_HEAP_STRSAVE(mxa, resource_domain)) == NULL) { + smb_config_unlock(); + status = ERROR_NOT_ENOUGH_MEMORY; + break; + } + + smb_config_unlock(); + rb->buf100.wki100_langroup = (unsigned char *)p; + status = ERROR_SUCCESS; + break; + + default: + param->result.bufptr.nullptr = 0; + status = ERROR_INVALID_LEVEL; + break; + } + + if (status != ERROR_SUCCESS) { + bzero(param, sizeof (struct mslm_NetWkstaGetInfo)); + param->status = status; + } + + return (MLRPC_DRC_OK); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netdfs.c b/usr/src/lib/smbsrv/libmlsvc/common/netdfs.c new file mode 100644 index 000000000000..aad7e1bbd046 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/netdfs.c @@ -0,0 +1,519 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Net DFS server side RPC service. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + char *server; + char *share; + char *path; + char *buf; +} netdfs_unc_t; + +static int netdfs_unc_parse(struct mlrpc_xaction *, const char *, + netdfs_unc_t *); + +static int netdfs_s_getver(void *, struct mlrpc_xaction *); +static int netdfs_s_add(void *, struct mlrpc_xaction *); +static int netdfs_s_remove(void *, struct mlrpc_xaction *); +static int netdfs_s_setinfo(void *, struct mlrpc_xaction *); +static int netdfs_s_getinfo(void *, struct mlrpc_xaction *); +static int netdfs_s_enum(void *, struct mlrpc_xaction *); +static int netdfs_s_move(void *, struct mlrpc_xaction *); +static int netdfs_s_rename(void *, struct mlrpc_xaction *); +static int netdfs_s_addstdroot(void *, struct mlrpc_xaction *); +static int netdfs_s_remstdroot(void *, struct mlrpc_xaction *); +static int netdfs_s_enumex(void *, struct mlrpc_xaction *); + +static mlrpc_stub_table_t netdfs_stub_table[] = { + { netdfs_s_getver, NETDFS_OPNUM_GETVER }, + { netdfs_s_add, NETDFS_OPNUM_ADD }, + { netdfs_s_remove, NETDFS_OPNUM_REMOVE }, + { netdfs_s_setinfo, NETDFS_OPNUM_SETINFO }, + { netdfs_s_getinfo, NETDFS_OPNUM_GETINFO }, + { netdfs_s_enum, NETDFS_OPNUM_ENUM }, + { netdfs_s_rename, NETDFS_OPNUM_RENAME }, + { netdfs_s_move, NETDFS_OPNUM_MOVE }, + { netdfs_s_addstdroot, NETDFS_OPNUM_ADDSTDROOT }, + { netdfs_s_remstdroot, NETDFS_OPNUM_REMSTDROOT }, + { netdfs_s_enumex, NETDFS_OPNUM_ENUMEX }, + {0} +}; + +static mlrpc_service_t netdfs_service = { + "NETDFS", /* name */ + "DFS", /* desc */ + "\\dfs", /* endpoint */ + PIPE_NTSVCS, /* sec_addr_port */ + NETDFS_ABSTRACT_UUID, NETDFS_ABSTRACT_VERS, + NETDFS_TRANSFER_UUID, NETDFS_TRANSFER_VERS, + + 0, /* no bind_instance_size */ + 0, /* no bind_req() */ + 0, /* no unbind_and_close() */ + 0, /* use generic_call_stub() */ + + &TYPEINFO(netdfs_interface), /* interface ti */ + netdfs_stub_table /* stub_table */ +}; + +/* + * Register the NETDFS RPC interface with the RPC runtime library. + * The service must be registered in order to use either the client + * side or the server side functions. + */ +void +netdfs_initialize(void) +{ + (void) mlrpc_register_service(&netdfs_service); +} + +/* + * Return the version. + * + * We have to indicate that we emulate a Windows 2003 Server or the + * client will not use the EnumEx RPC and this would limit support + * to a single DFS root. + */ +/*ARGSUSED*/ +static int +netdfs_s_getver(void *arg, struct mlrpc_xaction *mxa) +{ + struct netdfs_getver *param = arg; + + param->version = DFS_MANAGER_VERSION_W2K3; + return (MLRPC_DRC_OK); +} + +/* + * Add a new volume or additional storage for an existing volume at + * dfs_path. + */ +static int +netdfs_s_add(void *arg, struct mlrpc_xaction *mxa) +{ + struct netdfs_add *param = arg; + netdfs_unc_t unc; + DWORD status = ERROR_SUCCESS; + + if (param->dfs_path == NULL || param->server == NULL || + param->share == NULL) { + bzero(param, sizeof (struct netdfs_add)); + param->status = ERROR_INVALID_PARAMETER; + return (MLRPC_DRC_OK); + } + + if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) { + status = ERROR_INVALID_PARAMETER; + } else { + if (unc.path == NULL) + status = ERROR_BAD_PATHNAME; + + if (unc.share == NULL) + status = ERROR_INVALID_SHARENAME; + } + + if (param->status != ERROR_SUCCESS) { + bzero(param, sizeof (struct netdfs_add)); + param->status = status; + return (MLRPC_DRC_OK); + } + + bzero(param, sizeof (struct netdfs_add)); + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +/* + * netdfs_s_remove + * + * Remove a volume or additional storage for volume from the DFS at + * dfs_path. When applied to the last storage in a volume, removes + * the volume from the DFS. + */ +static int +netdfs_s_remove(void *arg, struct mlrpc_xaction *mxa) +{ + struct netdfs_remove *param = arg; + netdfs_unc_t unc; + DWORD status = ERROR_SUCCESS; + + if (param->dfs_path == NULL || param->server == NULL || + param->share == NULL) { + bzero(param, sizeof (struct netdfs_remove)); + param->status = ERROR_INVALID_PARAMETER; + return (MLRPC_DRC_OK); + } + + if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) { + status = ERROR_INVALID_PARAMETER; + } else { + if (unc.path == NULL) + status = ERROR_BAD_PATHNAME; + + if (unc.share == NULL) + status = ERROR_INVALID_SHARENAME; + } + + if (param->status != ERROR_SUCCESS) { + bzero(param, sizeof (struct netdfs_remove)); + param->status = status; + return (MLRPC_DRC_OK); + } + + bzero(param, sizeof (struct netdfs_remove)); + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +/* + * Set information about the volume or storage. If the server and share + * are specified, the information set is specific to that server and + * share. Otherwise the information is specific to the volume as a whole. + * + * Valid levels are 100-102. + */ +/*ARGSUSED*/ +static int +netdfs_s_setinfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct netdfs_setinfo *param = arg; + netdfs_unc_t unc; + DWORD status = ERROR_SUCCESS; + + if (param->dfs_path == NULL) { + bzero(param, sizeof (struct netdfs_setinfo)); + param->status = ERROR_INVALID_PARAMETER; + return (MLRPC_DRC_OK); + } + + if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) { + status = ERROR_INVALID_PARAMETER; + } else { + if (unc.share == NULL) + status = ERROR_INVALID_SHARENAME; + } + + if (param->status != ERROR_SUCCESS) { + bzero(param, sizeof (struct netdfs_setinfo)); + param->status = status; + return (MLRPC_DRC_OK); + } + + switch (param->info.level) { + case 100: + case 101: + case 102: + break; + + default: + bzero(param, sizeof (struct netdfs_setinfo)); + param->status = ERROR_INVALID_LEVEL; + return (MLRPC_DRC_OK); + } + + bzero(param, sizeof (struct netdfs_setinfo)); + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +/* + * Get information about the volume or storage. If the server and share + * are specified, the information returned is specific to that server + * and share. Otherwise the information is specific to the volume as a + * whole. + * + * Valid levels are 1-4, 100-104. + */ +/*ARGSUSED*/ +static int +netdfs_s_getinfo(void *arg, struct mlrpc_xaction *mxa) +{ + struct netdfs_getinfo *param = arg; + netdfs_unc_t unc; + DWORD status = ERROR_SUCCESS; + + if (param->dfs_path == NULL) { + bzero(param, sizeof (struct netdfs_getinfo)); + param->status = ERROR_INVALID_PARAMETER; + return (MLRPC_DRC_OK); + } + + if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) { + status = ERROR_INVALID_PARAMETER; + } else { + if (unc.share == NULL) + status = ERROR_INVALID_SHARENAME; + } + + if (param->status != ERROR_SUCCESS) { + bzero(param, sizeof (struct netdfs_getinfo)); + param->status = status; + return (MLRPC_DRC_OK); + } + + switch (param->level) { + case 1: + case 2: + case 3: + case 4: + case 100: + case 101: + case 102: + case 103: + case 104: + break; + + default: + bzero(param, sizeof (struct netdfs_getinfo)); + param->status = ERROR_INVALID_LEVEL; + return (MLRPC_DRC_OK); + } + + bzero(param, sizeof (struct netdfs_getinfo)); + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +/* + * Get information about all of the volumes in the DFS. dfs_name is + * the "server" part of the UNC name used to refer to this particular + * DFS. + * + * Valid levels are 1-3. + */ +/*ARGSUSED*/ +static int +netdfs_s_enum(void *arg, struct mlrpc_xaction *mxa) +{ + struct netdfs_enum *param = arg; + + switch (param->level) { + case 1: + case 2: + case 3: + break; + + default: + (void) bzero(param, sizeof (struct netdfs_enum)); + param->status = ERROR_INVALID_LEVEL; + return (MLRPC_DRC_OK); + } + + (void) bzero(param, sizeof (struct netdfs_enum)); + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +/* + * Move a DFS volume and all subordinate volumes from one place in the + * DFS to another place in the DFS. + */ +/*ARGSUSED*/ +static int +netdfs_s_move(void *arg, struct mlrpc_xaction *mxa) +{ + struct netdfs_move *param = arg; + + if (param->dfs_path == NULL || param->new_path == NULL) { + bzero(param, sizeof (struct netdfs_move)); + param->status = ERROR_INVALID_PARAMETER; + return (MLRPC_DRC_OK); + } + + bzero(param, sizeof (struct netdfs_move)); + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +/* + * Rename the current path in a DFS to a new path in the same DFS. + */ +/*ARGSUSED*/ +static int +netdfs_s_rename(void *arg, struct mlrpc_xaction *mxa) +{ + struct netdfs_rename *param = arg; + + if (param->dfs_path == NULL || param->new_path == NULL) { + bzero(param, sizeof (struct netdfs_rename)); + param->status = ERROR_INVALID_PARAMETER; + return (MLRPC_DRC_OK); + } + + bzero(param, sizeof (struct netdfs_rename)); + param->status = ERROR_ACCESS_DENIED; + return (MLRPC_DRC_OK); +} + +/* + * Add a DFS root share. + */ +/*ARGSUSED*/ +static int +netdfs_s_addstdroot(void *arg, struct mlrpc_xaction *mxa) +{ + struct netdfs_addstdroot *param = arg; + + bzero(param, sizeof (struct netdfs_addstdroot)); + param->status = ERROR_INVALID_PARAMETER; + return (MLRPC_DRC_OK); +} + +/* + * Remove a DFS root share. + */ +/*ARGSUSED*/ +static int +netdfs_s_remstdroot(void *arg, struct mlrpc_xaction *mxa) +{ + struct netdfs_remstdroot *param = arg; + + bzero(param, sizeof (struct netdfs_remstdroot)); + param->status = ERROR_INVALID_PARAMETER; + return (MLRPC_DRC_OK); +} + +/* + * Get information about all of the volumes in the DFS. dfs_path is + * the "server" part of the UNC name used to refer to this particular + * DFS. + * + * Valid levels are 1-3, 300. + */ +static int +netdfs_s_enumex(void *arg, struct mlrpc_xaction *mxa) +{ + struct netdfs_enumex *param = arg; + netdfs_unc_t unc; + DWORD status = ERROR_SUCCESS; + + if (param->dfs_path == NULL) { + bzero(param, sizeof (struct netdfs_enumex)); + param->status = ERROR_INVALID_PARAMETER; + return (MLRPC_DRC_OK); + } + + if (param->resume_handle == NULL) + param->resume_handle = MLRPC_HEAP_NEW(mxa, DWORD); + + if (param->resume_handle) + *(param->resume_handle) = 0; + + if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) { + status = ERROR_INVALID_PARAMETER; + } else { + if (unc.path == NULL) + status = ERROR_BAD_PATHNAME; + + if (unc.share == NULL) + status = ERROR_INVALID_SHARENAME; + } + + if (param->status != ERROR_SUCCESS) { + bzero(param, sizeof (struct netdfs_enumex)); + param->status = status; + return (MLRPC_DRC_OK); + } + + param->info = MLRPC_HEAP_NEW(mxa, struct netdfs_enum_info); + if (param->info == NULL) { + bzero(param, sizeof (struct netdfs_enumex)); + param->status = ERROR_NOT_ENOUGH_MEMORY; + return (MLRPC_DRC_OK); + } + + bzero(param->info, sizeof (struct netdfs_enumex)); + param->status = ERROR_SUCCESS; + return (MLRPC_DRC_OK); +} + +/* + * Parse a UNC path (\\server\share\path) into components. + * Path separators are converted to forward slashes. + * + * Returns 0 on success, otherwise -1 to indicate an error. + */ +static int +netdfs_unc_parse(struct mlrpc_xaction *mxa, const char *path, netdfs_unc_t *unc) +{ + char *p; + + if (path == NULL || unc == NULL) + return (-1); + + if ((unc->buf = MLRPC_HEAP_STRSAVE(mxa, (char *)path)) == NULL) + return (-1); + + if ((p = strchr(unc->buf, '\n')) != NULL) + *p = '\0'; + + (void) strsubst(unc->buf, '\\', '/'); + (void) strcanon(unc->buf, "/"); + + unc->server = unc->buf; + unc->server += strspn(unc->buf, "/"); + + if (unc->server) { + unc->share = strchr(unc->server, '/'); + if ((p = unc->share) != NULL) { + unc->share += strspn(unc->share, "/"); + *p = '\0'; + } + } + + if (unc->share) { + unc->path = strchr(unc->share, '/'); + if ((p = unc->path) != NULL) { + unc->path += strspn(unc->path, "/"); + *p = '\0'; + } + } + + if (unc->path) { + if ((p = strchr(unc->path, '\0')) != NULL) { + if (*(--p) == '/') + *p = '\0'; + } + } + + return (0); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c new file mode 100644 index 000000000000..2eb3e78bb794 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c @@ -0,0 +1,502 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * NETR challenge/response client functions. + * + * NT_STATUS_INVALID_PARAMETER + * NT_STATUS_NO_TRUST_SAM_ACCOUNT + * NT_STATUS_ACCESS_DENIED + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +int netr_setup_authenticator(netr_info_t *, struct netr_authenticator *, + struct netr_authenticator *); +DWORD netr_validate_chain(netr_info_t *, struct netr_authenticator *); + +static int netr_server_req_challenge(mlsvc_handle_t *, netr_info_t *); +static int netr_server_authenticate2(mlsvc_handle_t *, netr_info_t *); +static int netr_gen_password(BYTE *, BYTE *, BYTE *); + +/* + * Shared with netr_logon.c + */ +netr_info_t netr_global_info; + +/* + * netlogon_auth + * + * This is the core of the NETLOGON authentication protocol. + * Do the challenge response authentication. + * + * Prior to calling this function, an anonymous session to the NETLOGON + * pipe on a domain controller(server) should have already been opened. + */ +DWORD +netlogon_auth(char *server, mlsvc_handle_t *netr_handle, DWORD flags) +{ + netr_info_t *netr_info; + int rc; + DWORD random_challenge[2]; + + netr_info = &netr_global_info; + bzero(netr_info, sizeof (netr_info_t)); + + netr_info->flags |= flags; + + rc = smb_getnetbiosname(netr_info->hostname, MLSVC_DOMAIN_NAME_MAX); + if (rc != 0) + return (NT_STATUS_UNSUCCESSFUL); + + (void) snprintf(netr_info->server, sizeof (netr_info->server), + "\\\\%s", server); + + random_challenge[0] = random(); + random_challenge[1] = random(); + + (void) memcpy(&netr_info->client_challenge, random_challenge, + sizeof (struct netr_credential)); + + if ((rc = netr_server_req_challenge(netr_handle, netr_info)) == 0) { + rc = netr_server_authenticate2(netr_handle, netr_info); + if (rc == 0) + netr_info->flags |= NETR_FLG_VALID; + } + + return ((rc) ? NT_STATUS_UNSUCCESSFUL : NT_STATUS_SUCCESS); +} + +/* + * netr_open + * + * Open an anonymous session to the NETLOGON pipe on a domain + * controller and bind to the NETR RPC interface. We store the + * remote server's native OS type - we may need it due to + * differences between versions of Windows. + */ +int +netr_open(char *server, char *domain, mlsvc_handle_t *netr_handle) +{ + int fid; + int remote_os = 0; + int remote_lm = 0; + int server_pdc; + char *username; + + if (mlsvc_anonymous_logon(server, domain, &username) != 0) + return (-1); + + fid = mlsvc_open_pipe(server, domain, username, "\\NETLOGON"); + if (fid < 0) + return (-1); + + if (mlsvc_rpc_bind(netr_handle, fid, "NETR") < 0) { + (void) mlsvc_close_pipe(fid); + return (-1); + } + + (void) mlsvc_session_native_values(fid, &remote_os, &remote_lm, + &server_pdc); + netr_handle->context->server_os = remote_os; + netr_handle->context->server_pdc = server_pdc; + return (0); +} + +/* + * netr_close + * + * Close a NETLOGON pipe and free the RPC context. + */ +int +netr_close(mlsvc_handle_t *netr_handle) +{ + (void) mlsvc_close_pipe(netr_handle->context->fid); + free(netr_handle->context); + return (0); +} + +/* + * netr_server_req_challenge + */ +static int +netr_server_req_challenge(mlsvc_handle_t *netr_handle, netr_info_t *netr_info) +{ + struct netr_ServerReqChallenge arg; + mlrpc_heapref_t heap; + int opnum; + int rc; + + bzero(&arg, sizeof (struct netr_ServerReqChallenge)); + opnum = NETR_OPNUM_ServerReqChallenge; + + arg.servername = (unsigned char *)netr_info->server; + arg.hostname = (unsigned char *)netr_info->hostname; + + (void) memcpy(&arg.client_challenge, &netr_info->client_challenge, + sizeof (struct netr_credential)); + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap); + if (rc == 0) { + if (arg.status != 0) { + mlsvc_rpc_report_status(opnum, arg.status); + rc = -1; + } else { + (void) memcpy(&netr_info->server_challenge, + &arg.server_challenge, + sizeof (struct netr_credential)); + } + } + + mlsvc_rpc_free(netr_handle->context, &heap); + return (rc); +} + +/* + * netr_server_authenticate2 + */ +static int +netr_server_authenticate2(mlsvc_handle_t *netr_handle, netr_info_t *netr_info) +{ + struct netr_ServerAuthenticate2 arg; + mlrpc_heapref_t heap; + int opnum; + int rc; + char account_name[MLSVC_DOMAIN_NAME_MAX * 2]; + + bzero(&arg, sizeof (struct netr_ServerAuthenticate2)); + opnum = NETR_OPNUM_ServerAuthenticate2; + + (void) snprintf(account_name, sizeof (account_name), "%s$", + netr_info->hostname); + + arg.servername = (unsigned char *)netr_info->server; + arg.account_name = (unsigned char *)account_name; + arg.account_type = NETR_WKSTA_TRUST_ACCOUNT_TYPE; + arg.hostname = (unsigned char *)netr_info->hostname; + arg.negotiate_flags = NETR_NEGOTIATE_FLAGS; + + smb_tracef("server=[%s] account_name=[%s] hostname=[%s]\n", + netr_info->server, account_name, netr_info->hostname); + + if (netr_gen_session_key(netr_info) != SMBAUTH_SUCCESS) + return (-1); + + if (netr_gen_credentials(netr_info->session_key, + &netr_info->client_challenge, + 0, + &netr_info->client_credential) != SMBAUTH_SUCCESS) { + return (-1); + } + + if (netr_gen_credentials(netr_info->session_key, + &netr_info->server_challenge, + 0, + &netr_info->server_credential) != SMBAUTH_SUCCESS) { + return (-1); + } + + (void) memcpy(&arg.client_credential, &netr_info->client_credential, + sizeof (struct netr_credential)); + + (void) mlsvc_rpc_init(&heap); + + rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap); + if (rc == 0) { + if (arg.status != 0) { + mlsvc_rpc_report_status(opnum, arg.status); + rc = -1; + } else { + rc = memcmp(&netr_info->server_credential, + &arg.server_credential, + sizeof (struct netr_credential)); + } + } + + mlsvc_rpc_free(netr_handle->context, &heap); + return (rc); +} + +/* + * netr_gen_session_key + * + * Generate a session key from the client and server challenges. The + * algorithm is a two stage hash. For the first hash, the input is + * the combination of the client and server challenges, the key is + * the first 8 bytes of the password. The initial password is formed + * using the NT password hash on the local hostname in lower case. + * The result is stored in a temporary buffer. + * + * input: challenge + * key: passwd lower 8 bytes + * output: intermediate result + * + * For the second hash, the input is the result of the first hash and + * the key is the last 8 bytes of the password. + * + * input: result of first hash + * key: passwd upper 8 bytes + * output: session_key + * + * The final output should be the session key. + * + * FYI: smb_auth_DES(output, key, input) + * + * If any difficulties occur using the cryptographic framework, the + * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is + * returned. + */ +int +netr_gen_session_key(netr_info_t *netr_info) +{ + unsigned char md4hash[32]; + unsigned char buffer[8]; + DWORD data[2]; + DWORD *client_challenge; + DWORD *server_challenge; + int rc; + char *machine_passwd; + DWORD new_data[2]; + + client_challenge = (DWORD *)(uintptr_t)&netr_info->client_challenge; + server_challenge = (DWORD *)(uintptr_t)&netr_info->server_challenge; + bzero(md4hash, 32); + + /* + * We should check (netr_info->flags & NETR_FLG_INIT) and use + * the appropriate password but it isn't working yet. So we + * always use the default one for now. + */ + smb_config_rdlock(); + machine_passwd = smb_config_getstr(SMB_CI_MACHINE_PASSWD); + + if (!machine_passwd || *machine_passwd == 0) { + smb_config_unlock(); + return (-1); + } + + bzero(netr_info->password, sizeof (netr_info->password)); + (void) strlcpy((char *)netr_info->password, (char *)machine_passwd, + sizeof (netr_info->password)); + + rc = smb_auth_ntlm_hash((char *)machine_passwd, md4hash); + smb_config_unlock(); + + if (rc != SMBAUTH_SUCCESS) + return (SMBAUTH_FAILURE); + + data[0] = LE_IN32(&client_challenge[0]) + LE_IN32(&server_challenge[0]); + data[1] = LE_IN32(&client_challenge[1]) + LE_IN32(&server_challenge[1]); + LE_OUT32(&new_data[0], data[0]); + LE_OUT32(&new_data[1], data[1]); + + rc = smb_auth_DES(buffer, 8, md4hash, 8, (unsigned char *)new_data, 8); + if (rc != SMBAUTH_SUCCESS) + return (rc); + + rc = smb_auth_DES(netr_info->session_key, 8, &md4hash[9], 8, buffer, 8); + return (rc); +} + +/* + * netr_gen_credentials + * + * Generate a set of credentials from a challenge and a session key. + * The algorithm is a two stage hash. For the first hash, the + * timestamp is added to the challenge and the result is stored in a + * temporary buffer: + * + * input: challenge (including timestamp) + * key: session_key + * output: intermediate result + * + * For the second hash, the input is the result of the first hash and + * a strange partial key is used: + * + * input: result of first hash + * key: funny partial key + * output: credentiails + * + * The final output should be an encrypted set of credentials. + * + * FYI: smb_auth_DES(output, key, input) + * + * If any difficulties occur using the cryptographic framework, the + * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is + * returned. + */ +int +netr_gen_credentials(BYTE *session_key, netr_cred_t *challenge, + DWORD timestamp, netr_cred_t *out_cred) +{ + unsigned char buffer[8]; + unsigned char partial_key[8]; + DWORD data[2]; + DWORD *p; + int rc; + + p = (DWORD *)(uintptr_t)challenge; + data[0] = p[0] + LE_IN32(×tamp); + data[1] = p[1]; + + if (smb_auth_DES(buffer, 8, session_key, 8, + (unsigned char *)data, 8) != SMBAUTH_SUCCESS) + return (SMBAUTH_FAILURE); + + bzero(partial_key, 8); + partial_key[0] = session_key[7]; + + rc = smb_auth_DES((unsigned char *)out_cred, 8, partial_key, 8, + buffer, 8); + return (rc); +} + +/* + * netr_server_password_set + * + * Attempt to change the trust account password for this system. + * + * Note that this call may legitimately fail if the registry on the + * domain controller has been setup to deny attempts to change the + * trust account password. In this case we should just continue to + * use the original password. + * + * Possible status values: + * NT_STATUS_ACCESS_DENIED + */ +int +netr_server_password_set(mlsvc_handle_t *netr_handle, netr_info_t *netr_info) +{ + struct netr_PasswordSet arg; + mlrpc_heapref_t heap; + int opnum; + int rc; + BYTE new_password[NETR_OWF_PASSWORD_SZ]; + char account_name[MLSVC_DOMAIN_NAME_MAX * 2]; + + bzero(&arg, sizeof (struct netr_PasswordSet)); + opnum = NETR_OPNUM_ServerPasswordSet; + + (void) snprintf(account_name, sizeof (account_name), "%s$", + netr_info->hostname); + + arg.servername = (unsigned char *)netr_info->server; + arg.account_name = (unsigned char *)account_name; + arg.account_type = NETR_WKSTA_TRUST_ACCOUNT_TYPE; + arg.hostname = (unsigned char *)netr_info->hostname; + + /* + * Set up the client side authenticator. + */ + if (netr_setup_authenticator(netr_info, &arg.auth, 0) != + SMBAUTH_SUCCESS) { + return (-1); + } + + /* + * Generate a new password from the old password. + */ + if (netr_gen_password(netr_info->session_key, + netr_info->password, new_password) == SMBAUTH_FAILURE) { + return (-1); + } + + (void) memcpy(&arg.uas_new_password, &new_password, + NETR_OWF_PASSWORD_SZ); + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap); + if ((rc != 0) || (arg.status != 0)) { + mlsvc_rpc_report_status(opnum, arg.status); + mlsvc_rpc_free(netr_handle->context, &heap); + return (-1); + } + + /* + * Check the returned credentials. The server returns the new + * client credential rather than the new server credentiali, + * as documented elsewhere. + * + * Generate the new seed for the credential chain. Increment + * the timestamp and add it to the client challenge. Then we + * need to copy the challenge to the credential field in + * preparation for the next cycle. + */ + if (netr_validate_chain(netr_info, &arg.auth) == 0) { + /* + * Save the new password. + */ + (void) memcpy(netr_info->password, new_password, + NETR_OWF_PASSWORD_SZ); + } + + mlsvc_rpc_free(netr_handle->context, &heap); + return (0); +} + +/* + * netr_gen_password + * + * Generate a new pasword from the old password and the session key. + * The algorithm is a two stage hash. The session key is used in the + * first hash but only part of the session key is used in the second + * hash. + * + * If any difficulties occur using the cryptographic framework, the + * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is + * returned. + */ +static int +netr_gen_password(BYTE *session_key, BYTE *old_password, BYTE *new_password) +{ + unsigned char partial_key[8]; + int rv; + + rv = smb_auth_DES(new_password, 8, session_key, 8, old_password, 8); + if (rv != SMBAUTH_SUCCESS) + return (rv); + + bzero(partial_key, 8); + partial_key[0] = session_key[7]; + + rv = smb_auth_DES(&new_password[8], 8, partial_key, 8, + &old_password[8], 8); + return (rv); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c new file mode 100644 index 000000000000..d26d6f0dc785 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c @@ -0,0 +1,584 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * NETR SamLogon and SamLogoff RPC client functions. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int netr_open(char *server, char *domain, mlsvc_handle_t *netr_handle); +extern int netr_close(mlsvc_handle_t *netr_handle); +extern DWORD netlogon_auth(char *server, mlsvc_handle_t *netr_handle, + DWORD flags); +extern int netr_setup_authenticator(netr_info_t *, struct netr_authenticator *, + struct netr_authenticator *); +extern DWORD netr_validate_chain(netr_info_t *, struct netr_authenticator *); + +static DWORD netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *, + netr_client_t *, smb_userinfo_t *); +static void netr_invalidate_chain(void); +static void netr_interactive_samlogon(netr_info_t *, netr_client_t *, + struct netr_logon_info1 *); +static void netr_network_samlogon(netr_info_t *, netr_client_t *, + netr_response_t *, netr_response_t *, struct netr_logon_info2 *); +static void netr_setup_identity(mlrpc_heap_t *, netr_client_t *, + netr_logon_id_t *); + +/* + * Shared with netr_auth.c + */ +extern netr_info_t netr_global_info; + +/* + * netlogon_logon + * + * This is the entry point for authenticating a remote logon. The + * parameters here all refer to the remote user and workstation, i.e. + * the domain is the user's account domain, not our primary domain. + * In order to make it easy to track which domain is being used at + * each stage, and to reduce the number of things being pushed on the + * stack, the client information is bundled up in the clnt structure. + * + * If the user is successfully authenticated, an access token will be + * built and NT_STATUS_SUCCESS will be returned. Otherwise a non-zero + * NT status will be returned, in which case the token contents will + * be invalid. + */ +DWORD +netlogon_logon(netr_client_t *clnt, smb_userinfo_t *user_info) +{ + char resource_domain[SMB_PI_MAX_DOMAIN]; + mlsvc_handle_t netr_handle; + smb_ntdomain_t *di; + DWORD status; + int retries = 0; + + smb_config_rdlock(); + (void) strlcpy(resource_domain, smb_config_getstr(SMB_CI_DOMAIN_NAME), + sizeof (resource_domain)); + smb_config_unlock(); + + /* + * If the SMB info cache is not valid, + * try to locate a domain controller. + */ + if ((di = smb_getdomaininfo(0)) == NULL) { + (void) mlsvc_locate_domain_controller(resource_domain); + + if ((di = smb_getdomaininfo(0)) == NULL) + return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + } + + if ((mlsvc_echo(di->server)) < 0) { + /* + * We had a session to the DC but it's not responding. + * So drop the credential chain and find another DC. + */ + netr_invalidate_chain(); + (void) mlsvc_locate_domain_controller(resource_domain); + + if ((di = smb_getdomaininfo(0)) == NULL) + return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + } + + do { + status = netr_open(di->server, di->domain, &netr_handle); + if (status != 0) + return (status); + + if ((netr_global_info.flags & NETR_FLG_VALID) == 0) { + status = netlogon_auth(di->server, &netr_handle, + NETR_FLG_NULL); + + if (status != 0) { + (void) netr_close(&netr_handle); + return (NT_STATUS_LOGON_FAILURE); + } + + netr_global_info.flags |= NETR_FLG_VALID; + } + + status = netr_server_samlogon(&netr_handle, + &netr_global_info, di->server, clnt, user_info); + + (void) netr_close(&netr_handle); + } while (status == NT_STATUS_INSUFFICIENT_LOGON_INFO && retries++ < 3); + + if (retries >= 3) + status = NT_STATUS_LOGON_FAILURE; + + return (status); +} + +static DWORD +netr_setup_userinfo(struct netr_validation_info3 *info3, + smb_userinfo_t *user_info, netr_client_t *clnt) +{ + smb_sid_attrs_t *other_grps; + char *username, *domain; + int i, nbytes; + + user_info->sid_name_use = SidTypeUser; + user_info->rid = info3->UserId; + user_info->primary_group_rid = info3->PrimaryGroupId; + user_info->domain_sid = nt_sid_dup((nt_sid_t *)info3->LogonDomainId); + + if (user_info->domain_sid == NULL) + return (NT_STATUS_NO_MEMORY); + + user_info->user_sid = nt_sid_splice(user_info->domain_sid, + user_info->rid); + if (user_info->user_sid == NULL) + return (NT_STATUS_NO_MEMORY); + + user_info->pgrp_sid = nt_sid_splice(user_info->domain_sid, + user_info->primary_group_rid); + if (user_info->pgrp_sid == NULL) + return (NT_STATUS_NO_MEMORY); + + username = (info3->EffectiveName.str) + ? (char *)info3->EffectiveName.str : clnt->username; + domain = (info3->LogonDomainName.str) + ? (char *)info3->LogonDomainName.str : clnt->domain; + + if (username) + user_info->name = strdup(username); + if (domain) + user_info->domain_name = strdup(domain); + + if (user_info->name == NULL || user_info->domain_name == NULL) + return (NT_STATUS_NO_MEMORY); + + nbytes = info3->GroupCount * sizeof (smb_rid_attrs_t); + if (nbytes) { + if ((user_info->groups = malloc(nbytes)) != NULL) { + user_info->n_groups = info3->GroupCount; + (void) memcpy(user_info->groups, + info3->GroupIds, nbytes); + } else { + return (NT_STATUS_NO_MEMORY); + } + } + + nbytes = info3->SidCount * sizeof (smb_sid_attrs_t); + if (nbytes) { + if ((other_grps = malloc(nbytes)) != NULL) { + user_info->other_grps = other_grps; + for (i = 0; i < info3->SidCount; i++) { + other_grps[i].attrs = + info3->ExtraSids[i].attributes; + + other_grps[i].sid = nt_sid_dup( + (nt_sid_t *)info3->ExtraSids[i].sid); + + if (other_grps[i].sid == NULL) + break; + } + user_info->n_other_grps = i; + } else { + return (NT_STATUS_NO_MEMORY); + } + } + + mlsvc_setadmin_user_info(user_info); + return (NT_STATUS_SUCCESS); +} + +/* + * netr_server_samlogon + * + * NetrServerSamLogon RPC: interactive or network. It is assumed that + * we have already authenticated with the PDC. If everything works, + * we build a user info structure and return it, where the caller will + * probably build an access token. + * + * Returns an NT status. There are numerous possibilities here. + * For example: + * NT_STATUS_INVALID_INFO_CLASS + * NT_STATUS_INVALID_PARAMETER + * NT_STATUS_ACCESS_DENIED + * NT_STATUS_PASSWORD_MUST_CHANGE + * NT_STATUS_NO_SUCH_USER + * NT_STATUS_WRONG_PASSWORD + * NT_STATUS_LOGON_FAILURE + * NT_STATUS_ACCOUNT_RESTRICTION + * NT_STATUS_INVALID_LOGON_HOURS + * NT_STATUS_INVALID_WORKSTATION + * NT_STATUS_INTERNAL_ERROR + * NT_STATUS_PASSWORD_EXPIRED + * NT_STATUS_ACCOUNT_DISABLED + */ +DWORD +netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info, + char *server, netr_client_t *clnt, smb_userinfo_t *user_info) +{ + struct netr_SamLogon arg; + struct netr_authenticator auth; + struct netr_authenticator ret_auth; + struct netr_logon_info1 info1; + struct netr_logon_info2 info2; + struct netr_validation_info3 *info3; + netr_response_t nt_rsp; + netr_response_t lm_rsp; + mlrpc_heapref_t heap; + int opnum; + int rc, len; + DWORD status; + + bzero(&arg, sizeof (struct netr_SamLogon)); + opnum = NETR_OPNUM_SamLogon; + (void) mlsvc_rpc_init(&heap); + + /* + * Should we get the server and hostname from netr_info? + */ + len = strlen(server) + 4; + arg.servername = alloca(len); + (void) snprintf((char *)arg.servername, len, "\\\\%s", server); + + arg.hostname = alloca(MLSVC_DOMAIN_NAME_MAX); + rc = smb_gethostname((char *)arg.hostname, MLSVC_DOMAIN_NAME_MAX, 0); + if (rc != 0) { + mlrpc_heap_destroy(heap.heap); + return (NT_STATUS_INTERNAL_ERROR); + } + + rc = netr_setup_authenticator(netr_info, &auth, &ret_auth); + if (rc != SMBAUTH_SUCCESS) { + mlrpc_heap_destroy(heap.heap); + return (NT_STATUS_INTERNAL_ERROR); + } + + arg.auth = &auth; + arg.ret_auth = &ret_auth; + arg.validation_level = NETR_VALIDATION_LEVEL3; + arg.logon_info.logon_level = clnt->logon_level; + arg.logon_info.switch_value = clnt->logon_level; + + switch (clnt->logon_level) { + case NETR_INTERACTIVE_LOGON: + netr_setup_identity(heap.heap, clnt, &info1.identity); + netr_interactive_samlogon(netr_info, clnt, &info1); + arg.logon_info.ru.info1 = &info1; + break; + + case NETR_NETWORK_LOGON: + netr_setup_identity(heap.heap, clnt, &info2.identity); + netr_network_samlogon(netr_info, clnt, &nt_rsp, &lm_rsp, + &info2); + arg.logon_info.ru.info2 = &info2; + break; + + default: + mlrpc_heap_destroy(heap.heap); + return (NT_STATUS_INVALID_PARAMETER); + } + + rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap); + if (rc != 0) { + bzero(netr_info, sizeof (netr_info_t)); + status = NT_STATUS_INVALID_PARAMETER; + } else if (arg.status != 0) { + status = NT_SC_VALUE(arg.status); + + /* + * We need to validate the chain even though we have + * a non-zero status. If the status is ACCESS_DENIED + * this will trigger a new credential chain. However, + * a valid credential is returned with some status + * codes; for example, WRONG_PASSWORD. + */ + (void) netr_validate_chain(netr_info, arg.ret_auth); + } else { + status = netr_validate_chain(netr_info, arg.ret_auth); + if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) { + mlsvc_rpc_free(netr_handle->context, &heap); + return (status); + } + + info3 = arg.ru.info3; + status = netr_setup_userinfo(info3, user_info, clnt); + } + + mlsvc_rpc_free(netr_handle->context, &heap); + return (status); +} + +/* + * netr_interactive_samlogon + * + * Set things up for an interactive SamLogon. Copy the NT and LM + * passwords to the logon structure and hash them with the session + * key. + */ +static void +netr_interactive_samlogon(netr_info_t *netr_info, netr_client_t *clnt, + struct netr_logon_info1 *info1) +{ + BYTE key[NETR_OWF_PASSWORD_SZ]; + + (void) memcpy(&info1->lm_owf_password, + clnt->lm_password.lm_password_val, sizeof (netr_owf_password_t)); + + (void) memcpy(&info1->nt_owf_password, + clnt->nt_password.nt_password_val, sizeof (netr_owf_password_t)); + + (void) memset(key, 0, NETR_OWF_PASSWORD_SZ); + (void) memcpy(key, netr_info->session_key, NETR_SESSION_KEY_SZ); + + rand_hash((unsigned char *)&info1->lm_owf_password, + NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ); + + rand_hash((unsigned char *)&info1->nt_owf_password, + NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ); +} + +/* + * netr_network_samlogon + * + * Set things up for a network SamLogon. We provide a copy of the random + * challenge, that we sent to the client, to the domain controller. This + * is the key that the client will have used to encrypt the NT and LM + * passwords. Note that Windows 9x clients may not provide both passwords. + */ +/*ARGSUSED*/ +static void +netr_network_samlogon(netr_info_t *netr_info, netr_client_t *clnt, + netr_response_t *ntr, netr_response_t *lmr, struct netr_logon_info2 *info2) +{ + bcopy(clnt->challenge_key.challenge_key_val, info2->lm_challenge.data, + 8); + + if (clnt->nt_password.nt_password_len == NETR_CR_PASSWORD_SIZE) { + ntr->length = NETR_CR_PASSWORD_SIZE; + ntr->start = 0; + ntr->max_length = NETR_CR_PASSWORD_SIZE; + bcopy(clnt->nt_password.nt_password_val, ntr->data, + NETR_CR_PASSWORD_SIZE); + + info2->nt_response.length = NETR_CR_PASSWORD_SIZE; + info2->nt_response.max_length = NETR_CR_PASSWORD_SIZE; + info2->nt_response.data = ntr; + } else { + info2->nt_response.length = 0; + info2->nt_response.max_length = 0; + info2->nt_response.data = 0; + } + + if (clnt->lm_password.lm_password_len == NETR_CR_PASSWORD_SIZE) { + lmr->length = NETR_CR_PASSWORD_SIZE; + lmr->start = 0; + lmr->max_length = NETR_CR_PASSWORD_SIZE; + bcopy(clnt->lm_password.lm_password_val, lmr->data, + NETR_CR_PASSWORD_SIZE); + + info2->lm_response.length = NETR_CR_PASSWORD_SIZE; + info2->lm_response.max_length = NETR_CR_PASSWORD_SIZE; + info2->lm_response.data = lmr; + } else { + info2->lm_response.length = 0; + info2->lm_response.max_length = 0; + info2->lm_response.data = 0; + } +} + +/* + * netr_setup_authenticator + * + * Set up the request and return authenticators. A new credential is + * generated from the session key, the current client credential and + * the current time, i.e. + * + * NewCredential = Cred(SessionKey, OldCredential, time); + * + * The timestamp, which is used as a random seed, is stored in both + * the request and return authenticators. + * + * If any difficulties occur using the cryptographic framework, the + * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is + * returned. + */ +int +netr_setup_authenticator(netr_info_t *netr_info, + struct netr_authenticator *auth, struct netr_authenticator *ret_auth) +{ + bzero(auth, sizeof (struct netr_authenticator)); + +#ifdef _BIG_ENDIAN + netr_info->timestamp = 0; +#else + netr_info->timestamp = time(0) << 8; +#endif + auth->timestamp = netr_info->timestamp; + + if (netr_gen_credentials(netr_info->session_key, + &netr_info->client_credential, + netr_info->timestamp, + (netr_cred_t *)&auth->credential) != SMBAUTH_SUCCESS) + return (SMBAUTH_FAILURE); + + if (ret_auth) { + bzero(ret_auth, sizeof (struct netr_authenticator)); + ret_auth->timestamp = netr_info->timestamp; + } + + return (SMBAUTH_SUCCESS); +} + +/* + * Validate the returned credentials and update the credential chain. + * The server returns an updated client credential rather than a new + * server credential. The server uses (timestamp + 1) when generating + * the credential. + * + * Generate the new seed for the credential chain. The new seed is + * formed by adding (timestamp + 1) to the current client credential. + * The only quirk is the DWORD style addition. + * + * Returns NT_STATUS_INSUFFICIENT_LOGON_INFO if auth->credential is a + * NULL pointer. The Authenticator field of the SamLogon response packet + * sent by the Samba 3 PDC always return NULL pointer if the received + * SamLogon request is not immediately followed by the ServerReqChallenge + * and ServerAuthenticate2 requests. + * + * Returns NT_STATUS_SUCCESS if the server returned a valid credential. + * Otherwise we retirm NT_STATUS_UNSUCCESSFUL. + */ +DWORD +netr_validate_chain(netr_info_t *netr_info, struct netr_authenticator *auth) +{ + netr_cred_t cred; + DWORD result = NT_STATUS_SUCCESS; + DWORD *dwp; + + ++netr_info->timestamp; + + if (netr_gen_credentials(netr_info->session_key, + &netr_info->client_credential, + netr_info->timestamp, &cred) != SMBAUTH_SUCCESS) + return (NT_STATUS_INTERNAL_ERROR); + + if (&auth->credential == 0) { + /* + * If the validation fails, destroy the credential chain. + * This should trigger a new authentication chain. + */ + bzero(netr_info, sizeof (netr_info_t)); + return (NT_STATUS_INSUFFICIENT_LOGON_INFO); + } + + result = memcmp(&cred, &auth->credential, sizeof (netr_cred_t)); + if (result != 0) { + /* + * If the validation fails, destroy the credential chain. + * This should trigger a new authentication chain. + */ + bzero(netr_info, sizeof (netr_info_t)); + result = NT_STATUS_UNSUCCESSFUL; + } else { + /* + * Otherwise generate the next step in the chain. + */ + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + dwp = (DWORD *)&netr_info->client_credential; + dwp[0] += netr_info->timestamp; + + netr_info->flags |= NETR_FLG_VALID; + } + + return (result); +} + +/* + * netr_invalidate_chain + * + * Mark the credential chain as invalid so that it will be recreated + * on the next attempt. + */ +static void +netr_invalidate_chain(void) +{ + netr_global_info.flags &= ~NETR_FLG_VALID; +} + +/* + * netr_setup_identity + * + * Set up the client identity information. All of this information is + * specifically related to the client user and workstation attempting + * to access this system. It may not be in our primary domain. + * + * I don't know what logon_id is, it seems to be a unique identifier. + * Increment it before each use. + */ +static void +netr_setup_identity(mlrpc_heap_t *heap, netr_client_t *clnt, + netr_logon_id_t *identity) +{ + static DWORD logon_id; + + if (logon_id == 0) + logon_id = 0xDCD0; + + ++logon_id; + clnt->logon_id = logon_id; + + identity->parameter_control = 0; + identity->logon_id.LowPart = logon_id; + identity->logon_id.HighPart = 0; + + mlrpc_heap_mkvcs(heap, clnt->domain, + (mlrpc_vcbuf_t *)&identity->domain_name); + + mlrpc_heap_mkvcs(heap, clnt->username, + (mlrpc_vcbuf_t *)&identity->username); + + /* + * Some systems prefix the client workstation name with \\. + * It doesn't seem to make any difference whether it's there + * or not. + */ + mlrpc_heap_mkvcs(heap, clnt->workstation, + (mlrpc_vcbuf_t *)&identity->workstation); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/samlib.c b/usr/src/lib/smbsrv/libmlsvc/common/samlib.c new file mode 100644 index 000000000000..aa75c2678ed8 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/samlib.c @@ -0,0 +1,512 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module provides the high level interface to the SAM RPC + * functions. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * Valid values for the OEM OWF password encryption. + */ +#define SAM_PASSWORD_516 516 +#define SAM_KEYLEN 16 + +extern DWORD samr_set_user_info(mlsvc_handle_t *, smb_auth_info_t *); +static int get_user_group_info(mlsvc_handle_t *, smb_userinfo_t *); + +/* + * sam_lookup_user_info + * + * Lookup user information in the SAM database on the specified server + * (domain controller). The LSA interface is used to obtain the user + * RID, the domain name and the domain SID (user privileges are TBD). + * Then the various SAM layers are opened using the domain SID and the + * user RID to obtain the users's group membership information. + * + * The information is returned in the user_info structure. The caller + * is responsible for allocating and releasing this structure. If the + * lookup is successful, sid_name_use will be set to SidTypeUser. + * + * On success 0 is returned. Otherwise a -ve error code. + */ +int +sam_lookup_user_info(char *server, char *domain_name, + char *account_name, char *password, smb_userinfo_t *user_info) +{ + mlsvc_handle_t samr_handle; + mlsvc_handle_t domain_handle; + mlsvc_handle_t user_handle; + struct samr_sid *sid; + int rc; + DWORD access_mask; + DWORD status; + + if (lsa_lookup_name(server, domain_name, account_name, user_info) != 0) + return (-1); + + if (user_info->sid_name_use != SidTypeUser || + user_info->rid == 0 || user_info->domain_sid == 0) { + return (-1); + } + + rc = samr_open(MLSVC_IPC_USER, server, domain_name, account_name, + password, SAM_LOOKUP_INFORMATION, &samr_handle); + if (rc != 0) + return (-1); +#if 0 + rc = samr_lookup_domain(&samr_handle, domain_name, user_info); + if (rc != 0) + return (-1); +#endif + sid = (struct samr_sid *)user_info->domain_sid; + + status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, + sid, &domain_handle); + if (status == 0) { +#if 0 + (void) samr_lookup_domain_names(&domain_handle, account_name, + user_info); +#endif + access_mask = STANDARD_RIGHTS_EXECUTE | SAM_ACCESS_USER_READ; + + rc = samr_open_user(&domain_handle, access_mask, + user_info->rid, &user_handle); + + if (rc == 0) { + (void) get_user_group_info(&user_handle, user_info); + (void) samr_close_handle(&user_handle); + } + + (void) samr_close_handle(&domain_handle); + } + + (void) samr_close_handle(&samr_handle); + return (rc); +} + +/* + * get_user_group_info + * + * This is a private function to obtain the primary group and group + * memberships for the user specified by the user_handle. This function + * should only be called from sam_lookup_user_info. + * + * On success 0 is returned. Otherwise -1 is returned. + */ +static int +get_user_group_info(mlsvc_handle_t *user_handle, smb_userinfo_t *user_info) +{ + union samr_user_info sui; + int rc; + + rc = samr_query_user_info(user_handle, SAMR_QUERY_USER_GROUPRID, &sui); + if (rc != 0) + return (-1); + + rc = samr_query_user_groups(user_handle, user_info); + if (rc != 0) + return (-1); + + user_info->primary_group_rid = sui.info9.group_rid; + return (0); +} + +/* + * sam_create_trust_account + * + * Create a trust account for this system. + * + * SAMR_AF_WORKSTATION_TRUST_ACCOUNT: servers and workstations. + * SAMR_AF_SERVER_TRUST_ACCOUNT: domain controllers. + * + * Returns NT status codes. + */ +DWORD +sam_create_trust_account(char *server, char *domain, smb_auth_info_t *auth) +{ + smb_userinfo_t *user_info; + char account_name[MAXHOSTNAMELEN]; + DWORD status; + + if (smb_gethostname(account_name, MAXHOSTNAMELEN - 2, 1) != 0) + return (NT_STATUS_NO_MEMORY); + + (void) strlcat(account_name, "$", MAXHOSTNAMELEN); + + if ((user_info = mlsvc_alloc_user_info()) == 0) + return (NT_STATUS_NO_MEMORY); + + /* + * The trust account value here should match + * the value that will be used when the user + * information is set on this account. + */ + status = sam_create_account(server, domain, account_name, + auth, SAMR_AF_WORKSTATION_TRUST_ACCOUNT, user_info); + + mlsvc_free_user_info(user_info); + return (status); +} + + +/* + * sam_create_account + * + * Create the specified domain account in the SAM database on the + * domain controller. + * + * Account flags: + * SAMR_AF_NORMAL_ACCOUNT + * SAMR_AF_WORKSTATION_TRUST_ACCOUNT + * SAMR_AF_SERVER_TRUST_ACCOUNT + * + * Returns NT status codes. + */ +DWORD +sam_create_account(char *server, char *domain_name, char *account_name, + smb_auth_info_t *auth, DWORD account_flags, smb_userinfo_t *user_info) +{ + mlsvc_handle_t samr_handle; + mlsvc_handle_t domain_handle; + mlsvc_handle_t user_handle; + union samr_user_info sui; + struct samr_sid *sid; + DWORD rid; + DWORD status; + int rc; + + rc = samr_open(MLSVC_IPC_ADMIN, server, domain_name, 0, 0, + SAM_CONNECT_CREATE_ACCOUNT, &samr_handle); + + if (rc != 0) { + status = NT_STATUS_OPEN_FAILED; + smb_tracef("SamCreateAccount[%s\\%s]: %s", + domain_name, account_name, xlate_nt_status(status)); + return (status); + } + + if (samr_handle.context->server_os == NATIVE_OS_WIN2000) { + nt_domain_t *ntdp; + + if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) { + (void) lsa_query_account_domain_info(); + if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) { + (void) samr_close_handle(&samr_handle); + status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + smb_tracef("SamCreateAccount[%s\\%s]: %s", + domain_name, account_name, + xlate_nt_status(status)); + return (status); + } + } + + sid = (struct samr_sid *)ntdp->sid; + } else { + if (samr_lookup_domain(&samr_handle, + domain_name, user_info) != 0) { + (void) samr_close_handle(&samr_handle); + smb_tracef("SamCreateAccount[%s]: lookup failed", + account_name); + + return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + } + + sid = (struct samr_sid *)user_info->domain_sid; + } + + status = samr_open_domain(&samr_handle, + SAM_DOMAIN_CREATE_ACCOUNT, sid, &domain_handle); + + if (status == NT_STATUS_SUCCESS) { + status = samr_create_user(&domain_handle, account_name, + account_flags, &rid, &user_handle); + + if (status == NT_STATUS_SUCCESS) { + (void) samr_query_user_info(&user_handle, + SAMR_QUERY_USER_UNKNOWN16, &sui); + + (void) samr_get_user_pwinfo(&user_handle); + (void) samr_set_user_info(&user_handle, auth); + (void) samr_close_handle(&user_handle); + } else if (status == NT_STATUS_USER_EXISTS) { + mlsvc_release_user_info(user_info); + + rc = lsa_lookup_name(server, domain_name, account_name, + user_info); + if (rc == 0) + rid = user_info->rid; + status = 0; + } else { + smb_tracef("SamCreateAccount[%s]: %s", + account_name, xlate_nt_status(status)); + } + + (void) samr_close_handle(&domain_handle); + } else { + smb_tracef("SamCreateAccount[%s]: open domain failed", + account_name); + status = (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + } + + (void) samr_close_handle(&samr_handle); + return (status); +} + + +/* + * sam_remove_trust_account + * + * Attempt to remove the workstation trust account for this system. + * Administrator access is required to perform this operation. + * + * Returns NT status codes. + */ +DWORD +sam_remove_trust_account(char *server, char *domain) +{ + char account_name[MAXHOSTNAMELEN]; + + if (smb_gethostname(account_name, MAXHOSTNAMELEN - 2, 1) != 0) + return (NT_STATUS_NO_MEMORY); + + (void) strcat(account_name, "$"); + + return (sam_delete_account(server, domain, account_name)); +} + + +/* + * sam_delete_account + * + * Attempt to remove an account from the SAM database on the specified + * server. + * + * Returns NT status codes. + */ +DWORD +sam_delete_account(char *server, char *domain_name, char *account_name) +{ + mlsvc_handle_t samr_handle; + mlsvc_handle_t domain_handle; + mlsvc_handle_t user_handle; + smb_userinfo_t *user_info; + struct samr_sid *sid; + DWORD rid; + DWORD access_mask; + DWORD status; + int rc; + + if ((user_info = mlsvc_alloc_user_info()) == 0) + return (NT_STATUS_NO_MEMORY); + + rc = samr_open(MLSVC_IPC_ADMIN, server, domain_name, 0, 0, + SAM_LOOKUP_INFORMATION, &samr_handle); + + if (rc != 0) { + mlsvc_free_user_info(user_info); + return (NT_STATUS_OPEN_FAILED); + } + + if (samr_handle.context->server_os == NATIVE_OS_WIN2000) { + nt_domain_t *ntdp; + + if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) { + (void) lsa_query_account_domain_info(); + if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) { + + (void) samr_close_handle(&samr_handle); + return (NT_STATUS_NO_SUCH_DOMAIN); + } + } + + sid = (struct samr_sid *)ntdp->sid; + } else { + if (samr_lookup_domain( + &samr_handle, domain_name, user_info) != 0) { + (void) samr_close_handle(&samr_handle); + mlsvc_free_user_info(user_info); + return (NT_STATUS_NO_SUCH_DOMAIN); + } + + sid = (struct samr_sid *)user_info->domain_sid; + } + + status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, + sid, &domain_handle); + if (status == 0) { + mlsvc_release_user_info(user_info); + status = samr_lookup_domain_names(&domain_handle, + account_name, user_info); + + if (status == 0) { + rid = user_info->rid; + access_mask = STANDARD_RIGHTS_EXECUTE | DELETE; + + rc = samr_open_user(&domain_handle, access_mask, + rid, &user_handle); + if (rc == 0) { + if (samr_delete_user(&user_handle) != 0) + (void) samr_close_handle(&user_handle); + } + } + + (void) samr_close_handle(&domain_handle); + } + + (void) samr_close_handle(&samr_handle); + mlsvc_free_user_info(user_info); + return (status); +} + +/* + * sam_lookup_name + * + * Lookup an account name in the SAM database on the specified domain + * controller. Provides the account RID on success. + * + * Returns NT status codes. + */ +DWORD +sam_lookup_name(char *server, char *domain_name, char *account_name, + DWORD *rid_ret) +{ + mlsvc_handle_t samr_handle; + mlsvc_handle_t domain_handle; + smb_userinfo_t *user_info; + struct samr_sid *domain_sid; + int rc; + DWORD status; + + *rid_ret = 0; + + if ((user_info = mlsvc_alloc_user_info()) == 0) + return (NT_STATUS_NO_MEMORY); + + rc = samr_open(MLSVC_IPC_ANON, server, domain_name, 0, 0, + SAM_LOOKUP_INFORMATION, &samr_handle); + + if (rc != 0) { + mlsvc_free_user_info(user_info); + return (NT_STATUS_OPEN_FAILED); + } + + rc = samr_lookup_domain(&samr_handle, domain_name, user_info); + if (rc != 0) { + (void) samr_close_handle(&samr_handle); + mlsvc_free_user_info(user_info); + return (NT_STATUS_NO_SUCH_DOMAIN); + } + + domain_sid = (struct samr_sid *)user_info->domain_sid; + + status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, + domain_sid, &domain_handle); + if (status == 0) { + mlsvc_release_user_info(user_info); + + status = samr_lookup_domain_names(&domain_handle, + account_name, user_info); + if (status == 0) + *rid_ret = user_info->rid; + + (void) samr_close_handle(&domain_handle); + } + + (void) samr_close_handle(&samr_handle); + mlsvc_free_user_info(user_info); + return (status); +} + + +/* + * sam_get_local_domains + * + * Query a remote server to get the list of local domains that it + * supports. + * + * Returns NT status codes. + */ +DWORD +sam_get_local_domains(char *server, char *domain_name) +{ + mlsvc_handle_t samr_handle; + DWORD status; + int rc; + + rc = samr_open(MLSVC_IPC_ANON, server, domain_name, 0, 0, + SAM_ENUM_LOCAL_DOMAIN, &samr_handle); + if (rc != 0) + return (NT_STATUS_OPEN_FAILED); + + status = samr_enum_local_domains(&samr_handle); + (void) samr_close_handle(&samr_handle); + return (status); +} + +/* + * sam_oem_password + * + * Generate an OEM password. + */ +int sam_oem_password(oem_password_t *oem_password, unsigned char *new_password, + unsigned char *old_password) +{ + mts_wchar_t *unicode_password; + int length; + +#ifdef PBSHORTCUT + assert(sizeof (oem_password_t) == SAM_PASSWORD_516); +#endif /* PBSHORTCUT */ + + length = strlen((char const *)new_password); + unicode_password = alloca((length + 1) * sizeof (mts_wchar_t)); + + length = smb_auth_qnd_unicode((unsigned short *)unicode_password, + (char *)new_password, length); + oem_password->length = length; + + (void) memcpy(&oem_password->data[512 - length], + unicode_password, length); + + rand_hash((unsigned char *)oem_password, sizeof (oem_password_t), + old_password, SAM_KEYLEN); + + return (0); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/samr_lookup.c b/usr/src/lib/smbsrv/libmlsvc/common/samr_lookup.c new file mode 100644 index 000000000000..7eccd83e222c --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/samr_lookup.c @@ -0,0 +1,565 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Security Access Manager RPC (SAMR) library interface functions for + * query and lookup calls. + */ + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int samr_setup_user_info(WORD, struct samr_QueryUserInfo *, + union samr_user_info *); +static void samr_set_user_unknowns(struct samr_SetUserInfo23 *); +static void samr_set_user_logon_hours(struct samr_SetUserInfo *); +static int samr_set_user_password(smb_auth_info_t *, BYTE *); + +/* + * samr_lookup_domain + * + * Lookup up the domain SID for the specified domain name. The handle + * should be one returned from samr_connect. The results will be + * returned in user_info - which should have been allocated by the + * caller. On success sid_name_use will be set to SidTypeDomain. + * + * Returns 0 on success, otherwise returns -ve error code. + */ +int +samr_lookup_domain(mlsvc_handle_t *samr_handle, char *domain_name, + smb_userinfo_t *user_info) +{ + struct samr_LookupDomain arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + int rc; + size_t length; + + if (mlsvc_is_null_handle(samr_handle) || + domain_name == NULL || user_info == NULL) { + return (-1); + } + + context = samr_handle->context; + opnum = SAMR_OPNUM_LookupDomain; + bzero(&arg, sizeof (struct samr_LookupDomain)); + + (void) memcpy(&arg.handle, &samr_handle->handle, + sizeof (samr_handle_t)); + + length = mts_wcequiv_strlen(domain_name); + if (context->server_os == NATIVE_OS_WIN2000) + length += sizeof (mts_wchar_t); + + arg.domain_name.length = length; + arg.domain_name.allosize = length; + arg.domain_name.str = (unsigned char *)domain_name; + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(context, opnum, &arg, &heap); + if (rc == 0) { + user_info->sid_name_use = SidTypeDomain; + user_info->domain_sid = nt_sid_dup((nt_sid_t *)arg.sid); + user_info->domain_name = MEM_STRDUP("mlrpc", domain_name); + } + + mlsvc_rpc_free(context, &heap); + return (rc); +} + +/* + * samr_enum_local_domains + * + * Get the list of local domains supported by a server. + * + * Returns NT status codes. + */ +DWORD +samr_enum_local_domains(mlsvc_handle_t *samr_handle) +{ + struct samr_EnumLocalDomain arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + DWORD status; + + if (mlsvc_is_null_handle(samr_handle)) + return (NT_STATUS_INVALID_PARAMETER); + + context = samr_handle->context; + opnum = SAMR_OPNUM_EnumLocalDomains; + bzero(&arg, sizeof (struct samr_EnumLocalDomain)); + + (void) memcpy(&arg.handle, &samr_handle->handle, + sizeof (samr_handle_t)); + arg.enum_context = 0; + arg.max_length = 0x00002000; /* Value used by NT */ + + (void) mlsvc_rpc_init(&heap); + if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) { + status = NT_STATUS_INVALID_PARAMETER; + } else { + status = NT_SC_VALUE(arg.status); + + /* + * Handle none-mapped status quietly. + */ + if (status != NT_STATUS_NONE_MAPPED) + mlsvc_rpc_report_status(opnum, arg.status); + } + + return (status); +} + +/* + * samr_lookup_domain_names + * + * Lookup up a name + * returned in user_info - which should have been allocated by the + * caller. On success sid_name_use will be set to SidTypeDomain. + * + * Returns 0 on success. Otherwise returns an NT status code. + */ +DWORD +samr_lookup_domain_names(mlsvc_handle_t *domain_handle, char *name, + smb_userinfo_t *user_info) +{ + struct samr_LookupNames arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + DWORD status; + size_t length; + + if (mlsvc_is_null_handle(domain_handle) || + name == NULL || user_info == NULL) { + return (NT_STATUS_INVALID_PARAMETER); + } + + context = domain_handle->context; + opnum = SAMR_OPNUM_LookupNames; + bzero(&arg, sizeof (struct samr_LookupNames)); + + (void) memcpy(&arg.handle, &domain_handle->handle, + sizeof (samr_handle_t)); + arg.n_entry = 1; + arg.max_n_entry = 1000; + arg.index = 0; + arg.total = 1; + + length = mts_wcequiv_strlen(name); + if (context->server_os == NATIVE_OS_WIN2000) + length += sizeof (mts_wchar_t); + + arg.name.length = length; + arg.name.allosize = length; + arg.name.str = (unsigned char *)name; + + (void) mlsvc_rpc_init(&heap); + if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) { + status = NT_STATUS_INVALID_PARAMETER; + } else if (arg.status != 0) { + status = NT_SC_VALUE(arg.status); + + /* + * Handle none-mapped status quietly. + */ + if (status != NT_STATUS_NONE_MAPPED) + mlsvc_rpc_report_status(opnum, arg.status); + } else { + user_info->name = MEM_STRDUP("mlrpc", name); + user_info->sid_name_use = arg.rid_types.rid_type[0]; + user_info->rid = arg.rids.rid[0]; + status = 0; + } + + mlsvc_rpc_free(context, &heap); + return (status); +} + +/* + * samr_query_user_info + * + * Query information on a specific user. The handle must be a valid + * user handle obtained via samr_open_user. + * + * Returns 0 on success, otherwise returns -ve error code. + */ +int +samr_query_user_info(mlsvc_handle_t *user_handle, WORD switch_value, + union samr_user_info *user_info) +{ + struct samr_QueryUserInfo arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + int rc; + + if (mlsvc_is_null_handle(user_handle) || user_info == 0) + return (-1); + + context = user_handle->context; + opnum = SAMR_OPNUM_QueryUserInfo; + bzero(&arg, sizeof (struct samr_QueryUserInfo)); + + (void) memcpy(&arg.user_handle, &user_handle->handle, + sizeof (samr_handle_t)); + arg.switch_value = switch_value; + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(context, opnum, &arg, &heap); + if (rc == 0) { + if (arg.status != 0) + rc = -1; + else + rc = samr_setup_user_info(switch_value, &arg, + user_info); + } + + mlsvc_rpc_free(context, &heap); + return (rc); +} + + +/* + * samr_setup_user_info + * + * Private function to set up the samr_user_info data. Dependent on + * the switch value this function may use strdup which will malloc + * memory. The caller is responsible for deallocating this memory. + * + * Returns 0 on success, otherwise returns -1. + */ +static int samr_setup_user_info(WORD switch_value, + struct samr_QueryUserInfo *arg, union samr_user_info *user_info) +{ + struct samr_QueryUserInfo1 *info1; + struct samr_QueryUserInfo6 *info6; + + switch (switch_value) { + case 1: + info1 = &arg->ru.info1; + user_info->info1.username = strdup( + (char const *)info1->username.str); + user_info->info1.fullname = strdup( + (char const *)info1->fullname.str); + user_info->info1.description = strdup( + (char const *)info1->description.str); + user_info->info1.unknown = 0; + user_info->info1.group_rid = info1->group_rid; + return (0); + + case 6: + info6 = &arg->ru.info6; + user_info->info6.username = strdup( + (char const *)info6->username.str); + user_info->info6.fullname = strdup( + (char const *)info6->fullname.str); + return (0); + + case 7: + user_info->info7.username = strdup( + (char const *)arg->ru.info7.username.str); + return (0); + + case 8: + user_info->info8.fullname = strdup( + (char const *)arg->ru.info8.fullname.str); + return (0); + + case 9: + user_info->info9.group_rid = arg->ru.info9.group_rid; + return (0); + + case 16: + return (0); + + default: + break; + }; + + return (-1); +} + +/* + * samr_query_user_groups + * + * Query the groups for a specific user. The handle must be a valid + * user handle obtained via samr_open_user. The list of groups is + * returned in group_info. Note that group_info->groups is allocated + * using malloc. The caller is responsible for deallocating this + * memory when it is no longer required. If group_info->n_entry is 0 + * then no memory was allocated. + * + * Returns 0 on success, otherwise returns -1. + */ +int +samr_query_user_groups(mlsvc_handle_t *user_handle, smb_userinfo_t *user_info) +{ + struct samr_QueryUserGroups arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + int rc; + int nbytes; + + if (mlsvc_is_null_handle(user_handle) || user_info == NULL) + return (-1); + + context = user_handle->context; + opnum = SAMR_OPNUM_QueryUserGroups; + bzero(&arg, sizeof (struct samr_QueryUserGroups)); + + (void) memcpy(&arg.user_handle, &user_handle->handle, + sizeof (samr_handle_t)); + + (void) mlsvc_rpc_init(&heap); + + rc = mlsvc_rpc_call(context, opnum, &arg, &heap); + if (rc == 0) { + if (arg.info == 0) { + rc = -1; + } else { + nbytes = arg.info->n_entry * + sizeof (struct samr_UserGroups); + user_info->groups = malloc(nbytes); + + if (user_info->groups == NULL) { + user_info->n_groups = 0; + rc = -1; + } else { + user_info->n_groups = arg.info->n_entry; + (void) memcpy(user_info->groups, + arg.info->groups, nbytes); + } + } + } + mlsvc_rpc_free(context, &heap); + return (rc); +} + + +/* + * samr_get_user_pwinfo + * + * Get some user password info. I'm not sure what this is yet but it is + * part of the create user sequence. The handle must be a valid user + * handle. Since I don't know what this is returning, I haven't provided + * any return data yet. + * + * Returns 0 on success. Otherwise returns an NT status code. + */ +DWORD +samr_get_user_pwinfo(mlsvc_handle_t *user_handle) +{ + struct samr_GetUserPwInfo arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + DWORD status; + + if (mlsvc_is_null_handle(user_handle)) + return (NT_STATUS_INVALID_PARAMETER); + + context = user_handle->context; + opnum = SAMR_OPNUM_GetUserPwInfo; + bzero(&arg, sizeof (struct samr_GetUserPwInfo)); + (void) memcpy(&arg.user_handle, &user_handle->handle, + sizeof (samr_handle_t)); + + (void) mlsvc_rpc_init(&heap); + if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) { + status = NT_STATUS_INVALID_PARAMETER; + } else if (arg.status != 0) { + mlsvc_rpc_report_status(opnum, arg.status); + status = NT_SC_VALUE(arg.status); + } else { + status = 0; + } + + mlsvc_rpc_free(context, &heap); + return (status); +} + + +/* + * samr_set_user_info + * + * Returns 0 on success. Otherwise returns an NT status code. + * NT status codes observed so far: + * NT_STATUS_WRONG_PASSWORD + */ +/*ARGSUSED*/ +DWORD +samr_set_user_info(mlsvc_handle_t *user_handle, smb_auth_info_t *auth) +{ + struct samr_SetUserInfo arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + DWORD status = 0; + + if (mlsvc_is_null_handle(user_handle)) + return (NT_STATUS_INVALID_PARAMETER); + + (void) mlsvc_rpc_init(&heap); + + context = user_handle->context; + opnum = SAMR_OPNUM_SetUserInfo; + bzero(&arg, sizeof (struct samr_SetUserInfo)); + (void) memcpy(&arg.user_handle, &user_handle->handle, + sizeof (samr_handle_t)); + + arg.info.index = SAMR_SET_USER_INFO_23; + arg.info.switch_value = SAMR_SET_USER_INFO_23; + + samr_set_user_unknowns(&arg.info.ru.info23); + samr_set_user_logon_hours(&arg); + + if (samr_set_user_password(auth, arg.info.ru.info23.password) < 0) + status = NT_STATUS_INTERNAL_ERROR; + + if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) { + status = NT_STATUS_INVALID_PARAMETER; + } else if (arg.status != 0) { + mlsvc_rpc_report_status(opnum, arg.status); + status = NT_SC_VALUE(arg.status); + } + + mlsvc_rpc_free(context, &heap); + return (status); +} + +static void +samr_set_user_unknowns(struct samr_SetUserInfo23 *info) +{ + bzero(info, sizeof (struct samr_SetUserInfo23)); + + info->sd.length = 0; + info->sd.data = 0; + info->user_rid = 0; + info->group_rid = MLSVC_DOMAIN_GROUP_RID_USERS; + + /* + * The trust account value used here should probably + * match the one used to create the trust account. + */ + info->acct_info = SAMR_AF_WORKSTATION_TRUST_ACCOUNT; + info->flags = 0x09F827FA; +} + +/* + * samr_set_user_logon_hours + * + * SamrSetUserInfo appears to contain some logon hours information, which + * looks like a varying, conformant array. The top level contains a value + * (units), which probably indicates the how to interpret the array. The + * array definition looks like it contains a maximum size, an initial + * offset and a bit length (units/8), followed by the bitmap. + * + * (info) + * +-------+ + * | units | + * +-------+ (hours) + * | hours |-->+-----------+ + * +-------+ | max_is | + * +-----------+ + * | first_is | + * +-----------+ + * | length_is | + * +------------------------+ + * | bitmap[length_is] | + * +---------+--------------+ + * + * 10080 minutes/week => 10080/8 = 1260 (0x04EC) bytes. + * 168 hours/week => 168/8 = 21 (0xA8) bytes. + * In the netmon examples seen so far, all bits are set to 1, i.e. + * an array containing 0xff. This is probably the default setting. + * + * ndrgen has a problem with complex [size_is] statements (length/8). + * So, for now, we fake it using two separate components. + */ +static void +samr_set_user_logon_hours(struct samr_SetUserInfo *sui) +{ + sui->logon_hours.size = SAMR_HOURS_MAX_SIZE; + sui->logon_hours.first = 0; + sui->logon_hours.length = SAMR_SET_USER_HOURS_SZ; + (void) memset(sui->logon_hours.bitmap, 0xFF, SAMR_SET_USER_HOURS_SZ); + + sui->info.ru.info23.logon_info.units = SAMR_HOURS_PER_WEEK; + sui->info.ru.info23.logon_info.hours = (DWORD)sui->logon_hours.bitmap; +} + +/* + * samr_set_user_password + * + * Set the initial password for the user. + * + * Returns 0 if everything goes well, -1 if there is trouble generating a + * key. + */ +static int +samr_set_user_password(smb_auth_info_t *auth, BYTE *oem_password) +{ + unsigned char nt_key[SMBAUTH_SESSION_KEY_SZ]; + char hostname[64]; + + randomize((char *)oem_password, SAMR_SET_USER_DATA_SZ); + + /* + * The new password is going to be + * the hostname in lower case. + */ + if (smb_gethostname(hostname, 64, 0) != 0) + return (-1); + + (void) utf8_strlwr(hostname); + + if (smb_auth_gen_session_key(auth, nt_key) != SMBAUTH_SUCCESS) + return (-1); + + /* + * Generate the OEM password from the hostname and the user session + * key(nt_key). + */ + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + (void) sam_oem_password((oem_password_t *)oem_password, + (unsigned char *)hostname, nt_key); + return (0); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/samr_open.c b/usr/src/lib/smbsrv/libmlsvc/common/samr_open.c new file mode 100644 index 000000000000..38dca838cdb9 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/samr_open.c @@ -0,0 +1,684 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Security Access Manager RPC (SAMR) library interface functions for + * connect, open and close calls. The SAM is a hierarchical database. + * If you want to talk to the SAM you need a SAM handle, if you want + * to work with a domain, you need to use the SAM handle to obtain a + * domain handle. Then you can use the domain handle to obtain a user + * handle etc. Be careful about returning null handles to the + * application. Use of a null handle may crash the domain controller + * if you attempt to use it. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/*LINTED E_STATIC_UNUSED*/ +static DWORD samr_connect1(char *, char *, char *, DWORD, mlsvc_handle_t *); +static DWORD samr_connect2(char *, char *, char *, DWORD, mlsvc_handle_t *); +static DWORD samr_connect3(char *, char *, char *, DWORD, mlsvc_handle_t *); +static DWORD samr_connect4(char *, char *, char *, DWORD, mlsvc_handle_t *); + +/* + * samr_open + * + * This is a wrapper round samr_connect to ensure that we connect using + * the appropriate session and logon. We default to the resource domain + * information if the caller doesn't supply a server name and a domain + * name. We store the remote server's native OS type - we may need it + * due to differences between platforms like NT and Windows 2000. + * + * On success 0 is returned. Otherwise a -ve error code. + */ +int +samr_open(int ipc_mode, char *server, char *domain, char *username, + char *password, DWORD access_mask, mlsvc_handle_t *samr_handle) +{ + smb_ntdomain_t *di; + int remote_os; + int remote_lm; + int rc; + + if ((di = smb_getdomaininfo(0)) == NULL) + return (-1); + + if (server == NULL || domain == NULL) { + server = di->server; + domain = di->domain; + } + + switch (ipc_mode) { + case MLSVC_IPC_USER: + /* + * Use the supplied credentials. + */ + rc = mlsvc_user_logon(server, domain, username, password); + break; + + case MLSVC_IPC_ADMIN: + /* + * Use the resource domain administrator credentials. + */ + server = di->server; + domain = di->domain; + username = smbrdr_ipc_get_user(); + + rc = mlsvc_admin_logon(server, domain); + break; + + case MLSVC_IPC_ANON: + default: + rc = mlsvc_anonymous_logon(server, domain, &username); + break; + } + + if (rc != 0) + return (-1); + + rc = samr_connect(server, domain, username, access_mask, samr_handle); + if (rc == 0) { + (void) mlsvc_session_native_values(samr_handle->context->fid, + &remote_os, &remote_lm, 0); + samr_handle->context->server_os = remote_os; + } + return (rc); +} + + +/* + * samr_connect + * + * Connect to the SAM on the specified server (domain controller). + * This is the entry point for the various SAM connect calls. We do + * parameter validation and open the samr named pipe here. The actual + * RPC is based on the native OS of the server. + * + * Returns 0 on success. Otherwise returns a -ve error code. + */ +int +samr_connect(char *server, char *domain, char *username, DWORD access_mask, + mlsvc_handle_t *samr_handle) +{ + DWORD status; + int remote_os; + int remote_lm; + int fid; + int rc = 0; + + if (server == NULL || domain == NULL || + username == NULL || samr_handle == NULL) + return (-1); + + if ((fid = mlsvc_open_pipe(server, domain, username, "\\samr")) < 0) + return (-1); + + if (mlsvc_rpc_bind(samr_handle, fid, "SAMR") < 0) { + (void) mlsvc_close_pipe(fid); + return (-1); + } + + (void) mlsvc_session_native_values(fid, &remote_os, &remote_lm, 0); + + switch (remote_os) { + case NATIVE_OS_NT5_1: + status = samr_connect4(server, domain, username, access_mask, + samr_handle); + break; + + case NATIVE_OS_NT5_0: + status = samr_connect3(server, domain, username, access_mask, + samr_handle); + break; + + case NATIVE_OS_NT4_0: + default: + status = samr_connect2(server, domain, username, access_mask, + samr_handle); + break; + } + + if (status != NT_STATUS_SUCCESS) { + (void) mlsvc_close_pipe(fid); + free(samr_handle->context); + rc = -1; + } + return (rc); +} + +/* + * samr_connect1 + * + * Original SAMR connect call; probably used on Windows NT 3.51. + * Windows 95 uses this call with the srvmgr tools update. + * Servername appears to be a dword rather than a string. + * The first word contains '\' and the second word contains 0x001, + * (which is probably uninitialized junk: 0x0001005c. + */ +/*ARGSUSED*/ +static DWORD +samr_connect1(char *server, char *domain, char *username, DWORD access_mask, + mlsvc_handle_t *samr_handle) +{ + struct samr_ConnectAnon arg; + mlrpc_heapref_t heapref; + int opnum; + DWORD status; + + bzero(&arg, sizeof (struct samr_ConnectAnon)); + opnum = SAMR_OPNUM_ConnectAnon; + status = NT_STATUS_SUCCESS; + + (void) mlsvc_rpc_init(&heapref); + arg.servername = (DWORD *)mlrpc_heap_malloc(heapref.heap, + sizeof (DWORD)); + *(arg.servername) = 0x0001005c; + arg.access_mask = access_mask; + + if (mlsvc_rpc_call(samr_handle->context, opnum, &arg, &heapref) != 0) { + status = NT_STATUS_UNSUCCESSFUL; + } else if (arg.status != 0) { + status = NT_SC_VALUE(arg.status); + } else { + (void) memcpy(&samr_handle->handle, &arg.handle, + sizeof (ms_handle_t)); + + if (mlsvc_is_null_handle(samr_handle)) + status = NT_STATUS_INVALID_HANDLE; + } + + mlsvc_rpc_free(samr_handle->context, &heapref); + return (status); +} + +/* + * samr_connect2 + * + * Connect to the SAM on a Windows NT 4.0 server (domain controller). + * We need the domain controller name and, if everything works, we + * return a handle. This function adds the double backslash prefx to + * make it easy for applications. + * + * Returns 0 on success. Otherwise returns a -ve error code. + */ +/*ARGSUSED*/ +static DWORD +samr_connect2(char *server, char *domain, char *username, DWORD access_mask, + mlsvc_handle_t *samr_handle) +{ + struct samr_Connect arg; + mlrpc_heapref_t heapref; + int opnum; + DWORD status; + int len; + + bzero(&arg, sizeof (struct samr_Connect)); + opnum = SAMR_OPNUM_Connect; + status = NT_STATUS_SUCCESS; + + (void) mlsvc_rpc_init(&heapref); + len = strlen(server) + 4; + arg.servername = mlrpc_heap_malloc(heapref.heap, len); + (void) snprintf((char *)arg.servername, len, "\\\\%s", server); + arg.access_mask = access_mask; + + if (mlsvc_rpc_call(samr_handle->context, opnum, &arg, &heapref) != 0) { + status = NT_STATUS_UNSUCCESSFUL; + } else if (arg.status != 0) { + status = NT_SC_VALUE(arg.status); + } else { + (void) memcpy(&samr_handle->handle, &arg.handle, + sizeof (ms_handle_t)); + + if (mlsvc_is_null_handle(samr_handle)) + status = NT_STATUS_INVALID_HANDLE; + } + + mlsvc_rpc_free(samr_handle->context, &heapref); + return (status); +} + +/* + * samr_connect3 + * + * Connect to the SAM on a Windows 2000 domain controller. + */ +/*ARGSUSED*/ +static DWORD +samr_connect3(char *server, char *domain, char *username, DWORD access_mask, + mlsvc_handle_t *samr_handle) +{ + struct samr_Connect3 arg; + mlrpc_heapref_t heapref; + int opnum; + DWORD status; + int len; + + bzero(&arg, sizeof (struct samr_Connect3)); + opnum = SAMR_OPNUM_Connect3; + status = NT_STATUS_SUCCESS; + + (void) mlsvc_rpc_init(&heapref); + len = strlen(server) + 4; + arg.servername = mlrpc_heap_malloc(heapref.heap, len); + (void) snprintf((char *)arg.servername, len, "\\\\%s", server); + arg.unknown_02 = 0x00000002; + arg.access_mask = access_mask; + + if (mlsvc_rpc_call(samr_handle->context, opnum, &arg, &heapref) != 0) { + status = NT_STATUS_UNSUCCESSFUL; + } else if (arg.status != 0) { + status = NT_SC_VALUE(arg.status); + } else { + (void) memcpy(&samr_handle->handle, &arg.handle, + sizeof (ms_handle_t)); + + if (mlsvc_is_null_handle(samr_handle)) + status = NT_STATUS_INVALID_HANDLE; + } + + mlsvc_rpc_free(samr_handle->context, &heapref); + return (status); +} + +/* + * samr_connect4 + * + * Connect to the SAM on a Windows XP domain controller. On Windows + * XP, the server should be the fully qualified DNS domain name with + * a double backslash prefix. At this point, it is assumed that we + * need to add the prefix and the DNS domain name here. + * + * If this call succeeds, a SAMR handle is placed in samr_handle and + * zero is returned. Otherwise, a -ve error code is returned. + */ +/*ARGSUSED*/ +static DWORD +samr_connect4(char *server, char *domain, char *username, DWORD access_mask, + mlsvc_handle_t *samr_handle) +{ + struct samr_Connect4 arg; + mlrpc_heapref_t heapref; + char *dns_name; + int len; + int opnum; + DWORD status; + + bzero(&arg, sizeof (struct samr_Connect4)); + opnum = SAMR_OPNUM_Connect; + status = NT_STATUS_SUCCESS; + + (void) mlsvc_rpc_init(&heapref); + dns_name = mlrpc_heap_malloc(heapref.heap, MAXHOSTNAMELEN); + (void) smb_getdomainname(dns_name, MAXHOSTNAMELEN); + + if (strlen(dns_name) > 0) { + len = strlen(server) + strlen(dns_name) + 4; + arg.servername = mlrpc_heap_malloc(heapref.heap, len); + (void) snprintf((char *)arg.servername, len, "\\\\%s.%s", + server, dns_name); + } else { + len = strlen(server) + 4; + arg.servername = mlrpc_heap_malloc(heapref.heap, len); + (void) snprintf((char *)arg.servername, len, "\\\\%s", server); + } + + arg.access_mask = SAM_ENUM_LOCAL_DOMAIN; + arg.unknown2_00000001 = 0x00000001; + arg.unknown3_00000001 = 0x00000001; + arg.unknown4_00000003 = 0x00000003; + arg.unknown5_00000000 = 0x00000000; + + if (mlsvc_rpc_call(samr_handle->context, opnum, &arg, &heapref) != 0) { + status = NT_STATUS_UNSUCCESSFUL; + } else if (arg.status != 0) { + status = NT_SC_VALUE(arg.status); + } else { + + (void) memcpy(&samr_handle->handle, &arg.handle, + sizeof (ms_handle_t)); + + if (mlsvc_is_null_handle(samr_handle)) + status = NT_STATUS_INVALID_HANDLE; + } + + mlsvc_rpc_free(samr_handle->context, &heapref); + + return (status); +} + + +/* + * samr_close_handle + * + * This is function closes any valid handle, i.e. sam, domain, user etc. + * Just to be safe we check for, and reject, null handles. The handle + * returned by the SAM server is all null. If the handle being closed is + * the top level connect handle, we also close the pipe. Then we zero + * out the handle to invalidate it. Things go badly if you attempt to + * use an invalid handle, i.e. the DC crashes. + */ +int +samr_close_handle(mlsvc_handle_t *desc) +{ + struct samr_CloseHandle arg; + mlrpc_heapref_t heap; + int opnum; + int rc; + + if (mlsvc_is_null_handle(desc)) + return (-1); + + opnum = SAMR_OPNUM_CloseHandle; + bzero(&arg, sizeof (struct samr_CloseHandle)); + (void) memcpy(&arg.handle, &desc->handle, sizeof (ms_handle_t)); + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(desc->context, opnum, &arg, &heap); + mlsvc_rpc_free(desc->context, &heap); + + if (desc->context->handle == &desc->handle) { + (void) mlsvc_close_pipe(desc->context->fid); + free(desc->context); + } + + bzero(desc, sizeof (mlsvc_handle_t)); + return (rc); +} + +/* + * samr_open_domain + * + * We use a SAM handle to obtain a handle for a domain, specified by + * the SID. The SID can be obtain via the LSA interface. A handle for + * the domain is returned in domain_handle. + */ +DWORD +samr_open_domain(mlsvc_handle_t *samr_handle, DWORD access_mask, + struct samr_sid *sid, mlsvc_handle_t *domain_handle) +{ + struct samr_OpenDomain arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + DWORD status; + + if (mlsvc_is_null_handle(samr_handle) || + sid == 0 || domain_handle == 0) { + return (NT_STATUS_INVALID_PARAMETER); + } + + context = samr_handle->context; + opnum = SAMR_OPNUM_OpenDomain; + bzero(&arg, sizeof (struct samr_OpenDomain)); + (void) memcpy(&arg.handle, &samr_handle->handle, sizeof (ms_handle_t)); + + arg.access_mask = access_mask; + arg.sid = sid; + + (void) mlsvc_rpc_init(&heap); + if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) { + status = NT_STATUS_UNSUCCESSFUL; + } else if (arg.status != 0) { + status = arg.status; + } else { + status = NT_STATUS_SUCCESS; + (void) memcpy(&domain_handle->handle, &arg.domain_handle, + sizeof (ms_handle_t)); + domain_handle->context = context; + if (mlsvc_is_null_handle(domain_handle)) + status = NT_STATUS_INVALID_HANDLE; + } + + if (status != NT_STATUS_SUCCESS) + mlsvc_rpc_report_status(opnum, status); + + mlsvc_rpc_free(context, &heap); + return (status); +} + +/* + * samr_open_user + * + * Use a domain handle to obtain a handle for a user, specified by the + * user RID. A user RID (effectively a uid) can be obtained via the + * LSA interface. A handle for the user is returned in user_handle. + * Once you have a user handle it should be possible to query the SAM + * for information on that user. + */ +int +samr_open_user(mlsvc_handle_t *domain_handle, DWORD access_mask, DWORD rid, + mlsvc_handle_t *user_handle) +{ + struct samr_OpenUser arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + int rc; + + if (mlsvc_is_null_handle(domain_handle) || user_handle == NULL) + return (-1); + + context = domain_handle->context; + opnum = SAMR_OPNUM_OpenUser; + bzero(&arg, sizeof (struct samr_OpenUser)); + (void) memcpy(&arg.handle, &domain_handle->handle, + sizeof (ms_handle_t)); + arg.access_mask = access_mask; + arg.rid = rid; + + (void) mlsvc_rpc_init(&heap); + rc = mlsvc_rpc_call(context, opnum, &arg, &heap); + if (rc == 0) { + if (arg.status != 0) { + mlsvc_rpc_report_status(opnum, arg.status); + rc = -1; + } else { + (void) memcpy(&user_handle->handle, &arg.user_handle, + sizeof (ms_handle_t)); + user_handle->context = context; + + if (mlsvc_is_null_handle(user_handle)) + rc = -1; + } + } + + mlsvc_rpc_free(context, &heap); + return (rc); +} + +/* + * samr_delete_user + * + * Delete the user specified by the user_handle. + */ +DWORD +samr_delete_user(mlsvc_handle_t *user_handle) +{ + struct samr_DeleteUser arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + DWORD status; + + if (mlsvc_is_null_handle(user_handle)) + return (NT_STATUS_INVALID_PARAMETER); + + context = user_handle->context; + opnum = SAMR_OPNUM_DeleteUser; + bzero(&arg, sizeof (struct samr_DeleteUser)); + (void) memcpy(&arg.user_handle, &user_handle->handle, + sizeof (ms_handle_t)); + + (void) mlsvc_rpc_init(&heap); + if (mlsvc_rpc_call(context, opnum, &arg, &heap) != 0) { + status = NT_STATUS_INVALID_PARAMETER; + } else if (arg.status != 0) { + mlsvc_rpc_report_status(opnum, arg.status); + status = NT_SC_VALUE(arg.status); + } else { + status = 0; + } + + mlsvc_rpc_free(context, &heap); + return (status); +} + +/* + * samr_open_group + * + * Use a domain handle to obtain a handle for a group, specified by the + * group RID. A group RID (effectively a gid) can be obtained via the + * LSA interface. A handle for the group is returned in group_handle. + * Once you have a group handle it should be possible to query the SAM + * for information on that group. + */ +int +samr_open_group( + mlsvc_handle_t *domain_handle, + DWORD rid, + mlsvc_handle_t *group_handle) +{ + struct samr_OpenGroup arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + int rc; + + if (mlsvc_is_null_handle(domain_handle) || group_handle == 0) + return (-1); + + context = domain_handle->context; + opnum = SAMR_OPNUM_OpenGroup; + bzero(&arg, sizeof (struct samr_OpenUser)); + (void) memcpy(&arg.handle, &domain_handle->handle, + sizeof (ms_handle_t)); + arg.access_mask = SAM_LOOKUP_INFORMATION | SAM_ACCESS_USER_READ; + arg.rid = rid; + + (void) mlsvc_rpc_init(&heap); + + rc = mlsvc_rpc_call(context, opnum, &arg, &heap); + if (rc == 0) { + if (arg.status != 0) { + mlsvc_rpc_report_status(opnum, arg.status); + rc = -1; + } else { + (void) memcpy(&group_handle->handle, &arg.group_handle, + sizeof (ms_handle_t)); + group_handle->context = context; + if (mlsvc_is_null_handle(group_handle)) + rc = -1; + } + } + + mlsvc_rpc_free(context, &heap); + return (rc); +} + +/* + * samr_create_user + * + * Create a user in the domain specified by the domain handle. If this + * call is successful, the server will return the RID for the user and + * a user handle, which may be used to set or query the SAM. + * + * Observed status codes: + * NT_STATUS_INVALID_PARAMETER + * NT_STATUS_INVALID_ACCOUNT_NAME + * NT_STATUS_ACCESS_DENIED + * NT_STATUS_USER_EXISTS + * + * Returns 0 on success. Otherwise returns an NT status code. + */ +DWORD +samr_create_user(mlsvc_handle_t *domain_handle, char *username, + DWORD account_flags, DWORD *rid, mlsvc_handle_t *user_handle) +{ + struct samr_CreateUser arg; + struct mlsvc_rpc_context *context; + mlrpc_heapref_t heap; + int opnum; + int rc; + DWORD status = 0; + + if (mlsvc_is_null_handle(domain_handle) || + username == NULL || rid == NULL) { + return (NT_STATUS_INVALID_PARAMETER); + } + + context = domain_handle->context; + opnum = SAMR_OPNUM_CreateUser; + + bzero(&arg, sizeof (struct samr_CreateUser)); + (void) memcpy(&arg.handle, &domain_handle->handle, + sizeof (ms_handle_t)); + + (void) mlsvc_rpc_init(&heap); + mlrpc_heap_mkvcs(heap.heap, username, (mlrpc_vcbuf_t *)&arg.username); + + arg.account_flags = account_flags; + arg.unknown_e00500b0 = 0xE00500B0; + + rc = mlsvc_rpc_call(context, opnum, &arg, &heap); + if (rc != 0) { + status = NT_STATUS_INVALID_PARAMETER; + } else if (arg.status != 0) { + status = NT_SC_VALUE(arg.status); + + if (status != NT_STATUS_USER_EXISTS) { + smb_tracef("SamrCreateUser[%s]: %s", username, + xlate_nt_status(status)); + } + } else { + (void) memcpy(&user_handle->handle, &arg.user_handle, + sizeof (ms_handle_t)); + user_handle->context = context; + *rid = arg.rid; + + if (mlsvc_is_null_handle(user_handle)) + status = NT_STATUS_INVALID_HANDLE; + else + status = 0; + } + + mlsvc_rpc_free(context, &heap); + return (status); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/secdb.c b/usr/src/lib/smbsrv/libmlsvc/common/secdb.c new file mode 100644 index 000000000000..cc4d96456f9d --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/secdb.c @@ -0,0 +1,932 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Security database interface. + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +extern uint32_t netlogon_logon(netr_client_t *clnt, smb_userinfo_t *uinfo); +static uint32_t smb_logon_domain(netr_client_t *clnt, smb_userinfo_t *uinfo); +static uint32_t smb_logon_local(netr_client_t *clnt, smb_userinfo_t *uinfo); +static uint32_t smb_logon_none(netr_client_t *clnt, smb_userinfo_t *uinfo); + +static uint32_t smb_setup_luinfo(smb_userinfo_t *, netr_client_t *, uid_t); + +static int smb_token_is_member(smb_token_t *token, nt_sid_t *sid); +static int smb_token_is_valid(smb_token_t *token); +static smb_win_grps_t *smb_token_create_wingrps(smb_userinfo_t *user_info); + +static smb_posix_grps_t *smb_token_create_pxgrps(uid_t uid); + +/* Consolidation private function from Network Repository */ +extern int _getgroupsbymember(const char *, gid_t[], int, int); + +static idmap_stat +smb_token_idmap(smb_token_t *token, smb_idmap_batch_t *sib) +{ + idmap_stat stat; + smb_idmap_t *sim; + smb_id_t *id; + int i; + + if (!token || !sib) + return (IDMAP_ERR_ARG); + + sim = sib->sib_maps; + + if (token->tkn_flags & SMB_ATF_ANON) { + token->tkn_user->i_id = UID_NOBODY; + token->tkn_owner->i_id = UID_NOBODY; + } else { + /* User SID */ + id = token->tkn_user; + sim->sim_id = &id->i_id; + stat = smb_idmap_batch_getid(sib->sib_idmaph, sim++, + id->i_sidattr.sid, SMB_IDMAP_USER); + + if (stat != IDMAP_SUCCESS) + return (stat); + + /* Owner SID */ + id = token->tkn_owner; + sim->sim_id = &id->i_id; + stat = smb_idmap_batch_getid(sib->sib_idmaph, sim++, + id->i_sidattr.sid, SMB_IDMAP_UNKNOWN); + + if (stat != IDMAP_SUCCESS) + return (stat); + } + + /* Primary Group SID */ + id = token->tkn_primary_grp; + sim->sim_id = &id->i_id; + stat = smb_idmap_batch_getid(sib->sib_idmaph, sim++, + id->i_sidattr.sid, SMB_IDMAP_GROUP); + + if (stat != IDMAP_SUCCESS) + return (stat); + + /* Other Windows Group SIDs */ + for (i = 0; i < token->tkn_win_grps->wg_count; i++, sim++) { + id = &token->tkn_win_grps->wg_groups[i]; + sim->sim_id = &id->i_id; + stat = smb_idmap_batch_getid(sib->sib_idmaph, sim, + id->i_sidattr.sid, SMB_IDMAP_GROUP); + + if (stat != IDMAP_SUCCESS) + break; + } + + return (stat); +} + +/* + * smb_token_sids2ids + * + * This will map all the SIDs of the access token to UIDs/GIDs. + * + * Returns 0 upon success. Otherwise, returns -1. + */ +static int +smb_token_sids2ids(smb_token_t *token) +{ + idmap_stat stat; + int nmaps, retries = 0; + smb_idmap_batch_t sib; + + /* + * Number of idmap lookups: user SID, owner SID, primary group SID, + * and all Windows group SIDs + */ + if (token->tkn_flags & SMB_ATF_ANON) + /* + * Don't include user and owner SID, they're Anonymous + */ + nmaps = 1; + else + nmaps = 3; + + nmaps += token->tkn_win_grps->wg_count; + + do { + stat = smb_idmap_batch_create(&sib, nmaps, SMB_IDMAP_SID2ID); + if (stat != IDMAP_SUCCESS) { + syslog(LOG_ERR, "smb_token_sids2ids:" + " idmap_get_create failed (%d)", stat); + return (-1); + } + + stat = smb_token_idmap(token, &sib); + if (stat != IDMAP_SUCCESS) { + smb_idmap_batch_destroy(&sib); + return (-1); + } + + stat = smb_idmap_batch_getmappings(&sib); + smb_idmap_batch_destroy(&sib); + if (stat == IDMAP_ERR_RPC_HANDLE) + if (smb_idmap_restart() < 0) + break; + } while (stat == IDMAP_ERR_RPC_HANDLE && retries++ < 3); + + return (stat == IDMAP_SUCCESS ? 0 : -1); +} + +/* + * smb_token_create_pxgrps + * + * Setup the POSIX group membership of the access token if the given UID is + * a POSIX UID (non-ephemeral). Both the user's primary group and + * supplementary groups will be added to the POSIX group array of the access + * token. + */ +static smb_posix_grps_t * +smb_token_create_pxgrps(uid_t uid) +{ + struct passwd *pwd; + smb_posix_grps_t *pgrps; + int ngroups_max, num; + gid_t *gids; + + if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) < 0) { + syslog(LOG_ERR, "smb_logon: failed to get _SC_NGROUPS_MAX"); + return (NULL); + } + + pwd = getpwuid(uid); + if (pwd == NULL) { + pgrps = malloc(sizeof (smb_posix_grps_t)); + if (pgrps == NULL) + return (NULL); + + pgrps->pg_ngrps = 0; + return (pgrps); + } + + if (pwd->pw_name == NULL) { + pgrps = malloc(sizeof (smb_posix_grps_t)); + if (pgrps == NULL) + return (NULL); + + pgrps->pg_ngrps = 1; + pgrps->pg_grps[0] = pwd->pw_gid; + return (pgrps); + } + + gids = (gid_t *)malloc(ngroups_max * sizeof (gid_t)); + if (gids == NULL) { + return (NULL); + } + bzero(gids, ngroups_max * sizeof (gid_t)); + + gids[0] = pwd->pw_gid; + + /* + * Setup the groups starting at index 1 (the last arg) + * of gids array. + */ + num = _getgroupsbymember(pwd->pw_name, gids, ngroups_max, 1); + + if (num == -1) { + syslog(LOG_ERR, "smb_logon: unable " + "to get user's supplementary groups"); + num = 1; + } + + pgrps = (smb_posix_grps_t *)malloc(SMB_POSIX_GRPS_SIZE(num)); + if (pgrps) { + pgrps->pg_ngrps = num; + bcopy(gids, pgrps->pg_grps, num * sizeof (gid_t)); + } + + free(gids); + return (pgrps); +} + +/* + * smb_token_destroy + * + * Release all of the memory associated with a token structure. Ensure + * that the token has been unlinked before calling. + */ +void +smb_token_destroy(smb_token_t *token) +{ + smb_win_grps_t *groups; + int i; + + if (token == NULL) + return; + + if (token->tkn_user) { + free(token->tkn_user->i_sidattr.sid); + free(token->tkn_user); + } + + if (token->tkn_owner) { + free(token->tkn_owner->i_sidattr.sid); + free(token->tkn_owner); + } + + if (token->tkn_primary_grp) { + free(token->tkn_primary_grp->i_sidattr.sid); + free(token->tkn_primary_grp); + } + + if ((groups = token->tkn_win_grps) != NULL) { + for (i = 0; i < groups->wg_count; ++i) + free(groups->wg_groups[i].i_sidattr.sid); + free(groups); + } + + smb_privset_free(token->tkn_privileges); + + free(token->tkn_posix_grps); + free(token->tkn_account_name); + free(token->tkn_domain_name); + free(token->tkn_session_key); + + free(token); +} + +static smb_id_t * +smb_token_create_id(nt_sid_t *sid) +{ + smb_id_t *id; + + id = (smb_id_t *)malloc(sizeof (smb_id_t)); + if (id == NULL) + return (NULL); + + id->i_id = (uid_t)-1; + id->i_sidattr.attrs = 7; + id->i_sidattr.sid = nt_sid_dup(sid); + + if (id->i_sidattr.sid == NULL) { + free(id); + id = NULL; + } + + return (id); +} + +/* + * Token owner should be set to local Administrators group + * in two cases: + * 1. The logged on user is a member of Domain Admins group + * 2. he/she is a member of local Administrators group + */ +static smb_id_t * +smb_token_create_owner(smb_userinfo_t *user_info) +{ + smb_id_t *owner; + +#ifdef PBSHORTCUT + if (user_info->flags & SMB_UINFO_FLAG_ADMIN) { + well_known_account_t *wka; + /* + * Need Winchester update on Group ID as file owner issue. + * For now, the file owner will always be set with user SID. + */ + wka = nt_builtin_lookup("Administratrors"); + owner = smb_token_create_id(wka->binsid); + } else +#endif + owner = smb_token_create_id(user_info->user_sid); + return (owner); +} + +static smb_privset_t * +smb_token_create_privs(smb_userinfo_t *user_info) +{ + smb_privset_t *privs; + nt_group_t *grp; + + privs = smb_privset_new(); + if (privs == NULL) + return (NULL); + + (void) nt_groups_member_privs(user_info->user_sid, privs); + + if (user_info->flags & SMB_UINFO_FLAG_ADMIN) { + grp = nt_group_getinfo("Administrators", RWLOCK_READER); + if (grp) { + nt_group_add_groupprivs(grp, privs); + nt_group_putinfo(grp); + } + + /* + * This privilege is required for view/edit of SACL + */ + smb_privset_enable(privs, SE_SECURITY_LUID); + } + + return (privs); +} + +static void +smb_token_set_flags(smb_token_t *token, smb_userinfo_t *user_info) +{ + well_known_account_t *wka; + + if (user_info->flags & SMB_UINFO_FLAG_ANON) { + token->tkn_flags |= SMB_ATF_ANON; + return; + } + + if (user_info->rid == DOMAIN_USER_RID_GUEST) { + token->tkn_flags |= SMB_ATF_GUEST; + return; + } + + wka = nt_builtin_lookup("Administrators"); + if (wka->binsid && smb_token_is_member(token, wka->binsid)) + token->tkn_flags |= SMB_ATF_ADMIN; + + wka = nt_builtin_lookup("Power Users"); + if (wka->binsid && smb_token_is_member(token, wka->binsid)) + token->tkn_flags |= SMB_ATF_POWERUSER; + + wka = nt_builtin_lookup("Backup Operators"); + if (wka->binsid && smb_token_is_member(token, wka->binsid)) + token->tkn_flags |= SMB_ATF_BACKUPOP; + +} + +/* + * smb_token_create + * + * Build an access token based on the given user information (user_info). + * + * If everything is successful, a pointer to an access token is + * returned. Otherwise a null pointer is returned. + */ +static smb_token_t * +smb_token_create(smb_userinfo_t *user_info) +{ + smb_token_t *token; + + if (user_info->sid_name_use != SidTypeUser) { + return (NULL); + } + + token = (smb_token_t *)malloc(sizeof (smb_token_t)); + if (token == NULL) { + syslog(LOG_ERR, "smb_token_create: resource shortage"); + return (NULL); + } + bzero(token, sizeof (smb_token_t)); + + /* User */ + token->tkn_user = smb_token_create_id(user_info->user_sid); + if (token->tkn_user == NULL) { + smb_token_destroy(token); + syslog(LOG_ERR, "smb_token_create: resource shortage"); + return (NULL); + } + + /* Owner */ + token->tkn_owner = smb_token_create_owner(user_info); + if (token->tkn_owner == NULL) { + syslog(LOG_ERR, "smb_token_create: resource shortage"); + smb_token_destroy(token); + return (NULL); + } + + /* Primary Group */ + token->tkn_primary_grp = smb_token_create_id(user_info->pgrp_sid); + if (token->tkn_primary_grp == NULL) { + syslog(LOG_ERR, "smb_token_create: resource shortage"); + smb_token_destroy(token); + return (NULL); + } + + /* Privileges */ + token->tkn_privileges = smb_token_create_privs(user_info); + if (token->tkn_privileges == NULL) { + smb_token_destroy(token); + syslog(LOG_ERR, "smb_token_create: resource shortage"); + return (NULL); + } + + /* Windows Groups */ + token->tkn_win_grps = smb_token_create_wingrps(user_info); + + smb_token_set_flags(token, user_info); + + /* + * IMPORTANT + * + * This function has to be called after all the SIDs in the + * token are setup (i.e. user, owner, primary and supplementary + * groups) and before setting up Solaris groups. + */ + if (smb_token_sids2ids(token) != 0) { + syslog(LOG_ERR, "smb_token_create: idmap failed"); + smb_token_destroy(token); + return (NULL); + } + + /* Solaris Groups */ + token->tkn_posix_grps = smb_token_create_pxgrps(token->tkn_user->i_id); + + if (user_info->session_key) { + token->tkn_session_key = malloc(sizeof (smb_session_key_t)); + if (token->tkn_session_key == NULL) { + syslog(LOG_ERR, "smb_token_create: resource shortage"); + smb_token_destroy(token); + return (NULL); + } + + (void) memcpy(token->tkn_session_key, + user_info->session_key, sizeof (smb_session_key_t)); + } + + token->tkn_account_name = strdup(user_info->name); + token->tkn_domain_name = strdup(user_info->domain_name); + + if (!smb_token_is_valid(token)) { + smb_token_destroy(token); + return (NULL); + } + + return (token); +} + +/* + * smb_token_create_wingrps + * + * This private function supports smb_token_create() by mapping the group + * information in the user_info structure to the form required in an + * access token. The main difference is that the user_info contains + * RIDs while and access token contains full SIDs. Memory allocated + * here will be deallocated as part of smb_token_destroy(). + * + * If everything is successful, a pointer to a smb_win_grps_t + * structure is returned. Otherwise a null pointer is returned. + */ +static smb_win_grps_t * +smb_token_create_wingrps(smb_userinfo_t *user_info) +{ + static char *wk_grps[] = + {"Authenticated Users", "NETWORK", "Administrators"}; + smb_win_grps_t *tkn_grps; + smb_sid_attrs_t *dlg_grps; + smb_rid_attrs_t *g_grps; + smb_sid_attrs_t *grp; + nt_sid_t *builtin_sid; + uint32_t n_gg, n_lg, n_dlg, n_wg; + uint32_t i, j; + int size, count; + + if (user_info == NULL) + return (NULL); + + n_gg = user_info->n_groups; /* Global Groups */ + n_dlg = user_info->n_other_grps; /* Domain Local Groups */ + + /* Local Groups */ + n_lg = nt_groups_member_ngroups(user_info->user_sid); + + /* Well known Groups */ + if ((user_info->flags & SMB_UINFO_FLAG_ADMIN) == SMB_UINFO_FLAG_DADMIN) + /* if user is a domain admin but not a local admin */ + n_wg = 3; + else if (user_info->flags & SMB_UINFO_FLAG_ANON) + n_wg = 0; + else + n_wg = 2; + + count = n_gg + n_dlg + n_lg + n_wg; + size = sizeof (smb_win_grps_t) + (count * sizeof (smb_id_t)); + + tkn_grps = (smb_win_grps_t *)malloc(size); + if (tkn_grps == NULL) { + return (NULL); + } + bzero(tkn_grps, size); + + tkn_grps->wg_count = count; + + /* Add global groups */ + g_grps = user_info->groups; + for (i = 0; i < n_gg; ++i) { + grp = &tkn_grps->wg_groups[i].i_sidattr; + grp->attrs = g_grps[i].attributes; + grp->sid = nt_sid_splice(user_info->domain_sid, g_grps[i].rid); + } + + if (n_gg == 0) { + /* + * if there's no global group should add the + * primary group. + */ + grp = &tkn_grps->wg_groups[i++].i_sidattr; + grp->attrs = 0x7; + grp->sid = nt_sid_dup(user_info->pgrp_sid); + tkn_grps->wg_count++; + } + + /* Add domain local groups */ + dlg_grps = user_info->other_grps; + for (j = 0; j < n_dlg; ++j, ++i) { + grp = &tkn_grps->wg_groups[i].i_sidattr; + grp->attrs = dlg_grps[j].attrs; + grp->sid = nt_sid_dup(dlg_grps[j].sid); + } + + if (n_lg) { + /* Add local groups */ + (void) nt_groups_member_groups(user_info->user_sid, + &tkn_grps->wg_groups[i], n_lg); + i += n_lg; + } + + /* Add well known groups */ + for (j = 0; j < n_wg; ++j, ++i) { + builtin_sid = nt_builtin_lookup_name(wk_grps[j], NULL); + tkn_grps->wg_groups[i].i_sidattr.sid = builtin_sid; + tkn_grps->wg_groups[i].i_sidattr.attrs = 0x7; + } + + return (tkn_grps); +} + +/* + * smb_logon + * + * Performs user authentication and creates a token if the + * authentication is successful. + * + * Returns pointer to the created token. + */ +smb_token_t * +smb_logon(netr_client_t *clnt) +{ + smb_token_t *token = NULL; + smb_userinfo_t *uinfo; + uint32_t status; + + if ((uinfo = mlsvc_alloc_user_info()) == 0) + return (NULL); + + switch (clnt->flags) { + case NETR_CFLG_DOMAIN: + /* Pass through authentication with DC */ + status = smb_logon_domain(clnt, uinfo); + break; + + case NETR_CFLG_LOCAL: + /* Local authentication */ + status = smb_logon_local(clnt, uinfo); + break; + + case NETR_CFLG_ANON: + /* Anonymous user; no authentication */ + status = smb_logon_none(clnt, uinfo); + break; + + default: + status = NT_STATUS_INVALID_PARAMETER; + break; + } + + if (status == NT_STATUS_SUCCESS) + token = smb_token_create(uinfo); + + mlsvc_free_user_info(uinfo); + return (token); +} + +/* + * smb_logon_domain + * + * Performs pass through authentication with PDC. + */ +static uint32_t +smb_logon_domain(netr_client_t *clnt, smb_userinfo_t *uinfo) +{ + uint32_t status; + + if ((status = netlogon_logon(clnt, uinfo)) != 0) { + if (status == NT_STATUS_CANT_ACCESS_DOMAIN_INFO) { + if ((status = netlogon_logon(clnt, uinfo)) != 0) { + syslog(LOG_INFO, "SmbLogon[%s\\%s]: %s", + clnt->domain, clnt->username, + xlate_nt_status(status)); + return (status); + } + } + } + + return (status); +} + +/* + * smb_logon_local + * + * Check to see if connected user has an entry in the local + * smbpasswd database. If it has, tries both LM hash and NT + * hash with user's password(s) to authenticate the user. + */ +static uint32_t +smb_logon_local(netr_client_t *clnt, smb_userinfo_t *uinfo) +{ + smb_passwd_t smbpw; + boolean_t lm_ok, nt_ok; + uint32_t status; + + if (smb_pwd_getpasswd(clnt->username, &smbpw) == NULL) { + /* + * If user doesn't have entry either in smbpasswd + * or passwd it's considered as an invalid user. + */ + status = NT_STATUS_NO_SUCH_USER; + syslog(LOG_NOTICE, "SmbLogon[%s\\%s]: %s", + clnt->domain, clnt->username, + xlate_nt_status(status)); + return (status); + } + + if (smbpw.pw_flags & SMB_PWF_DISABLE) + return (NT_STATUS_ACCOUNT_DISABLED); + + nt_ok = lm_ok = B_FALSE; + if ((smbpw.pw_flags & SMB_PWF_LM) && + (clnt->lm_password.lm_password_len != 0)) { + lm_ok = smb_auth_validate_lm( + clnt->challenge_key.challenge_key_val, + clnt->challenge_key.challenge_key_len, + &smbpw, + clnt->lm_password.lm_password_val, + clnt->lm_password.lm_password_len, + clnt->username); + } + + if (!lm_ok && (clnt->nt_password.nt_password_len != 0)) { + nt_ok = smb_auth_validate_nt( + clnt->challenge_key.challenge_key_val, + clnt->challenge_key.challenge_key_len, + &smbpw, + clnt->nt_password.nt_password_val, + clnt->nt_password.nt_password_len, + clnt->username); + } + + if (!nt_ok && !lm_ok) { + status = NT_STATUS_WRONG_PASSWORD; + syslog(LOG_NOTICE, "SmbLogon[%s\\%s]: %s", + clnt->domain, clnt->username, + xlate_nt_status(status)); + return (status); + } + + status = smb_setup_luinfo(uinfo, clnt, smbpw.pw_uid); + return (status); +} + +/* + * smb_logon_none + * + * Setup user information for anonymous user. + * No authentication is required. + */ +static uint32_t +smb_logon_none(netr_client_t *clnt, smb_userinfo_t *uinfo) +{ + return (smb_setup_luinfo(uinfo, clnt, (uid_t)-1)); +} + +/* + * smb_setup_luinfo + * + * Setup local user information based on the client information and + * user's record in the local password file. + */ +static uint32_t +smb_setup_luinfo(smb_userinfo_t *lui, netr_client_t *clnt, uid_t uid) +{ + idmap_stat stat; + smb_idmap_batch_t sib; + smb_idmap_t *umap, *gmap; + nt_group_t *grp; + struct passwd pw; + char pwbuf[1024]; + + lui->sid_name_use = SidTypeUser; + lui->domain_sid = nt_sid_dup(nt_domain_local_sid()); + lui->name = strdup(clnt->username); + lui->domain_name = strdup(clnt->domain); + lui->n_groups = 0; + lui->groups = NULL; + lui->n_other_grps = 0; + lui->other_grps = NULL; + lui->flags = 0; + + if (lui->name == NULL || lui->domain_name == NULL || + lui->domain_sid == NULL) + return (NT_STATUS_INVALID_PARAMETER); + + if (clnt->flags & NETR_CFLG_ANON) { + lui->user_sid = nt_builtin_lookup_name("Anonymous", NULL); + lui->pgrp_sid = nt_builtin_lookup_name("Anonymous", NULL); + lui->flags = SMB_UINFO_FLAG_ANON; + + if (lui->user_sid == NULL || lui->pgrp_sid == NULL) + return (NT_STATUS_NO_MEMORY); + + return (NT_STATUS_SUCCESS); + } + + if (getpwuid_r(uid, &pw, pwbuf, sizeof (pwbuf)) == NULL) + return (NT_STATUS_NO_SUCH_USER); + + /* Get the SID for user's uid & gid */ + stat = smb_idmap_batch_create(&sib, 2, SMB_IDMAP_ID2SID); + if (stat != IDMAP_SUCCESS) { + return (NT_STATUS_INTERNAL_ERROR); + } + + umap = &sib.sib_maps[0]; + stat = smb_idmap_batch_getsid(sib.sib_idmaph, umap, pw.pw_uid, + SMB_IDMAP_USER); + + if (stat != IDMAP_SUCCESS) { + smb_idmap_batch_destroy(&sib); + return (NT_STATUS_INTERNAL_ERROR); + } + + gmap = &sib.sib_maps[1]; + stat = smb_idmap_batch_getsid(sib.sib_idmaph, gmap, pw.pw_gid, + SMB_IDMAP_GROUP); + + if (stat != IDMAP_SUCCESS) { + smb_idmap_batch_destroy(&sib); + return (NT_STATUS_INTERNAL_ERROR); + } + + stat = smb_idmap_batch_getmappings(&sib); + + if (stat != IDMAP_SUCCESS) { + return (NT_STATUS_INTERNAL_ERROR); + } + + lui->rid = umap->sim_rid; + lui->user_sid = nt_sid_dup(umap->sim_sid); + + lui->primary_group_rid = gmap->sim_rid; + lui->pgrp_sid = nt_sid_dup(gmap->sim_sid); + + smb_idmap_batch_destroy(&sib); + + if ((lui->user_sid == NULL) || (lui->pgrp_sid == NULL)) + return (NT_STATUS_NO_MEMORY); + + grp = nt_group_getinfo("Administrators", RWLOCK_READER); + if (grp) { + if (nt_group_is_member(grp, lui->user_sid)) + lui->flags = SMB_UINFO_FLAG_LADMIN; + nt_group_putinfo(grp); + } + + return (NT_STATUS_SUCCESS); +} + +/* + * smb_token_is_valid + * + * check to see if specified fields of the given access + * token are valid. + * Returns 1 if all of them are valid; otherwise 0. + */ +static int +smb_token_is_valid(smb_token_t *token) +{ + int valid; + + valid = (token->tkn_user != 0) && + (token->tkn_user->i_sidattr.sid != 0) && + (token->tkn_privileges != 0) && + (token->tkn_win_grps != 0) && + (token->tkn_owner != 0) && + (token->tkn_owner->i_sidattr.sid != 0) && + (token->tkn_primary_grp != 0) && + (token->tkn_primary_grp->i_sidattr.sid != 0); + + return (valid); +} + +/* + * smb_token_user_sid + * + * Return a pointer to the user SID in the specified token. A null + * pointer indicates an error. + */ +static nt_sid_t * +smb_token_user_sid(smb_token_t *token) +{ + if (token && token->tkn_user) + return ((token)->tkn_user->i_sidattr.sid); + + return (NULL); +} + +/* + * smb_token_group_sid + * + * Return a pointer to the group SID as indicated by the iterator. + * Setting the iterator to 0 before calling this function will return + * the first group, which will always be the primary group. The + * iterator will be incremented before returning the SID so that this + * function can be used to cycle through the groups. The caller can + * adjust the iterator as required between calls to obtain any specific + * group. + * + * On success a pointer to the appropriate group SID will be returned. + * Otherwise a null pointer will be returned. + */ +static nt_sid_t * +smb_token_group_sid(smb_token_t *token, int *iterator) +{ + smb_win_grps_t *groups; + int index; + + if (token == NULL || iterator == NULL) { + return (NULL); + } + + if ((groups = token->tkn_win_grps) == NULL) { + return (NULL); + } + + index = *iterator; + + if (index < 0 || index >= groups->wg_count) { + return (NULL); + } + + ++(*iterator); + return (groups->wg_groups[index].i_sidattr.sid); +} + +/* + * smb_token_is_member + * + * This function will determine whether or not the specified SID is a + * member of a token. The user SID and all group SIDs are tested. + * Returns 1 if the SID is a member of the token. Otherwise returns 0. + */ +static int +smb_token_is_member(smb_token_t *token, nt_sid_t *sid) +{ + nt_sid_t *tsid; + int iterator = 0; + + tsid = smb_token_user_sid(token); + while (tsid) { + if (nt_sid_is_equal(tsid, sid)) + return (1); + + tsid = smb_token_group_sid(token, &iterator); + } + + return (0); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/smb_autohome.c b/usr/src/lib/smbsrv/libmlsvc/common/smb_autohome.c new file mode 100644 index 000000000000..a9810bd53823 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/smb_autohome.c @@ -0,0 +1,413 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SMB_AUTOHOME_KEYSIZ 128 +#define SMB_AUTOHOME_MAXARG 4 +#define SMB_AUTOHOME_BUFSIZ 2048 + +typedef struct smb_autohome_info { + struct smb_autohome_info *magic1; + FILE *fp; + smb_autohome_t autohome; + char buf[SMB_AUTOHOME_BUFSIZ]; + char *argv[SMB_AUTOHOME_MAXARG]; + int lineno; + struct smb_autohome_info *magic2; +} smb_autohome_info_t; + +static smb_autohome_info_t smb_ai; + +static smb_autohome_t *smb_autohome_make_entry(smb_autohome_info_t *); +static char *smb_autohome_keysub(const char *, char *, int); +static smb_autohome_info_t *smb_autohome_getinfo(void); + +/* + * Add an autohome share. See smb_autohome(4) for details. + * + * If share directory contains backslash path separators, they will + * be converted to forward slash to support NT/DOS path style for + * autohome shares. + * + * Returns 0 on success or -1 to indicate an error. + */ +int +smb_autohome_add(const char *username) +{ + lmshare_info_t si; + char *sharename; + smb_autohome_t *ai; + + if (username == NULL) + return (-1); + + if ((sharename = strdup(username)) == NULL) + return (-1); + + if ((ai = smb_autohome_lookup(sharename)) == NULL) { + free(sharename); + return (0); + } + + (void) memset(&si, 0, sizeof (lmshare_info_t)); + (void) strlcpy(si.directory, ai->ah_path, MAXPATHLEN); + (void) strsubst(si.directory, '\\', '/'); + (void) strlcpy(si.container, ai->ah_container, MAXPATHLEN); + + if (lmshare_is_dir(si.directory) == 0) { + free(sharename); + return (0); + } + + if (lmshare_exists(sharename) != 0) { + (void) lmshare_getinfo(sharename, &si); + if (!(si.mode & LMSHRM_TRANS)) { + free(sharename); + return (0); + } + } else { + (void) strcpy(si.share_name, sharename); + si.mode = LMSHRM_TRANS; + } + + (void) lmshare_add(&si, 0); + free(sharename); + return (0); +} + +/* + * Remove an autohome share. + * + * Returns 0 on success or -1 to indicate an error. + */ +int +smb_autohome_remove(const char *username) +{ + lmshare_info_t si; + char *sharename; + + if (username == NULL) + return (-1); + + if ((sharename = strdup(username)) == NULL) + return (-1); + + if (lmshare_getinfo(sharename, &si) == NERR_Success) { + if (si.mode & LMSHRM_TRANS) { + (void) lmshare_delete(sharename, 0); + } + } + + free(sharename); + return (0); +} + +/* + * Find out if a share is an autohome share. + * + * Returns 1 if the share is an autohome share. + * Otherwise returns 0. + */ +int +smb_is_autohome(const lmshare_info_t *si) +{ + if (si && (si->mode & LMSHRM_TRANS) && + (lmshare_is_restricted((char *)si->share_name) == 0)) { + return (1); + } + + return (0); +} + +/* + * Search the autohome database for the specified name. The name cannot + * be an empty string or begin with * or +. + * 1. Search the file for the specified name. + * 2. Check for the wildcard rule and, if present, treat it as a match. + * 3. Check for the nsswitch rule and, if present, lookup the name + * via the name services. Note that the nsswitch rule will never + * be applied if the wildcard rule is present. + * + * Returns a pointer to the entry on success or null on failure. + */ +smb_autohome_t * +smb_autohome_lookup(const char *name) +{ + struct passwd *pw; + smb_autohome_t *ah = 0; + + if (name == NULL) + return (NULL); + + if (*name == '\0' || *name == '*' || *name == '+') + return (NULL); + + smb_autohome_setent(); + + while ((ah = smb_autohome_getent(name)) != NULL) { + if (strcasecmp(ah->ah_name, name) == 0) + break; + } + + if (ah == NULL) { + smb_autohome_setent(); + + while ((ah = smb_autohome_getent(name)) != NULL) { + if (strcasecmp(ah->ah_name, "*") == 0) { + ah->ah_name = (char *)name; + break; + } + } + } + + if (ah == NULL) { + smb_autohome_setent(); + + while ((ah = smb_autohome_getent("+nsswitch")) != NULL) { + if (strcasecmp("+nsswitch", ah->ah_name) != 0) + continue; + if ((pw = getpwnam(name)) == NULL) { + ah = 0; + break; + } + + ah->ah_name = pw->pw_name; + + if (ah->ah_path) + ah->ah_container = ah->ah_path; + + ah->ah_path = pw->pw_dir; + break; + } + } + + smb_autohome_endent(); + return (ah); +} + +/* + * Open or rewind the autohome database. + */ +void +smb_autohome_setent(void) +{ + smb_autohome_info_t *si; + char *mappath; + char filename[MAXNAMELEN]; + + if ((si = smb_autohome_getinfo()) != 0) { + (void) fseek(si->fp, 0L, SEEK_SET); + si->lineno = 0; + return; + } + + if ((si = &smb_ai) == 0) + return; + + smb_config_rdlock(); + if ((mappath = smb_config_get(SMB_CI_AUTOHOME_MAP)) == NULL) + mappath = SMB_AUTOHOME_PATH; + (void) snprintf(filename, MAXNAMELEN, "%s/%s", mappath, + SMB_AUTOHOME_FILE); + smb_config_unlock(); + + if ((si->fp = fopen(filename, "r")) == NULL) + return; + + si->magic1 = si; + si->magic2 = si; + si->lineno = 0; +} + +/* + * Close the autohome database and invalidate the autohome info. + * We can't zero the whole info structure because the application + * should still have access to the data after the file is closed. + */ +void +smb_autohome_endent(void) +{ + smb_autohome_info_t *si; + + if ((si = smb_autohome_getinfo()) != 0) { + (void) fclose(si->fp); + si->fp = 0; + si->magic1 = 0; + si->magic2 = 0; + } +} + +/* + * Return the next entry in the autohome database, opening the file + * if necessary. Returns null on EOF or error. + * + * Note that we are not looking for the specified name. The name is + * only used for key substitution, so that the caller sees the entry + * in expanded form. + */ +smb_autohome_t * +smb_autohome_getent(const char *name) +{ + smb_autohome_info_t *si; + char *bp; + + if ((si = smb_autohome_getinfo()) == 0) { + smb_autohome_setent(); + + if ((si = smb_autohome_getinfo()) == 0) + return (0); + } + + /* + * Find the next non-comment, non-empty line. + * Anything after a # is a comment and can be discarded. + * Discard a newline to avoid it being included in the parsing + * that follows. + * Leading and training whitespace is discarded, and replicated + * whitespace is compressed to simplify the token parsing, + * although strsep() deals with that better than strtok(). + */ + do { + if (fgets(si->buf, SMB_AUTOHOME_BUFSIZ, si->fp) == 0) + return (0); + + ++si->lineno; + + if ((bp = strpbrk(si->buf, "#\r\n")) != 0) + *bp = '\0'; + + (void) trim_whitespace(si->buf); + bp = strcanon(si->buf, " \t"); + } while (*bp == '\0'); + + (void) smb_autohome_keysub(name, si->buf, SMB_AUTOHOME_BUFSIZ); + return (smb_autohome_make_entry(si)); +} + +/* + * Set up an autohome entry from the line buffer. The line should just + * contain tokens separated by single whitespace. The line format is: + * + */ +static smb_autohome_t * +smb_autohome_make_entry(smb_autohome_info_t *si) +{ + char *bp; + int i; + + bp = si->buf; + + for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i) + si->argv[i] = 0; + + for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i) { + do { + if ((si->argv[i] = strsep((char **)&bp, " \t")) == 0) + break; + } while (*(si->argv[i]) == '\0'); + + if (si->argv[i] == 0) + break; + } + + if ((si->autohome.ah_name = si->argv[0]) == NULL) { + /* + * Sanity check: the name could be an empty + * string but it can't be a null pointer. + */ + return (0); + } + + if ((si->autohome.ah_path = si->argv[1]) == NULL) + si->autohome.ah_path = ""; + + if ((si->autohome.ah_container = si->argv[2]) == NULL) + si->autohome.ah_container = ""; + + return (&si->autohome); +} + +/* + * Substitute the ? and & map keys. + * ? is replaced by the first character of the name + * & is replaced by the whole name. + */ +static char * +smb_autohome_keysub(const char *name, char *buf, int buflen) +{ + char key[SMB_AUTOHOME_KEYSIZ]; + char *ampersand; + char *tmp; + + (void) strlcpy(key, buf, SMB_AUTOHOME_KEYSIZ); + + if ((tmp = strpbrk(key, " \t")) == NULL) + return (NULL); + + *tmp = '\0'; + + if (strcmp(key, "*") == 0 && name != NULL) + (void) strlcpy(key, name, SMB_AUTOHOME_KEYSIZ); + + (void) strsubst(buf, '?', *key); + + while ((ampersand = strchr(buf, '&')) != NULL) { + if ((tmp = strdup(ampersand + 1)) == NULL) + return (0); + + (void) strlcpy(ampersand, key, buflen); + (void) strlcat(ampersand, tmp, buflen); + free(tmp); + } + + return (buf); +} + +/* + * Get a pointer to the context buffer and validate it. + */ +static smb_autohome_info_t * +smb_autohome_getinfo(void) +{ + smb_autohome_info_t *si; + + if ((si = &smb_ai) == 0) + return (0); + + if ((si->magic1 == si) && (si->magic2 == si) && (si->fp != NULL)) + return (si); + + return (0); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/smb_share_util.c b/usr/src/lib/smbsrv/libmlsvc/common/smb_share_util.c new file mode 100644 index 000000000000..8e5a7ca03166 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/smb_share_util.c @@ -0,0 +1,106 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include + +static pthread_mutex_t smb_group_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Sharemanager shared API */ + +void +smb_build_lmshare_info(char *share_name, char *path, + sa_optionset_t opts, lmshare_info_t *si) +{ + sa_property_t prop; + char *val = NULL; + + bzero(si, sizeof (lmshare_info_t)); + /* Share is read from SMF so it should be permanent */ + si->mode = LMSHRM_PERM; + + (void) strlcpy(si->directory, path, sizeof (si->directory)); + (void) strlcpy(si->share_name, share_name, sizeof (si->share_name)); + + if (opts == NULL) + return; + + prop = (sa_property_t)sa_get_property(opts, SHOPT_AD_CONTAINER); + if (prop != NULL) { + if ((val = sa_get_property_attr(prop, "value")) != NULL) { + (void) strlcpy(si->container, val, + sizeof (si->container)); + free(val); + } + } + + prop = (sa_property_t)sa_get_property(opts, "description"); + if (prop != NULL) { + if ((val = sa_get_property_attr(prop, "value")) != NULL) { + (void) strlcpy(si->comment, val, sizeof (si->comment)); + free(val); + } + } +} + +/* + * smb_get_smb_share_group + * + * Creates "smb" share group for putting in shares + * created by windows client. + */ +sa_group_t +smb_get_smb_share_group(sa_handle_t handle) +{ + sa_group_t group = NULL; + int err; + + (void) pthread_mutex_lock(&smb_group_mutex); + group = sa_get_group(handle, SMB_DEFAULT_SHARE_GROUP); + if (group != NULL) { + (void) pthread_mutex_unlock(&smb_group_mutex); + return (group); + } + group = sa_create_group(handle, SMB_DEFAULT_SHARE_GROUP, &err); + if (group == NULL) { + (void) pthread_mutex_unlock(&smb_group_mutex); + return (NULL); + } + if (group != NULL) { + if (sa_create_optionset(group, + SMB_DEFAULT_SHARE_GROUP) == NULL) { + (void) sa_remove_group(group); + group = NULL; + } + } + (void) pthread_mutex_unlock(&smb_group_mutex); + return (group); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_client.c b/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_client.c new file mode 100644 index 000000000000..aa9b12d24132 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_client.c @@ -0,0 +1,554 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Server Service (srvsvc) client side RPC library interface. The + * srvsvc interface allows a client to query a server for information + * on shares, sessions, connections and files on the server. Some + * functions are available via anonymous IPC while others require + * administrator privilege. Also, some functions return NT status + * values while others return Win32 errors codes. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * Information level for NetShareGetInfo. + */ +DWORD srvsvc_info_level = 1; + +static int srvsvc_net_remote_tod(char *, char *, struct timeval *, struct tm *); + +/* + * Ensure that an appropriate session and logon exists for the srvsvc + * client calls. Open and bind the RPC interface. + * + * On success 0 is returned. Otherwise a -ve error code. + */ +int +srvsvc_open(int ipc_mode, char *server, char *domain, char *username, + char *password, mlsvc_handle_t *handle, mlrpc_heapref_t *heapref) +{ + smb_ntdomain_t *di; + int fid; + int rc; + + if ((di = smb_getdomaininfo(0)) == NULL) + return (-1); + + if (server == NULL || domain == NULL) { + server = di->server; + domain = di->domain; + } + + switch (ipc_mode) { + case MLSVC_IPC_USER: + /* + * Use the supplied credentials. + */ + rc = mlsvc_user_logon(server, domain, username, password); + break; + + case MLSVC_IPC_ADMIN: + /* + * Use the resource domain administrator credentials. + */ + server = di->server; + domain = di->domain; + username = smbrdr_ipc_get_user(); + + rc = mlsvc_admin_logon(server, domain); + break; + + case MLSVC_IPC_ANON: + default: + rc = mlsvc_anonymous_logon(server, domain, &username); + break; + } + + if (rc != 0) + return (-1); + + fid = mlsvc_open_pipe(server, domain, username, "\\srvsvc"); + if (fid < 0) + return (-1); + + if ((rc = mlsvc_rpc_bind(handle, fid, "SRVSVC")) < 0) { + (void) mlsvc_close_pipe(fid); + return (rc); + } + + rc = mlsvc_rpc_init(heapref); + return (rc); +} + +/* + * Close the srvsvc pipe and free the associated context. This function + * should only be called if the open was successful. + */ +void +srvsvc_close(mlsvc_handle_t *handle, mlrpc_heapref_t *heapref) +{ + mlsvc_rpc_free(handle->context, heapref); + (void) mlsvc_close_pipe(handle->context->fid); + free(handle->context); +} + +/* + * This is a client side routine for NetShareGetInfo. + * Levels 0 and 1 work with an anonymous connection but + * level 2 requires administrator access. + */ +int +srvsvc_net_share_get_info(char *server, char *domain, char *netname) +{ + struct mlsm_NetShareGetInfo arg; + mlsvc_handle_t handle; + mlrpc_heapref_t heap; + int rc; + int opnum; + struct mslm_NetShareGetInfo0 *info0; + struct mslm_NetShareGetInfo1 *info1; + struct mslm_NetShareGetInfo2 *info2; + int ipc_mode; + int len; + + if (netname == NULL) + return (-1); + + if (srvsvc_info_level == 2) + ipc_mode = MLSVC_IPC_ADMIN; + else + ipc_mode = MLSVC_IPC_ANON; + + rc = srvsvc_open(ipc_mode, server, domain, 0, 0, &handle, &heap); + if (rc != 0) + return (-1); + + opnum = SRVSVC_OPNUM_NetShareGetInfo; + bzero(&arg, sizeof (struct mlsm_NetShareGetInfo)); + + len = strlen(server) + 4; + arg.servername = mlrpc_heap_malloc(heap.heap, len); + if (arg.servername == NULL) { + srvsvc_close(&handle, &heap); + return (-1); + } + + (void) snprintf((char *)arg.servername, len, "\\\\%s", server); + arg.netname = (LPTSTR)netname; + arg.level = srvsvc_info_level; /* share information level */ + + rc = mlsvc_rpc_call(handle.context, opnum, &arg, &heap); + if ((rc != 0) || (arg.status != 0)) { + srvsvc_close(&handle, &heap); + return (-1); + } + + switch (arg.result.switch_value) { + case 0: + info0 = arg.result.ru.info0; + smb_tracef("srvsvc shi0_netname=%s", info0->shi0_netname); + break; + + case 1: + info1 = arg.result.ru.info1; + smb_tracef("srvsvc shi1_netname=%s", info1->shi1_netname); + smb_tracef("srvsvc shi1_type=%u", info1->shi1_type); + + if (info1->shi1_comment) + smb_tracef("srvsvc shi1_comment=%s", + info1->shi1_comment); + break; + + case 2: + info2 = arg.result.ru.info2; + smb_tracef("srvsvc shi2_netname=%s", info2->shi2_netname); + smb_tracef("srvsvc shi2_type=%u", info2->shi2_type); + + if (info2->shi2_comment) + smb_tracef("srvsvc shi2_comment=%s", + info2->shi2_comment); + + smb_tracef("srvsvc shi2_perms=%d", info2->shi2_permissions); + smb_tracef("srvsvc shi2_max_use=%d", info2->shi2_max_uses); + smb_tracef("srvsvc shi2_cur_use=%d", info2->shi2_current_uses); + + if (info2->shi2_path) + smb_tracef("srvsvc shi2_path=%s", info2->shi2_path); + + if (info2->shi2_passwd) + smb_tracef("srvsvc shi2_passwd=%s", info2->shi2_passwd); + break; + + default: + smb_tracef("srvsvc: unknown level"); + break; + } + + srvsvc_close(&handle, &heap); + return (0); +} + +/* + * This is a client side routine for NetSessionEnum. + * NetSessionEnum requires administrator rights. + */ +int +srvsvc_net_session_enum(char *server, char *domain, char *netname) +{ + struct mslm_NetSessionEnum arg; + mlsvc_handle_t handle; + mlrpc_heapref_t heap; + int rc; + int opnum; + struct mslm_infonres infonres; + struct mslm_SESSION_INFO_1 *nsi1; + int len; + + if (netname == NULL) + return (-1); + + rc = srvsvc_open(MLSVC_IPC_ADMIN, server, domain, 0, 0, &handle, &heap); + if (rc != 0) + return (-1); + + opnum = SRVSVC_OPNUM_NetSessionEnum; + bzero(&arg, sizeof (struct mslm_NetSessionEnum)); + + len = strlen(server) + 4; + arg.servername = mlrpc_heap_malloc(heap.heap, len); + if (arg.servername == NULL) { + srvsvc_close(&handle, &heap); + return (-1); + } + + (void) snprintf((char *)arg.servername, len, "\\\\%s", server); + infonres.entriesread = 0; + infonres.entries = 0; + arg.level = 1; + arg.result.level = 1; + arg.result.bufptr.p = &infonres; + arg.resume_handle = 0; + arg.pref_max_len = 0xFFFFFFFF; + + rc = mlsvc_rpc_call(handle.context, opnum, &arg, &heap); + if ((rc != 0) || (arg.status != 0)) { + srvsvc_close(&handle, &heap); + return (-1); + } + + /* Only the first session info is dereferenced. */ + nsi1 = ((struct mslm_infonres *)arg.result.bufptr.p)->entries; + + smb_tracef("srvsvc switch_value=%d", arg.level); + smb_tracef("srvsvc sesi1_cname=%s", nsi1->sesi1_cname); + smb_tracef("srvsvc sesi1_uname=%s", nsi1->sesi1_uname); + smb_tracef("srvsvc sesi1_nopens=%u", nsi1->sesi1_nopens); + smb_tracef("srvsvc sesi1_time=%u", nsi1->sesi1_time); + smb_tracef("srvsvc sesi1_itime=%u", nsi1->sesi1_itime); + smb_tracef("srvsvc sesi1_uflags=%u", nsi1->sesi1_uflags); + + srvsvc_close(&handle, &heap); + return (0); +} + +/* + * This is a client side routine for NetConnectEnum. + * NetConnectEnum requires administrator rights. + * Level 0 and level 1 requests are supported. + */ +int +srvsvc_net_connect_enum(char *server, char *domain, char *netname, int level) +{ + struct mslm_NetConnectEnum arg; + mlsvc_handle_t handle; + mlrpc_heapref_t heap; + int rc; + int opnum; + struct mslm_NetConnectInfo1 info1; + struct mslm_NetConnectInfo0 info0; + struct mslm_NetConnectInfoBuf1 *cib1; + int len; + + if (netname == NULL) + return (-1); + + rc = srvsvc_open(MLSVC_IPC_ADMIN, server, domain, 0, 0, &handle, &heap); + if (rc != 0) + return (-1); + + opnum = SRVSVC_OPNUM_NetConnectEnum; + bzero(&arg, sizeof (struct mslm_NetConnectEnum)); + + len = strlen(server) + 4; + arg.servername = mlrpc_heap_malloc(heap.heap, len); + if (arg.servername == NULL) { + srvsvc_close(&handle, &heap); + return (-1); + } + + (void) snprintf((char *)arg.servername, len, "\\\\%s", server); + arg.qualifier = (LPTSTR)netname; + + switch (level) { + case 0: + arg.info.level = 0; + arg.info.switch_value = 0; + arg.info.ru.info0 = &info0; + info0.entries_read = 0; + info0.ci0 = 0; + break; + case 1: + arg.info.level = 1; + arg.info.switch_value = 1; + arg.info.ru.info1 = &info1; + info1.entries_read = 0; + info1.ci1 = 0; + break; + default: + srvsvc_close(&handle, &heap); + return (-1); + } + + arg.resume_handle = 0; + arg.pref_max_len = 0xFFFFFFFF; + + rc = mlsvc_rpc_call(handle.context, opnum, &arg, &heap); + if ((rc != 0) || (arg.status != 0)) { + srvsvc_close(&handle, &heap); + return (-1); + } + + smb_tracef("srvsvc switch_value=%d", arg.info.switch_value); + + switch (level) { + case 0: + if (arg.info.ru.info0 && arg.info.ru.info0->ci0) { + smb_tracef("srvsvc coni0_id=%x", + arg.info.ru.info0->ci0->coni0_id); + } + break; + case 1: + if (arg.info.ru.info1 && arg.info.ru.info1->ci1) { + cib1 = arg.info.ru.info1->ci1; + + smb_tracef("srvsvc coni_uname=%s", + cib1->coni1_username ? + (char *)cib1->coni1_username : "(null)"); + smb_tracef("srvsvc coni1_netname=%s", + cib1->coni1_netname ? + (char *)cib1->coni1_netname : "(null)"); + smb_tracef("srvsvc coni1_nopens=%u", + cib1->coni1_num_opens); + smb_tracef("srvsvc coni1_time=%u", cib1->coni1_time); + smb_tracef("srvsvc coni1_num_users=%u", + cib1->coni1_num_users); + } + break; + + default: + smb_tracef("srvsvc: unknown level"); + break; + } + + srvsvc_close(&handle, &heap); + return (0); +} + +/* + * Synchronize the local system clock with the domain controller. + */ +void +srvsvc_timesync(void) +{ + smb_ntdomain_t *di; + struct timeval tv; + struct tm tm; + time_t tsecs; + + if ((di = smb_getdomaininfo(0)) == NULL) + return; + + if (srvsvc_net_remote_tod(di->server, di->domain, &tv, &tm) != 0) + return; + + if (settimeofday(&tv, 0)) + smb_tracef("unable to set system time"); + + tsecs = time(0); + (void) localtime_r(&tsecs, &tm); + smb_tracef("SrvsvcTimeSync %s", ctime((time_t *)&tv.tv_sec)); +} + +/* + * NetRemoteTOD to get the current GMT time from a Windows NT server. + */ +int +srvsvc_gettime(unsigned long *t) +{ + smb_ntdomain_t *di; + struct timeval tv; + struct tm tm; + + if ((di = smb_getdomaininfo(0)) == NULL) + return (-1); + + if (srvsvc_net_remote_tod(di->server, di->domain, &tv, &tm) != 0) + return (-1); + + *t = tv.tv_sec; + return (0); +} + +/* + * This is a client side routine for NetRemoteTOD, which gets the time + * and date from a remote system. The time information is returned in + * the timeval and tm. + * + * typedef struct _TIME_OF_DAY_INFO { + * DWORD tod_elapsedt; // seconds since 00:00:00 January 1 1970 GMT + * DWORD tod_msecs; // arbitrary milliseconds (since reset) + * DWORD tod_hours; // current hour [0-23] + * DWORD tod_mins; // current minute [0-59] + * DWORD tod_secs; // current second [0-59] + * DWORD tod_hunds; // current hundredth (0.01) second [0-99] + * LONG tod_timezone; // time zone of the server + * DWORD tod_tinterval; // clock tick time interval + * DWORD tod_day; // day of the month [1-31] + * DWORD tod_month; // month of the year [1-12] + * DWORD tod_year; // current year + * DWORD tod_weekday; // day of the week since sunday [0-6] + * } TIME_OF_DAY_INFO; + * + * The time zone of the server is calculated in minutes from Greenwich + * Mean Time (GMT). For time zones west of Greenwich, the value is + * positive; for time zones east of Greenwich, the value is negative. + * A value of -1 indicates that the time zone is undefined. + * + * The clock tick value represents a resolution of one ten-thousandth + * (0.0001) second. + */ +int +srvsvc_net_remote_tod(char *server, char *domain, struct timeval *tv, + struct tm *tm) +{ + char timebuf[64]; + struct mslm_NetRemoteTOD arg; + struct mslm_TIME_OF_DAY_INFO *tod; + mlsvc_handle_t handle; + mlrpc_heapref_t heap; + int rc; + int opnum; + int len; + + rc = srvsvc_open(MLSVC_IPC_ANON, server, domain, 0, 0, &handle, &heap); + if (rc != 0) + return (-1); + + opnum = SRVSVC_OPNUM_NetRemoteTOD; + bzero(&arg, sizeof (struct mslm_NetRemoteTOD)); + + len = strlen(server) + 4; + arg.servername = mlrpc_heap_malloc(heap.heap, len); + if (arg.servername == NULL) { + srvsvc_close(&handle, &heap); + return (-1); + } + + (void) snprintf((char *)arg.servername, len, "\\\\%s", server); + + rc = mlsvc_rpc_call(handle.context, opnum, &arg, &heap); + if ((rc != 0) || (arg.status != 0)) { + srvsvc_close(&handle, &heap); + return (-1); + } + + /* + * We're assigning milliseconds to microseconds + * here but the value's not really relevant. + */ + tod = arg.bufptr; + + if (tv) { + tv->tv_sec = tod->tod_elapsedt; + tv->tv_usec = tod->tod_msecs; + smb_tracef("RemoteTime: %s", ctime(&tv->tv_sec)); + } + + if (tm) { + tm->tm_sec = tod->tod_secs; + tm->tm_min = tod->tod_mins; + tm->tm_hour = tod->tod_hours; + tm->tm_mday = tod->tod_day; + tm->tm_mon = tod->tod_month - 1; + tm->tm_year = tod->tod_year - 1900; + tm->tm_wday = tod->tod_weekday; + + (void) strftime(timebuf, sizeof (timebuf), + "NetRemoteTOD: %D %T", tm); + smb_tracef("NetRemoteTOD: %s", timebuf); + } + + srvsvc_close(&handle, &heap); + return (0); +} + +void +srvsvc_net_test(char *server, char *domain, char *netname) +{ + smb_ntdomain_t *di; + + (void) smb_tracef("%s %s %s", server, domain, netname); + + if ((di = smb_getdomaininfo(0)) != NULL) { + server = di->server; + domain = di->domain; + } + + (void) srvsvc_net_share_get_info(server, domain, netname); +#if 0 + /* + * The NetSessionEnum server-side definition was updated. + * Disabled until the client-side has been updated. + */ + (void) srvsvc_net_session_enum(server, domain, netname); +#endif + (void) srvsvc_net_connect_enum(server, domain, netname, 0); + (void) srvsvc_net_connect_enum(server, domain, netname, 1); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/i386/Makefile b/usr/src/lib/smbsrv/libmlsvc/i386/Makefile new file mode 100644 index 000000000000..f91f0270e9ad --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/i386/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/smbsrv/libmlsvc/sparc/Makefile b/usr/src/lib/smbsrv/libmlsvc/sparc/Makefile new file mode 100644 index 000000000000..f91f0270e9ad --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/sparc/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/smbsrv/libmlsvc/sparcv9/Makefile b/usr/src/lib/smbsrv/libmlsvc/sparcv9/Makefile new file mode 100644 index 000000000000..a2f97019c815 --- /dev/null +++ b/usr/src/lib/smbsrv/libmlsvc/sparcv9/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/smbsrv/libsmb/Makefile b/usr/src/lib/smbsrv/libsmb/Makefile new file mode 100644 index 000000000000..cbe44c764cac --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +HDRS= libsmb.h + +include ../Makefile.smbsrv diff --git a/usr/src/lib/smbsrv/libsmb/Makefile.com b/usr/src/lib/smbsrv/libsmb/Makefile.com new file mode 100644 index 000000000000..c47940628259 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/Makefile.com @@ -0,0 +1,86 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +LIBRARY= libsmb.a +VERS= .1 + +OBJS_SHARED = \ + smb_common_door_decode.o \ + smb_match.o \ + smb_msgbuf.o \ + smb_native.o \ + smb_oem.o \ + smb_opmlang.o \ + smb_share_door_decode.o \ + smb_sid.o \ + smb_status_xlat.o \ + smb_strcase.o \ + smb_string.o \ + smb_token.o \ + smb_token_xdr.o \ + smb_utf8.o \ + smb_xdr_utils.o + +OBJS_COMMON = \ + smb_api_door_calls.o \ + smb_auth.o \ + smb_cfg.o \ + smb_crypt.o \ + smb_ctxbuf.o \ + smb_domain.o \ + smb_door_client.o \ + smb_door_encdec.o \ + smb_doorclnt.o \ + smb_downcalls.o \ + smb_group_door_encdec.o \ + smb_group_xdr.o \ + smb_ht.o \ + smb_idmap.o \ + smb_info.o \ + smb_mac.o \ + smb_pwdutil.o \ + smb_privilege.o \ + smb_scfutil.o \ + smb_util.o \ + smb_wins.o \ + smb_wksids.o + +OBJECTS= $(OBJS_COMMON) $(OBJS_SHARED) + +include ../../../Makefile.lib +include ../../Makefile.lib + +INCS += -I$(SRC)/common/smbsrv + +LDLIBS += -lscf -lmd -lnsl -lpkcs11 -lc -lidmap +CPPFLAGS += $(INCS) -D_REENTRANT + +SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \ + $(OBJS_SHARED:%.o=$(SRC)/common/smbsrv/%.c) + +include ../../Makefile.targ +include ../../../Makefile.targ diff --git a/usr/src/lib/smbsrv/libsmb/amd64/Makefile b/usr/src/lib/smbsrv/libsmb/amd64/Makefile new file mode 100644 index 000000000000..a2f97019c815 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/amd64/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/smbsrv/libsmb/common/libsmb.h b/usr/src/lib/smbsrv/libsmb/common/libsmb.h new file mode 100644 index 000000000000..44da30085d47 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/libsmb.h @@ -0,0 +1,778 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBSMB_H +#define _LIBSMB_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include +#include + +#include + +/* + * XXX - These header files are here, only because other libraries + * can compile. Move the header files in to the internal header files + * of other libraries, once the restructure is complete. libsmb.h does not + * need these header files. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* End of header files to be removed. */ + +/* Max value length of all SMB properties */ +#define MAX_VALUE_BUFLEN 512 +#define SMB_PI_MAX_DOMAIN_U 48 + +#define SMBD_FMRI_PREFIX "network/smb/server" +#define SMBD_DEFAULT_INSTANCE_FMRI "svc:/network/smb/server:default" +#define SMBD_PG_NAME "smbd" +#define SMBD_PROTECTED_PG_NAME "read" + +#define SMBD_SMF_OK 0 +#define SMBD_SMF_NO_MEMORY 1 /* no memory for data structures */ +#define SMBD_SMF_SYSTEM_ERR 2 /* system error, use errno */ +#define SMBD_SMF_NO_PERMISSION 3 /* no permission for operation */ + +#define SCH_STATE_UNINIT 0 +#define SCH_STATE_INITIALIZING 1 +#define SCH_STATE_INIT 2 + +typedef struct smb_scfhandle { + scf_handle_t *scf_handle; + int scf_state; + scf_service_t *scf_service; + scf_scope_t *scf_scope; + scf_transaction_t *scf_trans; + scf_transaction_entry_t *scf_entry; + scf_propertygroup_t *scf_pg; + scf_instance_t *scf_instance; + scf_iter_t *scf_inst_iter; + scf_iter_t *scf_pg_iter; +} smb_scfhandle_t; + +/* + * CIFS Configuration Management + */ + +/* macros for the description of all config params */ +#define SMB_CD_RDR_IPCMODE "rdr_ipcmode" +#define SMB_CD_RDR_IPCUSER "rdr_ipcuser" +#define SMB_CD_RDR_IPCPWD "rdr_ipcpasswd" + +#define SMB_CD_OPLOCK_ENABLE "oplock_enable" +#define SMB_CD_OPLOCK_TIMEOUT "oplock_timeout" + +#define SMB_CD_AUTOHOME_MAP "autohome_map" + +#define SMB_CD_DOMAIN_SID "domain_sid" +#define SMB_CD_DOMAIN_MEMB "domain_member" +#define SMB_CD_DOMAIN_NAME "domain_name" +#define SMB_CD_DOMAIN_SRV "pdc" + +#define SMB_CD_WINS_SRV1 "wins_server_1" +#define SMB_CD_WINS_SRV2 "wins_server_2" +#define SMB_CD_WINS_EXCL "wins_exclude" + +#define SMB_CD_SRVSVC_SHRSET_ENABLE "srvsvc_sharesetinfo_enable" +#define SMB_CD_LOGR_ENABLE "logr_enable" +#define SMB_CD_MLRPC_KALIVE "mlrpc_keep_alive_interval" + +#define SMB_CD_MAX_BUFSIZE "max_bufsize" +#define SMB_CD_MAX_WORKERS "max_workers" +#define SMB_CD_MAX_CONNECTIONS "max_connections" +#define SMB_CD_KEEPALIVE "keep_alive" +#define SMB_CD_RESTRICT_ANON "restrict_anonymous" + +#define SMB_CD_SIGNING_ENABLE "signing_enabled" +#define SMB_CD_SIGNING_REQD "signing_required" +#define SMB_CD_SIGNING_CHECK "signing_check" + +#define SMB_CD_FLUSH_REQUIRED "flush_required" +#define SMB_CD_SYNC_ENABLE "sync_enable" +#define SMB_CD_DIRSYMLINK_DISABLE "dir_symlink_disable" +#define SMB_CD_ANNONCE_QUOTA "announce_quota" + +#define SMB_CD_SECURITY "security" +#define SMB_CD_NBSCOPE "netbios_scope" +#define SMB_CD_SYS_CMNT "system_comment" +#define SMB_CD_LM_LEVEL "lmauth_level" +#define SMB_CD_MSDCS_DISABLE "msdcs_disable" + +#define SMB_CD_ADS_ENABLE "ads_enable" +#define SMB_CD_ADS_USER "ads_user" +#define SMB_CD_ADS_PASSWD "ads_passwd" +#define SMB_CD_ADS_DOMAIN "ads_domain" +#define SMB_CD_ADS_USER_CONTAINER "ads_user_container" +#define SMB_CD_ADS_SITE "ads_site" +#define SMB_CD_ADS_IPLOOKUP "ads_ip_lookup" + +#define SMB_CD_DYNDNS_ENABLE "ddns_enable" +#define SMB_CD_DYNDNS_RETRY_COUNT "ddns_retry_cnt" +#define SMB_CD_DYNDNS_RETRY_SEC "ddns_retry_sec" + +#define SMB_CD_MACHINE_PASSWD "machine_passwd" + +/* configuration identifier */ +typedef enum { + SMB_CI_RDR_IPCMODE = 0, + SMB_CI_RDR_IPCUSER, + SMB_CI_RDR_IPCPWD, + + SMB_CI_OPLOCK_ENABLE, + SMB_CI_OPLOCK_TIMEOUT, + + SMB_CI_AUTOHOME_MAP, + + SMB_CI_DOMAIN_SID, + SMB_CI_DOMAIN_MEMB, + SMB_CI_DOMAIN_NAME, + SMB_CI_DOMAIN_SRV, + + SMB_CI_WINS_SRV1, + SMB_CI_WINS_SRV2, + SMB_CI_WINS_EXCL, + + SMB_CI_SRVSVC_SHRSET_ENABLE, + SMB_CI_LOGR_ENABLE, + SMB_CI_MLRPC_KALIVE, + + SMB_CI_MAX_BUFSIZE, + SMB_CI_MAX_WORKERS, + SMB_CI_MAX_CONNECTIONS, + SMB_CI_KEEPALIVE, + SMB_CI_RESTRICT_ANON, + + SMB_CI_SIGNING_ENABLE, + SMB_CI_SIGNING_REQD, + SMB_CI_SIGNING_CHECK, + + SMB_CI_FLUSH_REQUIRED, + SMB_CI_SYNC_ENABLE, + SMB_CI_DIRSYMLINK_DISABLE, + SMB_CI_ANNONCE_QUOTA, + + SMB_CI_SECURITY, + SMB_CI_NBSCOPE, + SMB_CI_SYS_CMNT, + SMB_CI_LM_LEVEL, + SMB_CI_MSDCS_DISABLE, + + SMB_CI_ADS_ENABLE, + SMB_CI_ADS_USER, + SMB_CI_ADS_PASSWD, + SMB_CI_ADS_DOMAIN, + SMB_CI_ADS_USER_CONTAINER, + SMB_CI_ADS_SITE, + SMB_CI_ADS_IPLOOKUP, + + SMB_CI_DYNDNS_ENABLE, + SMB_CI_DYNDNS_RETRY_COUNT, + SMB_CI_DYNDNS_RETRY_SEC, + + SMB_CI_MACHINE_PASSWD, + SMB_CI_MAX +} smb_cfg_id_t; + +/* SMF helper functions */ +extern smb_scfhandle_t *smb_smf_scf_init(char *); +extern void smb_smf_scf_fini(smb_scfhandle_t *); +extern int smb_smf_start_transaction(smb_scfhandle_t *); +extern int smb_smf_end_transaction(smb_scfhandle_t *); +extern int smb_smf_set_string_property(smb_scfhandle_t *, char *, char *); +extern int smb_smf_get_string_property(smb_scfhandle_t *, char *, + char *, size_t); +extern int smb_smf_set_integer_property(smb_scfhandle_t *, char *, int64_t); +extern int smb_smf_get_integer_property(smb_scfhandle_t *, char *, int64_t *); +extern int smb_smf_set_boolean_property(smb_scfhandle_t *, char *, uint8_t); +extern int smb_smf_get_boolean_property(smb_scfhandle_t *, char *, uint8_t *); +extern int smb_smf_set_opaque_property(smb_scfhandle_t *, char *, + void *, size_t); +extern int smb_smf_get_opaque_property(smb_scfhandle_t *, char *, + void *, size_t); +extern int smb_smf_create_service_pgroup(smb_scfhandle_t *, char *); +extern int smb_smf_delete_service_pgroup(smb_scfhandle_t *, char *); +extern int smb_smf_create_instance_pgroup(smb_scfhandle_t *, char *); +extern int smb_smf_delete_instance_pgroup(smb_scfhandle_t *, char *); +extern int smb_smf_delete_property(smb_scfhandle_t *, char *); +extern int smb_smf_instance_exists(smb_scfhandle_t *, char *); +extern int smb_smf_instance_create(smb_scfhandle_t *, char *, char *); +extern int smb_smf_instance_delete(smb_scfhandle_t *, char *); +extern smb_scfhandle_t *smb_smf_get_iterator(char *); +extern int smb_smf_get_property(smb_scfhandle_t *, int, char *, char *, + size_t); +extern int smb_smf_set_property(smb_scfhandle_t *, int, char *, char *); + +/* Configuration management functions */ +extern int smb_config_load(void); +extern void smb_config_rdlock(void); +extern void smb_config_wrlock(void); +extern void smb_config_unlock(void); +extern char *smb_config_get(smb_cfg_id_t); +extern char *smb_config_getstr(smb_cfg_id_t); +extern int smb_config_getyorn(smb_cfg_id_t); +extern uint32_t smb_config_getnum(smb_cfg_id_t); + +/* + * smb_config_getenv + * + * Retrieves the property value from SMF. + * Caller must free the returned buffer. + * + */ +extern char *smb_config_getenv(smb_cfg_id_t id); + +extern int smb_config_set(smb_cfg_id_t, char *); +extern int smb_config_setnum(smb_cfg_id_t, uint32_t); +extern uint8_t smb_config_get_fg_flag(void); +extern int smb_config_setenv(smb_cfg_id_t id, char *); +extern char *smb_config_get_localsid(void); +extern int smb_config_secmode_fromstr(char *secmode); +extern char *smb_config_secmode_tostr(int secmode); +extern int smb_config_get_secmode(void); +extern int smb_config_set_secmode(int secmode); +extern int smb_config_set_idmap_domain(char *value); +extern int smb_config_set_idmap_gc(char *value); +extern int smb_config_refresh_idmap(void); + +/* smb_door_client.c */ +typedef struct smb_joininfo { + char domain_name[SMB_PI_MAX_DOMAIN]; + char domain_username[BUF_LEN + 1]; + char domain_passwd[BUF_LEN + 1]; + uint32_t mode; +} smb_joininfo_t; + +/* APIs to communicate with SMB daemon via door calls */ +extern int smbd_set_param(smb_cfg_id_t, char *); +extern int smbd_get_param(smb_cfg_id_t, char *); +extern int smbd_get_security_mode(int *); +extern int smbd_netbios_reconfig(void); +extern uint32_t smb_join(smb_joininfo_t *info); + + +#define SMB_DOMAIN_NOMACHINE_SID -1 +#define SMB_DOMAIN_NODOMAIN_SID -2 + +extern int nt_domain_init(char *resource_domain, uint32_t secmode); + +/* Following set of functions, manipulate WINS server configuration */ +extern int smb_wins_allow_list(char *config_list, char *allow_list); +extern int smb_wins_exclude_list(char *config_list, char *exclude_list); +extern boolean_t smb_wins_is_excluded(in_addr_t ipaddr, + unsigned long *exclude_list, int nexclude); +extern void smb_wins_build_list(char *buf, uint32_t iplist[], int max_naddr); +extern int smb_wins_iplist(char *list, uint32_t iplist[], int max_naddr); + +/* + * Information on a particular domain: the domain name, the + * name of a controller (PDC or BDC) and it's ip address. + */ +typedef struct smb_ntdomain { + char domain[SMB_PI_MAX_DOMAIN_U]; + char server[SMB_PI_MAX_DOMAIN_U]; + uint32_t ipaddr; +} smb_ntdomain_t; + +/* SMB domain information management functions */ +extern void smb_purge_domain_info(void); +extern int smb_is_domain_member(void); +extern uint8_t smb_get_fg_flag(void); +extern void smb_set_domain_member(int set); +extern smb_ntdomain_t *smb_getdomaininfo(uint32_t timeout); +extern void smb_setdomaininfo(char *domain, char *server, uint32_t ipaddr); +extern void smb_logdomaininfo(smb_ntdomain_t *di); +extern uint32_t smb_get_security_mode(void); + +extern int nt_priv_presentable_num(void); + +/* + * Following set of function, handle calls to SMB Kernel driver, via + * Kernel doors interface. + */ +extern uint64_t smb_dwncall_user_num(void); +extern int smb_dwncall_share(int, char *, char *); + +/* + * buffer context structure. This is used to keep track of the buffer + * context. + * + * basep: points to the beginning of the buffer + * curp: points to the current offset + * endp: points to the limit of the buffer + */ +typedef struct { + unsigned char *basep; + unsigned char *curp; + unsigned char *endp; +} smb_ctxbuf_t; + +extern int smb_ctxbuf_init(smb_ctxbuf_t *ctx, unsigned char *buf, + size_t buflen); +extern int smb_ctxbuf_len(smb_ctxbuf_t *ctx); +extern int smb_ctxbuf_printf(smb_ctxbuf_t *ctx, const char *fmt, ...); + +/* Functions to handle SMB daemon communications with idmap service */ +extern int smb_idmap_start(void); +extern void smb_idmap_stop(void); +extern int smb_idmap_restart(void); + +/* Miscellaneous functions */ +extern void hexdump(unsigned char *, int); +extern size_t bintohex(const char *, size_t, char *, size_t); +extern size_t hextobin(const char *, size_t, char *, size_t); +extern char *trim_whitespace(char *buf); +extern void randomize(char *, unsigned); +extern void rand_hash(unsigned char *, size_t, unsigned char *, size_t); + +extern int smb_getdomainname(char *, size_t); +extern int smb_getfqhostname(char *, size_t); +extern int smb_gethostname(char *, size_t, int); +extern int smb_getnetbiosname(char *, size_t); + +void smb_trace(const char *s); +void smb_tracef(const char *fmt, ...); + +/* + * Authentication + */ + +#define SMBAUTH_LM_MAGIC_STR "KGS!@#$%" + +#define SMBAUTH_HASH_SZ 16 /* also LM/NTLM/NTLMv2 Hash size */ +#define SMBAUTH_LM_RESP_SZ 24 /* also NTLM Response size */ +#define SMBAUTH_LM_PWD_SZ 14 /* LM password size */ +#define SMBAUTH_V2_CLNT_CHALLENGE_SZ 8 /* both LMv2 and NTLMv2 */ +#define SMBAUTH_SESSION_KEY_SZ SMBAUTH_HASH_SZ +#define SMBAUTH_HEXHASH_SZ (SMBAUTH_HASH_SZ * 2) + +#define SMBAUTH_FAILURE 1 +#define SMBAUTH_SUCCESS 0 +#define MD_DIGEST_LEN 16 + +/* + * Name Types + * + * The list of names near the end of the data blob (i.e. the ndb_names + * field of the smb_auth_data_blob_t data structure) can be classify into + * the following types: + * + * 0x0000 Indicates the end of the list. + * 0x0001 The name is a NetBIOS machine name (e.g. server name) + * 0x0002 The name is an NT Domain NetBIOS name. + * 0x0003 The name is the server's DNS hostname. + * 0x0004 The name is a W2K Domain name (a DNS name). + */ +#define SMBAUTH_NAME_TYPE_LIST_END 0x0000 +#define SMBAUTH_NAME_TYPE_SERVER_NETBIOS 0x0001 +#define SMBAUTH_NAME_TYPE_DOMAIN_NETBIOS 0x0002 +#define SMBAUTH_NAME_TYPE_SERVER_DNS 0x0003 +#define SMBAUTH_NAME_TYPE_DOMAIN_DNS 0x0004 + +/* + * smb_auth_name_entry_t + * + * Each name entry in the data blob consists of the following 3 fields: + * + * nne_type - name type + * nne_len - the length of the name + * nne_name - the name, in uppercase UCS-2LE Unicode format + */ +typedef struct smb_auth_name_entry { + unsigned short nne_type; + unsigned short nne_len; + mts_wchar_t nne_name[SMB_PI_MAX_DOMAIN * 2]; +} smb_auth_name_entry_t; + +/* + * smb_auth_data_blob + * + * The format of this NTLMv2 data blob structure is as follow: + * + * - Blob Signature 0x01010000 (4 bytes) + * - Reserved (0x00000000) (4 bytes) + * - Timestamp Little-endian, 64-bit signed value representing + * the number of tenths of a microsecond since January 1, 1601. + * (8 bytes) + * - Client Challenge (8 bytes) + * - Unknown1 (4 bytes) + * - List of Target Information (variable length) + * - Unknown2 (4 bytes) + */ +typedef struct smb_auth_data_blob { + unsigned char ndb_signature[4]; + unsigned char ndb_reserved[4]; + uint64_t ndb_timestamp; + unsigned char ndb_clnt_challenge[SMBAUTH_V2_CLNT_CHALLENGE_SZ]; + unsigned char ndb_unknown[4]; + smb_auth_name_entry_t ndb_names[2]; + unsigned char ndb_unknown2[4]; +} smb_auth_data_blob_t; + +#define SMBAUTH_BLOB_MAXLEN (sizeof (smb_auth_data_blob_t)) +#define SMBAUTH_CI_MAXLEN SMBAUTH_LM_RESP_SZ +#define SMBAUTH_CS_MAXLEN (SMBAUTH_BLOB_MAXLEN + SMBAUTH_HASH_SZ) + +/* + * smb_auth_info_t + * + * The structure contains all the authentication information + * needed for the preparaton of the SMBSessionSetupAndx request + * and the user session key. + * + * hash - NTLM hash + * hash_v2 - NTLMv2 hash + * ci_len - the length of the case-insensitive password + * ci - case-insensitive password + * (If NTLMv2 authentication mechanism is used, it + * represents the LMv2 response. Otherwise, it + * is empty.) + * cs_len - the length of the case-sensitive password + * cs - case-sensitive password + * (If NTLMv2 authentication mechanism is used, it + * represents the NTLMv2 response. Otherwise, it + * represents the NTLM response.) + * data_blob - NTLMv2 data blob + */ +typedef struct smb_auth_info { + unsigned char hash[SMBAUTH_HASH_SZ]; + unsigned char hash_v2[SMBAUTH_HASH_SZ]; + unsigned short ci_len; + unsigned char ci[SMBAUTH_CI_MAXLEN]; + unsigned short cs_len; + unsigned char cs[SMBAUTH_CS_MAXLEN]; + int lmcompatibility_lvl; + smb_auth_data_blob_t data_blob; +} smb_auth_info_t; + +extern int smb_getdomainname(char *, size_t); +extern int smb_getfqhostname(char *, size_t); +extern int smb_gethostname(char *, size_t, int); +extern int smb_getnetbiosname(char *, size_t); + +void smb_trace(const char *s); +void smb_tracef(const char *fmt, ...); + +/* + * SMB password management + */ + +#define SMB_PWF_LM 0x01 /* LM hash is present */ +#define SMB_PWF_NT 0x02 /* NT hash is present */ +#define SMB_PWF_DISABLE 0x04 /* Account is disabled */ + +typedef struct smb_passwd { + uid_t pw_uid; + uint32_t pw_flags; + unsigned char pw_lmhash[SMBAUTH_HASH_SZ]; + unsigned char pw_nthash[SMBAUTH_HASH_SZ]; +} smb_passwd_t; + +/* + * Control flags passed to smb_pwd_setcntl + */ +#define SMB_PWC_DISABLE 0x01 +#define SMB_PWC_ENABLE 0x02 +#define SMB_PWC_NOLM 0x04 + +#define SMB_PWE_SUCCESS 0 +#define SMB_PWE_USER_UNKNOWN 1 +#define SMB_PWE_USER_DISABLE 2 +#define SMB_PWE_CLOSE_FAILED 3 +#define SMB_PWE_OPEN_FAILED 4 +#define SMB_PWE_WRITE_FAILED 6 +#define SMB_PWE_UPDATE_FAILED 7 +#define SMB_PWE_STAT_FAILED 8 +#define SMB_PWE_BUSY 9 +#define SMB_PWE_DENIED 10 +#define SMB_PWE_SYSTEM_ERROR 11 +#define SMB_PWE_MAX 12 + +extern smb_passwd_t *smb_pwd_getpasswd(const char *, smb_passwd_t *); +extern int smb_pwd_setpasswd(const char *, const char *); +extern int smb_pwd_setcntl(const char *, int); + +extern int smb_auth_qnd_unicode(mts_wchar_t *dst, char *src, int length); +extern int smb_auth_hmac_md5(unsigned char *data, int data_len, + unsigned char *key, int key_len, unsigned char *digest); + +/* + * A variation on HMAC-MD5 known as HMACT64 is used by Windows systems. + * The HMACT64() function is the same as the HMAC-MD5() except that + * it truncates the input key to 64 bytes rather than hashing it down + * to 16 bytes using the MD5() function. + */ +#define SMBAUTH_HMACT64(D, Ds, K, Ks, digest) \ + smb_auth_hmac_md5(D, Ds, K, (Ks > 64) ? 64 : Ks, digest) + +extern int smb_auth_DES(unsigned char *, int, unsigned char *, int, + unsigned char *, int); + +extern int smb_auth_md4(unsigned char *, unsigned char *, int); +extern int smb_auth_lm_hash(char *, unsigned char *); +extern int smb_auth_ntlm_hash(char *, unsigned char *); + +extern int smb_auth_set_info(char *, char *, + unsigned char *, char *, unsigned char *, + int, int, smb_auth_info_t *); + +extern int smb_auth_gen_session_key(smb_auth_info_t *, unsigned char *); + +boolean_t smb_auth_validate_lm(unsigned char *, uint32_t, smb_passwd_t *, + unsigned char *, int, char *); +boolean_t smb_auth_validate_nt(unsigned char *, uint32_t, smb_passwd_t *, + unsigned char *, int, char *); + +/* + * SMB MAC Signing + */ + +#define SMB_MAC_KEY_SZ (SMBAUTH_SESSION_KEY_SZ + SMBAUTH_CS_MAXLEN) +#define SMB_SIG_OFFS 14 /* signature field offset within header */ +#define SMB_SIG_SIZE 8 /* SMB signature size */ + +/* + * Signing flags: + * + * SMB_SCF_ENABLE Signing is enabled. + * + * SMB_SCF_REQUIRED Signing is enabled and required. + * This flag shouldn't be set if + * SMB_SCF_ENABLE isn't set. + * + * SMB_SCF_STARTED Signing will start after receiving + * the first non-anonymous SessionSetup + * request. + * + * SMB_SCF_KEY_ISSET_THIS_LOGON Indicates whether the MAC key has just + * been set for this logon. (prior to + * sending the SMBSessionSetup request) + * + */ +#define SMB_SCF_ENABLE 0x01 +#define SMB_SCF_REQUIRED 0x02 +#define SMB_SCF_STARTED 0x04 +#define SMB_SCF_KEY_ISSET_THIS_LOGON 0x08 + +/* + * smb_sign_ctx + * + * SMB signing context. + * + * ssc_seqnum sequence number + * ssc_keylen mac key length + * ssc_mid multiplex id - reserved + * ssc_flags flags + * ssc_mackey mac key + * ssc_sign mac signature + * + */ +typedef struct smb_sign_ctx { + unsigned int ssc_seqnum; + unsigned short ssc_keylen; + unsigned short ssc_mid; + unsigned int ssc_flags; + unsigned char ssc_mackey[SMB_MAC_KEY_SZ]; + unsigned char ssc_sign[SMB_SIG_SIZE]; +} smb_sign_ctx_t; + +extern int smb_mac_init(smb_sign_ctx_t *sign_ctx, smb_auth_info_t *auth); +extern int smb_mac_calc(smb_sign_ctx_t *sign_ctx, + const unsigned char *buf, size_t buf_len, unsigned char *mac_sign); +extern int smb_mac_chk(smb_sign_ctx_t *sign_ctx, + const unsigned char *buf, size_t buf_len); +extern int smb_mac_sign(smb_sign_ctx_t *sign_ctx, + unsigned char *buf, size_t buf_len); +extern void smb_mac_inc_seqnum(smb_sign_ctx_t *sign_ctx); +extern void smb_mac_dec_seqnum(smb_sign_ctx_t *sign_ctx); + +/* + * Each domain is categorized using the enum values below. + * The local domain refers to the local machine and is named + * after the local hostname. The primary domain is the domain + * that the system joined. All other domains are either + * trusted or untrusted, as defined by the primary domain PDC. + * + * This enum must be kept in step with the table of strings + * in ntdomain.c. + */ +typedef enum nt_domain_type { + NT_DOMAIN_NULL, + NT_DOMAIN_BUILTIN, + NT_DOMAIN_LOCAL, + NT_DOMAIN_PRIMARY, + NT_DOMAIN_ACCOUNT, + NT_DOMAIN_TRUSTED, + NT_DOMAIN_UNTRUSTED, + NT_DOMAIN_NUM_TYPES +} nt_domain_type_t; + + +/* + * This is the information that is held about each domain. The database + * is a linked list that is threaded through the domain structures. As + * the number of domains in the database should be small (32 max), this + * should be sufficient. + */ +typedef struct nt_domain { + struct nt_domain *next; + nt_domain_type_t type; + char *name; + nt_sid_t *sid; +} nt_domain_t; + +nt_domain_t *nt_domain_new(nt_domain_type_t type, char *name, nt_sid_t *sid); +void nt_domain_delete(nt_domain_t *domain); +nt_domain_t *nt_domain_add(nt_domain_t *new_domain); +void nt_domain_remove(nt_domain_t *domain); +void nt_domain_flush(nt_domain_type_t domain_type); +void nt_domain_sync(void); +char *nt_domain_xlat_type(nt_domain_type_t domain_type); +nt_domain_type_t nt_domain_xlat_type_name(char *type_name); +nt_domain_t *nt_domain_lookup_name(char *domain_name); +nt_domain_t *nt_domain_lookup_sid(nt_sid_t *domain_sid); +nt_domain_t *nt_domain_lookupbytype(nt_domain_type_t type); +nt_sid_t *nt_domain_local_sid(void); + +#define SMB_GROUP_PER_LIST 5 + +/* + * This structure takes different args passed from the client/server routines + * of the SMB local group door service. Extend this structure if a new type + * client paramater needs to be passed. + */ +typedef struct ntgrp_dr_arg { + char *gname; + char *desc; + char *member; + char *newgname; + uint32_t privid; + uint32_t priv_attr; + int offset; + char *scope; + int type; + int count; + uint32_t ntstatus; +} ntgrp_dr_arg_t; + +typedef struct ntgrp { + DWORD rid; /* Rid of the group */ + char *name; /* Name of the group */ + char *desc; /* Desc of gruup */ + char *type; /* sid_name_use */ + char *sid; /* Sid */ + DWORD attr; /* Attribute */ +} ntgrp_t; + +typedef struct ntgrp_list { + int cnt; + ntgrp_t groups[SMB_GROUP_PER_LIST]; +} ntgrp_list_t; + +typedef char *members_list; +typedef struct ntgrp_member_list { + DWORD rid; /* Rid of the group in which members belong */ + int cnt; /* members */ + members_list members[SMB_GROUP_PER_LIST]; +} ntgrp_member_list_t; + +typedef struct ntpriv { + DWORD id; /* Id of priv */ + char *name; /* Name of priv */ +} ntpriv_t; +typedef ntpriv_t *privs_t; + +typedef struct ntpriv_list { + int cnt; /* Number of privs */ + privs_t privs[ANY_SIZE_ARRAY]; /* privs only presentable ones */ +} ntpriv_list_t; + + +/* the xdr functions */ +extern bool_t xdr_ntgrp_dr_arg_t(XDR *, ntgrp_dr_arg_t *); +extern bool_t xdr_ntgrp_t(XDR *, ntgrp_t *); +extern bool_t xdr_ntgrp_list_t(XDR *, ntgrp_list_t *); +extern bool_t xdr_members_list(XDR *, members_list *); +extern bool_t xdr_ntgrp_member_list_t(XDR *, ntgrp_member_list_t *); +extern bool_t xdr_ntpriv_t(XDR *, ntpriv_t *); +extern bool_t xdr_privs_t(XDR *, privs_t *); +extern bool_t xdr_ntpriv_list_t(XDR *, ntpriv_list_t *); + +extern void smb_group_free_memberlist(ntgrp_member_list_t *, int); +extern void smb_group_free_list(ntgrp_list_t *, int); +extern void smb_group_free_privlist(ntpriv_list_t *, int); + +extern uint32_t smb_group_add(char *, char *); +extern uint32_t smb_group_modify(char *, char *, char *); +extern uint32_t smb_group_delete(char *); +extern uint32_t smb_group_member_remove(char *, char *); +extern uint32_t smb_group_member_add(char *, char *); +extern uint32_t smb_group_priv_num(int *); +extern uint32_t smb_group_priv_list(ntpriv_list_t **); +extern uint32_t smb_group_priv_get(char *, uint32_t, uint32_t *); +extern uint32_t smb_group_priv_set(char *, uint32_t, uint32_t); +extern uint32_t smb_group_count(int *); +extern uint32_t smb_group_list(int, ntgrp_list_t **, char *, int); +extern uint32_t smb_group_member_count(char *, int *); +extern uint32_t smb_group_member_list(char *, int, ntgrp_member_list_t **); + +extern char *smb_dr_encode_grp_privlist(uint32_t, ntpriv_list_t *, size_t *); +extern ntpriv_list_t *smb_dr_decode_grp_privlist(char *, size_t); + +extern char *smb_dr_encode_grp_list(uint32_t, ntgrp_list_t *, size_t *); +extern ntgrp_list_t *smb_dr_decode_grp_list(char *, size_t); + +extern char *smb_dr_encode_grp_memberlist(uint32_t, ntgrp_member_list_t *, + size_t *); +extern ntgrp_member_list_t *smb_dr_decode_grp_memberlist(char *buf, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBSMB_H */ diff --git a/usr/src/lib/smbsrv/libsmb/common/llib-lsmb b/usr/src/lib/smbsrv/libsmb/common/llib-lsmb new file mode 100644 index 000000000000..e900ce80471a --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/llib-lsmb @@ -0,0 +1,31 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/*LINTLIBRARY*/ +/*PROTOLIB1*/ + +#include diff --git a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers new file mode 100644 index 000000000000..05c89f93ff80 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers @@ -0,0 +1,325 @@ +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +SUNWprivate { + global: + bintohex; + codepage_islower; + codepage_isupper; + codepage_tolower; + codepage_toupper; + hexdump; + hextobin; + ht_add_item; + ht_clean_table; + ht_clear_delete; + ht_create_table; + ht_destroy_table; + ht_find_item; + ht_findfirst; + ht_findnext; + ht_findnext; + ht_get_total_items; + ht_mark_delete; + ht_register_callback; + ht_remove_item; + ht_replace_item; + ht_set_cmpfn; + smb_msgbuf_base; + smb_msgbuf_decode; + smb_msgbuf_dword_align; + smb_msgbuf_encode; + smb_msgbuf_fclear; + smb_msgbuf_fset; + smb_msgbuf_has_space; + smb_msgbuf_init; + smb_msgbuf_size; + smb_msgbuf_term; + smb_msgbuf_used; + smb_msgbuf_word_align; + mts_mbstos; + mts_mbstowcs; + mts_mbtowc; + mts_sbequiv_strlen; + mts_stombs; + mts_wcequiv_strlen; + mts_wcstombs; + mts_wctomb; + netr_client_mkabsolute; + nt_builtin_findfirst; + nt_builtin_findnext; + nt_builtin_fini; + nt_builtin_init; + nt_builtin_is_wellknown; + nt_builtin_lookup; + nt_builtin_lookup_domain; + nt_builtin_lookup_name; + nt_builtin_lookup_sid; + nt_domain_add; + nt_domain_flush; + nt_domain_init; + nt_domain_local_sid; + nt_domain_lookup_name; + nt_domain_lookup_sid; + nt_domain_lookupbytype; + nt_domain_new; + nt_sid_dup; + nt_sid_format2; + nt_sid_format; + nt_sid_gen_null_sid; + nt_sid_get_rid; + nt_sid_is_builtin; + nt_sid_is_equal; + nt_sid_is_indomain; + nt_sid_is_local; + nt_sid_is_valid; + nt_sid_length; + nt_sid_logf; + nt_sid_name_use; + nt_sid_splice; + nt_sid_split; + nt_sid_strtosid; + oem_get_smb_cpid; + oem_get_telnet_cpid; + oem_language_set; + oemstounicodes; + rand_hash; + randomize; + smb_auth_DES; + smb_auth_gen_session_key; + smb_auth_ntlm_hash; + smb_auth_qnd_unicode; + smb_auth_set_info; + smb_auth_validate_lm; + smb_auth_validate_nt; + smb_config_get; + smb_config_get_fg_flag; + smb_config_get_localsid; + smb_config_get_secmode; + smb_config_getenv; + smb_config_getnum; + smb_config_getstr; + smb_config_getyorn; + smb_config_load; + smb_config_rdlock; + smb_config_refresh_idmap; + smb_config_secmode_fromstr; + smb_config_secmode_tostr; + smb_config_set; + smb_config_set_idmap_domain; + smb_config_set_idmap_gc; + smb_config_set_secmode; + smb_config_setenv; + smb_config_setnum; + smb_config_unlock; + smb_config_wrlock; + smb_ctxbuf_init; + smb_ctxbuf_len; + smb_ctxbuf_printf; + smb_dr_decode_arg_get_token; + smb_dr_decode_common; + smb_dr_decode_finish; + smb_dr_decode_grp_list; + smb_dr_decode_grp_memberlist; + smb_dr_decode_grp_privlist; + smb_dr_decode_start; + smb_dr_decode_string; + smb_dr_encode_common; + smb_dr_encode_finish; + smb_dr_encode_grp_list; + smb_dr_encode_grp_memberlist; + smb_dr_encode_grp_privlist; + smb_dr_encode_res_token; + smb_dr_encode_start; + smb_dr_encode_string; + smb_dr_free_string; + smb_dr_get_BYTE; + smb_dr_get_buf; + smb_dr_get_dword; + smb_dr_get_int32; + smb_dr_get_lmshare; + smb_dr_get_lmshr_iterator; + smb_dr_get_lmshr_list; + smb_dr_get_opcode; + smb_dr_get_res_stat; + smb_dr_get_short; + smb_dr_get_string; + smb_dr_get_uint32; + smb_dr_get_uint64; + smb_dr_get_ushort; + smb_dr_get_word; + smb_dr_put_BYTE; + smb_dr_put_buf; + smb_dr_put_dword; + smb_dr_put_int32; + smb_dr_put_kconfig; + smb_dr_put_lmshare; + smb_dr_put_lmshr_iterator; + smb_dr_put_lmshr_list; + smb_dr_put_short; + smb_dr_put_string; + smb_dr_put_uint32; + smb_dr_put_uint64; + smb_dr_put_ushort; + smb_dr_put_word; + smb_dr_set_opcode; + smb_dr_set_res_stat; + smb_dr_ulist_free; + smb_dwncall_get_users; + smb_dwncall_install_callback; + smb_dwncall_share; + smb_dwncall_user_num; + smb_get_fg_flag; + smb_get_security_mode; + smb_getdomaininfo; + smb_getdomainname; + smb_getfqhostname; + smb_gethostname; + smb_getnetbiosname; + smb_group_add; + smb_group_count; + smb_group_delete; + smb_group_free_list; + smb_group_free_memberlist; + smb_group_free_privlist; + smb_group_list; + smb_group_member_add; + smb_group_member_count; + smb_group_member_list; + smb_group_member_remove; + smb_group_modify; + smb_group_priv_get; + smb_group_priv_list; + smb_group_priv_num; + smb_group_priv_set; + smb_idmap_batch_create; + smb_idmap_batch_destroy; + smb_idmap_batch_getid; + smb_idmap_batch_getmappings; + smb_idmap_batch_getsid; + smb_idmap_getsid; + smb_idmap_restart; + smb_idmap_start; + smb_idmap_stop; + smb_is_domain_member; + smb_join; + smb_load_kconfig; + smb_mac_chk; + smb_mac_dec_seqnum; + smb_mac_inc_seqnum; + smb_mac_init; + smb_mac_sign; + smb_match83; + smb_match; + smb_match_ci; + smb_priv_getbyname; + smb_priv_getbyvalue; + smb_priv_presentable_ids; + smb_priv_presentable_num; + smb_privset_copy; + smb_privset_enable; + smb_privset_free; + smb_privset_init; + smb_privset_log; + smb_privset_new; + smb_privset_query; + smb_privset_size; + smb_privset_validate; + smb_purge_domain_info; + smb_trace; + smb_tracef; + xdr_ntgrp_dr_arg_t; + xdr_ntgrp_list_t; + xdr_ntgrp_member_list_t; + xdr_ntpriv_list_t; + smb_pwd_getpasswd; + smb_pwd_setcntl; + smb_pwd_setpasswd; + smb_set_domain_member; + smb_setdomaininfo; + smb_smf_create_instance_pgroup; + smb_smf_create_service_pgroup; + smb_smf_delete_instance_pgroup; + smb_smf_delete_property; + smb_smf_delete_service_pgroup; + smb_smf_end_transaction; + smb_smf_get_boolean_property; + smb_smf_get_integer_property; + smb_smf_get_iterator; + smb_smf_get_opaque_property; + smb_smf_get_string_property; + smb_smf_instance_create; + smb_smf_instance_delete; + smb_smf_instance_exists; + smb_smf_scf_fini; + smb_smf_scf_init; + smb_smf_set_boolean_property; + smb_smf_set_integer_property; + smb_smf_set_opaque_property; + smb_smf_set_string_property; + smb_smf_start_transaction; + smb_token_log; + smb_token_mkselfrel; + smb_token_print; + smb_token_query_privilege; + smb_trace; + smb_tracef; + smb_wins_allow_list; + smb_wins_build_list; + smb_wins_exclude_list; + smb_wins_iplist; + smb_wins_is_excluded; + smbd_get_param; + smbd_get_security_mode; + smbd_netbios_reconfig; + smbd_set_param; + smbnative_lm_value; + smbnative_os_value; + smbnative_pdc_value; + strcanon; + strsep; + strsubst; + trim_whitespace; + unicodestooems; + utf8_isstrascii; + utf8_isstrlwr; + utf8_isstrupr; + utf8_strcasecmp; + utf8_strlwr; + utf8_strncasecmp; + utf8_strupr; + xdr_ntgrp_dr_arg_t; + xdr_ntgrp_list_t; + xdr_ntgrp_member_list_t; + xdr_ntpriv_list_t; + xdr_smb_dr_bytes_t; + xdr_smb_dr_string_t; + xdr_smb_dr_ulist_t; + xdr_smb_dr_user_ctx_t; + xlate_nt_status; + local: + *; +}; diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_api_door_calls.c b/usr/src/lib/smbsrv/libsmb/common/smb_api_door_calls.c new file mode 100644 index 000000000000..4a84b5d462dc --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_api_door_calls.c @@ -0,0 +1,864 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Door calls invoked by CLIs to obtain various SMB door service provided + * by SMB daemon. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* indexed via opcode (smb_dr_opcode_t) */ +char *smbapi_desc[] = { + "", + "", + "", + "", + "SmbapiUserList", + "SmbGroupAdd", + "SmbGroupDelete", + "SmbGroupAddMember", + "SmbGroupRemoveMember", + "SmbGroupGetCount", + "SmbGroupGetCacheSize", + "SmbGroupModify", + "SmbGroupPresentablePrivNum", + "SmbGroupPresentablePriv", + "SmbGroupGetPriv", + "SmbGroupSetPriv", + "SmbGroupListGroups", + "SmbGroupListMembers", + "SmbGroupMembersCount", + 0 +}; + +/* + * This function will return information on the connected users + * starting at the given offset. + * + * At most 50 users (i.e. SMB_DR_MAX_USER) will be returned via this + * function. Multiple calls might be needed to obtain all connected + * users. + * + * smb_dr_ulist_free must be called to free memory allocated for the + * account and workstation fields of each user in the returned list. + */ +int +smb_api_ulist(int offset, smb_dr_ulist_t *users) +{ + char *buf, *rbufp; + size_t buflen, rbufsize; + int rc = -1; + uint_t opcode = SMB_DR_USER_LIST; + int fd; + + bzero(users, sizeof (smb_dr_ulist_t)); + buf = smb_dr_encode_common(opcode, &offset, xdr_uint32_t, &buflen); + if (!buf) + return (-1); + + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1) + return (-1); + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + if (rbufp) { + rc = smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_smb_dr_ulist_t, users); + + } + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + (void) close(fd); + return (rc); +} + +/* Routines for SMB Group Door Client APIs */ +uint32_t +smb_group_add(char *gname, char *desc) +{ + ntgrp_dr_arg_t *args; + char *buf, *rbufp; + size_t buflen, rbufsize; + uint32_t rc = NT_STATUS_UNSUCCESSFUL; + int opcode = SMB_DR_GROUP_ADD; + int fd; + + if ((gname == 0) || (*gname == 0)) { + syslog(LOG_ERR, "%s: invalid parameter(s)", + smbapi_desc[opcode]); + return (NT_STATUS_INVALID_PARAMETER); + } + + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1) + return (NT_STATUS_INTERNAL_ERROR); + + /* Encode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, "%s: cannot allocate memory", + smbapi_desc[opcode]); + (void) close(fd); + return (NT_STATUS_NO_MEMORY); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + args->gname = gname; + args->desc = desc; + if ((buf = smb_dr_encode_common(opcode, args, + xdr_ntgrp_dr_arg_t, &buflen)) == 0) { + syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]); + free(args); + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + free(args); + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + + /* Decode Result. */ + if (rbufp) { + if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, &rc) != 0) { + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + (void) close(fd); + return (rc); +} + +uint32_t +smb_group_delete(char *gname) +{ + char *buf, *rbufp; + size_t buflen, rbufsize; + uint32_t rc = NT_STATUS_UNSUCCESSFUL; + int opcode = SMB_DR_GROUP_DELETE; + int fd; + + if ((gname == 0) || (*gname == 0)) { + syslog(LOG_ERR, "%s: invalid parameter(s)", + smbapi_desc[opcode]); + return (NT_STATUS_INVALID_PARAMETER); + } + + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1) + return (NT_STATUS_INTERNAL_ERROR); + + /* Encode */ + if ((buf = smb_dr_encode_string(opcode, gname, &buflen)) == 0) { + syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]); + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + + /* Decode Result. */ + if (rbufp) { + if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, &rc) != 0) { + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + (void) close(fd); + return (rc); +} + +uint32_t +smb_group_member_add(char *gname, char *member) +{ + ntgrp_dr_arg_t *args; + char *buf, *rbufp; + size_t buflen, rbufsize; + uint32_t rc = NT_STATUS_UNSUCCESSFUL; + int opcode = SMB_DR_GROUP_MEMBER_ADD; + int fd; + + if ((gname == 0) || (*gname == 0) || + (member == 0) || (*member == 0)) { + syslog(LOG_ERR, "%s: invalid parameter(s)", + smbapi_desc[opcode]); + return (NT_STATUS_INVALID_PARAMETER); + } + + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1) + return (NT_STATUS_INTERNAL_ERROR); + + /* Encode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, "%s: cannot allocate memory", + smbapi_desc[opcode]); + (void) close(fd); + return (NT_STATUS_NO_MEMORY); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + args->gname = gname; + args->member = member; + if ((buf = smb_dr_encode_common(opcode, args, xdr_ntgrp_dr_arg_t, + &buflen)) == 0) { + syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]); + free(args); + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + free(args); + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + + /* Decode Result. */ + if (rbufp) { + if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, &rc) != 0) { + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + (void) close(fd); + return (rc); +} + +uint32_t +smb_group_member_remove(char *gname, char *member) +{ + ntgrp_dr_arg_t *args; + char *buf, *rbufp; + size_t buflen, rbufsize; + uint32_t rc = NT_STATUS_UNSUCCESSFUL; + int opcode = SMB_DR_GROUP_MEMBER_REMOVE; + int fd; + + if ((gname == 0) || (*gname == 0) || + (member == 0) || (*member == 0)) { + syslog(LOG_ERR, "%s: invalid parameter(s)", + smbapi_desc[opcode]); + return (NT_STATUS_INVALID_PARAMETER); + } + + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1) + return (NT_STATUS_INTERNAL_ERROR); + + /* Encode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, "%s: cannot allocate memory", + smbapi_desc[opcode]); + (void) close(fd); + return (NT_STATUS_NO_MEMORY); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + args->gname = gname; + args->member = member; + if ((buf = smb_dr_encode_common(opcode, args, xdr_ntgrp_dr_arg_t, + &buflen)) == 0) { + syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]); + free(args); + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + free(args); + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + + /* Decode Result. */ + if (rbufp) { + if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, &rc) != 0) { + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + (void) close(fd); + return (rc); +} + + +uint32_t +smb_group_count(int *cnt) +{ + char *buf, *rbufp; + size_t buflen, rbufsize; + uint32_t rc = NT_STATUS_UNSUCCESSFUL; + uint_t opcode = SMB_DR_GROUP_COUNT; + int fd; + + if (cnt == 0) { + syslog(LOG_ERR, "%s: invalid parameter(s)", + smbapi_desc[opcode]); + return (NT_STATUS_INVALID_PARAMETER); + } + *cnt = 0; + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, + smbapi_desc[opcode]) == -1) + return (NT_STATUS_INTERNAL_ERROR); + + if ((buf = smb_dr_set_opcode(opcode, &buflen)) == 0) { + (void) close(fd); + return (NT_STATUS_INVALID_PARAMETER); + } + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + + /* Decode Result */ + if (rbufp) { + if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, cnt) != 0) { + (void) close(fd); + return (NT_STATUS_INVALID_PARAMETER); + } + rc = NT_STATUS_SUCCESS; + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + (void) close(fd); + return (rc); +} + +uint32_t +smb_group_cachesize(int *sz) +{ + char *buf, *rbufp; + size_t buflen, rbufsize; + uint32_t rc = NT_STATUS_UNSUCCESSFUL; + uint_t opcode = SMB_DR_GROUP_CACHE_SIZE; + int fd; + + if (sz == 0) { + syslog(LOG_ERR, "%s: invalid parameter(s)", + smbapi_desc[opcode]); + return (NT_STATUS_INVALID_PARAMETER); + } + *sz = 0; + + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, + smbapi_desc[opcode]) == -1) + return (NT_STATUS_INTERNAL_ERROR); + + if ((buf = smb_dr_set_opcode(opcode, &buflen)) == 0) { + (void) close(fd); + return (NT_STATUS_INVALID_PARAMETER); + } + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + + /* Decode Result */ + if (rbufp) { + if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, sz) != 0) { + (void) close(fd); + return (NT_STATUS_INVALID_PARAMETER); + } + rc = NT_STATUS_SUCCESS; + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + (void) close(fd); + return (rc); +} + +uint32_t +smb_group_modify(char *gname, char *newgname, char *desc) +{ + ntgrp_dr_arg_t *args; + char *buf, *rbufp; + size_t buflen, rbufsize; + uint32_t rc = NT_STATUS_UNSUCCESSFUL; + int opcode = SMB_DR_GROUP_MODIFY; + int fd; + + if ((gname == 0) || (*gname == 0) || + (newgname == 0) || (*newgname == 0)) { + syslog(LOG_ERR, "%s: invalid parameter(s)", + smbapi_desc[opcode]); + return (NT_STATUS_INVALID_PARAMETER); + } + + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1) + return (NT_STATUS_INTERNAL_ERROR); + + /* Encode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, "%s: cannot allocate memory", + smbapi_desc[opcode]); + (void) close(fd); + return (NT_STATUS_NO_MEMORY); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + args->gname = gname; + args->desc = desc; + args->newgname = newgname; + if ((buf = smb_dr_encode_common(opcode, args, xdr_ntgrp_dr_arg_t, + &buflen)) == 0) { + syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]); + free(args); + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + free(args); + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + + /* Decode Result. */ + if (rbufp) { + if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, &rc) != 0) { + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + (void) close(fd); + return (rc); +} + +uint32_t +smb_group_priv_num(int *num) +{ + char *buf, *rbufp; + size_t buflen, rbufsize; + uint32_t rc = NT_STATUS_UNSUCCESSFUL; + int opcode = SMB_DR_GROUP_PRIV_NUM; + int fd; + + if (num == 0) { + syslog(LOG_ERR, "%s: invalid parameter(s)", + smbapi_desc[opcode]); + return (NT_STATUS_INVALID_PARAMETER); + } + *num = 0; + + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, + smbapi_desc[opcode]) == -1) + return (NT_STATUS_INTERNAL_ERROR); + + if ((buf = smb_dr_set_opcode(opcode, &buflen)) == 0) { + (void) close(fd); + return (NT_STATUS_INVALID_PARAMETER); + } + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + + /* Decode Result */ + if (rbufp) { + if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, num) != 0) { + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + rc = NT_STATUS_SUCCESS; + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + (void) close(fd); + return (rc); +} + +uint32_t +smb_group_priv_list(ntpriv_list_t **list) +{ + char *buf, *rbufp; + size_t buflen, rbufsize; + uint32_t rc = NT_STATUS_UNSUCCESSFUL; + int opcode = SMB_DR_GROUP_PRIV_LIST; + int fd; + *list = NULL; + + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, + smbapi_desc[opcode]) == -1) + return (NT_STATUS_INTERNAL_ERROR); + + if ((buf = smb_dr_set_opcode(opcode, &buflen)) == 0) { + (void) close(fd); + return (NT_STATUS_INVALID_PARAMETER); + } + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + + /* Decode Result */ + if (rbufp) { + if ((*list = smb_dr_decode_grp_privlist( + rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET)) == 0) { + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + rc = NT_STATUS_SUCCESS; + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + (void) close(fd); + return (rc); +} + +uint32_t +smb_group_priv_get(char *gname, uint32_t privid, uint32_t *privval) +{ + char *buf, *rbufp; + size_t buflen, rbufsize; + ntgrp_dr_arg_t *args; + uint32_t rc = NT_STATUS_UNSUCCESSFUL; + int opcode = SMB_DR_GROUP_PRIV_GET; + int fd; + uint32_t retval; + + *privval = SE_PRIVILEGE_DISABLED; + + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1) + return (NT_STATUS_INTERNAL_ERROR); + + /* Encode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, "%s: cannot allocate memory", + smbapi_desc[opcode]); + (void) close(fd); + return (NT_STATUS_NO_MEMORY); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + args->gname = gname; + args->privid = privid; + if ((buf = smb_dr_encode_common(opcode, args, xdr_ntgrp_dr_arg_t, + &buflen)) == 0) { + syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]); + free(args); + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + free(args); + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + + /* Decode Result. */ + if (rbufp) { + if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, + &retval) != 0) { + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + *privval = retval; + rc = NT_STATUS_SUCCESS; + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + (void) close(fd); + return (rc); +} + +uint32_t +smb_group_priv_set(char *gname, uint32_t privid, uint32_t priv_attr) +{ + char *buf, *rbufp; + size_t buflen, rbufsize; + ntgrp_dr_arg_t *args; + uint32_t rc = NT_STATUS_UNSUCCESSFUL; + int opcode = SMB_DR_GROUP_PRIV_SET; + int fd; + + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1) + return (NT_STATUS_INTERNAL_ERROR); + + /* Encode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, "%s: cannot allocate memory", + smbapi_desc[opcode]); + (void) close(fd); + return (NT_STATUS_NO_MEMORY); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + args->gname = gname; + args->privid = privid; + args->priv_attr = priv_attr; + if ((buf = smb_dr_encode_common(opcode, args, xdr_ntgrp_dr_arg_t, + &buflen)) == 0) { + syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]); + free(args); + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + free(args); + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + + /* Decode Result. */ + if (rbufp) { + if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, &rc) != 0) { + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + (void) close(fd); + return (rc); +} + +uint32_t +smb_group_list(int offset, ntgrp_list_t **list, char *scope, int type) +{ + char *buf, *rbufp; + size_t buflen, rbufsize; + ntgrp_dr_arg_t *args; + uint32_t rc = NT_STATUS_UNSUCCESSFUL; + int opcode = SMB_DR_GROUP_LIST; + int fd; + *list = NULL; + + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1) + return (NT_STATUS_INTERNAL_ERROR); + + /* Encode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, "%s: cannot allocate memory", + smbapi_desc[opcode]); + (void) close(fd); + return (NT_STATUS_NO_MEMORY); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + args->offset = offset; + args->type = type; + args->scope = scope; + if ((buf = smb_dr_encode_common(opcode, args, xdr_ntgrp_dr_arg_t, + &buflen)) == 0) { + syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]); + free(args); + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + free(args); + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + + /* Decode Result. */ + if (rbufp) { + if ((*list = smb_dr_decode_grp_list(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET)) == 0) { + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + rc = NT_STATUS_SUCCESS; + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + (void) close(fd); + return (rc); +} + +uint32_t +smb_group_member_list(char *gname, int offset, ntgrp_member_list_t **members) +{ + char *buf, *rbufp; + size_t buflen, rbufsize; + ntgrp_dr_arg_t *args; + uint32_t rc = NT_STATUS_UNSUCCESSFUL; + int opcode = SMB_DR_GROUP_MEMBER_LIST; + int fd; + *members = NULL; + + if ((gname == 0) || (*gname == 0)) { + syslog(LOG_ERR, "%s: invalid parameter(s)", + smbapi_desc[opcode]); + return (NT_STATUS_INVALID_PARAMETER); + } + + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1) + return (NT_STATUS_INTERNAL_ERROR); + + /* Encode */ + if ((args = (ntgrp_dr_arg_t *)malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, "%s: cannot allocate memory for ret_mem_list", + smbapi_desc[opcode]); + (void) close(fd); + return (NT_STATUS_NO_MEMORY); + } + bzero(args, sizeof (ntgrp_dr_arg_t)); + args->gname = gname; + args->offset = offset; + if ((buf = smb_dr_encode_common(opcode, args, xdr_ntgrp_dr_arg_t, + &buflen)) == 0) { + syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]); + free(args); + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + free(args); + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + + /* Decode Result. */ + if (rbufp) { + if ((*members = smb_dr_decode_grp_memberlist( + rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET)) == 0) { + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + rc = NT_STATUS_SUCCESS; + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + (void) close(fd); + return (rc); +} + +uint32_t +smb_group_member_count(char *gname, int *cnt) +{ + char *buf, *rbufp; + size_t buflen, rbufsize; + ntgrp_dr_arg_t *dec_args; + uint32_t rc = NT_STATUS_UNSUCCESSFUL; + int opcode = SMB_DR_GROUP_MEMBER_COUNT; + int fd; + + if ((gname == 0) || (*gname == 0) || (cnt == 0)) { + syslog(LOG_ERR, "%s: invalid parameter(s)", + smbapi_desc[opcode]); + return (NT_STATUS_INVALID_PARAMETER); + } + + if (smb_dr_clnt_open(&fd, SMB_DR_SVC_NAME, smbapi_desc[opcode]) == -1) + return (NT_STATUS_INTERNAL_ERROR); + + /* Encode */ + if ((buf = smb_dr_encode_string(opcode, gname, &buflen)) == 0) { + syslog(LOG_ERR, "%s: Encode error", smbapi_desc[opcode]); + (void) close(fd); + return (NT_STATUS_INTERNAL_ERROR); + } + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smbapi_desc[opcode]); + + /* Decode Result. */ + if ((dec_args = (ntgrp_dr_arg_t *) + malloc(sizeof (ntgrp_dr_arg_t))) == 0) { + syslog(LOG_ERR, "%s: cannot allocate memory", + smbapi_desc[opcode]); + (void) close(fd); + return (NT_STATUS_NO_MEMORY); + } + bzero(dec_args, sizeof (ntgrp_dr_arg_t)); + if (rbufp) { + if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_ntgrp_dr_arg_t, dec_args) + != 0) { + free(dec_args); + (void) close(fd); + return (dec_args->ntstatus); + } + } + *cnt = dec_args->count; + rc = dec_args->ntstatus; + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + free(dec_args); + (void) close(fd); + return (rc); +} + +/* Helper functions for local group door service to free up data structures */ +void +smb_group_free_privlist(ntpriv_list_t *list, int deletelist) +{ + int i; + if (!list) + return; + if (list->privs != NULL) { + for (i = 0; i < list->cnt; i++) { + if (list->privs[i] != NULL) { + free(list->privs[i]->name); + free(list->privs[i]); + } + } + if (deletelist) + free(list); + } +} + +void +smb_group_free_list(ntgrp_list_t *list, int entries_only) +{ + int i; + + if (!list) { + return; + } + + for (i = 0; i < list->cnt; i++) { + free(list->groups[i].name); + free(list->groups[i].desc); + free(list->groups[i].type); + free(list->groups[i].sid); + } + if (!entries_only) + free(list); +} + +void +smb_group_free_memberlist(ntgrp_member_list_t *members, + int entries_only) +{ + int i; + + if (!members) { + return; + } + + for (i = 0; i < members->cnt; i++) { + free(members->members[i]); + } + if (!entries_only) + free(members); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_auth.c b/usr/src/lib/smbsrv/libsmb/common/smb_auth.c new file mode 100644 index 000000000000..d8950616dbde --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_auth.c @@ -0,0 +1,706 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include + +extern void randomize(char *data, unsigned len); +static uint64_t unix_micro_to_nt_time(struct timeval *unix_time); + +/* + * smb_auth_qnd_unicode + * + * Quick and dirty unicode conversion! + * Returns the length of dst in bytes. + */ +int +smb_auth_qnd_unicode(mts_wchar_t *dst, char *src, int length) +{ + int i; + + unsigned int cpid = oem_get_telnet_cpid(); + unsigned int count; + mts_wchar_t new_char; + + if ((count = oemstounicodes(dst, src, length, cpid)) == 0) { + for (i = 0; i < length; ++i) { + new_char = (mts_wchar_t)src[i] & 0xff; + dst[i] = LE_IN16(&new_char); + } + dst[i] = 0; + count = length; + } + + return (count * sizeof (mts_wchar_t)); +} + +/* + * smb_auth_lmupr + * + * Converts the given LM password to all uppercase. + * The standard strupr cannot + * be used here because lm_pwd doesn't have to be + * nul terminated. + */ +static void +smb_auth_lmupr(unsigned char *lm_pwd) +{ + unsigned char *p = lm_pwd; + int i; + + for (i = 0; (*p) && (i < SMBAUTH_LM_PWD_SZ); i++) { + if (mts_isascii(*p)) { + *p = codepage_toupper(*p); + p++; + } + } +} + +/* + * smb_auth_lm_hash + * + * Source: Implementing CIFS (Chris Hertel) + * + * 1. The password, as entered by user, is either padded with nulls + * or trimmed to 14 bytes. + * . Note that the 14-byte result string is not handled as a + * nul-terminated string. + * . The given password is OEM not Unicode + * + * 2. The 14-byte password is converted to all uppercase + * + * 3. The result is used as key to encrypt the KGS magic string to + * make a 16-byte hash. + */ +int +smb_auth_lm_hash(char *password, unsigned char *lm_hash) +{ + unsigned char lm_pwd[SMBAUTH_LM_PWD_SZ]; + + bzero((void *)lm_pwd, SMBAUTH_LM_PWD_SZ); + (void) strncpy((char *)lm_pwd, password, SMBAUTH_LM_PWD_SZ); + smb_auth_lmupr(lm_pwd); + + return (smb_auth_DES(lm_hash, SMBAUTH_HASH_SZ, lm_pwd, + SMBAUTH_LM_PWD_SZ, (unsigned char *)SMBAUTH_LM_MAGIC_STR, + sizeof (SMBAUTH_LM_MAGIC_STR))); +} + +/* + * smb_auth_lm_response + * + * Create a LM response from the given LM hash and challenge. + * + * Returns SMBAUTH_FAILURE if any problems occur, SMBAUTH_SUCCESS if + * all goes well. + */ +static int +smb_auth_lm_response(unsigned char *hash, + unsigned char *challenge, int clen, + unsigned char *lm_rsp) +{ + unsigned char S21[21]; + + /* + * 14-byte LM Hash should be padded with 5 nul bytes to create + * a 21-byte string to be used in producing LM response + */ + bzero(&S21[SMBAUTH_HASH_SZ], 5); + bcopy(hash, S21, SMBAUTH_HASH_SZ); + + /* padded LM Hash -> LM Response */ + return (smb_auth_DES(lm_rsp, SMBAUTH_LM_RESP_SZ, S21, 21, + challenge, clen)); +} + +/* + * smb_auth_ntlm_hash + * + * Make NTLM Hash (using MD4) from the given password. + * The result will contain a 16-byte NTLM hash. + */ +int +smb_auth_ntlm_hash(char *password, unsigned char *hash) +{ + mts_wchar_t *unicode_password; + int length; + int rc; + + if (password == NULL || hash == NULL) + return (SMBAUTH_FAILURE); + + length = strlen(password); + unicode_password = (mts_wchar_t *) + malloc((length + 1) * sizeof (mts_wchar_t)); + + if (unicode_password == NULL) + return (SMBAUTH_FAILURE); + + length = smb_auth_qnd_unicode(unicode_password, password, length); + rc = smb_auth_md4(hash, (unsigned char *)unicode_password, length); + + free(unicode_password); + return (rc); +} + +/* + * smb_auth_ntlm_response + * + * Make LM/NTLM response from the given LM/NTLM Hash and given + * challenge. + */ +static int +smb_auth_ntlm_response(unsigned char *hash, + unsigned char *challenge, int clen, + unsigned char *ntlm_rsp) +{ + unsigned char S21[21]; + + bcopy(hash, S21, SMBAUTH_HASH_SZ); + bzero(&S21[SMBAUTH_HASH_SZ], 5); + if (smb_auth_DES((unsigned char *)ntlm_rsp, SMBAUTH_LM_RESP_SZ, + S21, 21, challenge, clen) == SMBAUTH_FAILURE) + return (0); + return (SMBAUTH_LM_RESP_SZ); +} + +/* + * smb_auth_gen_data_blob + * + * Fill the NTLMv2 data blob structure with information as described in + * "Implementing CIFS, The Common Internet File System". (pg. 282) + */ +static void +smb_auth_gen_data_blob(smb_auth_data_blob_t *blob, char *ntdomain) +{ + struct timeval now; + + (void) memset(blob->ndb_signature, 1, 2); + (void) memset(&blob->ndb_signature[2], 0, 2); + (void) memset(blob->ndb_reserved, 0, sizeof (blob->ndb_reserved)); + + (void) gettimeofday(&now, 0); + blob->ndb_timestamp = unix_micro_to_nt_time(&now); + randomize((char *)blob->ndb_clnt_challenge, + SMBAUTH_V2_CLNT_CHALLENGE_SZ); + (void) memset(blob->ndb_unknown, 0, sizeof (blob->ndb_unknown)); + blob->ndb_names[0].nne_len = smb_auth_qnd_unicode( + blob->ndb_names[0].nne_name, ntdomain, strlen(ntdomain)); + blob->ndb_names[0].nne_type = SMBAUTH_NAME_TYPE_DOMAIN_NETBIOS; + blob->ndb_names[1].nne_len = 0; + blob->ndb_names[1].nne_type = SMBAUTH_NAME_TYPE_LIST_END; + *blob->ndb_names[1].nne_name = 0; + (void) memset(blob->ndb_unknown2, 0, sizeof (blob->ndb_unknown2)); +} + +/* + * smb_auth_memcpy + * + * It increments the pointer to the destination buffer for the easy of + * concatenation. + */ +static void +smb_auth_memcpy(unsigned char **dstbuf, + unsigned char *srcbuf, + int srcbuf_len) +{ + (void) memcpy(*dstbuf, srcbuf, srcbuf_len); + *dstbuf += srcbuf_len; +} + +/* + * smb_auth_blob_to_string + * + * Prepare the data blob string which will be used in NTLMv2 response + * generation. + * + * Assumption: Caller must allocate big enough buffer to prevent buffer + * overrun. + * + * Returns the len of the data blob string. + */ +static int +smb_auth_blob_to_string(smb_auth_data_blob_t *blob, unsigned char *data_blob) +{ + unsigned char *bufp = data_blob; + + smb_auth_memcpy(&bufp, blob->ndb_signature, + sizeof (blob->ndb_signature)); + smb_auth_memcpy(&bufp, blob->ndb_reserved, + sizeof (blob->ndb_reserved)); + smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_timestamp, + sizeof (blob->ndb_timestamp)); + smb_auth_memcpy(&bufp, blob->ndb_clnt_challenge, + SMBAUTH_V2_CLNT_CHALLENGE_SZ); + smb_auth_memcpy(&bufp, blob->ndb_unknown, sizeof (blob->ndb_unknown)); + smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[0].nne_type, + sizeof (blob->ndb_names[0].nne_type)); + smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[0].nne_len, + sizeof (blob->ndb_names[0].nne_len)); + smb_auth_memcpy(&bufp, (unsigned char *)blob->ndb_names[0].nne_name, + blob->ndb_names[0].nne_len); + smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[1].nne_type, + sizeof (blob->ndb_names[1].nne_type)); + smb_auth_memcpy(&bufp, (unsigned char *)&blob->ndb_names[1].nne_len, + sizeof (blob->ndb_names[1].nne_len)); + smb_auth_memcpy(&bufp, blob->ndb_unknown2, sizeof (blob->ndb_unknown2)); + + /*LINTED E_PTRDIFF_OVERFLOW*/ + return (bufp - data_blob); +} + +/* + * smb_auth_ntlmv2_hash + * + * The NTLM v2 hash will be created from the given NTLM hash, username, + * and the NETBIOS name of the domain. + * + * The NTLMv2 hash will be returned via the ntlmv2_hash parameter which + * will be used in the calculation of the NTLMv2 and LMv2 responses. + */ +static int +smb_auth_ntlmv2_hash(unsigned char *ntlm_hash, + char *username, + char *ntdomain, + unsigned char *ntlmv2_hash) +{ + mts_wchar_t *data; + int data_len; + unsigned char *buf; + int rc; + + if (username == NULL || ntdomain == NULL) + return (SMBAUTH_FAILURE); + + (void) utf8_strupr(username); + (void) utf8_strupr(ntdomain); + + data_len = strlen(username) + strlen(ntdomain); + buf = (unsigned char *)malloc((data_len + 1) * sizeof (char)); + if (buf == NULL) + return (SMBAUTH_FAILURE); + + (void) snprintf((char *)buf, data_len + 1, "%s%s", username, ntdomain); + data = (mts_wchar_t *)malloc((data_len + 1) * sizeof (mts_wchar_t)); + if (data == NULL) { + free(buf); + return (SMBAUTH_FAILURE); + } + + data_len = smb_auth_qnd_unicode(data, (char *)buf, data_len); + rc = SMBAUTH_HMACT64((unsigned char *)data, data_len, ntlm_hash, + SMBAUTH_HASH_SZ, ntlmv2_hash); + + free(buf); + free(data); + return (rc); +} + +/* + * smb_auth_v2_response + * + * Caculates either the LMv2 or NTLMv2 response. + * + * Same algorithm is used for calculating both LMv2 or NTLMv2 responses. + * This routine will return NTLMv2 response if the data blob information + * is passed in as the clnt_data. Otherwise, it will return LMv2 response + * with the 8-byte client challenge(a.k.a blip) as the clnt_data. + * + * (LM/NTLM)v2 response is the hmac-md5 hash of the specified data + * (server challenge + NTLMv2 data blob or LMv2 client challenge) + * using the NTLMv2 hash as the key. + * + * Returns the size of the corresponding v2 response upon success. + * Otherwise, returns -1 on error. + */ +static int +smb_auth_v2_response( + unsigned char *hash, + unsigned char *srv_challenge, int slen, + unsigned char *clnt_data, int clen, + unsigned char *v2_rsp) +{ + unsigned char *hmac_data; + + hmac_data = (unsigned char *)malloc((slen + clen) * sizeof (char)); + if (!hmac_data) { + return (-1); + } + + (void) memcpy(hmac_data, srv_challenge, slen); + (void) memcpy(&hmac_data[slen], clnt_data, clen); + if (SMBAUTH_HMACT64(hmac_data, slen + clen, (unsigned char *)hash, + SMBAUTH_HASH_SZ, (unsigned char *)v2_rsp) != SMBAUTH_SUCCESS) + return (-1); + (void) memcpy(&v2_rsp[SMBAUTH_HASH_SZ], clnt_data, clen); + + free(hmac_data); + return (SMBAUTH_HASH_SZ + clen); +} + +/* + * smb_auth_set_info + * + * Fill the smb_auth_info instance with either NTLM or NTLMv2 related + * authentication information based on the LMCompatibilityLevel. + * + * If the LMCompatibilityLevel equals 2, the SMB Redirector will perform + * NTLM challenge/response authentication which requires the NTLM hash and + * NTLM response. + * + * If the LMCompatibilityLevel is 3 or above, the SMB Redirector will + * perfrom NTLMv2 challenge/response authenticatoin which requires the + * NTLM hash, NTLMv2 hash, NTLMv2 response and LMv2 response. + * + * Returns -1 on error. Otherwise, returns 0 upon success. + */ +int +smb_auth_set_info(char *username, + char *password, + unsigned char *ntlm_hash, + char *domain, + unsigned char *srv_challenge_key, + int srv_challenge_len, + int lmcomp_lvl, + smb_auth_info_t *auth) +{ + unsigned short blob_len; + unsigned char blob_buf[SMBAUTH_BLOB_MAXLEN]; + int rc; + + auth->lmcompatibility_lvl = lmcomp_lvl; + if (lmcomp_lvl == 2) { + auth->ci_len = 0; + *auth->ci = 0; + if (!ntlm_hash) { + if (smb_auth_ntlm_hash(password, auth->hash) != + SMBAUTH_SUCCESS) + return (-1); + } else { + (void) memcpy(auth->hash, ntlm_hash, SMBAUTH_HASH_SZ); + } + + auth->cs_len = smb_auth_ntlm_response(auth->hash, + srv_challenge_key, srv_challenge_len, auth->cs); + } else { + if (!ntlm_hash) { + if (smb_auth_ntlm_hash(password, auth->hash) != + SMBAUTH_SUCCESS) + return (-1); + } else { + (void) memcpy(auth->hash, ntlm_hash, SMBAUTH_HASH_SZ); + } + + if (smb_auth_ntlmv2_hash(auth->hash, username, + domain, auth->hash_v2) != SMBAUTH_SUCCESS) + return (-1); + + /* generate data blob */ + smb_auth_gen_data_blob(&auth->data_blob, domain); + blob_len = smb_auth_blob_to_string(&auth->data_blob, blob_buf); + + /* generate NTLMv2 response */ + rc = smb_auth_v2_response(auth->hash_v2, srv_challenge_key, + srv_challenge_len, blob_buf, blob_len, auth->cs); + + if (rc < 0) + return (-1); + + auth->cs_len = rc; + + /* generate LMv2 response */ + rc = smb_auth_v2_response(auth->hash_v2, srv_challenge_key, + srv_challenge_len, auth->data_blob.ndb_clnt_challenge, + SMBAUTH_V2_CLNT_CHALLENGE_SZ, auth->ci); + + if (rc < 0) + return (-1); + + auth->ci_len = rc; + } + + return (0); +} + +/* + * smb_auth_gen_session_key + * + * Generate the NTLM user session key if LMCompatibilityLevel is 2 or + * NTLMv2 user session key if LMCompatibilityLevel is 3 or above. + * + * NTLM_Session_Key = MD4(NTLM_Hash); + * + * NTLMv2_Session_Key = HMAC_MD5(NTLMv2Hash, 16, NTLMv2_HMAC, 16) + * + * Prior to calling this function, the auth instance should be set + * via smb_auth_set_info(). + * + * Returns the appropriate session key. + */ +int +smb_auth_gen_session_key(smb_auth_info_t *auth, unsigned char *session_key) +{ + int rc; + + if (auth->lmcompatibility_lvl == 2) + rc = smb_auth_md4(session_key, auth->hash, SMBAUTH_HASH_SZ); + else + rc = SMBAUTH_HMACT64((unsigned char *)auth->cs, + SMBAUTH_HASH_SZ, (unsigned char *)auth->hash_v2, + SMBAUTH_SESSION_KEY_SZ, session_key); + + return (rc); +} + +/* 100's of ns between 1/1/1970 and 1/1/1601 */ +#define NT_TIME_BIAS (134774LL * 24LL * 60LL * 60LL * 10000000LL) + +static uint64_t +unix_micro_to_nt_time(struct timeval *unix_time) +{ + uint64_t nt_time; + + nt_time = unix_time->tv_sec; + nt_time *= 10000000; /* seconds to 100ns */ + nt_time += unix_time->tv_usec * 10; + return (nt_time + NT_TIME_BIAS); +} + +static boolean_t +smb_lm_password_ok( + unsigned char *challenge, + uint32_t clen, + unsigned char *lm_hash, + unsigned char *passwd) +{ + unsigned char lm_resp[SMBAUTH_LM_RESP_SZ]; + int rc; + + rc = smb_auth_lm_response(lm_hash, challenge, clen, lm_resp); + if (rc != SMBAUTH_SUCCESS) + return (B_FALSE); + + return (bcmp(lm_resp, passwd, SMBAUTH_LM_RESP_SZ) == 0); +} + +static boolean_t +smb_ntlm_password_ok( + unsigned char *challenge, + uint32_t clen, + unsigned char *ntlm_hash, + unsigned char *passwd) +{ + unsigned char ntlm_resp[SMBAUTH_LM_RESP_SZ]; + int rc; + + rc = smb_auth_ntlm_response(ntlm_hash, challenge, clen, ntlm_resp); + + if (rc != SMBAUTH_LM_RESP_SZ) + return (B_FALSE); + + return (bcmp(ntlm_resp, passwd, SMBAUTH_LM_RESP_SZ) == 0); +} + +static boolean_t +smb_ntlmv2_password_ok( + unsigned char *challenge, + uint32_t clen, + unsigned char *ntlm_hash, + unsigned char *passwd, + int pwdlen, + char *username) +{ + unsigned char *clnt_blob; + int clnt_blob_len; + unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ]; + unsigned char *ntlmv2_resp; + boolean_t ok; + + clnt_blob_len = pwdlen - SMBAUTH_HASH_SZ; + clnt_blob = &passwd[SMBAUTH_HASH_SZ]; + + /* + * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS" + * + * * The NTLMv2 Hash is created from: + * - NTLM hash + * - user's username, and + * - the name of the logon destination(i.e. the NetBIOS name of either + * the SMB server or NT Domain against which the suer is trying to + * authenticate. + * + * (N.L.) With my experience, this is not exactly true. It's really + * tricky how the NTLMv2 hash is generated by the Windows client when + * logging into a standalone server using NTLMv2 challenge / response. + * The NTLMv2 hash is actually created with the destination info="" + * as opposed to the SMB server name mentioned in the book. + */ + if (smb_auth_ntlmv2_hash(ntlm_hash, username, "", ntlmv2_hash) != + SMBAUTH_SUCCESS) { + return (B_FALSE); + } + + ntlmv2_resp = (unsigned char *)malloc(SMBAUTH_HASH_SZ + clnt_blob_len); + if (ntlmv2_resp == NULL) + return (B_FALSE); + + if (smb_auth_v2_response(ntlmv2_hash, challenge, + clen, clnt_blob, clnt_blob_len, ntlmv2_resp) < 0) { + free(ntlmv2_resp); + return (B_FALSE); + } + + ok = (bcmp(passwd, ntlmv2_resp, pwdlen) == 0); + + free(ntlmv2_resp); + return (ok); +} + +static int +smb_lmv2_password_ok( + unsigned char *challenge, + uint32_t clen, + unsigned char *ntlm_hash, + unsigned char *passwd, + char *username) +{ + unsigned char *clnt_challenge; + unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ]; + unsigned char lmv2_resp[SMBAUTH_LM_RESP_SZ]; + + clnt_challenge = &passwd[SMBAUTH_HASH_SZ]; + + /* + * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS" + * + * The NTLMv2 Hash is created from: + * - NTLM hash + * - user's username, and + * - the name of the logon destination(i.e. the NetBIOS name of either + * the SMB server or NT Domain against which the suer is trying to + * authenticate. + * + * (N.L.) With my experience, this is not exactly true. It's really + * tricky how the NTLMv2 hash is generated by the Windows client when + * logging into a standalone server using LMv2 challenge/response. + * The NTLMv2 hash is actually created with the destination info = "" + * as opposed to the SMB server name mentioned in the book. + */ + if (smb_auth_ntlmv2_hash(ntlm_hash, username, "", ntlmv2_hash) != + SMBAUTH_SUCCESS) { + return (B_FALSE); + } + if (smb_auth_v2_response(ntlmv2_hash, challenge, + clen, clnt_challenge, SMBAUTH_V2_CLNT_CHALLENGE_SZ, + lmv2_resp) < 0) { + return (B_FALSE); + } + + return (bcmp(passwd, lmv2_resp, SMBAUTH_LM_RESP_SZ) == 0); +} + +/* + * smb_auth_validate_lm + * + * Validates given LM/LMv2 client response, passed in passwd arg, against + * stored user's password, passed in smbpw + * + * If LM level <=3 server accepts LM responses, otherwise LMv2 + */ +boolean_t +smb_auth_validate_lm( + unsigned char *challenge, + uint32_t clen, + smb_passwd_t *smbpw, + unsigned char *passwd, + int pwdlen, + char *username) +{ + int lmlevel; + boolean_t ok = B_FALSE; + + if (pwdlen != SMBAUTH_LM_RESP_SZ) + return (B_FALSE); + + smb_config_rdlock(); + lmlevel = smb_config_getnum(SMB_CI_LM_LEVEL); + smb_config_unlock(); + + if (lmlevel <= 3) { + ok = smb_lm_password_ok(challenge, clen, smbpw->pw_lmhash, + passwd); + } + + if (!ok) + ok = smb_lmv2_password_ok(challenge, clen, smbpw->pw_nthash, + passwd, username); + + return (ok); +} + +/* + * smb_auth_validate_nt + * + * Validates given NTLM/NTLMv2 client response, passed in passwd arg, against + * stored user's password, passed in smbpw + * + * If LM level <=4 server accepts NTLM/NTLMv2 responses, otherwise only NTLMv2 + */ +boolean_t +smb_auth_validate_nt( + unsigned char *challenge, + uint32_t clen, + smb_passwd_t *smbpw, + unsigned char *passwd, + int pwdlen, + char *username) +{ + int lmlevel; + boolean_t ok; + + smb_config_rdlock(); + lmlevel = smb_config_getnum(SMB_CI_LM_LEVEL); + smb_config_unlock(); + + if ((lmlevel == 5) && (pwdlen <= SMBAUTH_LM_RESP_SZ)) + return (B_FALSE); + + if (pwdlen > SMBAUTH_LM_RESP_SZ) + ok = smb_ntlmv2_password_ok(challenge, clen, + smbpw->pw_nthash, passwd, pwdlen, username); + else + ok = smb_ntlm_password_ok(challenge, clen, + smbpw->pw_nthash, passwd); + + return (ok); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c new file mode 100644 index 000000000000..844789e367b6 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c @@ -0,0 +1,1090 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * CIFS configuration management library + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct smb_cfg_param { + char *sc_pg; + char *sc_name; + int sc_type; + char *sc_value; + uint32_t sc_flags; +} smb_cfg_param_t; + +/* + * config parameter flags + */ +#define SMB_CF_NOTINIT 0x00 /* Not initialized yet */ +#define SMB_CF_DEFINED 0x01 /* Defined/read from env */ +#define SMB_CF_MODIFIED 0x02 /* Has been modified */ +#define SMB_CF_SYSTEM 0x04 /* system; not part of cifs config */ + +#define SMB_CL_NONE 0 +#define SMB_CL_READ 1 +#define SMB_CL_WRITE 2 + +/* idmap SMF fmri and Property Group */ +#define IDMAP_FMRI_PREFIX "system/idmap" +#define MACHINE_SID "machine_sid" +#define MAPPING_DOMAIN "mapping_domain" +#define GLOBAL_CATALOG "global_catalog" +#define IDMAP_PG_NAME "config" + +#define SMB_SECMODE_WORKGRP_STR "workgroup" +#define SMB_SECMODE_DOMAIN_STR "domain" + +#define SMB_ENC_LEN 1024 +#define SMB_DEC_LEN 256 + +static char *b64_data = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static rwlock_t smb_cfg_rwlk; +static int lock_type = SMB_CL_NONE; + +/* + * IMPORTANT: any changes to the order of this table's entries + * need to be reflected in smb_cfg_id_t enum in libsmb.h + */ +static smb_cfg_param_t smb_cfg_table[] = +{ + /* Redirector configuration, User space */ + {SMBD_PG_NAME, SMB_CD_RDR_IPCMODE, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + {SMBD_PROTECTED_PG_NAME, SMB_CD_RDR_IPCUSER, + SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + {SMBD_PROTECTED_PG_NAME, SMB_CD_RDR_IPCPWD, + SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + + /* Oplock configuration, Kernel Only */ + {SMBD_PG_NAME, SMB_CD_OPLOCK_ENABLE, + SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_OPLOCK_TIMEOUT, + SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT}, + + /* Autohome configuration */ + {SMBD_PG_NAME, SMB_CD_AUTOHOME_MAP, + SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + + /* Domain/PDC configuration */ + {SMBD_PG_NAME, SMB_CD_DOMAIN_SID, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_DOMAIN_MEMB, SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_DOMAIN_NAME, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_DOMAIN_SRV, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + + /* WINS configuration */ + {SMBD_PG_NAME, SMB_CD_WINS_SRV1, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_WINS_SRV2, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_WINS_EXCL, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + + /* RPC services configuration */ + {SMBD_PG_NAME, SMB_CD_SRVSVC_SHRSET_ENABLE, + SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_LOGR_ENABLE, SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_MLRPC_KALIVE, + SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT}, + + /* Kmod specific configuration */ + {SMBD_PG_NAME, SMB_CD_MAX_BUFSIZE, SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_MAX_WORKERS, SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_MAX_CONNECTIONS, + SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_KEEPALIVE, SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_RESTRICT_ANON, + SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + + {SMBD_PG_NAME, SMB_CD_SIGNING_ENABLE, + SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_SIGNING_REQD, + SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_SIGNING_CHECK, + SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + + /* Kmod tuning configuration */ + {SMBD_PG_NAME, SMB_CD_FLUSH_REQUIRED, + SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_SYNC_ENABLE, SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_DIRSYMLINK_DISABLE, + SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_ANNONCE_QUOTA, + SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + + /* SMBd configuration */ + {SMBD_PG_NAME, SMB_CD_SECURITY, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_NBSCOPE, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_SYS_CMNT, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_LM_LEVEL, SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_MSDCS_DISABLE, + SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + + /* ADS Configuration */ + {SMBD_PG_NAME, SMB_CD_ADS_ENABLE, SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + {SMBD_PROTECTED_PG_NAME, SMB_CD_ADS_USER, + SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + {SMBD_PROTECTED_PG_NAME, SMB_CD_ADS_PASSWD, + SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_ADS_DOMAIN, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_ADS_USER_CONTAINER, + SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_ADS_SITE, SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_ADS_IPLOOKUP, + SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + + /* Dynamic DNS */ + {SMBD_PG_NAME, SMB_CD_DYNDNS_ENABLE, + SCF_TYPE_BOOLEAN, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_DYNDNS_RETRY_COUNT, + SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT}, + {SMBD_PG_NAME, SMB_CD_DYNDNS_RETRY_SEC, + SCF_TYPE_INTEGER, 0, SMB_CF_NOTINIT}, + + {SMBD_PROTECTED_PG_NAME, SMB_CD_MACHINE_PASSWD, + SCF_TYPE_ASTRING, 0, SMB_CF_NOTINIT} + /* SMB_CI_MAX */ +}; + +static boolean_t smb_is_base64(unsigned char c); +static char *smb_base64_encode(char *str_to_encode); +static char *smb_base64_decode(char *encoded_str); +static int smb_config_update(smb_cfg_param_t *cfg, char *value); +static int smb_config_save_all(); +static int smb_config_save(char *pgname); + +static boolean_t +smb_is_base64(unsigned char c) +{ + return (isalnum(c) || (c == '+') || (c == '/')); +} + +/* + * smb_base64_encode + * + * Encode a string using base64 algorithm. + * Caller should free the returned buffer when done. + */ +static char * +smb_base64_encode(char *str_to_encode) +{ + int ret_cnt = 0; + int i = 0, j = 0; + char arr_3[3], arr_4[4]; + int len = strlen(str_to_encode); + char *ret = malloc(SMB_ENC_LEN); + + if (ret == NULL) { + return (NULL); + } + + while (len--) { + arr_3[i++] = *(str_to_encode++); + if (i == 3) { + arr_4[0] = (arr_3[0] & 0xfc) >> 2; + arr_4[1] = ((arr_3[0] & 0x03) << 4) + + ((arr_3[1] & 0xf0) >> 4); + arr_4[2] = ((arr_3[1] & 0x0f) << 2) + + ((arr_3[2] & 0xc0) >> 6); + arr_4[3] = arr_3[2] & 0x3f; + + for (i = 0; i < 4; i++) + ret[ret_cnt++] = b64_data[arr_4[i]]; + i = 0; + } + } + + if (i) { + for (j = i; j < 3; j++) + arr_3[j] = '\0'; + + arr_4[0] = (arr_3[0] & 0xfc) >> 2; + arr_4[1] = ((arr_3[0] & 0x03) << 4) + + ((arr_3[1] & 0xf0) >> 4); + arr_4[2] = ((arr_3[1] & 0x0f) << 2) + + ((arr_3[2] & 0xc0) >> 6); + arr_4[3] = arr_3[2] & 0x3f; + + for (j = 0; j < (i + 1); j++) + ret[ret_cnt++] = b64_data[arr_4[j]]; + + while (i++ < 3) + ret[ret_cnt++] = '='; + } + + ret[ret_cnt++] = '\0'; + return (ret); +} + +/* + * smb_base64_decode + * + * Decode using base64 algorithm. + * Caller should free the returned buffer when done. + */ +static char * +smb_base64_decode(char *encoded_str) +{ + int len = strlen(encoded_str); + int i = 0, j = 0; + int en_ind = 0; + char arr_4[4], arr_3[3]; + int ret_cnt = 0; + char *ret = malloc(SMB_DEC_LEN); + char *p; + + if (ret == NULL) { + return (NULL); + } + + while (len-- && (encoded_str[en_ind] != '=') && + smb_is_base64(encoded_str[en_ind])) { + arr_4[i++] = encoded_str[en_ind]; + en_ind++; + if (i == 4) { + for (i = 0; i < 4; i++) { + if ((p = strchr(b64_data, arr_4[i])) == NULL) + return (NULL); + + arr_4[i] = (int)(p - b64_data); + } + + arr_3[0] = (arr_4[0] << 2) + + ((arr_4[1] & 0x30) >> 4); + arr_3[1] = ((arr_4[1] & 0xf) << 4) + + ((arr_4[2] & 0x3c) >> 2); + arr_3[2] = ((arr_4[2] & 0x3) << 6) + + arr_4[3]; + + for (i = 0; i < 3; i++) + ret[ret_cnt++] = arr_3[i]; + + i = 0; + } + } + + if (i) { + for (j = i; j < 4; j++) + arr_4[j] = 0; + + for (j = 0; j < 4; j++) { + if ((p = strchr(b64_data, arr_4[j])) == NULL) + return (NULL); + + arr_4[j] = (int)(p - b64_data); + } + arr_3[0] = (arr_4[0] << 2) + + ((arr_4[1] & 0x30) >> 4); + arr_3[1] = ((arr_4[1] & 0xf) << 4) + + ((arr_4[2] & 0x3c) >> 2); + arr_3[2] = ((arr_4[2] & 0x3) << 6) + + arr_4[3]; + for (j = 0; j < (i - 1); j++) + ret[ret_cnt++] = arr_3[j]; + } + + ret[ret_cnt++] = '\0'; + return (ret); +} + +/* + * Basically commit the transaction. + */ +static int +smb_config_saveenv(smb_scfhandle_t *handle) +{ + int ret = 0; + + ret = smb_smf_end_transaction(handle); + + smb_smf_scf_fini(handle); + return (ret); +} + +/* + * smb_config_getenv + * + * Get the property value from SMF. + */ +char * +smb_config_getenv(smb_cfg_id_t id) +{ + smb_scfhandle_t *handle; + char *value; + + if ((value = malloc(MAX_VALUE_BUFLEN * sizeof (char))) == NULL) + return (NULL); + + handle = smb_smf_scf_init(SMBD_FMRI_PREFIX); + if (handle == NULL) { + free(value); + return (NULL); + } + + (void) smb_smf_create_service_pgroup(handle, smb_cfg_table[id].sc_pg); + + if (smb_smf_get_property(handle, smb_cfg_table[id].sc_type, + smb_cfg_table[id].sc_name, value, + sizeof (char) * MAX_VALUE_BUFLEN) != 0) { + smb_smf_scf_fini(handle); + free(value); + return (NULL); + } + + smb_smf_scf_fini(handle); + return (value); +} + +/* + * smb_config_getenv_dec + * + * For protected property, the value obtained from SMF will be decoded. + * The decoded property value will be returned. + * + * This function should only be called by smb_config_load to populate + * the SMB config cache. + */ +static char * +smb_config_getenv_dec(smb_cfg_id_t id) +{ + smb_scfhandle_t *handle; + char *value; + char *dec; + + if ((value = malloc(MAX_VALUE_BUFLEN * sizeof (char))) == NULL) + return (NULL); + + handle = smb_smf_scf_init(SMBD_FMRI_PREFIX); + if (handle == NULL) { + free(value); + return (NULL); + } + + (void) smb_smf_create_service_pgroup(handle, smb_cfg_table[id].sc_pg); + + if (smb_smf_get_property(handle, smb_cfg_table[id].sc_type, + smb_cfg_table[id].sc_name, value, + sizeof (char) * MAX_VALUE_BUFLEN) != 0) { + smb_smf_scf_fini(handle); + free(value); + return (NULL); + } + smb_smf_scf_fini(handle); + if (strcmp(smb_cfg_table[id].sc_pg, SMBD_PROTECTED_PG_NAME)) + return (value); + + if (!value) + return (NULL); + + if (*value == '\0') { + free(value); + return (NULL); + } + + dec = smb_base64_decode(value); + free(value); + return (dec); +} + +static char * +smb_config_getenv_generic(char *name, char *svc_fmri_prefix, char *svc_propgrp) +{ + smb_scfhandle_t *handle; + char *value; + + if ((value = malloc(MAX_VALUE_BUFLEN * sizeof (char))) == NULL) + return (NULL); + + handle = smb_smf_scf_init(svc_fmri_prefix); + if (handle == NULL) { + free(value); + return (NULL); + } + + (void) smb_smf_create_service_pgroup(handle, svc_propgrp); + + if (smb_smf_get_string_property(handle, name, value, + sizeof (char) * MAX_VALUE_BUFLEN) != 0) { + smb_smf_scf_fini(handle); + free(value); + return (NULL); + } + + smb_smf_scf_fini(handle); + return (value); + +} + +int +smb_config_setenv_generic(char *svc_fmri_prefix, char *svc_propgrp, + char *name, char *value) +{ + smb_scfhandle_t *handle = NULL; + int rc = 0; + + + handle = smb_smf_scf_init(svc_fmri_prefix); + if (handle == NULL) { + return (1); + } + + (void) smb_smf_create_service_pgroup(handle, svc_propgrp); + + if (smb_smf_start_transaction(handle) != SMBD_SMF_OK) { + smb_smf_scf_fini(handle); + return (1); + } + + if (smb_smf_set_string_property(handle, name, value) != SMBD_SMF_OK) + rc = 1; + + if (smb_smf_end_transaction(handle) != SMBD_SMF_OK) + rc = 1; + + smb_smf_scf_fini(handle); + return (rc); +} + +/* + * smb_config_setenv + * + * For protected properties, the value will be encoded using base64 + * algorithm. The encoded string will be stored in SMF. + */ +int +smb_config_setenv(smb_cfg_id_t id, char *value) +{ + smb_scfhandle_t *handle = NULL; + char *enc = NULL; + int is_protected = 0; + + if ((id >= SMB_CI_MAX) || (id < 0)) { + return (1); + } + + handle = smb_smf_scf_init(SMBD_FMRI_PREFIX); + if (handle == NULL) { + return (1); + } + + (void) smb_smf_create_service_pgroup(handle, smb_cfg_table[id].sc_pg); + + if (smb_smf_start_transaction(handle) != SMBD_SMF_OK) { + smb_smf_scf_fini(handle); + return (1); + } + + if (strcmp(smb_cfg_table[id].sc_pg, SMBD_PROTECTED_PG_NAME) == 0) { + if ((value == NULL) || (*value == '\0')) { + (void) smb_smf_end_transaction(handle); + smb_smf_scf_fini(handle); + return (1); + } + + if ((enc = smb_base64_encode(value)) == NULL) { + (void) smb_smf_end_transaction(handle); + smb_smf_scf_fini(handle); + return (1); + } + + is_protected = 1; + } + + if (smb_smf_set_property(handle, smb_cfg_table[id].sc_type, + smb_cfg_table[id].sc_name, is_protected ? enc : value) + != SMBD_SMF_OK) { + if (enc) + free(enc); + (void) smb_smf_end_transaction(handle); + smb_smf_scf_fini(handle); + return (1); + } + + if (enc) + free(enc); + + if (smb_smf_end_transaction(handle) != SMBD_SMF_OK) { + smb_smf_scf_fini(handle); + return (1); + } + + smb_smf_scf_fini(handle); + return (0); +} + +static void +smb_config_setenv_trans(smb_scfhandle_t *handle, int type, + char *name, char *value) +{ + if (smb_smf_set_property(handle, type, name, value) != SMBD_SMF_OK) { + syslog(LOG_ERR, "Failed to save service property %s", name); + } +} + +/* + * smb_config_setenv_trans_protected + * + * This function should only be called to set protected properties + * in SMF. The argument 'value' will be encoded using base64 algorithm. + * The encoded string will be stored in SMF. + */ +static void +smb_config_setenv_trans_protected(smb_scfhandle_t *handle, char *name, + char *value) +{ + char *enc; + + if ((value == NULL) || (*value == '\0')) + return; + + if ((enc = smb_base64_encode(value)) == NULL) + return; + + if (smb_smf_set_string_property(handle, name, enc) != SMBD_SMF_OK) { + syslog(LOG_ERR, "Failed to save service protected property" + " %s", name); + } + + free(enc); +} + +int +smb_config_unsetenv(smb_cfg_id_t id) +{ + smb_scfhandle_t *handle = NULL; + int ret = 1; + + handle = smb_smf_scf_init(SMBD_FMRI_PREFIX); + if (handle == NULL) { + return (ret); + } + + (void) smb_smf_create_service_pgroup(handle, smb_cfg_table[id].sc_pg); + if (smb_smf_start_transaction(handle) != SMBD_SMF_OK) { + smb_smf_scf_fini(handle); + return (ret); + } + ret = smb_smf_delete_property(handle, smb_cfg_table[id].sc_name); + (void) smb_smf_end_transaction(handle); + + smb_smf_scf_fini(handle); + return (ret); +} + +static int +smb_config_unsetenv_trans(smb_scfhandle_t *handle, char *name) +{ + return (smb_smf_delete_property(handle, name)); +} + +/* + * smb_config_load + * + * Loads all the CIFS configuration parameters and sets up the + * config table. + */ +int +smb_config_load() +{ + smb_cfg_id_t id; + smb_cfg_param_t *cfg; + char *value; + + (void) rw_rdlock(&smb_cfg_rwlk); + for (id = 0; id < SMB_CI_MAX; id++) { + value = smb_config_getenv_dec(id); + cfg = &smb_cfg_table[id]; + /* + * enval == 0 could mean two things, either the + * config param is not defined, or it has been + * removed. If the variable has already been defined + * and now enval is 0, it should be removed, otherwise + * we don't need to do anything in this case. + */ + if ((cfg->sc_flags & SMB_CF_DEFINED) || value) { + if (smb_config_update(cfg, value) != 0) { + (void) rw_unlock(&smb_cfg_rwlk); + if (value) + free(value); + return (1); + } + } + if (value) { + free(value); + } + } + + (void) rw_unlock(&smb_cfg_rwlk); + + return (0); +} + +/* + * smb_config_get + * + * Returns value of the specified config param. + * The return value is a string pointer to the locally + * allocated memory if the config param is defined + * otherwise it would be NULL. + * + * This function MUST be called after a smb_config_rd/wrlock + * function. Caller MUST NOT modify the returned buffer directly. + */ +char * +smb_config_get(smb_cfg_id_t id) +{ + if (id < SMB_CI_MAX) + return (smb_cfg_table[id].sc_value); + + return (0); +} + +/* + * smb_config_getstr + * + * Returns value of the specified config param. + * The returned pointer never will be NULL if the given + * 'id' is valid. If the config param is not defined its + * default value will be returned. + * + * This function MUST be called after a smb_config_rd/wrlock + * function. Caller MUST NOT modify the returned buffer directly. + */ +char * +smb_config_getstr(smb_cfg_id_t id) +{ + smb_cfg_param_t *cfg; + + if (id < SMB_CI_MAX) { + cfg = &smb_cfg_table[id]; + if (cfg->sc_value) + return (cfg->sc_value); + } + + return (NULL); +} + +/* + * smb_config_getnum + * + * Returns the value of a numeric config param. + * If the config param is not defined it'll return the + * default value. + * + * This function MUST be called after a smb_config_rd/wrlock + * function. + */ +uint32_t +smb_config_getnum(smb_cfg_id_t id) +{ + smb_cfg_param_t *cfg; + char *strval = NULL; + + if (id < SMB_CI_MAX) { + cfg = &smb_cfg_table[id]; + if (cfg->sc_value) + strval = cfg->sc_value; + + if (strval) + return (strtol(strval, 0, 10)); + } + + return (0); +} + +/* + * smb_config_getyorn + * + * Returns the value of a yes/no config param. + * Returns 1 is config is set to "yes", otherwise 0. + * + * This function MUST be called after a smb_config_rd/wrlock + * function. + */ +int +smb_config_getyorn(smb_cfg_id_t id) +{ + char *val; + + val = smb_config_get(id); + if (val) { + if (strcasecmp(val, "true") == 0) + return (1); + } + + return (0); +} + +/* + * smb_config_set + * + * Set/update the specified config param with the given + * value. If the value is NULL the config param will be + * unset as if it is not defined. + * + * This function MUST be called after a smb_config_wrlock + * function. + */ +int +smb_config_set(smb_cfg_id_t id, char *value) +{ + smb_cfg_param_t *cfg; + int rc = 0; + + if (id < SMB_CI_MAX) { + cfg = &smb_cfg_table[id]; + rc = smb_config_update(cfg, value); + if (rc == 0) + cfg->sc_flags |= SMB_CF_MODIFIED; + return (rc); + } + + return (1); +} + +/* + * smb_config_setnum + * + * Set/update the specified config param with the given + * value. This is used for numeric config params. The given + * number will be converted to string before setting the + * config param. + * + * This function MUST be called after a smb_config_wrlock + * function. + */ +int +smb_config_setnum(smb_cfg_id_t id, uint32_t num) +{ + smb_cfg_param_t *cfg; + char value[32]; + int rc = 0; + + if (id < SMB_CI_MAX) { + cfg = &smb_cfg_table[id]; + (void) snprintf(value, sizeof (value), "%u", num); + rc = smb_config_update(cfg, value); + if (rc == 0) + cfg->sc_flags |= SMB_CF_MODIFIED; + return (rc); + } + + return (1); +} + +/* + * smb_config_rdlock + * + * Lock the config table for read access. + * This function MUST be called before any kind of + * read access to the config table i.e. all flavors of + * smb_config_get function + */ +void +smb_config_rdlock() +{ + (void) rw_rdlock(&smb_cfg_rwlk); + lock_type = SMB_CL_READ; +} + +/* + * smb_config_wrlock + * + * Lock the config table for write access. + * This function MUST be called before any kind of + * write access to the config table i.e. all flavors of + * smb_config_set function + */ +void +smb_config_wrlock() +{ + (void) rw_wrlock(&smb_cfg_rwlk); + lock_type = SMB_CL_WRITE; +} + +/* + * smb_config_wrlock + * + * Unlock the config table. + * If the config table has been locked for write access + * smb_config_save_all() will be called to save the changes + * before unlocking the table. + * + * This function MUST be called after smb_config_rd/wrlock + */ +void +smb_config_unlock() +{ + if (lock_type == SMB_CL_WRITE) + (void) smb_config_save_all(); + (void) rw_unlock(&smb_cfg_rwlk); +} + +/* + * smb_config_save_all + * + * Save all modified parameters to SMF. + */ +static int +smb_config_save_all() +{ + int rc; + + if ((rc = smb_config_save(SMBD_PG_NAME)) != 0) + return (rc); + + return (smb_config_save(SMBD_PROTECTED_PG_NAME)); +} + +/* + * smb_config_save + * + * Scan the config table and call smb_config_setenv/smb_config_unsetenv + * for params in the specified property group that has been modified. + * When the scan is finished, smb_config_saveenv() will be called to + * make the changes persistent. + */ +static int +smb_config_save(char *pgname) +{ + smb_cfg_id_t id; + smb_cfg_param_t *cfg; + smb_scfhandle_t *handle = NULL; + int dorefresh = 0; + + handle = smb_smf_scf_init(SMBD_FMRI_PREFIX); + if (handle == NULL) { + syslog(LOG_ERR, "smbd: cannot save configuration"); + return (1); + } + + (void) smb_smf_create_service_pgroup(handle, pgname); + if (smb_smf_start_transaction(handle) != SMBD_SMF_OK) { + syslog(LOG_ERR, "smbd: cannot save configuration"); + return (1); + } + + for (id = 0; id < SMB_CI_MAX; id++) { + cfg = &smb_cfg_table[id]; + if (strcmp(cfg->sc_pg, pgname)) + continue; + + if (cfg->sc_flags & SMB_CF_MODIFIED) { + if (cfg->sc_value) { + if (strcmp(pgname, SMBD_PG_NAME) == 0) + smb_config_setenv_trans(handle, + cfg->sc_type, cfg->sc_name, + cfg->sc_value); + else + smb_config_setenv_trans_protected( + handle, cfg->sc_name, + cfg->sc_value); + } else { + (void) smb_config_unsetenv_trans(handle, + cfg->sc_name); + } + cfg->sc_flags &= ~SMB_CF_MODIFIED; + dorefresh = 1; + } + } + + if (smb_config_saveenv(handle) != 0) { + syslog(LOG_ERR, "smbd: cannot save configuration"); + return (1); + } + if (dorefresh) + (void) smf_refresh_instance(SMBD_DEFAULT_INSTANCE_FMRI); + return (0); +} + +/* + * smb_config_update + * + * Updates the specified config param with the given value. + * This function is called both on (re)load and set. + */ +static int +smb_config_update(smb_cfg_param_t *cfg, char *value) +{ + char *curval; + int rc = 0; + int len; + + if (value) { + len = strlen(value); + if (cfg->sc_value) { + curval = (char *)realloc(cfg->sc_value, + (len + 1)); + } else { + curval = (char *)malloc(len + 1); + } + + if (curval) { + cfg->sc_value = curval; + (void) strcpy(cfg->sc_value, value); + cfg->sc_flags |= SMB_CF_DEFINED; + } else { + rc = 1; + } + } else if (cfg->sc_value) { + free(cfg->sc_value); + cfg->sc_value = NULL; + cfg->sc_flags &= ~SMB_CF_DEFINED; + } + + return (rc); +} + +uint8_t +smb_config_get_fg_flag() +{ + uint8_t run_fg = 0; /* Default is to run in daemon mode */ + smb_scfhandle_t *handle = NULL; + + handle = smb_smf_scf_init(SMBD_FMRI_PREFIX); + if (handle == NULL) { + return (run_fg); + } + + if (smb_smf_create_service_pgroup(handle, + SMBD_PG_NAME) != SMBD_SMF_OK) { + smb_smf_scf_fini(handle); + return (run_fg); + } + + if (smb_smf_get_boolean_property(handle, "run_fg", &run_fg) != 0) { + smb_smf_scf_fini(handle); + return (run_fg); + } + + smb_smf_scf_fini(handle); + + return (run_fg); +} + +/* + * smb_config_get_localsid + * + * Returns value of the "config/machine_sid" parameter + * from the IDMAP SMF configuration repository. + * + */ +char * +smb_config_get_localsid(void) +{ + return (smb_config_getenv_generic(MACHINE_SID, IDMAP_FMRI_PREFIX, + IDMAP_PG_NAME)); +} + +/* + * smb_config_set_idmap_domain + * + * Set the "config/mapping_domain" parameter from IDMAP SMF repository. + */ +int +smb_config_set_idmap_domain(char *value) +{ + return (smb_config_setenv_generic(IDMAP_FMRI_PREFIX, IDMAP_PG_NAME, + MAPPING_DOMAIN, value)); +} + +/* + * smb_config_set_idmap_gc + * + * Set the "config/global_catalog" parameter from IDMAP SMF repository. + */ +int +smb_config_set_idmap_gc(char *value) +{ + return (smb_config_setenv_generic(IDMAP_FMRI_PREFIX, IDMAP_PG_NAME, + GLOBAL_CATALOG, value)); +} + +/* + * smb_config_refresh_idmap + * + * Refresh IDMAP SMF service after making changes to its configuration. + */ +int +smb_config_refresh_idmap(void) +{ + char instance[32]; + + (void) snprintf(instance, sizeof (instance), "%s:default", + IDMAP_FMRI_PREFIX); + return (smf_refresh_instance(instance)); +} + +int +smb_config_secmode_fromstr(char *secmode) +{ + if (secmode == NULL) + return (SMB_SECMODE_WORKGRP); + + if (strcasecmp(secmode, SMB_SECMODE_DOMAIN_STR) == 0) + return (SMB_SECMODE_DOMAIN); + + return (SMB_SECMODE_WORKGRP); +} + +char * +smb_config_secmode_tostr(int secmode) +{ + if (secmode == SMB_SECMODE_DOMAIN) + return (SMB_SECMODE_DOMAIN_STR); + + return (SMB_SECMODE_WORKGRP_STR); +} + +int +smb_config_get_secmode() +{ + char *p; + + p = smb_config_getstr(SMB_CI_SECURITY); + return (smb_config_secmode_fromstr(p)); +} + +int +smb_config_set_secmode(int secmode) +{ + char *p; + + p = smb_config_secmode_tostr(secmode); + return (smb_config_set(SMB_CI_SECURITY, p)); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_crypt.c b/usr/src/lib/smbsrv/libsmb/common/smb_crypt.c new file mode 100644 index 000000000000..94489b0b400e --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_crypt.c @@ -0,0 +1,204 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include + +static void smb_auth_keyprep(const unsigned char *pwstr, unsigned char *keystr); + +/* + * smb_auth_md4 + * + * Compute an MD4 digest. + */ +int +smb_auth_md4(unsigned char *result, unsigned char *input, int length) +{ + MD4_CTX md4_context; + + MD4Init(&md4_context); + MD4Update(&md4_context, input, length); + MD4Final(result, &md4_context); + return (SMBAUTH_SUCCESS); +} + +int +smb_auth_hmac_md5(unsigned char *data, + int data_len, + unsigned char *key, + int key_len, + unsigned char *digest) +{ + CK_RV rv; + CK_MECHANISM mechanism; + CK_OBJECT_HANDLE hKey; + CK_SESSION_HANDLE hSession; + unsigned long diglen = MD_DIGEST_LEN; + + mechanism.mechanism = CKM_MD5_HMAC; + mechanism.pParameter = 0; + mechanism.ulParameterLen = 0; + rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession); + if (rv != CKR_OK) { + return (SMBAUTH_FAILURE); + } + + rv = SUNW_C_KeyToObject(hSession, mechanism.mechanism, + key, key_len, &hKey); + if (rv != CKR_OK) { + (void) C_CloseSession(hSession); + return (SMBAUTH_FAILURE); + } + + /* Initialize the digest operation in the session */ + rv = C_SignInit(hSession, &mechanism, hKey); + if (rv != CKR_OK) { + (void) C_DestroyObject(hSession, hKey); + (void) C_CloseSession(hSession); + return (SMBAUTH_FAILURE); + } + rv = C_SignUpdate(hSession, (CK_BYTE_PTR)data, data_len); + if (rv != CKR_OK) { + (void) C_DestroyObject(hSession, hKey); + (void) C_CloseSession(hSession); + return (SMBAUTH_FAILURE); + } + rv = C_SignFinal(hSession, (CK_BYTE_PTR)digest, &diglen); + if (rv != CKR_OK) { + (void) C_DestroyObject(hSession, hKey); + (void) C_CloseSession(hSession); + return (SMBAUTH_FAILURE); + } + (void) C_DestroyObject(hSession, hKey); + (void) C_CloseSession(hSession); + if (diglen != MD_DIGEST_LEN) { + return (SMBAUTH_FAILURE); + } + return (SMBAUTH_SUCCESS); +} + +int +smb_auth_DES(unsigned char *Result, int ResultLen, + unsigned char *Key, int KeyLen, + unsigned char *Data, int DataLen) +{ + CK_RV rv; + CK_MECHANISM mechanism; + CK_OBJECT_HANDLE hKey; + CK_SESSION_HANDLE hSession; + CK_ULONG ciphertext_len; + uchar_t des_key[8]; + int error = 0; + int K, D; + int k, d; + + /* Calculate proper number of iterations */ + K = KeyLen / 7; + D = DataLen / 8; + + if (ResultLen < (K * 8 * D)) { + return (SMBAUTH_FAILURE); + } + + /* + * Use SUNW convenience function to initialize the cryptoki + * library, and open a session with a slot that supports + * the mechanism we plan on using. + */ + mechanism.mechanism = CKM_DES_ECB; + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession); + if (rv != CKR_OK) { + return (SMBAUTH_FAILURE); + } + + for (k = 0; k < K; k++) { + smb_auth_keyprep(&Key[k * 7], des_key); + rv = SUNW_C_KeyToObject(hSession, mechanism.mechanism, + des_key, 8, &hKey); + if (rv != CKR_OK) { + error = 1; + goto exit_session; + } + /* Initialize the encryption operation in the session */ + rv = C_EncryptInit(hSession, &mechanism, hKey); + if (rv != CKR_OK) { + error = 1; + goto exit_encrypt; + } + ciphertext_len = DataLen; + for (d = 0; d < D; d++) { + /* Read in the data and encrypt this portion */ + rv = C_EncryptUpdate(hSession, + (CK_BYTE_PTR)Data + (d * 8), 8, + &Result[(k * (8 * D)) + (d * 8)], + &ciphertext_len); + if (rv != CKR_OK) { + error = 1; + goto exit_encrypt; + } + } + (void) C_DestroyObject(hSession, hKey); + } + goto exit_session; + +exit_encrypt: + (void) C_DestroyObject(hSession, hKey); +exit_session: + (void) C_CloseSession(hSession); + + if (error) + return (SMBAUTH_FAILURE); + + return (SMBAUTH_SUCCESS); +} + +/* + * smb_auth_keyprep + * + * Takes 7 bytes of keying material and expands it into 8 bytes with + * the keying material in the upper 7 bits of each byte. + */ +static void +smb_auth_keyprep(const unsigned char *pwstr, unsigned char *keystr) +{ + keystr[0] = pwstr[0]; + keystr[1] = (pwstr[0] << 7) | (pwstr[1] >> 1); + keystr[2] = (pwstr[1] << 6) | (pwstr[2] >> 2); + keystr[3] = (pwstr[2] << 5) | (pwstr[3] >> 3); + keystr[4] = (pwstr[3] << 4) | (pwstr[4] >> 4); + keystr[5] = (pwstr[4] << 3) | (pwstr[5] >> 5); + keystr[6] = (pwstr[5] << 2) | (pwstr[6] >> 6); + keystr[7] = pwstr[6] << 1; +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_ctxbuf.c b/usr/src/lib/smbsrv/libsmb/common/smb_ctxbuf.c new file mode 100644 index 000000000000..2b51f2b6af69 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_ctxbuf.c @@ -0,0 +1,120 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Buffer manipulation routines. These routines can be used to format + * data within a data buffer without worrying about overrunning the + * buffer. + * + * A ctxbuf_t structure is used to track the current location within + * the buffer. The ctxbuf_init() must be called first to initialize the + * context structure. ctxbuf_printf() can then be called to fill the buffer. + * ctxbuf_printf will discard any data that would overrun the buffer and + * the buffer will always be null terminated. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include + +/* + * smb_ctxbuf_init + * + * Initialize the buffer context structure. + * This must be called before any of the other + * buffer routines can be used. + * + * Returns -1 if invalid parameters, 0 otherwise + */ +int +smb_ctxbuf_init(smb_ctxbuf_t *ctx, unsigned char *buf, size_t buflen) +{ + if (ctx == 0 || buf == 0 || buflen == 0) + return (-1); + + buf[0] = '\0'; + + ctx->basep = buf; + ctx->curp = buf; + ctx->endp = &buf[buflen]; + + return (0); +} + +/* + * smb_ctxbuf_len + * + * Return the amount of data stored in the buffer, + * excluding the terminating null character. Similar + * to strlen() + * + * Returns 0 if the ctx is invalid. + */ +int +smb_ctxbuf_len(smb_ctxbuf_t *ctx) +{ + if (ctx == 0 || ctx->basep == 0 || + ctx->curp == 0 || ctx->endp == 0) + return (0); + else + /*LINTED E_PTRDIFF_OVERFLOW*/ + return (ctx->curp - ctx->basep); +} + +/* + * smb_ctxbuf_printf + * + * Move formatted output (based on fmt string) to the buffer + * identified in ctxbuf. Any output characters beyond the buffer + * are discarded and a null character is written at the end of the + * characters actually written. + * + * Returns + * Always return the number of bytes actually written (excluding the + * terminating null). + */ +int +smb_ctxbuf_printf(smb_ctxbuf_t *ctx, const char *fmt, ...) +{ + int n; + va_list args; + + if (ctx == 0 || ctx->basep == 0 || + ctx->curp == 0 || ctx->endp == 0) + return (-1); + + va_start(args, fmt); + /*LINTED E_PTRDIFF_OVERFLOW*/ + n = vsnprintf((char *)ctx->curp, ctx->endp-ctx->curp, fmt, args); + ctx->curp += n; + va_end(args); + + /* + * return the number of bytes moved into the buffer. + */ + return (n); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_domain.c b/usr/src/lib/smbsrv/libsmb/common/smb_domain.c new file mode 100644 index 000000000000..22ea5e61fbde --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_domain.c @@ -0,0 +1,394 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file defines the NT domain environment values and the domain + * database interface. The database is a single linked list of + * structures containing domain type, name and SID information. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + + +static void nt_domain_unlist(nt_domain_t *); + +/* + * Valid domain type identifiers as text. This table must be kept + * in step with the nt_domain_type_t enum in ntdomain.h. + */ +static char *nt_domain_type_name[NT_DOMAIN_NUM_TYPES] = { + "null", + "builtin", + "local", + "primary", + "account", + "trusted", + "untrusted" +}; + + +static rwlock_t nt_domain_lock; +static nt_domain_t *nt_domain_list; + +/* + * nt_domain_init + * + * NT domain database one time initialization. This function should + * be called during module installation. + * + * Returns 0 on successful domain initialization. Less than zero otherwise. + */ +int +nt_domain_init(char *resource_domain, uint32_t secmode) +{ + nt_domain_t *domain; + nt_sid_t *sid; + char *sidstr; + char *lsidstr; + char hostname[MAXHOSTNAMELEN]; + + if (rwlock_init(&nt_domain_lock, USYNC_THREAD, NULL)) + return (SMB_DOMAIN_NODOMAIN_SID); + + if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0) { + (void) rwlock_destroy(&nt_domain_lock); + return (SMB_DOMAIN_NOMACHINE_SID); + } + + lsidstr = smb_config_get_localsid(); + + if (lsidstr) { + sid = nt_sid_strtosid(lsidstr); + + if (sid) { + domain = nt_domain_new(NT_DOMAIN_LOCAL, hostname, sid); + (void) nt_domain_add(domain); + free(sid); + } + free(lsidstr); + } else { + (void) rwlock_destroy(&nt_domain_lock); + return (SMB_DOMAIN_NOMACHINE_SID); + } + + if (secmode == SMB_SECMODE_DOMAIN) { + sid = nt_sid_strtosid(NT_BUILTIN_DOMAIN_SIDSTR); + domain = nt_domain_new(NT_DOMAIN_BUILTIN, "BUILTIN", sid); + (void) nt_domain_add(domain); + free(sid); + + smb_config_rdlock(); + sidstr = smb_config_get(SMB_CI_DOMAIN_SID); + if (sidstr) { + sid = nt_sid_strtosid(sidstr); + smb_config_unlock(); + domain = nt_domain_new(NT_DOMAIN_PRIMARY, + resource_domain, sid); + (void) nt_domain_add(domain); + free(sid); + } else { + smb_config_unlock(); + (void) rwlock_destroy(&nt_domain_lock); + return (SMB_DOMAIN_NODOMAIN_SID); + } + + } + return (0); +} + +/* + * nt_domain_new + * + * Allocate and initialize a new domain structure. On success, a pointer to + * the new domain structure is returned. Otherwise a null pointer is returned. + */ +nt_domain_t * +nt_domain_new(nt_domain_type_t type, char *name, nt_sid_t *sid) +{ + nt_domain_t *new_domain; + + if ((name == NULL) || (sid == NULL)) + return (NULL); + + if (type == NT_DOMAIN_NULL || type >= NT_DOMAIN_NUM_TYPES) + return (NULL); + + if ((new_domain = malloc(sizeof (nt_domain_t))) == NULL) + return (NULL); + + bzero(new_domain, sizeof (nt_domain_t)); + new_domain->type = type; + new_domain->name = strdup(name); + new_domain->sid = nt_sid_dup(sid); + + return (new_domain); +} + +/* + * nt_domain_delete + * + * Free the memory used by the specified domain structure. + */ +void +nt_domain_delete(nt_domain_t *domain) +{ + if (domain) { + free(domain->name); + free(domain->sid); + free(domain); + } +} + + +/* + * nt_domain_add + * + * Add a domain structure to the global list. There is no checking + * for duplicates. If it's the primary domain, we save the SID in the + * environment. Returns a pointer to the new domain entry on success. + * Otherwise a null pointer is returned. + */ +nt_domain_t * +nt_domain_add(nt_domain_t *new_domain) +{ + char *sidstr; + + if (new_domain == NULL) + return (NULL); + + (void) rw_wrlock(&nt_domain_lock); + + new_domain->next = nt_domain_list; + nt_domain_list = new_domain; + + if (new_domain->type == NT_DOMAIN_PRIMARY) { + sidstr = nt_sid_format(new_domain->sid); + smb_config_wrlock(); + (void) smb_config_set(SMB_CI_DOMAIN_SID, sidstr); + smb_config_unlock(); + free(sidstr); + } + (void) rw_unlock(&nt_domain_lock); + + return (new_domain); +} + + +/* + * nt_domain_remove + * + * Remove a domain from the global list. The memory + * used by the structure is not freed. + */ +void +nt_domain_remove(nt_domain_t *domain) +{ + (void) rw_wrlock(&nt_domain_lock); + nt_domain_unlist(domain); + (void) rw_unlock(&nt_domain_lock); +} + + +/* + * nt_domain_flush + * + * Flush all domains of the specified type from the list. This is + * useful for things like updating the list of trusted domains. + */ +void +nt_domain_flush(nt_domain_type_t domain_type) +{ + nt_domain_t *domain = nt_domain_list; + + (void) rw_wrlock(&nt_domain_lock); + while (domain) { + if (domain->type == domain_type) { + nt_domain_unlist(domain); + nt_domain_delete(domain); + domain = nt_domain_list; + continue; + } + domain = domain->next; + } + (void) rw_unlock(&nt_domain_lock); +} + +/* + * nt_domain_xlat_type + * + * Translate a domain type into a text string. + */ +char * +nt_domain_xlat_type(nt_domain_type_t domain_type) +{ + if (domain_type < NT_DOMAIN_NUM_TYPES) + return (nt_domain_type_name[domain_type]); + else + return ("unknown"); +} + + +/* + * nt_domain_xlat_type_name + * + * Translate a domain type test string into a domain type. + */ +nt_domain_type_t +nt_domain_xlat_type_name(char *type_name) +{ + int i; + + for (i = 0; i < NT_DOMAIN_NUM_TYPES; ++i) + if (utf8_strcasecmp(nt_domain_type_name[i], type_name) == 0) + return (i); + + return (NT_DOMAIN_NUM_TYPES); +} + + +/* + * nt_domain_lookup_name + * + * Lookup a domain by its domain name. If the domain is in the list, + * a pointer to it is returned. Otherwise a null pointer is returned. + */ +nt_domain_t * +nt_domain_lookup_name(char *domain_name) +{ + nt_domain_t *domain = nt_domain_list; + + (void) rw_rdlock(&nt_domain_lock); + while (domain) { + if (utf8_strcasecmp(domain->name, domain_name) == 0) + break; + + domain = domain->next; + } + (void) rw_unlock(&nt_domain_lock); + + return (domain); +} + + +/* + * nt_domain_lookup_sid + * + * Lookup a domain by its domain SID. If the domain is in the list, + * a pointer to it is returned. Otherwise a null pointer is returned. + */ +nt_domain_t * +nt_domain_lookup_sid(nt_sid_t *domain_sid) +{ + nt_domain_t *domain = nt_domain_list; + + (void) rw_rdlock(&nt_domain_lock); + while (domain) { + if (nt_sid_is_equal(domain->sid, domain_sid)) + break; + + domain = domain->next; + } + (void) rw_unlock(&nt_domain_lock); + + return (domain); +} + + +/* + * nt_domain_lookupbytype + * + * Lookup a domain by its type. The first matching entry in the list + * is returned. Otherwise a null pointer is returned. + */ +nt_domain_t * +nt_domain_lookupbytype(nt_domain_type_t type) +{ + nt_domain_t *domain = nt_domain_list; + + (void) rw_rdlock(&nt_domain_lock); + while (domain) { + if (domain->type == type) + break; + + domain = domain->next; + } + (void) rw_unlock(&nt_domain_lock); + + return (domain); +} + + +/* + * nt_domain_local_sid + * + * Return a pointer to the local domain SID. Each system has a SID that + * represents the local domain, which is named after the local hostname. + * The local domain SID must exist. + */ +nt_sid_t * +nt_domain_local_sid(void) +{ + nt_domain_t *domain = nt_domain_list; + + (void) rw_rdlock(&nt_domain_lock); + while (domain) { + if (domain->type == NT_DOMAIN_LOCAL) + break; + + domain = domain->next; + } + (void) rw_unlock(&nt_domain_lock); + + return (domain->sid); +} + + +static void +nt_domain_unlist(nt_domain_t *domain) +{ + nt_domain_t **ppdomain = &nt_domain_list; + + while (*ppdomain) { + if (*ppdomain == domain) { + *ppdomain = domain->next; + domain->next = NULL; + return; + } + ppdomain = &(*ppdomain)->next; + } +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_door_client.c b/usr/src/lib/smbsrv/libsmb/common/smb_door_client.c new file mode 100644 index 000000000000..7e09b237b3ce --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_door_client.c @@ -0,0 +1,411 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * User-space door client for SMBd + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +static int smb_door_fildes = -1; + +static char *smbd_desc[] = { + "", + "SmbdJoinDomain", + "SmbdGetParam", + "SmbdSetParam", + "SmbdNetbiosReconfig", + 0 +}; + +/* + * Returns 0 on success. Otherwise, -1. + */ +static int +smbd_door_open(int opcode) +{ + int rc = 0; + + if (smb_door_fildes == -1 && + (smb_door_fildes = open(SMBD_DOOR_NAME, O_RDONLY)) < 0) { + syslog(LOG_ERR, "%s: open %s failed %s", smbd_desc[opcode], + SMBD_DOOR_NAME, strerror(errno)); + rc = -1; + } + + return (rc); +} + +/* + * Return 0 upon success. Otherwise, -1. + */ +static int +smbd_door_check_srv_status(int opcode, smb_dr_ctx_t *dec_ctx) +{ + int status = smb_dr_get_int32(dec_ctx); + int err; + int rc = -1; + + switch (status) { + case SMBD_DOOR_SRV_SUCCESS: + rc = 0; + break; + + case SMBD_DOOR_SRV_ERROR: + err = smb_dr_get_uint32(dec_ctx); + syslog(LOG_ERR, "%s: Encountered door server error %s", + smbd_desc[opcode], strerror(err)); + break; + + default: + syslog(LOG_ERR, "%s: Unknown door server status", + smbd_desc[opcode]); + } + + if (rc != 0) { + if ((err = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + smbd_desc[opcode], strerror(err)); + } + } + + return (rc); +} + +uint32_t +smb_join(smb_joininfo_t *jdi) +{ + door_arg_t arg; + char *buf; + uint32_t used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + int status; + uint32_t rc; + int opcode = SMBD_DOOR_JOIN; + + if ((jdi == 0) || (*jdi->domain_name == 0)) { + syslog(LOG_ERR, "%s: invalid parameter(s)", smbd_desc[opcode]); + return (NT_STATUS_INVALID_PARAMETER); + } + + if (smbd_door_open(opcode) == -1) { + syslog(LOG_ERR, "%s: cannot open the door", smbd_desc[opcode]); + return (NT_STATUS_INTERNAL_ERROR); + } + + buf = MEM_MALLOC("smb_door_client", SMBD_DOOR_SIZE); + if (!buf) { + syslog(LOG_ERR, "%s: resource shortage", smbd_desc[opcode]); + return (NT_STATUS_NO_MEMORY); + } + + enc_ctx = smb_dr_encode_start(buf, SMBD_DOOR_SIZE); + if (enc_ctx == 0) { + syslog(LOG_ERR, "%s: encode start failed", smbd_desc[opcode]); + MEM_FREE("smb_door_client", buf); + return (NT_STATUS_INTERNAL_ERROR); + } + + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_uint32(enc_ctx, jdi->mode); + smb_dr_put_string(enc_ctx, jdi->domain_name); + smb_dr_put_string(enc_ctx, jdi->domain_username); + smb_dr_put_string(enc_ctx, jdi->domain_passwd); + + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + smbd_desc[opcode], strerror(status)); + MEM_FREE("smb_door_client", buf); + return (NT_STATUS_INTERNAL_ERROR); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = SMBD_DOOR_SIZE; + + if (door_call(smb_door_fildes, &arg) < 0) { + syslog(LOG_ERR, "%s: Door call failed %s", smbd_desc[opcode], + strerror(errno)); + MEM_FREE("smb_door_client", buf); + smb_door_fildes = -1; + return (NT_STATUS_INTERNAL_ERROR); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (smbd_door_check_srv_status(opcode, dec_ctx) != 0) { + MEM_FREE("smb_door_client", buf); + return (NT_STATUS_INTERNAL_ERROR); + } + + rc = smb_dr_get_uint32(dec_ctx); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + smbd_desc[opcode], strerror(status)); + MEM_FREE("smb_door_client", buf); + return (NT_STATUS_INTERNAL_ERROR); + } + + MEM_FREE("smb_door_client", buf); + return (rc); +} + +int +smbd_netbios_reconfig() +{ + door_arg_t arg; + char *buf; + uint32_t used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + int status; + DWORD rc; + int opcode = SMBD_DOOR_NETBIOS_RECONFIG; + + if (smbd_door_open(opcode) == -1) { + syslog(LOG_ERR, "%s: cannot open the door", smbd_desc[opcode]); + return (1); + } + + buf = MEM_MALLOC("smb_door_client", SMBD_DOOR_SIZE); + if (!buf) { + syslog(LOG_ERR, "%s: resource shortage", smbd_desc[opcode]); + return (1); + } + + enc_ctx = smb_dr_encode_start(buf, SMBD_DOOR_SIZE); + if (enc_ctx == 0) { + syslog(LOG_ERR, "%s: encode start failed", smbd_desc[opcode]); + MEM_FREE("smb_door_client", buf); + return (1); + } + + smb_dr_put_uint32(enc_ctx, opcode); + + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + smbd_desc[opcode], strerror(status)); + MEM_FREE("smb_door_client", buf); + return (1); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = SMBD_DOOR_SIZE; + + if (door_call(smb_door_fildes, &arg) < 0) { + syslog(LOG_ERR, "%s: Door call failed %s", smbd_desc[opcode], + strerror(errno)); + MEM_FREE("smb_door_client", buf); + smb_door_fildes = -1; + return (1); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + rc = smb_dr_get_uint32(dec_ctx); + + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + smbd_desc[opcode], strerror(status)); + MEM_FREE("smb_door_client", buf); + return (1); + } + MEM_FREE("smb_door_client", buf); + return (rc); +} + +int +smbd_set_param(smb_cfg_id_t id, char *value) +{ + door_arg_t arg; + char *buf; + uint32_t used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + int status; + DWORD rc; + int opcode = SMBD_DOOR_PARAM_SET; + + if (smbd_door_open(opcode) == -1) { + syslog(LOG_ERR, "%s: cannot open the door", smbd_desc[opcode]); + return (1); + } + + buf = MEM_MALLOC("smb_door_client", SMBD_DOOR_SIZE); + if (!buf) { + syslog(LOG_ERR, "%s: resource shortage", smbd_desc[opcode]); + return (1); + } + + enc_ctx = smb_dr_encode_start(buf, SMBD_DOOR_SIZE); + if (enc_ctx == 0) { + syslog(LOG_ERR, "%s: encode start failed", smbd_desc[opcode]); + MEM_FREE("smb_door_client", buf); + return (1); + } + + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_uint32(enc_ctx, id); + smb_dr_put_string(enc_ctx, value); + + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + smbd_desc[opcode], strerror(status)); + MEM_FREE("smb_door_client", buf); + return (1); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = SMBD_DOOR_SIZE; + + if (door_call(smb_door_fildes, &arg) < 0) { + syslog(LOG_ERR, "%s: Door call failed %s", smbd_desc[opcode], + strerror(errno)); + MEM_FREE("smb_door_client", buf); + smb_door_fildes = -1; + return (1); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + rc = smb_dr_get_uint32(dec_ctx); + + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + smbd_desc[opcode], strerror(status)); + MEM_FREE("smb_door_client", buf); + return (1); + } + MEM_FREE("smb_door_client", buf); + return (rc); +} + +int +smbd_get_param(smb_cfg_id_t id, char *value) +{ + door_arg_t arg; + char *buf; + char *tmp = NULL; + uint32_t used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + int status; + DWORD rc; + int opcode = SMBD_DOOR_PARAM_GET; + + if (smbd_door_open(opcode) == -1) { + syslog(LOG_ERR, "%s: cannot open the door", smbd_desc[opcode]); + return (1); + } + + buf = MEM_MALLOC("smb_door_client", SMBD_DOOR_SIZE); + if (!buf) { + syslog(LOG_ERR, "%s: resource shortage", smbd_desc[opcode]); + return (1); + } + + enc_ctx = smb_dr_encode_start(buf, SMBD_DOOR_SIZE); + if (enc_ctx == 0) { + syslog(LOG_ERR, "%s: encode start failed", smbd_desc[opcode]); + MEM_FREE("smb_door_client", buf); + return (1); + } + + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_uint32(enc_ctx, id); + + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + syslog(LOG_ERR, "%s: Encode error %s", + smbd_desc[opcode], strerror(status)); + MEM_FREE("smb_door_client", buf); + return (1); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = SMBD_DOOR_SIZE; + + if (door_call(smb_door_fildes, &arg) < 0) { + syslog(LOG_ERR, "%s: Door call failed %s", smbd_desc[opcode], + strerror(errno)); + MEM_FREE("smb_door_client", buf); + smb_door_fildes = -1; + return (1); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + rc = smb_dr_get_uint32(dec_ctx); + tmp = smb_dr_get_string(dec_ctx); + (void) strcpy(value, tmp); + + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + syslog(LOG_ERR, "%s: Decode error %s", + smbd_desc[opcode], strerror(status)); + MEM_FREE("smb_door_client", buf); + return (1); + } + MEM_FREE("smb_door_client", buf); + return (rc); +} + +int +smbd_get_security_mode(int *mode) +{ + char buf[64]; + int rc; + + buf[0] = '\0'; + rc = smbd_get_param(SMB_CI_SECURITY, buf); + *mode = smb_config_secmode_fromstr(buf); + return (rc); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_door_encdec.c b/usr/src/lib/smbsrv/libsmb/common/smb_door_encdec.c new file mode 100644 index 000000000000..3734f6e49c83 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_door_encdec.c @@ -0,0 +1,313 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * smb_dr_decode_common + * + * This function can be used to decode both door request and result buffer. + * pre-condition: data is non-null pointer, and is bzero'd. + */ +int +smb_dr_decode_common(char *buf, size_t len, xdrproc_t proc, void *data) +{ + XDR xdrs; + int rc = 0; + + if (!data) { + syslog(LOG_ERR, "smb_dr_decode_common: invalid param"); + return (-1); + } + + xdrmem_create(&xdrs, buf, len, XDR_DECODE); + if (!proc(&xdrs, data)) { + rc = -1; + } + xdr_destroy(&xdrs); + return (rc); +} + +/* + * smb_dr_encode_common + * + * This function can be used to encode both request and result door buffer. + * The 'opcode' paramater is set to the 'opcode' of the operation to be invoked + * on the server, by the client. The server sets the same 'opcode' paramater + * to indicate the 'status' of the door call. + * + * This function will first encode integer value 'opcode' (opcode/status), + * followed by the data (which will be encoded via the specified XDR routine). + * + * Returns encoded buffer upon success. Otherwise, returns NULL. + */ +char * +smb_dr_encode_common(uint_t opcode, void *data, xdrproc_t proc, size_t *len) +{ + XDR xdrs; + char *buf; + + if (proc && !data) { + syslog(LOG_ERR, "smb_dr_encode_common: invalid param"); + *len = 0; + return (NULL); + } + + *len = xdr_sizeof(xdr_uint32_t, &opcode); + if (proc) + *len += xdr_sizeof(proc, data); + buf = (char *)malloc(*len); + if (!buf) { + syslog(LOG_ERR, "smb_dr_encode_common: resource shortage"); + *len = 0; + return (NULL); + } + xdrmem_create(&xdrs, buf, *len, XDR_ENCODE); + if (!xdr_uint32_t(&xdrs, &opcode)) { + syslog(LOG_DEBUG, "smb_dr_encode_common: encode error 1"); + free(buf); + *len = 0; + xdr_destroy(&xdrs); + return (NULL); + } + + if (proc && !proc(&xdrs, data)) { + syslog(LOG_DEBUG, "smb_dr_encode_common: encode error 2"); + free(buf); + buf = NULL; + *len = 0; + } + + xdr_destroy(&xdrs); + return (buf); +} + +/* + * Get the opcode of the door argument buffer. + */ +int +smb_dr_get_opcode(char *argp, size_t arg_size) +{ + int opcode; + + if (smb_dr_decode_common(argp, arg_size, xdr_uint32_t, &opcode) != 0) + opcode = -1; + return (opcode); +} + +/* + * Set the opcode of the door argument buffer. + */ +char * +smb_dr_set_opcode(uint32_t opcode, size_t *len) +{ + char *buf; + + buf = smb_dr_encode_common(opcode, NULL, NULL, len); + return (buf); +} + +/* + * Get the status of the door result buffer. + */ +int +smb_dr_get_res_stat(char *rbufp, size_t rbuf_size) +{ + int stat; + if (smb_dr_decode_common(rbufp, rbuf_size, xdr_uint32_t, &stat) != 0) + stat = -1; + return (stat); +} + +/* + * Set the status of the door result buffer. + */ +char * +smb_dr_set_res_stat(uint32_t stat, size_t *len) +{ + char *buf; + + buf = smb_dr_encode_common(stat, NULL, NULL, len); + return (buf); +} + +char * +smb_dr_encode_res_token(smb_token_t *token, size_t *len) +{ + smb_dr_bytes_t res; + char *buf = NULL; + + res.bytes_val = smb_token_mkselfrel(token, &res.bytes_len); + if (!res.bytes_val) { + syslog(LOG_ERR, "smb_dr_encode_res_token: mkselfrel error"); + *len = 0; + return (NULL); + } + + if ((buf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &res, + xdr_smb_dr_bytes_t, len)) == NULL) { + syslog(LOG_ERR, "smb_dr_encode_res_token: failed"); + *len = 0; + free(res.bytes_val); + return (NULL); + + } + free(res.bytes_val); + return (buf); +} + +char * +smb_dr_encode_kshare(smb_dr_kshare_t *kshare, size_t *buflen) +{ + smb_dr_bytes_t res; + char *buf = NULL; + + res.bytes_val = smb_kshare_mkselfrel(kshare, &res.bytes_len); + + free(kshare->k_path); + free(kshare->k_sharename); + + if (!res.bytes_val) + return (NULL); + + buf = smb_dr_encode_common(SMB_KDR_SHARE, &res, xdr_smb_dr_bytes_t, + buflen); + + free(res.bytes_val); + + return (buf); +} + +/* + * smb_kshare_mkselfrel + * + * encode: structure -> flat buffer (buffer size) + * Pre-condition: kshare is non-null. + */ + +uint8_t * +smb_kshare_mkselfrel(smb_dr_kshare_t *kshare, uint32_t *len) +{ + uint8_t *buf; + XDR xdrs; + + if (!kshare) + return (NULL); + + *len = xdr_sizeof(xdr_smb_dr_kshare_t, kshare); + buf = (uint8_t *)malloc(*len); + if (!buf) + return (NULL); + + xdrmem_create(&xdrs, (const caddr_t)buf, *len, XDR_ENCODE); + + if (!xdr_smb_dr_kshare_t(&xdrs, kshare)) { + *len = 0; + free(buf); + buf = NULL; + } + + xdr_destroy(&xdrs); + return (buf); +} + +char * +smb_dr_encode_string(uint32_t opcode, char *str, size_t *len) +{ + char *buf; + smb_dr_string_t res; + + res.buf = str; + + if ((buf = smb_dr_encode_common(opcode, &res, + xdr_smb_dr_string_t, len)) == NULL) + syslog(LOG_ERR, "smb_dr_encode_string: failed"); + return (buf); +} + +char * +smb_dr_decode_string(char *buf, size_t len) +{ + smb_dr_string_t res; + char *str = NULL; + + bzero(&res, sizeof (smb_dr_string_t)); + if (smb_dr_decode_common(buf, len, xdr_smb_dr_string_t, + &res) == 0) { + str = res.buf; + } else { + syslog(LOG_ERR, "smb_dr_decode_string: failed"); + } + return (str); +} + +netr_client_t * +smb_dr_decode_arg_get_token(char *buf, size_t len) +{ + smb_dr_bytes_t arg; + netr_client_t *clnt_info; + + bzero(&arg, sizeof (smb_dr_bytes_t)); + if (smb_dr_decode_common(buf, len, xdr_smb_dr_bytes_t, &arg) + != 0) { + syslog(LOG_ERR, "smb_dr_decode_arg_get_token: failed"); + xdr_free(xdr_smb_dr_bytes_t, (char *)&arg); + return (NULL); + } + clnt_info = netr_client_mkabsolute(arg.bytes_val, + arg.bytes_len); + xdr_free(xdr_smb_dr_bytes_t, (char *)&arg); + return (clnt_info); +} + +void +smb_dr_ulist_free(smb_dr_ulist_t *ulist) +{ + int i; + smb_dr_user_ctx_t *uinfo; + + if (!ulist) + return; + + for (i = 0; i < ulist->dul_cnt; i++) { + uinfo = &ulist->dul_users[i]; + + if (!uinfo) + continue; + + xdr_free(xdr_smb_dr_ulist_t, (char *)ulist); + } + + free(ulist); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c b/usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c new file mode 100644 index 000000000000..36373f10f000 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c @@ -0,0 +1,148 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * User-space door client routines for both SMB daemon and CLIs. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Returns 0 on success. Otherwise, -1. + */ +int +smb_dr_clnt_open(int *fd, char *path, char *op_desc) +{ + int rc = 0; + + if (!op_desc) + op_desc = "unknown operation"; + + if (!path || !fd) + return (-1); + + if ((*fd = open(path, O_RDONLY)) < 0) { + syslog(LOG_ERR, "%s: open %s failed %s", op_desc, + path, strerror(errno)); + rc = -1; + } + + return (rc); +} + +/* + * smb_dr_clnt_call + * + * This function will make a door call to the server function + * associated with the door descriptor fd. The specified door + * request buffer (i.e. argp) will be passed as the argument to the + * door_call(). Upon success, the result buffer is returned. Otherwise, + * NULL pointer is returned. The size of the result buffer is returned + * via rbufsize. + */ +char * +smb_dr_clnt_call(int fd, char *argp, size_t arg_size, size_t *rbufsize, + char *op_desc) +{ + door_arg_t arg; + + if (!argp) { + syslog(LOG_ERR, "smb_dr_clnt_call: invalid parameter"); + return (NULL); + } + + arg.data_ptr = argp; + arg.data_size = arg_size; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = argp; + arg.rsize = arg_size; + + if (!op_desc) + op_desc = "unknown operation"; + + if (door_call(fd, &arg) < 0) { + syslog(LOG_ERR, "%s: Door call failed %s", op_desc, + strerror(errno)); + free(argp); + argp = NULL; + return (NULL); + } + + if (smb_dr_get_res_stat(arg.data_ptr, arg.rsize) + != SMB_DR_OP_SUCCESS) { + smb_dr_clnt_free(argp, arg_size, arg.rbuf, arg.rsize); + *rbufsize = 0; + return (NULL); + } + *rbufsize = arg.rsize; + return (arg.data_ptr); +} + +/* + * smb_dr_clnt_free + * + * This function should be invoked to free both the argument/result door buffer + * regardless of the status of the door call. + * + * The doorfs allocates a new buffer if the result buffer passed by the client + * is too small. This function will munmap if that happens. + */ +/*ARGSUSED*/ +void +smb_dr_clnt_free(char *argp, size_t arg_size, char *rbufp, size_t rbuf_size) +{ + if (argp) { + if (argp == rbufp) { + free(argp); + argp = NULL; + } else if (rbufp) { + free(argp); + argp = NULL; + if (munmap(rbufp, rbuf_size) != 0) { + syslog(LOG_ERR, "munmap failed"); + } + } + } else { + if (rbufp) { + if (munmap(rbufp, rbuf_size) != 0) { + syslog(LOG_ERR, "munmap failed"); + } + } + } +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_downcalls.c b/usr/src/lib/smbsrv/libsmb/common/smb_downcalls.c new file mode 100644 index 000000000000..2f76b4e875aa --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_downcalls.c @@ -0,0 +1,195 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Down calls to SMB Kmod for obtaining various kernel door services. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* indexed via opcode (smb_kdr_opcode_t) */ +char *smb_dwncall_info[] = { + "SmbDwncallNumUser", + "SmbDwncallUserList", + "SmbDwncallShare", + 0 +}; + +static smb_dwncall_get_desc_t get_dwncall_desc; + +int +smb_dwncall_install_callback(smb_dwncall_get_desc_t get_desc_cb) +{ + if (!get_desc_cb) + return (-1); + + get_dwncall_desc = get_desc_cb; + return (0); +} + +int +smb_dwncall_init_fd(uint_t opcode) +{ + int fd; + + if (!get_dwncall_desc) { + syslog(LOG_DEBUG, "%s: failed (unable to get fd)", + smb_dwncall_info[opcode]); + return (-1); + } + + if ((fd = get_dwncall_desc()) == -1) { + syslog(LOG_ERR, "%s: failed (invalid fd)", + smb_dwncall_info[opcode]); + return (-1); + } + + return (fd); +} + +uint64_t +smb_dwncall_user_num() +{ + char *buf, *rbufp; + size_t buflen, rbufsize; + int64_t num; + uint_t opcode = SMB_KDR_USER_NUM; + int fd; + + if ((fd = smb_dwncall_init_fd(opcode)) < 0) + return (0); + + buf = smb_dr_set_opcode(opcode, &buflen); + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smb_dwncall_info[opcode]); + if (rbufp) { + if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_uint32_t, &num) != 0) { + num = -1; + } + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + return (num); +} + +/* + * smb_dwncall_get_users + * + * The calling function must free the output parameter 'users'. + */ +int +smb_dwncall_get_users(int offset, smb_dr_ulist_t *users) +{ + char *buf = NULL, *rbufp; + size_t buflen, rbufsize; + uint_t opcode = SMB_KDR_USER_LIST; + int fd, rc = -1; + + bzero(users, sizeof (smb_dr_ulist_t)); + if ((fd = smb_dwncall_init_fd(opcode)) < 0) + return (-1); + + buf = smb_dr_encode_common(opcode, &offset, xdr_uint32_t, &buflen); + if (!buf) { + syslog(LOG_ERR, "smb_dwncall_get_users: encode error"); + return (-1); + } + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smb_dwncall_info[opcode]); + + + if (rbufp) { + rc = smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_smb_dr_ulist_t, users); + if (rc) + syslog(LOG_ERR, "smb_dwncall_get_users: decode error"); + else + rc = users->dul_cnt; + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + return (rc); +} + +/* + * smb_dwncall_share() + * + * This is a downcall to the kernel that is executed + * upon share enable and disable. + */ + +int +smb_dwncall_share(int op, char *path, char *sharename) +{ + char *buf = NULL, *rbufp; + size_t buflen, rbufsize; + int32_t opcode = SMB_KDR_SHARE; + smb_dr_kshare_t kshare; + int fd, rc = -1; + + if ((op != LMSHR_ADD) && + (op != LMSHR_DELETE)) + return (-1); + + if ((fd = smb_dwncall_init_fd(opcode)) < 0) { + syslog(LOG_ERR, "smb_dwncall_share: init error"); + return (-1); + } + + kshare.k_op = op; + kshare.k_path = strdup(path); + kshare.k_sharename = strdup(sharename); + + buf = smb_dr_encode_kshare(&kshare, &buflen); + + if (!buf) { + syslog(LOG_ERR, "smb_dwncall_share: encode error"); + return (-1); + } + + rbufp = smb_dr_clnt_call(fd, buf, buflen, &rbufsize, + smb_dwncall_info[opcode]); + + if (rbufp) { + if (smb_dr_decode_common(rbufp + SMB_DR_DATA_OFFSET, + rbufsize - SMB_DR_DATA_OFFSET, xdr_int32_t, &rc) != 0) { + rc = -1; + } + } + + smb_dr_clnt_free(buf, buflen, rbufp, rbufsize); + return (rc); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_group_door_encdec.c b/usr/src/lib/smbsrv/libsmb/common/smb_group_door_encdec.c new file mode 100644 index 000000000000..0b297649269b --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_group_door_encdec.c @@ -0,0 +1,337 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include + +/* + * smb_grplist_mkselfrel + * + * encode: structure -> flat buffer (buffer size) + * Pre-condition: obj is non-null. + */ +static uint8_t * +smb_grplist_mkselfrel(ntgrp_list_t *obj, uint32_t *len) +{ + uint8_t *buf; + XDR xdrs; + + if (!obj) { + syslog(LOG_ERR, "smb_grplist_mkselfrel: invalid parameter"); + return (NULL); + } + *len = xdr_sizeof(xdr_ntgrp_list_t, obj); + buf = (uint8_t *)malloc(*len); + + xdrmem_create(&xdrs, (const caddr_t)buf, *len, XDR_ENCODE); + + if (!xdr_ntgrp_list_t(&xdrs, obj)) { + syslog(LOG_ERR, "smb_grplist_mkselfrel: XDR encode error"); + free(buf); + *len = 0; + buf = NULL; + } + + xdr_destroy(&xdrs); + return (buf); +} + +/* + * smb_grplist_mkabsolute + * + * decode: flat buffer -> structure + */ +static ntgrp_list_t * +smb_grplist_mkabsolute(uint8_t *buf, uint32_t len) +{ + ntgrp_list_t *obj; + XDR xdrs; + + xdrmem_create(&xdrs, (const caddr_t)buf, len, XDR_DECODE); + + if ((obj = (ntgrp_list_t *) + malloc(sizeof (ntgrp_list_t))) == 0) { + syslog(LOG_ERR, "smb_grplist_mkabsolute: resource shortage"); + xdr_destroy(&xdrs); + return (NULL); + } + bzero(obj, sizeof (ntgrp_list_t)); + if (!xdr_ntgrp_list_t(&xdrs, obj)) { + syslog(LOG_ERR, "smb_grplist_mkabsolute: XDR decode error"); + smb_group_free_list(obj, 1); + obj = NULL; + } + + xdr_destroy(&xdrs); + return (obj); +} + +/* + * smb_grpmemberlist_mkselfrel + * + * encode: structure -> flat buffer (buffer size) + * Pre-condition: obj is non-null. + */ +static uint8_t * +smb_grpmemberlist_mkselfrel(ntgrp_member_list_t *obj, uint32_t *len) +{ + uint8_t *buf; + XDR xdrs; + + if (!obj) { + syslog(LOG_ERR, + "smb_grpmemberlist_mkselfrel: invalid parameter"); + return (NULL); + } + *len = xdr_sizeof(xdr_ntgrp_member_list_t, obj); + buf = (uint8_t *)malloc(*len); + xdrmem_create(&xdrs, (const caddr_t)buf, *len, XDR_ENCODE); + if (!xdr_ntgrp_member_list_t(&xdrs, obj)) { + syslog(LOG_ERR, + "smb_grpmemberlist_mkselfrel: XDR encode error"); + free(buf); + *len = 0; + buf = NULL; + } + + xdr_destroy(&xdrs); + return (buf); +} + +/* + * ntgrp_list_mkabsolute + * + * decode: flat buffer -> structure + */ +static ntgrp_member_list_t * +smb_grpmemberlist_mkabsolute(uint8_t *buf, uint32_t len) +{ + ntgrp_member_list_t *obj = NULL; + XDR xdrs; + + xdrmem_create(&xdrs, (const caddr_t)buf, len, XDR_DECODE); + + if ((obj = (ntgrp_member_list_t *) + malloc(sizeof (ntgrp_member_list_t))) == 0) { + xdr_destroy(&xdrs); + syslog(LOG_ERR, + "smb_grpmemberlist_mkabsolute: resource shortage"); + return (NULL); + } + bzero(obj, sizeof (ntgrp_member_list_t)); + bzero(obj->members, SMB_GROUP_PER_LIST * sizeof (members_list)); + if (!xdr_ntgrp_member_list_t(&xdrs, obj)) { + syslog(LOG_ERR, + "smb_grpmemberlist_mkabsolute: XDR decode error"); + smb_group_free_memberlist(obj, 1); + obj = NULL; + } + + xdr_destroy(&xdrs); + return (obj); +} + +/* + * smb_privlist_mkselfrel + * + * encode: structure -> flat buffer (buffer size) + * Pre-condition: obj is non-null. + */ +static uint8_t * +smb_grpprivlist_mkselfrel(ntpriv_list_t *obj, uint32_t *len) +{ + uint8_t *buf; + XDR xdrs; + + if (!obj) { + syslog(LOG_ERR, + "smb_grpprivlist_mkselfrel: invalid parameter"); + return (NULL); + } + *len = xdr_sizeof(xdr_ntpriv_list_t, obj); + buf = (uint8_t *)malloc(*len); + if (!buf) { + syslog(LOG_ERR, + "smb_grpprivlist_mkselfrel: resource shortage"); + return (NULL); + } + xdrmem_create(&xdrs, (const caddr_t)buf, *len, XDR_ENCODE); + if (!xdr_ntpriv_list_t(&xdrs, obj)) { + syslog(LOG_ERR, + "smb_grpprivlist_mkselfrel: XDR encode error"); + *len = 0; + free(buf); + buf = NULL; + } + xdr_destroy(&xdrs); + return (buf); +} + +/* + * smb_privlist_mkabsolute + * + * decode: flat buffer -> structure + */ +static ntpriv_list_t * +smb_grpprivlist_mkabsolute(uint8_t *buf, uint32_t len) +{ + ntpriv_list_t *obj = NULL; + XDR xdrs; + uint32_t status; + int length = 0, num_privs = 0; + + xdrmem_create(&xdrs, (const caddr_t)buf, len, XDR_DECODE); + status = smb_group_priv_num(&num_privs); + if (status != 0) { + syslog(LOG_ERR, + "smb_grpprivlist_mkabsolute: Cannot get privlist."); + xdr_destroy(&xdrs); + return (NULL); + } + + if (num_privs > 0) { + length = sizeof (int) + (num_privs * sizeof (privs_t)); + if ((obj = (ntpriv_list_t *)malloc(length)) == 0) { + syslog(LOG_ERR, + "smb_grpprivlist_mkabsolute: resource shortage"); + xdr_destroy(&xdrs); + return (NULL); + } + } + bzero(obj, sizeof (ntpriv_list_t)); + bzero(obj->privs, num_privs * sizeof (privs_t)); + if (!xdr_ntpriv_list_t(&xdrs, obj)) { + syslog(LOG_ERR, "smb_grpprivlist_mkabsolute: XDR decode error"); + smb_group_free_privlist(obj, 1); + obj = NULL; + } + xdr_destroy(&xdrs); + return (obj); +} + +char * +smb_dr_encode_grp_list(uint32_t opcode, ntgrp_list_t *list, + size_t *len) +{ + char *buf; + smb_dr_bytes_t arg; + + arg.bytes_val = smb_grplist_mkselfrel(list, &arg.bytes_len); + + buf = smb_dr_encode_common(opcode, &arg, xdr_smb_dr_bytes_t, len); + free(arg.bytes_val); + return (buf); +} + +ntgrp_list_t * +smb_dr_decode_grp_list(char *buf, size_t len) +{ + smb_dr_bytes_t arg; + ntgrp_list_t *list; + + bzero(&arg, sizeof (smb_dr_bytes_t)); + if (smb_dr_decode_common(buf, len, xdr_smb_dr_bytes_t, &arg) + != 0) { + syslog(LOG_ERR, "smb_dr_decode_grplist: XDR decode error"); + xdr_free(xdr_smb_dr_bytes_t, (char *)&arg); + return (NULL); + } + list = smb_grplist_mkabsolute(arg.bytes_val, arg.bytes_len); + xdr_free(xdr_smb_dr_bytes_t, (char *)&arg); + return (list); +} + +char * +smb_dr_encode_grp_memberlist(uint32_t opcode, + ntgrp_member_list_t *list, size_t *len) +{ + char *buf; + smb_dr_bytes_t arg; + + arg.bytes_val = smb_grpmemberlist_mkselfrel(list, &arg.bytes_len); + + buf = smb_dr_encode_common(opcode, &arg, xdr_smb_dr_bytes_t, len); + free(arg.bytes_val); + return (buf); +} + +ntgrp_member_list_t * +smb_dr_decode_grp_memberlist(char *buf, size_t len) +{ + smb_dr_bytes_t arg; + ntgrp_member_list_t *list; + + bzero(&arg, sizeof (smb_dr_bytes_t)); + if (smb_dr_decode_common(buf, len, xdr_smb_dr_bytes_t, &arg) + != 0) { + syslog(LOG_ERR, + "smb_dr_decode_grpmemberlist: XDR decode error"); + xdr_free(xdr_smb_dr_bytes_t, (char *)&arg); + return (NULL); + } + list = smb_grpmemberlist_mkabsolute(arg.bytes_val, arg.bytes_len); + xdr_free(xdr_smb_dr_bytes_t, (char *)&arg); + return (list); +} + +char * +smb_dr_encode_grp_privlist(uint32_t opcode, + ntpriv_list_t *list, size_t *len) +{ + char *buf; + smb_dr_bytes_t arg; + + arg.bytes_val = smb_grpprivlist_mkselfrel(list, &arg.bytes_len); + + buf = smb_dr_encode_common(opcode, &arg, xdr_smb_dr_bytes_t, len); + free(arg.bytes_val); + return (buf); +} + +ntpriv_list_t * +smb_dr_decode_grp_privlist(char *buf, size_t len) +{ + smb_dr_bytes_t arg; + ntpriv_list_t *list; + + bzero(&arg, sizeof (smb_dr_bytes_t)); + if (smb_dr_decode_common(buf, len, xdr_smb_dr_bytes_t, &arg) + != 0) { + syslog(LOG_ERR, "smb_dr_decode_grp_privlist: XDR decode error"); + xdr_free(xdr_smb_dr_bytes_t, (char *)&arg); + return (NULL); + } + list = smb_grpprivlist_mkabsolute(arg.bytes_val, arg.bytes_len); + xdr_free(xdr_smb_dr_bytes_t, (char *)&arg); + return (list); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_group_xdr.c b/usr/src/lib/smbsrv/libsmb/common/smb_group_xdr.c new file mode 100644 index 000000000000..723a6471f9c7 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_group_xdr.c @@ -0,0 +1,158 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include +#include + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file was originally generated using rpcgen. + */ + +bool_t +xdr_ntgrp_dr_arg_t(xdrs, objp) + XDR *xdrs; + ntgrp_dr_arg_t *objp; +{ + if (!xdr_string(xdrs, &objp->gname, ~0)) + return (FALSE); + if (!xdr_string(xdrs, &objp->desc, ~0)) + return (FALSE); + if (!xdr_string(xdrs, &objp->member, ~0)) + return (FALSE); + if (!xdr_string(xdrs, &objp->newgname, ~0)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->privid)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->priv_attr)) + return (FALSE); + if (!xdr_int(xdrs, &objp->offset)) + return (FALSE); + if (!xdr_string(xdrs, &objp->scope, ~0)) + return (FALSE); + if (!xdr_int(xdrs, &objp->type)) + return (FALSE); + if (!xdr_int(xdrs, &objp->count)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->ntstatus)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_ntgrp_t(xdrs, objp) + XDR *xdrs; + ntgrp_t *objp; +{ + if (!xdr_uint32_t(xdrs, &objp->rid)) + return (FALSE); + if (!xdr_string(xdrs, &objp->name, ~0)) + return (FALSE); + if (!xdr_string(xdrs, &objp->desc, ~0)) + return (FALSE); + if (!xdr_string(xdrs, &objp->type, ~0)) + return (FALSE); + if (!xdr_string(xdrs, &objp->sid, ~0)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->attr)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_ntgrp_list_t(xdrs, objp) + XDR *xdrs; + ntgrp_list_t *objp; +{ + if (!xdr_int(xdrs, &objp->cnt)) + return (FALSE); + if (!xdr_vector(xdrs, (char *)objp->groups, SMB_GROUP_PER_LIST, + sizeof (ntgrp_t), (xdrproc_t)xdr_ntgrp_t)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_members_list(xdrs, objp) + XDR *xdrs; + members_list *objp; +{ + if (!xdr_string(xdrs, objp, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_ntgrp_member_list_t(xdrs, objp) + XDR *xdrs; + ntgrp_member_list_t *objp; +{ + if (!xdr_uint32_t(xdrs, &objp->rid)) + return (FALSE); + if (!xdr_int(xdrs, &objp->cnt)) + return (FALSE); + if (!xdr_vector(xdrs, (char *)objp->members, SMB_GROUP_PER_LIST, + sizeof (members_list), (xdrproc_t)xdr_members_list)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_ntpriv_t(xdrs, objp) + XDR *xdrs; + ntpriv_t *objp; +{ + if (!xdr_uint32_t(xdrs, &objp->id)) + return (FALSE); + if (!xdr_string(xdrs, &objp->name, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_privs_t(xdrs, objp) + XDR *xdrs; + privs_t *objp; +{ + if (!xdr_pointer(xdrs, (char **)objp, sizeof (ntpriv_t), + (xdrproc_t)xdr_ntpriv_t)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_ntpriv_list_t(xdrs, objp) + XDR *xdrs; + ntpriv_list_t *objp; +{ + if (!xdr_int(xdrs, &objp->cnt)) + return (FALSE); + if (!xdr_vector(xdrs, (char *)objp->privs, objp->cnt, + sizeof (privs_t), (xdrproc_t)xdr_privs_t)) + return (FALSE); + return (TRUE); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_ht.c b/usr/src/lib/smbsrv/libsmb/common/smb_ht.c new file mode 100644 index 000000000000..d167931539cf --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_ht.c @@ -0,0 +1,639 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Generic hash table library. The hash table is an array of pointers + * to items. Hash collisions are handled using linked lists from the + * table entries. A handle is associated with each table, which is used + * to maintain the hash table. + * + * +------+ +-------+ +----+ +----+ + * |handle|---> |index 0|--->|item|--->|item|---> + * | ... | +-------+ +----+ +----+ + * | ... | |index 1|---> + * +------+ +-------+ +----+ +----+ +----+ + * |index 2|--->|item|--->|item|--->|item|---> + * +-------+ +----+ +----+ +----+ + * | ... |---> + * +-------+ + * | ... |---> + * +-------+ + * |index n|---> + * +-------+ + * + */ + +#include +#include +#include + +static size_t ht_default_hash(HT_HANDLE *handle, const char *key); + +/* + * ht_is_power2 + * + * Inline function to determine if a value is a power of two. This + * function is used by the library to validate the table size when + * a new table is created. + * + * Returns 1 if value given is power of two, otherwise returns 0. + */ +static size_t +ht_is_power2(size_t value) +{ + return (((value & (value - 1)) == 0)? 1 : 0); +} + + +/* + * ht_create_table + * + * Create a hash table. The table size must be a positive integer and + * must be a power of two. The key size must be a positive integer. + * For null terminated keys, the key size does not need to include the + * null terminating character. The type of key is indicated by the + * flags (see hash_table.h). + * + * The handle and the table are are malloc'd using a single call, to + * avoid two allocations. The table is located immediately after the + * handle. + * + * On success a pointer to an opaque handle is returned. Otherwise a + * null pointer is returned. + */ +HT_HANDLE * +ht_create_table(size_t table_size, size_t key_size, size_t flags) +{ + HT_HANDLE *ht; + size_t msize; + size_t i; + + if ((table_size == 0) || (key_size == 0)) + return (NULL); + + if (ht_is_power2(table_size) == 0) + return (NULL); + + msize = sizeof (HT_HANDLE) + (sizeof (HT_TABLE_ENTRY) * table_size); + + if ((ht = (HT_HANDLE *)malloc(msize)) == 0) + return (NULL); + + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + ht->ht_table = (HT_TABLE_ENTRY *)((char *)ht + sizeof (HT_HANDLE)); + ht->ht_table_size = table_size; + ht->ht_table_mask = table_size - 1; + ht->ht_key_size = key_size; + ht->ht_total_items = 0; + ht->ht_flags = flags; + ht->ht_hash = ht_default_hash; + ht->ht_callback = 0; + ht->ht_sequence = random(); + ht->ht_cmp = ((flags & HTHF_FIXED_KEY) == 0) + ? (HT_CMP)strncmp : (HT_CMP)memcmp; + + for (i = 0; i < table_size; i++) + bzero(&ht->ht_table[i], sizeof (HT_TABLE_ENTRY)); + + return (ht); +} + + +/* + * ht_destroy_table + * + * Destroy a hash table. All entries in the table are removed, which + * may invoke the callback if it's installed, and the memory is freed. + */ +void +ht_destroy_table(HT_HANDLE *handle) +{ + HT_ITEM *item; + HT_ITERATOR iterator; + + if (handle == 0) + return; + + /* To remove marked entries */ + (void) ht_clean_table(handle); + while ((item = ht_findfirst(handle, &iterator)) != 0) + (void) ht_remove_item(handle, item->hi_key); + + free(handle); +} + + +/* + * ht_get_total_items + * + * Return the total number of items in the table. Returns -1 if the + * handle is invalid. + */ +size_t +ht_get_total_items(HT_HANDLE *handle) +{ + if (handle == 0) + return ((size_t)-1); + + return (handle->ht_total_items); +} + + +/* + * ht_default_hash + * + * Default hash function to compute the table index (hash value) based + * on the specified key. This will identify the location for the + * corresponding item in the hash table. The handle and key pointers + * should be validated before this function is called. + * + * Returns the table index location for the item. + */ +static size_t +ht_default_hash(HT_HANDLE *handle, const char *key) +{ + unsigned int hash_ndx = 0; + size_t rval; + + if ((handle->ht_flags & HTHF_FIXED_KEY) == 0) { + while (*key) { + hash_ndx += *key; + ++key; + } + } else { + int key_len = handle->ht_key_size; + + while (key_len--) { + hash_ndx += *key; + ++key; + } + } + + rval = (hash_ndx * HASH_MESH_VALUE) & handle->ht_table_mask; + return (rval); +} + + +/* + * ht_set_cmpfn + * + * Replace the current compare function. As the this is function + * for comparing items' key, it should not be called while there are + * items in the table. + */ +void +ht_set_cmpfn(HT_HANDLE *handle, HT_CMP cmpfn) +{ + if (handle) + handle->ht_cmp = cmpfn; +} + +/* + * ht_add_item + * + * Adds an item to a hash table. The hash table is identified by the + * handle and the key is used to generate a hashed index. The data + * item can be null; it is never dereferenced. We don't check for + * duplicates. If duplicate keys are added to the table, the last + * item added will be to the front of the duplicate list. + * + * The table sequence number may be modified here. + * + * If the item is successfully inserted, a pointer to the item object + * is returned. Otherwise a null pointer is returned. + */ +HT_ITEM * +ht_add_item(HT_HANDLE *handle, const char *key, const void *data) +{ + size_t h_index, key_len; + size_t msize; + HT_ITEM *item; + + if (handle == 0 || key == 0) + return (NULL); + + if (handle->ht_flags & HTHF_FIXED_KEY) { + key_len = handle->ht_key_size; + } else { + key_len = strlen(key); + + if (key_len > handle->ht_key_size) + return (NULL); + + /* Include the null terminator */ + ++key_len; + } + + msize = key_len + sizeof (HT_ITEM); + + if ((item = malloc(msize)) == 0) + return (NULL); + + item->hi_key = (char *)item + sizeof (HT_ITEM); + (void) memcpy(item->hi_key, key, key_len); + item->hi_data = (void *)data; + item->hi_flags = 0; + + h_index = handle->ht_hash(handle, key); + + /* + * Add to the front of the list. + */ + item->hi_next = handle->ht_table[h_index].he_head; + handle->ht_table[h_index].he_head = item; + + handle->ht_table[h_index].he_count++; + handle->ht_total_items++; + handle->ht_sequence++; + + return (item); +} + + +/* + * ht_replace_item + * + * Replace an item in a hash table. The item associated with key is removed + * using ht_remove_item and a new item is added using ht_add_item. We rely + * on parameter validation in ht_remove_item and ht_add_item. + * + * The table sequence number may be modified here. + */ +HT_ITEM * +ht_replace_item(HT_HANDLE *handle, const char *key, const void *data) +{ + (void) ht_remove_item(handle, key); + + return (ht_add_item(handle, key, data)); +} + + +/* + * ht_remove_item + * + * Remove an item from a hash table. If there are duplicate keys, then the + * first key found will be deleted. Note that the data pointer is never + * dereferenced. If a callback is installed, it will be invoked and the + * return value will be null. Otherwise, the data pointer supplied by the + * application will be returned. If there is an error, a null pointer will + * be returned. + * + * The table sequence number may be modified here. + */ +void * +ht_remove_item(HT_HANDLE *handle, const char *key) +{ + size_t h_index; + HT_ITEM *cur, *prev; + int key_len; + void *data = 0; + + if (handle == 0 || key == 0) + return (NULL); + + if ((handle->ht_flags & HTHF_FIXED_KEY) == 0) + key_len = strlen(key) + 1; + else + key_len = handle->ht_key_size; + + h_index = handle->ht_hash(handle, key); + + cur = handle->ht_table[h_index].he_head; + prev = 0; + + while (cur) { + if (!(cur->hi_flags & HTIF_MARKED_DELETED) && + (handle->ht_cmp(cur->hi_key, key, key_len) == 0)) { + /* found key */ + if (prev == 0) + handle->ht_table[h_index].he_head = + cur->hi_next; + else + prev->hi_next = cur->hi_next; + + if (handle->ht_callback) + handle->ht_callback(cur); + else + data = cur->hi_data; + + /* + * Since the key and the item were allocated as + * a single chunk, we only need one free here. + */ + free(cur); + + handle->ht_table[h_index].he_count--; + handle->ht_total_items--; + handle->ht_sequence++; + break; + } + + prev = cur; + cur = cur->hi_next; + } + + return (data); +} + +/* + * ht_find_item + * + * Find an item in a hash table. If there are duplicate keys then the + * first item found (which will be the last one added) will be returned. + * + * Returns a pointer to an item. Otherwise returns a null pointer to + * indicate an error or that the key didn't match anything in the table. + */ +HT_ITEM * +ht_find_item(HT_HANDLE *handle, const char *key) +{ + size_t h_index; + HT_ITEM *cur; + int key_len; + + if (handle == 0 || key == 0) + return (NULL); + + if ((handle->ht_flags & HTHF_FIXED_KEY) == 0) + key_len = strlen(key) + 1; + else + key_len = handle->ht_key_size; + + h_index = handle->ht_hash(handle, key); + cur = handle->ht_table[h_index].he_head; + + while (cur) { + if (!(cur->hi_flags & HTIF_MARKED_DELETED) && + (handle->ht_cmp(cur->hi_key, key, key_len) == 0)) + return (cur); + + cur = cur->hi_next; + } + + return (NULL); +} + + +/* + * ht_register_callback + * + * Register an application callback function that can be used to process + * an item when it is removed from the table, i.e. free any memory + * allocated for that data item. + * + * The previous callback function pointer, which may be null, before + * registering the new one. This provides the caller with the option to + * restore a previous callback as required. + */ +HT_CALLBACK +ht_register_callback(HT_HANDLE *handle, HT_CALLBACK callback) +{ + HT_CALLBACK old_callback; + + if (handle == 0) + return (NULL); + + old_callback = handle->ht_callback; + handle->ht_callback = callback; + + return (old_callback); +} + + +/* + * ht_clean_table + * + * This function removes all the items that are marked for deletion. Note + * that this will invoke the callback, if one has been installed. If this + * call is used, the callback mechanism is the only way for an application + * to free the item data if it was dynamically allocated. + * + * The table sequence number may be modified here. + * + * Returns 0 if the handle is valid; otherwise returns -1. + */ +size_t +ht_clean_table(HT_HANDLE *handle) +{ + size_t i; + HT_ITEM *cur, *prev; + + if (handle == 0) + return ((size_t)-1); + + for (i = 0; i < handle->ht_table_size; i++) { + cur = handle->ht_table[i].he_head; + prev = 0; + + while (cur) { + if (cur->hi_flags & HTIF_MARKED_DELETED) { + /* + * We have a marked item: remove it. + */ + if (prev == 0) + handle->ht_table[i].he_head = + cur->hi_next; + else + prev->hi_next = cur->hi_next; + + if (handle->ht_callback) + handle->ht_callback(cur); + + /* + * Since the key and the item were allocated as + * a single chunk, we only need one free here. + */ + free(cur); + + handle->ht_table[i].he_count--; + handle->ht_sequence++; + + if (prev == 0) + cur = handle->ht_table[i].he_head; + else + cur = prev->hi_next; + continue; + } + + prev = cur; + cur = cur->hi_next; + } + } + + return (0); +} + + +/* + * ht_mark_delete + * + * This function marks an item for deletion, which may be useful when + * using findfirst/findnext to avoid modifying the table during the + * table scan. Marked items can be removed later using ht_clean_table. + */ +void +ht_mark_delete(HT_HANDLE *handle, HT_ITEM *item) +{ + if (handle && item) { + item->hi_flags |= HTIF_MARKED_DELETED; + handle->ht_total_items--; + } +} + +/* + * ht_clear_delete + * + * This function clear an item from marked for deletion list. + */ +void +ht_clear_delete(HT_HANDLE *handle, HT_ITEM *item) +{ + if (handle && item) { + item->hi_flags &= ~HTIF_MARKED_DELETED; + handle->ht_total_items++; + } +} + +/* + * ht_bucket_search + * + * Returns first item which is not marked as deleted + * in the specified bucket by 'head' + */ +static HT_ITEM * +ht_bucket_search(HT_ITEM *head) +{ + HT_ITEM *item = head; + while ((item != 0) && (item->hi_flags & HTIF_MARKED_DELETED)) + item = item->hi_next; + + return (item); +} + +/* + * ht_findfirst + * + * This function is used to begin an iteration through the hash table. + * The iterator is initialized and the first item in the table (as + * determined by the hash algorithm) is returned. The current sequence + * number is stored in the iterator to determine whether or not the + * the table has changed between calls. If the table is empty, a null + * pointer is returned. + */ +HT_ITEM * +ht_findfirst(HT_HANDLE *handle, HT_ITERATOR *iterator) +{ + HT_ITEM *item; + size_t h_index; + + if (handle == 0 || iterator == 0 || handle->ht_total_items == 0) + return (NULL); + + (void) memset(iterator, 0, sizeof (HT_ITERATOR)); + iterator->hti_handle = handle; + iterator->hti_sequence = handle->ht_sequence; + + for (h_index = 0; h_index < handle->ht_table_size; ++h_index) { + item = ht_bucket_search(handle->ht_table[h_index].he_head); + if (item != 0) { + iterator->hti_index = h_index; + iterator->hti_item = item; + return (item); + } + } + + return (NULL); +} + +/* + * ht_findnext + * + * Find the next item in the table for the given iterator. Iterators must + * be initialized by ht_findfirst, which will also return the first item + * in the table. If an item is available, a pointer to it is returned. + * Otherwise a null pointer is returned. A null pointer may indicate: + * + * - an invalid iterator (i.e. ht_findfirst has not been called) + * - the table has changed since the previous findfirst/findnext + * - the entire table has been traversed + * + * The caller can use ht_get_total_items to determine whether or not all + * of the items in the table have been visited. + */ +HT_ITEM * +ht_findnext(HT_ITERATOR *iterator) +{ + HT_HANDLE *handle; + HT_ITEM *item; + size_t total; + size_t index; + + if (iterator == 0 || iterator->hti_handle == 0 || + iterator->hti_sequence == 0) { + /* Invalid iterator */ + return (NULL); + } + + handle = iterator->hti_handle; + + if (iterator->hti_item == 0 || + iterator->hti_sequence != handle->ht_sequence) { + /* + * No more items or the table has changed + * since the last call. + */ + return (NULL); + } + + /* + * Check for another item in the current bucket. + */ + item = ht_bucket_search(iterator->hti_item->hi_next); + if (item != 0) { + iterator->hti_item = item; + return (item); + } + + /* + * Nothing else in the current bucket. Look for another + * bucket with something in it and return the head item. + */ + total = handle->ht_table_size; + for (index = iterator->hti_index + 1; index < total; ++index) { + item = ht_bucket_search(handle->ht_table[index].he_head); + if (item != 0) { + iterator->hti_index = index; + iterator->hti_item = item; + return (item); + } + } + + iterator->hti_index = 0; + iterator->hti_item = 0; + iterator->hti_sequence = 0; + return (NULL); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_idmap.c b/usr/src/lib/smbsrv/libsmb/common/smb_idmap.c new file mode 100644 index 000000000000..f410fe3237d7 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_idmap.c @@ -0,0 +1,382 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include + +static idmap_handle_t *idmap_clnt_hdl = NULL; +static int smb_idmap_batch_binsid(smb_idmap_batch_t *sib); + +/* + * smb_idmap_start + * + * This function initializes the idmap client handle. It should be called + * at startup. + */ +int +smb_idmap_start(void) +{ + idmap_stat stat; + + if (idmap_clnt_hdl) + return (0); + + stat = idmap_init(&idmap_clnt_hdl); + if (stat < 0) { + syslog(LOG_ERR, "smb_idmap_start: idmap_init failed (%s)", + idmap_stat2string(NULL, stat)); + return (-1); + } + + return (0); +} + +/* + * smb_idmap_stop + * + * This function destroys the idmap client handle. It should be called + * prior to exiting the SMB daemon. + */ +void +smb_idmap_stop(void) +{ + if (idmap_clnt_hdl) { + (void) idmap_fini(idmap_clnt_hdl); + idmap_clnt_hdl = NULL; + } +} + +/* + * smb_idmap_restart + * + * This function should be called when the idmap client handle + * becomes invalid. + */ +int +smb_idmap_restart(void) +{ + smb_idmap_stop(); + if (smb_idmap_start() != 0) { + syslog(LOG_ERR, "smb_idmap_restart: smb_idmap_start failed"); + return (-1); + } + + return (0); +} + +/* + * smb_idmap_getsid + * + * Tries to get a mapping for the given uid/gid + */ +idmap_stat +smb_idmap_getsid(uid_t id, int idtype, nt_sid_t **sid) +{ + smb_idmap_batch_t sib; + idmap_stat stat; + + stat = smb_idmap_batch_create(&sib, 1, SMB_IDMAP_ID2SID); + if (stat != IDMAP_SUCCESS) + return (stat); + + stat = smb_idmap_batch_getsid(sib.sib_idmaph, &sib.sib_maps[0], + id, idtype); + + if (stat != IDMAP_SUCCESS) { + smb_idmap_batch_destroy(&sib); + return (stat); + } + + stat = smb_idmap_batch_getmappings(&sib); + + if (stat != IDMAP_SUCCESS) { + smb_idmap_batch_destroy(&sib); + return (stat); + } + + *sid = nt_sid_dup(sib.sib_maps[0].sim_sid); + + smb_idmap_batch_destroy(&sib); + + return (IDMAP_SUCCESS); +} + +/* + * smb_idmap_batch_create + * + * Creates and initializes the context for batch ID mapping. + */ +idmap_stat +smb_idmap_batch_create(smb_idmap_batch_t *sib, uint16_t nmap, int flags) +{ + idmap_stat stat; + + if (!sib) + return (IDMAP_ERR_ARG); + + bzero(sib, sizeof (smb_idmap_batch_t)); + stat = idmap_get_create(idmap_clnt_hdl, &sib->sib_idmaph); + if (stat != IDMAP_SUCCESS) + return (stat); + + sib->sib_flags = flags; + sib->sib_nmap = nmap; + sib->sib_size = nmap * sizeof (smb_idmap_t); + sib->sib_maps = malloc(sib->sib_size); + if (!sib->sib_maps) + return (IDMAP_ERR_MEMORY); + + bzero(sib->sib_maps, sib->sib_size); + return (IDMAP_SUCCESS); +} + +/* + * smb_idmap_batch_destroy + * + * Frees the batch ID mapping context. + */ +void +smb_idmap_batch_destroy(smb_idmap_batch_t *sib) +{ + nt_sid_t *sid; + char *domsid; + int i; + + if (!sib) + return; + + if (sib->sib_idmaph) { + idmap_get_destroy(sib->sib_idmaph); + sib->sib_idmaph = NULL; + } + + if (!sib->sib_maps) + return; + + switch (sib->sib_flags) { + case SMB_IDMAP_SID2ID: + /* + * SIDs are allocated only when mapping + * UID/GID to SIDs + */ + for (i = 0; i < sib->sib_nmap; i++) { + sid = sib->sib_maps[i].sim_sid; + if (sid) + free(sid); + } + break; + case SMB_IDMAP_ID2SID: + /* + * SID prefixes are allocated only when mapping + * SIDs to UID/GID + */ + for (i = 0; i < sib->sib_nmap; i++) { + domsid = sib->sib_maps[i].sim_domsid; + if (domsid) + free(domsid); + } + break; + default: + break; + } + + if (sib->sib_size && sib->sib_maps) { + free(sib->sib_maps); + sib->sib_maps = NULL; + } +} + +/* + * smb_idmap_batch_getid + * + * Queue a request to map the given SID to a UID or GID. + * + * sim->sim_id should point to variable that's supposed to + * hold the returned UID/GID. This needs to be setup by caller + * of this function. + * If requested ID type is known, it's passed as 'idtype', + * if it's unknown it'll be returned in sim->sim_idtype. + */ +idmap_stat +smb_idmap_batch_getid(idmap_get_handle_t *idmaph, smb_idmap_t *sim, + nt_sid_t *sid, int idtype) +{ + nt_sid_t *tmpsid; + idmap_stat stat; + int flag = 0; + + if (!idmaph || !sim || !sid) + return (IDMAP_ERR_ARG); + + tmpsid = nt_sid_dup(sid); + if (!tmpsid) + return (IDMAP_ERR_MEMORY); + + if (nt_sid_split(tmpsid, &sim->sim_rid) != 0) { + free(tmpsid); + return (IDMAP_ERR_ARG); + } + + sim->sim_domsid = nt_sid_format(tmpsid); + free(tmpsid); + + switch (idtype) { + case SMB_IDMAP_USER: + stat = idmap_get_uidbysid(idmaph, sim->sim_domsid, + sim->sim_rid, flag, sim->sim_id, &sim->sim_stat); + break; + + case SMB_IDMAP_GROUP: + stat = idmap_get_gidbysid(idmaph, sim->sim_domsid, + sim->sim_rid, flag, sim->sim_id, &sim->sim_stat); + break; + + case SMB_IDMAP_UNKNOWN: + stat = idmap_get_pidbysid(idmaph, sim->sim_domsid, + sim->sim_rid, flag, sim->sim_id, &sim->sim_idtype, + &sim->sim_stat); + break; + + default: + return (IDMAP_ERR_ARG); + } + + return (stat); +} + +/* + * smb_idmap_batch_getsid + * + * Queue a request to map the given UID/GID to a SID. + * + * sim->sim_domsid and sim->sim_rid will contain the mapping + * result upon successful process of the batched request. + */ +idmap_stat +smb_idmap_batch_getsid(idmap_get_handle_t *idmaph, smb_idmap_t *sim, + uid_t id, int idtype) +{ + idmap_stat stat; + int flag = 0; + + if (!idmaph || !sim) + return (IDMAP_ERR_ARG); + + switch (idtype) { + case SMB_IDMAP_USER: + stat = idmap_get_sidbyuid(idmaph, id, flag, + &sim->sim_domsid, &sim->sim_rid, &sim->sim_stat); + break; + + case SMB_IDMAP_GROUP: + stat = idmap_get_sidbygid(idmaph, id, flag, + &sim->sim_domsid, &sim->sim_rid, &sim->sim_stat); + break; + + case SMB_IDMAP_EVERYONE: + /* Everyone S-1-1-0 */ + sim->sim_domsid = "S-1-1"; + sim->sim_rid = 0; + sim->sim_stat = IDMAP_SUCCESS; + stat = IDMAP_SUCCESS; + break; + + default: + return (IDMAP_ERR_ARG); + } + + return (stat); +} + +/* + * smb_idmap_batch_getmappings + * + * trigger ID mapping service to get the mappings for queued + * requests. + * + * Checks the result of all the queued requests. + */ +idmap_stat +smb_idmap_batch_getmappings(smb_idmap_batch_t *sib) +{ + idmap_stat stat = IDMAP_SUCCESS; + int i; + + stat = idmap_get_mappings(sib->sib_idmaph); + if (stat != IDMAP_SUCCESS) { + return (stat); + } + + /* + * Check the status for all the queued requests + */ + for (i = 0; i < sib->sib_nmap; i++) { + if (sib->sib_maps[i].sim_stat != IDMAP_SUCCESS) { + return (sib->sib_maps[i].sim_stat); + } + } + + if (smb_idmap_batch_binsid(sib) != 0) { + stat = IDMAP_ERR_OTHER; + } + + return (stat); +} + +/* + * smb_idmap_batch_binsid + * + * Convert sidrids to binary sids + * + * Returns 0 if successful and non-zero upon failure. + */ +static int +smb_idmap_batch_binsid(smb_idmap_batch_t *sib) +{ + nt_sid_t *sid; + smb_idmap_t *sim; + int i; + + if (sib->sib_flags & SMB_IDMAP_SID2ID) + /* This operation is not required */ + return (0); + + sim = sib->sib_maps; + for (i = 0; i < sib->sib_nmap; sim++, i++) { + if (sim->sim_domsid == NULL) + return (-1); + + sid = nt_sid_strtosid(sim->sim_domsid); + if (sid == NULL) + return (-1); + + sim->sim_sid = nt_sid_splice(sid, sim->sim_rid); + free(sid); + } + + return (0); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_info.c b/usr/src/lib/smbsrv/libsmb/common/smb_info.c new file mode 100644 index 000000000000..24d7b1b3d13b --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_info.c @@ -0,0 +1,382 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static smb_ntdomain_t smbpdc_cache; +static mutex_t smbpdc_mtx; +static cond_t smbpdc_cv; + +extern int getdomainname(char *, int); + +uint32_t +smb_get_security_mode() +{ + uint32_t mode; + + smb_config_rdlock(); + mode = smb_config_get_secmode(); + smb_config_unlock(); + + return (mode); +} + +/* + * smb_purge_domain_info + * + * Clean out the environment in preparation for joining a domain. + * This ensures that we don't have any old information lying around. + */ +void +smb_purge_domain_info(void) +{ + smb_config_wrlock(); + (void) smb_config_set(SMB_CI_DOMAIN_NAME, 0); + (void) smb_config_set(SMB_CI_DOMAIN_SID, 0); + (void) smb_config_set(SMB_CI_DOMAIN_MEMB, 0); + smb_config_unlock(); +} + +int +smb_is_domain_member(void) +{ + int is_memb; + + smb_config_rdlock(); + is_memb = smb_config_getyorn(SMB_CI_DOMAIN_MEMB); + smb_config_unlock(); + + return (is_memb); +} + +uint8_t +smb_get_fg_flag(void) +{ + uint8_t run_fg; + + smb_config_rdlock(); + run_fg = smb_config_get_fg_flag(); + smb_config_unlock(); + + return (run_fg); +} + +void +smb_set_domain_member(int set) +{ + char *member; + + smb_config_wrlock(); + member = (set) ? "true" : "false"; + (void) smb_config_set(SMB_CI_DOMAIN_MEMB, member); + smb_config_unlock(); +} + +/* + * smb_getdomaininfo + * + * Returns a pointer to the cached domain data. The caller can specify + * whether or not he is prepared to wait if the cache is not yet valid + * and for how long. The specified timeout is in seconds. + */ +smb_ntdomain_t * +smb_getdomaininfo(uint32_t timeout) +{ + timestruc_t to; + int err; + + if (timeout != 0) { + (void) mutex_lock(&smbpdc_mtx); + while (smbpdc_cache.ipaddr == 0) { + to.tv_sec = timeout; + to.tv_nsec = 0; + err = cond_reltimedwait(&smbpdc_cv, &smbpdc_mtx, &to); + if (err == ETIME) + break; + } + (void) mutex_unlock(&smbpdc_mtx); + } + + if (smbpdc_cache.ipaddr != 0) + return (&smbpdc_cache); + else + return (0); +} + +void +smb_logdomaininfo(smb_ntdomain_t *di) +{ + char ipstr[16]; + + (void) inet_ntop(AF_INET, (const void *)&di->ipaddr, ipstr, + sizeof (ipstr)); + syslog(LOG_DEBUG, "smbd: %s (%s:%s)", di->domain, di->server, ipstr); +} + +/* + * smb_setdomaininfo + * + * Set the information for the specified domain. If the information is + * non-null, the notification event is raised to wakeup any threads + * blocking on the cache. + */ +void +smb_setdomaininfo(char *domain, char *server, uint32_t ipaddr) +{ + char *p; + + bzero(&smbpdc_cache, sizeof (smb_ntdomain_t)); + + if (domain && server && ipaddr) { + (void) strlcpy(smbpdc_cache.domain, domain, SMB_PI_MAX_DOMAIN); + (void) strlcpy(smbpdc_cache.server, server, SMB_PI_MAX_DOMAIN); + + /* + * Remove DNS domain name extension + * to avoid confusing NetBIOS. + */ + if ((p = strchr(smbpdc_cache.domain, '.')) != 0) + *p = '\0'; + + if ((p = strchr(smbpdc_cache.server, '.')) != 0) + *p = '\0'; + + (void) mutex_lock(&smbpdc_mtx); + smbpdc_cache.ipaddr = ipaddr; + (void) cond_broadcast(&smbpdc_cv); + (void) mutex_unlock(&smbpdc_mtx); + } +} + +void +smb_load_kconfig(smb_kmod_cfg_t *kcfg) +{ + smb_config_rdlock(); + bzero(kcfg, sizeof (smb_kmod_cfg_t)); + + kcfg->skc_maxbufsize = smb_config_getnum(SMB_CI_MAX_BUFSIZE); + kcfg->skc_maxworkers = smb_config_getnum(SMB_CI_MAX_WORKERS); + kcfg->skc_keepalive = smb_config_getnum(SMB_CI_KEEPALIVE); + if ((kcfg->skc_keepalive != 0) && + (kcfg->skc_keepalive < SMB_PI_KEEP_ALIVE_MIN)) + kcfg->skc_keepalive = SMB_PI_KEEP_ALIVE_MIN; + kcfg->skc_restrict_anon = smb_config_getyorn(SMB_CI_RESTRICT_ANON); + + kcfg->skc_signing_enable = smb_config_getyorn(SMB_CI_SIGNING_ENABLE); + kcfg->skc_signing_required = smb_config_getyorn(SMB_CI_SIGNING_REQD); + kcfg->skc_signing_check = smb_config_getyorn(SMB_CI_SIGNING_CHECK); + + kcfg->skc_oplock_enable = smb_config_getyorn(SMB_CI_OPLOCK_ENABLE); + kcfg->skc_oplock_timeout = smb_config_getnum(SMB_CI_OPLOCK_TIMEOUT); + + kcfg->skc_flush_required = smb_config_getyorn(SMB_CI_FLUSH_REQUIRED); + kcfg->skc_sync_enable = smb_config_getyorn(SMB_CI_SYNC_ENABLE); + kcfg->skc_dirsymlink_enable = + !smb_config_getyorn(SMB_CI_DIRSYMLINK_DISABLE); + kcfg->skc_announce_quota = smb_config_getyorn(SMB_CI_ANNONCE_QUOTA); + kcfg->skc_announce_quota = smb_config_getyorn(SMB_CI_ANNONCE_QUOTA); + + kcfg->skc_secmode = smb_config_get_secmode(); + kcfg->skc_lmlevel = smb_config_getnum(SMB_CI_LM_LEVEL); + kcfg->skc_maxconnections = smb_config_getnum(SMB_CI_MAX_CONNECTIONS); + + (void) strlcpy(kcfg->skc_resource_domain, + smb_config_getstr(SMB_CI_DOMAIN_NAME), + sizeof (kcfg->skc_resource_domain)); + + (void) smb_gethostname(kcfg->skc_hostname, + sizeof (kcfg->skc_hostname), 1); + + (void) strlcpy(kcfg->skc_system_comment, + smb_config_getstr(SMB_CI_SYS_CMNT), + sizeof (kcfg->skc_system_comment)); + + smb_config_unlock(); +} + +/* + * Get the current system NetBIOS name. The hostname is truncated at + * the first `.` or 15 bytes, whichever occurs first, and converted + * to uppercase (by smb_gethostname). Text that appears after the + * first '.' is considered to be part of the NetBIOS scope. + * + * Returns 0 on success, otherwise -1 to indicate an error. + */ +int +smb_getnetbiosname(char *buf, size_t buflen) +{ + if (smb_gethostname(buf, buflen, 1) != 0) + return (-1); + + if (buflen >= NETBIOS_NAME_SZ) + buf[NETBIOS_NAME_SZ - 1] = '\0'; + + return (0); +} + +/* + * Get the current system node name. The returned name is guaranteed + * to be null-terminated (gethostname may not null terminate the name). + * If the hostname has been fully-qualified for some reason, the domain + * part will be removed. If the caller would like the name in upper + * case, it is folded to uppercase. + * + * If gethostname fails, the returned buffer will contain an empty + * string. + */ +int +smb_gethostname(char *buf, size_t buflen, int upcase) +{ + char *p; + + if (buf == NULL || buflen == 0) + return (-1); + + if (gethostname(buf, buflen) != 0) { + *buf = '\0'; + return (-1); + } + + buf[buflen - 1] = '\0'; + + if ((p = strchr(buf, '.')) != NULL) + *p = '\0'; + + if (upcase) + (void) utf8_strupr(buf); + + return (0); +} + +/* + * The ADS domain is often the same as the DNS domain but they can be + * different - one might be a sub-domain of the other. + * + * If an ADS domain name has been configured, return it. Otherwise, + * return the DNS domain name. + * + * If getdomainname fails, the returned buffer will contain an empty + * string. + */ +int +smb_getdomainname(char *buf, size_t buflen) +{ + char *domain; + + if (buf == NULL || buflen == 0) + return (-1); + + smb_config_rdlock(); + + domain = smb_config_getstr(SMB_CI_ADS_DOMAIN); + if ((domain != NULL) && (*domain != '\0')) { + (void) strlcpy(buf, domain, buflen); + smb_config_unlock(); + return (0); + } + + smb_config_unlock(); + + if (getdomainname(buf, buflen) != 0) { + *buf = '\0'; + return (-1); + } + + return (0); +} + +/* + * Obtain the fully-qualified name for this machine. If the + * hostname is fully-qualified, accept it. Otherwise, try to + * find an appropriate domain name to append to the hostname. + */ +int +smb_getfqhostname(char *buf, size_t buflen) +{ + char hostname[MAXHOSTNAMELEN]; + char domain[MAXHOSTNAMELEN]; + + hostname[0] = '\0'; + domain[0] = '\0'; + + if (smb_gethostname(hostname, MAXHOSTNAMELEN, 0) != 0) + return (-1); + + if (smb_getdomainname(domain, MAXHOSTNAMELEN) != 0) + return (-1); + + if (hostname[0] == '\0') + return (-1); + + if (domain[0] == '\0') { + (void) strlcpy(buf, hostname, buflen); + return (0); + } + + (void) snprintf(buf, buflen, "%s.%s", hostname, domain); + return (0); +} + +/* + * Temporary fbt for dtrace until user space sdt enabled. + */ +void +smb_tracef(const char *fmt, ...) +{ + va_list ap; + char buf[128]; + + va_start(ap, fmt); + (void) vsnprintf(buf, 128, fmt, ap); + va_end(ap); + + smb_trace(buf); +} + +/* + * Temporary fbt for dtrace until user space sdt enabled. + */ +void +smb_trace(const char *s) +{ + syslog(LOG_DEBUG, "%s", s); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_mac.c b/usr/src/lib/smbsrv/libsmb/common/smb_mac.c new file mode 100644 index 000000000000..57fb74530c62 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_mac.c @@ -0,0 +1,207 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB MAC Signing support. + */ + +#include +#include +#include + +#include + +#include + +/* + * smb_mac_init + * + * Calculates the MAC key using the specified user session + * key (NTLM or NTLMv2). + * + * Returns SMBAUTH_SUCCESS if key generation was successful, + * SMBAUTH_FAILURE if not. + */ +int +smb_mac_init(smb_sign_ctx_t *sign_ctx, smb_auth_info_t *auth) +{ + unsigned char S16[SMBAUTH_SESSION_KEY_SZ]; + + if (smb_auth_gen_session_key(auth, S16) != SMBAUTH_SUCCESS) + return (SMBAUTH_FAILURE); + bcopy(S16, sign_ctx->ssc_mackey, SMBAUTH_SESSION_KEY_SZ); + bcopy(auth->cs, &(sign_ctx->ssc_mackey[SMBAUTH_SESSION_KEY_SZ]), + auth->cs_len); + sign_ctx->ssc_keylen = SMBAUTH_SESSION_KEY_SZ + auth->cs_len; + return (SMBAUTH_SUCCESS); +} + +/* + * smb_mac_calc + * + * Calculates MAC signature for the given buffer and returns + * it in the mac_sign parameter. + * + * The MAC signature is calculated as follows: + * + * data = concat(MAC_Key, MAC_Key_Len, SMB_Msg, SMB_Msg_Len); + * hash = MD5(data); + * MAC = head(hash, 8); + * + * The tricky part is that a sequence number should be used + * in calculation instead of the signature field in the + * SMB header. + * + * Returns SMBAUTH_SUCCESS if cryptology framework use was successful, + * SMBAUTH_FAILURE if not. + */ +int +smb_mac_calc(smb_sign_ctx_t *sign_ctx, const unsigned char *buf, + size_t buf_len, unsigned char *mac_sign) +{ + CK_RV rv; + CK_MECHANISM mechanism; + CK_SESSION_HANDLE hSession; + unsigned long diglen = MD_DIGEST_LEN; + int rc = SMBAUTH_FAILURE; + + int offset_end_of_sig = (SMB_SIG_OFFS + SMB_SIG_SIZE); + unsigned char seq_buf[SMB_SIG_SIZE]; + unsigned char mac[16]; + + /* + * put seq_num into the first 4 bytes and + * zero out the next 4 bytes + */ + bcopy(&sign_ctx->ssc_seqnum, seq_buf, 4); + bzero(seq_buf + 4, 4); + + mechanism.mechanism = CKM_MD5; + mechanism.pParameter = 0; + mechanism.ulParameterLen = 0; + + rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession); + if (rv != CKR_OK) + return (SMBAUTH_FAILURE); + + /* Initialize the digest operation in the session */ + rv = C_DigestInit(hSession, &mechanism); + if (rv != CKR_OK) + goto smbmacdone; + + /* init with the MAC key */ + rv = C_DigestUpdate(hSession, sign_ctx->ssc_mackey, + sign_ctx->ssc_keylen); + if (rv != CKR_OK) + goto smbmacdone; + + /* copy in SMB packet info till signature field */ + rv = C_DigestUpdate(hSession, (CK_BYTE_PTR)buf, SMB_SIG_OFFS); + if (rv != CKR_OK) + goto smbmacdone; + + /* copy in the seq_buf instead of the signature */ + rv = C_DigestUpdate(hSession, seq_buf, sizeof (seq_buf)); + if (rv != CKR_OK) + goto smbmacdone; + + /* copy in the rest of the packet, skipping the signature */ + rv = C_DigestUpdate(hSession, (CK_BYTE_PTR)buf + offset_end_of_sig, + buf_len - offset_end_of_sig); + if (rv != CKR_OK) + goto smbmacdone; + + rv = C_DigestFinal(hSession, mac, &diglen); + if (rv != CKR_OK) + goto smbmacdone; + + bcopy(mac, mac_sign, SMB_SIG_SIZE); + rc = SMBAUTH_SUCCESS; + +smbmacdone: + (void) C_CloseSession(hSession); + return (rc); +} + +/* + * smb_mac_chk + * + * Calculates MAC signature for the given buffer + * and compares it to the signature in the given context. + * Return 1 if the signature are match, otherwise, return (0); + */ +int +smb_mac_chk(smb_sign_ctx_t *sign_ctx, + const unsigned char *buf, size_t buf_len) +{ + unsigned char mac_sign[SMB_SIG_SIZE]; + + /* calculate mac signature */ + if (smb_mac_calc(sign_ctx, buf, buf_len, mac_sign) != SMBAUTH_SUCCESS) + return (0); + + /* compare the signatures */ + if (memcmp(sign_ctx->ssc_sign, mac_sign, SMB_SIG_SIZE) == 0) + return (1); + + return (0); +} + +/* + * smb_mac_sign + * + * Calculates MAC signature for the given buffer, + * and write it to the buffer's signature field. + * + * Returns SMBAUTH_SUCCESS if cryptology framework use was successful, + * SMBAUTH_FAILURE if not. + */ +int +smb_mac_sign(smb_sign_ctx_t *sign_ctx, unsigned char *buf, size_t buf_len) +{ + unsigned char mac_sign[SMB_SIG_SIZE]; + + /* calculate mac signature */ + if (smb_mac_calc(sign_ctx, buf, buf_len, mac_sign) != SMBAUTH_SUCCESS) + return (SMBAUTH_FAILURE); + + /* put mac signature in the header's signature field */ + (void) memcpy(buf + SMB_SIG_OFFS, mac_sign, SMB_SIG_SIZE); + return (SMBAUTH_SUCCESS); +} + +void +smb_mac_inc_seqnum(smb_sign_ctx_t *sign_ctx) +{ + sign_ctx->ssc_seqnum++; +} + +void +smb_mac_dec_seqnum(smb_sign_ctx_t *sign_ctx) +{ + sign_ctx->ssc_seqnum--; +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_privilege.c b/usr/src/lib/smbsrv/libsmb/common/smb_privilege.c new file mode 100644 index 000000000000..ce7cef198a84 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_privilege.c @@ -0,0 +1,361 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module provides the interface to the built-in privilege names + * and id's. NT privileges are known on the network using strings. Each + * system assigns locally unique identifiers (LUID) for use within the + * system. Each built-in privilege also has a display-name, which is a + * short description of the privilege. The functions here provide an + * interface to map between LUIDs, names and display names. + */ + +#include +#include + +#include +#include +#include + +#define SMB_PRIV_MIN 2 +#define SMB_PRIV_MAX 24 + +static char *smb_priv_getname(uint32_t id); + +/* + * Table of built-in privilege id's, names and display strings. This + * table matches the response from an NT4.0 PDC LSARPC service. + * Requests for values 0 and 1 return STATUS_NO_SUCH_PRIVILEGE. + * + * SE_UNSOLICITED_INPUT_NAME/SeUnsolicitedInputPrivilege is defined in + * winnt.h but doesn't appear in the list reported by the NT4.0 LSA. + */ +static smb_privinfo_t priv_table[] = { + { 0, "", "", 0 }, + { 1, "", "", 0 }, + { 2, SE_CREATE_TOKEN_NAME, "Create a token object", 0 }, + { 3, SE_ASSIGNPRIMARYTOKEN_NAME, "Replace a process level token", 0 }, + { 4, SE_LOCK_MEMORY_NAME, "Lock pages in memory", 0 }, + { 5, SE_INCREASE_QUOTA_NAME, "Increase quotas", 0 }, + { 6, SE_MACHINE_ACCOUNT_NAME, "Add workstations to domain", 0 }, + { 7, SE_TCB_NAME, "Act as part of the operating system", 0 }, + { 8, SE_SECURITY_NAME, "Manage auditing and security log", 0 }, + { 9, SE_TAKE_OWNERSHIP_NAME, + "Take ownership of files or other objects", PF_PRESENTABLE }, + { 10, SE_LOAD_DRIVER_NAME, "Load and unload device drivers", 0 }, + { 11, SE_SYSTEM_PROFILE_NAME, "Profile system performance", 0 }, + { 12, SE_SYSTEMTIME_NAME, "Change the system time", 0 }, + { 13, SE_PROF_SINGLE_PROCESS_NAME, "Profile single process", 0 }, + { 14, SE_INC_BASE_PRIORITY_NAME, "Increase scheduling priority", 0 }, + { 15, SE_CREATE_PAGEFILE_NAME, "Create a pagefile", 0 }, + { 16, SE_CREATE_PERMANENT_NAME, "Create permanent shared objects", 0 }, + { 17, SE_BACKUP_NAME, "Back up files and directories", + PF_PRESENTABLE }, + { 18, SE_RESTORE_NAME, "Restore files and directories", + PF_PRESENTABLE }, + { 19, SE_SHUTDOWN_NAME, "Shut down the system", 0 }, + { 20, SE_DEBUG_NAME, "Debug programs", 0 }, + { 21, SE_AUDIT_NAME, "Generate security audits", 0 }, + { 22, SE_SYSTEM_ENVIRONMENT_NAME, + "Modify firmware environment values", 0 }, + { 23, SE_CHANGE_NOTIFY_NAME, "Bypass traverse checking", 0 }, + { 24, SE_REMOTE_SHUTDOWN_NAME, + "Force shutdown from a remote system", 0 } +}; + +/* + * smb_priv_presentable_num + * + * Returns number of presentable privileges + */ +int +smb_priv_presentable_num() +{ + int i, num; + + num = 0; + for (i = SMB_PRIV_MIN; i <= SMB_PRIV_MAX; i++) + if (priv_table[i].flags == PF_PRESENTABLE) + num++; + + return (num); +} + +/* + * smb_priv_presentable_ids + * + * Returns IDs of presentable privileges + * Returns 0 in case of invalid parameter and 1 on success. + */ +int +smb_priv_presentable_ids(uint32_t *ids, int num) +{ + int i, j; + + if (ids == NULL || num <= 0) + return (0); + + for (i = SMB_PRIV_MIN, j = 0; i <= SMB_PRIV_MAX; i++) + if (priv_table[i].flags == PF_PRESENTABLE) + ids[j++] = priv_table[i].id; + + return (1); +} + +/* + * smb_priv_getbyvalue + * + * Return the privilege info for the specified id (low part of the LUID). + * Returns a null pointer if id is out-of-range. + */ +smb_privinfo_t * +smb_priv_getbyvalue(uint32_t id) +{ + if (id < SMB_PRIV_MIN || id > SMB_PRIV_MAX) + return (0); + + return (&priv_table[id]); +} + + +/* + * smb_priv_getbyname + * + * Return the privilege info for the specified name. Returns a null + * pointer if we can't find a matching name in the table. + */ +smb_privinfo_t * +smb_priv_getbyname(char *name) +{ + smb_privinfo_t *entry; + int i; + + if (name == 0) + return (0); + + for (i = SMB_PRIV_MIN; i <= SMB_PRIV_MAX; ++i) { + entry = &priv_table[i]; + + if (utf8_strcasecmp(name, entry->name) == 0) + return (entry); + } + + return (0); +} + +/* + * smb_privset_size + * + * Returns the memory block size needed to keep a complete + * set of privileges in a smb_privset_t structure. + */ +int +smb_privset_size() +{ + int pcnt = SMB_PRIV_MAX - SMB_PRIV_MIN + 1; + + return (2 * sizeof (uint32_t) + + pcnt * sizeof (smb_luid_attrs_t)); +} + +/* + * smb_privset_validate + * + * Validates the given privilege set structure + * Returns 1 if the structure is Ok, otherwise returns 0. + */ +int +smb_privset_validate(smb_privset_t *privset) +{ + int count; + uint32_t i; + + if (privset == 0) { + return (0); + } + + count = SMB_PRIV_MAX - SMB_PRIV_MIN + 1; + + if (privset->priv_cnt != count) { + return (0); + } + + for (i = 0; i < count; i++) { + if (privset->priv[i].luid.hi_part != 0) { + return (0); + } + + if (privset->priv[i].luid.lo_part != + i + SMB_PRIV_MIN) { + return (0); + } + } + + return (1); +} + +/* + * smb_privset_init + * + * initialize all privileges in disable state. + */ +void +smb_privset_init(smb_privset_t *privset) +{ + int count; + uint32_t i; + + if (privset == 0) + return; + + count = SMB_PRIV_MAX - SMB_PRIV_MIN + 1; + + privset->priv_cnt = count; + privset->control = 0; + for (i = 0; i < count; i++) { + privset->priv[i].luid.hi_part = 0; + privset->priv[i].luid.lo_part = i + SMB_PRIV_MIN; + privset->priv[i].attrs = 0; + } +} + +/* + * smb_privset_new + * + * Allocate memory and initialize all privileges in disable state. + * Returns pointer to allocated space or NULL if there is not + * enough memory. + */ +smb_privset_t * +smb_privset_new() +{ + smb_privset_t *privset; + + privset = malloc(smb_privset_size()); + if (privset == NULL) + return (NULL); + + smb_privset_init(privset); + + return (privset); +} + +/* + * smb_privset_copy + * + * Copy privleges information specified by 'src' to the + * buffer specified by dst. + */ +void +smb_privset_copy(smb_privset_t *dst, smb_privset_t *src) +{ + if (src == 0 || dst == 0) + return; + + (void) memcpy(dst, src, smb_privset_size()); +} + +/* + * smb_privset_free + * + * This will free the memory allocated by the 'privset'. + */ +void +smb_privset_free(smb_privset_t *privset) +{ + free(privset); +} + +void +smb_privset_enable(smb_privset_t *privset, uint32_t id) +{ + int i; + + if (privset == NULL) + return; + + for (i = 0; i < privset->priv_cnt; i++) { + if (privset->priv[i].luid.lo_part == id) + privset->priv[i].attrs = SE_PRIVILEGE_ENABLED; + } +} + +void +smb_privset_log(smb_privset_t *privset) +{ + smb_luid_t *luid; + int i, ecnt; + + if (privset == NULL) + return; + + for (i = 0, ecnt = 0; i < privset->priv_cnt; ++i) { + if (privset->priv[i].attrs != 0) { + ecnt++; + } + } + + syslog(LOG_DEBUG, " Privilege Count: %d (Enable=%d)", + privset->priv_cnt, ecnt); + + for (i = 0; i < privset->priv_cnt; ++i) { + if (privset->priv[i].attrs != 0) { + luid = &privset->priv[i].luid; + syslog(LOG_DEBUG, " %s", + smb_priv_getname(luid->lo_part)); + } + } +} + +int +smb_privset_query(smb_privset_t *privset, uint32_t id) +{ + int i; + + if (privset == NULL) + return (0); + + for (i = 0; privset->priv_cnt; i++) { + if (privset->priv[i].luid.lo_part == id) { + if (privset->priv[i].attrs == SE_PRIVILEGE_ENABLED) + return (1); + else + return (0); + } + } + + return (0); +} + +static char * +smb_priv_getname(uint32_t id) +{ + if (id < SMB_PRIV_MIN || id > SMB_PRIV_MAX) + return ("Unknown Privilege"); + + return (priv_table[id].name); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c b/usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c new file mode 100644 index 000000000000..10026d7418fd --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c @@ -0,0 +1,529 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SMB_PASSWD "/var/smb/smbpasswd" +#define SMB_OPASSWD "/var/smb/osmbpasswd" +#define SMB_PASSTEMP "/var/smb/ptmp" +#define SMB_PASSLCK "/var/smb/.pwd.lock" + +#define SMB_PWD_DISABLE "*DIS*" +#define SMB_PWD_BUFSIZE 256 + +#define S_WAITTIME 15 + +typedef enum { + SMB_PWD_NAME = 0, + SMB_PWD_UID, + SMB_PWD_LMHASH, + SMB_PWD_NTHASH, + SMB_PWD_NARG +} smb_pwdarg_t; + +static struct flock flock = { + 0, /* l_type */ + 0, /* l_whence */ + 0, /* l_start */ + 0, /* l_len */ + 0, /* l_sysid */ + 0 /* l_pid */ + }; + +static pid_t lck_pid = 0; /* process's pid at last lock */ +static thread_t lck_tid = 0; /* thread that holds the lock */ +static int fildes = -1; +static mutex_t lck_lock = DEFAULTMUTEX; + +typedef struct smb_pwbuf { + char *pw_name; + smb_passwd_t *pw_pwd; +} smb_pwbuf_t; + +static int smb_pwd_lock(void); +static int smb_pwd_unlock(void); +static int smb_pwd_flck(void); +static int smb_pwd_fulck(void); + +static smb_pwbuf_t *smb_pwd_fgetent(FILE *, smb_pwbuf_t *, char *, size_t); +static int smb_pwd_fputent(FILE *, smb_pwbuf_t *); +static int smb_pwd_chgpwent(smb_passwd_t *, const char *, int); +static int smb_pwd_update(const char *, const char *, int); + +/* + * smb_pwd_get + * + * Returns a smb password structure for the given user name. + * smbpw is a pointer to a buffer allocated by the caller. + * + * Returns NULL upon failure. + */ +smb_passwd_t * +smb_pwd_getpasswd(const char *name, smb_passwd_t *smbpw) +{ + char buf[SMB_PWD_BUFSIZE]; + boolean_t found = B_FALSE; + smb_pwbuf_t pwbuf; + int err; + FILE *fp; + + err = smb_pwd_lock(); + if (err != SMB_PWE_SUCCESS) + return (NULL); + + if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) { + (void) smb_pwd_unlock(); + return (NULL); + } + + pwbuf.pw_name = NULL; + pwbuf.pw_pwd = smbpw; + + while (smb_pwd_fgetent(fp, &pwbuf, buf, sizeof (buf)) != NULL) { + if (strcmp(name, pwbuf.pw_name) == 0) { + if ((smbpw->pw_flags & (SMB_PWF_LM | SMB_PWF_NT))) + found = B_TRUE; + break; + } + } + + (void) fclose(fp); + (void) smb_pwd_unlock(); + + if (!found) { + bzero(smbpw, sizeof (smb_passwd_t)); + return (NULL); + } + + return (smbpw); +} + +/* + * smb_pwd_set + * + * Update/add the given user to the smbpasswd file. + */ +int +smb_pwd_setpasswd(const char *name, const char *password) +{ + return (smb_pwd_update(name, password, 0)); +} + +/* + * smb_pwd_setcntl + * + * Change the account state. This can be making the account + * disable/enable or removing its LM hash. + */ +int +smb_pwd_setcntl(const char *name, int control) +{ + if (control == 0) + return (SMB_PWE_SUCCESS); + + return (smb_pwd_update(name, NULL, control)); +} + +static int +smb_pwd_update(const char *name, const char *password, int control) +{ + struct stat64 stbuf; + FILE *src, *dst; + int tempfd; + char buf[SMB_PWD_BUFSIZE]; + int err = SMB_PWE_SUCCESS; + smb_pwbuf_t pwbuf; + smb_passwd_t smbpw; + boolean_t newent = B_TRUE; + boolean_t user_disable = B_FALSE; + char uxbuf[1024]; + struct passwd uxpw; + int lm_level; + char *lm_str; + + err = smb_pwd_lock(); + if (err != SMB_PWE_SUCCESS) + return (err); + + if (stat64(SMB_PASSWD, &stbuf) < 0) { + err = SMB_PWE_STAT_FAILED; + goto passwd_exit; + } + + if ((tempfd = open(SMB_PASSTEMP, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { + err = SMB_PWE_OPEN_FAILED; + goto passwd_exit; + } + + if ((dst = fdopen(tempfd, "wF")) == NULL) { + err = SMB_PWE_OPEN_FAILED; + goto passwd_exit; + } + + if ((src = fopen(SMB_PASSWD, "rF")) == NULL) { + err = SMB_PWE_OPEN_FAILED; + (void) fclose(dst); + (void) unlink(SMB_PASSTEMP); + goto passwd_exit; + } + + lm_str = smb_config_getenv(SMB_CI_LM_LEVEL); + if (lm_str) { + lm_level = strtoul(lm_str, 0, 10); + free(lm_str); + } else { + lm_level = 4; + } + + if (lm_level >= 4) + control |= SMB_PWC_NOLM; + + /* + * copy old password entries to temporary file while replacing + * the entry that matches "name" + */ + pwbuf.pw_name = NULL; + pwbuf.pw_pwd = &smbpw; + + while (smb_pwd_fgetent(src, &pwbuf, buf, sizeof (buf)) != NULL) { + if (strcmp(pwbuf.pw_name, name) == 0) { + err = smb_pwd_chgpwent(&smbpw, password, control); + if (err == SMB_PWE_USER_DISABLE) + user_disable = B_TRUE; + err = smb_pwd_fputent(dst, &pwbuf); + newent = B_FALSE; + } else { + err = smb_pwd_fputent(dst, &pwbuf); + } + + if (err != SMB_PWE_SUCCESS) { + (void) fclose(src); + (void) fclose(dst); + goto passwd_exit; + } + } + + if (newent) { + if (getpwnam_r(name, &uxpw, uxbuf, sizeof (uxbuf))) { + pwbuf.pw_name = uxpw.pw_name; + smbpw.pw_flags = 0; + smbpw.pw_uid = uxpw.pw_uid; + (void) smb_pwd_chgpwent(&smbpw, password, control); + err = smb_pwd_fputent(dst, &pwbuf); + } else { + err = SMB_PWE_USER_UNKNOWN; + } + + if (err != SMB_PWE_SUCCESS) { + (void) fclose(src); + (void) fclose(dst); + goto passwd_exit; + } + } + + (void) fclose(src); + if (fclose(dst) != 0) { + err = SMB_PWE_CLOSE_FAILED; + goto passwd_exit; /* Don't trust the temporary file */ + } + + /* Rename temp to passwd */ + if (unlink(SMB_OPASSWD) && access(SMB_OPASSWD, 0) == 0) { + err = SMB_PWE_UPDATE_FAILED; + (void) unlink(SMB_PASSTEMP); + goto passwd_exit; + } + + if (link(SMB_PASSWD, SMB_OPASSWD) == -1) { + err = SMB_PWE_UPDATE_FAILED; + (void) unlink(SMB_PASSTEMP); + goto passwd_exit; + } + + if (rename(SMB_PASSTEMP, SMB_PASSWD) == -1) { + err = SMB_PWE_UPDATE_FAILED; + (void) unlink(SMB_PASSTEMP); + goto passwd_exit; + } + + (void) chmod(SMB_PASSWD, 0400); + +passwd_exit: + (void) smb_pwd_unlock(); + if ((err == SMB_PWE_SUCCESS) && user_disable) + err = SMB_PWE_USER_DISABLE; + + return (err); +} + +/* + * smb_getpwent + * + * Parse the buffer in the passed pwbuf and fill in the + * smb password structure to point to the parsed information. + * The entry format is: + * + * ::: + * + * Returns a pointer to the password structure on success, + * otherwise returns NULL. + */ +static smb_pwbuf_t * +smb_pwd_fgetent(FILE *fp, smb_pwbuf_t *pwbuf, char *buf, size_t bufsize) +{ + char *argv[SMB_PWD_NARG]; + smb_passwd_t *pw; + smb_pwdarg_t i; + int lm_len, nt_len; + + if (fgets(buf, bufsize, fp) == NULL) + return (NULL); + (void) trim_whitespace(buf); + + for (i = 0; i < SMB_PWD_NARG; ++i) { + if ((argv[i] = strsep((char **)&buf, ":")) == 0) { + return (NULL); + } + } + + if ((*argv[SMB_PWD_NAME] == '\0') || (*argv[SMB_PWD_UID] == '\0')) + return (NULL); + + pwbuf->pw_name = argv[SMB_PWD_NAME]; + pw = pwbuf->pw_pwd; + bzero(pw, sizeof (smb_passwd_t)); + pw->pw_uid = strtoul(argv[SMB_PWD_UID], 0, 10); + + if (strcmp(argv[SMB_PWD_LMHASH], SMB_PWD_DISABLE) == 0) { + pw->pw_flags |= SMB_PWF_DISABLE; + (void) strcpy((char *)pw->pw_lmhash, SMB_PWD_DISABLE); + (void) strcpy((char *)pw->pw_nthash, SMB_PWD_DISABLE); + return (pwbuf); + } + + lm_len = strlen(argv[SMB_PWD_LMHASH]); + if (lm_len == SMBAUTH_HEXHASH_SZ) { + (void) hextobin(argv[SMB_PWD_LMHASH], SMBAUTH_HEXHASH_SZ, + (char *)pw->pw_lmhash, SMBAUTH_HASH_SZ); + + pw->pw_flags |= SMB_PWF_LM; + } else if (lm_len != 0) { + return (NULL); + } + + nt_len = strlen(argv[SMB_PWD_NTHASH]); + if (nt_len == SMBAUTH_HEXHASH_SZ) { + (void) hextobin(argv[SMB_PWD_NTHASH], SMBAUTH_HEXHASH_SZ, + (char *)pw->pw_nthash, SMBAUTH_HASH_SZ); + + pw->pw_flags |= SMB_PWF_NT; + } else if (nt_len != 0) { + return (NULL); + } + + return (pwbuf); +} + +static int +smb_pwd_chgpwent(smb_passwd_t *smbpw, const char *password, int control) +{ + if (control & SMB_PWC_DISABLE) { + smbpw->pw_flags |= SMB_PWF_DISABLE; + (void) strcpy((char *)smbpw->pw_lmhash, SMB_PWD_DISABLE); + (void) strcpy((char *)smbpw->pw_nthash, SMB_PWD_DISABLE); + smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT); + return (SMB_PWE_SUCCESS); + } else if ((control & SMB_PWC_ENABLE) && + (smbpw->pw_flags & SMB_PWF_DISABLE)) { + *smbpw->pw_lmhash = '\0'; + *smbpw->pw_nthash = '\0'; + smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT); + return (SMB_PWE_SUCCESS); + } + + /* No password update if account is disabled */ + if (smbpw->pw_flags & SMB_PWF_DISABLE) + return (SMB_PWE_USER_DISABLE); + + if (control & SMB_PWC_NOLM) { + smbpw->pw_flags &= ~SMB_PWF_LM; + *smbpw->pw_lmhash = '\0'; + } else { + smbpw->pw_flags |= SMB_PWF_LM; + (void) smb_auth_lm_hash((char *)password, smbpw->pw_lmhash); + } + + smbpw->pw_flags |= SMB_PWF_NT; + (void) smb_auth_ntlm_hash((char *)password, smbpw->pw_nthash); + return (SMB_PWE_SUCCESS); +} + +/* + * smb_putpwent + * + * Creates LM and NTLM hash from the given plain text password + * and write them along with user's name and Id to the smbpasswd + * file. + */ +static int +smb_pwd_fputent(FILE *fp, smb_pwbuf_t *pwbuf) +{ + smb_passwd_t *pw = pwbuf->pw_pwd; + char hex_nthash[SMBAUTH_HEXHASH_SZ+1]; + char hex_lmhash[SMBAUTH_HEXHASH_SZ+1]; + int rc; + + if ((pw->pw_flags & SMB_PWF_LM) == SMB_PWF_LM) { + (void) bintohex((char *)pw->pw_lmhash, SMBAUTH_HASH_SZ, + hex_lmhash, SMBAUTH_HEXHASH_SZ); + hex_lmhash[SMBAUTH_HEXHASH_SZ] = '\0'; + } else { + (void) strcpy(hex_lmhash, (char *)pw->pw_lmhash); + } + + if ((pw->pw_flags & SMB_PWF_NT) == SMB_PWF_NT) { + (void) bintohex((char *)pw->pw_nthash, SMBAUTH_HASH_SZ, + hex_nthash, SMBAUTH_HEXHASH_SZ); + hex_nthash[SMBAUTH_HEXHASH_SZ] = '\0'; + } else { + (void) strcpy(hex_nthash, (char *)pw->pw_nthash); + } + + rc = fprintf(fp, "%s:%d:%s:%s\n", pwbuf->pw_name, pw->pw_uid, + hex_lmhash, hex_nthash); + + if (rc <= 0) + return (SMB_PWE_WRITE_FAILED); + + return (SMB_PWE_SUCCESS); +} + +static int +smb_pwd_lock(void) +{ + int res; + + if (smb_pwd_flck()) { + switch (errno) { + case EINTR: + res = SMB_PWE_BUSY; + break; + case EACCES: + res = SMB_PWE_DENIED; + break; + case 0: + res = SMB_PWE_SUCCESS; + break; + } + } else + res = SMB_PWE_SUCCESS; + + return (res); +} + +static int +smb_pwd_unlock(void) +{ + if (smb_pwd_fulck()) + return (SMB_PWE_SYSTEM_ERROR); + + return (SMB_PWE_SUCCESS); +} + +static int +smb_pwd_flck(void) +{ + int seconds = 0; + + (void) mutex_lock(&lck_lock); + for (;;) { + if (lck_pid != 0 && lck_pid != getpid()) { + /* somebody forked */ + lck_pid = 0; + lck_tid = 0; + } + + if (lck_tid == 0) { + if ((fildes = creat(SMB_PASSLCK, 0600)) == -1) + break; + flock.l_type = F_WRLCK; + if (fcntl(fildes, F_SETLK, &flock) != -1) { + lck_pid = getpid(); + lck_tid = thr_self(); + (void) mutex_unlock(&lck_lock); + return (0); + } + (void) close(fildes); + fildes = -1; + } + + if (seconds++ >= S_WAITTIME) { + /* + * For compatibility with the past, pretend + * that we were interrupted by SIGALRM. + */ + errno = EINTR; + break; + } + + (void) mutex_unlock(&lck_lock); + (void) sleep(1); + (void) mutex_lock(&lck_lock); + } + (void) mutex_unlock(&lck_lock); + + return (-1); +} + +static int +smb_pwd_fulck(void) +{ + (void) mutex_lock(&lck_lock); + if (lck_tid == thr_self() && fildes >= 0) { + flock.l_type = F_UNLCK; + (void) fcntl(fildes, F_SETLK, &flock); + (void) close(fildes); + fildes = -1; + lck_pid = 0; + lck_tid = 0; + (void) mutex_unlock(&lck_lock); + return (0); + } + (void) mutex_unlock(&lck_lock); + return (-1); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_scfutil.c b/usr/src/lib/smbsrv/libsmb/common/smb_scfutil.c new file mode 100644 index 000000000000..1f5083f44830 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_scfutil.c @@ -0,0 +1,1035 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* helper functions for using libscf with CIFS */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +/* + * smb_smf_scf_log_error(msg) + * Logs error messages from scf API's + */ +static void +smb_smf_scf_log_error(char *msg) +{ + if (!msg) { + syslog(LOG_ERR, " SMBD SMF problem: %s\n", + scf_strerror(scf_error())); + } else { /*LINTED E_SEC_PRINTF_E_VAR_FMT*/ + syslog(LOG_ERR, msg, scf_strerror(scf_error())); + } +} + +/* + * Check if instance with given name exists for a service. + * Returns 0 is instance exist + */ +int +smb_smf_instance_exists(smb_scfhandle_t *handle, char *inst_name) +{ + int ret = SMBD_SMF_OK; + if (handle == NULL) + return (SMBD_SMF_SYSTEM_ERR); + + handle->scf_instance = scf_instance_create(handle->scf_handle); + if (scf_service_get_instance(handle->scf_service, inst_name, + handle->scf_instance) != SCF_SUCCESS) + ret = SMBD_SMF_SYSTEM_ERR; + + scf_instance_destroy(handle->scf_instance); + handle->scf_instance = NULL; + return (ret); +} + +/* + * Create a service instance. returns 0 if successful. + * If instance already exists enable it. + */ +int +smb_smf_instance_create(smb_scfhandle_t *handle, char *serv_prefix, + char *inst_name) +{ + char *instance; + int ret = SMBD_SMF_OK; + int sz; + + if (handle == NULL) + return (SMBD_SMF_SYSTEM_ERR); + + if (!serv_prefix || !inst_name) + return (SMBD_SMF_SYSTEM_ERR); + + sz = strlen(serv_prefix) + strlen(inst_name) + 2; + instance = malloc(sz); + if (!instance) + return (SMBD_SMF_NO_MEMORY); + + (void) snprintf(instance, sz, "%s:%s", serv_prefix, inst_name); + handle->scf_instance = scf_instance_create(handle->scf_handle); + if (scf_service_get_instance(handle->scf_service, inst_name, + handle->scf_instance) != SCF_SUCCESS) { + if (scf_service_add_instance(handle->scf_service, + inst_name, handle->scf_instance) == SCF_SUCCESS) { + if (smf_enable_instance(instance, 0)) + ret = SMBD_SMF_SYSTEM_ERR; + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + } else { + if (smf_enable_instance(instance, 0)) + ret = SMBD_SMF_SYSTEM_ERR; + } + free(instance); + return (ret); +} + +/* + * Delete a specified instance. Return SMBD_SMF_OK for success. + */ +int +smb_smf_instance_delete(smb_scfhandle_t *handle, char *inst_name) +{ + int ret = SMBD_SMF_OK; + + if (handle == NULL) + return (SMBD_SMF_SYSTEM_ERR); + + handle->scf_instance = scf_instance_create(handle->scf_handle); + if (scf_service_get_instance(handle->scf_service, inst_name, + handle->scf_instance) == SCF_SUCCESS) { + if (scf_instance_delete(handle->scf_instance) == SCF_SUCCESS) { + return (ret); + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + } else { + smb_smf_scf_log_error(NULL); + ret = SMBD_SMF_SYSTEM_ERR; + } + return (ret); +} + +/* + * smb_smf_create_service_pgroup(handle, pgroup) + * + * create a new property group at service level. + */ +int +smb_smf_create_service_pgroup(smb_scfhandle_t *handle, char *pgroup) +{ + int ret = SMBD_SMF_OK; + int err; + + if (handle == NULL) { + return (SMBD_SMF_SYSTEM_ERR); + } + + /* + * only create a handle if it doesn't exist. It is ok to exist + * since the pg handle will be set as a side effect. + */ + if (handle->scf_pg == NULL) + handle->scf_pg = scf_pg_create(handle->scf_handle); + + /* + * if the pgroup exists, we are done. If it doesn't, then we + * need to actually add one to the service instance. + */ + if (scf_service_get_pg(handle->scf_service, + pgroup, handle->scf_pg) != 0) { + /* doesn't exist so create one */ + if (scf_service_add_pg(handle->scf_service, pgroup, + SCF_GROUP_APPLICATION, 0, handle->scf_pg) != 0) { + err = scf_error(); + if (err != SCF_ERROR_NONE) + smb_smf_scf_log_error(NULL); + switch (err) { + case SCF_ERROR_PERMISSION_DENIED: + ret = SMBD_SMF_NO_PERMISSION; + break; + default: + ret = SMBD_SMF_SYSTEM_ERR; + break; + } + } + } + return (ret); +} + +/* + * smb_smf_create_instance_pgroup(handle, pgroup) + * + * create a new property group at instance level. + */ +int +smb_smf_create_instance_pgroup(smb_scfhandle_t *handle, char *pgroup) +{ + int ret = SMBD_SMF_OK; + int err; + + if (handle == NULL) { + return (SMBD_SMF_SYSTEM_ERR); + } + + /* + * only create a handle if it doesn't exist. It is ok to exist + * since the pg handle will be set as a side effect. + */ + if (handle->scf_pg == NULL) + handle->scf_pg = scf_pg_create(handle->scf_handle); + + /* + * if the pgroup exists, we are done. If it doesn't, then we + * need to actually add one to the service instance. + */ + if (scf_instance_get_pg(handle->scf_instance, + pgroup, handle->scf_pg) != 0) { + /* doesn't exist so create one */ + if (scf_instance_add_pg(handle->scf_instance, pgroup, + SCF_GROUP_FRAMEWORK, 0, handle->scf_pg) != 0) { + err = scf_error(); + if (err != SCF_ERROR_NONE) + smb_smf_scf_log_error(NULL); + switch (err) { + case SCF_ERROR_PERMISSION_DENIED: + ret = SMBD_SMF_NO_PERMISSION; + break; + default: + ret = SMBD_SMF_SYSTEM_ERR; + break; + } + } + } + return (ret); +} + +/* + * smb_smf_delete_service_pgroup(handle, pgroup) + * + * remove the property group from the current service. + * but only if it actually exists. + */ +int +smb_smf_delete_service_pgroup(smb_scfhandle_t *handle, char *pgroup) +{ + int ret = SMBD_SMF_OK; + int err; + + if (handle == NULL) { + return (SMBD_SMF_SYSTEM_ERR); + } + + /* + * only create a handle if it doesn't exist. It is ok to exist + * since the pg handle will be set as a side effect. + */ + if (handle->scf_pg == NULL) + handle->scf_pg = scf_pg_create(handle->scf_handle); + + /* + * only delete if it does exist. + */ + if (scf_service_get_pg(handle->scf_service, + pgroup, handle->scf_pg) == 0) { + /* does exist so delete it */ + if (scf_pg_delete(handle->scf_pg) != 0) { + ret = SMBD_SMF_SYSTEM_ERR; + err = scf_error(); + if (err != SCF_ERROR_NONE) { + smb_smf_scf_log_error("SMF delpg " + "problem: %s\n"); + } + } + } else { + err = scf_error(); + if (err != SCF_ERROR_NONE) + smb_smf_scf_log_error("SMF getpg problem: %s\n"); + ret = SMBD_SMF_SYSTEM_ERR; + } + if (ret == SMBD_SMF_SYSTEM_ERR && + scf_error() == SCF_ERROR_PERMISSION_DENIED) { + ret = SMBD_SMF_NO_PERMISSION; + } + return (ret); +} + +/* + * smb_smf_delete_instance_pgroup(handle, pgroup) + * + * remove the property group from the current instance. + * but only if it actually exists. + */ +int +smb_smf_delete_instance_pgroup(smb_scfhandle_t *handle, char *pgroup) +{ + int ret = SMBD_SMF_OK; + int err; + + if (handle == NULL) + return (SMBD_SMF_SYSTEM_ERR); + + /* + * only create a handle if it doesn't exist. It is ok to exist + * since the pg handle will be set as a side effect. + */ + if (handle->scf_pg == NULL) + handle->scf_pg = scf_pg_create(handle->scf_handle); + + /* + * only delete if it does exist. + */ + if (scf_instance_get_pg(handle->scf_instance, + pgroup, handle->scf_pg) == 0) { + /* does exist so delete it */ + if (scf_pg_delete(handle->scf_pg) != 0) { + ret = SMBD_SMF_SYSTEM_ERR; + err = scf_error(); + if (err != SCF_ERROR_NONE) { + smb_smf_scf_log_error("SMF delpg " + "problem: %s\n"); + } + } + } else { + err = scf_error(); + if (err != SCF_ERROR_NONE) + smb_smf_scf_log_error("SMF getpg problem: %s\n"); + ret = SMBD_SMF_SYSTEM_ERR; + } + if (ret == SMBD_SMF_SYSTEM_ERR && + scf_error() == SCF_ERROR_PERMISSION_DENIED) + ret = SMBD_SMF_NO_PERMISSION; + + return (ret); +} + +/* + * Start transaction on current pg in handle. + * The pg could be service or instance level. + * Must be called after pg handle is obtained + * from create or get. + */ +int +smb_smf_start_transaction(smb_scfhandle_t *handle) +{ + int ret = SMBD_SMF_OK; + + if (!handle || (!handle->scf_pg)) + return (SMBD_SMF_SYSTEM_ERR); + + /* + * lookup the property group and create it if it doesn't already + * exist. + */ + if (handle->scf_state == SCH_STATE_INIT) { + if (ret == SMBD_SMF_OK) { + handle->scf_trans = + scf_transaction_create(handle->scf_handle); + if (handle->scf_trans != NULL) { + if (scf_transaction_start(handle->scf_trans, + handle->scf_pg) != 0) { + ret = SMBD_SMF_SYSTEM_ERR; + scf_transaction_destroy( + handle->scf_trans); + handle->scf_trans = NULL; + } + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + } + } + if (ret == SMBD_SMF_SYSTEM_ERR && + scf_error() == SCF_ERROR_PERMISSION_DENIED) + ret = SMBD_SMF_NO_PERMISSION; + + return (ret); +} + +/* + * smb_smf_end_transaction(handle) + * + * Commit the changes that were added to the transaction in the + * handle. Do all necessary cleanup. + */ +int +smb_smf_end_transaction(smb_scfhandle_t *handle) +{ + int ret = SMBD_SMF_OK; + + if (handle == NULL) + return (SMBD_SMF_SYSTEM_ERR); + + if (handle->scf_trans == NULL) { + ret = SMBD_SMF_SYSTEM_ERR; + } else { + if (scf_transaction_commit(handle->scf_trans) < 0) { + ret = SMBD_SMF_SYSTEM_ERR; + smb_smf_scf_log_error("Failed to commit " + "transaction: %s"); + } + scf_transaction_destroy_children(handle->scf_trans); + scf_transaction_destroy(handle->scf_trans); + handle->scf_trans = NULL; + } + return (ret); +} + +/* + * Deletes property in current pg + */ +int +smb_smf_delete_property(smb_scfhandle_t *handle, char *propname) +{ + int ret = SMBD_SMF_OK; + scf_transaction_entry_t *entry = NULL; + + if (handle == NULL) + return (SMBD_SMF_SYSTEM_ERR); + + /* + * properties must be set in transactions and don't take + * effect until the transaction has been ended/committed. + */ + entry = scf_entry_create(handle->scf_handle); + if (entry != NULL) { + if (scf_transaction_property_delete(handle->scf_trans, entry, + propname) != 0) { + ret = SMBD_SMF_SYSTEM_ERR; + } + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + if (ret == SMBD_SMF_SYSTEM_ERR) { + switch (scf_error()) { + case SCF_ERROR_PERMISSION_DENIED: + ret = SMBD_SMF_NO_PERMISSION; + break; + } + } + + /* + * cleanup if there were any errors that didn't leave these + * values where they would be cleaned up later. + */ + if ((ret != SMBD_SMF_OK) && (entry != NULL)) + scf_entry_destroy(entry); + + return (ret); +} + +/* + * Sets string property in current pg + */ +int +smb_smf_set_string_property(smb_scfhandle_t *handle, + char *propname, char *valstr) +{ + int ret = SMBD_SMF_OK; + scf_value_t *value = NULL; + scf_transaction_entry_t *entry = NULL; + + if (handle == NULL) + return (SMBD_SMF_SYSTEM_ERR); + + /* + * properties must be set in transactions and don't take + * effect until the transaction has been ended/committed. + */ + value = scf_value_create(handle->scf_handle); + entry = scf_entry_create(handle->scf_handle); + if (value != NULL && entry != NULL) { + if (scf_transaction_property_change(handle->scf_trans, entry, + propname, SCF_TYPE_ASTRING) == 0 || + scf_transaction_property_new(handle->scf_trans, entry, + propname, SCF_TYPE_ASTRING) == 0) { + if (scf_value_set_astring(value, valstr) == 0) { + if (scf_entry_add_value(entry, value) != 0) { + ret = SMBD_SMF_SYSTEM_ERR; + scf_value_destroy(value); + } + /* the value is in the transaction */ + value = NULL; + } else { + /* value couldn't be constructed */ + ret = SMBD_SMF_SYSTEM_ERR; + } + /* the entry is in the transaction */ + entry = NULL; + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + if (ret == SMBD_SMF_SYSTEM_ERR) { + switch (scf_error()) { + case SCF_ERROR_PERMISSION_DENIED: + ret = SMBD_SMF_NO_PERMISSION; + break; + } + } + + /* + * cleanup if there were any errors that didn't leave these + * values where they would be cleaned up later. + */ + if (value != NULL) + scf_value_destroy(value); + if (entry != NULL) + scf_entry_destroy(entry); + return (ret); +} + +/* + * Gets string property value.upto sz size. + * Caller is responsible to have enough memory allocated. + */ +int +smb_smf_get_string_property(smb_scfhandle_t *handle, char *propname, + char *valstr, size_t sz) +{ + int ret = SMBD_SMF_OK; + scf_value_t *value; + scf_property_t *prop; + + if (handle == NULL) + return (SMBD_SMF_SYSTEM_ERR); + + value = scf_value_create(handle->scf_handle); + prop = scf_property_create(handle->scf_handle); + if (value && prop && + (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) { + if (scf_property_get_value(prop, value) == 0) { + if (scf_value_get_astring(value, valstr, sz) < 0) { + ret = SMBD_SMF_SYSTEM_ERR; + } + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + if (value != NULL) + scf_value_destroy(value); + if (prop != NULL) + scf_property_destroy(prop); + return (ret); +} + +/* + * Set integer value of property. + * The value is returned as int64_t value + * Caller ensures appropriate translation. + */ +int +smb_smf_set_integer_property(smb_scfhandle_t *handle, char *propname, + int64_t valint) +{ + int ret = SMBD_SMF_OK; + scf_value_t *value = NULL; + scf_transaction_entry_t *entry = NULL; + + if (handle == NULL) + return (SMBD_SMF_SYSTEM_ERR); + + /* + * properties must be set in transactions and don't take + * effect until the transaction has been ended/committed. + */ + value = scf_value_create(handle->scf_handle); + entry = scf_entry_create(handle->scf_handle); + if (value != NULL && entry != NULL) { + if (scf_transaction_property_change(handle->scf_trans, entry, + propname, SCF_TYPE_INTEGER) == 0 || + scf_transaction_property_new(handle->scf_trans, entry, + propname, SCF_TYPE_INTEGER) == 0) { + scf_value_set_integer(value, valint); + if (scf_entry_add_value(entry, value) != 0) { + ret = SMBD_SMF_SYSTEM_ERR; + scf_value_destroy(value); + } + /* the value is in the transaction */ + value = NULL; + } + /* the entry is in the transaction */ + entry = NULL; + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + if (ret == SMBD_SMF_SYSTEM_ERR) { + switch (scf_error()) { + case SCF_ERROR_PERMISSION_DENIED: + ret = SMBD_SMF_NO_PERMISSION; + break; + } + } + /* + * cleanup if there were any errors that didn't leave these + * values where they would be cleaned up later. + */ + if (value != NULL) + scf_value_destroy(value); + if (entry != NULL) + scf_entry_destroy(entry); + return (ret); +} + +/* + * Gets integer property value. + * Caller is responsible to have enough memory allocated. + */ +int +smb_smf_get_integer_property(smb_scfhandle_t *handle, char *propname, + int64_t *valint) +{ + int ret = SMBD_SMF_OK; + scf_value_t *value = NULL; + scf_property_t *prop = NULL; + + if (handle == NULL) + return (SMBD_SMF_SYSTEM_ERR); + + value = scf_value_create(handle->scf_handle); + prop = scf_property_create(handle->scf_handle); + if ((prop) && (value) && + (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) { + if (scf_property_get_value(prop, value) == 0) { + if (scf_value_get_integer(value, + valint) != 0) { + ret = SMBD_SMF_SYSTEM_ERR; + } + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + if (value != NULL) + scf_value_destroy(value); + if (prop != NULL) + scf_property_destroy(prop); + return (ret); +} + +/* + * Set boolean value of property. + * The value is returned as int64_t value + * Caller ensures appropriate translation. + */ +int +smb_smf_set_boolean_property(smb_scfhandle_t *handle, char *propname, + uint8_t valbool) +{ + int ret = SMBD_SMF_OK; + scf_value_t *value = NULL; + scf_transaction_entry_t *entry = NULL; + + if (handle == NULL) + return (SMBD_SMF_SYSTEM_ERR); + + /* + * properties must be set in transactions and don't take + * effect until the transaction has been ended/committed. + */ + value = scf_value_create(handle->scf_handle); + entry = scf_entry_create(handle->scf_handle); + if (value != NULL && entry != NULL) { + if (scf_transaction_property_change(handle->scf_trans, entry, + propname, SCF_TYPE_BOOLEAN) == 0 || + scf_transaction_property_new(handle->scf_trans, entry, + propname, SCF_TYPE_BOOLEAN) == 0) { + scf_value_set_boolean(value, valbool); + if (scf_entry_add_value(entry, value) != 0) { + ret = SMBD_SMF_SYSTEM_ERR; + scf_value_destroy(value); + } + /* the value is in the transaction */ + value = NULL; + } + /* the entry is in the transaction */ + entry = NULL; + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + if (ret == SMBD_SMF_SYSTEM_ERR) { + switch (scf_error()) { + case SCF_ERROR_PERMISSION_DENIED: + ret = SMBD_SMF_NO_PERMISSION; + break; + } + } + /* + * cleanup if there were any errors that didn't leave these + * values where they would be cleaned up later. + */ + if (value != NULL) + scf_value_destroy(value); + if (entry != NULL) + scf_entry_destroy(entry); + return (ret); +} + +/* + * Gets boolean property value. + * Caller is responsible to have enough memory allocated. + */ +int +smb_smf_get_boolean_property(smb_scfhandle_t *handle, char *propname, + uint8_t *valbool) +{ + int ret = SMBD_SMF_OK; + scf_value_t *value = NULL; + scf_property_t *prop = NULL; + + if (handle == NULL) + return (SMBD_SMF_SYSTEM_ERR); + + value = scf_value_create(handle->scf_handle); + prop = scf_property_create(handle->scf_handle); + if ((prop) && (value) && + (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) { + if (scf_property_get_value(prop, value) == 0) { + if (scf_value_get_boolean(value, + valbool) != 0) { + ret = SMBD_SMF_SYSTEM_ERR; + } + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + if (value != NULL) + scf_value_destroy(value); + if (prop != NULL) + scf_property_destroy(prop); + return (ret); +} + +/* + * Sets a blob property value. + */ +int +smb_smf_set_opaque_property(smb_scfhandle_t *handle, char *propname, + void *voidval, size_t sz) +{ + int ret = SMBD_SMF_OK; + scf_value_t *value; + scf_transaction_entry_t *entry; + + if (handle == NULL) + return (SMBD_SMF_SYSTEM_ERR); + + /* + * properties must be set in transactions and don't take + * effect until the transaction has been ended/committed. + */ + value = scf_value_create(handle->scf_handle); + entry = scf_entry_create(handle->scf_handle); + if (value != NULL && entry != NULL) { + if (scf_transaction_property_change(handle->scf_trans, entry, + propname, SCF_TYPE_OPAQUE) == 0 || + scf_transaction_property_new(handle->scf_trans, entry, + propname, SCF_TYPE_OPAQUE) == 0) { + if (scf_value_set_opaque(value, voidval, sz) == 0) { + if (scf_entry_add_value(entry, value) != 0) { + ret = SMBD_SMF_SYSTEM_ERR; + scf_value_destroy(value); + } + /* the value is in the transaction */ + value = NULL; + } else { + /* value couldn't be constructed */ + ret = SMBD_SMF_SYSTEM_ERR; + } + /* the entry is in the transaction */ + entry = NULL; + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + if (ret == SMBD_SMF_SYSTEM_ERR) { + switch (scf_error()) { + case SCF_ERROR_PERMISSION_DENIED: + ret = SMBD_SMF_NO_PERMISSION; + break; + } + } + /* + * cleanup if there were any errors that didn't leave these + * values where they would be cleaned up later. + */ + if (value != NULL) + scf_value_destroy(value); + if (entry != NULL) + scf_entry_destroy(entry); + return (ret); +} + +/* + * Gets a blob property value. + * Caller is responsible to have enough memory allocated. + */ +int +smb_smf_get_opaque_property(smb_scfhandle_t *handle, char *propname, + void *v, size_t sz) +{ + int ret = SMBD_SMF_OK; + scf_value_t *value = NULL; + scf_property_t *prop = NULL; + + if (handle == NULL) + return (SMBD_SMF_SYSTEM_ERR); + + value = scf_value_create(handle->scf_handle); + prop = scf_property_create(handle->scf_handle); + if ((prop) && (value) && + (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) { + if (scf_property_get_value(prop, value) == 0) { + if (scf_value_get_opaque(value, (char *)v, sz) != sz) { + ret = SMBD_SMF_SYSTEM_ERR; + } + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + } else { + ret = SMBD_SMF_SYSTEM_ERR; + } + if (value != NULL) + scf_value_destroy(value); + if (prop != NULL) + scf_property_destroy(prop); + return (ret); +} + +/* + * Get property based on property type. Returns string value of that + * property. Only SCF_TYPE_ASTRING, SCF_TYPE_INTEGER, SCF_TYPE_BOOLEAN + * supported. + */ +int +smb_smf_get_property(smb_scfhandle_t *handle, int proptype, char *propname, + char *valstr, size_t sz) +{ + int64_t valint = 0; + uint8_t valbool = 0; + int ret = SMBD_SMF_OK; + + switch (proptype) { + case SCF_TYPE_ASTRING: + ret = smb_smf_get_string_property(handle, propname, + valstr, sz); + break; + case SCF_TYPE_INTEGER: + if ((ret = smb_smf_get_integer_property(handle, propname, + &valint)) != 0) + return (ret); + (void) snprintf(valstr, sz, "%lld", valint); + break; + case SCF_TYPE_BOOLEAN: + if ((ret = smb_smf_get_boolean_property(handle, propname, + &valbool)) != 0) + return (ret); + (void) strlcpy(valstr, (valbool ? "true" : "false"), sz); + break; + default: + return (SMBD_SMF_SYSTEM_ERR); + } + return (ret); +} + +/* + * Set property based on property type. + * Only SCF_TYPE_ASTRING, SCF_TYPE_INTEGER, SCF_TYPE_BOOLEAN supported. + */ +int +smb_smf_set_property(smb_scfhandle_t *handle, int proptype, + char *propname, char *valstr) +{ + int64_t valint = 0; + uint8_t valbool = 0; + int ret = SMBD_SMF_OK; + + switch (proptype) { + case SCF_TYPE_ASTRING: + ret = smb_smf_set_string_property(handle, propname, + valstr); + break; + case SCF_TYPE_INTEGER: + valint = strtol(valstr, 0, 10); + ret = smb_smf_set_integer_property(handle, propname, + valint); + break; + case SCF_TYPE_BOOLEAN: + if (strcasecmp(valstr, "true") == 0) + valbool = 1; + ret = smb_smf_set_boolean_property(handle, propname, valbool); + break; + default: + return (SMBD_SMF_SYSTEM_ERR); + } + return (ret); +} + +/* + * Gets an instance iterator for the service specified. + */ +smb_scfhandle_t * +smb_smf_get_iterator(char *svc_name) +{ + smb_scfhandle_t *handle = NULL; + + handle = smb_smf_scf_init(svc_name); + if (!handle) + return (NULL); + + handle->scf_inst_iter = scf_iter_create(handle->scf_handle); + if (handle->scf_inst_iter) { + if (scf_iter_service_instances(handle->scf_inst_iter, + handle->scf_service) != 0) { + smb_smf_scf_fini(handle); + handle = NULL; + } else { + handle->scf_instance = NULL; + } + } else { + smb_smf_scf_fini(handle); + handle = NULL; + } + return (handle); +} + +/* + * smb_smf_scf_init() + * + * must be called before using any of the SCF functions. + * Returns smb_scfhandle_t pointer if success. + */ +smb_scfhandle_t * +smb_smf_scf_init(char *svc_name) +{ + smb_scfhandle_t *handle; + + handle = malloc(sizeof (smb_scfhandle_t)); + if (handle != NULL) { + bzero((char *)handle, sizeof (smb_scfhandle_t)); + handle->scf_state = SCH_STATE_INITIALIZING; + handle->scf_handle = scf_handle_create(SCF_VERSION); + if (handle->scf_handle != NULL) { + if (scf_handle_bind(handle->scf_handle) == 0) { + handle->scf_scope = + scf_scope_create(handle->scf_handle); + if (scf_handle_get_local_scope( + handle->scf_handle, handle->scf_scope) != 0) + goto err; + + handle->scf_service = + scf_service_create(handle->scf_handle); + + if (scf_scope_get_service(handle->scf_scope, + svc_name, handle->scf_service) + != SCF_SUCCESS) { + goto err; + } + handle->scf_pg = + scf_pg_create(handle->scf_handle); + handle->scf_state = SCH_STATE_INIT; + } else { + goto err; + } + } else { + free(handle); + handle = NULL; + smb_smf_scf_log_error("Could not access SMF " + "repository: %s\n"); + } + } + return (handle); + + /* error handling/unwinding */ +err: + (void) smb_smf_scf_fini(handle); + (void) smb_smf_scf_log_error("SMF initialization problem: %s\n"); + return (NULL); +} + +/* + * smb_smf_scf_fini(handle) + * + * must be called when done. Called with the handle allocated in + * smb_smf_scf_init(), it cleans up the state and frees any SCF resources + * still in use. + */ +void +smb_smf_scf_fini(smb_scfhandle_t *handle) +{ + if (handle != NULL) { + int unbind = 0; + scf_iter_destroy(handle->scf_pg_iter); + handle->scf_pg_iter = NULL; + + scf_iter_destroy(handle->scf_inst_iter); + handle->scf_inst_iter = NULL; + + unbind = 1; + scf_scope_destroy(handle->scf_scope); + handle->scf_scope = NULL; + + scf_instance_destroy(handle->scf_instance); + handle->scf_instance = NULL; + + scf_service_destroy(handle->scf_service); + handle->scf_service = NULL; + + scf_pg_destroy(handle->scf_pg); + handle->scf_pg = NULL; + + handle->scf_state = SCH_STATE_UNINIT; + if (unbind) + (void) scf_handle_unbind(handle->scf_handle); + scf_handle_destroy(handle->scf_handle); + handle->scf_handle = NULL; + + free(handle); + } +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_util.c b/usr/src/lib/smbsrv/libsmb/common/smb_util.c new file mode 100644 index 000000000000..bce8efee8ea4 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_util.c @@ -0,0 +1,336 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define C2H(c) "0123456789ABCDEF"[(c)] +#define H2C(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \ + ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : \ + ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : \ + '\0') +#define DEFAULT_SBOX_SIZE 256 + +/* + * + * hexdump + * + * Simple hex dump display function. Displays nbytes of buffer in hex and + * printable format. Non-printing characters are shown as '.'. It is safe + * to pass a null pointer. Each line begins with the offset. If nbytes is + * 0, the line will be blank except for the offset. Example output: + * + * 00000000 54 68 69 73 20 69 73 20 61 20 70 72 6F 67 72 61 This is a progra + * 00000010 6D 20 74 65 73 74 2E 00 m test.. + * + */ +void +hexdump_offset(unsigned char *buffer, int nbytes, unsigned long *start, int log) +{ + static char *hex = "0123456789ABCDEF"; + int i, count; + int offset; + unsigned char *p; + char ascbuf[64]; + char hexbuf[64]; + char *ap = ascbuf; + char *hp = hexbuf; + + if ((p = buffer) == 0) { + if (log) + syslog(LOG_DEBUG, "hexdump: (null)"); + else + (void) printf("hexdump: (null)\n"); + return; + } + + offset = *start; + + *ap = '\0'; + *hp = '\0'; + count = 0; + + for (i = 0; i < nbytes; ++i) { + if (i && (i % 16) == 0) { + if (log) + syslog(LOG_DEBUG, + "%06X %s %s", offset, hexbuf, ascbuf); + else + (void) printf("%06X %s %s\n", + offset, hexbuf, ascbuf); + ap = ascbuf; + hp = hexbuf; + count = 0; + offset += 16; + } + + ap += sprintf(ap, "%c", + (*p >= 0x20 && *p < 0x7F) ? *p : '.'); + hp += sprintf(hp, " %c%c", + hex[(*p >> 4) & 0x0F], hex[(*p & 0x0F)]); + ++p; + ++count; + } + + if (count) { + if (log) + syslog(LOG_DEBUG, + "%06X %-48s %s", offset, hexbuf, ascbuf); + else + (void) printf("%06X %-48s %s\n", + offset, hexbuf, ascbuf); + + offset += count; + } + + *start = offset; +} + +void +hexdump(unsigned char *buffer, int nbytes) +{ + unsigned long start = 0; + + hexdump_offset(buffer, nbytes, &start, 1); +} + +/* + * bintohex + * + * Converts the given binary data (srcbuf) to + * its equivalent hex chars (hexbuf). + * + * hexlen should be at least twice as srclen. + * if hexbuf is not big enough returns 0. + * otherwise returns number of valid chars in + * hexbuf which is srclen * 2. + */ +size_t +bintohex(const char *srcbuf, size_t srclen, + char *hexbuf, size_t hexlen) +{ + size_t outlen; + char c; + + outlen = srclen << 1; + + if (hexlen < outlen) + return (0); + + while (srclen-- > 0) { + c = *srcbuf++; + *hexbuf++ = C2H(c & 0xF); + *hexbuf++ = C2H((c >> 4) & 0xF); + } + + return (outlen); +} + +/* + * hextobin + * + * Converts hex to binary. + * + * Assuming hexbuf only contains hex digits (chars) + * this function convert every two bytes of hexbuf + * to one byte and put it in dstbuf. + * + * hexlen should be an even number. + * dstlen should be at least half of hexlen. + * + * Returns 0 if sizes are not correct, otherwise + * returns the number of converted bytes in dstbuf + * which is half of hexlen. + */ +size_t +hextobin(const char *hexbuf, size_t hexlen, + char *dstbuf, size_t dstlen) +{ + size_t outlen; + + if ((hexlen % 2) != 0) + return (0); + + outlen = hexlen >> 1; + if (dstlen < outlen) + return (0); + + while (hexlen > 0) { + *dstbuf = H2C(*hexbuf) & 0x0F; + hexbuf++; + *dstbuf++ |= (H2C(*hexbuf) << 4) & 0xF0; + hexbuf++; + + hexlen -= 2; + } + + return (outlen); +} + +/* + * trim_whitespace + * + * Trim leading and trailing whitespace chars (as defined by isspace) + * from a buffer. Example; if the input buffer contained " text ", + * it will contain "text", when we return. We assume that the buffer + * contains a null terminated string. A pointer to the buffer is + * returned. + */ +char * +trim_whitespace(char *buf) +{ + char *p = buf; + char *q = buf; + + if (buf == 0) + return (0); + + while (*p && isspace(*p)) + ++p; + + while ((*q = *p++) != 0) + ++q; + + if (q != buf) { + while ((--q, isspace(*q)) != 0) + *q = '\0'; + } + + return (buf); +} + +/* + * randomize + * + * Randomize the contents of the specified buffer. + */ +void +randomize(char *data, unsigned len) +{ + unsigned dwlen = len / 4; + unsigned remlen = len % 4; + unsigned tmp; + unsigned i; /*LINTED E_BAD_PTR_CAST_ALIGN*/ + unsigned *p = (unsigned *)data; + + for (i = 0; i < dwlen; ++i) + *p++ = random(); + + if (remlen) { + tmp = random(); + (void) memcpy(p, &tmp, remlen); + } +} + +/* + * This is the hash mechanism used to encrypt passwords for commands like + * SamrSetUserInformation. It uses a 256 byte s-box. + */ +void +rand_hash( + unsigned char *data, + size_t datalen, + unsigned char *key, + size_t keylen) +{ + unsigned char sbox[DEFAULT_SBOX_SIZE]; + unsigned char tmp; + unsigned char index_i = 0; + unsigned char index_j = 0; + unsigned char j = 0; + int i; + + for (i = 0; i < DEFAULT_SBOX_SIZE; ++i) + sbox[i] = (unsigned char)i; + + for (i = 0; i < DEFAULT_SBOX_SIZE; ++i) { + j += (sbox[i] + key[i % keylen]); + + tmp = sbox[i]; + sbox[i] = sbox[j]; + sbox[j] = tmp; + } + + for (i = 0; i < datalen; ++i) { + index_i++; + index_j += sbox[index_i]; + + tmp = sbox[index_i]; + sbox[index_i] = sbox[index_j]; + sbox[index_j] = tmp; + + tmp = sbox[index_i] + sbox[index_j]; + data[i] = data[i] ^ sbox[tmp]; + } +} + +/* + * strsep + * + * The strsep() function locates, in the string referenced by *stringp, the + * first occurrence of any character in the string delim (or the terminating + * `\0' character) and replaces it with a `\0'. The location of the next + * character after the delimiter character (or NULL, if the end of the + * string was reached) is stored in *stringp. The original value of + * *stringp is returned. + * + * If *stringp is initially NULL, strsep() returns NULL. + */ +char * +strsep(char **stringp, const char *delim) +{ + char *s; + const char *spanp; + int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + + for (tok = s; ; ) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_wins.c b/usr/src/lib/smbsrv/libsmb/common/smb_wins.c new file mode 100644 index 000000000000..09498abfca21 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_wins.c @@ -0,0 +1,210 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB WINS support functions + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +/* + * smb_wins_iplist + * + * Get a string containing a list of comma separated IP addresses + * and return an array containing numeric equivalent for string IPs. + * + * Returns the number of parsed IPs. + * Return -1 if list is badly formatted. + * This routine need fix for IPv6 + */ +int +smb_wins_iplist(char *list, uint32_t iplist[], int max_naddr) +{ + char *ip, *ctx; + char *tmp; + int n = 0; + + if ((list == NULL) || (*list == '\0')) + return (0); + + if ((tmp = strdup(list)) == NULL) + return (0); + + ip = strtok_r(tmp, ",", &ctx); + while (ip && (n < max_naddr)) { + ip = trim_whitespace(ip); + if (*ip != 0) { + if (inet_pton(AF_INET, ip, &iplist[n]) == 1) { + n++; + } else { + return (-1); + } + } + ip = strtok_r(0, ",", &ctx); + } + + free(tmp); + return (n); +} + +/* + * smb_wins_is_excluded + * + * Check to see if the given IP addr shouldn't be registered in WINS. + * + * Returns 1 if it's excluded, 0 if it's not. + */ +boolean_t +smb_wins_is_excluded(in_addr_t ipaddr, + unsigned long *exclude_list, int nexclude) +{ + int i; + + if (nexclude == 0) + return (B_FALSE); + + for (i = 0; i < nexclude; i++) + if (ipaddr == exclude_list[i]) { + return (B_TRUE); + } + + return (B_FALSE); +} + +/* + * Build a CSV list of ips to be excluded. + * This function needs fix for IPv6 + */ +void +smb_wins_build_list(char *buf, uint32_t iplist[], int max_naddr) +{ + char ipstr[16]; + int i; + + if (!buf) + return; + + buf[0] = '\0'; + for (i = 0; i < max_naddr; i++) { + /* XXX these will be removed */ + /*LINTED*/ + if (iplist[i] == -1) + continue; + + if (inet_ntop(AF_INET, (const void *)(&iplist[i]), ipstr, + sizeof (ipstr)) == 0) + continue; + (void) strcat(buf, ipstr); + (void) strcat(buf, ","); + } + buf[strlen(buf)-1] = '\0'; +} + +/* + * This function build the new WINS exclude list from + * configured list + new additions to exclude list + * It also assumes that the buffers are of enough space. + */ +int +smb_wins_exclude_list(char *config_list, char *exclude_list) +{ + int ccnt, ecnt, already_there; + int i, j; + uint32_t ncur_list[SMB_PI_MAX_NETWORKS]; + uint32_t ecur_list[SMB_PI_MAX_NETWORKS]; + + ccnt = smb_wins_iplist(config_list, ncur_list, SMB_PI_MAX_NETWORKS); + if (ccnt < 0) + return (-1); + + ecnt = smb_wins_iplist(exclude_list, ecur_list, SMB_PI_MAX_NETWORKS); + if (ecnt < 0) + return (-1); + + if ((ccnt + ecnt) > SMB_PI_MAX_NETWORKS) + return (-1); + + for (i = 0; i < ecnt; i++) { + already_there = 0; + for (j = 0; j < ccnt; j++) { + if (ncur_list[j] == ecur_list[i]) { + already_there = 1; + } + } + if (already_there) + continue; + + ncur_list[ccnt++] = ecur_list[i]; + } + + smb_wins_build_list(config_list, ncur_list, ccnt); + return (0); +} + +/* + * This function build the new WINS allow list from + * configured list - new allowed list + * It also assumes that the buffers are of enough space. + */ +int +smb_wins_allow_list(char *config_list, char *allow_list) +{ + int ccnt, acnt; + int i, j; + uint32_t ncur_list[SMB_PI_MAX_NETWORKS]; + uint32_t acur_list[SMB_PI_MAX_NETWORKS]; + + ccnt = smb_wins_iplist(config_list, ncur_list, SMB_PI_MAX_NETWORKS); + if (ccnt < 0) + return (-1); + + acnt = smb_wins_iplist(allow_list, acur_list, SMB_PI_MAX_NETWORKS); + if (acnt < 0) + return (0); + + for (i = 0; i < acnt; i++) { + for (j = 0; j < ccnt; j++) { + if (ncur_list[j] == (in_addr_t)(-1)) + continue; + if (ncur_list[j] == acur_list[i]) { + ncur_list[j] = (in_addr_t)(-1); + } + } + } + smb_wins_build_list(config_list, ncur_list, ccnt); + return (0); +} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_wksids.c b/usr/src/lib/smbsrv/libsmb/common/smb_wksids.c new file mode 100644 index 000000000000..0a40b95418a1 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/common/smb_wksids.c @@ -0,0 +1,350 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module provides the interface to builtin domain information. + * These are the predefined groups and aliases in the NT AUTHORITY or + * BUILTIN domains, and some other miscellaneous bits. + */ + +#include +#include +#include +#include +#include + +/* + * This table should contain all of the NT builtin domain names. + */ +static char *domain[] = { + "LOCAL", + "BUILTIN", + "NT AUTHORITY", + "UNKNOWN" +}; + +static int wk_init = 0; +static rwlock_t wk_rwlock; + +/* + * This table should contain all of the builtin domains, groups and + * aliases. The order is important because we do string compares on + * the SIDs. For each domain, ensure that the domain SID appears + * before any aliases in that domain. + */ +static well_known_account_t wkt[] = { + { SidTypeWellKnownGroup, 0, "S-1-0-0", "Null", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 1, "S-1-1-0", "Everyone", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 1, "S-1-2-0", "LOCAL", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 1, "S-1-3-0", "CREATOR OWNER", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 1, "S-1-3-1", "CREATOR GROUP", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 1, "S-1-3-2", "CREATOR OWNER SERVER", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 1, "S-1-3-3", "CREATOR GROUP SERVER", + LGF_HIDDEN, 0, NULL}, + { SidTypeDomain, 1, "S-1-4", "NON UNIQUE", + LGF_HIDDEN, 0, NULL}, + { SidTypeDomain, 2, "S-1-5", "NT AUTHORITY", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 2, "S-1-5-1", "DIALUP", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 2, "S-1-5-2", "NETWORK", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 2, "S-1-5-3", "BATCH", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 2, "S-1-5-4", "INTERACTIVE", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 2, "S-1-5-6", "SERVICE", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 2, "S-1-5-7", "ANONYMOUS", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 2, "S-1-5-8", "PROXY", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 2, "S-1-5-9", "SERVER", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 2, "S-1-5-10", "SELF", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 2, "S-1-5-11", "Authenticated Users", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 2, "S-1-5-12", "RESTRICTED", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 2, "S-1-5-18", "SYSTEM", + LGF_HIDDEN, 0, NULL}, + { SidTypeWellKnownGroup, 2, "S-1-5-21", "NON_UNIQUE", + LGF_HIDDEN, 0, NULL}, + { SidTypeDomain, 2, "S-1-5-32", "BUILTIN", + LGF_HIDDEN, 0, NULL}, + { SidTypeAlias, 1, "S-1-5-32-544", "Administrators", + 0, "Members can fully administer the computer/domain", NULL }, + { SidTypeAlias, 1, "S-1-5-32-545", "Users", + LGF_HIDDEN, 0, NULL}, + { SidTypeAlias, 1, "S-1-5-32-546", "Guests", + LGF_HIDDEN, 0, NULL}, + { SidTypeAlias, 1, "S-1-5-32-547", "Power Users", + 0, "Members can share directories", NULL }, + { SidTypeAlias, 1, "S-1-5-32-548", "Account Operators", + LGF_HIDDEN, 0, NULL}, + { SidTypeAlias, 1, "S-1-5-32-549", "Server Operators", + LGF_HIDDEN, 0, NULL}, + { SidTypeAlias, 1, "S-1-5-32-550", "Print Operators", + LGF_HIDDEN, 0, NULL}, + { SidTypeAlias, 1, "S-1-5-32-551", "Backup Operators", + 0, "Members can bypass file security to back up files", NULL }, + { SidTypeAlias, 1, "S-1-5-32-552", "Replicator", + LGF_HIDDEN, 0, NULL} +}; + + +/* + * nt_builtin_lookup_sid + * + * Search the wkt looking for a match on the specified SID. If the + * SID matches a builtin entry, the associated name is returned. + * Otherwise a null pointer is returned. + */ +char * +nt_builtin_lookup_sid(nt_sid_t *sid, WORD *sid_name_use) +{ + well_known_account_t *entry; + char *sidbuf; + int sidlen; + int i; + + if ((sidbuf = nt_sid_format(sid)) == 0) { + return (0); + } + + sidlen = strlen(sidbuf); + + for (i = 0; i < sizeof (wkt)/sizeof (wkt[0]); ++i) { + entry = &wkt[i]; + + if (strncmp(sidbuf, entry->sid, sidlen) == 0) { + if (sid_name_use) + *sid_name_use = entry->sid_name_use; + free(sidbuf); + return (entry->name); + } + } + + free(sidbuf); + return (0); +} + + +/* + * nt_builtin_lookup_name + * + * Search the wkt looking for a match on the specified name. If the + * name matches a builtin entry, the associated SID (which is in + * malloc'd memory) is returned. Otherwise a null pointer is returned. + */ +nt_sid_t * +nt_builtin_lookup_name(char *name, WORD *sid_name_use) +{ + well_known_account_t *entry; + int i; + + for (i = 0; i < sizeof (wkt)/sizeof (wkt[0]); ++i) { + entry = &wkt[i]; + + if (!utf8_strcasecmp(name, entry->name)) { + if (sid_name_use) + *sid_name_use = entry->sid_name_use; + return (nt_sid_strtosid(entry->sid)); + } + } + + return (0); +} + +/* + * nt_builtin_lookup + * + * Search the wkt looking for a match on the specified name. If the + * name matches a builtin entry then pointer to that entry will be + * returned. Otherwise 0 is returned. + */ +well_known_account_t * +nt_builtin_lookup(char *name) +{ + well_known_account_t *entry; + int i; + + (void) rw_rdlock(&wk_rwlock); + for (i = 0; i < sizeof (wkt)/sizeof (wkt[0]); ++i) { + entry = &wkt[i]; + + if (!utf8_strcasecmp(name, entry->name)) { + (void) rw_unlock(&wk_rwlock); + return (entry); + } + } + + (void) rw_unlock(&wk_rwlock); + return (0); +} + + +/* + * nt_builtin_is_wellknown + * + * Search the wkt looking for a match on the specified name. If the + * name matches a builtin entry returns 1. Otherwise returns 0. + */ +int +nt_builtin_is_wellknown(char *name) +{ + well_known_account_t *entry; + int i; + + for (i = 0; i < sizeof (wkt)/sizeof (wkt[0]); ++i) { + entry = &wkt[i]; + + if (!utf8_strcasecmp(name, entry->name)) { + return (1); + } + } + + return (0); +} + +/* + * nt_builtin_lookup_domain + * + * Return the builtin domain name for the specified alias or group name. + */ +char * +nt_builtin_lookup_domain(char *name) +{ + well_known_account_t *entry; + char *domain_name; + int i; + + for (i = 0; i < sizeof (wkt)/sizeof (wkt[0]); ++i) { + entry = &wkt[i]; + + if (!utf8_strcasecmp(name, entry->name)) { + domain_name = domain[entry->domain_ix]; + return (domain_name); + } + } + + return (0); +} + +/* + * nt_builtin_findfirst + * + * Returns pointer to the first entry of well known sids table. + */ +well_known_account_t * +nt_builtin_findfirst(DWORD *iterator) +{ + *iterator = 1; + return (&wkt[0]); +} + +/* + * nt_builtin_findnext + * + * Returns pointer to the entry of well known sids table specified + * by the iterator. Increments iterator to point to the next entry. + */ +well_known_account_t * +nt_builtin_findnext(DWORD *iterator) +{ + if (*iterator < sizeof (wkt)/sizeof (wkt[0])) + return (&wkt[(*iterator)++]); + + return (0); +} + +/* + * nt_builtin_init + * + * Generate binary SIDs from the string SIDs in the table + * and set the proper field. + * + * Caller MUST not store the binary SID pointer anywhere that + * could lead to freeing it. + * + * This function should only be called once. + */ +int +nt_builtin_init() +{ + well_known_account_t *entry; + int i; + + (void) rw_wrlock(&wk_rwlock); + if (wk_init) { + (void) rw_unlock(&wk_rwlock); + return (1); + } + + for (i = 0; i < sizeof (wkt)/sizeof (wkt[0]); ++i) { + entry = &wkt[i]; + entry->binsid = nt_sid_strtosid(entry->sid); + if (entry->binsid == NULL) { + (void) rw_unlock(&wk_rwlock); + nt_builtin_fini(); + return (0); + } + } + + wk_init = 1; + (void) rw_unlock(&wk_rwlock); + return (1); +} + +void +nt_builtin_fini() +{ + int i; + + (void) rw_wrlock(&wk_rwlock); + if (wk_init == 0) { + (void) rw_unlock(&wk_rwlock); + return; + } + + for (i = 0; i < sizeof (wkt)/sizeof (wkt[0]); ++i) { + if (wkt[i].binsid) { + free(wkt[i].binsid); + wkt[i].binsid = NULL; + } + } + + wk_init = 0; + (void) rw_unlock(&wk_rwlock); +} diff --git a/usr/src/lib/smbsrv/libsmb/i386/Makefile b/usr/src/lib/smbsrv/libsmb/i386/Makefile new file mode 100644 index 000000000000..f91f0270e9ad --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/i386/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/smbsrv/libsmb/sparc/Makefile b/usr/src/lib/smbsrv/libsmb/sparc/Makefile new file mode 100644 index 000000000000..f91f0270e9ad --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/sparc/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/smbsrv/libsmb/sparcv9/Makefile b/usr/src/lib/smbsrv/libsmb/sparcv9/Makefile new file mode 100644 index 000000000000..a2f97019c815 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmb/sparcv9/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/smbsrv/libsmbns/Makefile b/usr/src/lib/smbsrv/libsmbns/Makefile new file mode 100644 index 000000000000..495ff5688d7a --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +HDRS= libsmbns.h + +include ../Makefile.smbsrv diff --git a/usr/src/lib/smbsrv/libsmbns/Makefile.com b/usr/src/lib/smbsrv/libsmbns/Makefile.com new file mode 100644 index 000000000000..2166b2640df0 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/Makefile.com @@ -0,0 +1,62 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +LIBRARY= libsmbns.a +VERS= .1 + +OBJS_SHARED = \ + smb_netbios_util.o \ + +OBJS_COMMON= \ + smbns_ads.o \ + smbns_browser.o \ + smbns_dyndns.o \ + smbns_krb.o \ + smbns_ksetpwd.o \ + smbns_netbios.o \ + smbns_netbios_cache.o \ + smbns_netbios_datagram.o\ + smbns_netbios_name.o \ + smbns_netlogon.o \ + smbns_nicconfig.o + +OBJECTS= $(OBJS_COMMON) $(OBJS_SHARED) + +include ../../../Makefile.lib +include ../../Makefile.lib + +SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \ + $(OBJS_SHARED:%.o=$(SRC)/common/smbsrv/%.c) + +LDLIBS += -lsmb -lgss -lldap -lresolv -lnsl -lsocket -lc +CPPFLAGS += -D_REENTRANT + +# DYNLIB libraries do not have lint libs and are not linted +$(DYNLIB) := LDLIBS += -lkrb5 + +include ../../Makefile.targ +include ../../../Makefile.targ diff --git a/usr/src/lib/smbsrv/libsmbns/amd64/Makefile b/usr/src/lib/smbsrv/libsmbns/amd64/Makefile new file mode 100644 index 000000000000..a2f97019c815 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/amd64/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/smbsrv/libsmbns/common/libsmbns.h b/usr/src/lib/smbsrv/libsmbns/common/libsmbns.h new file mode 100644 index 000000000000..a05c197c35cc --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/libsmbns.h @@ -0,0 +1,184 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBSMBNS_H +#define _LIBSMBNS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ADS typedef/data structures and functions */ +#define ADS_MAXBUFLEN 100 + +typedef struct ads_handle_s { + char *user; /* admin user to create share in ADS */ + char *pwd; /* user password */ + char *container; /* user container in ADS */ + char *domain; /* ADS domain */ + char *domain_dn; /* domain in Distinquish Name format */ + char *ip_addr; /* ip addr in string format */ + char *hostname; /* fully qualified hostname */ + char *site; /* local ADS site */ + LDAP *ld; /* LDAP handle */ +} ADS_HANDLE; + +/* + * The possible return status of the adjoin routine. + */ +typedef enum adjoin_status { + ADJOIN_SUCCESS = 0, + ADJOIN_ERR_GET_HANDLE, + ADJOIN_ERR_GEN_PASSWD, + ADJOIN_ERR_ADD_TRUST_ACCT, + ADJOIN_ERR_GET_ENCTYPES, + ADJOIN_ERR_GET_HOST_PRINC, + ADJOIN_ERR_INIT_KRB_CTX, + ADJOIN_ERR_GET_KRB_PRINC, + ADJOIN_ERR_KSETPWD, + ADJOIN_ERR_MOD_TRUST_ACCT, + ADJOIN_ERR_WRITE_KEYTAB, + ADJOIN_ERR_IDMAP_SET_DOMAIN, + ADJOIN_ERR_IDMAP_SET_GC, + ADJOIN_ERR_IDMAP_REFRESH, + ADJOIN_ERR_IDMAP_CCACHE, + + ADJOIN_NUM_STATUS +} adjoin_status_t; + +/* ADS functions */ +extern ADS_HANDLE *ads_open(void); +extern void ads_close(ADS_HANDLE *); +extern int ads_publish_share(ADS_HANDLE *, const char *, const char *, + const char *, const char *); +extern int ads_remove_share(ADS_HANDLE *, const char *, const char *, + const char *, const char *); +extern int ads_build_unc_name(char *, int, const char *, const char *); +extern int ads_lookup_share(ADS_HANDLE *, const char *, const char *, char *); +extern int ads_add_share(ADS_HANDLE *, const char *, const char *, + const char *); + +extern adjoin_status_t adjoin(char *, int); +extern char *adjoin_report_err(adjoin_status_t status); + +/* DYNDNS functions */ +extern int dyndns_update(void); +extern int dyndns_clear_rev_zone(void); + +/* Kerberos initialization function */ +extern int smb_kinit(char *user, char *passwd); + + +/* NETBIOS Functions */ +extern int msdcs_lookup_ads(void); +extern void smb_netbios_start(void); +extern void smb_netbios_shutdown(void); +extern void smb_netbios_name_reconfig(void); + +/* Browser Configure */ +extern void smb_browser_config(void); + +extern void smb_netlogon_request(int, int, char *); + +/* + * NIC listing and config + */ +#define MAXIFS 256 +#define SIZE_IP 17 + +typedef struct { + char ifname[LIFNAMSIZ]; + uint32_t ip; + uint32_t mask; + uint32_t broadcast; + boolean_t exclude; + uint64_t flags; + char groupname[LIFGRNAMSIZ]; + char **aliases; + int naliases; +} net_cfg_t; +typedef struct { + net_cfg_t *net_cfg_list; + int net_cfg_cnt; +} net_cfg_list_t; + +struct if_list { + char name[IFNAMSIZ+1]; + struct if_list *next; +}; + +struct ip_alias { + char name[SIZE_IP]; + struct ip_alias *next; +}; + +#define GATEWAY_FILE "/etc/defaultrouter" + +/* NIC Config functions */ +extern void smb_resolver_init(void); +extern void smb_resolver_close(void); +extern int smb_get_nameservers(struct in_addr *, int); +extern uint16_t smb_get_next_resid(void); +extern void smb_nic_lock(void); +extern void smb_nic_unlock(void); +extern int smb_nic_init(void); +extern void smb_nic_build_info(void); +extern net_cfg_t *smb_nic_get_byind(int, net_cfg_t *); +extern net_cfg_t *smb_nic_get_bysubnet(uint32_t, net_cfg_t *); +extern net_cfg_t *smb_nic_get_byip(uint32_t, net_cfg_t *); +extern int smb_nic_get_num(void); +extern int smb_nic_get_IP(char *, uint32_t *uip); +extern int smb_nic_get_broadcast(char *, uint32_t *uip); +extern int smb_nic_get_netmask(char *, uint32_t *uip); +extern int smb_nic_get_IP_aliases(char *, struct ip_alias **); +extern int smb_nic_get_number(void); +extern int smb_nic_get_num_physical(void); +extern int smb_nic_get_num_logical(void); +extern int smb_nic_get_num_aliases(char *); +extern int smb_nic_get_default_gateway(char *, unsigned int); +extern int smb_nic_flags(char *, uint64_t *); +extern int smb_nic_build_if_name(char ***); +extern int smb_nic_build_network_structures(net_cfg_t **, int *); +extern char *smb_nic_get_ifnames(int, int); +extern int smb_nic_validate_ip_address(char *); +extern int smb_nic_status(char *, uint64_t); +extern int smb_nic_get_group(char *lifname, char *grname); +extern int smb_nic_set_group(char *lifname, char *grname); +extern int smb_nic_clear_niclist(net_cfg_t *, int); +extern int smb_nic_clear_name_list(char **, int); +extern int smb_nic_clear_ip_alias(struct ip_alias *); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBSMBNS_H */ diff --git a/usr/src/lib/smbsrv/libsmbns/common/llib-lsmbns b/usr/src/lib/smbsrv/libsmbns/common/llib-lsmbns new file mode 100644 index 000000000000..5420d543b500 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/llib-lsmbns @@ -0,0 +1,31 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/*LINTLIBRARY*/ +/*PROTOLIB1*/ + +#include diff --git a/usr/src/lib/smbsrv/libsmbns/common/mapfile-vers b/usr/src/lib/smbsrv/libsmbns/common/mapfile-vers new file mode 100644 index 000000000000..d20641252db7 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/mapfile-vers @@ -0,0 +1,81 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +SUNWprivate { + global: + adjoin; + adjoin_report_err; + ads_add_share; + ads_build_unc_name; + ads_close; + ads_lookup_share; + ads_open; + ads_publish_share; + ads_remove_share; + dyndns_clear_rev_zone; + dyndns_update; + msdcs_lookup_ads; + smb_browser_config; + smb_get_nameservers; + smb_get_next_resid; + smb_kinit; + smb_netbios_name_reconfig; + smb_netbios_start; + smb_netbios_shutdown; + smb_netlogon_request; + smb_nic_build_if_name; + smb_nic_build_network_structures; + smb_nic_clear_ip_alias; + smb_nic_clear_name_list; + smb_nic_clear_niclist; + smb_nic_free_niclist; + smb_nic_get_IP; + smb_nic_get_IP_aliases; + smb_nic_get_broadcast; + smb_nic_build_info; + smb_nic_get_byind; + smb_nic_get_byip; + smb_nic_get_bysubnet; + smb_nic_get_default_gateway; + smb_nic_get_group; + smb_nic_get_ifnames; + smb_nic_get_netmask; + smb_nic_get_num; + smb_nic_get_num_aliases; + smb_nic_get_number; + smb_nic_get_num_logical; + smb_nic_get_num_physical; + smb_nic_init; + smb_nic_lock; + smb_nic_status; + smb_nic_unlock; + smb_nic_validate_ip_address; + smb_resolver_close; + smb_resolver_init; + local: + *; +}; diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c new file mode 100644 index 000000000000..c57971e455ea --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c @@ -0,0 +1,2304 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define ADS_DN_MAX 300 +#define ADS_MAXMSGLEN 512 +#define ADS_HOST_PREFIX "host/" +#define ADS_COMPUTERS_CN "Computers" + +/* current ADS server to communicate with */ +ADS_HOST_INFO *ads_host_info = NULL; +mutex_t ads_mtx; + +/* + * adjoin_errmsg + * + * Use the adjoin return status defined in adjoin_status_t as the index + * to this table. + */ +static char *adjoin_errmsg[] = { + "ADJOIN succeeded.", + "ADJOIN failed to get handle.", + "ADJOIN failed to generate machine password.", + "ADJOIN failed to add workstation trust account.", + "ADJOIN failed to get list of encryption types.", + "ADJOIN failed to get host principal.", + "ADJOIN failed to initialize kerberos context.", + "ADJOIN failed to get kerberos principal.", + "ADJOIN failed to set machine account password on AD.", + "ADJOIN failed to modify workstation trust account.", + "ADJOIN failed to write Keberos keytab file.", + "ADJOIN failed to configure idmap(mapping domain).", + "ADJOIN failed to configure idmap(global catalog).", + "ADJOIN failed to refresh idmap service." + "ADJOIN failed to remove idmap ccache." +}; + +static int ads_bind(ADS_HANDLE *); +static void ads_get_computer_dn(ADS_HANDLE *, char *, size_t); +static char *ads_get_host_principal(char *fqhost); +static char *ads_get_host_principal_w_realm(char *princ, char *domain); +static int ads_get_host_principals(char *fqhost, char *domain, + char **princ, char **princ_r); +static int ads_add_computer(ADS_HANDLE *ah); +static void ads_del_computer(ADS_HANDLE *ah); +static int ads_lookup_computer_n_attr(ADS_HANDLE *ah, char *attr, char **val); +static int ads_modify_computer(ADS_HANDLE *ah, int des_only); +static krb5_kvno ads_lookup_computer_attr_kvno(ADS_HANDLE *ah); +static int ads_gen_machine_passwd(char *machine_passwd, int bufsz); +static void ads_set_host_info(ADS_HOST_INFO *host); +static ADS_HOST_INFO *ads_get_host_info(void); + +/* + * ads_build_unc_name + * + * Construct the UNC name of the share object in the format of + * \\hostname.domain\shareUNC + * + * Returns 0 on success, -1 on error. + */ +int +ads_build_unc_name(char *unc_name, int maxlen, + const char *hostname, const char *shareUNC) +{ + char my_domain[ADS_MAXBUFLEN]; + + if (smb_getdomainname(my_domain, sizeof (my_domain)) != 0) + return (-1); + + (void) snprintf(unc_name, maxlen, "\\\\%s.%s\\%s", + hostname, my_domain, shareUNC); + return (0); +} + +/* + * ads_skip_domain_name + * Skip domain name format in DNS message. The format is a sequence of + * ascii labels with each label having a length byte at the beginning. + * The domain name is terminated with a NULL character. + * i.e. 3sun3com0 + * Parameters: + * bufptr: address of pointer of buffer that contains domain name + * Returns: + * bufptr: points to the data after the domain name label + */ +static void +ads_skip_domain_name(char **bufptr) +{ + int i = 0; + unsigned char c, d; + + c = (*bufptr)[i++]; + d = c & 0xC0; + while (c != 0 && (d != 0xC0)) { /* do nothing */ + c = (*bufptr)[i++]; + d = c & 0xC0; + } + + if (d == 0xC0) + /* skip 2nd byte in 2 byte ptr info */ + i++; + *bufptr += i; +} + +static int +ads_is_ptr(char *buf, int len, char *offset_ptr, char **new_loc) +{ + uint16_t offset; + unsigned char c; + + c = len & 0xC0; + if (c == 0xC0) { + offset_ptr = dyndns_get_nshort(offset_ptr, &offset); + offset &= 0x3FFF; + if (offset > NS_PACKETSZ) { + return (-1); + } + *new_loc = buf + offset; + return (1); + } + return (0); +} + +/* + * ads_get_domain_name + * Converts the domain name format in DNS message back to string format. + * The format is a sequence of ascii labels with each label having a length + * byte at the beginning. The domain name is terminated with a NULL + * character. + * i.e. 6procom3com0 -> procom.com + * Parameters: + * bufptr : address of pointer to buffer that contains domain name + * dname_len: length of domain name in label format + * Returns: + * NULL : error + * domain name: in string format using allocated memory + * bufptr : points to the data after the domain name label + */ +static char * +ads_get_domain_name(char *buf, char **bufptr) +{ + char str[256], *ptr, *new_loc; + int i, j, k, len, ret; + int skip = 0; + i = 0; + k = 0; + ptr = *bufptr; + + /* get len of first label */ + len = ptr[i++]; + if ((ret = ads_is_ptr(buf, len, &ptr[i-1], &new_loc)) == 1) { + if (skip == 0) { + /* skip up to first ptr */ + skip = i; + } + + i = 0; + ptr = new_loc; + + /* get len of first label */ + len = ptr[i++]; + } else { + if (ret == -1) { + return (NULL); + } + } + + while (len) { + if ((len > 63) || (k >= 255)) + return (NULL); + + for (j = 0; j < len; j++) + str[k++] = ptr[i++]; + + /* get len of next label */ + len = ptr[i++]; + if ((ret = ads_is_ptr(buf, len, &ptr[i-1], &new_loc)) == 1) { + if (skip == 0) { + /* skip up to first ptr */ + skip = i; + } + i = 0; + ptr = new_loc; + + /* get len of first label */ + len = ptr[i++]; + } else if (ret == -1) { + return (NULL); + } + + if (len) { + /* replace label len or ptr with '.' */ + str[k++] = '.'; + } + } + + str[k] = 0; + + if (skip) { + /* skip name with ptr or just ptr */ + *bufptr += skip + 1; + } else { + /* skip name */ + *bufptr += i; + } + + return (strdup(str)); +} + +/* + * ads_ping + * Ping IP without displaying log. This is used to ping an ADS server to see + * if it is still alive before connecting to it with TCP. + * Taken from os/service/ping.c + * Parameters: + * hostinetaddr: 4 bytes IP address in network byte order + * Returns: + * -1: error + * 0: successful + */ +/*ARGSUSED*/ +static int +ads_ping(unsigned long hostinetaddr) +{ + return (0); +} + +/* + * ads_free_host_list + */ +static void +ads_free_host_list(ADS_HOST_INFO *host_list, int count) +{ + int i; + for (i = 0; i < count; i++) { + free(host_list[i].name); + } + free(host_list); +} + +/* + * ads_set_host_info + * Cache the result of the ADS discovery if the cache is empty. + */ +static void +ads_set_host_info(ADS_HOST_INFO *host) +{ + (void) mutex_lock(&ads_mtx); + if (!ads_host_info) + ads_host_info = host; + (void) mutex_unlock(&ads_mtx); +} + +/* + * ads_get_host_info + * Get the cached ADS host info. + */ +static ADS_HOST_INFO * +ads_get_host_info(void) +{ + ADS_HOST_INFO *host; + + (void) mutex_lock(&ads_mtx); + host = ads_host_info; + (void) mutex_unlock(&ads_mtx); + return (host); +} +/* + * ads_find_host + * This routine builds a DNS service location message and sends it to the + * DNS server via UDP to query it for a list of ADS server(s). Once a reply + * is received, the reply message is parsed to get the hostname and IP + * addresses of the ADS server(s). One ADS server will be selected from the + * list. A ping is sent to each host at a time and the one that respond will + * be selected. + * + * The service location of _ldap._tcp.dc.msdcs. is used to + * guarantee that Microsoft domain controllers are returned. Microsoft domain + * controllers are also ADS servers. + * + * The ADS hostnames are stored in the answer section of the DNS reply message. + * The IP addresses are stored in the additional section. If the additional + * section does not contain any IP addresses then a DNS query by hostname is + * sent to get the IP address of the hostname. This is very unlikely. + * + * The DNS reply message may be in compress formed. The compression is done + * on repeating domain name label in the message. i.e hostname. + * Parameters: + * ns: Nameserver to use to find the ADS host + * domain: domain of ADS host. + * Returns: + * ADS host: fully qualified hostname, ip address, ldap port + * port : LDAP port of ADS host + */ +/*ARGSUSED*/ +ADS_HOST_INFO * +ads_find_host(char *ns, char *domain, int *port, char *service, int *go_next) +{ + int s; + uint16_t id, rid, data_len, eport; + int ipaddr; + struct hostent *h; + char buf[NS_PACKETSZ], buf2[NS_PACKETSZ]; + char *bufptr, *str; + int i, ret; + int queryReq; + uint16_t query_cnt, ans_cnt, namser_cnt, addit_cnt; + int quest_type, quest_class; + int dns_ip, decode_ip; + struct in_addr addr; + uint16_t flags = 0; + int force_recurs = 0; + ADS_HOST_INFO *ads_hosts_list = NULL, *ads_host; + ADS_HOST_INFO *ads_hosts_list2 = NULL; + + *go_next = 0; + + /* + * If we have already found an ADS server, skip the ads_find_host + * process. Returns the ADS host from the cache. + */ + ads_host = ads_get_host_info(); + if (ads_host) + return (ads_host); + + if (ns == NULL || *ns == 0) { + return (NULL); + } + dns_ip = inet_addr(ns); + + if ((s = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0) + return (NULL); + +retry: + /* build query request */ + queryReq = REQ_QUERY; + query_cnt = 1; + ans_cnt = 0; + namser_cnt = 0; + addit_cnt = 0; + + (void) memset(buf, 0, NS_PACKETSZ); + bufptr = buf; + id = smb_get_next_resid(); + if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), id, queryReq, + query_cnt, ans_cnt, namser_cnt, addit_cnt, flags) == -1) { + (void) close(s); + return (NULL); + } + + quest_type = ns_t_srv; + quest_class = ns_c_in; + + if (dyndns_build_quest_zone(&bufptr, BUFLEN_UDP(bufptr, buf), service, + quest_type, quest_class) == -1) { + (void) close(s); + return (NULL); + } + + if (dyndns_udp_send_recv(s, buf, bufptr - buf, buf2) == -1) { + (void) close(s); + syslog(LOG_ERR, "smb_ads: send/receive error"); + *go_next = 1; + return (NULL); + } + (void) close(s); + + (void) dyndns_get_nshort(buf2, &rid); + if (id != rid) + return (NULL); + + /* + * check if query is successful by checking error + * field in UDP + */ + ret = buf2[3] & 0xf; + if (ret != NOERROR) { + syslog(LOG_ERR, "smb_ads: DNS query for ADS host error: %d: ", + ret); + dyndns_msg_err(ret); + *go_next = 1; + return (NULL); + } + + bufptr = buf2; + bufptr += 2; /* Skip ID section */ + bufptr = dyndns_get_nshort(bufptr, &flags); + bufptr = dyndns_get_nshort(bufptr, &query_cnt); + bufptr = dyndns_get_nshort(bufptr, &ans_cnt); + bufptr = dyndns_get_nshort(bufptr, &namser_cnt); + bufptr = dyndns_get_nshort(bufptr, &addit_cnt); + + if (ans_cnt == 0) { + /* Check if the server supports recursive queries */ + if (force_recurs++ == 0 && (flags & DNSF_RECUR_SUPP) != 0) { + flags = DNSF_RECUR_QRY; + goto retry; + } + + syslog(LOG_DEBUG, "smb_ads: No ADS host found: " + "No answer section\n"); + return (NULL); + } + + /* skip question section */ + if (query_cnt == 1) { + ads_skip_domain_name(&bufptr); + bufptr += 4; + } else { + syslog(LOG_ERR, "smb_ads: No ADS host found, malformed " + "question section, query_cnt: %d???\n", query_cnt); + return (NULL); + } + + ads_hosts_list = (ADS_HOST_INFO *) + malloc(sizeof (ADS_HOST_INFO)*ans_cnt); + if (ads_hosts_list == NULL) + return (NULL); + + bzero(ads_hosts_list, sizeof (ADS_HOST_INFO) * ans_cnt); + + /* check answer section */ + for (i = 0; i < ans_cnt; i++) { + ads_skip_domain_name(&bufptr); + + /* skip type, class, ttl */ + bufptr += 8; + + /* len of data after this point */ + bufptr = dyndns_get_nshort(bufptr, &data_len); + + /* skip priority, weight */ + bufptr += 4; + bufptr = dyndns_get_nshort(bufptr, &eport); + ads_hosts_list[i].port = eport; + + if ((str = ads_get_domain_name(buf2, &bufptr)) == NULL) { + syslog(LOG_ERR, "smb_ads: No ADS host found, " + "error decoding DNS answer section\n"); + ads_free_host_list(ads_hosts_list, ans_cnt); + return (NULL); + } + ads_hosts_list[i].name = str; + } + + /* check authority section */ + for (i = 0; i < namser_cnt; i++) { + ads_skip_domain_name(&bufptr); + + /* skip type, class, ttl */ + bufptr += 8; + + /* get len of data */ + bufptr = dyndns_get_nshort(bufptr, &data_len); + + /* skip data */ + bufptr += data_len; + } + + /* check additional section to get IP address of ads host */ + decode_ip = 1; + smb_config_rdlock(); + if (smb_config_getyorn(SMB_CI_ADS_IPLOOKUP) == 1) + decode_ip = 0; + smb_config_unlock(); + + if (decode_ip && (addit_cnt > 0)) { + int j; + + ads_hosts_list2 = (ADS_HOST_INFO *) + malloc(sizeof (ADS_HOST_INFO) * addit_cnt); + if (ads_hosts_list2 == NULL) { + ads_free_host_list(ads_hosts_list, ans_cnt); + return (NULL); + } + + bzero(ads_hosts_list2, sizeof (ADS_HOST_INFO) * addit_cnt); + + for (i = 0; i < addit_cnt; i++) { + + if ((str = ads_get_domain_name(buf2, + &bufptr)) == NULL) { + syslog(LOG_ERR, "smb_ads: No ADS host found, " + "error decoding DNS additional section\n"); + ads_free_host_list(ads_hosts_list, ans_cnt); + ads_free_host_list(ads_hosts_list2, addit_cnt); + return (NULL); + } + + ads_hosts_list2[i].name = str; + bufptr += 10; + bufptr = dyndns_get_int(bufptr, &ipaddr); + ads_hosts_list2[i].ip_addr = ipaddr; + } + + /* pick a host that is up */ + for (i = 0; i < addit_cnt; i++) { + if (ads_ping(ads_hosts_list2[i].ip_addr) != 0) { + continue; + } + for (j = 0; j < ans_cnt; j++) + if (strcmp(ads_hosts_list2[i].name, + ads_hosts_list[j].name) == 0) + break; + if (j == ans_cnt) { + ads_free_host_list(ads_hosts_list, ans_cnt); + ads_free_host_list(ads_hosts_list2, addit_cnt); + return (NULL); + } + ads_host = (ADS_HOST_INFO *) + malloc(sizeof (ADS_HOST_INFO)); + if (ads_host == NULL) { + ads_free_host_list(ads_hosts_list, ans_cnt); + ads_free_host_list(ads_hosts_list2, addit_cnt); + return (NULL); + } + bzero(ads_host, sizeof (ADS_HOST_INFO)); + ads_host->name = strdup(ads_hosts_list[j].name); + if (ads_host->name == NULL) { + ads_free_host_list(ads_hosts_list, ans_cnt); + ads_free_host_list(ads_hosts_list2, addit_cnt); + return (NULL); + } + ads_host->ip_addr = ads_hosts_list2[i].ip_addr; + ads_host->port = ads_hosts_list[j].port; + *port = ads_host->port; + addr.s_addr = ads_host->ip_addr; + syslog(LOG_DEBUG, "smb_ads: Found ADS server: %s (%s)" + " from %s\n", ads_host->name, inet_ntoa(addr), ns); + ads_free_host_list(ads_hosts_list, ans_cnt); + ads_free_host_list(ads_hosts_list2, addit_cnt); + ads_set_host_info(ads_host); + return (ads_host); + } + ads_free_host_list(ads_hosts_list2, addit_cnt); + } else { + /* use DNS to get IP address of ads host */ + /* + * Shouldn't get here unless entries exist in + * DNS but DNS server did + * not put them in additional section of DNS reply packet. + */ + for (i = 0; i < ans_cnt; i++) { + h = gethostbyname(ads_hosts_list[i].name); + if (h == NULL) + continue; + if (h->h_addr == NULL) + continue; + (void) memcpy(&ads_hosts_list[i].ip_addr, + h->h_addr, sizeof (addr.s_addr)); + if (ads_ping(ads_hosts_list[i].ip_addr) == 0) { + ads_host = (ADS_HOST_INFO *) + malloc(sizeof (ADS_HOST_INFO)); + if (ads_host == NULL) { + ads_free_host_list(ads_hosts_list, + ans_cnt); + return (NULL); + } + bzero(ads_host, sizeof (ADS_HOST_INFO)); + ads_host->name = strdup(ads_hosts_list[i].name); + if (ads_host->name == NULL) { + ads_free_host_list(ads_hosts_list, + ans_cnt); + return (NULL); + } + ads_host->ip_addr = ads_hosts_list[i].ip_addr; + ads_host->port = ads_hosts_list[i].port; + *port = ads_host->port; + addr.s_addr = ads_host->ip_addr; + syslog(LOG_DEBUG, "smb_ads: Found ADS server" + " using DNS: %s (%s) port %d", + ads_host->name, inet_ntoa(addr), + ads_host->port); + ads_free_host_list(ads_hosts_list, ans_cnt); + ads_set_host_info(ads_host); + return (ads_host); + } + } + } + syslog(LOG_ERR, "smb_ads: Can't get IP for " + "ADS host or ADS host is down.\n"); + ads_free_host_list(ads_hosts_list, ans_cnt); + + *go_next = 1; + return (NULL); +} + +/* + * ads_convert_domain + * Converts a domain string into its distinguished name i.e. a unique + * name for an entry in the Directory Service. + * Memory is allocated + * for the new string. + * i.e. procom.com -> dc=procom,dc=com + * Parameters: + * s: fully qualified DNS domain string + * Returns: + * NULL if error + * DNS domain in LDAP DN string format + */ +static char * +ads_convert_domain(char *s) +{ + char *t, *s2, *t2; + int len, cnt; + + if (s == NULL || *s == 0) + return (NULL); + + cnt = 0; + t = s; + while (*t) { + if (*t++ == '.') { + cnt++; + } + } + + len = 3 + strlen(s) + cnt*3 + 1; + + s2 = (char *)malloc(len); + if (s2 == NULL) + return (NULL); + + bzero(s2, len); + + t = s2; + (void) strncpy(t, "dc=", 3); + t += 3; + t2 = s; + while (*s) { + if (*s == '.') { + if (t + 3 >= s2 + len - 1) { + syslog(LOG_ERR, "[ads_convert_domain] " + "buffer overrun for string " + "conversion of %s: tot buf " + "sz alloc: %d, last " + "written buf offset: %d\n", + t2, len, t+3-s2); + free(s2); + return (NULL); + } + (void) strncpy(t, ",dc=", 4); + t += 4; + s++; + } else { + if (t >= s2 + len - 1) { + syslog(LOG_ERR, "[ads_convert_domain] " + "buffer overrun for string " + "conversion of %s: tot buf " + "sz alloc: %d, last " + "written buf offset: %d\n", + t2, len, t-s2); + free(s2); + return (NULL); + } + *t++ = *s++; + } + } + *t = '\0'; + return (s2); +} + +/* + * ads_free_host_info + * Free the memory use by the global ads_host_info and set it to NULL. + */ +void +ads_free_host_info(void) +{ + (void) mutex_lock(&ads_mtx); + if (ads_host_info) { + free(ads_host_info->name); + free(ads_host_info); + ads_host_info = NULL; + } + (void) mutex_unlock(&ads_mtx); +} + +/* + * ads_open + * Open a LDAP connection to an ADS server. + * If ADS is enabled and the administrative username, password, container, and + * ADS domain are defined then query DNS to find an ADS server if this is the + * very first call to this routine. After an ADS server is found then this + * server will be used everytime this routine is called until the system is + * rebooted or the ADS server becomes unavailable then an ADS server will + * be queried again. The ADS server is always ping before an LDAP connection + * is made to it. If the pings fail then DNS is used once more to find an + * available ADS server. If the ping is successful then an LDAP connection + * is made to the ADS server. After the connection is made then an ADS handle + * is created to be returned. + * + * After the LDAP connection, the LDAP version will be set to 3 using + * ldap_set_option(). + * + * The ads_bind() routine is also called before the ADS handle is returned. + * Parameters: + * None + * Returns: + * NULL : can't connect to ADS server or other errors + * ADS_HANDLE* : handle to ADS server + */ +ADS_HANDLE * +ads_open(void) +{ + ADS_HANDLE *ah; + LDAP *ld; + int version = 3, ads_port, find_ads_retry; + char *adminUser, *password, *container; + char domain[MAXHOSTNAMELEN]; + int enable; + ADS_HOST_INFO *ads_host = NULL; + struct in_addr addr; + char *site, *service = NULL, *site_service = NULL; + int service_sz; + struct in_addr ns_list[MAXNS]; + int i, cnt, go_next; + + if (smb_getdomainname(domain, MAXHOSTNAMELEN) != 0) + return (NULL); + + smb_config_rdlock(); + enable = smb_config_getyorn(SMB_CI_ADS_ENABLE); + if (!enable) { + smb_config_unlock(); + return (NULL); + } + adminUser = smb_config_getstr(SMB_CI_ADS_USER); + if (adminUser == NULL || *adminUser == 0) { + syslog(LOG_ERR, "smb_ads: admin user is not set"); + smb_config_unlock(); + return (NULL); + } + password = smb_config_getstr(SMB_CI_ADS_PASSWD); + if (password == NULL || *password == 0) { + syslog(LOG_ERR, "smb_ads: admin user password is not set"); + smb_config_unlock(); + return (NULL); + } + container = smb_config_getstr(SMB_CI_ADS_USER_CONTAINER); + if (container == NULL || *container == 0) + container = "cn=Users"; + + site = smb_config_getstr(SMB_CI_ADS_SITE); + smb_config_unlock(); + + + + find_ads_retry = 0; +find_ads_host: + + ads_host = ads_get_host_info(); + if (!ads_host) { + if (site && *site != 0) { + service_sz = strlen("_ldap._tcp.._sites.dc._msdcs.") + + strlen(site) + strlen(domain) + 1; + site_service = (char *)malloc(service_sz); + if (site_service == NULL) { + syslog(LOG_ERR, "smb_ads: No ADS host found" + " malloc failed..."); + return (NULL); + } + (void) snprintf(site_service, service_sz, + "_ldap._tcp.%s._sites.dc._msdcs.%s", site, domain); + } + service_sz = strlen("_ldap._tcp.dc._msdcs.") + strlen(domain) + + 1; + service = (char *)malloc(service_sz); + if (service == NULL) { + syslog(LOG_ERR, "smb_ads: No ADS host found malloc" + " failed..."); + if (site_service != NULL) + (void) free(site_service); + return (NULL); + } + (void) snprintf(service, service_sz, "_ldap._tcp.dc._msdcs.%s", + domain); + + cnt = smb_get_nameservers(ns_list, MAXNS); + + ads_host = NULL; + go_next = 0; + for (i = 0; i < cnt; i++) { + if (site_service != NULL) { + ads_host = ads_find_host(inet_ntoa(ns_list[i]), + domain, &ads_port, site_service, &go_next); + } + if (ads_host == NULL) { + ads_host = ads_find_host(inet_ntoa(ns_list[i]), + domain, &ads_port, service, &go_next); + } + if (ads_host != NULL) + break; + if (go_next == 0) + break; + } + } + + if (site_service) + (void) free(site_service); + if (service) + (void) free(service); + + if (ads_host == NULL) { + syslog(LOG_ERR, "smb_ads: No ADS host found from " + "configured nameservers"); + return (NULL); + } + + if (ads_ping(ads_host->ip_addr) != 0) { + ads_free_host_info(); + ads_host = NULL; + if (find_ads_retry == 0) { + find_ads_retry = 1; + goto find_ads_host; + } + return (NULL); + } + + ah = (ADS_HANDLE *)malloc(sizeof (ADS_HANDLE)); + if (ah == NULL) { + return (NULL); + } + (void) memset(ah, 0, sizeof (ADS_HANDLE)); + + addr.s_addr = ads_host->ip_addr; + if ((ld = ldap_init((char *)inet_ntoa(addr), ads_host->port)) == NULL) { + syslog(LOG_ERR, "smb_ads: Could not open connection " + "to host: %s\n", ads_host->name); + ads_free_host_info(); + ads_host = NULL; + free(ah); + if (find_ads_retry == 0) { + find_ads_retry = 1; + goto find_ads_host; + } + return (NULL); + } + + if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version) + != LDAP_SUCCESS) { + syslog(LOG_ERR, "smb_ads: Could not set " + "LDAP_OPT_PROTOCOL_VERSION %d\n", version); + ads_free_host_info(); + free(ah); + (void) ldap_unbind(ld); + return (NULL); + } + + ah->ld = ld; + ah->user = strdup(adminUser); + ah->pwd = strdup(password); + ah->container = strdup(container); + ah->domain = strdup(domain); + + if ((ah->user == NULL) || (ah->pwd == NULL) || + (ah->container == NULL) || (ah->domain == NULL)) { + ads_close(ah); + return (NULL); + } + + ah->domain_dn = ads_convert_domain(domain); + if (ah->domain_dn == NULL) { + ads_close(ah); + return (NULL); + } + + ah->hostname = strdup(ads_host->name); + if (ah->hostname == NULL) { + ads_close(ah); + return (NULL); + } + if (site) { + ah->site = strdup(site); + if (ah->site == NULL) { + ads_close(ah); + return (NULL); + } + } else { + ah->site = NULL; + } + + if (ads_bind(ah) == -1) { + ads_close(ah); + return (NULL); + } + + return (ah); +} + +/* + * ads_close + * Close connection to ADS server and free memory allocated for ADS handle. + * LDAP unbind is called here. + * Parameters: + * ah: handle to ADS server + * Returns: + * void + */ +void +ads_close(ADS_HANDLE *ah) +{ + int len; + + if (ah == NULL) + return; + /* close and free connection resources */ + if (ah->ld) + (void) ldap_unbind(ah->ld); + + free(ah->user); + if (ah->pwd) { + len = strlen(ah->pwd); + /* zero out the memory that contains user's password */ + if (len > 0) + bzero(ah->pwd, len); + free(ah->pwd); + } + free(ah->container); + free(ah->domain); + free(ah->domain_dn); + free(ah->hostname); + free(ah->site); + free(ah); +} + +/* + * ads_display_stat + * Display error message for GSS-API routines. + * Parameters: + * maj: GSS major status + * min: GSS minor status + * Returns: + * None + */ +static void +ads_display_stat(OM_uint32 maj, OM_uint32 min) +{ + gss_buffer_desc msg; + OM_uint32 msg_ctx = 0; + OM_uint32 min2; + (void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID, + &msg_ctx, &msg); + syslog(LOG_ERR, "smb_ads: major status error: %s\n", (char *)msg.value); + (void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID, + &msg_ctx, &msg); + syslog(LOG_ERR, "smb_ads: minor status error: %s\n", (char *)msg.value); +} + +/* + * free_attr + * Free memory allocated when publishing a share. + * Parameters: + * addattrs: an array of LDAPMod pointers + * Returns: + * None + */ +static void +free_attr(LDAPMod *addattrs[]) +{ + int i; + for (i = 0; addattrs[i]; i++) { + free(addattrs[i]); + } +} + +/* + * ads_acquire_cred + * Called by ads_bind() to get a handle to administrative user's credential + * stored locally on the system. The credential is the TGT. If the attempt at + * getting handle fails then a second attempt will be made after getting a + * new TGT. + * Please look at ads_bind() for more information. + * + * Paramters: + * ah : handle to ADS server + * kinit_retry: if 0 then a second attempt will be made to get handle to the + * credential if the first attempt fails + * Returns: + * cred_handle: handle to the administrative user's credential (TGT) + * oid : contains Kerberos 5 object identifier + * kinit_retry: A 1 indicates that a second attempt has been made to get + * handle to the credential and no further attempts can be made + * -1 : error + * 0 : success + */ +static int +ads_acquire_cred(ADS_HANDLE *ah, gss_cred_id_t *cred_handle, gss_OID *oid, + int *kinit_retry) +{ + return (krb5_acquire_cred_kinit(ah->user, ah->pwd, cred_handle, oid, + kinit_retry, "ads")); +} + +/* + * ads_establish_sec_context + * Called by ads_bind() to establish a security context to an LDAP service on + * an ADS server. If the attempt at establishing the security context fails + * then a second attempt will be made by ads_bind() if a new TGT has not been + * already obtained in ads_acquire_cred. The second attempt, if allowed, will + * obtained a new TGT here and a new handle to the credential will also be + * obtained in ads_acquire_cred. LDAP SASL bind is used to send and receive + * the GSS tokens to and from the ADS server. + * Please look at ads_bind for more information. + * Paramters: + * ah : handle to ADS server + * cred_handle : handle to administrative user's credential (TGT) + * oid : Kerberos 5 object identifier + * kinit_retry : if 0 then a second attempt can be made to establish a + * security context with ADS server if first attempt fails + * Returns: + * gss_context : security context to ADS server + * sercred : encrypted ADS server's supported security layers + * do_acquire_cred: if 1 then a second attempt will be made to establish a + * security context with ADS server after getting a new + * handle to the user's credential + * kinit_retry : if 1 then a second attempt will be made to establish a + * a security context and no further attempts can be made + * -1 : error + * 0 : success + */ +static int +ads_establish_sec_context(ADS_HANDLE *ah, gss_ctx_id_t *gss_context, + gss_cred_id_t cred_handle, gss_OID oid, struct berval **sercred, + int *kinit_retry, int *do_acquire_cred) +{ + OM_uint32 maj, min, time_rec; + char service_name[ADS_MAXBUFLEN], *user_dn; + gss_buffer_desc send_tok, service_buf; + gss_name_t target_name; + gss_buffer_desc input; + gss_buffer_desc *inputptr; + struct berval cred; + OM_uint32 ret_flags; + int stat, len; + int gss_flags; + + /* + * 6 additional bytes for the "cn=,, " and the null terminator + */ + len = strlen(ah->user) + strlen(ah->container) + + strlen(ah->domain_dn) + 6; + + if ((user_dn = (char *)malloc(len)) == NULL) + return (-1); + + (void) snprintf(user_dn, len, "cn=%s,%s,%s", ah->user, ah->container, + ah->domain_dn); + + (void) snprintf(service_name, ADS_MAXBUFLEN, "ldap@%s", ah->hostname); + service_buf.value = service_name; + service_buf.length = strlen(service_name)+1; + if ((maj = gss_import_name(&min, &service_buf, + (gss_OID) gss_nt_service_name, + &target_name)) != GSS_S_COMPLETE) { + ads_display_stat(maj, min); + (void) gss_release_oid(&min, &oid); + free(user_dn); + return (-1); + } + + *gss_context = GSS_C_NO_CONTEXT; + *sercred = NULL; + inputptr = GSS_C_NO_BUFFER; + gss_flags = GSS_C_MUTUAL_FLAG; + do { + if (krb5_establish_sec_ctx_kinit(ah->user, ah->pwd, + cred_handle, gss_context, target_name, oid, + gss_flags, inputptr, &send_tok, + &ret_flags, &time_rec, kinit_retry, + do_acquire_cred, &maj, "ads") == -1) { + (void) gss_release_oid(&min, &oid); + (void) gss_release_name(&min, &target_name); + free(user_dn); + return (-1); + } + + cred.bv_val = send_tok.value; + cred.bv_len = send_tok.length; + if (*sercred) { + ber_bvfree(*sercred); + *sercred = NULL; + } + stat = ldap_sasl_bind_s(ah->ld, user_dn, "GSSAPI", + &cred, NULL, NULL, sercred); + if (stat != LDAP_SUCCESS && + stat != LDAP_SASL_BIND_IN_PROGRESS) { + /* LINTED - E_SEC_PRINTF_VAR_FMT */ + syslog(LOG_ERR, ldap_err2string(stat)); + (void) gss_release_oid(&min, &oid); + (void) gss_release_name(&min, &target_name); + (void) gss_release_buffer(&min, &send_tok); + free(user_dn); + return (-1); + } + input.value = (*sercred)->bv_val; + input.length = (*sercred)->bv_len; + inputptr = &input; + if (send_tok.length > 0) + (void) gss_release_buffer(&min, &send_tok); + } while (maj != GSS_S_COMPLETE); + + (void) gss_release_oid(&min, &oid); + (void) gss_release_name(&min, &target_name); + free(user_dn); + + return (0); +} + +/* + * ads_negotiate_sec_layer + * Call by ads_bind() to negotiate additional security layer for further + * communication after security context establishment. No additional security + * is needed so a "no security layer" is negotiated. The security layer is + * described in the SASL RFC 2478 and this step is needed for secure LDAP + * binding. LDAP SASL bind is used to send and receive the GSS tokens to and + * from the ADS server. + * Please look at ads_bind for more information. + * + * Paramters: + * ah : handle to ADS server + * gss_context: security context to ADS server + * sercred : encrypted ADS server's supported security layers + * Returns: + * -1 : error + * 0 : success + */ +static int +ads_negotiate_sec_layer(ADS_HANDLE *ah, gss_ctx_id_t gss_context, + struct berval *sercred) +{ + OM_uint32 maj, min; + gss_buffer_desc unwrap_inbuf, unwrap_outbuf; + gss_buffer_desc wrap_inbuf, wrap_outbuf; + int conf_state, sec_layer; + char auth_id[5]; + struct berval cred; + int stat; + gss_qop_t qt; + + /* check for server supported security layer */ + unwrap_inbuf.value = sercred->bv_val; + unwrap_inbuf.length = sercred->bv_len; + if ((maj = gss_unwrap(&min, gss_context, + &unwrap_inbuf, &unwrap_outbuf, + &conf_state, &qt)) != GSS_S_COMPLETE) { + ads_display_stat(maj, min); + if (sercred) + ber_bvfree(sercred); + return (-1); + } + sec_layer = *((char *)unwrap_outbuf.value); + (void) gss_release_buffer(&min, &unwrap_outbuf); + if (!(sec_layer & 1)) { + syslog(LOG_ERR, "smb_ads: ADS server does not support " + "no security layer!\n"); + if (sercred) ber_bvfree(sercred); + return (-1); + } + if (sercred) ber_bvfree(sercred); + + /* no security layer needed after successful binding */ + auth_id[0] = 0x01; + + /* byte 2-4: max client recv size in network byte order */ + auth_id[1] = 0x00; + auth_id[2] = 0x40; + auth_id[3] = 0x00; + wrap_inbuf.value = auth_id; + wrap_inbuf.length = 4; + conf_state = 0; + if ((maj = gss_wrap(&min, gss_context, conf_state, 0, &wrap_inbuf, + &conf_state, &wrap_outbuf)) != GSS_S_COMPLETE) { + ads_display_stat(maj, min); + return (-1); + } + + cred.bv_val = wrap_outbuf.value; + cred.bv_len = wrap_outbuf.length; + sercred = NULL; + stat = ldap_sasl_bind_s(ah->ld, NULL, "GSSAPI", &cred, NULL, NULL, + &sercred); + if (stat != LDAP_SUCCESS && stat != LDAP_SASL_BIND_IN_PROGRESS) { + /* LINTED - E_SEC_PRINTF_VAR_FMT */ + syslog(LOG_ERR, ldap_err2string(stat)); + (void) gss_release_buffer(&min, &wrap_outbuf); + return (-1); + } + + (void) gss_release_buffer(&min, &wrap_outbuf); + if (sercred) + ber_bvfree(sercred); + + return (0); +} + +/* + * ads_bind + * Use secure binding to bind to ADS server. + * Use GSS-API with Kerberos 5 as the security mechanism and LDAP SASL with + * Kerberos 5 as the security mechanisn to authenticate, obtain a security + * context, and securely bind an administrative user so that other LDAP + * commands can be used, i.e. add and delete. + * + * To obtain the security context, a Kerberos ticket-granting ticket (TGT) + * for the user is needed to obtain a ticket for the LDAP service. To get + * a TGT for the user, the username and password is needed. Once a TGT is + * obtained then it will be stored locally and used until it is expired. + * This routine will automatically obtained a TGT for the first time or when + * it expired. LDAP SASL bind is then finally used to send GSS tokens to + * obtain a security context for the LDAP service on the ADS server. If + * there is any problem getting the security context then a new TGT will be + * obtain to try getting the security context once more. + * + * After the security context is obtain and established, the LDAP SASL bind + * is used to negotiate an additional security layer. No further security is + * needed so a "no security layer" is negotiated. After this the security + * context can be deleted and further LDAP commands can be sent to the ADS + * server until a LDAP unbind command is issued to the ADS server. + * Paramaters: + * ah: handle to ADS server + * Returns: + * -1: error + * 0: success + */ +static int +ads_bind(ADS_HANDLE *ah) +{ + OM_uint32 min; + gss_cred_id_t cred_handle; + gss_ctx_id_t gss_context; + OM_uint32 maj; + gss_OID oid; + struct berval *sercred; + int kinit_retry, do_acquire_cred; + + kinit_retry = 0; + do_acquire_cred = 0; + acquire_cred: + + if (ads_acquire_cred(ah, &cred_handle, &oid, &kinit_retry)) + return (-1); + + if (ads_establish_sec_context(ah, &gss_context, cred_handle, + oid, &sercred, &kinit_retry, &do_acquire_cred)) { + (void) gss_release_cred(&min, &cred_handle); + if (do_acquire_cred) { + do_acquire_cred = 0; + goto acquire_cred; + } + return (-1); + } + + if (ads_negotiate_sec_layer(ah, gss_context, sercred)) { + (void) gss_release_cred(&min, &cred_handle); + (void) gss_delete_sec_context(&min, &gss_context, NULL); + return (-1); + } + + if ((maj = gss_release_cred(&min, &cred_handle)) + != GSS_S_COMPLETE) { + syslog(LOG_ERR, "smb_ads: Can't release credential handle\n"); + ads_display_stat(maj, min); + (void) gss_delete_sec_context(&min, &gss_context, NULL); + return (-1); + } + + if ((maj = gss_delete_sec_context(&min, &gss_context, NULL)) + != GSS_S_COMPLETE) { + syslog(LOG_ERR, "smb_ads: Can't delete security context\n"); + ads_display_stat(maj, min); + return (-1); + } + + return (0); +} + +/* + * ads_add_share + * Call by ads_publish_share to create share object in ADS. + * This routine specifies the attributes of an ADS LDAP share object. The first + * attribute and values define the type of ADS object, the share object. The + * second attribute and value define the UNC of the share data for the share + * object. The LDAP synchronous add command is used to add the object into ADS. + * The container location to add the object needs to specified. + * Parameters: + * ah : handle to ADS server + * adsShareName: name of share object to be created in ADS + * shareUNC : share name on NetForce + * adsContainer: location in ADS to create share object + * + * Returns: + * -1 : error + * 0 : success + */ +int +ads_add_share(ADS_HANDLE *ah, const char *adsShareName, + const char *unc_name, const char *adsContainer) +{ + LDAPMod *addattrs[3]; + char *tmp1[5], *tmp2[5]; + int j = -1; + char *share_dn; + char buf[ADS_MAXMSGLEN]; + + int len, ret; + + len = 5 + strlen(adsShareName) + strlen(adsContainer) + + strlen(ah->domain_dn) + 1; + + share_dn = (char *)malloc(len); + if (share_dn == NULL) + return (-1); + + (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName, + adsContainer, ah->domain_dn); + + addattrs[++j] = (LDAPMod *)malloc(sizeof (LDAPMod)); + addattrs[j]->mod_op = LDAP_MOD_ADD; + addattrs[j]->mod_type = "objectClass"; + tmp1[0] = "top"; + tmp1[1] = "leaf"; + tmp1[2] = "connectionPoint"; + tmp1[3] = "volume"; + tmp1[4] = 0; + addattrs[j]->mod_values = tmp1; + + addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod)); + addattrs[j]->mod_op = LDAP_MOD_ADD; + addattrs[j]->mod_type = "uNCName"; + + tmp2[0] = (char *)unc_name; + tmp2[1] = 0; + addattrs[j]->mod_values = tmp2; + + addattrs[++j] = 0; + + if ((ret = ldap_add_s(ah->ld, share_dn, addattrs)) != LDAP_SUCCESS) { + (void) snprintf(buf, ADS_MAXMSGLEN, + "ads_add_share: %s:", share_dn); + /* LINTED - E_SEC_PRINTF_VAR_FMT */ + syslog(LOG_ERR, ldap_err2string(ret)); + free_attr(addattrs); + free(share_dn); + return (ret); + } + free(share_dn); + free_attr(addattrs); + + (void) snprintf(buf, ADS_MAXMSGLEN, + "Share %s has been added to ADS container: %s.\n", adsShareName, + adsContainer); + syslog(LOG_DEBUG, "smb_ads: %s", buf); + + return (0); +} + +/* + * ads_del_share + * Call by ads_remove_share to remove share object from ADS. The container + * location to remove the object needs to specified. The LDAP synchronous + * delete command is used. + * Parameters: + * ah : handle to ADS server + * adsShareName: name of share object in ADS to be removed + * adsContainer: location of share object in ADS + * Returns: + * -1 : error + * 0 : success + */ +static int +ads_del_share(ADS_HANDLE *ah, const char *adsShareName, + const char *adsContainer) +{ + char *share_dn, buf[ADS_MAXMSGLEN]; + int len, ret; + + len = 5 + strlen(adsShareName) + strlen(adsContainer) + + strlen(ah->domain_dn) + 1; + + share_dn = (char *)malloc(len); + if (share_dn == NULL) + return (-1); + + (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName, + adsContainer, ah->domain_dn); + if ((ret = ldap_delete_s(ah->ld, share_dn)) != LDAP_SUCCESS) { + /* LINTED - E_SEC_PRINTF_VAR_FMT */ + syslog(LOG_ERR, ldap_err2string(ret)); + free(share_dn); + return (-1); + } + free(share_dn); + + (void) snprintf(buf, ADS_MAXMSGLEN, + "Share %s has been removed from ADS container: %s.\n", + adsShareName, adsContainer); + syslog(LOG_DEBUG, "smb_ads: %s", buf); + + return (0); +} + + +/* + * ads_escape_search_filter_chars + * + * This routine will escape the special characters found in a string + * that will later be passed to the ldap search filter. + * + * RFC 1960 - A String Representation of LDAP Search Filters + * 3. String Search Filter Definition + * If a value must contain one of the characters '*' OR '(' OR ')', + * these characters + * should be escaped by preceding them with the backslash '\' character. + * + * RFC 2252 - LDAP Attribute Syntax Definitions + * a backslash quoting mechanism is used to escape + * the following separator symbol character (such as "'", "$" or "#") if + * it should occur in that string. + */ +static int +ads_escape_search_filter_chars(const char *src, char *dst) +{ + int avail = ADS_MAXBUFLEN - 1; /* reserve a space for NULL char */ + + if (src == NULL || dst == NULL) + return (-1); + + while (*src) { + if (!avail) { + *dst = 0; + return (-1); + } + + switch (*src) { + case '\\': + case '\'': + case '$': + case '#': + case '*': + case '(': + case ')': + *dst++ = '\\'; + avail--; + /* fall through */ + + default: + *dst++ = *src++; + avail--; + } + } + + *dst = 0; + + return (0); +} + +/* + * ads_lookup_share + * The search filter is set to search for a specific share name in the + * specified ADS container. The LDSAP synchronous search command is used. + * Parameters: + * ah : handle to ADS server + * adsShareName: name of share object in ADS to be searched + * adsContainer: location of share object in ADS + * Returns: + * -1 : error + * 0 : not found + * 1 : found + */ +int +ads_lookup_share(ADS_HANDLE *ah, const char *adsShareName, + const char *adsContainer, char *unc_name) +{ + char *attrs[4], filter[ADS_MAXBUFLEN]; + char *share_dn; + int len, ret; + LDAPMessage *res; + char tmpbuf[ADS_MAXBUFLEN]; + + if (adsShareName == NULL || adsContainer == NULL) + return (-1); + + len = 5 + strlen(adsShareName) + strlen(adsContainer) + + strlen(ah->domain_dn) + 1; + + share_dn = (char *)malloc(len); + if (share_dn == NULL) + return (-1); + + (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName, + adsContainer, ah->domain_dn); + + res = NULL; + attrs[0] = "cn"; + attrs[1] = "objectClass"; + attrs[2] = "uNCName"; + attrs[3] = NULL; + + if (ads_escape_search_filter_chars(unc_name, tmpbuf) != 0) { + free(share_dn); + return (-1); + } + + (void) snprintf(filter, sizeof (filter), + "(&(objectClass=volume)(uNCName=%s))", tmpbuf); + + if ((ret = ldap_search_s(ah->ld, share_dn, + LDAP_SCOPE_BASE, filter, attrs, 0, &res)) != LDAP_SUCCESS) { + /* LINTED - E_SEC_PRINTF_VAR_FMT */ + syslog(LOG_ERR, ldap_err2string(ret)); + (void) ldap_msgfree(res); + free(share_dn); + return (0); + } + + (void) free(share_dn); + + /* no match is found */ + if (ldap_count_entries(ah->ld, res) == 0) { + (void) ldap_msgfree(res); + return (0); + } + + /* free the search results */ + (void) ldap_msgfree(res); + + return (1); +} + +/* + * ads_convert_directory + * Convert relative share directory to UNC to be appended to hostname. + * i.e. cvol/a/b -> cvol\a\b + */ +char * +ads_convert_directory(char *rel_dir) +{ + char *t, *s2; + int len; + + if (rel_dir == NULL) + return (NULL); + + len = strlen(rel_dir) + 1; + s2 = (char *)malloc(len); + if (s2 == NULL) + return (NULL); + + t = s2; + while (*rel_dir) { + if (*rel_dir == '/') { + *t++ = '\\'; + rel_dir++; + } else { + *t++ = *rel_dir++; + } + } + *t = '\0'; + return (s2); +} + +/* + * ads_publish_share + * Publish share into ADS. If a share name already exist in ADS in the same + * container then the existing share object is removed before adding the new + * share object. + * Parameters: + * ah : handle return from ads_open + * adsShareName: name of share to be added to ADS directory + * shareUNC : name of share on client, can be NULL to use the same name + * as adsShareName + * adsContainer: location for share to be added in ADS directory, ie + * ou=share_folder + * uncType : use UNC_HOSTNAME to use hostname for UNC, use UNC_HOSTADDR + * to use host ip addr for UNC. + * Returns: + * -1 : error + * 0 : success + */ +int +ads_publish_share(ADS_HANDLE *ah, const char *adsShareName, + const char *shareUNC, const char *adsContainer, const char *hostname) +{ + int ret; + char unc_name[ADS_MAXBUFLEN]; + + if (adsShareName == NULL || adsContainer == NULL) + return (-1); + + if (shareUNC == 0 || *shareUNC == 0) + shareUNC = adsShareName; + + if (ads_build_unc_name(unc_name, sizeof (unc_name), + hostname, shareUNC) < 0) { + syslog(LOG_DEBUG, "smb_ads: Cannot publish share '%s' " + "[missing UNC name]", shareUNC); + return (-1); + } + + ret = ads_lookup_share(ah, adsShareName, adsContainer, unc_name); + + switch (ret) { + case 1: + (void) ads_del_share(ah, adsShareName, adsContainer); + ret = ads_add_share(ah, adsShareName, unc_name, adsContainer); + break; + + case 0: + ret = ads_add_share(ah, adsShareName, unc_name, adsContainer); + if (ret == LDAP_ALREADY_EXISTS) { + syslog(LOG_DEBUG, "smb_ads: Cannot publish share '%s' " + "[name is already in use]", adsShareName); + ret = -1; + } + break; + + case -1: + default: + /* return with error code */ + ret = -1; + } + + return (ret); +} + +/* + * ads_remove_share + * Remove share from ADS. A search is done first before explicitly removing + * the share. + * Parameters: + * ah : handle return from ads_open + * adsShareName: name of share to be removed from ADS directory + * adsContainer: location for share to be removed from ADS directory, ie + * ou=share_folder + * Returns: + * -1 : error + * 0 : success + */ +int +ads_remove_share(ADS_HANDLE *ah, const char *adsShareName, const char *shareUNC, + const char *adsContainer, const char *hostname) +{ + int ret; + char unc_name[ADS_MAXBUFLEN]; + + if (adsShareName == NULL || adsContainer == NULL) + return (-1); + if (shareUNC == 0 || *shareUNC == 0) + shareUNC = adsShareName; + + if (ads_build_unc_name(unc_name, sizeof (unc_name), + hostname, shareUNC) < 0) { + syslog(LOG_DEBUG, "smb_ads: Unable to remove share '%s' from " + "ADS [missing UNC name]", shareUNC); + return (-1); + } + + ret = ads_lookup_share(ah, adsShareName, adsContainer, unc_name); + if (ret == 0) + return (0); + if (ret == -1) + return (-1); + + return (ads_del_share(ah, adsShareName, adsContainer)); +} + +/* + * ads_get_computer_dn + * + * Build the distinguish name for this system. + */ +static void +ads_get_computer_dn(ADS_HANDLE *ah, char *buf, size_t buflen) +{ + char hostname[MAXHOSTNAMELEN]; + + (void) smb_gethostname(hostname, MAXHOSTNAMELEN, 0); + (void) snprintf(buf, buflen, "cn=%s,cn=%s,%s", + hostname, ADS_COMPUTERS_CN, ah->domain_dn); +} + +static char * +ads_get_host_principal(char *fqhost) +{ + int len; + char *princ; + + if (!fqhost) + return (NULL); + + len = strlen(ADS_HOST_PREFIX) + strlen(fqhost) + 1; + princ = (char *)malloc(len); + + if (!princ) { + syslog(LOG_ERR, "ads_get_host_principal: resource shortage"); + return (NULL); + } + (void) snprintf(princ, len, "%s%s", ADS_HOST_PREFIX, + fqhost); + + return (princ); +} + +static char * +ads_get_host_principal_w_realm(char *princ, char *domain) +{ + int len; + char *realm; + char *princ_r; + + if (!princ || !domain) + return (NULL); + + realm = strdup(domain); + if (!realm) + return (NULL); + + (void) utf8_strupr(realm); + + len = strlen(princ) + 1 + strlen(realm) + 1; + princ_r = (char *)malloc(len); + if (!princ_r) { + syslog(LOG_ERR, "ads_get_host_principal_w_realm: resource" + " shortage"); + free(realm); + return (NULL); + } + + (void) snprintf(princ_r, len, "%s@%s", princ, realm); + free(realm); + + return (princ_r); +} + +/* + * ads_get_host_principals + * + * If fqhost is NULL, this function will attempt to obtain fully qualified + * hostname prior to generating the host principals. + */ +static int +ads_get_host_principals(char *fqhost, char *domain, char **princ, + char **princ_r) +{ + char hostname[MAXHOSTNAMELEN]; + + *princ = *princ_r = NULL; + + if (fqhost) { + (void) strlcpy(hostname, fqhost, MAXHOSTNAMELEN); + } else { + if (smb_getfqhostname(hostname, MAXHOSTNAMELEN) != 0) + return (-1); + } + + if ((*princ = ads_get_host_principal(hostname)) == NULL) { + return (-1); + } + + *princ_r = ads_get_host_principal_w_realm(*princ, domain); + if (*princ_r == NULL) { + free(*princ); + return (-1); + } + + return (0); +} + +/* + * ads_add_computer + * + * Returns 0 upon success. Otherwise, returns -1. + */ +static int +ads_add_computer(ADS_HANDLE *ah) +{ + LDAPMod *addattrs[7]; + char *oc_vals[6], *sam_val[2], *usr_val[2]; + char *svc_val[2], *ctl_val[2], *fqh_val[2]; + int j = -1; + int ret, usrctl_flags = 0; + char sam_acct[MAXHOSTNAMELEN + 1]; + char fqhost[MAXHOSTNAMELEN]; + char dn[ADS_DN_MAX]; + char *user_principal, *svc_principal; + char usrctl_buf[16]; + + if (smb_getfqhostname(fqhost, MAXHOSTNAMELEN) != 0) + return (-1); + + if (smb_gethostname(sam_acct, MAXHOSTNAMELEN, 0) != 0) + return (-1); + + (void) strlcat(sam_acct, "$", MAXHOSTNAMELEN + 1); + + if (ads_get_host_principals(fqhost, ah->domain, &svc_principal, + &user_principal) == -1) { + syslog(LOG_ERR, + "ads_add_computer: unable to get host principal"); + return (-1); + } + + ads_get_computer_dn(ah, dn, ADS_DN_MAX); + + addattrs[++j] = (LDAPMod *)malloc(sizeof (LDAPMod)); + addattrs[j]->mod_op = LDAP_MOD_ADD; + addattrs[j]->mod_type = "objectClass"; + oc_vals[0] = "top"; + oc_vals[1] = "person"; + oc_vals[2] = "organizationalPerson"; + oc_vals[3] = "user"; + oc_vals[4] = "computer"; + oc_vals[5] = 0; + addattrs[j]->mod_values = oc_vals; + + addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod)); + addattrs[j]->mod_op = LDAP_MOD_ADD; + addattrs[j]->mod_type = "sAMAccountName"; + + sam_val[0] = sam_acct; + sam_val[1] = 0; + addattrs[j]->mod_values = sam_val; + + + addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod)); + addattrs[j]->mod_op = LDAP_MOD_ADD; + addattrs[j]->mod_type = "userPrincipalName"; + + usr_val[0] = user_principal; + usr_val[1] = 0; + addattrs[j]->mod_values = usr_val; + + addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod)); + addattrs[j]->mod_op = LDAP_MOD_ADD; + addattrs[j]->mod_type = "servicePrincipalName"; + + svc_val[0] = svc_principal; + svc_val[1] = 0; + addattrs[j]->mod_values = svc_val; + + addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod)); + addattrs[j]->mod_op = LDAP_MOD_ADD; + addattrs[j]->mod_type = "userAccountControl"; + + usrctl_flags |= (ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | + ADS_USER_ACCT_CTL_PASSWD_NOTREQD | + ADS_USER_ACCT_CTL_ACCOUNTDISABLE); + + (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags); + ctl_val[0] = usrctl_buf; + ctl_val[1] = 0; + addattrs[j]->mod_values = ctl_val; + addattrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod)); + addattrs[j]->mod_op = LDAP_MOD_ADD; + addattrs[j]->mod_type = "dNSHostName"; + + fqh_val[0] = fqhost; + fqh_val[1] = 0; + addattrs[j]->mod_values = fqh_val; + addattrs[++j] = 0; + + if ((ret = ldap_add_s(ah->ld, dn, addattrs)) != LDAP_SUCCESS) { + syslog(LOG_ERR, "ads_add_computer: %s", ldap_err2string(ret)); + ret = -1; + } + + free_attr(addattrs); + free(user_principal); + free(svc_principal); + + return (ret); +} + +/* + * Delete an ADS computer account. + */ +static void +ads_del_computer(ADS_HANDLE *ah) +{ + char dn[ADS_DN_MAX]; + int rc; + + ads_get_computer_dn(ah, dn, ADS_DN_MAX); + + if ((rc = ldap_delete_s(ah->ld, dn)) != LDAP_SUCCESS) { + syslog(LOG_DEBUG, "ads_del_computer: %s", + ldap_err2string(rc)); + } +} + +/* + * ads_lookup_computer_n_attr + * + * Lookup the value of the specified attribute on the computer + * object. If the specified attribute can be found, its value is returned + * via 'val' parameter. + * + * 'attr' parameter can be set to NULL if you only attempt to + * see whether the computer object exists on AD or not. + * + * Return: + * 1 if both the computer and the specified attribute is found. + * 0 if either the computer or the specified attribute is not found. + * -1 on error. + */ +static int +ads_lookup_computer_n_attr(ADS_HANDLE *ah, char *attr, char **val) +{ + char *attrs[2], filter[ADS_MAXBUFLEN]; + LDAPMessage *res, *entry; + char **vals; + char tmpbuf[ADS_MAXBUFLEN]; + char my_hostname[MAXHOSTNAMELEN], sam_acct[MAXHOSTNAMELEN + 1]; + char dn[ADS_DN_MAX]; + + if (smb_gethostname(my_hostname, MAXHOSTNAMELEN, 0) != 0) + return (-1); + + (void) snprintf(sam_acct, sizeof (sam_acct), "%s$", my_hostname); + ads_get_computer_dn(ah, dn, ADS_DN_MAX); + + res = NULL; + attrs[0] = attr; + attrs[1] = NULL; + + if (ads_escape_search_filter_chars(sam_acct, tmpbuf) != 0) { + return (-1); + } + + (void) snprintf(filter, sizeof (filter), + "(&(objectClass=computer)(sAMAccountName=%s))", + tmpbuf); + + if (ldap_search_s(ah->ld, dn, LDAP_SCOPE_BASE, filter, attrs, 0, + &res) != LDAP_SUCCESS) { + (void) ldap_msgfree(res); + return (0); + } + + if (attr) { + /* no match for the specified attribute is found */ + if (ldap_count_entries(ah->ld, res) == 0) { + if (val) + *val = NULL; + + (void) ldap_msgfree(res); + return (0); + } + + entry = ldap_first_entry(ah->ld, res); + if (entry) { + vals = ldap_get_values(ah->ld, entry, attr); + if (!vals && val) { + *val = NULL; + (void) ldap_msgfree(res); + return (0); + } + + if (vals[0] != NULL && val) + *val = strdup(vals[0]); + } + } + + /* free the search results */ + (void) ldap_msgfree(res); + return (1); +} + +/* + * ads_find_computer + * + * Return: + * 1 if found. + * 0 if not found or encounters error. + */ +static int +ads_find_computer(ADS_HANDLE *ah) +{ + return (ads_lookup_computer_n_attr(ah, NULL, NULL) == 1); +} + +/* + * ads_modify_computer + * + * Modify the user account control attribute of an existing computer + * object on AD. + * + * Returns 0 on success. Otherwise, returns -1. + */ +static int +ads_modify_computer(ADS_HANDLE *ah, int des_only) +{ + LDAPMod *attrs[6]; + char *ctl_val[2]; + int j = -1; + int ret, usrctl_flags = 0; + char dn[ADS_DN_MAX]; + char usrctl_buf[16]; + + ads_get_computer_dn(ah, dn, ADS_DN_MAX); + + attrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod)); + attrs[j]->mod_op = LDAP_MOD_REPLACE; + attrs[j]->mod_type = "userAccountControl"; + + usrctl_flags |= (ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | + ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION | + ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD); + + if (des_only) + usrctl_flags |= ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY; + + (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags); + ctl_val[0] = usrctl_buf; + ctl_val[1] = 0; + attrs[j]->mod_values = ctl_val; + + attrs[++j] = 0; + + if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { + syslog(LOG_ERR, "ads_modify_computer: %s", + ldap_err2string(ret)); + ret = -1; + } + + free_attr(attrs); + return (ret); +} + +/* + * ads_lookup_computer_attr_kvno + * + * Lookup the value of the Kerberos version number attribute of the computer + * account. + */ +static krb5_kvno +ads_lookup_computer_attr_kvno(ADS_HANDLE *ah) +{ + char *val = NULL; + int kvno = 1; + + if (ads_lookup_computer_n_attr(ah, "ms-DS-KeyVersionNumber", + &val) == 1) { + if (val) { + kvno = atoi(val); + free(val); + } + } + + return (kvno); +} + +/* + * ads_gen_machine_passwd + * + * Returned a null-terminated machine password generated randomly + * from [0-9a-zA-Z] character set. In order to pass the password + * quality check (three character classes), an uppercase letter is + * used as the first character of the machine password. + */ +static int +ads_gen_machine_passwd(char *machine_passwd, int bufsz) +{ + char *data = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJK" + "LMNOPQRSTUVWXYZ"; + int datalen = strlen(data); + int i, data_idx; + + if (!machine_passwd || bufsz == 0) + return (-1); + + /* + * The decimal value of upper case 'A' is 65. Randomly pick + * an upper-case letter from the ascii table. + */ + machine_passwd[0] = (random() % 26) + 65; + for (i = 1; i < bufsz - 1; i++) { + data_idx = random() % datalen; + machine_passwd[i] = data[data_idx]; + } + + machine_passwd[bufsz - 1] = 0; + return (0); +} + +/* + * adjoin + * + * Besides the NT-4 style domain join (using MS-RPC), CIFS server also + * provides the domain join using Kerberos Authentication, Keberos + * Change & Set password, and LDAP protocols. Basically, adjoin + * operation would require the following tickets to be acquired for the + * the user account that is provided for the domain join. + * + * 1) a Keberos TGT ticket, + * 2) a ldap service ticket, and + * 3) kadmin/changpw service ticket + * + * The ADS client first sends a ldap search request to find out whether + * or not the workstation trust account already exists in the Active Directory. + * The existing computer object for this workstation will be removed and + * a new one will be added. The machine account password is randomly + * generated and set for the newly created computer object using KPASSD + * protocol (See RFC 3244). Once the password is set, our ADS client + * finalizes the machine account by modifying the user acount control + * attribute of the computer object. Kerberos keys derived from the machine + * account password will be stored locally in /etc/krb5/krb5.keytab file. + * That would be needed while acquiring Kerberos TGT ticket for the host + * principal after the domain join operation. + */ +adjoin_status_t +adjoin(char *machine_passwd, int len) +{ + ADS_HANDLE *ah = NULL; + krb5_enctype enctypes[10]; + krb5_context ctx = NULL; + krb5_principal krb5princ; + krb5_kvno kvno; + char *princ, *princ_r; + int des_only, delete = 1, fini_krbctx = 1; + adjoin_status_t rc = ADJOIN_SUCCESS; + struct stat fstat; + + char *idmap_ccache = "/var/run/idmap/ccache"; + + if ((ah = ads_open()) == NULL) + return (ADJOIN_ERR_GET_HANDLE); + + if (ads_gen_machine_passwd(machine_passwd, len) != 0) { + ads_close(ah); + return (ADJOIN_ERR_GEN_PASSWD); + } + + if (ads_find_computer(ah)) + ads_del_computer(ah); + + if (ads_add_computer(ah) != 0) { + ads_close(ah); + return (ADJOIN_ERR_ADD_TRUST_ACCT); + } + + /* + * Call library functions that can be used to get + * the list of encryption algorithms available on the system. + * (similar to what 'encrypt -l' CLI does). For now, + * unless someone has modified the configuration of the + * cryptographic framework (very unlikely), the following is the + * list of algorithms available on any system running Nevada + * by default. + */ + enctypes[0] = ENCTYPE_DES_CBC_CRC; + enctypes[1] = ENCTYPE_DES_CBC_MD5; + enctypes[2] = ENCTYPE_ARCFOUR_HMAC; + enctypes[3] = ENCTYPE_AES128_CTS_HMAC_SHA1_96; + + des_only = 0; + + /* + * If we are talking to a Longhorn server, we need to set up + * the msDS-SupportedEncryptionTypes attribute of the computer + * object accordingly + * + * The code to modify the msDS-SupportedEncryptionTypes can be + * added once we figure out why the Longhorn server rejects the + * SmbSessionSetup request sent by SMB redirector. + */ + + if (ads_get_host_principals(NULL, ah->domain, &princ, &princ_r) == -1) { + ads_del_computer(ah); + ads_close(ah); + return (ADJOIN_ERR_GET_HOST_PRINC); + } + + if (smb_krb5_ctx_init(&ctx) != 0) { + fini_krbctx = 0; + rc = ADJOIN_ERR_INIT_KRB_CTX; + goto adjoin_cleanup; + } + + if (smb_krb5_get_principal(ctx, princ_r, &krb5princ) != 0) { + rc = ADJOIN_ERR_GET_KRB_PRINC; + goto adjoin_cleanup; + } + + if (smb_krb5_setpwd(ctx, krb5princ, machine_passwd) != 0) { + rc = ADJOIN_ERR_KSETPWD; + goto adjoin_cleanup; + } + + kvno = ads_lookup_computer_attr_kvno(ah); + if (ads_modify_computer(ah, des_only) != 0) { + rc = ADJOIN_ERR_MOD_TRUST_ACCT; + goto adjoin_cleanup; + } + if (smb_krb5_write_keytab(ctx, krb5princ, "/etc/krb5/krb5.keytab", kvno, + machine_passwd, enctypes, 4) != 0) { + rc = ADJOIN_ERR_WRITE_KEYTAB; + goto adjoin_cleanup; + } + + /* Set IDMAP config */ + if (smb_config_set_idmap_domain(ah->domain) != 0) { + rc = ADJOIN_ERR_IDMAP_SET_DOMAIN; + goto adjoin_cleanup; + } + + if (smb_config_set_idmap_gc(ah->hostname) != 0) { + rc = ADJOIN_ERR_IDMAP_SET_GC; + goto adjoin_cleanup; + } + + /* Refresh IDMAP service */ + if (smb_config_refresh_idmap() != 0) { + rc = ADJOIN_ERR_IDMAP_REFRESH; + goto adjoin_cleanup; + } + + /* Remove the idmap ccache */ + if (stat(idmap_ccache, &fstat) == 0) { + if (remove(idmap_ccache) != 0) { + rc = ADJOIN_ERR_IDMAP_CCACHE; + goto adjoin_cleanup; + } + } + + delete = 0; +adjoin_cleanup: + if (delete) + ads_del_computer(ah); + + if (fini_krbctx) + smb_krb5_ctx_fini(ctx); + + ads_close(ah); + free(princ); + free(princ_r); + + return (rc); +} + +/* + * adjoin_report_err + * + * Display error message for the specific adjoin error code. + */ +char * +adjoin_report_err(adjoin_status_t status) +{ + if (status < 0 || status >= ADJOIN_NUM_STATUS) + return ("ADJOIN: unknown status"); + + return (adjoin_errmsg[status]); +} diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.h b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.h new file mode 100644 index 000000000000..af52f0ebfc9b --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.h @@ -0,0 +1,87 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_ADS_H +#define _SMBSRV_ADS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * UserAccountControl flags: manipulate user account properties. + * + * The hexadecimal value of the following property flags are based on MSDN + * article # 305144. + */ +#define ADS_USER_ACCT_CTL_SCRIPT 0x00000001 +#define ADS_USER_ACCT_CTL_ACCOUNTDISABLE 0x00000002 +#define ADS_USER_ACCT_CTL_HOMEDIR_REQUIRED 0x00000008 +#define ADS_USER_ACCT_CTL_LOCKOUT 0x00000010 +#define ADS_USER_ACCT_CTL_PASSWD_NOTREQD 0x00000020 +#define ADS_USER_ACCT_CTL_PASSWD_CANT_CHANGE 0x00000040 +#define ADS_USER_ACCT_CTL_ENCRYPTED_TEXT_PWD_ALLOWED 0x00000080 +#define ADS_USER_ACCT_CTL_TMP_DUP_ACCT 0x00000100 +#define ADS_USER_ACCT_CTL_NORMAL_ACCT 0x00000200 +#define ADS_USER_ACCT_CTL_INTERDOMAIN_TRUST_ACCT 0x00000800 +#define ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT 0x00001000 +#define ADS_USER_ACCT_CTL_SRV_TRUST_ACCT 0x00002000 +#define ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD 0x00010000 +#define ADS_USER_ACCT_CTL_MNS_LOGON_ACCT 0x00020000 +#define ADS_USER_ACCT_CTL_SMARTCARD_REQUIRED 0x00040000 +#define ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION 0x00080000 +#define ADS_USER_ACCT_CTL_NOT_DELEGATED 0x00100000 +#define ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY 0x00200000 +#define ADS_USER_ACCT_CTL_DONT_REQ_PREAUTH 0x00400000 +#define ADS_USER_ACCT_CTL_PASSWD_EXPIRED 0x00800000 +#define ADS_USER_ACCT_CTL_TRUSTED_TO_AUTH_FOR_DELEGATION 0x01000000 + +typedef struct ads_host_info_s { + char *name; /* fully qualified hostname */ + int port; /* ldap port */ + in_addr_t ip_addr; /* network byte order */ +} ADS_HOST_INFO; + +#define UNC_HOSTADDR 0 /* use ip addr in UNC */ +#define UNC_HOSTNAME 1 /* use hostname in UNC */ +#define ADS_PATH_SCRN_LEN 60 + +ADS_HOST_INFO *ads_find_host(char *, char *, int *, char *, int *); +char *ads_convert_directory(char *); + + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_ADS_H */ diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_browser.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_browser.c new file mode 100644 index 000000000000..853d1ff814b8 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_browser.c @@ -0,0 +1,1410 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#define SMB_SERVER_SIGNATURE 0xaa550415 + +/* + * Macro definitions: + */ +static char *lanman = MAILSLOT_LANMAN; +static char *browse = MAILSLOT_BROWSE; + +typedef struct server_info { + uint32_t type; + uint32_t signature; + char major; + char minor; + char hostname[NETBIOS_NAME_SZ]; + char comment[SMB_PI_MAX_COMMENT]; + char update_count; + struct name_entry name; +} server_info_t; + +#define BROWSER_NF_INVALID 0x00 +#define BROWSER_NF_VALID 0x01 + +typedef struct browser_netinfo { + uint32_t flags; + int next_announce; + int reps; + int interval; + server_info_t server; + mutex_t mtx; +} browser_netinfo_t; + +/* + * Local Data Definitions: + */ +static struct browser_netinfo smb_browser_info[SMB_PI_MAX_NETWORKS]; + +static void smb_browser_init(void); + +static inline browser_netinfo_t * +smb_browser_getnet(int net) +{ + browser_netinfo_t *subnet; + + if (net < smb_nic_get_num()) { + subnet = &smb_browser_info[net]; + (void) mutex_lock(&subnet->mtx); + if (subnet->flags & BROWSER_NF_VALID) + return (subnet); + } + + return (0); +} + +static inline void +smb_browser_putnet(browser_netinfo_t *netinfo) +{ + if (netinfo) + (void) mutex_unlock(&netinfo->mtx); +} + +/* + * 3. Browser Overview + * + * Hosts involved in the browsing process can be separated into two + * distinct groups, browser clients and browser servers (often referred to + * simply as "browsers"). + * + * A browser is a server which maintains information about servers - + * primarily the domain they are in and the services that they are running + * -- and about domains. Browsers may assume several different roles in + * their lifetimes, and dynamically switch between them. + * + * Browser clients are of two types: workstations and (non-browser) + * servers. In the context of browsing, workstations query browsers for the + * information they contain; servers supply browsers the information by + * registering with them. Note that, at times, browsers may themselves + * behave as browser clients and query other browsers. + * + * For the purposes of this specification, a domain is simply a name with + * which to associate a group of resources such as computers, servers and + * users. Domains allow a convenient means for browser clients to restrict + * the scope of a search when they query browser servers. Every domain has + * a "master" server called the Primary Domain Controller (PDC) that + * manages various activities within the domain. + * + * One browser for each domain on a subnet is designated the Local Master + * Browser for that domain. Servers in its domain on the subnet register + * with it, as do the Local Master Browsers for other domains on the + * subnet. It uses these registrations to maintain authoritative + * information about its domain on its subnet. If there are other subnets + * in the network, it also knows the name of the server running the + * domain's Domain Master Browser; it registers with it, and uses it to + * obtain information about the rest of the network (see below). + * + * Clients on a subnet query browsers designated as the Backup Browsers for + * the subnet (not the Master Browser). Backup Browsers maintain a copy of + * the information on the Local Master Browser; they get it by periodically + * querying the Local Master Browser for all of its information. Clients + * find the Backup Browsers by asking the Local Master Browser. Clients are + * expected to spread their queries evenly across Backup Browsers to + * balance the load. + * + * The Local Master Browser is dynamically elected automatically. Multiple + * Backup Browser Servers may exist per subnet; they are selected from + * among the potential browser servers by the Local Master Browser, which + * is configured to select enough to handle the expected query load. + * + * When there are multiple subnets, a Domain Master Browser is assigned + * the task of keeping the multiple subnets in synchronization. The Primary + * Domain Controller (PDC) always acts as the Domain Master Browser. The + * Domain Master Browser periodically acts as a client and queries all the + * Local Master Browsers for its domain, asking them for a list containing + * all the domains and all the servers in their domain known within their + * subnets; it merges all the replies into a single master list. This + * allows a Domain Master Browser server to act as a collection point for + * inter-subnet browsing information. Local Master Browsers periodically + * query the Domain Master Browser to retrieve the network-wide information + * it maintains. + * + * When a domain spans only a single subnet, there will not be any distinct + * Local Master Browser; this role will be handled by the Domain Master + * Browser. Similarly, the Domain Master Browser is always the Local Master + * Browser for the subnet it is on. + * + * When a browser client suspects that the Local Master Browser has failed, + * the client will instigate an election in which the browser servers + * participate, and some browser servers may change roles. + * + * Some characteristics of a good browsing mechanism include: + * . minimal network traffic + * . minimum server discovery time + * . minimum change discovery latency + * . immunity to machine failures + * + * Historically, Browser implementations had been very closely tied to + * NETBIOS and datagrams. The early implementations caused a lot of + * broadcast traffic. See Appendix D for an overview that presents how the + * Browser specification evolved. + * + * 4. Browsing Protocol Architecture + * + * This section first describes the how the browsing protocol is layered, + * then describes the roles of clients, servers, and browsers in the + * browsing subsystem. + * + * 4.1 Layering of Browsing Protocol Requests + * + * Most of the browser functionality is implemented using mailslots. + * Mailslots provide a mechanism for fast, unreliable unidirectional data + * transfer; they are named via ASCII "mailslot (path) name". Mailslots are + * implemented using the CIFS Transact SMB which is encapsulated in a + * NETBIOS datagram. Browser protocol requests are sent to browser specific + * mailslots using some browser-specific NETBIOS names. These datagrams can + * either be unicast or broadcast, depending on whether the NETBIOS name is + * a "unique name" or a "group name". Various data structures, which are + * detailed subsequently within this document, flow as the data portion of + * the Transact SMB. + * + * Here is an example of a generic browser SMB, showing how a browser + * request is encapsulated in a TRANSACT SMB request. Note that the PID, + * TID, MID, UID, and Flags are all 0 in mailslot requests. + * + * SMB: C transact, File = \MAILSLOT\BROWSE + * SMB: SMB Status = Error Success + * SMB: Error class = No Error + * SMB: Error code = No Error + * SMB: Header: PID = 0x0000 TID = 0x0000 MID = 0x0000 UID = 0x0000 + * SMB: Tree ID (TID) = 0 (0x0) + * SMB: Process ID (PID) = 0 (0x0) + * SMB: User ID (UID) = 0 (0x0) + * SMB: Multiplex ID (MID) = 0 (0x0) + * SMB: Flags Summary = 0 (0x0) + * SMB: Command = C transact + * SMB: Word count = 17 + * SMB: Word parameters + * SMB: Total parm bytes = 0 + * SMB: Total data bytes = 33 + * SMB: Max parm bytes = 0 + * SMB: Max data bytes = 0 + * SMB: Max setup words = 0 + * SMB: Transact Flags Summary = 0 (0x0) + * SMB: ...............0 = Leave session intact + * SMB: ..............0. = Response required + * SMB: Transact timeout = 0 (0x0) + * SMB: Parameter bytes = 0 (0x0) + * SMB: Parameter offset = 0 (0x0) + * SMB: Data bytes = 33 (0x21) + * SMB: Data offset = 86 (0x56) + * SMB: Setup word count = 3 + * SMB: Setup words + * SMB: Mailslot opcode = Write mailslot + * SMB: Transaction priority = 1 + * SMB: Mailslot class = Unreliable (broadcast) + * SMB: Byte count = 50 + * SMB: Byte parameters + * SMB: Path name = \MAILSLOT\BROWSE + * SMB: Transaction data + * SMB: Data: Number of data bytes remaining = 33 (0x0021) + * + * Note the SMB command is Transact, the opcode within the Transact SMB is + * Mailslot Write, and the browser data structure is carried as the + * Transact data. + * The Transaction data begins with an opcode, that signifies the operation + * and determines the size and structure of data that follows. This opcode + * is named as per one of the below: + * + * HostAnnouncement 1 + * AnnouncementRequest 2 + * RequestElection 8 + * GetBackupListReq 9 + * GetBackupListResp 10 + * BecomeBackup 11 + * DomainAnnouncment 12 + * MasterAnnouncement 13 + * LocalMasterAnnouncement 15 + * + * Browser datagrams are often referred to as simply browser frames. The + * frames are in particular, referred to by the name of the opcode within + * the Transaction data e.g. a GetBackupListReq browser frame, a + * RequestElection browser frame, etc. + * + * The structures that are sent as the data portion of the Transact SMB are + * described in section(s) 6.2 through 6.12 in this document. These + * structures are tightly packed, i.e. there are no intervening pad bytes + * in the structure, unless they are explicitly described as being there. + * All quantities are sent in native Intel format and multi-byte values are + * transmitted least significant byte first. + * + * Besides mailslots and Transaction SMBs, the other important piece of the + * browser architecture is the NetServerEnum2 request. This request that + * allows an application to interrogate a Browser Server and obtain a + * complete list of resources (servers, domains, etc) known to that Browser + * server. Details of the NetServerEnum2 request are presented in section + * 6.4. Some examples of the NetServerEnum2 request being used are when a + * Local Master Browser sends a NetServerEnum2 request to the Domain Master + * Browser and vice versa. Another example is when a browser client sends a + * NetServerEnum2 request to a Backup Browser server. + * + * 4.3 Non-Browser Server + * + * A non-browser server is a server that has some resource(s) or service(s) + * it wishes to advertise as being available using the browsing protocol. + * Examples of non-browser servers would be an SQL server, print server, + * etc. + * + * A non-browser server MUST periodically send a HostAnnouncement browser + * frame, specifying the type of resources or services it is advertising. + * Details are in section 6.5. + * + * A non-browser server SHOULD announce itself relatively frequently when + * it first starts up in order to make its presence quickly known to the + * browsers and thence to potential clients. The frequency of the + * announcements SHOULD then be gradually stretched, so as to minimize + * network traffic. Typically, non-browser servers announce themselves + * once every minute upon start up and then gradually adjust the frequency + * of the announcements to once every 12 minutes. + * + * A non-browser server SHOULD send a HostAnnouncement browser frame + * specifying a type of 0 just prior to shutting down, to allow it to + * quickly be removed from the list of available servers. + * + * A non-browser server MUST receive and process AnnouncementRequest frames + * from the Local Master Browser, and MUST respond with a HostAnnouncement + * frame, after a delay chosen randomly from the interval [0,30] seconds. + * AnnouncementRequests typically happen when a Local Master Browser starts + * up with an empty list of servers for the domain, and wants to fill it + * quickly. The 30 second range for responses prevents the Master Browser + * from becoming overloaded and losing replies, as well as preventing the + * network from being flooded with responses. + * + * 4.4 Browser Servers + * + * The following sections describe the roles of the various types of + * browser servers. + * + * 4.4.1 Potential Browser Server + * + * A Potential Browser server is a browser server that is capable of being + * a Backup Browser server or Master Browser server, but is not currently + * fulfilling either of those roles. + * + * A Potential Browser MUST set type SV_TYPE_POTENTIAL_BROWSER (see section + * 6.4.1) in its HostAnnouncement until it is ready to shut down. In its + * last HostAnnouncement frame before it shuts down, it SHOULD specify a + * type of 0. + * + * A Potential Browser server MUST receive and process BecomeBackup frames + * (see section 6.9) and become a backup browser upon their receipt. + * + * A Potential Browser MUST participate in browser elections (see section + * 6.8). + * + * 4.4.2 Backup Browser + * + * Backup Browser servers are a subset of the Potential Browsers that have + * been chosen by the Master Browser on their subnet to be the Backup + * Browsers for the subnet. + * + * A Backup Browser MUST set type SV_TYPE_BACKUP_BROWSER (see section + * 6.4.1) in its HostAnnouncement until it is ready to shut down. In its + * last HostAnnouncement frame before it shuts down, it SHOULD specify a + * type of 0. + * + * A Backup Browser MUST listen for a LocalMasterAnnouncement frame (see + * section 6.10) from the Local Master Browser, and use it to set the name + * of the Master Browser it queries for the server and domain lists. + * + * A Backup Browsers MUST periodically make a NetServerEnum2 request of + * the Master Browser on its subnet for its domain to get a list of servers + * in that domain, as well as a list of domains. The period is a + * configuration option balancing currency of the information with network + * traffic costs - a typical value is 15 minutes. + * + * A Backup Browser SHOULD force an election by sending a RequestElection + * frame (see section 6.7) if it does not get a response to its periodic + * NetServeEnum2 request to the Master Browser. + * + * A Backup Browser MUST receive and process NetServerEnum2 requests from + * browser clients, for its own domain and others. If the request is for a + * list of servers in its domain, or for a list of domains, it can answer + * from its internal lists. If the request is for a list of servers in a + * domain different than the one it serves, it sends a NetServerEnum2 + * request to the Domain Master Browser for that domain (which it can in + * find in its list of domains and their Domain Master Browsers). + * + * A Backup Browser MUST participate in browser elections (see section + * 6.8). + * + * 4.4.3 Master Browser + * + * Master Browsers are responsible for: + * . indicating it is a Master Browser + * . receiving server announcements and building a list of such servers + * and keeping it reasonably up-to-date. + * . returning lists of Backup Browsers to browser clients. + * . ensuring an appropriate number of Backup Browsers are available. + * . announcing their existence to other Master Browsers on their subnet, + * to the Domain Master Browser for their domain, and to all browsers in + * their domain on their subnet + * . forwarding requests for lists of servers on other domains to the + * Master Browser for that domain + * . keeping a list of domains in its subnet + * . synchronizing with the Domain Master Browser (if any) for its domain + * . participating in browser elections + * . ensuring that there is only one Master Browser on its subnet + * + * A Master Browser MUST set type SV_TYPE_MASTER_BROWSER (see section + * 6.4.1) in its HostAnnouncement until it is ready to shut down. In its + * last HostAnnouncement frame before it shuts down, it SHOULD specify a + * type of 0. + * + * A Master Browser MUST receive and process HostAnnouncement frames from + * servers, adding the server name and other information to its servers + * list; it must mark them as "local" entries. Periodically, it MUST check + * all local server entries to see if a server's HostAnnouncement has timed + * out (no HostAnnouncement received for three times the periodicity the + * server gave in the last received HostAnnouncement) and remove timed-out + * servers from its list. + * + * A Master Browser MUST receive and process DomainAnnouncement frames (see + * section 6.12) and maintain the domain names and their associated (Local) + * Master Browsers in its internal domain list until they time out; it must + * mark these as "local" entries. Periodically, it MUST check all local + * domain entries to see if a server's DomainAnnouncement has timed out (no + * DomainAnnouncement received for three times the periodicity the server + * gave in the last received DomainAnnouncement) and remove timed-out + * servers from its list. + * + * A Master Browser MUST receive and process GetBackupListRequest frames + * from clients, returning GetBackupListResponse frames containing a list + * of the Backup Servers for its domain. + * + * A Master Browser MUST eventually send BecomeBackup frames (see section + * 6.9) to one or more Potential Browser servers to increase the number of + * Backup Browsers if there are not enough Backup Browsers to handle the + * anticipated query load. Note: possible good times for checking for + * sufficient backup browsers are after being elected, when timing out + * server HostAnnouncements, and when receiving a server's HostAnnouncement + * for the first time. + * + * A Master Browser MUST periodically announce itself and the domain it + * serves to other (Local) Master Browsers on its subnet, by sending a + * DomainAnnouncement frame (see section 6.12) to its subnet. + * + * A Master Browser MUST send a MasterAnnouncement frame (see section 6.11) + * to the Domain Master Browser after it is first elected, and periodically + * thereafter. This informs the Domain Master Browser of the presence of + * all the Master Browsers. + * + * A Master Browser MUST periodically announce itself to all browsers for + * its domain on its subnet by sending a LocalMasterAnnouncement frame (see + * section 6.10). + * + * A Master Browser MUST receive and process NetServerEnum2 requests from + * browser clients, for its own domain and others. If the request is for a + * list of servers in its domain, or for a list of domains, it can answer + * from its internal lists. Entries in its list marked "local" MUST have + * the SV_TYPE_LOCAL_LIST_ONLY bit set in the returned results; it must be + * clear for all other entries. If the request is for a list of servers in + * a domain different than the one it serves, it sends a NetServerEnum2 + * request to the Domain Master Browser for that domain (which it can in + * find in its list of domains and their Domain Master Browsers). + * + * Note: The list of servers that the Master Browser maintains and + * returns to the Backup Browsers, is limited in size to 64K of + * data. This will limit the number of systems that can be in a + * browse list in a single workgroup or domain to approximately two + * thousand systems. + * + * A Master Browser SHOULD request all servers to register with it by + * sending an AnnouncementRequest frame, if, on becoming the Master Browser + * by winning an election, its server list is empty. Otherwise, clients + * might get an incomplete list of servers until the servers' periodic + * registrations fill the server list. + * + * If the Master Browser on a subnet is not the Primary Domain Controller + * (PDC), then it is a Local Master Browser. + * + * A Local Master Browser MUST periodically synchronize with the Domain + * Master Browser (which is the PDC). This synchronization is performed by + * making a NetServerEnum2 request to the Domain Master Browser and merging + * the results with its list of servers and domains. An entry from the + * Domain Master Browser should be marked "non-local", and must not + * overwrite an entry with the same name marked "local". The Domain Master + * Browser is located as specified in Appendix B. + * + * A Master Browser MUST participate in browser elections (see section + * 6.8). + * + * A Master Browser MUST, if it receives a HostAnnouncement, + * DomainAnnouncement, or LocalMasterAnnouncement frame another system that + * claims to be the Master Browser for its domain, demote itself from + * Master Browser and force an election. This ensures that there is only + * ever one Master Browser in each workgroup or domain. + * + * A Master Browser SHOULD, if it loses an election, become a Backup + * Browser (without being told to do so by the new Master Browser). Since + * it has more up-to-date information in its lists than a Potential + * Browser, it is more efficient to have it be a Backup Browser than to + * promote a Potential Browser. + * + * 4.4.3.1 Preferred Master Browser + * + * A Preferred Master Browser supports exactly the same protocol elements + * as a Potential Browser, except as follows. + * + * A Preferred Master Browser MUST always force an election when it starts + * up. + * + * A Preferred Master Browser MUST participate in browser elections (see + * section 6.8). + * + * A Preferred Master Browser MUST set the Preferred Master bit in the + * RequestElection frame (see section 6.7) to bias the election in its + * favor. + * + * A Preferred Master Browser SHOULD, if it loses an election, + * automatically become a Backup Browser, without being told to do so by + * the Master Browser. + * + * 4.4.4 Domain Master Browser + * + * Since the Domain Master Browser always runs on the PDC, it must + * implement all the protocols required of a PDC in addition to the + * browsing protocol, and that is way beyond the scope of this + * specification. + * + * 5. Mailslot Protocol Specification + * + * The only transaction allowed to a mailslot is a mailslot write. Mailslot + * writes requests are encapsulated in TRANSACT SMBs. The following table + * shows the interpretation of the TRANSACT SMB parameters for a mailslot + * transaction: + * + * Name Value Description + * Command SMB_COM_TRANSACTION + * Name STRING name of mail slot to write; + * must start with "\\MAILSLOT\\" + * SetupCount 3 Always 3 for mailslot writes + * Setup[0] 1 Command code == write mailslot + * Setup[1] Ignored + * Setup[2] Ignored + * TotalDataCount n Size of data in bytes to write to + * the mailslot + * Data[ n ] The data to write to the mailslot + * + */ + +/* + * SMB: C transact, File = \MAILSLOT\BROWSE + * SMB: SMB Status = Error Success + * SMB: Error class = No Error + * SMB: Error code = No Error + * SMB: Header: PID = 0x0000 TID = 0x0000 MID = 0x0000 UID = 0x0000 + * SMB: Tree ID (TID) = 0 (0x0) + * SMB: Process ID (PID) = 0 (0x0) + * SMB: User ID (UID) = 0 (0x0) + * SMB: Multiplex ID (MID) = 0 (0x0) + * SMB: Flags Summary = 0 (0x0) + * SMB: Command = C transact + * SMB: Word count = 17 + * SMB: Word parameters + * SMB: Total parm bytes = 0 + * SMB: Total data bytes = 33 + * SMB: Max parm bytes = 0 + * SMB: Max data bytes = 0 + * SMB: Max setup words = 0 + * SMB: Transact Flags Summary = 0 (0x0) + * SMB: ...............0 = Leave session intact + * SMB: ..............0. = Response required + * SMB: Transact timeout = 0 (0x0) + * SMB: Parameter bytes = 0 (0x0) + * SMB: Parameter offset = 0 (0x0) + * SMB: Data bytes = 33 (0x21) + * SMB: Data offset = 86 (0x56) + * SMB: Setup word count = 3 + * SMB: Setup words + * SMB: Mailslot opcode = Write mailslot + * SMB: Transaction priority = 1 + * SMB: Mailslot class = Unreliable (broadcast) + * SMB: Byte count = 50 + * SMB: Byte parameters + * SMB: Path name = \MAILSLOT\BROWSE + * SMB: Transaction data + * SMB: Data: Number of data bytes remaining = 33 (0x0021) + * + * 5. Mailslot Protocol Specification + * + * The only transaction allowed to a mailslot is a mailslot write. Mailslot + * writes requests are encapsulated in TRANSACT SMBs. The following table + * shows the interpretation of the TRANSACT SMB parameters for a mailslot + * transaction: + * + * Name Value Description + * Command SMB_COM_TRANSACTION + * Name STRING name of mail slot to write; + * must start with "\MAILSLOT\" + * SetupCount 3 Always 3 for mailslot writes + * Setup[0] 1 Command code == write mailslot + * Setup[1] Ignored + * Setup[2] Ignored + * TotalDataCount n Size of data in bytes to write to + * the mailslot + * Data[ n ] The data to write to the mailslot + * + * Magic 0xFF 'S' 'M' 'B' + * smb_com a byte, the "first" command + * Error a 4-byte union, ignored in a request + * smb_flg a one byte set of eight flags + * smb_flg2 a two byte set of 16 flags + * . twelve reserved bytes, have a role + * in connectionless transports (IPX, UDP?) + * smb_tid a 16-bit tree ID, a mount point sorta, + * 0xFFFF is this command does not have + * or require a tree context + * smb_pid a 16-bit process ID + * smb_uid a 16-bit user ID, specific to this "session" + * and mapped to a system (bona-fide) UID + * smb_mid a 16-bit multiplex ID, used to differentiate + * multiple simultaneous requests from the same + * process (pid) (ref RPC "xid") + */ + +int +smb_browser_load_transact_header(unsigned char *buffer, int maxcnt, + int data_count, int reply, char *mailbox) +{ + smb_msgbuf_t mb; + int mailboxlen; + char *fmt; + int result; + short class = (reply == ONE_WAY_TRANSACTION) ? 2 : 0; + + /* + * If the mailboxlen is an even number we need to pad the + * header so that the data starts on a word boundary. + */ + fmt = "Mb4.bw20.bwwwwb.wl2.wwwwb.wwwws"; + mailboxlen = strlen(mailbox) + 1; + + if ((mailboxlen & 0x01) == 0) { + ++mailboxlen; + fmt = "Mb4.bw20.bwwwwb.wl2.wwwwb.wwwws."; + } + + bzero(buffer, maxcnt); + smb_msgbuf_init(&mb, buffer, maxcnt, 0); + + result = smb_msgbuf_encode(&mb, fmt, + SMB_COM_TRANSACTION, /* Command */ + 0x18, + 0x3, + 17, /* Count of parameter words */ + 0, /* Total Parameter words sent */ + data_count, /* Total Data bytes sent */ + 2, /* Max Parameters to return */ + 0, /* Max data bytes to return */ + 0, /* Max setup bytes to return */ + reply, /* No reply */ + 0xffffffff, /* Timeout */ + 0, /* Parameter bytes sent */ + 0, /* Parameter offset */ + data_count, /* Data bytes sent */ + 69 + mailboxlen, /* Data offset */ + 3, /* Setup word count */ + 1, /* Setup word[0] */ + 0, /* Setup word[1] */ + class, /* Setup word[2] */ + mailboxlen + data_count, /* Total request bytes */ + mailbox); /* Mailbox address */ + + smb_msgbuf_term(&mb); + return (result); +} + +/* + * smb_net_id + * + * Lookup for the given IP in the NICs info table. + * If it finds a matching entry it'll return the index, + * otherwise returns -1. + * + * SMB network table and SMB browser info table share + * the same index. + */ +int +smb_net_id(uint32_t ipaddr) +{ + uint32_t myaddr, mask; + int net, smb_nc_cnt; + + smb_nc_cnt = smb_nic_get_num(); + for (net = 0; net < smb_nc_cnt; net++) { + net_cfg_t cfg; + if (smb_nic_get_byind(net, &cfg) == NULL) + break; + mask = cfg.mask; + myaddr = cfg.ip; + if ((ipaddr & mask) == (myaddr & mask)) + return (net); + } + + return (-1); +} + +/* + * smb_browser_get_srvname + * + */ +struct name_entry * +smb_browser_get_srvname(unsigned short netid) +{ + if (netid < smb_nic_get_num()) + return (&(smb_browser_info[netid].server.name)); + + return (NULL); +} + +static int +smb_browser_addr_of_subnet(struct name_entry *name, int subnet, + struct name_entry *result) +{ + uint32_t ipaddr, mask, saddr; + struct addr_entry *addr; + int smb_nc_cnt; + net_cfg_t cfg; + + smb_nc_cnt = smb_nic_get_num(); + if ((name == 0) || subnet >= smb_nc_cnt) + return (-1); + + if (smb_nic_get_byind(subnet, &cfg) == NULL) + return (-1); + ipaddr = cfg.ip; + mask = cfg.mask; + + *result = *name; + addr = &name->addr_list; + do { + saddr = addr->sin.sin_addr.s_addr; + if ((saddr & mask) == (ipaddr & mask)) { + *result = *name; + result->addr_list = *addr; + result->addr_list.forw = result->addr_list.back = + &result->addr_list; + return (0); + } + addr = addr->forw; + } while (addr != &name->addr_list); + + return (-1); +} + + +static int +smb_browser_bcast_addr_of_subnet(struct name_entry *name, int net, + struct name_entry *result) +{ + uint32_t broadcast; + int smb_nc_cnt; + net_cfg_t cfg; + + smb_nc_cnt = smb_nic_get_num(); + if (net >= smb_nc_cnt) + return (-1); + + if (name != 0 && name != result) + *result = *name; + + if (smb_nic_get_byind(net, &cfg) == NULL) + return (-1); + + broadcast = cfg.broadcast; + result->addr_list.sin.sin_family = AF_INET; + result->addr_list.sinlen = sizeof (result->addr_list.sin); + result->addr_list.sin.sin_addr.s_addr = broadcast; + result->addr_list.sin.sin_port = htons(DGM_SRVC_UDP_PORT); + result->addr_list.forw = result->addr_list.back = &result->addr_list; + return (0); +} + +/* + * 6.5 HostAnnouncement Browser Frame + * + * To advertise its presence, i.e. to publish itself as being available, a + * non-browser server sends a HostAnnouncement browser frame. If the server + * is a member of domain "D", this frame is sent to the NETBIOS unique name + * D(1d) and mailslot "\\MAILSLOT\\BROWSE". The definition of the + * HostAnnouncement frame is: + * + * struct { + * unsigned short Opcode; + * unsigned char UpdateCount; + * uint32_t Periodicity; + * unsigned char ServerName[]; + * unsigned char VersionMajor; + * unsigned char VersionMinor; + * uint32_t Type; + * uint32_t Signature; + * unsigned char Comment[]; + * } + * + * where: + * Opcode - Identifies this structure as a browser server + * announcement and is defined as HostAnnouncement with a + * value of decimal 1. + * + * UpdateCount - must be sent as zero and ignored on receipt. + * + * Periodicity - The announcement frequency of the server (in + * seconds). The server will be removed from the browse list + * if it has not been heard from in 3X its announcement + * frequency. In no case will the server be removed from the + * browse list before the period 3X has elapsed. Actual + * implementations may take more than 3X to actually remove + * the server from the browse list. + * + * ServerName - Null terminated ASCII server name (up to 16 bytes + * in length). + * + * VersionMajor - The major version number of the OS the server + * is running. it will be returned by NetServerEnum2. + * + * VersionMinor - The minor version number of the OS the server + * is running. This is entirely informational and does not + * have any significance for the browsing protocol. + * + * Type - Specifies the type of the server. The server type bits + * are specified in the NetServerEnum2 section. + * + * Signature - The browser protocol minor version number in the + * low 8 bits, the browser protocol major version number in + * the next higher 8 bits and the signature 0xaa55 in the + * high 16 bits of this field. Thus, for this version of the + * browser protocol (1.15) this field has the value + * 0xaa55010f. This may used to isolate browser servers that + * are running out of revision browser software; otherwise, + * it is ignored. + * + * Comment - Null terminated ASCII comment for the server. + * Limited to 43 bytes. + * + * When a non-browser server starts up, it announces itself in the manner + * described once every minute. The frequency of these statements is + * gradually stretched to once every 12 minutes. + * + * Note: older non-browser servers in a domain "D" sent HostAnnouncement + * frames to the NETBIOS group name D(00). Non-Browser servers supporting + * version 1.15 of the browsing protocol SHOULD NOT use this NETBIOS name, + * but for backwards compatibility Master Browsers MAY receive and process + * HostAnnouncement frames on this name as described above for D(1d). + */ + +void +smb_browser_send_HostAnnouncement(int net, int32_t next_announcement, + struct addr_entry *addr, char suffix) +{ + smb_msgbuf_t mb; + int offset, announce_len, data_length; + struct name_entry dest_name; + struct name_entry server_name; + struct browser_netinfo *subnet; + server_info_t *server; + unsigned char *buffer; + uint32_t type; + char resource_domain[SMB_PI_MAX_DOMAIN]; + + syslog(LOG_DEBUG, "smb_browse: send_HostAnnouncement(%d)", net); + + smb_config_rdlock(); + (void) strlcpy(resource_domain, + smb_config_getstr(SMB_CI_DOMAIN_NAME), SMB_PI_MAX_DOMAIN); + (void) utf8_strupr(resource_domain); + smb_config_unlock(); + + if (addr == 0) { + /* Local master Browser */ + smb_init_name_struct( + (unsigned char *)resource_domain, suffix, + 0, 0, 0, 0, 0, &dest_name); + if (smb_browser_bcast_addr_of_subnet(0, net, &dest_name) < 0) + return; + } else { + smb_init_name_struct( + (unsigned char *)resource_domain, suffix, + 0, 0, 0, 0, 0, &dest_name); + dest_name.addr_list = *addr; + dest_name.addr_list.forw = dest_name.addr_list.back = + &dest_name.addr_list; + } + + /* give some extra room */ + buffer = (unsigned char *)malloc(MAX_DATAGRAM_LENGTH * 2); + if (buffer == 0) { + syslog(LOG_ERR, "HostAnnouncement: resource shortage"); + return; + } + + subnet = smb_browser_getnet(net); + if (subnet == 0) { + free(buffer); + return; + } + + server = &subnet->server; + + data_length = 1 + 1 + 4 + 16 + 1 + 1 + 4 + 4 + + strlen(server->comment) + 1; + + if ((offset = smb_browser_load_transact_header(buffer, + MAX_DATAGRAM_LENGTH, data_length, ONE_WAY_TRANSACTION, + browse)) < 0) { + + smb_browser_putnet(subnet); + free(buffer); + return; + } + + /* + * A non-browser server SHOULD send a HostAnnouncement browser frame + * specifying a type of 0 just prior to shutting down, to allow it to + * quickly be removed from the list of available servers. + */ + type = (nb_status.state & NETBIOS_SHUTTING_DOWN) ? 0 : server->type; + + smb_msgbuf_init(&mb, buffer + offset, MAX_DATAGRAM_LENGTH - offset, 0); + announce_len = smb_msgbuf_encode(&mb, "bbl16cbblls", + (char)HOST_ANNOUNCEMENT, /* Announcement opcode */ + (char)++subnet->server.update_count, + next_announcement * 60000, /* Periodicity in MilliSeconds */ + server->hostname, /* Server name */ + server->major, /* our major version */ + server->minor, /* our minor version */ + type, /* server type */ + server->signature, /* Signature */ + server->comment); /* Let 'em know */ + + server_name = server->name; + smb_browser_putnet(subnet); + + if (announce_len > 0) + (void) smb_netbios_datagram_send(&server_name, &dest_name, + buffer, offset + announce_len); + + free(buffer); + smb_msgbuf_term(&mb); +} + +void +smb_browser_process_AnnouncementRequest(struct datagram *datagram, + char *mailbox) +{ + struct browser_netinfo *subnet; + unsigned int next_announcement; + uint32_t delay = random() % 29; /* in seconds */ + int net; + + if (strcmp(mailbox, lanman) != 0) { + syslog(LOG_DEBUG, "smb_browse: Wrong Mailbox (%s)", mailbox); + return; + } + + net = smb_net_id(datagram->src.addr_list.sin.sin_addr.s_addr); + if (net < 0) { + /* We don't know who this is so ignore it... */ + return; + } + + (void) sleep(delay); + + subnet = smb_browser_getnet(net); + if (subnet) { + next_announcement = subnet->next_announce * 60 * 1000; + smb_browser_putnet(subnet); + smb_browser_send_HostAnnouncement(net, next_announcement, + &datagram->src.addr_list, 0x1D); + } +} + +void * +smb_browser_dispatch(void *arg) +{ + struct datagram *datagram = (struct datagram *)arg; + smb_msgbuf_t mb; + int rc; + unsigned char command; + unsigned char parameter_words; + unsigned short total_parameter_words; + unsigned short total_data_count; + unsigned short max_parameters_to_return; + unsigned short max_data_to_return; + unsigned char max_setup_bytes_to_return; + unsigned short reply; + unsigned short parameter_bytes_sent; + unsigned short parameter_offset; + unsigned short data_bytes_sent; + unsigned short data_offset; + unsigned char setup_word_count; + unsigned short setup_word_0; + unsigned short setup_word_1; + unsigned short setup_word_2; + unsigned short total_request_bytes; + char *mailbox; + unsigned char message_type; + unsigned char *data; + int datalen; + + syslog(LOG_DEBUG, "smb_browse: packet_received"); + + smb_msgbuf_init(&mb, datagram->data, datagram->data_length, 0); + rc = smb_msgbuf_decode(&mb, "Mb27.bwwwwb.w6.wwwwb.wwwws", + &command, /* Command */ + ¶meter_words, /* Count of parameter words */ + &total_parameter_words, /* Total Parameter words sent */ + &total_data_count, /* Total Data bytes sent */ + &max_parameters_to_return, /* Max Parameters to return */ + &max_data_to_return, /* Max data bytes to return */ + &max_setup_bytes_to_return, /* Max setup bytes to return */ + &reply, /* No reply */ + ¶meter_bytes_sent, /* Parameter bytes sent */ + ¶meter_offset, /* Parameter offset */ + &data_bytes_sent, /* Data bytes sent */ + &data_offset, /* Data offset */ + &setup_word_count, /* Setup word count */ + &setup_word_0, /* Setup word[0] */ + &setup_word_1, /* Setup word[1] */ + &setup_word_2, /* Setup word[2] */ + &total_request_bytes, /* Total request bytes */ + &mailbox); /* Mailbox address */ + + if (rc < 0) { + syslog(LOG_ERR, "smb_browser_dispatch: decode error"); + smb_msgbuf_term(&mb); + free(datagram); + return (0); + } + + data = &datagram->data[data_offset]; + datalen = datagram->data_length - data_offset; + + /* + * The PDC location protocol, i.e. anything on the \\NET + * mailslot, is handled by the smb_netlogon module. + */ + if (strncasecmp("\\MAILSLOT\\NET\\", mailbox, 14) == 0) { + smb_netlogon_receive(datagram, mailbox, data, datalen); + smb_msgbuf_term(&mb); + free(datagram); + return (0); + } + + /* + * If it's not a netlogon message, assume it's a browser request. + * This is not the most elegant way to extract the command byte + * but at least we no longer use it to get the netlogon opcode. + */ + message_type = datagram->data[data_offset]; + + switch (message_type) { + case ANNOUNCEMENT_REQUEST : + smb_browser_process_AnnouncementRequest(datagram, mailbox); + break; + + default: + syslog(LOG_DEBUG, "smb_browse: invalid message_type(%d, %x)", + message_type, message_type); + break; + } + + smb_msgbuf_term(&mb); + free(datagram); + return (0); +} + + +/* + * 11.1 Registered unique names + * + * (00) + * This name is used by all servers and clients to receive second + * class mailslot messages. A system must add this name in order to + * receive mailslot messages. The only browser requests that should + * appear on this name are BecomeBackup, GetBackupListResp, + * MasterAnnouncement, and LocalMasterAnnouncement frames. All other + * datagrams (other than the expected non-browser datagrams) may be + * ignored and an error logged. + * + * (1d) + * This name is used to identify a master browser server for domain + * "DOMAIN" on a subnet. A master browser server adds this name as a + * unique NETBIOS name when it becomes master browser. If the attempt + * to add the name fails, the master browser server assumes that there + * is another master in the domain and will fail to come up. It may + * log an error if the failure occurs more than 3 times in a row (this + * either indicates some form of network misconfiguration or a + * software error). The only requests that should appear on this name + * are GetBackupListRequest and HostAnnouncement requests. All other + * datagrams on this name may be ignored (and an error logged). If + * running a NETBIOS name service (NBNS, such as WINS), this name + * should not be registered with the NBNS. + * + * (1b) + * This name is used to identify the Domain Master Browser for domain + * "DOMAIN" (which is also the primary domain controller). It is a + * unique name added only by the primary domain controller. The + * primary domain controller will respond to GetBackupListRequest on + * this name just as it responds to these requests on the (1d) + * name. + * + * 11.2 Registered group names + * + * (01)(02)__MSBROWSE__(02)(01) + * This name is used by Master Browsers to announce themselves to the + * other Master Browsers on a subnet. It is added as a group name by + * all Master Browser servers. The only broadcasts that should appear + * on this name is DomainAnnouncement requests. All other datagrams + * can be ignored. + * + * (00) + * This name is used by clients and servers in domain "DOMAIN" to + * process server announcements. The only requests that should appear + * on this name that the browser is interested in are + * AnnouncementRequest and NETLOGON_QUERY (to locate the PDC) packets. + * All other unidentifiable requests may be ignored (and an error + * logged). + * + * (1E) + * This name is used for announcements to browsers for domain "DOMAIN" + * on a subnet. This name is registered by all the browser servers in + * the domain. The only requests that should appear on this name are + * RequestElection and AnnouncementRequest packets. All other + * datagrams may be ignored (and an error logged). + * + * (1C) + * This name is registered by Primary Domain Controllers. + */ + +void +smb_browser_config(void) +{ + struct name_entry name; + struct name_entry master; + struct name_entry dest; + struct name_entry *entry; + int smb_nc_cnt; + net_cfg_t cfg; + int net; + char resource_domain[SMB_PI_MAX_DOMAIN]; + + syslog(LOG_DEBUG, "smb_browse: reconfigure"); + + smb_browser_init(); + + smb_config_rdlock(); + (void) strlcpy(resource_domain, + smb_config_getstr(SMB_CI_DOMAIN_NAME), SMB_PI_MAX_DOMAIN); + (void) utf8_strupr(resource_domain); + smb_config_unlock(); + + /* domain<00> */ + smb_init_name_struct((unsigned char *)resource_domain, 0x00, + 0, 0, 0, 0, 0, &name); + entry = smb_name_find_name(&name); + smb_name_unlock_name(entry); + + smb_nc_cnt = smb_nic_get_num(); + for (net = 0; net < smb_nc_cnt; net++) { + if (smb_nic_get_byind(net, &cfg) == NULL) + break; + if (cfg.exclude) + continue; + smb_init_name_struct( + (unsigned char *)resource_domain, 0x00, 0, + cfg.ip, htons(DGM_SRVC_UDP_PORT), + NAME_ATTR_GROUP, NAME_ATTR_LOCAL, &name); + (void) smb_name_add_name(&name); + } + + /* All our local master browsers */ + smb_init_name_struct((unsigned char *)resource_domain, 0x1D, + 0, 0, 0, 0, 0, &dest); + entry = smb_name_find_name(&dest); + + if (entry) { + for (net = 0; net < smb_nc_cnt; net++) { + if (smb_browser_addr_of_subnet(entry, net, &master) + == 0) { + syslog(LOG_DEBUG, + "smbd: Master browser found at %s", + inet_ntoa(master.addr_list.sin.sin_addr)); + } + } + smb_name_unlock_name(entry); + } + smb_init_name_struct((unsigned char *)resource_domain, + 0x1B, 0, 0, 0, 0, 0, &dest); + + if ((entry = smb_name_find_name(&dest)) != 0) { + syslog(LOG_DEBUG, "smbd: Domain Master browser for %s is %s", + resource_domain, + inet_ntoa(entry->addr_list.sin.sin_addr)); + smb_name_unlock_name(entry); + } +} + +static void +smb_browser_init() +{ + struct browser_netinfo *subnet; + struct server_info *server; + char cmnt[SMB_PI_MAX_COMMENT], hostname[MAXHOSTNAMELEN]; + int i, j; + int smb_nc_cnt; + net_cfg_t cfg; + + (void) smb_gethostname(hostname, MAXHOSTNAMELEN, 1); + + smb_config_rdlock(); + (void) strlcpy(cmnt, smb_config_getstr(SMB_CI_SYS_CMNT), + sizeof (cmnt)); + smb_config_unlock(); + + smb_nc_cnt = smb_nic_get_num(); + for (i = 0; i < smb_nc_cnt; i++) { + if (smb_nic_get_byind(i, &cfg) == NULL) + break; + if (cfg.exclude) + continue; + + subnet = &smb_browser_info[i]; + (void) mutex_lock(&subnet->mtx); + + /* One Minute announcements for first five */ + subnet->flags = BROWSER_NF_VALID; + subnet->next_announce = 1; + subnet->interval = 1; + subnet->reps = 5; + + server = &subnet->server; + bzero(server, sizeof (struct server_info)); + + server->type = MY_SERVER_TYPE; + server->major = SMB_VERSION_MAJOR; + server->minor = SMB_VERSION_MINOR; + server->signature = SMB_SERVER_SIGNATURE; + (void) strlcpy(server->comment, cmnt, SMB_PI_MAX_COMMENT); + + (void) snprintf(server->hostname, NETBIOS_NAME_SZ, "%.15s", + hostname); + + /* + * 00 is workstation service. + * 20 is file server service. + */ + smb_init_name_struct((unsigned char *)server->hostname, 0x20, 0, + cfg.ip, htons(DGM_SRVC_UDP_PORT), + NAME_ATTR_UNIQUE, NAME_ATTR_LOCAL, &server->name); + + (void) mutex_unlock(&subnet->mtx); + } + + /* Invalidate unconfigured NICs */ + for (j = i; j < SMB_PI_MAX_NETWORKS; j++) { + subnet = &smb_browser_info[j]; + (void) mutex_lock(&subnet->mtx); + subnet->flags = BROWSER_NF_INVALID; + (void) mutex_unlock(&subnet->mtx); + } +} + +/* + * smb_browser_non_master_duties + * + * To advertise its presence, i.e. to publish itself as being available, a + * non-browser server sends a HostAnnouncement browser frame. If the server + * is a member of domain "D", this frame is sent to the NETBIOS unique name + * D(1d) and mailslot "\\MAILSLOT\\BROWSE". + */ +void +smb_browser_non_master_duties(int net) +{ + struct browser_netinfo *subnet; + struct name_entry name; + struct name_entry *dest; + struct addr_entry addr; + int interval; + char resource_domain[SMB_PI_MAX_DOMAIN]; + + subnet = smb_browser_getnet(net); + if (subnet == 0) + return; + + interval = subnet->interval; + smb_browser_putnet(subnet); + + smb_browser_send_HostAnnouncement(net, interval, 0, 0x1D); + + smb_config_rdlock(); + (void) strlcpy(resource_domain, + smb_config_getstr(SMB_CI_DOMAIN_NAME), SMB_PI_MAX_DOMAIN); + (void) utf8_strupr(resource_domain); + smb_config_unlock(); + + smb_init_name_struct((unsigned char *)resource_domain, 0x1D, + 0, 0, 0, 0, 0, &name); + + if ((dest = smb_name_find_name(&name))) { + addr = dest->addr_list; + addr.forw = addr.back = &addr; + smb_name_unlock_name(dest); + smb_browser_send_HostAnnouncement(net, interval, &addr, 0x1D); + } else { + smb_init_name_struct( + (unsigned char *)resource_domain, 0x1B, + 0, 0, 0, 0, 0, &name); + if ((dest = smb_name_find_name(&name))) { + addr = dest->addr_list; + addr.forw = addr.back = &addr; + smb_name_unlock_name(dest); + smb_browser_send_HostAnnouncement(net, interval, + &addr, 0x1B); + } + } + + subnet = smb_browser_getnet(net); + /* + * One Minute announcements for first five + * minutes, one munute longer each round + * until 12 minutes and every 12 minutes + * thereafter. + */ + if (--subnet->reps == 0) { + if (subnet->interval < 12) + subnet->interval++; + + subnet->reps = 1; + } + + subnet->next_announce = subnet->interval; + smb_browser_putnet(subnet); +} + + +/* + * browser_sleep + * + * Put browser in 1 minute sleep if netbios services are not + * shutting down and both name and datagram services are still + * running. It'll wake up after 1 minute or if one of the above + * conditions go false. It checks the conditions again and return + * 1 if everything is ok or 0 if browser shouldn't continue + * running. + */ +static int +browser_sleep() +{ + int slept = 0; + timestruc_t to; + + (void) mutex_lock(&nb_status.mtx); + while (((nb_status.state & NETBIOS_SHUTTING_DOWN) == 0) && + (nb_status.state & NETBIOS_NAME_SVC_RUNNING) && + (nb_status.state & NETBIOS_DATAGRAM_SVC_RUNNING)) { + + if (slept) { + (void) mutex_unlock(&nb_status.mtx); + return (1); + } + + to.tv_sec = 60; /* 1 minute */ + to.tv_nsec = 0; + (void) cond_reltimedwait(&nb_status.cv, &nb_status.mtx, &to); + slept = 1; + } + (void) mutex_unlock(&nb_status.mtx); + + return (0); +} + +/* + * smb_browser_start + * + * Smb Netbios browser daemon. + */ +/*ARGSUSED*/ +void * +smb_browser_daemon(void *arg) +{ + int net; + int next_announce; + struct browser_netinfo *subnet; + int run = 1; + int smb_nc_cnt; + net_cfg_t cfg; + + smb_browser_config(); + + nb_status.state |= NETBIOS_BROWSER_RUNNING; + + while (run) { + smb_nc_cnt = smb_nic_get_num(); + for (net = 0; net < smb_nc_cnt; net++) { + if (smb_nic_get_byind(net, &cfg) == NULL) + break; + if (cfg.exclude) + continue; + + subnet = smb_browser_getnet(net); + next_announce = --subnet->next_announce; + smb_browser_putnet(subnet); + + if (next_announce > 0 || cfg.broadcast == 0) + continue; + + smb_browser_non_master_duties(net); + } + + run = browser_sleep(); + } + + smb_netbios_chg_status(NETBIOS_BROWSER_RUNNING, 0); + return (0); +} diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_browser.h b/usr/src/lib/smbsrv/libsmbns/common/smbns_browser.h new file mode 100644 index 000000000000..17f3fdf4a978 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_browser.h @@ -0,0 +1,190 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _BROWSER_H_ +#define _BROWSER_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * NetBIOS name types describe the functionality of the registration. + * A following list of NetBIOS suffixes (16th Character of the NetBIOS + * Name) is detailed in Microsoft knowledge base article Q163409. + * + * Name Number(h) Type Usage + * -------------------------------------------------------------------------- + * 00 U Workstation Service + * 01 U Messenger Service + * <\\--__MSBROWSE__> 01 G Master Browser + * 03 U Messenger Service + * 06 U RAS Server Service + * 1F U NetDDE Service + * 20 U File Server Service + * 21 U RAS Client Service + * 22 U Microsoft Exchange Interchange(MSMail + * Connector) + * 23 U Microsoft Exchange Store + * 24 U Microsoft Exchange Directory + * 30 U Modem Sharing Server Service + * 31 U Modem Sharing Client Service + * 43 U SMS Clients Remote Control + * 44 U SMS Administrators Remote Control + * Tool + * 45 U SMS Clients Remote Chat + * 46 U SMS Clients Remote Transfer + * 4C U DEC Pathworks TCPIP service on + * Windows NT + * 52 U DEC Pathworks TCPIP service on + * Windows NT + * 87 U Microsoft Exchange Message Transfer + * Agent + * 6A U Microsoft Exchange IMC + * BE U Network Monitor Agent + * BF U Network Monitor Application + * 03 U Messenger Service + * 00 G Domain Name + * 1B U Domain Master Browser + * 1C G Domain Controllers + * 1D U Master Browser + * 1E G Browser Service Elections + * 1C G IIS + * 00 U IIS + * [2B] U Lotus Notes Server Service + * IRISMULTICAST [2F] G Lotus Notes + * IRISNAMESERVER [33] G Lotus Notes + * Forte_$ND800ZA [20] U DCA IrmaLan Gateway Server Service + * + * Unique (U): The name may have only one IP address assigned to it. On + * a network device multiple occurrences of a single name may appear to + * be registered. The suffix may be the only unique character in the name. + * + * Group (G): A normal group; the single name may exist with many IP + * addresses. WINS responds to a name query on a group name with the + * limited broadcast address (255.255.255.255). Because routers block + * the transmission of these addresses, the Internet Group was designed + * to service communications between subnets. + * + * Multihomed (M): The name is unique, but due to multiple network + * interfaces on the same computer this configuration is necessary to + * permit the registration. The maximum number of addresses is 25. + * + * Internet Group (I): This is a special configuration of the group name + * used to manage Windows NT Domain names. + * + * Domain Name (D): New in Windows NT 4.0. + */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Message flags used when building the SMB transact headers. + */ +#define TWO_WAY_TRANSACTION 0x00 +#define END_SESSION_TRANSACTION 0x01 +#define ONE_WAY_TRANSACTION 0x02 + + +/* + * Browser commands associated with the BROWSE and MSBROWSE mailslots. + */ +#define HOST_ANNOUNCEMENT 0x01 +#define ANNOUNCEMENT_REQUEST 0x02 +#define REQUEST_ELECTION 0x08 +#define GET_BACKUP_LIST_REQ 0x09 +#define GET_BACKUP_LIST_RESP 0x0A +#define BECOME_BACKUP 0x0B +#define DOMAIN_ANNOUNCEMENT 0x0C +#define MASTER_ANNOUNCEMENT 0x0D +#define LOCAL_MASTER_ANNOUNCEMENT 0x0F + + +/* + * Opcodes associated with NETLOGON or NTLOGON mailslots (KB 109626). + * LOGON_REQUEST LM1.0/2.0 LOGON Request from client + * LOGON_RESPONSE LM1.0 Response to LOGON_REQUEST + * LOGON_CENTRAL_QUERY LM1.0 QUERY for centralized init + * LOGON_DISTRIB_QUERY LM1.0 QUERY for non-centralized init + * LOGON_CENTRAL_RESPONSE LM1.0 response to LOGON_CENTRAL_QUERY + * LOGON_DISTRIB_RESPONSE LM1.0 resp to LOGON_DISTRIB_QUERY + * LOGON_RESPONSE2 LM2.0 Response to LOGON_REQUEST + * LOGON_PRIMARY_QUERY QUERY for Primary DC + * LOGON_START_PRIMARY announce startup of Primary DC + * LOGON_FAIL_PRIMARY announce failed Primary DC + * LOGON_UAS_CHANGE announce change to UAS or SAM + * LOGON_NO_USER announce no user on machine + * LOGON_PRIMARY_RESPONSE response to LOGON_PRIMARY_QUERY + * LOGON_RELOGON_RESPONSE LM1.0/2.0 resp to relogon request + * LOGON_WKSTINFO_RESPONSE LM1.0/2.0 resp to interrogate request + * LOGON_PAUSE_RESPONSE LM2.0 resp when NETLOGON is paused + * LOGON_USER_UNKNOWN LM2.0 response when user is unknown + * LOGON_UPDATE_ACCOUNT LM2.1 announce account updates + * LOGON_SAM_LOGON_REQUEST SAM LOGON request from client + * LOGON_SAM_LOGON_RESPONSE SAM Response to SAM logon request + * LOGON_SAM_PAUSE_RESPONSE SAM response when NETLOGON is paused + * LOGON_SAM_USER_UNKNOWN SAM response when user is unknown + * LOGON_SAM_WKSTINFO_RESPONSE SAM response to interrogate request + */ +#define LOGON_REQUEST 0 +#define LOGON_RESPONSE 1 +#define LOGON_CENTRAL_QUERY 2 +#define LOGON_DISTRIB_QUERY 3 +#define LOGON_CENTRAL_RESPONSE 4 +#define LOGON_DISTRIB_RESPONSE 5 +#define LOGON_RESPONSE2 6 +#define LOGON_PRIMARY_QUERY 7 +#define LOGON_START_PRIMARY 8 +#define LOGON_FAIL_PRIMARY 9 +#define LOGON_UAS_CHANGE 10 +#define LOGON_NO_USER 11 +#define LOGON_PRIMARY_RESPONSE 12 +#define LOGON_RELOGON_RESPONSE 13 +#define LOGON_WKSTINFO_RESPONSE 14 +#define LOGON_PAUSE_RESPONSE 15 +#define LOGON_USER_UNKNOWN 16 +#define LOGON_UPDATE_ACCOUNT 17 +#define LOGON_SAM_LOGON_REQUEST 18 +#define LOGON_SAM_LOGON_RESPONSE 19 +#define LOGON_SAM_PAUSE_RESPONSE 20 +#define LOGON_SAM_USER_UNKNOWN 21 +#define LOGON_SAM_WKSTINFO_RESPONSE 22 + + +/* + * Local protocol flags used to indicate which version of the + * netlogon protocol to use when attempting to find the PDC. + */ +#define NETLOGON_PROTO_NETLOGON 0x01 +#define NETLOGON_PROTO_SAMLOGON 0x02 + +#ifdef __cplusplus +} +#endif + + +#endif /* _BROWSER_H_ */ diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.c new file mode 100644 index 000000000000..474adca09b41 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.c @@ -0,0 +1,1894 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* internal use, in dyndns_add_entry */ +#define DEL_NONE 2 +/* Maximum retires if not authoritative */ +#define MAX_AUTH_RETRIES 3 + + +static int +dyndns_enabled(void) +{ + int enabled; + + smb_config_rdlock(); + enabled = smb_config_getyorn(SMB_CI_DYNDNS_ENABLE); + smb_config_unlock(); + + return ((enabled) ? 1 : 0); +} + +/* + * XXX The following should be removed once head/arpa/nameser_compat.h + * defines BADSIG, BADKEY, BADTIME macros + */ +#ifndef BADSIG +#define BADSIG ns_r_badsig +#endif /* BADSIG */ + +#ifndef BADKEY +#define BADKEY ns_r_badkey +#endif /* BADKEY */ + +#ifndef BADTIME +#define BADTIME ns_r_badtime +#endif /* BADTIME */ + +/* + * dyndns_msg_err + * Display error message for DNS error code found in the DNS header in + * reply message. + * Parameters: + * err: DNS errer code + * Returns: + * None + */ +void +dyndns_msg_err(int err) +{ + switch (err) { + case NOERROR: + break; + case FORMERR: + syslog(LOG_ERR, "DNS message format error\n"); + break; + case SERVFAIL: + syslog(LOG_ERR, "DNS server internal error\n"); + break; + case NXDOMAIN: + syslog(LOG_ERR, "DNS entry should exist but does not exist\n"); + break; + case NOTIMP: + syslog(LOG_ERR, "DNS opcode not supported\n"); + break; + case REFUSED: + syslog(LOG_ERR, "DNS operation refused\n"); + break; + case YXDOMAIN: + syslog(LOG_ERR, "DNS entry shouldn't exist but does exist\n"); + break; + case YXRRSET: + syslog(LOG_ERR, "DNS RRSet shouldn't exist but does exist\n"); + break; + case NXRRSET: + syslog(LOG_ERR, "DNS RRSet should exist but does not exist\n"); + break; + case NOTAUTH: + syslog(LOG_ERR, "DNS server is not authoritative " + "for specified zone\n"); + break; + case NOTZONE: + syslog(LOG_ERR, "Name in Prereq or Update section not " + "within specified zone\n"); + break; + case BADSIG: + syslog(LOG_ERR, "Bad transaction signature (TSIG)"); + break; + case BADKEY: + syslog(LOG_ERR, "Bad transaction key (TKEY)"); + break; + case BADTIME: + syslog(LOG_ERR, "Time not synchronized"); + break; + + default: + syslog(LOG_ERR, "Unknown DNS error\n"); + } +} + +/* + * display_stat + * Display GSS error message from error code. This routine is used to display + * the mechanism independent and mechanism specific error messages for GSS + * routines. The major status error code is the mechanism independent error + * code and the minor status error code is the mechanism specific error code. + * Parameters: + * maj: GSS major status + * min: GSS minor status + * Returns: + * None + */ +static void +display_stat(OM_uint32 maj, OM_uint32 min) +{ + gss_buffer_desc msg; + OM_uint32 msg_ctx = 0; + OM_uint32 min2; + (void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID, + &msg_ctx, &msg); + syslog(LOG_ERR, "dyndns: GSS major status error: %s\n", + (char *)msg.value); + (void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID, + &msg_ctx, &msg); + syslog(LOG_ERR, "dyndns: GSS minor status error: %s\n", + (char *)msg.value); +} + +static char * +dyndns_put_nshort(char *buf, uint16_t val) +{ + uint16_t nval; + + nval = htons(val); + (void) memcpy(buf, &nval, sizeof (uint16_t)); + buf += sizeof (uint16_t); + return (buf); +} + +char * +dyndns_get_nshort(char *buf, uint16_t *val) +{ + uint16_t nval; + + (void) memcpy(&nval, buf, sizeof (uint16_t)); + *val = ntohs(nval); + buf += sizeof (uint16_t); + return (buf); +} + +static char * +dyndns_put_nlong(char *buf, uint32_t val) +{ + uint32_t lval; + + lval = htonl(val); + (void) memcpy(buf, &lval, sizeof (uint32_t)); + buf += sizeof (uint32_t); + return (buf); +} + +static char * +dyndns_put_byte(char *buf, char val) +{ + *buf = val; + buf++; + return (buf); +} + +static char * +dyndns_put_int(char *buf, int val) +{ + (void) memcpy(buf, &val, sizeof (int)); + buf += sizeof (int); + return (buf); +} + +char * +dyndns_get_int(char *buf, int *val) +{ + (void) memcpy(val, buf, sizeof (int)); + buf += sizeof (int); + return (buf); +} + + +/* + * dyndns_stuff_str + * Converts a domain string by removing periods and replacing with a byte value + * of how many characters following period. A byte value is placed in front + * to indicate how many characters before first period. A NULL character is + * placed at the end. i.e. host.procom.com -> 4host5procom3com0 + * Buffer space checking is done by caller. + * Parameters: + * ptr : address of pointer to buffer to store converted string + * zone: domain name string + * Returns: + * ptr: address of pointer to next available buffer space + * -1 : error + * 0 : success + */ +static int +dyndns_stuff_str(char **ptr, char *zone) +{ + int len; + char *lenPtr, *zonePtr; + + for (zonePtr = zone; *zonePtr; ) { + lenPtr = *ptr; + *ptr = *ptr + 1; + len = 0; + while (*zonePtr != '.' && *zonePtr != 0) { + *ptr = dyndns_put_byte(*ptr, *zonePtr); + zonePtr++; + len++; + } + *lenPtr = len; + if (*zonePtr == '.') + zonePtr++; + } + *ptr = dyndns_put_byte(*ptr, 0); + return (0); +} + +/* + * dyndns_build_header + * Build the header for DNS query and DNS update request message. + * Parameters: + * ptr : address of pointer to buffer to store header + * buf_len : buffer length + * msg_id : message id + * query_req : use REQ_QUERY for query message or REQ_UPDATE for + * update message + * quest_zone_cnt : number of question record for query message or + * number of zone record for update message + * ans_prereq_cnt : number of answer record for query message or + * number of prerequisite record for update message + * nameser_update_cnt: number of name server for query message or + * number of update record for update message + * addit_cnt : number of additional record + * flags : query flags word + * Returns: + * ptr: address of pointer to next available buffer space + * -1 : error + * 0 : success + */ +int +dyndns_build_header(char **ptr, int buf_len, uint16_t msg_id, int query_req, + uint16_t quest_zone_cnt, uint16_t ans_prereq_cnt, + uint16_t nameser_update_cnt, uint16_t addit_cnt, int flags) +{ + uint16_t opcode; + + if (buf_len < 12) { + syslog(LOG_ERR, "dyndns: no more buf for header section\n"); + return (-1); + } + + *ptr = dyndns_put_nshort(*ptr, msg_id); /* mesg ID */ + if (query_req == REQ_QUERY) + opcode = ns_o_query; /* query msg */ + else + opcode = ns_o_update << 11; /* update msg */ + opcode |= flags; + /* mesg opcode */ + *ptr = dyndns_put_nshort(*ptr, opcode); + /* zone record count */ + *ptr = dyndns_put_nshort(*ptr, quest_zone_cnt); + /* prerequiste record count */ + *ptr = dyndns_put_nshort(*ptr, ans_prereq_cnt); + /* update record count */ + *ptr = dyndns_put_nshort(*ptr, nameser_update_cnt); + /* additional record count */ + *ptr = dyndns_put_nshort(*ptr, addit_cnt); + + return (0); +} + +/* + * dyndns_build_quest_zone + * Build the question section for query message or zone section for + * update message. + * Parameters: + * ptr : address of pointer to buffer to store question or zone section + * buf_len: buffer length + * name : question or zone name + * type : type of question or zone + * class : class of question or zone + * Returns: + * ptr: address of pointer to next available buffer space + * -1 : error + * 0 : success + */ +int +dyndns_build_quest_zone(char **ptr, int buf_len, char *name, int type, + int class) +{ + char *zonePtr; + + if ((strlen(name) + 6) > buf_len) { + syslog(LOG_ERR, "dyndns: no more buf " + "for question/zone section\n"); + return (-1); + } + + zonePtr = *ptr; + (void) dyndns_stuff_str(&zonePtr, name); + *ptr = zonePtr; + *ptr = dyndns_put_nshort(*ptr, type); + *ptr = dyndns_put_nshort(*ptr, class); + return (0); +} + +/* + * dyndns_build_update + * Build update section of update message for adding and removing a record. + * If the ttl value is 0 then this message is for record deletion. + * + * Parameters: + * ptr : address of pointer to buffer to store update section + * buf_len : buffer length + * name : resource name of this record + * type : type of this record + * class : class of this record + * ttl : time-to-live, cached time of this entry by others and not + * within DNS database, a zero value for record(s) deletion + * data : data of this resource record + * forw_rev: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone + * add_del : UPDATE_ADD for adding entry, UPDATE_DEL for removing zone + * del_type: DEL_ONE for deleting one entry, DEL_ALL for deleting all + * entries of the same resource name. Only valid for UPDATE_DEL. + * Returns: + * ptr: address of pointer to next available buffer space + * -1 : error + * 0 : success + */ +static int +dyndns_build_update(char **ptr, int buf_len, char *name, int type, int class, + uint32_t ttl, char *data, int forw_rev, int add_del, int del_type) +{ + char *namePtr; + int rec_len, data_len; + + rec_len = strlen(name) + 10; + if (add_del == UPDATE_ADD) { + if (forw_rev == UPDATE_FORW) + data_len = 4; + else + data_len = strlen(data) + 2; + } else { + if (del_type == DEL_ALL) + data_len = 0; + else if (forw_rev == UPDATE_FORW) + data_len = 4; + else + data_len = strlen(data) + 2; + } + + if (rec_len + data_len > buf_len) { + syslog(LOG_ERR, "dyndns: no more buf for update section\n"); + return (-1); + } + + namePtr = *ptr; + (void) dyndns_stuff_str(&namePtr, name); + *ptr = namePtr; + *ptr = dyndns_put_nshort(*ptr, type); + *ptr = dyndns_put_nshort(*ptr, class); + *ptr = dyndns_put_nlong(*ptr, ttl); + + if (add_del == UPDATE_DEL && del_type == DEL_ALL) { + *ptr = dyndns_put_nshort(*ptr, 0); + return (0); + } + + if (forw_rev == UPDATE_FORW) { + *ptr = dyndns_put_nshort(*ptr, 4); + *ptr = dyndns_put_int(*ptr, inet_addr(data)); /* ip address */ + } else { + *ptr = dyndns_put_nshort(*ptr, strlen(data)+2); + namePtr = *ptr; + (void) dyndns_stuff_str(&namePtr, data); /* hostname */ + *ptr = namePtr; + } + return (0); +} + +/* + * dyndns_build_tkey + * Build TKEY section to establish security context for secure dynamic DNS + * update. DNS header and question sections need to be build before this + * section. The TKEY data are the tokens generated during security context + * establishment and the TKEY message is used to transmit those tokens, one + * at a time, to the DNS server. + * Parameters: + * ptr : address of pointer to buffer to store TKEY + * buf_len : buffer length + * name : key name, must be unique and same as for TSIG record + * key_expire: expiration time of this key in second + * data : TKEY data + * data_size : data size + * Returns: + * ptr: address of the pointer to the next available buffer space + * -1 : error + * 0 : success + */ +static int +dyndns_build_tkey(char **ptr, int buf_len, char *name, int key_expire, + char *data, int data_size) +{ + char *namePtr; + struct timeval tp; + + if (strlen(name)+2 + 45 + data_size > buf_len) { + syslog(LOG_ERR, "dyndns: no more buf for TKEY record\n"); + return (-1); + } + + namePtr = *ptr; + (void) dyndns_stuff_str(&namePtr, name); /* unique global name */ + *ptr = namePtr; + *ptr = dyndns_put_nshort(*ptr, ns_t_tkey); + *ptr = dyndns_put_nshort(*ptr, ns_c_any); + *ptr = dyndns_put_nlong(*ptr, 0); + /* 19 + 14 + data_size + 2 */ + *ptr = dyndns_put_nshort(*ptr, 35 + data_size); + namePtr = *ptr; + (void) dyndns_stuff_str(&namePtr, "gss.microsoft.com"); + *ptr = namePtr; + (void) gettimeofday(&tp, 0); + *ptr = dyndns_put_nlong(*ptr, tp.tv_sec); /* inception */ + /* expiration, 86400 */ + *ptr = dyndns_put_nlong(*ptr, tp.tv_sec + key_expire); + *ptr = dyndns_put_nshort(*ptr, MODE_GSS_API); /* mode: gss-api */ + *ptr = dyndns_put_nshort(*ptr, 0); /* error */ + *ptr = dyndns_put_nshort(*ptr, data_size); /* key size */ + (void) memcpy(*ptr, data, data_size); /* key data */ + *ptr += data_size; + *ptr = dyndns_put_nshort(*ptr, 0); /* other */ + return (0); +} + +/* + * dyndns_build_tsig + * Build TSIG section for secure dynamic DNS update. This routine will be + * called twice. First called with TSIG_UNSIGNED, and second with TSIG_SIGNED. + * The TSIG data is NULL and ignored for TSIG_UNSIGNED and is the update request + * message encrypted for TSIG_SIGNED. The message id must be the same id as the + * one in the update request before it is encrypted. + * Parameters: + * ptr : address of pointer to buffer to store TSIG + * buf_len : buffer length + * msg_id : message id + * name : key name, must be the same as in TKEY record + * fudge_time : amount of error time allow in seconds + * data : TSIG data if TSIG_SIGNED, otherwise NULL + * data_size : size of data, otherwise 0 if data is NULL + * data_signed: TSIG_SIGNED to indicate data is signed and encrypted, + * otherwise TSIG_UNSIGNED + * Returns: + * ptr: address of pointer to next available buffer space + * -1 : error + * 0 : success + */ +static int +dyndns_build_tsig(char **ptr, int buf_len, int msg_id, char *name, + int fudge_time, char *data, int data_size, int data_signed) +{ + char *namePtr; + struct timeval tp; + int signtime, fudge, rec_len; + + if (data_signed == TSIG_UNSIGNED) + rec_len = strlen(name)+2 + 37; + else + rec_len = strlen(name)+2 + 45 + data_size; + + if (rec_len > buf_len) { + syslog(LOG_ERR, "dyndns: no more buf for TSIG record\n"); + return (-1); + } + + namePtr = *ptr; + (void) dyndns_stuff_str(&namePtr, name); /* unique global name */ + *ptr = namePtr; + if (data_signed == TSIG_SIGNED) + *ptr = dyndns_put_nshort(*ptr, ns_t_tsig); + *ptr = dyndns_put_nshort(*ptr, ns_c_any); + *ptr = dyndns_put_nlong(*ptr, 0); + if (data_signed == TSIG_SIGNED) { + /* 19 + 10 + data_size + 6 */ + *ptr = dyndns_put_nshort(*ptr, 35 + data_size); + } + namePtr = *ptr; + (void) dyndns_stuff_str(&namePtr, "gss.microsoft.com"); + *ptr = namePtr; + (void) gettimeofday(&tp, 0); + signtime = tp.tv_sec >> 16; + *ptr = dyndns_put_nlong(*ptr, signtime); /* sign time */ + fudge = tp.tv_sec << 16; + fudge |= fudge_time; + *ptr = dyndns_put_nlong(*ptr, fudge); /* fudge time */ + if (data_signed == TSIG_SIGNED) { + /* signed data size */ + *ptr = dyndns_put_nshort(*ptr, data_size); + (void) memcpy(*ptr, data, data_size); /* signed data */ + *ptr += data_size; + *ptr = dyndns_put_nshort(*ptr, msg_id); /* original id */ + } + *ptr = dyndns_put_nshort(*ptr, 0); /* error */ + *ptr = dyndns_put_nshort(*ptr, 0); /* other */ + return (0); +} + +/* + * dyndns_open_init_socket + * This routine creates a SOCK_STREAM or SOCK_DGRAM socket and initializes it + * by doing bind() and setting linger option to off. + * + * Parameters: + * sock_type: SOCK_STREAM for TCP or SOCK_DGRAM for UDP + * dest_addr: destination address in network byte order + * port : destination port number + * Returns: + * descriptor: descriptor referencing the created socket + * -1 : error + */ +int +dyndns_open_init_socket(int sock_type, unsigned long dest_addr, int port) +{ + int s; + struct sockaddr_in my_addr; + struct linger l; + struct sockaddr_in serv_addr; + + if ((s = socket(AF_INET, sock_type, 0)) == -1) { + syslog(LOG_ERR, "dyndns: socket err\n"); + return (-1); + } + + l.l_onoff = 0; + if (setsockopt(s, SOL_SOCKET, SO_LINGER, + (char *)&l, sizeof (l)) == -1) { + syslog(LOG_ERR, "dyndns: setsocket err\n"); + (void) close(s); + return (-1); + } + + bzero(&my_addr, sizeof (my_addr)); + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons(0); + my_addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (bind(s, (struct sockaddr *)&my_addr, sizeof (my_addr)) < 0) { + syslog(LOG_ERR, "dyndns: client bind err\n"); + (void) close(s); + return (-1); + } + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(port); + serv_addr.sin_addr.s_addr = dest_addr; + + if (connect(s, (struct sockaddr *)&serv_addr, + sizeof (struct sockaddr_in)) < 0) { + syslog(LOG_ERR, "dyndns: client connect err (%s)\n", + strerror(errno)); + (void) close(s); + return (-1); + } + + return (s); +} + +/* + * dyndns_acquire_cred + * This routine is used to acquire a GSS credential handle to a user's Kerberos + * ticket-granting ticket (TGT) stored locally on the system. If getting a + * handle fails, then a new TGT will be obtained again before trying to get a + * handle once more. + * The user's password is taken from the environment variable + * lookup.dns.dynamic.passwd and is encrypted. + * Paramaters: + * kinit_retry: if 0 then a new TGT can be obtained before second attempt to + * get a handle to TGT if first attempt fails + * Returns: + * user_name : name of user to get credential handle from + * credHandle : handle to user's credential (TGT) + * oid : contains Kerberos 5 object identifier + * kinit_retry: 1 if a new TGT has been acquired in this routine, otherwise 0 + * -1 : error + */ +static int +dyndns_acquire_cred(gss_cred_id_t *credHandle, char *user_name, + gss_OID *oid, int *kinit_retry) +{ + char *p, pwd[100]; + + smb_config_rdlock(); + p = smb_config_getstr(SMB_CI_ADS_USER); + if (p == NULL || *p == 0) { + syslog(LOG_ERR, "No user configured for " + "secure dynamic DNS update.\n"); + smb_config_unlock(); + return (-1); + } + (void) strcpy(user_name, p); + + p = smb_config_getstr(SMB_CI_ADS_PASSWD); + if (p == NULL || *p == 0) { + syslog(LOG_ERR, "No password configured for " + "secure dynamic DNS update.\n"); + smb_config_unlock(); + return (-1); + } + smb_config_unlock(); + + (void) strcpy(pwd, p); + + return krb5_acquire_cred_kinit(user_name, pwd, credHandle, + oid, kinit_retry, "dyndns"); +} + +/* + * dyndns_build_tkey_msg + * This routine is used to build the TKEY message to transmit GSS tokens + * during GSS security context establishment for secure DNS update. The + * TKEY message format uses the DNS query message format. The TKEY section + * is the answer section of the query message format. + * Microsoft uses a value of 86400 seconds (24 hours) for key expiration time. + * Parameters: + * buf : buffer to build and store TKEY message + * key_name: a unique key name, this same key name must be also be used in + * the TSIG message + * out_tok : TKEY message data (GSS tokens) + * Returns: + * id : message id of this TKEY message + * message size: the size of the TKEY message + * -1 : error + */ +static int +dyndns_build_tkey_msg(char *buf, char *key_name, uint16_t *id, + gss_buffer_desc *out_tok) +{ + int queryReq, zoneCount, preqCount, updateCount, additionalCount; + int zoneType, zoneClass; + char *bufptr; + + queryReq = REQ_QUERY; + /* query section of query request */ + zoneCount = 1; + /* answer section of query request */ + preqCount = 1; + updateCount = 0; + additionalCount = 0; + + (void) memset(buf, 0, MAX_TCP_SIZE); + bufptr = buf; + *id = smb_get_next_resid(); + + /* add TCP length info that follows this field */ + bufptr = dyndns_put_nshort(bufptr, + 26 + (strlen(key_name)+2)*2 + 35 + out_tok->length); + + if (dyndns_build_header(&bufptr, BUFLEN_TCP(bufptr, buf), *id, queryReq, + zoneCount, preqCount, updateCount, additionalCount, 0) == -1) { + return (-1); + } + + zoneType = ns_t_tkey; + zoneClass = ns_c_in; + if (dyndns_build_quest_zone(&bufptr, BUFLEN_TCP(bufptr, buf), key_name, + zoneType, zoneClass) == -1) { + return (-1); + } + + if (dyndns_build_tkey(&bufptr, BUFLEN_TCP(bufptr, buf), key_name, + 86400, out_tok->value, out_tok->length) == -1) { + return (-1); + } + + return (bufptr - buf); +} + +/* + * dyndns_establish_sec_ctx + * This routine is used to establish a security context with the DNS server + * by building TKEY messages and sending them to the DNS server. TKEY messages + * are also received from the DNS server for processing. The security context + * establishment is done with the GSS client on the system producing a token + * and sending the token within the TKEY message to the GSS server on the DNS + * server. The GSS server then processes the token and then send a TKEY reply + * message with a new token to be processed by the GSS client. The GSS client + * processes the new token and then generates a new token to be sent to the + * GSS server. This cycle is continued until the security establishment is + * done. TCP is used to send and receive TKEY messages. + * If gss_init_sec_context fails then a new TGT will be acquired so that + * security establishment can be retry once more by the caller after getting + * a handle to the new TGT (credential). + * Parameters: + * credHandle : handle to credential + * s : socket descriptor to DNS server + * key_name : TKEY key name + * dns_hostname: fully qualified DNS hostname + * oid : contains Kerberos 5 object identifier + * user_name : name of user to perform DNS update + * kinit_retry : if 0 and gss_init_sec_context fails then get new TGT so + * the caller can restart doing security context establishment + * Returns: + * gss_context : handle to security context + * kinit_retry : 1 if a new TGT has been acquired in this routine, + * otherwise 0 + * do_acquire_cred: if 1 then caller will restart security context + * establishment + * -1 : error + */ +static int +dyndns_establish_sec_ctx(gss_ctx_id_t *gss_context, gss_cred_id_t credHandle, + int s, char *key_name, char *dns_hostname, gss_OID oid, char *user_name, + int *kinit_retry, int *do_acquire_cred) +{ + uint16_t id, rid, rsz; + char buf[MAX_TCP_SIZE], buf2[MAX_TCP_SIZE]; + int ret; + char *service_name, *tmpptr; + int service_sz; + OM_uint32 min, maj, time_rec; + gss_buffer_desc service_buf, in_tok, out_tok; + gss_name_t target_name; + gss_buffer_desc *inputptr; + int gss_flags; + OM_uint32 ret_flags; + int buf_sz; + char *p, pwd[100]; + + smb_config_rdlock(); + p = smb_config_getstr(SMB_CI_ADS_PASSWD); + if (p == NULL || *p == 0) { + syslog(LOG_ERR, "No password configured for " + "secure dynamic DNS update.\n"); + smb_config_unlock(); + return (-1); + } + smb_config_unlock(); + (void) strcpy(pwd, p); + + service_sz = strlen(dns_hostname) + 5; + service_name = (char *)malloc(sizeof (char) * service_sz); + if (service_name == NULL) { + syslog(LOG_ERR, "Malloc failed for %d bytes ", service_sz); + smb_config_unlock(); + return (-1); + } + (void) snprintf(service_name, service_sz, "DNS@%s", dns_hostname); + service_buf.value = service_name; + service_buf.length = strlen(service_name)+1; + if ((maj = gss_import_name(&min, &service_buf, + (gss_OID) gss_nt_service_name, + &target_name)) != GSS_S_COMPLETE) { + display_stat(maj, min); + (void) gss_release_oid(&min, &oid); + (void) free(service_name); + return (-1); + } + (void) free(service_name); + + inputptr = GSS_C_NO_BUFFER; + *gss_context = GSS_C_NO_CONTEXT; + gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG | GSS_C_REPLAY_FLAG | + GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG; + do { + if (krb5_establish_sec_ctx_kinit(user_name, pwd, credHandle, + gss_context, target_name, oid, gss_flags, inputptr, + &out_tok, &ret_flags, &time_rec, kinit_retry, + do_acquire_cred, &maj, "dyndns") == -1) { + (void) gss_release_oid(&min, &oid); + (void) gss_release_name(&min, &target_name); + return (-1); + } + + if ((maj == GSS_S_COMPLETE) && + !(ret_flags & GSS_C_REPLAY_FLAG)) { + syslog(LOG_ERR, "dyndns: No GSS_C_REPLAY_FLAG\n"); + if (out_tok.length > 0) + (void) gss_release_buffer(&min, &out_tok); + (void) gss_release_oid(&min, &oid); + (void) gss_release_name(&min, &target_name); + return (-1); + } + + if ((maj == GSS_S_COMPLETE) && + !(ret_flags & GSS_C_MUTUAL_FLAG)) { + syslog(LOG_ERR, "dyndns: No GSS_C_MUTUAL_FLAG\n"); + if (out_tok.length > 0) + (void) gss_release_buffer(&min, &out_tok); + (void) gss_release_oid(&min, &oid); + (void) gss_release_name(&min, &target_name); + return (-1); + } + + if (out_tok.length > 0) { + if ((buf_sz = dyndns_build_tkey_msg(buf, key_name, + &id, &out_tok)) <= 0) { + (void) gss_release_buffer(&min, &out_tok); + (void) gss_release_oid(&min, &oid); + (void) gss_release_name(&min, &target_name); + return (-1); + } + + (void) gss_release_buffer(&min, &out_tok); + + if (send(s, buf, buf_sz, 0) == -1) { + syslog(LOG_ERR, "dyndns: TKEY send error\n"); + (void) gss_release_oid(&min, &oid); + (void) gss_release_name(&min, &target_name); + return (-1); + } + + bzero(buf2, MAX_TCP_SIZE); + if (recv(s, buf2, MAX_TCP_SIZE, 0) == -1) { + syslog(LOG_ERR, "dyndns: TKEY " + "reply recv error\n"); + (void) gss_release_oid(&min, &oid); + (void) gss_release_name(&min, &target_name); + return (-1); + } + + ret = buf2[5] & 0xf; /* error field in TCP */ + if (ret != NOERROR) { + syslog(LOG_ERR, "dyndns: Error in " + "TKEY reply: %d: ", ret); + dyndns_msg_err(ret); + (void) gss_release_oid(&min, &oid); + (void) gss_release_name(&min, &target_name); + return (-1); + } + + tmpptr = &buf2[2]; + (void) dyndns_get_nshort(tmpptr, &rid); + if (id != rid) { + (void) gss_release_oid(&min, &oid); + (void) gss_release_name(&min, &target_name); + return (-1); + } + + tmpptr = &buf2[59+(strlen(key_name)+2)*2]; + (void) dyndns_get_nshort(tmpptr, &rsz); + in_tok.length = rsz; + + /* bsd38 -> 2*7=14 */ + in_tok.value = &buf2[61+(strlen(key_name)+2)*2]; + inputptr = &in_tok; + } + + } while (maj != GSS_S_COMPLETE); + + (void) gss_release_oid(&min, &oid); + (void) gss_release_name(&min, &target_name); + + return (0); +} + +/* + * dyndns_get_sec_context + * Get security context for secure dynamic DNS update. This routine opens + * a TCP socket to the DNS server and calls routines to get a handle to a + * locally cached user's credential and establish a security context with + * the DNS server to perform secure dynamic DNS update. If getting security + * context fails then a retry may be done after reobtaining new credential and + * getting a new credential handle. If obtaining new credential has been + * done earlier during getting a handle to credential then there is no need to + * do a retry for security context. + * Parameters: + * hostname: fully qualified hostname + * dns_ip : ip address of hostname in network byte order + * Returns: + * gss_handle: gss credential handle + * gss_context: gss security context + * -1: error + * 0: success + */ +static gss_ctx_id_t +dyndns_get_sec_context(const char *hostname, int dns_ip) +{ + int s; + gss_cred_id_t credHandle; + gss_ctx_id_t gss_context; + gss_OID oid; + OM_uint32 min; + struct hostent *hentry; + int kinit_retry, do_acquire_cred; + char *key_name, dns_hostname[255], user_name[50]; + + key_name = (char *)hostname; + + hentry = gethostbyaddr((char *)&dns_ip, 4, AF_INET); + if (hentry == NULL) { + syslog(LOG_ERR, "dyndns: Can't get DNS " + "hostname from DNS ip.\n"); + return (NULL); + } + (void) strcpy(dns_hostname, hentry->h_name); + + if ((s = dyndns_open_init_socket(SOCK_STREAM, dns_ip, 53)) < 0) { + return (NULL); + } + + kinit_retry = 0; + do_acquire_cred = 0; + acquire_cred: + + if (dyndns_acquire_cred(&credHandle, user_name, &oid, &kinit_retry)) { + (void) close(s); + return (NULL); + } + + if (dyndns_establish_sec_ctx(&gss_context, credHandle, s, key_name, + dns_hostname, oid, user_name, &kinit_retry, &do_acquire_cred)) { + (void) gss_release_cred(&min, &credHandle); + if (do_acquire_cred) { + do_acquire_cred = 0; + goto acquire_cred; + } + (void) close(s); + return (NULL); + } + + (void) close(s); + + (void) gss_release_cred(&min, &credHandle); + return (gss_context); +} + +/* + * dyndns_build_add_remove_msg + * This routine builds the update request message for adding and removing DNS + * entries which is used for non-secure and secure DNS update. + * This routine builds an UDP message. + * Parameters: + * buf : buffer to build message + * update_zone: the type of zone to update, use UPDATE_FORW for forward + * lookup zone, use UPDATE_REV for reverse lookup zone + * hostname : fully qualified hostname to update DNS with + * ip_addr : IP address of hostname + * life_time : cached time of this entry by others and not within DNS + * database + * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry + * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all + * entries of the same resource name. Only valid for UPDATE_DEL. + * addit_cnt : Indicate how many record is in the additional section of + * the DNS message. A value of zero is always used with + * non-secure update message. For secure update message, + * the value will be one because the signed TSIG message + * is added as the additional record of the DNS update message. + * id : DNS message ID. If a positive value then this ID value is + * used, otherwise the next incremented value is used + * level : This is the domain level which we send the request to, level + * zero is the default level, it can go upto 2 in reverse zone + * and virtually to any level in forward zone. + * Returns: + * buf : buffer containing update message + * id : DNS message ID + * int : size of update message + * -1 : error + * + * This function is changed to handle dynamic DNS update retires to higher + * authoritative domains. + */ +static int +dyndns_build_add_remove_msg(char *buf, int update_zone, const char *hostname, + const char *ip_addr, int life_time, int update_type, int del_type, + int addit_cnt, uint16_t *id, int level) +{ + int a, b, c, d; + char *bufptr; + int queryReq, zoneCount, preqCount, updateCount, additionalCount; + char *zone, *resource, *data, zone_buf[100], resrc_buf[100]; + int zoneType, zoneClass, type, class, ttl; + char *p; + + queryReq = REQ_UPDATE; + zoneCount = 1; + preqCount = 0; + updateCount = 1; + additionalCount = addit_cnt; + + (void) memset(buf, 0, NS_PACKETSZ); + bufptr = buf; + + if (*id == 0) + *id = smb_get_next_resid(); + + if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), *id, queryReq, + zoneCount, preqCount, updateCount, additionalCount, 0) == -1) { + return (-1); + } + + zoneType = ns_t_soa; + zoneClass = ns_c_in; + + if (update_zone == UPDATE_FORW) { + p = (char *)hostname; + + /* Try higher domains according to the level requested */ + do { + /* domain */ + if ((zone = (char *)strchr(p, '.')) == NULL) + return (-1); + zone += 1; + p = zone; + } while (--level >= 0); + resource = (char *)hostname; + data = (char *)ip_addr; + } else { + (void) sscanf(ip_addr, "%d.%d.%d.%d", &a, &b, &c, &d); + (void) sprintf(zone_buf, "%d.%d.%d.in-addr.arpa", c, b, a); + zone = p = zone_buf; + + /* Try higher domains according to the level requested */ + while (--level >= 0) { + /* domain */ + if ((zone = (char *)strchr(p, '.')) == NULL) { + return (-1); + } + zone += 1; + p = zone; + } + + (void) sprintf(resrc_buf, "%d.%d.%d.%d.in-addr.arpa", + d, c, b, a); + resource = resrc_buf; /* ip info */ + data = (char *)hostname; + } + + if (dyndns_build_quest_zone(&bufptr, BUFLEN_UDP(bufptr, buf), zone, + zoneType, zoneClass) == -1) { + return (-1); + } + + if (update_zone == UPDATE_FORW) + type = ns_t_a; + else + type = ns_t_ptr; + + if (update_type == UPDATE_ADD) { + class = ns_c_in; + ttl = life_time; + } else { + if (del_type == DEL_ONE) + class = ns_c_none; /* remove one */ + else + class = ns_c_any; /* remove all */ + ttl = 0; + } + if (dyndns_build_update(&bufptr, BUFLEN_UDP(bufptr, buf), + resource, type, class, ttl, data, update_zone, + update_type, del_type) == -1) { + return (-1); + } + + return (bufptr - buf); +} + +/* + * dyndns_build_unsigned_tsig_msg + * This routine is used to build the unsigned TSIG message for signing. The + * unsigned TSIG message contains the update request message with certain TSIG + * fields included. An error time of 300 seconds is used for fudge time. This + * is the number used by Microsoft clients. + * This routine builds a UDP message. + * Parameters: + * buf : buffer to build message + * update_zone: the type of zone to update, use UPDATE_FORW for forward + * lookup zone, use UPDATE_REV for reverse lookup zone + * hostname : fully qualified hostname to update DNS with + * ip_addr : IP address of hostname + * life_time : cached time of this entry by others and not within DNS + * database + * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry + * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all + * entries of the same resource name. Only valid for UPDATE_DEL. + * key_name : same key name used in TKEY message + * id : DNS message ID. If a positive value then this ID value is + * used, otherwise the next incremented value is used + * level : This is the domain level which we send the request to, level + * zero is the default level, it can go upto 2 in reverse zone + * and virtually to any level in forward zone. + * Returns: + * buf : buffer containing update message + * id : DNS message ID + * int : size of update message + * -1 : error + */ +static int +dyndns_build_unsigned_tsig_msg(char *buf, int update_zone, const char *hostname, + const char *ip_addr, int life_time, int update_type, int del_type, + char *key_name, uint16_t *id, int level) +{ + char *bufptr; + int buf_sz; + + if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname, + ip_addr, life_time, update_type, del_type, 0, id, level)) <= 0) { + return (-1); + } + + bufptr = buf + buf_sz; + + if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf), 0, + key_name, 300, NULL, 0, TSIG_UNSIGNED) == -1) { + return (-1); + } + + return (bufptr - buf); +} + +/* + * dyndns_build_signed_tsig_msg + * This routine build the signed TSIG message which contains the update + * request message encrypted. An error time of 300 seconds is used for fudge + * time. This is the number used by Microsoft clients. + * This routine builds a UDP message. + * Parameters: + * buf : buffer to build message + * update_zone: the type of zone to update, use UPDATE_FORW for forward + * lookup zone, use UPDATE_REV for reverse lookup zone + * hostname : fully qualified hostname to update DNS with + * ip_addr : IP address of hostname + * life_time : cached time of this entry by others and not within DNS + * database + * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry + * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all + * entries of the same resource name. Only valid for UPDATE_DEL. + * key_name : same key name used in TKEY message + * id : DNS message ID. If a positive value then this ID value is + * used, otherwise the next incremented value is used + * in_mic : the update request message encrypted + * level : This is the domain level which we send the request to, level + * zero is the default level, it can go upto 2 in reverse zone + * and virtually to any level in forward zone. + * + * Returns: + * buf : buffer containing update message + * id : DNS message ID + * int : size of update message + * -1 : error + */ +static int +dyndns_build_signed_tsig_msg(char *buf, int update_zone, const char *hostname, + const char *ip_addr, int life_time, int update_type, int del_type, + char *key_name, uint16_t *id, gss_buffer_desc *in_mic, int level) +{ + char *bufptr; + int buf_sz; + + if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname, + ip_addr, life_time, update_type, del_type, 1, id, level)) <= 0) { + return (-1); + } + + bufptr = buf + buf_sz; + + if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf), + *id, key_name, 300, in_mic->value, + in_mic->length, TSIG_SIGNED) == -1) { + return (-1); + } + + return (bufptr - buf); +} + +/* + * dyndns_udp_send_recv + * This routine sends and receives UDP DNS request and reply messages. Time + * out value and retry count is indicated by two environment variables: + * lookup_dns_retry_cnt + * lookup_dns_retry_sec + * If either of these two variables are undefined or their value exceed the + * value of 10 then a default value of 3 retry and/or a default value of 3 + * secs are used. + * + * Pre-condition: Caller must call dyndns_open_init_socket() before calling + * this function. + * + * Parameters: + * s : socket descriptor + * buf : buffer containing data to send + * buf_sz : size of data to send + * Returns: + * -1 : error + * rec_buf: reply dat + * 0 : success + */ +int +dyndns_udp_send_recv(int s, char *buf, int buf_sz, char *rec_buf) +{ + int i, retval, addr_len, max_retries; + struct timeval tv, timeout; + fd_set rfds; + struct sockaddr_in from_addr; + char *p; + + smb_config_rdlock(); + p = smb_config_getstr(SMB_CI_DYNDNS_RETRY_COUNT); + if (p == NULL || *p == 0) { + max_retries = 3; + } else { + max_retries = atoi(p); + if (max_retries < 1 || max_retries > 10) + max_retries = 3; + } + + p = smb_config_getstr(SMB_CI_DYNDNS_RETRY_SEC); + timeout.tv_usec = 0; + if (p == NULL || *p == 0) { + timeout.tv_sec = 3; + } else { + timeout.tv_sec = atoi(p); + if (timeout.tv_sec < 1 || timeout.tv_sec > 10) + timeout.tv_sec = 3; + } + smb_config_unlock(); + + for (i = 0; i < max_retries + 1; i++) { + if (send(s, buf, buf_sz, 0) == -1) { + syslog(LOG_ERR, "dyndns: UDP send error (%s)\n", + strerror(errno)); + return (-1); + } + + FD_ZERO(&rfds); + FD_SET(s, &rfds); + + tv = timeout; + + retval = select(s+1, &rfds, NULL, NULL, &tv); + + if (retval == -1) { + return (-1); + } else if (retval > 0) { + bzero(rec_buf, NS_PACKETSZ); + /* required by recvfrom */ + addr_len = sizeof (struct sockaddr_in); + if (recvfrom(s, rec_buf, NS_PACKETSZ, 0, + (struct sockaddr *)&from_addr, &addr_len) == -1) { + syslog(LOG_ERR, "dyndns: UDP recv err\n"); + return (-1); + } + break; + } + } + + if (i == (max_retries + 1)) { /* did not receive anything */ + syslog(LOG_ERR, "dyndns: max retries for UDP recv reached\n"); + return (-1); + } + + return (0); +} + +/* + * dyndns_sec_add_remove_entry + * Perform secure dynamic DNS update after getting security context. + * This routine opens a UDP socket to the DNS sever, gets the security context, + * builds the unsigned TSIG message and signed TSIG message. The signed TSIG + * message containing the encrypted update request message is sent to the DNS + * server. The response is received and check for error. If there is no + * error then credential handle and security context are released and the local + * NSS cached is purged. + * Parameters: + * update_zone : UPDATE_FORW for forward zone, UPDATE_REV for reverse zone + * hostname : fully qualified hostname + * ip_addr : ip address of hostname in string format + * life_time : cached time of this entry by others and not within DNS + * database + * max_retries : maximum retries for sending DNS update request + * recv_timeout: receive timeout + * update_type : UPDATE_ADD for adding entry, UPDATE_DEL for removing entry + * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all + * entries of the same resource name. Only valid for UPDATE_DEL + * dns_str : DNS IP address in string format + * Returns: + * -1: error + * 0: success + * + * This function is enhanced to handle the case of NOTAUTH error when DNS server + * is not authoritative for specified zone. In this case we need to resend the + * same request to the higher authoritative domains. + * This is true for both secure and unsecure dynamic DNS updates. + */ +static int +dyndns_sec_add_remove_entry(int update_zone, const char *hostname, + const char *ip_addr, int life_time, int update_type, int del_type, + char *dns_str) +{ + int s2; + uint16_t id, rid; + char buf[NS_PACKETSZ], buf2[NS_PACKETSZ]; + int ret; + OM_uint32 min, maj; + gss_buffer_desc in_mic, out_mic; + gss_ctx_id_t gss_context; + int dns_ip; + char *key_name; + int buf_sz; + int level = 0; + + assert(dns_str); + assert(*dns_str); + + dns_ip = inet_addr(dns_str); + +sec_retry_higher: + + if ((gss_context = dyndns_get_sec_context(hostname, + dns_ip)) == NULL) { + return (-1); + } + + key_name = (char *)hostname; + + if ((s2 = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0) { + (void) gss_delete_sec_context(&min, &gss_context, NULL); + return (-1); + } + + id = 0; + if ((buf_sz = dyndns_build_unsigned_tsig_msg(buf, update_zone, hostname, + ip_addr, life_time, update_type, del_type, + key_name, &id, level)) <= 0) { + (void) close(s2); + (void) gss_delete_sec_context(&min, &gss_context, NULL); + return (-1); + } + + in_mic.length = buf_sz; + in_mic.value = buf; + + /* sign update message */ + if ((maj = gss_get_mic(&min, gss_context, 0, &in_mic, &out_mic)) != + GSS_S_COMPLETE) { + display_stat(maj, min); + (void) close(s2); + (void) gss_delete_sec_context(&min, &gss_context, NULL); + return (-1); + } + + if ((buf_sz = dyndns_build_signed_tsig_msg(buf, update_zone, hostname, + ip_addr, life_time, update_type, del_type, key_name, &id, + &out_mic, level)) <= 0) { + (void) close(s2); + (void) gss_release_buffer(&min, &out_mic); + (void) gss_delete_sec_context(&min, &gss_context, NULL); + return (-1); + } + + (void) gss_release_buffer(&min, &out_mic); + + if (dyndns_udp_send_recv(s2, buf, buf_sz, buf2)) { + (void) close(s2); + (void) gss_delete_sec_context(&min, &gss_context, NULL); + return (-1); + } + + (void) close(s2); + + (void) gss_delete_sec_context(&min, &gss_context, NULL); + + ret = buf2[3] & 0xf; /* error field in UDP */ + + /* + * If it is a NOTAUTH error we should retry with higher domains + * until we get a successful reply or the maximum retries is met. + */ + if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES) + goto sec_retry_higher; + + /* check here for update request is successful */ + if (ret != NOERROR) { + syslog(LOG_ERR, "dyndns: Error in TSIG reply: %d: ", ret); + dyndns_msg_err(ret); + return (-1); + } + + (void) dyndns_get_nshort(buf2, &rid); + if (id != rid) + return (-1); + + return (0); +} + +/* + * dyndns_seach_entry + * Query DNS server for entry. This routine can indicate if an entry exist + * or not during forward or reverse lookup. Also can indicate if the data + * of the entry matched. For example, for forward lookup, the entry is + * searched using the hostname and the data is the IP address. For reverse + * lookup, the entry is searched using the IP address and the data is the + * hostname. + * Parameters: + * update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone + * hostname : fully qualified hostname + * ip_addr : ip address of hostname in string format + * update_type: UPDATE_ADD for adding entry, UPDATE_DEL for removing entry + * Returns: + * time_out: no use + * is_match: is 1 for found matching entry, otherwise 0 + * 1 : an entry exist but not necessarily match + * 0 : an entry does not exist + */ +/*ARGSUSED*/ +static int +dyndns_search_entry(int update_zone, const char *hostname, const char *ip_addr, + int update_type, struct timeval *time_out, int *is_match) +{ + struct hostent *hentry; + struct in_addr in; + in_addr_t ip; + int i; + + *is_match = 0; + if (update_zone == UPDATE_FORW) { + hentry = gethostbyname(hostname); + if (hentry) { + ip = inet_addr(ip_addr); + for (i = 0; hentry->h_addr_list[i]; i++) { + (void) memcpy(&in.s_addr, + hentry->h_addr_list[i], sizeof (in.s_addr)); + if (ip == in.s_addr) { + *is_match = 1; + break; + } + } + return (1); + } + } else { + int dns_ip = inet_addr(ip_addr); + hentry = gethostbyaddr((char *)&dns_ip, 4, AF_INET); + if (hentry) { + if (strncasecmp(hentry->h_name, hostname, + strlen(hostname)) == 0) { + *is_match = 1; + } + return (1); + } + } + + /* entry does not exist */ + return (0); +} + +/* + * dyndns_add_remove_entry + * Perform non-secure dynamic DNS update. If fail then tries secure update. + * This routine opens a UDP socket to the DNS sever, build the update request + * message, and sends the message to the DNS server. The response is received + * and check for error. If there is no error then the local NSS cached is + * purged. DNS may be used to check to see if an entry already exist before + * adding or to see if an entry does exist before removing it. Adding + * duplicate entries or removing non-existing entries does not cause any + * problems. DNS is not check when doing a delete all. + * Parameters: + * update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone + * hostname : fully qualified hostname + * ip_addr : ip address of hostname in string format + * life_time : cached time of this entry by others and not within DNS + * database + * update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry + * do_check : DNS_CHECK to check first in DNS, DNS_NOCHECK for no DNS + * checking before update + * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all + * entries of the same resource name. Only valid for UPDATE_DEL. + * dns_str : DNS IP address in string format + * Returns: + * -1: error + * 0: success + * + * This function is enhanced to handle the case of NOTAUTH error when DNS server + * is not authoritative for specified zone. In this case we need to resend the + * same request to the higher authoritative domains. + * This is true for both secure and unsecure dynamic DNS updates. + */ +static int +dyndns_add_remove_entry(int update_zone, const char *hostname, + const char *ip_addr, int life_time, int update_type, + int do_check, int del_type, char *dns_str) +{ + int s; + uint16_t id, rid; + char buf[NS_PACKETSZ], buf2[NS_PACKETSZ]; + int ret, dns_ip; + int is_exist, is_match; + struct timeval timeout; + int buf_sz; + int level = 0; + + assert(dns_str); + assert(*dns_str); + + dns_ip = inet_addr(dns_str); + + if (do_check == DNS_CHECK && del_type != DEL_ALL) { + is_exist = dyndns_search_entry(update_zone, hostname, ip_addr, + update_type, &timeout, &is_match); + + if (update_type == UPDATE_ADD && is_exist && is_match) { + return (0); + } else if (update_type == UPDATE_DEL && !is_exist) { + return (0); + } + } + +retry_higher: + if ((s = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0) { + return (-1); + } + + id = 0; + if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname, + ip_addr, life_time, update_type, del_type, 0, &id, level)) <= 0) { + (void) close(s); + return (-1); + } + + if (dyndns_udp_send_recv(s, buf, buf_sz, buf2)) { + (void) close(s); + return (-1); + } + + (void) close(s); + + ret = buf2[3] & 0xf; /* error field in UDP */ + + /* + * If it is a NOTAUTH error we should retry with higher domains + * until we get a successful reply + */ + if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES) + goto retry_higher; + + /* check here for update request is successful */ + if (ret == NOERROR) { + (void) dyndns_get_nshort(buf2, &rid); + if (id != rid) + return (-1); + return (0); + } + + if (ret == NOTIMP) { + syslog(LOG_ERR, "dyndns: DNS does not " + "support dynamic update\n"); + return (-1); + } else if (ret == NOTAUTH) { + syslog(LOG_ERR, "dyndns: DNS is not authoritative for " + "zone name in zone section\n"); + return (-1); + } + + ret = dyndns_sec_add_remove_entry(update_zone, hostname, + ip_addr, life_time, update_type, del_type, dns_str); + + return (ret); +} + +/* + * dyndns_add_entry + * Main routine to add an entry into DNS. The attempt will be made on the + * the servers returned by smb_get_nameserver(). Upon a successful + * attempt on any one of the server, the function will exit with 0. + * Otherwise, -1 is retuned to indicate the update attempt on all the + * nameservers has failed. + * + * Parameters: + * update_zone: the type of zone to update, use UPDATE_FORW for forward + * lookup zone, use UPDATE_REV for reverse lookup zone + * hostname : fully qualified hostname + * ip_addr : ip address of hostname in string format + * life_time : cached time of this entry by others and not within DNS + * database + * Returns: + * -1: error + * 0: success + */ +static int +dyndns_add_entry(int update_zone, const char *hostname, const char *ip_addr, + int life_time) +{ + char *dns_str; + struct in_addr ns_list[MAXNS]; + int i, cnt; + int addr, rc = 0; + + if (hostname == NULL || ip_addr == NULL) { + return (-1); + } + + addr = (int)inet_addr(ip_addr); + if ((addr == -1) || (addr == 0)) { + return (-1); + } + + cnt = smb_get_nameservers(ns_list, MAXNS); + + for (i = 0; i < cnt; i++) { + dns_str = inet_ntoa(ns_list[i]); + if ((dns_str == NULL) || + (strcmp(dns_str, "0.0.0.0") == 0)) { + continue; + } + + if (update_zone == UPDATE_FORW) { + syslog(LOG_DEBUG, "Dynamic update on forward lookup " + "zone for %s (%s)...\n", hostname, ip_addr); + } else { + syslog(LOG_DEBUG, "Dynamic update on reverse lookup " + "zone for %s (%s)...\n", hostname, ip_addr); + } + if (dyndns_add_remove_entry(update_zone, hostname, + ip_addr, life_time, + UPDATE_ADD, DNS_NOCHECK, DEL_NONE, dns_str) != -1) { + rc = 1; + break; + } + } + + return (rc ? 0 : -1); +} + +/* + * dyndns_remove_entry + * Main routine to remove an entry or all entries of the same resource name + * from DNS. The update attempt will be made on the primary DNS server. If + * there is a failure then another attempt will be made on the secondary DNS + * server. + * Parameters: + * update_zone: the type of zone to update, use UPDATE_FORW for forward + * lookup zone, use UPDATE_REV for reverse lookup zone + * hostname : fully qualified hostname + * ip_addr : ip address of hostname in string format + * del_type : DEL_ONE for deleting one entry, DEL_ALL for deleting all + * entries of the same resource name. Only valid for UPDATE_DEL + * Returns: + * -1: error + * 0: success + */ +static int +dyndns_remove_entry(int update_zone, const char *hostname, const char *ip_addr, + int del_type) +{ + char *dns_str; + struct in_addr ns_list[MAXNS]; + int i, cnt, scnt; + int addr; + + if ((hostname == NULL || ip_addr == NULL)) { + return (-1); + } + + addr = (int)inet_addr(ip_addr); + if ((addr == -1) || (addr == 0)) { + return (-1); + } + + cnt = smb_get_nameservers(ns_list, MAXNS); + scnt = 0; + + for (i = 0; i < cnt; i++) { + dns_str = inet_ntoa(ns_list[i]); + if ((dns_str == NULL) || + (strcmp(dns_str, "0.0.0.0") == 0)) { + continue; + } + + if (update_zone == UPDATE_FORW) { + if (del_type == DEL_ONE) { + syslog(LOG_DEBUG, "Dynamic update " + "on forward lookup " + "zone for %s (%s)...\n", hostname, ip_addr); + } else { + syslog(LOG_DEBUG, "Removing all " + "entries of %s " + "in forward lookup zone...\n", hostname); + } + } else { + if (del_type == DEL_ONE) { + syslog(LOG_DEBUG, "Dynamic update " + "on reverse lookup " + "zone for %s (%s)...\n", hostname, ip_addr); + } else { + syslog(LOG_DEBUG, "Removing all " + "entries of %s " + "in reverse lookup zone...\n", ip_addr); + } + } + if (dyndns_add_remove_entry(update_zone, hostname, ip_addr, 0, + UPDATE_DEL, DNS_NOCHECK, del_type, dns_str) != -1) { + scnt++; + break; + } + } + if (scnt) + return (0); + return (-1); +} + +/* + * dyndns_update + * Perform dynamic update on both forward and reverse lookup zone using + * current hostname and IP addresses. Before updating DNS, existing host + * entries with the same hostname in the forward lookup zone are removed + * and existing pointer entries with the same IP addresses in the reverse + * lookup zone are removed. After DNS update, host entries for current + * hostname will show current IP addresses and pointer entries for current + * IP addresses will show current hostname. + * Parameters: + * None + * Returns: + * -1: some dynamic DNS updates errors + * 0: successful + */ +int +dyndns_update(void) +{ + int i, forw_update_ok, error; + char fqdn[MAXHOSTNAMELEN]; + char *my_ip; + int nc_cnt; + struct in_addr addr; + int rc; + + if (!dyndns_enabled()) + return (-1); + + if (smb_getfqhostname(fqdn, MAXHOSTNAMELEN) != 0) + return (-1); + + nc_cnt = smb_nic_get_num(); + + error = 0; + forw_update_ok = 0; + + /* + * Dummy IP is okay since we are removing all using the hostname. + */ + if (dyndns_remove_entry(UPDATE_FORW, fqdn, "1.1.1.1", DEL_ALL) == 0) { + forw_update_ok = 1; + } else { + error++; + } + + for (i = 0; i < nc_cnt; i++) { + net_cfg_t cfg; + if (smb_nic_get_byind(i, &cfg) == NULL) + break; + addr.s_addr = cfg.ip; + if (addr.s_addr == 0) + continue; + if (smb_nic_status(cfg.ifname, IFF_STANDBY) || + smb_nic_status(cfg.ifname, IFF_PRIVATE)) + continue; + + my_ip = (char *)strdup(inet_ntoa(addr)); + if (my_ip == NULL) { + error++; + continue; + } + + if (forw_update_ok) { + rc = dyndns_add_entry(UPDATE_FORW, fqdn, my_ip, + DDNS_TTL); + + if (rc == -1) + error++; + } + + rc = dyndns_remove_entry(UPDATE_REV, fqdn, my_ip, DEL_ALL); + if (rc == 0) { + rc = dyndns_add_entry(UPDATE_REV, fqdn, my_ip, + DDNS_TTL); + } + + if (rc == -1) + error++; + + (void) free(my_ip); + } + + return ((error == 0) ? 0 : -1); +} + +/* + * dyndns_clear_rev_zone + * Clear the rev zone records. Must be called to clear the OLD if list + * of down records prior to updating the list with new information. + * + * Parameters: + * None + * Returns: + * -1: some dynamic DNS updates errors + * 0: successful + */ +int +dyndns_clear_rev_zone(void) +{ + int i, error; + char fqdn[MAXHOSTNAMELEN]; + char *my_ip; + int nc_cnt; + struct in_addr addr; + int rc; + + if (!dyndns_enabled()) + return (-1); + + if (smb_getfqhostname(fqdn, MAXHOSTNAMELEN) != 0) + return (-1); + + nc_cnt = smb_nic_get_num(); + + error = 0; + + for (i = 0; i < nc_cnt; i++) { + net_cfg_t cfg; + if (smb_nic_get_byind(i, &cfg) == NULL) + break; + addr.s_addr = cfg.ip; + if (addr.s_addr == 0) + continue; + if (smb_nic_status(cfg.ifname, IFF_STANDBY) || + smb_nic_status(cfg.ifname, IFF_PRIVATE)) + continue; + + my_ip = (char *)strdup(inet_ntoa(addr)); + if (my_ip == NULL) { + error++; + continue; + } + + rc = dyndns_remove_entry(UPDATE_REV, fqdn, my_ip, DEL_ALL); + if (rc != 0) + error++; + + (void) free(my_ip); + } + + return ((error == 0) ? 0 : -1); +} diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.h b/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.h new file mode 100644 index 000000000000..7140eb4d596e --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.h @@ -0,0 +1,205 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_DYNDNS_H +#define _SMBSRV_DYNDNS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +/* + * Header section format: + * + * The header contains the following fields: + * + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ID | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |QR| Opcode |AA|TC|RD|RA| Z | RCODE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QDCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ANCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | NSCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ARCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * where: + * + * ID A 16 bit identifier assigned by the program that + * generates any kind of query. This identifier is copied + * the corresponding reply and can be used by the requester + * to match up replies to outstanding queries. + * + * QR A one bit field that specifies whether this message is a + * query (0), or a response (1). + * + * OPCODE A four bit field that specifies kind of query in this + * message. This value is set by the originator of a query + * and copied into the response. The values are: + * + * 0 a standard query (QUERY) + * + * 1 an inverse query (IQUERY) + * + * 2 a server status request (STATUS) + * + * 3-15 reserved for future use + * + * AA Authoritative Answer - this bit is valid in responses, + * and specifies that the responding name server is an + * authority for the domain name in question section. + * + * Note that the contents of the answer section may have + * multiple owner names because of aliases. The AA bit + * + * corresponds to the name which matches the query name, or + * the first owner name in the answer section. + * + * TC TrunCation - specifies that this message was truncated + * due to length greater than that permitted on the + * transmission channel. + * + * RD Recursion Desired - this bit may be set in a query and + * is copied into the response. If RD is set, it directs + * the name server to pursue the query recursively. + * Recursive query support is optional. + * + * RA Recursion Available - this be is set or cleared in a + * response, and denotes whether recursive query support is + * available in the name server. + * + * Z Reserved for future use. Must be zero in all queries + * and responses. + * + * RCODE Response code - this 4 bit field is set as part of + * responses. The values have the following + * interpretation: + * + * 0 No error condition + * + * 1 Format error - The name server was + * unable to interpret the query. + * + * 2 Server failure - The name server was + * unable to process this query due to a + * problem with the name server. + * + * 3 Name Error - Meaningful only for + * responses from an authoritative name + * server, this code signifies that the + * domain name referenced in the query does + * not exist. + * + * 4 Not Implemented - The name server does + * not support the requested kind of query. + * + * 5 Refused - The name server refuses to + * perform the specified operation for + * policy reasons. For example, a name + * server may not wish to provide the + * information to the particular requester, + * or a name server may not wish to perform + * a particular operation (e.g., zone + * + * transfer) for particular data. + * + * 6-15 Reserved for future use. + * + * QDCOUNT an unsigned 16 bit integer specifying the number of + * entries in the question section. + * + * ANCOUNT an unsigned 16 bit integer specifying the number of + * resource records in the answer section. + * + * NSCOUNT an unsigned 16 bit integer specifying the number of name + * server resource records in the authority records + * section. + * + * ARCOUNT an unsigned 16 bit integer specifying the number of + * resource records in the additional records section. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Other definitions: */ +#define REQ_QUERY 1 /* DNS query request */ +#define REQ_UPDATE 0 /* DNS update request */ +#define UPDATE_FORW 1 /* Update forward lookup zone */ +#define UPDATE_REV 0 /* Update reverse lookup zone */ +#define UPDATE_ADD 1 /* Update add request */ +#define UPDATE_DEL 0 /* Update remove request */ +#define MODE_GSS_API 3 /* Key negotiation mode */ + +/* Max buffer size for send and receive buffer */ +#define MAX_BUF_SIZE 2000 +#define MAX_RETRIES 3 /* Max number of send retries if no response */ +#define TSIG_SIGNED 1 /* TSIG contains signed data */ +#define TSIG_UNSIGNED 0 /* TSIG does not conain signed data */ +#define DNS_CHECK 1 /* Check DNS for entry */ +#define DNS_NOCHECK 0 /* Don't check DNS for entry */ +#define MAX_TCP_SIZE 2000 /* max tcp DNS message size */ + +/* Delete 1 entry */ +#define DEL_ONE 1 +/* Delete all entries of the same resource name */ +#define DEL_ALL 0 + +#define DNSF_RECUR_SUPP 0x80 /* Server can do recursive queries */ +#define DNSF_RECUR_QRY 0x100 /* Query is recursive */ + +#define BUFLEN_TCP(x, y) (MAX_TCP_SIZE-(x-y)) +#define BUFLEN_UDP(x, y) (NS_PACKETSZ-(x-y)) + +extern char *dyndns_get_nshort(char *, uint16_t *); +extern char *dyndns_get_int(char *, int *); +extern int dyndns_build_header(char **, int, uint16_t, int, + uint16_t, uint16_t, uint16_t, uint16_t, int); +extern int dyndns_build_quest_zone(char **, int, char *, int, int); +extern int dyndns_open_init_socket(int sock_type, unsigned long dest_addr, + int port); +extern int dyndns_udp_send_recv(int, char *, int, char *); +extern void dyndns_msg_err(int); + +/* + * DDNS_TTL is the time to live in DNS caches. Note that this + * does not affect the entry in the authoritative DNS database. + */ +#define DDNS_TTL 1200 + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_DYNDNS_H */ diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c new file mode 100644 index 000000000000..c1a2de791303 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c @@ -0,0 +1,543 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Copyright 1990 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * Initialize a credentials cache. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static int krb5_acquire_cred_kinit_main(); + +typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type; + +struct k_opts { + /* in seconds */ + krb5_deltat starttime; + krb5_deltat lifetime; + krb5_deltat rlife; + + int forwardable; + int proxiable; + int addresses; + + int not_forwardable; + int not_proxiable; + int no_addresses; + + int verbose; + + char *principal_name; + char *principal_passwd; + char *service_name; + char *keytab_name; + char *k5_cache_name; + char *k4_cache_name; + + action_type action; +}; + +struct k5_data { + krb5_context ctx; + krb5_ccache cc; + krb5_principal me; + char *name; +}; + +static int +k5_begin(struct k_opts *opts, struct k5_data *k5) +{ + int code; + code = krb5_init_context(&k5->ctx); + if (code) { + return (code); + } + + if ((code = krb5_cc_default(k5->ctx, &k5->cc))) { + return (code); + } + + /* Use specified name */ + if ((code = krb5_parse_name(k5->ctx, opts->principal_name, &k5->me))) { + return (code); + } + + code = krb5_unparse_name(k5->ctx, k5->me, &k5->name); + if (code) { + return (code); + } + opts->principal_name = k5->name; + + return (0); +} + +static void +k5_end(struct k5_data *k5) +{ + if (k5->name) + krb5_free_unparsed_name(k5->ctx, k5->name); + if (k5->me) + krb5_free_principal(k5->ctx, k5->me); + if (k5->cc) + krb5_cc_close(k5->ctx, k5->cc); + if (k5->ctx) + krb5_free_context(k5->ctx); + (void) memset(k5, 0, sizeof (*k5)); +} + +static int +k5_kinit(struct k_opts *opts, struct k5_data *k5) +{ + int notix = 1; + krb5_keytab keytab = 0; + krb5_creds my_creds; + krb5_error_code code = 0; + krb5_get_init_creds_opt options; + const char *errmsg; + + krb5_get_init_creds_opt_init(&options); + (void) memset(&my_creds, 0, sizeof (my_creds)); + + /* + * From this point on, we can goto cleanup because my_creds is + * initialized. + */ + if (opts->lifetime) + krb5_get_init_creds_opt_set_tkt_life(&options, opts->lifetime); + if (opts->rlife) + krb5_get_init_creds_opt_set_renew_life(&options, opts->rlife); + if (opts->forwardable) + krb5_get_init_creds_opt_set_forwardable(&options, 1); + if (opts->not_forwardable) + krb5_get_init_creds_opt_set_forwardable(&options, 0); + if (opts->proxiable) + krb5_get_init_creds_opt_set_proxiable(&options, 1); + if (opts->not_proxiable) + krb5_get_init_creds_opt_set_proxiable(&options, 0); + if (opts->addresses) { + krb5_address **addresses = NULL; + code = krb5_os_localaddr(k5->ctx, &addresses); + if (code != 0) { + errmsg = error_message(code); + syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "k5_kinit: " + "getting local addresses (%s)"), errmsg); + goto cleanup; + } + krb5_get_init_creds_opt_set_address_list(&options, addresses); + } + if (opts->no_addresses) + krb5_get_init_creds_opt_set_address_list(&options, NULL); + + if ((opts->action == INIT_KT) && opts->keytab_name) { + code = krb5_kt_resolve(k5->ctx, opts->keytab_name, &keytab); + if (code != 0) { + errmsg = error_message(code); + syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "k5_kinit: " + "resolving keytab %s (%s)"), errmsg, + opts->keytab_name); + goto cleanup; + } + } + + switch (opts->action) { + case INIT_PW: + code = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me, + opts->principal_passwd, NULL, 0, opts->starttime, + opts->service_name, &options); + break; + case INIT_KT: + code = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me, + keytab, opts->starttime, opts->service_name, &options); + break; + case VALIDATE: + code = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me, + k5->cc, opts->service_name); + break; + case RENEW: + code = krb5_get_renewed_creds(k5->ctx, &my_creds, k5->me, + k5->cc, opts->service_name); + break; + } + + if (code) { + char *doing = 0; + switch (opts->action) { + case INIT_PW: + case INIT_KT: + doing = dgettext(TEXT_DOMAIN, "k5_kinit: " + "getting initial credentials"); + break; + case VALIDATE: + doing = dgettext(TEXT_DOMAIN, "k5_kinit: " + "validating credentials"); + break; + case RENEW: + doing = dgettext(TEXT_DOMAIN, "k5_kinit: " + "renewing credentials"); + break; + } + + /* + * If got code == KRB5_AP_ERR_V4_REPLY && got_k4, we should + * let the user know that maybe he/she wants -4. + */ + if (code == KRB5KRB_AP_ERR_V4_REPLY) { + syslog(LOG_ERR, "%s\n" + "The KDC doesn't support v5. " + "You may want the -4 option in the future", doing); + return (1); + } else if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) { + syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "%s " + "(Password incorrect)"), doing); + } else { + errmsg = error_message(code); + syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "%s (%s)"), + doing, errmsg); + } + goto cleanup; + } + + if (!opts->lifetime) { + /* We need to figure out what lifetime to use for Kerberos 4. */ + opts->lifetime = my_creds.times.endtime - + my_creds.times.authtime; + } + + code = krb5_cc_initialize(k5->ctx, k5->cc, k5->me); + if (code) { + errmsg = error_message(code); + syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "k5_kinit: " + "initializing cache %s (%s)"), + opts->k5_cache_name?opts->k5_cache_name:"", errmsg); + goto cleanup; + } + + code = krb5_cc_store_cred(k5->ctx, k5->cc, &my_creds); + if (code) { + errmsg = error_message(code); + syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "k5_kinit: " + "storing credentials (%s)"), errmsg); + goto cleanup; + } + + notix = 0; + + cleanup: + if (my_creds.client == k5->me) { + my_creds.client = 0; + } + krb5_free_cred_contents(k5->ctx, &my_creds); + if (keytab) + krb5_kt_close(k5->ctx, keytab); + return (notix?0:1); +} + +int +smb_kinit(char *user, char *passwd) +{ + struct k_opts opts; + struct k5_data k5; + int authed_k5 = 0; + + (void) memset(&opts, 0, sizeof (opts)); + opts.action = INIT_PW; + opts.principal_name = strdup(user); + opts.principal_passwd = strdup(passwd); + + (void) memset(&k5, 0, sizeof (k5)); + + if (k5_begin(&opts, &k5) != 0) { + syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "smb_kinit: " + "NOT Authenticated to Kerberos v5 k5_begin failed\n")); + return (0); + } + + authed_k5 = k5_kinit(&opts, &k5); + if (authed_k5) { + syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN, "smb_kinit: " + "Authenticated to Kerberos v5\n")); + } else { + syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN, "smb_kinit: " + "NOT Authenticated to Kerberos v5\n")); + } + + k5_end(&k5); + + return (authed_k5); +} + +/* + * krb5_display_stat + * Display error message for GSS-API routines. + * Parameters: + * maj : GSS major status + * min : GSS minor status + * caller_mod: module name that calls this routine so that the module name + * can be displayed with the error messages + * Returns: + * None + */ +static void +krb5_display_stat(OM_uint32 maj, OM_uint32 min, char *caller_mod) +{ + gss_buffer_desc msg; + OM_uint32 msg_ctx = 0; + OM_uint32 min2; + (void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID, + &msg_ctx, &msg); + syslog(LOG_ERR, "%s: major status error: %s\n", + caller_mod, (char *)msg.value); + (void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID, + &msg_ctx, &msg); + syslog(LOG_ERR, "%s: minor status error: %s\n", + caller_mod, (char *)msg.value); +} + +/* + * krb5_acquire_cred_kinit + * + * Wrapper for krb5_acquire_cred_kinit_main with mutex to protect credential + * cache file when calling krb5_acquire_cred or kinit. + */ + +int +krb5_acquire_cred_kinit(char *user, char *pwd, gss_cred_id_t *cred_handle, + gss_OID *oid, int *kinit_retry, char *caller_mod) +{ + int ret; + + ret = krb5_acquire_cred_kinit_main(user, pwd, + cred_handle, oid, kinit_retry, caller_mod); + return (ret); +} + +/* + * krb5_acquire_cred_kinit_main + * + * This routine is called both by ADS and Dyn DNS modules to get a handle to + * administrative user's credential stored locally on the system. The + * credential is the TGT. If the attempt at getting handle fails then a second + * attempt will be made after getting a new TGT. + * + * Paramters: + * user : username to retrieve a handle to its credential + * pwd : password of username in case obtaining a new TGT is needed + * kinit_retry: if 0 then a second attempt will be made to get handle to the + * credential if the first attempt fails + * caller_mod : name of module that call this routine so that the module name + * can be included with error messages + * Returns: + * cred_handle: handle to the administrative user's credential (TGT) + * oid : contains Kerberos 5 object identifier + * kinit_retry: A 1 indicates that a second attempt has been made to get + * handle to the credential and no further attempts can be made + * -1 : error + * 0 : success + */ +static int +krb5_acquire_cred_kinit_main(char *user, char *pwd, gss_cred_id_t *cred_handle, + gss_OID *oid, int *kinit_retry, char *caller_mod) +{ + OM_uint32 maj, min; + gss_name_t desired_name; + gss_OID_set desired_mechs; + gss_buffer_desc oidstr, name_buf; + char str[50], user_name[50]; + + acquire_cred: + + /* Object Identifier for Kerberos 5 */ + (void) strcpy(str, "{ 1 2 840 113554 1 2 2 }"); + oidstr.value = str; + oidstr.length = strlen(str); + if ((maj = gss_str_to_oid(&min, &oidstr, oid)) != GSS_S_COMPLETE) { + krb5_display_stat(maj, min, caller_mod); + return (-1); + } + if ((maj = gss_create_empty_oid_set(&min, &desired_mechs)) + != GSS_S_COMPLETE) { + krb5_display_stat(maj, min, caller_mod); + (void) gss_release_oid(&min, oid); + return (-1); + } + if ((maj = gss_add_oid_set_member(&min, *oid, &desired_mechs)) + != GSS_S_COMPLETE) { + krb5_display_stat(maj, min, caller_mod); + (void) gss_release_oid(&min, oid); + (void) gss_release_oid_set(&min, &desired_mechs); + return (-1); + } + + (void) strcpy(user_name, user); + name_buf.value = user_name; + name_buf.length = strlen(user_name)+1; + if ((maj = gss_import_name(&min, &name_buf, GSS_C_NT_USER_NAME, + &desired_name)) != GSS_S_COMPLETE) { + krb5_display_stat(maj, min, caller_mod); + (void) gss_release_oid(&min, oid); + (void) gss_release_oid_set(&min, &desired_mechs); + return (-1); + } + + if ((maj = gss_acquire_cred(&min, desired_name, 0, desired_mechs, + GSS_C_INITIATE, cred_handle, NULL, NULL)) != GSS_S_COMPLETE) { + if (!*kinit_retry) { + (void) gss_release_oid(&min, oid); + (void) gss_release_oid_set(&min, &desired_mechs); + (void) gss_release_name(&min, &desired_name); + syslog(LOG_ERR, "%s: Retry kinit to " + "acquire credential.\n", caller_mod); + (void) smb_kinit(user, pwd); + *kinit_retry = 1; + goto acquire_cred; + } else { + krb5_display_stat(maj, min, caller_mod); + (void) gss_release_oid(&min, oid); + (void) gss_release_oid_set(&min, &desired_mechs); + (void) gss_release_name(&min, &desired_name); + return (-1); + } + } + (void) gss_release_oid_set(&min, &desired_mechs); + (void) gss_release_name(&min, &desired_name); + + return (0); +} + +/* + * krb5_establish_sec_ctx_kinit + * + * This routine is called by both the ADS and Dyn DNS modules to establish a + * security context before ADS or Dyn DNS updates are allowed. If establishing + * a security context fails for any reason, a second attempt will be made after + * a new TGT is obtained. This routine is called many time as needed until + * a security context is established. + * + * The resources use for the security context must be released if security + * context establishment process fails. + * Parameters: + * user : user used in establishing a security context for. Is used for + * obtaining a new TGT for a second attempt at establishing + * security context + * pwd : password of above user + * cred_handle: a handle to the user credential (TGT) stored locally + * gss_context: initially set to GSS_C_NO_CONTEXT but will contain a handle + * to a security context + * target_name: contains service name to establish a security context with, + * ie ldap or dns + * gss_flags : flags used in establishing security context + * inputptr : initially set to GSS_C_NO_BUFFER but will be token data + * received from service's server to be processed to generate + * further token to be sent back to service's server during + * security context establishment + * kinit_retry: if 0 then a second attempt will be made to get handle to the + * credential if the first attempt fails + * caller_mod : name of module that call this routine so that the module name + * can be included with error messages + * Returns: + * gss_context : a handle to a security context + * out_tok : token data to be sent to service's server to establish + * security context + * ret_flags : return flags + * time_rec : valid time for security context, not currently used + * kinit_retry : A 1 indicates that a second attempt has been made to get + * handle to the credential and no further attempts can be + * made + * do_acquire_cred: A 1 indicates that a new handle to the local credential + * is needed for second attempt at security context + * establishment + * maj : major status code used if determining is security context + * establishment is successful + */ +int +krb5_establish_sec_ctx_kinit(char *user, char *pwd, + gss_cred_id_t cred_handle, gss_ctx_id_t *gss_context, + gss_name_t target_name, gss_OID oid, int gss_flags, + gss_buffer_desc *inputptr, gss_buffer_desc* out_tok, + OM_uint32 *ret_flags, OM_uint32 *time_rec, + int *kinit_retry, int *do_acquire_cred, + OM_uint32 *maj, char *caller_mod) +{ + OM_uint32 min; + + *maj = gss_init_sec_context(&min, cred_handle, gss_context, + target_name, oid, gss_flags, 0, NULL, inputptr, NULL, + out_tok, ret_flags, time_rec); + if (*maj != GSS_S_COMPLETE && *maj != GSS_S_CONTINUE_NEEDED) { + if (*gss_context != NULL) + (void) gss_delete_sec_context(&min, gss_context, NULL); + + if (!*kinit_retry) { + syslog(LOG_ERR, "%s: Retry kinit to establish " + "security context.\n", caller_mod); + (void) smb_kinit(user, pwd); + *kinit_retry = 1; + *do_acquire_cred = 1; + return (-1); + } else { + krb5_display_stat(*maj, min, caller_mod); + return (-1); + } + } + return (0); +} diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h new file mode 100644 index 000000000000..22028fcdb541 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h @@ -0,0 +1,64 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_SMB_KRB5_H +#define _SMBSRV_SMB_KRB5_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern gss_OID gss_nt_user_name; +extern gss_OID gss_nt_machine_uid_name; +extern gss_OID gss_nt_string_uid_name; +extern gss_OID gss_nt_service_name; +extern gss_OID gss_nt_exported_name; +extern gss_OID gss_nt_service_name_v2; + +int krb5_acquire_cred_kinit(char *, char *, gss_cred_id_t *, + gss_OID *, int *, char *); +int krb5_establish_sec_ctx_kinit(char *, char *, gss_cred_id_t, + gss_ctx_id_t *, gss_name_t, gss_OID, int, gss_buffer_desc *, + gss_buffer_desc *, OM_uint32 *, OM_uint32 *, int *, + int *, OM_uint32 *, char *); +int smb_krb5_ctx_init(krb5_context *ctx); +void smb_krb5_ctx_fini(krb5_context ctx); +int smb_krb5_get_principal(krb5_context ctx, char *princ_str, + krb5_principal *princ); +int smb_krb5_setpwd(krb5_context ctx, krb5_principal princ, char *passwd); +int smb_krb5_write_keytab(krb5_context ctx, krb5_principal princ, + char *fname, krb5_kvno kvno, char *passwd, krb5_enctype *enctypes, + int enctype_count); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_SMB_KRB5_H */ diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c new file mode 100644 index 000000000000..a6c99799d173 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c @@ -0,0 +1,242 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int smb_krb5_ktadd(krb5_context ctx, krb5_keytab kt, + const krb5_principal princ, krb5_enctype enctype, krb5_kvno kvno, + const char *pw); + +/* + * smb_krb5_ctx_init + * + * Initialize the kerberos context. + * Return 0 on success. Otherwise, return -1. + */ +int +smb_krb5_ctx_init(krb5_context *ctx) +{ + if (krb5_init_context(ctx) != 0) + return (-1); + + return (0); +} + +/* + * smb_krb5_get_principal + * + * Setup the krb5_principal given the host principal in string format. + * Return 0 on success. Otherwise, return -1. + */ +int +smb_krb5_get_principal(krb5_context ctx, char *princ_str, krb5_principal *princ) +{ + if (krb5_parse_name(ctx, princ_str, princ) != 0) + return (-1); + + return (0); +} + +/* + * smb_krb5_ctx_fini + * + * Free the kerberos context. + */ +void +smb_krb5_ctx_fini(krb5_context ctx) +{ + krb5_free_context(ctx); +} + +/* + * smb_ksetpw + * + * Set the workstation trust account password. + * Returns 0 on success. Otherwise, returns non-zero value. + */ +int +smb_krb5_setpwd(krb5_context ctx, krb5_principal princ, char *passwd) +{ + krb5_error_code code; + krb5_ccache cc = NULL; + int result_code; + krb5_data result_code_string, result_string; + + (void) memset(&result_code_string, 0, sizeof (result_code_string)); + (void) memset(&result_string, 0, sizeof (result_string)); + + if ((code = krb5_cc_default(ctx, &cc)) != 0) { + syslog(LOG_ERR, "smb_krb5_setpwd: failed to find a ccache\n"); + return (-1); + } + + code = krb5_set_password_using_ccache(ctx, cc, passwd, princ, + &result_code, &result_code_string, &result_string); + + krb5_cc_close(ctx, cc); + + if (code != 0) + (void) syslog(LOG_ERR, + "smb_krb5_setpwd: Result: %.*s (%d) %.*s\n", + result_code == 0 ? + strlen("success") : result_code_string.length, + result_code == 0 ? "success" : result_code_string.data, + result_code, result_string.length, result_string.data); + return (code); +} + +/* + * smb_krb5_write_keytab + * + * Write all the Kerberos keys to the keytab file. + * Returns 0 on success. Otherwise, returns -1. + */ +int +smb_krb5_write_keytab(krb5_context ctx, krb5_principal princ, char *fname, + krb5_kvno kvno, char *passwd, krb5_enctype *enctypes, int enctype_count) +{ + krb5_keytab kt = NULL; + char *ktname; + int i, len; + int rc = 0; + struct stat fstat; + + if (stat(fname, &fstat) == 0) { + if (remove(fname) != 0) { + syslog(LOG_ERR, "smb_krb5_write_keytab: cannot remove" + " existing keytab"); + return (-1); + } + } + + len = snprintf(NULL, 0, "WRFILE:%s", fname) + 1; + if ((ktname = malloc(len)) == NULL) { + syslog(LOG_ERR, "smb_krb5_write_keytab: resource shortage"); + return (-1); + } + + (void) snprintf(ktname, len, "WRFILE:%s", fname); + + if (krb5_kt_resolve(ctx, ktname, &kt) != 0) { + syslog(LOG_ERR, "smb_krb5_write_keytab: failed to open/create " + "keytab %s\n", fname); + free(ktname); + return (-1); + } + + free(ktname); + + for (i = 0; i < enctype_count; i++) { + if (smb_krb5_ktadd(ctx, kt, princ, enctypes[i], kvno, passwd) + != 0) { + rc = -1; + break; + } + + } + + if (kt != NULL) + krb5_kt_close(ctx, kt); + + return (rc); +} + +/* + * smb_krb5_ktadd + * + * Add a Keberos key to the keytab file. + * Returns 0 on success. Otherwise, returns -1. + */ +static int +smb_krb5_ktadd(krb5_context ctx, krb5_keytab kt, const krb5_principal princ, + krb5_enctype enctype, krb5_kvno kvno, const char *pw) +{ + krb5_keytab_entry *entry; + krb5_data password, salt; + krb5_keyblock key; + krb5_error_code code; + char buf[100]; + int rc = 0; + + if ((code = krb5_enctype_to_string(enctype, buf, sizeof (buf)))) { + syslog(LOG_ERR, "smb_krb5_ktadd[%d]: unknown enctype", + enctype); + return (-1); + } + + if ((entry = (krb5_keytab_entry *) malloc(sizeof (*entry))) == NULL) { + syslog(LOG_ERR, "smb_krb5_ktadd[%d]: resource shortage", + enctype); + return (-1); + } + + (void) memset((char *)entry, 0, sizeof (*entry)); + + password.length = strlen(pw); + password.data = (char *)pw; + + if ((code = krb5_principal2salt(ctx, princ, &salt)) != 0) { + syslog(LOG_ERR, "smb_krb5_ktadd[%d]: failed to compute salt", + enctype); + free(entry); + return (-1); + } + + code = krb5_c_string_to_key(ctx, enctype, &password, &salt, &key); + krb5_xfree(salt.data); + if (code != 0) { + syslog(LOG_ERR, "smb_krb5_ktadd[%d]: failed to generate key", + enctype); + free(entry); + return (-1); + } + + (void) memcpy(&entry->key, &key, sizeof (krb5_keyblock)); + entry->vno = kvno; + entry->principal = princ; + + if ((code = krb5_kt_add_entry(ctx, kt, entry)) != 0) { + syslog(LOG_ERR, "smb_krb5_ktadd[%d] failed to add entry to " + "keytab (%d)", enctype, code); + rc = -1; + } + + free(entry); + return (rc); +} diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios.c new file mode 100644 index 000000000000..7b165e3b447d --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios.c @@ -0,0 +1,300 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Main startup code for SMB/NETBIOS and some utility routines + * for the NETBIOS layer. + */ + +#include +#include +#include +#include +#include +#include + +#include + +netbios_status_t nb_status; + +static pthread_t smb_nbns_thr; /* name service */ +static pthread_t smb_nbds_thr; /* dgram service */ +static pthread_t smb_nbts_thr; /* timer */ +static pthread_t smb_nbbs_thr; /* browser */ + +static void *smb_netbios_timer(void *); + +void +smb_netbios_chg_status(uint32_t status, int set) +{ + (void) mutex_lock(&nb_status.mtx); + if (set) + nb_status.state |= status; + else + nb_status.state &= ~status; + (void) cond_broadcast(&nb_status.cv); + (void) mutex_unlock(&nb_status.mtx); +} + +void +smb_netbios_shutdown(void) +{ + smb_netbios_chg_status(NETBIOS_SHUTTING_DOWN, 1); + + (void) pthread_join(smb_nbts_thr, 0); + (void) pthread_join(smb_nbbs_thr, 0); + (void) pthread_join(smb_nbns_thr, 0); + (void) pthread_join(smb_nbds_thr, 0); + + nb_status.state = NETBIOS_SHUT_DOWN; +} + +void +smb_netbios_start() +{ + int rc; + mutex_t *mp; + cond_t *cvp; + + /* Startup Netbios named; port 137 */ + rc = pthread_create(&smb_nbns_thr, 0, + smb_netbios_name_service_daemon, 0); + if (rc) + return; + + mp = &nb_status.mtx; + cvp = &nb_status.cv; + + (void) mutex_lock(mp); + + while (!(nb_status.state & (NETBIOS_NAME_SVC_RUNNING | + NETBIOS_NAME_SVC_FAILED))) { + (void) cond_wait(cvp, mp); + } + + if (nb_status.state & NETBIOS_NAME_SVC_FAILED) { + (void) mutex_unlock(mp); + (void) fprintf(stderr, + "smbd: Netbios Name service startup failed!"); + smb_netbios_shutdown(); + return; + } + (void) mutex_unlock(mp); + + (void) fprintf(stderr, "smbd: Netbios Name service started."); + smb_netbios_name_config(); + + /* Startup Netbios datagram service; port 138 */ + rc = pthread_create(&smb_nbds_thr, 0, + smb_netbios_datagram_service_daemon, 0); + if (rc == 0) { + (void) mutex_lock(mp); + while (!(nb_status.state & (NETBIOS_DATAGRAM_SVC_RUNNING | + NETBIOS_DATAGRAM_SVC_FAILED))) { + (void) cond_wait(cvp, mp); + } + + if (nb_status.state & NETBIOS_DATAGRAM_SVC_FAILED) { + (void) mutex_unlock(mp); + (void) fprintf(stderr, "smbd: Netbios Datagram service " + "startup failed!"); + smb_netbios_shutdown(); + return; + } + (void) mutex_unlock(mp); + } else { + smb_netbios_shutdown(); + return; + } + + (void) fprintf(stderr, "smbd: Netbios Datagram service started."); + + /* Startup Netbios browser service */ + rc = pthread_create(&smb_nbbs_thr, 0, smb_browser_daemon, 0); + if (rc) { + smb_netbios_shutdown(); + return; + } + + (void) fprintf(stderr, "smbd: Netbios Browser client started."); + + /* Startup Our internal, 1 second resolution, timer */ + rc = pthread_create(&smb_nbts_thr, 0, smb_netbios_timer, 0); + if (rc == 0) { + (void) mutex_lock(mp); + while (!(nb_status.state & (NETBIOS_TIMER_RUNNING | + NETBIOS_TIMER_FAILED))) { + (void) cond_wait(cvp, mp); + } + + if (nb_status.state & NETBIOS_TIMER_FAILED) { + (void) mutex_unlock(mp); + smb_netbios_shutdown(); + return; + } + (void) mutex_unlock(mp); + } else { + smb_netbios_shutdown(); + return; + } + + (void) fprintf(stderr, "smbd: Netbios Timer service started."); +} + +/*ARGSUSED*/ +static void * +smb_netbios_timer(void *arg) +{ + static unsigned int ticks; + + smb_netbios_chg_status(NETBIOS_TIMER_RUNNING, 1); + + while ((nb_status.state & NETBIOS_SHUTTING_DOWN) == 0) { + (void) sleep(1); + + if (nb_status.state & NETBIOS_DATAGRAM_SVC_RUNNING) + smb_netbios_datagram_tick(); + else + break; + + if (nb_status.state & NETBIOS_NAME_SVC_RUNNING) { + smb_netbios_name_tick(); + + /* every 10 minutes */ + if ((ticks % 600) == 0) + smb_netbios_cache_clean(); + } + else + break; + } + + nb_status.state &= ~NETBIOS_TIMER_RUNNING; + if ((nb_status.state & NETBIOS_SHUTTING_DOWN) == 0) { + /* either name or datagram service has failed */ + smb_netbios_shutdown(); + } + + return (0); +} + +int +smb_first_level_name_encode(struct name_entry *name, + unsigned char *out, int max_out) +{ + return (netbios_first_level_name_encode(name->name, name->scope, + out, max_out)); +} + +int +smb_first_level_name_decode(unsigned char *in, struct name_entry *name) +{ + return (netbios_first_level_name_decode((char *)in, (char *)name->name, + (char *)name->scope)); +} + +/* + * smb_encode_netbios_name + * + * Set up the name and scope fields in the destination name_entry structure. + * The name is padded with spaces to 15 bytes. The suffix is copied into the + * last byte, i.e. "netbiosname ". The scope is copied and folded + * to uppercase. + */ +void +smb_encode_netbios_name(unsigned char *name, char suffix, unsigned char *scope, + struct name_entry *dest) +{ + char tmp_name[NETBIOS_NAME_SZ]; + mts_wchar_t wtmp_name[NETBIOS_NAME_SZ]; + unsigned int cpid; + int len; + size_t rc; + + len = 0; + rc = mts_mbstowcs(wtmp_name, (const char *)name, NETBIOS_NAME_SZ); + + if (rc != (size_t)-1) { + wtmp_name[NETBIOS_NAME_SZ - 1] = 0; + cpid = oem_get_smb_cpid(); + rc = unicodestooems(tmp_name, wtmp_name, NETBIOS_NAME_SZ, cpid); + if (rc > 0) + len = strlen(tmp_name); + } + + (void) memset(dest->name, ' ', NETBIOS_NAME_SZ - 1); + if (len) { + (void) utf8_strupr(tmp_name); + (void) memcpy(dest->name, tmp_name, len); + } + dest->name[NETBIOS_NAME_SZ - 1] = suffix; + + if (scope == NULL) { + smb_config_rdlock(); + (void) strlcpy((char *)dest->scope, + smb_config_getstr(SMB_CI_NBSCOPE), NETBIOS_DOMAIN_NAME_MAX); + smb_config_unlock(); + } else { + (void) strlcpy((char *)dest->scope, (const char *)scope, + NETBIOS_DOMAIN_NAME_MAX); + } + (void) utf8_strupr((char *)dest->scope); +} + +void +smb_init_name_struct(unsigned char *name, char suffix, unsigned char *scope, + uint32_t ipaddr, unsigned short port, uint32_t attr, + uint32_t addr_attr, struct name_entry *dest) +{ + bzero(dest, sizeof (struct name_entry)); + smb_encode_netbios_name(name, suffix, scope, dest); + + switch (smb_node_type) { + case 'H': + dest->attributes = attr | NAME_ATTR_OWNER_TYPE_HNODE; + break; + case 'M': + dest->attributes = attr | NAME_ATTR_OWNER_TYPE_MNODE; + break; + case 'P': + dest->attributes = attr | NAME_ATTR_OWNER_TYPE_PNODE; + break; + case 'B': + default: + dest->attributes = attr | NAME_ATTR_OWNER_TYPE_BNODE; + break; + } + + dest->addr_list.refresh_ttl = dest->addr_list.ttl = + TO_SECONDS(DEFAULT_TTL); + + dest->addr_list.sin.sin_family = AF_INET; + dest->addr_list.sinlen = sizeof (dest->addr_list.sin); + dest->addr_list.sin.sin_addr.s_addr = ipaddr; + dest->addr_list.sin.sin_port = port; + dest->addr_list.attributes = addr_attr; + dest->addr_list.forw = dest->addr_list.back = &dest->addr_list; +} diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios.h b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios.h new file mode 100644 index 000000000000..54ac5cf2ab63 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios.h @@ -0,0 +1,1617 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMB_NETBIOS_H_ +#define _SMB_NETBIOS_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * 4.2. NAME SERVICE PACKETS + * + * 4.2.1. GENERAL FORMAT OF NAME SERVICE PACKETS + * + * The NetBIOS Name Service packets follow the packet structure defined + * in the Domain Name Service (DNS) RFC 883 [7 (pg 26-31)]. The + * structures are compatible with the existing DNS packet formats, + * however, additional types and codes have been added to work with + * NetBIOS. + * + * If Name Service packets are sent over a TCP connection they are + * preceded by a 16 bit unsigned integer representing the length of the + * Name Service packet. + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * + ------ ------- + + * | HEADER | + * + ------ ------- + + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / QUESTION ENTRIES / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / ANSWER RESOURCE RECORDS / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / AUTHORITY RESOURCE RECORDS / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / ADDITIONAL RESOURCE RECORDS / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* + * 4.2.1.1 HEADER + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID | OPCODE | NM_FLAGS | RCODE | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | QDCOUNT | ANCOUNT | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NSCOUNT | ARCOUNT | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Field Description + * + * NAME_TRN_ID Transaction ID for Name Service Transaction. + * Requester places a unique value for each active + * transaction. Responder puts NAME_TRN_ID value + * from request packet in response packet. + * + * OPCODE Packet type code, see table below. + * + * NM_FLAGS Flags for operation, see table below. + * + * RCODE Result codes of request. Table of RCODE values + * for each response packet below. + * + * QDCOUNT Unsigned 16 bit integer specifying the number of + * entries in the question section of a Name + * Service packet. Always zero (0) for responses. + * Must be non-zero for all NetBIOS Name requests. + * + * ANCOUNT Unsigned 16 bit integer specifying the number of + * resource records in the answer section of a Name + * Service packet. + * + * NSCOUNT Unsigned 16 bit integer specifying the number of + * resource records in the authority section of a + * Name Service packet. + * + * ARCOUNT Unsigned 16 bit integer specifying the number of + * resource records in the additional records + * section of a Name Service packet. + */ + + +/* + * The OPCODE field is defined as: + * + * 0 1 2 3 4 + * +---+---+---+---+---+ + * | R | OPCODE | + * +---+---+---+---+---+ + * + * Symbol Bit(s) Description + * + * OPCODE 1-4 Operation specifier: + * 0 = query + * 5 = registration + * 6 = release + * 7 = WACK + * 8 = refresh + * + * R 0 RESPONSE flag: + * if bit == 0 then request packet + * if bit == 1 then response packet. + */ + +/* + * The NM_FLAGS field is defined as: + * + * + * 0 1 2 3 4 5 6 + * +---+---+---+---+---+---+---+ + * |AA |TC |RD |RA | 0 | 0 | B | + * +---+---+---+---+---+---+---+ + * + * Symbol Bit(s) Description + * + * B 6 Broadcast Flag. + * = 1: packet was broadcast or multicast + * = 0: unicast + * + * RA 3 Recursion Available Flag. + * + * Only valid in responses from a NetBIOS Name + * Server -- must be zero in all other + * responses. + * + * If one (1) then the NBNS supports recursive + * query, registration, and release. + * + * If zero (0) then the end-node must iterate + * for query and challenge for registration. + * + * RD 2 Recursion Desired Flag. + * + * May only be set on a request to a NetBIOS + * Name Server. + * + * The NBNS will copy its state into the + * response packet. + * + * If one (1) the NBNS will iterate on the + * query, registration, or release. + * + * TC 1 Truncation Flag. + * + * Set if this message was truncated because the + * datagram carrying it would be greater than + * 576 bytes in length. Use TCP to get the + * information from the NetBIOS Name Server. + * + * AA 0 Authoritative Answer flag. + * + * Must be zero (0) if R flag of OPCODE is zero + * (0). + * + * If R flag is one (1) then if AA is one (1) + * then the node responding is an authority for + * the domain name. + * + * End nodes responding to queries always set + * this bit in responses. + */ + +/* + * 4.2.1.2 QUESTION SECTION + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / QUESTION_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | QUESTION_TYPE | QUESTION_CLASS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Field Description + * + * QUESTION_NAME The compressed name representation of the + * NetBIOS name for the request. + * + * QUESTION_TYPE The type of request. The values for this field + * are specified for each request. + * + * QUESTION_CLASS The class of the request. The values for this + * field are specified for each request. + * + * QUESTION_TYPE is defined as: + * + * Symbol Value Description: + * + * NB 0x0020 NetBIOS general Name Service Resource Record + * NBSTAT 0x0021 NetBIOS NODE STATUS Resource Record (See NODE + * STATUS REQUEST) + * + * QUESTION_CLASS is defined as: + * + * Symbol Value Description: + * + * IN 0x0001 Internet class + */ + +/* + * 4.2.1.3 RESOURCE RECORD + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RR_TYPE | RR_CLASS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RDLENGTH | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + * / / + * / RDATA / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Field Description + * + * RR_NAME The compressed name representation of the + * NetBIOS name corresponding to this resource + * record. + * + * RR_TYPE Resource record type code + * + * RR_CLASS Resource record class code + * + * TTL The Time To Live of a the resource record's + * name. + * + * RDLENGTH Unsigned 16 bit integer that specifies the + * number of bytes in the RDATA field. + * + * RDATA RR_CLASS and RR_TYPE dependent field. Contains + * the resource information for the NetBIOS name. + * + * RESOURCE RECORD RR_TYPE field definitions: + * + * Symbol Value Description: + * + * A 0x0001 IP address Resource Record (See REDIRECT NAME + * QUERY RESPONSE) + * NS 0x0002 Name Server Resource Record (See REDIRECT + * NAME QUERY RESPONSE) + * NULL 0x000A NULL Resource Record (See WAIT FOR + * ACKNOWLEDGEMENT RESPONSE) + * NB 0x0020 NetBIOS general Name Service Resource Record + * (See NB_FLAGS and NB_ADDRESS, below) + * NBSTAT 0x0021 NetBIOS NODE STATUS Resource Record (See NODE + * STATUS RESPONSE) + * + * RESOURCE RECORD RR_CLASS field definitions: + * + * Symbol Value Description: + * + * IN 0x0001 Internet class + * + * NB_FLAGS field of the RESOURCE RECORD RDATA field for RR_TYPE of + * "NB": + * + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | G | ONT | RESERVED | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * Symbol Bit(s) Description: + * + * RESERVED 3-15 Reserved for future use. Must be zero (0). + * ONT 1,2 Owner Node Type: + * 00 = B node + * 01 = P node + * 10 = M node + * 11 = Reserved for future use + * For registration requests this is the + * claimant's type. + * For responses this is the actual owner's + * type. + * + * G 0 Group Name Flag. + * If one (1) then the RR_NAME is a GROUP + * NetBIOS name. + * If zero (0) then the RR_NAME is a UNIQUE + * NetBIOS name. + * + * The NB_ADDRESS field of the RESOURCE RECORD RDATA field for + * RR_TYPE of "NB" is the IP address of the name's owner. + */ + +/* + * 4.2.2. NAME REGISTRATION REQUEST + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |0| 0x5 |0|0|1|0|0 0|B| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0001 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / QUESTION_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Since the RR_NAME is the same name as the QUESTION_NAME, the + * RR_NAME representation must use pointers to the QUESTION_NAME + * name's labels to guarantee the length of the datagram is less + * than the maximum 576 bytes. See section above on name formats + * and also page 31 and 32 of RFC 883, Domain Names - Implementation + * and Specification, for a complete description of compressed name + * label pointers. + */ + +/* + * 4.2.3 NAME OVERWRITE REQUEST & DEMAND + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |0| 0x5 |0|0|0|0|0 0|B| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0001 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / QUESTION_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* + * 4.2.4 NAME REFRESH REQUEST + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |0| 0x9 |0|0|0|0|0 0|B| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0001 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / QUESTION_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* + * 4.2.5 POSITIVE NAME REGISTRATION RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x5 |1|0|1|1|0 0|0| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* + * 4.2.6 NEGATIVE NAME REGISTRATION RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x5 |1|0|1|1|0 0|0| RCODE | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * RCODE field values: + * + * Symbol Value Description: + * + * FMT_ERR 0x1 Format Error. Request was invalidly + * formatted. + * SRV_ERR 0x2 Server failure. Problem with NBNS, cannot + * process name. + * IMP_ERR 0x4 Unsupported request error. Allowable only + * for challenging NBNS when gets an Update type + * registration request. + * RFS_ERR 0x5 Refused error. For policy reasons server + * will not register this name from this host. + * ACT_ERR 0x6 Active error. Name is owned by another node. + * CFT_ERR 0x7 Name in conflict error. A UNIQUE name is + * owned by more than one node. + */ + +/* + * 4.2.7 END-NODE CHALLENGE REGISTRATION RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x5 |1|0|1|0|0 0|0| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* + * 4.2.8 NAME CONFLICT DEMAND + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x5 |1|0|1|1|0 0|0| 0x7 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x00000000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 |0|ONT|0| 0x000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x00000000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * This packet is identical to a NEGATIVE NAME REGISTRATION RESPONSE + * with RCODE = CFT_ERR. + */ + +/* + * 4.2.9 NAME RELEASE REQUEST & DEMAND + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |0| 0x6 |0|0|0|0|0 0|B| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0001 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / QUESTION_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x00000000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Since the RR_NAME is the same name as the QUESTION_NAME, the + * RR_NAME representation must use label string pointers to the + * QUESTION_NAME labels to guarantee the length of the datagram is + * less than the maximum 576 bytes. This is the same condition as + * with the NAME REGISTRATION REQUEST. + */ + +/* + * 4.2.10 POSITIVE NAME RELEASE RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x6 |1|0|0|0|0 0|0| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* + * 4.2.11 NEGATIVE NAME RELEASE RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x6 |1|0|0|0|0 0|0| RCODE | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * RCODE field values: + * + * Symbol Value Description: + * + * FMT_ERR 0x1 Format Error. Request was invalidly + * formatted. + * + * SRV_ERR 0x2 Server failure. Problem with NBNS, cannot + * process name. + * + * RFS_ERR 0x5 Refused error. For policy reasons server + * will not release this name from this host. + * + * ACT_ERR 0x6 Active error. Name is owned by another node. + * Only that node may release it. A NetBIOS + * Name Server can optionally allow a node to + * release a name it does not own. This would + * facilitate detection of inactive names for + * nodes that went down silently. + */ + +/* + * 4.2.12 NAME QUERY REQUEST + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |0| 0x0 |0|0|1|0|0 0|B| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0001 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / QUESTION_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* + * 4.2.13 POSITIVE NAME QUERY RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x0 |1|T|1|?|0 0|0| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RDLENGTH | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + * | | + * / ADDR_ENTRY ARRAY / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * The ADDR_ENTRY ARRAY a sequence of zero or more ADDR_ENTRY + * records. Each ADDR_ENTRY record represents an owner of a name. + * For group names there may be multiple entries. However, the list + * may be incomplete due to packet size limitations. Bit 22, "T", + * will be set to indicate truncated data. + * + * Each ADDR_ENTRY has the following format: + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_FLAGS | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS (continued) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* + * 4.2.14 NEGATIVE NAME QUERY RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x0 |1|0|1|?|0 0|0| RCODE | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NULL (0x000A) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x00000000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * RCODE field values: + * + * Symbol Value Description + * + * FMT_ERR 0x1 Format Error. Request was invalidly + * formatted. + * SRV_ERR 0x2 Server failure. Problem with NBNS, cannot + * process name. + * NAM_ERR 0x3 Name Error. The name requested does not + * exist. + * IMP_ERR 0x4 Unsupported request error. Allowable only + * for challenging NBNS when gets an Update type + * registration request. + * RFS_ERR 0x5 Refused error. For policy reasons server + * will not register this name from this host. + */ + +/* + * 4.2.15 REDIRECT NAME QUERY RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x0 |0|0|1|0|0 0|0| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0001 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NS (0x0002) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RDLENGTH | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + * | | + * / NSD_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | A (0x0001) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0004 | NSD_IP_ADDR | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NSD_IP_ADDR, continued | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * An end node responding to a NAME QUERY REQUEST always responds + * with the AA and RA bits set for both the NEGATIVE and POSITIVE + * NAME QUERY RESPONSE packets. An end node never sends a REDIRECT + * NAME QUERY RESPONSE packet. + * + * When the requestor receives the REDIRECT NAME QUERY RESPONSE it + * must reiterate the NAME QUERY REQUEST to the NBNS specified by + * the NSD_IP_ADDR field of the A type RESOURCE RECORD in the + * ADDITIONAL section of the response packet. This is an optional + * packet for the NBNS. + * + * The NSD_NAME and the RR_NAME in the ADDITIONAL section of the + * response packet are the same name. Space can be optimized if + * label string pointers are used in the RR_NAME which point to the + * labels in the NSD_NAME. + * + * The RR_NAME in the AUTHORITY section is the name of the domain + * the NBNS called by NSD_NAME has authority over. + */ + +/* + * 4.2.16 WAIT FOR ACKNOWLEDGEMENT (WACK) RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x7 |1|0|0|0|0 0|0| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NULL (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0002 | OPCODE | NM_FLAGS | 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * The NAME_TRN_ID of the WACK RESPONSE packet is the same + * NAME_TRN_ID of the request that the NBNS is telling the requestor + * to wait longer to complete. The RR_NAME is the name from the + * request, if any. If no name is available from the request then + * it is a null name, single byte of zero. + * + * The TTL field of the ResourceRecord is the new time to wait, in + * seconds, for the request to complete. The RDATA field contains + * the OPCODE and NM_FLAGS of the request. + * + * A TTL value of 0 means that the NBNS can not estimate the time it + * may take to complete a response. + */ + +/* + * 4.2.17 NODE STATUS REQUEST + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |0| 0x0 |0|0|0|0|0 0|B| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0001 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / QUESTION_NAME / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NBSTAT (0x0021) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* + * 4.2.18 NODE STATUS RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x0 |1|0|0|0|0 0|0| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NBSTAT (0x0021) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x00000000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RDLENGTH | NUM_NAMES | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + * | | + * + + + * / NODE_NAME ARRAY / + * + + + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * + + + * / STATISTICS / + * + + + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * The NODE_NAME ARRAY is an array of zero or more NUM_NAMES entries + * of NODE_NAME records. Each NODE_NAME entry represents an active + * name in the same NetBIOS scope as the requesting name in the + * local name table of the responder. RR_NAME is the requesting + * name. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define QUEUE_INSERT_TAIL(q, e) \ + ((e)->back) = (void *)((q)->back); \ + ((e)->forw) = (void *)(q); \ + ((q)->back->forw) = (void *)(e); \ + ((q)->back) = (void *)(e); + +#define QUEUE_CLIP(e) \ + (e)->forw->back = (e)->back; \ + (e)->back->forw = (e)->forw; \ + (e)->forw = 0; \ + (e)->back = 0; + +#define NETBIOS_NAME_SVC_LAUNCHED 0x00001 +#define NETBIOS_NAME_SVC_RUNNING 0x00002 +#define NETBIOS_NAME_SVC_FAILED 0x00004 + +#define NETBIOS_DATAGRAM_SVC_LAUNCHED 0x00010 +#define NETBIOS_DATAGRAM_SVC_RUNNING 0x00020 +#define NETBIOS_DATAGRAM_SVC_FAILED 0x00040 + +#define NETBIOS_TIMER_LAUNCHED 0x00100 +#define NETBIOS_TIMER_RUNNING 0x00200 +#define NETBIOS_TIMER_FAILED 0x00400 + +#define NETBIOS_BROWSER_LAUNCHED 0x01000 +#define NETBIOS_BROWSER_RUNNING 0x02000 +#define NETBIOS_BROWSER_FAILED 0x04000 + +#define NETBIOS_SHUTTING_DOWN 0x10000 +#define NETBIOS_SHUT_DOWN 0x20000 + +char smb_node_type; + +typedef struct { + mutex_t mtx; + cond_t cv; + uint32_t state; +} netbios_status_t; +extern netbios_status_t nb_status; + +/* + * NAME service definitions + */ +#define ADDR_FLAG_INVALID 0x0000 +#define ADDR_FLAG_VALID 0x0001 + +typedef struct addr_entry { + struct addr_entry *forw; + struct addr_entry *back; + uint32_t attributes; + uint32_t conflict_timer; + uint32_t refresh_ttl; + uint32_t ttl; + struct sockaddr_in sin; + int sinlen; + uint32_t flags; +} addr_entry_t; + +/* + * The NODE_NAME ARRAY is an array of zero or more NUM_NAMES entries + * of NODE_NAME records. Each NODE_NAME entry represents an active + * name in the same NetBIOS scope as the requesting name in the + * local name table of the responder. RR_NAME is the requesting + * name. + * + * NODE_NAME Entry: + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * +--- ---+ + * | | + * +--- NETBIOS FORMAT NAME ---+ + * | | + * +--- ---+ + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * The NAME_FLAGS field: + * + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | G | ONT |DRG|CNF|ACT|PRM| RESERVED | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * The NAME_FLAGS field is defined as: + * + * Symbol Bit(s) Description: + * + * RESERVED 7-15 Reserved for future use. Must be zero (0). + * PRM 6 Permanent Name Flag. If one (1) then entry + * is for the permanent node name. Flag is zero + * (0) for all other names. + * ACT 5 Active Name Flag. All entries have this flag + * set to one (1). + * CNF 4 Conflict Flag. If one (1) then name on this + * node is in conflict. + * DRG 3 Deregister Flag. If one (1) then this name + * is in the process of being deleted. + * ONT 1,2 Owner Node Type: + * 00 = B node + * 01 = P node + * 10 = M node + * 11 = Reserved for future use + * G 0 Group Name Flag. + * name. + * If zero (0) then it is a UNIQUE NetBIOS name. + */ + +typedef struct name_entry { + struct name_entry *forw; + struct name_entry *back; + unsigned char name[NETBIOS_NAME_SZ]; + unsigned char scope[NETBIOS_DOMAIN_NAME_MAX]; + unsigned short attributes; + struct addr_entry addr_list; + mutex_t mtx; +} name_entry; + +struct name_question { + struct name_entry *name; + unsigned question_type; + unsigned question_class; +}; + +struct resource_record { + /* + * These two flags and address are contained within RDATA + * when rr_type==0x0020 (NB - NetBIOS general Name Service) + * and rr_class==0x01 (IN - Internet Class). + */ + + struct name_entry *name; + unsigned short rr_type; + unsigned short rr_class; + uint32_t ttl; + unsigned short rdlength; + unsigned char *rdata; +}; + +struct name_packet { + unsigned short name_trn_id; + unsigned short info; + + unsigned qdcount; /* question entries */ + unsigned ancount; /* answer recs */ + unsigned nscount; /* authority recs */ + unsigned arcount; /* additional recs */ + + struct name_question *question; + struct resource_record *answer; + struct resource_record *authority; + struct resource_record *additional; + + unsigned char block_data[4]; /* begining of space */ +}; + +#define NAME_OPCODE_R 0x8000 /* RESPONSE flag: 1 bit */ +#define NAME_OPCODE_OPCODE_MASK 0x7800 /* OPCODE Field: 4 bits */ +#define NAME_OPCODE_QUERY 0x0000 +#define NAME_OPCODE_REGISTRATION 0x2800 +#define NAME_OPCODE_RELEASE 0x3000 +#define NAME_OPCODE_WACK 0x3800 +#define NAME_OPCODE_REFRESH 0x4000 +#define NAME_OPCODE_MULTIHOME 0x7800 +#define NAME_NM_FLAGS_AA 0x0400 /* Authoritative Answer:1 bit */ +#define NAME_NM_FLAGS_TC 0x0200 /* Truncation: 1 bit */ +#define NAME_NM_FLAGS_RD 0x0100 /* Recursion desired: 1 bit */ +#define NAME_NM_FLAGS_RA 0x0080 /* Recursion available: 1 bit */ +#define NAME_NM_FLAGS_x2 0x0040 /* reserved, mbz: 1 bit */ +#define NAME_NM_FLAGS_x1 0x0020 /* reserved, mbz: 1 bit */ +#define NAME_NM_FLAGS_B 0x0010 /* Broadcast: 1 bit */ +#define NAME_RCODE_MASK 0x000f /* RCODE Field: 4 bits */ +#define RCODE_FMT_ERR 0x0001 +#define RCODE_SRV_ERR 0x0002 +#define RCODE_NAM_ERR 0x0003 +#define RCODE_IMP_ERR 0x0004 +#define RCODE_RFS_ERR 0x0005 +#define RCODE_ACT_ERR 0x0006 +#define RCODE_CFT_ERR 0x0007 + +#define NM_FLAGS_UNICAST 0 +#define NM_FLAGS_BROADCAST NAME_NM_FLAGS_B + +#define PACKET_TYPE(x) ((x) & (NAME_OPCODE_R | NAME_OPCODE_OPCODE_MASK | \ + NAME_NM_FLAGS_AA | NAME_NM_FLAGS_RD)) + +#define RCODE(x) ((x) & NAME_RCODE_MASK) +#define POSITIVE_RESPONSE(x) (RCODE(x) == 0) +#define NEGATIVE_RESPONSE(x) (RCODE(x) != 0) + +#define END_NODE_CHALLENGE_REGISTRATION_REQUEST \ + (NAME_OPCODE_REGISTRATION | NAME_NM_FLAGS_AA | NAME_NM_FLAGS_RD) +#define END_NODE_CHALLENGE_NAME_REGISTRATION_RESPONSE \ + (NAME_OPCODE_R | END_NODE_CHALLENGE_REGISTRATION_REQUEST) + +#define NAME_QUERY_REQUEST \ + (NAME_OPCODE_QUERY | NAME_NM_FLAGS_RD) +#define NAME_QUERY_RESPONSE \ + (NAME_OPCODE_R | NAME_QUERY_REQUEST | \ + NAME_NM_FLAGS_AA | NAME_NM_FLAGS_RD) + +#define NODE_STATUS_REQUEST \ + (NAME_OPCODE_QUERY) +#define NODE_STATUS_RESPONSE \ + (NAME_OPCODE_R | NODE_STATUS_REQUEST | NAME_NM_FLAGS_AA) + +#define REDIRECT_NAME_QUERY_RESPONSE \ + (NAME_OPCODE_R | NAME_QUERY_REQUEST | NAME_NM_FLAGS_RD) + +#define NAME_REFRESH_REQUEST \ + (NAME_OPCODE_REFRESH) +#define NAME_REGISTRATION_REQUEST \ + (NAME_OPCODE_REGISTRATION | NAME_NM_FLAGS_RD) +#define NAME_MULTIHOME_REGISTRATION_REQUEST \ + (NAME_OPCODE_MULTIHOME | NAME_NM_FLAGS_RD) +#define NAME_REGISTRATION_RESPONSE \ + (NAME_OPCODE_R | NAME_REGISTRATION_REQUEST | NAME_NM_FLAGS_AA) + +#define NAME_RELEASE_REQUEST \ + (NAME_OPCODE_RELEASE) +#define NAME_RELEASE_RESPONSE \ + (NAME_OPCODE_R | NAME_RELEASE_REQUEST | NAME_NM_FLAGS_AA) + +#define WACK_RESPONSE \ + (NAME_OPCODE_R | NAME_OPCODE_WACK | NAME_NM_FLAGS_AA) + +#define NAME_QUESTION_TYPE_NB 0x0020 +#define NAME_QUESTION_TYPE_NBSTAT 0x0021 +#define NAME_QUESTION_CLASS_IN 0x0001 + + +#define NAME_RR_TYPE_A 0x0001 /* IP Address */ +#define NAME_RR_TYPE_NS 0x0002 /* Name Server */ +#define NAME_RR_TYPE_NULL 0x000A /* NULL */ +#define NAME_RR_TYPE_NB 0x0020 /* NetBIOS Name Service */ +#define NAME_RR_TYPE_NBSTAT 0x0021 /* NetBIOS Node Status */ + +#define NAME_RR_CLASS_IN 0x0001 /* NetBIOS Node Status */ + +#define NAME_NB_FLAGS_ONT_MASK (3<<13) +#define NAME_NB_FLAGS_ONT_B (0<<13) /* B-node (broadcast) */ +#define NAME_NB_FLAGS_ONT_P (1<<13) /* P-node (point-to-point) */ +#define NAME_NB_FLAGS_ONT_M (2<<13) /* M-node (multicast) */ +#define NAME_NB_FLAGS_ONT_resv (3<<13) +#define NAME_NB_FLAGS_G (1<<15) /* Group Name */ + +#define UNICAST 0 +#define BROADCAST 1 +#define POINTCAST 2 + +#define NAME_ATTR_UNIQUE 0x0000 +#define NAME_ATTR_GROUP 0x8000 +#define NAME_ATTR_OWNER_NODE_TYPE 0x6000 +#define NAME_ATTR_OWNER_TYPE_BNODE 0x0000 +#define NAME_ATTR_OWNER_TYPE_PNODE 0x2000 +#define NAME_ATTR_OWNER_TYPE_MNODE 0x4000 +#define NAME_ATTR_OWNER_TYPE_HNODE 0x6000 +#define NAME_ATTR_DEREGISTER 0x1000 +#define NAME_ATTR_CONFLICT 0x0800 +#define NAME_ATTR_ACTIVE_NAME 0x0400 +#define NAME_ATTR_PERMANENT 0x0200 +#define NAME_ATTR_RESERVED 0x01FF +#define NAME_ATTR_LOCAL 0x0001 + +#define NODE_TYPE(x) ((x) & NAME_ATTR_OWNER_NODE_TYPE)) +#define IS_BNODE(x) (NODE_TYPE(x) == NAME_ATTR_OWNER_TYPE_BNODE) +#define IS_PNODE(x) (NODE_TYPE(x) == NAME_ATTR_OWNER_TYPE_PNODE) +#define IS_MNODE(x) (NODE_TYPE(x) == NAME_ATTR_OWNER_TYPE_MNODE) +#define IS_HNODE(x) (NODE_TYPE(x) == NAME_ATTR_OWNER_TYPE_HNODE) + +#define IS_UNIQUE(x) (((x) & NAME_ATTR_GROUP) == 0) +#define IS_GROUP(x) (((x) & NAME_ATTR_GROUP) != 0) +#define IS_PERMANENT(x) (((x) & NAME_ATTR_PERMANENT) != 0) +#define IS_CONFLICTING(x) (((x) & NAME_ATTR_CONFLICT) != 0) +#define IS_ACTIVE(x) (((x) & NAME_ATTR_ACTIVE) != 0) +#define IS_DEGREGISTERED(x) (((x) & NAME_ATTR_ACTIVE) != 0) + +#define IS_LOCAL(x) (((x) & NAME_ATTR_LOCAL) != 0) +#define IS_PUBLIC(x) (((x) & NAME_ATTR_LOCAL) == 0) +#define PUBLIC_BITS(x) ((x) & ~NAME_ATTR_RESERVED) + +#define SAME_SCOPE(scope, e) (strcmp((scope), ((e)->scope)) == 0) + +/* + * STATISTICS Field of the NODE STATUS RESPONSE: + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | UNIT_ID (Unique unit ID) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | UNIT_ID,continued | JUMPERS | TEST_RESULT | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | VERSION_NUMBER | PERIOD_OF_STATISTICS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NUMBER_OF_CRCs | NUMBER_ALIGNMENT_ERRORS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NUMBER_OF_COLLISIONS | NUMBER_SEND_ABORTS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NUMBER_GOOD_SENDS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NUMBER_GOOD_RECEIVES | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NUMBER_RETRANSMITS | NUMBER_NO_RESOURCE_CONDITIONS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NUMBER_FREE_COMMAND_BLOCKS | TOTAL_NUMBER_COMMAND_BLOCKS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |MAX_TOTAL_NUMBER_COMMAND_BLOCKS| NUMBER_PENDING_SESSIONS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MAX_NUMBER_PENDING_SESSIONS | MAX_TOTAL_SESSIONS_POSSIBLE | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SESSION_DATA_PACKET_SIZE | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +typedef struct { + unsigned char unit_id[6]; + unsigned char jumpers; + unsigned char test_result; + unsigned short version_number; + unsigned short statistical_period; + unsigned short crc_errors; + unsigned short alignment_errors; + unsigned short collisions; + unsigned short send_aborts; + unsigned int good_sends; + unsigned int good_receives; + unsigned short retransmits; + unsigned short no_resource_conditions; + unsigned short free_command_blocks; + unsigned short total_command_blocks; + unsigned short max_total_command_blocks; + unsigned short pending_sessions; + unsigned short max_pending_sessions; + unsigned short total_possible_sessions; + unsigned short session_data_packet_size; +} node_status_response; + +/* + * 4.4.1. NetBIOS DATAGRAM HEADER + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MSG_TYPE | FLAGS | DGM_ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SOURCE_IP | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SOURCE_PORT | DGM_LENGTH | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | PACKET_OFFSET | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +typedef struct { + unsigned char msg_type; + unsigned char flags; + unsigned short dgm_id; + uint32_t source_ip; + unsigned short source_port; + unsigned short dgm_length; + unsigned short packet_offset; +} datagram_header; + +/* + * MSG_TYPE values (in hexidecimal): + * + * 10 - DIRECT_UNIQUE DATAGRAM + * 11 - DIRECT_GROUP DATAGRAM + * 12 - BROADCAST DATAGRAM + * 13 - DATAGRAM ERROR + * 14 - DATAGRAM QUERY REQUEST + * 15 - DATAGRAM POSITIVE QUERY RESPONSE + * 16 - DATAGRAM NEGATIVE QUERY RESPONSE + */ +#define DATAGRAM_TYPE_DIRECT_UNIQUE 0x10 +#define DATAGRAM_TYPE_DIRECT_GROUP 0x11 +#define DATAGRAM_TYPE_BROADCAST 0x12 +#define DATAGRAM_TYPE_ERROR_DATAGRAM 0x13 +#define DATAGRAM_TYPE_QUERY_REQUEST 0x14 +#define DATAGRAM_TYPE_POSITIVE_RESPONSE 0x15 +#define DATAGRAM_TYPE_NEGATIVE_RESPONSE 0x16 + + +/* + * Bit definitions of the FLAGS field: + * + * 0 1 2 3 4 5 6 7 + * +---+---+---+---+---+---+---+---+ + * | 0 | 0 | 0 | 0 | SNT | F | M | + * +---+---+---+---+---+---+---+---+ + * + * Symbol Bit(s) Description + * + * M 7 MORE flag, If set then more NetBIOS datagram + * fragments follow. + * + * F 6 FIRST packet flag, If set then this is first + * (and possibly only) fragment of NetBIOS + * datagram + * + * SNT 4,5 Source End-Node type: + * 00 = B node + * 01 = P node + * 10 = M node + * 11 = H node + * RESERVED 0-3 Reserved, must be zero (0) + */ +#define DATAGRAM_FLAGS_MORE 0x01 +#define DATAGRAM_FLAGS_FIRST 0x02 +#define DATAGRAM_FLAGS_SRC_TYPE 0x0c +#define DATAGRAM_FLAGS_B_NODE 0x00 +#define DATAGRAM_FLAGS_P_NODE 0x04 +#define DATAGRAM_FLAGS_M_NODE 0x08 +#define DATAGRAM_FLAGS_H_NODE 0x0C +#define DATAGRAM_FLAGS_NBDD 0x0c +#define DATAGRAM_FLAGS_RESERVED 0xf0 + +/* + * 4.4.2. DIRECT_UNIQUE, DIRECT_GROUP, & BROADCAST DATAGRAM + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MSG_TYPE | FLAGS | DGM_ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SOURCE_IP | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SOURCE_PORT | DGM_LENGTH | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | PACKET_OFFSET | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + * | | + * / SOURCE_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / DESTINATION_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / USER_DATA / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +typedef struct { + datagram_header header; + unsigned char *source_name; + unsigned char *destination_name; + unsigned char *user_data; +} datagram_packet; + + +/* + * 4.4.3. DATAGRAM ERROR PACKET + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MSG_TYPE | FLAGS | DGM_ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SOURCE_IP | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SOURCE_PORT | ERROR_CODE | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * ERROR_CODE values (in hexidecimal): + * + * 82 - DESTINATION NAME NOT PRESENT + * 83 - INVALID SOURCE NAME FORMAT + * 84 - INVALID DESTINATION NAME FORMAT + */ + +typedef struct { + unsigned char msg_type; + unsigned char flags; + unsigned short dgm_id; + uint32_t source_ip; + unsigned short source_port; + unsigned char error; +} datagram_error_packet; + +/* + * 4.4.4. DATAGRAM QUERY REQUEST + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MSG_TYPE | FLAGS | DGM_ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SOURCE_IP | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SOURCE_PORT | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + * | | + * / DESTINATION_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 4.4.5. DATAGRAM POSITIVE AND NEGATIVE QUERY RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MSG_TYPE | FLAGS | DGM_ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SOURCE_IP | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SOURCE_PORT | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + * | | + * / DESTINATION_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +typedef struct datagram_query_packet { + unsigned char msg_type; + unsigned char flags; + unsigned short dgm_id; + uint32_t source_ip; + unsigned short source_port; + unsigned char destination_name[MAX_NAME_LENGTH]; +} datagram_query_packet; + + +typedef struct datagram { + struct datagram *forw; + struct datagram *back; + struct addr_entry inaddr; + int discard_timer; + unsigned char packet_type; + unsigned char flags; + unsigned short datagram_id; + struct name_entry src; + struct name_entry dest; + unsigned short offset; + unsigned short data_length; + unsigned char *data; + unsigned int rawbytes; + unsigned char rawbuf[MAX_DATAGRAM_LENGTH]; +} datagram; + +typedef struct datagram_queue { + struct datagram *forw; + struct datagram *back; +} datagram_queue; + +typedef struct name_queue { + struct name_entry head; + mutex_t mtx; +} name_queue_t; + +#define NETBIOS_EMPTY_NAME (unsigned char *)"" + +#define NETBIOS_NAME_IS_STAR(name) \ + (bcmp(name, "*\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", NETBIOS_NAME_SZ) == 0) + +void smb_netbios_chg_status(uint32_t status, int set); + +/* + * Name Cache Functions + */ +int smb_netbios_cache_init(void); +void smb_netbios_cache_fini(void); +void smb_netbios_cache_dump(void); +void smb_netbios_cache_print(void); +void smb_netbios_cache_diag(char ** pbuf); +int smb_netbios_cache_count(void); +void smb_netbios_cache_clean(void); +void smb_netbios_cache_reset_ttl(void); +void smb_netbios_cache_delete_locals(name_queue_t *delq); +void smb_netbios_cache_refresh(name_queue_t *refq); + +int smb_netbios_cache_insert(struct name_entry *name); +int smb_netbios_cache_insert_list(struct name_entry *name); +void smb_netbios_cache_delete(struct name_entry *name); +int smb_netbios_cache_delete_addr(struct name_entry *name); +struct name_entry *smb_netbios_cache_lookup(struct name_entry *name); +struct name_entry *smb_netbios_cache_lookup_addr(struct name_entry *name); +void smb_netbios_cache_update_entry(struct name_entry *entry, + struct name_entry *name); +void smb_netbios_cache_unlock_entry(struct name_entry *name); +unsigned char *smb_netbios_cache_status(unsigned char *buf, int bufsize, + unsigned char *scope); + +void smb_netbios_name_dump(struct name_entry *entry); +void smb_netbios_name_logf(struct name_entry *entry); +void smb_netbios_name_freeaddrs(struct name_entry *entry); +struct name_entry *smb_netbios_name_dup(struct name_entry *entry, + int alladdr); + +/* Name service functions */ +void *smb_netbios_name_service_daemon(void *); +void smb_init_name_struct(unsigned char *, char, + unsigned char *, uint32_t, unsigned short, + uint32_t, uint32_t, struct name_entry *); + +struct name_entry *smb_name_find_name(struct name_entry *name); +int smb_name_add_name(struct name_entry *name); +int smb_name_delete_name(struct name_entry *name); +void smb_name_unlock_name(struct name_entry *name); + +void smb_netbios_name_config(void); +void smb_netbios_name_unconfig(void); +void smb_netbios_name_tick(void); + +int smb_first_level_name_encode(struct name_entry *name, + unsigned char *out, int max_out); +int smb_first_level_name_decode(unsigned char *in, + struct name_entry *name); +void smb_encode_netbios_name(unsigned char *name, + char suffix, unsigned char *scope, + struct name_entry *dest); + +/* Datagram service functions */ +void *smb_netbios_datagram_service_daemon(void *); +int smb_netbios_datagram_send(struct name_entry *, + struct name_entry *, unsigned char *, int); +void smb_netbios_datagram_tick(void); + + +/* browser functions */ +void smb_browser_config(void); +void *smb_browser_dispatch(void *arg); +void *smb_browser_daemon(void *); +int smb_net_id(uint32_t ipaddr); +struct name_entry *smb_browser_get_srvname(unsigned short netid); +int smb_browser_load_transact_header(unsigned char *buffer, + int maxcnt, int data_count, int reply, char *mailbox); + +/* Netlogon function */ +/* + * smb_netlogon_receive + * + * This is where we handle all incoming NetLogon messages. Currently, we + * ignore requests from anyone else. We are only interested in responses + * to our own requests. The NetLogonResponse provides the name of the PDC. + * If we don't already have a controller name, we use the name provided + * in the message. Otherwise we use the name already in the environment. + */ +void smb_netlogon_receive(struct datagram *datagram, char *mailbox, + unsigned char *data, int datalen); + +#endif /* _SMB_NETBIOS_H_ */ diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_cache.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_cache.c new file mode 100644 index 000000000000..1e47658700bc --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_cache.c @@ -0,0 +1,776 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define NETBIOS_HTAB_SZ 128 +#define NETBIOS_HKEY_SZ (NETBIOS_NAME_SZ + NETBIOS_DOMAIN_NAME_MAX) + +#define NETBIOS_NAMEBUF char namebuf[20] + +#define NETBIOS_SAME_IP(addr1, addr2) \ + ((addr1)->sin.sin_addr.s_addr == (addr2)->sin.sin_addr.s_addr) + +int smb_netbios_name_debug = 0; + +typedef char nb_key_t[NETBIOS_HKEY_SZ]; +static HT_HANDLE *smb_netbios_cache = 0; +static rwlock_t nb_cache_lock; + +static char *smb_strname(struct name_entry *name, char *buf, int bufsize); +static void hash_callback(HT_ITEM *item); +static int smb_netbios_match(const char *key1, const char *key2, size_t n); +static void smb_netbios_cache_key(char *key, unsigned char *name, + unsigned char *scope); + +int +smb_netbios_cache_init() +{ + (void) rw_wrlock(&nb_cache_lock); + if (smb_netbios_cache == 0) { + smb_netbios_cache = ht_create_table(NETBIOS_HTAB_SZ, + NETBIOS_HKEY_SZ, HTHF_FIXED_KEY); + if (smb_netbios_cache == 0) { + syslog(LOG_ERR, + "smbd: cannot create NetBIOS name cache"); + (void) rw_unlock(&nb_cache_lock); + return (0); + } + (void) ht_register_callback(smb_netbios_cache, hash_callback); + ht_set_cmpfn(smb_netbios_cache, smb_netbios_match); + } + (void) rw_unlock(&nb_cache_lock); + + return (1); +} + +void +smb_netbios_cache_fini() +{ + (void) rw_wrlock(&nb_cache_lock); + ht_destroy_table(smb_netbios_cache); + smb_netbios_cache = 0; + (void) rw_unlock(&nb_cache_lock); +} + +void +smb_netbios_cache_clean() +{ + (void) rw_wrlock(&nb_cache_lock); + (void) ht_clean_table(smb_netbios_cache); + (void) rw_unlock(&nb_cache_lock); +} + +/* + * smb_netbios_cache_lookup + * + * Searches the name cache for the given entry, if found + * the entry will be locked before returning to caller + * so caller MUST unlock the entry after it's done with it. + */ +struct name_entry * +smb_netbios_cache_lookup(struct name_entry *name) +{ + HT_ITEM *item; + nb_key_t key; + struct name_entry *entry = NULL; + unsigned char scope[SMB_PI_MAX_SCOPE]; + unsigned char hostname[MAXHOSTNAMELEN]; + + if (NETBIOS_NAME_IS_STAR(name->name)) { + /* Return our address */ + smb_config_rdlock(); + (void) strlcpy((char *)scope, + smb_config_getstr(SMB_CI_NBSCOPE), sizeof (scope)); + (void) utf8_strupr((char *)scope); + smb_config_unlock(); + + if (smb_getnetbiosname((char *)hostname, MAXHOSTNAMELEN) != 0) + return (NULL); + + smb_encode_netbios_name(hostname, 0x00, scope, name); + } + + (void) rw_rdlock(&nb_cache_lock); + + smb_netbios_cache_key(key, name->name, name->scope); + item = ht_find_item(smb_netbios_cache, key); + if (item) { + entry = (struct name_entry *)item->hi_data; + (void) mutex_lock(&entry->mtx); + if ((entry->attributes & NAME_ATTR_CONFLICT) != 0) { + (void) mutex_unlock(&entry->mtx); + entry = NULL; + } + } + + (void) rw_unlock(&nb_cache_lock); + return (entry); +} + +void +smb_netbios_cache_unlock_entry(struct name_entry *name) +{ + if (name) + (void) mutex_unlock(&name->mtx); +} + +/* + * smb_netbios_cache_lookup_addr + * + * lookup the given 'name' in the cache and then checks + * if the address also matches with the found entry. + * 'name' is supposed to contain only one address. + * + * The found entry will be locked before returning to caller + * so caller MUST unlock the entry after it's done with it. + */ +struct name_entry * +smb_netbios_cache_lookup_addr(struct name_entry *name) +{ + struct name_entry *entry = 0; + struct addr_entry *addr; + struct addr_entry *name_addr; + HT_ITEM *item; + nb_key_t key; + + (void) rw_rdlock(&nb_cache_lock); + smb_netbios_cache_key(key, name->name, name->scope); + item = ht_find_item(smb_netbios_cache, key); + + if (item && item->hi_data) { + name_addr = &name->addr_list; + entry = (struct name_entry *)item->hi_data; + (void) mutex_lock(&entry->mtx); + addr = &entry->addr_list; + do { + if (NETBIOS_SAME_IP(addr, name_addr)) { + /* note that entry lock isn't released here */ + (void) rw_unlock(&nb_cache_lock); + return (entry); + } + addr = addr->forw; + } while (addr != &entry->addr_list); + (void) mutex_unlock(&entry->mtx); + } + + (void) rw_unlock(&nb_cache_lock); + return (0); +} + +int +smb_netbios_cache_insert(struct name_entry *name) +{ + struct name_entry *entry; + struct addr_entry *addr; + struct addr_entry *name_addr; + HT_ITEM *item; + nb_key_t key; + + /* No point in adding a name with IP address 255.255.255.255 */ + if (name->addr_list.sin.sin_addr.s_addr == 0xffffffff) + return (0); + + (void) rw_wrlock(&nb_cache_lock); + smb_netbios_cache_key(key, name->name, name->scope); + item = ht_find_item(smb_netbios_cache, key); + + if (item && item->hi_data) { + /* Name already exists */ + entry = (struct name_entry *)item->hi_data; + (void) mutex_lock(&entry->mtx); + + name_addr = &name->addr_list; + addr = &entry->addr_list; + if (NETBIOS_SAME_IP(addr, name_addr) && + (addr->sin.sin_port == name_addr->sin.sin_port)) { + entry->attributes |= + name_addr->attributes & NAME_ATTR_LOCAL; + syslog(LOG_DEBUG, "cache_insert: exists"); + (void) mutex_unlock(&entry->mtx); + (void) rw_unlock(&nb_cache_lock); + return (0); /* exists */ + } + + /* Was not primary: looks for others */ + for (addr = entry->addr_list.forw; + addr != &entry->addr_list; addr = addr->forw) { + if (NETBIOS_SAME_IP(addr, name_addr) && + (addr->sin.sin_port == name_addr->sin.sin_port)) { + syslog(LOG_DEBUG, "cache_insert: dup"); + (void) mutex_unlock(&entry->mtx); + (void) rw_unlock(&nb_cache_lock); + return (0); /* exists */ + } + } + + addr = (struct addr_entry *)malloc(sizeof (struct addr_entry)); + if (addr == 0) { + (void) mutex_unlock(&entry->mtx); + (void) rw_unlock(&nb_cache_lock); + return (-1); + } + *addr = name->addr_list; + entry->attributes |= addr->attributes; + QUEUE_INSERT_TAIL(&entry->addr_list, addr); + (void) mutex_unlock(&entry->mtx); + (void) rw_unlock(&nb_cache_lock); + return (0); + } + + entry = (struct name_entry *)malloc(sizeof (struct name_entry)); + if (entry == 0) { + (void) rw_unlock(&nb_cache_lock); + return (-1); + } + *entry = *name; + entry->addr_list.forw = entry->addr_list.back = &entry->addr_list; + entry->attributes |= entry->addr_list.attributes; + (void) mutex_init(&entry->mtx, 0, 0); + if (ht_replace_item(smb_netbios_cache, key, entry) == 0) { + free(entry); + (void) rw_unlock(&nb_cache_lock); + return (-1); + } + + (void) rw_unlock(&nb_cache_lock); + return (0); +} + + +void +smb_netbios_cache_delete(struct name_entry *name) +{ + nb_key_t key; + HT_ITEM *item; + struct name_entry *entry; + + (void) rw_wrlock(&nb_cache_lock); + smb_netbios_cache_key(key, name->name, name->scope); + item = ht_find_item(smb_netbios_cache, key); + if (item && item->hi_data) { + entry = (struct name_entry *)item->hi_data; + (void) mutex_lock(&entry->mtx); + ht_mark_delete(smb_netbios_cache, item); + (void) mutex_unlock(&entry->mtx); + } + (void) rw_unlock(&nb_cache_lock); +} + +/* + * smb_netbios_cache_insert_list + * + * Insert a name with multiple addresses + */ +int +smb_netbios_cache_insert_list(struct name_entry *name) +{ + struct name_entry entry; + struct addr_entry *addr; + + addr = &name->addr_list; + do { + smb_init_name_struct(NETBIOS_EMPTY_NAME, 0, name->scope, + addr->sin.sin_addr.s_addr, + addr->sin.sin_port, + name->attributes, + addr->attributes, + &entry); + (void) memcpy(entry.name, name->name, NETBIOS_NAME_SZ); + entry.addr_list.refresh_ttl = entry.addr_list.ttl = + addr->refresh_ttl; + (void) smb_netbios_cache_insert(&entry); + addr = addr->forw; + } while (addr != &name->addr_list); + + return (0); +} + +void +smb_netbios_cache_update_entry(struct name_entry *entry, + struct name_entry *name) +{ + struct addr_entry *addr; + struct addr_entry *name_addr; + + addr = &entry->addr_list; + name_addr = &name->addr_list; + + if (IS_UNIQUE(entry->attributes)) { + do { + addr->ttl = name_addr->ttl; + addr = addr->forw; + } while (addr != &entry->addr_list); + + } else { + do { + if (NETBIOS_SAME_IP(addr, name_addr) && + (addr->sin.sin_port == name_addr->sin.sin_port)) { + addr->ttl = name_addr->ttl; + return; + } + addr = addr->forw; + } while (addr != &entry->addr_list); + } +} + +/* + * smb_netbios_cache_status + * + * Scan the name cache and gather status for + * Node Status response for names in the given scope + */ +unsigned char * +smb_netbios_cache_status(unsigned char *buf, int bufsize, unsigned char *scope) +{ + HT_ITERATOR hti; + HT_ITEM *item; + struct name_entry *name; + unsigned char *numnames; + unsigned char *scan; + unsigned char *scan_end; + + scan = buf; + scan_end = scan + bufsize; + + numnames = scan++; + *numnames = 0; + + (void) rw_rdlock(&nb_cache_lock); + item = ht_findfirst(smb_netbios_cache, &hti); + do { + if (item == 0) + break; + + if (item->hi_data == 0) + continue; + + if ((scan + NETBIOS_NAME_SZ + 2) >= scan_end) + /* no room for adding next entry */ + break; + + name = (struct name_entry *)item->hi_data; + (void) mutex_lock(&name->mtx); + + if (IS_LOCAL(name->attributes) && + (strcasecmp((char *)scope, (char *)name->scope) == 0)) { + bcopy(name->name, scan, NETBIOS_NAME_SZ); + scan += NETBIOS_NAME_SZ; + *scan++ = PUBLIC_BITS(name->attributes) >> 8; + *scan++ = PUBLIC_BITS(name->attributes); + (*numnames)++; + } + + (void) mutex_unlock(&name->mtx); + } while ((item = ht_findnext(&hti)) != 0); + (void) rw_unlock(&nb_cache_lock); + + return (scan); +} + +void +smb_netbios_cache_reset_ttl() +{ + struct addr_entry *addr; + struct name_entry *name; + HT_ITERATOR hti; + HT_ITEM *item; + + (void) rw_rdlock(&nb_cache_lock); + item = ht_findfirst(smb_netbios_cache, &hti); + do { + if (item == 0) + break; + + if (item->hi_data == 0) + continue; + + name = (struct name_entry *)item->hi_data; + (void) mutex_lock(&name->mtx); + + addr = &name->addr_list; + do { + if (addr->ttl < 1) { + if (addr->refresh_ttl) + addr->ttl = addr->refresh_ttl; + else + addr->refresh_ttl = addr->ttl = + TO_SECONDS(DEFAULT_TTL); + } + addr = addr->forw; + } while (addr != &name->addr_list); + + (void) mutex_unlock(&name->mtx); + } while ((item = ht_findnext(&hti)) != 0); + (void) rw_unlock(&nb_cache_lock); +} + +/* + * Returns TRUE when given name is added to the refresh queue + * FALSE if not. + */ +static boolean_t +smb_netbios_cache_insrefq(name_queue_t *refq, HT_ITEM *item) +{ + struct name_entry *name; + struct name_entry *refent; + + name = (struct name_entry *)item->hi_data; + + if (IS_LOCAL(name->attributes)) { + if (IS_UNIQUE(name->attributes)) { + refent = smb_netbios_name_dup(name, 1); + if (refent) + QUEUE_INSERT_TAIL(&refq->head, refent) + + /* next name */ + return (B_TRUE); + } + } else { + ht_mark_delete(smb_netbios_cache, item); + refent = smb_netbios_name_dup(name, 0); + if (refent) + QUEUE_INSERT_TAIL(&refq->head, refent) + + /* next name */ + return (B_TRUE); + } + + return (B_FALSE); +} + +/* + * smb_netbios_cache_refresh + * + * Scans the name cache and add all local unique names + * and non-local names the passed refresh queue. Non- + * local names will also be marked as deleted. + * + * NOTE that the caller MUST protect the queue using + * its mutex + */ +void +smb_netbios_cache_refresh(name_queue_t *refq) +{ + struct name_entry *name; + struct addr_entry *addr; + HT_ITERATOR hti; + HT_ITEM *item; + + bzero(&refq->head, sizeof (refq->head)); + refq->head.forw = refq->head.back = &refq->head; + + (void) rw_rdlock(&nb_cache_lock); + item = ht_findfirst(smb_netbios_cache, &hti); + do { /* name loop */ + if (item == 0) + break; + + if (item->hi_data == 0) + continue; + + name = (struct name_entry *)item->hi_data; + (void) mutex_lock(&name->mtx); + + addr = &name->addr_list; + do { /* address loop */ + if (addr->ttl > 0) { + addr->ttl--; + if (addr->ttl == 0) { + if (smb_netbios_cache_insrefq(refq, + item)) + break; + } + } + addr = addr->forw; + } while (addr != &name->addr_list); + + (void) mutex_unlock(&name->mtx); + } while ((item = ht_findnext(&hti)) != 0); + (void) rw_unlock(&nb_cache_lock); +} + +/* + * smb_netbios_cache_delete_locals + * + * Scans the name cache and add all local names to + * the passed delete queue. + * + * NOTE that the caller MUST protect the queue using + * its mutex + */ +void +smb_netbios_cache_delete_locals(name_queue_t *delq) +{ + struct name_entry *entry; + struct name_entry *delent; + HT_ITERATOR hti; + HT_ITEM *item; + + bzero(&delq->head, sizeof (delq->head)); + delq->head.forw = delq->head.back = &delq->head; + + (void) rw_wrlock(&nb_cache_lock); + item = ht_findfirst(smb_netbios_cache, &hti); + do { + if (item == 0) + break; + + if (item->hi_data == 0) + continue; + + entry = (struct name_entry *)item->hi_data; + (void) mutex_lock(&entry->mtx); + + if (IS_LOCAL(entry->attributes)) { + ht_mark_delete(smb_netbios_cache, item); + delent = smb_netbios_name_dup(entry, 1); + if (delent) + QUEUE_INSERT_TAIL(&delq->head, delent) + } + + (void) mutex_unlock(&entry->mtx); + } while ((item = ht_findnext(&hti)) != 0); + (void) rw_unlock(&nb_cache_lock); +} + +void +smb_netbios_name_freeaddrs(struct name_entry *entry) +{ + struct addr_entry *addr; + + if (entry == 0) + return; + + while ((addr = entry->addr_list.forw) != &entry->addr_list) { + QUEUE_CLIP(addr); + free(addr); + } +} + +/* + * smb_netbios_cache_count + * + * Returns the number of names in the cache + */ +int +smb_netbios_cache_count() +{ + int cnt; + + (void) rw_rdlock(&nb_cache_lock); + cnt = ht_get_total_items(smb_netbios_cache); + (void) rw_unlock(&nb_cache_lock); + + return (cnt); +} + +void +smb_netbios_cache_dump(void) +{ + struct name_entry *name; + HT_ITERATOR hti; + HT_ITEM *item; + + (void) rw_rdlock(&nb_cache_lock); + item = ht_findfirst(smb_netbios_cache, &hti); + while (item) { + if (item->hi_data) { + name = (struct name_entry *)item->hi_data; + (void) mutex_lock(&name->mtx); + smb_netbios_name_dump(name); + (void) mutex_unlock(&name->mtx); + } + item = ht_findnext(&hti); + } + (void) rw_unlock(&nb_cache_lock); +} + +void +smb_netbios_name_dump(struct name_entry *entry) +{ + struct addr_entry *addr; + int count = 0; + + if (smb_netbios_name_debug == 0) + return; + + syslog(LOG_DEBUG, "name='%15.15s<%02X>' scope='%s' attr=0x%x", + entry->name, entry->name[15], + entry->scope, entry->attributes); + addr = &entry->addr_list; + do { + syslog(LOG_DEBUG, "addr_list[%d]:", count++); + syslog(LOG_DEBUG, " attributes = 0x%x", addr->attributes); + syslog(LOG_DEBUG, " conflict_timer = %d", + addr->conflict_timer); + syslog(LOG_DEBUG, " refresh_ttl = %d", addr->refresh_ttl); + syslog(LOG_DEBUG, " ttl = %d", addr->ttl); + syslog(LOG_DEBUG, " sin.sin_addr = %s", + inet_ntoa(addr->sin.sin_addr)); + syslog(LOG_DEBUG, " sin.sin_port = %d", addr->sin.sin_port); + syslog(LOG_DEBUG, " sin.sinlen = %d", addr->sinlen); + addr = addr->forw; + } while (addr != &entry->addr_list); +} + +void +smb_netbios_name_logf(struct name_entry *entry) +{ + struct addr_entry *addr; + NETBIOS_NAMEBUF; + + (void) smb_strname(entry, namebuf, sizeof (namebuf)); + syslog(LOG_DEBUG, "%s flags=0x%x\n", namebuf, entry->attributes); + addr = &entry->addr_list; + do { + syslog(LOG_DEBUG, " %s ttl=%d flags=0x%x", + inet_ntoa(addr->sin.sin_addr), + addr->ttl, addr->attributes); + addr = addr->forw; + } while (addr && (addr != &entry->addr_list)); +} + +/* + * smb_netbios_name_dup + * + * Duplicate the given name entry. If 'alladdr' is 0 only + * copy the primary address otherwise duplicate all the + * addresses. NOTE that the duplicate structure is not + * like a regular cache entry i.e. it's a contiguous block + * of memory and each addr structure doesn't have it's own + * allocated memory. So, the returned structure can be freed + * by one free call. + */ +struct name_entry * +smb_netbios_name_dup(struct name_entry *entry, int alladdr) +{ + struct addr_entry *addr; + struct addr_entry *dup_addr; + struct name_entry *dup; + int addr_cnt = 0; + int size = 0; + + if (alladdr) { + addr = entry->addr_list.forw; + while (addr && (addr != &entry->addr_list)) { + addr_cnt++; + addr = addr->forw; + } + } + + size = sizeof (struct name_entry) + + (addr_cnt * sizeof (struct addr_entry)); + dup = (struct name_entry *)malloc(size); + if (dup == 0) + return (0); + + bzero(dup, size); + + dup->forw = dup->back = dup; + dup->attributes = entry->attributes; + (void) memcpy(dup->name, entry->name, NETBIOS_NAME_SZ); + (void) strlcpy((char *)dup->scope, (char *)entry->scope, + NETBIOS_DOMAIN_NAME_MAX); + dup->addr_list = entry->addr_list; + dup->addr_list.forw = dup->addr_list.back = &dup->addr_list; + + if (alladdr == 0) + return (dup); + + /* LINTED - E_BAD_PTR_CAST_ALIGN */ + dup_addr = (struct addr_entry *)((unsigned char *)dup + + sizeof (struct name_entry)); + + addr = entry->addr_list.forw; + while (addr && (addr != &entry->addr_list)) { + *dup_addr = *addr; + QUEUE_INSERT_TAIL(&dup->addr_list, dup_addr); + addr = addr->forw; + dup_addr++; + } + + return (dup); +} + +static char * +smb_strname(struct name_entry *name, char *buf, int bufsize) +{ + char *p; + + (void) snprintf(buf, bufsize, "%15.15s", name->name); + p = strchr(buf, ' '); + if (p) + (void) snprintf(p, 5, "<%02X>", name->name[15]); + + return (buf); +} + +static void +hash_callback(HT_ITEM *item) +{ + struct name_entry *entry; + + if (item && item->hi_data) { + entry = (struct name_entry *)item->hi_data; + smb_netbios_name_freeaddrs(entry); + free(entry); + } +} + + +/*ARGSUSED*/ +static int +smb_netbios_match(const char *key1, const char *key2, size_t n) +{ + int res; + + res = bcmp(key1, key2, NETBIOS_NAME_SZ); + if (res == 0) { + /* Names are the same, compare scopes */ + res = strcmp(key1 + NETBIOS_NAME_SZ, key2 + NETBIOS_NAME_SZ); + } + + return (res); +} + +static void +smb_netbios_cache_key(char *key, unsigned char *name, unsigned char *scope) +{ + bzero(key, NETBIOS_HKEY_SZ); + (void) memcpy(key, name, NETBIOS_NAME_SZ); + (void) memcpy(key + NETBIOS_NAME_SZ, scope, + strlen((const char *)scope)); +} diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_datagram.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_datagram.c new file mode 100644 index 000000000000..2775ccfe7ae1 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_datagram.c @@ -0,0 +1,1061 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Description: + * + * Contains base code for netbios datagram service. + * + * Relavent sections from RFC1002: + * + * 5.3. NetBIOS DATAGRAM SERVICE PROTOCOLS + * + * The following are GLOBAL variables and should be NetBIOS user + * configurable: + * + * - SCOPE_ID: the non-leaf section of the domain name preceded by a + * '.' which represents the domain of the NetBIOS scope for the + * NetBIOS name. The following protocol description only supports + * single scope operation. + * + * - MAX_DATAGRAM_LENGTH: the maximum length of an IP datagram. The + * minimal maximum length defined in for IP is 576 bytes. This + * value is used when determining whether to fragment a NetBIOS + * datagram. Implementations are expected to be capable of + * receiving unfragmented NetBIOS datagrams up to their maximum + * size. + * + * - BROADCAST_ADDRESS: the IP address B-nodes use to send datagrams + * with group name destinations and broadcast datagrams. The + * default is the IP broadcast address for a single IP network. + * + * + * The following are Defined Constants for the NetBIOS Datagram + * Service: + * + * - DGM_SRVC_UDP_PORT: the globally well-known UDP port allocated + * where the NetBIOS Datagram Service receives UDP packets. See + * section 6, "Defined Constants", for its value. + */ + +/* + * + * 6. DEFINED CONSTANTS AND VARIABLES + * + * GENERAL: + * + * SCOPE_ID The name of the NetBIOS scope. + * + * This is expressed as a character + * string meeting the requirements of + * the domain name system and without + * a leading or trailing "dot". + * + * An implementation may elect to make + * this a single global value for the + * node or allow it to be specified + * with each separate NetBIOS name + * (thus permitting cross-scope + * references.) + * + * BROADCAST_ADDRESS An IP address composed of the + * node network and subnetwork + * numbers with all remaining bits set + * to one. + * + * I.e. "Specific subnet" broadcast + * addressing according to section 2.3 + * of RFC 950. + * + * BCAST_REQ_RETRY_TIMEOUT 250 milliseconds. + * An adaptive timer may be used. + * + * BCAST_REQ_RETRY_COUNT 3 + * + * UCAST_REQ_RETRY_TIMEOUT 5 seconds + * An adaptive timer may be used. + * + * UCAST_REQ_RETRY_COUNT 3 + * + * MAX_DATAGRAM_LENGTH 576 bytes (default) + * + * DATAGRAM SERVICE: + * + * DGM_SRVC_UDP_PORT 138 (decimal) + * + * FRAGMENT_TO 2 seconds (default) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static int datagram_sock = -1; +static short datagram_id = 1; +static struct datagram_queue smb_datagram_queue; +static mutex_t smb_dgq_mtx; + +/* + * Function: smb_netbios_datagram_tick(void) + * + * Description: + * + * Called once a second to handle time to live timeouts in + * datagram assembly queue. + * + * Inputs: + * + * Returns: + * void -> Nothing at all... + */ + +void +smb_netbios_datagram_tick(void) +{ + struct datagram *entry; + struct datagram *next; + + (void) mutex_lock(&smb_dgq_mtx); + + for (entry = smb_datagram_queue.forw; + entry != (struct datagram *)((uintptr_t)&smb_datagram_queue); + entry = next) { + next = entry->forw; + if (--entry->discard_timer == 0) { + /* Toss it */ + QUEUE_CLIP(entry); + free(entry); + } + } + (void) mutex_unlock(&smb_dgq_mtx); +} + +void +smb_netbios_datagram_fini() +{ + struct datagram *entry; + + (void) mutex_lock(&smb_dgq_mtx); + while ((entry = smb_datagram_queue.forw) != + (struct datagram *)((uintptr_t)&smb_datagram_queue)) { + QUEUE_CLIP(entry); + free(entry); + } + (void) mutex_unlock(&smb_dgq_mtx); +} + +/* + * Function: int smb_netbios_send_Bnode_datagram(unsigned char *data, + * struct name_entry *source, struct name_entry *destination, + * uint32_t broadcast) + * + * Description from rfc1002: + * + * 5.3.1. B NODE TRANSMISSION OF NetBIOS DATAGRAMS + * + * PROCEDURE send_datagram(data, source, destination, broadcast) + * + * (* + * * user initiated processing on B node + * *) + * + * BEGIN + * group = FALSE; + * + * do name discovery on destination name, returns name type and + * IP address; + * + * IF name type is group name THEN + * BEGIN + * group = TRUE; + * END + * + * (* + * * build datagram service UDP packet; + * *) + * convert source and destination NetBIOS names into + * half-ASCII, biased encoded name; + * SOURCE_NAME = cat(source, SCOPE_ID); + * SOURCE_IP = this nodes IP address; + * SOURCE_PORT = DGM_SRVC_UDP_PORT; + * + * IF NetBIOS broadcast THEN + * BEGIN + * DESTINATION_NAME = cat("*", SCOPE_ID) + * END + * ELSE + * BEGIN + * DESTINATION_NAME = cat(destination, SCOPE_ID) + * END + * + * MSG_TYPE = select_one_from_set + * {BROADCAST, DIRECT_UNIQUE, DIRECT_GROUP} + * DGM_ID = next transaction id for Datagrams; + * DGM_LENGTH = length of data + length of second level encoded + * source and destination names; + * + * IF (length of the NetBIOS Datagram, including UDP and + * IP headers, > MAX_DATAGRAM_LENGTH) THEN + * BEGIN + * (* + * * fragment NetBIOS datagram into 2 UDP packets + * *) + * Put names into 1st UDP packet and any data that fits + * after names; + * Set MORE and FIRST bits in 1st UDP packets FLAGS; + * OFFSET in 1st UDP = 0; + * + * Replicate NetBIOS Datagram header from 1st UDP packet + * into 2nd UDP packet; + * Put rest of data in 2nd UDP packet; + * Clear MORE and FIRST bits in 2nd UDP packets FLAGS; + * OFFSET in 2nd UDP = DGM_LENGTH - number of name and + * data bytes in 1st UDP; + * END + * BEGIN + * (* + * * Only need one UDP packet + * *) + * USER_DATA = data; + * Clear MORE bit and set FIRST bit in FLAGS; + * OFFSET = 0; + * END + * + * IF (group == TRUE) OR (NetBIOS broadcast) THEN + * BEGIN + * send UDP packet(s) to BROADCAST_ADDRESS; + * END + * ELSE + * BEGIN + * send UDP packet(s) to IP address returned by name + * discovery; + * END + * END (* procedure *) + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MSG_TYPE | FLAGS | DGM_ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SOURCE_IP | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SOURCE_PORT | DGM_LENGTH | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | PACKET_OFFSET | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * MSG_TYPE values (in hexidecimal): + * + * 10 - DIRECT_UNIQUE DATAGRAM + * 11 - DIRECT_GROUP DATAGRAM + * 12 - BROADCAST DATAGRAM + * 13 - DATAGRAM ERROR + * 14 - DATAGRAM QUERY REQUEST + * 15 - DATAGRAM POSITIVE QUERY RESPONSE + * 16 - DATAGRAM NEGATIVE QUERY RESPONSE + * + * Bit definitions of the FLAGS field: + * + * 0 1 2 3 4 5 6 7 + * +---+---+---+---+---+---+---+---+ + * | 0 | 0 | 0 | 0 | SNT | F | M | + * +---+---+---+---+---+---+---+---+ + * + * Symbol Bit(s) Description + * + * M 7 MORE flag, If set then more NetBIOS datagram + * fragments follow. + * + * F 6 FIRST packet flag, If set then this is first + * (and possibly only) fragment of NetBIOS + * datagram + * + * SNT 4,5 Source End-Node type: + * 00 = B node + * 01 = P node + * 10 = M node + * 11 = NBDD + * RESERVED 0-3 Reserved, must be zero (0) + * (But MS sets bit 3 in this field) + * + */ + +int +smb_netbios_datagram_send(struct name_entry *src, struct name_entry *dest, + unsigned char *data, int length) +{ + uint32_t ipaddr; + size_t count, srclen, destlen, sinlen; + struct addr_entry *addr; + struct sockaddr_in sin; + char *buffer; + char ha_source[NETBIOS_DOMAIN_NAME_MAX]; + char ha_dest[NETBIOS_DOMAIN_NAME_MAX]; + net_cfg_t cfg; + + (void) smb_first_level_name_encode(src, (unsigned char *)ha_source, + sizeof (ha_source)); + srclen = strlen(ha_source) + 1; + + (void) smb_first_level_name_encode(dest, (unsigned char *)ha_dest, + sizeof (ha_dest)); + destlen = strlen(ha_dest) + 1; + + /* give some extra room */ + buffer = (char *)malloc(MAX_DATAGRAM_LENGTH * 4); + if (buffer == 0) { + syslog(LOG_ERR, "netbios: datagram send (resource shortage)"); + return (-1); + } + + buffer[0] = DATAGRAM_TYPE_DIRECT_UNIQUE; + switch (smb_node_type) { + case 'B': + buffer[1] = DATAGRAM_FLAGS_B_NODE | DATAGRAM_FLAGS_FIRST; + break; + case 'P': + buffer[1] = DATAGRAM_FLAGS_P_NODE | DATAGRAM_FLAGS_FIRST; + break; + case 'M': + buffer[1] = DATAGRAM_FLAGS_M_NODE | DATAGRAM_FLAGS_FIRST; + break; + case 'H': + default: + buffer[1] = DATAGRAM_FLAGS_H_NODE | DATAGRAM_FLAGS_FIRST; + break; + } + + datagram_id++; + BE_OUT16(&buffer[2], datagram_id); + (void) memcpy(&buffer[4], &src->addr_list.sin.sin_addr.s_addr, + sizeof (uint32_t)); + (void) memcpy(&buffer[8], &src->addr_list.sin.sin_port, + sizeof (uint16_t)); + BE_OUT16(&buffer[10], length + srclen + destlen); + BE_OUT16(&buffer[12], 0); + + bcopy(ha_source, &buffer[14], srclen); + bcopy(ha_dest, &buffer[14 + srclen], destlen); + bcopy(data, &buffer[14 + srclen + destlen], length); + count = &buffer[14 + srclen + destlen + length] - buffer; + + bzero(&sin, sizeof (sin)); + sin.sin_family = AF_INET; + sinlen = sizeof (sin); + addr = &dest->addr_list; + do { + ipaddr = addr->sin.sin_addr.s_addr; + /* Don't send anything to myself... */ + if (smb_nic_get_byip(ipaddr, &cfg) != NULL) { + goto next; + } + + sin.sin_addr.s_addr = ipaddr; + sin.sin_port = addr->sin.sin_port; + (void) sendto(datagram_sock, buffer, count, 0, + (struct sockaddr *)&sin, sinlen); + +next: addr = addr->forw; + } while (addr != &dest->addr_list); + free(buffer); + return (0); +} + + +int +smb_netbios_datagram_send_to_net(struct name_entry *src, + struct name_entry *dest, char *data, int length) +{ + uint32_t ipaddr; + size_t count, srclen, destlen, sinlen; + struct addr_entry *addr; + struct sockaddr_in sin; + char *buffer; + char ha_source[NETBIOS_DOMAIN_NAME_MAX]; + char ha_dest[NETBIOS_DOMAIN_NAME_MAX]; + net_cfg_t cfg; + + (void) smb_first_level_name_encode(src, (unsigned char *)ha_source, + sizeof (ha_source)); + srclen = strlen(ha_source) + 1; + + (void) smb_first_level_name_encode(dest, (unsigned char *)ha_dest, + sizeof (ha_dest)); + destlen = strlen(ha_dest) + 1; + + /* give some extra room */ + buffer = (char *)malloc(MAX_DATAGRAM_LENGTH * 4); + if (buffer == 0) { + syslog(LOG_ERR, "netbios: datagram send (resource shortage)"); + return (-1); + } + + buffer[0] = DATAGRAM_TYPE_DIRECT_UNIQUE; + switch (smb_node_type) { + case 'B': + buffer[1] = DATAGRAM_FLAGS_B_NODE | DATAGRAM_FLAGS_FIRST; + break; + case 'P': + buffer[1] = DATAGRAM_FLAGS_P_NODE | DATAGRAM_FLAGS_FIRST; + break; + case 'M': + buffer[1] = DATAGRAM_FLAGS_M_NODE | DATAGRAM_FLAGS_FIRST; + break; + case 'H': + default: + buffer[1] = DATAGRAM_FLAGS_H_NODE | DATAGRAM_FLAGS_FIRST; + break; + } + + datagram_id++; + BE_OUT16(&buffer[2], datagram_id); + (void) memcpy(&buffer[4], &src->addr_list.sin.sin_addr.s_addr, + sizeof (uint32_t)); + (void) memcpy(&buffer[8], &src->addr_list.sin.sin_port, + sizeof (uint16_t)); + BE_OUT16(&buffer[10], length + srclen + destlen); + BE_OUT16(&buffer[12], 0); + + bcopy(ha_source, &buffer[14], srclen); + bcopy(ha_dest, &buffer[14 + srclen], destlen); + bcopy(data, &buffer[14 + srclen + destlen], length); + count = &buffer[14 + srclen + destlen + length] - buffer; + + bzero(&sin, sizeof (sin)); + sin.sin_family = AF_INET; + sinlen = sizeof (sin); + addr = &dest->addr_list; + do { + ipaddr = addr->sin.sin_addr.s_addr; + if (smb_nic_get_byip(ipaddr, &cfg) != NULL) { + goto next; + } + sin.sin_addr.s_addr = ipaddr; + sin.sin_port = addr->sin.sin_port; + (void) sendto(datagram_sock, buffer, count, 0, + (struct sockaddr *)&sin, sinlen); + +next: addr = addr->forw; + } while (addr != &dest->addr_list); + free(buffer); + return (0); +} + + +int +smb_datagram_decode(struct datagram *datagram, int bytes) +{ + unsigned char *ha_src; + unsigned char *ha_dest; + unsigned char *data; + + if (bytes < DATAGRAM_HEADER_LENGTH) { + syslog(LOG_ERR, "NbtDatagramDecode[%d]: too small packet", + bytes); + return (-1); + } + + ha_src = &datagram->rawbuf[DATAGRAM_HEADER_LENGTH]; + ha_dest = ha_src + strlen((char *)ha_src) + 1; + data = ha_dest + strlen((char *)ha_dest) + 1; + + bzero(&datagram->src, sizeof (struct name_entry)); + bzero(&datagram->dest, sizeof (struct name_entry)); + + datagram->rawbytes = bytes; + datagram->packet_type = datagram->rawbuf[0]; + datagram->flags = datagram->rawbuf[1]; + datagram->datagram_id = BE_IN16(&datagram->rawbuf[2]); + + datagram->src.addr_list.sinlen = sizeof (struct sockaddr_in); + (void) memcpy(&datagram->src.addr_list.sin.sin_addr.s_addr, + &datagram->rawbuf[4], sizeof (uint32_t)); + (void) memcpy(&datagram->src.addr_list.sin.sin_port, + &datagram->rawbuf[8], sizeof (uint16_t)); + datagram->src.addr_list.forw = datagram->src.addr_list.back = + &datagram->src.addr_list; + + datagram->data = data; + datagram->data_length = BE_IN16(&datagram->rawbuf[10]); + datagram->offset = BE_IN16(&datagram->rawbuf[12]); + + if (smb_first_level_name_decode(ha_src, &datagram->src) < 0) { + syslog(LOG_DEBUG, "NbtDatagram[%s]: invalid calling name", + inet_ntoa(datagram->src.addr_list.sin.sin_addr)); + syslog(LOG_DEBUG, "Calling name: <%02X>%32.32s", + ha_src[0], &ha_src[1]); + } + + datagram->dest.addr_list.forw = datagram->dest.addr_list.back = + &datagram->dest.addr_list; + + if (smb_first_level_name_decode(ha_dest, &datagram->dest) < 0) { + syslog(LOG_DEBUG, "NbtDatagram[%s]: invalid called name", + inet_ntoa(datagram->src.addr_list.sin.sin_addr)); + syslog(LOG_DEBUG, "Called name: <%02X>%32.32s", ha_dest[0], + &ha_dest[1]); + } + + return (0); +} + + +/* + * Function: int smb_netbios_process_BPM_datagram(unsigned char *packet, + * struct addr_entry *addr) + * + * Description from rfc1002: + * + * 5.3.3. RECEPTION OF NetBIOS DATAGRAMS BY ALL NODES + * + * The following algorithm discards out of order NetBIOS Datagram + * fragments. An implementation which reassembles out of order + * NetBIOS Datagram fragments conforms to this specification. The + * fragment discard timer is initialized to the value FRAGMENT_TIMEOUT. + * This value should be user configurable. The default value is + * given in Section 6, "Defined Constants and Variables". + * + * PROCEDURE datagram_packet(packet) + * + * (* + * * processing initiated by datagram packet reception + * * on B, P and M nodes + * *) + * BEGIN + * (* + * * if this node is a P node, ignore + * * broadcast packets. + * *) + * + * IF this is a P node AND incoming packet is + * a broadcast packet THEN + * BEGIN + * discard packet; + * END + * + * CASE packet type OF + * + * DATAGRAM SERVICE: + * BEGIN + * IF FIRST bit in FLAGS is set THEN + * BEGIN + * IF MORE bit in FLAGS is set THEN + * BEGIN + * Save 1st UDP packet of the Datagram; + * Set this Datagrams fragment discard + * timer to FRAGMENT_TIMEOUT; + * return; + * END + * ELSE + * Datagram is composed of a single + * UDP packet; + * END + * ELSE + * BEGIN + * (* Have the second fragment of a Datagram *) + * + * Search for 1st fragment by source IP address + * and DGM_ID; + * IF found 1st fragment THEN + * Process both UDP packets; + * ELSE + * BEGIN + * discard 2nd fragment UDP packet; + * return; + * END + * END + * + * IF DESTINATION_NAME is '*' THEN + * BEGIN + * (* NetBIOS broadcast *) + * + * deliver USER_DATA from UDP packet(s) to all + * outstanding receive broadcast + * datagram requests; + * return; + * END + * ELSE + * BEGIN (* non-broadcast *) + * (* Datagram for Unique or Group Name *) + * + * IF DESTINATION_NAME is not present in the + * local name table THEN + * BEGIN + * (* destination not present *) + * build DATAGRAM ERROR packet, clear + * FIRST and MORE bit, put in + * this nodes IP and PORT, set + * ERROR_CODE; + * send DATAGRAM ERROR packet to + * source IP address and port + * of UDP; + * discard UDP packet(s); + * return; + * END + * ELSE + * BEGIN (* good *) + * (* + * * Replicate received NetBIOS datagram for + * * each recipient + * *) + * FOR EACH pending NetBIOS users receive + * datagram operation + * BEGIN + * IF source name of operation + * matches destination name + * of packet THEN + * BEGIN + * deliver USER_DATA from UDP + * packet(s); + * END + * END (* for each *) + * return; + * END (* good *) + * END (* non-broadcast *) + * END (* datagram service *) + * + * DATAGRAM ERROR: + * BEGIN + * (* + * * name service returned incorrect information + * *) + * + * inform local name service that incorrect + * information was provided; + * + * IF this is a P or M node THEN + * BEGIN + * (* + * * tell NetBIOS Name Server that it may + * * have given incorrect information + * *) + * + * send NAME RELEASE REQUEST with name + * and incorrect IP address to NetBIOS + * Name Server; + * END + * END (* datagram error *) + * + * END (* case *) + * END + */ + +static struct datagram * +smb_netbios_datagram_getq(struct datagram *datagram) +{ + struct datagram *prev = 0; + + (void) mutex_lock(&smb_dgq_mtx); + for (prev = smb_datagram_queue.forw; + prev != (struct datagram *)((uintptr_t)&smb_datagram_queue); + prev = prev->forw) { + if (prev->src.addr_list.sin.sin_addr.s_addr == + datagram->src.addr_list.sin.sin_addr.s_addr) { + /* Something waiting */ + QUEUE_CLIP(prev); + (void) mutex_unlock(&smb_dgq_mtx); + bcopy(datagram->data, &prev->data[prev->data_length], + datagram->data_length); + prev->data_length += datagram->data_length; + free(datagram); + return (prev); + } + } + (void) mutex_unlock(&smb_dgq_mtx); + + return (0); +} + +static void +smb_netbios_BPM_datagram(struct datagram *datagram) +{ + struct name_entry *entry = 0; + struct datagram *qpacket = 0; + pthread_t browser_dispatch; + + switch (datagram->packet_type) { + case DATAGRAM_TYPE_BROADCAST : + if (smb_node_type == 'P') { + /* + * if this node is a P node, ignore + * broadcast packets. + */ + break; + } + /* FALLTHROUGH */ + + case DATAGRAM_TYPE_DIRECT_UNIQUE : + case DATAGRAM_TYPE_DIRECT_GROUP : + if ((datagram->flags & DATAGRAM_FLAGS_FIRST) != 0) { + if (datagram->flags & DATAGRAM_FLAGS_MORE) { + /* Save 1st UDP packet of the Datagram */ + datagram->discard_timer = FRAGMENT_TIMEOUT; + (void) mutex_lock(&smb_dgq_mtx); + QUEUE_INSERT_TAIL(&smb_datagram_queue, datagram) + (void) mutex_unlock(&smb_dgq_mtx); + return; + } + /* process datagram */ + } else { + qpacket = smb_netbios_datagram_getq(datagram); + if (qpacket) { + datagram = qpacket; + goto process_datagram; + } + break; + } + +process_datagram: + entry = 0; + if ((strcmp((char *)datagram->dest.name, "*") == 0) || + ((entry = + smb_netbios_cache_lookup(&datagram->dest)) != 0)) { + if (entry) { + int is_local = IS_LOCAL(entry->attributes); + smb_netbios_cache_unlock_entry(entry); + + if (is_local) { + (void) pthread_create(&browser_dispatch, + 0, smb_browser_dispatch, + (void *)datagram); + (void) pthread_detach(browser_dispatch); + return; + } + } + + datagram->rawbuf[0] = DATAGRAM_TYPE_ERROR_DATAGRAM; + datagram->rawbuf[1] &= DATAGRAM_FLAGS_SRC_TYPE; + + (void) memcpy(&datagram->rawbuf[4], + &datagram->src.addr_list.sin.sin_addr.s_addr, + sizeof (uint32_t)); + BE_OUT16(&datagram->rawbuf[8], DGM_SRVC_UDP_PORT); + + (void) sendto(datagram_sock, datagram->rawbuf, + datagram->rawbytes, 0, + (struct sockaddr *)&datagram->src.addr_list.sin, + datagram->src.addr_list.sinlen); + } + break; + + case DATAGRAM_TYPE_ERROR_DATAGRAM : + break; + } + free(datagram); +} + + +/* + * smb_netbios_process_NBDD_datagram + * + * Description from rfc1002: + * + * + * 5.3.4. PROTOCOLS FOR THE NBDD + * + * The key to NetBIOS Datagram forwarding service is the packet + * delivered to the destination end node must have the same NetBIOS + * header as if the source end node sent the packet directly to the + * destination end node. Consequently, the NBDD does not reassemble + * NetBIOS Datagrams. It forwards the UDP packet as is. + * + * PROCEDURE datagram_packet(packet) + * + * (* + * * processing initiated by a incoming datagram service + * * packet on a NBDD node. + * *) + * + * BEGIN + * CASE packet type OF + * + * DATAGRAM SERVICE: + * BEGIN + * IF packet was sent as a directed + * NetBIOS datagram THEN + * BEGIN + * (* + * * provide group forwarding service + * * + * * Forward datagram to each member of the + * * group. Can forward via: + * * 1) get list of group members and send + * * the DATAGRAM SERVICE packet unicast + * * to each + * * 2) use Group Multicast, if available + * * 3) combination of 1) and 2) + * *) + * + * ... + * + * END + * + * ELSE + * BEGIN + * (* + * * provide broadcast forwarding service + * * + * * Forward datagram to every node in the + * * NetBIOS scope. Can forward via: + * * 1) get list of group members and send + * * the DATAGRAM SERVICE packet unicast + * * to each + * * 2) use Group Multicast, if available + * * 3) combination of 1) and 2) + * *) + * + * ... + * + * END + * END (* datagram service *) + * + * DATAGRAM ERROR: + * BEGIN + * (* + * * Should never receive these because Datagrams + * * forwarded have source end node IP address and + * * port in NetBIOS header. + * *) + * + * send DELETE NAME REQUEST with incorrect name and + * IP address to NetBIOS Name Server; + * + * END (* datagram error *) + * + * DATAGRAM QUERY REQUEST: + * BEGIN + * IF can send packet to DESTINATION_NAME THEN + * BEGIN + * (* + * * NBDD is able to relay Datagrams for + * * this name + * *) + * + * send POSITIVE DATAGRAM QUERY RESPONSE to + * REQUEST source IP address and UDP port + * with requests DGM_ID; + * END + * ELSE + * BEGIN + * (* + * * NBDD is NOT able to relay Datagrams for + * * this name + * *) + * + * send NEGATIVE DATAGRAM QUERY RESPONSE to + * REQUEST source IP address and UDP port + * + * with requests DGM_ID; + * END + * END (* datagram query request *) + * + * END (* case *) + * END (* procedure *) + */ + + +/* + * Function: int smb_netbios_datagram_service_daemon(void) + * + * Description: + * + * 4.4. DATAGRAM SERVICE PACKETS + * + * 4.4.1. NetBIOS DATAGRAM HEADER + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MSG_TYPE | FLAGS | DGM_ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SOURCE_IP | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SOURCE_PORT | DGM_LENGTH | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | PACKET_OFFSET | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * MSG_TYPE values (in hexidecimal): + * + * 10 - DIRECT_UNIQUE DATAGRAM + * 11 - DIRECT_GROUP DATAGRAM + * 12 - BROADCAST DATAGRAM + * 13 - DATAGRAM ERROR + * 14 - DATAGRAM QUERY REQUEST + * 15 - DATAGRAM POSITIVE QUERY RESPONSE + * 16 - DATAGRAM NEGATIVE QUERY RESPONSE + * + * Bit definitions of the FLAGS field: + * + * 0 1 2 3 4 5 6 7 + * +---+---+---+---+---+---+---+---+ + * | 0 | 0 | 0 | 0 | SNT | F | M | + * +---+---+---+---+---+---+---+---+ + * + * Symbol Bit(s) Description + * + * M 7 MORE flag, If set then more NetBIOS datagram + * fragments follow. + * + * F 6 FIRST packet flag, If set then this is first + * (and possibly only) fragment of NetBIOS + * datagram + * + * SNT 4,5 Source End-Node type: + * 00 = B node + * 01 = P node + * 10 = M node + * 11 = NBDD + * RESERVED 0-3 Reserved, must be zero (0) + * + * Inputs: + * Nothing + * + * Returns: + * int -> Description + */ + +/*ARGSUSED*/ +void * +smb_netbios_datagram_service_daemon(void *arg) +{ + struct sockaddr_in sin; + struct datagram *datagram; + int bytes, flag = 1; + net_cfg_t cfg; + + (void) mutex_lock(&smb_dgq_mtx); + bzero(&smb_datagram_queue, sizeof (smb_datagram_queue)); + smb_datagram_queue.forw = smb_datagram_queue.back = + (struct datagram *)((uintptr_t)&smb_datagram_queue); + (void) mutex_unlock(&smb_dgq_mtx); + + if ((datagram_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, + "smbd: Could not create AF_INET, SOCK_DGRAM, socket"); + smb_netbios_chg_status(NETBIOS_DATAGRAM_SVC_FAILED, 1); + return (0); + } + + bzero(&sin, sizeof (sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(DGM_SRVC_UDP_PORT); + if (bind(datagram_sock, (struct sockaddr *)&sin, sizeof (sin)) != 0) { + syslog(LOG_ERR, "smbd: Bind to name service port %d failed", + DGM_SRVC_UDP_PORT); + (void) close(datagram_sock); + smb_netbios_chg_status(NETBIOS_DATAGRAM_SVC_FAILED, 1); + return (0); + } + (void) setsockopt(datagram_sock, SOL_SOCKET, SO_BROADCAST, &flag, + sizeof (flag)); + + smb_netbios_chg_status(NETBIOS_DATAGRAM_SVC_RUNNING, 1); + + while (((nb_status.state & NETBIOS_SHUTTING_DOWN) == 0) || + (nb_status.state & NETBIOS_BROWSER_RUNNING)) { + if ((datagram = (struct datagram *) + malloc(sizeof (struct datagram))) == 0) { + /* Sleep for 10 sec and try again */ + (void) sleep(10); + continue; + } + +ignore: bzero(&datagram->inaddr, sizeof (struct addr_entry)); + datagram->inaddr.sinlen = sizeof (datagram->inaddr.sin); + datagram->inaddr.forw = datagram->inaddr.back = + &datagram->inaddr; + + if ((bytes = recvfrom(datagram_sock, datagram->rawbuf, + MAX_DATAGRAM_LENGTH, 0, + (struct sockaddr *)&datagram->inaddr.sin, + &datagram->inaddr.sinlen)) < 0) { + syslog(LOG_ERR, + "smbd: NETBIOS datagram - recvfrom failed"); + smb_netbios_chg_status(NETBIOS_DATAGRAM_SVC_FAILED, 1); + break; + } + + /* Ignore any incoming packets from myself... */ + if (smb_nic_get_byip( + datagram->inaddr.sin.sin_addr.s_addr, + &cfg) != NULL) { + goto ignore; + } + if (smb_datagram_decode(datagram, bytes) < 0) + goto ignore; + + /* + * This code was doing the wrong thing with responses from a + * Windows2000 PDC because both DATAGRAM_FLAGS_H_NODE and + * DATAGRAM_FLAGS_NBDD are defined to be the same value (see + * netbios.h). Since the Windows2000 PDC wants to be an H-Node, + * we need to handle all messages via smb_netbios_BPM_datagram. + * + * if ((datagram->flags & DATAGRAM_FLAGS_SRC_TYPE) == + * DATAGRAM_FLAGS_NBDD) + * smb_netbios_NBDD_datagram(datagram); + * else + * smb_netbios_BPM_datagram(datagram); + */ + + smb_netbios_BPM_datagram(datagram); + } + + smb_netbios_chg_status(NETBIOS_DATAGRAM_SVC_RUNNING, 0); + + (void) mutex_lock(&nb_status.mtx); + while (nb_status.state & NETBIOS_BROWSER_RUNNING) + (void) cond_wait(&nb_status.cv, &nb_status.mtx); + (void) mutex_unlock(&nb_status.mtx); + + (void) close(datagram_sock); + smb_netbios_datagram_fini(); + syslog(LOG_DEBUG, "smbd: Netbios Datagram Service is down\n"); + return (0); +} + +static char +/* LINTED - E_STATIC_UNUSED */ +nb_fmt_flags(unsigned char flags) +{ + switch (flags & DATAGRAM_FLAGS_SRC_TYPE) { + case DATAGRAM_FLAGS_B_NODE: return ('B'); + case DATAGRAM_FLAGS_P_NODE: return ('P'); + case DATAGRAM_FLAGS_M_NODE: return ('M'); + case DATAGRAM_FLAGS_H_NODE: return ('H'); + default: return ('?'); + } +} diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_name.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_name.c new file mode 100644 index 000000000000..2b2d20e3c382 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_name.c @@ -0,0 +1,4738 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Description: + * + * Contains base code for netbios name service. + * + * + * 6. DEFINED CONSTANTS AND VARIABLES + * + * GENERAL: + * + * SCOPE_ID The name of the NetBIOS scope. + * + * This is expressed as a character + * string meeting the requirements of + * the domain name system and without + * a leading or trailing "dot". + * + * An implementation may elect to make + * this a single global value for the + * node or allow it to be specified + * with each separate NetBIOS name + * (thus permitting cross-scope + * references.) + * + * BROADCAST_ADDRESS An IP address composed of the + * nodes's network and subnetwork + * numbers with all remaining bits set + * to one. + * + * I.e. "Specific subnet" broadcast + * addressing according to section 2.3 + * of RFC 950. + * + * BCAST_REQ_RETRY_TIMEOUT 250 milliseconds. + * An adaptive timer may be used. + * + * BCAST_REQ_RETRY_COUNT 3 + * + * UCAST_REQ_RETRY_TIMEOUT 5 seconds + * An adaptive timer may be used. + * + * UCAST_REQ_RETRY_COUNT 3 + * + * MAX_DATAGRAM_LENGTH 576 bytes (default) + * + * + * NAME SERVICE: + * + * REFRESH_TIMER Negotiated with NAME for each name. + * + * CONFLICT_TIMER 1 second + * Implementations may chose a longer + * value. + * + * + * NAME_SERVICE_TCP_PORT 137 (decimal) + * + * NAME_SERVICE_UDP_PORT 137 (decimal) + * + * INFINITE_TTL 0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define NAME_HEADER_SIZE 12 + +typedef struct name_reply { + struct name_reply *forw; + struct name_reply *back; + struct name_packet *packet; + struct addr_entry *addr; + unsigned short name_trn_id; + unsigned short flags; +} name_reply; + +static struct name_reply reply_queue; +static mutex_t rq_mtx; + +static mutex_t reply_mtx; +static cond_t reply_cv; + +static name_queue_t delete_queue; +static name_queue_t refresh_queue; + +/* + * Flag to control whether or not NetBIOS name refresh requests + * are logged. Set to non-zero to enable logging. + */ + +static unsigned short netbios_name_transcation_id = 1; +static int name_sock = 0; + +static int bcast_num = 0; +static int nbns_num = 0; +static struct addr_entry smb_bcast_list[SMB_PI_MAX_NETWORKS]; +static struct addr_entry smb_nbns[SMB_PI_MAX_WINS]; + +static int smb_netbios_process_response(unsigned short, struct addr_entry *, + struct name_packet *, uint32_t); + +static int smb_send_name_service_packet(struct addr_entry *addr, + struct name_packet *packet); + +static int +smb_end_node_challenge(struct name_reply *reply_info) +{ + int rc; + uint32_t retry; + unsigned short tid; + struct resource_record *answer; + struct name_question question; + struct addr_entry *addr; + struct name_entry *destination; + struct name_packet packet; + struct timespec st; + + /* + * The response packet has in it the address of the presumed owner + * of the name. Challenge that owner. If owner either does not + * respond or indicates that he no longer owns the name, claim the + * name. Otherwise, the name cannot be claimed. + */ + + if ((answer = reply_info->packet->answer) == 0) + return (-1); + + destination = answer->name; + question.name = answer->name; + + packet.info = NAME_QUERY_REQUEST | NM_FLAGS_UNICAST; + packet.qdcount = 1; /* question entries */ + packet.question = &question; + packet.ancount = 0; /* answer recs */ + packet.answer = NULL; + packet.nscount = 0; /* authority recs */ + packet.authority = NULL; + packet.arcount = 0; /* additional recs */ + packet.additional = NULL; + + addr = &destination->addr_list; + for (retry = 0; retry < UCAST_REQ_RETRY_COUNT; retry++) { + tid = netbios_name_transcation_id++; + packet.name_trn_id = tid; + if (smb_send_name_service_packet(addr, &packet) >= 0) { + if ((rc = smb_netbios_process_response(tid, addr, + &packet, UCAST_REQ_RETRY_TIMEOUT)) != 0) + return (rc); + } + st.tv_sec = 0; + st.tv_nsec = (UCAST_REQ_RETRY_TIMEOUT * 1000000); + (void) nanosleep(&st, 0); + } + /* No reply */ + return (0); +} + + +static struct name_reply * +smb_name_get_reply(unsigned short tid, uint32_t timeout) +{ + unsigned short info; + struct resource_record *answer; + struct name_reply *reply; + uint32_t wait_time, to_save; /* in millisecond */ + struct timeval wt; + timestruc_t to; + + to_save = timeout; + reply = (struct name_reply *)malloc(sizeof (struct name_reply)); + if (reply != 0) { + reply->flags = 0; + reply->name_trn_id = tid; + (void) mutex_lock(&rq_mtx); + QUEUE_INSERT_TAIL(&reply_queue, reply); + (void) mutex_unlock(&rq_mtx); + + for (;;) { + (void) gettimeofday(&wt, 0); + wait_time = wt.tv_usec / 1000; + + (void) mutex_lock(&reply_mtx); + to.tv_sec = 0; + to.tv_nsec = timeout * 1000000; + (void) cond_reltimedwait(&reply_cv, &reply_mtx, &to); + (void) mutex_unlock(&reply_mtx); + + if (reply->flags != 0) { + info = reply->packet->info; + if (PACKET_TYPE(info) == WACK_RESPONSE) { + answer = reply->packet->answer; + wait_time = (answer) ? + TO_MILLISECONDS(answer->ttl) : + DEFAULT_TTL; + free(reply->addr); + free(reply->packet); + timeout = to_save + wait_time; + reply->flags = 0; + reply->name_trn_id = tid; + (void) mutex_lock(&rq_mtx); + QUEUE_INSERT_TAIL(&reply_queue, reply); + (void) mutex_unlock(&rq_mtx); + continue; + } + return (reply); + } + (void) gettimeofday(&wt, 0); + wait_time = (wt.tv_usec / 1000) - wait_time; + if (wait_time >= timeout) { + (void) mutex_lock(&rq_mtx); + QUEUE_CLIP(reply); + (void) mutex_unlock(&rq_mtx); + free(reply); + break; + } + timeout -= wait_time; + } + } + + return (0); +} + +static void +smb_reply_ready(struct name_packet *packet, struct addr_entry *addr) +{ + struct name_reply *reply; + struct resource_record *answer; + + (void) mutex_lock(&rq_mtx); + for (reply = reply_queue.forw; reply != &reply_queue; + reply = reply->forw) { + if (reply->name_trn_id == packet->name_trn_id) { + QUEUE_CLIP(reply); + (void) mutex_unlock(&rq_mtx); + + reply->addr = addr; + reply->packet = packet; + + (void) mutex_lock(&reply_mtx); + reply->flags |= 0x0001; /* reply ready */ + (void) cond_signal(&reply_cv); + (void) mutex_unlock(&reply_mtx); + + return; + } + } + (void) mutex_unlock(&rq_mtx); + + /* Presumably nobody is waiting any more... */ + free(addr); + + answer = packet->answer; + if (answer) + smb_netbios_name_freeaddrs(answer->name); + free(packet); +} + +static int +smb_netbios_process_response(unsigned short tid, struct addr_entry *addr, + struct name_packet *packet, uint32_t timeout) +{ + int rc = 0; + unsigned short info; + struct name_reply *reply; + struct resource_record *answer; + struct name_entry *name; + struct name_entry *entry; + struct name_question *question; + uint32_t ttl; + + if ((reply = smb_name_get_reply(tid, timeout)) == 0) { + return (0); /* No reply: retry */ + } + info = reply->packet->info; + answer = reply->packet->answer; + + /* response */ + switch (PACKET_TYPE(info)) { + case NAME_QUERY_RESPONSE: + if (POSITIVE_RESPONSE(info)) { + addr = &answer->name->addr_list; + do { + /* + * Make sure that remote name is not + * flagged local + */ + addr->attributes &= ~NAME_ATTR_LOCAL; + + addr->refresh_ttl = addr->ttl = + (answer && answer->ttl) ? + (answer->ttl >> 1) : + TO_SECONDS(DEFAULT_TTL); + addr = addr->forw; + } while (addr != &answer->name->addr_list); + smb_netbios_name_dump(answer->name); + (void) smb_netbios_cache_insert_list(answer->name); + rc = 1; + } else { + rc = -1; + } + break; + + case NAME_REGISTRATION_RESPONSE: + if (NEGATIVE_RESPONSE(info)) { + if (RCODE(info) == RCODE_CFT_ERR) { + if (answer == 0) { + rc = -RCODE(info); + break; + } + + name = answer->name; + entry = smb_netbios_cache_lookup(name); + if (entry) { + /* + * a name in the state "conflict + * detected" does not "logically" exist + * on that node. No further session + * will be accepted on that name. + * No datagrams can be sent against + * that name. + * Such an entry will not be used for + * purposes of processing incoming + * request packets. + * The only valid user NetBIOS operation + * against such a name is DELETE NAME. + */ + entry->attributes |= NAME_ATTR_CONFLICT; + syslog(LOG_DEBUG, + "NETBIOS Name conflict: %15.15s", + entry->name); + smb_netbios_cache_unlock_entry(entry); + } + } + rc = -RCODE(info); + break; + } + + /* + * name can be added: + * adjust refresh timeout value, + * TTL, for this name + */ + question = packet->question; + ttl = (answer && answer->ttl) ? answer->ttl >> 1 + : TO_SECONDS(DEFAULT_TTL); + if ((entry = smb_netbios_cache_lookup(question->name)) != 0) { + addr = &entry->addr_list; + do { + if ((addr->refresh_ttl == 0) || + (ttl < addr->refresh_ttl)) + addr->refresh_ttl = addr->ttl = ttl; + addr = addr->forw; + } while (addr != &entry->addr_list); + smb_netbios_cache_unlock_entry(entry); + } + + rc = 1; + break; + + case NAME_RELEASE_RESPONSE: + rc = 1; + break; + + case END_NODE_CHALLENGE_REGISTRATION_REQUEST: + /* + * The response packet has in it the + * address of the presumed owner of the + * name. Challenge that owner. If + * owner either does not respond or + * indicates that he no longer owns the + * name, claim the name. Otherwise, + * the name cannot be claimed. + */ + rc = smb_end_node_challenge(reply); + break; + + default: + rc = 0; + break; + } + + if (answer) + smb_netbios_name_freeaddrs(answer->name); + free(reply->addr); + free(reply->packet); + free(reply); + return (rc); /* retry */ +} + +/* + * smb_name_buf_from_packet + * + * Description: + * Convert a NetBIOS Name Server Packet Block (npb) + * into the bits and bytes destined for the wire. + * The "buf" is used as a heap. + * + * Inputs: + * char * buf -> Buffer, from the wire + * unsigned n_buf -> Length of 'buf' + * name_packet *npb -> Packet block, decode into + * unsigned n_npb -> Max bytes in 'npb' + * + * Returns: + * >0 -> Encode successful, value is length of packet in "buf" + * -1 -> Hard error, can not possibly encode + * -2 -> Need more memory in buf -- it's too small + */ + +static int +smb_name_buf_from_packet(unsigned char *buf, + int n_buf, + struct name_packet *npb) +{ + struct addr_entry *raddr; + unsigned char *heap = buf; + unsigned char *end_heap = heap + n_buf; + unsigned char *dnptrs[32]; + unsigned char comp_name_buf[MAX_NAME_LENGTH]; + unsigned int tmp; + int i, step; + + if (n_buf < NAME_HEADER_SIZE) + return (-1); /* no header, impossible */ + + dnptrs[0] = heap; + dnptrs[1] = 0; + + BE_OUT16(heap, npb->name_trn_id); + heap += 2; + + BE_OUT16(heap, npb->info); + heap += 2; + + BE_OUT16(heap, npb->qdcount); + heap += 2; + + BE_OUT16(heap, npb->ancount); + heap += 2; + + BE_OUT16(heap, npb->nscount); + heap += 2; + + BE_OUT16(heap, npb->arcount); + heap += 2; + + for (i = 0; i < npb->qdcount; i++) { + if ((heap + 34 + 4) > end_heap) + return (-2); + + (void) smb_first_level_name_encode(npb->question[i].name, + comp_name_buf, sizeof (comp_name_buf)); + (void) strcpy((char *)heap, (char *)comp_name_buf); + heap += strlen((char *)comp_name_buf) + 1; + + BE_OUT16(heap, npb->question[i].question_type); + heap += 2; + + BE_OUT16(heap, npb->question[i].question_class); + heap += 2; + } + + for (step = 1; step <= 3; step++) { + struct resource_record *nrr; + int n; + + /* truly ugly, but saves code copying */ + if (step == 1) { + n = npb->ancount; + nrr = npb->answer; + } else if (step == 2) { + n = npb->nscount; + nrr = npb->authority; + } else { /* step == 3 */ + n = npb->arcount; + nrr = npb->additional; + } + + for (i = 0; i < n; i++) { + if ((heap + 34 + 10) > end_heap) + return (-2); + + (void) smb_first_level_name_encode(nrr->name, + comp_name_buf, sizeof (comp_name_buf)); + (void) strcpy((char *)heap, (char *)comp_name_buf); + heap += strlen((char *)comp_name_buf) + 1; + + BE_OUT16(heap, nrr[i].rr_type); + heap += 2; + + BE_OUT16(heap, nrr[i].rr_class); + heap += 2; + + BE_OUT32(heap, nrr[i].ttl); + heap += 4; + + BE_OUT16(heap, nrr[i].rdlength); + heap += 2; + + if ((tmp = nrr[i].rdlength) > 0) { + if ((heap + tmp) > end_heap) + return (-2); + + if (nrr[i].rr_type == NAME_RR_TYPE_NB && + nrr[i].rr_class == NAME_RR_CLASS_IN && + tmp >= 6 && nrr[i].rdata == 0) { + tmp = nrr[i].name->attributes & + (NAME_ATTR_GROUP | + NAME_ATTR_OWNER_NODE_TYPE); + BE_OUT16(heap, tmp); + heap += 2; + + raddr = &nrr[i].name->addr_list; + (void) memcpy(heap, + &raddr->sin.sin_addr.s_addr, + sizeof (uint32_t)); + heap += 4; + } else { + bcopy(nrr[i].rdata, heap, tmp); + heap += tmp; + } + } + } + } + return (heap - buf); +} + +/* + * strnchr + * + * Lookup for character 'c' in first 'n' chars of string 's'. + * Returns pointer to the found char, otherwise returns 0. + */ +static char * +strnchr(const char *s, char c, int n) +{ + char *ps = (char *)s; + char *es = (char *)s + n; + + while (ps < es && *ps) { + if (*ps == c) + return (ps); + + ++ps; + } + + if (*ps == '\0' && c == '\0') + return (ps); + + return (0); +} + +/*ARGSUSED*/ +static int +is_multihome(char *name) +{ + return ((smb_nic_get_num() > 1) ? 1 : 0); +} + +/* + * smb_netbios_getname + * + * Get the Netbios name part of the given record. + * Does some boundary checks. + * + * Returns the name length on success, otherwise + * returns 0. + */ +static int +smb_netbios_getname(char *name, char *buf, char *buf_end) +{ + char *name_end; + int name_len; + + if (buf >= buf_end) { + /* no room for a NB name */ + return (0); + } + + name_end = strnchr(buf, '\0', buf_end - buf + 1); + if (name_end == 0) { + /* not a valid NB name */ + return (0); + } + + name_len = name_end - buf + 1; + + (void) strlcpy(name, buf, name_len); + return (name_len); +} + + +/* + * smb_name_buf_to_packet + * + * Description: + * Convert the bits and bytes that came from the wire + * into a NetBIOS Name Server Packet Block (npb). + * The "block" is used as a heap. + * + * Inputs: + * char * buf -> Buffer, from the wire + * int n_buf -> Length of 'buf' + * name_packet *npb -> Packet block, decode into + * int n_npb -> Max bytes in 'npb' + * + * Returns: + * >0 -> Decode (parse) successful, value is byte length of npb + * -1 -> Hard error, can not possibly decode + * -2 -> Need more memory in npb -- it's too small + */ + +static struct name_packet * +smb_name_buf_to_packet(char *buf, int n_buf) +{ + struct name_packet *npb; + unsigned char *heap; + unsigned char *scan = (unsigned char *)buf; + unsigned char *scan_end = scan + n_buf; + char name_buf[MAX_NAME_LENGTH]; + struct resource_record *nrr = 0; + int rc, i, n, nn, ns; + unsigned short name_trn_id, info; + unsigned short qdcount, ancount, nscount, arcount; + struct addr_entry *next; + int name_len; + + if (n_buf < NAME_HEADER_SIZE) { + /* truncated header */ + syslog(LOG_DEBUG, "SmbNBNS: packet is too short (%d)", + n_buf); + return (0); + } + + name_trn_id = BE_IN16(scan); scan += 2; + info = BE_IN16(scan); scan += 2; + qdcount = BE_IN16(scan); scan += 2; + ancount = BE_IN16(scan); scan += 2; + nscount = BE_IN16(scan); scan += 2; + arcount = BE_IN16(scan); scan += 2; + + ns = sizeof (struct name_entry); + n = n_buf + sizeof (struct name_packet) + + ((unsigned)qdcount * (sizeof (struct name_question) + ns)) + + ((unsigned)ancount * (sizeof (struct resource_record) + ns)) + + ((unsigned)nscount * (sizeof (struct resource_record) + ns)) + + ((unsigned)arcount * (sizeof (struct resource_record) + ns)); + + if ((npb = (struct name_packet *)malloc(n)) == 0) { + return (0); + } + bzero(npb, n); + + heap = npb->block_data; + npb->name_trn_id = name_trn_id; + npb->info = info; + npb->qdcount = qdcount; + npb->ancount = ancount; + npb->nscount = nscount; + npb->arcount = arcount; + + /* scan is in position for question entries */ + + /* + * Measure the space needed for the tables + */ + if (qdcount > 0) { + /* LINTED - E_BAD_PTR_CAST_ALIGN */ + npb->question = (struct name_question *)heap; + heap += qdcount * sizeof (struct name_question); + for (i = 0; i < qdcount; i++) { + /* LINTED - E_BAD_PTR_CAST_ALIGN */ + npb->question[i].name = (struct name_entry *)heap; + heap += sizeof (struct name_entry); + } + } + + /* LINTED - E_BAD_PTR_CAST_ALIGN */ + nrr = (struct resource_record *)heap; + + if (ancount > 0) { + /* LINTED - E_BAD_PTR_CAST_ALIGN */ + npb->answer = (struct resource_record *)heap; + heap += ancount * sizeof (struct resource_record); + } + + if (nscount > 0) { + /* LINTED - E_BAD_PTR_CAST_ALIGN */ + npb->authority = (struct resource_record *)heap; + heap += nscount * sizeof (struct resource_record); + } + + if (arcount > 0) { + /* LINTED - E_BAD_PTR_CAST_ALIGN */ + npb->additional = (struct resource_record *)heap; + heap += arcount * sizeof (struct resource_record); + } + + /* + * Populate each resource_record's .name field. + * Done as a second pass so that all resource records + * (answer, authority, additional) are consecutive via nrr[i]. + */ + for (i = 0; i < (ancount + nscount + arcount); i++) { + /* LINTED - E_BAD_PTR_CAST_ALIGN */ + nrr[i].name = (struct name_entry *)heap; + heap += sizeof (struct name_entry); + } + + + for (i = 0; i < npb->qdcount; i++) { + name_len = smb_netbios_getname(name_buf, (char *)scan, + (char *)scan_end); + if (name_len <= 0) { + free(npb); + return (0); + } + + smb_init_name_struct(NETBIOS_EMPTY_NAME, 0, 0, 0, 0, 0, 0, + npb->question[i].name); + rc = smb_first_level_name_decode((unsigned char *)name_buf, + npb->question[i].name); + if (rc < 0) { + /* Couldn't decode the question name */ + free(npb); + return (0); + } + + scan += name_len; + if (scan + 4 > scan_end) { + /* no room for Question Type(2) and Class(2) fields */ + free(npb); + return (0); + } + + npb->question[i].question_type = BE_IN16(scan); scan += 2; + npb->question[i].question_class = BE_IN16(scan); scan += 2; + } + + /* + * Cheat. Remaining sections are of the same resource_record + * format. Table space is consecutive. + */ + + for (i = 0; i < (ancount + nscount + arcount); i++) { + if (scan[0] == 0xc0) { + /* Namebuf is reused... */ + rc = 2; + } else { + name_len = smb_netbios_getname(name_buf, (char *)scan, + (char *)scan_end); + if (name_len <= 0) { + free(npb); + return (0); + } + rc = name_len; + } + scan += rc; + + if (scan + 10 > scan_end) { + /* + * no room for RR_TYPE (2), RR_CLASS (2), TTL (4) and + * RDLENGTH (2) fields. + */ + free(npb); + return (0); + } + + smb_init_name_struct(NETBIOS_EMPTY_NAME, 0, 0, 0, 0, 0, 0, + nrr[i].name); + if ((rc = smb_first_level_name_decode((unsigned char *)name_buf, + nrr[i].name)) < 0) { + free(npb); + return (0); + } + + nrr[i].rr_type = BE_IN16(scan); scan += 2; + nrr[i].rr_class = BE_IN16(scan); scan += 2; + nrr[i].ttl = BE_IN32(scan); scan += 4; + nrr[i].rdlength = BE_IN16(scan); scan += 2; + + if ((n = nrr[i].rdlength) > 0) { + if ((scan + n) > scan_end) { + /* no room for RDATA */ + free(npb); + return (0); + } + bcopy(scan, heap, n); + + nn = n; + if (nrr[i].rr_type == 0x0020 && + nrr[i].rr_class == 0x01 && n >= 6) { + while (nn) { + if (nn == 6) + next = &nrr[i].name->addr_list; + else { + next = (struct addr_entry *) + malloc( + sizeof (struct addr_entry)); + if (next == 0) { + /* not enough memory */ + free(npb); + return (0); + } + QUEUE_INSERT_TAIL( + &nrr[i].name->addr_list, + next); + } + nrr[i].name->attributes = + BE_IN16(scan); + next->sin.sin_family = AF_INET; + next->sinlen = sizeof (next->sin); + (void) memcpy( + &next->sin.sin_addr.s_addr, + scan + 2, sizeof (uint32_t)); + next->sin.sin_port = + htons(DGM_SRVC_UDP_PORT); + nn -= 6; + scan += 6; + } + } else { + nrr[i].rdata = heap; + scan += n; + } + heap += n; + } + } + return (npb); +} + + +/* + * smb_send_name_service_packet + * + * Description: + * + * Send out a name service packet to proper destination. + * + * Inputs: + * struct netbios_name *dest -> NETBIOS name of destination + * struct name_packet *packet -> Packet to send + * + * Returns: + * success -> >0 + * failure -> <=0 + */ + +static int +smb_send_name_service_packet(struct addr_entry *addr, + struct name_packet *packet) +{ + unsigned char buf[MAX_DATAGRAM_LENGTH]; + int len; + + if ((len = smb_name_buf_from_packet(buf, sizeof (buf), packet)) < 0) { + errno = EINVAL; + return (-1); + } + + return (sendto(name_sock, buf, len, MSG_EOR, + (struct sockaddr *)&addr->sin, addr->sinlen)); +} + +/* + * 4.2.1.1. HEADER + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID | OPCODE | NM_FLAGS | RCODE | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | QDCOUNT | ANCOUNT | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NSCOUNT | ARCOUNT | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Field Description + * + * NAME_TRN_ID Transaction ID for Name Service Transaction. + * Requester places a unique value for each active + * transaction. Responder puts NAME_TRN_ID value + * from request packet in response packet. + * + * OPCODE Packet type code, see table below. + * + * NM_FLAGS Flags for operation, see table below. + * + * RCODE Result codes of request. Table of RCODE values + * for each response packet below. + * + * QDCOUNT Unsigned 16 bit integer specifying the number of + * entries in the question section of a Name + * + * Service packet. Always zero (0) for responses. + * Must be non-zero for all NetBIOS Name requests. + * + * ANCOUNT Unsigned 16 bit integer specifying the number of + * resource records in the answer section of a Name + * Service packet. + * + * NSCOUNT Unsigned 16 bit integer specifying the number of + * resource records in the authority section of a + * Name Service packet. + * + * ARCOUNT Unsigned 16 bit integer specifying the number of + * resource records in the additional records + * section of a Name Service packet. + * + * The OPCODE field is defined as: + * + * 0 1 2 3 4 + * +---+---+---+---+---+ + * | R | OPCODE | + * +---+---+---+---+---+ + * + * Symbol Bit(s) Description + * + * OPCODE 1-4 Operation specifier: + * 0 = query + * 5 = registration + * 6 = release + * 7 = WACK + * 8 = refresh + * + * R 0 RESPONSE flag: + * if bit == 0 then request packet + * if bit == 1 then response packet. + */ + + +/* + * The NM_FLAGS field is defined as: + * + * + * 0 1 2 3 4 5 6 + * +---+---+---+---+---+---+---+ + * |AA |TC |RD |RA | 0 | 0 | B | + * +---+---+---+---+---+---+---+ + * + * Symbol Bit(s) Description + * + * B 6 Broadcast Flag. + * = 1: packet was broadcast or multicast + * = 0: unicast + * + * RA 3 Recursion Available Flag. + * + * Only valid in responses from a NetBIOS Name + * Server -- must be zero in all other + * responses. + * + * If one (1) then the NAME supports recursive + * query, registration, and release. + * + * If zero (0) then the end-node must iterate + * for query and challenge for registration. + * + * RD 2 Recursion Desired Flag. + * + * May only be set on a request to a NetBIOS + * Name Server. + * + * The NAME will copy its state into the + * response packet. + * + * If one (1) the NAME will iterate on the + * query, registration, or release. + * + * TC 1 Truncation Flag. + * + * Set if this message was truncated because the + * datagram carrying it would be greater than + * 576 bytes in length. Use TCP to get the + * information from the NetBIOS Name Server. + * + * AA 0 Authoritative Answer flag. + * + * Must be zero (0) if R flag of OPCODE is zero + * (0). + * + * If R flag is one (1) then if AA is one (1) + * then the node responding is an authority for + * the domain name. + * + * End nodes responding to queries always set + * this bit in responses. + * + */ + +/* + * 4.2.1.2. QUESTION SECTION + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / QUESTION_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | QUESTION_TYPE | QUESTION_CLASS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Field Description + * + * QUESTION_NAME The compressed name representation of the + * NetBIOS name for the request. + * + * QUESTION_TYPE The type of request. The values for this field + * are specified for each request. + * + * QUESTION_CLASS The class of the request. The values for this + * field are specified for each request. + * + * QUESTION_TYPE is defined as: + * + * Symbol Value Description: + * + * NB 0x0020 NetBIOS general Name Service Resource Record + * NBSTAT 0x0021 NetBIOS NODE STATUS Resource Record (See NODE + * STATUS REQUEST) + * + * QUESTION_CLASS is defined as: + * + * Symbol Value Description: + * + * IN 0x0001 Internet class + */ + +#define QUESTION_TYPE_NETBIOS_GENERAL 0x20 +#define QUESTION_TYPE_NETBIOS_STATUS 0x21 + +#define QUESTION_CLASS_INTERNET 0x0001 + +/* + * + * 4.2.1.3. RESOURCE RECORD + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RR_TYPE | RR_CLASS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RDLENGTH | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + * / / + * / RDATA / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Field Description + * + * RR_NAME The compressed name representation of the + * NetBIOS name corresponding to this resource + * record. + * + * RR_TYPE Resource record type code + * + * RR_CLASS Resource record class code + * + * TTL The Time To Live of a the resource record's + * name. + * + * RDLENGTH Unsigned 16 bit integer that specifies the + * number of bytes in the RDATA field. + * + * RDATA RR_CLASS and RR_TYPE dependent field. Contains + * the resource information for the NetBIOS name. + * + * RESOURCE RECORD RR_TYPE field definitions: + * + * Symbol Value Description: + * + * A 0x0001 IP address Resource Record (See REDIRECT NAME + * QUERY RESPONSE) + * NS 0x0002 Name Server Resource Record (See REDIRECT + * NAME QUERY RESPONSE) + * NULL 0x000A NULL Resource Record (See WAIT FOR + * ACKNOWLEDGEMENT RESPONSE) + * NB 0x0020 NetBIOS general Name Service Resource Record + * (See NB_FLAGS and NB_ADDRESS, below) + * NBSTAT 0x0021 NetBIOS NODE STATUS Resource Record (See NODE + * STATUS RESPONSE) + */ + +#define RR_TYPE_IP_ADDRESS_RESOURCE 0x0001 +#define RR_TYPE_NAME_SERVER_RESOURCE 0x0002 +#define RR_TYPE_NULL_RESOURCE 0x000A +#define RR_TYPE_NETBIOS_RESOURCE 0x0020 +#define RR_TYPE_NETBIOS_STATUS 0x0021 + +/* + * + * RESOURCE RECORD RR_CLASS field definitions: + * + * Symbol Value Description: + * + * IN 0x0001 Internet class + */ +#define RR_CLASS_INTERNET_CLASS 0x0001 + +/* + * + * NB_FLAGS field of the RESOURCE RECORD RDATA field for RR_TYPE of + * "NB": + * + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | G | ONT | RESERVED | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * Symbol Bit(s) Description: + * + * RESERVED 3-15 Reserved for future use. Must be zero (0). + * ONT 1,2 Owner Node Type: + * 00 = B node + * 01 = P node + * 10 = M node + * 11 = Reserved for future use + * For registration requests this is the + * claimant's type. + * For responses this is the actual owner's + * type. + * + * G 0 Group Name Flag. + * If one (1) then the RR_NAME is a GROUP + * NetBIOS name. + * If zero (0) then the RR_NAME is a UNIQUE + * NetBIOS name. + * + * The NB_ADDRESS field of the RESOURCE RECORD RDATA field for + * RR_TYPE of "NB" is the IP address of the name's owner. + * + */ +#define RR_FLAGS_NB_ONT_MASK 0x6000 +#define RR_FLAGS_NB_ONT_B_NODE 0x0000 +#define RR_FLAGS_NB_ONT_P_NODE 0x2000 +#define RR_FLAGS_NB_ONT_M_NODE 0x4000 +#define RR_FLAGS_NB_ONT_RESERVED 0x6000 + +#define RR_FLAGS_NB_GROUP_NAME 0x8000 + +/* + * smb_netbios_send_rcv + * + * This function sends the given NetBIOS packet to the given + * address and get back the response. If send operation is not + * successful, it's repeated 'retries' times. + * + * Returns: + * 0 Unsuccessful send operation; no reply + * 1 Got reply + */ +static int +smb_netbios_send_rcv(int bcast, struct addr_entry *destination, + struct name_packet *packet, + uint32_t retries, uint32_t timeout) +{ + uint32_t retry; + unsigned short tid; + struct timespec st; + int rc; + + for (retry = 0; retry < retries; retry++) { + if ((destination->flags & ADDR_FLAG_VALID) == 0) + return (0); + + tid = netbios_name_transcation_id++; + packet->name_trn_id = tid; + if (smb_send_name_service_packet(destination, packet) >= 0) { + rc = smb_netbios_process_response(tid, destination, + packet, timeout); + + if ((rc > 0) || (bcast == BROADCAST)) + return (1); + + if (rc != 0) + return (0); + } + + st.tv_sec = 0; + st.tv_nsec = (timeout * 1000000); + (void) nanosleep(&st, 0); + } + + return (0); +} + +/* + * 4.2.2. NAME REGISTRATION REQUEST + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |0| 0x5 |0|0|1|0|0 0|B| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0001 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / QUESTION_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Since the RR_NAME is the same name as the QUESTION_NAME, the + * RR_NAME representation must use pointers to the QUESTION_NAME + * name's labels to guarantee the length of the datagram is less + * than the maximum 576 bytes. See section above on name formats + * and also page 31 and 32 of RFC 883, Domain Names - Implementation + * and Specification, for a complete description of compressed name + * label pointers. + */ +static int +smb_send_name_registration_request(int bcast, struct name_question *question, + struct resource_record *additional) +{ + int gotreply = 0; + uint32_t retries; + uint32_t timeout; + struct addr_entry *destination; + struct name_packet packet; + unsigned char type; + int i, addr_num, rc; + + type = question->name->name[15]; + if ((type != 0x00) && (type != 0x20)) { + syslog(LOG_ERR, "netbios: error trying to register" + " non-local name"); + smb_netbios_name_logf(question->name); + question->name->attributes &= ~NAME_ATTR_LOCAL; + return (-1); + } + + if (bcast == BROADCAST) { + if (bcast_num == 0) + return (0); + destination = smb_bcast_list; + addr_num = bcast_num; + retries = BCAST_REQ_RETRY_COUNT; + timeout = BCAST_REQ_RETRY_TIMEOUT; + packet.info = NAME_REGISTRATION_REQUEST | NM_FLAGS_BROADCAST; + } else { + if (nbns_num == 0) + return (0); + destination = smb_nbns; + addr_num = nbns_num; + retries = UCAST_REQ_RETRY_COUNT; + timeout = UCAST_REQ_RETRY_TIMEOUT; + packet.info = NAME_REGISTRATION_REQUEST | NM_FLAGS_UNICAST; + } + + packet.qdcount = 1; /* question entries */ + packet.question = question; + packet.ancount = 0; /* answer recs */ + packet.answer = NULL; + packet.nscount = 0; /* authority recs */ + packet.authority = NULL; + packet.arcount = 1; /* additional recs */ + packet.additional = additional; + + if (IS_UNIQUE(question->name->attributes) && + (is_multihome((char *)(question->name->name)))) + packet.info |= NAME_MULTIHOME_REGISTRATION_REQUEST; + + for (i = 0; i < addr_num; i++) { + /* + * Only register with the Primary WINS server, + * unless we got no reply. + */ + if ((bcast == UNICAST) && gotreply) + break; + + rc = smb_netbios_send_rcv(bcast, &destination[i], &packet, + retries, timeout); + if (rc == 1) + gotreply = 1; + } + + return (gotreply); +} + +/* + * + * 4.2.4. NAME REFRESH REQUEST + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |0| 0x8 |0|0|0|0|0 0|B| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0001 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / QUESTION_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +/*ARGSUSED*/ +static int +smb_send_name_refresh_request(int bcast, struct name_question *question, + struct resource_record *additional, int force) +{ + int rc = 0; + int gotreply = 0; + uint32_t retries; + uint32_t timeout; + struct addr_entry *addr; + struct addr_entry *destination; + struct name_packet packet; + unsigned char type; + int i, addr_num, q_addrs = 0; + + type = question->name->name[15]; + if ((type != 0x00) && (type != 0x20)) { + syslog(LOG_ERR, "attempt to refresh non-local name"); + smb_netbios_name_logf(question->name); + question->name->attributes &= ~NAME_ATTR_LOCAL; + return (-1); + } + switch (bcast) { + case BROADCAST : + if (bcast_num == 0) + return (-1); + destination = smb_bcast_list; + addr_num = bcast_num; + retries = BCAST_REQ_RETRY_COUNT; + timeout = BCAST_REQ_RETRY_TIMEOUT; + packet.info = NAME_REFRESH_REQUEST | NM_FLAGS_BROADCAST; + break; + + case UNICAST : + if (nbns_num == 0) + return (-1); + destination = smb_nbns; + addr_num = nbns_num; + retries = UCAST_REQ_RETRY_COUNT; + timeout = UCAST_REQ_RETRY_TIMEOUT; + packet.info = NAME_REFRESH_REQUEST | NM_FLAGS_UNICAST; + break; + + default: + destination = &question->name->addr_list; + /* + * the value of addr_num is irrelvant here, because + * the code is going to do special_process so it doesn't + * need the addr_num. We set a value here just to avoid + * compiler warning. + */ + addr_num = 0; + retries = UCAST_REQ_RETRY_COUNT; + timeout = UCAST_REQ_RETRY_TIMEOUT; + packet.info = NAME_REFRESH_REQUEST | NM_FLAGS_UNICAST; + q_addrs = 1; + break; + } + + if (IS_UNIQUE(question->name->attributes) && + (is_multihome((char *)(question->name->name)))) + packet.info |= NAME_MULTIHOME_REGISTRATION_REQUEST; + + packet.qdcount = 1; /* question entries */ + packet.question = question; + packet.ancount = 0; /* answer recs */ + packet.answer = NULL; + packet.nscount = 0; /* authority recs */ + packet.authority = NULL; + packet.arcount = 1; /* additional recs */ + packet.additional = additional; + + if (q_addrs) + goto special_process; + + for (i = 0; i < addr_num; i++) { + rc = smb_netbios_send_rcv(bcast, &destination[i], &packet, + retries, timeout); + if (rc == 1) + gotreply = 1; + } + + return (gotreply); + +special_process: + addr = destination; + do { + rc = smb_netbios_send_rcv(bcast, addr, &packet, + retries, timeout); + if (rc == 1) + gotreply = 1; + addr = addr->forw; + } while (addr != destination); + + return (gotreply); +} + +/* + * 4.2.5. POSITIVE NAME REGISTRATION RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x5 |1|0|1|1|0 0|0| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * + * + * 4.2.6. NEGATIVE NAME REGISTRATION RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x5 |1|0|1|1|0 0|0| RCODE | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * RCODE field values: + * + * Symbol Value Description: + * + * FMT_ERR 0x1 Format Error. Request was invalidly + * formatted. + * SRV_ERR 0x2 Server failure. Problem with NAME, cannot + * process name. + * IMP_ERR 0x4 Unsupported request error. Allowable only + * for challenging NAME when gets an Update type + * registration request. + * RFS_ERR 0x5 Refused error. For policy reasons server + * will not register this name from this host. + * ACT_ERR 0x6 Active error. Name is owned by another node. + * CFT_ERR 0x7 Name in conflict error. A UNIQUE name is + * owned by more than one node. + */ +static int +smb_send_name_registration_response(struct addr_entry *addr, + struct name_packet *original_packet, unsigned short rcode) +{ + struct name_packet packet; + struct resource_record answer; + + bzero(&packet, sizeof (struct name_packet)); + bzero(&answer, sizeof (struct resource_record)); + + packet.name_trn_id = original_packet->name_trn_id; + packet.info = NAME_REGISTRATION_RESPONSE | NAME_NM_FLAGS_RA | + (rcode & NAME_RCODE_MASK); + packet.qdcount = 0; /* question entries */ + packet.question = NULL; + packet.ancount = 1; /* answer recs */ + packet.answer = &answer; + packet.nscount = 0; /* authority recs */ + packet.authority = NULL; + packet.arcount = 0; /* additional recs */ + packet.additional = NULL; + + answer.name = original_packet->question->name; + answer.rr_type = NAME_QUESTION_TYPE_NB; + answer.rr_class = NAME_QUESTION_CLASS_IN; + answer.ttl = original_packet->additional->ttl; + answer.rdlength = original_packet->additional->rdlength; + answer.rdata = original_packet->additional->rdata; + + return (smb_send_name_service_packet(addr, &packet)); +} + +/* + * 4.2.9. NAME RELEASE REQUEST & DEMAND + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |0| 0x6 |0|0|0|0|0 0|B| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0001 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / QUESTION_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x00000000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Since the RR_NAME is the same name as the QUESTION_NAME, the + * RR_NAME representation must use label string pointers to the + * QUESTION_NAME labels to guarantee the length of the datagram is + * less than the maximum 576 bytes. This is the same condition as + * with the NAME REGISTRATION REQUEST. + */ +static int +smb_send_name_release_request_and_demand(int bcast, + struct name_question *question, struct resource_record *additional) +{ + int gotreply = 0; + int i, rc; + int addr_num; + uint32_t retries; + uint32_t timeout; + struct addr_entry *destination; + struct name_packet packet; + + if (bcast == BROADCAST) { + if (bcast_num == 0) + return (-1); + destination = smb_bcast_list; + addr_num = bcast_num; + retries = 1; /* BCAST_REQ_RETRY_COUNT */ + timeout = 100; /* BCAST_REQ_RETRY_TIMEOUT */ + packet.info = NAME_RELEASE_REQUEST | NM_FLAGS_BROADCAST; + } else { + if (nbns_num == 0) + return (-1); + destination = smb_nbns; + addr_num = nbns_num; + retries = 1; /* UCAST_REQ_RETRY_COUNT */ + timeout = 100; /* UCAST_REQ_RETRY_TIMEOUT */ + packet.info = NAME_RELEASE_REQUEST | NM_FLAGS_UNICAST; + } + + packet.qdcount = 1; /* question entries */ + packet.question = question; + packet.ancount = 0; /* answer recs */ + packet.answer = NULL; + packet.nscount = 0; /* authority recs */ + packet.authority = NULL; + packet.arcount = 1; /* additional recs */ + packet.additional = additional; + + for (i = 0; i < addr_num; i++) { + rc = smb_netbios_send_rcv(bcast, &destination[i], &packet, + retries, timeout); + if (rc == 1) + gotreply = 1; + } + + return (gotreply); +} + +/* + * 4.2.10. POSITIVE NAME RELEASE RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x6 |1|0|0|0|0 0|0| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * + * 4.2.11. NEGATIVE NAME RELEASE RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x6 |1|0|0|0|0 0|0| RCODE | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0006 | NB_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * RCODE field values: + * + * Symbol Value Description: + * + * FMT_ERR 0x1 Format Error. Request was invalidly + * formatted. + * + * SRV_ERR 0x2 Server failure. Problem with NAME, cannot + * process name. + * + * RFS_ERR 0x5 Refused error. For policy reasons server + * will not release this name from this host. + * + * ACT_ERR 0x6 Active error. Name is owned by another node. + * Only that node may release it. A NetBIOS + * Name Server can optionally allow a node to + * release a name it does not own. This would + * facilitate detection of inactive names for + * nodes that went down silently. + */ +static int +/* LINTED - E_STATIC_UNUSED */ +smb_send_name_release_response(struct addr_entry *addr, + struct name_packet *original_packet, unsigned short rcode) +{ + struct name_packet packet; + struct resource_record answer; + + bzero(&packet, sizeof (struct name_packet)); + bzero(&answer, sizeof (struct resource_record)); + + packet.name_trn_id = original_packet->name_trn_id; + packet.info = NAME_RELEASE_RESPONSE | (rcode & NAME_RCODE_MASK); + packet.qdcount = 0; /* question entries */ + packet.question = NULL; + packet.ancount = 1; /* answer recs */ + packet.answer = &answer; + packet.nscount = 0; /* authority recs */ + packet.authority = NULL; + packet.arcount = 0; /* additional recs */ + packet.additional = NULL; + + answer.name = original_packet->question->name; + answer.rr_type = NAME_QUESTION_TYPE_NB; + answer.rr_class = NAME_QUESTION_CLASS_IN; + answer.ttl = original_packet->additional->ttl; + answer.rdlength = original_packet->additional->rdlength; + answer.rdata = original_packet->additional->rdata; + + return (smb_send_name_service_packet(addr, &packet)); +} + +/* + * + * 4.2.12. NAME QUERY REQUEST + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |0| 0x0 |0|0|1|0|0 0|B| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0001 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / QUESTION_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +static int +smb_send_name_query_request(int bcast, struct name_question *question) +{ + int rc = 0; + uint32_t retry, retries; + uint32_t timeout; + unsigned short tid; + struct addr_entry *destination; + struct name_packet packet; + int i, addr_num; + struct timespec st; + + if (bcast == BROADCAST) { + if (bcast_num == 0) + return (-1); + destination = smb_bcast_list; + addr_num = bcast_num; + retries = BCAST_REQ_RETRY_COUNT; + timeout = BCAST_REQ_RETRY_TIMEOUT; + packet.info = NAME_QUERY_REQUEST | NM_FLAGS_BROADCAST; + } else { + if (nbns_num == 0) + return (-1); + destination = smb_nbns; + addr_num = nbns_num; + retries = UCAST_REQ_RETRY_COUNT; + timeout = UCAST_REQ_RETRY_TIMEOUT; + packet.info = NAME_QUERY_REQUEST | NM_FLAGS_UNICAST; + } + packet.qdcount = 1; /* question entries */ + packet.question = question; + packet.ancount = 0; /* answer recs */ + packet.answer = NULL; + packet.nscount = 0; /* authority recs */ + packet.authority = NULL; + packet.arcount = 0; /* additional recs */ + packet.additional = NULL; + + for (i = 0; i < addr_num; i++) { + for (retry = 0; retry < retries; retry++) { + if ((destination->flags & ADDR_FLAG_VALID) == 0) + break; + tid = netbios_name_transcation_id++; + packet.name_trn_id = tid; + + if (smb_send_name_service_packet(&destination[i], + &packet) >= 0) { + if ((rc = smb_netbios_process_response(tid, + &destination[i], + &packet, timeout)) != 0) + break; + } + st.tv_sec = 0; + st.tv_nsec = (timeout * 1000000); + (void) nanosleep(&st, 0); + } + } + + return (rc); +} + + +/* + * + * 4.2.13. POSITIVE NAME QUERY RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x0 |1|T|1|?|0 0|0| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB (0x0020) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RDLENGTH | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + * | | + * / ADDR_ENTRY ARRAY / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * The ADDR_ENTRY ARRAY a sequence of zero or more ADDR_ENTRY + * records. Each ADDR_ENTRY record represents an owner of a name. + * For group names there may be multiple entries. However, the list + * may be incomplete due to packet size limitations. Bit 22, "T", + * will be set to indicate truncated data. + * + * Each ADDR_ENTRY has the following format: + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_FLAGS | NB_ADDRESS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NB_ADDRESS (continued) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * + * + * 4.2.14. NEGATIVE NAME QUERY RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x0 |1|0|1|?|0 0|0| RCODE | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * / / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NULL (0x000A) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x00000000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * RCODE field values: + * + * Symbol Value Description + * + * FMT_ERR 0x1 Format Error. Request was invalidly + * formatted. + * SRV_ERR 0x2 Server failure. Problem with NAME, cannot + * process name. + * NAM_ERR 0x3 Name Error. The name requested does not + * exist. + * IMP_ERR 0x4 Unsupported request error. Allowable only + * for challenging NAME when gets an Update type + * registration request. + * RFS_ERR 0x5 Refused error. For policy reasons server + * will not register this name from this host. + */ +static int +smb_send_name_query_response(struct addr_entry *addr, + struct name_packet *original_packet, struct name_entry *entry, + unsigned short rcode) +{ + struct addr_entry *raddr; + struct name_packet packet; + struct resource_record answer; + unsigned short attr; + unsigned char data[MAX_DATAGRAM_LENGTH]; + unsigned char *scan = data; + + packet.name_trn_id = original_packet->name_trn_id; + packet.info = NAME_QUERY_RESPONSE | (rcode & NAME_RCODE_MASK); + packet.qdcount = 0; /* question entries */ + packet.question = NULL; + packet.ancount = 1; /* answer recs */ + packet.answer = &answer; + packet.nscount = 0; /* authority recs */ + packet.authority = NULL; + packet.arcount = 0; /* additional recs */ + packet.additional = NULL; + + answer.name = entry; + answer.rr_class = NAME_QUESTION_CLASS_IN; + answer.ttl = entry->addr_list.ttl; + answer.rdata = data; + if (rcode) { + answer.rr_type = NAME_RR_TYPE_NULL; + answer.rdlength = 0; + bzero(data, 6); + } else { + answer.rdlength = 0; + answer.rr_type = NAME_QUESTION_TYPE_NB; + raddr = &entry->addr_list; + scan = data; + do { + attr = entry->attributes & (NAME_ATTR_GROUP | + NAME_ATTR_OWNER_NODE_TYPE); + + BE_OUT16(scan, attr); scan += 2; + (void) memcpy(scan, &raddr->sin.sin_addr.s_addr, + sizeof (uint32_t)); + scan += 4; + + answer.rdlength += 6; + raddr = raddr->forw; + } while (raddr != &entry->addr_list); + } + + return (smb_send_name_service_packet(addr, &packet)); +} + +/* + * 4.2.18. NODE STATUS RESPONSE + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_TRN_ID |1| 0x0 |1|0|0|0|0 0|0| 0x0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0001 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x0000 | 0x0000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * / RR_NAME / + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NBSTAT (0x0021) | IN (0x0001) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x00000000 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RDLENGTH | NUM_NAMES | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + * | | + * + + + * / NODE_NAME ARRAY / + * + + + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * + + + * / STATISTICS / + * + + + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * The NODE_NAME ARRAY is an array of zero or more NUM_NAMES entries + * of NODE_NAME records. Each NODE_NAME entry represents an active + * name in the same NetBIOS scope as the requesting name in the + * local name table of the responder. RR_NAME is the requesting + * name. + * + * NODE_NAME Entry: + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * +--- ---+ + * | | + * +--- NETBIOS FORMAT NAME ---+ + * | | + * +--- ---+ + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NAME_FLAGS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * The NAME_FLAGS field: + * + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | G | ONT |DRG|CNF|ACT|PRM| RESERVED | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * The NAME_FLAGS field is defined as: + * + * Symbol Bit(s) Description: + * + * RESERVED 7-15 Reserved for future use. Must be zero (0). + * PRM 6 Permanent Name Flag. If one (1) then entry + * is for the permanent node name. Flag is zero + * (0) for all other names. + * ACT 5 Active Name Flag. All entries have this flag + * set to one (1). + * CNF 4 Conflict Flag. If one (1) then name on this + * node is in conflict. + * DRG 3 Deregister Flag. If one (1) then this name + * is in the process of being deleted. + * ONT 1,2 Owner Node Type: + * 00 = B node + * 01 = P node + * 10 = M node + * 11 = Reserved for future use + * G 0 Group Name Flag. + * If one (1) then the name is a GROUP NetBIOS + * name. + * If zero (0) then it is a UNIQUE NetBIOS name. + */ +#define NAME_FLAGS_PERMANENT_NAME 0x0200 +#define NAME_FLAGS_ACTIVE_NAME 0x0400 +#define NAME_FLAGS_CONFLICT 0x0800 +#define NAME_FLAGS_DEREGISTER 0x1000 +#define NAME_FLAGS_ONT_MASK 0x6000 +#define NAME_FLAGS_ONT_B_NODE 0x0000 +#define NAME_FLAGS_ONT_P_NODE 0x2000 +#define NAME_FLAGS_ONT_M_NODE 0x4000 +#define NAME_FLAGS_ONT_RESERVED 0x6000 +#define NAME_FLAGS_GROUP_NAME 0x8000 + + +/* + * STATISTICS Field of the NODE STATUS RESPONSE: + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | UNIT_ID (Unique unit ID) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | UNIT_ID,continued | JUMPERS | TEST_RESULT | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | VERSION_NUMBER | PERIOD_OF_STATISTICS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NUMBER_OF_CRCs | NUMBER_ALIGNMENT_ERRORS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NUMBER_OF_COLLISIONS | NUMBER_SEND_ABORTS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NUMBER_GOOD_SENDS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NUMBER_GOOD_RECEIVES | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NUMBER_RETRANSMITS | NUMBER_NO_RESOURCE_CONDITIONS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NUMBER_FREE_COMMAND_BLOCKS | TOTAL_NUMBER_COMMAND_BLOCKS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |MAX_TOTAL_NUMBER_COMMAND_BLOCKS| NUMBER_PENDING_SESSIONS | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MAX_NUMBER_PENDING_SESSIONS | MAX_TOTAL_SESSIONS_POSSIBLE | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SESSION_DATA_PACKET_SIZE | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +#define MAX_NETBIOS_REPLY_DATA_SIZE 500 + +static int +smb_send_node_status_response(struct addr_entry *addr, + struct name_packet *original_packet) +{ + uint32_t net_ipaddr, max_connections; + struct arpreq arpreq; + struct name_packet packet; + struct resource_record answer; + unsigned char *scan; + unsigned char *scan_end; + unsigned char data[MAX_NETBIOS_REPLY_DATA_SIZE]; + net_cfg_t cfg; + + bzero(&packet, sizeof (struct name_packet)); + bzero(&answer, sizeof (struct resource_record)); + + packet.name_trn_id = original_packet->name_trn_id; + packet.info = NODE_STATUS_RESPONSE; + packet.qdcount = 0; /* question entries */ + packet.question = NULL; + packet.ancount = 1; /* answer recs */ + packet.answer = &answer; + packet.nscount = 0; /* authority recs */ + packet.authority = NULL; + packet.arcount = 0; /* additional recs */ + packet.additional = NULL; + + answer.name = original_packet->question->name; + answer.rr_type = NAME_RR_TYPE_NBSTAT; + answer.rr_class = NAME_QUESTION_CLASS_IN; + answer.ttl = 0; + answer.rdata = data; + + scan = smb_netbios_cache_status(data, MAX_NETBIOS_REPLY_DATA_SIZE, + original_packet->question->name->scope); + + scan_end = data + MAX_NETBIOS_REPLY_DATA_SIZE; + + if (smb_nic_get_bysubnet(addr->sin.sin_addr.s_addr, &cfg) == NULL) + net_ipaddr = 0; + else + net_ipaddr = cfg.ip; + + smb_config_rdlock(); + max_connections = smb_config_getnum(SMB_CI_MAX_CONNECTIONS); + smb_config_unlock(); + for (;;) { + if ((scan + 6) >= scan_end) { + packet.info |= NAME_NM_FLAGS_TC; + break; + } + + if (net_ipaddr != 0) { + struct sockaddr_in *s_in; + int s; + + s = socket(AF_INET, SOCK_DGRAM, 0); + /* LINTED - E_BAD_PTR_CAST_ALIGN */ + s_in = (struct sockaddr_in *)&arpreq.arp_pa; + s_in->sin_family = AF_INET; + s_in->sin_addr.s_addr = net_ipaddr; + if (ioctl(s, SIOCGARP, (caddr_t)&arpreq) < 0) { + bzero(scan, 6); + } else { + bcopy(&arpreq.arp_ha.sa_data, scan, 6); + } + (void) close(s); + } else { + bzero(scan, 6); + } + scan += 6; + + if ((scan + 26) >= scan_end) { + packet.info |= NAME_NM_FLAGS_TC; + break; + } + bzero(scan, 26); + scan += 26; + + if ((scan + 2) >= scan_end) { + packet.info |= NAME_NM_FLAGS_TC; + break; + } + BE_OUT16(scan, 0); scan += 2; + + if ((scan + 2) >= scan_end) { + packet.info |= NAME_NM_FLAGS_TC; + break; + } + BE_OUT16(scan, 0); scan += 2; + + if ((scan + 2) >= scan_end) { + packet.info |= NAME_NM_FLAGS_TC; + break; + } + BE_OUT16(scan, 0); scan += 2; + + if ((scan + 2) >= scan_end) { + packet.info |= NAME_NM_FLAGS_TC; + break; + } + BE_OUT16(scan, 0); scan += 2; + + if ((scan + 2) >= scan_end) { + packet.info |= NAME_NM_FLAGS_TC; + break; + } + BE_OUT16(scan, 0); scan += 2; + + if ((scan + 2) >= scan_end) { + packet.info |= NAME_NM_FLAGS_TC; + break; + } + BE_OUT16(scan, 0); scan += 2; + + if ((scan + 2) >= scan_end) { + packet.info |= NAME_NM_FLAGS_TC; + break; + } + BE_OUT16(scan, 0); scan += 2; + + if ((scan + 2) >= scan_end) { + packet.info |= NAME_NM_FLAGS_TC; + break; + } + BE_OUT16(scan, max_connections); scan += 2; + + if ((scan + 2) >= scan_end) { + packet.info |= NAME_NM_FLAGS_TC; + break; + } + + BE_OUT16(scan, 0); scan += 2; + + break; + /*NOTREACHED*/ + } + answer.rdlength = scan - data; + return (smb_send_name_service_packet(addr, &packet)); +} + +/* + * + * 5.1. NAME SERVICE PROTOCOLS + * + * A REQUEST packet is always sent to the well known UDP port - + * NAME_SERVICE_UDP_PORT. The destination address is normally + * either the IP broadcast address or the address of the NAME - the + * address of the NAME server it set up at initialization time. In + * rare cases, a request packet will be sent to an end node, e.g. a + * NAME QUERY REQUEST sent to "challenge" a node. + * + * A RESPONSE packet is always sent to the source UDP port and + * source IP address of the request packet. + * + * A DEMAND packet must always be sent to the well known UDP port - + * NAME_SERVICE_UDP_PORT. There is no restriction on the target IP + * address. + * + * Terms used in this section: + * + * tid - Transaction ID. This is a value composed from + * the requestor's IP address and a unique 16 bit + * value generated by the originator of the + * transaction. + */ + + +/* + * + * 5.1.1. B-NODE ACTIVITY + * + * 5.1.1.1. B-NODE ADD NAME + * + * PROCEDURE add_name(name) + * + * (* + * * Host initiated processing for a B node + * *) + * BEGIN + * + * REPEAT + * + * (* build name service packet *) + * + * ONT = B_NODE; (* broadcast node *) + * G = UNIQUE; (* unique name *) + * TTL = 0; + * + * broadcast NAME REGISTRATION REQUEST packet; + * + * (* + * * remote node(s) will send response packet + * * if applicable + * *) + * pause(BCAST_REQ_RETRY_TIMEOUT); + * + * UNTIL response packet is received or + * retransmit count has been exceeded + * + * IF no response packet was received THEN + * BEGIN (* no response *) + * (* + * * Build packet + * *) + * + * ONT = B_NODE; (* broadcast node *) + * G = UNIQUE; (* unique name *) + * TTL = 0; + * + * (* + * * Let other nodes known you have the name + * *) + * + * broadcast NAME UPDATE REQUEST packet; + * (* name can be added to local name table *) + * return success; + * END (* no response *) + * ELSE + * BEGIN (* got response *) + * + * (* + * * Match return transaction id + * * against tid sent in request + * *) + * + * IF NOT response tid = request tid THEN + * BEGIN + * ignore response packet; + * END + * ELSE + * CASE packet type OF + * + * NEGATIVE NAME REGISTRATION RESPONSE: + * + * return failure; (* name cannot be added *) + * + * POSITIVE NAME REGISTRATION RESPONSE: + * END-NODE CHALLENGE NAME REGISTRATION RESPONSE: + * + * (* + * * B nodes should normally not get this + * * response. + * *) + * + * ignore packet; + * END (* case *); + * END (* got response *) + * END (* procedure *) + * + * + * + * 5.1.1.2. B-NODE ADD_GROUP NAME + * + * PROCEDURE add_group_name(name) + * + * (* + * * Host initiated processing for a B node + * *) + * + * BEGIN + * (* + * * same as for a unique name with the + * * exception that the group bit (G) must + * * be set in the request packets. + * *) + * + * ... + * G = GROUP; + * ... + * ... + * + * (* + * * broadcast request ... + * *) + * + * + * END + */ +static int +smb_name_Bnode_add_name(struct name_entry *name) +{ + struct name_question question; + struct resource_record additional; + unsigned char data[8]; + unsigned short attr; + struct addr_entry *addr; + int rc = 0; + + addr = &name->addr_list; + + do { + /* build name service packet */ + question.name = name; + /* + * question.name->attributes |= NAME_NB_FLAGS_ONT_B; + * This is commented because NAME_NB_FLAGS_ONT_B is 0 + */ + question.question_type = NAME_QUESTION_TYPE_NB; + question.question_class = NAME_QUESTION_CLASS_IN; + + additional.name = name; + additional.rr_class = NAME_QUESTION_CLASS_IN; + additional.ttl = 0; + additional.rdata = data; + additional.rdlength = 6; + additional.rr_type = NAME_QUESTION_TYPE_NB; + attr = name->attributes & (NAME_ATTR_GROUP | + NAME_ATTR_OWNER_NODE_TYPE); + + BE_OUT16(&data[0], attr); + (void) memcpy(&data[2], &addr->sin.sin_addr.s_addr, + sizeof (uint32_t)); + + rc |= smb_send_name_registration_request(BROADCAST, &question, + &additional); + addr = addr->forw; + + } while (addr != &name->addr_list); + + return (rc); +} + +/* + * 5.1.1.3. B-NODE FIND_NAME + * + * PROCEDURE find_name(name) + * + * (* + * * Host initiated processing for a B node + * *) + * + * BEGIN + * + * REPEAT + * (* + * * build packet + * *) + * ONT = B; + * TTL = 0; + * G = DONT CARE; + * raddr = raddr->forw; + * + * broadcast NAME QUERY REQUEST packet; + * (* + * * a node might send response packet + * *) + * + * pause(BCAST_REQ_RETRY_TIMEOUT); + * UNTIL response packet received OR + * max transmit threshold exceeded + * + * IF no response packet received THEN + * return failure; + * ELSE + * IF NOT response tid = request tid THEN + * ignore packet; + * ELSE + * CASE packet type OF + * POSITIVE NAME QUERY RESPONSE: + * (* + * * Start a timer to detect conflict. + * * + * * Be prepared to detect conflict if + * * any more response packets are received. + * * + * *) + * + * save response as authoritative response; + * start_timer(CONFLICT_TIMER); + * return success; + * + * NEGATIVE NAME QUERY RESPONSE: + * REDIRECT NAME QUERY RESPONSE: + * + * (* + * * B Node should normally not get either + * * response. + * *) + * + * ignore response packet; + * + * END (* case *) + * END (* procedure *) + */ +static int +smb_name_Bnode_find_name(struct name_entry *name) +{ + struct name_question question; + + question.name = name; + question.question_type = NAME_QUESTION_TYPE_NB; + question.question_class = NAME_QUESTION_CLASS_IN; + + return (smb_send_name_query_request(BROADCAST, &question)); +} + +/* + * 5.1.1.4. B NODE NAME RELEASE + * + * PROCEDURE delete_name (name) + * BEGIN + * + * REPEAT + * + * (* + * * build packet + * *) + * ... + * + * (* + * * send request + * *) + * + * broadcast NAME RELEASE REQUEST packet; + * + * (* + * * no response packet expected + * *) + * + * pause(BCAST_REQ_RETRY_TIMEOUT); + * + * UNTIL retransmit count has been exceeded + * END (* procedure *) + */ +static int +smb_name_Bnode_delete_name(struct name_entry *name) +{ + struct name_question question; + struct resource_record additional; + struct addr_entry *raddr; + unsigned char data[MAX_DATAGRAM_LENGTH]; + unsigned char *scan = data; + uint32_t attr; + + /* build packet */ + question.name = name; + question.question_type = NAME_QUESTION_TYPE_NB; + question.question_class = NAME_QUESTION_CLASS_IN; + + additional.name = name; + additional.rr_class = NAME_QUESTION_CLASS_IN; + additional.ttl = 0; + additional.rdata = data; + additional.rdlength = 0; + additional.rr_type = NAME_QUESTION_TYPE_NB; + raddr = &name->addr_list; + scan = data; + do { + attr = name->attributes & (NAME_ATTR_GROUP | + NAME_ATTR_OWNER_NODE_TYPE); + + BE_OUT16(scan, attr); scan += 2; + (void) memcpy(scan, &raddr->sin.sin_addr.s_addr, + sizeof (uint32_t)); + scan += 4; + + additional.rdlength += 6; + } while (raddr != &name->addr_list); + + return (smb_send_name_release_request_and_demand(BROADCAST, + &question, &additional)); +} + +/* + * + * 5.1.2. P-NODE ACTIVITY + * + * All packets sent or received by P nodes are unicast UDP packets. + * A P node sends name service requests to the NAME node that is + * specified in the P-node configuration. + * + * 5.1.2.1. P-NODE ADD_NAME + * + * PROCEDURE add_name(name) + * + * (* + * * Host initiated processing for a P node + * *) + * + * BEGIN + * + * REPEAT + * (* + * * build packet + * *) + * + * ONT = P; + * G = UNIQUE; + * ... + * + * (* + * * send request + * *) + * + * unicast NAME REGISTRATION REQUEST packet; + * + * (* + * * NAME will send response packet + * *) + * + * IF receive a WACK RESPONSE THEN + * pause(time from TTL field of response); + * ELSE + * pause(UCAST_REQ_RETRY_TIMEOUT); + * UNTIL response packet is received OR + * retransmit count has been exceeded + * + * IF no response packet was received THEN + * BEGIN (* no response *) + * (* + * * NAME is down. Cannot claim name. + * *) + * + * return failure; (* name cannot be claimed *) + * END (* no response *) + * ELSE + * BEGIN (* response *) + * IF NOT response tid = request tid THEN + * BEGIN + * (* Packet may belong to another transaction *) + * ignore response packet; + * END + * ELSE + * CASE packet type OF + * + * POSITIVE NAME REGISTRATION RESPONSE: + * + * (* + * * name can be added + * *) + * + * adjust refresh timeout value, TTL, for this name; + * return success; (* name can be added *) + * + * NEGATIVE NAME REGISTRATION RESPONSE: + * return failure; (* name cannot be added *) + * + * END-NODE CHALLENGE REGISTRATION REQUEST: + * BEGIN (* end node challenge *) + * + * (* + * * The response packet has in it the + * * address of the presumed owner of the + * * name. Challenge that owner. + * * If owner either does not + * * respond or indicates that he no longer + * * owns the name, claim the name. + * * Otherwise, the name cannot be claimed. + * * + * *) + * + * REPEAT + * (* + * * build packet + * *) + * ... + * + * unicast NAME QUERY REQUEST packet to the + * address contained in the END NODE + * CHALLENGE RESPONSE packet; + * + * (* + * * remote node may send response packet + * *) + * + * pause(UCAST_REQ_RETRY_TIMEOUT); + * + * UNTIL response packet is received or + * retransmit count has been exceeded + * IF no response packet is received OR + * NEGATIVE NAME QUERY RESPONSE packet + * received THEN + * BEGIN (* update *) + * + * (* + * * name can be claimed + * *) + * + * REPEAT + * + * (* + * * build packet + * *) + * ... + * + * unicast NAME UPDATE REQUEST to NAME; + * + * (* + * * NAME node will send response packet + * *) + * + * IF receive a WACK RESPONSE THEN + * pause(time from TTL field of response); + * ELSE + * pause(UCAST_REQ_RETRY_TIMEOUT); + * UNTIL response packet is received or + * retransmit count has been exceeded + * IF no response packet received THEN + * BEGIN (* no response *) + * + * (* + * * name could not be claimed + * *) + * + * return failure; + * END (* no response *) + * ELSE + * CASE packet type OF + * POSITIVE NAME REGISTRATION RESPONSE: + * (* + * * add name + * *) + * return success; + * NEGATIVE NAME REGISTRATION RESPONSE: + * + * (* + * * you lose ... + * *) + * return failure; + * END (* case *) + * END (* update *) + * ELSE + * + * (* + * * received a positive response to the "challenge" + * * Remote node still has name + * *) + * + * return failure; + * END (* end node challenge *) + * END (* response *) + * END (* procedure *) + * + * + * 5.1.2.2. P-NODE ADD GROUP NAME + * + * PROCEDURE add_group_name(name) + * + * (* + * * Host initiated processing for a P node + * *) + * + * BEGIN + * (* + * * same as for a unique name, except that the + * * request packet must indicate that a + * * group name claim is being made. + * *) + * + * ... + * G = GROUP; + * ... + * + * (* + * * send packet + * *) + * ... + * + * + * END + */ +static int +smb_name_Pnode_add_name(struct name_entry *name) +{ + struct name_question question; + struct resource_record additional; + unsigned char data[8]; + unsigned short attr; + struct addr_entry *addr; + int rc = 0; + + /* build packet */ + addr = &name->addr_list; + do { + question.name = name; + question.question_type = NAME_QUESTION_TYPE_NB; + question.question_class = NAME_QUESTION_CLASS_IN; + + additional.name = name; + additional.rr_class = NAME_QUESTION_CLASS_IN; + additional.ttl = 0; + additional.rdata = data; + additional.rdlength = 6; + additional.rr_type = NAME_QUESTION_TYPE_NB; + attr = name->attributes & + (NAME_ATTR_GROUP | NAME_ATTR_OWNER_NODE_TYPE); + + BE_OUT16(&data[0], attr); + (void) memcpy(&data[2], &addr->sin.sin_addr.s_addr, + sizeof (uint32_t)); + + rc |= smb_send_name_registration_request(UNICAST, &question, + &additional); + + addr = addr->forw; + + } while (addr != &name->addr_list); + + return (rc); +} + +static int +smb_name_Pnode_refresh_name(struct name_entry *name) +{ + struct name_question question; + struct resource_record additional; + unsigned char data[8]; + unsigned short attr; + struct addr_entry *addr; + int rc = 0; + + /* build packet */ + addr = &name->addr_list; + do { + question.name = name; + question.question_type = NAME_QUESTION_TYPE_NB; + question.question_class = NAME_QUESTION_CLASS_IN; + + additional.name = name; + additional.rr_class = NAME_QUESTION_CLASS_IN; + additional.ttl = 0; + additional.rdata = data; + additional.rdlength = 6; + additional.rr_type = NAME_QUESTION_TYPE_NB; + attr = name->attributes & + (NAME_ATTR_GROUP | NAME_ATTR_OWNER_NODE_TYPE); + + BE_OUT16(&data[0], attr); + (void) memcpy(&data[2], &addr->sin.sin_addr.s_addr, + sizeof (uint32_t)); + + rc |= smb_send_name_refresh_request(UNICAST, &question, + &additional, 1); + + addr = addr->forw; + } while (addr != &name->addr_list); + + return (rc); +} + +/* + * 5.1.2.3. P-NODE FIND NAME + * + * PROCEDURE find_name(name) + * + * (* + * * Host initiated processing for a P node + * *) + * + * BEGIN + * REPEAT + * (* + * * build packet + * *) + * + * ONT = P; + * G = DONT CARE; + * + * unicast NAME QUERY REQUEST packet; + * + * (* + * * a NAME node might send response packet + * *) + * + * IF receive a WACK RESPONSE THEN + * pause(time from TTL field of response); + * ELSE + * pause(UCAST_REQ_RETRY_TIMEOUT); + * UNTIL response packet received OR + * max transmit threshold exceeded + * + * IF no response packet received THEN + * return failure; + * ELSE + * IF NOT response tid = request tid THEN + * ignore packet; + * ELSE + * CASE packet type OF + * POSITIVE NAME QUERY RESPONSE: + * return success; + * + * REDIRECT NAME QUERY RESPONSE: + * + * (* + * * NAME node wants this end node + * * to use some other NAME node + * * to resolve the query. + * *) + * + * repeat query with NAME address + * in the response packet; + * NEGATIVE NAME QUERY RESPONSE: + * return failure; + * + * END (* case *) + * END (* procedure *) + */ +static int +smb_name_Pnode_find_name(struct name_entry *name) +{ + struct name_question question; + + /* + * Host initiated processing for a P node + */ + question.name = name; + question.name->attributes |= NAME_NB_FLAGS_ONT_P; + question.question_type = NAME_QUESTION_TYPE_NB; + question.question_class = NAME_QUESTION_CLASS_IN; + + return (smb_send_name_query_request(UNICAST, &question)); +} + +/* + * 5.1.2.4. P-NODE DELETE_NAME + * + * PROCEDURE delete_name (name) + * + * (* + * * Host initiated processing for a P node + * *) + * + * BEGIN + * + * REPEAT + * + * (* + * * build packet + * *) + * ... + * + * (* + * * send request + * *) + * + * unicast NAME RELEASE REQUEST packet; + * IF receive a WACK RESPONSE THEN + * pause(time from TTL field of response); + * ELSE + * pause(UCAST_REQ_RETRY_TIMEOUT); + * UNTIL retransmit count has been exceeded + * or response been received + * + * IF response has been received THEN + * CASE packet type OF + * POSITIVE NAME RELEASE RESPONSE: + * return success; + * NEGATIVE NAME RELEASE RESPONSE: + * + * (* + * * NAME does want node to delete this + * * name !!! + * *) + * + * return failure; + * END (* case *) + * END (* procedure *) + */ +static int +smb_name_Pnode_delete_name(struct name_entry *name) +{ + struct name_question question; + struct resource_record additional; + struct addr_entry *raddr; + unsigned char data[MAX_DATAGRAM_LENGTH]; + unsigned char *scan = data; + uint32_t attr; + + /* build packet */ + question.name = name; + question.name->attributes |= NAME_NB_FLAGS_ONT_P; + question.question_type = NAME_QUESTION_TYPE_NB; + question.question_class = NAME_QUESTION_CLASS_IN; + + additional.name = name; + additional.rr_class = NAME_QUESTION_CLASS_IN; + additional.ttl = 0; + additional.rdata = data; + additional.rdlength = 0; + additional.rr_type = NAME_QUESTION_TYPE_NB; + raddr = &name->addr_list; + do { + scan = data; + attr = name->attributes & (NAME_ATTR_GROUP | + NAME_ATTR_OWNER_NODE_TYPE); + + BE_OUT16(scan, attr); scan += 2; + BE_OUT32(scan, raddr->sin.sin_addr.s_addr); scan += 4; + (void) memcpy(scan, &raddr->sin.sin_addr.s_addr, + sizeof (uint32_t)); + scan += 4; + + additional.rdlength = 6; + raddr = raddr->forw; + (void) smb_send_name_release_request_and_demand(UNICAST, + &question, &additional); + } while (raddr != &name->addr_list); + + return (1); +} + +/* + * 5.1.3. M-NODE ACTIVITY + * + * M nodes behavior is similar to that of P nodes with the addition + * of some B node-like broadcast actions. M node name service + * proceeds in two steps: + * + * 1.Use broadcast UDP based name service. Depending on the + * operation, goto step 2. + * + * 2.Use directed UDP name service. + * + * The following code for M nodes is exactly the same as for a P + * node, with the exception that broadcast operations are done + * before P type operation is attempted. + * + * 5.1.3.1. M-NODE ADD NAME + * + * PROCEDURE add_name(name) + * + * (* + * * Host initiated processing for a M node + * *) + * + * BEGIN + * + * (* + * * check if name exists on the + * * broadcast area + * *) + * REPEAT + * (* build packet *) + * + * .... + * broadcast NAME REGISTRATION REQUEST packet; + * pause(BCAST_REQ_RETRY_TIMEOUT); + * + * UNTIL response packet is received or + * retransmit count has been exceeded + * + * IF valid response received THEN + * BEGIN + * (* cannot claim name *) + * + * return failure; + * END + * + * (* + * * No objections received within the + * * broadcast area. + * * Send request to name server. + * *) + * + * REPEAT + * (* + * * build packet + * *) + * + * ONT = M; + * ... + * + * unicast NAME REGISTRATION REQUEST packet; + * + * (* + * * remote NAME will send response packet + * *) + * + * IF receive a WACK RESPONSE THEN + * pause(time from TTL field of response); + * ELSE + * pause(UCAST_REQ_RETRY_TIMEOUT); + * + * UNTIL response packet is received or + * retransmit count has been exceeded + * + * IF no response packet was received THEN + * BEGIN (* no response *) + * (* + * * NAME is down. Cannot claim name. + * *) + * return failure; (* name cannot be claimed *) + * END (* no response *) + * ELSE + * BEGIN (* response *) + * IF NOT response tid = request tid THEN + * BEGIN + * ignore response packet; + * END + * ELSE + * CASE packet type OF + * POSITIVE NAME REGISTRATION RESPONSE: + * + * (* + * * name can be added + * *) + * + * adjust refresh timeout value, TTL; + * return success; (* name can be added *) + * + * NEGATIVE NAME REGISTRATION RESPONSE: + * return failure; (* name cannot be added *) + * + * END-NODE CHALLENGE REGISTRATION REQUEST: + * BEGIN (* end node challenge *) + * + * (* + * * The response packet has in it the + * * address of the presumed owner of the + * * name. Challenge that owner. + * * If owner either does not + * * respond or indicates that he no longer + * * owns the name, claim the name. + * * Otherwise, the name cannot be claimed. + * * + * *) + * + * REPEAT + * (* + * * build packet + * *) + * ... + * + * (* + * * send packet to address contained in the + * * response packet + * *) + * + * unicast NAME QUERY REQUEST packet; + * + * (* + * * remote node may send response packet + * *) + * + * pause(UCAST_REQ_RETRY_TIMEOUT); + * + * UNTIL response packet is received or + * retransmit count has been exceeded + * IF no response packet is received THEN + * BEGIN (* no response *) + * + * (* + * * name can be claimed + * *) + * REPEAT + * + * (* + * * build packet + * *) + * ... + * + * unicast NAME UPDATE REQUEST to NAME; + * + * (* + * * NAME node will send response packet + * *) + * + * IF receive a WACK RESPONSE THEN + * pause(time from TTL field of response); + * ELSE + * pause(UCAST_REQ_RETRY_TIMEOUT); + * + * UNTIL response packet is received or + * retransmit count has been exceeded + * IF no response packet received THEN + * BEGIN (* no response *) + * + * (* + * * name could not be claimed + * *) + * + * return failure; + * END (* no response *) + * ELSE + * CASE packet type OF + * POSITIVE NAME REGISTRATION RESPONSE: + * (* + * * add name + * *) + * + * return success; + * NEGATIVE NAME REGISTRATION RESPONSE: + * (* + * * you lose ... + * *) + * + * return failure; + * END (* case *) + * END (* no response *) + * ELSE + * IF NOT response tid = request tid THEN + * BEGIN + * ignore response packet; + * END + * + * (* + * * received a response to the "challenge" + * * packet + * *) + * + * CASE packet type OF + * POSITIVE NAME QUERY: + * + * (* + * * remote node still has name. + * *) + * + * return failure; + * NEGATIVE NAME QUERY: + * + * (* + * * remote node no longer has name + * *) + * + * return success; + * END (* case *) + * END (* end node challenge *) + * END (* case *) + * END (* response *) + * END (* procedure *) + * + * + * 5.1.3.2. M-NODE ADD GROUP NAME + * + * PROCEDURE add_group_name(name) + * + * (* + * * Host initiated processing for a P node + * *) + * + * BEGIN + * (* + * * same as for a unique name, except that the + * * request packet must indicate that a + * * group name claim is being made. + * *) + * + * ... + * G = GROUP; + * ... + * + * (* + * * send packet + * *) + * ... + * + * + * END + */ +static int +smb_name_Mnode_add_name(struct name_entry *name) +{ + if (smb_name_Bnode_add_name(name) > 0) { + if (nbns_num == 0) + return (1); /* No name server configured */ + + return (smb_name_Pnode_add_name(name)); + } + return (-1); +} + +static int +smb_name_Hnode_add_name(struct name_entry *name) +{ + if (nbns_num > 0) { + if (smb_name_Pnode_add_name(name) == 1) + return (1); + } + + return (smb_name_Bnode_add_name(name)); +} + +/* + * 5.1.3.3. M-NODE FIND NAME + * + * PROCEDURE find_name(name) + * + * (* + * * Host initiated processing for a M node + * *) + * + * BEGIN + * (* + * * check if any node on the broadcast + * * area has the name + * *) + * + * REPEAT + * (* build packet *) + * ... + * + * broadcast NAME QUERY REQUEST packet; + * pause(BCAST_REQ_RETRY_TIMEOUT); + * UNTIL response packet received OR + * max transmit threshold exceeded + * + * IF valid response received THEN + * BEGIN + * save response as authoritative response; + * start_timer(CONFLICT_TIMER); + * return success; + * END + * + * (* + * * no valid response on the b'cast segment. + * * Try the name server. + * *) + * + * REPEAT + * (* + * * build packet + * *) + * + * ONT = M; + * G = DONT CARE; + * + * unicast NAME QUERY REQUEST packet to NAME; + * + * (* + * * a NAME node might send response packet + * *) + * + * IF receive a WACK RESPONSE THEN + * pause(time from TTL field of response); + * ELSE + * pause(UCAST_REQ_RETRY_TIMEOUT); + * UNTIL response packet received OR + * max transmit threshold exceeded + * + * IF no response packet received THEN + * return failure; + * ELSE + * IF NOT response tid = request tid THEN + * ignore packet; + * ELSE + * CASE packet type OF + * POSITIVE NAME QUERY RESPONSE: + * return success; + * + * REDIRECT NAME QUERY RESPONSE: + * + * (* + * * NAME node wants this end node + * * to use some other NAME node + * * to resolve the query. + * *) + * + * repeat query with NAME address + * in the response packet; + * NEGATIVE NAME QUERY RESPONSE: + * return failure; + * + * END (* case *) + * END (* procedure *) + */ +static int +smb_name_Mnode_find_name(struct name_entry *name) +{ + if (smb_name_Bnode_find_name(name) == 1) + return (1); + + if (nbns_num == 0) + return (1); /* No name server configured */ + + return (smb_name_Pnode_find_name(name)); +} + +static int +smb_name_Hnode_find_name(struct name_entry *name) +{ + if (nbns_num > 0) + if (smb_name_Pnode_find_name(name) == 1) + return (1); + + return (smb_name_Bnode_find_name(name)); +} + +/* + * 5.1.3.4. M-NODE DELETE NAME + * + * PROCEDURE delete_name (name) + * + * (* + * * Host initiated processing for a P node + * *) + * + * BEGIN + * (* + * * First, delete name on NAME + * *) + * + * REPEAT + * + * (* + * * build packet + * struct addr_entry *addr; + * *) + * ... + * + * (* + * * send request + * *) + * + * unicast NAME RELEASE REQUEST packet to NAME; + * + * IF receive a WACK RESPONSE THEN + * pause(time from TTL field of response); + * ELSE + * pause(UCAST_REQ_RETRY_TIMEOUT); + * UNTIL retransmit count has been exceeded + * or response been received + * + * IF response has been received THEN + * CASE packet type OF + * POSITIVE NAME RELEASE RESPONSE: + * (* + * * Deletion of name on b'cast segment is deferred + * * until after NAME has deleted the name + * *) + * + * REPEAT + * (* build packet *) + * + * ... + * broadcast NAME RELEASE REQUEST; + * pause(BCAST_REQ_RETRY_TIMEOUT); + * UNTIL rexmt threshold exceeded + * + * return success; + * NEGATIVE NAME RELEASE RESPONSE: + * + * (* + * * NAME does want node to delete this + * * name + * *) + * return failure; + * END (* case *) + * END (* procedure *) + */ +static int +smb_name_Mnode_delete_name(struct name_entry *name) +{ + (void) smb_name_Bnode_delete_name(name); + + if (nbns_num == 0) + return (-1); /* No name server configured */ + + if (smb_name_Pnode_delete_name(name) > 0) + return (1); + + return (-1); +} + +static int +smb_name_Hnode_delete_name(struct name_entry *name) +{ + if (nbns_num > 0) + if (smb_name_Pnode_delete_name(name) > 0) + return (1); + + return (smb_name_Bnode_delete_name(name)); +} + +/* + * 5.1.1.5. B-NODE INCOMING PACKET PROCESSING + * + * Following processing is done when broadcast or unicast packets + * are received at the NAME_SERVICE_UDP_PORT. + * + * PROCEDURE process_incoming_packet(packet) + * + * (* + * * Processing initiated by incoming packets for a B node + * *) + * + * BEGIN + * (* + * * Note: response packets are always sent + * * to: + * * source IP address of request packet + * * source UDP port of request packet + * *) + * + * CASE packet type OF + * + * NAME REGISTRATION REQUEST (UNIQUE): + * IF name exists in local name table THEN + * send NEGATIVE_NAME_REGISTRATION_RESPONSE ; + * NAME REGISTRATION REQUEST (GROUP): + * IF name exists in local name table THEN + * BEGIN + * IF local entry is a unique name THEN + * send NEGATIVE_NAME_REGISTRATION_RESPONSE ; + * END + * NAME QUERY REQUEST: + * IF name exists in local name table THEN + * BEGIN + * build response packet; + * send POSITIVE_NAME_QUERY_RESPONSE; + * POSITIVE NAME QUERY RESPONSE: + * IF name conflict timer is not active THEN + * BEGIN + * (* + * * timer has expired already... ignore this + * * packet + * *) + * + * return; + * END + * ELSE (* timer is active *) + * IF a response for this name has previously been + * received THEN + * BEGIN (* existing entry *) + * + * (* + * * we sent out a request packet, and + * * have already received (at least) + * * one response + * * + * * Check if conflict exists. + * * If so, send out a conflict packet. + * * + * * Note: detecting conflict does NOT + * * affect any existing sessions. + * * + * *) + * + * (* + * * Check for name conflict. + * * See "Name Conflict" in Concepts and Methods + * *) + * check saved authoritative response against + * information in this response packet; + * IF conflict detected THEN + * BEGIN + * unicast NAME CONFLICT DEMAND packet; + * IF entry exists in cache THEN + * BEGIN + * remove entry from cache; + * END + * END + * END (* existing entry *) + * ELSE + * BEGIN + * (* + * * Note: If this was the first response + * * to a name query, it would have been + * * handled in the + * * find_name() procedure. + * *) + * + * ignore packet; + * END + * NAME CONFLICT DEMAND: + * IF name exists in local name table THEN + * BEGIN + * mark name as conflict detected; + * + * (* + * * a name in the state "conflict detected" + * * does not "logically" exist on that node. + * * No further session will be accepted on + * * that name. + * * No datagrams can be sent against that name. + * * Such an entry will not be used for + * * purposes of processing incoming request + * * packets. + * * The only valid user NetBIOS operation + * * against such a name is DELETE NAME. + * *) + * END + * NAME RELEASE REQUEST: + * IF caching is being done THEN + * BEGIN + * remove entry from cache; + * END + * NAME UPDATE REQUEST: + * IF caching is being done THEN + * BEGIN + * IF entry exists in cache already, + * update cache; + * ELSE IF name is "interesting" THEN + * BEGIN + * add entry to cache; + * END + * END + * + * NODE STATUS REQUEST: + * IF name exists in local name table THEN + * BEGIN + * (* + * * send only those names that are + * * in the same scope as the scope + * * field in the request packet + * *) + * + * send NODE STATUS RESPONSE; + * END + * END + */ +static void +smb_name_process_Bnode_packet(struct name_packet *packet, + struct addr_entry *addr) +{ + struct name_entry *name; + struct name_entry *entry; + struct name_question *question; + struct resource_record *additional; + + question = packet->question; + additional = packet->additional; + + switch (packet->info & NAME_OPCODE_OPCODE_MASK) { + case NAME_OPCODE_REFRESH: + /* Guard against malformed packets */ + if ((question == 0) || (additional == 0)) + break; + if (additional->name->addr_list.sin.sin_addr.s_addr == 0) + break; + + name = question->name; + name->addr_list.ttl = additional->ttl; + name->attributes = additional->name->attributes; + name->addr_list.sin = additional->name->addr_list.sin; + name->addr_list.forw = name->addr_list.back = &name->addr_list; + + if ((entry = smb_netbios_cache_lookup_addr(name)) != 0) { + smb_netbios_cache_update_entry(entry, question->name); + smb_netbios_cache_unlock_entry(entry); + } + else + (void) smb_netbios_cache_insert(question->name); + break; + + case NAME_OPCODE_QUERY: + /* + * This opcode covers both NAME_QUERY_REQUEST and + * NODE_STATUS_REQUEST. They can be distinguished + * based on the type of question entry. + */ + + /* All query requests have to have question entry */ + if (question == 0) + break; + + if (question->question_type == NAME_QUESTION_TYPE_NB) { + name = question->name; + if ((entry = smb_netbios_cache_lookup(name)) != 0) { + (void) smb_send_name_query_response(addr, + packet, entry, 0); + smb_netbios_cache_unlock_entry(entry); + } + } + else + if (question->question_type == NAME_QUESTION_TYPE_NBSTAT) { + /* + * Name of "*" may be used to force node to + * divulge status for administrative purposes + */ + name = question->name; + entry = 0; + if (NETBIOS_NAME_IS_STAR(name->name) || + ((entry = smb_netbios_cache_lookup(name)) != 0)) { + if (entry) + smb_netbios_cache_unlock_entry(entry); + /* + * send only those names that are + * in the same scope as the scope + * field in the request packet + */ + (void) smb_send_node_status_response(addr, + packet); + } + } + break; + + default: + break; + } +} + +/* + * 5.1.2.5. P-NODE INCOMING PACKET PROCESSING + * + * Processing initiated by reception of packets at a P node + * + * PROCEDURE process_incoming_packet(packet) + * + * (* + * * Processing initiated by incoming packets at a P node + * *) + * + * BEGIN + * (* + * * always ignore UDP broadcast packets + * *) + * + * IF packet was sent as a broadcast THEN + * BEGIN + * ignore packet; + * return; + * END + * CASE packet type of + * + * NAME CONFLICT DEMAND: + * IF name exists in local name table THEN + * mark name as in conflict; + * return; + * + * NAME QUERY REQUEST: + * IF name exists in local name table THEN + * BEGIN (* name exists *) + * + * (* + * * build packet + * *) + * ... + * + * (* + * * send response to the IP address and port + * * number from which the request was received. + * *) + * + * send POSITIVE_NAME_QUERY_RESPONSE ; + * return; + * END (* exists *) + * ELSE + * BEGIN (* does not exist *) + * + * (* + * * send response to the requestor + * *) + * + * send NEGATIVE_NAME_QUERY_RESPONSE ; + * return; + * END (* does not exist *) + * NODE STATUS REQUEST: + * (* + * * Name of "*" may be used for force node to + * * divulge status for administrative purposes + * *) + * IF name in local name table OR name = "*" THEN + * BEGIN + * (* + * * Build response packet and + * * send to requestor node + * * Send only those names that are + * * in the same scope as the scope + * * in the request packet. + * *) + * + * send NODE_STATUS_RESPONSE; + * END + * + * NAME RELEASE REQUEST: + * (* + * * This will be received if the NAME wants to flush the + * * name from the local name table, or from the local + * * cache. + * *) + * + * IF name exists in the local name table THEN + * BEGIN + * delete name from local name table; + * inform user that name has been deleted; + * END + * END (* case *) + * END (* procedure *) + * + * (* + * * Incoming packet processing on a NS node + * *) + * + * BEGIN + * IF packet was sent as a broadcast THEN + * BEGIN + * discard packet; + * return; + * END + * CASE packet type of + * + * NAME REGISTRATION REQUEST (UNIQUE): + * IF unique name exists in data base THEN + * BEGIN (* unique name exists *) + * (* + * * NAME node may be a "passive" + * * server in that it expects the + * * end node to do the challenge + * * server. Such a NAME node is + * * called a "non-secure" server. + * * A "secure" server will do the + * * challenging before it sends + * * back a response packet. + * *) + * + * IF non-secure THEN + * BEGIN + * (* + * * build response packet + * *) + * ... + * + * + * (* + * * let end node do the challenge + * *) + * + * send END-NODE CHALLENGE NAME REGISTRATION + * RESPONSE; + * return; + * END + * ELSE + * (* + * * secure server - do the name + * * challenge operation + * *) + * + * REPEAT + * send NAME QUERY REQUEST; + * pause(UCAST_REQ_RETRY_TIMEOUT); + * UNTIL response has been received or + * retransmit count has been exceeded + * IF no response was received THEN + * BEGIN + * + * (* node down *) + * + * update data base - remove entry; + * update data base - add new entry; + * send POSITIVE NAME REGISTRATION RESPONSE; + * return; + * END + * ELSE + * BEGIN (* challenged node replied *) + * (* + * * challenged node replied with + * * a response packet + * *) + * + * CASE packet type + * + * POSITIVE NAME QUERY RESPONSE: + * + * (* + * * name still owned by the + * * challenged node + * * + * * build packet and send response + * *) + * ... + * + * + * (* + * * Note: The NAME will need to + * * keep track (based on transaction id) of + * * the IP address and port number + * * of the original requestor. + * *) + * + * send NEGATIVE NAME REGISTRATION RESPONSE; + * return; + * NEGATIVE NAME QUERY RESPONSE: + * + * update data base - remove entry; + * update data base - add new entry; + * + * (* + * * build response packet and send + * * response + * *) + * send POSITIVE NAME REGISTRATION RESPONSE; + * return; + * END (* case *) + * END (* challenged node replied *) + * END (* unique name exists in data base *) + * ELSE + * IF group name exists in data base THEN + * BEGIN (* group names exists *) + * + * (* + * * Members of a group name are NOT + * * challenged. + * * Make the assumption that + * * at least some of the group members + * * are still alive. + * * Refresh mechanism will + * * allow the NAME to detect when all + * * members of a group no longer use that + * * name + * *) + * + * send NEGATIVE NAME REGISTRATION RESPONSE; + * END (* group name exists *) + * ELSE + * BEGIN (* name does not exist *) + * + * (* + * * Name does not exist in data base + * * + * * This code applies to both non-secure + * * and secure server. + * *) + * + * update data base - add new entry; + * send POSITIVE NAME REGISTRATION RESPONSE; + * return; + * END + * + * NAME QUERY REQUEST: + * IF name exists in data base THEN + * BEGIN + * (* + * * build response packet and send to + * * requestor + * *) + * ... + * + * send POSITIVE NAME QUERY RESPONSE; + * return; + * ELSE + * BEGIN + * (* + * * build response packet and send to + * * requestor + * *) + * ... + * + * send NEGATIVE NAME QUERY RESPONSE; + * return; + * END + * + * NAME REGISTRATION REQUEST (GROUP): + * IF name exists in data base THEN + * BEGIN + * IF local entry is a unique name THEN + * BEGIN (* local is unique *) + * + * IF non-secure THEN + * BEGIN + * send END-NODE CHALLENGE NAME + * REGISTRATION RESPONSE; + * return; + * END + * + * REPEAT + * send NAME QUERY REQUEST; + * pause(UCAST_REQ_RETRY_TIMEOUT); + * UNTIL response received or + * retransmit count exceeded + * IF no response received or + * NEGATIVE NAME QUERY RESPONSE + * received THEN + * BEGIN + * update data base - remove entry; + * update data base - add new entry; + * send POSITIVE NAME REGISTRATION RESPONSE; + * return; + * END + * ELSE + * BEGIN + * (* + * * name still being held + * * by challenged node + * *) + * + * send NEGATIVE NAME REGISTRATION RESPONSE; + * END + * END (* local is unique *) + * ELSE + * BEGIN (* local is group *) + * (* + * * existing entry is a group name + * *) + * + * update data base - remove entry; + * update data base - add new entry; + * send POSITIVE NAME REGISTRATION RESPONSE; + * return; + * END (* local is group *) + * END (* names exists *) + * ELSE + * BEGIN (* does not exist *) + * + * (* name does not exist in data base *) + * + * update data base - add new entry; + * send POSITIVE NAME REGISTRATION RESPONSE; + * return; + * END (* does not exist *) + * + * NAME RELEASE REQUEST: + * + * (* + * * secure server may choose to disallow + * * a node from deleting a name + * *) + * + * update data base - remove entry; + * send POSITIVE NAME RELEASE RESPONSE; + * return; + * + * NAME UPDATE REQUEST: + * + * (* + * * End-node completed a successful challenge, + * * no update database + * *) + * + * IF secure server THEN + * send NEGATIVE NAME REGISTRATION RESPONSE; + * ELSE + * BEGIN (* new entry *) + * IF entry already exists THEN + * update data base - remove entry; + * update data base - add new entry; + * send POSITIVE NAME REGISTRATION RESPONSE; + * start_timer(TTL); + * END + * + * NAME REFRESH REQUEST: + * check for consistency; + * + * IF node not allowed to have name THEN + * BEGIN + * + * (* + * * tell end node that it can't have name + * *) + * send NEGATIVE NAME REGISTRATION RESPONSE; + * END + * ELSE + * BEGIN + * + * (* + * * send confirmation response to the + * * end node. + * *) + * send POSITIVE NAME REGISTRATION; + * start_timer(TTL); + * END + * return; + * END (* case *) + * END (* procedure *) + */ +static void +smb_name_process_Pnode_packet(struct name_packet *packet, + struct addr_entry *addr) +{ + struct name_entry *name; + struct name_entry *entry; + struct name_question *question; + struct resource_record *additional; + + question = packet->question; + additional = packet->additional; + + if (packet->info & NAME_NM_FLAGS_B) { + /* + * always ignore UDP broadcast packets + */ + return; + } + + switch (packet->info & NAME_OPCODE_OPCODE_MASK) { + case NAME_OPCODE_REFRESH: + /* Guard against malformed packets */ + if ((question == 0) || (additional == 0)) + break; + if (additional->name->addr_list.sin.sin_addr.s_addr == 0) + break; + + name = question->name; + name->addr_list.ttl = additional->ttl; + name->attributes = additional->name->attributes; + name->addr_list.sin = additional->name->addr_list.sin; + name->addr_list.forw = name->addr_list.back = &name->addr_list; + + if ((entry = smb_netbios_cache_lookup(name)) != 0) { + smb_netbios_cache_update_entry(entry, name); + smb_netbios_cache_unlock_entry(entry); + } + else + (void) smb_netbios_cache_insert(name); + + (void) smb_send_name_registration_response(addr, packet, 0); + break; + + case NAME_OPCODE_QUERY: + /* + * This opcode covers both NAME_QUERY_REQUEST and + * NODE_STATUS_REQUEST. They can be distinguished + * based on the type of question entry. + */ + + /* All query requests have to have question entry */ + if (question == 0) + break; + + if (question->question_type == NAME_QUESTION_TYPE_NB) { + name = question->name; + if ((entry = smb_netbios_cache_lookup(name)) != 0) { + /* + * send response to the IP address and port + * number from which the request was received. + */ + (void) smb_send_name_query_response(addr, + packet, entry, 0); + smb_netbios_cache_unlock_entry(entry); + } else { + /* + * send response to the requestor + */ + (void) smb_send_name_query_response(addr, + packet, name, RCODE_NAM_ERR); + } + } + else + if (question->question_type == NAME_QUESTION_TYPE_NBSTAT) { + /* + * Name of "*" may be used to force node to + * divulge status for administrative purposes + */ + name = question->name; + entry = 0; + if (NETBIOS_NAME_IS_STAR(name->name) || + ((entry = smb_netbios_cache_lookup(name)) != 0)) { + /* + * send only those names that are + * in the same scope as the scope + * field in the request packet + */ + if (entry) + smb_netbios_cache_unlock_entry(entry); + (void) smb_send_node_status_response(addr, + packet); + } + } + break; + + default: + break; + } +} + +/* + * 5.1.3.5. M-NODE INCOMING PACKET PROCESSING + * + * Processing initiated by reception of packets at a M node + * + * PROCEDURE process_incoming_packet(packet) + * + * (* + * * Processing initiated by incoming packets at a M node + * *) + * + * BEGIN + * CASE packet type of + * + * NAME CONFLICT DEMAND: + * IF name exists in local name table THEN + * mark name as in conflict; + * return; + * + * NAME QUERY REQUEST: + * IF name exists in local name table THEN + * BEGIN (* name exists *) + * + * (* + * * build packet + * *) + * ... + * + * (* + * * send response to the IP address and port + * * number from which the request was received. + * *) + * + * send POSITIVE NAME QUERY RESPONSE ; + * return; + * END (* exists *) + * ELSE + * BEGIN (* does not exist *) + * + * (* + * * send response to the requestor + * *) + * + * IF request NOT broadcast THEN + * (* + * * Don't send negative responses to + * * queries sent by B nodes + * *) + * send NEGATIVE NAME QUERY RESPONSE ; + * return; + * END (* does not exist *) + * NODE STATUS REQUEST: + * BEGIN + * (* + * * Name of "*" may be used to force node to + * * divulge status for administrative purposes + * *) + * IF name in local name table OR name = "*" THEN + * (* + * * Build response packet and + * * send to requestor node + * * Send only those names that are + * * in the same scope as the scope + * * in the request packet. + * *) + * + * send NODE STATUS RESPONSE; + * END + * + * NAME RELEASE REQUEST: + * (* + * * This will be received if the NAME wants to flush the + * * name from the local name table, or from the local + * * cache. + * *) + * + * IF name exists in the local name table THEN + * BEGIN + * delete name from local name table; + * inform user that name has been deleted; + * END + * NAME REGISTRATION REQUEST (UNIQUE): + * IF name exists in local name table THEN + * send NEGATIVE NAME REGISTRATION RESPONSE ; + * NAME REGISTRATION REQUEST (GROUP): + * IF name exists in local name table THEN + * BEGIN + * IF local entry is a unique name THEN + * send NEGATIVE NAME REGISTRATION RESPONSE ; + * END + * END (* case *) + * END (* procedure *) + */ +static void +smb_name_process_Mnode_packet(struct name_packet *packet, + struct addr_entry *addr) +{ + if (packet->info & NAME_NM_FLAGS_B) + smb_name_process_Bnode_packet(packet, addr); + else + smb_name_process_Pnode_packet(packet, addr); +} + +static void +smb_name_process_Hnode_packet(struct name_packet *packet, + struct addr_entry *addr) +{ + if (packet->info & NAME_NM_FLAGS_B) + smb_name_process_Bnode_packet(packet, addr); + else + smb_name_process_Pnode_packet(packet, addr); +} + + +/* + * smb_netbios_name_tick + * + * Called once a second to handle name server timeouts. + */ +void +smb_netbios_name_tick(void) +{ + struct name_entry *name; + struct name_entry *entry; + + (void) mutex_lock(&refresh_queue.mtx); + smb_netbios_cache_refresh(&refresh_queue); + + while ((name = refresh_queue.head.forw) != &refresh_queue.head) { + QUEUE_CLIP(name); + if (IS_LOCAL(name->attributes)) { + if (IS_UNIQUE(name->attributes)) { + (void) smb_name_Pnode_refresh_name(name); + } + } else { + entry = smb_name_find_name(name); + smb_name_unlock_name(entry); + } + free(name); + } + (void) mutex_unlock(&refresh_queue.mtx); + + smb_netbios_cache_reset_ttl(); +} + + +/* + * smb_name_find_name + * + * Lookup name cache for the given name. + * If it's not in the cache it'll send a + * name query request and then lookup the + * cache again. Note that if a name is + * returned it's locked and called MUST + * unlock it by calling smb_name_unlock_name() + */ +struct name_entry * +smb_name_find_name(struct name_entry *name) +{ + struct name_entry *result; + + if ((result = smb_netbios_cache_lookup(name)) == 0) { + switch (smb_node_type) { + case 'B': + (void) smb_name_Bnode_find_name(name); + break; + case 'P': + (void) smb_name_Pnode_find_name(name); + break; + case 'M': + (void) smb_name_Mnode_find_name(name); + break; + case 'H': + default: + (void) smb_name_Hnode_find_name(name); + break; + } + return (smb_netbios_cache_lookup(name)); + } + + return (result); +} + +void +smb_name_unlock_name(struct name_entry *name) +{ + smb_netbios_cache_unlock_entry(name); +} + +int +smb_name_add_name(struct name_entry *name) +{ + int rc = 1; + + smb_netbios_name_dump(name); + + switch (smb_node_type) { + case 'B': + rc = smb_name_Bnode_add_name(name); + break; + case 'P': + rc = smb_name_Pnode_add_name(name); + break; + case 'M': + rc = smb_name_Mnode_add_name(name); + break; + case 'H': + default: + rc = smb_name_Hnode_add_name(name); + break; + } + + if (rc >= 0) + (void) smb_netbios_cache_insert(name); + + return (rc); +} + +int +smb_name_delete_name(struct name_entry *name) +{ + int rc; + unsigned char type; + + type = name->name[15]; + if ((type != 0x00) && (type != 0x20)) { + syslog(LOG_ERR, + "netbios: error trying to delete non-local name"); + smb_netbios_name_logf(name); + name->attributes &= ~NAME_ATTR_LOCAL; + return (-1); + } + + smb_netbios_cache_delete(name); + + switch (smb_node_type) { + case 'B': + rc = smb_name_Bnode_delete_name(name); + break; + case 'P': + rc = smb_name_Pnode_delete_name(name); + break; + case 'M': + rc = smb_name_Mnode_delete_name(name); + break; + case 'H': + default: + rc = smb_name_Hnode_delete_name(name); + break; + } + + if (rc > 0) + return (0); + + return (-1); +} + +typedef struct { + struct addr_entry *addr; + char *buf; + int length; +} worker_param_t; + +/* + * smb_netbios_worker + * + * Process incoming request/response packets for Netbios + * name service (on port 138). + */ +void * +smb_netbios_worker(void *arg) +{ + worker_param_t *p = (worker_param_t *)arg; + struct addr_entry *addr = p->addr; + struct name_packet *packet; + + if ((packet = smb_name_buf_to_packet(p->buf, p->length)) != 0) { + if (packet->info & NAME_OPCODE_R) { + /* Reply packet */ + smb_reply_ready(packet, addr); + free(p->buf); + free(p); + return (0); + } + + /* Request packet */ + switch (smb_node_type) { + case 'B': + smb_name_process_Bnode_packet(packet, addr); + break; + case 'P': + smb_name_process_Pnode_packet(packet, addr); + break; + case 'M': + smb_name_process_Mnode_packet(packet, addr); + break; + case 'H': + default: + smb_name_process_Hnode_packet(packet, addr); + break; + } + + if (packet->answer) + smb_netbios_name_freeaddrs(packet->answer->name); + free(packet); + } + else + { + syslog(LOG_DEBUG, "SmbNBNS: error decoding received packet"); + } + + free(addr); + free(p->buf); + free(p); + return (0); +} + +static void +smb_netbios_wins_config(char *ip) +{ + uint32_t ipaddr; + + ipaddr = inet_addr(ip); + if (ipaddr != INADDR_NONE) { + smb_nbns[nbns_num].flags = ADDR_FLAG_VALID; + smb_nbns[nbns_num].sinlen = sizeof (struct sockaddr_in); + smb_nbns[nbns_num].sin.sin_family = AF_INET; + smb_nbns[nbns_num].sin.sin_addr.s_addr = ipaddr; + smb_nbns[nbns_num++].sin.sin_port = + htons(NAME_SERVICE_UDP_PORT); + smb_node_type = 'H'; + } +} + +void +smb_netbios_name_config(void) +{ + uint32_t ipaddr; + struct name_entry name; + char myname[MAXHOSTNAMELEN]; + int i; + int smb_nc_cnt; + net_cfg_t cfg; + + if (smb_getnetbiosname(myname, MAXHOSTNAMELEN) != 0) + return; + + /* Start with no broadcast addresses */ + bcast_num = 0; + bzero(smb_bcast_list, sizeof (addr_entry_t) * SMB_PI_MAX_NETWORKS); + + smb_nc_cnt = smb_nic_get_num(); + /* Add all of my broadcast addresses */ + for (i = 0; i < smb_nc_cnt; i++) { + if (smb_nic_get_byind(i, &cfg) == NULL) { + break; + } + smb_bcast_list[bcast_num].flags = ADDR_FLAG_VALID; + smb_bcast_list[bcast_num].attributes = NAME_ATTR_LOCAL; + smb_bcast_list[bcast_num].sinlen = sizeof (struct sockaddr_in); + smb_bcast_list[bcast_num].sin.sin_family = AF_INET; + smb_bcast_list[bcast_num].sin.sin_port = + htons(NAME_SERVICE_UDP_PORT); + smb_bcast_list[bcast_num++].sin.sin_addr.s_addr = + cfg.broadcast; + } + + /* Start with no WINS */ + smb_node_type = 'B'; + nbns_num = 0; + bzero(smb_nbns, sizeof (addr_entry_t) * SMB_PI_MAX_WINS); + + /* add any configured WINS */ + smb_config_rdlock(); + smb_netbios_wins_config(smb_config_getstr(SMB_CI_WINS_SRV1)); + smb_netbios_wins_config(smb_config_getstr(SMB_CI_WINS_SRV2)); + smb_config_unlock(); + + for (i = 0; i < smb_nc_cnt; i++) { + if (smb_nic_get_byind(i, &cfg) == NULL) { + break; + } + if (cfg.exclude) + continue; + + ipaddr = cfg.ip; + smb_init_name_struct((unsigned char *)myname, 0x00, 0, ipaddr, + htons(DGM_SRVC_UDP_PORT), NAME_ATTR_UNIQUE, + NAME_ATTR_LOCAL, &name); + (void) smb_name_add_name(&name); + + smb_init_name_struct((unsigned char *)myname, 0x20, 0, + ipaddr, htons(DGM_SRVC_UDP_PORT), + NAME_ATTR_UNIQUE, NAME_ATTR_LOCAL, &name); + (void) smb_name_add_name(&name); + } +} + +void +smb_netbios_name_unconfig(void) +{ + struct name_entry *name; + + (void) mutex_lock(&delete_queue.mtx); + smb_netbios_cache_delete_locals(&delete_queue); + + while ((name = delete_queue.head.forw) != &delete_queue.head) { + QUEUE_CLIP(name); + (void) smb_name_delete_name(name); + free(name); + } + (void) mutex_unlock(&delete_queue.mtx); +} + +void +smb_netbios_name_reconfig(void) +{ + smb_netbios_name_unconfig(); + smb_netbios_name_config(); +} + +/* + * process_incoming Function: void smb_netbios_name_service_daemon(void) + * + * Description: + * + * Put test description here. + * + * Inputs: + * Nothing + * + * Returns: + * int -> Description + */ +/*ARGSUSED*/ +void * +smb_netbios_name_service_daemon(void *arg) +{ + struct sockaddr_in sin; + struct addr_entry *addr; + int len; + int flag = 1; + char *buf; + worker_param_t *worker_param; + net_cfg_t cfg; + + /* + * Initialize reply_queue + */ + bzero(&reply_queue, sizeof (reply_queue)); + reply_queue.forw = reply_queue.back = &reply_queue; + + if (!smb_netbios_cache_init()) + return (0); + + bcast_num = 0; + bzero(smb_bcast_list, sizeof (addr_entry_t) * SMB_PI_MAX_NETWORKS); + + if ((name_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, + "smbd: Could not create AF_INET, SOCK_DGRAM, socket"); + smb_netbios_cache_fini(); + smb_netbios_chg_status(NETBIOS_NAME_SVC_FAILED, 1); + return (0); + } + + (void) setsockopt(name_sock, SOL_SOCKET, SO_BROADCAST, &flag, + sizeof (flag)); + + bzero(&sin, sizeof (struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_port = htons(NAME_SERVICE_UDP_PORT); + if (bind(name_sock, (struct sockaddr *)&sin, sizeof (sin)) != 0) { + syslog(LOG_ERR, + "smbd: Bind to name service port %d failed (%d)", + NAME_SERVICE_UDP_PORT, errno); + smb_netbios_cache_fini(); + (void) close(name_sock); + smb_netbios_chg_status(NETBIOS_NAME_SVC_FAILED, 1); + return (0); + } + + smb_netbios_chg_status(NETBIOS_NAME_SVC_RUNNING, 1); + + while (((nb_status.state & NETBIOS_SHUTTING_DOWN) == 0) || + (nb_status.state & NETBIOS_BROWSER_RUNNING)) { + if ((buf = malloc(MAX_DATAGRAM_LENGTH)) == 0) { + /* Sleep for 10 sec and try again */ + (void) sleep(10); + continue; + } + if ((addr = (struct addr_entry *) + malloc(sizeof (struct addr_entry))) == 0) { + /* Sleep for 10 sec and try again */ + free(buf); + (void) sleep(10); + continue; + } +ignore: bzero(addr, sizeof (struct addr_entry)); + addr->sinlen = sizeof (addr->sin); + addr->forw = addr->back = addr; + + if ((len = recvfrom(name_sock, buf, MAX_DATAGRAM_LENGTH, + 0, (struct sockaddr *)&addr->sin, &addr->sinlen)) < 0) { + if (errno == ENOMEM || errno == ENFILE || + errno == EMFILE) { + /* Sleep for 10 sec and try again */ + free(buf); + free(addr); + (void) sleep(10); + continue; + } + syslog(LOG_ERR, + "smbd: NETBIOS name service - recvfrom failed"); + free(buf); + free(addr); + smb_netbios_chg_status(NETBIOS_NAME_SVC_FAILED, 1); + goto shutdown; + } + + /* Ignore any incoming packets from myself... */ + if (smb_nic_get_byip(addr->sin.sin_addr.s_addr, + &cfg) != NULL) { + goto ignore; + } + + /* + * Launch a netbios worker to process the received packet. + */ + worker_param = (worker_param_t *) + malloc(sizeof (worker_param_t)); + if (worker_param) { + pthread_t worker; + pthread_attr_t tattr; + + worker_param->addr = addr; + worker_param->buf = buf; + worker_param->length = len; + + (void) pthread_attr_init(&tattr); + (void) pthread_attr_setdetachstate(&tattr, + PTHREAD_CREATE_DETACHED); + (void) pthread_create(&worker, &tattr, + smb_netbios_worker, worker_param); + (void) pthread_attr_destroy(&tattr); + } + } + +shutdown: + smb_netbios_chg_status(NETBIOS_NAME_SVC_RUNNING, 0); + + (void) mutex_lock(&nb_status.mtx); + while (nb_status.state & NETBIOS_BROWSER_RUNNING) + (void) cond_wait(&nb_status.cv, &nb_status.mtx); + (void) mutex_unlock(&nb_status.mtx); + + if ((nb_status.state & NETBIOS_NAME_SVC_FAILED) == 0) { + /* this might delay shutdown, do we want to do this? */ + /* + * it'll send name release requests but nobody's waiting + * for response and it'll eventually timeout. + */ + smb_netbios_name_unconfig(); + } + (void) close(name_sock); + smb_netbios_cache_fini(); + syslog(LOG_DEBUG, "smbd: Netbios Name Service is down\n"); + return (0); +} diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_netlogon.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_netlogon.c new file mode 100644 index 000000000000..d74bcb168d49 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_netlogon.c @@ -0,0 +1,643 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module handles the primary domain controller location protocol. + * The document claims to be version 1.15 of the browsing protocol. It also + * claims to specify the mailslot protocol. + * + * The NETLOGON protocol uses \MAILSLOT\NET mailslots. The protocol + * specification is incomplete, contains errors and is out-of-date but + * it does provide some useful background information. The document + * doesn't mention the NETLOGON_SAMLOGON version of the protocol. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static void smb_netlogon_query(struct name_entry *server, char *mailbox, + char *domain); + +static void smb_netlogon_samlogon(struct name_entry *server, char *mailbox, + char *domain); + +static void smb_netlogon_send(struct name_entry *name, char *domain, + unsigned char *buffer, int count); + +static void smb_netlogon_rdc_rsp(char *src_name, uint32_t src_ipaddr); +static int better_dc(uint32_t cur_ip, uint32_t new_ip); + +static char resource_domain[SMB_PI_MAX_DOMAIN]; + +/* + * smb_netlogon_request + * + * This is the entry point locating the resource domain PDC. A netlogon + * request is sent using the specified protocol on the specified network. + * Note that we need to know the domain SID in order to use the samlogon + * format. + * + * Netlogon responses are received asynchronously and eventually handled + * in smb_netlogon_receive. + */ +void +smb_netlogon_request(int net, int protocol, char *domain) +{ + struct name_entry *server; + nt_domain_t *ntdp; + + server = smb_browser_get_srvname(net); + if (server == 0) + return; + + (void) strlcpy(resource_domain, domain, + sizeof (resource_domain)); + + if (strlen(resource_domain) > 0) { + ntdp = nt_domain_lookup_name(resource_domain); + if (protocol == NETLOGON_PROTO_SAMLOGON && ntdp) + smb_netlogon_samlogon(server, + MAILSLOT_NETLOGON_SAMLOGON_RDC, + resource_domain); + else + smb_netlogon_query(server, + MAILSLOT_NETLOGON_RDC, + resource_domain); + } +} + +/* + * smb_netlogon_receive + * + * This is where we handle all incoming NetLogon messages. Currently, we + * ignore requests from anyone else. We are only interested in responses + * to our own requests. The NetLogonResponse provides the name of the PDC. + * If we don't already have a controller name, we use the name provided + * in the message. Otherwise we use the name already in the environment. + */ +void +smb_netlogon_receive(struct datagram *datagram, + char *mailbox, + unsigned char *data, + int datalen) +{ + struct netlogon_opt { + char *mailslot; + void (*handler)(); + } netlogon_opt[] = { + { MAILSLOT_NETLOGON_RDC, smb_netlogon_rdc_rsp }, + { MAILSLOT_NETLOGON_SAMLOGON_RDC, smb_netlogon_rdc_rsp }, + }; + + smb_msgbuf_t mb; + unsigned short opcode; + char src_name[SMB_PI_MAX_HOST]; + mts_wchar_t unicode_src_name[SMB_PI_MAX_HOST]; + unsigned int cpid = oem_get_smb_cpid(); + uint32_t src_ipaddr; + char *junk; + char *primary; + char *domain; + int i; + char ipstr[16]; + int rc; + + src_ipaddr = datagram->src.addr_list.sin.sin_addr.s_addr; + + /* + * The datagram->src.name is in oem codepage format. + * Therefore, we need to convert it to unicode and + * store it in multi-bytes format. + */ + (void) oemstounicodes(unicode_src_name, (char *)datagram->src.name, + SMB_PI_MAX_HOST, cpid); + (void) mts_wcstombs(src_name, unicode_src_name, SMB_PI_MAX_HOST); + + (void) trim_whitespace(src_name); + + (void) inet_ntop(AF_INET, (const void *)(&src_ipaddr), ipstr, + sizeof (ipstr)); + syslog(LOG_DEBUG, "NetLogonReceive: src=%s [%s], mbx=%s", + src_name, ipstr, mailbox); + + smb_msgbuf_init(&mb, data, datalen, 0); + + if (smb_msgbuf_decode(&mb, "w", &opcode) < 0) { + syslog(LOG_ERR, "NetLogonReceive: decode error"); + smb_msgbuf_term(&mb); + return; + } + + switch (opcode) { + case LOGON_PRIMARY_RESPONSE: + /* + * Message contains: + * PDC name (MBS), PDC name (Unicode), Domain name (unicode) + */ + rc = smb_msgbuf_decode(&mb, "sUU", &junk, &primary, &domain); + if (rc < 0) { + syslog(LOG_ERR, + "NetLogonResponse: opcode %d decode error", + opcode); + smb_msgbuf_term(&mb); + return; + } + break; + + case LOGON_SAM_LOGON_RESPONSE: + case LOGON_SAM_USER_UNKNOWN: + /* + * Message contains: + * PDC name, User name, Domain name (all unicode) + */ + rc = smb_msgbuf_decode(&mb, "UUU", &primary, &junk, &domain); + if (rc < 0) { + syslog(LOG_ERR, + "NetLogonResponse: opcode %d decode error", + opcode); + smb_msgbuf_term(&mb); + return; + } + + /* + * skip past the "\\" prefix + */ + primary += strspn(primary, "\\"); + break; + + default: + /* + * We don't respond to PDC discovery requests. + */ + syslog(LOG_DEBUG, "NetLogonReceive: opcode 0x%04x", opcode); + smb_msgbuf_term(&mb); + return; + } + + if (domain == 0 || primary == 0) { + syslog(LOG_ERR, "NetLogonResponse: malformed packet"); + smb_msgbuf_term(&mb); + return; + } + + syslog(LOG_DEBUG, "DC Offer Dom=%s PDC=%s From=%s", + domain, primary, src_name); + + if (strcasecmp(domain, resource_domain)) { + syslog(LOG_DEBUG, "NetLogonResponse: other domain " + "%s, requested %s", domain, resource_domain); + smb_msgbuf_term(&mb); + return; + } + + for (i = 0; i < sizeof (netlogon_opt)/sizeof (netlogon_opt[0]); ++i) { + if (strcasecmp(netlogon_opt[i].mailslot, mailbox) == 0) { + syslog(LOG_DEBUG, "NetLogonReceive: %s", mailbox); + (*netlogon_opt[i].handler)(primary, src_ipaddr); + smb_msgbuf_term(&mb); + return; + } + } + + syslog(LOG_DEBUG, "NetLogonReceive[%s]: unknown mailslot", mailbox); + smb_msgbuf_term(&mb); +} + + + +/* + * smb_netlogon_query + * + * Build and send a LOGON_PRIMARY_QUERY to the MAILSLOT_NETLOGON. At some + * point we should receive a LOGON_PRIMARY_RESPONSE in the mailslot we + * specify in the request. + * + * struct NETLOGON_QUERY { + * unsigned short Opcode; # LOGON_PRIMARY_QUERY + * char ComputerName[]; # ASCII hostname. The response + * # is sent to (00). + * char MailslotName[]; # MAILSLOT_NETLOGON + * char Pad[]; # Pad to short + * wchar_t ComputerName[] # UNICODE hostname + * DWORD NT_Version; # 0x00000001 + * WORD LmNTToken; # 0xffff + * WORD Lm20Token; # 0xffff + * }; + */ +static void +smb_netlogon_query(struct name_entry *server, + char *mailbox, + char *domain) +{ + smb_msgbuf_t mb; + int offset, announce_len, data_length, name_lengths; + unsigned char buffer[MAX_DATAGRAM_LENGTH]; + char hostname[MAXHOSTNAMELEN]; + + if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0) + return; + + name_lengths = strlen(mailbox)+1+strlen(hostname)+1; + + /* + * The (name_lengths & 1) part is to word align the name_lengths + * before the wc equiv strlen and the "+ 2" is to cover the two + * zero bytes that terminate the wchar string. + */ + data_length = sizeof (short) + name_lengths + (name_lengths & 1) + + mts_wcequiv_strlen(hostname) + 2 + sizeof (long) + sizeof (short) + + sizeof (short); + + offset = smb_browser_load_transact_header(buffer, + sizeof (buffer), data_length, ONE_WAY_TRANSACTION, + MAILSLOT_NETLOGON); + + if (offset < 0) + return; + + smb_msgbuf_init(&mb, buffer + offset, sizeof (buffer) - offset, 0); + + announce_len = smb_msgbuf_encode(&mb, "wssUlww", + (short)LOGON_PRIMARY_QUERY, + hostname, + mailbox, + hostname, + 0x1, + 0xffff, + 0xffff); + + if (announce_len <= 0) { + smb_msgbuf_term(&mb); + syslog(LOG_ERR, "NetLogonQuery: encode error"); + return; + } + + smb_netlogon_send(server, domain, buffer, offset + announce_len); + smb_msgbuf_term(&mb); +} + + +/* + * smb_netlogon_samlogon + * + * The SamLogon version of the NetLogon request uses the workstation trust + * account and, I think, may be a prerequisite to the challenge/response + * netr authentication. The trust account username is the hostname with a + * $ appended. The mailslot for this request is MAILSLOT_NTLOGON. At some + * we should receive a LOGON_SAM_LOGON_RESPONSE in the mailslot we + * specify in the request. + * + * struct NETLOGON_SAM_LOGON { + * unsigned short Opcode; # LOGON_SAM_LOGON_REQUEST + * unsigned short RequestCount; # 0 + * wchar_t UnicodeComputerName; # hostname + * wchar_t UnicodeUserName; # hostname$ + * char *MailslotName; # response mailslot + * DWORD AllowableAccountControlBits; # 0x80 = WorkstationTrustAccount + * DWORD DomainSidSize; # domain sid length in bytes + * BYTE *DomainSid; # domain sid + * uint32_t NT_Version; # 0x00000001 + * unsigned short LmNTToken; # 0xffff + * unsigned short Lm20Token; # 0xffff + * }; + */ +static void +smb_netlogon_samlogon(struct name_entry *server, + char *mailbox, + char *domain) +{ + smb_msgbuf_t mb; + nt_domain_t *ntdp; + nt_sid_t *domain_sid; + unsigned domain_sid_len; + char *username; + unsigned char buffer[MAX_DATAGRAM_LENGTH]; + int offset; + int announce_len; + int data_length; + int name_length; + char hostname[MAXHOSTNAMELEN]; + + syslog(LOG_DEBUG, "NetLogonSamLogonReq: %s", domain); + + if ((ntdp = nt_domain_lookup_name(domain)) == 0) { + syslog(LOG_ERR, "NetLogonSamLogonReq[%s]: no sid", domain); + return; + } + + domain_sid = ntdp->sid; + domain_sid_len = nt_sid_length(domain_sid); + nt_sid_logf(domain_sid); + + if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0) + return; + + /* + * The username will be the trust account name on the PDC. + */ + name_length = strlen(hostname) + 2; + username = alloca(name_length); + (void) snprintf(username, name_length, "%s$", hostname); + + /* + * Add 2 to wide-char equivalent strlen to cover the + * two zero bytes that terminate the wchar string. + */ + name_length = strlen(mailbox)+1; + + data_length = sizeof (short) + + sizeof (short) + + mts_wcequiv_strlen(hostname) + 2 + + mts_wcequiv_strlen(username) + 2 + + name_length + + sizeof (long) + + sizeof (long) + + domain_sid_len + 3 /* padding */ + + sizeof (long) + + sizeof (short) + + sizeof (short); + + offset = smb_browser_load_transact_header(buffer, + sizeof (buffer), data_length, ONE_WAY_TRANSACTION, + MAILSLOT_NTLOGON); + + if (offset < 0) { + syslog(LOG_ERR, "NetLogonSamLogonReq: header error"); + return; + } + + /* + * The domain SID is padded with 3 leading zeros. + */ + smb_msgbuf_init(&mb, buffer + offset, sizeof (buffer) - offset, 0); + announce_len = smb_msgbuf_encode(&mb, "wwUUsll3.#clww", + (short)LOGON_SAM_LOGON_REQUEST, + 0, /* RequestCount */ + hostname, /* UnicodeComputerName */ + username, /* UnicodeUserName */ + mailbox, /* MailslotName */ + 0x00000080, /* AllowableAccountControlBits */ + domain_sid_len, /* DomainSidSize */ + domain_sid_len, domain_sid, /* DomainSid */ + 0x00000001, /* NT_Version */ + 0xffff, /* LmNTToken */ + 0xffff); /* Lm20Token */ + + if (announce_len <= 0) { + syslog(LOG_ERR, "NetLogonSamLogonReq: encode error"); + smb_msgbuf_term(&mb); + return; + } + + smb_netlogon_send(server, domain, buffer, offset + announce_len); + smb_msgbuf_term(&mb); +} + + +/* + * Send a query for each version of the protocol. + */ +static void +smb_netlogon_send(struct name_entry *name, + char *domain, + unsigned char *buffer, + int count) +{ + static char suffix[] = { 0x1B, 0x1C }; + struct name_entry dname; + struct name_entry *dest; + struct name_entry *dest_dup; + int i; + + for (i = 0; i < sizeof (suffix)/sizeof (suffix[0]); i++) { + smb_init_name_struct((unsigned char *)domain, suffix[i], + 0, 0, 0, 0, 0, &dname); + + syslog(LOG_DEBUG, "smb_netlogon_send"); + smb_netbios_name_dump(&dname); + if ((dest = smb_name_find_name(&dname)) != 0) { + dest_dup = smb_netbios_name_dup(dest, 1); + smb_name_unlock_name(dest); + if (dest_dup) { + (void) smb_netbios_datagram_send(name, dest_dup, + buffer, count); + free(dest_dup); + } + } else { + syslog(LOG_DEBUG, "smbd: NBNS couldn't find %s<0x%X>", + domain, suffix[i]); + } + } +} + +/* + * smb_netlogon_rdc_rsp + * + * This is where we process netlogon responses for the resource domain. + * The src_name is the real name of the remote machine. + */ +static void +smb_netlogon_rdc_rsp(char *src_name, uint32_t src_ipaddr) +{ + static int initialized = 0; + smb_ntdomain_t *pi; + uint32_t ipaddr; + uint32_t prefer_ipaddr = 0; + char ipstr[16]; + char srcip[16]; + char *p; + int rc; + + (void) inet_ntop(AF_INET, (const void *)(&src_ipaddr), + srcip, sizeof (srcip)); + + smb_config_rdlock(); + if ((p = smb_config_get(SMB_CI_DOMAIN_SRV)) != 0) { + rc = inet_pton(AF_INET, p, &prefer_ipaddr); + if (rc == 0) + prefer_ipaddr = 0; + + if (!initialized) { + (void) inet_ntop(AF_INET, + (const void *)(&prefer_ipaddr), + ipstr, sizeof (ipstr)); + syslog(LOG_DEBUG, "SMB DC Preference: %s", ipstr); + initialized = 1; + } + } + smb_config_unlock(); + + syslog(LOG_DEBUG, "DC Offer [%s]: %s [%s]", + resource_domain, src_name, srcip); + + if ((pi = smb_getdomaininfo(0)) != 0) { + if (prefer_ipaddr != 0 && prefer_ipaddr == pi->ipaddr) { + syslog(LOG_DEBUG, "DC for %s: %s [%s]", + resource_domain, src_name, srcip); + return; + } + + ipaddr = pi->ipaddr; + } else + ipaddr = 0; + + if (better_dc(ipaddr, src_ipaddr) || + (prefer_ipaddr != 0 && prefer_ipaddr == src_ipaddr)) { + smb_setdomaininfo(resource_domain, src_name, + src_ipaddr); + syslog(LOG_DEBUG, "DC discovered for %s: %s [%s]", + resource_domain, src_name, srcip); + } +} + +static int +better_dc(uint32_t cur_ip, uint32_t new_ip) +{ + net_cfg_t cfg; + + /* + * If we don't have any current DC, + * then use the new one of course. + */ + if (cur_ip == 0) + return (1); + + if (smb_nic_get_bysubnet(cur_ip, &cfg) != NULL) + return (0); + if (smb_nic_get_bysubnet(new_ip, &cfg) != NULL) + return (1); + /* + * Otherwise, just keep the old one. + */ + return (0); +} + +/* + * msdcs_lookup_ads + * + * Try to find a domain controller in ADS. Actually we want to query DNS + * but we need to find out if ADS is enabled and this is probably the + * best way. The IP address isn't set up in the ADS_HANDLE so we need to + * make the ads_find_host call. This will only succeed if ADS is enabled. + * + * Returns 1 if a domain controller was found and its name and IP address + * have been updated. Otherwise returns 0. + */ +int +msdcs_lookup_ads(void) +{ + ADS_HOST_INFO *hinfo = 0; + int ads_port = 0; + char ads_domain[MAXHOSTNAMELEN]; + char site_service[MAXHOSTNAMELEN]; + char service[MAXHOSTNAMELEN]; + char *site; + char *p; + char *ip_addr; + struct in_addr ns_list[MAXNS]; + int i, cnt, go_next; + + if (smb_getdomainname(ads_domain, MAXHOSTNAMELEN) != 0) + return (0); + + /* + * Initialize the NT domain name. + */ + (void) strlcpy(resource_domain, ads_domain, SMB_PI_MAX_DOMAIN); + if ((p = strchr(resource_domain, '.')) != 0) + *p = '\0'; + + smb_config_rdlock(); + if (smb_config_getyorn(SMB_CI_MSDCS_DISABLE) != 0) { + /* + * The system administrator doesn't + * want to use ADS to find the PDC. + */ + syslog(LOG_DEBUG, "msdcsLookupADS: disabled"); + smb_config_unlock(); + return (0); + } + site = smb_config_getstr(SMB_CI_ADS_SITE); + smb_config_unlock(); + + syslog(LOG_DEBUG, "msdcsLookupADS %s, MAXHOSTNAMELEN=%d", + ads_domain, MAXHOSTNAMELEN); + if (site && *site != 0) { + (void) snprintf(site_service, MAXHOSTNAMELEN, + "_ldap._tcp.%s._sites.dc._msdcs.%s", + site, ads_domain); + } + + (void) snprintf(service, MAXHOSTNAMELEN, + "_ldap._tcp.dc._msdcs.%s", ads_domain); + + cnt = smb_get_nameservers(ns_list, MAXNS); + + go_next = 0; + for (i = 0; i < cnt; i++) { + ip_addr = inet_ntoa(ns_list[i]); + + hinfo = ads_find_host(ip_addr, ads_domain, &ads_port, + site_service, &go_next); + + if (hinfo == NULL) { + hinfo = ads_find_host(ip_addr, ads_domain, &ads_port, + service, &go_next); + } + + if ((hinfo != NULL) || (go_next == 0)) + break; + } + + if (hinfo == NULL) { + syslog(LOG_DEBUG, "msdcsLookupADS: unable to find host"); + return (0); + } + + syslog(LOG_DEBUG, "msdcsLookupADS: %s [%I]", hinfo->name, + hinfo->ip_addr); + + /* + * Remove the domain extension - the + * NetBIOS browser can't handle it. + */ + if ((p = strchr(hinfo->name, '.')) != 0) + *p = '\0'; + + smb_netlogon_rdc_rsp(hinfo->name, hinfo->ip_addr); + + return (1); +} diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_nicconfig.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_nicconfig.c new file mode 100644 index 000000000000..0aaf8338b406 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_nicconfig.c @@ -0,0 +1,1163 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +static int smb_nic_get_list(struct if_list **); +static void smb_nic_clear_if_list(struct if_list *); + +/* This is the list we will monitor */ +static net_cfg_list_t smb_nic_list = { NULL, 0 }; +static pthread_mutex_t smb_nic_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t smb_ns_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Nameserver information */ +static struct __res_state smb_res; + +void +smb_resolver_init(void) +{ + int ret; + (void) pthread_mutex_lock(&smb_ns_mutex); + ret = res_ninit(&smb_res); + (void) pthread_mutex_unlock(&smb_ns_mutex); + if (ret < 0) { + syslog(LOG_ERR, "Failed to initialize resolver lib"); + } +} + +void +smb_resolver_close(void) +{ + (void) pthread_mutex_lock(&smb_ns_mutex); + res_nclose(&smb_res); + (void) pthread_mutex_unlock(&smb_ns_mutex); +} + +int +smb_get_nameservers(struct in_addr *ips, int sz) +{ + union res_sockaddr_union set[MAXNS]; + int i, cnt; + + if (ips == NULL) + return (0); + (void) pthread_mutex_lock(&smb_ns_mutex); + cnt = res_getservers(&smb_res, set, MAXNS); + for (i = 0; i < cnt; i++) { + if (i >= sz) + break; + ips[i] = set[i].sin.sin_addr; + syslog(LOG_DEBUG, "NS Found %s name server\n", + inet_ntoa(ips[i])); + } + syslog(LOG_DEBUG, "NS Found %d name servers\n", i); + (void) pthread_mutex_unlock(&smb_ns_mutex); + return (i); +} + +uint16_t +smb_get_next_resid(void) +{ + uint16_t id; + (void) pthread_mutex_lock(&smb_ns_mutex); + id = ++smb_res.id; + (void) pthread_mutex_unlock(&smb_ns_mutex); + return (id); +} + +/* + * The common NIC library will provide functions to obtain information + * on all interfaces. Information will include IP addresses, netmasks + * and broadcast address, as well as network statistic details. + */ + +/* + * Return IP string address associated with interface argument. + * If an error occurs, -1 will be returned. + * A return value of 1 indicates an unconfigured IP address + */ +static int +smb_nic_get_ip_addr(char *interface, char *IP, unsigned int IP_length) +{ + struct lifreq lifrr; + struct sockaddr_in *sa; + int sfd; + + sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + + if (sfd < 0) { + syslog(LOG_ERR, "%s", "nic_get_IP:socket open failed\n"); + return (-1); + } + + (void) strncpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name)); + + if (ioctl(sfd, SIOCGLIFADDR, &lifrr) < 0) { + syslog(LOG_ERR, "%s", "nic_get_IP: get IP address failed\n"); + (void) close(sfd); + return (-1); + } + /* Test length of allocated memory to avoid buffer overflow */ + if (IP_length < SIZE_IP) { + syslog(LOG_ERR, "%s", "nic_get_IP: insufficient memory" + "allocation\n"); + (void) close(sfd); + return (-1); + } + sa = (struct sockaddr_in *) &lifrr.lifr_addr; + (void) strncpy(IP, inet_ntoa(sa->sin_addr), SIZE_IP); + /* Check for unconfigured interface */ + if (strncmp(IP, "0.0.0.0", sizeof (IP)) == 0) { + syslog(LOG_ERR, "%s", "nic_get_IP: unconfigured interface\n"); + (void) close(sfd); + return (1); + } + (void) close(sfd); + return (0); +} + +/* + * Return IP address associated with interface argument. If an error occurs, + * -1 will be returned. A return value of 1 indicates an unconfigured IP + * address + */ +int +smb_nic_get_IP(char *interface, uint32_t *uip) +{ + struct lifreq lifrr; + struct sockaddr_in *sa; + int sfd; + + sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + + if (sfd < 0) { + syslog(LOG_ERR, "%s", "nic_get_IP:socket open failed\n"); + return (-1); + } + + (void) strncpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name)); + + if (ioctl(sfd, SIOCGLIFADDR, &lifrr) < 0) { + syslog(LOG_ERR, "%s", "nic_get_IP: get IP address failed\n"); + (void) close(sfd); + return (-1); + } + sa = (struct sockaddr_in *) &lifrr.lifr_addr; + if (uip != NULL) + *uip = (uint32_t)sa->sin_addr.s_addr; + (void) close(sfd); + return (0); +} + +/* + * Return broadcast address associated with interface argument.If an error + * occurs, -1 will be returned. A return value of 1 indicates an unconfigured + * broadcast address + */ +int +smb_nic_get_broadcast(char *interface, uint32_t *uip) +{ + struct lifreq lifrr; + struct sockaddr_in *sa; + int sfd; + + sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sfd < 0) { + syslog(LOG_ERR, "%s", "nic_get_broadcast:" + "socket open failed\n"); + return (-1); + } + + (void) strncpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name)); + + if (ioctl(sfd, SIOCGLIFBRDADDR, &lifrr) < 0) { + syslog(LOG_ERR, "%s", "nic_get_broadcast:" + "get broadcast address failed\n"); + (void) close(sfd); + return (-1); + } + sa = (struct sockaddr_in *)&lifrr.lifr_broadaddr; + if (uip != NULL) + *uip = (uint32_t)sa->sin_addr.s_addr; + (void) close(sfd); + return (0); + +} + +/* + * Return netmask address associated with interface argument. If error occurs, + * -1 will be returned. A return value of 1 indicates an unconfigured netmask + * address + */ +int +smb_nic_get_netmask(char *interface, uint32_t *uip) +{ + struct lifreq lifrr; + struct sockaddr_in *sa; + int sfd; + + sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sfd < 0) { + syslog(LOG_ERR, "%s", "nic_get_netmask:" + "socket open failed\n"); + return (-1); + } + + (void) strncpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name)); + + if (ioctl(sfd, SIOCGLIFNETMASK, &lifrr) < 0) { + syslog(LOG_ERR, "%s", "nic_get_netmask:" + "get netmask address failed\n"); + (void) close(sfd); + return (-1); + } + sa = (struct sockaddr_in *)&lifrr.lifr_addr; + if (uip != NULL) + *uip = (uint32_t)sa->sin_addr.s_addr; + (void) close(sfd); + return (0); + +} + +/* + * Fill ip_alias with IP addresses if any + * If it returns 0, there are no associated aliases with the interface. + * If it returns -1, there was an error + * If it returns 1, there are associated IP aliases with the interface. + */ +int +smb_nic_get_IP_aliases(char *interface, struct ip_alias **list) +{ + char ** names = NULL; + int result = 0; + int numnics, i, ret = 0; + char IP[SIZE_IP]; + struct ip_alias *tmp; + + *list = NULL; + + /* If the interface is a logical interface, return immediately */ + if (strchr(interface, ':') != NULL) { + syslog(LOG_ERR, "%s", "nic_get_IP_aliases:" + "invalid physical interface"); + return (ret); + } + + numnics = smb_nic_build_if_name(&names); + + for (i = 0; i < numnics; i++) { + /* + * Compare passed interface name to all other interface names. + * If it matches in the form of :1, it is an associated alias + * Example bge1:1's ip address is an ip alias of bge1 + */ + if (strncasecmp(interface, names[i], strlen(names[0])) == 0 && + strchr(names[i], ':') != 0) { + + result = smb_nic_get_ip_addr(names[i], + IP, sizeof (IP)); + if (result == -1) + return (result); + + tmp = (struct ip_alias *)malloc( + sizeof (struct ip_alias)); + if (tmp == NULL) { + syslog(LOG_ERR, "%s", "nic_get" + "_IP_aliases: out of memory"); + (void) smb_nic_clear_name_list(names, + numnics); + return (-1); + } + + (void) strncpy(tmp->name, IP, sizeof (tmp->name)); + tmp->next = *list; + *list = tmp; + ret = 1; + } + } + (void) smb_nic_clear_name_list(names, numnics); + return (ret); +} + +/* + * Return number of plumbed interfaces. Loopback interface is ignored + */ +int +smb_nic_get_number(void) +{ + struct lifnum lifn; + int numifs = 0, sfd; + + sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sfd < 0) { + syslog(LOG_ERR, "%s", "nic_get_number:" + "socket open failed"); + return (-1); + } + + lifn.lifn_family = AF_INET; + lifn.lifn_flags = 0; + + if (ioctl(sfd, SIOCGLIFNUM, &lifn) < 0) { + syslog(LOG_ERR, "%s", "nic_get_number:" + "unable to determine number"); + (void) close(sfd); + return (-1); + } + + numifs = lifn.lifn_count - 1; /* loopback */ + (void) close(sfd); + return (numifs); +} + +/* + * Given an interface name, return the name of the group it belongs to. + */ +int +smb_nic_get_group(char *lifname, char *grname) +{ + struct lifreq lifr; + int sfd; + int save_errno; + + sfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sfd < 0) { + syslog(LOG_ERR, "%s", "nic_set_group:socket open failed"); + return (-1); + } + if (strchr(lifname, ':') == NULL) { + (void) memset(lifr.lifr_groupname, 0, + sizeof (lifr.lifr_groupname)); + (void) strncpy(lifr.lifr_name, lifname, + sizeof (lifr.lifr_name)); + if (ioctl(sfd, SIOCGLIFGROUPNAME, (caddr_t)&lifr) >= 0) { + if (strlen(lifr.lifr_groupname) > 0) { + (void) strncpy(grname, lifr.lifr_groupname, + sizeof (lifr.lifr_groupname)); + } + } else { + save_errno = errno; + syslog(LOG_ERR, "smb_nic_get_group: ioctl failed"); + (void) close(sfd); + errno = save_errno; + return (-1); + } + } + (void) close(sfd); + return (0); +} + +/* + * Read the /etc/defaultrouter file for the gateway address. If an error occurs, + * -1 will be returned. + */ +int +smb_nic_get_default_gateway(char *gw, unsigned int gw_length) +{ + FILE *fp; + + fp = fopen(GATEWAY_FILE, "r"); + + if (fp == NULL) { + (void) fclose(fp); + return (-1); + } else { + /* Test length of allocated memory to avoid buffer overflow */ + if (gw_length < SIZE_IP) { + syslog(LOG_ERR, "%s", "get_default_gateway: " + "insufficient memory allocation\n"); + (void) fclose(fp); + return (-1); + } + (void) fgets(gw, SIZE_IP, fp); + (void) fclose(fp); + } + + return (0); +} + +/* + * Build the list of interface names, both physical and logical. + * A pointer to a pointer to a char will be filled with the info + */ +int +smb_nic_build_if_name(char ***if_names) +{ + struct if_list *iflist; + struct if_list *iflistptr; + int num_ifs, i; + + /* Get the interfaces */ + num_ifs = smb_nic_get_list(&iflist); + + /* Build the list of names */ + *if_names = (char **)malloc(sizeof (char *) * num_ifs); + + if (if_names == NULL) { + syslog(LOG_ERR, "%s", "Unable to build interface names"); + return (-1); + } + + for (i = 0, iflistptr = iflist; i < num_ifs; + iflistptr = iflistptr->next, i++) { + (*if_names)[i] = (char *)strdup(iflistptr->name); + } + (void) smb_nic_clear_if_list(iflist); + return (num_ifs); +} + +/* + * Get number of physical interfaces + */ +int +smb_nic_get_num_physical(void) +{ + char **names = NULL; + int phys_ifs = 0; + int i, result = 0; + /* Get list of interface names */ + result = smb_nic_build_if_name(&names); + if (result == -1) { + syslog(LOG_ERR, "%s", "Unable to determine num interfaces"); + return (-1); + } + for (i = 0; i < result; i++) { + if (strchr(names[i], ':') == NULL) { + /* It's a physical interface */ + phys_ifs++; + } + } + (void) smb_nic_clear_name_list(names, result); + return (phys_ifs); +} + +/* + * Get number of logical interfaces + */ +int +smb_nic_get_num_logical(void) +{ + char **names = NULL; + int log_ifs = 0; + int i, result = 0; + /* Get list of interface names */ + result = smb_nic_build_if_name(&names); + if (result == -1) { + syslog(LOG_ERR, "%s", "Unable to determine num interfaces"); + return (-1); + } + for (i = 0; i < result; i++) { + if (strchr(names[i], ':') != NULL) { + /* It's a logical interface */ + log_ifs++; + } + } + (void) smb_nic_clear_name_list(names, result); + return (log_ifs); +} + +/* + * Get number of aliases associated with an interface + */ +int +smb_nic_get_num_aliases(char *interface) +{ + char **names = NULL; + int aliases = 0; + int i, result = 0; + + if (interface == NULL) { + syslog(LOG_ERR, "%s", "Interface name not supplied"); + return (-1); + } + /* Get list of interface names */ + result = smb_nic_build_if_name(&names); + if (result == -1) { + syslog(LOG_ERR, "%s", "Unable to determine num interfaces"); + return (-1); + } + for (i = 0; i < result; i++) { + if (strncasecmp(interface, names[i], strlen(names[0])) == 0 && + strchr(names[i], ':') != 0) { + /* It's an alias */ + aliases++; + } + } + (void) smb_nic_clear_name_list(names, result); + if (aliases == 0) + return (1); /* Minimum of 1 for NULL assignment */ + else + return (aliases); +} + +/* + * Get the list of currently plumbed interface names. The loopback(lo0) + * port is ignored + */ +static int +smb_nic_get_list(struct if_list **list) +{ + int cnt = 0; + struct if_list *tmp, *p; + int n, s; + char *buf; + struct ifconf ifc; + register struct ifreq *ifrp = NULL; + struct ifreq ifr; + int numifs = 0; + unsigned int bufsize = 0; + + *list = NULL; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + syslog(LOG_ERR, "%s", "get_net_list: socket"); + return (-1); + } + + if (ioctl(s, SIOCGIFNUM, (char *)&numifs) < 0) { + syslog(LOG_ERR, "%s", "get number of interfaces"); + return (-1); + } + + bufsize = numifs * sizeof (struct ifreq); + buf = (char *)malloc(bufsize); + if (buf == NULL) { + syslog(LOG_ERR, "%s", "out of memory\n"); + (void) close(s); + return (-1); + } + ifc.ifc_len = bufsize; + ifc.ifc_buf = buf; + + if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) { + syslog(LOG_ERR, "%s", "Unable to get interface list\n"); + (void) close(s); + (void) free(buf); + return (-1); + } + + ifrp = ifc.ifc_req; + for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifrp++) { + /* Get the flags so that we can skip the loopback interface */ + (void) memset((char *)&ifr, '\0', sizeof (ifr)); + (void) strncpy(ifr.ifr_name, ifrp->ifr_name, + sizeof (ifr.ifr_name)); + + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { + syslog(LOG_ERR, "%s", "unable to determine flags"); + (void) close(s); + (void) free(buf); + return (-1); + } + + if (ifr.ifr_flags & IFF_LOOPBACK) + continue; + if ((ifr.ifr_flags & IFF_UP) == 0) + continue; + tmp = (struct if_list *)malloc(sizeof (struct if_list)); + if (tmp == NULL) { + syslog(LOG_ERR, "%s", "out of memory\n"); + (void) close(s); + (void) free(buf); + return (-1); + } + + tmp->next = NULL; + (void) strncpy(tmp->name, ifrp->ifr_name, sizeof (tmp->name)); + if (*list == NULL) { + *list = tmp; + } else { + for (p = *list; p->next; p = p->next) + ; + p->next = tmp; + } + cnt++; + } + (void) close(s); + (void) free(buf); + return (cnt); +} + +/* + * This will mimick the workings of ifconfig -a command. A net_cfg + * pointer will be passed, and all information will be assigned + * within this function. Memory will be assigned in this function + * also so the user doesn't have to worry about it. Freeing memory + * will be handled in a different function - smb_nic_clear_memory + */ +int +smb_nic_build_network_structures(net_cfg_t **nc, int *number) +{ + char ** names = NULL; + int res, numnics = 0; + int num_aliases = 0; + uint32_t uip; + uint64_t flags; + int i = 0; + int j = 1; + int k = 0; + struct ip_alias *list = NULL; + net_cfg_t *nc_array; + char excludestr[MAX_EXCLUDE_LIST_LEN]; + ipaddr_t exclude[SMB_PI_MAX_NETWORKS]; + int nexclude; + char *winsexclude; + + *number = 0; + numnics = smb_nic_build_if_name(&names); + nc_array = *nc = malloc(sizeof (net_cfg_t) * numnics); + if (nc_array == NULL) { + (void) smb_nic_clear_name_list(names, numnics); + return (-1); + } + bzero(nc_array, sizeof (net_cfg_t) * numnics); + + smb_config_rdlock(); + excludestr[0] = '\0'; + winsexclude = smb_config_getstr(SMB_CI_WINS_EXCL); + if (winsexclude != NULL) + (void) strlcpy(excludestr, winsexclude, sizeof (excludestr)); + smb_config_unlock(); + nexclude = smb_wins_iplist(excludestr, exclude, SMB_PI_MAX_NETWORKS); + + for (i = 0; i < numnics; i++) { + + if (strchr(names[i], ':') == NULL) { + /* Will not provide info on logical interfaces */ + + (void) memset ((*nc), 0, sizeof (net_cfg_t)); + num_aliases = smb_nic_get_num_aliases(names[i]); + if (num_aliases == -1) { + (void) smb_nic_clear_name_list(names, numnics); + free (*nc); + return (-1); + } + + (*nc)->aliases = (char **)malloc( + (sizeof (char) * IP_ABITS) * num_aliases); + if ((*nc)->aliases == NULL) { + (void) smb_nic_clear_name_list(names, numnics); + free (*nc); + return (-1); + } + (void) strncpy((*nc)->ifname, names[i], + sizeof ((*nc)->ifname)); + (*nc)->naliases = num_aliases; + + res = smb_nic_get_IP((*nc)->ifname, &uip); + if (res == -1) { /* error retrieving IP address */ + (void) smb_nic_clear_name_list(names, numnics); + free ((*nc)->aliases); + free (*nc); + return (-1); + } + (*nc)->ip = uip; + if (smb_wins_is_excluded(uip, (ulong_t *)exclude, + nexclude)) + (*nc)->exclude = B_TRUE; + res = smb_nic_get_netmask((*nc)->ifname, &uip); + if (res == -1) { /* error retrieving netmask address */ + (void) smb_nic_clear_name_list(names, numnics); + free ((*nc)->aliases); + free (*nc); + return (-1); + } + (*nc)->mask = uip; + res = smb_nic_get_broadcast((*nc)->ifname, &uip); + if (res == -1) { /* error retrieving broadcast add */ + (void) smb_nic_clear_name_list(names, numnics); + free ((*nc)->aliases); + free (*nc); + return (-1); + } + (*nc)->broadcast = uip; + res = smb_nic_get_group((*nc)->ifname, + (*nc)->groupname); + if (res == -1) { /* error retrieving group name */ + (void) smb_nic_clear_name_list(names, numnics); + free ((*nc)->aliases); + free (*nc); + return (-1); + } + res = smb_nic_flags((*nc)->ifname, &flags); + if (res == -1) { /* error retrieving flags */ + (void) smb_nic_clear_name_list(names, numnics); + free ((*nc)->aliases); + free (*nc); + return (-1); + } + (*nc)->flags = flags; + /* + * If an interface has no associated alias, the alias + * field will be set to NULL + */ + res = smb_nic_get_IP_aliases((*nc)->ifname, &list); + if (res == -1) { + (*nc)->aliases[k] = NULL; + (void) smb_nic_clear_name_list(names, numnics); + free ((*nc)->aliases); + free (*nc); + return (-1); + } + + if (res == 0) { + (*nc)->aliases[k] = NULL; + + } else { /* There will be aliases */ + + (*nc)->aliases[0] = (char *)list->name; + while (list->next != NULL) { + (*nc)->aliases[j] = + (char *)(list->next); + j++; + list = list->next; + } + } + k++; + j = 1; + (*nc)++; /* increment pointer */ + } + } /* end for */ + + *nc = nc_array; + *number = k; + (void) smb_nic_clear_name_list(names, numnics); + return (0); +} + +/* + * Return a space separated list of interface names depending on specified + * flags. Either flags argument can be set to 0 if the caller chooses. + * Returns NULL if no interfaces match the passed flags + * flags_on: flags which must be on in each interface returned + * flags_off : flags which must be off in each interface returned + */ +char * +smb_nic_get_ifnames(int flags_on, int flags_off) +{ + struct ifconf ifc; + int numifs, i, sfd; + char *ifnames; + + + sfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sfd == -1) + return (NULL); + + if ((ioctl(sfd, SIOCGIFNUM, &numifs) == -1) || (numifs <= 0)) { + (void) close(sfd); + return (NULL); + } + + ifnames = malloc(numifs * (LIFNAMSIZ + 1)); + if (ifnames == NULL) { + return (NULL); + } + ifc.ifc_len = (numifs * sizeof (struct ifreq)); + ifc.ifc_req = malloc(numifs * sizeof (struct ifreq)); + if (ifc.ifc_req == NULL) { + free(ifnames); + return (NULL); + } + + if (ioctl(sfd, SIOCGIFCONF, &ifc) == -1) { + (void) close(sfd); + free(ifnames); + free(ifc.ifc_req); + return (NULL); + } + + for (i = 0; i < numifs; i++) { + if (ioctl(sfd, SIOCGIFFLAGS, &ifc.ifc_req[i]) == 0) { + if ((ifc.ifc_req[i].ifr_flags & + (flags_on | flags_off)) != flags_on) { + continue; + } + } + + (void) strcat(ifnames, ifc.ifc_req[i].ifr_name); + (void) strcat(ifnames, " "); + } + + if (strlen(ifnames) > 1) + ifnames[strlen(ifnames) - 1] = '\0'; + + (void) close(sfd); + free(ifc.ifc_req); + + return (ifnames); +} + +/* + * Function to determine if passed address is of form a.b.c.d. + */ +int +smb_nic_validate_ip_address(char *IP) +{ + in_addr_t addr; + if ((int)(addr = inet_addr(IP)) == -1) { + syslog(LOG_ERR, "%s", "IP-address must be" + " of the form a.b.c.d"); + return (addr); + } + else + return (0); + +} + +/* + * Get flags associated with if + * -1 means there was an error retrieving the data + * 0 success + */ +int +smb_nic_flags(char *interface, uint64_t *flag) +{ + struct lifreq lifrr; + int sfd; + + sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + + if (sfd < 0) { + syslog(LOG_ERR, "%s", "smb_get_nic_flags: socket open failed"); + return (-1); + } + + (void) strncpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name)); + + if (ioctl(sfd, SIOCGLIFFLAGS, &lifrr) < 0) { + syslog(LOG_ERR, "%s", "smb_get_nic_flags: get flags failed"); + (void) close(sfd); + return (-1); + } + + (void) close(sfd); + if (flag != NULL) + *flag = lifrr.lifr_flags; + return (0); +} + +/* + * The following list is taken from if.h. The function takes the + * given interface name, and the passed flag(s), and returns true if + * the flag is associated with the interface, and false if not. + * + * IFF_UP interface is up + * IFF_BROADCAST broadcast address valid + * IFF_LOOPBACK is a loopback net + * IFF_POINTOPOINT interface is point-to-point link + * IFF_RUNNING resources allocated + * IFF_MULTICAST supports multicast + * IFF_MULTI_BCAST multicast using broadcast address + * IFF_UNNUMBERED non-unique address + * IFF_DHCPRUNNING DHCP controls this interface + * IFF_PRIVATE do not advertise + * IFF_DEPRECATED interface address deprecated + * IFF_ANYCAST Anycast address + * IFF_IPV4 IPv4 interface + * IFF_IPV6 IPv6 interface + * IFF_NOFAILOVER Don't failover on NIC failure + * IFF_FAILED NIC has failed + * IFF_STANDBY Standby NIC to be used on failures + * IFF_OFFLINE NIC has been offlined + * -1 means there was an error retrieving the data + * 0 indicates false - the flag isn't associated + * 1 indicates true - the flag is associated + */ +int +smb_nic_status(char *interface, uint64_t flag) +{ + struct lifreq lifrr; + int sfd; + + sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + + if (sfd < 0) { + syslog(LOG_ERR, "%s", "nic_status: socket open failed"); + return (-1); + } + + (void) strncpy(lifrr.lifr_name, interface, sizeof (lifrr.lifr_name)); + + if (ioctl(sfd, SIOCGLIFFLAGS, &lifrr) < 0) { + syslog(LOG_ERR, "%s", "nic_status: get flags failed"); + (void) close(sfd); + return (-1); + } + + if (lifrr.lifr_flags & flag) { + (void) close(sfd); + return (1); /* associated */ + } else { + (void) close(sfd); + return (0); /* not associated */ + } +} + +/* + * Free allocated memory for net_cfg structures. Takes number of allocated + * structures as argument also + */ +int +smb_nic_clear_niclist(net_cfg_t *niclist, int amount) +{ + int i, j = 0; + + if (niclist == NULL) + return (-1); + for (i = 0; i < amount; i++) { + while (niclist[i].aliases[j] != NULL) { + free(niclist[i].aliases[j]); + j++; + } + free(niclist[i].aliases); + j = 0; + } + free(niclist); + + return (0); +} + +int +smb_nic_free_niclist(net_cfg_list_t *niclist) +{ + return (smb_nic_clear_niclist(niclist->net_cfg_list, + niclist->net_cfg_cnt)); +} + +/* + * Free allocated memory for names lists. Takes number of allocated + * pointers as argument also + */ +int +smb_nic_clear_name_list(char **names, int amount) +{ + int i; + + for (i = 0; i < amount; i++) { + free(names[i]); + } + + free(names); + return (0); +} + +/* Free allocated memory for names lists. */ + +static void +smb_nic_clear_if_list(struct if_list *iflist) +{ + struct if_list *tmp; + + if (iflist == NULL) + return; + for (; iflist != NULL; iflist = tmp) { + tmp = iflist->next; + free(iflist); + } +} + +/* Free allocated memory for alias lists. */ +int +smb_nic_clear_ip_alias(struct ip_alias *iplist) +{ + struct ip_alias *tmp; + + for (; iplist != NULL; iplist = tmp) { + tmp = iplist->next; + free(iplist); + } + + return (0); +} + +/* + * smb_nic_lock + * + * Lock the nic table + */ +void +smb_nic_lock(void) +{ + (void) pthread_mutex_lock(&smb_nic_mutex); +} + +/* + * smb_nic_unlock + * + * Unlock the nic table. + * + * This function MUST be called after lock + */ +void +smb_nic_unlock(void) +{ + (void) pthread_mutex_unlock(&smb_nic_mutex); +} + +int +smb_nic_init() +{ + smb_nic_lock(); + smb_nic_list.net_cfg_cnt = 0; + (void) smb_nic_build_network_structures(&smb_nic_list.net_cfg_list, + &smb_nic_list.net_cfg_cnt); + smb_nic_unlock(); + return (0); +} + +/* + * Initialize interface list. + */ +void +smb_nic_build_info(void) +{ + smb_nic_lock(); + if (smb_nic_list.net_cfg_list) { + (void) smb_nic_free_niclist(&smb_nic_list); + smb_nic_list.net_cfg_list = NULL; + } + smb_nic_list.net_cfg_cnt = 0; + (void) smb_nic_build_network_structures(&smb_nic_list.net_cfg_list, + &smb_nic_list.net_cfg_cnt); + if (smb_nic_list.net_cfg_cnt == 0) { + syslog(LOG_ERR, "smb: No network interfaces are configured " + "smb server may not function properly"); + } + smb_nic_unlock(); +} + +/* + * Get number of interfaces. + */ +int +smb_nic_get_num(void) +{ + int sz; + smb_nic_lock(); + sz = smb_nic_list.net_cfg_cnt; + smb_nic_unlock(); + return (sz); +} + +/* + * Get if by index + * Returns: NULL if not found. + */ +net_cfg_t * +smb_nic_get_byind(int ind, net_cfg_t *cfg) +{ + if (cfg == NULL) + return (cfg); + smb_nic_lock(); + if (ind > smb_nic_list.net_cfg_cnt) { + smb_nic_unlock(); + return (NULL); + } + bcopy(&smb_nic_list.net_cfg_list[ind], cfg, sizeof (net_cfg_t)); + smb_nic_unlock(); + return (cfg); +} + +/* + * Get if by subnet + * Returns: NULL if not found. + */ +net_cfg_t * +smb_nic_get_bysubnet(uint32_t ipaddr, net_cfg_t *cfg) +{ + int i; + net_cfg_t *tcfg; + + if (cfg == NULL) + return (cfg); + smb_nic_lock(); + bzero(cfg, sizeof (net_cfg_t)); + for (i = 0; i < smb_nic_list.net_cfg_cnt; i++) { + tcfg = &smb_nic_list.net_cfg_list[i]; + if ((ipaddr & tcfg->mask) == + (tcfg->ip & tcfg->mask)) { + bcopy(tcfg, cfg, sizeof (net_cfg_t)); + smb_nic_unlock(); + return (cfg); + } + } + smb_nic_unlock(); + return (NULL); +} + +/* + * Get if by ip. + * Returns: NULL if not found. + */ +net_cfg_t * +smb_nic_get_byip(uint32_t ipaddr, net_cfg_t *cfg) +{ + int i; + net_cfg_t *tcfg; + + if (cfg == NULL) + return (cfg); + smb_nic_lock(); + bzero(cfg, sizeof (net_cfg_t)); + for (i = 0; i < smb_nic_list.net_cfg_cnt; i++) { + tcfg = &smb_nic_list.net_cfg_list[i]; + if (ipaddr == tcfg->ip) { + bcopy(tcfg, cfg, sizeof (net_cfg_t)); + smb_nic_unlock(); + return (cfg); + } + } + smb_nic_unlock(); + return (NULL); +} diff --git a/usr/src/lib/smbsrv/libsmbns/i386/Makefile b/usr/src/lib/smbsrv/libsmbns/i386/Makefile new file mode 100644 index 000000000000..f91f0270e9ad --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/i386/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/smbsrv/libsmbns/sparc/Makefile b/usr/src/lib/smbsrv/libsmbns/sparc/Makefile new file mode 100644 index 000000000000..f91f0270e9ad --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/sparc/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/smbsrv/libsmbns/sparcv9/Makefile b/usr/src/lib/smbsrv/libsmbns/sparcv9/Makefile new file mode 100644 index 000000000000..a2f97019c815 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbns/sparcv9/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/smbsrv/libsmbrdr/Makefile b/usr/src/lib/smbsrv/libsmbrdr/Makefile new file mode 100644 index 000000000000..6b376227e4c3 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +HDRS= libsmbrdr.h + +include ../Makefile.smbsrv diff --git a/usr/src/lib/smbsrv/libsmbrdr/Makefile.com b/usr/src/lib/smbsrv/libsmbrdr/Makefile.com new file mode 100644 index 000000000000..822331f26b3c --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/Makefile.com @@ -0,0 +1,53 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +LIBRARY = libsmbrdr.a +VERS = .1 + +OBJS_COMMON = \ + smbrdr_ipc_util.o \ + smbrdr_lib.o \ + smbrdr_logon.o \ + smbrdr_netbios.o \ + smbrdr_netuse.o \ + smbrdr_read_andx.o \ + smbrdr_rpcpipe.o \ + smbrdr_session.o \ + smbrdr_transact.o + +OBJECTS= $(OBJS_COMMON) $(OBJS_SHARED) + +include ../../../Makefile.lib +include ../../Makefile.lib + +LDLIBS += -lsmb -lnsl -lsocket -lc + +SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \ + $(OBJS_SHARED:%.o=$(SRC)/common/smbsrv/%.c) + +include ../../Makefile.targ +include ../../../Makefile.targ diff --git a/usr/src/lib/smbsrv/libsmbrdr/amd64/Makefile b/usr/src/lib/smbsrv/libsmbrdr/amd64/Makefile new file mode 100644 index 000000000000..a2f97019c815 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/amd64/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/libsmbrdr.h b/usr/src/lib/smbsrv/libsmbrdr/common/libsmbrdr.h new file mode 100644 index 000000000000..846ac9d306ac --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/common/libsmbrdr.h @@ -0,0 +1,97 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBSMBRDR_H +#define _LIBSMBRDR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Redirector IPC functions + * + * The following functions are required by the mlsvc_validate_user to + * apply new authentication information for the authenticated IPC, rollback + * or commit the changes to the original authentication information. + */ +extern void smbrdr_ipc_set(char *, unsigned char *); +extern void smbrdr_ipc_commit(void); +extern void smbrdr_ipc_rollback(void); +extern int smbrdr_ipc_skip_lsa_query(void); +extern int smbrdr_ipc_get_mode(void); +extern void smbrdr_ipc_save_mode(char *val); +extern unsigned smbrdr_ipc_get_flags(void); +extern void smbrdr_ipc_set_fallback(void); +extern void smbrdr_ipc_unset_fallback(void); +extern int smbrdr_ipc_is_fallback(void); + +/* + * Functions for obtaining the resource domain administrator credentials. + */ +extern char *smbrdr_ipc_get_user(void); +extern char *smbrdr_ipc_get_passwd(void); +extern int smbrdr_ipc_is_valid(void); + + +/* Redirector LOGON functions */ +extern int mlsvc_anonymous_logon(char *, char *, char **); +extern int mlsvc_user_logon(char *, char *, char *, char *); +extern int mlsvc_admin_logon(char *, char *); + +extern int smbrdr_rpc_readx(int, char *, int); + + +/* Redirector rpcpipe functions */ +extern int mlsvc_open_pipe(char *, char *, char *, char *); +extern int mlsvc_close_pipe(int); + + +/* Redirector session functions */ +extern void smbrdr_init(void); +extern int mlsvc_locate_domain_controller(char *); +extern int mlsvc_session_native_values(int, int *, int *, int *); +extern void mlsvc_check_sessions(void); +extern int mlsvc_echo(char *); +extern void mlsvc_disconnect(char *); + + +extern int smbrdr_rpc_transact(int, char *, int, char *, int); + + +/* DEBUG functions */ +extern void smbrdr_dump_ofiles(void); +extern void smbrdr_dump_sessions(void); +extern void smbrdr_dump_netuse(); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBSMBRDR_H */ diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/llib-lsmbrdr b/usr/src/lib/smbsrv/libsmbrdr/common/llib-lsmbrdr new file mode 100644 index 000000000000..d238202484c7 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/common/llib-lsmbrdr @@ -0,0 +1,31 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/*LINTLIBRARY*/ +/*PROTOLIB1*/ + +#include diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/mapfile-vers b/usr/src/lib/smbsrv/libsmbrdr/common/mapfile-vers new file mode 100644 index 000000000000..0ea435ee5536 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/common/mapfile-vers @@ -0,0 +1,63 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +SUNWprivate { + global: + mlsvc_admin_logon; + mlsvc_anonymous_logon; + mlsvc_check_sessions; + mlsvc_close_pipe; + mlsvc_disconnect; + mlsvc_echo; + mlsvc_install_pdc_cb; + mlsvc_locate_domain_controller; + mlsvc_open_pipe; + mlsvc_session_native_values; + mlsvc_user_getauth; + mlsvc_user_logon; + smbrdr_dump_netuse; + smbrdr_dump_ofiles; + smbrdr_dump_sessions; + smbrdr_init; + smbrdr_ipc_get_mode; + smbrdr_ipc_commit; + smbrdr_ipc_get_flags; + smbrdr_ipc_get_user; + smbrdr_ipc_rollback; + smbrdr_ipc_set; + smbrdr_ipc_skip_lsa_query; + smbrdr_ipc_is_fallback; + smbrdr_ipc_is_valid; + smbrdr_ipc_get_passwd; + smbrdr_ipc_save_mode; + smbrdr_ipc_set_fallback; + smbrdr_ipc_unset_fallback; + smbrdr_rpc_readx; + smbrdr_rpc_transact; + local: + *; +}; diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr.h b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr.h new file mode 100644 index 000000000000..35db51adc626 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr.h @@ -0,0 +1,241 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBRDR_H_ +#define _SMBRDR_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define SMBRDR_REQ_BUFSZ 4096 + +#define MAX_ACCOUNT_NAME 32 +#define MAX_SHARE_NAME 32 +#define MAX_SCOPE_NAME 64 +#define MAX_FILE_PATH 128 + +/* + * The number of shares and pipes is limited to 48 based on the note + * below. This really shouldn't cause a problem because we always + * our shares and named pipes are always opened and closed round every + * RPC transaction. This also tends to limit the number of active + * logons because we (currently) need two named pipes per logon. + * + * Q141709 Limit of 49 named pipe connections from a single workstation. + * If a named pipe server creates more than 49 distincly named pipes, a + * single client cannot connect more than 49 pipes on the named pipe + * server. Chapter 4, p113. Network Programming for Microsoft Windows + * Anthony Jones and Jim Ohlund, Microsoft Press, ISBN: 0-7356-0560-2 + */ +#define N_NETUSE_TABLE 48 +#define N_OFILE_TABLE 48 + +/* + * Logon's states + */ +#define SDB_LSTATE_START 0 +#define SDB_LSTATE_INIT 1 +#define SDB_LSTATE_LOGGING_OFF 2 +#define SDB_LSTATE_SETUP 3 + +#define SDB_LOGON_NONE 0 +#define SDB_LOGON_GUEST 1 +#define SDB_LOGON_ANONYMOUS 2 +#define SDB_LOGON_USER 3 + +typedef struct sdb_logon { + struct sdb_session *session; + char username[MAX_ACCOUNT_NAME]; + unsigned short uid; + unsigned int type; + unsigned short state; + smb_auth_info_t auth; +} sdb_logon_t; + +/* + * Session's states + * + * SDB_SSTATE_START ready to be used + * SDB_SSTATE_INIT initialized + * SDB_SSTATE_STALE lost transport connection + * SDB_SSTATE_DISCONNECTING disconnecting: logoff the user + * disconnect trees, close files + * SDB_SSTATE_CLEANING was in STALE state now just + * cleaning up + * SDB_SSTATE_CONNECTED got transport connection + * SDB_SSTATE_NEGOTIATED did SMB negotiate + */ +#define SDB_SSTATE_START 0 +#define SDB_SSTATE_INIT 1 +#define SDB_SSTATE_STALE 2 +#define SDB_SSTATE_DISCONNECTING 3 +#define SDB_SSTATE_CLEANING 4 +#define SDB_SSTATE_CONNECTED 5 +#define SDB_SSTATE_NEGOTIATED 6 + +#define SDB_SLCK_READ 1 +#define SDB_SLCK_WRITE 2 + +struct sdb_session { + smb_ntdomain_t di; + char scope[SMB_PI_MAX_SCOPE]; + char native_os[SMB_PI_MAX_NATIVE_OS]; + char native_lanman[SMB_PI_MAX_LANMAN]; + int sock; + short port; + unsigned short secmode; + uint32_t sesskey; + uint32_t challenge_len; + unsigned char challenge_key[32]; + unsigned char smb_flags; + unsigned short smb_flags2; + unsigned short vc; + uint32_t remote_caps; + unsigned short state; + unsigned int sid; /* session id */ + int remote_os; + int remote_lm; + int pdc_type; + smb_sign_ctx_t sign_ctx; + sdb_logon_t logon; + rwlock_t rwl; +}; + +/* + * Netuse's states + */ +#define SDB_NSTATE_START 0 +#define SDB_NSTATE_INIT 1 +#define SDB_NSTATE_DISCONNECTING 2 +#define SDB_NSTATE_CONNECTED 3 + +struct sdb_netuse { + struct sdb_session *session; + unsigned short state; + int letter; /* local identity */ + unsigned int sid; + unsigned short uid; + unsigned short tid; /* remote identity */ + char share[MAX_SHARE_NAME]; + mutex_t mtx; +}; + +/* + * Ofile's states + */ +#define SDB_FSTATE_START 0 +#define SDB_FSTATE_INIT 1 +#define SDB_FSTATE_CLOSING 2 +#define SDB_FSTATE_OPEN 3 + +struct sdb_ofile { + struct sdb_session *session; + struct sdb_netuse *netuse; + unsigned short state; + unsigned int sid; + unsigned short uid; + unsigned short tid; + unsigned short fid; /* remote identity */ + char path[MAX_FILE_PATH]; + mutex_t mtx; +}; + +typedef struct smbrdr_handle { + unsigned char *srh_buf; + smb_msgbuf_t srh_mbuf; + unsigned int srh_mbflags; + unsigned char srh_cmd; + struct sdb_session *srh_session; + struct sdb_logon *srh_user; + struct sdb_netuse *srh_tree; +} smbrdr_handle_t; + +/* + * smbrdr_netbios.c + */ +void nb_lock(void); +void nb_unlock(void); +void nb_close(int); +int nb_keep_alive(int); + +int nb_send(int, unsigned char *, unsigned); +int nb_rcv(int, unsigned char *, unsigned, long); +int nb_exchange(int, unsigned char *, unsigned, + unsigned char *, unsigned, long); +int nb_session_request(int, char *, char *, char *, char *); + +/* + * smbrdr_session.c + */ +int smbrdr_negotiate(char *); +struct sdb_session *smbrdr_session_lock(char *, char *, int); +void smbrdr_session_unlock(struct sdb_session *); + +/* + * smbrdr_logon.c + */ +int smbrdr_smb_logoff(struct sdb_logon *); + +/* smbrdr_netuse.c */ +void smbrdr_netuse_logoff(unsigned short); +struct sdb_netuse *smbrdr_netuse_get(int); +unsigned short mlsvc_tree_connect(char *, char *, char *); +int smbrdr_tree_disconnect(unsigned short); +void smbrdr_netuse_put(struct sdb_netuse *); + +/* + * smbrdr_rpcpipe.c + */ +void smbrdr_ofile_end_of_share(unsigned short); +struct sdb_ofile *smbrdr_ofile_get(int); +void smbrdr_ofile_put(struct sdb_ofile *); + +/* smbrdr_lib.c */ +DWORD smbrdr_request_init(smbrdr_handle_t *, unsigned char, + struct sdb_session *, struct sdb_logon *, struct sdb_netuse *); +DWORD smbrdr_send(smbrdr_handle_t *); +DWORD smbrdr_rcv(smbrdr_handle_t *, int); +DWORD smbrdr_exchange(smbrdr_handle_t *, smb_hdr_t *, long); +void smbrdr_handle_free(smbrdr_handle_t *); +int smbrdr_sign_init(struct sdb_session *, struct sdb_logon *); +int smbrdr_sign_fini(struct sdb_session *); +int smbrdr_sign_unset_key(struct sdb_session *); + +void smbrdr_lock_transport(void); +void smbrdr_unlock_transport(void); + +#endif /* _SMBRDR_H_ */ diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_ipc_util.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_ipc_util.c new file mode 100644 index 000000000000..908a43986435 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_ipc_util.c @@ -0,0 +1,382 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * The IPC connection information is encapsulated within SMB Redirector. + * Utility functions are defined here to allow other modules to get and + * set the ipc configuration, as well as, to rollback or commit the + * changes to the original authentication information. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +/* + * The binary NTLM hash is 16 bytes. When it is converted to hexidecimal, + * it will be at most twice as long. + */ +#define SMBRDR_IPC_HEX_PASSWD_MAXLEN (SMBAUTH_HASH_SZ * 2) + 1 +#define SMBRDR_IPC_GETDOMAIN_TIMEOUT 10000 + +static rwlock_t smbrdr_ipc_lock; +static smbrdr_ipc_t ipc_info; +static smbrdr_ipc_t orig_ipc_info; + +/* + * smbrdr_ipc_init + * + * Get system configuration regarding IPC connection + * credentials and initialize related variables. + * This function will normally be called at startup + * (i.e. at the time smbrdr gets loaded). + */ +void +smbrdr_ipc_init(void) +{ + char *p; + + bzero(&ipc_info, sizeof (smbrdr_ipc_t)); + bzero(&orig_ipc_info, sizeof (smbrdr_ipc_t)); + + smb_config_rdlock(); + p = smb_config_getstr(SMB_CI_RDR_IPCMODE); + + if (!strncasecmp(p, IPC_MODE_AUTH, IPC_MODE_STRLEN)) { + ipc_info.mode = MLSVC_IPC_ADMIN; + + p = smb_config_getstr(SMB_CI_RDR_IPCUSER); + if (p) + (void) strlcpy(ipc_info.user, p, + MLSVC_ACCOUNT_NAME_MAX); + else + syslog(LOG_WARNING, "smbrdr: (ipc) no admin user name"); + + p = smb_config_get(SMB_CI_RDR_IPCPWD); + if (p) { + if (strlen(p) != SMBRDR_IPC_HEX_PASSWD_MAXLEN - 1) { + *ipc_info.passwd = 0; + syslog(LOG_WARNING, + "smbrdr: (ipc) invalid admin password"); + } else { + (void) hextobin(p, + SMBRDR_IPC_HEX_PASSWD_MAXLEN - 1, + ipc_info.passwd, SMBAUTH_HASH_SZ); + } + } else { + *ipc_info.passwd = 0; + syslog(LOG_WARNING, "smbrdr: (ipc) no admin password"); + } + + } else { + if (!strcasecmp(p, IPC_MODE_FALLBACK_ANON)) + ipc_info.flags |= IPC_FLG_FALLBACK_ANON; + + ipc_info.mode = MLSVC_IPC_ANON; + (void) strlcpy(ipc_info.user, MLSVC_ANON_USER, + MLSVC_ACCOUNT_NAME_MAX); + *ipc_info.passwd = 0; + } + smb_config_unlock(); +} + +/* + * smbrdr_ipc_set + * + * The given username and password hash will be applied to the + * ipc_info which will be used by mlsvc_validate_user(). + * + * If mlsvc_validate_user() succeeds, the calling function is responsible + * for invoking smbrdr_ipc_commit() for updating the environment + * variables. Otherwise, it should invoke smbrdr_ipc_rollback() to restore + * the previous credentials. + */ +void +smbrdr_ipc_set(char *plain_user, unsigned char *passwd_hash) +{ + (void) rw_wrlock(&smbrdr_ipc_lock); + if (ipc_info.flags & IPC_FLG_FALLBACK_ANON) + ipc_info.mode = MLSVC_IPC_ADMIN; + + (void) strlcpy(ipc_info.user, plain_user, sizeof (ipc_info.user)); + (void) memcpy(ipc_info.passwd, passwd_hash, SMBAUTH_HASH_SZ); + ipc_info.flags |= IPC_FLG_NEED_VERIFY; + (void) rw_unlock(&smbrdr_ipc_lock); + +} + +/* + * smbrdr_ipc_commit + * + * Save the new admin credentials as environment variables. + * The binary NTLM password hash is first converted to a + * hex string before storing in the environment variable. + * + * The credentials also saved to the original IPC info as + * rollback data in case the join domain process + * fails in the future. + */ +void +smbrdr_ipc_commit() +{ + unsigned char hexpass[SMBRDR_IPC_HEX_PASSWD_MAXLEN]; + + (void) rw_wrlock(&smbrdr_ipc_lock); + smb_config_wrlock(); + (void) smb_config_set(SMB_CI_RDR_IPCUSER, ipc_info.user); + (void) bintohex(ipc_info.passwd, sizeof (ipc_info.passwd), + (char *)hexpass, sizeof (hexpass)); + hexpass[SMBRDR_IPC_HEX_PASSWD_MAXLEN - 1] = 0; + (void) smb_config_set(SMB_CI_RDR_IPCPWD, (char *)hexpass); + + ipc_info.flags &= ~IPC_FLG_NEED_VERIFY; + + if (ipc_info.flags & IPC_FLG_FALLBACK_ANON) { + ipc_info.flags &= ~IPC_FLG_FALLBACK_ANON; + ipc_info.mode = MLSVC_IPC_ADMIN; + (void) smb_config_set(SMB_CI_RDR_IPCMODE, IPC_MODE_AUTH); + syslog(LOG_DEBUG, "smbrdr: (ipc) Authenticated IPC " + "connection has been restored"); + } + + (void) memcpy(&orig_ipc_info, &ipc_info, sizeof (smbrdr_ipc_t)); + smb_config_unlock(); + (void) rw_unlock(&smbrdr_ipc_lock); +} + +/* + * smbrdr_ipc_rollback + * + * Restore the original credentials + */ +void +smbrdr_ipc_rollback() +{ + (void) rw_wrlock(&smbrdr_ipc_lock); + (void) strlcpy(ipc_info.user, orig_ipc_info.user, + sizeof (ipc_info.user)); + (void) memcpy(ipc_info.passwd, orig_ipc_info.passwd, + sizeof (ipc_info.passwd)); + + ipc_info.flags &= ~IPC_FLG_NEED_VERIFY; + + if (ipc_info.flags & IPC_FLG_FALLBACK_ANON) + ipc_info.mode = MLSVC_IPC_ANON; + (void) rw_unlock(&smbrdr_ipc_lock); +} + +/* + * Get & Set functions + */ +int +smbrdr_ipc_get_mode() +{ + int mode; + + (void) rw_rdlock(&smbrdr_ipc_lock); + mode = ipc_info.mode; + (void) rw_unlock(&smbrdr_ipc_lock); + + return (mode); +} + +char * +smbrdr_ipc_get_user() +{ + char *user; + + (void) rw_rdlock(&smbrdr_ipc_lock); + user = ipc_info.user; + (void) rw_unlock(&smbrdr_ipc_lock); + return (user); +} + +char * +smbrdr_ipc_get_passwd() +{ + char *passwd; + + (void) rw_rdlock(&smbrdr_ipc_lock); + passwd = ipc_info.passwd; + (void) rw_unlock(&smbrdr_ipc_lock); + return (passwd); +} + +unsigned +smbrdr_ipc_get_flags() +{ + unsigned flags; + + (void) rw_rdlock(&smbrdr_ipc_lock); + flags = ipc_info.flags; + (void) rw_unlock(&smbrdr_ipc_lock); + return (flags); +} + +void +smbrdr_ipc_set_fallback() +{ + (void) rw_wrlock(&smbrdr_ipc_lock); + ipc_info.flags |= IPC_FLG_FALLBACK_ANON; + (void) rw_unlock(&smbrdr_ipc_lock); +} + +void +smbrdr_ipc_unset_fallback() +{ + (void) rw_wrlock(&smbrdr_ipc_lock); + ipc_info.flags &= ~IPC_FLG_FALLBACK_ANON; + (void) rw_unlock(&smbrdr_ipc_lock); +} + +/* + * Whether the smbrdr.ipc.mode is set to fallback,anon or not + */ +int +smbrdr_ipc_is_fallback() +{ + int is_fallback; + + smb_config_rdlock(); + is_fallback = (!strcasecmp(smb_config_getstr(SMB_CI_RDR_IPCMODE), + IPC_MODE_FALLBACK_ANON) ? 1 : 0); + smb_config_unlock(); + + return (is_fallback); +} + +/* + * smbrdr_ipc_save_mode + * + * Set the SMBRDR_IPC_MODE_ENV variable and update the + * IPC mode of the cache. + */ +void +smbrdr_ipc_save_mode(char *val) +{ + (void) rw_wrlock(&smbrdr_ipc_lock); + smb_config_wrlock(); + (void) smb_config_set(SMB_CI_RDR_IPCMODE, val); + ipc_info.mode = !strncasecmp(val, IPC_MODE_AUTH, IPC_MODE_STRLEN) + ? MLSVC_IPC_ADMIN : MLSVC_IPC_ANON; + smb_config_unlock(); + (void) rw_unlock(&smbrdr_ipc_lock); +} + +/* + * smbrdr_ipc_skip_lsa_query + * + * Determine whether LSA monitor should skip the LSA query due to the + * incomplete authentication information if IPC is configured to be + * authenticated. + */ +int +smbrdr_ipc_skip_lsa_query() +{ + char *user, *pwd; + + if (ipc_info.mode != MLSVC_IPC_ADMIN) + return (0); + + smb_config_rdlock(); + user = smb_config_get(SMB_CI_RDR_IPCUSER); + pwd = smb_config_get(SMB_CI_RDR_IPCPWD); + smb_config_unlock(); + if ((user == NULL) && pwd) + return (1); + + (void) rw_rdlock(&smbrdr_ipc_lock); + user = ipc_info.user; + pwd = ipc_info.passwd; + (void) rw_unlock(&smbrdr_ipc_lock); + + return (!(*user && *pwd)); +} + +static char * +smbrdr_ipc_modestr(int mode) +{ + switch (mode) { + case MLSVC_IPC_ANON: + return ("Anonymous"); + + case MLSVC_IPC_ADMIN: + return ("Authenticated"); + + default: + return ("Unknown"); + } +} + +/* + * For debugging purposes only. + */ +void +smbrdr_ipc_loginfo() +{ + smbrdr_ipc_t tmp; + smbrdr_ipc_t tmporg; + + (void) rw_rdlock(&smbrdr_ipc_lock); + (void) memcpy(&tmp, &ipc_info, sizeof (smbrdr_ipc_t)); + (void) memcpy(&tmporg, &orig_ipc_info, sizeof (smbrdr_ipc_t)); + (void) rw_unlock(&smbrdr_ipc_lock); + + syslog(LOG_DEBUG, "smbrdr: current IPC info:"); + syslog(LOG_DEBUG, "\t%s (user=%s, flags:0x%X)", + smbrdr_ipc_modestr(tmp.mode), tmp.user, tmp.flags); + + syslog(LOG_DEBUG, "smbrdr: original IPC info:"); + syslog(LOG_DEBUG, "\t%s (user=%s, flags:0x%X)", + smbrdr_ipc_modestr(tmporg.mode), tmporg.user, tmporg.flags); +} + +/* + * smbrdr_ipc_is_valid + * + * Determine whether the ipc_info has been validated or not. + * + */ +int +smbrdr_ipc_is_valid() +{ + int isvalid; + + (void) rw_rdlock(&smbrdr_ipc_lock); + isvalid = (ipc_info.flags & IPC_FLG_NEED_VERIFY) ? 0 : 1; + (void) rw_unlock(&smbrdr_ipc_lock); + + return (isvalid); +} diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_ipc_util.h b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_ipc_util.h new file mode 100644 index 000000000000..a967e91b8628 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_ipc_util.h @@ -0,0 +1,85 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_IPC_UTIL_H +#define _SMBSRV_IPC_UTIL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file defines the data structure for the IPC connection and utility + * function prototypes. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SMBRDR_IPC_MODE_ENV "smbrdr.ipc.mode" +#define SMBRDR_IPC_USER_ENV "smbrdr.ipc.user" +#define SMBRDR_IPC_PASSWD_ENV "smbrdr.ipc.passwd" + +#define IPC_MODE_STRLEN 4 +#define IPC_MODE_ANON "anon" +#define IPC_MODE_AUTH "auth" +#define IPC_MODE_FALLBACK_ANON "fallback,anon" + +#define IPC_FLG_FALLBACK_ANON 0x00000001 +#define IPC_FLG_NEED_VERIFY 0x00000002 + +/* + * smbrdr_ipc_t + * + * This structure contains information regarding the IPC configuration, + * as well as, the authentication info needed for connecting to the + * IPC$ share if the IPC connection is configured to be authenticated. + * + * IPC connection to the Primary Domain Controller [PDC] can be + * configured to be either anonymous or authenticated. Therefore, + * the IPC mode will be set to either one of the following values: + * MLSVC_IPC_ANON + * MLSVC_IPC_ADMIN + * + * The IPC_FLG_FALLBACK_ANON can be set in flags field to indicate whether + * a fallback from authenticated IPC to anonymous IPC has occurred. This + * flag will be unset once the join domain operation succeeds. + */ +typedef struct { + int mode; + char user[MLSVC_ACCOUNT_NAME_MAX]; + char passwd[SMBAUTH_HASH_SZ]; + unsigned flags; +} smbrdr_ipc_t; + + +void smbrdr_ipc_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_IPC_UTIL_H */ diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_lib.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_lib.c new file mode 100644 index 000000000000..ac4743548c51 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_lib.c @@ -0,0 +1,592 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file provides some common functionality for SMB Redirector + * module. + */ + +#include +#include +#include +#include +#include + +static DWORD smbrdr_handle_setup(smbrdr_handle_t *srh, unsigned char cmd, + struct sdb_session *session, struct sdb_logon *logon, + struct sdb_netuse *netuse); + +static int smbrdr_hdr_setup(smbrdr_handle_t *srh); + +static DWORD smbrdr_hdr_process(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr); + +static int smbrdr_sign(smb_sign_ctx_t *sign_ctx, smb_msgbuf_t *mb); +static int smbrdr_sign_chk(smb_sign_ctx_t *sign_ctx, smb_msgbuf_t *mb, + unsigned char *signature); + +void smbrdr_lock_transport() { nb_lock(); } +void smbrdr_unlock_transport() { nb_unlock(); } + +/* + * smbrdr_request_init + * + * Setup a handle with given information and then + * setup a SMB header structure. + * + * Returns: + * + * NT_STATUS_NO_MEMORY no memory for creating request + * NT_STATUS_INTERNAL_ERROR header encode failed or crypto failed + * NT_STATUS_SUCCESS successful + */ +DWORD +smbrdr_request_init(smbrdr_handle_t *srh, + unsigned char cmd, + struct sdb_session *session, + struct sdb_logon *logon, + struct sdb_netuse *netuse) +{ + DWORD status; + + status = smbrdr_handle_setup(srh, cmd, session, logon, netuse); + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_DEBUG, "Smbrdr[%d]: initialization failed", cmd); + return (status); + } + + if (smbrdr_hdr_setup(srh) < SMB_HEADER_LEN) { + syslog(LOG_DEBUG, "Smbrdr[%d]: cannot setup header", cmd); + smbrdr_handle_free(srh); + return (NT_STATUS_INTERNAL_ERROR); + } + + return (NT_STATUS_SUCCESS); +} + +/* + * smbrdr_send + * + * Send the SMB packet pointed by the given handle over + * network. + * + * Returns: + * + * NT_STATUS_INTERNAL_ERROR crypto framework failure + * NT_STATUS_UNEXPECTED_NETWORK_ERROR send failed + * NT_STATUS_SUCCESS successful + */ +DWORD +smbrdr_send(smbrdr_handle_t *srh) +{ + int rc; + + if (smbrdr_sign(&srh->srh_session->sign_ctx, &srh->srh_mbuf) != + SMBAUTH_SUCCESS) + return (NT_STATUS_INTERNAL_ERROR); + + rc = nb_send(srh->srh_session->sock, srh->srh_buf, + smb_msgbuf_used(&srh->srh_mbuf)); + + if (rc < 0) { + /* + * Make the sequence number of the next SMB request even + * to avoid DC from failing the next SMB request with + * ACCESS_DENIED. + */ + smb_mac_dec_seqnum(&srh->srh_session->sign_ctx); + return (NT_STATUS_UNEXPECTED_NETWORK_ERROR); + } + + return (NT_STATUS_SUCCESS); +} + +/* + * smbrdr_rcv + * + * Receive a SMB response and decode the packet header. + * + * "Implementing CIFS" book, SMB requests always have an even sequence + * number and replies always have an odd. + * + * With the original code, if the SMB Redirector skip the counter increment + * in the event of any failure during SmbSessionSetupAndX, it causes the + * domain controller to fail the next SMB request(odd sequence number) + * with ACCESS_DENIED. + * + * Smbrdr module should use the same sequence number (i.e. ssc_seqnum of the + * SMB Sign context) for generating the MAC signature for all incoming + * responses per SmbTransact request. Otherwise, the validation will fail. + * It is now fixed by decrementing the sequence number prior to validating + * the subsequent responses for a single request. + * + * Returns: + * + * status code returned by smbrdr_hdr_process() + * NT_STATUS_UNEXPECTED_NETWORK_ERROR receive failed + * NT_STATUS_SUCCESS successful + */ +DWORD +smbrdr_rcv(smbrdr_handle_t *srh, int is_first_rsp) +{ + smb_hdr_t smb_hdr; + DWORD status; + int rc; + smb_sign_ctx_t *sign_ctx = &srh->srh_session->sign_ctx; + + rc = nb_rcv(srh->srh_session->sock, srh->srh_buf, SMBRDR_REQ_BUFSZ, 0); + if (rc < 0) { + smb_mac_inc_seqnum(sign_ctx); + return (NT_STATUS_UNEXPECTED_NETWORK_ERROR); + } + + /* initialize for processing response */ + smb_msgbuf_init(&srh->srh_mbuf, srh->srh_buf, rc, srh->srh_mbflags); + + status = smbrdr_hdr_process(srh, &smb_hdr); + if (status != NT_STATUS_SUCCESS) { + smb_mac_inc_seqnum(sign_ctx); + return (status); + } + + if (!is_first_rsp) + smb_mac_dec_seqnum(sign_ctx); + + if (!smbrdr_sign_chk(sign_ctx, + &srh->srh_mbuf, smb_hdr.extra.extra.security_sig)) { + syslog(LOG_ERR, "SmbrdrExchange[%d]: bad signature", + srh->srh_cmd); + return (NT_STATUS_INVALID_NETWORK_RESPONSE); + } + + return (NT_STATUS_SUCCESS); +} + +/* + * smbrdr_exchange + * + * Send the SMB packet pointed by the given handle over + * network. Receive the response and decode the packet header. + * + * From "Implementing CIFS" book, SMB requests always have an even sequence + * number and replies always have an odd. + * + * With the original code, if the SMB Redirector skips the counter increment + * in the event of any failure during SmbSessionSetupAndX, it causes the + * domain controller to fail the next SMB request(odd sequence number) + * with ACCESS_DENIED. + * + * Returns: + * + * status code returned by smbrdr_hdr_process() + * NT_STATUS_INTERNAL_ERROR crypto framework failure + * NT_STATUS_UNEXPECTED_NETWORK_ERROR send/receive failed + * NT_STATUS_SUCCESS successful + */ +DWORD +smbrdr_exchange(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr, long timeout) +{ + smb_sign_ctx_t *sign_ctx; + smb_msgbuf_t *mb; + DWORD status; + int rc; + + mb = &srh->srh_mbuf; + sign_ctx = &srh->srh_session->sign_ctx; + + if (smbrdr_sign(sign_ctx, mb) != SMBAUTH_SUCCESS) + return (NT_STATUS_INTERNAL_ERROR); + + rc = nb_exchange(srh->srh_session->sock, + srh->srh_buf, smb_msgbuf_used(mb), + srh->srh_buf, SMBRDR_REQ_BUFSZ, timeout); + + if (rc < 0) { + syslog(LOG_ERR, "SmbrdrExchange[%d]: failed (%d)", + srh->srh_cmd, rc); + + if (srh->srh_cmd != SMB_COM_ECHO) { + /* + * Since SMB echo is used to check the session + * status then don't destroy the session if it's + * SMB echo. + */ + srh->srh_session->state = SDB_SSTATE_STALE; + } + smb_mac_inc_seqnum(sign_ctx); + return (NT_STATUS_UNEXPECTED_NETWORK_ERROR); + } + + /* initialize for processing response */ + smb_msgbuf_init(mb, srh->srh_buf, rc, srh->srh_mbflags); + + status = smbrdr_hdr_process(srh, smb_hdr); + if (status != NT_STATUS_SUCCESS) { + smb_mac_inc_seqnum(sign_ctx); + return (status); + } + + /* Signature validation */ + if (!smbrdr_sign_chk(sign_ctx, mb, smb_hdr->extra.extra.security_sig)) { + syslog(LOG_ERR, "SmbrdrExchange[%d]: bad signature", + srh->srh_cmd); + return (NT_STATUS_INVALID_NETWORK_RESPONSE); + } + + return (NT_STATUS_SUCCESS); +} + +/* + * smbrdr_handle_free + * + * Frees the memories allocated for the given handle. + */ +void +smbrdr_handle_free(smbrdr_handle_t *srh) +{ + if (srh) { + smb_msgbuf_term(&srh->srh_mbuf); + free(srh->srh_buf); + } +} + + +/* + * smbrdr_sign_init + * + * This function is called from SessionSetup and initialize the + * signing context for the session if the connected user isn't + * anonymous. This has to call before smbrdr_request_init() + * because it modifies smb_flags2. + * + * The following description is taken out from the "Implementing CIFS" + * book(pg. 304): + * + * "Once the MAC signing has been initialized within a session, all + * messages are numbered using the same counters and signed using + * the same Session Key. This is true even if additional SESSION + * SETUP ANDX exchanges occur." + * + * The original SMB packet signing implementation calculates a MAC + * key each time the SMB Redirector sends the SmbSessionSetupAndx + * request for any non-anonymous/non-guest user which is not desired + * whenever there is a change in the user session key. + * + * If NTLMv2 authentication is used, the MAC key generated for each + * SessionSetup is unique. Since the domain controller expects the + * signature of all incoming requests are signed by the same MAC key + * (i.e. the one that generated for the first non-anonymous SessionSetup), + * access denied is returned for any subsequent SmbSessionSetupAndX + * request. + */ +int +smbrdr_sign_init(struct sdb_session *session, struct sdb_logon *logon) +{ + smb_sign_ctx_t *sign_ctx; + int rc = 0; + + sign_ctx = &session->sign_ctx; + + if ((sign_ctx->ssc_flags & SMB_SCF_REQUIRED) && + !(sign_ctx->ssc_flags & SMB_SCF_STARTED) && + (logon->type != SDB_LOGON_ANONYMOUS)) { + if (smb_mac_init(sign_ctx, &logon->auth) != SMBAUTH_SUCCESS) { + syslog(LOG_DEBUG, "SmbrdrSignInit: mac_init failed"); + return (-1); + } + sign_ctx->ssc_flags |= + (SMB_SCF_STARTED | SMB_SCF_KEY_ISSET_THIS_LOGON); + session->smb_flags2 |= SMB_FLAGS2_SMB_SECURITY_SIGNATURE; + syslog(LOG_DEBUG, "SmbrdrSignInit: mac key is set"); + rc = 1; + } + + return (rc); +} + +/* + * smbrdr_sign_fini + * + * Invalidate the MAC key if the first non-anonymous/non-guest user logon + * fail. + */ +int +smbrdr_sign_fini(struct sdb_session *session) +{ + smb_sign_ctx_t *sign_ctx; + int rc = 0; + + sign_ctx = &session->sign_ctx; + + if (sign_ctx->ssc_flags & SMB_SCF_KEY_ISSET_THIS_LOGON) { + sign_ctx->ssc_flags &= ~SMB_SCF_STARTED; + sign_ctx->ssc_flags &= ~SMB_SCF_KEY_ISSET_THIS_LOGON; + sign_ctx->ssc_seqnum = 0; + syslog(LOG_DEBUG, "SmbrdrSignFini: packet signing stopped"); + rc = 1; + } + + return (rc); +} + +/* + * smbrdr_sign_unset_key + * + * The SMB_SCF_KEY_ISSET_THIS_LOGON should be unset upon the successful + * SmbSessionSetupAndX request for the first non-anonymous/non-guest + * logon. + */ +int +smbrdr_sign_unset_key(struct sdb_session *session) +{ + smb_sign_ctx_t *sign_ctx; + int rc = 0; + + sign_ctx = &session->sign_ctx; + + if (sign_ctx->ssc_flags & SMB_SCF_KEY_ISSET_THIS_LOGON) { + sign_ctx->ssc_flags &= ~SMB_SCF_KEY_ISSET_THIS_LOGON; + syslog(LOG_DEBUG, "SmbrdrSignUnsetKey: unset KEY_ISSET flag"); + rc = 1; + } + + return (rc); +} + +/* + * smbrdr_handle_setup + * + * Allocates a buffer for sending/receiving a SMB request. + * Initialize a smb_msgbuf structure with the allocated buffer. + * Setup given handle (srh) with the specified information. + * + * Returns: + * + * NT_STATUS_NO_MEMORY not enough memory + * NT_STATUS_SUCCESS successful + */ +static DWORD +smbrdr_handle_setup(smbrdr_handle_t *srh, + unsigned char cmd, + struct sdb_session *session, + struct sdb_logon *logon, + struct sdb_netuse *netuse) +{ + srh->srh_buf = (unsigned char *)malloc(SMBRDR_REQ_BUFSZ); + if (srh->srh_buf == 0) { + syslog(LOG_ERR, "Smbrdr[%d]: resource shortage", cmd); + return (NT_STATUS_NO_MEMORY); + } + bzero(srh->srh_buf, SMBRDR_REQ_BUFSZ); + + srh->srh_mbflags = (session->remote_caps & CAP_UNICODE) + ? SMB_MSGBUF_UNICODE : 0; + + smb_msgbuf_init(&srh->srh_mbuf, srh->srh_buf, + SMBRDR_REQ_BUFSZ, srh->srh_mbflags); + + srh->srh_cmd = cmd; + srh->srh_session = session; + srh->srh_user = logon; + srh->srh_tree = netuse; + + return (NT_STATUS_SUCCESS); +} + +/* + * smbrdr_hdr_setup + * + * Build an SMB header based on the information in the given handle. + * The SMB header is described in section 3.2 of the CIFS spec. + * As this is a canned function, no error checking is performed here. + * The return value from smb_msgbuf_encode is simply returned to the caller. + */ +static int +smbrdr_hdr_setup(smbrdr_handle_t *srh) +{ + static unsigned short my_pid = 0; + + if (!my_pid) + my_pid = getpid(); + + return (smb_msgbuf_encode(&srh->srh_mbuf, "Mb4.bw12.wwww", + srh->srh_cmd, + srh->srh_session->smb_flags, + srh->srh_session->smb_flags2, + (srh->srh_tree) ? srh->srh_tree->tid : 0, + my_pid, + (srh->srh_user) ? srh->srh_user->uid : 0, + 0 /* mid */)); +} + +/* + * Canned SMB header decode. + */ +static int +smb_decode_nt_hdr(smb_msgbuf_t *mb, smb_hdr_t *hdr) +{ + return (smb_msgbuf_decode(mb, SMB_HEADER_NT_FMT, + &hdr->command, + &hdr->status.ntstatus, + &hdr->flags, + &hdr->flags2, + &hdr->pid_high, + SMB_SIG_SIZE, + &hdr->extra.extra.security_sig, + &hdr->tid, + &hdr->pid, + &hdr->uid, + &hdr->mid)); +} + +/* + * smbrdr_hdr_process + * + * Assuming 'srh->srh_mbuf' contains a response from a Windows client, + * decodes the 32 bytes SMB header. + * + * Returns: + * + * NT_STATUS_INVALID_NETWORK_RESPONSE error decoding the header + * NT_STATUS_REPLY_MESSAGE_MISMATCH response doesn't match the request + * NT_STATUS_SUCCESS successful + * smb_hdr->status.ntstatus error returned by server + */ +static DWORD +smbrdr_hdr_process(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr) +{ + int rc; + + /* + * Returns the number of decoded bytes on success + * or some negative MSGBUF_XXX error code on failure + */ + rc = smb_decode_nt_hdr(&srh->srh_mbuf, smb_hdr); + + if (rc < SMB_HEADER_LEN) { + syslog(LOG_ERR, "Smbrdr[%d]: bad SMB header (%d)", + srh->srh_cmd, rc); + return (NT_STATUS_INVALID_NETWORK_RESPONSE); + } + + if (smb_hdr->status.ntstatus != 0) { + /* + * MSDN article: 193839 + * "The buffer overflow status is usually returned by a Windows + * server to inform the client that there is more data in its + * buffer than it could put in the packet. + * + * The buffer overflow should not be considered as an error. + * Subsequent SmbReadX request is required to obtain the + * remaining data in the server's buffer. + */ + if (NT_SC_VALUE(smb_hdr->status.ntstatus) + == NT_STATUS_BUFFER_OVERFLOW) { + syslog(LOG_DEBUG, "Smbrdr[%d]: %s", srh->srh_cmd, + xlate_nt_status(smb_hdr->status.ntstatus)); + } else { + syslog(LOG_ERR, "Smbrdr[%d]: request failed (%s)", + srh->srh_cmd, + xlate_nt_status(smb_hdr->status.ntstatus)); + return (smb_hdr->status.ntstatus); + } + } + + if (smb_hdr->command != srh->srh_cmd) { + syslog(LOG_ERR, "Smbrdr[%d]: reply mismatch (%d)", + srh->srh_cmd, smb_hdr->command); + return (NT_STATUS_REPLY_MESSAGE_MISMATCH); + } + + return (NT_STATUS_SUCCESS); +} + +/* + * smbrdr_sign + * + * Signs the given outgoing packet according to the + * specified signing context. + * + * The client and server each maintain an integer counter + * which they initialize to zero. Both counters are + * incremented for every SMB message - that's once for a + * request and once for a reply. As a result, requests sent + * by SMB Redirector always have an even sequence number + * and replies from the Windows server always have an odd + * number. + * + * Based on the observed Windows 2003 behavior, any SMB + * request will fail with NT_STATUS_ACCESS_DENIED if its + * sequence number is not even. + * + * The function can fail if there is trouble with the cryptographic + * framework and if that happens SMBAUTH_FAILURE is returned. In the + * normal case SMBAUTH_SUCCESS is returned. + */ +static int +smbrdr_sign(smb_sign_ctx_t *sign_ctx, smb_msgbuf_t *mb) +{ + if (sign_ctx->ssc_flags & SMB_SCF_STARTED) { + if (sign_ctx->ssc_seqnum % 2) { + syslog(LOG_DEBUG, "SmbrdrSign: even sequence number" + " is expected(%d)", + sign_ctx->ssc_seqnum); + } + if (smb_mac_sign(sign_ctx, smb_msgbuf_base(mb), + smb_msgbuf_used(mb)) != SMBAUTH_SUCCESS) + return (SMBAUTH_FAILURE); + sign_ctx->ssc_seqnum++; + } + return (SMBAUTH_SUCCESS); +} + + +/* + * smbrdr_sign_chk + * + * Validates SMB MAC signature in the in-coming message. + * Return 1 if the signature are match; otherwise, return 0; + * + * When packet signing is enabled, the sequence number kept in the + * sign_ctx structure will be incremented when a SMB request is + * sent and upon the receipt of the first SmbTransact response + * if SMB fragmentation occurs. + */ +static int +smbrdr_sign_chk(smb_sign_ctx_t *sign_ctx, smb_msgbuf_t *mb, + unsigned char *signature) +{ + int sign_ok = 1; + + if (sign_ctx->ssc_flags & SMB_SCF_STARTED) { + (void) memcpy(sign_ctx->ssc_sign, signature, SMB_SIG_SIZE); + sign_ok = smb_mac_chk(sign_ctx, smb_msgbuf_base(mb), + smb_msgbuf_size(mb)); + sign_ctx->ssc_seqnum++; + } + + return (sign_ok); +} diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_logon.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_logon.c new file mode 100644 index 000000000000..7dee7e41d23a --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_logon.c @@ -0,0 +1,710 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB session logon and logoff functions. See CIFS section 4.1. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define SMBRDR_PWD_NULL 0 +#define SMBRDR_PWD_USER 1 +#define SMBRDR_PWD_HASH 2 + +static int smbrdr_smb_session_setupandx(struct sdb_logon *logon); +static boolean_t smbrdr_logon_validate(char *server, char *username); +static struct sdb_logon *smbrdr_logon_init(struct sdb_session *session, + char *username, char *pwd, int pwd_type); +static int smbrdr_logon_user(char *server, char *username, char *pwd, + int pwd_type); +static int smbrdr_authenticate(char *primary_domain, char *account_name, + char *pwd, int pwd_type); + +/* + * mlsvc_anonymous_logon + * + * Set up an anonymous session. If the session to the resource domain + * controller appears to be okay we shouldn't need to do anything here. + * Otherwise we clean up the stale session and create a new one. + */ +int +mlsvc_anonymous_logon(char *domain_controller, char *domain_name, + char **username) +{ + int rc = 0; + + if (username == NULL) { + syslog(LOG_ERR, "smbrdr: (anon logon) %s", + xlate_nt_status(NT_STATUS_INVALID_PARAMETER)); + return (-1); + } + + /* + * if the system is configured to establish Authenticated IPC + * connection to PDC + */ + if (smbrdr_ipc_get_mode() == MLSVC_IPC_ADMIN) { + rc = mlsvc_admin_logon(domain_controller, domain_name); + /* + * it is possible for the system to fallback to use + * anonymous IPC + */ + if (smbrdr_ipc_get_mode() != MLSVC_IPC_ADMIN) + *username = MLSVC_ANON_USER; + else + *username = smbrdr_ipc_get_user(); + + syslog(LOG_DEBUG, "smbrdr: (admin logon) %s", *username); + return (rc); + } + + *username = MLSVC_ANON_USER; + + if (smbrdr_logon_validate(domain_controller, MLSVC_ANON_USER)) + /* session & user are good use them */ + return (0); + + + if (smbrdr_negotiate(domain_name) != 0) { + syslog(LOG_ERR, "smbrdr: (anon logon) negotiate <%s> failed", + (domain_name ? domain_name : "NoName")); + return (-1); + } + + if (smbrdr_logon_user(domain_controller, MLSVC_ANON_USER, 0, + SMBRDR_PWD_NULL) < 0) { + syslog(LOG_ERR, "smbrdr: (anon logon) logon failed"); + rc = -1; + } + + return (rc); +} + +int +mlsvc_user_getauth(char *domain_controller, char *username, + smb_auth_info_t *auth) +{ + struct sdb_session *session; + + if (auth) { + bzero(auth, sizeof (smb_auth_info_t)); + session = smbrdr_session_lock(domain_controller, username, + SDB_SLCK_READ); + if (session) { + *auth = session->logon.auth; + smbrdr_session_unlock(session); + return (0); + } + } + + return (-1); +} + +/* + * mlsvc_user_logon + * + * Set up a user session. If the session to the resource domain controller + * appears to be okay we shouldn't need to do anything here. Otherwise we + * clean up the stale session and create a new one. Once a session is + * established, we leave it intact. It should only need to be set up again + * due to an inactivity timeout or a domain controller reset. + */ +int +mlsvc_user_logon(char *domain_controller, char *domain_name, char *username, + char *password) +{ + int erc; + + if (smbrdr_logon_validate(domain_controller, username)) + return (0); + + if (smbrdr_negotiate(domain_name) != 0) { + syslog(LOG_ERR, "smbrdr: (user logon) negotiate failed"); + return (-1); + } + + erc = smbrdr_authenticate(domain_name, username, password, + SMBRDR_PWD_USER); + + return ((erc == AUTH_USER_GRANT) ? 0 : -1); +} + +/* + * mlsvc_admin_logon + * + * Unlike mlsvc_user_logon, mlsvc_admin_logon doesn't take + * any username or password as function arguments. + */ +int +mlsvc_admin_logon(char *domain_controller, char *domain_name) +{ + char password[PASS_LEN + 1]; + int erc; + char *username, *dummy; + + username = smbrdr_ipc_get_user(); + (void) memcpy(password, smbrdr_ipc_get_passwd(), SMBAUTH_HASH_SZ); + + if (*username == 0) { + syslog(LOG_ERR, "smbrdr: admin logon (no admin user)"); + return (-1); + } + + if (smbrdr_logon_validate(domain_controller, username)) + return (0); + + if (smbrdr_negotiate(domain_name) != 0) { + syslog(LOG_ERR, "smbrdr: admin logon (negotiate failed)"); + return (-1); + } + + erc = smbrdr_authenticate(domain_name, username, password, + SMBRDR_PWD_HASH); + + /* + * Fallback to anonmyous IPC logon if the IPC password hash is no + * longer valid. It happens when the administrator password has + * been reset from the Domain Controller. + */ + if (erc < 0 && smbrdr_ipc_is_valid()) { + if (!smbrdr_ipc_is_fallback()) + syslog(LOG_DEBUG, "smbrdr: admin logon " + "(fallback to anonymous IPC)"); + smbrdr_ipc_set_fallback(); + smbrdr_ipc_save_mode(IPC_MODE_FALLBACK_ANON); + erc = mlsvc_anonymous_logon(domain_controller, domain_name, + &dummy); + } + + return ((erc == AUTH_USER_GRANT) ? 0 : -1); +} + +/* + * smbrdr_authenticate + * + * Authenticate primary_domain\account_name. + * + * Returns: + * 0 User access granted + * 1 Guest access granted + * 2 IPC access granted + * (<0) Error + */ +static int +smbrdr_authenticate(char *primary_domain, char *account_name, + char *pwd, int pwd_type) +{ + smb_ntdomain_t *di; + + if (pwd == NULL) + return (AUTH_USER_GRANT | AUTH_IPC_ONLY_GRANT); + + + if ((di = smb_getdomaininfo(0)) == 0) { + syslog(LOG_ERR, "MlsvcAuthenticate[%s]: %s", account_name, + xlate_nt_status(NT_STATUS_CANT_ACCESS_DOMAIN_INFO)); + return (-1); + } + + /* + * Ensure that the domain name is uppercase. + */ + (void) utf8_strupr(primary_domain); + + /* + * We can only authenticate a user via a controller in the user's + * primary domain. If the user's domain name doesn't match the + * authenticating server's domain, reject the request before we + * create a logon entry for the user. Although the logon will be + * denied eventually, we don't want a logon structure for a user + * in the resource domain that is pointing to a session structure + * for the account domain. If this happened to be our resource + * domain user, we would not be able to use that account to connect + * to the resource domain. + */ + if (strcasecmp(di->domain, primary_domain)) { + syslog(LOG_ERR, "MlsvcAuthenticate: %s\\%s: not account domain", + primary_domain, account_name); + return (-2); + } + + return (smbrdr_logon_user(di->server, account_name, pwd, pwd_type)); +} + +/* + * smbrdr_logon_user + * + * This is the entry point for logging a user onto the domain. The + * session structure should have been obtained via a successful call + * to smbrdr_smb_connect. We allocate a logon structure to hold the + * user details and attempt to logon using smbrdr_smb_session_setupandx. Note + * that we expect the password fields to have been encrypted before + * this call. + * + * On success, the logon structure will be returned. Otherwise a null + * pointer will be returned. + */ +static int +smbrdr_logon_user(char *server, char *username, char *pwd, int pwd_type) +{ + struct sdb_session *session; + struct sdb_logon *logon; + struct sdb_logon old_logon; + + if (server == 0 || username == 0 || + ((pwd == 0) && (pwd_type != SMBRDR_PWD_NULL))) { + return (-1); + } + + session = smbrdr_session_lock(server, 0, SDB_SLCK_WRITE); + if (session == 0) { + syslog(LOG_ERR, "smbrdr: (logon[%s]) no session with %s", + username, server); + return (-1); + } + + bzero(&old_logon, sizeof (struct sdb_logon)); + + logon = &session->logon; + if (logon->type != SDB_LOGON_NONE) { + if (strcasecmp(logon->username, username) == 0) { + /* The requested user has already been logged in */ + smbrdr_session_unlock(session); + return ((logon->type == SDB_LOGON_GUEST) + ? AUTH_GUEST_GRANT : AUTH_USER_GRANT); + } + + old_logon = *logon; + } + + logon = smbrdr_logon_init(session, username, pwd, pwd_type); + + if (logon == 0) { + syslog(LOG_ERR, "smbrdr: (logon[%s]) resource shortage", + username); + smbrdr_session_unlock(session); + return (-1); + } + + if (smbrdr_smb_session_setupandx(logon) < 0) { + free(logon); + smbrdr_session_unlock(session); + return (-1); + } + + session->logon = *logon; + free(logon); + + if (old_logon.type != SDB_LOGON_NONE) { + (void) smbrdr_smb_logoff(&old_logon); + } + + smbrdr_session_unlock(session); + return ((logon->type == SDB_LOGON_GUEST) + ? AUTH_GUEST_GRANT : AUTH_USER_GRANT); +} + + +/* + * smbrdr_smb_session_setupandx + * + * Build and send an SMB session setup command. This is used to log a + * user onto the domain. See CIFS section 4.1.2. + * + * Returns 0 on success. Otherwise returns a -ve error code. + */ +static int +smbrdr_smb_session_setupandx(struct sdb_logon *logon) +{ + struct sdb_session *session; + smb_hdr_t smb_hdr; + smbrdr_handle_t srh; + smb_msgbuf_t *mb; + char *native_os; + char *native_lanman; + unsigned short data_bytes; + unsigned short guest; + unsigned long capabilities; + unsigned short null_size; + size_t (*strlen_fn)(const char *s); + DWORD status; + int rc; + + /* + * Paranoia check - we should never get this + * far without a valid session structure. + */ + if ((session = logon->session) == 0) { + syslog(LOG_ERR, "smbrdr_smb_session_setupandx: no data"); + return (-1); + } + + if (session->remote_caps & CAP_UNICODE) { + strlen_fn = mts_wcequiv_strlen; + null_size = sizeof (mts_wchar_t); + session->smb_flags2 |= SMB_FLAGS2_UNICODE; + } else { + strlen_fn = strlen; + null_size = sizeof (char); + } + + if (smbrdr_sign_init(session, logon) < 0) + return (-1); + + status = smbrdr_request_init(&srh, SMB_COM_SESSION_SETUP_ANDX, + session, 0, 0); + + if (status != NT_STATUS_SUCCESS) { + (void) smbrdr_sign_fini(session); + syslog(LOG_ERR, "SmbrdrSessionSetup: %s", + xlate_nt_status(status)); + return (-1); + } + mb = &srh.srh_mbuf; + + /* + * Regardless of the server's capabilities or what's + * reported in smb_flags2, we should report our full + * capabilities. + */ + capabilities = CAP_UNICODE | CAP_NT_SMBS | CAP_STATUS32; + + /* + * Compute the BCC for unicode or ASCII strings. + */ + data_bytes = logon->auth.ci_len + logon->auth.cs_len + null_size; + data_bytes += strlen_fn(session->native_os) + null_size; + data_bytes += strlen_fn(session->native_lanman) + null_size; + + if (logon->type == SDB_LOGON_ANONYMOUS) { + /* + * Anonymous logon: no username or domain name. + * We still need to include two null characters. + */ + data_bytes += (2 * null_size); + + rc = smb_msgbuf_encode(mb, "bb.wwwwlwwllwlu.u.", + 13, /* smb_wct */ + 0xff, /* AndXCommand (none) */ + 32 + 26 + 3 + data_bytes, /* AndXOffset */ + SMBRDR_REQ_BUFSZ, /* MaxBufferSize */ + 1, /* MaxMpxCount */ + 0, /* VcNumber */ + 0, /* SessionKey */ + 1, /* CaseInsensitivePassLength */ + 0, /* CaseSensitivePassLength */ + 0, /* Reserved */ + capabilities, /* Capabilities */ + data_bytes, /* smb_bcc */ + 0, /* No user or domain */ + session->native_os, /* NativeOS */ + session->native_lanman); /* NativeLanMan */ + } else { + data_bytes += strlen_fn(logon->username) + null_size; + data_bytes += strlen_fn(session->di.domain) + null_size; + + rc = smb_msgbuf_encode(mb, "bb.wwwwlwwllw#c#cuuu.u.", + 13, /* smb_wct */ + 0xff, /* AndXCommand (none) */ + 32 + 26 + 3 + data_bytes, /* AndXOffset */ + SMBRDR_REQ_BUFSZ, /* MaxBufferSize */ + 1, /* MaxMpxCount */ + session->vc, /* VcNumber */ + session->sesskey, /* SessionKey */ + logon->auth.ci_len, /* CaseInsensitivePassLength */ + logon->auth.cs_len, /* CaseSensitivePassLength */ + 0, /* Reserved */ + capabilities, /* Capabilities */ + data_bytes, /* smb_bcc */ + logon->auth.ci_len, /* ci length spec */ + logon->auth.ci, /* CaseInsensitivePassword */ + logon->auth.cs_len, /* cs length spec */ + logon->auth.cs, /* CaseSensitivePassword */ + logon->username, /* AccountName */ + session->di.domain, /* PrimaryDomain */ + session->native_os, /* NativeOS */ + session->native_lanman); /* NativeLanMan */ + } + + if (rc <= 0) { + syslog(LOG_ERR, "smbrdr_smb_session_setupandx: encode failed"); + smbrdr_handle_free(&srh); + (void) smbrdr_sign_fini(session); + return (-1); + } + + status = smbrdr_exchange(&srh, &smb_hdr, 0); + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "SmbrdrSessionSetup: %s", + xlate_nt_status(status)); + smbrdr_handle_free(&srh); + (void) smbrdr_sign_fini(session); + return (-1); + } + + rc = smb_msgbuf_decode(mb, "5.w2.u", &guest, &native_os); + + /* + * There was a problem in decoding response from + * a Samba 2.x PDC. This server sends strings in ASCII + * format and there is one byte with value 0 between + * native_os and native_lm: + * + * FF 53 4D 42 73 00 .SMBs. + * 00 00 00 88 01 00 00 00 00 00 00 00 00 00 00 00 ................ + * 00 00 00 00 BB 00 64 00 00 00 03 FF 00 00 00 01 ......d......... + * 00 1C 00 55 6E 69 78 00 53 61 6D 62 61 20 32 2E ...Unix.Samba.2. + * 32 2E 38 61 00 53 41 4D 42 41 5F 44 4F 4D 00 2.8a.SAMBA_DOM. + * + * The byte doesn't seem to be padding because when change in + * native OS from Unix to Unix1 the 0 byte is still there: + * + * FF 53 4D 42 73 00 .SMBs. + * 00 00 00 88 01 00 00 00 00 00 00 00 00 00 00 00 ................ + * 00 00 00 00 BB 00 64 00 00 00 03 FF 00 00 00 00 ......d......... + * 00 1D 00 55 6E 69 78 31 00 53 61 6D 62 61 20 32 ...Unix1.Samba.2 + * 2E 32 2E 38 61 00 53 41 4D 42 41 5F 44 4F 4D 00 .2.8a.SAMBA_DOM. + */ + if (rc > 0) { + if (session->remote_caps & CAP_UNICODE) + rc = smb_msgbuf_decode(mb, "u", &native_lanman); + else + rc = smb_msgbuf_decode(mb, ".u", &native_lanman); + } + + if (rc <= 0) { + syslog(LOG_ERR, "RdrSessionSetup: decode failed"); + smbrdr_handle_free(&srh); + (void) smbrdr_sign_fini(session); + return (-1); + } + + session->remote_os = smbnative_os_value(native_os); + session->remote_lm = smbnative_lm_value(native_lanman); + session->pdc_type = smbnative_pdc_value(native_lanman); + + logon->uid = smb_hdr.uid; + if (guest) + logon->type = SDB_LOGON_GUEST; + + smbrdr_handle_free(&srh); + (void) smbrdr_sign_unset_key(session); + + logon->state = SDB_LSTATE_SETUP; + + return (0); +} + +/* + * smbrdr_smb_logoff + * + * Build and send an SMB session logoff (SMB_COM_LOGOFF_ANDX) command. + * This is the inverse of an SMB_COM_SESSION_SETUP_ANDX. See CIFS + * section 4.1.3. The logon structure should have been obtained from a + * successful call to smbrdr_logon_user. + * + * Returns 0 on success. Otherwise returns a -ve error code. + */ +int +smbrdr_smb_logoff(struct sdb_logon *logon) +{ + struct sdb_session *session; + smbrdr_handle_t srh; + smb_hdr_t smb_hdr; + DWORD status; + int rc; + + if (logon->state != SDB_LSTATE_SETUP) { + /* No user to logoff */ + bzero(logon, sizeof (struct sdb_logon)); + return (0); + } + + if ((session = logon->session) == 0) { + bzero(logon, sizeof (struct sdb_logon)); + return (0); + } + + logon->state = SDB_LSTATE_LOGGING_OFF; + smbrdr_netuse_logoff(logon->uid); + + if ((session->state != SDB_SSTATE_NEGOTIATED) && + (session->state != SDB_SSTATE_DISCONNECTING)) { + bzero(logon, sizeof (struct sdb_logon)); + return (0); + } + + status = smbrdr_request_init(&srh, SMB_COM_LOGOFF_ANDX, + session, logon, 0); + + if (status != NT_STATUS_SUCCESS) { + logon->state = SDB_LSTATE_SETUP; + syslog(LOG_ERR, "smbrdr: logoff %s (%s)", logon->username, + xlate_nt_status(status)); + return (-1); + } + + rc = smb_msgbuf_encode(&srh.srh_mbuf, "bbbww", 2, 0xff, 0, 0, 0); + if (rc < 0) { + logon->state = SDB_LSTATE_SETUP; + smbrdr_handle_free(&srh); + syslog(LOG_ERR, "smbrdr: logoff %s (encode failed)", + logon->username); + return (rc); + } + + status = smbrdr_exchange(&srh, &smb_hdr, 0); + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "smbrdr: logoff %s (%s)", logon->username, + xlate_nt_status(status)); + rc = -1; + } else { + rc = 0; + } + + bzero(logon, sizeof (struct sdb_logon)); + smbrdr_handle_free(&srh); + return (rc); +} + + +/* + * smbrdr_logon_init + * + * Find a slot for account logon information. The account information + * is associated with a session so we need a valid session slot before + * calling this function. If we already have a record of the specified + * account, a pointer to that record is returned. Otherwise we attempt + * to allocate a new one. + */ +static struct sdb_logon * +smbrdr_logon_init(struct sdb_session *session, char *username, + char *pwd, int pwd_type) +{ + struct sdb_logon *logon; + int smbrdr_lmcomplvl; + int rc; + + logon = (struct sdb_logon *)malloc(sizeof (sdb_logon_t)); + if (logon == 0) + return (0); + + bzero(logon, sizeof (struct sdb_logon)); + logon->session = session; + + if (strcmp(username, "IPC$") == 0) + logon->type = SDB_LOGON_ANONYMOUS; + else + logon->type = SDB_LOGON_USER; + + (void) strlcpy(logon->username, username, MAX_ACCOUNT_NAME); + + smb_config_rdlock(); + smbrdr_lmcomplvl = smb_config_getnum(SMB_CI_LM_LEVEL); + smb_config_unlock(); + + switch (pwd_type) { + case SMBRDR_PWD_USER: + rc = smb_auth_set_info(username, pwd, 0, session->di.domain, + session->challenge_key, session->challenge_len, + smbrdr_lmcomplvl, &logon->auth); + + if (rc != 0) { + free(logon); + return (0); + } + break; + + case SMBRDR_PWD_HASH: + rc = smb_auth_set_info(username, 0, (unsigned char *)pwd, + session->di.domain, session->challenge_key, + session->challenge_len, smbrdr_lmcomplvl, &logon->auth); + + if (rc != 0) { + free(logon); + return (0); + } + break; + + case SMBRDR_PWD_NULL: + logon->auth.ci_len = 1; + *(logon->auth.ci) = 0; + logon->auth.cs_len = 0; + break; + + default: + /* Unknown password type */ + free(logon); + return (0); + } + + logon->state = SDB_LSTATE_INIT; + return (logon); +} + +/* + * smbrdr_logon_validate + * + * if session is there and it's alive and also the required + * user is already logged in don't need to do anything + * otherwise clear the session structure. + */ +static boolean_t +smbrdr_logon_validate(char *server, char *username) +{ + struct sdb_session *session; + boolean_t valid = B_FALSE; + + session = smbrdr_session_lock(server, username, SDB_SLCK_WRITE); + if (session) { + if (nb_keep_alive(session->sock) == 0) { + valid = B_TRUE; + } else { + session->state = SDB_SSTATE_STALE; + syslog(LOG_DEBUG, "smbrdr: (logon) stale session"); + } + + smbrdr_session_unlock(session); + } + + return (valid); +} diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netbios.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netbios.c new file mode 100644 index 000000000000..2066dab05142 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netbios.c @@ -0,0 +1,457 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * NetBIOS support functions. NetBIOS is documented in the following + * RFC documents: + * + * RFC 1001: Protocol Standard for a NetBIOS Service on a TCP/UDP + * Transport: Concepts and Methods + * + * RFC 1002: Protocol Standard for a NetBIOS Service on a TCP/UDP + * Transport: Detailed Specifications + * + */ + +#define BSD_BYTE_STRING_PROTOTYPES + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MAX_NETBIOS_NAME_SIZE 16 + +#define SESSION_MESSAGE 0x00 +#define SESSION_REQUEST 0x81 +#define POSITIVE_SESSION_RESPONSE 0x82 +#define NEGATIVE_SESSION_RESPONSE 0x83 +#define RETARGET_SESSION_RESPONSE 0x84 +#define SESSION_KEEP_ALIVE 0x85 + +#define NB_READ_MSG_ERR_EOF 0 +#define NB_READ_MSG_ERR -1 +#define NB_READ_MSG_ERR_OVERFLOW -2 +#define NB_READ_MSG_ERR_UNDERFLOW -3 +#define NB_RCV_MSG_ERR_INVTYPE -4 + +/* + * Semaphore object used to serialize access through NetBIOS exchange. + */ +static mutex_t nb_mutex; + +static int nb_write_msg(int fd, unsigned char *buf, unsigned count, int type); +static int nb_read_msg(int fd, unsigned char *buf, unsigned max_buf, + int *type, long timeout); +static int nb_read_itter(int fd, unsigned char *buf, unsigned cnt); +static int nb_first_level_name_encode(char *name, char *scope, + unsigned char *out, int max_out); + + +/* + * nb_lock + * + * Acquire semaphore for doing netbios operations + */ +void +nb_lock() +{ + (void) mutex_lock(&nb_mutex); +} + +/* + * nb_lock + * + * Release netbios semaphore. + */ +void +nb_unlock() +{ + (void) mutex_unlock(&nb_mutex); +} + +void +nb_close(int fd) +{ + (void) mutex_lock(&nb_mutex); + if (fd > 0) { + (void) close(fd); + (void) printf("[%d] socket (%d) closed\n", pthread_self(), fd); + } + (void) mutex_unlock(&nb_mutex); +} + +/* + * nb_keep_alive + * + * Send the NetBIOS keep alive message. No response is expected but we + * do need to ignore keep-alive messages in nb_exchange. The semaphore + * ensures compatibility/serialization with nb_exchange to allow us to + * call this function from a separate thread. + */ +int +nb_keep_alive(int fd) +{ + int nothing; + int rc; + + (void) mutex_lock(&nb_mutex); + + rc = nb_write_msg(fd, (unsigned char *)¬hing, 0, SESSION_KEEP_ALIVE); + if (rc < 0) + syslog(LOG_ERR, "nb_keep_alive: write failed"); + + (void) mutex_unlock(&nb_mutex); + return (rc); +} + +/* + * nb_send + * + * This is just a wrapper round the nb_write_msg. + */ +int +nb_send(int fd, unsigned char *send_buf, unsigned send_cnt) +{ + int rc; + + if ((rc = nb_write_msg(fd, send_buf, send_cnt, SESSION_MESSAGE)) < 0) + syslog(LOG_ERR, "nb_send: write failed: rc=%d", rc); + + return (rc); +} + +/* + * nb_rcv + * + * This is a wrapper round the nb_read_msg() so that if a + * keep-alive message is received, just discard it and go + * back to look for the real response. + */ +int +nb_rcv(int fd, unsigned char *recv_buf, unsigned recv_max, long timeout) +{ + int rc; + int type; + + do { + rc = nb_read_msg(fd, recv_buf, recv_max, &type, timeout); + if (rc < 0) { + syslog(LOG_ERR, "nb_rcv: read failed: rc=%d", rc); + return (rc); + } + } while (type == SESSION_KEEP_ALIVE); + + if (type != SESSION_MESSAGE) { + syslog(LOG_ERR, "nb_rcv: invalid type: %d", type); + return (NB_RCV_MSG_ERR_INVTYPE); + } + + return (rc); +} + +/* + * nb_exchange + * + * This is the NetBIOS workhorse function where we do the send/receive + * message exchange. A semaphore is used to serialize access because + * we may get swapped out between the send and receive operations and + * another thread could enter here and collect our response. If a + * keep-alive message is received, just discard it and go back to look + * for the real response. + * + * Note: With the addition of support for SMB over TCP, this function + * may be exchanging NetBIOS-less SMB data. + */ +int +nb_exchange(int fd, unsigned char *send_buf, unsigned send_cnt, + unsigned char *recv_buf, unsigned recv_max, long timeout) +{ + int rc; + + (void) mutex_lock(&nb_mutex); + + rc = nb_send(fd, send_buf, send_cnt); + if (rc == send_cnt) + rc = nb_rcv(fd, recv_buf, recv_max, timeout); + + (void) mutex_unlock(&nb_mutex); + return (rc); +} + +/* + * nb_session_request + * + * We should never see descriptor 0 (stdin) or -1. + */ +int +nb_session_request(int fd, char *called_name, char *called_scope, + char *calling_name, char *calling_scope) +{ + unsigned char sr_buf[200]; + int len; + int rc; + int type; + + if (fd == 0 || fd == -1) + return (-1); + + rc = nb_first_level_name_encode(called_name, called_scope, sr_buf, 100); + len = rc; + rc = nb_first_level_name_encode(calling_name, calling_scope, + sr_buf+len, 100); + len += rc; + + (void) mutex_lock(&nb_mutex); + + rc = nb_write_msg(fd, (unsigned char *)sr_buf, len, SESSION_REQUEST); + if (rc < 0) { + syslog(LOG_ERR, "nb_session_request: write failed:" + " rc=%d", rc); + (void) mutex_unlock(&nb_mutex); + return (rc); + } + + for (;;) { + rc = nb_read_msg(fd, (unsigned char *)sr_buf, + sizeof (sr_buf), &type, 0); + if (rc < 0) { + (void) mutex_unlock(&nb_mutex); + return (rc); + } + + if ((rc == 0) && (type == -1)) { + (void) mutex_unlock(&nb_mutex); + return (-1); /* EOF */ + } + + if (type == POSITIVE_SESSION_RESPONSE) { + (void) mutex_unlock(&nb_mutex); + return (0); + } + + if (type == NEGATIVE_SESSION_RESPONSE) { + (void) mutex_unlock(&nb_mutex); + return (-1); + } + } + + /* NOTREACHED */ + (void) mutex_unlock(&nb_mutex); + return (-1); +} + + + +/* + * nb_write_msg + */ +static int +nb_write_msg(int fd, unsigned char *buf, unsigned count, int type) +{ + struct iovec iov[2]; + unsigned char header[4]; + int rc; + + if (fd == 0 || fd == -1) { + /* + * We should never see descriptor 0 (stdin). + */ + syslog(LOG_ERR, "nb_write_msg: invalid descriptor (%d)", fd); + return (-1); + } + + /* + * The NetBIOS message length is limited to 17 bits but + * we use this layer for SMB over both NetBIOS and TCP + * (NetBIOS-less SMB). When using SMB over TCP the length + * is 24 bits but we are ignoring that for now because we + * don't expect any messages larger than 64KB. + */ + header[0] = type; + header[1] = (count >> 16) & 1; + header[2] = count >> 8; + header[3] = count; + + iov[0].iov_base = (caddr_t)header; + iov[0].iov_len = 4; + iov[1].iov_base = (caddr_t)buf; + iov[1].iov_len = count; + + rc = writev(fd, iov, 2); + if (rc != 4 + count) { + syslog(LOG_ERR, "nb_write_msg: writev rc=%d", rc); + return (-3); /* error */ + } + + return (count); +} + + +/* + * nb_read_msg + * + * Added select to ensure that we don't block forever waiting for a + * message. + */ +static int +nb_read_msg(int fd, unsigned char *buf, unsigned max_buf, + int *type, long timeout) +{ + unsigned char header[4]; + int length; + int rc; + fd_set readfds; + struct timeval tval; + + *type = -1; + + if (fd == 0 || fd == -1) { + /* + * We should never see descriptor 0 (stdin). + */ + syslog(LOG_ERR, "nb_write_msg: invalid descriptor (%d)", fd); + return (NB_READ_MSG_ERR); + } + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + tval.tv_sec = (timeout == 0) ? 45 : timeout; + tval.tv_usec = 0; + + if ((rc = select(fd + 1, &readfds, 0, 0, &tval)) <= 0) { + syslog(LOG_ERR, "nb_read_msg: select: %d", rc); + return (NB_READ_MSG_ERR); + } + + if ((rc = nb_read_itter(fd, header, 4)) < 0) + return (rc); /* error */ + + if (rc != 4) + return (NB_READ_MSG_ERR_EOF); /* EOF */ + + /* + * The NetBIOS message length is limited to 17 bits but + * we use this layer for SMB over both NetBIOS and TCP + * (NetBIOS-less SMB). When using SMB over TCP the length + * is 24 bits but we are ignoring that for now because we + * don't expect any messages larger than 64KB. + */ + *type = header[0]; + length = ((header[1]&1) << 16) + (header[2]<<8) + header[3]; + + if (length > max_buf) + return (NB_READ_MSG_ERR_OVERFLOW); /* error overflow */ + + if ((rc = nb_read_itter(fd, buf, length)) != length) + return (NB_READ_MSG_ERR_UNDERFLOW); /* error underflow */ + + return (rc); +} + + +/* + * nb_read_itter + * + * We should never see descriptor 0 (stdin) or -1. + */ +static int +nb_read_itter(int fd, unsigned char *buf, unsigned cnt) +{ + int ix; + int rc; + + for (ix = 0; ix < cnt; ix += rc) { + if (fd == 0 || fd == -1) + return (-1); + + if ((rc = read(fd, buf+ix, cnt-ix)) < 0) + return (rc); + + if (rc == 0) + break; + } + + return (ix); +} + + +/* + * nb_first_level_name_encode + */ +static int +nb_first_level_name_encode(char *name, char *scope, + unsigned char *out, int max_out) +{ + unsigned char ch, len; + unsigned char *in; + unsigned char *lp; + unsigned char *op = out; + unsigned char *op_end = op + max_out; + + in = (unsigned char *)name; + *op++ = 0x20; + for (len = 0; ((ch = *in) != 0) && len < MAX_NETBIOS_NAME_SIZE; + len++, in++) { + *op++ = 'A' + ((ch >> 4) & 0xF); + *op++ = 'A' + ((ch) & 0xF); + } + + for (; len < MAX_NETBIOS_NAME_SIZE; len++) { + ch = ' '; + *op++ = 'A' + ((ch >> 4) & 0xF); + *op++ = 'A' + ((ch) & 0xF); + } + + in = (unsigned char *)scope; + len = 0; + lp = op++; + for (; op < op_end; in++) { + ch = *in; + if (ch == 0) { + if ((*lp = len) != 0) + *op++ = 0; + break; + } + if (ch == '.') { + *lp = (op - lp) - 1; + lp = op++; + len = 0; + } else { + *op++ = ch; + len++; + } + } + + return ((int)(op - out)); +} diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netuse.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netuse.c new file mode 100644 index 000000000000..508b258cd663 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netuse.c @@ -0,0 +1,458 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Tree connect and disconnect functions to support SMB shares. + * These functions are described in the CIFS draft 1.0 Protocol + * Specification (December 19, 1997). + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include + + +/* + * The table of shares set up with the domain controller. + */ +static struct sdb_netuse netuse_table[N_NETUSE_TABLE]; + +static int smbrdr_smb_tcon(struct sdb_session *session, + struct sdb_netuse *netuse, char *path, int path_len); + +static struct sdb_netuse *smbrdr_netuse_alloc(struct sdb_session *session, + char *sharename); +static int smbrdr_smb_tdcon(struct sdb_netuse *netuse); + +static void +smbrdr_netuse_clear(struct sdb_netuse *netuse) +{ + bzero(netuse, sizeof (struct sdb_netuse) - sizeof (mutex_t)); +} + +static void +smbrdr_netuse_free(struct sdb_netuse *netuse) +{ + smbrdr_netuse_clear(netuse); + (void) mutex_unlock(&netuse->mtx); +} + +/* + * mlsvc_tree_connect + * + * Establish a share (tree connect). We need to retrieve the session + * for the specified host and allocate a netuse structure. We set up + * the path here (UNC encoded) to make handling the malloc/free easier + * and pass everything on to smbrdr_smb_tcon where, if everything goes well, + * a valid tid will be stored in the netuse structure. + * + * On success, a pointer to the netuse is returned. Otherwise the + * netuse is cleared and a null pointer is returned. + */ +unsigned short +mlsvc_tree_connect(char *hostname, char *username, char *sharename) +{ + struct sdb_session *session; + struct sdb_netuse *netuse; + char *path; + int path_len; + + /* + * Make sure there is a session & logon for given info + */ + session = smbrdr_session_lock(hostname, username, SDB_SLCK_READ); + if (session == 0) { + syslog(LOG_ERR, "smbrdr: (tcon) no session for %s@%s", + username, hostname); + return (0); + } + + + if ((netuse = smbrdr_netuse_alloc(session, sharename)) == 0) { + syslog(LOG_ERR, "smbrdr: (tcon) init failed"); + smbrdr_session_unlock(session); + return (0); + } + + /* + * Add some padding for the back-slash separators + * and the null-terminator. + */ + path_len = SMB_PI_MAX_HOST + MAX_SHARE_NAME + 5; + + if ((path = (char *)malloc(path_len)) == 0) { + smbrdr_netuse_free(netuse); + smbrdr_session_unlock(session); + syslog(LOG_ERR, "smbrdr: (tcon) resource shortage"); + return (0); + } + + bzero(path, path_len); + (void) snprintf(path, path_len, "\\\\%s\\%s", hostname, sharename); + if (session->remote_caps & CAP_UNICODE) + path_len = mts_wcequiv_strlen(path); + else + path_len = strlen(path); + + if (smbrdr_smb_tcon(session, netuse, path, path_len) < 0) { + smbrdr_netuse_free(netuse); + smbrdr_session_unlock(session); + free(path); + syslog(LOG_ERR, "smbrdr: (tcon) failed connecting to %s", path); + return (0); + } + + free(path); + (void) mutex_unlock(&netuse->mtx); + smbrdr_session_unlock(session); + return (netuse->tid); +} + + +/* + * smbrdr_smb_tcon + * + * This message requests a share (tree connect) request to the server + * associated with the session. The password is not relevant here if + * the session was establishment using setup_andx. The outgoing tid + * will be ignored - a valid one will be returned by the server. + * + * Returns 0 on success. Otherwise returns a -ve error code. + */ +static int +smbrdr_smb_tcon(struct sdb_session *session, struct sdb_netuse *netuse, + char *path, int path_len) +{ + smb_hdr_t smb_hdr; + smbrdr_handle_t srh; + smb_msgbuf_t *mb; + unsigned short flags; + char *password; + unsigned short password_len; + char *service; + unsigned service_len; + unsigned short data_bytes; + DWORD status; + int rc; + + status = smbrdr_request_init(&srh, SMB_COM_TREE_CONNECT_ANDX, + session, &session->logon, 0); + + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "SmbrdrTcon: %s", xlate_nt_status(status)); + return (-1); + } + + mb = &srh.srh_mbuf; + + flags = 0; /* no flags */ + password = ""; + password_len = 1; /* including nul */ + service = "?????"; /* does this work? */ + service_len = strlen(service); + + /* + * Calculate the BCC. The path is in UNICODE + * but the service is in ASCII. + */ + data_bytes = password_len; + data_bytes += path_len + 1; + data_bytes += service_len + 1; + + rc = smb_msgbuf_encode(mb, "bb1.wwww#cus", + 4, /* smb_wct */ + 0xff, /* AndXCommand (none) */ + 0xffff, /* AndXOffset */ + flags, /* Flags */ + password_len, /* PasswordLength */ + data_bytes+1, /* smb_bcc */ + password_len, password, /* Password */ + path, /* Path */ + service); /* Service */ + + if (rc <= 0) { + syslog(LOG_ERR, "smbrdr_smb_tcon: encode failed"); + smbrdr_handle_free(&srh); + return (-1); + } + + status = smbrdr_exchange(&srh, &smb_hdr, 0); + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "SmbrdrTcon: %s", xlate_nt_status(status)); + rc = -1; + } else { + rc = 0; + } + + netuse->tid = smb_hdr.tid; + netuse->state = SDB_NSTATE_CONNECTED; + smbrdr_handle_free(&srh); + return (rc); +} + + +/* + * smbrdr_netuse_logoff + * + * This function can be used when closing a session to ensure that all + * shares associated with the specified session are disconnected and + * the resources released. We also notify the pipe interface to ensure + * that any pipes associated with this share are also closed. This + * function silently ignores errors because we have no idea what state + * the session is in. We are more interested in releasing resources. + */ +void +smbrdr_netuse_logoff(unsigned short uid) +{ + struct sdb_netuse *netuse; + int i; + + for (i = 0; i < N_NETUSE_TABLE; ++i) { + netuse = &netuse_table[i]; + (void) mutex_lock(&netuse->mtx); + if (netuse->uid == uid) + (void) smbrdr_smb_tdcon(netuse); + (void) mutex_unlock(&netuse->mtx); + } +} + +int +smbrdr_tree_disconnect(unsigned short tid) +{ + struct sdb_netuse *netuse; + int rc = -1; + + netuse = smbrdr_netuse_get(tid); + if (netuse) { + (void) smbrdr_smb_tdcon(netuse); + smbrdr_netuse_put(netuse); + rc = 0; + } + + return (rc); +} + +/* + * smbrdr_smb_tdcon + * + * Disconnect a share. This message informs the server that we no longer + * wish to access the resource specified by tid, obtained via a prior + * mlsvc_tree_connect. The tid is passed in the SMB header so the setup + * for this call is very straightforward. + * + * Returns 0 on success. Otherwise returns a -ve error code. + */ +static int +smbrdr_smb_tdcon(struct sdb_netuse *netuse) +{ + struct sdb_session *session; + smbrdr_handle_t srh; + smb_hdr_t smb_hdr; + DWORD status; + int rc; + + netuse->state = SDB_NSTATE_DISCONNECTING; + smbrdr_ofile_end_of_share(netuse->tid); + + if ((session = netuse->session) == 0) { + smbrdr_netuse_clear(netuse); + return (0); + } + + if ((session->state != SDB_SSTATE_NEGOTIATED) && + (session->state != SDB_SSTATE_DISCONNECTING)) { + smbrdr_netuse_clear(netuse); + return (0); + } + + status = smbrdr_request_init(&srh, SMB_COM_TREE_DISCONNECT, + session, &session->logon, netuse); + + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "smbrdr: (tdcon) %s", xlate_nt_status(status)); + /* should we clear here? */ + smbrdr_netuse_clear(netuse); + return (-1); + } + + rc = smb_msgbuf_encode(&srh.srh_mbuf, "bw.", 0, 0); + if (rc < 0) { + syslog(LOG_ERR, "smbrdr: (tdcon) encode failed"); + smbrdr_handle_free(&srh); + /* should we clear here? */ + smbrdr_netuse_clear(netuse); + return (rc); + } + + status = smbrdr_exchange(&srh, &smb_hdr, 0); + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "smbrdr: (tdcon) %s", xlate_nt_status(status)); + rc = -1; + } else { + rc = 0; + } + + smbrdr_handle_free(&srh); + smbrdr_netuse_clear(netuse); + return (rc); +} + + +/* + * smbrdr_netuse_alloc + * + * Find a slot in the table for a share. Each share is associated with + * a session and assigned a local drive letter name and a sharename. + * If a slot is already allocated to the specified share, a pointer to + * it is returned. Otherwise we allocate and initialize a new slot in + * the table. If the table is full, a null pointer will be returned. + * + * IMPORTANT! the returned netuse will be locked caller has to unlock + * it after it's done with the pointer. + */ +static struct sdb_netuse * +smbrdr_netuse_alloc(struct sdb_session *session, char *sharename) +{ + struct sdb_netuse *netuse; + int i; + + if (session == 0 || sharename == 0) { + syslog(LOG_ERR, "smbrdr: (tcon) invalid arg"); + return (0); + } + + for (i = 0; i < N_NETUSE_TABLE; ++i) { + netuse = &netuse_table[i]; + + (void) mutex_lock(&netuse->mtx); + if (netuse->state == SDB_NSTATE_START) { + netuse->session = session; + netuse->letter = i + '0'; + netuse->sid = session->sid; + netuse->uid = session->logon.uid; + netuse->tid = 0; + (void) strcpy(netuse->share, sharename); + netuse->state = SDB_NSTATE_INIT; + return (netuse); + } + (void) mutex_unlock(&netuse->mtx); + } + + syslog(LOG_WARNING, "smbrdr: (tcon) table full"); + return (0); +} + +/* + * smbrdr_netuse_put + * + * Unlock given netuse structure. + */ +void +smbrdr_netuse_put(struct sdb_netuse *netuse) +{ + (void) mutex_unlock(&netuse->mtx); +} + +/* + * smbrdr_netuse_get + * + * Find the netuse structure associated with the specified tid and + * return a pointer to it. A null pointer is returned if no match + * can be found. + * + * IMPORTANT! the returned netuse will be locked caller has to unlock + * it after it's done with the pointer. + */ +struct sdb_netuse * +smbrdr_netuse_get(int tid) +{ + struct sdb_session *session; + struct sdb_netuse *netuse; + int i; + + for (i = 0; i < N_NETUSE_TABLE; ++i) { + netuse = &netuse_table[i]; + + (void) mutex_lock(&netuse->mtx); + + if (netuse->tid == tid) { + session = netuse->session; + + /* + * status check: + * make sure all the structures are in the right state + */ + if (session && + (netuse->state == SDB_NSTATE_CONNECTED) && + (session->logon.state == SDB_LSTATE_SETUP) && + (session->state == SDB_SSTATE_NEGOTIATED)) { + /* sanity check */ + if ((netuse->sid == session->sid) && + (netuse->uid == session->logon.uid)) + return (netuse); + else + /* invalid structure */ + smbrdr_netuse_clear(netuse); + } + + } + + (void) mutex_unlock(&netuse->mtx); + } + + syslog(LOG_WARNING, "smbrdr: (lookup) no such TID %d", tid); + return (0); +} + +/* + * smbrdr_dump_netuse + */ +void +smbrdr_dump_netuse() +{ + struct sdb_netuse *netuse; + int i; + + for (i = 0; i < N_NETUSE_TABLE; ++i) { + netuse = &netuse_table[i]; + (void) mutex_lock(&netuse->mtx); + if (netuse->session) { + syslog(LOG_DEBUG, "tree[%d]: %s (tid=%d)", i, + netuse->share, netuse->tid); + syslog(LOG_DEBUG, "tree[%d]: session(%d), user(%d)", + i, netuse->session->sock, netuse->uid); + } + (void) mutex_unlock(&netuse->mtx); + } +} diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_read_andx.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_read_andx.c new file mode 100644 index 000000000000..ef90fa79ac65 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_read_andx.c @@ -0,0 +1,210 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB ReadX functions to support MLRPC. + */ + +#include +#include + +#include + +#include +#include +#include + +#define SMBRDR_READX_RSP_OVERHEAD \ + (NETBIOS_HDR_SZ + SMB_HEADER_LEN + sizeof (smb_read_andx_rsp_t)) +#define SMBRDR_READX_RSP_DATA_MAXLEN \ + (SMBRDR_REQ_BUFSZ - SMBRDR_READX_RSP_OVERHEAD) + +static int smbrdr_decode_readx_rsp(smb_msgbuf_t *, char *, unsigned, + smb_read_andx_rsp_t *); + +static void smbrdr_dump_readx_rsp(smb_read_andx_rsp_t *); + +/* + * smbrdr_rpc_readx + * + * Send SMB_COM_READ_ANDX request. + */ +int +smbrdr_rpc_readx(int fid, char *in_buf, int in_len) +{ + struct sdb_netuse *netuse; + struct sdb_ofile *ofile; + smb_read_andx_rsp_t rsp; + smbrdr_handle_t srh; + smb_msgbuf_t *mb; + DWORD status; + int rc, max_return; + + if ((ofile = smbrdr_ofile_get(fid)) == 0) + return (-1); + + netuse = ofile->netuse; + + status = smbrdr_request_init(&srh, SMB_COM_READ_ANDX, + netuse->session, &netuse->session->logon, netuse); + + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "SmbrdrReadAndx: %s", xlate_nt_status(status)); + smbrdr_ofile_put(ofile); + return (-1); + } + + mb = &(srh.srh_mbuf); + + max_return = (in_len > SMBRDR_READX_RSP_DATA_MAXLEN) ? + SMBRDR_READX_RSP_DATA_MAXLEN : in_len; + + rc = smb_msgbuf_encode(mb, "bbbwwlwwlwlw", + 12, /* Count of parameter words */ + 0xFF, /* Secondary (X) command; 0xFF = none */ + 0, /* Reserved (must be 0) */ + 0, /* Offset to next command WordCount */ + ofile->fid, /* File handle */ + 0, /* Offset in file to begin read */ + max_return, /* Max number of bytes to return */ + /* Reserved for obsolescent requests [0 = non-blocking read] */ + max_return, + /* + * High 16 bits of MaxCount if CAP_LARGE_READX; + * else MUST BE ZERO + */ + 0, + max_return, /* Reserved for obsolescent requests */ + /* Upper 32 bits of offset (only if WordCount is 12) */ + 0, + 0); /* Count of data bytes = 0 */ + + if (rc < 0) { + syslog(LOG_ERR, "SmbrdrReadAndx: smbrdr_prep_readx_req failed"); + smbrdr_handle_free(&srh); + smbrdr_ofile_put(ofile); + return (rc); + } + + smbrdr_lock_transport(); + + status = smbrdr_send(&srh); + if (status != NT_STATUS_SUCCESS) { + smbrdr_unlock_transport(); + smbrdr_handle_free(&srh); + smbrdr_ofile_put(ofile); + syslog(LOG_ERR, "SmbrdrReadAndx: send failed"); + return (-1); + } + + status = smbrdr_rcv(&srh, 1); + + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "SmbrdrReadAndx: nb_rcv failed"); + smbrdr_unlock_transport(); + smbrdr_handle_free(&srh); + smbrdr_ofile_put(ofile); + return (-1); + } + + rc = smbrdr_decode_readx_rsp(mb, in_buf, in_len, &rsp); + + if (rc < 0) { + syslog(LOG_ERR, "SmbrdrReadAndx: read decode failure!"); + smbrdr_unlock_transport(); + smbrdr_handle_free(&srh); + smbrdr_ofile_put(ofile); + return (-1); + } + + smbrdr_unlock_transport(); + smbrdr_handle_free(&srh); + smbrdr_ofile_put(ofile); + + return ((rc < 0) ? rc : rsp.DataLength); +} + +static void +smbrdr_dump_readx_rsp(smb_read_andx_rsp_t *rsp) +{ + + syslog(LOG_DEBUG, "[SmbReadX Rsp] WordCount:%x,AndXCmd:%x," + " AndXReserved:%x, AndXOffset:%d", + rsp->WordCount, rsp->AndXCmd, rsp->AndXReserved, rsp->AndXOffset); + + syslog(LOG_DEBUG, "[SmbReadX Rsp] Remaining:%d, Mode:%d, Reserved:%x, " + "DataLen:%d, DataOffset:%d, ByteCount: %d", + rsp->Remaining, rsp->DataCompactionMode, rsp->Reserved, + rsp->DataLength, rsp->DataOffset, rsp->ByteCount); +} + +/* + * smbrdr_decode_readx_rsp + * + * Decode the response from the SMB_COM_READ_ANDX request. The payload + * of the response is appended to the end of SmbTransact response data + * in the MLRPC receive buffer. + * + * Return -1 on error, 0 upon success. + */ +static int +smbrdr_decode_readx_rsp(smb_msgbuf_t *mb, + char *in, + unsigned in_len, + smb_read_andx_rsp_t *rsp) +{ + int rc; + + rc = smb_msgbuf_decode(mb, "bbbwwwwwwlwwww", + &rsp->WordCount, + &rsp->AndXCmd, + &rsp->AndXReserved, + &rsp->AndXOffset, + &rsp->Remaining, + &rsp->DataCompactionMode, + &rsp->Reserved, + &rsp->DataLength, + &rsp->DataOffset, + &rsp->DataLengthHigh, + &rsp->Reserved2[0], + &rsp->Reserved2[1], + &rsp->Reserved2[2], + &rsp->ByteCount); + + if (rc <= 0) + return (-1); + + smbrdr_dump_readx_rsp(rsp); + + /* it should never happen, but check anyway */ + if (rsp->DataLength > in_len) + return (-1); + + bcopy(mb->base + rsp->DataOffset, in, rsp->DataLength); + + return (0); +} diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_rpcpipe.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_rpcpipe.c new file mode 100644 index 000000000000..b89eddf27583 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_rpcpipe.c @@ -0,0 +1,518 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Functions to open and close named pipes. These functions are + * described in the CIFS 1.0 Protocol Specification (December 19, 1997). + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +static int smbrdr_smb_close(struct sdb_ofile *ofile); +static DWORD smbrdr_smb_ntcreate(struct sdb_ofile *ofile); +static struct sdb_ofile *smbrdr_ofile_alloc(struct sdb_netuse *netuse, + char *name); + +static void +smbrdr_ofile_clear(struct sdb_ofile *ofile) +{ + bzero(ofile, sizeof (struct sdb_ofile) - sizeof (mutex_t)); +} + +static void +smbrdr_ofile_free(struct sdb_ofile *ofile) +{ + smbrdr_ofile_clear(ofile); + (void) mutex_unlock(&ofile->mtx); +} + + +/* + * The ofile table. + */ +static struct sdb_ofile ofile_table[N_OFILE_TABLE]; + +static int mlsvc_pipe_recon_wait = 50; +static int mlsvc_pipe_recon_tries = 3; + + +/* + * mlsvc_open_pipe + * + * Open an RPC pipe on hostname. On success, return the fid. Otherwise + * returns a -ve error code. + */ +int +mlsvc_open_pipe(char *hostname, char *domain, char *username, char *pipename) +{ + struct sdb_netuse *netuse; + struct sdb_ofile *ofile; + unsigned short tid; + DWORD status; + int retry; + struct timespec st; + + tid = mlsvc_tree_connect(hostname, username, "IPC$"); + if (tid == 0) { + syslog(LOG_ERR, "smbrdr: (open) %s %s %s %s %s", + hostname, domain, username, pipename, + xlate_nt_status(NT_STATUS_UNEXPECTED_NETWORK_ERROR)); + return (-1); + } + + netuse = smbrdr_netuse_get(tid); + if (netuse == 0) { + syslog(LOG_ERR, "smbrdr: (open) %s %s %s %s %s", + hostname, domain, username, pipename, + xlate_nt_status(NT_STATUS_CONNECTION_INVALID)); + return (-1); + } + + if ((ofile = smbrdr_ofile_alloc(netuse, pipename)) == 0) { + syslog(LOG_ERR, "smbrdr: (open) %s %s %s %s %s", + hostname, domain, username, pipename, + xlate_nt_status(NT_STATUS_INSUFFICIENT_RESOURCES)); + smbrdr_netuse_put(netuse); + return (-1); + } + + status = NT_STATUS_OPEN_FAILED; + + for (retry = 0; retry < mlsvc_pipe_recon_tries; retry++) { + status = smbrdr_smb_ntcreate(ofile); + + switch (status) { + case NT_STATUS_SUCCESS: + (void) mutex_unlock(&ofile->mtx); + smbrdr_netuse_put(netuse); + return (ofile->fid); + + case NT_STATUS_PIPE_NOT_AVAILABLE: + case NT_STATUS_PIPE_BUSY: + /* + * The server might return this error if it is + * temporarily busy or unable to create a pipe. + * We wait here before trying again to see if + * the pipe becomes available. + */ + st.tv_sec = 0; + st.tv_nsec = mlsvc_pipe_recon_wait * 1000000; + (void) nanosleep(&st, 0); + break; + + default: + /* + * Something else went wrong: no more retries. + */ + retry = mlsvc_pipe_recon_tries; + break; + } + } + + syslog(LOG_DEBUG, "smbrdr: (open) %s %s %s %s %s", + hostname, domain, username, pipename, + xlate_nt_status(status)); + smbrdr_ofile_free(ofile); + smbrdr_netuse_put(netuse); + return (-1); +} + +/* + * mlsvc_close_pipe + * + * Close the named pipe represented by fid. + */ +int +mlsvc_close_pipe(int fid) +{ + struct sdb_ofile *ofile; + unsigned short tid; + int rc; + + if ((ofile = smbrdr_ofile_get(fid)) == 0) { + syslog(LOG_ERR, "mlsvc_close_pipe: unknown file (%d)", fid); + return (-1); + } + + tid = ofile->tid; + rc = smbrdr_smb_close(ofile); + smbrdr_ofile_put(ofile); + + if (rc == 0) + (void) smbrdr_tree_disconnect(tid); + + return (rc); +} + +/* + * smbrdr_ofile_put + * + * Unlock given ofile structure. + */ +void +smbrdr_ofile_put(struct sdb_ofile *ofile) +{ + if (ofile) + (void) mutex_unlock(&ofile->mtx); +} + +/* + * smbrdr_ofile_get + * + * Locate the ofile for the specified fid. Just to be safe, ensure that + * the netuse pointer is valid. Return a pointer to the ofile structure. + * Return a null pointer if a valid ofile cannot be found. + */ +struct sdb_ofile * +smbrdr_ofile_get(int fid) +{ + struct sdb_session *session; + struct sdb_netuse *netuse; + struct sdb_ofile *ofile; + int i; + + for (i = 0; i < N_OFILE_TABLE; ++i) { + ofile = &ofile_table[i]; + + (void) mutex_lock(&ofile->mtx); + + if (ofile->fid == fid) { + session = ofile->session; + netuse = ofile->netuse; + + /* + * status check: + * make sure all the structures are in the right state + */ + if (session && netuse && + (ofile->state == SDB_FSTATE_OPEN) && + (netuse->state == SDB_NSTATE_CONNECTED) && + (session->logon.state == SDB_LSTATE_SETUP) && + (session->state == SDB_SSTATE_NEGOTIATED)) { + /* sanity check */ + if ((ofile->sid == session->sid) && + (ofile->uid == session->logon.uid) && + (ofile->tid == netuse->tid)) { + return (ofile); + } else { + /* invalid structure */ + smbrdr_ofile_clear(ofile); + } + } + } + + (void) mutex_unlock(&ofile->mtx); + } + + syslog(LOG_WARNING, "smbrdr: (lookup) no such FID %d", fid); + return (0); +} + +/* + * smbrdr_ofile_end_of_share + * + * This function can be used when closing a share to ensure that all + * ofiles resources are released. Don't call mlsvc_close_pipe because + * that will call mlsvc_smb_tdcon and we don't know what state + * the share is in. The server will probably close all files anyway. + * We are more interested in releasing the ofile resources. + */ +void +smbrdr_ofile_end_of_share(unsigned short tid) +{ + struct sdb_ofile *ofile; + int i; + + for (i = 0; i < N_OFILE_TABLE; ++i) { + ofile = &ofile_table[i]; + (void) mutex_lock(&ofile->mtx); + if (ofile->tid == tid) + (void) smbrdr_smb_close(ofile); + (void) mutex_unlock(&ofile->mtx); + } +} + +/* + * smbrdr_dump_ofiles + * + * Dump the open files table. + */ +void +smbrdr_dump_ofiles() +{ + struct sdb_ofile *ofile; + struct sdb_netuse *netuse; + int i; + + for (i = 0; i < N_OFILE_TABLE; ++i) { + ofile = &ofile_table[i]; + (void) mutex_lock(&ofile->mtx); + netuse = ofile->netuse; + + if (netuse) { + syslog(LOG_DEBUG, "file[%d]: %s (fid=%d)", i, + ofile->path, ofile->fid); + syslog(LOG_DEBUG, + "file[%d]: session(%d), user(%d), tree(%d)", + i, netuse->session->sock, netuse->uid, + netuse->tid); + } + (void) mutex_unlock(&ofile->mtx); + } +} + +/* + * Private Functions + */ + +/* + * smbrdr_smb_close + * + * Send SMBClose request for the given open file. + */ +static int +smbrdr_smb_close(struct sdb_ofile *ofile) +{ + struct sdb_session *session; + struct sdb_netuse *netuse; + struct sdb_logon *logon; + smbrdr_handle_t srh; + smb_hdr_t smb_hdr; + DWORD status; + int fid; + int rc; + + if (ofile == 0) + return (0); + + ofile->state = SDB_FSTATE_CLOSING; + + if ((session = ofile->session) == 0) { + smbrdr_ofile_clear(ofile); + return (0); + } + + if ((session->state != SDB_SSTATE_NEGOTIATED) && + (session->state != SDB_SSTATE_DISCONNECTING)) { + smbrdr_ofile_clear(ofile); + return (0); + } + + fid = ofile->fid; + + netuse = ofile->netuse; + logon = &session->logon; + + status = smbrdr_request_init(&srh, SMB_COM_CLOSE, + session, logon, netuse); + + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "SmbrdrClose: %s", xlate_nt_status(status)); + smbrdr_ofile_clear(ofile); + return (-1); + } + + rc = smb_msgbuf_encode(&srh.srh_mbuf, + "(wct)b (fid)w (lwrtm)l (bcc)w (pad).", + 3, /* WordCount */ + fid, /* Fid */ + 0x00000000ul, /* LastWriteTime */ + 0); /* ByteCount */ + + if (rc <= 0) { + syslog(LOG_ERR, "SmbrdrClose: encode failed"); + smbrdr_handle_free(&srh); + smbrdr_ofile_clear(ofile); + return (-1); + } + + status = smbrdr_exchange(&srh, &smb_hdr, 0); + if (status != NT_STATUS_SUCCESS) + syslog(LOG_ERR, "SmbrdrClose: %s", xlate_nt_status(status)); + + smbrdr_handle_free(&srh); + smbrdr_ofile_clear(ofile); + return (0); +} + +/* + * smbrdr_ofile_alloc + * + * Allocate an ofile for the specified name. File info is associated + * with a share so we need a valid share before calling this function. + * If a slot is already allocated to the specified file, a pointer to + * that slot is returned. Otherwise we allocate and initialize a new + * slot in the table. If the table is full, a null pointer will be + * returned. + */ +static struct sdb_ofile * +smbrdr_ofile_alloc(struct sdb_netuse *netuse, char *name) +{ + struct sdb_ofile *ofile; + int i; + + for (i = 0; i < N_OFILE_TABLE; ++i) { + ofile = &ofile_table[i]; + + (void) mutex_lock(&ofile->mtx); + if (ofile->netuse == 0) { + + ofile->session = netuse->session; + ofile->netuse = netuse; + ofile->sid = netuse->session->sid; + ofile->uid = netuse->session->logon.uid; + ofile->tid = netuse->tid; + ofile->fid = 0; + (void) strcpy(ofile->path, name); + ofile->state = SDB_FSTATE_INIT; + return (ofile); + } + + (void) mutex_unlock(&ofile->mtx); + } + + syslog(LOG_WARNING, "smbrdr: (open) table full"); + return (0); +} + +/* + * smbrdr_smb_ntcreate + * + * This will do an SMB_COM_NT_CREATE_ANDX with lots of default values. + * All of the underlying session and share data should already be set + * up before we get here. If everything works we'll get a valid fid. + */ +static DWORD +smbrdr_smb_ntcreate(struct sdb_ofile *ofile) +{ + struct sdb_logon *logon; + struct sdb_netuse *netuse; + struct sdb_session *sess; + smbrdr_handle_t srh; + smb_hdr_t smb_hdr; + smb_msgbuf_t *mb; + char *path; + unsigned path_len; + int data_bytes; + int rc; + unsigned short fid; + int null_size; + DWORD status; + + netuse = ofile->netuse; + sess = netuse->session; + logon = &sess->logon; + + /* + * If this was a general purpose interface, we should support + * full UNC semantics but we only use this for RPC over named + * pipes with well-known endpoints. + */ + path_len = strlen(ofile->path) + 2; + path = alloca(path_len); + + if (ofile->path[0] != '\\') + (void) snprintf(path, path_len, "\\%s", ofile->path); + else + (void) strcpy(path, ofile->path); + + if (sess->remote_caps & CAP_UNICODE) { + path_len = mts_wcequiv_strlen(path); + null_size = sizeof (mts_wchar_t); + } else { + path_len = strlen(path); + null_size = sizeof (char); + } + + syslog(LOG_DEBUG, "SmbRdrNtCreate: %d %s", path_len, path); + + status = smbrdr_request_init(&srh, SMB_COM_NT_CREATE_ANDX, + sess, logon, netuse); + + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "SmbrdrNtCreate: %s", xlate_nt_status(status)); + return (NT_STATUS_INVALID_PARAMETER_1); + } + + mb = &srh.srh_mbuf; + + data_bytes = path_len + null_size; + + rc = smb_msgbuf_encode(mb, + "(wct)b (andx)b1.w (resv). (nlen)w (flg)l" + "(rdf)l (dacc)l (allo)q (efa)l (shr)l (cdisp)l (copt)l (impl)l" + "(secf)b (bcc)w (name)u", + 24, /* smb_wct */ + 0xff, /* AndXCommand (none) */ + 0x0000, /* AndXOffset */ + path_len, /* Unicode NameLength */ + 0x00000006ul, /* Flags (oplocks) */ + 0, /* RootDirectoryFid */ + 0x0002019Ful, /* DesiredAccess */ + 0x0ull, /* AllocationSize */ + 0x00000000ul, /* ExtFileAttributes */ + 0x00000003ul, /* ShareAccess (RW) */ + 0x00000001ul, /* CreateDisposition (OpenExisting) */ + 0x00000000ul, /* CreateOptions */ + 0x00000002ul, /* ImpersonationLevel */ + 0x01u, /* SecurityFlags */ + data_bytes, /* smb_bcc */ + path); /* Name */ + + if (rc <= 0) { + smbrdr_handle_free(&srh); + return (NT_STATUS_INVALID_PARAMETER_1); + } + + status = smbrdr_exchange(&srh, &smb_hdr, 0); + if (status != NT_STATUS_SUCCESS) { + smbrdr_handle_free(&srh); + return (NT_SC_VALUE(status)); + } + + rc = smb_msgbuf_decode(mb, "(wct). (andx)4. (opl)1. (fid)w", &fid); + if (rc <= 0) { + smbrdr_handle_free(&srh); + return (NT_STATUS_INVALID_PARAMETER_2); + } + + ofile->fid = fid; + ofile->state = SDB_FSTATE_OPEN; + syslog(LOG_DEBUG, "SmbRdrNtCreate: fid=%d", ofile->fid); + smbrdr_handle_free(&srh); + return (NT_STATUS_SUCCESS); +} diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_session.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_session.c new file mode 100644 index 000000000000..535ad537d9a1 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_session.c @@ -0,0 +1,889 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module provides the netbios and SMB negotiation, connect and + * disconnect interface. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + + +static uint16_t smbrdr_ports[] = { + SMB_SRVC_TCP_PORT, + SSN_SRVC_TCP_PORT +}; + +static int smbrdr_nports = sizeof (smbrdr_ports) / sizeof (smbrdr_ports[0]); + +/* + * Pointer to the PDC location interface. + * To be set up by SMB when it loads. + */ +static mlsvc_locate_pdc_t mlsvc_locate_pdc; + +/* + * This is a temporary hack to stop the DC from closing a session + * due to inactivity. + */ +#define MLSVC_SESSION_FORCE_KEEPALIVE 10 + +/* + * This is the session data table. + * + * The rwlock synchronizes access to the session table + * + * The mutex is to make session lookup and create atomic + * so we don't end up with two sessions with the same + * system. + */ +static struct sdb_session session_table[MLSVC_DOMAIN_MAX]; +static mutex_t smbrdr_screate_mtx; +static unsigned int session_id = 0; + +static struct sdb_session *smbrdr_session_init(smb_ntdomain_t *di); +static int smbrdr_trnsprt_connect(struct sdb_session *, uint16_t); +static int smbrdr_session_connect(smb_ntdomain_t *di); +static int smbrdr_smb_negotiate(struct sdb_session *session); +static int smbrdr_smb_echo(struct sdb_session *session); +static void smbrdr_session_disconnect(struct sdb_session *session, int cleanup); +static int smbrdr_locate_dc(char *domain); + +static void +smbrdr_session_clear(struct sdb_session *session) +{ + bzero(session, sizeof (struct sdb_session) - sizeof (rwlock_t)); +} + +/* + * mlsvc_install_pdc_cb + * + * Function to be called by SMB initialization code to set up a + * callback to the PDC location interface. + */ +void +mlsvc_install_pdc_cb(mlsvc_locate_pdc_t locate_pdc_cb) +{ + mlsvc_locate_pdc = locate_pdc_cb; +} + +/* + * mlsvc_locate_domain_controller + * + * Locate a domain controller. Note that this may close an existing + * connection to the current domain controller. + */ +int +mlsvc_locate_domain_controller(char *domain) +{ + if (mlsvc_locate_pdc) + return (mlsvc_locate_pdc(domain)); + + return (0); +} + +/* + * Entry pointy for smbrdr initialization. + */ +void +smbrdr_init(void) +{ + smbrdr_ipc_init(); +} + +/* + * mlsvc_disconnect + * + * Disconnects the session with given server. + */ +void +mlsvc_disconnect(char *server) +{ + struct sdb_session *session; + + session = smbrdr_session_lock(server, 0, SDB_SLCK_WRITE); + if (session) { + smbrdr_session_disconnect(session, 0); + smbrdr_session_unlock(session); + } +} + +/* + * smbrdr_negotiate + * + * Negotiate a session with a domain controller in the specified domain. + * The domain must be one of values from the smbinfo that indicates the + * resource domain or the account domain. + * + * If a session already exists, we can use that one. Otherwise we create + * a new one. This sets up the session key and session security info that + * we'll need later to authenticate the user. The session security info + * is returned to support the SMB client pass-through authentication + * interface. + * + * Returns 0 on success, otherwise -1. + */ +int +smbrdr_negotiate(char *domain_name) +{ + struct sdb_session *session = 0; + smb_ntdomain_t *di; + int retry = 1; + int res = 0; + + if ((di = smb_getdomaininfo(0)) == 0) { + /* + * Attempting to locate a domain controller + * will shutdown an existing PDC connection. + */ + (void) smbrdr_locate_dc(domain_name); + di = smb_getdomaininfo(0); + } + + if (di == 0) { + syslog(LOG_ERR, "smbrdr: negotiate (cannot access domain)"); + return (-1); + } + + /* + * The mutex is to make session lookup and create atomic + * so we don't end up with two sessions with the same + * server. + */ + (void) mutex_lock(&smbrdr_screate_mtx); + while (retry > 0) { + session = smbrdr_session_lock(di->server, 0, SDB_SLCK_WRITE); + if (session != 0) { + if (nb_keep_alive(session->sock) == 0) { + /* session is good, use it */ + smbrdr_session_unlock(session); + break; + } else { + /* stale session */ + session->state = SDB_SSTATE_STALE; + smbrdr_session_unlock(session); + } + } + + if (smbrdr_session_connect(di) != 0) { + if (retry > 0) { + /* Do we really need to do this here? */ + (void) smbrdr_locate_dc(domain_name); + di = smb_getdomaininfo(0); + if (di == 0) { + syslog(LOG_ERR, "smbrdr: negotiate" + " (cannot access domain)"); + res = -1; + break; + } + retry--; + } + } else { + /* session is created */ + retry = 0; + } + } + (void) mutex_unlock(&smbrdr_screate_mtx); + + return (res); +} + +/* + * smbrdr_session_connect + * + * This is the entry point for establishing an SMB connection to a + * domain controller. A session structure is allocated, a netbios + * session is set up and the SMB protocol is negotiated. If this is + * successful, the returned session structure can be used to logon + * to the the domain. A null pointer is returned if the connect fails. + */ +static int +smbrdr_session_connect(smb_ntdomain_t *di) +{ + struct sdb_session *session; + uint16_t port; + int rc = 0; + + /* + * smbrdr_session_init() will lock the session so that it wouldn't + * be accessible until it's established otherwise another thread + * might get access to a session which is not fully established. + */ + if ((session = smbrdr_session_init(di)) == 0) { + syslog(LOG_ERR, "smbrdr: session init failed"); + return (-1); + } + + for (port = 0; port < smbrdr_nports; ++port) { + syslog(LOG_DEBUG, "smbrdr: trying port %d", + smbrdr_ports[port]); + + rc = smbrdr_trnsprt_connect(session, smbrdr_ports[port]); + + if (rc == 0) { + syslog(LOG_DEBUG, "smbrdr: connected port %d", + smbrdr_ports[port]); + break; + } + } + + if (rc < 0) { + smbrdr_session_clear(session); + smbrdr_session_unlock(session); + syslog(LOG_ERR, "smbrdr: NBT/TCP connect failed"); + return (-1); + } + + if (smbrdr_smb_negotiate(session) < 0) { + (void) close(session->sock); + smbrdr_session_clear(session); + smbrdr_session_unlock(session); + syslog(LOG_ERR, "smbrdr: SMB negotiate failed"); + return (-1); + } + + smbrdr_session_unlock(session); + return (0); +} + + +/* + * smbrdr_trnsprt_connect + * + * Set up the TCP/IP and NETBIOS protocols for a session. This is just + * standard socket sutff. The paranoia check for socket descriptor 0 + * is because we had a problem with this value and the console telnet + * interface will lock up if we use and/or close stdin (0). + * + * Return 0 on success. Otherwise return (-1) to indicate a problem. + */ +static int +smbrdr_trnsprt_connect(struct sdb_session *sess, uint16_t port) +{ + char hostname[MAXHOSTNAMELEN]; + struct sockaddr_in sin; + int sock, rc; + mts_wchar_t unicode_server_name[SMB_PI_MAX_DOMAIN]; + char server_name[SMB_PI_MAX_DOMAIN]; + unsigned int cpid = oem_get_smb_cpid(); + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) <= 0) { + /* + * We should never see descriptor 0 (stdin). + */ + syslog(LOG_ERR, "smbrdr: socket(%d) failed (%s)", sock, + strerror(errno)); + return (-1); + } + + bzero(&sin, sizeof (struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = sess->di.ipaddr; + sin.sin_port = htons(port); + + if ((rc = connect(sock, (struct sockaddr *)&sin, sizeof (sin))) < 0) { + syslog(LOG_ERR, "smbrdr: connect failed (%s)", strerror(errno)); + if (sock != 0) + (void) close(sock); + return (-1); + } + + (void) mts_mbstowcs(unicode_server_name, sess->di.server, + SMB_PI_MAX_DOMAIN); + rc = unicodestooems(server_name, unicode_server_name, + SMB_PI_MAX_DOMAIN, cpid); + if (rc == 0) { + syslog(LOG_ERR, "smbrdr: unicode conversion failed"); + if (sock != 0) + (void) close(sock); + return (-1); + } + + /* + * If we are using NetBIOS, we need to set up a NETBIOS session. + * This typically implies that we will be using port 139. + * Otherwise, we're doing NetBIOS-less SMB, i.e. SMB over TCP, + * which is typically on port 445. + */ + if (port == SSN_SRVC_TCP_PORT) { + if (smb_getnetbiosname(hostname, MAXHOSTNAMELEN) != 0) { + syslog(LOG_ERR, "smbrdr: no hostname"); + if (sock != 0) + (void) close(sock); + return (-1); + } + + rc = nb_session_request(sock, + server_name, sess->scope, hostname, sess->scope); + + if (rc != 0) { + syslog(LOG_ERR, + "smbrdr: NBT session request to %s failed %d", + server_name, rc); + if (sock != 0) + (void) close(sock); + return (-1); + } + } + + sess->sock = sock; + sess->port = port; + syslog(LOG_DEBUG, "smbrdr: connected on port %d", port); + sess->state = SDB_SSTATE_CONNECTED; + return (0); +} + +/* + * smbrdr_smb_negotiate + * + * Negotiate the protocol we are going to use as described in CIFS + * section 4.1.1. The only protocol we support is NT LM 0.12, so we + * really expect to see dialect 0 in the response. The only other + * data gathered is the session key. + * + * Negotiate using ASCII strings. + * + * Return 0 on success. Otherwise return a -ve error code. + */ +static int +smbrdr_smb_negotiate(struct sdb_session *sess) +{ + unsigned short dialect; + smbrdr_handle_t srh; + smb_hdr_t smb_hdr; + smb_msgbuf_t *mb; + DWORD status; + int rc; + uint8_t tmp_secmode; + uint8_t tmp_clen; + + status = smbrdr_request_init(&srh, SMB_COM_NEGOTIATE, sess, 0, 0); + + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "smbrdr: negotiate (%s)", + xlate_nt_status(status)); + return (-1); + } + + mb = &srh.srh_mbuf; + rc = smb_msgbuf_encode(mb, "(wct)b (bcc)w (dialect)bs", + 0, /* smb_wct */ + 12, /* smb_bcc */ + 0x02, /* dialect marker */ + "NT LM 0.12"); /* only dialect we care about */ + + if (rc <= 0) { + syslog(LOG_ERR, "smbrdr: negotiate (encode failed)"); + smbrdr_handle_free(&srh); + return (-1); + } + + status = smbrdr_exchange(&srh, &smb_hdr, 0); + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "smbrdr: negotiate (%s)", + xlate_nt_status(status)); + smbrdr_handle_free(&srh); + return (-1); + } + + sess->secmode = 0; + sess->sesskey = 0; + sess->challenge_len = 0; + + rc = smb_msgbuf_decode(mb, + "(wordcnt)1.(dialect)w(secm)b12.(skey)l(cap)l10.(klen)b2.", + &dialect, &tmp_secmode, &sess->sesskey, &sess->remote_caps, + &tmp_clen); + + if (rc <= 0 || dialect != 0) { + syslog(LOG_ERR, "smbrdr: negotiate (response error)"); + smbrdr_handle_free(&srh); + return (-1); + } + sess->secmode = tmp_secmode; + sess->challenge_len = tmp_clen; + + rc = smb_msgbuf_decode(mb, "#c", + sess->challenge_len, sess->challenge_key); + if (rc <= 0) { + syslog(LOG_ERR, "smbrdr: negotiate (decode error)"); + smbrdr_handle_free(&srh); + return (-1); + } + + smbrdr_handle_free(&srh); + + if ((sess->secmode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) && + (sess->secmode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED)) { + sess->sign_ctx.ssc_flags |= SMB_SCF_REQUIRED; + syslog(LOG_DEBUG, "smbrdr: %s requires signing", + sess->di.server); + } + + sess->state = SDB_SSTATE_NEGOTIATED; + return (0); +} + +/* + * smbrdr_session_init + * + * Allocate an available slot in session table for the specified domain + * information. + * + * IMPORTANT! the returned session will be locked caller has to unlock + * it by calling smbrdr_session_unlock() after it's done with + * the pointer. + */ +static struct sdb_session * +smbrdr_session_init(smb_ntdomain_t *di) +{ + struct sdb_session *session = 0; + int i; + char *p; + + if (di == 0) + return (0); + + for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) { + session = &session_table[i]; + + (void) rw_wrlock(&session->rwl); + if (session->state == SDB_SSTATE_START) { + smbrdr_session_clear(session); + bcopy(di, &session->di, sizeof (smb_ntdomain_t)); + (void) utf8_strupr(session->di.domain); + (void) utf8_strupr(session->di.server); + + smb_config_rdlock(); + p = smb_config_getstr(SMB_CI_NBSCOPE); + (void) strlcpy(session->scope, p, SMB_PI_MAX_SCOPE); + smb_config_unlock(); + + (void) strlcpy(session->native_os, + "Solaris", SMB_PI_MAX_NATIVE_OS); + (void) strlcpy(session->native_lanman, + "Windows NT 4.0", SMB_PI_MAX_LANMAN); + session->sock = -1; + session->port = smbrdr_ports[0]; + session->smb_flags = SMB_FLAGS_CANONICALIZED_PATHS + | SMB_FLAGS_CASE_INSENSITIVE; + + session->smb_flags2 = SMB_FLAGS2_KNOWS_LONG_NAMES + | SMB_FLAGS2_KNOWS_EAS; + + /* + * Note that by sending vc=0 server will shutdown all + * the other connections with NAS if there is any. + */ + session->vc = 0; + session->sid = ++session_id; + if (session->sid == 0) + session->sid = 1; + session->state = SDB_SSTATE_INIT; + return (session); + } + (void) rw_unlock(&session->rwl); + } + + syslog(LOG_WARNING, "smbrdr: no session available"); + return (0); +} + +/* + * smbrdr_session_disconnect + * + * This is the entry point for disconnecting an SMB connection. Ensure + * that all logons and shares associated with this session are + * terminated and then free the session. + * + * if 'cleanup' is 1 it means that only sessions that are not active + * should be cleaned up. if 'cleanup' is 0 disconnect the session in any + * states. + */ +static void +smbrdr_session_disconnect(struct sdb_session *session, int cleanup) +{ + int state; + + if (session == 0) { + syslog(LOG_ERR, "smbrdr: (disconnect) null session"); + return; + } + + state = session->state; + if ((state != SDB_SSTATE_DISCONNECTING) && + (state != SDB_SSTATE_CLEANING) && + (state != SDB_SSTATE_START)) { + if ((cleanup == 0) || (state == SDB_SSTATE_STALE)) { + /* + * if session is in stale state it means the connection + * is lost so no logoff, tdcon, or close can actually + * be sent, thus only cleanup our side. + */ + session->state = (state == SDB_SSTATE_STALE) + ? SDB_SSTATE_CLEANING : SDB_SSTATE_DISCONNECTING; + (void) smbrdr_smb_logoff(&session->logon); + nb_close(session->sock); + smbrdr_session_clear(session); + } + } +} + +/* + * smbrdr_session_unlock + * + * Unlock given session structure. + */ +void +smbrdr_session_unlock(struct sdb_session *session) +{ + if (session) + (void) rw_unlock(&session->rwl); +} + +/* + * smbrdr_session_lock + * + * Lookup the session associated with the specified domain controller. + * If a match is found, we return a pointer to the session, Otherwise + * we return null. Only sessions in "negotiated" state are checked. + * This mechanism is very simple and implies that we + * should only ever have one session open to any domain controller. + * + * IMPORTANT! the returned session will be locked caller has to unlock + * it by calling smbrdr_session_unlock() after it's done with + * the pointer. + */ +struct sdb_session * +smbrdr_session_lock(char *server, char *username, int lmode) +{ + struct sdb_session *session; + int i; + + if (server == 0) { + syslog(LOG_ERR, "smbrdr: (lookup) no server specified"); + return (0); + } + + for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) { + session = &session_table[i]; + + (lmode == SDB_SLCK_READ) ? (void) rw_rdlock(&session->rwl) : + (void) rw_wrlock(&session->rwl); + + if ((session->state == SDB_SSTATE_NEGOTIATED) && + (strcasecmp(session->di.server, server) == 0)) { + if (username) { + if (strcasecmp(username, + session->logon.username) == 0) + return (session); + + (void) rw_unlock(&session->rwl); + return (0); + } + return (session); + } + + (void) rw_unlock(&session->rwl); + } + + return (0); +} + +/* + * mlsvc_session_native_values + * + * Given a file id (i.e. a named pipe fid), return the remote native + * OS and LM values for the associated session. + */ +int +mlsvc_session_native_values(int fid, int *remote_os, + int *remote_lm, int *pdc_type) +{ + struct sdb_session *session; + struct sdb_netuse *netuse; + struct sdb_ofile *ofile; + + if (remote_os == 0 || remote_lm == 0) { + syslog(LOG_ERR, "mlsvc_session_native_values: null"); + return (-1); + } + + if ((ofile = smbrdr_ofile_get(fid)) == 0) { + syslog(LOG_ERR, + "mlsvc_session_native_values: unknown file (%d)", fid); + return (-1); + } + + netuse = ofile->netuse; + session = netuse->session; + + *remote_os = session->remote_os; + *remote_lm = session->remote_lm; + if (pdc_type) + *pdc_type = session->pdc_type; + smbrdr_ofile_put(ofile); + return (0); +} + +/* + * smbrdr_disconnect_sessions + * + * Disconnects/cleanups all the sessions + */ +static void +smbrdr_disconnect_sessions(int cleanup) +{ + struct sdb_session *session; + int i; + + for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) { + session = &session_table[i]; + (void) rw_wrlock(&session->rwl); + smbrdr_session_disconnect(&session_table[i], cleanup); + (void) rw_unlock(&session->rwl); + } +} + + +/* + * mlsvc_check_sessions + * + * This function should be run in an independent thread. At the time of + * writing it is called periodically from an infinite loop in the start + * up thread once initialization is complete. It sends a NetBIOS keep- + * alive message on each active session and handles cleanup if a session + * is closed from the remote end. Testing demonstrated that the domain + * controller will close a session after 15 minutes of inactivity. Note + * that neither NetBIOS keep-alive nor SMB echo is deemed as activity + * in this case, however, RPC requests appear to reset the timeout and + * keep the session open. Note that the NetBIOS request does stop the + * remote NetBIOS layer from timing out the connection. + */ +void +mlsvc_check_sessions(void) +{ + static int session_keep_alive; + struct sdb_session *session; + smb_ntdomain_t di; + int i; + + ++session_keep_alive; + + for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) { + session = &session_table[i]; + + (void) rw_wrlock(&session->rwl); + + if (session->state < SDB_SSTATE_CONNECTED) { + (void) rw_unlock(&session->rwl); + continue; + } + + /* + * NetBIOS is only used on with port 139. The keep alive + * is not relevant over NetBIOS-less SMB over port 445. + * This is just to see if the socket is still alive. + */ + if (session->port == SSN_SRVC_TCP_PORT) { + if (nb_keep_alive(session->sock) != 0) { + session->state = SDB_SSTATE_STALE; + (void) rw_unlock(&session->rwl); + continue; + } + } + + if (session_keep_alive >= MLSVC_SESSION_FORCE_KEEPALIVE) { + if (smbrdr_smb_echo(session) != 0) { + syslog(LOG_WARNING, + "smbrdr: monitor[%s] cannot contact %s", + session->di.domain, session->di.server); + (void) memcpy(&di, &session->di, + sizeof (smb_ntdomain_t)); + session->state = SDB_SSTATE_STALE; + (void) rw_unlock(&session->rwl); + if (smb_getdomaininfo(0) == 0) + (void) smbrdr_locate_dc(di.domain); + } + } else + (void) rw_unlock(&session->rwl); + } + + if (session_keep_alive >= MLSVC_SESSION_FORCE_KEEPALIVE) { + session_keep_alive = 0; + /* cleanup */ + smbrdr_disconnect_sessions(1); + } +} + +/* + * smbrdr_dump_sessions + * + * Debug function to dump the session table. + */ +void +smbrdr_dump_sessions(void) +{ + struct sdb_session *session; + struct sdb_logon *logon; + char ipstr[16]; + int i; + + for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) { + session = &session_table[i]; + + (void) rw_rdlock(&session->rwl); + if (session->state != SDB_SSTATE_START) { + (void) inet_ntop(AF_INET, + (const void *)(&session->di.ipaddr), + ipstr, sizeof (ipstr)); + + syslog(LOG_DEBUG, "session[%d]: state=%d", + i, session->state); + syslog(LOG_DEBUG, "session[%d]: %s %s (%s)", i, + session->di.domain, session->di.server, ipstr); + syslog(LOG_DEBUG, "session[%d]: %s %s (sock=%d)", i, + session->native_os, session->native_lanman, + session->sock); + + logon = &session->logon; + if (logon->type != SDB_LOGON_NONE) + syslog(LOG_DEBUG, "logon[%d]: %s (uid=%d)", + i, logon->username, logon->uid); + } + (void) rw_unlock(&session->rwl); + } +} + +/* + * mlsvc_echo + */ +int +mlsvc_echo(char *server) +{ + struct sdb_session *session; + int res = 0; + + if ((session = smbrdr_session_lock(server, 0, SDB_SLCK_WRITE)) == 0) + return (1); + + if (smbrdr_smb_echo(session) != 0) { + session->state = SDB_SSTATE_STALE; + res = -1; + } + + smbrdr_session_unlock(session); + return (res); +} + +/* + * smbrdr_smb_echo + * + * This request can be used to test the connection to the server. The + * server should echo the data sent. The server should ignore the tid + * in the header, so this request when there are no tree connections. + * See CIFS/1.0 section 4.1.7. + * + * Return 0 on success. Otherwise return a -ve error code. + */ +static int +smbrdr_smb_echo(struct sdb_session *session) +{ + static char *echo_str = "smbrdr"; + smbrdr_handle_t srh; + smb_hdr_t smb_hdr; + DWORD status; + int rc; + + if ((session->state == SDB_SSTATE_DISCONNECTING) || + (session->state == SDB_SSTATE_CLEANING) || + (session->state == SDB_SSTATE_STALE)) { + return (-1); + } + + status = smbrdr_request_init(&srh, SMB_COM_ECHO, session, 0, 0); + + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "SmbrdrEcho: %s", xlate_nt_status(status)); + return (-1); + } + + rc = smb_msgbuf_encode(&srh.srh_mbuf, "bwws", 1, 1, + strlen(echo_str), echo_str); + if (rc <= 0) { + syslog(LOG_ERR, "SmbrdrEcho: encode failed"); + smbrdr_handle_free(&srh); + return (-1); + } + + status = smbrdr_exchange(&srh, &smb_hdr, 10); + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "SmbrdrEcho: %s", xlate_nt_status(status)); + rc = -1; + } else { + rc = 0; + } + + smbrdr_handle_free(&srh); + return (rc); +} + +/* + * smbrdr_locate_dc + * + * Locate a domain controller. Note that this may close an existing + * connection to the current domain controller. + */ +static int +smbrdr_locate_dc(char *domain) +{ + if (mlsvc_locate_pdc) + return (mlsvc_locate_pdc(domain)); + + return (0); +} diff --git a/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_transact.c b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_transact.c new file mode 100644 index 000000000000..24bf3a5cea01 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_transact.c @@ -0,0 +1,254 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB transaction functions to support MLRPC. + */ + +#include +#include + +#include + +#include +#include +#include + +/* + * The pipe filename, length (including the null terminator) + * and the buffer size for the transaction. Moving to unicode + * revealed that the length should not include the null. + */ +#define TX_FILENAME "\\PIPE\\" +#define TX_FILENAME_ASCII_LEN 6 +#define TX_FILENAME_WCHAR_LEN 14 + + +static int prep_smb_transact(smb_msgbuf_t *, unsigned short, char *, + unsigned short, unsigned short, unsigned); +static int decode_smb_transact(smb_msgbuf_t *, char *, unsigned, + smb_transact_rsp_t *); + +/* + * smbrdr_rpc_transact + * + * Send a SMB_COM_TRANSACTION request. + */ +int +smbrdr_rpc_transact(int fid, char *out_buf, int out_len, + char *in_buf, int in_len) +{ + struct sdb_session *session; + struct sdb_netuse *netuse; + struct sdb_ofile *ofile; + struct sdb_logon *logon; + smb_transact_rsp_t rsp; + smbrdr_handle_t srh; + smb_msgbuf_t *mb; + DWORD status; + int rc; + unsigned short rcv_dcnt; + int cur_inlen; + int first_rsp; + + if ((ofile = smbrdr_ofile_get(fid)) == 0) + return (-1); + + netuse = ofile->netuse; + session = netuse->session; + logon = &session->logon; + + status = smbrdr_request_init(&srh, SMB_COM_TRANSACTION, + session, logon, netuse); + + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "SmbrdrTransact: %s", xlate_nt_status(status)); + smbrdr_ofile_put(ofile); + return (-1); + } + + mb = &srh.srh_mbuf; + + rc = prep_smb_transact(mb, ofile->fid, out_buf, out_len, in_len, + session->remote_caps & CAP_UNICODE); + if (rc < 0) { + syslog(LOG_ERR, + "smbrdr_rpc_transact: prep_smb_transact failed"); + smbrdr_handle_free(&srh); + smbrdr_ofile_put(ofile); + return (rc); + } + + smbrdr_lock_transport(); + + status = smbrdr_send(&srh); + if (status != NT_STATUS_SUCCESS) { + smbrdr_unlock_transport(); + smbrdr_handle_free(&srh); + smbrdr_ofile_put(ofile); + syslog(LOG_ERR, "smbrdr_rpc_transact: send failed"); + return (-1); + } + + rcv_dcnt = 0; + cur_inlen = in_len; + first_rsp = 1; + + do { + if (smbrdr_rcv(&srh, first_rsp) != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, "smbrdr_rpc_transact: nb_rcv failed"); + rc = -1; + break; + } + + rc = decode_smb_transact(mb, in_buf, cur_inlen, &rsp); + if (rc < 0 || rsp.TotalDataCount > in_len) { + syslog(LOG_ERR, + "SmbTransact: transact decode failure!"); + rc = -1; + break; + } + + rcv_dcnt += rsp.DataCount; + cur_inlen -= rsp.DataCount; + first_rsp = 0; + + } while (rcv_dcnt < rsp.TotalDataCount); + + smbrdr_unlock_transport(); + smbrdr_handle_free(&srh); + smbrdr_ofile_put(ofile); + + return ((rc < 0) ? rc : rcv_dcnt); +} + + +/* + * prep_smb_transact + * + * Prepare the SMB_COM_TRANSACTION request. + */ +static int +prep_smb_transact(smb_msgbuf_t *mb, unsigned short fid, char *out, + unsigned short out_len, unsigned short in_max, unsigned unicode) +{ + int data_off; + int rc; + unsigned short bcc; + + /* + * The byte count seems to include the pad + * byte to word align the filename and two + * spurious pad bytes between the filename + * and the transaction data. + */ + bcc = out_len + 3; + bcc += (unicode) ? TX_FILENAME_WCHAR_LEN : TX_FILENAME_ASCII_LEN; + + data_off = 32; /* sizeof SMB header up to smb_wct */ + data_off += 1; /* sizeof smb_wct */ + data_off += 16*2; /* sizeof word parameters */ + data_off += 2; /* sizeof smb_bcc */ + data_off += (unicode) ? TX_FILENAME_WCHAR_LEN : TX_FILENAME_ASCII_LEN; + data_off += 3; + /* this is where data starts */ + + rc = smb_msgbuf_encode(mb, + "(wct)b" + "(tpscnt)w (tdscnt)w (mprcnt)w (mdrcnt)w (msrcnt)b" + "(rsvd). (flags)w (timeo)l (rsvd1)2." + "(pscnt)w (psoff)w (dscnt)w (dsoff)w (suwcnt)b" + "(rsvd2). (pipop)w (fid)w (bcc)w (fname)u", + 16, /* smb_wct */ + 0, /* total parm bytes */ + out_len, /* total data bytes */ + 0, /* max parm bytes to ret */ + in_max, /* max data bytes to ret */ + 0, /* max setup words to ret */ + 0, /* transact flags */ + 0, /* transact timeout */ + 0, /* parameter bytes */ + data_off, /* parameter offset */ + out_len, /* data bytes */ + data_off, /* data offset */ + 2, /* total setup words */ + 0x0026, /* OP=TransactNmPipe */ + fid, /* FID */ + bcc, /* byte count */ + TX_FILENAME); /* file name */ + + /* + * Transaction data - padded. + */ + rc = smb_msgbuf_encode(mb, "..#c", out_len, out); + return (rc); +} + + +/* + * decode_smb_transact + * + * Decode the response from the SMB_COM_TRANSACTION request. + */ +static int +decode_smb_transact(smb_msgbuf_t *mb, char *in, unsigned in_len, + smb_transact_rsp_t *rsp) +{ + int rc; + + rc = smb_msgbuf_decode(mb, "b", &rsp->WordCount); + if (rc <= 0 || rsp->WordCount < 10) { + syslog(LOG_ERR, "SmbTransact: invalid word count"); + return (-1); + } + + rc = smb_msgbuf_decode(mb, + "(tpscnt)w (tdscnt)w (rsvd)2." + "(pscnt)w (psoff)w (psdisp)w (dscnt)w (dsoff)w" + "(dsdisp)w (suwcnt)b (rsvd). (bcc)w", + &rsp->TotalParamCount, /* Total parm bytes */ + &rsp->TotalDataCount, /* Total data bytes */ + &rsp->ParamCount, /* Parm bytes this buffer */ + &rsp->ParamOffset, /* Parm offset from hdr */ + &rsp->ParamDisplacement, /* Parm displacement */ + &rsp->DataCount, /* Data bytes this buffer */ + &rsp->DataOffset, /* Data offset from hdr */ + &rsp->DataDisplacement, /* Data displacement */ + &rsp->SetupCount, /* Setup word count */ + &rsp->BCC); /* smb_bcc */ + + if (rc <= 0) + return (-1); + + if (rsp->DataCount > in_len) + return (-1); + + bcopy(mb->base + rsp->DataOffset, + in + rsp->DataDisplacement, rsp->DataCount); + + return (0); +} diff --git a/usr/src/lib/smbsrv/libsmbrdr/i386/Makefile b/usr/src/lib/smbsrv/libsmbrdr/i386/Makefile new file mode 100644 index 000000000000..f91f0270e9ad --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/i386/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/smbsrv/libsmbrdr/sparc/Makefile b/usr/src/lib/smbsrv/libsmbrdr/sparc/Makefile new file mode 100644 index 000000000000..f91f0270e9ad --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/sparc/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/smbsrv/libsmbrdr/sparcv9/Makefile b/usr/src/lib/smbsrv/libsmbrdr/sparcv9/Makefile new file mode 100644 index 000000000000..a2f97019c815 --- /dev/null +++ b/usr/src/lib/smbsrv/libsmbrdr/sparcv9/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/pkgdefs/Makefile b/usr/src/pkgdefs/Makefile index 7843cd917407..5ae726e3c5ae 100644 --- a/usr/src/pkgdefs/Makefile +++ b/usr/src/pkgdefs/Makefile @@ -354,6 +354,9 @@ COMMON_SUBDIRS= \ SUNWslpr \ SUNWslpu \ SUNWsmapi \ + SUNWsmbskr \ + SUNWsmbsr \ + SUNWsmbsu \ SUNWsmedia \ SUNWsmediar \ SUNWsn1rint \ diff --git a/usr/src/pkgdefs/SUNW0on/prototype_com b/usr/src/pkgdefs/SUNW0on/prototype_com index 87facb2c0030..3d577416fdb2 100644 --- a/usr/src/pkgdefs/SUNW0on/prototype_com +++ b/usr/src/pkgdefs/SUNW0on/prototype_com @@ -256,6 +256,7 @@ f none usr/lib/help/auths/locale/SmfNWAMStates.html 444 root bin f none usr/lib/help/auths/locale/SmfPowerStates.html 444 root bin f none usr/lib/help/auths/locale/SmfRoutingStates.html 444 root bin f none usr/lib/help/auths/locale/SmfSendmailStates.html 444 root bin +f none usr/lib/help/auths/locale/SmfSMBStates.html 444 root bin f none usr/lib/help/auths/locale/SmfSshStates.html 444 root bin f none usr/lib/help/auths/locale/SmfSyslogStates.html 444 root bin f none usr/lib/help/auths/locale/SmfValueHeader.html 444 root bin @@ -266,6 +267,8 @@ f none usr/lib/help/auths/locale/SmfValueMDNS.html 444 root bin f none usr/lib/help/auths/locale/SmfValueNADD.html 444 root bin f none usr/lib/help/auths/locale/SmfValueNWAM.html 444 root bin f none usr/lib/help/auths/locale/SmfValueRouting.html 444 root bin +f none usr/lib/help/auths/locale/SmfValueSMB.html 444 root bin +f none usr/lib/help/auths/locale/AuthReadSMB.html 444 root bin f none usr/lib/help/auths/locale/SmfWpaStates.html 444 root bin f none usr/lib/help/auths/locale/NetworkHeader.html 444 root bin f none usr/lib/help/auths/locale/WifiConfig.html 444 root bin @@ -333,6 +336,7 @@ f none usr/lib/help/profiles/locale/RtObAccessMngmnt.html 444 root bin f none usr/lib/help/profiles/locale/RtProcManagement.html 444 root bin f none usr/lib/help/profiles/locale/RtRightsDelegate.html 444 root bin f none usr/lib/help/profiles/locale/RtSoftwareInstall.html 444 root bin +f none usr/lib/help/profiles/locale/RtSMBMngmnt.html 444 root bin f none usr/lib/help/profiles/locale/RtSysEvMngmnt.html 444 root bin f none usr/lib/help/profiles/locale/RtUserMngmnt.html 444 root bin f none usr/lib/help/profiles/locale/RtUserSecurity.html 444 root bin diff --git a/usr/src/pkgdefs/SUNWcsu/prototype_com b/usr/src/pkgdefs/SUNWcsu/prototype_com index 4c0a9807a570..a993f2eea62c 100644 --- a/usr/src/pkgdefs/SUNWcsu/prototype_com +++ b/usr/src/pkgdefs/SUNWcsu/prototype_com @@ -492,6 +492,7 @@ f none usr/lib/help/auths/locale/C/SmfNWAMStates.html 444 root bin f none usr/lib/help/auths/locale/C/SmfPowerStates.html 444 root bin f none usr/lib/help/auths/locale/C/SmfRoutingStates.html 444 root bin f none usr/lib/help/auths/locale/C/SmfSendmailStates.html 444 root bin +f none usr/lib/help/auths/locale/C/SmfSMBStates.html 444 root bin f none usr/lib/help/auths/locale/C/SmfSshStates.html 444 root bin f none usr/lib/help/auths/locale/C/SmfSyslogStates.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueHeader.html 444 root bin @@ -502,6 +503,8 @@ f none usr/lib/help/auths/locale/C/SmfValueMDNS.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueNADD.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueNWAM.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueRouting.html 444 root bin +f none usr/lib/help/auths/locale/C/SmfValueSMB.html 444 root bin +f none usr/lib/help/auths/locale/C/AuthReadSMB.html 444 root bin f none usr/lib/help/auths/locale/C/SmfWpaStates.html 444 root bin f none usr/lib/help/auths/locale/C/SysDate.html 444 root bin f none usr/lib/help/auths/locale/C/SysHeader.html 444 root bin @@ -551,6 +554,7 @@ f none usr/lib/help/profiles/locale/C/RtPrntAdmin.html 444 root bin f none usr/lib/help/profiles/locale/C/RtProcManagement.html 444 root bin f none usr/lib/help/profiles/locale/C/RtRightsDelegate.html 444 root bin f none usr/lib/help/profiles/locale/C/RtSoftwareInstall.html 444 root bin +f none usr/lib/help/profiles/locale/C/RtSMBMngmnt.html 444 root bin f none usr/lib/help/profiles/locale/C/RtSysEvMngmnt.html 444 root bin f none usr/lib/help/profiles/locale/C/RtUserMngmnt.html 444 root bin f none usr/lib/help/profiles/locale/C/RtUserSecurity.html 444 root bin diff --git a/usr/src/pkgdefs/SUNWhea/prototype_com b/usr/src/pkgdefs/SUNWhea/prototype_com index 727d760801de..81c0ddb2aef6 100644 --- a/usr/src/pkgdefs/SUNWhea/prototype_com +++ b/usr/src/pkgdefs/SUNWhea/prototype_com @@ -164,6 +164,7 @@ f none usr/include/ast/vmalloc.h 644 root bin f none usr/include/ast/wait.h 644 root bin f none usr/include/ast/wchar.h 644 root bin f none usr/include/ast/wordexp.h 644 root bin +f none usr/include/attr.h 644 root bin f none usr/include/atomic.h 644 root bin f none usr/include/auth_attr.h 644 root bin d none usr/include/bsm 755 root bin @@ -624,6 +625,7 @@ d none usr/include/sys 755 root bin f none usr/include/sys/acct.h 644 root bin f none usr/include/sys/acctctl.h 644 root bin f none usr/include/sys/acl.h 644 root bin +f none usr/include/sys/acl_impl.h 644 root bin f none usr/include/sys/aio.h 644 root bin f none usr/include/sys/aio_impl.h 644 root bin f none usr/include/sys/aio_req.h 644 root bin @@ -634,6 +636,7 @@ f none usr/include/sys/asm_linkage.h 644 root bin f none usr/include/sys/asynch.h 644 root bin f none usr/include/sys/atomic.h 644 root bin f none usr/include/sys/autoconf.h 644 root bin +f none usr/include/sys/attr.h 644 root bin f none usr/include/sys/auxv.h 644 root bin f none usr/include/sys/auxv_386.h 644 root bin f none usr/include/sys/auxv_SPARC.h 644 root bin @@ -1296,6 +1299,7 @@ f none usr/include/sys/tuneable.h 644 root bin f none usr/include/sys/turnstile.h 644 root bin f none usr/include/sys/types.h 644 root bin f none usr/include/sys/types32.h 644 root bin +f none usr/include/sys/tzfile.h 644 root bin f none usr/include/sys/u8_textprep.h 644 root bin f none usr/include/sys/uadmin.h 644 root bin f none usr/include/sys/ucontext.h 644 root bin diff --git a/usr/src/pkgdefs/SUNWsmbskr/Makefile b/usr/src/pkgdefs/SUNWsmbskr/Makefile new file mode 100644 index 000000000000..c1bfa57502d6 --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbskr/Makefile @@ -0,0 +1,38 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +include ../Makefile.com + +DATAFILES += depend + +.KEEP_STATE: + +all: $(FILES) postinstall preremove + +install: all pkg + +include ../Makefile.targ diff --git a/usr/src/pkgdefs/SUNWsmbskr/pkginfo.tmpl b/usr/src/pkgdefs/SUNWsmbskr/pkginfo.tmpl new file mode 100644 index 000000000000..1279fa65fa01 --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbskr/pkginfo.tmpl @@ -0,0 +1,51 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# This required package information file describes characteristics of the +# package, such as package abbreviation, full package name, package version, +# and package architecture. +# +PKG="SUNWsmbskr" +NAME="SMB Server (Kernel)" +ARCH="ISA" +VERSION="ONVERS,REV=0.0.0" +SUNW_PRODNAME="SunOS" +SUNW_PRODVERS="RELEASE/VERSION" +SUNW_PKGTYPE="root" +MAXINST="1000" +CATEGORY="system" +DESC="SMB Server kernel root components" +VENDOR="Sun Microsystems, Inc." +HOTLINE="Please contact your local service provider" +EMAIL="" +CLASSES="none" +BASEDIR=/ +SUNW_PKGVERS="1.0" +SUNW_PKG_ALLZONES="true" +SUNW_PKG_HOLLOW="true" +SUNW_PKG_THISZONE="false" diff --git a/usr/src/pkgdefs/SUNWsmbskr/postinstall b/usr/src/pkgdefs/SUNWsmbskr/postinstall new file mode 100644 index 000000000000..934bbe8467a7 --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbskr/postinstall @@ -0,0 +1,62 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +PATH="/usr/bin:/usr/sbin" +export PATH + +PKG_NAME=SUNWsmbskr +DRV=smbsrv +DRVPERM='* 0666 root sys' + +ADD_DRV=/usr/sbin/add_drv + +# +# Check if the BASEDIR option is needed +# +if [ "${BASEDIR}" = "/" ]; then + ADD_DRV_FLAGS="" + NAME_TO_MAJOR="/etc/name_to_major" +else + ADD_DRV_FLAGS="-b ${BASEDIR}" + NAME_TO_MAJOR="${BASEDIR}/etc/name_to_major" +fi + +# +# Make sure add_drv has not been previously executed before attempting +# to add the driver +# +grep "^${DRV} " ${NAME_TO_MAJOR} > /dev/null 2>&1 +if [ $? -ne 0 ]; then + ${ADD_DRV} ${ADD_DRV_FLAGS} -m '* 0666 root sys' ${DRV} + if [ $? -ne 0 ]; then + echo "${PKG_NAME}: add_drv ${DRV} failed." >&2 + exit 1 + fi +fi +exit 0 diff --git a/usr/src/pkgdefs/SUNWsmbskr/preremove b/usr/src/pkgdefs/SUNWsmbskr/preremove new file mode 100644 index 000000000000..2c19159f1f97 --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbskr/preremove @@ -0,0 +1,48 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +PATH="/usr/bin:/usr/sbin:${PATH}" +export PATH + +DRV=smbsrv + +REM_DRV=/usr/sbin/rem_drv + +# +# Check if the BASEDIR option is needed +# +if [ "${BASEDIR}" = "/" ]; then + REM_DRV_FLAGS="" +else + REM_DRV_FLAGS="-b ${BASEDIR}" +fi + +${REM_DRV} ${REM_DRV_FLAGS} ${DRV} + +exit 0 diff --git a/usr/src/pkgdefs/SUNWsmbskr/prototype_com b/usr/src/pkgdefs/SUNWsmbskr/prototype_com new file mode 100644 index 000000000000..245e5e2ba8a3 --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbskr/prototype_com @@ -0,0 +1,40 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# packaging files +i copyright +i depend +i pkginfo +i postinstall +i preremove +# +# SUNWsmbskr +# +d none kernel 755 root sys +d none kernel/kmdb 755 root sys +d none kernel/drv 755 root sys +f none kernel/drv/smbsrv.conf 644 root sys diff --git a/usr/src/pkgdefs/SUNWsmbskr/prototype_i386 b/usr/src/pkgdefs/SUNWsmbskr/prototype_i386 new file mode 100644 index 000000000000..bf813c18c045 --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbskr/prototype_i386 @@ -0,0 +1,36 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +!include prototype_com +# +# SUNWsmbskr +# +f none kernel/drv/smbsrv 755 root sys +d none kernel/drv/amd64 755 root sys +f none kernel/drv/amd64/smbsrv 755 root sys +f none kernel/kmdb/smbsrv 555 root sys +d none kernel/kmdb/amd64 755 root sys +f none kernel/kmdb/amd64/smbsrv 555 root sys diff --git a/usr/src/pkgdefs/SUNWsmbskr/prototype_sparc b/usr/src/pkgdefs/SUNWsmbskr/prototype_sparc new file mode 100644 index 000000000000..68ad9f0ed61e --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbskr/prototype_sparc @@ -0,0 +1,34 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +!include prototype_com +# +# SUNWsmbskr +# +d none kernel/drv/sparcv9 755 root sys +f none kernel/drv/sparcv9/smbsrv 755 root sys +d none kernel/kmdb/sparcv9 755 root sys +f none kernel/kmdb/sparcv9/smbsrv 555 root sys diff --git a/usr/src/pkgdefs/SUNWsmbsr/Makefile b/usr/src/pkgdefs/SUNWsmbsr/Makefile new file mode 100644 index 000000000000..b0543ab1fbf7 --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbsr/Makefile @@ -0,0 +1,38 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +include ../Makefile.com + +DATAFILES += i.manifest r.manifest + +.KEEP_STATE: + +all: $(FILES) depend + +install: all pkg + +include ../Makefile.targ diff --git a/usr/src/pkgdefs/SUNWsmbsr/depend b/usr/src/pkgdefs/SUNWsmbsr/depend new file mode 100644 index 000000000000..a7789666aa10 --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbsr/depend @@ -0,0 +1,54 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# This package information file defines software dependencies associated +# with the pkg. You can define three types of pkg dependencies with this file: +# P indicates a prerequisite for installation +# I indicates an incompatible package +# R indicates a reverse dependency +# see pkginfo(4), PKG parameter +# see pkginfo(4), NAME parameter +# see pkginfo(4), VERSION parameter +# see pkginfo(4), ARCH parameter +# +# () +# () +# ... +# +# ... +# + +P SUNWcar Core Architecture, (Root) +P SUNWcakr Core Solaris Kernel Architecture (Root) +P SUNWkvm Core Architecture, (Kvm) +P SUNWcsr Core Solaris, (Root) +P SUNWckr Core Solaris Kernel (Root) +P SUNWcsu Core Solaris, (Usr) +P SUNWcsd Core Solaris Devices +P SUNWcsl Core Solaris Libraries +P SUNWsmbskr SMB Kernel (Root) diff --git a/usr/src/pkgdefs/SUNWsmbsr/pkginfo.tmpl b/usr/src/pkgdefs/SUNWsmbsr/pkginfo.tmpl new file mode 100644 index 000000000000..334287c6640c --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbsr/pkginfo.tmpl @@ -0,0 +1,51 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# This required package information file describes characteristics of the +# package, such as package abbreviation, full package name, package version, +# and package architecture. +# +PKG="SUNWsmbsr" +NAME="SMB Server (Root)" +ARCH="ISA" +VERSION="ONVERS,REV=0.0.0" +SUNW_PRODNAME="SunOS" +SUNW_PRODVERS="RELEASE/VERSION" +SUNW_PKGTYPE="root" +MAXINST="1000" +CATEGORY="system" +DESC="=SMB Server root components" +VENDOR="Sun Microsystems, Inc." +HOTLINE="Please contact your local service provider" +EMAIL="" +CLASSES="none manifest" +BASEDIR=/ +SUNW_PKGVERS="1.0" +SUNW_PKG_ALLZONES="true" +SUNW_PKG_HOLLOW="false" +SUNW_PKG_THISZONE="false" diff --git a/usr/src/pkgdefs/SUNWsmbsr/prototype_com b/usr/src/pkgdefs/SUNWsmbsr/prototype_com new file mode 100644 index 000000000000..52fcaaa1a8e9 --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbsr/prototype_com @@ -0,0 +1,44 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# packaging files +i copyright +i depend +i pkginfo +i i.manifest +i r.manifest +# +# SUNWsmbsr +# +d none var 0755 root sys +d none var/smb 0755 root sys +f none var/smb/smbpasswd 0400 root sys +d none var/svc 0755 root sys +d none var/svc/manifest 0755 root sys +d none var/svc/manifest/network 0755 root sys +d none var/svc/manifest/network/smb 0755 root sys +f manifest var/svc/manifest/network/smb/server.xml 0444 root sys diff --git a/usr/src/pkgdefs/SUNWsmbsr/prototype_i386 b/usr/src/pkgdefs/SUNWsmbsr/prototype_i386 new file mode 100644 index 000000000000..893833a87f8f --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbsr/prototype_i386 @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# +!include prototype_com +# +# List files which are i386 specific here +# SUNWsmbsr +# diff --git a/usr/src/pkgdefs/SUNWsmbsr/prototype_sparc b/usr/src/pkgdefs/SUNWsmbsr/prototype_sparc new file mode 100644 index 000000000000..6ea63a15ea25 --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbsr/prototype_sparc @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# +!include prototype_com +# +# List files which are sparc specific here +# SUNWsmbsr +# diff --git a/usr/src/pkgdefs/SUNWsmbsu/Makefile b/usr/src/pkgdefs/SUNWsmbsu/Makefile new file mode 100644 index 000000000000..176ab5ba1fde --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbsu/Makefile @@ -0,0 +1,36 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +include ../Makefile.com + +.KEEP_STATE: + +all: $(FILES) depend preremove + +install: all pkg + +include ../Makefile.targ diff --git a/usr/src/pkgdefs/SUNWsmbsu/depend b/usr/src/pkgdefs/SUNWsmbsu/depend new file mode 100644 index 000000000000..53c41eedf392 --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbsu/depend @@ -0,0 +1,55 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# This package information file defines software dependencies associated +# with the pkg. You can define three types of pkg dependencies with this file: +# P indicates a prerequisite for installation +# I indicates an incompatible package +# R indicates a reverse dependency +# see pkginfo(4), PKG parameter +# see pkginfo(4), NAME parameter +# see pkginfo(4), VERSION parameter +# see pkginfo(4), ARCH parameter +# +# () +# () +# ... +# +# ... +# + +P SUNWcar Core Architecture, (Root) +P SUNWcakr Core Solaris Kernel Architecture (Root) +P SUNWkvm Core Architecture, (Kvm) +P SUNWcsr Core Solaris, (Root) +P SUNWckr Core Solaris Kernel (Root) +P SUNWcsu Core Solaris, (Usr) +P SUNWcsd Core Solaris Devices +P SUNWcsl Core Solaris Libraries +P SUNWsmbskr SMB Kernel (Root) +P SUNWsmbsr SMB Server (Root) diff --git a/usr/src/pkgdefs/SUNWsmbsu/pkginfo.tmpl b/usr/src/pkgdefs/SUNWsmbsu/pkginfo.tmpl new file mode 100644 index 000000000000..440c41086e60 --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbsu/pkginfo.tmpl @@ -0,0 +1,46 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +PKG="SUNWsmbsu" +NAME="SMB Server (Usr)" +ARCH="ISA" +VERSION="ONVERS,REV=0.0.0" +SUNW_PRODNAME="SunOS" +SUNW_PRODVERS="RELEASE/VERSION" +SUNW_PKGTYPE="usr" +MAXINST="1000" +CATEGORY="system" +DESC="SMB Server libraries and commands" +VENDOR="Sun Microsystems, Inc." +HOTLINE="Please contact your local service provider" +EMAIL="" +CLASSES="none" +BASEDIR=/ +SUNW_PKGVERS="1.0" +SUNW_PKG_ALLZONES="true" +SUNW_PKG_HOLLOW="false" +SUNW_PKG_THISZONE="false" diff --git a/usr/src/pkgdefs/SUNWsmbsu/preremove b/usr/src/pkgdefs/SUNWsmbsu/preremove new file mode 100644 index 000000000000..68fbce22af18 --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbsu/preremove @@ -0,0 +1,44 @@ +#!/bin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# Make sure that SMB server daemon (smbd) is not +# running before removing this package. +# +if [ "${PKG_INSTALL_ROOT:-/}" = "/" ]; then + for i in smbd + do + /usr/bin/pgrep -x -u 0 "$i" >/dev/null + if [ $? -ne 1 ]; then + echo "$i running; unable to remove package" + exit 1 + fi + done +fi + +exit 0 diff --git a/usr/src/pkgdefs/SUNWsmbsu/prototype_com b/usr/src/pkgdefs/SUNWsmbsu/prototype_com new file mode 100644 index 000000000000..eff699e240bd --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbsu/prototype_com @@ -0,0 +1,70 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +i pkginfo +i copyright +i depend +i preremove +# +# SUNWsmbsu +# +d none usr 755 root sys +d none usr/lib 755 root bin +d none usr/lib/smbsrv 755 root bin +f none usr/lib/smbsrv/libsmb.so.1 755 root bin +s none usr/lib/smbsrv/libsmb.so=libsmb.so.1 +f none usr/lib/smbsrv/llib-lsmb 644 root bin +f none usr/lib/smbsrv/llib-lsmb.ln 644 root bin +f none usr/lib/smbsrv/libmlrpc.so.1 755 root bin +s none usr/lib/smbsrv/libmlrpc.so=libmlrpc.so.1 +f none usr/lib/smbsrv/llib-lmlrpc 644 root bin +f none usr/lib/smbsrv/llib-lmlrpc.ln 644 root bin +f none usr/lib/smbsrv/libmlsvc.so.1 755 root bin +s none usr/lib/smbsrv/libmlsvc.so=libmlsvc.so.1 +f none usr/lib/smbsrv/llib-lmlsvc 644 root bin +f none usr/lib/smbsrv/llib-lmlsvc.ln 644 root bin +f none usr/lib/smbsrv/libsmbns.so.1 755 root bin +s none usr/lib/smbsrv/libsmbns.so=libsmbns.so.1 +f none usr/lib/smbsrv/llib-lsmbns 644 root bin +f none usr/lib/smbsrv/llib-lsmbns.ln 644 root bin +f none usr/lib/smbsrv/libsmbrdr.so.1 755 root bin +s none usr/lib/smbsrv/libsmbrdr.so=libsmbrdr.so.1 +f none usr/lib/smbsrv/llib-lsmbrdr 644 root bin +f none usr/lib/smbsrv/llib-lsmbrdr.ln 644 root bin +f none usr/lib/smbsrv/smbd 555 root bin +d none usr/sbin 755 root bin +f none usr/sbin/smbadm 555 root bin +f none usr/sbin/smbstat 555 root bin +d none usr/lib/mdb 755 root sys +d none usr/lib/mdb/kvm 755 root sys +d none usr/lib/fs 755 root sys +d none usr/lib/fs/smb 755 root sys +f none usr/lib/fs/smb/libshare_smb.so.1 755 root bin +s none usr/lib/fs/smb/libshare_smb.so=libshare_smb.so.1 +d none usr/lib/security 755 root bin +f none usr/lib/security/pam_smb_passwd.so.1 755 root bin +s none usr/lib/security/pam_smb_passwd.so=./pam_smb_passwd.so.1 diff --git a/usr/src/pkgdefs/SUNWsmbsu/prototype_i386 b/usr/src/pkgdefs/SUNWsmbsu/prototype_i386 new file mode 100644 index 000000000000..34176922014a --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbsu/prototype_i386 @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +!include prototype_com +f none usr/lib/mdb/kvm/smbsrv.so 555 root sys +d none usr/lib/mdb/kvm/amd64 755 root sys +f none usr/lib/mdb/kvm/amd64/smbsrv.so 555 root sys +d none usr/lib/fs/smb/amd64 755 root sys +f none usr/lib/fs/smb/amd64/libshare_smb.so.1 755 root bin +s none usr/lib/fs/smb/amd64/libshare_smb.so=libshare_smb.so.1 diff --git a/usr/src/pkgdefs/SUNWsmbsu/prototype_sparc b/usr/src/pkgdefs/SUNWsmbsu/prototype_sparc new file mode 100644 index 000000000000..2a8071982f92 --- /dev/null +++ b/usr/src/pkgdefs/SUNWsmbsu/prototype_sparc @@ -0,0 +1,32 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +!include prototype_com +d none usr/lib/mdb/kvm/sparcv9 755 root sys +f none usr/lib/mdb/kvm/sparcv9/smbsrv.so 555 root sys +d none usr/lib/fs/smb/sparcv9 755 root sys +f none usr/lib/fs/smb/sparcv9/libshare_smb.so.1 755 root bin +s none usr/lib/fs/smb/sparcv9/libshare_smb.so=libshare_smb.so.1 diff --git a/usr/src/pkgdefs/common_files/i.minorperm_i386 b/usr/src/pkgdefs/common_files/i.minorperm_i386 index c7bafd374c89..510f62a1394c 100644 --- a/usr/src/pkgdefs/common_files/i.minorperm_i386 +++ b/usr/src/pkgdefs/common_files/i.minorperm_i386 @@ -315,6 +315,7 @@ asy:* asy:*,cu ucode:* battery:* +smbsrv:* EOF } diff --git a/usr/src/pkgdefs/common_files/i.minorperm_sparc b/usr/src/pkgdefs/common_files/i.minorperm_sparc index 4a00d39f27f7..c5a202a5d015 100644 --- a/usr/src/pkgdefs/common_files/i.minorperm_sparc +++ b/usr/src/pkgdefs/common_files/i.minorperm_sparc @@ -332,6 +332,7 @@ profile:profile sdt:sdt systrace:systrace physmem:* +smbsrv:* EOF } diff --git a/usr/src/pkgdefs/etc/exception_list_i386 b/usr/src/pkgdefs/etc/exception_list_i386 index 58fc7ddd64a5..d1f9e0c85a98 100644 --- a/usr/src/pkgdefs/etc/exception_list_i386 +++ b/usr/src/pkgdefs/etc/exception_list_i386 @@ -867,6 +867,8 @@ usr/lib/libshare.so i386 usr/lib/amd64/libshare.so i386 usr/lib/fs/nfs/libshare_nfs.so i386 usr/lib/fs/nfs/amd64/libshare_nfs.so i386 +usr/lib/fs/smb/libshare_smb.so i386 +usr/lib/fs/smb/amd64/libshare_smb.so i386 usr/include/libshare_impl.h i386 usr/include/scfutil.h i386 usr/sbin/i86/sharemgr i386 @@ -899,3 +901,108 @@ etc/flash/postdeployment/ttydefs.cleanup i386 # ppm/srn modules # and should not be in any package # usr/include/sys/srn.h i386 +# +# Private/Internal header files of smbsrv. Do not ship. +# +usr/include/smbsrv i386 +usr/include/smbsrv/alloc.h i386 +usr/include/smbsrv/cifs.h i386 +usr/include/smbsrv/codepage.h i386 +usr/include/smbsrv/cp_cyrillic.h i386 +usr/include/smbsrv/cp_latin1.h i386 +usr/include/smbsrv/cp_latin2.h i386 +usr/include/smbsrv/cp_latin3.h i386 +usr/include/smbsrv/cp_latin4.h i386 +usr/include/smbsrv/cp_latin5.h i386 +usr/include/smbsrv/cp_latin6.h i386 +usr/include/smbsrv/cp_unicode.h i386 +usr/include/smbsrv/cp_usascii.h i386 +usr/include/smbsrv/crypt.h i386 +usr/include/smbsrv/ctype.h i386 +usr/include/smbsrv/doserror.h i386 +usr/include/smbsrv/hash_table.h i386 +usr/include/smbsrv/libmlrpc.h i386 +usr/include/smbsrv/libmlsvc.h i386 +usr/include/smbsrv/libsmb.h i386 +usr/include/smbsrv/libsmbns.h i386 +usr/include/smbsrv/libsmbrdr.h i386 +usr/include/smbsrv/lm.h i386 +usr/include/smbsrv/lmdfs.h i386 +usr/include/smbsrv/lmerr.h i386 +usr/include/smbsrv/lmshare.h i386 +usr/include/smbsrv/lmshare_door.h i386 +usr/include/smbsrv/lsalib.h i386 +usr/include/smbsrv/mac_cifs.h i386 +usr/include/smbsrv/mailslot.h i386 +usr/include/smbsrv/mbuf.h i386 +usr/include/smbsrv/mlrpc.h i386 +usr/include/smbsrv/mlsvc.h i386 +usr/include/smbsrv/mlsvc_util.h i386 +usr/include/smbsrv/msgbuf.h i386 +usr/include/smbsrv/ndr.h i386 +usr/include/smbsrv/netbios.h i386 +usr/include/smbsrv/netrauth.h i386 +usr/include/smbsrv/nmpipes.h i386 +usr/include/smbsrv/ntaccess.h i386 +usr/include/smbsrv/nterror.h i386 +usr/include/smbsrv/ntifs.h i386 +usr/include/smbsrv/ntlocale.h i386 +usr/include/smbsrv/ntsid.h i386 +usr/include/smbsrv/ntstatus.h i386 +usr/include/smbsrv/oem.h i386 +usr/include/smbsrv/samlib.h i386 +usr/include/smbsrv/smb.h i386 +usr/include/smbsrv/smb_common_door.h i386 +usr/include/smbsrv/smb_door_svc.h i386 +usr/include/smbsrv/smb_fsd.h i386 +usr/include/smbsrv/smb_fsops.h i386 +usr/include/smbsrv/smb_i18n.h i386 +usr/include/smbsrv/smb_idmap.h i386 +usr/include/smbsrv/smb_incl.h i386 +usr/include/smbsrv/smb_ioctl.h i386 +usr/include/smbsrv/smb_kproto.h i386 +usr/include/smbsrv/smb_privilege.h i386 +usr/include/smbsrv/smb_secdesc.h i386 +usr/include/smbsrv/smb_svc_sm.h i386 +usr/include/smbsrv/smb_token.h i386 +usr/include/smbsrv/smb_vops.h i386 +usr/include/smbsrv/smb_winpipe.h i386 +usr/include/smbsrv/smb_xdr.h i386 +usr/include/smbsrv/smbfmt.h i386 +usr/include/smbsrv/smbinfo.h i386 +usr/include/smbsrv/smbtrans.h i386 +usr/include/smbsrv/smbvar.h i386 +usr/include/smbsrv/string.h i386 +usr/include/smbsrv/svrapi.h i386 +usr/include/smbsrv/winioctl.h i386 +usr/include/smbsrv/winsvc.h i386 +usr/include/smbsrv/wintypes.h i386 +# +# Private/Internal ndl files of smbsrv. Do not ship. +# +usr/include/smbsrv/ndl i386 +usr/include/smbsrv/ndl/dssetup.ndl i386 +usr/include/smbsrv/ndl/eventlog.ndl i386 +usr/include/smbsrv/ndl/llsrpc.ndl i386 +usr/include/smbsrv/ndl/lsarpc.ndl i386 +usr/include/smbsrv/ndl/ndrtypes.ndl i386 +usr/include/smbsrv/ndl/netdfs.ndl i386 +usr/include/smbsrv/ndl/netlogon.ndl i386 +usr/include/smbsrv/ndl/rpcpdu.ndl i386 +usr/include/smbsrv/ndl/samrpc.ndl i386 +usr/include/smbsrv/ndl/spoolss.ndl i386 +usr/include/smbsrv/ndl/srvsvc.ndl i386 +usr/include/smbsrv/ndl/svcctl.ndl i386 +usr/include/smbsrv/ndl/winreg.ndl i386 +# +# Private/Internal dtrace scripts of smbsrv. Do not ship. +# +usr/lib/smbsrv/dtrace i386 +usr/lib/smbsrv/dtrace/msrpc.d i386 +usr/lib/smbsrv/dtrace/smbnode.d i386 +usr/lib/smbsrv/dtrace/smbvfs.d i386 +usr/lib/smbsrv/dtrace/stype.d i386 +# +# Private dirent, extended to include flags, for use by SMB server +# +usr/include/sys/extdirent.h i386 diff --git a/usr/src/pkgdefs/etc/exception_list_sparc b/usr/src/pkgdefs/etc/exception_list_sparc index 2244b8a91f10..3641342b7784 100644 --- a/usr/src/pkgdefs/etc/exception_list_sparc +++ b/usr/src/pkgdefs/etc/exception_list_sparc @@ -943,6 +943,8 @@ usr/lib/libshare.so sparc usr/lib/sparcv9/libshare.so sparc usr/lib/fs/nfs/libshare_nfs.so sparc usr/lib/fs/nfs/sparcv9/libshare_nfs.so sparc +usr/lib/fs/smb/libshare_smb.so sparc +usr/lib/fs/smb/sparcv9/libshare_smb.so sparc usr/include/libshare_impl.h sparc usr/include/scfutil.h sparc usr/sbin/sparcv9/sharemgr sparc @@ -983,3 +985,108 @@ usr/include/sys/kiconv_latin1.h sparc # ppm/srn modules # and should not be in any package # usr/include/sys/srn.h sparc +# +# Private/Internal header files of smbsrv. Do not ship. +# +usr/include/smbsrv sparc +usr/include/smbsrv/alloc.h sparc +usr/include/smbsrv/cifs.h sparc +usr/include/smbsrv/codepage.h sparc +usr/include/smbsrv/cp_cyrillic.h sparc +usr/include/smbsrv/cp_latin1.h sparc +usr/include/smbsrv/cp_latin2.h sparc +usr/include/smbsrv/cp_latin3.h sparc +usr/include/smbsrv/cp_latin4.h sparc +usr/include/smbsrv/cp_latin5.h sparc +usr/include/smbsrv/cp_latin6.h sparc +usr/include/smbsrv/cp_unicode.h sparc +usr/include/smbsrv/cp_usascii.h sparc +usr/include/smbsrv/crypt.h sparc +usr/include/smbsrv/ctype.h sparc +usr/include/smbsrv/doserror.h sparc +usr/include/smbsrv/hash_table.h sparc +usr/include/smbsrv/libmlrpc.h sparc +usr/include/smbsrv/libmlsvc.h sparc +usr/include/smbsrv/libsmb.h sparc +usr/include/smbsrv/libsmbns.h sparc +usr/include/smbsrv/libsmbrdr.h sparc +usr/include/smbsrv/lm.h sparc +usr/include/smbsrv/lmdfs.h sparc +usr/include/smbsrv/lmerr.h sparc +usr/include/smbsrv/lmshare.h sparc +usr/include/smbsrv/lmshare_door.h sparc +usr/include/smbsrv/lsalib.h sparc +usr/include/smbsrv/mac_cifs.h sparc +usr/include/smbsrv/mailslot.h sparc +usr/include/smbsrv/mbuf.h sparc +usr/include/smbsrv/mlrpc.h sparc +usr/include/smbsrv/mlsvc.h sparc +usr/include/smbsrv/mlsvc_util.h sparc +usr/include/smbsrv/msgbuf.h sparc +usr/include/smbsrv/ndr.h sparc +usr/include/smbsrv/netbios.h sparc +usr/include/smbsrv/netrauth.h sparc +usr/include/smbsrv/nmpipes.h sparc +usr/include/smbsrv/ntaccess.h sparc +usr/include/smbsrv/nterror.h sparc +usr/include/smbsrv/ntifs.h sparc +usr/include/smbsrv/ntlocale.h sparc +usr/include/smbsrv/ntsid.h sparc +usr/include/smbsrv/ntstatus.h sparc +usr/include/smbsrv/oem.h sparc +usr/include/smbsrv/samlib.h sparc +usr/include/smbsrv/smb.h sparc +usr/include/smbsrv/smb_common_door.h sparc +usr/include/smbsrv/smb_door_svc.h sparc +usr/include/smbsrv/smb_fsd.h sparc +usr/include/smbsrv/smb_fsops.h sparc +usr/include/smbsrv/smb_i18n.h sparc +usr/include/smbsrv/smb_idmap.h sparc +usr/include/smbsrv/smb_incl.h sparc +usr/include/smbsrv/smb_ioctl.h sparc +usr/include/smbsrv/smb_kproto.h sparc +usr/include/smbsrv/smb_privilege.h sparc +usr/include/smbsrv/smb_secdesc.h sparc +usr/include/smbsrv/smb_svc_sm.h sparc +usr/include/smbsrv/smb_token.h sparc +usr/include/smbsrv/smb_vops.h sparc +usr/include/smbsrv/smb_winpipe.h sparc +usr/include/smbsrv/smb_xdr.h sparc +usr/include/smbsrv/smbfmt.h sparc +usr/include/smbsrv/smbinfo.h sparc +usr/include/smbsrv/smbtrans.h sparc +usr/include/smbsrv/smbvar.h sparc +usr/include/smbsrv/string.h sparc +usr/include/smbsrv/svrapi.h sparc +usr/include/smbsrv/winioctl.h sparc +usr/include/smbsrv/winsvc.h sparc +usr/include/smbsrv/wintypes.h sparc +# +# Private/Internal ndl files of smbsrv. Do not ship. +# +usr/include/smbsrv/ndl sparc +usr/include/smbsrv/ndl/dssetup.ndl sparc +usr/include/smbsrv/ndl/eventlog.ndl sparc +usr/include/smbsrv/ndl/llsrpc.ndl sparc +usr/include/smbsrv/ndl/lsarpc.ndl sparc +usr/include/smbsrv/ndl/ndrtypes.ndl sparc +usr/include/smbsrv/ndl/netdfs.ndl sparc +usr/include/smbsrv/ndl/netlogon.ndl sparc +usr/include/smbsrv/ndl/rpcpdu.ndl sparc +usr/include/smbsrv/ndl/samrpc.ndl sparc +usr/include/smbsrv/ndl/spoolss.ndl sparc +usr/include/smbsrv/ndl/srvsvc.ndl sparc +usr/include/smbsrv/ndl/svcctl.ndl sparc +usr/include/smbsrv/ndl/winreg.ndl sparc +# +# Private/Internal dtrace scripts of smbsrv. Do not ship. +# +usr/lib/smbsrv/dtrace sparc +usr/lib/smbsrv/dtrace/msrpc.d sparc +usr/lib/smbsrv/dtrace/smbnode.d sparc +usr/lib/smbsrv/dtrace/smbvfs.d sparc +usr/lib/smbsrv/dtrace/stype.d sparc +# +# Private dirent, extended to include flags, for use by SMB server +# +usr/include/sys/extdirent.h sparc diff --git a/usr/src/tools/scripts/bfu.sh b/usr/src/tools/scripts/bfu.sh index 15c7ecefc9cb..26fb0ad2773d 100644 --- a/usr/src/tools/scripts/bfu.sh +++ b/usr/src/tools/scripts/bfu.sh @@ -167,6 +167,7 @@ all_zones_files=" etc/user_attr etc/uucp/[A-Z]* etc/vfstab + var/smb/* var/spool/cron/crontabs/* var/yp/Makefile var/yp/aliases diff --git a/usr/src/uts/Makefile b/usr/src/uts/Makefile index ccba9de179cc..82b011f368b6 100644 --- a/usr/src/uts/Makefile +++ b/usr/src/uts/Makefile @@ -96,7 +96,7 @@ COMMON_HDRDIRS= common/des common/fs common/gssapi common/inet common/net \ common/netinet common/nfs common/rpc common/sys common/vm \ common/c2 common/pcmcia/sys common/rpcsvc common/inet/kssl \ common/inet/nca common/inet/ipf/netinet common/ipp common/idmap \ - common/sharefs + common/sharefs common/smbsrv # These aren't the only headers in closed. But the other directories # are simple enough that they can be driven from the src tree. diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index c86259c8b139..a4d01364f13b 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -175,9 +175,9 @@ GENUNIX_OBJS += \ ioctl.o \ issetugid.o \ ippconf.o \ - kiconv.o \ - kdi.o \ kcpc.o \ + kdi.o \ + kiconv.o \ kmem.o \ ksyms_snapshot.o \ l_strplumb.o \ @@ -359,6 +359,8 @@ GENUNIX_OBJS += \ watchpoint.o \ yield.o \ scsi_confdata.o \ + xattr.o \ + xattr_common.o \ xdr_mblk.o \ xdr_mem.o \ xdr.o \ @@ -878,6 +880,120 @@ NFSSRV_OBJS += nfs_server.o nfs_srv.o nfs3_srv.o \ nfs4_srv_ns.o nfs4_db.o nfs4_srv_deleg.o \ nfs4_deleg_ops.o nfs4_srv_readdir.o nfs4_dispatch.o +SMBSRV_SHARED_OBJS += \ + smb_match.o \ + smb_msgbuf.o \ + smb_oem.o \ + smb_opmlang.o \ + smb_strcase.o \ + smb_string.o \ + smb_utf8.o \ + smb_common_door_decode.o \ + smb_xdr_utils.o \ + smb_token.o \ + smb_token_xdr.o \ + smb_sid.o \ + smb_status_xlat.o \ + smb_native.o \ + smb_netbios_util.o \ + smb_share_door_decode.o + +SMBSRV_OBJS += $(SMBSRV_SHARED_OBJS) \ + smb_acl.o \ + smb_alloc.o \ + smb_books.o \ + smb_check_directory.o \ + smb_close.o \ + smb_common_lock.o \ + smb_common_open.o \ + smb_common_search.o \ + smb_common_transact.o \ + smb_common_tree.o \ + smb_copy.o \ + smb_create.o \ + smb_create_directory.o \ + smb_delete.o \ + smb_delete_directory.o \ + smb_dispatch.o \ + smb_echo.o \ + smb_fem.o \ + smb_find.o \ + smb_find_notify_close.o \ + smb_find_unique.o \ + smb_flush.o \ + smb_forward.o \ + smb_fsd.o \ + smb_fsops.o \ + smb_init.o \ + smb_kdoor_encdec.o \ + smb_kdoor_ops.o \ + smb_kdoor_clnt.o \ + smb_kdoor_srv.o \ + smb_lock_byte_range.o \ + smb_lock_svc.o \ + smb_locking_andx.o \ + smb_logoff_andx.o \ + smb_mangle_name.o \ + smb_mbuf_marshaling.o \ + smb_memory_manager.o \ + smb_mbuf_util.o \ + smb_move.o \ + smb_negotiate.o \ + smb_net.o \ + smb_node.o \ + smb_nt_cancel.o \ + smb_nt_create_andx.o \ + smb_nt_transact_create.o \ + smb_nt_transact_ioctl.o \ + smb_nt_transact_notify_change.o \ + smb_nt_transact_security.o \ + smb_odir.o \ + smb_ofile.o \ + smb_open_andx.o \ + smb_path_name_reduction.o \ + smb_print.o \ + smb_process_exit.o \ + smb_query_information.o \ + smb_query_information2.o \ + smb_query_information_disk.o \ + smb_read.o \ + smb_rename.o \ + smb_rpc.o \ + smb_sd.o \ + smb_search.o \ + smb_seek.o \ + smb_session.o \ + smb_session_setup_andx.o \ + smb_svc_sm.o \ + smb_set_information.o \ + smb_set_information2.o \ + smb_signing.o \ + smb_share_kdoor_client.o \ + smb_tree.o \ + smb_trans2_create_directory.o \ + smb_trans2_dfs.o \ + smb_trans2_find.o \ + smb_trans2_open2.o \ + smb_trans2_query_file_info.o \ + smb_trans2_query_fs_information.o \ + smb_trans2_query_path_info.o \ + smb_trans2_set_file_information.o \ + smb_trans2_set_information.o \ + smb_trans2_set_path_information.o \ + smb_tree_connect.o \ + smb_tree_connect_andx.o \ + smb_tree_disconnect.o \ + smb_unlock_byte_range.o \ + smb_upcalls.o \ + smb_user.o \ + smb_util.o \ + smb_vfs.o \ + smb_vops.o \ + smb_winpipe.o \ + smb_write.o \ + smb_write_raw.o \ + smb_xlate.o + PCFS_OBJS += pc_alloc.o pc_dir.o pc_node.o pc_subr.o \ pc_vfsops.o pc_vnops.o @@ -1006,6 +1122,7 @@ ZFS_OBJS += \ zfs_acl.o \ zfs_ctldir.o \ zfs_dir.o \ + zfs_fuid.o \ zfs_ioctl.o \ zfs_log.o \ zfs_replay.o \ diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules index d843b1b9b4b9..9e7f6a84a5f9 100644 --- a/usr/src/uts/common/Makefile.rules +++ b/usr/src/uts/common/Makefile.rules @@ -222,6 +222,14 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/nfs/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(COMMONBASE)/smbsrv/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/smbsrv/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/objfs/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -270,6 +278,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/zfs/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(COMMONBASE)/xattr/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(COMMONBASE)/zfs/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -1142,6 +1154,12 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/mntfs/%.c $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/namefs/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(COMMONBASE)/smbsrv/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/smbsrv/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/nfs/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) @@ -1184,6 +1202,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/ufs_log/%.c $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/zfs/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(COMMONBASE)/xattr/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(COMMONBASE)/zfs/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) diff --git a/usr/src/uts/common/brand/lx/autofs/lx_autofs.c b/usr/src/uts/common/brand/lx/autofs/lx_autofs.c index b40b508625f9..c4c9e9681506 100644 --- a/usr/src/uts/common/brand/lx/autofs/lx_autofs.c +++ b/usr/src/uts/common/brand/lx/autofs/lx_autofs.c @@ -442,7 +442,7 @@ i_fifo_lookup(pid_t pgrp, int fd, file_t **fpp_wr, file_t **fpp_rd) } /* - * We need to drop fi_lock before we can try to aquire f_tlock + * We need to drop fi_lock before we can try to acquire f_tlock * the good news is that the file pointers are protected because * we're still holding uf_lock. */ @@ -707,7 +707,7 @@ i_bs_readdir(vnode_t *dvp, list_t *dir_stack, list_t *file_stack) iov.iov_len = dlen; (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL); - if (VOP_READDIR(dvp, &uio, kcred, &eof) != 0) { + if (VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0) != 0) { VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL); kmem_free(dbuf, dlen); return (-1); @@ -727,8 +727,8 @@ i_bs_readdir(vnode_t *dvp, list_t *dir_stack, list_t *file_stack) if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0) continue; - if (VOP_LOOKUP(dvp, - nm, &vp, NULL, 0, NULL, kcred) != 0) { + if (VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, kcred, + NULL, NULL, NULL) != 0) { kmem_free(dbuf, dlen); return (-1); } @@ -763,7 +763,8 @@ i_bs_destroy(vnode_t *dvp, char *path) char *dpath, *fpath; int ret; - if (VOP_LOOKUP(dvp, path, &vp, NULL, 0, NULL, kcred) != 0) { + if (VOP_LOOKUP(dvp, path, &vp, NULL, 0, NULL, kcred, + NULL, NULL, NULL) != 0) { /* A directory entry with this name doesn't actually exist. */ return; } @@ -771,7 +772,7 @@ i_bs_destroy(vnode_t *dvp, char *path) if ((vp->v_type & VDIR) == 0) { /* Easy, the directory entry is a file so delete it. */ VN_RELE(vp); - (void) VOP_REMOVE(dvp, path, kcred); + (void) VOP_REMOVE(dvp, path, kcred, NULL, 0); return; } @@ -796,7 +797,7 @@ i_bs_destroy(vnode_t *dvp, char *path) if (i_bs_readdir(dvp, &search_stack, NULL) != 0) goto exit; - /* Save the current directory a seperate stack. */ + /* Save the current directory a separate stack. */ i_stack_push(&dir_stack, (caddr_t)pdvp, (caddr_t)dvp, dpath); } @@ -818,7 +819,7 @@ i_bs_destroy(vnode_t *dvp, char *path) while (i_stack_pop(&file_stack, NULL, (caddr_t *)&vp, &fpath) == 0) { VN_RELE(vp) - ret = VOP_REMOVE(dvp, fpath, kcred); + ret = VOP_REMOVE(dvp, fpath, kcred, NULL, 0); i_strfree(fpath); if (ret != 0) { i_strfree(dpath); @@ -828,7 +829,7 @@ i_bs_destroy(vnode_t *dvp, char *path) /* Delete this directory. */ VN_RELE(dvp); - ret = VOP_RMDIR(pdvp, dpath, pdvp, kcred); + ret = VOP_RMDIR(pdvp, dpath, pdvp, kcred, NULL, 0); i_strfree(dpath); if (ret != 0) goto exit; @@ -862,7 +863,7 @@ i_bs_create(vnode_t *dvp, char *bs_name) vattr.va_mode = 0755; /* u+rwx,og=rx */ vattr.va_mask = AT_TYPE|AT_MODE; - if (VOP_MKDIR(dvp, bs_name, &vattr, &vp, kcred) != 0) + if (VOP_MKDIR(dvp, bs_name, &vattr, &vp, kcred, NULL, 0, NULL) != 0) return (NULL); return (vp); } @@ -1220,24 +1221,27 @@ static const fs_operation_def_t lx_autofs_vfstops[] = { * the underlying filesystem we're mounted on. */ static int -lx_autofs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) +lx_autofs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, + caller_context_t *ctp) { vnode_t *uvp = vp->v_data; - return (VOP_CLOSE(uvp, flag, count, offset, cr)); + return (VOP_CLOSE(uvp, flag, count, offset, cr, ctp)); } static int -lx_autofs_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp) +lx_autofs_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp, + caller_context_t *ctp, int flags) { vnode_t *uvp = vp->v_data; - return (VOP_READDIR(uvp, uiop, cr, eofp)); + return (VOP_READDIR(uvp, uiop, cr, eofp, ctp, flags)); } static int -lx_autofs_access(vnode_t *vp, int mode, int flags, cred_t *cr) +lx_autofs_access(vnode_t *vp, int mode, int flags, cred_t *cr, + caller_context_t *ctp) { vnode_t *uvp = vp->v_data; - return (VOP_ACCESS(uvp, mode, flags, cr)); + return (VOP_ACCESS(uvp, mode, flags, cr, ctp)); } static int @@ -1256,7 +1260,8 @@ lx_autofs_rwunlock(struct vnode *vp, int write_lock, caller_context_t *ctp) /*ARGSUSED*/ static int -lx_autofs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) +lx_autofs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr, + caller_context_t *ctp, int flags) { vnode_t *udvp = dvp->v_data; @@ -1269,10 +1274,10 @@ lx_autofs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) */ if (vn_matchops(cdir, lx_autofs_vn_ops)) { vnode_t *ucdir = cdir->v_data; - return (VOP_RMDIR(udvp, nm, ucdir, cr)); + return (VOP_RMDIR(udvp, nm, ucdir, cr, ctp, flags)); } - return (VOP_RMDIR(udvp, nm, cdir, cr)); + return (VOP_RMDIR(udvp, nm, cdir, cr, ctp, flags)); } /* @@ -1280,17 +1285,17 @@ lx_autofs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) * * For some VOP entry points we will first pass the request on to * the underlying filesystem we're mounted on. If there's an error - * then we immediatly return the error, but if the request succeedes + * then we immediately return the error, but if the request succeeds * we have to do some extra work before returning. */ static int -lx_autofs_open(vnode_t **vpp, int flag, cred_t *cr) +lx_autofs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ctp) { vnode_t *ovp = *vpp; vnode_t *uvp = ovp->v_data; int error; - if ((error = VOP_OPEN(&uvp, flag, cr)) != 0) + if ((error = VOP_OPEN(&uvp, flag, cr, ctp)) != 0) return (error); /* Check for clone opens. */ @@ -1304,12 +1309,13 @@ lx_autofs_open(vnode_t **vpp, int flag, cred_t *cr) } static int -lx_autofs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +lx_autofs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ctp) { vnode_t *uvp = vp->v_data; int error; - if ((error = VOP_GETATTR(uvp, vap, flags, cr)) != 0) + if ((error = VOP_GETATTR(uvp, vap, flags, cr, ctp)) != 0) return (error); /* Update the attributes with our filesystem id. */ @@ -1319,13 +1325,14 @@ lx_autofs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) static int lx_autofs_mkdir(vnode_t *dvp, char *nm, struct vattr *vap, vnode_t **vpp, - cred_t *cr) + cred_t *cr, caller_context_t *ctp, int flags, vsecattr_t *vsecp) { vnode_t *udvp = dvp->v_data; vnode_t *uvp = NULL; int error; - if ((error = VOP_MKDIR(udvp, nm, vap, &uvp, cr)) != 0) + if ((error = VOP_MKDIR(udvp, nm, vap, &uvp, cr, + ctp, flags, vsecp)) != 0) return (error); /* Update the attributes with our filesystem id. */ @@ -1341,7 +1348,7 @@ lx_autofs_mkdir(vnode_t *dvp, char *nm, struct vattr *vap, vnode_t **vpp, */ /*ARGSUSED*/ static void -lx_autofs_inactive(struct vnode *vp, struct cred *cr) +lx_autofs_inactive(struct vnode *vp, struct cred *cr, caller_context_t *ctp) { lx_autofs_vfs_t *data = vp->v_vfsp->vfs_data; @@ -1374,14 +1381,16 @@ lx_autofs_inactive(struct vnode *vp, struct cred *cr) static int lx_autofs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, - int flags, vnode_t *rdir, cred_t *cr) + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ctp, + int *direntflags, pathname_t *realpnp) { vnode_t *udvp = dvp->v_data; vnode_t *uvp = NULL; int error; /* First try to lookup if this path component already exitst. */ - if ((error = VOP_LOOKUP(udvp, nm, &uvp, pnp, flags, rdir, cr)) == 0) { + if ((error = VOP_LOOKUP(udvp, nm, &uvp, pnp, flags, rdir, cr, ctp, + direntflags, realpnp)) == 0) { *vpp = i_vn_alloc(dvp->v_vfsp, uvp); return (0); } @@ -1395,7 +1404,8 @@ lx_autofs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, return (error); /* Retry the lookup operation. */ - if ((error = VOP_LOOKUP(udvp, nm, &uvp, pnp, flags, rdir, cr)) == 0) { + if ((error = VOP_LOOKUP(udvp, nm, &uvp, pnp, flags, rdir, cr, ctp, + direntflags, realpnp)) == 0) { *vpp = i_vn_alloc(dvp->v_vfsp, uvp); return (0); } @@ -1405,7 +1415,7 @@ lx_autofs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, /*ARGSUSED*/ static int lx_autofs_ioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr, - int *rvalp) + int *rvalp, caller_context_t *ctp) { vnode_t *uvp = vp->v_data; @@ -1421,7 +1431,7 @@ lx_autofs_ioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr, } /* Pass any remaining ioctl on. */ - return (VOP_IOCTL(uvp, cmd, arg, mode, cr, rvalp)); + return (VOP_IOCTL(uvp, cmd, arg, mode, cr, rvalp, ctp)); } /* diff --git a/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c b/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c index a09f9fb2b711..588ca2e4226c 100644 --- a/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c +++ b/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c @@ -32,7 +32,7 @@ * * In order to preserve Solaris' security policy. This file system's * functionality does not override Solaris' security policies even if - * that means breaking Linux compatability. + * that means breaking Linux compatibility. * * Linux has no concept of lwps so we only implement procs here as in the * old /proc interface. @@ -75,19 +75,23 @@ extern time_t boot_time; */ vnodeops_t *lxpr_vnodeops; -static int lxpr_open(vnode_t **, int, cred_t *); -static int lxpr_close(vnode_t *, int, int, offset_t, cred_t *); +static int lxpr_open(vnode_t **, int, cred_t *, caller_context_t *); +static int lxpr_close(vnode_t *, int, int, offset_t, cred_t *, + caller_context_t *); static int lxpr_read(vnode_t *, uio_t *, int, cred_t *, caller_context_t *); -static int lxpr_getattr(vnode_t *, vattr_t *, int, cred_t *); -static int lxpr_access(vnode_t *, int, int, cred_t *); +static int lxpr_getattr(vnode_t *, vattr_t *, int, cred_t *, + caller_context_t *); +static int lxpr_access(vnode_t *, int, int, cred_t *, caller_context_t *); static int lxpr_lookup(vnode_t *, char *, vnode_t **, - pathname_t *, int, vnode_t *, cred_t *); -static int lxpr_readdir(vnode_t *, uio_t *, cred_t *, int *); -static int lxpr_readlink(vnode_t *, uio_t *, cred_t *); -static int lxpr_cmp(vnode_t *, vnode_t *); -static int lxpr_realvp(vnode_t *, vnode_t **); + pathname_t *, int, vnode_t *, cred_t *, caller_context_t *, int *, + pathname_t *); +static int lxpr_readdir(vnode_t *, uio_t *, cred_t *, int *, + caller_context_t *, int); +static int lxpr_readlink(vnode_t *, uio_t *, cred_t *, caller_context_t *); +static int lxpr_cmp(vnode_t *, vnode_t *, caller_context_t *); +static int lxpr_realvp(vnode_t *, vnode_t **, caller_context_t *); static int lxpr_sync(void); -static void lxpr_inactive(vnode_t *, cred_t *); +static void lxpr_inactive(vnode_t *, cred_t *, caller_context_t *); static vnode_t *lxpr_lookup_procdir(vnode_t *, char *); static vnode_t *lxpr_lookup_piddir(vnode_t *, char *); @@ -243,7 +247,7 @@ static lxpr_dirent_t netdir[] = { * lxpr_open(): Vnode operation for VOP_OPEN() */ static int -lxpr_open(vnode_t **vpp, int flag, cred_t *cr) +lxpr_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { vnode_t *vp = *vpp; lxpr_node_t *lxpnp = VTOLXP(vp); @@ -272,7 +276,7 @@ lxpr_open(vnode_t **vpp, int flag, cred_t *cr) * Need to hold rvp since VOP_OPEN() may release it. */ VN_HOLD(rvp); - error = VOP_OPEN(&rvp, flag, cr); + error = VOP_OPEN(&rvp, flag, cr, ct); if (error) { VN_RELE(rvp); } else { @@ -317,7 +321,8 @@ lxpr_open(vnode_t **vpp, int flag, cred_t *cr) */ /* ARGSUSED */ static int -lxpr_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) +lxpr_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, + caller_context_t *ct) { lxpr_node_t *lxpr = VTOLXP(vp); lxpr_nodetype_t type = lxpr->lxpr_type; @@ -705,7 +710,8 @@ lxpr_read_pid_maps(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf) *buf = '\0'; if (pbuf->vp != NULL) { vattr.va_mask = AT_FSID | AT_NODEID; - if (VOP_GETATTR(pbuf->vp, &vattr, 0, CRED()) == 0) { + if (VOP_GETATTR(pbuf->vp, &vattr, 0, CRED(), + NULL) == 0) { maj = getmajor(vattr.va_fsid); min = getminor(vattr.va_fsid); inode = vattr.va_nodeid; @@ -2042,7 +2048,8 @@ lxpr_read_fd(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf) * lxpr_getattr(): Vnode operation for VOP_GETATTR() */ static int -lxpr_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +lxpr_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { register lxpr_node_t *lxpnp = VTOLXP(vp); lxpr_nodetype_t type = lxpnp->lxpr_type; @@ -2060,14 +2067,14 @@ lxpr_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) /* * withold attribute information to owner or root */ - if ((error = VOP_ACCESS(rvp, 0, 0, cr)) != 0) { + if ((error = VOP_ACCESS(rvp, 0, 0, cr, ct)) != 0) { return (error); } /* * now its attributes */ - if ((error = VOP_GETATTR(rvp, vap, flags, cr)) != 0) { + if ((error = VOP_GETATTR(rvp, vap, flags, cr, ct)) != 0) { return (error); } @@ -2122,7 +2129,7 @@ lxpr_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) * lxpr_access(): Vnode operation for VOP_ACCESS() */ static int -lxpr_access(vnode_t *vp, int mode, int flags, cred_t *cr) +lxpr_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct) { lxpr_node_t *lxpnp = VTOLXP(vp); int shift = 0; @@ -2162,7 +2169,7 @@ lxpr_access(vnode_t *vp, int mode, int flags, cred_t *cr) /* * For these we use the underlying vnode's accessibility. */ - return (VOP_ACCESS(lxpnp->lxpr_realvp, mode, flags, cr)); + return (VOP_ACCESS(lxpnp->lxpr_realvp, mode, flags, cr, ct)); } /* If user is root allow access regardless of permission bits */ @@ -2207,7 +2214,8 @@ lxpr_lookup_not_a_dir(vnode_t *dp, char *comp) /* ARGSUSED */ static int lxpr_lookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pathp, - int flags, vnode_t *rdir, cred_t *cr) + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp) { lxpr_node_t *lxpnp = VTOLXP(dp); lxpr_nodetype_t type = lxpnp->lxpr_type; @@ -2227,7 +2235,7 @@ lxpr_lookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pathp, /* * restrict lookup permission to owner or root */ - if ((error = lxpr_access(dp, VEXEC, 0, cr)) != 0) { + if ((error = lxpr_access(dp, VEXEC, 0, cr, ct)) != 0) { return (error); } @@ -2477,7 +2485,8 @@ lxpr_lookup_procdir(vnode_t *dp, char *comp) */ /* ARGSUSED */ static int -lxpr_readdir(vnode_t *dp, uio_t *uiop, cred_t *cr, int *eofp) +lxpr_readdir(vnode_t *dp, uio_t *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) { lxpr_node_t *lxpnp = VTOLXP(dp); lxpr_nodetype_t type = lxpnp->lxpr_type; @@ -2499,7 +2508,7 @@ lxpr_readdir(vnode_t *dp, uio_t *uiop, cred_t *cr, int *eofp) /* * restrict readdir permission to owner or root */ - if ((error = lxpr_access(dp, VREAD, 0, cr)) != 0) + if ((error = lxpr_access(dp, VREAD, 0, cr, ct)) != 0) return (error); uoffset = uiop->uio_offset; @@ -2909,7 +2918,7 @@ lxpr_readdir_fddir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp) */ /* ARGSUSED */ static int -lxpr_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr) +lxpr_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct) { char bp[MAXPATHLEN + 1]; size_t buflen = sizeof (bp); @@ -2924,7 +2933,7 @@ lxpr_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr) /* Try to produce a symlink name for anything that has a realvp */ if (rvp != NULL) { - if ((error = lxpr_access(vp, VREAD, 0, CRED())) != 0) + if ((error = lxpr_access(vp, VREAD, 0, CRED(), ct)) != 0) return (error); if ((error = vnodetopath(NULL, rvp, bp, buflen, CRED())) != 0) return (error); @@ -2971,7 +2980,7 @@ lxpr_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr) */ /* ARGSUSED */ static void -lxpr_inactive(vnode_t *vp, cred_t *cr) +lxpr_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { lxpr_freenode(VTOLXP(vp)); } @@ -2995,7 +3004,7 @@ lxpr_sync() * lxpr_cmp(): Vnode operation for VOP_CMP() */ static int -lxpr_cmp(vnode_t *vp1, vnode_t *vp2) +lxpr_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct) { vnode_t *rvp; @@ -3007,7 +3016,7 @@ lxpr_cmp(vnode_t *vp1, vnode_t *vp2) vp2 = rvp; if (vn_matchops(vp1, lxpr_vnodeops) || vn_matchops(vp2, lxpr_vnodeops)) return (vp1 == vp2); - return (VOP_CMP(vp1, vp2)); + return (VOP_CMP(vp1, vp2, ct)); } @@ -3015,13 +3024,13 @@ lxpr_cmp(vnode_t *vp1, vnode_t *vp2) * lxpr_realvp(): Vnode operation for VOP_REALVP() */ static int -lxpr_realvp(vnode_t *vp, vnode_t **vpp) +lxpr_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct) { vnode_t *rvp; if ((rvp = VTOLXP(vp)->lxpr_realvp) != NULL) { vp = rvp; - if (VOP_REALVP(vp, &rvp) == 0) + if (VOP_REALVP(vp, &rvp, ct) == 0) vp = rvp; } diff --git a/usr/src/uts/common/c2/audit.c b/usr/src/uts/common/c2/audit.c index 944c1e8a28a2..a01733a9561c 100644 --- a/usr/src/uts/common/c2/audit.c +++ b/usr/src/uts/common/c2/audit.c @@ -468,7 +468,7 @@ audit_addcomponent(struct pathname *pnp) * flag = 1, path is absolute. Free any saved path and set flag to PAD_ABSPATH. * * If the (new) path is absolute, then we have to throw away whatever we have - * already accumulated since it is being superceeded by new path which is + * already accumulated since it is being superseded by new path which is * anchored at the root. * Note that if the path is relative, this function does nothing * TODO: @@ -641,7 +641,7 @@ file_is_public(struct vattr *attr) /* * ROUTINE: AUDIT_ATTRIBUTES - * PURPOSE: Audit the attributes so we can tell why the error occured + * PURPOSE: Audit the attributes so we can tell why the error occurred * CALLBY: AUDIT_SAVEPATH * AUDIT_VNCREATE_FINISH * AUS_FCHOWN...audit_event.c...audit_path.c @@ -659,7 +659,7 @@ audit_attributes(struct vnode *vp) if (vp) { attr.va_mask = AT_ALL; - if (VOP_GETATTR(vp, &attr, 0, CRED()) != 0) + if (VOP_GETATTR(vp, &attr, 0, CRED(), NULL) != 0) return; if (file_is_public(&attr) && (tad->tad_ctrl & PAD_PUBLIC_EV)) { @@ -1047,7 +1047,7 @@ audit_closef(struct file *fp) */ if ((vp = fp->f_vnode) != NULL) { attr.va_mask = AT_ALL; - getattr_ret = VOP_GETATTR(vp, &attr, 0, CRED()); + getattr_ret = VOP_GETATTR(vp, &attr, 0, CRED(), NULL); } /* @@ -1294,7 +1294,14 @@ audit_setfsat_path(int argnum) t_audit_data_t *tad; struct f_audit_data *fad; p_audit_data_t *pad; /* current process */ - + struct a { + long id; + long arg1; + long arg2; + long arg3; + long arg4; + long arg5; + } *uap; struct b { long arg1; long arg2; @@ -1306,6 +1313,7 @@ audit_setfsat_path(int argnum) if (clwp == NULL) return; uap1 = (struct b *)&clwp->lwp_ap[1]; + uap = (struct a *)clwp->lwp_ap; tad = U2A(u); @@ -1334,6 +1342,10 @@ audit_setfsat_path(int argnum) return; } + if (uap->id == 9 && tad->tad_atpath != NULL) { /* openattrdir */ + tad->tad_ctrl |= PAD_ATPATH; + return; + } if (tad->tad_atpath != NULL) { au_pathrele(tad->tad_atpath); tad->tad_atpath = NULL; @@ -1373,7 +1385,8 @@ audit_symlink_create(vnode_t *dvp, char *sname, char *target, int error) if (error) return; - error = VOP_LOOKUP(dvp, sname, &vp, NULL, 0, NULL, CRED()); + error = VOP_LOOKUP(dvp, sname, &vp, NULL, 0, NULL, CRED(), + NULL, NULL, NULL); if (error == 0) { audit_attributes(vp); VN_RELE(vp); @@ -1648,7 +1661,7 @@ audit_chdirec(vnode_t *vp, vnode_t **vpp) * the same object, it will not panic our system * QUESTION: * where to decrement the f_count????????????????? - * seems like I need to set a flag if f_count incrmented through audit_getf + * seems like I need to set a flag if f_count incremented through audit_getf */ /*ARGSUSED*/ @@ -1898,7 +1911,7 @@ audit_fdsend(fd, fp, error) } /* - * Record privileges sucessfully used and we attempted to use but + * Record privileges successfully used and we attempted to use but * didn't have. */ void diff --git a/usr/src/uts/common/c2/audit_event.c b/usr/src/uts/common/c2/audit_event.c index d9be55d304da..e1ffc61c8166 100644 --- a/usr/src/uts/common/c2/audit_event.c +++ b/usr/src/uts/common/c2/audit_event.c @@ -1040,6 +1040,9 @@ aui_fsat(au_event_t e) case 7: /* renameat */ e = AUE_RENAMEAT; break; + case 9: /* __openattrdirat */ + tad->tad_ctrl |= PAD_SAVPATH; + /*FALLTHROUGH*/ default: e = AUE_NULL; break; @@ -1866,7 +1869,7 @@ aus_close(struct t_audit_data *tad) au_uwrite(au_to_path(fad->fad_aupath)); if ((vp = fp->f_vnode) != NULL) { attr.va_mask = AT_ALL; - if (VOP_GETATTR(vp, &attr, 0, CRED()) == 0) { + if (VOP_GETATTR(vp, &attr, 0, CRED(), NULL) == 0) { /* * When write was not used and the file can be * considered public, skip the audit. @@ -4585,7 +4588,7 @@ auf_sendto(struct t_audit_data *tad, int error, rval_t *rval) } /* - * XXX socket(2) may be equivilent to open(2) on a unix domain + * XXX socket(2) may be equivalent to open(2) on a unix domain * socket. This needs investigation. */ @@ -4910,7 +4913,7 @@ au_door_lookup(int did) /* * Use the underlying vnode (we may be namefs mounted) */ - if (VOP_REALVP(fp->f_vnode, &vp)) + if (VOP_REALVP(fp->f_vnode, &vp, NULL)) vp = fp->f_vnode; if (vp == NULL || vp->v_type != VDOOR) { diff --git a/usr/src/uts/common/cpr/cpr_dump.c b/usr/src/uts/common/cpr/cpr_dump.c index 28fee49bf912..709b50631e9b 100644 --- a/usr/src/uts/common/cpr/cpr_dump.c +++ b/usr/src/uts/common/cpr/cpr_dump.c @@ -1015,7 +1015,8 @@ cpr_write(vnode_t *vp, caddr_t buffer, size_t size) } do_polled_io = 1; - error = VOP_DUMP(vp, cpr_buf, cpr_file_bn, cpr_buf_blocks); + error = VOP_DUMP(vp, cpr_buf, cpr_file_bn, cpr_buf_blocks, + NULL); do_polled_io = 0; CPR_DEBUG(CPR_DEBUG3, "done\n"); @@ -1045,7 +1046,7 @@ cpr_flush_write(vnode_t *vp) nblk = btod(cpr_wptr - cpr_buf); do_polled_io = 1; - error = VOP_DUMP(vp, (caddr_t)cpr_buf, cpr_file_bn, nblk); + error = VOP_DUMP(vp, (caddr_t)cpr_buf, cpr_file_bn, nblk, NULL); do_polled_io = 0; cpr_file_bn += nblk; diff --git a/usr/src/uts/common/cpr/cpr_main.c b/usr/src/uts/common/cpr/cpr_main.c index 65e911cb119a..96b1d295341e 100644 --- a/usr/src/uts/common/cpr/cpr_main.c +++ b/usr/src/uts/common/cpr/cpr_main.c @@ -259,7 +259,7 @@ cpr_log_status(int enable, int *svstat, vnode_t *vp) */ if (enable == 0) { if (error = VOP_IOCTL(vp, _FIOISLOG, - (uintptr_t)&status, FKIOCTL, CRED(), NULL)) { + (uintptr_t)&status, FKIOCTL, CRED(), NULL, NULL)) { mntpt = vfs_getmntpoint(vp->v_vfsp); prom_printf("%s: \"%s\", cant get logging " "status, error %d\n", str, refstr_value(mntpt), @@ -287,7 +287,7 @@ cpr_log_status(int enable, int *svstat, vnode_t *vp) */ if (*svstat == 1) { error = VOP_IOCTL(vp, cmd, (uintptr_t)&fl, - FKIOCTL, CRED(), NULL); + FKIOCTL, CRED(), NULL, NULL); if (error) { mntpt = vfs_getmntpoint(vp->v_vfsp); prom_printf("%s: \"%s\", cant %s logging, error %d\n", @@ -338,7 +338,7 @@ cpr_ufs_logging(int enable) return (error); cpr_log_status(enable, &def_status, vp); vfsp = vp->v_vfsp; - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); fname = cpr_build_statefile_path(); @@ -357,7 +357,7 @@ cpr_ufs_logging(int enable) */ if (vp->v_vfsp != vfsp && vp->v_type == VREG) cpr_log_status(enable, &sf_status, vp); - (void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); return (0); @@ -739,7 +739,7 @@ cpr_suspend(int sleeptype) rc = cpr_dump(C_VP); /* - * if any error occured during dump, more + * if any error occurred during dump, more * special handling for reusable: */ if (rc && cpr_reusable_mode) { diff --git a/usr/src/uts/common/cpr/cpr_misc.c b/usr/src/uts/common/cpr/cpr_misc.c index 1ec0452c81dc..e35789b4a956 100644 --- a/usr/src/uts/common/cpr/cpr_misc.c +++ b/usr/src/uts/common/cpr/cpr_misc.c @@ -189,7 +189,7 @@ cpr_get_config(void) } err = cpr_rdwr(UIO_READ, vp, cf, sizeof (*cf)); - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); if (err) { cpr_err(CE_CONT, fmt, "read", config_path, err); @@ -455,7 +455,7 @@ cpr_alloc_statefile(int alloc_retry) if (cpr_debug & (CPR_DEBUG1 | CPR_DEBUG7)) prom_printf(str); if (C_VP->v_type != VBLK) - (void) VOP_DUMPCTL(C_VP, DUMP_FREE, NULL); + (void) VOP_DUMPCTL(C_VP, DUMP_FREE, NULL, NULL); } else { /* * Open an exiting file for writing, the state file needs to be @@ -501,7 +501,7 @@ cpr_alloc_statefile(int alloc_retry) * Validate disk blocks allocation for the state file. * Ask the file system prepare itself for the dump operation. */ - if (rc = VOP_DUMPCTL(C_VP, DUMP_ALLOC, NULL)) { + if (rc = VOP_DUMPCTL(C_VP, DUMP_ALLOC, NULL, NULL)) { cpr_err(CE_CONT, "Error allocating " "blocks for cpr statefile."); return (rc); @@ -730,8 +730,8 @@ cpr_statef_close(void) { if (C_VP) { if (!cpr_reusable_mode) - (void) VOP_DUMPCTL(C_VP, DUMP_FREE, NULL); - (void) VOP_CLOSE(C_VP, FWRITE, 1, (offset_t)0, CRED()); + (void) VOP_DUMPCTL(C_VP, DUMP_FREE, NULL, NULL); + (void) VOP_CLOSE(C_VP, FWRITE, 1, (offset_t)0, CRED(), NULL); VN_RELE(C_VP); C_VP = 0; } @@ -771,9 +771,9 @@ cpr_write_deffile(cdef_t *cdef) if (rc = cpr_rdwr(UIO_WRITE, vp, cdef, sizeof (*cdef))) str = "write"; - else if (rc = VOP_FSYNC(vp, FSYNC, CRED())) + else if (rc = VOP_FSYNC(vp, FSYNC, CRED(), NULL)) str = "fsync"; - (void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); if (rc) { @@ -799,7 +799,7 @@ cpr_clear_definfo(void) return; mini.magic = mini.reusable = 0; (void) cpr_rdwr(UIO_WRITE, vp, &mini, sizeof (mini)); - (void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); } @@ -818,7 +818,7 @@ cpr_get_reusable_mode(void) return (0); rc = cpr_rdwr(UIO_READ, vp, &mini, sizeof (mini)); - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); if (rc == 0 && mini.magic == CPR_DEFAULT_MAGIC) return (mini.reusable); diff --git a/usr/src/uts/common/fs/autofs/auto_vnops.c b/usr/src/uts/common/fs/autofs/auto_vnops.c index 5ffcf57d83e1..9e1a6ef3870b 100644 --- a/usr/src/uts/common/fs/autofs/auto_vnops.c +++ b/usr/src/uts/common/fs/autofs/auto_vnops.c @@ -53,29 +53,39 @@ /* * Vnode ops for autofs */ -static int auto_open(vnode_t **, int, cred_t *); -static int auto_close(vnode_t *, int, int, offset_t, cred_t *); -static int auto_getattr(vnode_t *, vattr_t *, int, cred_t *); +static int auto_open(vnode_t **, int, cred_t *, caller_context_t *); +static int auto_close(vnode_t *, int, int, offset_t, cred_t *, + caller_context_t *); +static int auto_getattr(vnode_t *, vattr_t *, int, cred_t *, + caller_context_t *); static int auto_setattr(vnode_t *, vattr_t *, int, cred_t *, caller_context_t *); -static int auto_access(vnode_t *, int, int, cred_t *); +static int auto_access(vnode_t *, int, int, cred_t *, caller_context_t *); static int auto_lookup(vnode_t *, char *, vnode_t **, - pathname_t *, int, vnode_t *, cred_t *); + pathname_t *, int, vnode_t *, cred_t *, caller_context_t *, int *, + pathname_t *); static int auto_create(vnode_t *, char *, vattr_t *, vcexcl_t, - int, vnode_t **, cred_t *, int); -static int auto_remove(vnode_t *, char *, cred_t *); -static int auto_link(vnode_t *, vnode_t *, char *, cred_t *); -static int auto_rename(vnode_t *, char *, vnode_t *, char *, cred_t *); -static int auto_mkdir(vnode_t *, char *, vattr_t *, vnode_t **, cred_t *); -static int auto_rmdir(vnode_t *, char *, vnode_t *, cred_t *); -static int auto_readdir(vnode_t *, uio_t *, cred_t *, int *); -static int auto_symlink(vnode_t *, char *, vattr_t *, char *, cred_t *); -static int auto_readlink(vnode_t *, struct uio *, cred_t *); -static int auto_fsync(vnode_t *, int, cred_t *); -static void auto_inactive(vnode_t *, cred_t *); + int, vnode_t **, cred_t *, int, caller_context_t *, vsecattr_t *); +static int auto_remove(vnode_t *, char *, cred_t *, caller_context_t *, int); +static int auto_link(vnode_t *, vnode_t *, char *, cred_t *, + caller_context_t *, int); +static int auto_rename(vnode_t *, char *, vnode_t *, char *, cred_t *, + caller_context_t *, int); +static int auto_mkdir(vnode_t *, char *, vattr_t *, vnode_t **, cred_t *, + caller_context_t *, int, vsecattr_t *); +static int auto_rmdir(vnode_t *, char *, vnode_t *, cred_t *, + caller_context_t *, int); +static int auto_readdir(vnode_t *, uio_t *, cred_t *, int *, + caller_context_t *, int); +static int auto_symlink(vnode_t *, char *, vattr_t *, char *, cred_t *, + caller_context_t *, int); +static int auto_readlink(vnode_t *, struct uio *, cred_t *, + caller_context_t *); +static int auto_fsync(vnode_t *, int, cred_t *, caller_context_t *); +static void auto_inactive(vnode_t *, cred_t *, caller_context_t *); static int auto_rwlock(vnode_t *, int, caller_context_t *); static void auto_rwunlock(vnode_t *vp, int, caller_context_t *); -static int auto_seek(vnode_t *vp, offset_t, offset_t *); +static int auto_seek(vnode_t *vp, offset_t, offset_t *, caller_context_t *); static int auto_trigger_mount(vnode_t *, cred_t *, vnode_t **); @@ -113,7 +123,7 @@ const fs_operation_def_t auto_vnodeops_template[] = { /* ARGSUSED */ static int -auto_open(vnode_t **vpp, int flag, cred_t *cred) +auto_open(vnode_t **vpp, int flag, cred_t *cred, caller_context_t *ct) { vnode_t *newvp; int error; @@ -130,9 +140,9 @@ auto_open(vnode_t **vpp, int flag, cred_t *cred) */ VN_RELE(*vpp); *vpp = newvp; - error = VOP_ACCESS(*vpp, VREAD, 0, cred); + error = VOP_ACCESS(*vpp, VREAD, 0, cred, ct); if (!error) - error = VOP_OPEN(vpp, flag, cred); + error = VOP_OPEN(vpp, flag, cred, ct); } done: @@ -143,13 +153,24 @@ auto_open(vnode_t **vpp, int flag, cred_t *cred) /* ARGSUSED */ static int -auto_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cred) +auto_close( + vnode_t *vp, + int flag, + int count, + offset_t offset, + cred_t *cred, + caller_context_t *ct) { return (0); } static int -auto_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cred) +auto_getattr( + vnode_t *vp, + vattr_t *vap, + int flags, + cred_t *cred, + caller_context_t *ct) { fnnode_t *fnp = vntofn(vp); vnode_t *newvp; @@ -211,7 +232,7 @@ auto_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cred) fnp->fn_thread = curthread; fnp->fn_seen = newvp; mutex_exit(&fnp->fn_lock); - error = VOP_GETATTR(newvp, vap, flags, cred); + error = VOP_GETATTR(newvp, vap, flags, cred, ct); VN_RELE(newvp); mutex_enter(&fnp->fn_lock); fnp->fn_seen = 0; @@ -272,7 +293,7 @@ auto_setattr( if (vn_is_readonly(newvp)) error = EROFS; else - error = VOP_SETATTR(newvp, vap, flags, cred, NULL); + error = VOP_SETATTR(newvp, vap, flags, cred, ct); VN_RELE(newvp); } else error = ENOSYS; @@ -284,7 +305,12 @@ auto_setattr( /* ARGSUSED */ static int -auto_access(vnode_t *vp, int mode, int flags, cred_t *cred) +auto_access( + vnode_t *vp, + int mode, + int flags, + cred_t *cred, + caller_context_t *ct) { fnnode_t *fnp = vntofn(vp); vnode_t *newvp; @@ -299,7 +325,7 @@ auto_access(vnode_t *vp, int mode, int flags, cred_t *cred) /* * Node is mounted on. */ - error = VOP_ACCESS(newvp, mode, 0, cred); + error = VOP_ACCESS(newvp, mode, 0, cred, ct); VN_RELE(newvp); } else { int shift = 0; @@ -333,7 +359,10 @@ auto_lookup( pathname_t *pnp, int flags, vnode_t *rdir, - cred_t *cred) + cred_t *cred, + caller_context_t *ct, + int *direntflags, + pathname_t *realpnp) { int error = 0; vnode_t *newvp = NULL; @@ -354,7 +383,7 @@ auto_lookup( return (0); } - if (error = VOP_ACCESS(dvp, VEXEC, 0, cred)) + if (error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) return (error); if (nm[0] == '.' && nm[1] == 0) { @@ -387,7 +416,8 @@ auto_lookup( vp = dvp->v_vfsp->vfs_vnodecovered; VN_HOLD(vp); vfs_unlock(dvp->v_vfsp); - error = VOP_LOOKUP(vp, nm, vpp, pnp, flags, rdir, cred); + error = VOP_LOOKUP(vp, nm, vpp, pnp, flags, rdir, cred, + ct, direntflags, realpnp); VN_RELE(vp); return (error); } else { @@ -434,7 +464,7 @@ auto_lookup( vn_vfsunlock(dvp); if (!error) { error = VOP_LOOKUP(newvp, nm, vpp, pnp, - flags, rdir, cred); + flags, rdir, cred, ct, direntflags, realpnp); VN_RELE(newvp); } return (error); @@ -640,7 +670,9 @@ auto_create( int mode, vnode_t **vpp, cred_t *cred, - int flag) + int flag, + caller_context_t *ct, + vsecattr_t *vsecp) { vnode_t *newvp; int error; @@ -658,7 +690,7 @@ auto_create( error = EROFS; else error = VOP_CREATE(newvp, nm, va, excl, - mode, vpp, cred, flag); + mode, vpp, cred, flag, ct, vsecp); VN_RELE(newvp); } else error = ENOSYS; @@ -669,7 +701,12 @@ auto_create( } static int -auto_remove(vnode_t *dvp, char *nm, cred_t *cred) +auto_remove( + vnode_t *dvp, + char *nm, + cred_t *cred, + caller_context_t *ct, + int flags) { vnode_t *newvp; int error; @@ -686,7 +723,7 @@ auto_remove(vnode_t *dvp, char *nm, cred_t *cred) if (vn_is_readonly(newvp)) error = EROFS; else - error = VOP_REMOVE(newvp, nm, cred); + error = VOP_REMOVE(newvp, nm, cred, ct, flags); VN_RELE(newvp); } else error = ENOSYS; @@ -697,7 +734,13 @@ auto_remove(vnode_t *dvp, char *nm, cred_t *cred) } static int -auto_link(vnode_t *tdvp, vnode_t *svp, char *nm, cred_t *cred) +auto_link( + vnode_t *tdvp, + vnode_t *svp, + char *nm, + cred_t *cred, + caller_context_t *ct, + int flags) { vnode_t *newvp; int error; @@ -731,7 +774,7 @@ auto_link(vnode_t *tdvp, vnode_t *svp, char *nm, cred_t *cred) goto done; } - error = VOP_LINK(newvp, svp, nm, cred); + error = VOP_LINK(newvp, svp, nm, cred, ct, flags); VN_RELE(newvp); done: @@ -740,7 +783,14 @@ auto_link(vnode_t *tdvp, vnode_t *svp, char *nm, cred_t *cred) } static int -auto_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) +auto_rename( + vnode_t *odvp, + char *onm, + vnode_t *ndvp, + char *nnm, + cred_t *cr, + caller_context_t *ct, + int flags) { vnode_t *o_newvp, *n_newvp; int error; @@ -801,7 +851,7 @@ auto_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) goto done; } - error = VOP_RENAME(o_newvp, onm, n_newvp, nnm, cr); + error = VOP_RENAME(o_newvp, onm, n_newvp, nnm, cr, ct, flags); VN_RELE(o_newvp); if (n_newvp != ndvp) VN_RELE(n_newvp); @@ -812,7 +862,15 @@ auto_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) } static int -auto_mkdir(vnode_t *dvp, char *nm, vattr_t *va, vnode_t **vpp, cred_t *cred) +auto_mkdir( + vnode_t *dvp, + char *nm, + vattr_t *va, + vnode_t **vpp, + cred_t *cred, + caller_context_t *ct, + int flags, + vsecattr_t *vsecp) { vnode_t *newvp; int error; @@ -829,7 +887,8 @@ auto_mkdir(vnode_t *dvp, char *nm, vattr_t *va, vnode_t **vpp, cred_t *cred) if (vn_is_readonly(newvp)) error = EROFS; else - error = VOP_MKDIR(newvp, nm, va, vpp, cred); + error = VOP_MKDIR(newvp, nm, va, vpp, cred, ct, + flags, vsecp); VN_RELE(newvp); } else error = ENOSYS; @@ -840,7 +899,13 @@ auto_mkdir(vnode_t *dvp, char *nm, vattr_t *va, vnode_t **vpp, cred_t *cred) } static int -auto_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cred) +auto_rmdir( + vnode_t *dvp, + char *nm, + vnode_t *cdir, + cred_t *cred, + caller_context_t *ct, + int flags) { vnode_t *newvp; int error; @@ -857,7 +922,7 @@ auto_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cred) if (vn_is_readonly(newvp)) error = EROFS; else - error = VOP_RMDIR(newvp, nm, cdir, cred); + error = VOP_RMDIR(newvp, nm, cdir, cred, ct, flags); VN_RELE(newvp); } else error = ENOSYS; @@ -874,8 +939,15 @@ static int autofs_nobrowse = 0; #endif #define nextdp(dp) ((struct dirent64 *)((char *)(dp) + (dp)->d_reclen)) +/* ARGSUSED */ static int -auto_readdir(vnode_t *vp, uio_t *uiop, cred_t *cred, int *eofp) +auto_readdir( + vnode_t *vp, + uio_t *uiop, + cred_t *cred, + int *eofp, + caller_context_t *ct, + int flags) { struct autofs_rddirargs rda; autofs_rddirres rd; @@ -1161,7 +1233,9 @@ auto_symlink( char *lnknm, /* new entry */ vattr_t *tva, char *tnm, /* existing entry */ - cred_t *cred) + cred_t *cred, + caller_context_t *ct, + int flags) { vnode_t *newvp; int error; @@ -1179,7 +1253,8 @@ auto_symlink( if (vn_is_readonly(newvp)) error = EROFS; else - error = VOP_SYMLINK(newvp, lnknm, tva, tnm, cred); + error = VOP_SYMLINK(newvp, lnknm, tva, tnm, cred, + ct, flags); VN_RELE(newvp); } else error = ENOSYS; @@ -1191,7 +1266,7 @@ auto_symlink( /* ARGSUSED */ static int -auto_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr) +auto_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr, caller_context_t *ct) { fnnode_t *fnp = vntofn(vp); int error; @@ -1217,14 +1292,14 @@ auto_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr) /* ARGSUSED */ static int -auto_fsync(vnode_t *cp, int syncflag, cred_t *cred) +auto_fsync(vnode_t *cp, int syncflag, cred_t *cred, caller_context_t *ct) { return (0); } /* ARGSUSED */ static void -auto_inactive(vnode_t *vp, cred_t *cred) +auto_inactive(vnode_t *vp, cred_t *cred, caller_context_t *ct) { fnnode_t *fnp = vntofn(vp); fnnode_t *dfnp = fnp->fn_parent; @@ -1287,7 +1362,11 @@ auto_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ct) /* ARGSUSED */ static int -auto_seek(struct vnode *vp, offset_t ooff, offset_t *noffp) +auto_seek( + struct vnode *vp, + offset_t ooff, + offset_t *noffp, + caller_context_t *ct) { /* * Return 0 unconditionally, since we expect diff --git a/usr/src/uts/common/fs/cachefs/cachefs_cnode.c b/usr/src/uts/common/fs/cachefs/cachefs_cnode.c index 2036c59f207e..f57b732eb0dd 100644 --- a/usr/src/uts/common/fs/cachefs/cachefs_cnode.c +++ b/usr/src/uts/common/fs/cachefs/cachefs_cnode.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -133,7 +132,7 @@ cachefs_cnode_idle(struct vnode *vp, cred_t *cr) unlname, unlcred, vp); } - /* reaquire cnode lock */ + /* reacquire cnode lock */ mutex_enter(&cp->c_statelock); /* if a timeout occurred */ @@ -1340,7 +1339,7 @@ cachefs_cnode_move(cnode_t *cp) make_ascii_name(&cp->c_id, oname); make_ascii_name(&cid, nname); error = VOP_RENAME(ofgp->fg_dirvp, oname, fgp->fg_dirvp, - nname, kcred); + nname, kcred, NULL, 0); if (error) { ffnuke = 1; #ifdef CFSDEBUG @@ -1470,7 +1469,8 @@ cachefs_cnode_sync(cnode_t *cp) cp->c_backvp) { mutex_enter(&cp->c_statelock); if (cp->c_backvp) { - error = VOP_FSYNC(cp->c_backvp, FSYNC, kcred); + error = VOP_FSYNC(cp->c_backvp, FSYNC, kcred, + NULL); if (CFS_TIMEOUT(fscp, error)) { mutex_exit(&cp->c_statelock); cachefs_cd_release(fscp); @@ -1554,7 +1554,7 @@ cachefs_cnode_lostfound(cnode_t *cp, char *rname) else namep = "lostfile"; error = VOP_LOOKUP(cachep->c_lostfoundvp, namep, &nvp, - NULL, 0, NULL, kcred); + NULL, 0, NULL, kcred, NULL, NULL, NULL); if (error == 0) VN_RELE(nvp); if (error != ENOENT) { @@ -1569,7 +1569,7 @@ cachefs_cnode_lostfound(cnode_t *cp, char *rname) else namep = namebuf; error = VOP_LOOKUP(cachep->c_lostfoundvp, namep, &nvp, - NULL, 0, NULL, kcred); + NULL, 0, NULL, kcred, NULL, NULL, NULL); if (error == 0) VN_RELE(nvp); if (error == ENOENT) @@ -1587,7 +1587,7 @@ cachefs_cnode_lostfound(cnode_t *cp, char *rname) /* rename the file into the lost+found directory */ error = VOP_RENAME(fgp->fg_dirvp, oname, cachep->c_lostfoundvp, - namep, kcred); + namep, kcred, NULL, 0); if (error) { mutex_exit(&cachep->c_contentslock); goto out; @@ -1675,7 +1675,7 @@ cachefs_cnode_traverse(fscache_t *fscp, void (*routinep)(cnode_t *)) */ (routinep)(cp); - /* reaquire the cnode list lock */ + /* reacquire the cnode list lock */ mutex_enter(&fgp->fg_cnodelock); } @@ -1687,7 +1687,7 @@ cachefs_cnode_traverse(fscache_t *fscp, void (*routinep)(cnode_t *)) VN_RELE(CTOV(ocp)); } - /* reaquire the fscache lock */ + /* reacquire the fscache lock */ mutex_enter(&fscp->fs_fslock); } @@ -1730,7 +1730,7 @@ cnode_enable_caching(struct cnode *cp) iovp = cp->c_backvp; if (iovp) { (void) VOP_PUTPAGE(iovp, (offset_t)0, - (uint_t)0, B_INVAL, kcred); + (uint_t)0, B_INVAL, kcred, NULL); } mutex_enter(&cp->c_statelock); if (cp->c_backvp) { diff --git a/usr/src/uts/common/fs/cachefs/cachefs_cod.c b/usr/src/uts/common/fs/cachefs/cachefs_cod.c index fac80de2842e..be60ec4f4019 100644 --- a/usr/src/uts/common/fs/cachefs/cachefs_cod.c +++ b/usr/src/uts/common/fs/cachefs/cachefs_cod.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -93,7 +92,7 @@ c_cod_init_cached_object(fscache_t *fscp, cnode_t *cp, vattr_t *vap, /* get the attributes */ cp->c_attr.va_mask = AT_ALL; - error = VOP_GETATTR(cp->c_backvp, &cp->c_attr, 0, cr); + error = VOP_GETATTR(cp->c_backvp, &cp->c_attr, 0, cr, NULL); if (error) return (error); } else { @@ -170,7 +169,7 @@ c_cod_check_cached_object(struct fscache *fscp, struct cnode *cp, /* get the file attributes from the back fs */ attrs.va_mask = AT_ALL; - error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr); + error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr, NULL); backhit = 1; if (error) goto out; @@ -210,7 +209,7 @@ c_cod_check_cached_object(struct fscache *fscp, struct cnode *cp, } if ((CTOV(cp))->v_type == VREG) { attrs.va_mask = AT_ALL; - error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr); + error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr, NULL); if (error) goto out; } @@ -295,7 +294,7 @@ c_cod_modify_cached_object(struct fscache *fscp, struct cnode *cp, cred_t *cr) } attrs.va_mask = AT_ALL; ASSERT(cp->c_backvp != NULL); - error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr); + error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr, NULL); if (error) { mdp->md_vattr.va_mtime.tv_sec = 0; goto out; diff --git a/usr/src/uts/common/fs/cachefs/cachefs_dir.c b/usr/src/uts/common/fs/cachefs/cachefs_dir.c index 8e586946a6cf..d77b4ab36ae2 100644 --- a/usr/src/uts/common/fs/cachefs/cachefs_dir.c +++ b/usr/src/uts/common/fs/cachefs/cachefs_dir.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" @@ -108,7 +107,7 @@ cachefs_dir_look(cnode_t *dcp, char *nm, fid_t *cookiep, uint_t *flagp, dvp = dcp->c_frontvp; va.va_mask = AT_SIZE; /* XXX should save dir size */ - error = VOP_GETATTR(dvp, &va, 0, kcred); + error = VOP_GETATTR(dvp, &va, 0, kcred, NULL); if (error) { cachefs_inval_object(dcp); error = ENOTDIR; @@ -201,7 +200,7 @@ cachefs_dir_new(cnode_t *dcp, cnode_t *cp) #ifdef CFSDEBUG va.va_mask = AT_SIZE; - error = VOP_GETATTR(cp->c_frontvp, &va, 0, kcred); + error = VOP_GETATTR(cp->c_frontvp, &va, 0, kcred, NULL); if (error) goto out; ASSERT(va.va_size == 0); @@ -307,7 +306,7 @@ cachefs_dir_enter(cnode_t *dcp, char *nm, fid_t *cookiep, cfs_cid_t *cidp, * Get the current EOF for the directory(data file) */ va.va_mask = AT_SIZE; - error = VOP_GETATTR(dvp, &va, 0, kcred); + error = VOP_GETATTR(dvp, &va, 0, kcred, NULL); if (error) { cachefs_inval_object(dcp); error = ENOTDIR; @@ -465,7 +464,7 @@ cachefs_dir_rmentry(cnode_t *dcp, char *nm) ASSERT((dcp->c_flags & CN_NOCACHE) == 0); ASSERT(dvp != NULL); va.va_mask = AT_SIZE; - error = VOP_GETATTR(dvp, &va, 0, kcred); + error = VOP_GETATTR(dvp, &va, 0, kcred, NULL); if (error) { cachefs_inval_object(dcp); error = ENOTDIR; @@ -624,7 +623,7 @@ cachefs_dir_getentrys(struct cnode *dcp, u_offset_t beg_off, gdp = (struct dirent64 *)buf; *cntp = bufsize; va.va_mask = AT_SIZE; - error = VOP_GETATTR(dvp, &va, 0, kcred); + error = VOP_GETATTR(dvp, &va, 0, kcred, NULL); if (error) { *cntp = 0; *last_offp = 0; @@ -950,7 +949,7 @@ cachefs_dir_fill_common(cnode_t *dcp, cred_t *cr, iov.iov_base = buf; iov.iov_len = MAXBSIZE; (void) VOP_RWLOCK(backvp, V_WRITELOCK_FALSE, NULL); - error = VOP_READDIR(backvp, &uio, cr, &eof); + error = VOP_READDIR(backvp, &uio, cr, &eof, NULL, 0); VOP_RWUNLOCK(backvp, V_WRITELOCK_FALSE, NULL); if (error) goto out; @@ -1017,7 +1016,7 @@ cachefs_dir_empty(cnode_t *dcp) return (ENOTDIR); va.va_mask = AT_SIZE; - error = VOP_GETATTR(dvp, &va, 0, kcred); + error = VOP_GETATTR(dvp, &va, 0, kcred, NULL); if (error) return (ENOTDIR); @@ -1258,7 +1257,7 @@ cachefs_dir_complete(fscache_t *fscp, vnode_t *backvp, vnode_t *frontvp, */ va.va_mask = AT_SIZE; - error = VOP_GETATTR(frontvp, &va, 0, cr); + error = VOP_GETATTR(frontvp, &va, 0, cr, NULL); if (error) goto out; @@ -1296,7 +1295,7 @@ cachefs_dir_complete(fscache_t *fscp, vnode_t *backvp, vnode_t *frontvp, error = VOP_LOOKUP(backvp, dep->d_name, &entry_vp, (struct pathname *)NULL, 0, - (vnode_t *)NULL, cr); + (vnode_t *)NULL, cr, NULL, NULL, NULL); if (error) { /* lookup on .. in / on coc gets ENOENT */ if (error == ENOENT) { diff --git a/usr/src/uts/common/fs/cachefs/cachefs_dlog.c b/usr/src/uts/common/fs/cachefs/cachefs_dlog.c index e3ad1bc9dec2..5781d7a4d18e 100644 --- a/usr/src/uts/common/fs/cachefs/cachefs_dlog.c +++ b/usr/src/uts/common/fs/cachefs/cachefs_dlog.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -203,7 +202,7 @@ cachefs_dlog_setup(fscache_t *fscp, int createfile) /* see if the log file exists */ error = VOP_LOOKUP(fscp->fs_fscdirvp, CACHEFS_DLOG_FILE, - &fscp->fs_dlogfile, NULL, 0, NULL, kcred); + &fscp->fs_dlogfile, NULL, 0, NULL, kcred, NULL, NULL, NULL); if (error && (createfile == 0)) goto out; @@ -217,7 +216,7 @@ cachefs_dlog_setup(fscache_t *fscp, int createfile) vattr.va_type = VREG; vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID; error = VOP_CREATE(fscp->fs_fscdirvp, CACHEFS_DLOG_FILE, - &vattr, 0, 0666, &fscp->fs_dlogfile, kcred, 0); + &vattr, 0, 0666, &fscp->fs_dlogfile, kcred, 0, NULL, NULL); if (error) { #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DLOG) @@ -246,7 +245,7 @@ cachefs_dlog_setup(fscache_t *fscp, int createfile) vattr.va_type = VREG; vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID; error = VOP_CREATE(fscp->fs_fscdirvp, CACHEFS_DMAP_FILE, - &vattr, 0, 0666, &fscp->fs_dmapfile, kcred, 0); + &vattr, 0, 0666, &fscp->fs_dmapfile, kcred, 0, NULL, NULL); if (error) { #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DLOG) @@ -272,7 +271,7 @@ cachefs_dlog_setup(fscache_t *fscp, int createfile) /* find the end of the log file */ vattr.va_mask = AT_ALL; - error = VOP_GETATTR(fscp->fs_dlogfile, &vattr, 0, kcred); + error = VOP_GETATTR(fscp->fs_dlogfile, &vattr, 0, kcred, NULL); if (error) { #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DLOG) @@ -328,7 +327,7 @@ cachefs_dlog_setup(fscache_t *fscp, int createfile) error = VOP_LOOKUP(fscp->fs_fscdirvp, CACHEFS_DMAP_FILE, - &fscp->fs_dmapfile, NULL, 0, NULL, kcred); + &fscp->fs_dmapfile, NULL, 0, NULL, kcred, NULL, NULL, NULL); if (error) { #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DLOG) @@ -339,7 +338,7 @@ cachefs_dlog_setup(fscache_t *fscp, int createfile) } vattr.va_mask = AT_ALL; - error = VOP_GETATTR(fscp->fs_dmapfile, &vattr, 0, kcred); + error = VOP_GETATTR(fscp->fs_dmapfile, &vattr, 0, kcred, NULL); if (error) { #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_DLOG) @@ -359,13 +358,13 @@ cachefs_dlog_setup(fscache_t *fscp, int createfile) VN_RELE(fscp->fs_dlogfile); fscp->fs_dlogfile = NULL; (void) VOP_REMOVE(fscp->fs_fscdirvp, - CACHEFS_DLOG_FILE, kcred); + CACHEFS_DLOG_FILE, kcred, NULL, 0); } if (fscp->fs_dmapfile) { VN_RELE(fscp->fs_dmapfile); fscp->fs_dmapfile = NULL; (void) VOP_REMOVE(fscp->fs_fscdirvp, - CACHEFS_DMAP_FILE, kcred); + CACHEFS_DMAP_FILE, kcred, NULL, 0); } } if (lookupdone) { @@ -528,7 +527,7 @@ cachefs_dlog_output(fscache_t *fscp, cfs_dlog_entry_t *entp, uint_t *seqp) } /* - * Commmits a previously written dlog message. + * Commits a previously written dlog message. */ int cachefs_dlog_commit(fscache_t *fscp, off_t offset, int error) diff --git a/usr/src/uts/common/fs/cachefs/cachefs_filegrp.c b/usr/src/uts/common/fs/cachefs/cachefs_filegrp.c index 2da9b2bfda59..9e430fa0fbfc 100644 --- a/usr/src/uts/common/fs/cachefs/cachefs_filegrp.c +++ b/usr/src/uts/common/fs/cachefs/cachefs_filegrp.c @@ -224,12 +224,12 @@ filegrp_create(struct fscache *fscp, cfs_cid_t *cidp) fgp->fg_offsets = NULL; fgp->fg_alloclist = NULL; - fgp->fg_headersize = (u_int)sizeof (struct attrcache_header) + - (fgsize * (u_int)sizeof (struct attrcache_index)) + + fgp->fg_headersize = (uint_t)sizeof (struct attrcache_header) + + (fgsize * (uint_t)sizeof (struct attrcache_index)) + ((fgsize + 7) >> 3); fgp->fg_filesize = fgp->fg_headersize + - (fgsize * (u_int)sizeof (struct cfs_cachefs_metadata)); + (fgsize * (uint_t)sizeof (struct cfs_cachefs_metadata)); flags = fscp->fs_flags; if (flags & CFS_FS_READ) { @@ -324,7 +324,8 @@ filegrp_destroy(filegrp_t *fgp) /* remove the attrcache file */ make_ascii_name(&fgp->fg_id, name); fname = name; - error = VOP_REMOVE(fscp->fs_fsattrdir, fname, kcred); + error = VOP_REMOVE(fscp->fs_fsattrdir, fname, kcred, + NULL, 0); if (error) { cmn_err(CE_WARN, "cachefs: error in cache, run fsck"); @@ -452,7 +453,7 @@ filegrp_hold(filegrp_t *fgp) * Returns: * Preconditions: * precond(fgp is a valid filegrp object) - * precond(number of refrences to filegrp is > 0) + * precond(number of references to filegrp is > 0) */ void @@ -498,7 +499,7 @@ filegrp_rele(filegrp_t *fgp) * Returns 0 for success or a non-zero errno. * Preconditions: * precond(fgp is a valid filegrp object) - * precond(number of refrences to filegrp is > 0) + * precond(number of references to filegrp is > 0) * precond(filegrp is writable) */ @@ -586,7 +587,7 @@ filegrp_ffhold(filegrp_t *fgp) * Preconditions: * precond(fgp is a valid filegrp object) * precond(filegrp is writable) - * precond(number of refrences to filegrp is > 0) + * precond(number of references to filegrp is > 0) * precond(number of front file references is > 0) */ @@ -620,7 +621,7 @@ filegrp_ffrele(filegrp_t *fgp) make_ascii_name(&fgp->fg_id, name); fname = name; error = VOP_RMDIR(fscp->fs_fscdirvp, fname, - fscp->fs_fscdirvp, kcred); + fscp->fs_fscdirvp, kcred, NULL, 0); if (error == 0) { cachefs_freefile(fscp->fs_cache); cachefs_freeblocks(fscp->fs_cache, 1, @@ -687,7 +688,7 @@ filegrp_sync(filegrp_t *fgp) kcred, NULL); if (error == 0) - error = VOP_FSYNC(fgp->fg_attrvp, FSYNC, kcred); + error = VOP_FSYNC(fgp->fg_attrvp, FSYNC, kcred, NULL); if (error == 0) fgp->fg_flags &= ~CFS_FG_UPDATED; @@ -746,7 +747,7 @@ filegrp_read_metadata(filegrp_t *fgp, cfs_cid_t *cidp, /* see if metadata was ever written */ - index = (int) (cidp->cid_fileno - fgp->fg_id.cid_fileno); + index = (int)(cidp->cid_fileno - fgp->fg_id.cid_fileno); if (fgp->fg_offsets[index].ach_written == 0) { mutex_exit(&fgp->fg_mutex); return (ENOENT); @@ -794,7 +795,7 @@ filegrp_create_metadata(filegrp_t *fgp, struct cachefs_metadata *md, cachefscache_t *cachep = fscp->fs_cache; int slot; int bitno; - u_char mask; + uchar_t mask; int last; int xx; int index; @@ -816,13 +817,13 @@ filegrp_create_metadata(filegrp_t *fgp, struct cachefs_metadata *md, return (0); } - index = (int) (cidp->cid_fileno - fgp->fg_id.cid_fileno); + index = (int)(cidp->cid_fileno - fgp->fg_id.cid_fileno); ASSERT(index < fgp->fg_fscp->fs_info.fi_fgsize); last = (((fgp->fg_fscp->fs_info.fi_fgsize + 7) & ~(7)) / 8); for (xx = 0; xx < last; xx++) { - if (fgp->fg_alloclist[xx] != (u_char)0xff) { + if (fgp->fg_alloclist[xx] != (uchar_t)0xff) { for (mask = 1, bitno = 0; bitno < 8; bitno++) { if ((mask & fgp->fg_alloclist[xx]) == 0) { slot = (xx * 8) + bitno; @@ -948,7 +949,7 @@ filegrp_write_metadata(filegrp_t *fgp, cfs_cid_t *cidp, } /* mark metadata as having been written */ - index = (int) (cidp->cid_fileno - fgp->fg_id.cid_fileno); + index = (int)(cidp->cid_fileno - fgp->fg_id.cid_fileno); fgp->fg_offsets[index].ach_written = 1; /* update number of blocks used by the attrcache file */ @@ -983,7 +984,7 @@ filegrp_destroy_metadata(filegrp_t *fgp, cfs_cid_t *cidp) { int i; int bitno; - u_char mask = 1; + uchar_t mask = 1; int slot; @@ -1064,7 +1065,7 @@ filegrp_list_find(struct fscache *fscp, cfs_cid_t *cidp) fileno = fxx * fgsize; /* hash into array of file groups */ - findex = (int) (fxx & (CFS_FS_FGP_BUCKET_SIZE - 1)); + findex = (int)(fxx & (CFS_FS_FGP_BUCKET_SIZE - 1)); /* search set of file groups for this hash bucket */ for (fgp = fscp->fs_filegrp[findex]; @@ -1106,7 +1107,7 @@ filegrp_list_add(struct fscache *fscp, filegrp_t *fgp) ASSERT(fgp->fg_next == NULL); /* hash into array of file groups */ - findex = (int) ((fgp->fg_id.cid_fileno / fgsize) & + findex = (int)((fgp->fg_id.cid_fileno / fgsize) & (CFS_FS_FGP_BUCKET_SIZE - 1)); fgp->fg_next = fscp->fs_filegrp[findex]; @@ -1144,7 +1145,7 @@ filegrp_list_remove(struct fscache *fscp, filegrp_t *fgp) ASSERT(MUTEX_HELD(&fscp->fs_fslock)); /* hash into array of file groups */ - findex = (int) ((fgp->fg_id.cid_fileno / fgsize) & + findex = (int)((fgp->fg_id.cid_fileno / fgsize) & (CFS_FS_FGP_BUCKET_SIZE - 1)); fp = fscp->fs_filegrp[findex]; pfgp = &fscp->fs_filegrp[findex]; @@ -1360,7 +1361,7 @@ filegrpdir_find(filegrp_t *fgp) make_ascii_name(&fgp->fg_id, name); fname = name; error = VOP_LOOKUP(fscp->fs_fscdirvp, fname, &dirvp, NULL, - 0, NULL, kcred); + 0, NULL, kcred, NULL, NULL, NULL); if (error == 0) { fgp->fg_dirvp = dirvp; fgp->fg_flags &= ~CFS_FG_ALLOC_FILE; @@ -1421,7 +1422,7 @@ filegrpattr_find(struct filegrp *fgp) make_ascii_name(&fgp->fg_id, name); fname = name; error = VOP_LOOKUP(fscp->fs_fsattrdir, fname, - &attrvp, NULL, 0, NULL, kcred); + &attrvp, NULL, 0, NULL, kcred, NULL, NULL, NULL); if (error) { return (error); } @@ -1442,7 +1443,7 @@ filegrpattr_find(struct filegrp *fgp) fgp->fg_attrvp = attrvp; fgp->fg_header = ahp; fgp->fg_offsets = (struct attrcache_index *)(ahp + 1); - fgp->fg_alloclist = ((u_char *)fgp->fg_offsets) + + fgp->fg_alloclist = ((uchar_t *)fgp->fg_offsets) + (fscp->fs_info.fi_fgsize * sizeof (struct attrcache_index)); fgp->fg_flags &= ~CFS_FG_ALLOC_ATTR; @@ -1565,7 +1566,8 @@ filegrpdir_create(filegrp_t *fgp) attrp->va_gid = 0; attrp->va_type = VDIR; attrp->va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID; - error = VOP_MKDIR(fscp->fs_fscdirvp, fname, attrp, &dirvp, kcred); + error = VOP_MKDIR(fscp->fs_fscdirvp, fname, attrp, &dirvp, kcred, NULL, + 0, NULL); if (error) { fgp->fg_flags |= CFS_FG_NOCACHE; cachefs_freefile(fscp->fs_cache); @@ -1637,7 +1639,7 @@ filegrpattr_create(struct filegrp *fgp) attrp->va_type = VREG; attrp->va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID; error = VOP_CREATE(fscp->fs_fsattrdir, fname, attrp, EXCL, 0666, - &attrvp, kcred, 0); + &attrvp, kcred, 0, NULL, NULL); if (error) { cachefs_freefile(fscp->fs_cache); goto out; @@ -1664,7 +1666,7 @@ filegrpattr_create(struct filegrp *fgp) (nblks * MAXBSIZE) - fgp->fg_headersize); if (error) goto out; - error = VOP_FSYNC(attrvp, FSYNC, kcred); + error = VOP_FSYNC(attrvp, FSYNC, kcred, NULL); if (error) goto out; @@ -1694,7 +1696,8 @@ filegrpattr_create(struct filegrp *fgp) fgp->fg_flags |= CFS_FG_NOCACHE; if (attrvp) { VN_RELE(attrvp); - (void) VOP_REMOVE(fscp->fs_fsattrdir, fname, kcred); + (void) VOP_REMOVE(fscp->fs_fsattrdir, fname, kcred, + NULL, 0); cachefs_freefile(fscp->fs_cache); } if (nblks) @@ -1709,7 +1712,7 @@ filegrpattr_create(struct filegrp *fgp) fgp->fg_attrvp = attrvp; fgp->fg_header = ahp; fgp->fg_offsets = (struct attrcache_index *)(ahp + 1); - fgp->fg_alloclist = ((u_char *)fgp->fg_offsets) + + fgp->fg_alloclist = ((uchar_t *)fgp->fg_offsets) + (fscp->fs_info.fi_fgsize * sizeof (struct attrcache_index)); ahp->ach_count = 0; @@ -1751,7 +1754,7 @@ filegrp_cid_to_slot(filegrp_t *fgp, cfs_cid_t *cidp) int slot; int index; - index = (int) (cidp->cid_fileno - fgp->fg_id.cid_fileno); + index = (int)(cidp->cid_fileno - fgp->fg_id.cid_fileno); if (index > fgp->fg_fscp->fs_info.fi_fgsize) { cmn_err(CE_WARN, "cachefs: attrcache error, run fsck"); diff --git a/usr/src/uts/common/fs/cachefs/cachefs_fscache.c b/usr/src/uts/common/fs/cachefs/cachefs_fscache.c index 28c3f1c36d11..6dcb5430f271 100644 --- a/usr/src/uts/common/fs/cachefs/cachefs_fscache.c +++ b/usr/src/uts/common/fs/cachefs/cachefs_fscache.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" @@ -257,7 +256,7 @@ fscache_setup(fscache_t *fscp, ino64_t fsid, char *namep, return (error); } } else if (optp) { - /* compare the options to make sure they are compatable */ + /* compare the options to make sure they are compatible */ error = fscache_compare_options(fscp, optp); if (error) { cmn_err(CE_WARN, @@ -507,8 +506,8 @@ fscache_mounted(fscache_t *fscp, struct vfs *cfsvfsp, struct vfs *backvfsp) /* * Compares fscache state with new mount options - * to make sure compatable. - * Returns ESRCH if not compatable or 0 for success. + * to make sure compatible. + * Returns ESRCH if not compatible or 0 for success. */ int fscache_compare_options(fscache_t *fscp, struct cachefsoptions *optp) @@ -811,7 +810,8 @@ fscdir_create(cachefscache_t *cachep, char *namep, fscache_t *fscp) attrp->va_gid = 0; attrp->va_type = VDIR; attrp->va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID; - error = VOP_MKDIR(cachep->c_dirvp, namep, attrp, &fscdirvp, kcred); + error = VOP_MKDIR(cachep->c_dirvp, namep, attrp, &fscdirvp, kcred, + NULL, 0, NULL); if (error) { cmn_err(CE_WARN, "Can't create fs cache directory"); goto out; @@ -821,7 +821,7 @@ fscdir_create(cachefscache_t *cachep, char *namep, fscache_t *fscp) * Created the directory. Get the fileno. That'll be the cachefs_fsid. */ attrp->va_mask = AT_NODEID; - error = VOP_GETATTR(fscdirvp, attrp, 0, kcred); + error = VOP_GETATTR(fscdirvp, attrp, 0, kcred, NULL); if (error) { goto out; } @@ -832,7 +832,7 @@ fscdir_create(cachefscache_t *cachep, char *namep, fscache_t *fscp) attrp->va_type = VREG; attrp->va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID; error = VOP_CREATE(fscdirvp, CACHEFS_FSINFO, attrp, EXCL, - 0600, &infovp, kcred, 0); + 0600, &infovp, kcred, 0, NULL, NULL); if (error) { cmn_err(CE_WARN, "Can't create fs option file"); goto out; @@ -858,7 +858,7 @@ fscdir_create(cachefscache_t *cachep, char *namep, fscache_t *fscp) cid.cid_fileno = fsid; make_ascii_name(&cid, name); error = VOP_RENAME(cachep->c_dirvp, namep, cachep->c_dirvp, - name, kcred); + name, kcred, NULL, 0); if (error) { cmn_err(CE_WARN, "Can't rename cache directory"); goto out; @@ -866,7 +866,8 @@ fscdir_create(cachefscache_t *cachep, char *namep, fscache_t *fscp) attrp->va_mask = AT_MODE | AT_TYPE; attrp->va_mode = 0777; attrp->va_type = VLNK; - error = VOP_SYMLINK(cachep->c_dirvp, namep, attrp, name, kcred); + error = VOP_SYMLINK(cachep->c_dirvp, namep, attrp, name, kcred, NULL, + 0); if (error) { cmn_err(CE_WARN, "Can't create cache directory symlink"); goto out; @@ -880,7 +881,8 @@ fscdir_create(cachefscache_t *cachep, char *namep, fscache_t *fscp) attrp->va_gid = 0; attrp->va_type = VDIR; attrp->va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID; - error = VOP_MKDIR(fscdirvp, ATTRCACHE_NAME, attrp, &attrvp, kcred); + error = VOP_MKDIR(fscdirvp, ATTRCACHE_NAME, attrp, &attrvp, kcred, NULL, + 0, NULL); if (error) { cmn_err(CE_WARN, "Can't create attrcache dir for fscache"); goto out; @@ -939,7 +941,7 @@ fscdir_find(cachefscache_t *cachep, ino64_t fsid, fscache_t *fscp) /* try to find the directory */ error = VOP_LOOKUP(cachep->c_dirvp, dirname, &fscdirvp, NULL, - 0, NULL, kcred); + 0, NULL, kcred, NULL, NULL, NULL); if (error) goto out; @@ -953,7 +955,7 @@ fscdir_find(cachefscache_t *cachep, ino64_t fsid, fscache_t *fscp) /* try to find the info file */ error = VOP_LOOKUP(fscdirvp, CACHEFS_FSINFO, &infovp, - NULL, 0, NULL, kcred); + NULL, 0, NULL, kcred, NULL, NULL, NULL); if (error) { cmn_err(CE_WARN, "cachefs: fscdir_find_b: cache corruption" " run fsck, %s", dirname); @@ -975,7 +977,7 @@ fscdir_find(cachefscache_t *cachep, ino64_t fsid, fscache_t *fscp) /* try to find the attrcache directory */ error = VOP_LOOKUP(fscdirvp, ATTRCACHE_NAME, - &attrvp, NULL, 0, NULL, kcred); + &attrvp, NULL, 0, NULL, kcred, NULL, NULL, NULL); if (error) { cmn_err(CE_WARN, "cachefs: fscdir_find_d: cache corruption" " run fsck, %s", dirname); @@ -1076,7 +1078,7 @@ fscache_name_to_fsid(cachefscache_t *cachep, char *namep, ino64_t *fsidp) /* get the vnode of the name */ error = VOP_LOOKUP(cachep->c_dirvp, namep, &linkvp, NULL, 0, NULL, - kcred); + kcred, NULL, NULL, NULL); if (error) goto out; @@ -1096,7 +1098,7 @@ fscache_name_to_fsid(cachefscache_t *cachep, char *namep, ino64_t *fsidp) uio.uio_loffset = 0; uio.uio_fmode = 0; uio.uio_extflg = UIO_COPY_CACHED; - error = VOP_READLINK(linkvp, &uio, kcred); + error = VOP_READLINK(linkvp, &uio, kcred, NULL); if (error) { cmn_err(CE_WARN, "cachefs: Can't read filesystem cache link"); goto out; diff --git a/usr/src/uts/common/fs/cachefs/cachefs_ioctl.c b/usr/src/uts/common/fs/cachefs/cachefs_ioctl.c index 62c15fc35ec6..2a6b60a7a0ed 100644 --- a/usr/src/uts/common/fs/cachefs/cachefs_ioctl.c +++ b/usr/src/uts/common/fs/cachefs/cachefs_ioctl.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" @@ -1123,7 +1122,7 @@ cachefs_io_getinfo(vnode_t *vp, void *dinp, void *doutp) /* Get the length of the directory */ va.va_mask = AT_SIZE; - error = VOP_GETATTR(dcp->c_frontvp, &va, 0, kcred); + error = VOP_GETATTR(dcp->c_frontvp, &va, 0, kcred, NULL); if (error) { error = 0; goto out; @@ -1248,7 +1247,7 @@ cachefs_io_getattrfid(vnode_t *vp, void *dinp, void *doutp) cr = conj_cred(&gafid->cg_cred); CACHEFS_TMPPTR_SET(attrp, &va, tmpvap, vattr_t); tmpvap->va_mask = AT_ALL; - error = VOP_GETATTR(backvp, tmpvap, 0, cr); + error = VOP_GETATTR(backvp, tmpvap, 0, cr, NULL); CACHEFS_VATTR_COPYOUT(tmpvap, attrp, error); crfree(cr); @@ -1298,7 +1297,7 @@ cachefs_io_getattrname(vnode_t *vp, void *dinp, void *doutp) /* lookup the file name */ cr = conj_cred(&gap->cg_cred); error = VOP_LOOKUP(pbackvp, gap->cg_name, &cbackvp, - (struct pathname *)NULL, 0, (vnode_t *)NULL, cr); + (struct pathname *)NULL, 0, (vnode_t *)NULL, cr, NULL, NULL, NULL); if (error) { crfree(cr); VN_RELE(pbackvp); @@ -1307,12 +1306,12 @@ cachefs_io_getattrname(vnode_t *vp, void *dinp, void *doutp) CACHEFS_TMPPTR_SET(&retp->cg_attr, &va, tmpvap, vattr_t); tmpvap->va_mask = AT_ALL; - error = VOP_GETATTR(cbackvp, tmpvap, 0, cr); + error = VOP_GETATTR(cbackvp, tmpvap, 0, cr, NULL); CACHEFS_VATTR_COPYOUT(tmpvap, &retp->cg_attr, error); if (!error) { CACHEFS_TMPPTR_SET(&retp->cg_fid, &tmpfid, tmpfidp, fid_t); tmpfidp->fid_len = MAXFIDSZ; - error = VOP_FID(cbackvp, tmpfidp); + error = VOP_FID(cbackvp, tmpfidp, NULL); CACHEFS_FID_COPYOUT(tmpfidp, &retp->cg_fid); } @@ -1415,7 +1414,7 @@ cachefs_io_pushback(vnode_t *vp, void *dinp, void *doutp) } /* do open so NFS gets correct creds on writes */ - error = VOP_OPEN(&backvp, FWRITE, cr); + error = VOP_OPEN(&backvp, FWRITE, cr, NULL); if (error) { mutex_exit(&cp->c_statelock); goto out; @@ -1450,9 +1449,9 @@ cachefs_io_pushback(vnode_t *vp, void *dinp, void *doutp) off += amt; } - error = VOP_FSYNC(backvp, FSYNC, cr); + error = VOP_FSYNC(backvp, FSYNC, cr, NULL); if (error == 0) - error = VOP_CLOSE(backvp, FWRITE, 1, (offset_t)0, cr); + error = VOP_CLOSE(backvp, FWRITE, 1, (offset_t)0, cr, NULL); if (error) { mutex_exit(&cp->c_statelock); goto out; @@ -1469,7 +1468,7 @@ cachefs_io_pushback(vnode_t *vp, void *dinp, void *doutp) * new ctime and mtimes. */ va.va_mask = AT_ALL; - error = VOP_GETATTR(backvp, &va, 0, cr); + error = VOP_GETATTR(backvp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, &retp->pb_ctime, error); @@ -1527,21 +1526,21 @@ cachefs_io_create(vnode_t *vp, void *dinp, void *doutp) CACHEFS_TMPPTR_SET(&crp->cr_va, &va, tmpvap, vattr_t); CACHEFS_VATTR_COPYIN(&crp->cr_va, tmpvap); error = VOP_CREATE(dvp, crp->cr_name, tmpvap, - crp->cr_exclusive, crp->cr_mode, &cvp, cr, 0); + crp->cr_exclusive, crp->cr_mode, &cvp, cr, 0, NULL, NULL); if (error) goto out; /* get the fid of the file */ CACHEFS_TMPPTR_SET(&retp->cr_newfid, &tmpfid, tmpfidp, fid_t); tmpfidp->fid_len = MAXFIDSZ; - error = VOP_FID(cvp, tmpfidp); + error = VOP_FID(cvp, tmpfidp, NULL); if (error) goto out; CACHEFS_FID_COPYOUT(tmpfidp, &retp->cr_newfid); /* get attributes for the file */ va.va_mask = AT_ALL; - error = VOP_GETATTR(cvp, &va, 0, cr); + error = VOP_GETATTR(cvp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, &retp->cr_ctime, error); @@ -1614,10 +1613,11 @@ cachefs_io_remove(vnode_t *vp, void *dinp, void *doutp) /* if the caller wants the ctime after the remove */ if (ctimep) { - error = VOP_LOOKUP(dvp, rmp->rm_name, &cvp, NULL, 0, NULL, cr); + error = VOP_LOOKUP(dvp, rmp->rm_name, &cvp, NULL, 0, NULL, cr, + NULL, NULL, NULL); if (error == 0) { child_fid.fid_len = MAXFIDSZ; - error = VOP_FID(cvp, &child_fid); + error = VOP_FID(cvp, &child_fid, NULL); VN_RELE(cvp); } if (error) @@ -1625,7 +1625,7 @@ cachefs_io_remove(vnode_t *vp, void *dinp, void *doutp) } /* do the remove */ - error = VOP_REMOVE(dvp, rmp->rm_name, cr); + error = VOP_REMOVE(dvp, rmp->rm_name, cr, NULL, 0); if (error) goto out; @@ -1634,7 +1634,7 @@ cachefs_io_remove(vnode_t *vp, void *dinp, void *doutp) error = VFS_VGET(fscp->fs_backvfsp, &cvp, &child_fid); if (error == 0) { va.va_mask = AT_ALL; - error = VOP_GETATTR(cvp, &va, 0, cr); + error = VOP_GETATTR(cvp, &va, 0, cr, NULL); if (error == 0) { CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, ctimep, error); @@ -1697,13 +1697,13 @@ cachefs_io_link(vnode_t *vp, void *dinp, void *doutp) cr = conj_cred(&linkp->ln_cred); /* do the link */ - error = VOP_LINK(dvp, lvp, linkp->ln_name, cr); + error = VOP_LINK(dvp, lvp, linkp->ln_name, cr, NULL, 0); if (error) goto out; /* get the ctime */ va.va_mask = AT_ALL; - error = VOP_GETATTR(lvp, &va, 0, cr); + error = VOP_GETATTR(lvp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, ctimep, error); @@ -1771,14 +1771,14 @@ cachefs_io_rename(vnode_t *vp, void *dinp, void *doutp) /* if the caller wants the ctime of the target after deletion */ if (rnp->rn_del_getctime) { error = VOP_LOOKUP(ndvp, rnp->rn_newname, &cvp, NULL, 0, - NULL, cr); + NULL, cr, NULL, NULL, NULL); if (error) { cvp = NULL; /* paranoia */ goto out; } child_fid.fid_len = MAXFIDSZ; - error = VOP_FID(cvp, &child_fid); + error = VOP_FID(cvp, &child_fid, NULL); if (error) goto out; VN_RELE(cvp); @@ -1786,17 +1786,19 @@ cachefs_io_rename(vnode_t *vp, void *dinp, void *doutp) } /* do the rename */ - error = VOP_RENAME(odvp, rnp->rn_oldname, ndvp, rnp->rn_newname, cr); + error = VOP_RENAME(odvp, rnp->rn_oldname, ndvp, rnp->rn_newname, cr, + NULL, 0); if (error) goto out; /* get the new ctime on the renamed file */ - error = VOP_LOOKUP(ndvp, rnp->rn_newname, &cvp, NULL, 0, NULL, cr); + error = VOP_LOOKUP(ndvp, rnp->rn_newname, &cvp, NULL, 0, NULL, cr, + NULL, NULL, NULL); if (error) goto out; va.va_mask = AT_ALL; - error = VOP_GETATTR(cvp, &va, 0, cr); + error = VOP_GETATTR(cvp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, &retp->rn_ctime, error); @@ -1815,7 +1817,7 @@ cachefs_io_rename(vnode_t *vp, void *dinp, void *doutp) goto out; } va.va_mask = AT_ALL; - error = VOP_GETATTR(cvp, &va, 0, cr); + error = VOP_GETATTR(cvp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, &retp->rn_del_ctime, @@ -1878,14 +1880,14 @@ cachefs_io_mkdir(vnode_t *vp, void *dinp, void *doutp) /* make the directory */ CACHEFS_TMPPTR_SET(&mdirp->md_vattr, &va, tmpvap, vattr_t); CACHEFS_VATTR_COPYIN(&mdirp->md_vattr, tmpvap); - error = VOP_MKDIR(dvp, mdirp->md_name, tmpvap, &cvp, cr); + error = VOP_MKDIR(dvp, mdirp->md_name, tmpvap, &cvp, cr, NULL, 0, NULL); if (error) { if (error != EEXIST) goto out; /* if the directory already exists, then use it */ error = VOP_LOOKUP(dvp, mdirp->md_name, &cvp, - NULL, 0, NULL, cr); + NULL, 0, NULL, cr, NULL, NULL, NULL); if (error) { cvp = NULL; goto out; @@ -1899,14 +1901,14 @@ cachefs_io_mkdir(vnode_t *vp, void *dinp, void *doutp) /* get the fid of the directory */ CACHEFS_TMPPTR_SET(fidp, &tmpfid, tmpfidp, fid_t); tmpfidp->fid_len = MAXFIDSZ; - error = VOP_FID(cvp, tmpfidp); + error = VOP_FID(cvp, tmpfidp, NULL); if (error) goto out; CACHEFS_FID_COPYOUT(tmpfidp, fidp); /* get attributes of the directory */ va.va_mask = AT_ALL; - error = VOP_GETATTR(cvp, &va, 0, cr); + error = VOP_GETATTR(cvp, &va, 0, cr, NULL); if (error) goto out; @@ -1969,7 +1971,7 @@ cachefs_io_rmdir(vnode_t *vp, void *dinp, void *doutp) } cr = conj_cred(&rdp->rd_cred); - error = VOP_RMDIR(dvp, rdp->rd_name, dvp, cr); + error = VOP_RMDIR(dvp, rdp->rd_name, dvp, cr, NULL, 0); crfree(cr); VN_RELE(dvp); @@ -2019,18 +2021,19 @@ cachefs_io_symlink(vnode_t *vp, void *dinp, void *doutp) CACHEFS_TMPPTR_SET(&symp->sy_vattr, &va, tmpvap, vattr_t); CACHEFS_VATTR_COPYIN(&symp->sy_vattr, tmpvap); error = VOP_SYMLINK(dvp, symp->sy_name, tmpvap, - symp->sy_link, cr); + symp->sy_link, cr, NULL, 0); if (error) goto out; /* get the vnode for the symlink */ - error = VOP_LOOKUP(dvp, symp->sy_name, &svp, NULL, 0, NULL, cr); + error = VOP_LOOKUP(dvp, symp->sy_name, &svp, NULL, 0, NULL, cr, + NULL, NULL, NULL); if (error) goto out; /* get the attributes of the symlink */ va.va_mask = AT_ALL; - error = VOP_GETATTR(svp, &va, 0, cr); + error = VOP_GETATTR(svp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, &retp->sy_ctime, error); @@ -2041,7 +2044,7 @@ cachefs_io_symlink(vnode_t *vp, void *dinp, void *doutp) /* get the fid */ CACHEFS_TMPPTR_SET(&retp->sy_newfid, &tmpfid, tmpfidp, fid_t); tmpfidp->fid_len = MAXFIDSZ; - error = VOP_FID(svp, tmpfidp); + error = VOP_FID(svp, tmpfidp, NULL); if (error) goto out; CACHEFS_FID_COPYOUT(tmpfidp, &retp->sy_newfid); @@ -2120,7 +2123,7 @@ cachefs_io_setattr(vnode_t *vp, void *dinp, void *doutp) /* get the new ctime and mtime */ va.va_mask = AT_ALL; - error = VOP_GETATTR(cvp, &va, 0, cr); + error = VOP_GETATTR(cvp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, &retp->sa_ctime, error); @@ -2181,14 +2184,14 @@ cachefs_io_setsecattr(vnode_t *vp, void *dinp, void *doutp) /* set the ACL */ (void) VOP_RWLOCK(tvp, V_WRITELOCK_TRUE, NULL); - error = VOP_SETSECATTR(tvp, &vsec, 0, cr); + error = VOP_SETSECATTR(tvp, &vsec, 0, cr, NULL); VOP_RWUNLOCK(tvp, V_WRITELOCK_TRUE, NULL); if (error != 0) goto out; /* get the new ctime and mtime */ va.va_mask = AT_ALL; - error = VOP_GETATTR(tvp, &va, 0, cr); + error = VOP_GETATTR(tvp, &va, 0, cr, NULL); if (error) goto out; CACHEFS_TS_TO_CFS_TS_COPY(&va.va_ctime, &retp->sc_ctime, error); @@ -2223,7 +2226,7 @@ drop_backvp(cnode_t *cp) if (cp->c_backvp) { /* dump any pages, may be a dirty one */ (void) VOP_PUTPAGE(cp->c_backvp, (offset_t)0, 0, - B_INVAL | B_TRUNC, kcred); + B_INVAL | B_TRUNC, kcred, NULL); } mutex_exit(&cp->c_statelock); } diff --git a/usr/src/uts/common/fs/cachefs/cachefs_log.c b/usr/src/uts/common/fs/cachefs/cachefs_log.c index 8d0103cd3921..76a6453ba66a 100644 --- a/usr/src/uts/common/fs/cachefs/cachefs_log.c +++ b/usr/src/uts/common/fs/cachefs/cachefs_log.c @@ -57,7 +57,7 @@ /* * cfs_time_t is an int in both LP64 and ILP32. To avoid compiler warnings - * define its xdr here explicitely + * define its xdr here explicitly */ #define xdr_cfs_time_t(xdrs, p) xdr_int((xdrs), (int *)(p)) @@ -198,7 +198,8 @@ cachefs_log_kstat_snapshot(kstat_t *ksp, void *buf, int rw) bzero(lc, sizeof (*lc)); lc->lc_magic = CACHEFS_LOG_MAGIC; lc->lc_cachep = (uint64_t)(uintptr_t)cachep; - (void) VOP_REMOVE(cachep->c_dirvp, LOG_STATUS_NAME, kcred); + (void) VOP_REMOVE(cachep->c_dirvp, LOG_STATUS_NAME, kcred, NULL, + 0); return (0); } @@ -246,9 +247,9 @@ cachefs_log_save_lc(cachefscache_t *cachep) attr.va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID; if (((error = VOP_LOOKUP(cachep->c_dirvp, LOG_STATUS_NAME, &savevp, - NULL, 0, NULL, kcred)) != 0) && + NULL, 0, NULL, kcred, NULL, NULL, NULL)) != 0) && ((error = VOP_CREATE(cachep->c_dirvp, LOG_STATUS_NAME, &attr, EXCL, - 0600, &savevp, kcred, 0)) != 0)) + 0600, &savevp, kcred, 0, NULL, NULL)) != 0)) return (error); ASSERT(savevp != NULL); if (savevp == NULL) @@ -322,7 +323,7 @@ cachefs_log_destroy_cookie(cachefs_log_cookie_t *cl) * * opens the logfile, and stores the path string if its successful. * - * returns an errno if one occured. + * returns an errno if one occurred. * */ @@ -406,7 +407,7 @@ cachefs_log_logfile_open(cachefscache_t *cachep, char *path) } /* - * called when an error occured during logging. send the error to + * called when an error occurred during logging. send the error to * syslog, invalidate the logfile, and stop logging. */ @@ -437,7 +438,8 @@ cachefs_log_error(cachefscache_t *cachep, int error, int getlock) lc->lc_magic = CACHEFS_LOG_MAGIC; lc->lc_cachep = (uint64_t)(uintptr_t)cachep; if (writable) - (void) VOP_REMOVE(cachep->c_dirvp, LOG_STATUS_NAME, kcred); + (void) VOP_REMOVE(cachep->c_dirvp, LOG_STATUS_NAME, kcred, NULL, + 0); if (getlock) mutex_exit(&cachep->c_log_mutex); @@ -454,7 +456,7 @@ cachefs_log_write_header(struct vnode *vp, cachefscache_t *cachep, int error) XDR xdrm; attr.va_mask = AT_SIZE; - if ((error = VOP_GETATTR(vp, &attr, 0, kcred)) != 0) + if ((error = VOP_GETATTR(vp, &attr, 0, kcred, NULL)) != 0) goto out; if (attr.va_size != 0) { error = vn_rdwr(UIO_READ, vp, buffy, @@ -664,7 +666,7 @@ cachefs_log_process_queue(cachefscache_t *cachep, int getlock) cachefs_kmem_free(buffy, CACHEFS_LOG_ENCODE_SIZE); /* - * if an error occured, we need to free the buffers ourselves. + * if an error occurred, we need to free the buffers ourselves. * cachefs_destory_cookie() can't do it. */ diff --git a/usr/src/uts/common/fs/cachefs/cachefs_noopc.c b/usr/src/uts/common/fs/cachefs/cachefs_noopc.c index 5b9c3d70620b..245025bf0ad3 100644 --- a/usr/src/uts/common/fs/cachefs/cachefs_noopc.c +++ b/usr/src/uts/common/fs/cachefs/cachefs_noopc.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" @@ -81,7 +80,7 @@ c_nop_init_cached_object(fscache_t *fscp, cnode_t *cp, vattr_t *vap, /* get the attributes */ cp->c_attr.va_mask = AT_ALL; ASSERT(cp->c_backvp != NULL); - error = VOP_GETATTR(cp->c_backvp, &cp->c_attr, 0, cr); + error = VOP_GETATTR(cp->c_backvp, &cp->c_attr, 0, cr, NULL); if (error) return (error); } else { @@ -127,7 +126,7 @@ c_nop_check_cached_object(struct fscache *fscp, struct cnode *cp, /* get the file attributes from the back fs */ attrs.va_mask = AT_ALL; - error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr); + error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr, NULL); backhit = 1; if (error) goto out; @@ -181,7 +180,7 @@ c_nop_modify_cached_object(struct fscache *fscp, struct cnode *cp, cred_t *cr) return; } attrs.va_mask = AT_ALL; - error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr); + error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr, NULL); if (error) return; nlink = cp->c_attr.va_nlink; diff --git a/usr/src/uts/common/fs/cachefs/cachefs_resource.c b/usr/src/uts/common/fs/cachefs/cachefs_resource.c index c250f7cef16c..796d35223790 100644 --- a/usr/src/uts/common/fs/cachefs/cachefs_resource.c +++ b/usr/src/uts/common/fs/cachefs/cachefs_resource.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" @@ -76,7 +75,7 @@ static void cachefs_packed_pending(cachefscache_t *cachep); (&(cachep->c_rlinfo.rl_items[CACHEFS_RL_INDEX(type)])) /* - * This function moves an RL entry from whereever it currently is to + * This function moves an RL entry from wherever it currently is to * the back of the requested list. */ void @@ -989,7 +988,7 @@ cachefs_cachep_worker_thread(cachefscache_t *cachep) fl.l_sysid = 0; fl.l_pid = 0; error = VOP_FRLOCK(cachep->c_lockvp, F_SETLK, &fl, FWRITE, - (offset_t)0, NULL, kcred); + (offset_t)0, NULL, kcred, NULL); if (error) { cmn_err(CE_WARN, "cachefs: Can't lock Cache Lock File(r); Error %d\n", @@ -1058,7 +1057,7 @@ cachefs_cachep_worker_thread(cachefscache_t *cachep) fl.l_sysid = 0; fl.l_pid = 0; error = VOP_FRLOCK(cachep->c_lockvp, F_SETLK, &fl, - FWRITE, (offset_t)0, NULL, kcred); + FWRITE, (offset_t)0, NULL, kcred, NULL); if (error) { cmn_err(CE_WARN, "cachefs: Can't unlock lock file\n"); } @@ -1407,7 +1406,7 @@ cachefs_gc_front_atime(cachefscache_t *cachep) make_ascii_name(&dircid, namebuf); if (VOP_LOOKUP(fscp->fs_fscdirvp, namebuf, &dirvp, (struct pathname *)NULL, 0, - (vnode_t *)NULL, kcred) == 0) { + (vnode_t *)NULL, kcred, NULL, NULL, NULL) == 0) { make_ascii_name(&cid, namebuf); reledir++; } else { @@ -1417,7 +1416,7 @@ cachefs_gc_front_atime(cachefscache_t *cachep) } if (dirvp && VOP_LOOKUP(dirvp, namebuf, &filevp, (struct pathname *)NULL, 0, - (vnode_t *)NULL, kcred) == 0) { + (vnode_t *)NULL, kcred, NULL, NULL, NULL) == 0) { gotfile = 1; } if (reledir) @@ -1426,7 +1425,7 @@ cachefs_gc_front_atime(cachefscache_t *cachep) if (gotfile) { va.va_mask = AT_ATIME; - if (VOP_GETATTR(filevp, &va, 0, kcred) == 0) + if (VOP_GETATTR(filevp, &va, 0, kcred, NULL) == 0) rc = va.va_atime.tv_sec; VN_RELE(filevp); } diff --git a/usr/src/uts/common/fs/cachefs/cachefs_strict.c b/usr/src/uts/common/fs/cachefs/cachefs_strict.c index 75fd305760b2..9a4011b459fb 100644 --- a/usr/src/uts/common/fs/cachefs/cachefs_strict.c +++ b/usr/src/uts/common/fs/cachefs/cachefs_strict.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -93,7 +92,7 @@ c_strict_init_cached_object(fscache_t *fscp, cnode_t *cp, vattr_t *vap, /* get the attributes */ cp->c_attr.va_mask = AT_ALL; - error = VOP_GETATTR(cp->c_backvp, &cp->c_attr, 0, cr); + error = VOP_GETATTR(cp->c_backvp, &cp->c_attr, 0, cr, NULL); if (error) return (error); } else { @@ -146,7 +145,7 @@ c_strict_check_cached_object(struct fscache *fscp, struct cnode *cp, if (CFS_ISFS_BACKFS_NFSV4(fscp)) { backhit = 1; attrs.va_mask = AT_ALL; - error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr); + error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr, NULL); if (error) goto out; cp->c_attr = attrs; @@ -194,7 +193,7 @@ c_strict_check_cached_object(struct fscache *fscp, struct cnode *cp, /* get the file attributes from the back fs */ attrs.va_mask = AT_ALL; - error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr); + error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr, NULL); backhit = 1; if (error) goto out; @@ -239,7 +238,7 @@ c_strict_check_cached_object(struct fscache *fscp, struct cnode *cp, } if ((CTOV(cp))->v_type == VREG) { attrs.va_mask = AT_ALL; - error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr); + error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr, NULL); if (error) goto out; } @@ -335,7 +334,7 @@ c_strict_modify_cached_object(struct fscache *fscp, struct cnode *cp, attrs.va_mask = AT_ALL; ASSERT(cp->c_backvp != NULL); - error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr); + error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr, NULL); if (error) { mdp->md_vattr.va_mtime.tv_sec = 0; goto out; diff --git a/usr/src/uts/common/fs/cachefs/cachefs_subr.c b/usr/src/uts/common/fs/cachefs/cachefs_subr.c index b3aac88a35c1..1f82e638e601 100644 --- a/usr/src/uts/common/fs/cachefs/cachefs_subr.c +++ b/usr/src/uts/common/fs/cachefs/cachefs_subr.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -277,7 +276,7 @@ cachefs_cache_activate_ro(cachefscache_t *cachep, vnode_t *cdvp) /* get the mode bits of the cache directory */ attrp->va_mask = AT_ALL; - error = VOP_GETATTR(cdvp, attrp, 0, kcred); + error = VOP_GETATTR(cdvp, attrp, 0, kcred, NULL); if (error) goto out; @@ -290,7 +289,7 @@ cachefs_cache_activate_ro(cachefscache_t *cachep, vnode_t *cdvp) /* Get the lock file */ error = VOP_LOOKUP(cdvp, CACHEFS_LOCK_FILE, &lockvp, NULL, 0, NULL, - kcred); + kcred, NULL, NULL, NULL); if (error) { cmn_err(CE_WARN, "cachefs: activate_a: cache corruption" " run fsck.\n"); @@ -299,7 +298,7 @@ cachefs_cache_activate_ro(cachefscache_t *cachep, vnode_t *cdvp) /* Get the label file */ error = VOP_LOOKUP(cdvp, CACHELABEL_NAME, &labelvp, NULL, 0, NULL, - kcred); + kcred, NULL, NULL, NULL); if (error) { cmn_err(CE_WARN, "cachefs: activate_b: cache corruption" " run fsck.\n"); @@ -324,7 +323,8 @@ cachefs_cache_activate_ro(cachefscache_t *cachep, vnode_t *cdvp) } /* Open the resource file */ - error = VOP_LOOKUP(cdvp, RESOURCE_NAME, &rifvp, NULL, 0, NULL, kcred); + error = VOP_LOOKUP(cdvp, RESOURCE_NAME, &rifvp, NULL, 0, NULL, kcred, + NULL, NULL, NULL); if (error) { cmn_err(CE_WARN, "cachefs: activate_d: cache corruption" " run fsck.\n"); @@ -360,7 +360,7 @@ cachefs_cache_activate_ro(cachefscache_t *cachep, vnode_t *cdvp) /* Open the lost+found directory */ error = VOP_LOOKUP(cdvp, CACHEFS_LOSTFOUND_NAME, &lostfoundvp, - NULL, 0, NULL, kcred); + NULL, 0, NULL, kcred, NULL, NULL, NULL); if (error) { cmn_err(CE_WARN, "cachefs: activate_g: cache corruption" " run fsck.\n"); @@ -389,7 +389,7 @@ cachefs_cache_activate_ro(cachefscache_t *cachep, vnode_t *cdvp) /* if the LOG_STATUS_NAME file exists, read it in and set up logging */ error = VOP_LOOKUP(cachep->c_dirvp, LOG_STATUS_NAME, &statevp, - NULL, 0, NULL, kcred); + NULL, 0, NULL, kcred, NULL, NULL, NULL); if (error == 0) { int vnrw_error; @@ -738,7 +738,7 @@ cachefs_cache_rssync(struct cachefscache *cachep) if (error) { cmn_err(CE_WARN, "cachefs: Can't Write Cache RL Info\n"); } - error = VOP_FSYNC(cachep->c_resfilevp, FSYNC, kcred); + error = VOP_FSYNC(cachep->c_resfilevp, FSYNC, kcred, NULL); return (error); } @@ -974,11 +974,11 @@ cachefs_createfrontfile(cnode_t *cp, struct filegrp *fgp) if (cp->c_flags & CN_ASYNC_POP_WORKING) { /* lookup the already created front file */ error = VOP_LOOKUP(fgp->fg_dirvp, name, &cp->c_frontvp, - NULL, 0, NULL, kcred); + NULL, 0, NULL, kcred, NULL, NULL, NULL); } else { /* create the front file */ error = VOP_CREATE(fgp->fg_dirvp, name, attrp, EXCL, mode, - &cp->c_frontvp, kcred, 0); + &cp->c_frontvp, kcred, 0, NULL, NULL); } if (error) { #ifdef CFSDEBUG @@ -992,7 +992,7 @@ cachefs_createfrontfile(cnode_t *cp, struct filegrp *fgp) /* get a copy of the fid of the front file */ cp->c_metadata.md_fid.fid_len = MAXFIDSZ; - error = VOP_FID(cp->c_frontvp, &cp->c_metadata.md_fid); + error = VOP_FID(cp->c_frontvp, &cp->c_metadata.md_fid, NULL); if (error) { /* * If we get back ENOSPC then the fid we passed in was too @@ -1013,7 +1013,7 @@ cachefs_createfrontfile(cnode_t *cp, struct filegrp *fgp) if (error) { if (cp->c_frontvp) { VN_RELE(cp->c_frontvp); - (void) VOP_REMOVE(fgp->fg_dirvp, name, kcred); + (void) VOP_REMOVE(fgp->fg_dirvp, name, kcred, NULL, 0); cp->c_frontvp = NULL; } if (ffrele) @@ -1064,7 +1064,7 @@ cachefs_removefrontfile(cachefs_metadata_t *mdp, cfs_cid_t *cidp, return; } make_ascii_name(cidp, name); - error = VOP_REMOVE(fgp->fg_dirvp, name, kcred); + error = VOP_REMOVE(fgp->fg_dirvp, name, kcred, NULL, 0); if (error == ENOENT) enoent = 1; if ((error) && (error != ENOENT)) { @@ -1074,7 +1074,7 @@ cachefs_removefrontfile(cachefs_metadata_t *mdp, cfs_cid_t *cidp, if (mdp->md_flags & MD_ACLDIR) { (void) strcat(name, ".d"); error = VOP_RMDIR(fgp->fg_dirvp, name, fgp->fg_dirvp, - kcred); + kcred, NULL, 0); if ((error) && (error != ENOENT)) { cmn_err(CE_WARN, "frontfs rmdir error %s %d" "; run fsck\n", name, error); @@ -1210,7 +1210,7 @@ cachefs_getfrontfile(cnode_t *cp) /* get modify time of the front file */ va.va_mask = AT_MTIME; - error = VOP_GETATTR(cp->c_frontvp, &va, 0, kcred); + error = VOP_GETATTR(cp->c_frontvp, &va, 0, kcred, NULL); if (error) { cmn_err(CE_WARN, "cachefs: gff2: front file" " system error %d", error); @@ -1721,7 +1721,7 @@ cachefs_populate(cnode_t *cp, u_offset_t off, size_t popsize, vnode_t *frontvp, /* * due to compiler error we shifted cnode to the last argument slot. - * occured during large files project - XXX. + * occurred during large files project - XXX. */ void cachefs_cluster_allocmap(u_offset_t off, u_offset_t *popoffp, @@ -1911,7 +1911,7 @@ cachefs_readlink_back(cnode_t *cp, cred_t *cr, caddr_t *bufp, int *buflenp) CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_readlink (nfsv4): cnode %p, backvp %p\n", cp, cp->c_backvp)); - error = VOP_READLINK(cp->c_backvp, &uio, cr); + error = VOP_READLINK(cp->c_backvp, &uio, cr, NULL); if (error) { cachefs_kmem_free(buf, MAXPATHLEN); } else { @@ -1980,7 +1980,7 @@ cachefs_getbackvp(struct fscache *fscp, struct cnode *cp) flag |= FWRITE; } } - error = VOP_OPEN(&cp->c_backvp, flag, cp->c_cred); + error = VOP_OPEN(&cp->c_backvp, flag, cp->c_cred, NULL); if (error) { VN_RELE(cp->c_backvp); cp->c_backvp = NULL; @@ -2028,7 +2028,7 @@ cachefs_getcookie( * variable length fids we will need to change this. */ cookiep->fid_len = MAXFIDSZ; - error = VOP_FID(vp, cookiep); + error = VOP_FID(vp, cookiep, NULL); } else { bzero(cookiep, sizeof (*cookiep)); } @@ -2037,7 +2037,7 @@ cachefs_getcookie( if (attrp) { ASSERT(attrp != NULL); attrp->va_mask = AT_ALL; - error = VOP_GETATTR(vp, attrp, 0, cr); + error = VOP_GETATTR(vp, attrp, 0, cr, NULL); } } else { if (error == ENOSPC) { @@ -2254,7 +2254,7 @@ cachefs_async_putpage(struct cachefs_putpage_req *prp, cred_t *cr) ASSERT(CFS_ISFS_BACKFS_NFSV4(C_TO_FSCACHE(cp)) == 0); (void) VOP_PUTPAGE(prp->cp_vp, prp->cp_off, prp->cp_len, - prp->cp_flags, cr); + prp->cp_flags, cr, NULL); mutex_enter(&cp->c_iomutex); if (--cp->c_nio == 0) @@ -2378,7 +2378,7 @@ cachefs_async_populate(struct cachefs_populate_req *pop, cred_t *cr) make_ascii_name(&cid, name); (void) VOP_CREATE(fgp->fg_dirvp, name, attrp, - EXCL, 0666, &frontvp, kcred, 0); + EXCL, 0666, &frontvp, kcred, 0, NULL, NULL); cachefs_kmem_free(attrp, sizeof (struct vattr)); @@ -2433,7 +2433,7 @@ cachefs_async_populate(struct cachefs_populate_req *pop, cred_t *cr) if (error != 0) goto out; - error = VOP_FSYNC(frontvp, FSYNC, cr); + error = VOP_FSYNC(frontvp, FSYNC, cr, NULL); if (error != 0) { #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_ASYNCPOP) @@ -2453,7 +2453,7 @@ cachefs_async_populate(struct cachefs_populate_req *pop, cred_t *cr) } va.va_mask = AT_MTIME; - error = VOP_GETATTR(cp->c_frontvp, &va, 0, cr); + error = VOP_GETATTR(cp->c_frontvp, &va, 0, cr, NULL); if (error) { #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_ASYNCPOP) diff --git a/usr/src/uts/common/fs/cachefs/cachefs_vfsops.c b/usr/src/uts/common/fs/cachefs/cachefs_vfsops.c index fd1ee0b4d8ed..26631b3380d9 100644 --- a/usr/src/uts/common/fs/cachefs/cachefs_vfsops.c +++ b/usr/src/uts/common/fs/cachefs/cachefs_vfsops.c @@ -403,7 +403,7 @@ cachefs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) } fscache_list_add(cachep, fscp); } else { - /* compare the options to make sure they are compatable */ + /* compare the options to make sure they are compatible */ error = fscache_compare_options(fscp, cfs_options); if (error) { cmn_err(CE_WARN, @@ -426,7 +426,7 @@ cachefs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) error = 0; if (fscp->fs_fscdirvp) { error = VOP_LOOKUP(fscp->fs_fscdirvp, CACHEFS_DLOG_FILE, - &tmpdirvp, NULL, 0, NULL, kcred); + &tmpdirvp, NULL, 0, NULL, kcred, NULL, NULL, NULL); /* * If a log file exists and the cache is being mounted without @@ -550,7 +550,7 @@ cachefs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) */ error = VOP_PATHCONF(backrootvp, _PC_FILESIZEBITS, &maxfilesizebits, - kcred); + kcred, NULL); if (error) { cmn_err(CE_WARN, @@ -562,7 +562,8 @@ cachefs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) mutex_exit(&fscp->fs_fslock); /* remove the unmount file if it is there */ - (void) VOP_REMOVE(fscp->fs_fscdirvp, CACHEFS_UNMNT_FILE, kcred); + (void) VOP_REMOVE(fscp->fs_fscdirvp, CACHEFS_UNMNT_FILE, kcred, NULL, + 0); /* wake up the cache worker if ANY packed pending work */ mutex_enter(&cachep->c_contentslock); @@ -957,7 +958,7 @@ cachefs_unmount(vfs_t *vfsp, int flag, cred_t *cr) attr.va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID; if (fscp->fs_fscdirvp != NULL) xx = VOP_CREATE(fscp->fs_fscdirvp, CACHEFS_UNMNT_FILE, &attr, - NONEXCL, 0600, &nmvp, kcred, 0); + NONEXCL, 0600, &nmvp, kcred, 0, NULL, NULL); else xx = ENOENT; /* for unmounting when NOCACHE */ if (xx == 0) { @@ -1207,7 +1208,7 @@ cachefs_remount(struct vfs *vfsp, struct mounta *uap) error = 0; if (cachedirvp) { error = VOP_LOOKUP(cachedirvp, CACHEFS_DLOG_FILE, - &tmpdirvp, NULL, 0, NULL, kcred); + &tmpdirvp, NULL, 0, NULL, kcred, NULL, NULL, NULL); } cfs_options = (struct cachefsoptions *)STRUCT_FADDR(map, cfs_options); cacheid = (char *)STRUCT_FGETP(map, cfs_cacheid); diff --git a/usr/src/uts/common/fs/cachefs/cachefs_vnops.c b/usr/src/uts/common/fs/cachefs/cachefs_vnops.c index 5451c20467a7..795c70dc3dc7 100644 --- a/usr/src/uts/common/fs/cachefs/cachefs_vnops.c +++ b/usr/src/uts/common/fs/cachefs/cachefs_vnops.c @@ -136,60 +136,71 @@ static int cachefs_readback_translate(cnode_t *cp, uio_t *uiop, static int cachefs_setattr_common(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, caller_context_t *ct); -static int cachefs_open(struct vnode **, int, cred_t *); +static int cachefs_open(struct vnode **, int, cred_t *, + caller_context_t *); static int cachefs_close(struct vnode *, int, int, offset_t, - cred_t *); + cred_t *, caller_context_t *); static int cachefs_read(struct vnode *, struct uio *, int, cred_t *, caller_context_t *); static int cachefs_write(struct vnode *, struct uio *, int, cred_t *, caller_context_t *); static int cachefs_ioctl(struct vnode *, int, intptr_t, int, cred_t *, - int *); + int *, caller_context_t *); static int cachefs_getattr(struct vnode *, struct vattr *, int, - cred_t *); + cred_t *, caller_context_t *); static int cachefs_setattr(struct vnode *, struct vattr *, int, cred_t *, caller_context_t *); -static int cachefs_access(struct vnode *, int, int, cred_t *); +static int cachefs_access(struct vnode *, int, int, cred_t *, + caller_context_t *); static int cachefs_lookup(struct vnode *, char *, struct vnode **, - struct pathname *, int, struct vnode *, cred_t *); + struct pathname *, int, struct vnode *, cred_t *, + caller_context_t *, int *, pathname_t *); static int cachefs_create(struct vnode *, char *, struct vattr *, - enum vcexcl, int, struct vnode **, cred_t *, int); + enum vcexcl, int, struct vnode **, cred_t *, int, + caller_context_t *, vsecattr_t *); static int cachefs_create_connected(vnode_t *dvp, char *nm, vattr_t *vap, enum vcexcl exclusive, int mode, vnode_t **vpp, cred_t *cr); static int cachefs_create_disconnected(vnode_t *dvp, char *nm, vattr_t *vap, enum vcexcl exclusive, int mode, vnode_t **vpp, cred_t *cr); -static int cachefs_remove(struct vnode *, char *, cred_t *); +static int cachefs_remove(struct vnode *, char *, cred_t *, + caller_context_t *, int); static int cachefs_link(struct vnode *, struct vnode *, char *, - cred_t *); + cred_t *, caller_context_t *, int); static int cachefs_rename(struct vnode *, char *, struct vnode *, - char *, cred_t *); + char *, cred_t *, caller_context_t *, int); static int cachefs_mkdir(struct vnode *, char *, struct - vattr *, struct vnode **, cred_t *); + vattr *, struct vnode **, cred_t *, caller_context_t *, + int, vsecattr_t *); static int cachefs_rmdir(struct vnode *, char *, struct vnode *, - cred_t *); + cred_t *, caller_context_t *, int); static int cachefs_readdir(struct vnode *, struct uio *, - cred_t *, int *); + cred_t *, int *, caller_context_t *, int); static int cachefs_symlink(struct vnode *, char *, struct vattr *, - char *, cred_t *); -static int cachefs_readlink(struct vnode *, struct uio *, cred_t *); + char *, cred_t *, caller_context_t *, int); +static int cachefs_readlink(struct vnode *, struct uio *, cred_t *, + caller_context_t *); static int cachefs_readlink_connected(vnode_t *vp, uio_t *uiop, cred_t *cr); static int cachefs_readlink_disconnected(vnode_t *vp, uio_t *uiop); -static int cachefs_fsync(struct vnode *, int, cred_t *); -static void cachefs_inactive(struct vnode *, cred_t *); -static int cachefs_fid(struct vnode *, struct fid *); +static int cachefs_fsync(struct vnode *, int, cred_t *, + caller_context_t *); +static void cachefs_inactive(struct vnode *, cred_t *, caller_context_t *); +static int cachefs_fid(struct vnode *, struct fid *, caller_context_t *); static int cachefs_rwlock(struct vnode *, int, caller_context_t *); static void cachefs_rwunlock(struct vnode *, int, caller_context_t *); -static int cachefs_seek(struct vnode *, offset_t, offset_t *); +static int cachefs_seek(struct vnode *, offset_t, offset_t *, + caller_context_t *); static int cachefs_frlock(struct vnode *, int, struct flock64 *, - int, offset_t, struct flk_callback *, cred_t *); + int, offset_t, struct flk_callback *, cred_t *, + caller_context_t *); static int cachefs_space(struct vnode *, int, struct flock64 *, int, offset_t, cred_t *, caller_context_t *); -static int cachefs_realvp(struct vnode *, struct vnode **); +static int cachefs_realvp(struct vnode *, struct vnode **, + caller_context_t *); static int cachefs_getpage(struct vnode *, offset_t, size_t, uint_t *, struct page *[], size_t, struct seg *, caddr_t, - enum seg_rw, cred_t *); + enum seg_rw, cred_t *, caller_context_t *); static int cachefs_getapage(struct vnode *, u_offset_t, size_t, uint_t *, struct page *[], size_t, struct seg *, caddr_t, enum seg_rw, cred_t *); @@ -197,37 +208,42 @@ static int cachefs_getapage_back(struct vnode *, u_offset_t, size_t, uint_t *, struct page *[], size_t, struct seg *, caddr_t, enum seg_rw, cred_t *); static int cachefs_putpage(struct vnode *, offset_t, size_t, int, - cred_t *); + cred_t *, caller_context_t *); static int cachefs_map(struct vnode *, offset_t, struct as *, - caddr_t *, size_t, uchar_t, uchar_t, uint_t, cred_t *); + caddr_t *, size_t, uchar_t, uchar_t, uint_t, cred_t *, + caller_context_t *); static int cachefs_addmap(struct vnode *, offset_t, struct as *, - caddr_t, size_t, uchar_t, uchar_t, uint_t, cred_t *); + caddr_t, size_t, uchar_t, uchar_t, uint_t, cred_t *, + caller_context_t *); static int cachefs_delmap(struct vnode *, offset_t, struct as *, - caddr_t, size_t, uint_t, uint_t, uint_t, cred_t *); + caddr_t, size_t, uint_t, uint_t, uint_t, cred_t *, + caller_context_t *); static int cachefs_setsecattr(vnode_t *vp, vsecattr_t *vsec, - int flag, cred_t *cr); + int flag, cred_t *cr, caller_context_t *); static int cachefs_getsecattr(vnode_t *vp, vsecattr_t *vsec, - int flag, cred_t *cr); + int flag, cred_t *cr, caller_context_t *); static int cachefs_shrlock(vnode_t *, int, struct shrlock *, int, - cred_t *); + cred_t *, caller_context_t *); static int cachefs_getsecattr_connected(vnode_t *vp, vsecattr_t *vsec, int flag, cred_t *cr); static int cachefs_getsecattr_disconnected(vnode_t *vp, vsecattr_t *vsec, int flag, cred_t *cr); -static int cachefs_dump(struct vnode *, caddr_t, int, int); +static int cachefs_dump(struct vnode *, caddr_t, int, int, + caller_context_t *); static int cachefs_pageio(struct vnode *, page_t *, - u_offset_t, size_t, int, cred_t *); + u_offset_t, size_t, int, cred_t *, caller_context_t *); static int cachefs_writepage(struct vnode *vp, caddr_t base, int tcount, struct uio *uiop); -static int cachefs_pathconf(vnode_t *, int, ulong_t *, cred_t *); +static int cachefs_pathconf(vnode_t *, int, ulong_t *, cred_t *, + caller_context_t *); static int cachefs_read_backfs_nfsv4(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, caller_context_t *ct); static int cachefs_write_backfs_nfsv4(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, caller_context_t *ct); static int cachefs_getattr_backfs_nfsv4(vnode_t *vp, vattr_t *vap, - int flags, cred_t *cr); + int flags, cred_t *cr, caller_context_t *ct); static int cachefs_remove_backfs_nfsv4(vnode_t *dvp, char *nm, cred_t *cr, vnode_t *vp); static int cachefs_getpage_backfs_nfsv4(struct vnode *vp, offset_t off, @@ -305,7 +321,7 @@ cachefs_getvnodeops(void) } static int -cachefs_open(vnode_t **vpp, int flag, cred_t *cr) +cachefs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { int error = 0; cnode_t *cp = VTOC(*vpp); @@ -413,7 +429,7 @@ cachefs_open(vnode_t **vpp, int flag, cred_t *cr) CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_open (nfsv4): cnode %p, " "backvp %p\n", cp, cp->c_backvp)); - error = VOP_OPEN(&cp->c_backvp, flag, cr); + error = VOP_OPEN(&cp->c_backvp, flag, cr, ct); if (CFS_TIMEOUT(fscp, error)) { mutex_exit(&cp->c_statelock); cachefs_cd_release(fscp); @@ -469,7 +485,8 @@ cachefs_open(vnode_t **vpp, int flag, cred_t *cr) /* ARGSUSED */ static int -cachefs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) +cachefs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, + caller_context_t *ct) { int error = 0; cnode_t *cp = VTOC(vp); @@ -552,7 +569,7 @@ cachefs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) ("cachefs_close (nfsv4): cnode %p, " "backvp %p\n", cp, cp->c_backvp)); error = VOP_CLOSE(cp->c_backvp, flag, count, - offset, cr); + offset, cr, ct); if (CFS_TIMEOUT(fscp, error)) { mutex_exit(&cp->c_statelock); cachefs_cd_release(fscp); @@ -627,7 +644,7 @@ cachefs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) if (cp->c_backvp && (fscp->fs_cdconnected == CFS_CD_CONNECTED)) { error = VOP_CLOSE(cp->c_backvp, flag, close_cnt, - offset, cr); + offset, cr, ct); if (CFS_TIMEOUT(fscp, error)) { mutex_exit(&cp->c_statelock); cachefs_cd_release(fscp); @@ -1706,7 +1723,8 @@ cachefs_push_front(vnode_t *vp, struct buf *bp, size_t iolen, /*ARGSUSED*/ static int -cachefs_dump(struct vnode *vp, caddr_t foo1, int foo2, int foo3) +cachefs_dump(struct vnode *vp, caddr_t foo1, int foo2, int foo3, + caller_context_t *ct) { return (ENOSYS); /* should we panic if we get here? */ } @@ -1714,7 +1732,7 @@ cachefs_dump(struct vnode *vp, caddr_t foo1, int foo2, int foo3) /*ARGSUSED*/ static int cachefs_ioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, cred_t *cred, - int *rvalp) + int *rvalp, caller_context_t *ct) { int error; struct cnode *cp = VTOC(vp); @@ -1989,7 +2007,8 @@ cachefs_fileno_conflict(fscache_t *fscp, ino64_t old) /*ARGSUSED*/ static int -cachefs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +cachefs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { struct cnode *cp = VTOC(vp); fscache_t *fscp = C_TO_FSCACHE(cp); @@ -2007,7 +2026,7 @@ cachefs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) /* Call backfilesystem getattr if NFSv4 */ if (CFS_ISFS_BACKFS_NFSV4(fscp)) { - error = cachefs_getattr_backfs_nfsv4(vp, vap, flags, cr); + error = cachefs_getattr_backfs_nfsv4(vp, vap, flags, cr, ct); goto out; } @@ -2175,7 +2194,7 @@ cachefs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) */ static int cachefs_getattr_backfs_nfsv4(vnode_t *vp, vattr_t *vap, - int flags, cred_t *cr) + int flags, cred_t *cr, caller_context_t *ct) { cnode_t *cp = VTOC(vp); fscache_t *fscp = C_TO_FSCACHE(cp); @@ -2198,7 +2217,7 @@ cachefs_getattr_backfs_nfsv4(vnode_t *vp, vattr_t *vap, CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_getattr_backfs_nfsv4: cnode %p," " backvp %p\n", cp, backvp)); - error = VOP_GETATTR(backvp, vap, flags, cr); + error = VOP_GETATTR(backvp, vap, flags, cr, ct); /* Update attributes */ cp->c_attr = *vap; @@ -2252,7 +2271,7 @@ cachefs_setattr( held = 0; } - /* aquire access to the file system */ + /* acquire access to the file system */ error = cachefs_cd_access(fscp, connected, 1); if (error) break; @@ -2461,7 +2480,7 @@ cachefs_setattr_connected( /* XXX bob: given what modify_cobject does this seems unnecessary */ cp->c_attr.va_mask = AT_ALL; - error = VOP_GETATTR(cp->c_backvp, &cp->c_attr, 0, cr); + error = VOP_GETATTR(cp->c_backvp, &cp->c_attr, 0, cr, ct); if (error) goto out; @@ -2719,7 +2738,8 @@ cachefs_setattr_disconnected( /* ARGSUSED */ static int -cachefs_access(vnode_t *vp, int mode, int flags, cred_t *cr) +cachefs_access(vnode_t *vp, int mode, int flags, cred_t *cr, + caller_context_t *ct) { cnode_t *cp = VTOC(vp); fscache_t *fscp = C_TO_FSCACHE(cp); @@ -2842,7 +2862,7 @@ cachefs_access_connected(struct vnode *vp, int mode, int flags, cred_t *cr) CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_access (nfsv4): cnode %p, backvp %p\n", cp, cp->c_backvp)); - error = VOP_ACCESS(cp->c_backvp, mode, flags, cr); + error = VOP_ACCESS(cp->c_backvp, mode, flags, cr, NULL); /* * even though we don't `need' the ACL to do access @@ -2873,8 +2893,9 @@ cachefs_access_connected(struct vnode *vp, int mode, int flags, cred_t *cr) * CFS has a fastsymlink scheme. If the size of the link is < C_FSL_SIZE, then * the link is placed in the metadata itself (no front file is allocated). */ +/*ARGSUSED*/ static int -cachefs_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr) +cachefs_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct) { int error = 0; cnode_t *cp = VTOC(vp); @@ -3125,7 +3146,7 @@ cachefs_readlink_disconnected(vnode_t *vp, uio_t *uiop) /*ARGSUSED*/ static int -cachefs_fsync(vnode_t *vp, int syncflag, cred_t *cr) +cachefs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) { cnode_t *cp = VTOC(vp); int error = 0; @@ -3215,7 +3236,8 @@ cachefs_fsync(vnode_t *vp, int syncflag, cred_t *cr) CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_fsync (nfsv4): cnode %p, " "backvp %p\n", cp, cp->c_backvp)); - error = VOP_FSYNC(cp->c_backvp, syncflag, cr); + error = VOP_FSYNC(cp->c_backvp, syncflag, cr, + ct); if (CFS_TIMEOUT(fscp, error)) { mutex_exit(&cp->c_statelock); cachefs_cd_release(fscp); @@ -3304,13 +3326,13 @@ cachefs_sync_metadata(cnode_t *cp) if (cp->c_flags & CN_NEED_FRONT_SYNC) { if (cp->c_frontvp != NULL) { - error = VOP_FSYNC(cp->c_frontvp, FSYNC, kcred); + error = VOP_FSYNC(cp->c_frontvp, FSYNC, kcred, NULL); if (error) { cp->c_metadata.md_timestamp.tv_sec = 0; } else { va.va_mask = AT_MTIME; error = VOP_GETATTR(cp->c_frontvp, &va, 0, - kcred); + kcred, NULL); if (error) goto out; cp->c_metadata.md_timestamp = va.va_mtime; @@ -3376,8 +3398,9 @@ cachefs_sync_metadata(cnode_t *cp) * calls cachefs_inactive. * Because of the dnlc, it is not safe to grab most locks here. */ +/*ARGSUSED*/ static void -cachefs_inactive(struct vnode *vp, cred_t *cr) +cachefs_inactive(struct vnode *vp, cred_t *cr, caller_context_t *ct) { cnode_t *cp; struct cachefs_req *rp; @@ -3425,7 +3448,9 @@ cachefs_inactive(struct vnode *vp, cred_t *cr) /* ARGSUSED */ static int cachefs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, - struct pathname *pnp, int flags, vnode_t *rdir, cred_t *cr) + struct pathname *pnp, int flags, vnode_t *rdir, cred_t *cr, + caller_context_t *ct, int *direntflags, pathname_t *realpnp) + { int error = 0; cnode_t *dcp = VTOC(dvp); @@ -3740,13 +3765,13 @@ cachefs_lookup_back(vnode_t *dvp, char *nm, vnode_t **vpp, ("cachefs_lookup (nfsv4): dcp %p, dbackvp %p, name %s\n", dcp, dcp->c_backvp, nm)); error = VOP_LOOKUP(dcp->c_backvp, nm, &backvp, (struct pathname *)NULL, - 0, (vnode_t *)NULL, cr); + 0, (vnode_t *)NULL, cr, NULL, NULL, NULL); if (error) goto out; if (IS_DEVVP(backvp)) { struct vnode *devvp = backvp; - if (VOP_REALVP(devvp, &backvp) == 0) { + if (VOP_REALVP(devvp, &backvp, NULL) == 0) { VN_HOLD(backvp); VN_RELE(devvp); } @@ -3793,7 +3818,9 @@ cachefs_lookup_back(vnode_t *dvp, char *nm, vnode_t **vpp, /*ARGSUSED7*/ static int cachefs_create(vnode_t *dvp, char *nm, vattr_t *vap, - vcexcl_t exclusive, int mode, vnode_t **vpp, cred_t *cr, int flag) + vcexcl_t exclusive, int mode, vnode_t **vpp, cred_t *cr, int flag, + caller_context_t *ct, vsecattr_t *vsecp) + { cnode_t *dcp = VTOC(dvp); fscache_t *fscp = C_TO_FSCACHE(dcp); @@ -3942,9 +3969,8 @@ cachefs_create_connected(vnode_t *dvp, char *nm, vattr_t *vap, cachefs_access_connected(vp, mode, 0, cr)) == 0) { if ((vap->va_mask & AT_SIZE) && (vp->v_type == VREG)) { vap->va_mask = AT_SIZE; - error = - cachefs_setattr_common(vp, vap, - 0, cr, NULL); + error = cachefs_setattr_common(vp, vap, 0, + cr, NULL); } } if (error) { @@ -3978,11 +4004,11 @@ cachefs_create_connected(vnode_t *dvp, char *nm, vattr_t *vap, ("cachefs_create (nfsv4): dcp %p, dbackvp %p," "name %s\n", dcp, dcp->c_backvp, nm)); error = VOP_CREATE(dcp->c_backvp, nm, vap, exclusive, mode, - &devvp, cr, 0); + &devvp, cr, 0, NULL, NULL); mutex_exit(&dcp->c_statelock); if (error) goto out; - if (VOP_REALVP(devvp, &tvp) == 0) { + if (VOP_REALVP(devvp, &tvp, NULL) == 0) { VN_HOLD(tvp); VN_RELE(devvp); } else { @@ -4096,9 +4122,8 @@ cachefs_create_disconnected(vnode_t *dvp, char *nm, vattr_t *vap, if ((vap->va_mask & AT_SIZE) && (vp->v_type == VREG)) { vap->va_mask = AT_SIZE; - error = - cachefs_setattr_common(vp, vap, - 0, cr, NULL); + error = cachefs_setattr_common(vp, + vap, 0, cr, NULL); } } } @@ -4264,8 +4289,10 @@ cachefs_create_disconnected(vnode_t *dvp, char *nm, vattr_t *vap, return (error); } +/*ARGSUSED*/ static int -cachefs_remove(vnode_t *dvp, char *nm, cred_t *cr) +cachefs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct, + int flags) { cnode_t *dcp = VTOC(dvp); fscache_t *fscp = C_TO_FSCACHE(dcp); @@ -4511,7 +4538,7 @@ cachefs_remove_connected(vnode_t *dvp, char *nm, cred_t *cr, vnode_t *vp) } /* perform the remove on the back fs */ - error = VOP_REMOVE(dcp->c_backvp, nm, cr); + error = VOP_REMOVE(dcp->c_backvp, nm, cr, NULL, 0); if (error) { mutex_exit(&dcp->c_statelock); goto out; @@ -4641,7 +4668,7 @@ cachefs_remove_backfs_nfsv4(vnode_t *dvp, char *nm, cred_t *cr, vnode_t *vp) CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_remove (nfsv4): dcp %p, dbackvp %p, name %s\n", dcp, dbackvp, nm)); - error = VOP_REMOVE(dbackvp, nm, cr); + error = VOP_REMOVE(dbackvp, nm, cr, NULL, 0); if (error) { mutex_exit(&cp->c_statelock); goto out; @@ -4789,8 +4816,10 @@ cachefs_remove_disconnected(vnode_t *dvp, char *nm, cred_t *cr, return (error); } +/*ARGSUSED*/ static int -cachefs_link(vnode_t *tdvp, vnode_t *fvp, char *tnm, cred_t *cr) +cachefs_link(vnode_t *tdvp, vnode_t *fvp, char *tnm, cred_t *cr, + caller_context_t *ct, int flags) { fscache_t *fscp = VFS_TO_FSCACHE(tdvp->v_vfsp); cnode_t *tdcp = VTOC(tdvp); @@ -4813,7 +4842,7 @@ cachefs_link(vnode_t *tdvp, vnode_t *fvp, char *tnm, cred_t *cr) if (fscp->fs_cache->c_flags & (CACHE_NOFILL | CACHE_NOCACHE)) ASSERT(tdcp->c_flags & CN_NOCACHE); - if (VOP_REALVP(fvp, &realvp) == 0) { + if (VOP_REALVP(fvp, &realvp, ct) == 0) { fvp = realvp; } @@ -4939,7 +4968,7 @@ cachefs_link_connected(vnode_t *tdvp, vnode_t *fvp, char *tnm, cred_t *cr) CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_link (nfsv4): tdcp %p, tdbackvp %p, " "name %s\n", tdcp, tdcp->c_backvp, tnm)); - error = VOP_LINK(tdcp->c_backvp, backvp, tnm, cr); + error = VOP_LINK(tdcp->c_backvp, backvp, tnm, cr, NULL, 0); if (error) { mutex_exit(&tdcp->c_statelock); goto out; @@ -4973,7 +5002,7 @@ cachefs_link_connected(vnode_t *tdvp, vnode_t *fvp, char *tnm, cred_t *cr) /* XXX bob: given what modify_cobject does this seems unnecessary */ fcp->c_attr.va_mask = AT_ALL; - error = VOP_GETATTR(fcp->c_backvp, &fcp->c_attr, 0, cr); + error = VOP_GETATTR(fcp->c_backvp, &fcp->c_attr, 0, cr, NULL); mutex_exit(&fcp->c_statelock); out: if (backvp) @@ -5108,9 +5137,10 @@ cachefs_link_disconnected(vnode_t *tdvp, vnode_t *fvp, char *tnm, */ kmutex_t cachefs_rename_lock; +/*ARGSUSED*/ static int cachefs_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, - char *nnm, cred_t *cr) + char *nnm, cred_t *cr, caller_context_t *ct, int flags) { fscache_t *fscp = C_TO_FSCACHE(VTOC(odvp)); cachefscache_t *cachep = fscp->fs_cache; @@ -5125,7 +5155,7 @@ cachefs_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, if (getzoneid() != GLOBAL_ZONEID) return (EPERM); - if (VOP_REALVP(ndvp, &realvp) == 0) + if (VOP_REALVP(ndvp, &realvp, ct) == 0) ndvp = realvp; /* @@ -5286,7 +5316,7 @@ cachefs_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, bzero(&gone, sizeof (gone)); gone.fid_len = MAXFIDSZ; if (delvp != NULL) - (void) VOP_FID(delvp, &gone); + (void) VOP_FID(delvp, &gone, ct); cachefs_log_rename(cachep, error, fscp->fs_cfsvfsp, &gone, 0, (delvp != NULL), crgetuid(cr)); @@ -5407,7 +5437,8 @@ cachefs_rename_connected(vnode_t *odvp, char *onm, vnode_t *ndvp, ("cachefs_rename (nfsv4): odcp %p, odbackvp %p, " " ndcp %p, ndbackvp %p, onm %s, nnm %s\n", odcp, odcp->c_backvp, ndcp, ndcp->c_backvp, onm, nnm)); - error = VOP_RENAME(odcp->c_backvp, onm, ndcp->c_backvp, nnm, cr); + error = VOP_RENAME(odcp->c_backvp, onm, ndcp->c_backvp, nnm, cr, NULL, + 0); if (error) goto out; @@ -5780,9 +5811,10 @@ cachefs_rename_disconnected(vnode_t *odvp, char *onm, vnode_t *ndvp, return (error); } +/*ARGSUSED*/ static int cachefs_mkdir(vnode_t *dvp, char *nm, vattr_t *vap, vnode_t **vpp, - cred_t *cr) + cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp) { cnode_t *dcp = VTOC(dvp); fscache_t *fscp = C_TO_FSCACHE(dcp); @@ -5926,7 +5958,7 @@ cachefs_mkdir_connected(vnode_t *dvp, char *nm, vattr_t *vap, CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_mkdir (nfsv4): dcp %p, dbackvp %p, " "name %s\n", dcp, dcp->c_backvp, nm)); - error = VOP_MKDIR(dcp->c_backvp, nm, vap, &vp, cr); + error = VOP_MKDIR(dcp->c_backvp, nm, vap, &vp, cr, NULL, 0, NULL); mutex_exit(&dcp->c_statelock); if (error) { goto out; @@ -6150,8 +6182,10 @@ cachefs_mkdir_disconnected(vnode_t *dvp, char *nm, vattr_t *vap, return (error); } +/*ARGSUSED*/ static int -cachefs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) +cachefs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr, + caller_context_t *ct, int flags) { cnode_t *dcp = VTOC(dvp); fscache_t *fscp = C_TO_FSCACHE(dcp); @@ -6269,7 +6303,7 @@ cachefs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) } /* must not be current dir */ - if (VOP_CMP(vp, cdir)) { + if (VOP_CMP(vp, cdir, ct)) { error = EINVAL; break; } @@ -6372,7 +6406,7 @@ cachefs_rmdir_connected(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr, CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_rmdir (nfsv4): dcp %p, dbackvp %p, " "name %s\n", dcp, dcp->c_backvp, nm)); - error = VOP_RMDIR(dcp->c_backvp, nm, cdir, cr); + error = VOP_RMDIR(dcp->c_backvp, nm, cdir, cr, NULL, 0); if (error) goto out; @@ -6507,9 +6541,10 @@ cachefs_rmdir_disconnected(vnode_t *dvp, char *nm, vnode_t *cdir, return (error); } +/*ARGSUSED*/ static int cachefs_symlink(vnode_t *dvp, char *lnm, vattr_t *tva, - char *tnm, cred_t *cr) + char *tnm, cred_t *cr, caller_context_t *ct, int flags) { cnode_t *dcp = VTOC(dvp); fscache_t *fscp = C_TO_FSCACHE(dcp); @@ -6635,7 +6670,7 @@ cachefs_symlink_connected(vnode_t *dvp, char *lnm, vattr_t *tva, CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_symlink (nfsv4): dcp %p, dbackvp %p, " "lnm %s, tnm %s\n", dcp, dcp->c_backvp, lnm, tnm)); - error = VOP_SYMLINK(dcp->c_backvp, lnm, tva, tnm, cr); + error = VOP_SYMLINK(dcp->c_backvp, lnm, tva, tnm, cr, NULL, 0); if (error) { mutex_exit(&dcp->c_statelock); goto out; @@ -6650,7 +6685,8 @@ cachefs_symlink_connected(vnode_t *dvp, char *lnm, vattr_t *tva, CFSOP_MODIFY_COBJECT(fscp, dcp, cr); /* lookup the symlink we just created and get its fid and attrs */ - (void) VOP_LOOKUP(dcp->c_backvp, lnm, &backvp, NULL, 0, NULL, cr); + (void) VOP_LOOKUP(dcp->c_backvp, lnm, &backvp, NULL, 0, NULL, cr, + NULL, NULL, NULL); if (backvp == NULL) { if (CFS_ISFS_BACKFS_NFSV4(fscp) == 0) cachefs_nocache(dcp); @@ -6891,8 +6927,10 @@ cachefs_symlink_disconnected(vnode_t *dvp, char *lnm, vattr_t *tva, return (error); } +/*ARGSUSED*/ static int -cachefs_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp) +cachefs_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) { cnode_t *dcp = VTOC(vp); fscache_t *fscp = C_TO_FSCACHE(dcp); @@ -7087,7 +7125,8 @@ cachefs_readdir_connected(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp) CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_readdir (nfsv4): " "dcp %p, dbackvp %p\n", dcp, dcp->c_backvp)); - error = VOP_READDIR(dcp->c_backvp, uiop, cr, eofp); + error = VOP_READDIR(dcp->c_backvp, uiop, cr, eofp, + NULL, 0); VOP_RWUNLOCK(dcp->c_backvp, V_WRITELOCK_FALSE, NULL); } @@ -7132,7 +7171,7 @@ cachefs_readback_translate(cnode_t *cp, uio_t *uiop, cred_t *cr, int *eofp) uioin.uio_resid = buffysize; (void) VOP_RWLOCK(cp->c_backvp, V_WRITELOCK_FALSE, NULL); - error = VOP_READDIR(cp->c_backvp, &uioin, cr, eofp); + error = VOP_READDIR(cp->c_backvp, &uioin, cr, eofp, NULL, 0); VOP_RWUNLOCK(cp->c_backvp, V_WRITELOCK_FALSE, NULL); if (error != 0) @@ -7186,8 +7225,9 @@ cachefs_readdir_disconnected(vnode_t *vp, uio_t *uiop, cred_t *cr, return (error); } +/*ARGSUSED*/ static int -cachefs_fid(struct vnode *vp, struct fid *fidp) +cachefs_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct) { int error = 0; struct cnode *cp = VTOC(vp); @@ -7257,7 +7297,8 @@ cachefs_rwunlock(struct vnode *vp, int write_lock, caller_context_t *ctp) /* ARGSUSED */ static int -cachefs_seek(struct vnode *vp, offset_t ooff, offset_t *noffp) +cachefs_seek(struct vnode *vp, offset_t ooff, offset_t *noffp, + caller_context_t *ct) { return (0); } @@ -7266,10 +7307,11 @@ static int cachefs_lostpage = 0; /* * Return all the pages from [off..off+len] in file */ +/*ARGSUSED*/ static int cachefs_getpage(struct vnode *vp, offset_t off, size_t len, uint_t *protp, struct page *pl[], size_t plsz, struct seg *seg, - caddr_t addr, enum seg_rw rw, cred_t *cr) + caddr_t addr, enum seg_rw rw, cred_t *cr, caller_context_t *ct) { cnode_t *cp = VTOC(vp); int error; @@ -7441,7 +7483,7 @@ cachefs_getpage_backfs_nfsv4(struct vnode *vp, offset_t off, size_t len, ("cachefs_getpage_backfs_nfsv4: cnode %p, backvp %p\n", cp, backvp)); error = VOP_GETPAGE(backvp, off, len, protp, pl, plsz, seg, - addr, rw, cr); + addr, rw, cr, NULL); return (error); } @@ -7507,7 +7549,7 @@ cachefs_getapage(struct vnode *vp, u_offset_t off, size_t len, uint_t *protp, } error = VOP_GETPAGE(cp->c_backvp, off, PAGESIZE, protp, ourpl, PAGESIZE, seg, - addr, S_READ, cr); + addr, S_READ, cr, NULL); /* * backfs returns EFAULT when we are trying for a * page beyond EOF but cachefs has the knowledge that @@ -7588,13 +7630,14 @@ cachefs_getapage(struct vnode *vp, u_offset_t off, size_t len, uint_t *protp, /* else XXX assert CN_NOCACHE? */ error = VOP_GETPAGE(cp->c_backvp, (offset_t)off, PAGESIZE, protp, ourpl, popsize, - seg, addr, S_READ, cr); + seg, addr, S_READ, cr, NULL); if (error) goto out; fscp->fs_stats.st_misses++; } else { if (cp->c_flags & CN_POPULATION_PENDING) { - error = VOP_FSYNC(cp->c_frontvp, FSYNC, cr); + error = VOP_FSYNC(cp->c_frontvp, FSYNC, cr, + NULL); cp->c_flags &= ~CN_POPULATION_PENDING; if (error) { cachefs_nocache(cp); @@ -7608,7 +7651,7 @@ cachefs_getapage(struct vnode *vp, u_offset_t off, size_t len, uint_t *protp, */ error = VOP_GETPAGE(cp->c_frontvp, (offset_t)off, PAGESIZE, protp, ourpl, PAGESIZE, seg, addr, - rw, cr); + rw, cr, NULL); if (CACHEFS_LOG_LOGGING(cachep, CACHEFS_LOG_GPFRONT)) cachefs_log_gpfront(cachep, error, fscp->fs_cfsvfsp, @@ -7718,7 +7761,7 @@ cachefs_getapage_back(struct vnode *vp, u_offset_t off, size_t len, } error = VOP_GETPAGE(cp->c_backvp, (offset_t)off, PAGESIZE, protp, ourpl, PAGESIZE, seg, - addr, S_READ, cr); + addr, S_READ, cr, NULL); if (error) goto out; @@ -7776,8 +7819,10 @@ cachefs_getapage_back(struct vnode *vp, u_offset_t off, size_t len, return (error); } +/*ARGSUSED*/ static int -cachefs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr) +cachefs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr, + caller_context_t *ct) { cnode_t *cp = VTOC(vp); int error = 0; @@ -7874,7 +7919,7 @@ cachefs_putpage_backfs_nfsv4(vnode_t *vp, offset_t off, size_t len, int flags, CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_putpage_backfs_nfsv4: cnode %p, backvp %p\n", cp, backvp)); - error = VOP_PUTPAGE(backvp, off, len, flags, cr); + error = VOP_PUTPAGE(backvp, off, len, flags, cr, NULL); return (error); } @@ -8046,7 +8091,8 @@ cachefs_putpage_common(struct vnode *vp, offset_t off, size_t len, /*ARGSUSED*/ static int cachefs_map(struct vnode *vp, offset_t off, struct as *as, caddr_t *addrp, - size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) + size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct) { cnode_t *cp = VTOC(vp); fscache_t *fscp = C_TO_FSCACHE(cp); @@ -8224,7 +8270,8 @@ cachefs_map_backfs_nfsv4(struct vnode *vp, offset_t off, struct as *as, CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_map_backfs_nfsv4: cnode %p, backvp %p\n", cp, backvp)); - error = VOP_MAP(backvp, off, as, addrp, len, prot, maxprot, flags, cr); + error = VOP_MAP(backvp, off, as, addrp, len, prot, maxprot, flags, cr, + NULL); return (error); } @@ -8233,7 +8280,7 @@ cachefs_map_backfs_nfsv4(struct vnode *vp, offset_t off, struct as *as, static int cachefs_addmap(struct vnode *vp, offset_t off, struct as *as, caddr_t addr, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, - cred_t *cr) + cred_t *cr, caller_context_t *ct) { cnode_t *cp = VTOC(vp); fscache_t *fscp = C_TO_FSCACHE(cp); @@ -8261,7 +8308,7 @@ cachefs_addmap(struct vnode *vp, offset_t off, struct as *as, static int cachefs_delmap(struct vnode *vp, offset_t off, struct as *as, caddr_t addr, size_t len, uint_t prot, uint_t maxprot, uint_t flags, - cred_t *cr) + cred_t *cr, caller_context_t *ct) { cnode_t *cp = VTOC(vp); fscache_t *fscp = C_TO_FSCACHE(cp); @@ -8346,7 +8393,8 @@ cachefs_delmap(struct vnode *vp, offset_t off, struct as *as, /* ARGSUSED */ static int cachefs_frlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag, - offset_t offset, struct flk_callback *flk_cbp, cred_t *cr) + offset_t offset, struct flk_callback *flk_cbp, cred_t *cr, + caller_context_t *ct) { struct cnode *cp = VTOC(vp); int error; @@ -8382,7 +8430,7 @@ cachefs_frlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag, /* XXX bob: nfs does a bunch more checks than we do */ if (CFS_ISFS_LLOCK(fscp)) { ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0); - return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr)); + return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct)); } for (;;) { @@ -8441,7 +8489,7 @@ cachefs_frlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag, if (bfp->l_type != F_UNLCK && cmd != F_GETLK && !CFS_ISFS_BACKFS_NFSV4(fscp)) { error = cachefs_putpage( - vp, (offset_t)0, 0, B_INVAL, cr); + vp, (offset_t)0, 0, B_INVAL, cr, ct); if (error) { error = ENOLCK; VN_RELE(backvp); @@ -8453,7 +8501,8 @@ cachefs_frlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag, CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_frlock (nfsv4): cp %p, backvp %p\n", cp, backvp)); - error = VOP_FRLOCK(backvp, cmd, bfp, flag, offset, NULL, cr); + error = VOP_FRLOCK(backvp, cmd, bfp, flag, offset, NULL, cr, + ct); VN_RELE(backvp); if (CFS_TIMEOUT(fscp, error)) { connected = 1; @@ -8571,7 +8620,7 @@ cachefs_space_backfs_nfsv4(struct vnode *vp, int cmd, struct flock64 *bfp, /*ARGSUSED*/ static int -cachefs_realvp(struct vnode *vp, struct vnode **vpp) +cachefs_realvp(struct vnode *vp, struct vnode **vpp, caller_context_t *ct) { return (EINVAL); } @@ -8579,7 +8628,7 @@ cachefs_realvp(struct vnode *vp, struct vnode **vpp) /*ARGSUSED*/ static int cachefs_pageio(struct vnode *vp, page_t *pp, u_offset_t io_off, size_t io_len, - int flags, cred_t *cr) + int flags, cred_t *cr, caller_context_t *ct) { return (ENOSYS); } @@ -8618,7 +8667,7 @@ cachefs_setsecattr_connected(cnode_t *cp, CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_setsecattr (nfsv4): cp %p, backvp %p", cp, cp->c_backvp)); - error = VOP_SETSECATTR(cp->c_backvp, vsec, flag, cr); + error = VOP_SETSECATTR(cp->c_backvp, vsec, flag, cr, NULL); if (error) { goto out; } @@ -8742,8 +8791,10 @@ cachefs_setsecattr_disconnected(cnode_t *cp, return (error); } +/*ARGSUSED*/ static int -cachefs_setsecattr(vnode_t *vp, vsecattr_t *vsec, int flag, cred_t *cr) +cachefs_setsecattr(vnode_t *vp, vsecattr_t *vsec, int flag, cred_t *cr, + caller_context_t *ct) { cnode_t *cp = VTOC(vp); fscache_t *fscp = C_TO_FSCACHE(cp); @@ -8791,7 +8842,7 @@ cachefs_setsecattr(vnode_t *vp, vsecattr_t *vsec, int flag, cred_t *cr) held = 0; } - /* aquire access to the file system */ + /* acquire access to the file system */ error = cachefs_cd_access(fscp, connected, 1); if (error) break; @@ -8883,7 +8934,8 @@ cachefs_acl2perm(cnode_t *cp, vsecattr_t *vsec) } static int -cachefs_getsecattr(vnode_t *vp, vsecattr_t *vsec, int flag, cred_t *cr) +cachefs_getsecattr(vnode_t *vp, vsecattr_t *vsec, int flag, cred_t *cr, + caller_context_t *ct) { cnode_t *cp = VTOC(vp); fscache_t *fscp = C_TO_FSCACHE(cp); @@ -8913,7 +8965,7 @@ cachefs_getsecattr(vnode_t *vp, vsecattr_t *vsec, int flag, cred_t *cr) CFS_BACKFS_NFSV4_ASSERT_CNODE(cp); if (fscp->fs_info.fi_mntflags & CFS_NOACL) { - error = fs_fab_acl(vp, vsec, flag, cr); + error = fs_fab_acl(vp, vsec, flag, cr, ct); goto out; } @@ -8974,7 +9026,8 @@ cachefs_getsecattr(vnode_t *vp, vsecattr_t *vsec, int flag, cred_t *cr) } static int -cachefs_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr) +cachefs_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr, + caller_context_t *ct) { cnode_t *cp = VTOC(vp); fscache_t *fscp = C_TO_FSCACHE(cp); @@ -9014,7 +9067,7 @@ cachefs_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr) CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_shrlock (nfsv4): cp %p, backvp %p", cp, backvp)); - error = VOP_SHRLOCK(backvp, cmd, shr, flag, cr); + error = VOP_SHRLOCK(backvp, cmd, shr, flag, cr, ct); } out: @@ -9065,7 +9118,7 @@ cachefs_getsecattr_connected(vnode_t *vp, vsecattr_t *vsec, int flag, CFS_DPRINT_BACKFS_NFSV4(fscp, ("cachefs_getsecattr (nfsv4): cp %p, backvp %p", cp, cp->c_backvp)); - error = VOP_GETSECATTR(cp->c_backvp, vsec, flag, cr); + error = VOP_GETSECATTR(cp->c_backvp, vsec, flag, cr, NULL); if (error) goto out; @@ -9178,7 +9231,7 @@ cachefs_cacheacl(cnode_t *cp, vsecattr_t *vsecp) bzero(&vsec, sizeof (vsec)); vsecp->vsa_mask = VSA_ACL | VSA_ACLCNT | VSA_DFACL | VSA_DFACLCNT; - error = VOP_GETSECATTR(cp->c_backvp, vsecp, 0, kcred); + error = VOP_GETSECATTR(cp->c_backvp, vsecp, 0, kcred, NULL); if (error != 0) { goto out; } @@ -9256,7 +9309,7 @@ cachefs_cacheacl(cnode_t *cp, vsecattr_t *vsecp) ASSERT(vp != NULL); (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); - error = VOP_SETSECATTR(vp, vsecp, 0, kcred); + error = VOP_SETSECATTR(vp, vsecp, 0, kcred, NULL); VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); if (error != 0) { #ifdef CFSDEBUG @@ -9320,7 +9373,7 @@ cachefs_purgeacl(cnode_t *cp) (void) strcat(name, ".d"); (void) VOP_RMDIR(cp->c_filegrp->fg_dirvp, name, - cp->c_filegrp->fg_dirvp, kcred); + cp->c_filegrp->fg_dirvp, kcred, NULL, 0); } cp->c_metadata.md_flags &= ~(MD_ACL | MD_ACLDIR); @@ -9345,7 +9398,7 @@ cachefs_getacldirvp(cnode_t *cp) make_ascii_name(&cp->c_id, name); (void) strcat(name, ".d"); error = VOP_LOOKUP(cp->c_filegrp->fg_dirvp, - name, &cp->c_acldirvp, NULL, 0, NULL, kcred); + name, &cp->c_acldirvp, NULL, 0, NULL, kcred, NULL, NULL, NULL); if ((error != 0) && (error != ENOENT)) goto out; @@ -9360,7 +9413,7 @@ cachefs_getacldirvp(cnode_t *cp) AT_UID | AT_GID; error = VOP_MKDIR(cp->c_filegrp->fg_dirvp, - name, &va, &cp->c_acldirvp, kcred); + name, &va, &cp->c_acldirvp, kcred, NULL, 0, NULL); if (error != 0) goto out; } @@ -9432,7 +9485,7 @@ cachefs_getaclfromcache(cnode_t *cp, vsecattr_t *vsec) } if (vp != NULL) { - if ((error = VOP_GETSECATTR(vp, vsec, 0, kcred)) != 0) { + if ((error = VOP_GETSECATTR(vp, vsec, 0, kcred, NULL)) != 0) { #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_VOPS) printf("cachefs_getaclfromcache: error %d\n", @@ -9858,7 +9911,8 @@ cachefs_acl_access(struct cnode *cp, int mode, cred_t *cr) error = ETIMEDOUT; } if (error == 0) - error = VOP_GETSECATTR(cp->c_backvp, &vsec, 0, cr); + error = VOP_GETSECATTR(cp->c_backvp, &vsec, 0, cr, + NULL); if (error != 0) { #ifdef CFSDEBUG CFS_DEBUG(CFSDEBUG_VOPS) @@ -10104,8 +10158,8 @@ cachefs_modified(cnode_t *cp) /* identify file so fsck knows it is modified */ va.va_mode = 0766; va.va_mask = AT_MODE; - error = VOP_SETATTR(cp->c_frontvp, &va, - 0, kcred, NULL); + error = VOP_SETATTR(cp->c_frontvp, + &va, 0, kcred, NULL); if (error) { cmn_err(CE_WARN, "Cannot change ff mode.\n"); @@ -10184,7 +10238,8 @@ cachefs_vtype_aclok(vnode_t *vp) } static int -cachefs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) +cachefs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, + caller_context_t *ct) { int error = 0; fscache_t *fscp = C_TO_FSCACHE(VTOC(vp)); @@ -10202,7 +10257,7 @@ cachefs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) } (*valp)++; } else - error = fs_pathconf(vp, cmd, valp, cr); + error = fs_pathconf(vp, cmd, valp, cr, ct); return (error); } diff --git a/usr/src/uts/common/fs/ctfs/ctfs_all.c b/usr/src/uts/common/fs/ctfs/ctfs_all.c index 382f66da8ab7..6a0d080e56ee 100644 --- a/usr/src/uts/common/fs/ctfs/ctfs_all.c +++ b/usr/src/uts/common/fs/ctfs/ctfs_all.c @@ -50,7 +50,8 @@ static int ctfs_adir_do_readdir(vnode_t *, struct dirent64 *, int *, offset_t *, offset_t *, void *); -static int ctfs_adir_do_lookup(vnode_t *, const char *, vnode_t **, ino64_t *); +static int ctfs_adir_do_lookup(vnode_t *, const char *, vnode_t **, ino64_t *, + cred_t *); /* * ctfs_create_adirnode @@ -71,7 +72,12 @@ ctfs_create_adirnode(vnode_t *pvp) */ /* ARGSUSED */ static int -ctfs_adir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +ctfs_adir_getattr( + vnode_t *vp, + vattr_t *vap, + int flags, + cred_t *cr, + caller_context_t *ct) { int i, total; @@ -89,8 +95,10 @@ ctfs_adir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) return (0); } +/* ARGSUSED */ static int -ctfs_adir_do_lookup(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop) +ctfs_adir_do_lookup(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop, + cred_t *cr) { int i; contract_t *ct; diff --git a/usr/src/uts/common/fs/ctfs/ctfs_cdir.c b/usr/src/uts/common/fs/ctfs/ctfs_cdir.c index 4861f73244c9..0de17d43ebcd 100644 --- a/usr/src/uts/common/fs/ctfs/ctfs_cdir.c +++ b/usr/src/uts/common/fs/ctfs/ctfs_cdir.c @@ -95,7 +95,12 @@ ctfs_create_cdirnode(vnode_t *pvp, contract_t *ct) */ /* ARGSUSED */ static int -ctfs_cdir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +ctfs_cdir_getattr( + vnode_t *vp, + vattr_t *vap, + int flags, + cred_t *cr, + caller_context_t *ct) { ctfs_cdirnode_t *cdirnode = vp->v_data; @@ -129,7 +134,7 @@ ctfs_cdir_do_inode(vnode_t *vp, int index) */ /* ARGSUSED */ static void -ctfs_cdir_inactive(vnode_t *vp, cred_t *cr) +ctfs_cdir_inactive(vnode_t *vp, cred_t *cr, caller_context_t *cct) { ctfs_cdirnode_t *cdirnode = vp->v_data; contract_t *ct = cdirnode->ctfs_cn_contract; diff --git a/usr/src/uts/common/fs/ctfs/ctfs_ctl.c b/usr/src/uts/common/fs/ctfs/ctfs_ctl.c index da293cbb21cb..da2ac0b9c745 100644 --- a/usr/src/uts/common/fs/ctfs/ctfs_ctl.c +++ b/usr/src/uts/common/fs/ctfs/ctfs_ctl.c @@ -81,7 +81,12 @@ ctfs_create_ctlnode(vnode_t *pvp) */ /* ARGSUSED */ static int -ctfs_ctl_access(vnode_t *vp, int mode, int flags, cred_t *cr) +ctfs_ctl_access( + vnode_t *vp, + int mode, + int flags, + cred_t *cr, + caller_context_t *cct) { ctfs_ctlnode_t *ctlnode = vp->v_data; contract_t *ct = ctlnode->ctfs_ctl_contract; @@ -108,17 +113,17 @@ ctfs_ctl_access(vnode_t *vp, int mode, int flags, cred_t *cr) * constraints imposed by ctfs_ctl_access are met. */ static int -ctfs_ctl_open(vnode_t **vpp, int flag, cred_t *cr) +ctfs_ctl_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { if (flag != (FWRITE | FOFFMAX)) return (EINVAL); - return (ctfs_ctl_access(*vpp, VWRITE, 0, cr)); + return (ctfs_ctl_access(*vpp, VWRITE, 0, cr, ct)); } /* * ctfs_ctl_common_getattr - * Implements fucntionality common to ctl and status ctfs VOP_GETATTR + * Implements functionality common to ctl and status ctfs VOP_GETATTR * entry points. It assumes vp->v_data is set */ static int @@ -144,7 +149,8 @@ ctfs_ctl_common_getattr(vnode_t *vp, vattr_t *vap) */ /* ARGSUSED */ static int -ctfs_ctl_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +ctfs_ctl_getattr(vnode_t *vp, vattr_t *vap, int flags, + cred_t *cr, caller_context_t *ct) { vap->va_mode = 0222; @@ -156,7 +162,8 @@ ctfs_ctl_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) */ /* ARGSUSED */ static int -ctfs_stat_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +ctfs_stat_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { vap->va_mode = 0444; @@ -170,8 +177,14 @@ ctfs_stat_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) */ /* ARGSUSED */ static int -ctfs_ctl_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, - int *rvalp) +ctfs_ctl_ioctl( + vnode_t *vp, + int cmd, + intptr_t arg, + int flag, + cred_t *cr, + int *rvalp, + caller_context_t *cct) { ctfs_ctlnode_t *ctlnode = vp->v_data; contract_t *ct = ctlnode->ctfs_ctl_contract; @@ -257,8 +270,14 @@ ctfs_create_statnode(vnode_t *pvp) */ /* ARGSUSED */ static int -ctfs_stat_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, - int *rvalp) +ctfs_stat_ioctl( + vnode_t *vp, + int cmd, + intptr_t arg, + int flag, + cred_t *cr, + int *rvalp, + caller_context_t *cct) { ctfs_ctlnode_t *statnode = vp->v_data; contract_t *ct = statnode->ctfs_ctl_contract; diff --git a/usr/src/uts/common/fs/ctfs/ctfs_event.c b/usr/src/uts/common/fs/ctfs/ctfs_event.c index ac4447e4934a..c9a99e85fb45 100644 --- a/usr/src/uts/common/fs/ctfs/ctfs_event.c +++ b/usr/src/uts/common/fs/ctfs/ctfs_event.c @@ -191,7 +191,12 @@ ctfs_create_evnode(vnode_t *pvp) */ /*ARGSUSED*/ static int -ctfs_ev_access(vnode_t *vp, int mode, int flags, cred_t *cr) +ctfs_ev_access( + vnode_t *vp, + int mode, + int flags, + cred_t *cr, + caller_context_t *cct) { ctfs_evnode_t *evnode = vp->v_data; contract_t *ct = evnode->ctfs_ev_contract; @@ -214,7 +219,7 @@ ctfs_ev_access(vnode_t *vp, int mode, int flags, cred_t *cr) */ /* ARGSUSED */ static int -ctfs_ev_open(vnode_t **vpp, int flag, cred_t *cr) +ctfs_ev_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *cct) { ctfs_evnode_t *evnode = (*vpp)->v_data; contract_t *ct = evnode->ctfs_ev_contract; @@ -235,7 +240,7 @@ ctfs_ev_open(vnode_t **vpp, int flag, cred_t *cr) */ /* ARGSUSED */ static void -ctfs_ev_inactive(vnode_t *vp, cred_t *cr) +ctfs_ev_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { ctfs_evnode_t *evnode; vnode_t *pvp = gfs_file_parent(vp); @@ -258,7 +263,12 @@ ctfs_ev_inactive(vnode_t *vp, cred_t *cr) */ /* ARGSUSED */ static int -ctfs_ev_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +ctfs_ev_getattr( + vnode_t *vp, + vattr_t *vap, + int flags, + cred_t *cr, + caller_context_t *ct) { ctfs_evnode_t *evnode = vp->v_data; @@ -281,8 +291,14 @@ ctfs_ev_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) */ /* ARGSUSED */ static int -ctfs_ev_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, - int *rvalp) +ctfs_ev_ioctl( + vnode_t *vp, + int cmd, + intptr_t arg, + int flag, + cred_t *cr, + int *rvalp, + caller_context_t *ct) { ctfs_evnode_t *evnode = vp->v_data; @@ -293,9 +309,15 @@ ctfs_ev_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, /* * ctfs_ev_poll - VOP_POLL entry point */ +/*ARGSUSED*/ static int -ctfs_ev_poll(vnode_t *vp, short events, int anyyet, short *reventsp, - pollhead_t **php) +ctfs_ev_poll( + vnode_t *vp, + short events, + int anyyet, + short *reventsp, + pollhead_t **php, + caller_context_t *ct) { ctfs_evnode_t *evnode = vp->v_data; @@ -361,7 +383,7 @@ ctfs_create_bundle(vnode_t *pvp) */ /* ARGSUSED */ static int -ctfs_bu_open(vnode_t **vpp, int flag, cred_t *cr) +ctfs_bu_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { ctfs_bunode_t *bunode = (*vpp)->v_data; @@ -379,7 +401,7 @@ ctfs_bu_open(vnode_t **vpp, int flag, cred_t *cr) */ /* ARGSUSED */ static void -ctfs_bu_inactive(vnode_t *vp, cred_t *cr) +ctfs_bu_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { ctfs_bunode_t *bunode; vnode_t *pvp = gfs_file_parent(vp); @@ -400,7 +422,12 @@ ctfs_bu_inactive(vnode_t *vp, cred_t *cr) */ /* ARGSUSED */ static int -ctfs_bu_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +ctfs_bu_getattr( + vnode_t *vp, + vattr_t *vap, + int flags, + cred_t *cr, + caller_context_t *ct) { ctfs_bunode_t *bunode = vp->v_data; @@ -424,8 +451,14 @@ ctfs_bu_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) */ /* ARGSUSED */ static int -ctfs_bu_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, - int *rvalp) +ctfs_bu_ioctl( + vnode_t *vp, + int cmd, + intptr_t arg, + int flag, + cred_t *cr, + int *rvalp, + caller_context_t *ct) { ctfs_bunode_t *bunode = vp->v_data; @@ -436,9 +469,15 @@ ctfs_bu_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, /* * ctfs_bu_poll - VOP_POLL entry point */ +/*ARGSUSED*/ static int -ctfs_bu_poll(vnode_t *vp, short events, int anyyet, short *reventsp, - pollhead_t **php) +ctfs_bu_poll( + vnode_t *vp, + short events, + int anyyet, + short *reventsp, + pollhead_t **php, + caller_context_t *ct) { ctfs_bunode_t *bunode = vp->v_data; diff --git a/usr/src/uts/common/fs/ctfs/ctfs_latest.c b/usr/src/uts/common/fs/ctfs/ctfs_latest.c index 7d15dd494a28..6a2bfc80f117 100644 --- a/usr/src/uts/common/fs/ctfs/ctfs_latest.c +++ b/usr/src/uts/common/fs/ctfs/ctfs_latest.c @@ -65,7 +65,7 @@ ctfs_create_latenode(vnode_t *pvp) * ctfs_latest_getattr to obtain that file. */ static vnode_t * -ctfs_latest_nested_open(vnode_t *vp) +ctfs_latest_nested_open(vnode_t *vp, cred_t *cr) { contract_t *ct = ttolwp(curthread)->lwp_ct_latest[ gfs_file_index(gfs_file_parent(vp))]; @@ -77,7 +77,7 @@ ctfs_latest_nested_open(vnode_t *vp) gfs_file_set_index(cvp, -1); - VERIFY(gfs_dir_lookup(cvp, "status", &svp) == 0); + VERIFY(gfs_dir_lookup(cvp, "status", &svp, cr) == 0); VN_RELE(cvp); @@ -94,14 +94,19 @@ ctfs_latest_nested_open(vnode_t *vp) */ /* ARGSUSED */ static int -ctfs_latest_access(vnode_t *vp, int mode, int flags, cred_t *cr) +ctfs_latest_access( + vnode_t *vp, + int mode, + int flags, + cred_t *cr, + caller_context_t *ct) { vnode_t *nvp; if (mode & (VEXEC | VWRITE)) return (EACCES); - if (nvp = ctfs_latest_nested_open(vp)) { + if (nvp = ctfs_latest_nested_open(vp, cr)) { VN_RELE(nvp); return (0); } @@ -116,17 +121,17 @@ ctfs_latest_access(vnode_t *vp, int mode, int flags, cred_t *cr) * the LWP's latest contract. */ static int -ctfs_latest_open(vnode_t **vpp, int flag, cred_t *cr) +ctfs_latest_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { vnode_t *nvp; if (flag != (FREAD | FOFFMAX)) return (EINVAL); - if (nvp = ctfs_latest_nested_open(*vpp)) { + if (nvp = ctfs_latest_nested_open(*vpp, cr)) { VN_RELE(*vpp); *vpp = nvp; - return (VOP_OPEN(vpp, flag, cr)); + return (VOP_OPEN(vpp, flag, cr, ct)); } return (ESRCH); @@ -139,12 +144,17 @@ ctfs_latest_open(vnode_t **vpp, int flag, cred_t *cr) * latest contract. Otherwise it fakes up something bland. */ static int -ctfs_latest_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +ctfs_latest_getattr( + vnode_t *vp, + vattr_t *vap, + int flags, + cred_t *cr, + caller_context_t *ct) { vnode_t *nvp; - if (nvp = ctfs_latest_nested_open(vp)) { - int res = VOP_GETATTR(nvp, vap, flags, cr); + if (nvp = ctfs_latest_nested_open(vp, cr)) { + int res = VOP_GETATTR(nvp, vap, flags, cr, ct); VN_RELE(nvp); return (res); } diff --git a/usr/src/uts/common/fs/ctfs/ctfs_root.c b/usr/src/uts/common/fs/ctfs/ctfs_root.c index 1616a986cddd..8861b6d73b48 100644 --- a/usr/src/uts/common/fs/ctfs/ctfs_root.c +++ b/usr/src/uts/common/fs/ctfs/ctfs_root.c @@ -55,7 +55,7 @@ /* * ctfs, the contract filesystem. * - * Exposes the constract subsystem to userland. The structure of the + * Exposes the construct subsystem to userland. The structure of the * filesytem is a public interface, but the behavior of the files is * private and unstable. Contract consumers are expected to use * libcontract(3lib) to operate on ctfs file descriptors. @@ -399,7 +399,7 @@ ctfs_common_getattr(vnode_t *vp, vattr_t *vap) */ /* ARGSUSED */ int -ctfs_open(vnode_t **vpp, int flag, cred_t *cr) +ctfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { if ((flag & (FOFFMAX | FWRITE)) != FOFFMAX) return (EINVAL); @@ -414,7 +414,13 @@ ctfs_open(vnode_t **vpp, int flag, cred_t *cr) */ /* ARGSUSED */ int -ctfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) +ctfs_close( + vnode_t *vp, + int flag, + int count, + offset_t offset, + cred_t *cr, + caller_context_t *ct) { return (0); } @@ -424,7 +430,12 @@ ctfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) */ /* ARGSUSED */ int -ctfs_access_dir(vnode_t *vp, int mode, int flags, cred_t *cr) +ctfs_access_dir( + vnode_t *vp, + int mode, + int flags, + cred_t *cr, + caller_context_t *ct) { if (mode & VWRITE) return (EACCES); @@ -437,7 +448,12 @@ ctfs_access_dir(vnode_t *vp, int mode, int flags, cred_t *cr) */ /* ARGSUSED */ int -ctfs_access_readonly(vnode_t *vp, int mode, int flags, cred_t *cr) +ctfs_access_readonly( + vnode_t *vp, + int mode, + int flags, + cred_t *cr, + caller_context_t *ct) { if (mode & (VWRITE | VEXEC)) return (EACCES); @@ -450,7 +466,12 @@ ctfs_access_readonly(vnode_t *vp, int mode, int flags, cred_t *cr) */ /* ARGSUSED */ int -ctfs_access_readwrite(vnode_t *vp, int mode, int flags, cred_t *cr) +ctfs_access_readwrite( + vnode_t *vp, + int mode, + int flags, + cred_t *cr, + caller_context_t *ct) { if (mode & VEXEC) return (EACCES); @@ -463,7 +484,12 @@ ctfs_access_readwrite(vnode_t *vp, int mode, int flags, cred_t *cr) */ /* ARGSUSED */ static int -ctfs_root_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +ctfs_root_getattr( + vnode_t *vp, + vattr_t *vap, + int flags, + cred_t *cr, + caller_context_t *ct) { vap->va_type = VDIR; vap->va_mode = 0555; diff --git a/usr/src/uts/common/fs/ctfs/ctfs_sym.c b/usr/src/uts/common/fs/ctfs/ctfs_sym.c index bf825ab36670..571f8903b823 100644 --- a/usr/src/uts/common/fs/ctfs/ctfs_sym.c +++ b/usr/src/uts/common/fs/ctfs/ctfs_sym.c @@ -81,7 +81,12 @@ ctfs_create_symnode(vnode_t *pvp, contract_t *ct) */ /* ARGSUSED */ static int -ctfs_sym_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +ctfs_sym_getattr( + vnode_t *vp, + vattr_t *vap, + int flags, + cred_t *cr, + caller_context_t *ct) { ctfs_symnode_t *symnode = vp->v_data; @@ -104,7 +109,7 @@ ctfs_sym_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) */ /* ARGSUSED */ int -ctfs_sym_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr) +ctfs_sym_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct) { ctfs_symnode_t *symnode = vp->v_data; @@ -117,7 +122,7 @@ ctfs_sym_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr) */ /* ARGSUSED */ static void -ctfs_sym_inactive(vnode_t *vp, cred_t *cr) +ctfs_sym_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { ctfs_symnode_t *symnode; diff --git a/usr/src/uts/common/fs/ctfs/ctfs_tdir.c b/usr/src/uts/common/fs/ctfs/ctfs_tdir.c index 2d47c7233920..fd3c229c0f4b 100644 --- a/usr/src/uts/common/fs/ctfs/ctfs_tdir.c +++ b/usr/src/uts/common/fs/ctfs/ctfs_tdir.c @@ -58,7 +58,8 @@ static gfs_dirent_t ctfs_tdir_dirents[] = { static int ctfs_tdir_do_readdir(vnode_t *, struct dirent64 *, int *, offset_t *, offset_t *, void *); -static int ctfs_tdir_do_lookup(vnode_t *, const char *, vnode_t **, ino64_t *); +static int ctfs_tdir_do_lookup(vnode_t *, const char *, vnode_t **, ino64_t *, + cred_t *); static ino64_t ctfs_tdir_do_inode(vnode_t *, int); /* @@ -77,7 +78,12 @@ ctfs_create_tdirnode(vnode_t *pvp) */ /* ARGSUSED */ static int -ctfs_tdir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +ctfs_tdir_getattr( + vnode_t *vp, + vattr_t *vap, + int flags, + cred_t *cr, + caller_context_t *ct) { vap->va_type = VDIR; vap->va_mode = 0555; @@ -124,8 +130,10 @@ ctfs_tdir_do_readdir(vnode_t *vp, struct dirent64 *dp, int *eofp, return (0); } +/* ARGSUSED */ static int -ctfs_tdir_do_lookup(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop) +ctfs_tdir_do_lookup(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop, + cred_t *cr) { int i; contract_t *ct; diff --git a/usr/src/uts/common/fs/ctfs/ctfs_tmpl.c b/usr/src/uts/common/fs/ctfs/ctfs_tmpl.c index d99b8f56e84a..1786d1d79f14 100644 --- a/usr/src/uts/common/fs/ctfs/ctfs_tmpl.c +++ b/usr/src/uts/common/fs/ctfs/ctfs_tmpl.c @@ -75,7 +75,7 @@ ctfs_create_tmplnode(vnode_t *pvp) */ /* ARGSUSED */ static int -ctfs_tmpl_open(vnode_t **vpp, int flag, cred_t *cr) +ctfs_tmpl_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { if (flag != (FREAD | FWRITE | FOFFMAX)) return (EINVAL); @@ -88,7 +88,12 @@ ctfs_tmpl_open(vnode_t **vpp, int flag, cred_t *cr) */ /* ARGSUSED */ static int -ctfs_tmpl_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +ctfs_tmpl_getattr( + vnode_t *vp, + vattr_t *vap, + int flags, + cred_t *cr, + caller_context_t *ct) { vap->va_type = VREG; vap->va_mode = 0666; @@ -109,8 +114,14 @@ ctfs_tmpl_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) */ /* ARGSUSED */ static int -ctfs_tmpl_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, - int *rvalp) +ctfs_tmpl_ioctl( + vnode_t *vp, + int cmd, + intptr_t arg, + int flag, + cred_t *cr, + int *rvalp, + caller_context_t *ct) { ctfs_tmplnode_t *tmplnode = vp->v_data; ct_param_t param; @@ -158,7 +169,7 @@ ctfs_tmpl_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, */ /* ARGSUSED */ static void -ctfs_tmpl_inactive(vnode_t *vp, cred_t *cr) +ctfs_tmpl_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { ctfs_tmplnode_t *tmplnode; diff --git a/usr/src/uts/common/fs/dev/sdev_profile.c b/usr/src/uts/common/fs/dev/sdev_profile.c index 290cce18eef4..1756ac71156c 100644 --- a/usr/src/uts/common/fs/dev/sdev_profile.c +++ b/usr/src/uts/common/fs/dev/sdev_profile.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -75,13 +75,14 @@ prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv, /* get attribute from shadow, if present; else get default */ advp = dir->sdev_attrvp; - if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred) == 0) { - (void) VOP_GETATTR(*avpp, vap, 0, kcred); + if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred, + NULL, NULL, NULL) == 0) { + (void) VOP_GETATTR(*avpp, vap, 0, kcred, NULL); } else if (gdv == NULL || gdv->v_type == VDIR) { /* always create shadow directory */ *vap = sdev_vattr_dir; - if (advp && VOP_MKDIR(advp, name, - &sdev_vattr_dir, avpp, kcred) != 0) { + if (advp && VOP_MKDIR(advp, name, &sdev_vattr_dir, + avpp, kcred, NULL, 0, NULL) != 0) { *avpp = NULLVP; sdcmn_err10(("prof_getattr: failed to create " "shadow directory %s/%s\n", dir->sdev_path, name)); @@ -95,7 +96,7 @@ prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv, * attr for device nodes. */ struct vnode *rvp; - if (VOP_REALVP(gdv, &rvp) != 0) + if (VOP_REALVP(gdv, &rvp, NULL) != 0) rvp = gdv; devfs_get_defattr(rvp, vap, no_fs_perm); *avpp = NULLVP; @@ -209,7 +210,7 @@ prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp) /* find corresponding dir node in global dev */ if (gdir) { error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir, - NULL, 0, NULL, kcred); + NULL, 0, NULL, kcred, NULL, NULL, NULL); if (error == 0) { *gdirp = VTOSDEV(gnewdir); } else { /* it's ok if there no global dir */ @@ -390,7 +391,8 @@ is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir) struct vnode *gvp; struct sdev_node *gdir = dir->sdev_origin; - if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred) != 0) + if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred, + NULL, NULL, NULL) != 0) return (0); if (gvp->v_type != VDIR) { @@ -502,7 +504,7 @@ walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *)) iov.iov_base = (char *)dbuf; iov.iov_len = dlen; (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL); - error = VOP_READDIR(dvp, &uio, kcred, &eof); + error = VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0); VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL); dbuflen = dlen - uio.uio_resid; diff --git a/usr/src/uts/common/fs/dev/sdev_ptsops.c b/usr/src/uts/common/fs/dev/sdev_ptsops.c index 4ee3a5285718..f6d1fda64e60 100644 --- a/usr/src/uts/common/fs/dev/sdev_ptsops.c +++ b/usr/src/uts/common/fs/dev/sdev_ptsops.c @@ -294,7 +294,8 @@ devpts_prunedir(struct sdev_node *ddv) /*ARGSUSED3*/ static int devpts_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, - struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred) + struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, + caller_context_t *ct, int *direntflags, pathname_t *realpnp) { struct sdev_node *sdvp = VTOSDEV(dvp); struct sdev_node *dv; @@ -308,7 +309,7 @@ devpts_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, switch ((*vpp)->v_type) { case VCHR: dv = VTOSDEV(VTOS(*vpp)->s_realvp); - ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp) == ENOSYS); + ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS); break; case VDIR: dv = VTOSDEV(*vpp); @@ -333,21 +334,23 @@ devpts_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, /*ARGSUSED2*/ static int devpts_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, - int mode, struct vnode **vpp, struct cred *cred, int flag) + int mode, struct vnode **vpp, struct cred *cred, int flag, + caller_context_t *ct, vsecattr_t *vsecp) { int error; struct vnode *vp; *vpp = NULL; - error = devpts_lookup(dvp, nm, &vp, NULL, 0, NULL, cred); + error = devpts_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, + NULL); if (error == 0) { if (excl == EXCL) error = EEXIST; else if (vp->v_type == VDIR && (mode & VWRITE)) error = EISDIR; else - error = VOP_ACCESS(vp, mode, 0, cred); + error = VOP_ACCESS(vp, mode, 0, cred, ct); if (error) { VN_RELE(vp); @@ -365,9 +368,10 @@ devpts_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, * A /dev/pts entry will be created only after the first lookup of the slave * device succeeds. */ +/*ARGSUSED4*/ static int devpts_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, - int *eofp) + int *eofp, caller_context_t *ct, int flags) { struct sdev_node *sdvp = VTOSDEV(dvp); if (uiop->uio_offset == 0) { diff --git a/usr/src/uts/common/fs/dev/sdev_subr.c b/usr/src/uts/common/fs/dev/sdev_subr.c index d6d92c2621be..1075391d17ce 100644 --- a/usr/src/uts/common/fs/dev/sdev_subr.c +++ b/usr/src/uts/common/fs/dev/sdev_subr.c @@ -715,7 +715,7 @@ sdev_getlink(struct vnode *linkvp, char **link) uio.uio_segflg = UIO_SYSSPACE; uio.uio_llimit = MAXOFFSET_T; - err = VOP_READLINK(linkvp, &uio, kcred); + err = VOP_READLINK(linkvp, &uio, kcred, NULL); if (err) { cmn_err(CE_WARN, "readlink %s failed in dev\n", buf); kmem_free(buf, MAXPATHLEN); @@ -1155,7 +1155,7 @@ sdev_rnmnode(struct sdev_node *oddv, struct sdev_node *odv, timestruc_t now; vattr.va_mask = AT_MODE|AT_UID|AT_GID; - error = VOP_GETATTR(ovp, &vattr, 0, cred); + error = VOP_GETATTR(ovp, &vattr, 0, cred, NULL); if (error) return (error); @@ -1227,7 +1227,7 @@ sdev_rnmnode(struct sdev_node *oddv, struct sdev_node *odv, (void) sdev_dirdelete(nddv, *ndvp); *ndvp = NULL; error = VOP_RMDIR(nddv->sdev_attrvp, nnm, - nddv->sdev_attrvp, cred); + nddv->sdev_attrvp, cred, NULL, 0); if (error) goto err_out; } else { @@ -1249,7 +1249,7 @@ sdev_rnmnode(struct sdev_node *oddv, struct sdev_node *odv, *ndvp = NULL; if (bkstore) { error = VOP_REMOVE(nddv->sdev_attrvp, - nnm, cred); + nnm, cred, NULL, 0); if (error) goto err_out; } @@ -1419,7 +1419,7 @@ devname_find_by_devpath(char *devpath, struct vattr *vattr) } if (vattr) - (void) VOP_GETATTR(vp, vattr, 0, kcred); + (void) VOP_GETATTR(vp, vattr, 0, kcred, NULL); return (vp); } @@ -1447,7 +1447,7 @@ devname_configure_by_path(char *physpath, struct vattr *vattr) } if (vattr) - (void) VOP_GETATTR(vp, vattr, 0, kcred); + (void) VOP_GETATTR(vp, vattr, 0, kcred, NULL); return (vp); } @@ -1462,7 +1462,8 @@ devname_backstore_lookup(struct sdev_node *ddv, char *nm, struct vnode **rvp) ASSERT(rdvp); - rval = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, kcred); + rval = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, kcred, NULL, NULL, + NULL); return (rval); } @@ -1507,7 +1508,7 @@ sdev_filldir_from_store(struct sdev_node *ddv, int dlen, struct cred *cred) iov.iov_base = (char *)dbuf; iov.iov_len = dlen; (void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL); - error = VOP_READDIR(dirvp, &uio, kcred, &eof); + error = VOP_READDIR(dirvp, &uio, kcred, &eof, NULL, 0); VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL); dbuflen = dlen - uio.uio_resid; @@ -1552,7 +1553,7 @@ sdev_filldir_from_store(struct sdev_node *ddv, int dlen, struct cred *cred) continue; vattr.va_mask = AT_MODE|AT_UID|AT_GID; - error = VOP_GETATTR(vp, &vattr, 0, cred); + error = VOP_GETATTR(vp, &vattr, 0, cred, NULL); if (error) continue; @@ -1644,9 +1645,10 @@ sdev_shadow_node(struct sdev_node *dv, struct cred *cred) lookup: /* try to find it in the backing store */ - error = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, cred); + error = VOP_LOOKUP(rdvp, nm, rvp, NULL, 0, NULL, cred, NULL, NULL, + NULL); if (error == 0) { - if (VOP_REALVP(*rvp, &rrvp) == 0) { + if (VOP_REALVP(*rvp, &rrvp, NULL) == 0) { VN_HOLD(rrvp); VN_RELE(*rvp); *rvp = rrvp; @@ -1665,7 +1667,7 @@ sdev_shadow_node(struct sdev_node *dv, struct cred *cred) vap->va_mask |= AT_TYPE|AT_MODE; switch (vap->va_type) { case VDIR: - error = VOP_MKDIR(rdvp, nm, vap, rvp, cred); + error = VOP_MKDIR(rdvp, nm, vap, rvp, cred, NULL, 0, NULL); sdcmn_err9(("sdev_shadow_node: mkdir vp %p error %d\n", (void *)(*rvp), error)); break; @@ -1674,7 +1676,7 @@ sdev_shadow_node(struct sdev_node *dv, struct cred *cred) case VREG: case VDOOR: error = VOP_CREATE(rdvp, nm, vap, NONEXCL, VREAD|VWRITE, - rvp, cred, 0); + rvp, cred, 0, NULL, NULL); sdcmn_err9(("sdev_shadow_node: create vp %p, error %d\n", (void *)(*rvp), error)); if (!error) @@ -1682,7 +1684,8 @@ sdev_shadow_node(struct sdev_node *dv, struct cred *cred) break; case VLNK: ASSERT(dv->sdev_symlink); - error = VOP_SYMLINK(rdvp, nm, vap, dv->sdev_symlink, cred); + error = VOP_SYMLINK(rdvp, nm, vap, dv->sdev_symlink, cred, + NULL, 0); sdcmn_err9(("sdev_shadow_node: create symlink error %d\n", error)); break; @@ -1770,7 +1773,7 @@ sdev_cache_update(struct sdev_node *ddv, struct sdev_node **dv, char *nm, } /* - * retrive the named entry from the directory cache + * retrieve the named entry from the directory cache */ struct sdev_node * sdev_cache_lookup(struct sdev_node *ddv, char *nm) @@ -2224,7 +2227,7 @@ devname_lookup_func(struct sdev_node *ddv, char *nm, struct vnode **vpp, "found attrvp %p for %s\n", (void *)rvp, nm)); vattr.va_mask = AT_MODE|AT_UID|AT_GID; - error = VOP_GETATTR(rvp, &vattr, 0, cred); + error = VOP_GETATTR(rvp, &vattr, 0, cred, NULL); if (error) { rw_exit(&ddv->sdev_contents); if (dv) @@ -2583,10 +2586,10 @@ sdev_cleandir(struct sdev_node *ddv, char *expr, uint_t flags) if (bkstore == 1) { error = VOP_REMOVE(ddv->sdev_attrvp, - bks_name, kcred); + bks_name, kcred, NULL, 0); } else if (bkstore == 2) { error = VOP_RMDIR(ddv->sdev_attrvp, - bks_name, ddv->sdev_attrvp, kcred); + bks_name, ddv->sdev_attrvp, kcred, NULL, 0); } /* do not propagate the backing store errors */ @@ -2953,7 +2956,8 @@ sdev_modctl_lookup(const char *path, vnode_t **r_vp) while (pn_pathleft(&pn)) { ASSERT(vp->v_type == VDIR); (void) pn_getcomponent(&pn, nm); - error = VOP_LOOKUP(vp, nm, &cvp, NULL, 0, NULL, kcred); + error = VOP_LOOKUP(vp, nm, &cvp, NULL, 0, NULL, kcred, NULL, + NULL, NULL); VN_RELE(vp); if (error) @@ -3065,7 +3069,7 @@ sdev_modctl_readdir(const char *dir, char ***dirlistp, iov.iov_len = dlen; (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); - error = VOP_READDIR(vp, &uio, kcred, &eof); + error = VOP_READDIR(vp, &uio, kcred, &eof, NULL, 0); VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); dbuflen = dlen - uio.uio_resid; @@ -3659,8 +3663,8 @@ devname_setattr_func(struct vnode *vp, struct vattr *vap, int flags, * sdev_attr was allocated in sdev_mknode */ rw_enter(&dv->sdev_contents, RW_WRITER); - error = secpolicy_vnode_setattr(cred, vp, vap, dv->sdev_attr, - flags, sdev_unlocked_access, dv); + error = secpolicy_vnode_setattr(cred, vp, vap, + dv->sdev_attr, flags, sdev_unlocked_access, dv); if (error) { rw_exit(&dv->sdev_contents); rw_exit(&parent->sdev_contents); diff --git a/usr/src/uts/common/fs/dev/sdev_vfsops.c b/usr/src/uts/common/fs/dev/sdev_vfsops.c index ca971fe30c4b..bde50de8a2a2 100644 --- a/usr/src/uts/common/fs/dev/sdev_vfsops.c +++ b/usr/src/uts/common/fs/dev/sdev_vfsops.c @@ -324,7 +324,7 @@ sdev_mount(struct vfs *vfsp, struct vnode *mvp, struct mounta *uap, /* get acl flavor from attribute dir */ if (VOP_PATHCONF(avp, _PC_ACL_ENABLED, &sdev_data->sdev_acl_flavor, - kcred) != 0 || sdev_data->sdev_acl_flavor == 0) + kcred, NULL) != 0 || sdev_data->sdev_acl_flavor == 0) sdev_data->sdev_acl_flavor = _ACL_ACLENT_ENABLED; args = NULL; /* so it won't be freed below */ diff --git a/usr/src/uts/common/fs/dev/sdev_vnops.c b/usr/src/uts/common/fs/dev/sdev_vnops.c index 1d086c168a46..de1d83e75ace 100644 --- a/usr/src/uts/common/fs/dev/sdev_vnops.c +++ b/usr/src/uts/common/fs/dev/sdev_vnops.c @@ -76,7 +76,7 @@ /*ARGSUSED*/ static int -sdev_open(struct vnode **vpp, int flag, struct cred *cred) +sdev_open(struct vnode **vpp, int flag, struct cred *cred, caller_context_t *ct) { struct sdev_node *dv = VTOSDEV(*vpp); struct sdev_node *ddv = dv->sdev_dotdot; @@ -98,7 +98,7 @@ sdev_open(struct vnode **vpp, int flag, struct cred *cred) rw_exit(&ddv->sdev_contents); return (ENOENT); } - error = VOP_OPEN(&(dv->sdev_attrvp), flag, cred); + error = VOP_OPEN(&(dv->sdev_attrvp), flag, cred, ct); rw_exit(&ddv->sdev_contents); return (error); } @@ -106,7 +106,7 @@ sdev_open(struct vnode **vpp, int flag, struct cred *cred) /*ARGSUSED1*/ static int sdev_close(struct vnode *vp, int flag, int count, - offset_t offset, struct cred *cred) + offset_t offset, struct cred *cred, caller_context_t *ct) { struct sdev_node *dv = VTOSDEV(vp); @@ -124,7 +124,7 @@ sdev_close(struct vnode *vp, int flag, int count, return (ENOTSUP); ASSERT(dv->sdev_attrvp); - return (VOP_CLOSE(dv->sdev_attrvp, flag, count, offset, cred)); + return (VOP_CLOSE(dv->sdev_attrvp, flag, count, offset, cred, ct)); } /*ARGSUSED*/ @@ -148,9 +148,9 @@ sdev_read(struct vnode *vp, struct uio *uio, int ioflag, struct cred *cred, ASSERT(RW_READ_HELD(&VTOSDEV(vp)->sdev_contents)); ASSERT(dv->sdev_attrvp); - (void) VOP_RWLOCK(dv->sdev_attrvp, 0, NULL); + (void) VOP_RWLOCK(dv->sdev_attrvp, 0, ct); error = VOP_READ(dv->sdev_attrvp, uio, ioflag, cred, ct); - VOP_RWUNLOCK(dv->sdev_attrvp, 0, NULL); + VOP_RWUNLOCK(dv->sdev_attrvp, 0, ct); return (error); } @@ -175,9 +175,9 @@ sdev_write(struct vnode *vp, struct uio *uio, int ioflag, struct cred *cred, ASSERT(dv->sdev_attrvp); - (void) VOP_RWLOCK(dv->sdev_attrvp, 1, NULL); + (void) VOP_RWLOCK(dv->sdev_attrvp, 1, ct); error = VOP_WRITE(dv->sdev_attrvp, uio, ioflag, cred, ct); - VOP_RWUNLOCK(dv->sdev_attrvp, 1, NULL); + VOP_RWUNLOCK(dv->sdev_attrvp, 1, ct); if (error == 0) { sdev_update_timestamps(dv->sdev_attrvp, kcred, AT_MTIME); @@ -188,7 +188,7 @@ sdev_write(struct vnode *vp, struct uio *uio, int ioflag, struct cred *cred, /*ARGSUSED*/ static int sdev_ioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, - struct cred *cred, int *rvalp) + struct cred *cred, int *rvalp, caller_context_t *ct) { struct sdev_node *dv = VTOSDEV(vp); @@ -200,11 +200,12 @@ sdev_ioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, return (EINVAL); ASSERT(dv->sdev_attrvp); - return (VOP_IOCTL(dv->sdev_attrvp, cmd, arg, flag, cred, rvalp)); + return (VOP_IOCTL(dv->sdev_attrvp, cmd, arg, flag, cred, rvalp, ct)); } static int -sdev_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cr) +sdev_getattr(struct vnode *vp, struct vattr *vap, int flags, + struct cred *cr, caller_context_t *ct) { int error = 0; struct sdev_node *dv = VTOSDEV(vp); @@ -229,7 +230,7 @@ sdev_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cr) */ if (dv->sdev_attrvp) { rw_exit(&parent->sdev_contents); - error = VOP_GETATTR(dv->sdev_attrvp, vap, flags, cr); + error = VOP_GETATTR(dv->sdev_attrvp, vap, flags, cr, ct); sdev_vattr_merge(dv, vap); } else if (dirops && (fn = dirops->devnops_getattr)) { sdev_vattr_merge(dv, vap); @@ -255,7 +256,7 @@ sdev_setattr(struct vnode *vp, struct vattr *vap, int flags, static int sdev_getsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, - struct cred *cr) + struct cred *cr, caller_context_t *ct) { int error; struct sdev_node *dv = VTOSDEV(vp); @@ -267,20 +268,20 @@ sdev_getsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, (vsap->vsa_mask & (VSA_ACLCNT | VSA_DFACLCNT))) || (SDEV_ACL_FLAVOR(vp) == _ACL_ACE_ENABLED && (vsap->vsa_mask & (VSA_ACECNT | VSA_ACE)))) - return (fs_fab_acl(vp, vsap, flags, cr)); + return (fs_fab_acl(vp, vsap, flags, cr, ct)); return (ENOSYS); } - (void) VOP_RWLOCK(avp, 1, NULL); - error = VOP_GETSECATTR(avp, vsap, flags, cr); - VOP_RWUNLOCK(avp, 1, NULL); + (void) VOP_RWLOCK(avp, 1, ct); + error = VOP_GETSECATTR(avp, vsap, flags, cr, ct); + VOP_RWUNLOCK(avp, 1, ct); return (error); } static int sdev_setsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, - struct cred *cr) + struct cred *cr, caller_context_t *ct) { int error; struct sdev_node *dv = VTOSDEV(vp); @@ -301,7 +302,7 @@ sdev_setsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, */ ASSERT(RW_WRITE_HELD(&dv->sdev_contents)); sdev_vattr_merge(dv, dv->sdev_attr); - error = sdev_shadow_node(dv, cr); + error = sdev_shadow_node(dv, cr); if (error) { return (fs_nosys()); } @@ -316,9 +317,9 @@ sdev_setsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, } ASSERT(avp); - (void) VOP_RWLOCK(avp, V_WRITELOCK_TRUE, NULL); - error = VOP_SETSECATTR(avp, vsap, flags, cr); - VOP_RWUNLOCK(avp, V_WRITELOCK_TRUE, NULL); + (void) VOP_RWLOCK(avp, V_WRITELOCK_TRUE, ct); + error = VOP_SETSECATTR(avp, vsap, flags, cr, ct); + VOP_RWUNLOCK(avp, V_WRITELOCK_TRUE, ct); return (error); } @@ -343,7 +344,8 @@ sdev_unlocked_access(void *vdv, int mode, struct cred *cr) } static int -sdev_access(struct vnode *vp, int mode, int flags, struct cred *cr) +sdev_access(struct vnode *vp, int mode, int flags, struct cred *cr, + caller_context_t *ct) { struct sdev_node *dv = VTOSDEV(vp); int ret = 0; @@ -351,7 +353,7 @@ sdev_access(struct vnode *vp, int mode, int flags, struct cred *cr) ASSERT(dv->sdev_attr || dv->sdev_attrvp); if (dv->sdev_attrvp) { - ret = VOP_ACCESS(dv->sdev_attrvp, mode, flags, cr); + ret = VOP_ACCESS(dv->sdev_attrvp, mode, flags, cr, ct); } else if (dv->sdev_attr) { rw_enter(&dv->sdev_contents, RW_READER); ret = sdev_unlocked_access(dv, mode, cr); @@ -369,7 +371,8 @@ sdev_access(struct vnode *vp, int mode, int flags, struct cred *cr) /*ARGSUSED3*/ static int sdev_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, - struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred) + struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, + caller_context_t *ct, int *direntflags, pathname_t *realpnp) { struct sdev_node *parent; int error; @@ -378,7 +381,7 @@ sdev_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, ASSERT(parent); /* execute access is required to search the directory */ - if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred)) != 0) + if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) return (error); if (!SDEV_IS_GLOBAL(parent)) @@ -389,7 +392,8 @@ sdev_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, /*ARGSUSED2*/ static int sdev_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, - int mode, struct vnode **vpp, struct cred *cred, int flag) + int mode, struct vnode **vpp, struct cred *cred, int flag, + caller_context_t *ct, vsecattr_t *vsecp) { struct vnode *vp = NULL; struct vnode *avp; @@ -421,11 +425,12 @@ sdev_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, rw_exit(&parent->sdev_dotdot->sdev_contents); /* execute access is required to search the directory */ - if ((error = VOP_ACCESS(dvp, VEXEC|VWRITE, 0, cred)) != 0) + if ((error = VOP_ACCESS(dvp, VEXEC|VWRITE, 0, cred, ct)) != 0) return (error); /* check existing name */ - error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cred); +/* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ + error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL); /* name found */ if (error == 0) { @@ -436,7 +441,7 @@ sdev_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, /* allowing create/read-only an existing directory */ error = EISDIR; } else { - error = VOP_ACCESS(vp, mode, flag, cred); + error = VOP_ACCESS(vp, mode, flag, cred, ct); } if (error) { @@ -449,7 +454,7 @@ sdev_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, (vap->va_size == 0)) { ASSERT(parent->sdev_attrvp); error = VOP_CREATE(parent->sdev_attrvp, - nm, vap, excl, mode, &avp, cred, flag); + nm, vap, excl, mode, &avp, cred, flag, ct, vsecp); if (error) { VN_RELE(vp); @@ -505,7 +510,8 @@ sdev_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, } static int -sdev_remove(struct vnode *dvp, char *nm, struct cred *cred) +sdev_remove(struct vnode *dvp, char *nm, struct cred *cred, + caller_context_t *ct, int flags) { int error; struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); @@ -535,7 +541,7 @@ sdev_remove(struct vnode *dvp, char *nm, struct cred *cred) } /* execute access is required to search the directory */ - if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred)) != 0) { + if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) { rw_exit(&parent->sdev_contents); return (error); } @@ -556,7 +562,7 @@ sdev_remove(struct vnode *dvp, char *nm, struct cred *cred) } /* write access is required to remove an entry */ - if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred)) != 0) { + if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) { rw_exit(&parent->sdev_contents); VN_RELE(vp); return (error); @@ -601,7 +607,8 @@ sdev_remove(struct vnode *dvp, char *nm, struct cred *cred) */ if (bkstore) { ASSERT(parent->sdev_attrvp); - error = VOP_REMOVE(parent->sdev_attrvp, nm, cred); + error = VOP_REMOVE(parent->sdev_attrvp, nm, cred, + ct, flags); /* * do not report BUSY error * because the backing store ref count is released @@ -627,9 +634,10 @@ sdev_remove(struct vnode *dvp, char *nm, struct cred *cred) * - both oldnm and newnm are in the scope of /dev file system, * to simply the namespace management model. */ +/*ARGSUSED6*/ static int sdev_rename(struct vnode *odvp, char *onm, struct vnode *ndvp, char *nnm, - struct cred *cred) + struct cred *cred, caller_context_t *ct, int flags) { struct sdev_node *fromparent = NULL; struct vattr vattr; @@ -690,7 +698,9 @@ sdev_rename(struct vnode *odvp, char *onm, struct vnode *ndvp, char *nnm, mutex_enter(&sdev_lock); /* check existence of the source node */ - error = VOP_LOOKUP(odvp, onm, &ovp, NULL, 0, NULL, cred); +/* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ + error = VOP_LOOKUP(odvp, onm, &ovp, NULL, 0, NULL, cred, ct, + NULL, NULL); if (error) { sdcmn_err2(("sdev_rename: the source node %s exists\n", onm)); @@ -698,21 +708,23 @@ sdev_rename(struct vnode *odvp, char *onm, struct vnode *ndvp, char *nnm, return (error); } - if (VOP_REALVP(ovp, &realvp) == 0) { + if (VOP_REALVP(ovp, &realvp, ct) == 0) { VN_HOLD(realvp); VN_RELE(ovp); ovp = realvp; } /* check existence of destination */ - error = VOP_LOOKUP(ndvp, nnm, &nvp, NULL, 0, NULL, cred); +/* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ + error = VOP_LOOKUP(ndvp, nnm, &nvp, NULL, 0, NULL, cred, ct, + NULL, NULL); if (error && (error != ENOENT)) { mutex_exit(&sdev_lock); VN_RELE(ovp); return (error); } - if (nvp && (VOP_REALVP(nvp, &realvp) == 0)) { + if (nvp && (VOP_REALVP(nvp, &realvp, ct) == 0)) { VN_HOLD(realvp); VN_RELE(nvp); nvp = realvp; @@ -724,14 +736,14 @@ sdev_rename(struct vnode *odvp, char *onm, struct vnode *ndvp, char *nnm, */ if (odvp != ndvp) { vattr.va_mask = AT_FSID; - if (error = VOP_GETATTR(odvp, &vattr, 0, cred)) { + if (error = VOP_GETATTR(odvp, &vattr, 0, cred, ct)) { mutex_exit(&sdev_lock); VN_RELE(ovp); return (error); } fsid = vattr.va_fsid; vattr.va_mask = AT_FSID; - if (error = VOP_GETATTR(ndvp, &vattr, 0, cred)) { + if (error = VOP_GETATTR(ndvp, &vattr, 0, cred, ct)) { mutex_exit(&sdev_lock); VN_RELE(ovp); return (error); @@ -744,7 +756,7 @@ sdev_rename(struct vnode *odvp, char *onm, struct vnode *ndvp, char *nnm, } /* make sure the old entry can be deleted */ - error = VOP_ACCESS(odvp, VWRITE, 0, cred); + error = VOP_ACCESS(odvp, VWRITE, 0, cred, ct); if (error) { mutex_exit(&sdev_lock); VN_RELE(ovp); @@ -754,7 +766,7 @@ sdev_rename(struct vnode *odvp, char *onm, struct vnode *ndvp, char *nnm, /* make sure the destination allows creation */ samedir = (fromparent == toparent); if (!samedir) { - error = VOP_ACCESS(ndvp, VEXEC|VWRITE, 0, cred); + error = VOP_ACCESS(ndvp, VEXEC|VWRITE, 0, cred, ct); if (error) { mutex_exit(&sdev_lock); VN_RELE(ovp); @@ -856,11 +868,13 @@ sdev_rename(struct vnode *odvp, char *onm, struct vnode *ndvp, char *nnm, if (bkstore) { ASSERT(fromparent->sdev_attrvp); if (type != VDIR) { +/* XXXci - We may need to translate the C-I flags on VOP_REMOVE */ error = VOP_REMOVE(fromparent->sdev_attrvp, - onm, kcred); + onm, kcred, ct, 0); } else { +/* XXXci - We may need to translate the C-I flags on VOP_RMDIR */ error = VOP_RMDIR(fromparent->sdev_attrvp, - onm, fromparent->sdev_attrvp, kcred); + onm, fromparent->sdev_attrvp, kcred, ct, 0); } if (error) { @@ -882,9 +896,10 @@ sdev_rename(struct vnode *odvp, char *onm, struct vnode *ndvp, char *nnm, * tnm - path, e.g. /devices/... or /dev/... * lnm - dev_name */ +/*ARGSUSED6*/ static int sdev_symlink(struct vnode *dvp, char *lnm, struct vattr *tva, - char *tnm, struct cred *cred) + char *tnm, struct cred *cred, caller_context_t *ct, int flags) { int error; struct vnode *vp = NULL; @@ -907,11 +922,12 @@ sdev_symlink(struct vnode *dvp, char *lnm, struct vattr *tva, rw_exit(&parent->sdev_dotdot->sdev_contents); /* execute access is required to search a directory */ - if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred)) != 0) + if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) return (error); /* find existing name */ - error = VOP_LOOKUP(dvp, lnm, &vp, NULL, 0, NULL, cred); +/* XXXci - We may need to translate the C-I flags here */ + error = VOP_LOOKUP(dvp, lnm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL); if (error == 0) { ASSERT(vp); VN_RELE(vp); @@ -922,7 +938,7 @@ sdev_symlink(struct vnode *dvp, char *lnm, struct vattr *tva, return (error); /* write access is required to create a symlink */ - if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred)) != 0) + if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) return (error); /* put it into memory cache */ @@ -955,9 +971,10 @@ sdev_symlink(struct vnode *dvp, char *lnm, struct vattr *tva, return (0); } +/*ARGSUSED6*/ static int sdev_mkdir(struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp, - struct cred *cred) + struct cred *cred, caller_context_t *ct, int flags, vsecattr_t *vsecp) { int error; struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); @@ -979,12 +996,13 @@ sdev_mkdir(struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp, rw_exit(&parent->sdev_dotdot->sdev_contents); /* execute access is required to search the directory */ - if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred)) != 0) { + if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) { return (error); } /* find existing name */ - error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cred); +/* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ + error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL); if (error == 0) { VN_RELE(vp); return (EEXIST); @@ -993,7 +1011,7 @@ sdev_mkdir(struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp, return (error); /* require write access to create a directory */ - if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred)) != 0) { + if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) { return (error); } @@ -1030,7 +1048,8 @@ sdev_mkdir(struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp, */ /*ARGSUSED*/ static int -sdev_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred) +sdev_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred, + caller_context_t *ct, int flags) { int error = 0; struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); @@ -1053,7 +1072,7 @@ sdev_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred) rw_exit(&parent->sdev_dotdot->sdev_contents); /* execute access is required to search the directory */ - if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred)) != 0) + if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) return (error); /* check existing name */ @@ -1073,7 +1092,7 @@ sdev_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred) } /* write access is required to remove a directory */ - if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred)) != 0) { + if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) { rw_exit(&parent->sdev_contents); VN_RELE(vp); return (error); @@ -1132,7 +1151,7 @@ sdev_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred) if (SDEV_IS_PERSIST(parent)) { ASSERT(parent->sdev_attrvp); error = VOP_RMDIR(parent->sdev_attrvp, nm, - parent->sdev_attrvp, kcred); + parent->sdev_attrvp, kcred, ct, flags); sdcmn_err2(("sdev_rmdir: cleaning device %s is on" " disk error %d\n", parent->sdev_path, error)); } @@ -1148,7 +1167,8 @@ sdev_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred) * read the contents of a symbolic link */ static int -sdev_readlink(struct vnode *vp, struct uio *uiop, struct cred *cred) +sdev_readlink(struct vnode *vp, struct uio *uiop, struct cred *cred, + caller_context_t *ct) { struct sdev_node *dv; int error = 0; @@ -1159,7 +1179,7 @@ sdev_readlink(struct vnode *vp, struct uio *uiop, struct cred *cred) if (dv->sdev_attrvp) { /* non-NULL attrvp implys a persisted node at READY state */ - return (VOP_READLINK(dv->sdev_attrvp, uiop, cred)); + return (VOP_READLINK(dv->sdev_attrvp, uiop, cred, ct)); } else if (dv->sdev_symlink != NULL) { /* memory nodes, e.g. local nodes */ rw_enter(&dv->sdev_contents, RW_READER); @@ -1173,14 +1193,16 @@ sdev_readlink(struct vnode *vp, struct uio *uiop, struct cred *cred) return (ENOENT); } +/*ARGSUSED4*/ static int -sdev_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, int *eofp) +sdev_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, int *eofp, + caller_context_t *ct, int flags) { struct sdev_node *parent = VTOSDEV(dvp); int error; /* execute access is required to search the directory */ - if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred)) != 0) + if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) return (error); ASSERT(parent); @@ -1191,7 +1213,7 @@ sdev_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, int *eofp) /*ARGSUSED1*/ static void -sdev_inactive(struct vnode *vp, struct cred *cred) +sdev_inactive(struct vnode *vp, struct cred *cred, caller_context_t *ct) { int clean; struct sdev_node *dv = VTOSDEV(vp); @@ -1250,8 +1272,9 @@ sdev_inactive(struct vnode *vp, struct cred *cred) rw_exit(&ddv->sdev_contents); } +/*ARGSUSED2*/ static int -sdev_fid(struct vnode *vp, struct fid *fidp) +sdev_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct) { struct sdev_node *dv = VTOSDEV(vp); struct sdev_fid *sdev_fid; @@ -1293,7 +1316,8 @@ sdev_rwunlock(struct vnode *vp, int write_flag, caller_context_t *ctp) /*ARGSUSED1*/ static int -sdev_seek(struct vnode *vp, offset_t ooff, offset_t *noffp) +sdev_seek(struct vnode *vp, offset_t ooff, offset_t *noffp, + caller_context_t *ct) { struct vnode *attrvp = VTOSDEV(vp)->sdev_attrvp; @@ -1301,16 +1325,17 @@ sdev_seek(struct vnode *vp, offset_t ooff, offset_t *noffp) vp->v_type != VBLK && vp->v_type != VLNK); if (vp->v_type == VDIR) - return (fs_seek(vp, ooff, noffp)); + return (fs_seek(vp, ooff, noffp, ct)); ASSERT(attrvp); - return (VOP_SEEK(attrvp, ooff, noffp)); + return (VOP_SEEK(attrvp, ooff, noffp, ct)); } /*ARGSUSED1*/ static int sdev_frlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag, - offset_t offset, struct flk_callback *flk_cbp, struct cred *cr) + offset_t offset, struct flk_callback *flk_cbp, struct cred *cr, + caller_context_t *ct) { int error; struct sdev_node *dv = VTOSDEV(vp); @@ -1318,23 +1343,25 @@ sdev_frlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag, ASSERT(dv); ASSERT(dv->sdev_attrvp); error = VOP_FRLOCK(dv->sdev_attrvp, cmd, bfp, flag, offset, - flk_cbp, cr); + flk_cbp, cr, ct); return (error); } static int -sdev_setfl(struct vnode *vp, int oflags, int nflags, cred_t *cr) +sdev_setfl(struct vnode *vp, int oflags, int nflags, cred_t *cr, + caller_context_t *ct) { struct sdev_node *dv = VTOSDEV(vp); ASSERT(dv); ASSERT(dv->sdev_attrvp); - return (VOP_SETFL(dv->sdev_attrvp, oflags, nflags, cr)); + return (VOP_SETFL(dv->sdev_attrvp, oflags, nflags, cr, ct)); } static int -sdev_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) +sdev_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, + caller_context_t *ct) { switch (cmd) { case _PC_ACL_ENABLED: @@ -1342,7 +1369,7 @@ sdev_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) return (0); } - return (fs_pathconf(vp, cmd, valp, cr)); + return (fs_pathconf(vp, cmd, valp, cr, ct)); } vnodeops_t *sdev_vnodeops; diff --git a/usr/src/uts/common/fs/devfs/devfs_subr.c b/usr/src/uts/common/fs/devfs/devfs_subr.c index 4aa95bc321ad..eeaac944307a 100644 --- a/usr/src/uts/common/fs/devfs/devfs_subr.c +++ b/usr/src/uts/common/fs/devfs/devfs_subr.c @@ -694,10 +694,11 @@ dv_shadow_node( create_tried = 0; lookup: if (rdvp && (dv->dv_flags & DV_NO_FSPERM) == 0) { - error = VOP_LOOKUP(rdvp, nm, &rvp, pnp, LOOKUP_DIR, rdir, cred); + error = VOP_LOOKUP(rdvp, nm, &rvp, pnp, LOOKUP_DIR, rdir, cred, + NULL, NULL, NULL); /* factor out the snode since we only want the attribute node */ - if ((error == 0) && (VOP_REALVP(rvp, &rrvp) == 0)) { + if ((error == 0) && (VOP_REALVP(rvp, &rrvp, NULL) == 0)) { VN_HOLD(rrvp); VN_RELE(rvp); rvp = rrvp; @@ -760,7 +761,8 @@ dv_shadow_node( if ((error == ENOENT) && !create_tried) { switch (vp->v_type) { case VDIR: - error = VOP_MKDIR(rdvp, nm, &vattr, &rvp, kcred); + error = VOP_MKDIR(rdvp, nm, &vattr, &rvp, kcred, + NULL, 0, NULL); dsysdebug(error, ("vop_mkdir %s %s %d\n", VTODV(dvp)->dv_name, nm, error)); create_tried = 1; @@ -773,7 +775,7 @@ dv_shadow_node( */ if (flags & DV_SHADOW_CREATE) { error = VOP_CREATE(rdvp, nm, &vattr, NONEXCL, - VREAD|VWRITE, &rvp, kcred, 0); + VREAD|VWRITE, &rvp, kcred, 0, NULL, NULL); dsysdebug(error, ("vop_create %s %s %d\n", VTODV(dvp)->dv_name, nm, error)); create_tried = 1; @@ -1566,7 +1568,7 @@ devfs_remdrv_rmdir(vnode_t *dirvp, const char *dir, vnode_t *rvp) iov.iov_len = dlen; (void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL); - error = VOP_READDIR(dirvp, &uio, kcred, &eof); + error = VOP_READDIR(dirvp, &uio, kcred, &eof, NULL, 0); VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL); dbuflen = dlen - uio.uio_resid; @@ -1582,8 +1584,8 @@ devfs_remdrv_rmdir(vnode_t *dirvp, const char *dir, vnode_t *rvp) if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0) continue; - error = VOP_LOOKUP(dirvp, - nm, &vp, NULL, 0, NULL, kcred); + error = VOP_LOOKUP(dirvp, nm, + &vp, NULL, 0, NULL, kcred, NULL, NULL, NULL); dsysdebug(error, ("rem_drv %s/%s lookup (%d)\n", @@ -1599,13 +1601,14 @@ devfs_remdrv_rmdir(vnode_t *dirvp, const char *dir, vnode_t *rvp) error = devfs_remdrv_rmdir(vp, nm, rvp); if (error == 0) { error = VOP_RMDIR(dirvp, - (char *)nm, rvp, kcred); + (char *)nm, rvp, kcred, NULL, 0); dsysdebug(error, ("rem_drv %s/%s rmdir (%d)\n", dir, nm, error)); } } else { - error = VOP_REMOVE(dirvp, (char *)nm, kcred); + error = VOP_REMOVE(dirvp, (char *)nm, kcred, + NULL, 0); dsysdebug(error, ("rem_drv %s/%s remove (%d)\n", dir, nm, error)); @@ -1663,7 +1666,8 @@ devfs_remdrv_cleanup(const char *dir, const char *nodename) ASSERT(dirvp->v_type == VDIR); (void) pn_getcomponent(&pn, nm); ASSERT((strcmp(nm, ".") != 0) && (strcmp(nm, "..") != 0)); - error = VOP_LOOKUP(dirvp, nm, &vp, NULL, 0, rvp, kcred); + error = VOP_LOOKUP(dirvp, nm, &vp, NULL, 0, rvp, kcred, + NULL, NULL, NULL); if (error) { dcmn_err5(("remdrv_cleanup %s lookup error %d\n", nm, error)); @@ -1704,7 +1708,7 @@ devfs_remdrv_cleanup(const char *dir, const char *nodename) iov.iov_len = dlen; (void) VOP_RWLOCK(dirvp, V_WRITELOCK_FALSE, NULL); - error = VOP_READDIR(dirvp, &uio, kcred, &eof); + error = VOP_READDIR(dirvp, &uio, kcred, &eof, NULL, 0); VOP_RWUNLOCK(dirvp, V_WRITELOCK_FALSE, NULL); dbuflen = dlen - uio.uio_resid; @@ -1724,7 +1728,7 @@ devfs_remdrv_cleanup(const char *dir, const char *nodename) continue; error = VOP_LOOKUP(dirvp, nm, &vp, - NULL, 0, NULL, kcred); + NULL, 0, NULL, kcred, NULL, NULL, NULL); dsysdebug(error, ("rem_drv %s/%s lookup (%d)\n", @@ -1739,14 +1743,15 @@ devfs_remdrv_cleanup(const char *dir, const char *nodename) if (vp->v_type == VDIR) { error = devfs_remdrv_rmdir(vp, nm, rvp); if (error == 0) { - error = VOP_RMDIR(dirvp, - (char *)nm, rvp, kcred); + error = VOP_RMDIR(dirvp, (char *)nm, + rvp, kcred, NULL, 0); dsysdebug(error, ("rem_drv %s/%s rmdir (%d)\n", dir, nm, error)); } } else { - error = VOP_REMOVE(dirvp, (char *)nm, kcred); + error = VOP_REMOVE(dirvp, (char *)nm, kcred, + NULL, 0); dsysdebug(error, ("rem_drv %s/%s remove (%d)\n", dir, nm, error)); diff --git a/usr/src/uts/common/fs/devfs/devfs_vfsops.c b/usr/src/uts/common/fs/devfs/devfs_vfsops.c index ddd006ab7075..c6a6a2a896c9 100644 --- a/usr/src/uts/common/fs/devfs/devfs_vfsops.c +++ b/usr/src/uts/common/fs/devfs/devfs_vfsops.c @@ -556,7 +556,7 @@ devfs_devpolicy(vnode_t *vp, devplcy_t **dpp) if (devfs_mntinfo == NULL) return (rval); - if (VOP_REALVP(vp, &rvp) == 0 && vn_matchops(rvp, dv_vnodeops)) { + if (VOP_REALVP(vp, &rvp, NULL) == 0 && vn_matchops(rvp, dv_vnodeops)) { dvp = VTODV(rvp); rw_enter(&dvp->dv_contents, RW_READER); if (dvp->dv_priv) { diff --git a/usr/src/uts/common/fs/devfs/devfs_vnops.c b/usr/src/uts/common/fs/devfs/devfs_vnops.c index c55295b0f636..d45992900084 100644 --- a/usr/src/uts/common/fs/devfs/devfs_vnops.c +++ b/usr/src/uts/common/fs/devfs/devfs_vnops.c @@ -72,7 +72,8 @@ extern dev_t rconsdev; */ /*ARGSUSED*/ static int -devfs_open(struct vnode **vpp, int flag, struct cred *cred) +devfs_open(struct vnode **vpp, int flag, struct cred *cred, + caller_context_t *ct) { struct dv_node *dv = VTODV(*vpp); @@ -88,7 +89,7 @@ devfs_open(struct vnode **vpp, int flag, struct cred *cred) /*ARGSUSED1*/ static int devfs_close(struct vnode *vp, int flag, int count, - offset_t offset, struct cred *cred) + offset_t offset, struct cred *cred, caller_context_t *ct) { struct dv_node *dv = VTODV(vp); @@ -137,7 +138,7 @@ devfs_write(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cred, /*ARGSUSED*/ static int devfs_ioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, - struct cred *cred, int *rvalp) + struct cred *cred, int *rvalp, caller_context_t *ct) { dcmn_err2(("devfs_ioctl %s\n", VTODV(vp)->dv_name)); ASSERT(vp->v_type == VDIR); @@ -159,7 +160,8 @@ devfs_ioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, * memory based attributes. */ static int -devfs_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cr) +devfs_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cr, + caller_context_t *ct) { struct dv_node *dv = VTODV(vp); int error = 0; @@ -189,7 +191,7 @@ devfs_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cr) vap->va_mask = mask; } else { /* obtain from attribute store and merge */ - error = VOP_GETATTR(dv->dv_attrvp, vap, flags, cr); + error = VOP_GETATTR(dv->dv_attrvp, vap, flags, cr, ct); dsysdebug(error, ("vop_getattr %s %d\n", dv->dv_name, error)); dv_vattr_merge(dv, vap); } @@ -238,8 +240,8 @@ devfs_setattr_dir( again: if (dv->dv_attr) { - error = secpolicy_vnode_setattr(cr, vp, vap, dv->dv_attr, - flags, devfs_unlocked_access, dv); + error = secpolicy_vnode_setattr(cr, vp, vap, + dv->dv_attr, flags, devfs_unlocked_access, dv); if (error) goto out; @@ -296,8 +298,8 @@ again: if (dv->dv_attr) { * read/write. */ vattr = dv_vattr_dir; - if (VOP_GETATTR(dv->dv_attrvp, &vattr, - flags, cr) == 0) { + if (VOP_GETATTR(dv->dv_attrvp, + &vattr, flags, cr, NULL) == 0) { dv->dv_attr = kmem_alloc( sizeof (struct vattr), KM_SLEEP); *dv->dv_attr = vattr; @@ -456,7 +458,7 @@ devfs_setattr( ASSERT(dv->dv_attrvp); ASSERT(vp->v_type != VDIR); *vattrp = dv_vattr_file; - error = VOP_GETATTR(dv->dv_attrvp, vattrp, 0, cr); + error = VOP_GETATTR(dv->dv_attrvp, vattrp, 0, cr, ct); dsysdebug(error, ("vop_getattr %s %d\n", dv->dv_name, error)); if (error) @@ -514,7 +516,7 @@ devfs_setattr( ddv = dv->dv_dotdot; ASSERT(ddv->dv_attrvp); error = VOP_REMOVE(ddv->dv_attrvp, - dv->dv_name, cr); + dv->dv_name, cr, ct, 0); dsysdebug(error, ("vop_remove %s %s %d\n", ddv->dv_name, dv->dv_name, error)); @@ -573,7 +575,8 @@ devfs_setattr( } static int -devfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) +devfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, + caller_context_t *ct) { switch (cmd) { case _PC_ACL_ENABLED: @@ -587,11 +590,11 @@ devfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) */ ASSERT(dvroot); ASSERT(dvroot->dv_attrvp); - return (VOP_PATHCONF(dvroot->dv_attrvp, cmd, valp, cr)); + return (VOP_PATHCONF(dvroot->dv_attrvp, cmd, valp, cr, ct)); /*NOTREACHED*/ } - return (fs_pathconf(vp, cmd, valp, cr)); + return (fs_pathconf(vp, cmd, valp, cr, ct)); } /* @@ -599,7 +602,7 @@ devfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) */ static int devfs_getsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, - struct cred *cr) + struct cred *cr, caller_context_t *ct) { dvnode_t *dv = VTODV(vp); struct vnode *avp; @@ -614,12 +617,12 @@ devfs_getsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, /* fabricate the acl */ if (avp == NULL) { - error = fs_fab_acl(vp, vsap, flags, cr); + error = fs_fab_acl(vp, vsap, flags, cr, ct); rw_exit(&dv->dv_contents); return (error); } - error = VOP_GETSECATTR(avp, vsap, flags, cr); + error = VOP_GETSECATTR(avp, vsap, flags, cr, ct); dsysdebug(error, ("vop_getsecattr %s %d\n", VTODV(vp)->dv_name, error)); rw_exit(&dv->dv_contents); return (error); @@ -633,7 +636,7 @@ devfs_getsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, */ static int devfs_setsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, - struct cred *cr) + struct cred *cr, caller_context_t *ct) { dvnode_t *dv = VTODV(vp); struct vnode *avp; @@ -672,7 +675,7 @@ devfs_setsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, * store before forwarding the ACL. */ (void) VOP_RWLOCK(avp, V_WRITELOCK_TRUE, NULL); - error = VOP_SETSECATTR(avp, vsap, flags, cr); + error = VOP_SETSECATTR(avp, vsap, flags, cr, ct); dsysdebug(error, ("vop_setsecattr %s %d\n", VTODV(vp)->dv_name, error)); VOP_RWUNLOCK(avp, V_WRITELOCK_TRUE, NULL); @@ -717,7 +720,8 @@ devfs_unlocked_access(void *vdv, int mode, struct cred *cr) } static int -devfs_access(struct vnode *vp, int mode, int flags, struct cred *cr) +devfs_access(struct vnode *vp, int mode, int flags, struct cred *cr, + caller_context_t *ct) { struct dv_node *dv = VTODV(vp); int res; @@ -739,7 +743,7 @@ devfs_access(struct vnode *vp, int mode, int flags, struct cred *cr) } rw_exit(&dv->dv_contents); } - return (VOP_ACCESS(dv->dv_attrvp, mode, flags, cr)); + return (VOP_ACCESS(dv->dv_attrvp, mode, flags, cr, ct)); } /* @@ -801,7 +805,8 @@ devfs_access(struct vnode *vp, int mode, int flags, struct cred *cr) /*ARGSUSED3*/ static int devfs_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, - struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred) + struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, + caller_context_t *ct, int *direntflags, pathname_t *realpnp) { ASSERT(dvp->v_type == VDIR); dcmn_err2(("devfs_lookup: %s\n", nm)); @@ -820,7 +825,8 @@ devfs_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, /*ARGSUSED2*/ static int devfs_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, - int mode, struct vnode **vpp, struct cred *cred, int flag) + int mode, struct vnode **vpp, struct cred *cred, int flag, + caller_context_t *ct, vsecattr_t *vsecp) { int error; struct vnode *vp; @@ -833,7 +839,7 @@ devfs_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, else if (vp->v_type == VDIR && (mode & VWRITE)) error = EISDIR; else - error = VOP_ACCESS(vp, mode, 0, cred); + error = VOP_ACCESS(vp, mode, 0, cred, ct); if (error) { VN_RELE(vp); @@ -850,8 +856,10 @@ devfs_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, * Otherwise, simply return cached dv_node's. Hotplug code always call * devfs_clean() to invalid the dv_node cache. */ +/*ARGSUSED5*/ static int -devfs_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, int *eofp) +devfs_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, int *eofp, + caller_context_t *ct, int flags) { struct dv_node *ddv, *dv; struct dirent64 *de, *bufp; @@ -994,7 +1002,7 @@ full: dcmn_err3(("devfs_readdir: moving %lu bytes: " va.va_mask = AT_ATIME; gethrestime(&va.va_atime); rw_exit(&ddv->dv_contents); - (void) devfs_setattr(dvp, &va, 0, cred, NULL); + (void) devfs_setattr(dvp, &va, 0, cred, ct); rw_enter(&ddv->dv_contents, RW_READER); } @@ -1004,7 +1012,8 @@ full: dcmn_err3(("devfs_readdir: moving %lu bytes: " /*ARGSUSED*/ static int -devfs_fsync(struct vnode *vp, int syncflag, struct cred *cred) +devfs_fsync(struct vnode *vp, int syncflag, struct cred *cred, + caller_context_t *ct) { /* * Message goes to console only. Otherwise, the message @@ -1024,7 +1033,7 @@ devfs_fsync(struct vnode *vp, int syncflag, struct cred *cred) */ /*ARGSUSED1*/ static void -devfs_inactive(struct vnode *vp, struct cred *cred) +devfs_inactive(struct vnode *vp, struct cred *cred, caller_context_t *ct) { int destroy; struct dv_node *dv = VTODV(vp); @@ -1045,8 +1054,9 @@ devfs_inactive(struct vnode *vp, struct cred *cred) * XXX Why do we need this? NFS mounted /dev directories? * XXX Talk to peter staubach about this. */ +/*ARGSUSED2*/ static int -devfs_fid(struct vnode *vp, struct fid *fidp) +devfs_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct) { struct dv_node *dv = VTODV(vp); struct dv_fid *dv_fid; @@ -1095,7 +1105,8 @@ devfs_rwunlock(struct vnode *vp, int write_flag, caller_context_t *ct) */ /*ARGSUSED1*/ static int -devfs_seek(struct vnode *vp, offset_t ooff, offset_t *noffp) +devfs_seek(struct vnode *vp, offset_t ooff, offset_t *noffp, + caller_context_t *ct) { ASSERT(vp->v_type == VDIR); dcmn_err2(("devfs_seek %s\n", VTODV(vp)->dv_name)); diff --git a/usr/src/uts/common/fs/doorfs/door_sys.c b/usr/src/uts/common/fs/doorfs/door_sys.c index c139032e70aa..ec9d650d9946 100644 --- a/usr/src/uts/common/fs/doorfs/door_sys.c +++ b/usr/src/uts/common/fs/doorfs/door_sys.c @@ -69,14 +69,14 @@ #include /* - * The maximum amount of data (in bytes) that will be transfered using + * The maximum amount of data (in bytes) that will be transferred using * an intermediate kernel buffer. For sizes greater than this we map * in the destination pages and perform a 1-copy transfer. */ size_t door_max_arg = 16 * 1024; /* - * Maximum amount of data that will be transfered in a reply to a + * Maximum amount of data that will be transferred in a reply to a * door_upcall. Need to guard against a process returning huge amounts * of data and getting the kernel stuck in kmem_alloc. */ @@ -168,7 +168,7 @@ _init(void) cmn_err(CE_WARN, "door init: bad vfs ops"); return (error); } - vfs_setops(&door_vfs, door_vfsops); + VFS_INIT(&door_vfs, door_vfsops, NULL); door_vfs.vfs_flag = VFS_RDONLY; door_vfs.vfs_dev = doordev; vfs_make_fsid(&(door_vfs.vfs_fsid), doordev, 0); @@ -1726,7 +1726,7 @@ door_insert(struct file *fp, door_desc_t *dp) dp->d_data.d_desc.d_descriptor = fd; /* Fill in the attributes */ - if (VOP_REALVP(fp->f_vnode, &vp)) + if (VOP_REALVP(fp->f_vnode, &vp, NULL)) vp = fp->f_vnode; if (vp && vp->v_type == VDOOR) { if (VTOD(vp)->door_target == curproc) @@ -1796,7 +1796,7 @@ door_get_server(door_node_t *dp) if (!cv_wait_sig_swap_core(&pool->dp_cv, &door_knob, &signalled)) { /* - * If we were signalled and the door is still + * If we were signaled and the door is still * valid, pass the signal on to another waiter. */ if (signalled && !DOOR_INVALID(dp)) @@ -1905,7 +1905,7 @@ door_lookup(int did, file_t **fpp) /* * Use the underlying vnode (we may be namefs mounted) */ - if (VOP_REALVP(fp->f_vnode, &vp)) + if (VOP_REALVP(fp->f_vnode, &vp, NULL)) vp = fp->f_vnode; if (vp == NULL || vp->v_type != VDOOR) { @@ -2562,7 +2562,7 @@ door_translate_in(void) (void) closeandsetf(fd, NULL); } - if (VOP_REALVP(fp->f_vnode, &vp)) + if (VOP_REALVP(fp->f_vnode, &vp, NULL)) vp = fp->f_vnode; /* Set attributes */ @@ -3217,7 +3217,7 @@ door_upcall(vnode_t *vp, door_arg_t *param) struct file *fp; fp = *fpp; - if (VOP_REALVP(fp->f_vnode, &vp)) + if (VOP_REALVP(fp->f_vnode, &vp, NULL)) vp = fp->f_vnode; didpp->d_attributes = DOOR_HANDLE | @@ -3297,7 +3297,7 @@ door_ki_upcall(door_handle_t dh, door_arg_t *param) file_t *fp = DHTOF(dh); vnode_t *realvp; - if (VOP_REALVP(fp->f_vnode, &realvp)) + if (VOP_REALVP(fp->f_vnode, &realvp, NULL)) realvp = fp->f_vnode; return (door_upcall(realvp, param)); } @@ -3365,7 +3365,7 @@ door_ki_open(char *pathname, door_handle_t *dhp) if ((err = lookupname(pathname, UIO_SYSSPACE, FOLLOW, NULL, &vp)) != 0) return (err); - if (err = VOP_OPEN(&vp, FREAD, kcred)) { + if (err = VOP_OPEN(&vp, FREAD, kcred, NULL)) { VN_RELE(vp); return (err); } @@ -3389,7 +3389,7 @@ door_ki_info(door_handle_t dh, struct door_info *dip) file_t *fp = DHTOF(dh); vnode_t *vp; - if (VOP_REALVP(fp->f_vnode, &vp)) + if (VOP_REALVP(fp->f_vnode, &vp, NULL)) vp = fp->f_vnode; if (vp->v_type != VDOOR) return (EINVAL); @@ -3419,7 +3419,7 @@ door_ki_setparam(door_handle_t dh, int type, size_t val) file_t *fp = DHTOF(dh); vnode_t *vp; - if (VOP_REALVP(fp->f_vnode, &vp)) + if (VOP_REALVP(fp->f_vnode, &vp, NULL)) vp = fp->f_vnode; if (vp->v_type != VDOOR) return (EINVAL); @@ -3432,7 +3432,7 @@ door_ki_getparam(door_handle_t dh, int type, size_t *out) file_t *fp = DHTOF(dh); vnode_t *vp; - if (VOP_REALVP(fp->f_vnode, &vp)) + if (VOP_REALVP(fp->f_vnode, &vp, NULL)) vp = fp->f_vnode; if (vp->v_type != VDOOR) return (EINVAL); diff --git a/usr/src/uts/common/fs/doorfs/door_vnops.c b/usr/src/uts/common/fs/doorfs/door_vnops.c index 6d61c34f30a5..b50465f9c8f4 100644 --- a/usr/src/uts/common/fs/doorfs/door_vnops.c +++ b/usr/src/uts/common/fs/doorfs/door_vnops.c @@ -38,15 +38,17 @@ #include kmutex_t door_knob; -static int door_open(struct vnode **vpp, int flag, struct cred *cr); +static int door_open(struct vnode **vpp, int flag, struct cred *cr, + caller_context_t *ct); static int door_close(struct vnode *vp, int flag, int count, - offset_t offset, struct cred *cr); + offset_t offset, struct cred *cr, caller_context_t *ct); static int door_getattr(struct vnode *vp, struct vattr *vap, - int flags, struct cred *cr); -static void door_inactive(struct vnode *vp, struct cred *cr); + int flags, struct cred *cr, caller_context_t *ct); +static void door_inactive(struct vnode *vp, struct cred *cr, + caller_context_t *ct); static int door_access(struct vnode *vp, int mode, int flags, - struct cred *cr); -static int door_realvp(vnode_t *vp, vnode_t **vpp); + struct cred *cr, caller_context_t *ct); +static int door_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct); struct vfs door_vfs; @@ -70,7 +72,7 @@ const fs_operation_def_t door_vnodeops_template[] = { /* ARGSUSED */ static int -door_open(struct vnode **vpp, int flag, struct cred *cr) +door_open(struct vnode **vpp, int flag, struct cred *cr, caller_context_t *ct) { /* * MAC policy for doors. Restrict cross-zone open()s so that only @@ -103,7 +105,8 @@ door_close( int flag, int count, offset_t offset, - struct cred *cr + struct cred *cr, + caller_context_t *ct ) { door_node_t *dp = VTOD(vp); @@ -142,7 +145,8 @@ door_close( /* ARGSUSED */ static int -door_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cr) +door_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cr, + caller_context_t *ct) { static timestruc_t tzero = {0, 0}; extern dev_t doordev; @@ -169,7 +173,7 @@ door_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cr) /* ARGSUSED */ static void -door_inactive(struct vnode *vp, struct cred *cr) +door_inactive(struct vnode *vp, struct cred *cr, caller_context_t *ct) { door_node_t *dp = VTOD(vp); @@ -231,19 +235,20 @@ door_unbind_thread(door_node_t *dp) mutex_exit(&vp->v_lock); if (do_inactive) - door_inactive(vp, NULL); + door_inactive(vp, NULL, NULL); } /* ARGSUSED */ static int -door_access(struct vnode *vp, int mode, int flags, struct cred *cr) +door_access(struct vnode *vp, int mode, int flags, struct cred *cr, + caller_context_t *ct) { return (0); } /* ARGSUSED */ static int -door_realvp(vnode_t *vp, vnode_t **vpp) +door_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct) { *vpp = vp; return (0); diff --git a/usr/src/uts/common/fs/fd/fdops.c b/usr/src/uts/common/fs/fd/fdops.c index 3033d0ae06b0..3288872146f0 100644 --- a/usr/src/uts/common/fs/fd/fdops.c +++ b/usr/src/uts/common/fs/fd/fdops.c @@ -82,7 +82,7 @@ static int fdget(vnode_t *, char *, vnode_t **); /* ARGSUSED */ static int -fdopen(vnode_t **vpp, int mode, cred_t *cr) +fdopen(vnode_t **vpp, int mode, cred_t *cr, caller_context_t *ct) { if ((*vpp)->v_type != VDIR) { mutex_enter(&(*vpp)->v_lock); @@ -94,7 +94,8 @@ fdopen(vnode_t **vpp, int mode, cred_t *cr) /* ARGSUSED */ static int -fdclose(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) +fdclose(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, + caller_context_t *ct) { return (0); } @@ -163,7 +164,8 @@ fdread(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, caller_context_t *ct) /* ARGSUSED */ static int -fdgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +fdgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { vfs_t *vfsp = vp->v_vfsp; timestruc_t now; @@ -195,7 +197,7 @@ fdgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) /* ARGSUSED */ static int -fdaccess(vnode_t *vp, int mode, int flags, cred_t *cr) +fdaccess(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct) { return (0); } @@ -203,7 +205,8 @@ fdaccess(vnode_t *vp, int mode, int flags, cred_t *cr) /* ARGSUSED */ static int fdlookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pnp, - int flags, vnode_t *rdir, cred_t *cr) + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp) { if (comp[0] == 0 || strcmp(comp, ".") == 0 || strcmp(comp, "..") == 0) { VN_HOLD(dp); @@ -216,14 +219,16 @@ fdlookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pnp, /* ARGSUSED */ static int fdcreate(vnode_t *dvp, char *comp, vattr_t *vap, enum vcexcl excl, - int mode, vnode_t **vpp, cred_t *cr, int flag) + int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct, + vsecattr_t *vsecp) { return (fdget(dvp, comp, vpp)); } /* ARGSUSED */ static int -fdreaddir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp) +fdreaddir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp, caller_context_t *ct, + int flags) { /* bp holds one dirent structure */ u_offset_t bp[DIRENT64_RECLEN(FDNSIZE) / sizeof (u_offset_t)]; @@ -300,7 +305,7 @@ fdreaddir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp) /* ARGSUSED */ static void -fdinactive(vnode_t *vp, cred_t *cr) +fdinactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { mutex_enter(&vp->v_lock); ASSERT(vp->v_count >= 1); diff --git a/usr/src/uts/common/fs/fem.c b/usr/src/uts/common/fs/fem.c index 04886984e4a7..fec6c3575ac9 100644 --- a/usr/src/uts/common/fs/fem.c +++ b/usr/src/uts/common/fs/fem.c @@ -428,7 +428,7 @@ fem_release(struct fem_list *sp) */ static int -vhead_open(vnode_t **vpp, int mode, cred_t *cr) +vhead_open(vnode_t **vpp, int mode, cred_t *cr, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -440,21 +440,22 @@ vhead_open(vnode_t **vpp, int mode, cred_t *cr) func = (int (*)()) ((*vpp)->v_op->vop_open); arg0 = (void *)vpp; fem_unlock((*vpp)->v_femhead); - errc = (*func)(arg0, mode, cr); + errc = (*func)(arg0, mode, cr, ct); } else { fem_addref(femsp); fem_unlock((*vpp)->v_femhead); farg.fa_vnode.vpp = vpp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_open, femop_open); - errc = (*func)(arg0, mode, cr); + errc = (*func)(arg0, mode, cr, ct); fem_release(femsp); } return (errc); } static int -vhead_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) +vhead_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, + caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -466,14 +467,14 @@ vhead_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) func = (int (*)()) (vp->v_op->vop_close); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, flag, count, offset, cr); + errc = (*func)(arg0, flag, count, offset, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_close, femop_close); - errc = (*func)(arg0, flag, count, offset, cr); + errc = (*func)(arg0, flag, count, offset, cr, ct); fem_release(femsp); } return (errc); @@ -481,7 +482,7 @@ vhead_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) static int vhead_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, - struct caller_context *ct) + caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -508,7 +509,7 @@ vhead_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, static int vhead_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, - struct caller_context *ct) + caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -535,7 +536,7 @@ vhead_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, static int vhead_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, - int *rvalp) + int *rvalp, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -547,21 +548,22 @@ vhead_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, func = (int (*)()) (vp->v_op->vop_ioctl); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, cmd, arg, flag, cr, rvalp); + errc = (*func)(arg0, cmd, arg, flag, cr, rvalp, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_ioctl, femop_ioctl); - errc = (*func)(arg0, cmd, arg, flag, cr, rvalp); + errc = (*func)(arg0, cmd, arg, flag, cr, rvalp, ct); fem_release(femsp); } return (errc); } static int -vhead_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr) +vhead_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr, + caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -573,21 +575,22 @@ vhead_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr) func = (int (*)()) (vp->v_op->vop_setfl); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, oflags, nflags, cr); + errc = (*func)(arg0, oflags, nflags, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_setfl, femop_setfl); - errc = (*func)(arg0, oflags, nflags, cr); + errc = (*func)(arg0, oflags, nflags, cr, ct); fem_release(femsp); } return (errc); } static int -vhead_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +vhead_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -599,7 +602,7 @@ vhead_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) func = (int (*)()) (vp->v_op->vop_getattr); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, vap, flags, cr); + errc = (*func)(arg0, vap, flags, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -607,7 +610,7 @@ vhead_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_getattr, femop_getattr); - errc = (*func)(arg0, vap, flags, cr); + errc = (*func)(arg0, vap, flags, cr, ct); fem_release(femsp); } return (errc); @@ -642,7 +645,8 @@ vhead_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, } static int -vhead_access(vnode_t *vp, int mode, int flags, cred_t *cr) +vhead_access(vnode_t *vp, int mode, int flags, cred_t *cr, + caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -654,7 +658,7 @@ vhead_access(vnode_t *vp, int mode, int flags, cred_t *cr) func = (int (*)()) (vp->v_op->vop_access); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, mode, flags, cr); + errc = (*func)(arg0, mode, flags, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -662,7 +666,7 @@ vhead_access(vnode_t *vp, int mode, int flags, cred_t *cr) farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_access, femop_access); - errc = (*func)(arg0, mode, flags, cr); + errc = (*func)(arg0, mode, flags, cr, ct); fem_release(femsp); } return (errc); @@ -670,7 +674,8 @@ vhead_access(vnode_t *vp, int mode, int flags, cred_t *cr) static int vhead_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp, - int flags, vnode_t *rdir, cred_t *cr) + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp) { femarg_t farg; struct fem_list *femsp; @@ -682,7 +687,8 @@ vhead_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp, func = (int (*)()) (dvp->v_op->vop_lookup); arg0 = dvp; fem_unlock(dvp->v_femhead); - errc = (*func)(arg0, nm, vpp, pnp, flags, rdir, cr); + errc = (*func)(arg0, nm, vpp, pnp, flags, rdir, cr, ct, + direntflags, realpnp); } else { fem_addref(femsp); fem_unlock(dvp->v_femhead); @@ -690,7 +696,8 @@ vhead_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp, farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_lookup, femop_lookup); - errc = (*func)(arg0, nm, vpp, pnp, flags, rdir, cr); + errc = (*func)(arg0, nm, vpp, pnp, flags, rdir, cr, ct, + direntflags, realpnp); fem_release(femsp); } return (errc); @@ -698,7 +705,8 @@ vhead_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp, static int vhead_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, - int mode, vnode_t **vpp, cred_t *cr, int flag) + int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct, + vsecattr_t *vsecp) { femarg_t farg; struct fem_list *femsp; @@ -710,7 +718,8 @@ vhead_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, func = (int (*)()) (dvp->v_op->vop_create); arg0 = dvp; fem_unlock(dvp->v_femhead); - errc = (*func)(arg0, name, vap, excl, mode, vpp, cr, flag); + errc = (*func)(arg0, name, vap, excl, mode, vpp, cr, flag, + ct, vsecp); } else { fem_addref(femsp); fem_unlock(dvp->v_femhead); @@ -718,14 +727,16 @@ vhead_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_create, femop_create); - errc = (*func)(arg0, name, vap, excl, mode, vpp, cr, flag); + errc = (*func)(arg0, name, vap, excl, mode, vpp, cr, flag, + ct, vsecp); fem_release(femsp); } return (errc); } static int -vhead_remove(vnode_t *dvp, char *nm, cred_t *cr) +vhead_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct, + int flags) { femarg_t farg; struct fem_list *femsp; @@ -737,7 +748,7 @@ vhead_remove(vnode_t *dvp, char *nm, cred_t *cr) func = (int (*)()) (dvp->v_op->vop_remove); arg0 = dvp; fem_unlock(dvp->v_femhead); - errc = (*func)(arg0, nm, cr); + errc = (*func)(arg0, nm, cr, ct, flags); } else { fem_addref(femsp); fem_unlock(dvp->v_femhead); @@ -745,14 +756,15 @@ vhead_remove(vnode_t *dvp, char *nm, cred_t *cr) farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_remove, femop_remove); - errc = (*func)(arg0, nm, cr); + errc = (*func)(arg0, nm, cr, ct, flags); fem_release(femsp); } return (errc); } static int -vhead_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) +vhead_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr, + caller_context_t *ct, int flags) { femarg_t farg; struct fem_list *femsp; @@ -764,14 +776,14 @@ vhead_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) func = (int (*)()) (tdvp->v_op->vop_link); arg0 = tdvp; fem_unlock(tdvp->v_femhead); - errc = (*func)(arg0, svp, tnm, cr); + errc = (*func)(arg0, svp, tnm, cr, ct, flags); } else { fem_addref(femsp); fem_unlock(tdvp->v_femhead); farg.fa_vnode.vp = tdvp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_link, femop_link); - errc = (*func)(arg0, svp, tnm, cr); + errc = (*func)(arg0, svp, tnm, cr, ct, flags); fem_release(femsp); } return (errc); @@ -779,7 +791,7 @@ vhead_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) static int vhead_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, - cred_t *cr) + cred_t *cr, caller_context_t *ct, int flags) { femarg_t farg; struct fem_list *femsp; @@ -791,7 +803,7 @@ vhead_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, func = (int (*)()) (sdvp->v_op->vop_rename); arg0 = sdvp; fem_unlock(sdvp->v_femhead); - errc = (*func)(arg0, snm, tdvp, tnm, cr); + errc = (*func)(arg0, snm, tdvp, tnm, cr, ct, flags); } else { fem_addref(femsp); fem_unlock(sdvp->v_femhead); @@ -799,7 +811,7 @@ vhead_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_rename, femop_rename); - errc = (*func)(arg0, snm, tdvp, tnm, cr); + errc = (*func)(arg0, snm, tdvp, tnm, cr, ct, flags); fem_release(femsp); } return (errc); @@ -807,7 +819,7 @@ vhead_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, static int vhead_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, - cred_t *cr) + cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp) { femarg_t farg; struct fem_list *femsp; @@ -819,21 +831,22 @@ vhead_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, func = (int (*)()) (dvp->v_op->vop_mkdir); arg0 = dvp; fem_unlock(dvp->v_femhead); - errc = (*func)(arg0, dirname, vap, vpp, cr); + errc = (*func)(arg0, dirname, vap, vpp, cr, ct, flags, vsecp); } else { fem_addref(femsp); fem_unlock(dvp->v_femhead); farg.fa_vnode.vp = dvp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_mkdir, femop_mkdir); - errc = (*func)(arg0, dirname, vap, vpp, cr); + errc = (*func)(arg0, dirname, vap, vpp, cr, ct, flags, vsecp); fem_release(femsp); } return (errc); } static int -vhead_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) +vhead_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr, + caller_context_t *ct, int flags) { femarg_t farg; struct fem_list *femsp; @@ -845,21 +858,22 @@ vhead_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) func = (int (*)()) (dvp->v_op->vop_rmdir); arg0 = dvp; fem_unlock(dvp->v_femhead); - errc = (*func)(arg0, nm, cdir, cr); + errc = (*func)(arg0, nm, cdir, cr, ct, flags); } else { fem_addref(femsp); fem_unlock(dvp->v_femhead); farg.fa_vnode.vp = dvp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_rmdir, femop_rmdir); - errc = (*func)(arg0, nm, cdir, cr); + errc = (*func)(arg0, nm, cdir, cr, ct, flags); fem_release(femsp); } return (errc); } static int -vhead_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp) +vhead_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) { femarg_t farg; struct fem_list *femsp; @@ -871,7 +885,7 @@ vhead_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp) func = (int (*)()) (vp->v_op->vop_readdir); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, uiop, cr, eofp); + errc = (*func)(arg0, uiop, cr, eofp, ct, flags); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -879,7 +893,7 @@ vhead_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp) farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_readdir, femop_readdir); - errc = (*func)(arg0, uiop, cr, eofp); + errc = (*func)(arg0, uiop, cr, eofp, ct, flags); fem_release(femsp); } return (errc); @@ -887,7 +901,7 @@ vhead_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp) static int vhead_symlink(vnode_t *dvp, char *linkname, vattr_t *vap, char *target, - cred_t *cr) + cred_t *cr, caller_context_t *ct, int flags) { femarg_t farg; struct fem_list *femsp; @@ -899,7 +913,7 @@ vhead_symlink(vnode_t *dvp, char *linkname, vattr_t *vap, char *target, func = (int (*)()) (dvp->v_op->vop_symlink); arg0 = dvp; fem_unlock(dvp->v_femhead); - errc = (*func)(arg0, linkname, vap, target, cr); + errc = (*func)(arg0, linkname, vap, target, cr, ct, flags); } else { fem_addref(femsp); fem_unlock(dvp->v_femhead); @@ -907,14 +921,14 @@ vhead_symlink(vnode_t *dvp, char *linkname, vattr_t *vap, char *target, farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_symlink, femop_symlink); - errc = (*func)(arg0, linkname, vap, target, cr); + errc = (*func)(arg0, linkname, vap, target, cr, ct, flags); fem_release(femsp); } return (errc); } static int -vhead_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr) +vhead_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -926,7 +940,7 @@ vhead_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr) func = (int (*)()) (vp->v_op->vop_readlink); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, uiop, cr); + errc = (*func)(arg0, uiop, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -934,14 +948,14 @@ vhead_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr) farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_readlink, femop_readlink); - errc = (*func)(arg0, uiop, cr); + errc = (*func)(arg0, uiop, cr, ct); fem_release(femsp); } return (errc); } static int -vhead_fsync(vnode_t *vp, int syncflag, cred_t *cr) +vhead_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -953,21 +967,21 @@ vhead_fsync(vnode_t *vp, int syncflag, cred_t *cr) func = (int (*)()) (vp->v_op->vop_fsync); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, syncflag, cr); + errc = (*func)(arg0, syncflag, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_fsync, femop_fsync); - errc = (*func)(arg0, syncflag, cr); + errc = (*func)(arg0, syncflag, cr, ct); fem_release(femsp); } return (errc); } static void -vhead_inactive(vnode_t *vp, cred_t *cr) +vhead_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -978,7 +992,7 @@ vhead_inactive(vnode_t *vp, cred_t *cr) func = (void (*)()) (vp->v_op->vop_inactive); arg0 = vp; fem_unlock(vp->v_femhead); - (*func)(arg0, cr); + (*func)(arg0, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -986,13 +1000,13 @@ vhead_inactive(vnode_t *vp, cred_t *cr) farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, void, &arg0, vop_inactive, femop_inactive); - (*func)(arg0, cr); + (*func)(arg0, cr, ct); fem_release(femsp); } } static int -vhead_fid(vnode_t *vp, fid_t *fidp) +vhead_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1004,14 +1018,14 @@ vhead_fid(vnode_t *vp, fid_t *fidp) func = (int (*)()) (vp->v_op->vop_fid); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, fidp); + errc = (*func)(arg0, fidp, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_fid, femop_fid); - errc = (*func)(arg0, fidp); + errc = (*func)(arg0, fidp, ct); fem_release(femsp); } return (errc); @@ -1070,7 +1084,7 @@ vhead_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ct) } static int -vhead_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) +vhead_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1082,21 +1096,21 @@ vhead_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) func = (int (*)()) (vp->v_op->vop_seek); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, ooff, noffp); + errc = (*func)(arg0, ooff, noffp, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_seek, femop_seek); - errc = (*func)(arg0, ooff, noffp); + errc = (*func)(arg0, ooff, noffp, ct); fem_release(femsp); } return (errc); } static int -vhead_cmp(vnode_t *vp1, vnode_t *vp2) +vhead_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1108,14 +1122,14 @@ vhead_cmp(vnode_t *vp1, vnode_t *vp2) func = (int (*)()) (vp1->v_op->vop_cmp); arg0 = vp1; fem_unlock(vp1->v_femhead); - errc = (*func)(arg0, vp2); + errc = (*func)(arg0, vp2, ct); } else { fem_addref(femsp); fem_unlock(vp1->v_femhead); farg.fa_vnode.vp = vp1; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_cmp, femop_cmp); - errc = (*func)(arg0, vp2); + errc = (*func)(arg0, vp2, ct); fem_release(femsp); } return (errc); @@ -1123,7 +1137,8 @@ vhead_cmp(vnode_t *vp1, vnode_t *vp2) static int vhead_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, - offset_t offset, struct flk_callback *flk_cbp, cred_t *cr) + offset_t offset, struct flk_callback *flk_cbp, cred_t *cr, + caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1135,7 +1150,7 @@ vhead_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, func = (int (*)()) (vp->v_op->vop_frlock); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, cmd, bfp, flag, offset, flk_cbp, cr); + errc = (*func)(arg0, cmd, bfp, flag, offset, flk_cbp, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1143,7 +1158,7 @@ vhead_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_frlock, femop_frlock); - errc = (*func)(arg0, cmd, bfp, flag, offset, flk_cbp, cr); + errc = (*func)(arg0, cmd, bfp, flag, offset, flk_cbp, cr, ct); fem_release(femsp); } return (errc); @@ -1177,7 +1192,7 @@ vhead_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, } static int -vhead_realvp(vnode_t *vp, vnode_t **vpp) +vhead_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1189,7 +1204,7 @@ vhead_realvp(vnode_t *vp, vnode_t **vpp) func = (int (*)()) (vp->v_op->vop_realvp); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, vpp); + errc = (*func)(arg0, vpp, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1197,7 +1212,7 @@ vhead_realvp(vnode_t *vp, vnode_t **vpp) farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_realvp, femop_realvp); - errc = (*func)(arg0, vpp); + errc = (*func)(arg0, vpp, ct); fem_release(femsp); } return (errc); @@ -1206,7 +1221,7 @@ vhead_realvp(vnode_t *vp, vnode_t **vpp) static int vhead_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp, struct page **plarr, size_t plsz, struct seg *seg, caddr_t addr, - enum seg_rw rw, cred_t *cr) + enum seg_rw rw, cred_t *cr, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1219,7 +1234,7 @@ vhead_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp, arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, off, len, protp, plarr, plsz, seg, - addr, rw, cr); + addr, rw, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1228,14 +1243,15 @@ vhead_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp, vsop_find(&farg, &func, int, &arg0, vop_getpage, femop_getpage); errc = (*func)(arg0, off, len, protp, plarr, plsz, seg, - addr, rw, cr); + addr, rw, cr, ct); fem_release(femsp); } return (errc); } static int -vhead_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr) +vhead_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr, + caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1247,7 +1263,7 @@ vhead_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr) func = (int (*)()) (vp->v_op->vop_putpage); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, off, len, flags, cr); + errc = (*func)(arg0, off, len, flags, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1255,7 +1271,7 @@ vhead_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr) farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_putpage, femop_putpage); - errc = (*func)(arg0, off, len, flags, cr); + errc = (*func)(arg0, off, len, flags, cr, ct); fem_release(femsp); } return (errc); @@ -1264,7 +1280,7 @@ vhead_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr) static int vhead_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, - cred_t *cr) + cred_t *cr, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1277,7 +1293,7 @@ vhead_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, off, as, addrp, len, prot, maxprot, - flags, cr); + flags, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1285,7 +1301,7 @@ vhead_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_map, femop_map); errc = (*func)(arg0, off, as, addrp, len, prot, maxprot, - flags, cr); + flags, cr, ct); fem_release(femsp); } return (errc); @@ -1294,7 +1310,7 @@ vhead_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, static int vhead_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, - cred_t *cr) + cred_t *cr, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1307,7 +1323,7 @@ vhead_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, off, as, addr, len, prot, maxprot, - flags, cr); + flags, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1316,7 +1332,7 @@ vhead_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, vsop_find(&farg, &func, int, &arg0, vop_addmap, femop_addmap); errc = (*func)(arg0, off, as, addr, len, prot, maxprot, - flags, cr); + flags, cr, ct); fem_release(femsp); } return (errc); @@ -1324,7 +1340,8 @@ vhead_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, static int vhead_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, - size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr) + size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1337,7 +1354,7 @@ vhead_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, arg0 = vp; fem_unlock(vp->v_femhead); errc = (*func)(arg0, off, as, addr, len, prot, maxprot, - flags, cr); + flags, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1346,7 +1363,7 @@ vhead_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, vsop_find(&farg, &func, int, &arg0, vop_delmap, femop_delmap); errc = (*func)(arg0, off, as, addr, len, prot, maxprot, - flags, cr); + flags, cr, ct); fem_release(femsp); } return (errc); @@ -1354,7 +1371,7 @@ vhead_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, static int vhead_poll(vnode_t *vp, short events, int anyyet, short *reventsp, - struct pollhead **phpp) + struct pollhead **phpp, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1366,21 +1383,21 @@ vhead_poll(vnode_t *vp, short events, int anyyet, short *reventsp, func = (int (*)()) (vp->v_op->vop_poll); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, events, anyyet, reventsp, phpp); + errc = (*func)(arg0, events, anyyet, reventsp, phpp, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_poll, femop_poll); - errc = (*func)(arg0, events, anyyet, reventsp, phpp); + errc = (*func)(arg0, events, anyyet, reventsp, phpp, ct); fem_release(femsp); } return (errc); } static int -vhead_dump(vnode_t *vp, caddr_t addr, int lbdn, int dblks) +vhead_dump(vnode_t *vp, caddr_t addr, int lbdn, int dblks, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1392,21 +1409,22 @@ vhead_dump(vnode_t *vp, caddr_t addr, int lbdn, int dblks) func = (int (*)()) (vp->v_op->vop_dump); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, addr, lbdn, dblks); + errc = (*func)(arg0, addr, lbdn, dblks, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); farg.fa_vnode.vp = vp; farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_dump, femop_dump); - errc = (*func)(arg0, addr, lbdn, dblks); + errc = (*func)(arg0, addr, lbdn, dblks, ct); fem_release(femsp); } return (errc); } static int -vhead_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) +vhead_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, + caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1418,7 +1436,7 @@ vhead_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) func = (int (*)()) (vp->v_op->vop_pathconf); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, cmd, valp, cr); + errc = (*func)(arg0, cmd, valp, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1426,7 +1444,7 @@ vhead_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_pathconf, femop_pathconf); - errc = (*func)(arg0, cmd, valp, cr); + errc = (*func)(arg0, cmd, valp, cr, ct); fem_release(femsp); } return (errc); @@ -1434,7 +1452,7 @@ vhead_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) static int vhead_pageio(vnode_t *vp, struct page *pp, u_offset_t io_off, - size_t io_len, int flags, cred_t *cr) + size_t io_len, int flags, cred_t *cr, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1446,7 +1464,7 @@ vhead_pageio(vnode_t *vp, struct page *pp, u_offset_t io_off, func = (int (*)()) (vp->v_op->vop_pageio); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, pp, io_off, io_len, flags, cr); + errc = (*func)(arg0, pp, io_off, io_len, flags, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1454,14 +1472,14 @@ vhead_pageio(vnode_t *vp, struct page *pp, u_offset_t io_off, farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_pageio, femop_pageio); - errc = (*func)(arg0, pp, io_off, io_len, flags, cr); + errc = (*func)(arg0, pp, io_off, io_len, flags, cr, ct); fem_release(femsp); } return (errc); } static int -vhead_dumpctl(vnode_t *vp, int action, int *blkp) +vhead_dumpctl(vnode_t *vp, int action, int *blkp, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1473,7 +1491,7 @@ vhead_dumpctl(vnode_t *vp, int action, int *blkp) func = (int (*)()) (vp->v_op->vop_dumpctl); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, action, blkp); + errc = (*func)(arg0, action, blkp, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1481,14 +1499,15 @@ vhead_dumpctl(vnode_t *vp, int action, int *blkp) farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_dumpctl, femop_dumpctl); - errc = (*func)(arg0, action, blkp); + errc = (*func)(arg0, action, blkp, ct); fem_release(femsp); } return (errc); } static void -vhead_dispose(vnode_t *vp, struct page *pp, int flag, int dn, cred_t *cr) +vhead_dispose(vnode_t *vp, struct page *pp, int flag, int dn, cred_t *cr, + caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1499,7 +1518,7 @@ vhead_dispose(vnode_t *vp, struct page *pp, int flag, int dn, cred_t *cr) func = (void (*)()) (vp->v_op->vop_dispose); arg0 = vp; fem_unlock(vp->v_femhead); - (*func)(arg0, pp, flag, dn, cr); + (*func)(arg0, pp, flag, dn, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1507,13 +1526,14 @@ vhead_dispose(vnode_t *vp, struct page *pp, int flag, int dn, cred_t *cr) farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, void, &arg0, vop_dispose, femop_dispose); - (*func)(arg0, pp, flag, dn, cr); + (*func)(arg0, pp, flag, dn, cr, ct); fem_release(femsp); } } static int -vhead_setsecattr(vnode_t *vp, vsecattr_t *vsap, int flag, cred_t *cr) +vhead_setsecattr(vnode_t *vp, vsecattr_t *vsap, int flag, cred_t *cr, + caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1525,7 +1545,7 @@ vhead_setsecattr(vnode_t *vp, vsecattr_t *vsap, int flag, cred_t *cr) func = (int (*)()) (vp->v_op->vop_setsecattr); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, vsap, flag, cr); + errc = (*func)(arg0, vsap, flag, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1533,14 +1553,15 @@ vhead_setsecattr(vnode_t *vp, vsecattr_t *vsap, int flag, cred_t *cr) farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_setsecattr, femop_setsecattr); - errc = (*func)(arg0, vsap, flag, cr); + errc = (*func)(arg0, vsap, flag, cr, ct); fem_release(femsp); } return (errc); } static int -vhead_getsecattr(vnode_t *vp, vsecattr_t *vsap, int flag, cred_t *cr) +vhead_getsecattr(vnode_t *vp, vsecattr_t *vsap, int flag, cred_t *cr, + caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1552,7 +1573,7 @@ vhead_getsecattr(vnode_t *vp, vsecattr_t *vsap, int flag, cred_t *cr) func = (int (*)()) (vp->v_op->vop_getsecattr); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, vsap, flag, cr); + errc = (*func)(arg0, vsap, flag, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1560,7 +1581,7 @@ vhead_getsecattr(vnode_t *vp, vsecattr_t *vsap, int flag, cred_t *cr) farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_getsecattr, femop_getsecattr); - errc = (*func)(arg0, vsap, flag, cr); + errc = (*func)(arg0, vsap, flag, cr, ct); fem_release(femsp); } return (errc); @@ -1568,7 +1589,7 @@ vhead_getsecattr(vnode_t *vp, vsecattr_t *vsap, int flag, cred_t *cr) static int vhead_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, - cred_t *cr) + cred_t *cr, caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1580,7 +1601,7 @@ vhead_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, func = (int (*)()) (vp->v_op->vop_shrlock); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, cmd, shr, flag, cr); + errc = (*func)(arg0, cmd, shr, flag, cr, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1588,14 +1609,15 @@ vhead_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_shrlock, femop_shrlock); - errc = (*func)(arg0, cmd, shr, flag, cr); + errc = (*func)(arg0, cmd, shr, flag, cr, ct); fem_release(femsp); } return (errc); } static int -vhead_vnevent(vnode_t *vp, vnevent_t vnevent, vnode_t *dvp, char *cname) +vhead_vnevent(vnode_t *vp, vnevent_t vnevent, vnode_t *dvp, char *cname, + caller_context_t *ct) { femarg_t farg; struct fem_list *femsp; @@ -1607,7 +1629,7 @@ vhead_vnevent(vnode_t *vp, vnevent_t vnevent, vnode_t *dvp, char *cname) func = (int (*)()) (vp->v_op->vop_vnevent); arg0 = vp; fem_unlock(vp->v_femhead); - errc = (*func)(arg0, vnevent, dvp, cname); + errc = (*func)(arg0, vnevent, dvp, cname, ct); } else { fem_addref(femsp); fem_unlock(vp->v_femhead); @@ -1615,7 +1637,7 @@ vhead_vnevent(vnode_t *vp, vnevent_t vnevent, vnode_t *dvp, char *cname) farg.fa_fnode = femsp->feml_nodes + femsp->feml_tos; vsop_find(&farg, &func, int, &arg0, vop_vnevent, femop_vnevent); - errc = (*func)(arg0, vnevent, dvp, cname); + errc = (*func)(arg0, vnevent, dvp, cname, ct); fem_release(femsp); } return (errc); @@ -1958,7 +1980,7 @@ static struct fs_operation_def fshead_vfs_spec[] = { */ int -vnext_open(femarg_t *vf, int mode, cred_t *cr) +vnext_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -1968,11 +1990,12 @@ vnext_open(femarg_t *vf, int mode, cred_t *cr) vsop_find(vf, &func, int, &arg0, vop_open, femop_open); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, mode, cr)); + return ((*func)(arg0, mode, cr, ct)); } int -vnext_close(femarg_t *vf, int flag, int count, offset_t offset, cred_t *cr) +vnext_close(femarg_t *vf, int flag, int count, offset_t offset, cred_t *cr, + caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -1982,12 +2005,12 @@ vnext_close(femarg_t *vf, int flag, int count, offset_t offset, cred_t *cr) vsop_find(vf, &func, int, &arg0, vop_close, femop_close); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, flag, count, offset, cr)); + return ((*func)(arg0, flag, count, offset, cr, ct)); } int vnext_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr, - struct caller_context *ct) + caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2002,7 +2025,7 @@ vnext_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr, int vnext_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr, - struct caller_context *ct) + caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2017,7 +2040,7 @@ vnext_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr, int vnext_ioctl(femarg_t *vf, int cmd, intptr_t arg, int flag, cred_t *cr, - int *rvalp) + int *rvalp, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2027,11 +2050,12 @@ vnext_ioctl(femarg_t *vf, int cmd, intptr_t arg, int flag, cred_t *cr, vsop_find(vf, &func, int, &arg0, vop_ioctl, femop_ioctl); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, cmd, arg, flag, cr, rvalp)); + return ((*func)(arg0, cmd, arg, flag, cr, rvalp, ct)); } int -vnext_setfl(femarg_t *vf, int oflags, int nflags, cred_t *cr) +vnext_setfl(femarg_t *vf, int oflags, int nflags, cred_t *cr, + caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2041,11 +2065,12 @@ vnext_setfl(femarg_t *vf, int oflags, int nflags, cred_t *cr) vsop_find(vf, &func, int, &arg0, vop_setfl, femop_setfl); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, oflags, nflags, cr)); + return ((*func)(arg0, oflags, nflags, cr, ct)); } int -vnext_getattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr) +vnext_getattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2055,7 +2080,7 @@ vnext_getattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr) vsop_find(vf, &func, int, &arg0, vop_getattr, femop_getattr); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, vap, flags, cr)); + return ((*func)(arg0, vap, flags, cr, ct)); } int @@ -2074,7 +2099,8 @@ vnext_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr, } int -vnext_access(femarg_t *vf, int mode, int flags, cred_t *cr) +vnext_access(femarg_t *vf, int mode, int flags, cred_t *cr, + caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2084,12 +2110,13 @@ vnext_access(femarg_t *vf, int mode, int flags, cred_t *cr) vsop_find(vf, &func, int, &arg0, vop_access, femop_access); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, mode, flags, cr)); + return ((*func)(arg0, mode, flags, cr, ct)); } int vnext_lookup(femarg_t *vf, char *nm, vnode_t **vpp, pathname_t *pnp, - int flags, vnode_t *rdir, cred_t *cr) + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp) { int (*func)() = NULL; void *arg0 = NULL; @@ -2099,12 +2126,14 @@ vnext_lookup(femarg_t *vf, char *nm, vnode_t **vpp, pathname_t *pnp, vsop_find(vf, &func, int, &arg0, vop_lookup, femop_lookup); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, nm, vpp, pnp, flags, rdir, cr)); + return ((*func)(arg0, nm, vpp, pnp, flags, rdir, cr, ct, + direntflags, realpnp)); } int vnext_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl, - int mode, vnode_t **vpp, cred_t *cr, int flag) + int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct, + vsecattr_t *vsecp) { int (*func)() = NULL; void *arg0 = NULL; @@ -2114,11 +2143,12 @@ vnext_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl, vsop_find(vf, &func, int, &arg0, vop_create, femop_create); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, name, vap, excl, mode, vpp, cr, flag)); + return ((*func)(arg0, name, vap, excl, mode, vpp, cr, flag, ct, vsecp)); } int -vnext_remove(femarg_t *vf, char *nm, cred_t *cr) +vnext_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct, + int flags) { int (*func)() = NULL; void *arg0 = NULL; @@ -2128,11 +2158,12 @@ vnext_remove(femarg_t *vf, char *nm, cred_t *cr) vsop_find(vf, &func, int, &arg0, vop_remove, femop_remove); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, nm, cr)); + return ((*func)(arg0, nm, cr, ct, flags)); } int -vnext_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr) +vnext_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr, + caller_context_t *ct, int flags) { int (*func)() = NULL; void *arg0 = NULL; @@ -2142,11 +2173,12 @@ vnext_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr) vsop_find(vf, &func, int, &arg0, vop_link, femop_link); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, svp, tnm, cr)); + return ((*func)(arg0, svp, tnm, cr, ct, flags)); } int -vnext_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) +vnext_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr, + caller_context_t *ct, int flags) { int (*func)() = NULL; void *arg0 = NULL; @@ -2156,12 +2188,12 @@ vnext_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) vsop_find(vf, &func, int, &arg0, vop_rename, femop_rename); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, snm, tdvp, tnm, cr)); + return ((*func)(arg0, snm, tdvp, tnm, cr, ct, flags)); } int vnext_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, vnode_t **vpp, - cred_t *cr) + cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp) { int (*func)() = NULL; void *arg0 = NULL; @@ -2171,11 +2203,12 @@ vnext_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, vnode_t **vpp, vsop_find(vf, &func, int, &arg0, vop_mkdir, femop_mkdir); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, dirname, vap, vpp, cr)); + return ((*func)(arg0, dirname, vap, vpp, cr, ct, flags, vsecp)); } int -vnext_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr) +vnext_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr, + caller_context_t *ct, int flags) { int (*func)() = NULL; void *arg0 = NULL; @@ -2185,11 +2218,12 @@ vnext_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr) vsop_find(vf, &func, int, &arg0, vop_rmdir, femop_rmdir); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, nm, cdir, cr)); + return ((*func)(arg0, nm, cdir, cr, ct, flags)); } int -vnext_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp) +vnext_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) { int (*func)() = NULL; void *arg0 = NULL; @@ -2199,12 +2233,12 @@ vnext_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp) vsop_find(vf, &func, int, &arg0, vop_readdir, femop_readdir); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, uiop, cr, eofp)); + return ((*func)(arg0, uiop, cr, eofp, ct, flags)); } int vnext_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target, - cred_t *cr) + cred_t *cr, caller_context_t *ct, int flags) { int (*func)() = NULL; void *arg0 = NULL; @@ -2214,11 +2248,11 @@ vnext_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target, vsop_find(vf, &func, int, &arg0, vop_symlink, femop_symlink); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, linkname, vap, target, cr)); + return ((*func)(arg0, linkname, vap, target, cr, ct, flags)); } int -vnext_readlink(femarg_t *vf, uio_t *uiop, cred_t *cr) +vnext_readlink(femarg_t *vf, uio_t *uiop, cred_t *cr, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2228,11 +2262,11 @@ vnext_readlink(femarg_t *vf, uio_t *uiop, cred_t *cr) vsop_find(vf, &func, int, &arg0, vop_readlink, femop_readlink); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, uiop, cr)); + return ((*func)(arg0, uiop, cr, ct)); } int -vnext_fsync(femarg_t *vf, int syncflag, cred_t *cr) +vnext_fsync(femarg_t *vf, int syncflag, cred_t *cr, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2242,11 +2276,11 @@ vnext_fsync(femarg_t *vf, int syncflag, cred_t *cr) vsop_find(vf, &func, int, &arg0, vop_fsync, femop_fsync); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, syncflag, cr)); + return ((*func)(arg0, syncflag, cr, ct)); } void -vnext_inactive(femarg_t *vf, cred_t *cr) +vnext_inactive(femarg_t *vf, cred_t *cr, caller_context_t *ct) { void (*func)() = NULL; void *arg0 = NULL; @@ -2256,11 +2290,11 @@ vnext_inactive(femarg_t *vf, cred_t *cr) vsop_find(vf, &func, void, &arg0, vop_inactive, femop_inactive); ASSERT(func != NULL); ASSERT(arg0 != NULL); - (*func)(arg0, cr); + (*func)(arg0, cr, ct); } int -vnext_fid(femarg_t *vf, fid_t *fidp) +vnext_fid(femarg_t *vf, fid_t *fidp, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2270,7 +2304,7 @@ vnext_fid(femarg_t *vf, fid_t *fidp) vsop_find(vf, &func, int, &arg0, vop_fid, femop_fid); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, fidp)); + return ((*func)(arg0, fidp, ct)); } int @@ -2302,7 +2336,7 @@ vnext_rwunlock(femarg_t *vf, int write_lock, caller_context_t *ct) } int -vnext_seek(femarg_t *vf, offset_t ooff, offset_t *noffp) +vnext_seek(femarg_t *vf, offset_t ooff, offset_t *noffp, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2312,11 +2346,11 @@ vnext_seek(femarg_t *vf, offset_t ooff, offset_t *noffp) vsop_find(vf, &func, int, &arg0, vop_seek, femop_seek); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, ooff, noffp)); + return ((*func)(arg0, ooff, noffp, ct)); } int -vnext_cmp(femarg_t *vf, vnode_t *vp2) +vnext_cmp(femarg_t *vf, vnode_t *vp2, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2326,12 +2360,13 @@ vnext_cmp(femarg_t *vf, vnode_t *vp2) vsop_find(vf, &func, int, &arg0, vop_cmp, femop_cmp); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, vp2)); + return ((*func)(arg0, vp2, ct)); } int vnext_frlock(femarg_t *vf, int cmd, struct flock64 *bfp, int flag, - offset_t offset, struct flk_callback *flk_cbp, cred_t *cr) + offset_t offset, struct flk_callback *flk_cbp, cred_t *cr, + caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2341,7 +2376,7 @@ vnext_frlock(femarg_t *vf, int cmd, struct flock64 *bfp, int flag, vsop_find(vf, &func, int, &arg0, vop_frlock, femop_frlock); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, cmd, bfp, flag, offset, flk_cbp, cr)); + return ((*func)(arg0, cmd, bfp, flag, offset, flk_cbp, cr, ct)); } int @@ -2360,7 +2395,7 @@ vnext_space(femarg_t *vf, int cmd, struct flock64 *bfp, int flag, } int -vnext_realvp(femarg_t *vf, vnode_t **vpp) +vnext_realvp(femarg_t *vf, vnode_t **vpp, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2370,13 +2405,13 @@ vnext_realvp(femarg_t *vf, vnode_t **vpp) vsop_find(vf, &func, int, &arg0, vop_realvp, femop_realvp); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, vpp)); + return ((*func)(arg0, vpp, ct)); } int vnext_getpage(femarg_t *vf, offset_t off, size_t len, uint_t *protp, struct page **plarr, size_t plsz, struct seg *seg, caddr_t addr, - enum seg_rw rw, cred_t *cr) + enum seg_rw rw, cred_t *cr, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2387,12 +2422,12 @@ vnext_getpage(femarg_t *vf, offset_t off, size_t len, uint_t *protp, ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, off, len, protp, plarr, plsz, seg, addr, rw, - cr)); + cr, ct)); } int vnext_putpage(femarg_t *vf, offset_t off, size_t len, int flags, - cred_t *cr) + cred_t *cr, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2402,13 +2437,13 @@ vnext_putpage(femarg_t *vf, offset_t off, size_t len, int flags, vsop_find(vf, &func, int, &arg0, vop_putpage, femop_putpage); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, off, len, flags, cr)); + return ((*func)(arg0, off, len, flags, cr, ct)); } int vnext_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, - cred_t *cr) + cred_t *cr, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2419,13 +2454,13 @@ vnext_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp, ASSERT(func != NULL); ASSERT(arg0 != NULL); return ((*func)(arg0, off, as, addrp, len, prot, maxprot, flags, - cr)); + cr, ct)); } int vnext_addmap(femarg_t *vf, offset_t off, struct as *as, caddr_t addr, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, - cred_t *cr) + cred_t *cr, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2435,12 +2470,14 @@ vnext_addmap(femarg_t *vf, offset_t off, struct as *as, caddr_t addr, vsop_find(vf, &func, int, &arg0, vop_addmap, femop_addmap); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, off, as, addr, len, prot, maxprot, flags, cr)); + return ((*func)(arg0, off, as, addr, len, prot, maxprot, flags, + cr, ct)); } int vnext_delmap(femarg_t *vf, offset_t off, struct as *as, caddr_t addr, - size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr) + size_t len, uint_t prot, uint_t maxprot, uint_t flags, + cred_t *cr, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2450,12 +2487,13 @@ vnext_delmap(femarg_t *vf, offset_t off, struct as *as, caddr_t addr, vsop_find(vf, &func, int, &arg0, vop_delmap, femop_delmap); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, off, as, addr, len, prot, maxprot, flags, cr)); + return ((*func)(arg0, off, as, addr, len, prot, maxprot, flags, + cr, ct)); } int vnext_poll(femarg_t *vf, short events, int anyyet, short *reventsp, - struct pollhead **phpp) + struct pollhead **phpp, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2465,11 +2503,12 @@ vnext_poll(femarg_t *vf, short events, int anyyet, short *reventsp, vsop_find(vf, &func, int, &arg0, vop_poll, femop_poll); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, events, anyyet, reventsp, phpp)); + return ((*func)(arg0, events, anyyet, reventsp, phpp, ct)); } int -vnext_dump(femarg_t *vf, caddr_t addr, int lbdn, int dblks) +vnext_dump(femarg_t *vf, caddr_t addr, int lbdn, int dblks, + caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2479,11 +2518,12 @@ vnext_dump(femarg_t *vf, caddr_t addr, int lbdn, int dblks) vsop_find(vf, &func, int, &arg0, vop_dump, femop_dump); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, addr, lbdn, dblks)); + return ((*func)(arg0, addr, lbdn, dblks, ct)); } int -vnext_pathconf(femarg_t *vf, int cmd, ulong_t *valp, cred_t *cr) +vnext_pathconf(femarg_t *vf, int cmd, ulong_t *valp, cred_t *cr, + caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2493,12 +2533,12 @@ vnext_pathconf(femarg_t *vf, int cmd, ulong_t *valp, cred_t *cr) vsop_find(vf, &func, int, &arg0, vop_pathconf, femop_pathconf); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, cmd, valp, cr)); + return ((*func)(arg0, cmd, valp, cr, ct)); } int vnext_pageio(femarg_t *vf, struct page *pp, u_offset_t io_off, - size_t io_len, int flags, cred_t *cr) + size_t io_len, int flags, cred_t *cr, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2508,11 +2548,11 @@ vnext_pageio(femarg_t *vf, struct page *pp, u_offset_t io_off, vsop_find(vf, &func, int, &arg0, vop_pageio, femop_pageio); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, pp, io_off, io_len, flags, cr)); + return ((*func)(arg0, pp, io_off, io_len, flags, cr, ct)); } int -vnext_dumpctl(femarg_t *vf, int action, int *blkp) +vnext_dumpctl(femarg_t *vf, int action, int *blkp, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2522,11 +2562,12 @@ vnext_dumpctl(femarg_t *vf, int action, int *blkp) vsop_find(vf, &func, int, &arg0, vop_dumpctl, femop_dumpctl); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, action, blkp)); + return ((*func)(arg0, action, blkp, ct)); } void -vnext_dispose(femarg_t *vf, struct page *pp, int flag, int dn, cred_t *cr) +vnext_dispose(femarg_t *vf, struct page *pp, int flag, int dn, cred_t *cr, + caller_context_t *ct) { void (*func)() = NULL; void *arg0 = NULL; @@ -2536,11 +2577,12 @@ vnext_dispose(femarg_t *vf, struct page *pp, int flag, int dn, cred_t *cr) vsop_find(vf, &func, void, &arg0, vop_dispose, femop_dispose); ASSERT(func != NULL); ASSERT(arg0 != NULL); - (*func)(arg0, pp, flag, dn, cr); + (*func)(arg0, pp, flag, dn, cr, ct); } int -vnext_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, cred_t *cr) +vnext_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, cred_t *cr, + caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2550,11 +2592,12 @@ vnext_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, cred_t *cr) vsop_find(vf, &func, int, &arg0, vop_setsecattr, femop_setsecattr); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, vsap, flag, cr)); + return ((*func)(arg0, vsap, flag, cr, ct)); } int -vnext_getsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, cred_t *cr) +vnext_getsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, cred_t *cr, + caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2564,12 +2607,12 @@ vnext_getsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, cred_t *cr) vsop_find(vf, &func, int, &arg0, vop_getsecattr, femop_getsecattr); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, vsap, flag, cr)); + return ((*func)(arg0, vsap, flag, cr, ct)); } int vnext_shrlock(femarg_t *vf, int cmd, struct shrlock *shr, int flag, - cred_t *cr) + cred_t *cr, caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2579,11 +2622,12 @@ vnext_shrlock(femarg_t *vf, int cmd, struct shrlock *shr, int flag, vsop_find(vf, &func, int, &arg0, vop_shrlock, femop_shrlock); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, cmd, shr, flag, cr)); + return ((*func)(arg0, cmd, shr, flag, cr, ct)); } int -vnext_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *cname) +vnext_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *cname, + caller_context_t *ct) { int (*func)() = NULL; void *arg0 = NULL; @@ -2593,7 +2637,7 @@ vnext_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *cname) vsop_find(vf, &func, int, &arg0, vop_vnevent, femop_vnevent); ASSERT(func != NULL); ASSERT(arg0 != NULL); - return ((*func)(arg0, vnevent, dvp, cname)); + return ((*func)(arg0, vnevent, dvp, cname, ct)); } int diff --git a/usr/src/uts/common/fs/fifofs/fifosubr.c b/usr/src/uts/common/fs/fifofs/fifosubr.c index f44b8b3874f5..0d3d8023703d 100644 --- a/usr/src/uts/common/fs/fifofs/fifosubr.c +++ b/usr/src/uts/common/fs/fifofs/fifosubr.c @@ -29,7 +29,7 @@ /* * The routines defined in this file are supporting routines for FIFOFS - * file sytem type. + * file system type. */ #include #include @@ -164,7 +164,7 @@ static void fifo_reinit_vp(vnode_t *); * we can determine the fifodata address from any of its member fnodes. * This is essential for fifo_inactive. * - * The fnode constructor is designed to handle any fifodata struture, + * The fnode constructor is designed to handle any fifodata structure, * deducing the number of fnodes from the total size. Thus, the fnode * constructor does most of the work for the pipe constructor. */ @@ -410,8 +410,9 @@ fifovp(vnode_t *vp, cred_t *crp) * This way different fifo nodes sharing the same real vnode * can use realvp for communication. */ - if (VOP_REALVP(vp, &rvp) == 0) - vp = rvp; + + if (VOP_REALVP(vp, &rvp, NULL) == 0) + vp = rvp; fnp->fn_realvp = vp; fnp->fn_wcnt = 0; @@ -431,7 +432,7 @@ fifovp(vnode_t *vp, cred_t *crp) * initialize the times from vp. */ va.va_mask = AT_TIMES; - if (VOP_GETATTR(vp, &va, 0, crp) == 0) { + if (VOP_GETATTR(vp, &va, 0, crp, NULL) == 0) { fnp->fn_atime = va.va_atime.tv_sec; fnp->fn_mtime = va.va_mtime.tv_sec; fnp->fn_ctime = va.va_ctime.tv_sec; @@ -652,7 +653,7 @@ fifo_stropen(vnode_t **vpp, int flag, cred_t *crp, int dotwist, int lockheld) * Create new pipe on behalf of connld */ if (error = fifo_connld(vpp, flag, crp)) { - (void) fifo_close(oldvp, flag, 1, 0, crp); + (void) fifo_close(oldvp, flag, 1, 0, crp, NULL); mutex_enter(&fn_lock->flk_lock); goto out; } @@ -663,7 +664,7 @@ fifo_stropen(vnode_t **vpp, int flag, cred_t *crp, int dotwist, int lockheld) * we were in fifo_connld(), so * we want to make sure the close completes (yuk) */ - (void) fifo_close(oldvp, flag, 1, 0, crp); + (void) fifo_close(oldvp, flag, 1, 0, crp, NULL); /* * fifo_connld has changed the vp, so we * need to re-initialize locals @@ -1006,7 +1007,7 @@ fifo_connld(struct vnode **vpp, int flag, cred_t *crp) crhold(c); (void) closef(filep); VTOF(vp2)->fn_flag &= ~FIFOOPEN; - (void) fifo_close(vp2, flag, 1, (offset_t)0, c); + (void) fifo_close(vp2, flag, 1, (offset_t)0, c, NULL); crfree(c); VN_RELE(vp2); return (error); diff --git a/usr/src/uts/common/fs/fifofs/fifovnops.c b/usr/src/uts/common/fs/fifofs/fifovnops.c index afa01bb3ab31..9756d13705d7 100644 --- a/usr/src/uts/common/fs/fifofs/fifovnops.c +++ b/usr/src/uts/common/fs/fifofs/fifovnops.c @@ -73,26 +73,33 @@ */ static int fifo_read(vnode_t *, uio_t *, int, cred_t *, caller_context_t *); static int fifo_write(vnode_t *, uio_t *, int, cred_t *, caller_context_t *); -static int fifo_getattr(vnode_t *, vattr_t *, int, cred_t *); +static int fifo_getattr(vnode_t *, vattr_t *, int, cred_t *, + caller_context_t *); static int fifo_setattr(vnode_t *, vattr_t *, int, cred_t *, caller_context_t *); -static int fifo_realvp(vnode_t *, vnode_t **); -static int fifo_access(vnode_t *, int, int, cred_t *); +static int fifo_realvp(vnode_t *, vnode_t **, caller_context_t *); +static int fifo_access(vnode_t *, int, int, cred_t *, caller_context_t *); static int fifo_create(struct vnode *, char *, vattr_t *, enum vcexcl, - int, struct vnode **, struct cred *, int); -static int fifo_fid(vnode_t *, fid_t *); -static int fifo_fsync(vnode_t *, int, cred_t *); -static int fifo_seek(vnode_t *, offset_t, offset_t *); -static int fifo_ioctl(vnode_t *, int, intptr_t, int, cred_t *, int *); + int, struct vnode **, struct cred *, int, caller_context_t *, + vsecattr_t *); +static int fifo_fid(vnode_t *, fid_t *, caller_context_t *); +static int fifo_fsync(vnode_t *, int, cred_t *, caller_context_t *); +static int fifo_seek(vnode_t *, offset_t, offset_t *, caller_context_t *); +static int fifo_ioctl(vnode_t *, int, intptr_t, int, cred_t *, int *, + caller_context_t *); static int fifo_fastioctl(vnode_t *, int, intptr_t, int, cred_t *, int *); static int fifo_strioctl(vnode_t *, int, intptr_t, int, cred_t *, int *); -static int fifo_poll(vnode_t *, short, int, short *, pollhead_t **); -static int fifo_pathconf(vnode_t *, int, ulong_t *, cred_t *); -static void fifo_inactive(vnode_t *, cred_t *); +static int fifo_poll(vnode_t *, short, int, short *, pollhead_t **, + caller_context_t *); +static int fifo_pathconf(vnode_t *, int, ulong_t *, cred_t *, + caller_context_t *); +static void fifo_inactive(vnode_t *, cred_t *, caller_context_t *); static int fifo_rwlock(vnode_t *, int, caller_context_t *); static void fifo_rwunlock(vnode_t *, int, caller_context_t *); -static int fifo_setsecattr(struct vnode *, vsecattr_t *, int, struct cred *); -static int fifo_getsecattr(struct vnode *, vsecattr_t *, int, struct cred *); +static int fifo_setsecattr(struct vnode *, vsecattr_t *, int, struct cred *, + caller_context_t *); +static int fifo_getsecattr(struct vnode *, vsecattr_t *, int, struct cred *, + caller_context_t *); /* functions local to this file */ static boolean_t fifo_stayfast_enter(fifonode_t *); @@ -210,7 +217,7 @@ tsol_fifo_access(vnode_t *vp, int flag, cred_t *crp) * Note: namefs pipes come through this routine too. */ int -fifo_open(vnode_t **vpp, int flag, cred_t *crp) +fifo_open(vnode_t **vpp, int flag, cred_t *crp, caller_context_t *ct) { vnode_t *vp = *vpp; fifonode_t *fnp = VTOF(vp); @@ -280,7 +287,7 @@ fifo_open(vnode_t **vpp, int flag, cred_t *crp) * If we are opening for read (or writer) * indicate that the reader (or writer) is done with open * if there is a writer (or reader) waiting for us, wake them up - * and indicate that at least 1 read (or write) open has occured + * and indicate that at least 1 read (or write) open has occurred * this is need in the event the read (or write) side closes * before the writer (or reader) has a chance to wake up * i.e. it sees that a reader (or writer) was once there @@ -289,7 +296,7 @@ fifo_open(vnode_t **vpp, int flag, cred_t *crp) fnp->fn_rsynccnt--; /* reader done with open */ if (fnp->fn_flag & FIFOSYNC) { /* - * This indicates that a read open has occured + * This indicates that a read open has occurred * Only need to set if writer is actually asleep * Flag will be consumed by writer. */ @@ -301,7 +308,7 @@ fifo_open(vnode_t **vpp, int flag, cred_t *crp) fnp->fn_wsynccnt--; /* writer done with open */ if (fnp->fn_flag & FIFOSYNC) { /* - * This indicates that a write open has occured + * This indicates that a write open has occurred * Only need to set if reader is actually asleep * Flag will be consumed by reader. */ @@ -347,20 +354,20 @@ fifo_open(vnode_t **vpp, int flag, cred_t *crp) /* * Last reader to wakeup clear writer * Clear both writer and reader open - * occured flag incase other end is O_RDWR + * occurred flag incase other end is O_RDWR */ if (--fnp->fn_insync == 0 && fnp->fn_flag & FIFOWOCR) { fnp->fn_flag &= ~(FIFOWOCR|FIFOROCR); } mutex_exit(&fnp->fn_lock->flk_lock); - (void) fifo_close(*vpp, flag, 1, 0, crp); + (void) fifo_close(*vpp, flag, 1, 0, crp, ct); error = EINTR; goto done; } /* - * Last reader to wakeup clear writer open occured flag - * Clear both writer and reader open occured flag + * Last reader to wakeup clear writer open occurred flag + * Clear both writer and reader open occurred flag * incase other end is O_RDWR */ if (--fnp->fn_insync == 0 && @@ -374,7 +381,7 @@ fifo_open(vnode_t **vpp, int flag, cred_t *crp) fnp->fn_rcnt == fnp->fn_rsynccnt) { if ((flag & (FNDELAY|FNONBLOCK)) && fnp->fn_rcnt == 0) { mutex_exit(&fnp->fn_lock->flk_lock); - (void) fifo_close(*vpp, flag, 1, 0, crp); + (void) fifo_close(*vpp, flag, 1, 0, crp, ct); error = ENXIO; goto done; } @@ -385,21 +392,21 @@ fifo_open(vnode_t **vpp, int flag, cred_t *crp) /* * Last writer to wakeup clear * Clear both writer and reader open - * occured flag in case other end is O_RDWR + * occurred flag in case other end is O_RDWR */ if (--fnp->fn_insync == 0 && (fnp->fn_flag & FIFOROCR) != 0) { fnp->fn_flag &= ~(FIFOWOCR|FIFOROCR); } mutex_exit(&fnp->fn_lock->flk_lock); - (void) fifo_close(*vpp, flag, 1, 0, crp); + (void) fifo_close(*vpp, flag, 1, 0, crp, ct); error = EINTR; goto done; } /* - * Last writer to wakeup clear reader open occured flag + * Last writer to wakeup clear reader open occurred flag * Clear both writer and reader open - * occured flag in case other end is O_RDWR + * occurred flag in case other end is O_RDWR */ if (--fnp->fn_insync == 0 && (fnp->fn_flag & FIFOROCR) != 0) { @@ -425,7 +432,8 @@ fifo_open(vnode_t **vpp, int flag, cred_t *crp) */ /*ARGSUSED*/ int -fifo_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *crp) +fifo_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *crp, + caller_context_t *ct) { fifonode_t *fnp = VTOF(vp); fifonode_t *fn_dest = fnp->fn_dest; @@ -1079,7 +1087,7 @@ fifo_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *crp, done: /* * update vnode modification and change times - * make sure there were no errors and some data was transfered + * make sure there were no errors and some data was transferred */ if (error == 0 && write_size != uiop->uio_resid) { time_t now = gethrestime_sec(); @@ -1105,9 +1113,10 @@ fifo_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *crp, return (error); } +/*ARGSUSED6*/ static int fifo_ioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, - cred_t *cr, int *rvalp) + cred_t *cr, int *rvalp, caller_context_t *ct) { /* * Just a quick check @@ -1430,7 +1439,8 @@ fifo_strioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, * the node information from the credentials structure. */ int -fifo_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *crp) +fifo_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *crp, + caller_context_t *ct) { int error = 0; fifonode_t *fnp = VTOF(vp); @@ -1442,7 +1452,7 @@ fifo_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *crp) /* * for FIFOs or mounted pipes */ - if (error = VOP_GETATTR(fnp->fn_realvp, vap, flags, crp)) + if (error = VOP_GETATTR(fnp->fn_realvp, vap, flags, crp, ct)) return (error); mutex_enter(&fn_lock->flk_lock); /* set current times from fnode, even if older than vnode */ @@ -1537,10 +1547,10 @@ fifo_setattr( * Otherwise, return 0 (allow all access). */ int -fifo_access(vnode_t *vp, int mode, int flags, cred_t *crp) +fifo_access(vnode_t *vp, int mode, int flags, cred_t *crp, caller_context_t *ct) { if (VTOF(vp)->fn_realvp) - return (VOP_ACCESS(VTOF(vp)->fn_realvp, mode, flags, crp)); + return (VOP_ACCESS(VTOF(vp)->fn_realvp, mode, flags, crp, ct)); else return (0); } @@ -1552,13 +1562,14 @@ fifo_access(vnode_t *vp, int mode, int flags, cred_t *crp) /*ARGSUSED*/ static int fifo_create(struct vnode *dvp, char *name, vattr_t *vap, enum vcexcl excl, - int mode, struct vnode **vpp, struct cred *cr, int flag) + int mode, struct vnode **vpp, struct cred *cr, int flag, + caller_context_t *ct, vsecattr_t *vsecp) { int error; ASSERT(dvp && (dvp->v_flag & VROOT) && *name == '\0'); if (excl == NONEXCL) { - if (mode && (error = fifo_access(dvp, mode, 0, cr))) + if (mode && (error = fifo_access(dvp, mode, 0, cr, ct))) return (error); VN_HOLD(dvp); return (0); @@ -1571,7 +1582,7 @@ fifo_create(struct vnode *dvp, char *name, vattr_t *vap, enum vcexcl excl, * Otherwise, return 0. */ int -fifo_fsync(vnode_t *vp, int syncflag, cred_t *crp) +fifo_fsync(vnode_t *vp, int syncflag, cred_t *crp, caller_context_t *ct) { fifonode_t *fnp = VTOF(vp); vattr_t va; @@ -1581,7 +1592,7 @@ fifo_fsync(vnode_t *vp, int syncflag, cred_t *crp) bzero((caddr_t)&va, sizeof (va)); va.va_mask = AT_MTIME | AT_ATIME; - if (VOP_GETATTR(fnp->fn_realvp, &va, 0, crp) == 0) { + if (VOP_GETATTR(fnp->fn_realvp, &va, 0, crp, ct) == 0) { va.va_mask = 0; if (fnp->fn_mtime > va.va_mtime.tv_sec) { va.va_mtime.tv_sec = fnp->fn_mtime; @@ -1592,9 +1603,9 @@ fifo_fsync(vnode_t *vp, int syncflag, cred_t *crp) va.va_mask |= AT_ATIME; } if (va.va_mask != 0) - (void) VOP_SETATTR(fnp->fn_realvp, &va, 0, crp, NULL); + (void) VOP_SETATTR(fnp->fn_realvp, &va, 0, crp, ct); } - return (VOP_FSYNC(fnp->fn_realvp, syncflag, crp)); + return (VOP_FSYNC(fnp->fn_realvp, syncflag, crp, ct)); } /* @@ -1602,7 +1613,7 @@ fifo_fsync(vnode_t *vp, int syncflag, cred_t *crp) * vnode. Sync the file system and free the fifonode. */ void -fifo_inactive(vnode_t *vp, cred_t *crp) +fifo_inactive(vnode_t *vp, cred_t *crp, caller_context_t *ct) { fifonode_t *fnp; fifolock_t *fn_lock; @@ -1630,7 +1641,7 @@ fifo_inactive(vnode_t *vp, cred_t *crp) if (fnp->fn_realvp) { (void) fiforemove(fnp); mutex_exit(&ftable_lock); - (void) fifo_fsync(vp, FSYNC, crp); + (void) fifo_fsync(vp, FSYNC, crp, ct); VN_RELE(fnp->fn_realvp); vp->v_vfsp = NULL; } else @@ -1684,10 +1695,10 @@ fifo_inactive(vnode_t *vp, cred_t *crp) * Otherwise, return EINVAL. */ int -fifo_fid(vnode_t *vp, fid_t *fidfnp) +fifo_fid(vnode_t *vp, fid_t *fidfnp, caller_context_t *ct) { if (VTOF(vp)->fn_realvp) - return (VOP_FID(VTOF(vp)->fn_realvp, fidfnp)); + return (VOP_FID(VTOF(vp)->fn_realvp, fidfnp, ct)); else return (EINVAL); } @@ -1716,7 +1727,7 @@ fifo_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ctp) */ /*ARGSUSED*/ int -fifo_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) +fifo_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) { return (ESPIPE); } @@ -1725,13 +1736,13 @@ fifo_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) * If there is a realvp associated with vp, return it. */ int -fifo_realvp(vnode_t *vp, vnode_t **vpp) +fifo_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct) { vnode_t *rvp; if ((rvp = VTOF(vp)->fn_realvp) != NULL) { vp = rvp; - if (VOP_REALVP(vp, &rvp) == 0) + if (VOP_REALVP(vp, &rvp, ct) == 0) vp = rvp; } @@ -1742,9 +1753,10 @@ fifo_realvp(vnode_t *vp, vnode_t **vpp) /* * Poll for interesting events on a stream pipe */ +/* ARGSUSED */ int fifo_poll(vnode_t *vp, short events, int anyyet, short *reventsp, - pollhead_t **phpp) + pollhead_t **phpp, caller_context_t *ct) { fifonode_t *fnp, *fn_dest; fifolock_t *fn_lock; @@ -1854,7 +1866,8 @@ fifo_poll(vnode_t *vp, short events, int anyyet, short *reventsp, */ /* ARGSUSED */ int -fifo_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) +fifo_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, + caller_context_t *ct) { ulong_t val; int error = 0; @@ -1911,7 +1924,7 @@ fifo_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) default: if (VTOF(vp)->fn_realvp) error = VOP_PATHCONF(VTOF(vp)->fn_realvp, cmd, - &val, cr); + &val, cr, ct); else error = EINVAL; break; @@ -1927,7 +1940,8 @@ fifo_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) * Otherwise, return NOSYS. */ int -fifo_setsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *crp) +fifo_setsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *crp, + caller_context_t *ct) { int error; @@ -1937,9 +1951,10 @@ fifo_setsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *crp) * VOP_RWLOCK or VOP_RWUNLOCK, so we do it here instead. */ if (VTOF(vp)->fn_realvp) { - (void) VOP_RWLOCK(VTOF(vp)->fn_realvp, V_WRITELOCK_TRUE, NULL); - error = VOP_SETSECATTR(VTOF(vp)->fn_realvp, vsap, flag, crp); - VOP_RWUNLOCK(VTOF(vp)->fn_realvp, V_WRITELOCK_TRUE, NULL); + (void) VOP_RWLOCK(VTOF(vp)->fn_realvp, V_WRITELOCK_TRUE, ct); + error = VOP_SETSECATTR(VTOF(vp)->fn_realvp, vsap, flag, + crp, ct); + VOP_RWUNLOCK(VTOF(vp)->fn_realvp, V_WRITELOCK_TRUE, ct); return (error); } else return (fs_nosys()); @@ -1950,12 +1965,14 @@ fifo_setsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *crp) * an ACL from the permission bits that fifo_getattr() makes up. */ int -fifo_getsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *crp) +fifo_getsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *crp, + caller_context_t *ct) { if (VTOF(vp)->fn_realvp) - return (VOP_GETSECATTR(VTOF(vp)->fn_realvp, vsap, flag, crp)); + return (VOP_GETSECATTR(VTOF(vp)->fn_realvp, vsap, flag, + crp, ct)); else - return (fs_fab_acl(vp, vsap, flag, crp)); + return (fs_fab_acl(vp, vsap, flag, crp, ct)); } diff --git a/usr/src/uts/common/fs/fs_subr.c b/usr/src/uts/common/fs/fs_subr.c index c88e8b326809..5afbced14857 100644 --- a/usr/src/uts/common/fs/fs_subr.c +++ b/usr/src/uts/common/fs/fs_subr.c @@ -113,7 +113,8 @@ fs_nosys_map(struct vnode *vp, uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { return (ENOSYS); } @@ -128,7 +129,8 @@ fs_nosys_addmap(struct vnode *vp, uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { return (ENOSYS); } @@ -139,7 +141,8 @@ fs_nosys_poll(vnode_t *vp, register short events, int anyyet, register short *reventsp, - struct pollhead **phpp) + struct pollhead **phpp, + caller_context_t *ct) { return (ENOSYS); } @@ -156,6 +159,38 @@ fs_sync(struct vfs *vfspp, short flag, cred_t *cr) return (0); } +/* + * Does nothing but VOP_FSYNC must not fail. + */ +/* ARGSUSED */ +int +fs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) +{ + return (0); +} + +/* + * Does nothing but VOP_PUTPAGE must not fail. + */ +/* ARGSUSED */ +int +fs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr, + caller_context_t *ctp) +{ + return (0); +} + +/* + * Does nothing but VOP_IOCTL must not fail. + */ +/* ARGSUSED */ +int +fs_ioctl(vnode_t *vp, int com, intptr_t data, int flag, cred_t *cred, + int *rvalp) +{ + return (0); +} + /* * Read/write lock/unlock. Does nothing. */ @@ -175,8 +210,9 @@ fs_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ctp) /* * Compare two vnodes. */ +/*ARGSUSED2*/ int -fs_cmp(vnode_t *vp1, vnode_t *vp2) +fs_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct) { return (vp1 == vp2); } @@ -186,7 +222,7 @@ fs_cmp(vnode_t *vp1, vnode_t *vp2) */ /* ARGSUSED */ int -fs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) +fs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) { return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0); } @@ -197,13 +233,15 @@ fs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) /* ARGSUSED */ int fs_frlock(register vnode_t *vp, int cmd, struct flock64 *bfp, int flag, - offset_t offset, flk_callback_t *flk_cbp, cred_t *cr) + offset_t offset, flk_callback_t *flk_cbp, cred_t *cr, + caller_context_t *ct) { int frcmd; int nlmid; int error = 0; flk_callback_t serialize_callback; int serialize = 0; + v_mode_t mode; switch (cmd) { @@ -211,15 +249,13 @@ fs_frlock(register vnode_t *vp, int cmd, struct flock64 *bfp, int flag, case F_O_GETLK: if (flag & F_REMOTELOCK) { frcmd = RCMDLCK; - break; - } - if (flag & F_PXFSLOCK) { + } else if (flag & F_PXFSLOCK) { frcmd = PCMDLCK; - break; + } else { + frcmd = 0; + bfp->l_pid = ttoproc(curthread)->p_pid; + bfp->l_sysid = 0; } - bfp->l_pid = ttoproc(curthread)->p_pid; - bfp->l_sysid = 0; - frcmd = 0; break; case F_SETLK_NBMAND: @@ -238,6 +274,19 @@ fs_frlock(register vnode_t *vp, int cmd, struct flock64 *bfp, int flag, /*FALLTHROUGH*/ case F_SETLK: + if (flag & F_REMOTELOCK) { + frcmd = SETFLCK|RCMDLCK; + } else if (flag & F_PXFSLOCK) { + frcmd = SETFLCK|PCMDLCK; + } else { + frcmd = SETFLCK; + bfp->l_pid = ttoproc(curthread)->p_pid; + bfp->l_sysid = 0; + } + if (cmd == F_SETLK_NBMAND && + (bfp->l_type == F_RDLCK || bfp->l_type == F_WRLCK)) { + frcmd |= NBMLCK; + } /* * Check whether there is an NBMAND share reservation that * conflicts with the lock request. @@ -245,30 +294,31 @@ fs_frlock(register vnode_t *vp, int cmd, struct flock64 *bfp, int flag, if (nbl_need_check(vp)) { nbl_start_crit(vp, RW_WRITER); serialize = 1; + if (frcmd & NBMLCK) { + mode = (bfp->l_type == F_RDLCK) ? + V_READ : V_RDANDWR; + if (vn_is_mapped(vp, mode)) { + error = EAGAIN; + goto done; + } + } if (share_blocks_lock(vp, bfp)) { error = EAGAIN; goto done; } } - if (flag & F_REMOTELOCK) { - frcmd = SETFLCK|RCMDLCK; - break; - } - if (flag & F_PXFSLOCK) { - frcmd = SETFLCK|PCMDLCK; - break; - } - bfp->l_pid = ttoproc(curthread)->p_pid; - bfp->l_sysid = 0; - frcmd = SETFLCK; - if (cmd == F_SETLK_NBMAND && - (bfp->l_type == F_RDLCK || bfp->l_type == F_WRLCK)) { - /* would check here for conflict with mapped region */ - frcmd |= NBMLCK; - } break; case F_SETLKW: + if (flag & F_REMOTELOCK) { + frcmd = SETFLCK|SLPFLCK|RCMDLCK; + } else if (flag & F_PXFSLOCK) { + frcmd = SETFLCK|SLPFLCK|PCMDLCK; + } else { + frcmd = SETFLCK|SLPFLCK; + bfp->l_pid = ttoproc(curthread)->p_pid; + bfp->l_sysid = 0; + } /* * If there is an NBMAND share reservation that conflicts * with the lock request, block until the conflicting share @@ -283,24 +333,13 @@ fs_frlock(register vnode_t *vp, int cmd, struct flock64 *bfp, int flag, goto done; } } - if (flag & F_REMOTELOCK) { - frcmd = SETFLCK|SLPFLCK|RCMDLCK; - break; - } - if (flag & F_PXFSLOCK) { - frcmd = SETFLCK|SLPFLCK|PCMDLCK; - break; - } - bfp->l_pid = ttoproc(curthread)->p_pid; - bfp->l_sysid = 0; - frcmd = SETFLCK|SLPFLCK; break; case F_HASREMOTELOCKS: nlmid = GETNLMID(bfp->l_sysid); if (nlmid != 0) { /* booted as a cluster */ l_has_rmt(bfp) = - cl_flk_has_remote_locks_for_nlmid(vp, nlmid); + cl_flk_has_remote_locks_for_nlmid(vp, nlmid); } else { /* not booted as a cluster */ l_has_rmt(bfp) = flk_has_remote_locks(vp); } @@ -320,7 +359,7 @@ fs_frlock(register vnode_t *vp, int cmd, struct flock64 *bfp, int flag, if (serialize && (frcmd & SLPFLCK) != 0) { flk_add_callback(&serialize_callback, - frlock_serialize_blocked, vp, flk_cbp); + frlock_serialize_blocked, vp, flk_cbp); flk_cbp = &serialize_callback; } @@ -358,7 +397,12 @@ frlock_serialize_blocked(flk_cb_when_t when, void *infop) */ /* ARGSUSED */ int -fs_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr) +fs_setfl( + vnode_t *vp, + int oflags, + int nflags, + cred_t *cr, + caller_context_t *ct) { return (0); } @@ -375,7 +419,8 @@ fs_poll(vnode_t *vp, register short events, int anyyet, register short *reventsp, - struct pollhead **phpp) + struct pollhead **phpp, + caller_context_t *ct) { *reventsp = 0; if (events & POLLIN) @@ -397,7 +442,12 @@ fs_poll(vnode_t *vp, */ /* ARGSUSED */ int -fs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) +fs_pathconf( + vnode_t *vp, + int cmd, + ulong_t *valp, + cred_t *cr, + caller_context_t *ct) { register ulong_t val; register int error = 0; @@ -467,6 +517,19 @@ fs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) val = 0; break; + case _PC_CASE_BEHAVIOR: + val = _CASE_SENSITIVE; + if (vfs_has_feature(vp->v_vfsp, VFSFT_CASEINSENSITIVE) == 1) + val |= _CASE_INSENSITIVE; + if (vfs_has_feature(vp->v_vfsp, VFSFT_NOCASESENSITIVE) == 1) + val &= ~_CASE_SENSITIVE; + break; + + case _PC_SATTR_ENABLED: + case _PC_SATTR_EXISTS: + val = 0; + break; + default: error = EINVAL; break; @@ -482,7 +545,13 @@ fs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) */ /* ARGSUSED */ void -fs_dispose(struct vnode *vp, page_t *pp, int fl, int dn, struct cred *cr) +fs_dispose( + struct vnode *vp, + page_t *pp, + int fl, + int dn, + struct cred *cr, + caller_context_t *ct) { ASSERT(fl == B_FREE || fl == B_INVAL); @@ -495,7 +564,13 @@ fs_dispose(struct vnode *vp, page_t *pp, int fl, int dn, struct cred *cr) /* ARGSUSED */ void -fs_nodispose(struct vnode *vp, page_t *pp, int fl, int dn, struct cred *cr) +fs_nodispose( + struct vnode *vp, + page_t *pp, + int fl, + int dn, + struct cred *cr, + caller_context_t *ct) { cmn_err(CE_PANIC, "fs_nodispose invoked"); } @@ -505,30 +580,33 @@ fs_nodispose(struct vnode *vp, page_t *pp, int fl, int dn, struct cred *cr) */ /* ARGSUSED */ int -fs_fab_acl(vp, vsecattr, flag, cr) -vnode_t *vp; -vsecattr_t *vsecattr; -int flag; -cred_t *cr; +fs_fab_acl( + vnode_t *vp, + vsecattr_t *vsecattr, + int flag, + cred_t *cr, + caller_context_t *ct) { aclent_t *aclentp; ace_t *acep; struct vattr vattr; int error; + size_t aclsize; vsecattr->vsa_aclcnt = 0; + vsecattr->vsa_aclentsz = 0; vsecattr->vsa_aclentp = NULL; vsecattr->vsa_dfaclcnt = 0; /* Default ACLs are not fabricated */ vsecattr->vsa_dfaclentp = NULL; vattr.va_mask = AT_MODE | AT_UID | AT_GID; - if (error = VOP_GETATTR(vp, &vattr, 0, cr)) + if (error = VOP_GETATTR(vp, &vattr, 0, cr, ct)) return (error); if (vsecattr->vsa_mask & (VSA_ACLCNT | VSA_ACL)) { + aclsize = 4 * sizeof (aclent_t); vsecattr->vsa_aclcnt = 4; /* USER, GROUP, OTHER, and CLASS */ - vsecattr->vsa_aclentp = kmem_zalloc(4 * sizeof (aclent_t), - KM_SLEEP); + vsecattr->vsa_aclentp = kmem_zalloc(aclsize, KM_SLEEP); aclentp = vsecattr->vsa_aclentp; aclentp->a_type = USER_OBJ; /* Owner */ @@ -550,9 +628,10 @@ cred_t *cr; aclentp->a_perm = (ushort_t)(0007); aclentp->a_id = (gid_t)-1; /* Really undefined */ } else if (vsecattr->vsa_mask & (VSA_ACECNT | VSA_ACE)) { + aclsize = 6 * sizeof (ace_t); vsecattr->vsa_aclcnt = 6; - vsecattr->vsa_aclentp = kmem_zalloc(6 * sizeof (ace_t), - KM_SLEEP); + vsecattr->vsa_aclentp = kmem_zalloc(aclsize, KM_SLEEP); + vsecattr->vsa_aclentsz = aclsize; acep = vsecattr->vsa_aclentp; (void) memcpy(acep, trivial_acl, sizeof (ace_t) * 6); adjust_ace_pair(acep, (vattr.va_mode & 0700) >> 6); @@ -568,7 +647,13 @@ cred_t *cr; */ /* ARGSUSED4 */ int -fs_shrlock(struct vnode *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr) +fs_shrlock( + struct vnode *vp, + int cmd, + struct shrlock *shr, + int flag, + cred_t *cr, + caller_context_t *ct) { int error; @@ -581,13 +666,12 @@ fs_shrlock(struct vnode *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr) if (((shr->s_access & F_RDACC) && (flag & FREAD) == 0) || ((shr->s_access & F_WRACC) && (flag & FWRITE) == 0)) return (EBADF); - if (shr->s_deny & F_MANDDNY) + if (shr->s_access & (F_RMACC | F_MDACC)) + return (EINVAL); + if (shr->s_deny & (F_MANDDNY | F_RMDNY)) return (EINVAL); } if (cmd == F_SHARE_NBMAND) { - /* must have write permission to deny read access */ - if ((shr->s_deny & F_RDDNY) && (flag & FWRITE) == 0) - return (EBADF); /* make sure nbmand is allowed on the file */ if (!vp->v_vfsp || !(vp->v_vfsp->vfs_flag & VFS_NBMAND)) { @@ -633,7 +717,8 @@ fs_shrlock(struct vnode *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr) /*ARGSUSED1*/ int -fs_vnevent_nosupport(vnode_t *vp, vnevent_t vnevent, vnode_t *dvp, char *cname) +fs_vnevent_nosupport(vnode_t *vp, vnevent_t e, vnode_t *dvp, char *fnm, + caller_context_t *ct) { ASSERT(vp != NULL); return (ENOTSUP); @@ -641,7 +726,8 @@ fs_vnevent_nosupport(vnode_t *vp, vnevent_t vnevent, vnode_t *dvp, char *cname) /*ARGSUSED1*/ int -fs_vnevent_support(vnode_t *vp, vnevent_t vnevent, vnode_t *dvp, char *cname) +fs_vnevent_support(vnode_t *vp, vnevent_t e, vnode_t *dvp, char *fnm, + caller_context_t *ct) { ASSERT(vp != NULL); return (0); @@ -667,7 +753,7 @@ fs_acl_nontrivial(vnode_t *vp, cred_t *cr) int isnontrivial; /* determine the forms of ACLs maintained */ - error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &acl_styles, cr); + error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &acl_styles, cr, NULL); /* clear bits we don't understand and establish default acl_style */ acl_styles &= (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED); @@ -691,7 +777,7 @@ fs_acl_nontrivial(vnode_t *vp, cred_t *cr) } ASSERT(vsecattr.vsa_mask && acl_flavor); - error = VOP_GETSECATTR(vp, &vsecattr, 0, cr); + error = VOP_GETSECATTR(vp, &vsecattr, 0, cr, NULL); if (error == 0) break; @@ -739,3 +825,30 @@ fs_need_estale_retry(int retry_count) else return (0); } + + +static int (*fs_av_scan)(vnode_t *, cred_t *, int) = NULL; + +/* + * Routine for anti-virus scanner to call to register its scanning routine. + */ +void +fs_vscan_register(int (*av_scan)(vnode_t *, cred_t *, int)) +{ + fs_av_scan = av_scan; +} + +/* + * Routine for file systems to call to initiate anti-virus scanning. + * Scanning will only be done on REGular files (currently). + */ +int +fs_vscan(vnode_t *vp, cred_t *cr, int async) +{ + int ret = 0; + + if (fs_av_scan && vp->v_type == VREG) + ret = (*fs_av_scan)(vp, cr, async); + + return (ret); +} diff --git a/usr/src/uts/common/fs/fs_subr.h b/usr/src/uts/common/fs/fs_subr.h index f0b536d0f05b..a39e6ec7642b 100644 --- a/usr/src/uts/common/fs/fs_subr.h +++ b/usr/src/uts/common/fs/fs_subr.h @@ -56,32 +56,46 @@ extern int fs_nosys(); extern int fs_inval(); extern int fs_notdir(); extern int fs_nosys_map(struct vnode *, offset_t, struct as *, caddr_t *, - size_t, uchar_t, uchar_t, uint_t, struct cred *); + size_t, uchar_t, uchar_t, uint_t, struct cred *, + caller_context_t *); extern int fs_nosys_addmap(struct vnode *, offset_t, struct as *, caddr_t, - size_t, uchar_t, uchar_t, uint_t, struct cred *); + size_t, uchar_t, uchar_t, uint_t, struct cred *, + caller_context_t *); extern int fs_nosys_poll(struct vnode *, short, int, short *, - struct pollhead **); - + struct pollhead **, caller_context_t *); +extern int fs_ioctl(vnode_t *, int, intptr_t, int, cred_t *, int *); +extern int fs_putpage(vnode_t *, offset_t, size_t, int, cred_t *, + caller_context_t *); +extern int fs_fsync(vnode_t *, int, cred_t *, caller_context_t *); extern int fs_sync(struct vfs *, short, cred_t *); extern int fs_rwlock(vnode_t *, int, caller_context_t *); extern void fs_rwunlock(vnode_t *, int, caller_context_t *); -extern int fs_cmp(vnode_t *, vnode_t *); -extern int fs_seek(vnode_t *, offset_t, offset_t *); +extern int fs_cmp(vnode_t *, vnode_t *, caller_context_t *); +extern int fs_seek(vnode_t *, offset_t, offset_t *, caller_context_t *); extern int fs_frlock(vnode_t *, int, struct flock64 *, int, offset_t, - struct flk_callback *, cred_t *); -extern int fs_setfl(vnode_t *, int, int, cred_t *); -extern int fs_poll(vnode_t *, short, int, short *, struct pollhead **); -extern int fs_pathconf(struct vnode *, int, ulong_t *, struct cred *); + struct flk_callback *, cred_t *, caller_context_t *); +extern int fs_setfl(vnode_t *, int, int, cred_t *, caller_context_t *); +extern int fs_poll(vnode_t *, short, int, short *, struct pollhead **, + caller_context_t *); +extern int fs_pathconf(struct vnode *, int, ulong_t *, struct cred *, + caller_context_t *); extern void clkset(time_t); -extern void fs_dispose(struct vnode *, page_t *, int, int, struct cred *); -extern void fs_nodispose(struct vnode *, page_t *, int, int, struct cred *); -extern int fs_fab_acl(struct vnode *, vsecattr_t *, int flag, cred_t *); +extern void fs_dispose(struct vnode *, page_t *, int, int, struct cred *, + caller_context_t *); +extern void fs_nodispose(struct vnode *, page_t *, int, int, struct cred *, + caller_context_t *); +extern int fs_fab_acl(struct vnode *, vsecattr_t *, int flag, cred_t *, + caller_context_t *); extern int fs_shrlock(struct vnode *, int, struct shrlock *, int, - cred_t *); -extern int fs_vnevent_nosupport(vnode_t *, vnevent_t, vnode_t *, char *); -extern int fs_vnevent_support(vnode_t *, vnevent_t, vnode_t *, char *); + cred_t *, caller_context_t *); +extern int fs_vnevent_nosupport(vnode_t *, vnevent_t, vnode_t *dvp, + char *fnm, caller_context_t *); +extern int fs_vnevent_support(vnode_t *, vnevent_t, vnode_t *dvp, + char *fnm, caller_context_t *); extern int fs_acl_nontrivial(struct vnode *vp, struct cred *cr); extern int fs_need_estale_retry(int); +extern void fs_vscan_register(int (*av_scan)(vnode_t *, cred_t *, int)); +extern int fs_vscan(vnode_t *, cred_t *, int); #endif /* _KERNEL */ diff --git a/usr/src/uts/common/fs/fsflush.c b/usr/src/uts/common/fs/fsflush.c index d7b3ae1071c6..50b196ac17f2 100644 --- a/usr/src/uts/common/fs/fsflush.c +++ b/usr/src/uts/common/fs/fsflush.c @@ -100,7 +100,7 @@ fsf_stat_t fsf_total; /* total of counts */ ulong_t fsf_cycles; /* number of runs refelected in fsf_total */ /* - * data used to determine when we can coalese consecutive free pages + * data used to determine when we can coalesce consecutive free pages * into larger pages. */ #define MAX_PAGESIZES 32 @@ -131,7 +131,7 @@ fsflush_do_pages() u_offset_t offset; uint_t szc; - page_t *coal_page = NULL; /* 1st page in group to coalese */ + page_t *coal_page = NULL; /* 1st page in group to coalesce */ uint_t coal_szc = 0; /* size code, coal_page->p_szc */ uint_t coal_cnt = 0; /* count of pages seen */ @@ -288,7 +288,7 @@ fsflush_do_pages() page_unlock(pp); (void) VOP_PUTPAGE(vp, offset, PAGESIZE, B_ASYNC, - kcred); + kcred, NULL); VN_RELE(vp); } else { diff --git a/usr/src/uts/common/fs/gfs.c b/usr/src/uts/common/fs/gfs.c index c3e05aeced69..24f2ad2a7a1c 100644 --- a/usr/src/uts/common/fs/gfs.c +++ b/usr/src/uts/common/fs/gfs.c @@ -62,7 +62,7 @@ * * These routines are designed to play a support role for existing * pseudo-filesystems (such as procfs). They simplify common tasks, - * without enforcing the filesystem to hand over management to GFS. The + * without forcing the filesystem to hand over management to GFS. The * routines covered are: * * gfs_readdir_init() @@ -538,7 +538,7 @@ gfs_file_inactive(vnode_t *vp) gfs_dir_t *dp = NULL; void *data; - if (fp->gfs_parent == NULL) + if (fp->gfs_parent == NULL || (vp->v_flag & V_XATTRDIR)) goto found; dp = fp->gfs_parent->v_data; @@ -564,6 +564,9 @@ gfs_file_inactive(vnode_t *vp) ge = NULL; found: + if (vp->v_flag & V_XATTRDIR) { + mutex_enter(&fp->gfs_parent->v_lock); + } mutex_enter(&vp->v_lock); if (vp->v_count == 1) { /* @@ -577,13 +580,19 @@ gfs_file_inactive(vnode_t *vp) */ ge->gfse_vnode = NULL; } + if (vp->v_flag & V_XATTRDIR) { + fp->gfs_parent->v_xattrdir = NULL; + mutex_exit(&fp->gfs_parent->v_lock); + } mutex_exit(&vp->v_lock); /* * Free vnode and release parent */ if (fp->gfs_parent) { - gfs_dir_unlock(dp); + if (dp) { + gfs_dir_unlock(dp); + } VN_RELE(fp->gfs_parent); } else { ASSERT(vp->v_vfsp != NULL); @@ -594,6 +603,9 @@ gfs_file_inactive(vnode_t *vp) vp->v_count--; data = NULL; mutex_exit(&vp->v_lock); + if (vp->v_flag & V_XATTRDIR) { + mutex_exit(&fp->gfs_parent->v_lock); + } if (dp) gfs_dir_unlock(dp); } @@ -637,16 +649,17 @@ gfs_dir_inactive(vnode_t *vp) * If no static entry is found, we invoke the lookup callback, if any. The * arguments to this callback are: * - * int gfs_lookup_cb(vnode_t *pvp, const char *nm, vnode_t **vpp); + * int gfs_lookup_cb(vnode_t *pvp, const char *nm, vnode_t **vpp, cred_t *cr); * * pvp - parent vnode * nm - name of entry * vpp - pointer to resulting vnode + * cr - pointer to cred * * Returns 0 on success, non-zero on error. */ int -gfs_dir_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp) +gfs_dir_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp, cred_t *cr) { int i; gfs_dirent_t *ge; @@ -732,14 +745,22 @@ gfs_dir_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp) * directory. */ gfs_dir_unlock(dp); - ret = dp->gfsd_lookup(dvp, nm, &vp, &ino); + ret = dp->gfsd_lookup(dvp, nm, &vp, &ino, cr); gfs_dir_lock(dp); if (ret != 0) goto out; - fp = (gfs_file_t *)vp->v_data; - fp->gfs_index = -1; - fp->gfs_ino = ino; + /* + * The lookup_cb might be returning a non-GFS vnode. + * Currently this is true for extended attributes, + * where we're returning a vnode with v_data from an + * underlying fs. + */ + if ((dvp->v_flag & V_XATTRDIR) == 0) { + fp = (gfs_file_t *)vp->v_data; + fp->gfs_index = -1; + fp->gfs_ino = ino; + } } else { /* * No static entry found, and there is no lookup callback, so @@ -803,21 +824,31 @@ gfs_dir_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp) * Return 0 on success, or error on failure. */ int -gfs_dir_readdir(vnode_t *dvp, uio_t *uiop, int *eofp, void *data) +gfs_dir_readdir(vnode_t *dvp, uio_t *uiop, int *eofp, void *data, cred_t *cr, + caller_context_t *ct) { gfs_readdir_state_t gstate; int error, eof = 0; ino64_t ino, pino; offset_t off, next; gfs_dir_t *dp = dvp->v_data; + vnode_t *parent; ino = dp->gfsd_file.gfs_ino; + parent = dp->gfsd_file.gfs_parent; - if (dp->gfsd_file.gfs_parent == NULL) + if (parent == NULL) pino = ino; /* root of filesystem */ - else - pino = ((gfs_file_t *) - (dp->gfsd_file.gfs_parent->v_data))->gfs_ino; + else if (dvp->v_flag & V_XATTRDIR) { + vattr_t va; + + va.va_mask = AT_NODEID; + error = VOP_GETATTR(parent, &va, 0, cr, ct); + if (error) + return (error); + pino = va.va_nodeid; + } else + pino = ((gfs_file_t *)(parent->v_data))->gfs_ino; if ((error = gfs_readdir_init(&gstate, dp->gfsd_maxlen, 1, uiop, pino, ino)) != 0) @@ -870,9 +901,10 @@ gfs_dir_readdir(vnode_t *dvp, uio_t *uiop, int *eofp, void *data) /* ARGSUSED */ int gfs_vop_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp, - int flags, vnode_t *rdir, cred_t *cr) + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp) { - return (gfs_dir_lookup(dvp, nm, vpp)); + return (gfs_dir_lookup(dvp, nm, vpp, cr)); } /* @@ -883,9 +915,10 @@ gfs_vop_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp, */ /* ARGSUSED */ int -gfs_vop_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp) +gfs_vop_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) { - return (gfs_dir_readdir(vp, uiop, eofp, NULL)); + return (gfs_dir_readdir(vp, uiop, eofp, NULL, cr, ct)); } @@ -901,7 +934,8 @@ gfs_vop_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp) /* ARGSUSED */ int gfs_vop_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, - size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cred) + size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cred, + caller_context_t *ct) { int rv; ssize_t resid = len; @@ -972,7 +1006,7 @@ gfs_vop_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, */ /* ARGSUSED */ void -gfs_vop_inactive(vnode_t *vp, cred_t *cr) +gfs_vop_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { gfs_file_t *fp = vp->v_data; void *data; diff --git a/usr/src/uts/common/fs/hsfs/hsfs_vfsops.c b/usr/src/uts/common/fs/hsfs/hsfs_vfsops.c index 132dc9f54463..9c25cc524909 100644 --- a/usr/src/uts/common/fs/hsfs/hsfs_vfsops.c +++ b/usr/src/uts/common/fs/hsfs/hsfs_vfsops.c @@ -407,7 +407,7 @@ hsfs_unmount( mutex_exit(&hs_mounttab_lock); hsfs_fini_kstats(fsp); - (void) VOP_CLOSE(fsp->hsfs_devvp, FREAD, 1, (offset_t)0, cr); + (void) VOP_CLOSE(fsp->hsfs_devvp, FREAD, 1, (offset_t)0, cr, NULL); VN_RELE(fsp->hsfs_devvp); /* free path table space */ if (fsp->hsfs_ptbl != NULL) @@ -626,7 +626,7 @@ hs_mountfs( /* * Open the target device (file) for read only. */ - if (error = VOP_OPEN(&devvp, FREAD, cr)) { + if (error = VOP_OPEN(&devvp, FREAD, cr, NULL)) { VN_RELE(devvp); return (error); } @@ -641,7 +641,7 @@ hs_mountfs( } vap.va_mask = AT_SIZE; - if ((error = VOP_GETATTR(devvp, &vap, ATTR_COMM, cr)) != 0) { + if ((error = VOP_GETATTR(devvp, &vap, ATTR_COMM, cr, NULL)) != 0) { cmn_err(CE_NOTE, "Cannot get attributes of the CD-ROM driver"); goto cleanup; } @@ -937,7 +937,7 @@ hs_mountfs( return (0); cleanup: - (void) VOP_CLOSE(devvp, FREAD, 1, (offset_t)0, cr); + (void) VOP_CLOSE(devvp, FREAD, 1, (offset_t)0, cr, NULL); VN_RELE(devvp); if (fsp) kmem_free(fsp, sizeof (*fsp)); @@ -1365,14 +1365,14 @@ hs_getmdev(struct vfs *vfsp, char *fspec, int flags, dev_t *pdev, mode_t *mode, /* * Can we read from the device? */ - if ((error = VOP_ACCESS(vp, VREAD, 0, cr)) != 0 || + if ((error = VOP_ACCESS(vp, VREAD, 0, cr, NULL)) != 0 || (error = secpolicy_spec_open(cr, vp, FREAD)) != 0) { VN_RELE(vp); return (error); } vap.va_mask = AT_MODE; /* get protection mode */ - (void) VOP_GETATTR(vp, &vap, 0, CRED()); + (void) VOP_GETATTR(vp, &vap, 0, CRED(), NULL); *mode = vap.va_mode; dev = *pdev = vp->v_rdev; @@ -1507,7 +1507,7 @@ hsfs_mountroot(struct vfs *vfsp, enum whymountroot why) * multisession cd's. * * desc_sec is the same for high-sierra and iso 9660 formats, why - * there are two differnt #defines used in the code for this is + * there are two different #defines used in the code for this is * beyond me. These are standards, cast in concrete, right? * To be general, however, this function supports passing in different * values. diff --git a/usr/src/uts/common/fs/hsfs/hsfs_vnops.c b/usr/src/uts/common/fs/hsfs/hsfs_vnops.c index 29645bfe3ef0..bbd8afdd5a59 100644 --- a/usr/src/uts/common/fs/hsfs/hsfs_vnops.c +++ b/usr/src/uts/common/fs/hsfs/hsfs_vnops.c @@ -157,7 +157,10 @@ int hsched_invoke_strategy(struct hsfs *fsp); /* ARGSUSED */ static int -hsfs_fsync(vnode_t *cp, int syncflag, cred_t *cred) +hsfs_fsync(vnode_t *cp, + int syncflag, + cred_t *cred, + caller_context_t *ct) { return (0); } @@ -165,7 +168,10 @@ hsfs_fsync(vnode_t *cp, int syncflag, cred_t *cred) /*ARGSUSED*/ static int -hsfs_read(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cred, +hsfs_read(struct vnode *vp, + struct uio *uiop, + int ioflag, + struct cred *cred, struct caller_context *ct) { caddr_t base; @@ -299,7 +305,8 @@ hsfs_getattr( struct vnode *vp, struct vattr *vap, int flags, - struct cred *cred) + struct cred *cred, + caller_context_t *ct) { struct hsnode *hp; struct vfs *vfsp; @@ -342,7 +349,10 @@ hsfs_getattr( /*ARGSUSED*/ static int -hsfs_readlink(struct vnode *vp, struct uio *uiop, struct cred *cred) +hsfs_readlink(struct vnode *vp, + struct uio *uiop, + struct cred *cred, + caller_context_t *ct) { struct hsnode *hp; @@ -361,7 +371,9 @@ hsfs_readlink(struct vnode *vp, struct uio *uiop, struct cred *cred) /*ARGSUSED*/ static void -hsfs_inactive(struct vnode *vp, struct cred *cred) +hsfs_inactive(struct vnode *vp, + struct cred *cred, + caller_context_t *ct) { struct hsnode *hp; struct hsfs *fsp; @@ -427,7 +439,10 @@ hsfs_lookup( struct pathname *pnp, int flags, struct vnode *rdir, - struct cred *cred) + struct cred *cred, + caller_context_t *ct, + int *direntflags, + pathname_t *realpnp) { int error; int namelen = (int)strlen(nm); @@ -456,10 +471,12 @@ hsfs_lookup( /*ARGSUSED*/ static int hsfs_readdir( - struct vnode *vp, - struct uio *uiop, - struct cred *cred, - int *eofp) + struct vnode *vp, + struct uio *uiop, + struct cred *cred, + int *eofp, + caller_context_t *ct, + int flags) { struct hsnode *dhp; struct hsfs *fsp; @@ -634,8 +651,9 @@ hsfs_readdir( return (error); } +/*ARGSUSED2*/ static int -hsfs_fid(struct vnode *vp, struct fid *fidp) +hsfs_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct) { struct hsnode *hp; struct hsfid *fid; @@ -658,7 +676,10 @@ hsfs_fid(struct vnode *vp, struct fid *fidp) /*ARGSUSED*/ static int -hsfs_open(struct vnode **vpp, int flag, struct cred *cred) +hsfs_open(struct vnode **vpp, + int flag, + struct cred *cred, + caller_context_t *ct) { return (0); } @@ -670,7 +691,8 @@ hsfs_close( int flag, int count, offset_t offset, - struct cred *cred) + struct cred *cred, + caller_context_t *ct) { (void) cleanlocks(vp, ttoproc(curthread)->p_pid, 0); cleanshares(vp, ttoproc(curthread)->p_pid); @@ -679,7 +701,11 @@ hsfs_close( /*ARGSUSED2*/ static int -hsfs_access(struct vnode *vp, int mode, int flags, cred_t *cred) +hsfs_access(struct vnode *vp, + int mode, + int flags, + cred_t *cred, + caller_context_t *ct) { return (hs_access(vp, (mode_t)mode, cred)); } @@ -1531,6 +1557,7 @@ hsfs_getapage( return (err); } +/*ARGSUSED*/ static int hsfs_getpage( struct vnode *vp, @@ -1542,7 +1569,8 @@ hsfs_getpage( struct seg *seg, caddr_t addr, enum seg_rw rw, - struct cred *cred) + struct cred *cred, + caller_context_t *ct) { int err; uint_t filsiz; @@ -1671,11 +1699,12 @@ hsfs_putapage( /*ARGSUSED*/ static int hsfs_putpage( - struct vnode *vp, - offset_t off, - size_t len, - int flags, - struct cred *cr) + struct vnode *vp, + offset_t off, + size_t len, + int flags, + struct cred *cr, + caller_context_t *ct) { int error = 0; @@ -1755,7 +1784,8 @@ hsfs_map( uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cred) + struct cred *cred, + caller_context_t *ct) { struct segvn_crargs vn_a; int error; @@ -1821,7 +1851,8 @@ hsfs_addmap( uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct hsnode *hp; @@ -1846,7 +1877,8 @@ hsfs_delmap( uint_t prot, uint_t maxprot, uint_t flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct hsnode *hp; @@ -1863,7 +1895,11 @@ hsfs_delmap( /* ARGSUSED */ static int -hsfs_seek(struct vnode *vp, offset_t ooff, offset_t *noffp) +hsfs_seek( + struct vnode *vp, + offset_t ooff, + offset_t *noffp, + caller_context_t *ct) { return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0); } @@ -1877,7 +1913,8 @@ hsfs_frlock( int flag, offset_t offset, struct flk_callback *flk_cbp, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { struct hsnode *hp = VTOH(vp); @@ -1892,7 +1929,7 @@ hsfs_frlock( if (hp->hs_mapcnt > 0 && MANDLOCK(vp, hp->hs_dirent.mode)) return (EAGAIN); - return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr)); + return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct)); } static int @@ -2360,7 +2397,11 @@ hsched_enqueue_io(struct hsfs *fsp, struct hio *hsio, int ra) /* ARGSUSED */ static int -hsfs_pathconf(struct vnode *vp, int cmd, ulong_t *valp, struct cred *cr) +hsfs_pathconf(struct vnode *vp, + int cmd, + ulong_t *valp, + struct cred *cr, + caller_context_t *ct) { struct hsfs *fsp; @@ -2378,7 +2419,7 @@ hsfs_pathconf(struct vnode *vp, int cmd, ulong_t *valp, struct cred *cr) break; default: - error = fs_pathconf(vp, cmd, valp, cr); + error = fs_pathconf(vp, cmd, valp, cr, ct); } return (error); diff --git a/usr/src/uts/common/fs/lofs/lofs_vfsops.c b/usr/src/uts/common/fs/lofs/lofs_vfsops.c index 8616e90cd61a..8d654bcaeb02 100644 --- a/usr/src/uts/common/fs/lofs/lofs_vfsops.c +++ b/usr/src/uts/common/fs/lofs/lofs_vfsops.c @@ -287,7 +287,7 @@ lo_mount(struct vfs *vfsp, * intended filesystem, so we loopback mount the intended * filesystem instead of the AUTOFS filesystem. */ - (void) VOP_ACCESS(realrootvp, 0, 0, cr); + (void) VOP_ACCESS(realrootvp, 0, 0, cr, NULL); /* * We're interested in the top most filesystem. diff --git a/usr/src/uts/common/fs/lofs/lofs_vnops.c b/usr/src/uts/common/fs/lofs/lofs_vnops.c index 85138187c1b2..dbe2c7a8b9dc 100644 --- a/usr/src/uts/common/fs/lofs/lofs_vnops.c +++ b/usr/src/uts/common/fs/lofs/lofs_vnops.c @@ -48,7 +48,7 @@ */ static int -lo_open(vnode_t **vpp, int flag, struct cred *cr) +lo_open(vnode_t **vpp, int flag, struct cred *cr, caller_context_t *ct) { vnode_t *vp = *vpp; vnode_t *rvp; @@ -67,7 +67,7 @@ lo_open(vnode_t **vpp, int flag, struct cred *cr) * decide to release it. */ VN_HOLD(vp); - error = VOP_OPEN(&rvp, flag, cr); + error = VOP_OPEN(&rvp, flag, cr, ct); if (!error && rvp != vp) { /* @@ -106,13 +106,14 @@ lo_close( int flag, int count, offset_t offset, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { #ifdef LODEBUG lo_dprint(4, "lo_close vp %p realvp %p\n", vp, realvp(vp)); #endif vp = realvp(vp); - return (VOP_CLOSE(vp, flag, count, offset, cr)); + return (VOP_CLOSE(vp, flag, count, offset, cr, ct)); } static int @@ -144,20 +145,21 @@ lo_ioctl( intptr_t arg, int flag, struct cred *cr, - int *rvalp) + int *rvalp, + caller_context_t *ct) { #ifdef LODEBUG lo_dprint(4, "lo_ioctl vp %p realvp %p\n", vp, realvp(vp)); #endif vp = realvp(vp); - return (VOP_IOCTL(vp, cmd, arg, flag, cr, rvalp)); + return (VOP_IOCTL(vp, cmd, arg, flag, cr, rvalp, ct)); } static int -lo_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr) +lo_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr, caller_context_t *ct) { vp = realvp(vp); - return (VOP_SETFL(vp, oflags, nflags, cr)); + return (VOP_SETFL(vp, oflags, nflags, cr, ct)); } static int @@ -165,14 +167,15 @@ lo_getattr( vnode_t *vp, struct vattr *vap, int flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { int error; #ifdef LODEBUG lo_dprint(4, "lo_getattr vp %p realvp %p\n", vp, realvp(vp)); #endif - if (error = VOP_GETATTR(realvp(vp), vap, flags, cr)) + if (error = VOP_GETATTR(realvp(vp), vap, flags, cr, ct)) return (error); return (0); @@ -194,7 +197,12 @@ lo_setattr( } static int -lo_access(vnode_t *vp, int mode, int flags, struct cred *cr) +lo_access( + vnode_t *vp, + int mode, + int flags, + struct cred *cr, + caller_context_t *ct) { #ifdef LODEBUG lo_dprint(4, "lo_access vp %p realvp %p\n", vp, realvp(vp)); @@ -204,22 +212,22 @@ lo_access(vnode_t *vp, int mode, int flags, struct cred *cr) return (EROFS); } vp = realvp(vp); - return (VOP_ACCESS(vp, mode, flags, cr)); + return (VOP_ACCESS(vp, mode, flags, cr, ct)); } static int -lo_fsync(vnode_t *vp, int syncflag, struct cred *cr) +lo_fsync(vnode_t *vp, int syncflag, struct cred *cr, caller_context_t *ct) { #ifdef LODEBUG lo_dprint(4, "lo_fsync vp %p realvp %p\n", vp, realvp(vp)); #endif vp = realvp(vp); - return (VOP_FSYNC(vp, syncflag, cr)); + return (VOP_FSYNC(vp, syncflag, cr, ct)); } /*ARGSUSED*/ static void -lo_inactive(vnode_t *vp, struct cred *cr) +lo_inactive(vnode_t *vp, struct cred *cr, caller_context_t *ct) { #ifdef LODEBUG lo_dprint(4, "lo_inactive %p, realvp %p\n", vp, realvp(vp)); @@ -229,13 +237,13 @@ lo_inactive(vnode_t *vp, struct cred *cr) /* ARGSUSED */ static int -lo_fid(vnode_t *vp, struct fid *fidp) +lo_fid(vnode_t *vp, struct fid *fidp, caller_context_t *ct) { #ifdef LODEBUG lo_dprint(4, "lo_fid %p, realvp %p\n", vp, realvp(vp)); #endif vp = realvp(vp); - return (VOP_FID(vp, fidp)); + return (VOP_FID(vp, fidp, ct)); } /* @@ -288,7 +296,10 @@ lo_lookup( struct pathname *pnp, int flags, vnode_t *rdir, - struct cred *cr) + struct cred *cr, + caller_context_t *ct, + int *direntflags, + pathname_t *realpnp) { vnode_t *vp = NULL, *tvp = NULL, *nonlovp; int error, is_indirectloop; @@ -327,7 +338,8 @@ lo_lookup( /* * Do the normal lookup */ - if (error = VOP_LOOKUP(realdvp, nm, &vp, pnp, flags, rdir, cr)) { + if (error = VOP_LOOKUP(realdvp, nm, &vp, pnp, flags, rdir, cr, + ct, direntflags, realpnp)) { vp = NULL; goto out; } @@ -657,7 +669,9 @@ lo_create( int mode, vnode_t **vpp, struct cred *cr, - int flag) + int flag, + caller_context_t *ct, + vsecattr_t *vsecp) { int error; vnode_t *vp = NULL; @@ -670,7 +684,8 @@ lo_create( vp = realvp(*vpp); } - error = VOP_CREATE(realvp(dvp), nm, va, exclusive, mode, &vp, cr, flag); + error = VOP_CREATE(realvp(dvp), nm, va, exclusive, mode, &vp, cr, flag, + ct, vsecp); if (!error) { *vpp = makelonode(vp, vtoli(dvp->v_vfsp), 0); if (IS_DEVVP(*vpp)) { @@ -688,17 +703,28 @@ lo_create( } static int -lo_remove(vnode_t *dvp, char *nm, struct cred *cr) +lo_remove( + vnode_t *dvp, + char *nm, + struct cred *cr, + caller_context_t *ct, + int flags) { #ifdef LODEBUG lo_dprint(4, "lo_remove vp %p realvp %p\n", dvp, realvp(dvp)); #endif dvp = realvp(dvp); - return (VOP_REMOVE(dvp, nm, cr)); + return (VOP_REMOVE(dvp, nm, cr, ct, flags)); } static int -lo_link(vnode_t *tdvp, vnode_t *vp, char *tnm, struct cred *cr) +lo_link( + vnode_t *tdvp, + vnode_t *vp, + char *tnm, + struct cred *cr, + caller_context_t *ct, + int flags) { vnode_t *realvp; @@ -737,7 +763,7 @@ lo_link(vnode_t *tdvp, vnode_t *vp, char *tnm, struct cred *cr) * * We use VOP_REALVP here to continue the search. */ - if (VOP_REALVP(vp, &realvp) == 0) + if (VOP_REALVP(vp, &realvp, ct) == 0) vp = realvp; while (vn_matchops(tdvp, lo_vnodeops)) { @@ -745,7 +771,7 @@ lo_link(vnode_t *tdvp, vnode_t *vp, char *tnm, struct cred *cr) } if (vp->v_vfsp != tdvp->v_vfsp) return (EXDEV); - return (VOP_LINK(tdvp, vp, tnm, cr)); + return (VOP_LINK(tdvp, vp, tnm, cr, ct, flags)); } static int @@ -754,7 +780,9 @@ lo_rename( char *onm, vnode_t *ndvp, char *nnm, - struct cred *cr) + struct cred *cr, + caller_context_t *ct, + int flags) { vnode_t *tnvp; @@ -789,7 +817,13 @@ lo_rename( */ if (vn_matchops(ndvp, lo_vnodeops)) /* Not our problem. */ goto rename; - if (VOP_LOOKUP(ndvp, nnm, &tnvp, NULL, 0, NULL, cr) != 0) + + /* + * XXXci - Once case-insensitive behavior is implemented, it should + * be added here. + */ + if (VOP_LOOKUP(ndvp, nnm, &tnvp, NULL, 0, NULL, cr, + ct, NULL, NULL) != 0) goto rename; if (tnvp->v_type != VDIR) { VN_RELE(tnvp); @@ -822,7 +856,7 @@ lo_rename( if (odvp->v_vfsp != ndvp->v_vfsp) return (EXDEV); } - return (VOP_RENAME(odvp, onm, ndvp, nnm, cr)); + return (VOP_RENAME(odvp, onm, ndvp, nnm, cr, ct, flags)); } static int @@ -831,21 +865,24 @@ lo_mkdir( char *nm, struct vattr *va, vnode_t **vpp, - struct cred *cr) + struct cred *cr, + caller_context_t *ct, + int flags, + vsecattr_t *vsecp) { int error; #ifdef LODEBUG lo_dprint(4, "lo_mkdir vp %p realvp %p\n", dvp, realvp(dvp)); #endif - error = VOP_MKDIR(realvp(dvp), nm, va, vpp, cr); + error = VOP_MKDIR(realvp(dvp), nm, va, vpp, cr, ct, flags, vsecp); if (!error) *vpp = makelonode(*vpp, vtoli(dvp->v_vfsp), 0); return (error); } static int -lo_realvp(vnode_t *vp, vnode_t **vpp) +lo_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct) { #ifdef LODEBUG lo_dprint(4, "lo_realvp %p\n", vp); @@ -853,7 +890,7 @@ lo_realvp(vnode_t *vp, vnode_t **vpp) while (vn_matchops(vp, lo_vnodeops)) vp = realvp(vp); - if (VOP_REALVP(vp, vpp) != 0) + if (VOP_REALVP(vp, vpp, ct) != 0) *vpp = vp; return (0); } @@ -863,7 +900,9 @@ lo_rmdir( vnode_t *dvp, char *nm, vnode_t *cdir, - struct cred *cr) + struct cred *cr, + caller_context_t *ct, + int flags) { vnode_t *rvp = cdir; @@ -872,9 +911,9 @@ lo_rmdir( #endif /* if cdir is lofs vnode ptr get its real vnode ptr */ if (vn_matchops(dvp, vn_getops(rvp))) - (void) lo_realvp(cdir, &rvp); + (void) lo_realvp(cdir, &rvp, ct); dvp = realvp(dvp); - return (VOP_RMDIR(dvp, nm, rvp, cr)); + return (VOP_RMDIR(dvp, nm, rvp, cr, ct, flags)); } static int @@ -883,30 +922,42 @@ lo_symlink( char *lnm, struct vattr *tva, char *tnm, - struct cred *cr) + struct cred *cr, + caller_context_t *ct, + int flags) { #ifdef LODEBUG lo_dprint(4, "lo_symlink vp %p realvp %p\n", dvp, realvp(dvp)); #endif dvp = realvp(dvp); - return (VOP_SYMLINK(dvp, lnm, tva, tnm, cr)); + return (VOP_SYMLINK(dvp, lnm, tva, tnm, cr, ct, flags)); } static int -lo_readlink(vnode_t *vp, struct uio *uiop, struct cred *cr) +lo_readlink( + vnode_t *vp, + struct uio *uiop, + struct cred *cr, + caller_context_t *ct) { vp = realvp(vp); - return (VOP_READLINK(vp, uiop, cr)); + return (VOP_READLINK(vp, uiop, cr, ct)); } static int -lo_readdir(vnode_t *vp, struct uio *uiop, struct cred *cr, int *eofp) +lo_readdir( + vnode_t *vp, + struct uio *uiop, + struct cred *cr, + int *eofp, + caller_context_t *ct, + int flags) { #ifdef LODEBUG lo_dprint(4, "lo_readdir vp %p realvp %p\n", vp, realvp(vp)); #endif vp = realvp(vp); - return (VOP_READDIR(vp, uiop, cr, eofp)); + return (VOP_READDIR(vp, uiop, cr, eofp, ct, flags)); } static int @@ -924,20 +975,20 @@ lo_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ct) } static int -lo_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) +lo_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) { vp = realvp(vp); - return (VOP_SEEK(vp, ooff, noffp)); + return (VOP_SEEK(vp, ooff, noffp, ct)); } static int -lo_cmp(vnode_t *vp1, vnode_t *vp2) +lo_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct) { while (vn_matchops(vp1, lo_vnodeops)) vp1 = realvp(vp1); while (vn_matchops(vp2, lo_vnodeops)) vp2 = realvp(vp2); - return (VOP_CMP(vp1, vp2)); + return (VOP_CMP(vp1, vp2, ct)); } static int @@ -948,10 +999,11 @@ lo_frlock( int flag, offset_t offset, struct flk_callback *flk_cbp, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { vp = realvp(vp); - return (VOP_FRLOCK(vp, cmd, bfp, flag, offset, flk_cbp, cr)); + return (VOP_FRLOCK(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct)); } static int @@ -979,17 +1031,25 @@ lo_getpage( struct seg *seg, caddr_t addr, enum seg_rw rw, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { vp = realvp(vp); - return (VOP_GETPAGE(vp, off, len, prot, parr, psz, seg, addr, rw, cr)); + return (VOP_GETPAGE(vp, off, len, prot, parr, psz, seg, addr, rw, cr, + ct)); } static int -lo_putpage(vnode_t *vp, offset_t off, size_t len, int flags, struct cred *cr) +lo_putpage( + vnode_t *vp, + offset_t off, + size_t len, + int flags, + struct cred *cr, + caller_context_t *ct) { vp = realvp(vp); - return (VOP_PUTPAGE(vp, off, len, flags, cr)); + return (VOP_PUTPAGE(vp, off, len, flags, cr, ct)); } static int @@ -1002,10 +1062,11 @@ lo_map( uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { vp = realvp(vp); - return (VOP_MAP(vp, off, as, addrp, len, prot, maxprot, flags, cr)); + return (VOP_MAP(vp, off, as, addrp, len, prot, maxprot, flags, cr, ct)); } static int @@ -1018,10 +1079,12 @@ lo_addmap( uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { vp = realvp(vp); - return (VOP_ADDMAP(vp, off, as, addr, len, prot, maxprot, flags, cr)); + return (VOP_ADDMAP(vp, off, as, addr, len, prot, maxprot, flags, cr, + ct)); } static int @@ -1034,10 +1097,12 @@ lo_delmap( uint_t prot, uint_t maxprot, uint_t flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { vp = realvp(vp); - return (VOP_DELMAP(vp, off, as, addr, len, prot, maxprot, flags, cr)); + return (VOP_DELMAP(vp, off, as, addr, len, prot, maxprot, flags, cr, + ct)); } static int @@ -1046,24 +1111,30 @@ lo_poll( short events, int anyyet, short *reventsp, - struct pollhead **phpp) + struct pollhead **phpp, + caller_context_t *ct) { vp = realvp(vp); - return (VOP_POLL(vp, events, anyyet, reventsp, phpp)); + return (VOP_POLL(vp, events, anyyet, reventsp, phpp, ct)); } static int -lo_dump(vnode_t *vp, caddr_t addr, int bn, int count) +lo_dump(vnode_t *vp, caddr_t addr, int bn, int count, caller_context_t *ct) { vp = realvp(vp); - return (VOP_DUMP(vp, addr, bn, count)); + return (VOP_DUMP(vp, addr, bn, count, ct)); } static int -lo_pathconf(vnode_t *vp, int cmd, ulong_t *valp, struct cred *cr) +lo_pathconf( + vnode_t *vp, + int cmd, + ulong_t *valp, + struct cred *cr, + caller_context_t *ct) { vp = realvp(vp); - return (VOP_PATHCONF(vp, cmd, valp, cr)); + return (VOP_PATHCONF(vp, cmd, valp, cr, ct)); } static int @@ -1073,41 +1144,64 @@ lo_pageio( u_offset_t io_off, size_t io_len, int flags, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { vp = realvp(vp); - return (VOP_PAGEIO(vp, pp, io_off, io_len, flags, cr)); + return (VOP_PAGEIO(vp, pp, io_off, io_len, flags, cr, ct)); } static void -lo_dispose(vnode_t *vp, page_t *pp, int fl, int dn, cred_t *cr) +lo_dispose( + vnode_t *vp, + page_t *pp, + int fl, + int dn, + cred_t *cr, + caller_context_t *ct) { vp = realvp(vp); if (vp != NULL && !VN_ISKAS(vp)) - VOP_DISPOSE(vp, pp, fl, dn, cr); + VOP_DISPOSE(vp, pp, fl, dn, cr, ct); } static int -lo_setsecattr(vnode_t *vp, vsecattr_t *secattr, int flags, struct cred *cr) +lo_setsecattr( + vnode_t *vp, + vsecattr_t *secattr, + int flags, + struct cred *cr, + caller_context_t *ct) { if (vn_is_readonly(vp)) return (EROFS); vp = realvp(vp); - return (VOP_SETSECATTR(vp, secattr, flags, cr)); + return (VOP_SETSECATTR(vp, secattr, flags, cr, ct)); } static int -lo_getsecattr(vnode_t *vp, vsecattr_t *secattr, int flags, struct cred *cr) +lo_getsecattr( + vnode_t *vp, + vsecattr_t *secattr, + int flags, + struct cred *cr, + caller_context_t *ct) { vp = realvp(vp); - return (VOP_GETSECATTR(vp, secattr, flags, cr)); + return (VOP_GETSECATTR(vp, secattr, flags, cr, ct)); } static int -lo_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr) +lo_shrlock( + vnode_t *vp, + int cmd, + struct shrlock *shr, + int flag, + cred_t *cr, + caller_context_t *ct) { vp = realvp(vp); - return (VOP_SHRLOCK(vp, cmd, shr, flag, cr)); + return (VOP_SHRLOCK(vp, cmd, shr, flag, cr, ct)); } /* diff --git a/usr/src/uts/common/fs/lookup.c b/usr/src/uts/common/fs/lookup.c index 8ef37c431363..d30a35828355 100644 --- a/usr/src/uts/common/fs/lookup.c +++ b/usr/src/uts/common/fs/lookup.c @@ -209,6 +209,8 @@ lookuppnvp( int error; int nlink; int lookup_flags; + struct pathname presrvd; /* case preserved name */ + struct pathname *pp = NULL; vnode_t *startvp; vnode_t *zonevp = curproc->p_zone->zone_rootvp; /* zone root */ int must_be_directory = 0; @@ -219,7 +221,14 @@ lookuppnvp( cvp = NULL; if (rpnp) rpnp->pn_pathlen = 0; + lookup_flags = dirvpp ? LOOKUP_DIR : 0; + if (flags & FIGNORECASE) { + lookup_flags |= FIGNORECASE; + pn_alloc(&presrvd); + pp = &presrvd; + } + #ifdef C2_AUDIT if (audit_active) audit_anchorpath(pnp, vp == rootvp); @@ -319,6 +328,8 @@ lookuppnvp( (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) { vfs_unlock(vfsp); VN_RELE(cvp); + if (pp) + pn_free(pp); return (EIO); } VN_HOLD(vp); @@ -346,14 +357,14 @@ lookuppnvp( * be found. */ if ((flags & LOOKUP_CHECKREAD) && - (error = VOP_ACCESS(vp, VREAD, 0, cr)) != 0) + (error = VOP_ACCESS(vp, VREAD, 0, cr, NULL)) != 0) goto bad; /* * Perform a lookup in the current directory. */ error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags, - rootvp, cr); + rootvp, cr, NULL, NULL, pp); /* * Retry with kcred - If crossing mount points & error is EACCES. @@ -371,7 +382,7 @@ lookuppnvp( */ if ((error == EACCES) && retry_with_kcred) error = VOP_LOOKUP(vp, component, &tvp, pnp, lookup_flags, - rootvp, zone_kcred()); + rootvp, zone_kcred(), NULL, NULL, pp); cvp = tvp; if (error) { @@ -402,6 +413,8 @@ lookuppnvp( *compvpp = NULL; if (rootvp != rootdir) VN_RELE(rootvp); + if (pp) + pn_free(pp); return (0); } @@ -521,9 +534,19 @@ lookuppnvp( if (rpnp->pn_pathlen != 0 && rpnp->pn_path[rpnp->pn_pathlen-1] != '/') rpnp->pn_path[rpnp->pn_pathlen++] = '/'; - error = copystr(component, - rpnp->pn_path + rpnp->pn_pathlen, - rpnp->pn_bufsize - rpnp->pn_pathlen, &len); + if (flags & FIGNORECASE) { + /* + * Return the case-preserved name + * within the resolved path. + */ + error = copystr(pp->pn_path, + rpnp->pn_path + rpnp->pn_pathlen, + rpnp->pn_bufsize - rpnp->pn_pathlen, &len); + } else { + error = copystr(component, + rpnp->pn_path + rpnp->pn_pathlen, + rpnp->pn_bufsize - rpnp->pn_pathlen, &len); + } if (error) /* copystr() returns ENAMETOOLONG */ goto bad; rpnp->pn_pathlen += (len - 1); @@ -560,6 +583,8 @@ lookuppnvp( VN_RELE(cvp); if (rootvp != rootdir) VN_RELE(rootvp); + if (pp) + pn_free(pp); return (EINVAL); } #ifdef C2_AUDIT @@ -592,6 +617,8 @@ lookuppnvp( VN_RELE(cvp); if (rootvp != rootdir) VN_RELE(rootvp); + if (pp) + pn_free(pp); return (0); } @@ -649,6 +676,8 @@ lookuppnvp( VN_RELE(vp); if (rootvp != rootdir) VN_RELE(rootvp); + if (pp) + pn_free(pp); return (error); } @@ -791,15 +820,15 @@ vnode_match(vnode_t *v1, vnode_t *v2, cred_t *cr) * are currently in '/proc/self/fd', then '/proc/self/cwd' will compare * as the same vnode. */ - if (VOP_GETATTR(v1, &v1attr, 0, cr) != 0 || - VOP_GETATTR(v2, &v2attr, 0, cr) != 0 || + if (VOP_GETATTR(v1, &v1attr, 0, cr, NULL) != 0 || + VOP_GETATTR(v2, &v2attr, 0, cr, NULL) != 0 || v1attr.va_type == VLNK || v2attr.va_type == VLNK) return (0); v1attr.va_mask = v2attr.va_mask = AT_TYPE | AT_FSID | AT_NODEID; - if (VOP_GETATTR(v1, &v1attr, ATTR_REAL, cr) != 0 || - VOP_GETATTR(v2, &v2attr, ATTR_REAL, cr) != 0) + if (VOP_GETATTR(v1, &v1attr, ATTR_REAL, cr, NULL) != 0 || + VOP_GETATTR(v2, &v2attr, ATTR_REAL, cr, NULL) != 0) return (0); return (v1attr.va_fsid == v2attr.va_fsid && @@ -839,7 +868,7 @@ dirfindvp(vnode_t *vrootp, vnode_t *dvp, vnode_t *tvp, cred_t *cr, char *dbuf, uio.uio_extflg = UIO_COPY_CACHED; uio.uio_loffset = 0; - if ((error = VOP_ACCESS(dvp, VREAD, 0, cr)) != 0) + if ((error = VOP_ACCESS(dvp, VREAD, 0, cr, NULL)) != 0) return (error); while (!eof) { @@ -848,7 +877,7 @@ dirfindvp(vnode_t *vrootp, vnode_t *dvp, vnode_t *tvp, cred_t *cr, char *dbuf, iov.iov_len = dlen; (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL); - error = VOP_READDIR(dvp, &uio, cr, &eof); + error = VOP_READDIR(dvp, &uio, cr, &eof, NULL, 0); VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL); dbuflen = dlen - uio.uio_resid; @@ -869,7 +898,7 @@ dirfindvp(vnode_t *vrootp, vnode_t *dvp, vnode_t *tvp, cred_t *cr, char *dbuf, } error = VOP_LOOKUP(dvp, dp->d_name, &cmpvp, &pnp, 0, - vrootp, cr); + vrootp, cr, NULL, NULL, NULL); /* * We only want to bail out if there was an error other @@ -900,7 +929,8 @@ dirfindvp(vnode_t *vrootp, vnode_t *dvp, vnode_t *tvp, cred_t *cr, char *dbuf, * rare conditions (races and the special .zfs directory). */ if (error == 0) { - error = VOP_LOOKUP(dvp, ".zfs", &cmpvp, &pnp, 0, vrootp, cr); + error = VOP_LOOKUP(dvp, ".zfs", &cmpvp, &pnp, 0, vrootp, cr, + NULL, NULL, NULL); if (error == 0) { if (vnode_match(tvp, cmpvp, cr)) { (void) strcpy(dp->d_name, ".zfs"); @@ -955,7 +985,8 @@ localpath(char *path, struct vnode *vrootp, cred_t *cr) if (vn_ismntpt(vp) && traverse(&vp) != 0) break; - if (VOP_LOOKUP(vp, component, &cvp, &pn, 0, rootdir, cr) != 0) + if (VOP_LOOKUP(vp, component, &cvp, &pn, 0, rootdir, cr, + NULL, NULL, NULL) != 0) break; VN_RELE(vp); @@ -1087,8 +1118,8 @@ dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, cred_t *cr) */ if (vp->v_flag & VROOT) vp = vn_under(vp); - if ((err = VOP_LOOKUP(vp, "..", &pvp, &emptypn, 0, vrootp, cr)) - != 0) + if ((err = VOP_LOOKUP(vp, "..", &pvp, &emptypn, 0, vrootp, cr, + NULL, NULL, NULL)) != 0) goto out; /* @@ -1210,10 +1241,10 @@ vnodetopath_common(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, * lofs. The only difference between procfs and lofs is that opening * the file will return the underling vnode in the case of procfs. */ - if (vp->v_type == VDIR && VOP_REALVP(vp, &realvp) == 0 && + if (vp->v_type == VDIR && VOP_REALVP(vp, &realvp, NULL) == 0 && realvp != vp) { VN_HOLD(vp); - if (VOP_OPEN(&vp, FREAD, cr) == 0) + if (VOP_OPEN(&vp, FREAD, cr, NULL) == 0) doclose = 1; else VN_RELE(vp); @@ -1314,7 +1345,7 @@ vnodetopath_common(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, pn_free(&rpn); VN_RELE(vrootp); if (doclose) { - (void) VOP_CLOSE(vp, FREAD, 1, 0, cr); + (void) VOP_CLOSE(vp, FREAD, 1, 0, cr, NULL); VN_RELE(vp); } return (0); @@ -1356,7 +1387,7 @@ vnodetopath_common(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, VN_RELE(vrootp); if (doclose) { - (void) VOP_CLOSE(vp, FREAD, 1, 0, cr); + (void) VOP_CLOSE(vp, FREAD, 1, 0, cr, NULL); VN_RELE(vp); } @@ -1394,7 +1425,7 @@ dogetcwd(char *buf, size_t buflen) /* * Make sure we have permission to access the current directory. */ - if ((ret = VOP_ACCESS(vp, VEXEC, 0, CRED())) != 0) { + if ((ret = VOP_ACCESS(vp, VEXEC, 0, CRED(), NULL)) != 0) { if (cwd != NULL) refstr_rele(cwd); VN_RELE(vp); diff --git a/usr/src/uts/common/fs/mntfs/mntvnops.c b/usr/src/uts/common/fs/mntfs/mntvnops.c index 9083d5c5b2b0..9e519167cf03 100644 --- a/usr/src/uts/common/fs/mntfs/mntvnops.c +++ b/usr/src/uts/common/fs/mntfs/mntvnops.c @@ -670,7 +670,7 @@ mntfs_getmntopts(struct vfs *vfsp, char **bufp, size_t *lenp) /* ARGSUSED */ static int -mntopen(vnode_t **vpp, int flag, cred_t *cr) +mntopen(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { vnode_t *vp = *vpp; mntnode_t *nmnp; @@ -694,7 +694,8 @@ mntopen(vnode_t **vpp, int flag, cred_t *cr) /* ARGSUSED */ static int -mntclose(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) +mntclose(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, + caller_context_t *ct) { mntnode_t *mnp = VTOM(vp); @@ -767,7 +768,8 @@ mntread(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cred, caller_context_t *ct) static int -mntgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +mntgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { mntnode_t *mnp = VTOM(vp); int error; @@ -786,7 +788,7 @@ mntgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) /* * Attributes are same as underlying file with modifications */ - if (error = VOP_GETATTR(rvp, vap, flags, cr)) + if (error = VOP_GETATTR(rvp, vap, flags, cr, ct)) return (error); /* @@ -830,7 +832,8 @@ mntgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) static int -mntaccess(vnode_t *vp, int mode, int flags, cred_t *cr) +mntaccess(vnode_t *vp, int mode, int flags, cred_t *cr, + caller_context_t *ct) { mntnode_t *mnp = VTOM(vp); @@ -840,7 +843,7 @@ mntaccess(vnode_t *vp, int mode, int flags, cred_t *cr) /* * Do access check on the underlying directory vnode. */ - return (VOP_ACCESS(mnp->mnt_mountvp, mode, flags, cr)); + return (VOP_ACCESS(mnp->mnt_mountvp, mode, flags, cr, ct)); } @@ -882,14 +885,14 @@ mntfreenode(mntnode_t *mnp) /* ARGSUSED */ static int -mntfsync(vnode_t *vp, int syncflag, cred_t *cr) +mntfsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) { return (0); } /* ARGSUSED */ static void -mntinactive(vnode_t *vp, cred_t *cr) +mntinactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { mntnode_t *mnp = VTOM(vp); @@ -898,7 +901,8 @@ mntinactive(vnode_t *vp, cred_t *cr) /* ARGSUSED */ static int -mntseek(vnode_t *vp, offset_t ooff, offset_t *noffp) +mntseek(vnode_t *vp, offset_t ooff, offset_t *noffp, + caller_context_t *ct) { if (*noffp == 0) VTOM(vp)->mnt_offset = 0; @@ -913,7 +917,8 @@ mntseek(vnode_t *vp, offset_t ooff, offset_t *noffp) */ /* ARGSUSED */ static int -mntpoll(vnode_t *vp, short ev, int any, short *revp, pollhead_t **phpp) +mntpoll(vnode_t *vp, short ev, int any, short *revp, pollhead_t **phpp, + caller_context_t *ct) { mntnode_t *mnp = VTOM(vp); mntsnap_t *snap = &mnp->mnt_read; @@ -951,7 +956,7 @@ mntpoll(vnode_t *vp, short ev, int any, short *revp, pollhead_t **phpp) /* ARGSUSED */ static int mntioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, - cred_t *cr, int *rvalp) + cred_t *cr, int *rvalp, caller_context_t *ct) { uint_t *up = (uint_t *)arg; mntnode_t *mnp = VTOM(vp); diff --git a/usr/src/uts/common/fs/namefs/namevfs.c b/usr/src/uts/common/fs/namefs/namevfs.c index 88bafaa135f8..a6a4e083a646 100644 --- a/usr/src/uts/common/fs/namefs/namevfs.c +++ b/usr/src/uts/common/fs/namefs/namevfs.c @@ -357,7 +357,7 @@ nm_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *crp) */ rvp = NULLVP; if (vn_matchops(mvp, spec_getvnodeops()) && - VOP_REALVP(mvp, &rvp) == 0 && rvp && + VOP_REALVP(mvp, &rvp, NULL) == 0 && rvp && vn_matchops(rvp, devpts_getvnodeops())) { releasef(namefdp.fd); return (ENOTSUP); @@ -397,11 +397,11 @@ nm_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *crp) mutex_init(&nodep->nm_lock, NULL, MUTEX_DEFAULT, NULL); vattrp = &nodep->nm_vattr; vattrp->va_mask = AT_ALL; - if (error = VOP_GETATTR(mvp, vattrp, 0, crp)) + if (error = VOP_GETATTR(mvp, vattrp, 0, crp, NULL)) goto out; filevattr.va_mask = AT_ALL; - if (error = VOP_GETATTR(filevp, &filevattr, 0, crp)) + if (error = VOP_GETATTR(filevp, &filevattr, 0, crp, NULL)) goto out; /* * Make sure the user is the owner of the mount point @@ -652,7 +652,7 @@ nm_sync(vfs_t *vfsp, short flag, cred_t *crp) if (flag & SYNC_CLOSE) return (nm_umountall(nodep->nm_filevp, crp)); - return (VOP_FSYNC(nodep->nm_filevp, FSYNC, crp)); + return (VOP_FSYNC(nodep->nm_filevp, FSYNC, crp, NULL)); } /* diff --git a/usr/src/uts/common/fs/namefs/namevno.c b/usr/src/uts/common/fs/namefs/namevno.c index ab4767e578a1..f62af2b7f2bb 100644 --- a/usr/src/uts/common/fs/namefs/namevno.c +++ b/usr/src/uts/common/fs/namefs/namevno.c @@ -77,7 +77,7 @@ * file system, with the parent being the initial mount. */ int -nm_open(vnode_t **vpp, int flag, cred_t *crp) +nm_open(vnode_t **vpp, int flag, cred_t *crp, caller_context_t *ct) { struct namenode *nodep = VTONM(*vpp); int error = 0; @@ -95,7 +95,7 @@ nm_open(vnode_t **vpp, int flag, cred_t *crp) infilevp = outfilevp = nodep->nm_filevp; VN_HOLD(outfilevp); - if ((error = VOP_OPEN(&outfilevp, flag, crp)) != 0) { + if ((error = VOP_OPEN(&outfilevp, flag, crp, ct)) != 0) { VN_RELE(outfilevp); return (error); } @@ -163,16 +163,17 @@ nm_open(vnode_t **vpp, int flag, cred_t *crp) * the file descriptor. */ static int -nm_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *crp) +nm_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *crp, + caller_context_t *ct) { struct namenode *nodep = VTONM(vp); int error = 0; (void) cleanlocks(vp, ttoproc(curthread)->p_pid, 0); cleanshares(vp, ttoproc(curthread)->p_pid); - error = VOP_CLOSE(nodep->nm_filevp, flag, count, offset, crp); + error = VOP_CLOSE(nodep->nm_filevp, flag, count, offset, crp, ct); if (count == 1) { - (void) VOP_FSYNC(nodep->nm_filevp, FSYNC, crp); + (void) VOP_FSYNC(nodep->nm_filevp, FSYNC, crp, ct); /* * Before VN_RELE() we need to remove the vnode from * the hash table. We should only do so in the NMNMNT case. @@ -205,9 +206,10 @@ nm_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *crp, } static int -nm_ioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr, int *rvalp) +nm_ioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr, int *rvalp, + caller_context_t *ct) { - return (VOP_IOCTL(VTONM(vp)->nm_filevp, cmd, arg, mode, cr, rvalp)); + return (VOP_IOCTL(VTONM(vp)->nm_filevp, cmd, arg, mode, cr, rvalp, ct)); } /* @@ -216,7 +218,8 @@ nm_ioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr, int *rvalp) */ /* ARGSUSED */ static int -nm_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *crp) +nm_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *crp, + caller_context_t *ct) { struct namenode *nodep = VTONM(vp); struct vattr va; @@ -227,7 +230,7 @@ nm_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *crp) mutex_exit(&nodep->nm_lock); if ((va.va_mask = vap->va_mask & AT_SIZE) != 0) { - if (error = VOP_GETATTR(nodep->nm_filevp, &va, flags, crp)) + if (error = VOP_GETATTR(nodep->nm_filevp, &va, flags, crp, ct)) return (error); vap->va_size = va.va_size; } @@ -336,7 +339,8 @@ nm_setattr( */ /* ARGSUSED */ static int -nm_access(vnode_t *vp, int mode, int flags, cred_t *crp) +nm_access(vnode_t *vp, int mode, int flags, cred_t *crp, + caller_context_t *ct) { struct namenode *nodep = VTONM(vp); int error; @@ -345,7 +349,7 @@ nm_access(vnode_t *vp, int mode, int flags, cred_t *crp) error = nm_access_unlocked(nodep, mode, crp); mutex_exit(&nodep->nm_lock); if (error == 0) - return (VOP_ACCESS(nodep->nm_filevp, mode, flags, crp)); + return (VOP_ACCESS(nodep->nm_filevp, mode, flags, crp, ct)); else return (error); } @@ -358,13 +362,14 @@ nm_access(vnode_t *vp, int mode, int flags, cred_t *crp) /*ARGSUSED*/ static int nm_create(vnode_t *dvp, char *name, vattr_t *vap, enum vcexcl excl, - int mode, vnode_t **vpp, cred_t *cr, int flag) + int mode, vnode_t **vpp, cred_t *cr, int flag, + caller_context_t *ct, vsecattr_t *vsecp) { int error; ASSERT(dvp && *name == '\0'); if (excl == NONEXCL) { - if (mode && (error = nm_access(dvp, mode, 0, cr)) != 0) + if (mode && (error = nm_access(dvp, mode, 0, cr, ct)) != 0) return (error); VN_HOLD(dvp); return (0); @@ -377,21 +382,22 @@ nm_create(vnode_t *dvp, char *name, vattr_t *vap, enum vcexcl excl, */ /*ARGSUSED*/ static int -nm_link(vnode_t *tdvp, vnode_t *vp, char *tnm, cred_t *crp) +nm_link(vnode_t *tdvp, vnode_t *vp, char *tnm, cred_t *crp, + caller_context_t *ct, int flags) { return (EXDEV); } static int -nm_fsync(vnode_t *vp, int syncflag, cred_t *crp) +nm_fsync(vnode_t *vp, int syncflag, cred_t *crp, caller_context_t *ct) { - return (VOP_FSYNC(VTONM(vp)->nm_filevp, syncflag, crp)); + return (VOP_FSYNC(VTONM(vp)->nm_filevp, syncflag, crp, ct)); } /* Free the namenode */ /* ARGSUSED */ static void -nm_inactive(vnode_t *vp, cred_t *crp) +nm_inactive(vnode_t *vp, cred_t *crp, caller_context_t *ct) { struct namenode *nodep = VTONM(vp); @@ -413,9 +419,9 @@ nm_inactive(vnode_t *vp, cred_t *crp) } static int -nm_fid(vnode_t *vp, struct fid *fidnodep) +nm_fid(vnode_t *vp, struct fid *fidnodep, caller_context_t *ct) { - return (VOP_FID(VTONM(vp)->nm_filevp, fidnodep)); + return (VOP_FID(VTONM(vp)->nm_filevp, fidnodep, ct)); } static int @@ -431,21 +437,21 @@ nm_rwunlock(vnode_t *vp, int write, caller_context_t *ctp) } static int -nm_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) +nm_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) { - return (VOP_SEEK(VTONM(vp)->nm_filevp, ooff, noffp)); + return (VOP_SEEK(VTONM(vp)->nm_filevp, ooff, noffp, ct)); } /* * Return the vnode representing the file descriptor in vpp. */ static int -nm_realvp(vnode_t *vp, vnode_t **vpp) +nm_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct) { struct vnode *rvp; vp = VTONM(vp)->nm_filevp; - if (VOP_REALVP(vp, &rvp) == 0) + if (VOP_REALVP(vp, &rvp, ct) == 0) vp = rvp; *vpp = vp; return (0); @@ -453,9 +459,10 @@ nm_realvp(vnode_t *vp, vnode_t **vpp) static int nm_poll(vnode_t *vp, short events, int anyyet, short *reventsp, - pollhead_t **phpp) + pollhead_t **phpp, caller_context_t *ct) { - return (VOP_POLL(VTONM(vp)->nm_filevp, events, anyyet, reventsp, phpp)); + return (VOP_POLL(VTONM(vp)->nm_filevp, events, anyyet, reventsp, + phpp, ct)); } struct vnodeops *nm_vnodeops; diff --git a/usr/src/uts/common/fs/nbmlock.c b/usr/src/uts/common/fs/nbmlock.c index bb9dd887229e..add00c6b1f02 100644 --- a/usr/src/uts/common/fs/nbmlock.c +++ b/usr/src/uts/common/fs/nbmlock.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,8 +19,8 @@ * CDDL HEADER END */ /* - * Copyright (c) 2001 by Sun Microsystems, Inc. - * All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" @@ -133,13 +132,14 @@ nbl_conflict(vnode_t *vp, nbl_op_t op, /* attempted operation */ u_offset_t offset, /* ignore if not I/O */ ssize_t length, /* ignore if not I/O */ - int svmand) /* System V mandatory locking */ + int svmand, /* System V mandatory locking */ + caller_context_t *ct) /* caller context */ { ASSERT(nbl_in_crit(vp)); ASSERT(op == NBL_READ || op == NBL_WRITE || op == NBL_RENAME || op == NBL_REMOVE || op == NBL_READWRITE); - if (nbl_share_conflict(vp, op)) { + if (nbl_share_conflict(vp, op, ct)) { return (1); } @@ -150,7 +150,7 @@ nbl_conflict(vnode_t *vp, if (op == NBL_REMOVE || op == NBL_RENAME) return (0); - return (nbl_lock_conflict(vp, op, offset, length, svmand)); + return (nbl_lock_conflict(vp, op, offset, length, svmand, ct)); } /* @@ -167,7 +167,7 @@ nbl_svmand(vnode_t *vp, cred_t *cr, int *svp) int error; va.va_mask = AT_MODE; - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); if (error != 0) return (error); diff --git a/usr/src/uts/common/fs/nfs/nfs3_srv.c b/usr/src/uts/common/fs/nfs/nfs3_srv.c index 4f8276d75fa4..0660a85e5913 100644 --- a/usr/src/uts/common/fs/nfs/nfs3_srv.c +++ b/usr/src/uts/common/fs/nfs/nfs3_srv.c @@ -262,7 +262,8 @@ rfs3_setattr(SETATTR3args *args, SETATTR3res *resp, struct exportinfo *exi, offset = bva.va_size; length = ava.va_size - bva.va_size; } - if (nbl_conflict(vp, NBL_WRITE, offset, length, 0)) { + if (nbl_conflict(vp, NBL_WRITE, offset, length, 0, + NULL)) { error = EACCES; goto out; } @@ -298,7 +299,7 @@ rfs3_setattr(SETATTR3args *args, SETATTR3res *resp, struct exportinfo *exi, /* * Force modified metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cr); + (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL); if (error) goto out; @@ -369,11 +370,11 @@ rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_pre_op_attr) { dva.va_mask = AT_ALL; - dvap = VOP_GETATTR(dvp, &dva, 0, cr) ? NULL : &dva; + dvap = VOP_GETATTR(dvp, &dva, 0, cr, NULL) ? NULL : &dva; } #else dva.va_mask = AT_ALL; - dvap = VOP_GETATTR(dvp, &dva, 0, cr) ? NULL : &dva; + dvap = VOP_GETATTR(dvp, &dva, 0, cr, NULL) ? NULL : &dva; #endif if (args->what.name == nfs3nametoolong) { @@ -438,7 +439,7 @@ rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi, } } else { error = VOP_LOOKUP(dvp, args->what.name, &vp, - NULL, 0, NULL, cr); + NULL, 0, NULL, cr, NULL, NULL, NULL); } if (is_system_labeled() && error == 0) { @@ -463,12 +464,12 @@ rfs3_lookup(LOOKUP3args *args, LOOKUP3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_post_op_attr) { dva.va_mask = AT_ALL; - dvap = VOP_GETATTR(dvp, &dva, 0, cr) ? NULL : &dva; + dvap = VOP_GETATTR(dvp, &dva, 0, cr, NULL) ? NULL : &dva; } else dvap = NULL; #else dva.va_mask = AT_ALL; - dvap = VOP_GETATTR(dvp, &dva, 0, cr) ? NULL : &dva; + dvap = VOP_GETATTR(dvp, &dva, 0, cr, NULL) ? NULL : &dva; #endif if (error) @@ -585,7 +586,7 @@ rfs3_access(ACCESS3args *args, ACCESS3res *resp, struct exportinfo *exi, * as well be reflected to the server during the open. */ va.va_mask = AT_MODE; - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); if (error) goto out; @@ -618,7 +619,7 @@ rfs3_access(ACCESS3args *args, ACCESS3res *resp, struct exportinfo *exi, } if (args->access & ACCESS3_READ) { - error = VOP_ACCESS(vp, VREAD, 0, cr); + error = VOP_ACCESS(vp, VREAD, 0, cr, NULL); if (error) { if (curthread->t_flag & T_WOULDBLOCK) goto out; @@ -628,7 +629,7 @@ rfs3_access(ACCESS3args *args, ACCESS3res *resp, struct exportinfo *exi, resp->resok.access |= ACCESS3_READ; } if ((args->access & ACCESS3_LOOKUP) && vp->v_type == VDIR) { - error = VOP_ACCESS(vp, VEXEC, 0, cr); + error = VOP_ACCESS(vp, VEXEC, 0, cr, NULL); if (error) { if (curthread->t_flag & T_WOULDBLOCK) goto out; @@ -638,7 +639,7 @@ rfs3_access(ACCESS3args *args, ACCESS3res *resp, struct exportinfo *exi, } if (checkwriteperm && (args->access & (ACCESS3_MODIFY|ACCESS3_EXTEND))) { - error = VOP_ACCESS(vp, VWRITE, 0, cr); + error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL); if (error) { if (curthread->t_flag & T_WOULDBLOCK) goto out; @@ -650,7 +651,7 @@ rfs3_access(ACCESS3args *args, ACCESS3res *resp, struct exportinfo *exi, } if (checkwriteperm && (args->access & ACCESS3_DELETE) && vp->v_type == VDIR) { - error = VOP_ACCESS(vp, VWRITE, 0, cr); + error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL); if (error) { if (curthread->t_flag & T_WOULDBLOCK) goto out; @@ -659,7 +660,7 @@ rfs3_access(ACCESS3args *args, ACCESS3res *resp, struct exportinfo *exi, resp->resok.access |= ACCESS3_DELETE; } if (args->access & ACCESS3_EXECUTE) { - error = VOP_ACCESS(vp, VEXEC, 0, cr); + error = VOP_ACCESS(vp, VEXEC, 0, cr, NULL); if (error) { if (curthread->t_flag & T_WOULDBLOCK) goto out; @@ -726,7 +727,7 @@ rfs3_readlink(READLINK3args *args, READLINK3res *resp, struct exportinfo *exi, } va.va_mask = AT_ALL; - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); if (error) goto out; @@ -773,17 +774,17 @@ rfs3_readlink(READLINK3args *args, READLINK3res *resp, struct exportinfo *exi, uio.uio_loffset = 0; uio.uio_resid = MAXPATHLEN; - error = VOP_READLINK(vp, &uio, cr); + error = VOP_READLINK(vp, &uio, cr, NULL); #ifdef DEBUG if (rfs3_do_post_op_attr) { va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; } else vap = NULL; #else va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; #endif #if 0 /* notyet */ @@ -795,7 +796,7 @@ rfs3_readlink(READLINK3args *args, READLINK3res *resp, struct exportinfo *exi, /* * Force modified metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cr); + (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL); #endif if (error) { @@ -895,7 +896,8 @@ rfs3_read(READ3args *args, READ3res *resp, struct exportinfo *exi, if (nbl_need_check(vp)) { nbl_start_crit(vp, RW_READER); in_crit = 1; - if (nbl_conflict(vp, NBL_READ, args->offset, args->count, 0)) { + if (nbl_conflict(vp, NBL_READ, args->offset, args->count, 0, + NULL)) { error = EACCES; goto out; } @@ -905,7 +907,7 @@ rfs3_read(READ3args *args, READ3res *resp, struct exportinfo *exi, need_rwunlock = 1; va.va_mask = AT_ALL; - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); /* * If we can't get the attributes, then we can't do the @@ -927,11 +929,11 @@ rfs3_read(READ3args *args, READ3res *resp, struct exportinfo *exi, } if (crgetuid(cr) != va.va_uid) { - error = VOP_ACCESS(vp, VREAD, 0, cr); + error = VOP_ACCESS(vp, VREAD, 0, cr, NULL); if (error) { if (curthread->t_flag & T_WOULDBLOCK) goto out; - error = VOP_ACCESS(vp, VEXEC, 0, cr); + error = VOP_ACCESS(vp, VEXEC, 0, cr, NULL); if (error) goto out; } @@ -1008,7 +1010,7 @@ rfs3_read(READ3args *args, READ3res *resp, struct exportinfo *exi, } va.va_mask = AT_ALL; - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); #ifdef DEBUG if (rfs3_do_post_op_attr) { @@ -1036,7 +1038,7 @@ rfs3_read(READ3args *args, READ3res *resp, struct exportinfo *exi, /* * Force modified metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cr); + (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL); #endif if (in_crit) @@ -1160,7 +1162,8 @@ rfs3_write(WRITE3args *args, WRITE3res *resp, struct exportinfo *exi, if (nbl_need_check(vp)) { nbl_start_crit(vp, RW_READER); in_crit = 1; - if (nbl_conflict(vp, NBL_WRITE, args->offset, args->count, 0)) { + if (nbl_conflict(vp, NBL_WRITE, args->offset, args->count, 0, + NULL)) { error = EACCES; goto out; } @@ -1169,7 +1172,7 @@ rfs3_write(WRITE3args *args, WRITE3res *resp, struct exportinfo *exi, rwlock_ret = VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); bva.va_mask = AT_ALL; - error = VOP_GETATTR(vp, &bva, 0, cr); + error = VOP_GETATTR(vp, &bva, 0, cr, NULL); /* * If we can't get the attributes, then we can't do the @@ -1201,7 +1204,7 @@ rfs3_write(WRITE3args *args, WRITE3res *resp, struct exportinfo *exi, } if (crgetuid(cr) != bva.va_uid && - (error = VOP_ACCESS(vp, VWRITE, 0, cr))) + (error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL))) goto out; if (MANDLOCK(vp, bva.va_mode)) { @@ -1282,7 +1285,7 @@ rfs3_write(WRITE3args *args, WRITE3res *resp, struct exportinfo *exi, kmem_free(iovp, sizeof (*iovp) * iovcnt); ava.va_mask = AT_ALL; - avap = VOP_GETATTR(vp, &ava, 0, cr) ? NULL : &ava; + avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava; #ifdef DEBUG if (!rfs3_do_post_op_attr) @@ -1375,12 +1378,12 @@ rfs3_create(CREATE3args *args, CREATE3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_pre_op_attr) { dbva.va_mask = AT_ALL; - dbvap = VOP_GETATTR(dvp, &dbva, 0, cr) ? NULL : &dbva; + dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva; } else dbvap = NULL; #else dbva.va_mask = AT_ALL; - dbvap = VOP_GETATTR(dvp, &dbva, 0, cr) ? NULL : &dbva; + dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva; #endif davap = dbvap; @@ -1452,7 +1455,7 @@ rfs3_create(CREATE3args *args, CREATE3res *resp, struct exportinfo *exi, * Does file already exist? */ error = VOP_LOOKUP(dvp, args->where.name, &tvp, - NULL, 0, NULL, cr); + NULL, 0, NULL, cr, NULL, NULL, NULL); /* * Check to see if the file has been delegated @@ -1479,7 +1482,8 @@ rfs3_create(CREATE3args *args, CREATE3res *resp, struct exportinfo *exi, in_crit = 1; tva.va_mask = AT_SIZE; - error = VOP_GETATTR(tvp, &tva, 0, cr); + error = VOP_GETATTR(tvp, &tva, 0, cr, + NULL); /* * Can't check for conflicts, so return * error. @@ -1493,7 +1497,7 @@ rfs3_create(CREATE3args *args, CREATE3res *resp, struct exportinfo *exi, va.va_size - tva.va_size : tva.va_size - va.va_size; if (nbl_conflict(tvp, NBL_WRITE, - offset, len, 0)) { + offset, len, 0, NULL)) { error = EACCES; goto out; } @@ -1530,17 +1534,17 @@ rfs3_create(CREATE3args *args, CREATE3res *resp, struct exportinfo *exi, * passed as part of the arguments. */ error = VOP_CREATE(dvp, args->where.name, &va, excl, VWRITE, - &vp, cr, 0); + &vp, cr, 0, NULL, NULL); #ifdef DEBUG if (rfs3_do_post_op_attr) { dava.va_mask = AT_ALL; - davap = VOP_GETATTR(dvp, &dava, 0, cr) ? NULL : &dava; + davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava; } else davap = NULL; #else dava.va_mask = AT_ALL; - davap = VOP_GETATTR(dvp, &dava, 0, cr) ? NULL : &dava; + davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava; #endif if (error) { @@ -1561,7 +1565,7 @@ rfs3_create(CREATE3args *args, CREATE3res *resp, struct exportinfo *exi, * Lookup the file so that we can get a vnode for it. */ error = VOP_LOOKUP(dvp, args->where.name, &vp, NULL, 0, - NULL, cr); + NULL, cr, NULL, NULL, NULL); if (error) { /* * We couldn't find the file that we thought that @@ -1586,7 +1590,7 @@ rfs3_create(CREATE3args *args, CREATE3res *resp, struct exportinfo *exi, } va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; mtime = (nfstime3 *)&args->how.createhow3_u.verf; /* % with INT32_MAX to prevent overflows */ @@ -1615,7 +1619,7 @@ rfs3_create(CREATE3args *args, CREATE3res *resp, struct exportinfo *exi, } va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; /* * We need to check to make sure that the file got @@ -1638,7 +1642,7 @@ rfs3_create(CREATE3args *args, CREATE3res *resp, struct exportinfo *exi, va.va_size = reqsize; (void) VOP_SETATTR(vp, &va, 0, cr, NULL); va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; } } @@ -1664,8 +1668,8 @@ rfs3_create(CREATE3args *args, CREATE3res *resp, struct exportinfo *exi, /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cr); - (void) VOP_FSYNC(dvp, 0, cr); + (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL); + (void) VOP_FSYNC(dvp, 0, cr, NULL); VN_RELE(vp); VN_RELE(dvp); @@ -1730,12 +1734,12 @@ rfs3_mkdir(MKDIR3args *args, MKDIR3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_pre_op_attr) { dbva.va_mask = AT_ALL; - dbvap = VOP_GETATTR(dvp, &dbva, 0, cr) ? NULL : &dbva; + dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva; } else dbvap = NULL; #else dbva.va_mask = AT_ALL; - dbvap = VOP_GETATTR(dvp, &dbva, 0, cr) ? NULL : &dbva; + dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva; #endif davap = dbvap; @@ -1781,23 +1785,23 @@ rfs3_mkdir(MKDIR3args *args, MKDIR3res *resp, struct exportinfo *exi, va.va_mask |= AT_TYPE; va.va_type = VDIR; - error = VOP_MKDIR(dvp, args->where.name, &va, &vp, cr); + error = VOP_MKDIR(dvp, args->where.name, &va, &vp, cr, NULL, 0, NULL); #ifdef DEBUG if (rfs3_do_post_op_attr) { dava.va_mask = AT_ALL; - davap = VOP_GETATTR(dvp, &dava, 0, cr) ? NULL : &dava; + davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava; } else davap = NULL; #else dava.va_mask = AT_ALL; - davap = VOP_GETATTR(dvp, &dava, 0, cr) ? NULL : &dava; + davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava; #endif /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(dvp, 0, cr); + (void) VOP_FSYNC(dvp, 0, cr, NULL); if (error) goto out; @@ -1821,18 +1825,18 @@ rfs3_mkdir(MKDIR3args *args, MKDIR3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_post_op_attr) { va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; } else vap = NULL; #else va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; #endif /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(vp, 0, cr); + (void) VOP_FSYNC(vp, 0, cr, NULL); VN_RELE(vp); @@ -1886,12 +1890,12 @@ rfs3_symlink(SYMLINK3args *args, SYMLINK3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_pre_op_attr) { dbva.va_mask = AT_ALL; - dbvap = VOP_GETATTR(dvp, &dbva, 0, cr) ? NULL : &dbva; + dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva; } else dbvap = NULL; #else dbva.va_mask = AT_ALL; - dbvap = VOP_GETATTR(dvp, &dbva, 0, cr) ? NULL : &dbva; + dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva; #endif davap = dbvap; @@ -1943,28 +1947,29 @@ rfs3_symlink(SYMLINK3args *args, SYMLINK3res *resp, struct exportinfo *exi, va.va_type = VLNK; error = VOP_SYMLINK(dvp, args->where.name, &va, - args->symlink.symlink_data, cr); + args->symlink.symlink_data, cr, NULL, 0); #ifdef DEBUG if (rfs3_do_post_op_attr) { dava.va_mask = AT_ALL; - davap = VOP_GETATTR(dvp, &dava, 0, cr) ? NULL : &dava; + davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava; } else davap = NULL; #else dava.va_mask = AT_ALL; - davap = VOP_GETATTR(dvp, &dava, 0, cr) ? NULL : &dava; + davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava; #endif if (error) goto out; - error = VOP_LOOKUP(dvp, args->where.name, &vp, NULL, 0, NULL, cr); + error = VOP_LOOKUP(dvp, args->where.name, &vp, NULL, 0, NULL, cr, + NULL, NULL, NULL); /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(dvp, 0, cr); + (void) VOP_FSYNC(dvp, 0, cr, NULL); VN_RELE(dvp); @@ -1993,18 +1998,18 @@ rfs3_symlink(SYMLINK3args *args, SYMLINK3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_post_op_attr) { va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; } else vap = NULL; #else va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; #endif /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(vp, 0, cr); + (void) VOP_FSYNC(vp, 0, cr, NULL); VN_RELE(vp); @@ -2059,12 +2064,12 @@ rfs3_mknod(MKNOD3args *args, MKNOD3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_pre_op_attr) { dbva.va_mask = AT_ALL; - dbvap = VOP_GETATTR(dvp, &dbva, 0, cr) ? NULL : &dbva; + dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva; } else dbvap = NULL; #else dbva.va_mask = AT_ALL; - dbvap = VOP_GETATTR(dvp, &dbva, 0, cr) ? NULL : &dbva; + dbvap = VOP_GETATTR(dvp, &dbva, 0, cr, NULL) ? NULL : &dbva; #endif davap = dbvap; @@ -2152,23 +2157,23 @@ rfs3_mknod(MKNOD3args *args, MKNOD3res *resp, struct exportinfo *exi, mode = 0; error = VOP_CREATE(dvp, args->where.name, &va, excl, mode, - &vp, cr, 0); + &vp, cr, 0, NULL, NULL); #ifdef DEBUG if (rfs3_do_post_op_attr) { dava.va_mask = AT_ALL; - davap = VOP_GETATTR(dvp, &dava, 0, cr) ? NULL : &dava; + davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava; } else davap = NULL; #else dava.va_mask = AT_ALL; - davap = VOP_GETATTR(dvp, &dava, 0, cr) ? NULL : &dava; + davap = VOP_GETATTR(dvp, &dava, 0, cr, NULL) ? NULL : &dava; #endif /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(dvp, 0, cr); + (void) VOP_FSYNC(dvp, 0, cr, NULL); if (error) goto out; @@ -2194,18 +2199,18 @@ rfs3_mknod(MKNOD3args *args, MKNOD3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_post_op_attr) { va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; } else vap = NULL; #else va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; #endif /* * Force modified metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cr); + (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL); VN_RELE(vp); @@ -2256,12 +2261,12 @@ rfs3_remove(REMOVE3args *args, REMOVE3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_pre_op_attr) { bva.va_mask = AT_ALL; - bvap = VOP_GETATTR(vp, &bva, 0, cr) ? NULL : &bva; + bvap = VOP_GETATTR(vp, &bva, 0, cr, NULL) ? NULL : &bva; } else bvap = NULL; #else bva.va_mask = AT_ALL; - bvap = VOP_GETATTR(vp, &bva, 0, cr) ? NULL : &bva; + bvap = VOP_GETATTR(vp, &bva, 0, cr, NULL) ? NULL : &bva; #endif avap = bvap; @@ -2305,7 +2310,7 @@ rfs3_remove(REMOVE3args *args, REMOVE3res *resp, struct exportinfo *exi, * reservation and V4 delegations */ error = VOP_LOOKUP(vp, args->object.name, &targvp, NULL, 0, - NULL, cr); + NULL, cr, NULL, NULL, NULL); if (error != 0) goto out; @@ -2315,13 +2320,13 @@ rfs3_remove(REMOVE3args *args, REMOVE3res *resp, struct exportinfo *exi, } if (!nbl_need_check(targvp)) { - error = VOP_REMOVE(vp, args->object.name, cr); + error = VOP_REMOVE(vp, args->object.name, cr, NULL, 0); } else { nbl_start_crit(targvp, RW_READER); - if (nbl_conflict(targvp, NBL_REMOVE, 0, 0, 0)) { + if (nbl_conflict(targvp, NBL_REMOVE, 0, 0, 0, NULL)) { error = EACCES; } else { - error = VOP_REMOVE(vp, args->object.name, cr); + error = VOP_REMOVE(vp, args->object.name, cr, NULL, 0); } nbl_end_crit(targvp); } @@ -2331,18 +2336,18 @@ rfs3_remove(REMOVE3args *args, REMOVE3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_post_op_attr) { ava.va_mask = AT_ALL; - avap = VOP_GETATTR(vp, &ava, 0, cr) ? NULL : &ava; + avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava; } else avap = NULL; #else ava.va_mask = AT_ALL; - avap = VOP_GETATTR(vp, &ava, 0, cr) ? NULL : &ava; + avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava; #endif /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(vp, 0, cr); + (void) VOP_FSYNC(vp, 0, cr, NULL); if (error) goto out; @@ -2395,12 +2400,12 @@ rfs3_rmdir(RMDIR3args *args, RMDIR3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_pre_op_attr) { bva.va_mask = AT_ALL; - bvap = VOP_GETATTR(vp, &bva, 0, cr) ? NULL : &bva; + bvap = VOP_GETATTR(vp, &bva, 0, cr, NULL) ? NULL : &bva; } else bvap = NULL; #else bva.va_mask = AT_ALL; - bvap = VOP_GETATTR(vp, &bva, 0, cr) ? NULL : &bva; + bvap = VOP_GETATTR(vp, &bva, 0, cr, NULL) ? NULL : &bva; #endif avap = bvap; @@ -2439,23 +2444,23 @@ rfs3_rmdir(RMDIR3args *args, RMDIR3res *resp, struct exportinfo *exi, } } - error = VOP_RMDIR(vp, args->object.name, rootdir, cr); + error = VOP_RMDIR(vp, args->object.name, rootdir, cr, NULL, 0); #ifdef DEBUG if (rfs3_do_post_op_attr) { ava.va_mask = AT_ALL; - avap = VOP_GETATTR(vp, &ava, 0, cr) ? NULL : &ava; + avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava; } else avap = NULL; #else ava.va_mask = AT_ALL; - avap = VOP_GETATTR(vp, &ava, 0, cr) ? NULL : &ava; + avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava; #endif /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(vp, 0, cr); + (void) VOP_FSYNC(vp, 0, cr, NULL); if (error) { /* @@ -2544,12 +2549,12 @@ rfs3_rename(RENAME3args *args, RENAME3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_pre_op_attr) { fbva.va_mask = AT_ALL; - fbvap = VOP_GETATTR(fvp, &fbva, 0, cr) ? NULL : &fbva; + fbvap = VOP_GETATTR(fvp, &fbva, 0, cr, NULL) ? NULL : &fbva; } else fbvap = NULL; #else fbva.va_mask = AT_ALL; - fbvap = VOP_GETATTR(fvp, &fbva, 0, cr) ? NULL : &fbva; + fbvap = VOP_GETATTR(fvp, &fbva, 0, cr, NULL) ? NULL : &fbva; #endif favap = fbvap; @@ -2575,12 +2580,12 @@ rfs3_rename(RENAME3args *args, RENAME3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_pre_op_attr) { tbva.va_mask = AT_ALL; - tbvap = VOP_GETATTR(tvp, &tbva, 0, cr) ? NULL : &tbva; + tbvap = VOP_GETATTR(tvp, &tbva, 0, cr, NULL) ? NULL : &tbva; } else tbvap = NULL; #else tbva.va_mask = AT_ALL; - tbvap = VOP_GETATTR(tvp, &tbva, 0, cr) ? NULL : &tbva; + tbvap = VOP_GETATTR(tvp, &tbva, 0, cr, NULL) ? NULL : &tbva; #endif tavap = tbvap; @@ -2619,7 +2624,7 @@ rfs3_rename(RENAME3args *args, RENAME3res *resp, struct exportinfo *exi, * reservation or V4 delegations. */ error = VOP_LOOKUP(fvp, args->from.name, &srcvp, NULL, 0, - NULL, cr); + NULL, cr, NULL, NULL, NULL); if (error != 0) goto out; @@ -2638,7 +2643,8 @@ rfs3_rename(RENAME3args *args, RENAME3res *resp, struct exportinfo *exi, * first to avoid VOP_LOOKUP if possible. */ if (rfs4_deleg_policy != SRV_NEVER_DELEGATE && - VOP_LOOKUP(tvp, args->to.name, &targvp, NULL, 0, NULL, cr) == 0) { + VOP_LOOKUP(tvp, args->to.name, &targvp, NULL, 0, NULL, cr, + NULL, NULL, NULL) == 0) { if (rfs4_check_delegated(FWRITE, targvp, TRUE)) { VN_RELE(targvp); @@ -2650,14 +2656,14 @@ rfs3_rename(RENAME3args *args, RENAME3res *resp, struct exportinfo *exi, if (!nbl_need_check(srcvp)) { error = VOP_RENAME(fvp, args->from.name, tvp, - args->to.name, cr); + args->to.name, cr, NULL, 0); } else { nbl_start_crit(srcvp, RW_READER); - if (nbl_conflict(srcvp, NBL_RENAME, 0, 0, 0)) { + if (nbl_conflict(srcvp, NBL_RENAME, 0, 0, 0, NULL)) { error = EACCES; } else { error = VOP_RENAME(fvp, args->from.name, tvp, - args->to.name, cr); + args->to.name, cr, NULL, 0); } nbl_end_crit(srcvp); } @@ -2680,25 +2686,25 @@ rfs3_rename(RENAME3args *args, RENAME3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_post_op_attr) { fava.va_mask = AT_ALL; - favap = VOP_GETATTR(fvp, &fava, 0, cr) ? NULL : &fava; + favap = VOP_GETATTR(fvp, &fava, 0, cr, NULL) ? NULL : &fava; tava.va_mask = AT_ALL; - tavap = VOP_GETATTR(tvp, &tava, 0, cr) ? NULL : &tava; + tavap = VOP_GETATTR(tvp, &tava, 0, cr, NULL) ? NULL : &tava; } else { favap = NULL; tavap = NULL; } #else fava.va_mask = AT_ALL; - favap = VOP_GETATTR(fvp, &fava, 0, cr) ? NULL : &fava; + favap = VOP_GETATTR(fvp, &fava, 0, cr, NULL) ? NULL : &fava; tava.va_mask = AT_ALL; - tavap = VOP_GETATTR(tvp, &tava, 0, cr) ? NULL : &tava; + tavap = VOP_GETATTR(tvp, &tava, 0, cr, NULL) ? NULL : &tava; #endif /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(fvp, 0, cr); - (void) VOP_FSYNC(tvp, 0, cr); + (void) VOP_FSYNC(fvp, 0, cr, NULL); + (void) VOP_FSYNC(tvp, 0, cr, NULL); if (error) goto out; @@ -2764,12 +2770,12 @@ rfs3_link(LINK3args *args, LINK3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_pre_op_attr) { va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; } else vap = NULL; #else va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; #endif fh3 = &args->link.dir; @@ -2809,12 +2815,12 @@ rfs3_link(LINK3args *args, LINK3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_pre_op_attr) { bva.va_mask = AT_ALL; - bvap = VOP_GETATTR(dvp, &bva, 0, cr) ? NULL : &bva; + bvap = VOP_GETATTR(dvp, &bva, 0, cr, NULL) ? NULL : &bva; } else bvap = NULL; #else bva.va_mask = AT_ALL; - bvap = VOP_GETATTR(dvp, &bva, 0, cr) ? NULL : &bva; + bvap = VOP_GETATTR(dvp, &bva, 0, cr, NULL) ? NULL : &bva; #endif if (dvp->v_type != VDIR) { @@ -2849,30 +2855,30 @@ rfs3_link(LINK3args *args, LINK3res *resp, struct exportinfo *exi, } } - error = VOP_LINK(dvp, vp, args->link.name, cr); + error = VOP_LINK(dvp, vp, args->link.name, cr, NULL, 0); #ifdef DEBUG if (rfs3_do_post_op_attr) { va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; ava.va_mask = AT_ALL; - avap = VOP_GETATTR(dvp, &ava, 0, cr) ? NULL : &ava; + avap = VOP_GETATTR(dvp, &ava, 0, cr, NULL) ? NULL : &ava; } else { vap = NULL; avap = NULL; } #else va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; ava.va_mask = AT_ALL; - avap = VOP_GETATTR(dvp, &ava, 0, cr) ? NULL : &ava; + avap = VOP_GETATTR(dvp, &ava, 0, cr, NULL) ? NULL : &ava; #endif /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cr); - (void) VOP_FSYNC(dvp, 0, cr); + (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL); + (void) VOP_FSYNC(dvp, 0, cr, NULL); if (error) goto out; @@ -2925,7 +2931,7 @@ rfs3_link_getfh(LINK3args *args) * attributes - NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT * boolean - 1 * BYTES_PER_XDR_UNIT * file id - 2 * BYTES_PER_XDR_UNIT - * direcotory name length - 1 * BYTES_PER_XDR_UNIT + * directory name length - 1 * BYTES_PER_XDR_UNIT * cookie - 2 * BYTES_PER_XDR_UNIT * end of list - 1 * BYTES_PER_XDR_UNIT * end of file - 1 * BYTES_PER_XDR_UNIT @@ -2981,12 +2987,12 @@ rfs3_readdir(READDIR3args *args, READDIR3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_pre_op_attr) { va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; } else vap = NULL; #else va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; #endif if (vp->v_type != VDIR) { @@ -2994,7 +3000,7 @@ rfs3_readdir(READDIR3args *args, READDIR3res *resp, struct exportinfo *exi, goto out1; } - error = VOP_ACCESS(vp, VREAD, 0, cr); + error = VOP_ACCESS(vp, VREAD, 0, cr, NULL); if (error) goto out; @@ -3025,17 +3031,17 @@ rfs3_readdir(READDIR3args *args, READDIR3res *resp, struct exportinfo *exi, uio.uio_loffset = (offset_t)args->cookie; uio.uio_resid = count; - error = VOP_READDIR(vp, &uio, cr, &iseof); + error = VOP_READDIR(vp, &uio, cr, &iseof, NULL, 0); #ifdef DEBUG if (rfs3_do_post_op_attr) { va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; } else vap = NULL; #else va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; #endif if (error) { @@ -3108,7 +3114,7 @@ rfs3_readdir(READDIR3args *args, READDIR3res *resp, struct exportinfo *exi, /* * Force modified metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cr); + (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL); #endif VN_RELE(vp); @@ -3173,7 +3179,7 @@ rfs3_readdir_free(READDIR3res *resp) * attributes - NFS3_SIZEOF_FATTR3 * BYTES_PER_XDR_UNIT * status byte for file handle - 1 * BYTES_PER_XDR_UNIT * length of a file handle - 1 * BYTES_PER_XDR_UNIT - * Maxmum length of a file handle (NFS3_MAXFHSIZE) + * Maximum length of a file handle (NFS3_MAXFHSIZE) * name length of the entry to the nearest bytes */ #define NFS3_READDIRPLUS_ENTRY(namelen) \ @@ -3241,12 +3247,12 @@ rfs3_readdirplus(READDIRPLUS3args *args, READDIRPLUS3res *resp, #ifdef DEBUG if (rfs3_do_pre_op_attr) { va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; } else vap = NULL; #else va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; #endif if (vp->v_type != VDIR) { @@ -3254,7 +3260,7 @@ rfs3_readdirplus(READDIRPLUS3args *args, READDIRPLUS3res *resp, goto out; } - error = VOP_ACCESS(vp, VREAD, 0, cr); + error = VOP_ACCESS(vp, VREAD, 0, cr, NULL); if (error) goto out; @@ -3323,7 +3329,7 @@ rfs3_readdirplus(READDIRPLUS3args *args, READDIRPLUS3res *resp, uio.uio_resid = rd_unit; prev_len = rd_unit; - error = VOP_READDIR(vp, &uio, cr, &iseof); + error = VOP_READDIR(vp, &uio, cr, &iseof, NULL, 0); if (error) { kmem_free(data, args->dircount); @@ -3412,12 +3418,12 @@ rfs3_readdirplus(READDIRPLUS3args *args, READDIRPLUS3res *resp, #ifdef DEBUG if (rfs3_do_post_op_attr) { va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; } else vap = NULL; #else va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; #endif VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); @@ -3437,7 +3443,8 @@ rfs3_readdirplus(READDIRPLUS3args *args, READDIRPLUS3res *resp, infop[i].namelen = namlen[i]; - error = VOP_LOOKUP(vp, dp->d_name, &nvp, NULL, 0, NULL, cr); + error = VOP_LOOKUP(vp, dp->d_name, &nvp, NULL, 0, NULL, cr, + NULL, NULL, NULL); if (error) { infop[i].attr.attributes = FALSE; infop[i].fh.handle_follows = FALSE; @@ -3485,7 +3492,7 @@ rfs3_readdirplus(READDIRPLUS3args *args, READDIRPLUS3res *resp, /* * Force modified metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cr); + (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL); #endif VN_RELE(vp); @@ -3577,12 +3584,12 @@ rfs3_fsstat(FSSTAT3args *args, FSSTAT3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_post_op_attr) { va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; } else vap = NULL; #else va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; #endif VN_RELE(vp); @@ -3673,12 +3680,12 @@ rfs3_fsinfo(FSINFO3args *args, FSINFO3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_post_op_attr) { va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; } else vap = NULL; #else va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; #endif resp->status = NFS3_OK; @@ -3696,7 +3703,7 @@ rfs3_fsinfo(FSINFO3args *args, FSINFO3res *resp, struct exportinfo *exi, * Large file spec: want maxfilesize based on limit of * underlying filesystem. We can guess 2^31-1 if need be. */ - error = VOP_PATHCONF(vp, _PC_FILESIZEBITS, &l, cr); + error = VOP_PATHCONF(vp, _PC_FILESIZEBITS, &l, cr, NULL); VN_RELE(vp); @@ -3755,25 +3762,25 @@ rfs3_pathconf(PATHCONF3args *args, PATHCONF3res *resp, struct exportinfo *exi, #ifdef DEBUG if (rfs3_do_post_op_attr) { va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; } else vap = NULL; #else va.va_mask = AT_ALL; - vap = VOP_GETATTR(vp, &va, 0, cr) ? NULL : &va; + vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; #endif - error = VOP_PATHCONF(vp, _PC_LINK_MAX, &val, cr); + error = VOP_PATHCONF(vp, _PC_LINK_MAX, &val, cr, NULL); if (error) goto out; resp->resok.info.link_max = (uint32)val; - error = VOP_PATHCONF(vp, _PC_NAME_MAX, &val, cr); + error = VOP_PATHCONF(vp, _PC_NAME_MAX, &val, cr, NULL); if (error) goto out; resp->resok.info.name_max = (uint32)val; - error = VOP_PATHCONF(vp, _PC_NO_TRUNC, &val, cr); + error = VOP_PATHCONF(vp, _PC_NO_TRUNC, &val, cr, NULL); if (error) goto out; if (val == 1) @@ -3781,7 +3788,7 @@ rfs3_pathconf(PATHCONF3args *args, PATHCONF3res *resp, struct exportinfo *exi, else resp->resok.info.no_trunc = FALSE; - error = VOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &val, cr); + error = VOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &val, cr, NULL); if (error) goto out; if (val == 1) @@ -3837,7 +3844,7 @@ rfs3_commit(COMMIT3args *args, COMMIT3res *resp, struct exportinfo *exi, } bva.va_mask = AT_ALL; - error = VOP_GETATTR(vp, &bva, 0, cr); + error = VOP_GETATTR(vp, &bva, 0, cr, NULL); /* * If we can't get the attributes, then we can't do the @@ -3881,22 +3888,22 @@ rfs3_commit(COMMIT3args *args, COMMIT3res *resp, struct exportinfo *exi, } if (crgetuid(cr) != bva.va_uid && - (error = VOP_ACCESS(vp, VWRITE, 0, cr))) + (error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL))) goto out; - error = VOP_PUTPAGE(vp, args->offset, args->count, 0, cr); + error = VOP_PUTPAGE(vp, args->offset, args->count, 0, cr, NULL); if (!error) - error = VOP_FSYNC(vp, FNODSYNC, cr); + error = VOP_FSYNC(vp, FNODSYNC, cr, NULL); #ifdef DEBUG if (rfs3_do_post_op_attr) { ava.va_mask = AT_ALL; - avap = VOP_GETATTR(vp, &ava, 0, cr) ? NULL : &ava; + avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava; } else avap = NULL; #else ava.va_mask = AT_ALL; - avap = VOP_GETATTR(vp, &ava, 0, cr) ? NULL : &ava; + avap = VOP_GETATTR(vp, &ava, 0, cr, NULL) ? NULL : &ava; #endif if (error) diff --git a/usr/src/uts/common/fs/nfs/nfs3_vnops.c b/usr/src/uts/common/fs/nfs/nfs3_vnops.c index 39bdc1aa0086..51306f52e8db 100644 --- a/usr/src/uts/common/fs/nfs/nfs3_vnops.c +++ b/usr/src/uts/common/fs/nfs/nfs3_vnops.c @@ -101,7 +101,8 @@ static int nfs3create(vnode_t *, char *, struct vattr *, enum vcexcl, static int nfs3excl_create_settimes(vnode_t *, struct vattr *, cred_t *); static int nfs3mknod(vnode_t *, char *, struct vattr *, enum vcexcl, int, vnode_t **, cred_t *); -static int nfs3rename(vnode_t *, char *, vnode_t *, char *, cred_t *); +static int nfs3rename(vnode_t *, char *, vnode_t *, char *, cred_t *, + caller_context_t *); static int do_nfs3readdir(vnode_t *, rddir_cache *, cred_t *); static void nfs3readdir(vnode_t *, rddir_cache *, cred_t *); static void nfs3readdirplus(vnode_t *, rddir_cache *, cred_t *); @@ -160,59 +161,76 @@ static void nfs3_delmap_callback(struct as *, void *, uint_t); * more details on rnode locking. */ -static int nfs3_open(vnode_t **, int, cred_t *); -static int nfs3_close(vnode_t *, int, int, offset_t, cred_t *); +static int nfs3_open(vnode_t **, int, cred_t *, caller_context_t *); +static int nfs3_close(vnode_t *, int, int, offset_t, cred_t *, + caller_context_t *); static int nfs3_read(vnode_t *, struct uio *, int, cred_t *, caller_context_t *); static int nfs3_write(vnode_t *, struct uio *, int, cred_t *, caller_context_t *); -static int nfs3_ioctl(vnode_t *, int, intptr_t, int, cred_t *, int *); -static int nfs3_getattr(vnode_t *, struct vattr *, int, cred_t *); +static int nfs3_ioctl(vnode_t *, int, intptr_t, int, cred_t *, int *, + caller_context_t *); +static int nfs3_getattr(vnode_t *, struct vattr *, int, cred_t *, + caller_context_t *); static int nfs3_setattr(vnode_t *, struct vattr *, int, cred_t *, caller_context_t *); -static int nfs3_access(vnode_t *, int, int, cred_t *); -static int nfs3_readlink(vnode_t *, struct uio *, cred_t *); -static int nfs3_fsync(vnode_t *, int, cred_t *); -static void nfs3_inactive(vnode_t *, cred_t *); +static int nfs3_access(vnode_t *, int, int, cred_t *, caller_context_t *); +static int nfs3_readlink(vnode_t *, struct uio *, cred_t *, + caller_context_t *); +static int nfs3_fsync(vnode_t *, int, cred_t *, caller_context_t *); +static void nfs3_inactive(vnode_t *, cred_t *, caller_context_t *); static int nfs3_lookup(vnode_t *, char *, vnode_t **, - struct pathname *, int, vnode_t *, cred_t *); + struct pathname *, int, vnode_t *, cred_t *, + caller_context_t *, int *, pathname_t *); static int nfs3_create(vnode_t *, char *, struct vattr *, enum vcexcl, - int, vnode_t **, cred_t *, int); -static int nfs3_remove(vnode_t *, char *, cred_t *); -static int nfs3_link(vnode_t *, vnode_t *, char *, cred_t *); -static int nfs3_rename(vnode_t *, char *, vnode_t *, char *, cred_t *); -static int nfs3_mkdir(vnode_t *, char *, struct vattr *, - vnode_t **, cred_t *); -static int nfs3_rmdir(vnode_t *, char *, vnode_t *, cred_t *); + int, vnode_t **, cred_t *, int, caller_context_t *, + vsecattr_t *); +static int nfs3_remove(vnode_t *, char *, cred_t *, caller_context_t *, + int); +static int nfs3_link(vnode_t *, vnode_t *, char *, cred_t *, + caller_context_t *, int); +static int nfs3_rename(vnode_t *, char *, vnode_t *, char *, cred_t *, + caller_context_t *, int); +static int nfs3_mkdir(vnode_t *, char *, struct vattr *, vnode_t **, + cred_t *, caller_context_t *, int, vsecattr_t *); +static int nfs3_rmdir(vnode_t *, char *, vnode_t *, cred_t *, + caller_context_t *, int); static int nfs3_symlink(vnode_t *, char *, struct vattr *, char *, - cred_t *); -static int nfs3_readdir(vnode_t *, struct uio *, cred_t *, int *); -static int nfs3_fid(vnode_t *, fid_t *); + cred_t *, caller_context_t *, int); +static int nfs3_readdir(vnode_t *, struct uio *, cred_t *, int *, + caller_context_t *, int); +static int nfs3_fid(vnode_t *, fid_t *, caller_context_t *); static int nfs3_rwlock(vnode_t *, int, caller_context_t *); static void nfs3_rwunlock(vnode_t *, int, caller_context_t *); -static int nfs3_seek(vnode_t *, offset_t, offset_t *); +static int nfs3_seek(vnode_t *, offset_t, offset_t *, caller_context_t *); static int nfs3_getpage(vnode_t *, offset_t, size_t, uint_t *, page_t *[], size_t, struct seg *, caddr_t, - enum seg_rw, cred_t *); -static int nfs3_putpage(vnode_t *, offset_t, size_t, int, cred_t *); -static int nfs3_map(vnode_t *, offset_t, struct as *, caddr_t *, - size_t, uchar_t, uchar_t, uint_t, cred_t *); -static int nfs3_addmap(vnode_t *, offset_t, struct as *, caddr_t, - size_t, uchar_t, uchar_t, uint_t, cred_t *); + enum seg_rw, cred_t *, caller_context_t *); +static int nfs3_putpage(vnode_t *, offset_t, size_t, int, cred_t *, + caller_context_t *); +static int nfs3_map(vnode_t *, offset_t, struct as *, caddr_t *, size_t, + uchar_t, uchar_t, uint_t, cred_t *, caller_context_t *); +static int nfs3_addmap(vnode_t *, offset_t, struct as *, caddr_t, size_t, + uchar_t, uchar_t, uint_t, cred_t *, caller_context_t *); static int nfs3_frlock(vnode_t *, int, struct flock64 *, int, offset_t, - struct flk_callback *, cred_t *); + struct flk_callback *, cred_t *, caller_context_t *); static int nfs3_space(vnode_t *, int, struct flock64 *, int, offset_t, cred_t *, caller_context_t *); -static int nfs3_realvp(vnode_t *, vnode_t **); -static int nfs3_delmap(vnode_t *, offset_t, struct as *, caddr_t, - size_t, uint_t, uint_t, uint_t, cred_t *); -static int nfs3_pathconf(vnode_t *, int, ulong_t *, cred_t *); +static int nfs3_realvp(vnode_t *, vnode_t **, caller_context_t *); +static int nfs3_delmap(vnode_t *, offset_t, struct as *, caddr_t, size_t, + uint_t, uint_t, uint_t, cred_t *, caller_context_t *); +static int nfs3_pathconf(vnode_t *, int, ulong_t *, cred_t *, + caller_context_t *); static int nfs3_pageio(vnode_t *, page_t *, u_offset_t, size_t, int, - cred_t *); -static void nfs3_dispose(vnode_t *, page_t *, int, int, cred_t *); -static int nfs3_setsecattr(vnode_t *, vsecattr_t *, int, cred_t *); -static int nfs3_getsecattr(vnode_t *, vsecattr_t *, int, cred_t *); -static int nfs3_shrlock(vnode_t *, int, struct shrlock *, int, cred_t *); + cred_t *, caller_context_t *); +static void nfs3_dispose(vnode_t *, page_t *, int, int, cred_t *, + caller_context_t *); +static int nfs3_setsecattr(vnode_t *, vsecattr_t *, int, cred_t *, + caller_context_t *); +static int nfs3_getsecattr(vnode_t *, vsecattr_t *, int, cred_t *, + caller_context_t *); +static int nfs3_shrlock(vnode_t *, int, struct shrlock *, int, cred_t *, + caller_context_t *); struct vnodeops *nfs3_vnodeops; @@ -272,7 +290,7 @@ nfs3_getvnodeops(void) /* ARGSUSED */ static int -nfs3_open(vnode_t **vpp, int flag, cred_t *cr) +nfs3_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { int error; struct vattr va; @@ -321,8 +339,10 @@ nfs3_open(vnode_t **vpp, int flag, cred_t *cr) return (error); } +/* ARGSUSED */ static int -nfs3_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) +nfs3_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, + caller_context_t *ct) { rnode_t *rp; int error; @@ -396,7 +416,8 @@ nfs3_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) */ if ((flag & FWRITE) && vn_has_cached_data(vp)) { if (VTOMI(vp)->mi_flags & MI_NOCTO) { - error = nfs3_putpage(vp, (offset_t)0, 0, B_ASYNC, cr); + error = nfs3_putpage(vp, (offset_t)0, 0, B_ASYNC, + cr, ct); if (error == EAGAIN) error = 0; } else @@ -1160,7 +1181,8 @@ nfs3read(vnode_t *vp, caddr_t base, offset_t offset, int count, /* ARGSUSED */ static int -nfs3_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) +nfs3_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp, + caller_context_t *ct) { if (nfs_zone() != VTOMI(vp)->mi_zone) @@ -1173,8 +1195,10 @@ nfs3_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) } } +/* ARGSUSED */ static int -nfs3_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) +nfs3_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, + caller_context_t *ct) { int error; rnode_t *rp; @@ -1217,7 +1241,7 @@ nfs3_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) mutex_enter(&rp->r_statelock); rp->r_gcount++; mutex_exit(&rp->r_statelock); - error = nfs3_putpage(vp, (offset_t)0, 0, 0, cr); + error = nfs3_putpage(vp, (offset_t)0, 0, 0, cr, ct); mutex_enter(&rp->r_statelock); if (error && (error == ENOSPC || error == EDQUOT)) { if (!rp->r_error) @@ -1293,7 +1317,7 @@ nfs3setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) rp->r_count > 0 || rp->r_mapcnt > 0)) { ASSERT(vp->v_type != VCHR); - error = nfs3_putpage(vp, (offset_t)0, 0, 0, cr); + error = nfs3_putpage(vp, (offset_t)0, 0, 0, cr, NULL); if (error && (error == ENOSPC || error == EDQUOT)) { mutex_enter(&rp->r_statelock); if (!rp->r_error) @@ -1446,12 +1470,12 @@ static int nfs3_accessx(void *vp, int mode, cred_t *cr) { ASSERT(nfs_zone() == VTOMI((vnode_t *)vp)->mi_zone); - return (nfs3_access(vp, mode, 0, cr)); + return (nfs3_access(vp, mode, 0, cr, NULL)); } /* ARGSUSED */ static int -nfs3_access(vnode_t *vp, int mode, int flags, cred_t *cr) +nfs3_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct) { int error; ACCESS3args args; @@ -1584,8 +1608,9 @@ nfs3_access(vnode_t *vp, int mode, int flags, cred_t *cr) static int nfs3_do_symlink_cache = 1; +/* ARGSUSED */ static int -nfs3_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr) +nfs3_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr, caller_context_t *ct) { int error; READLINK3args args; @@ -1693,8 +1718,9 @@ nfs3_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr) * metadata changes are not cached on the client before being * sent to the server. */ +/* ARGSUSED */ static int -nfs3_fsync(vnode_t *vp, int syncflag, cred_t *cr) +nfs3_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) { int error; @@ -1714,8 +1740,9 @@ nfs3_fsync(vnode_t *vp, int syncflag, cred_t *cr) * operation while it was open, it got renamed instead. Here we * remove the renamed file. */ +/* ARGSUSED */ static void -nfs3_inactive(vnode_t *vp, cred_t *cr) +nfs3_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { rnode_t *rp; @@ -1775,7 +1802,8 @@ nfs3_inactive(vnode_t *vp, cred_t *cr) if (vn_has_cached_data(vp) && ((rp->r_flags & RDIRTY) || rp->r_count > 0)) { ASSERT(vp->v_type != VCHR); - error = nfs3_putpage(vp, (offset_t)0, 0, 0, cr); + error = nfs3_putpage(vp, (offset_t)0, 0, 0, + cr, ct); if (error) { mutex_enter(&rp->r_statelock); if (!rp->r_error) @@ -1832,9 +1860,11 @@ nfs3_inactive(vnode_t *vp, cred_t *cr) * Remote file system operations having to do with directory manipulation. */ +/* ARGSUSED */ static int nfs3_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, - int flags, vnode_t *rdir, cred_t *cr) + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp) { int error; vnode_t *vp; @@ -1957,7 +1987,7 @@ nfs3lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, * just need to check access. */ if (strcmp(nm, ".") == 0) { - error = nfs3_access(dvp, VEXEC, 0, cr); + error = nfs3_access(dvp, VEXEC, 0, cr, NULL); if (error) return (error); VN_HOLD(dvp); @@ -2018,7 +2048,7 @@ nfs3lookup_dnlc(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr) return (error); vp = dnlc_lookup(dvp, nm); if (vp != NULL) { - error = nfs3_access(dvp, VEXEC, 0, cr); + error = nfs3_access(dvp, VEXEC, 0, cr, NULL); if (error) { VN_RELE(vp); return (error); @@ -2134,7 +2164,8 @@ static int nfs3_create_misses = 0; /* ARGSUSED */ static int nfs3_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, - int mode, vnode_t **vpp, cred_t *cr, int lfaware) + int mode, vnode_t **vpp, cred_t *cr, int lfaware, caller_context_t *ct, + vsecattr_t *vsecp) { int error; vnode_t *vp; @@ -2171,7 +2202,7 @@ nfs3_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, * just need to check access. */ } else if (strcmp(nm, ".") == 0) { - error = nfs3_access(dvp, VEXEC, 0, cr); + error = nfs3_access(dvp, VEXEC, 0, cr, ct); if (error) { nfs_rw_exit(&drp->r_rwlock); return (error); @@ -2200,7 +2231,7 @@ nfs3_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, vp = specvp(vp, vp->v_rdev, vp->v_type, cr); VN_RELE(tempvp); } - if (!(error = VOP_ACCESS(vp, mode, 0, cr))) { + if (!(error = VOP_ACCESS(vp, mode, 0, cr, ct))) { if ((vattr.va_mask & AT_SIZE) && vp->v_type == VREG) { rp = VTOR(vp); @@ -2230,7 +2261,7 @@ nfs3_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, /* * existing file got truncated, notify. */ - vnevent_create(vp); + vnevent_create(vp, ct); *vpp = vp; } return (error); @@ -2438,7 +2469,7 @@ nfs3create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, */ VN_RELE(vp); (void) nfs3_remove(dvp, - nm, cr); + nm, cr, NULL, 0); return (error); } } @@ -2515,7 +2546,7 @@ nfs3create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, * application. */ VN_RELE(vp); - (void) nfs3_remove(dvp, nm, cr); + (void) nfs3_remove(dvp, nm, cr, NULL, 0); return (error); } } @@ -2737,8 +2768,9 @@ nfs3mknod(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, * we rename it instead of removing it and nfs_inactive * will remove the new name. */ +/* ARGSUSED */ static int -nfs3_remove(vnode_t *dvp, char *nm, cred_t *cr) +nfs3_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct, int flags) { int error; REMOVE3args args; @@ -2792,7 +2824,7 @@ nfs3_remove(vnode_t *dvp, char *nm, cred_t *cr) (rp->r_unldvp == NULL || strcmp(nm, rp->r_unlname) == 0)) { mutex_exit(&rp->r_statelock); tmpname = newname(); - error = nfs3rename(dvp, nm, dvp, tmpname, cr); + error = nfs3rename(dvp, nm, dvp, tmpname, cr, ct); if (error) kmem_free(tmpname, MAXNAMELEN); else { @@ -2821,7 +2853,7 @@ nfs3_remove(vnode_t *dvp, char *nm, cred_t *cr) */ if (vn_has_cached_data(vp) && ((rp->r_flags & RDIRTY) || rp->r_count > 0)) { - error = nfs3_putpage(vp, (offset_t)0, 0, 0, cr); + error = nfs3_putpage(vp, (offset_t)0, 0, 0, cr, ct); if (error && (error == ENOSPC || error == EDQUOT)) { mutex_enter(&rp->r_statelock); if (!rp->r_error) @@ -2868,7 +2900,7 @@ nfs3_remove(vnode_t *dvp, char *nm, cred_t *cr) } if (error == 0) { - vnevent_remove(vp, dvp, nm); + vnevent_remove(vp, dvp, nm, ct); } VN_RELE(vp); @@ -2877,8 +2909,10 @@ nfs3_remove(vnode_t *dvp, char *nm, cred_t *cr) return (error); } +/* ARGSUSED */ static int -nfs3_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) +nfs3_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr, + caller_context_t *ct, int flags) { int error; LINK3args args; @@ -2891,7 +2925,7 @@ nfs3_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) if (nfs_zone() != VTOMI(tdvp)->mi_zone) return (EPERM); - if (VOP_REALVP(svp, &realvp) == 0) + if (VOP_REALVP(svp, &realvp, ct) == 0) svp = realvp; mi = VTOMI(svp); @@ -2949,29 +2983,32 @@ nfs3_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) /* * Notify the source file of this link operation. */ - vnevent_link(svp); + vnevent_link(svp, ct); } return (error); } +/* ARGSUSED */ static int -nfs3_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) +nfs3_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr, + caller_context_t *ct, int flags) { vnode_t *realvp; if (nfs_zone() != VTOMI(odvp)->mi_zone) return (EPERM); - if (VOP_REALVP(ndvp, &realvp) == 0) + if (VOP_REALVP(ndvp, &realvp, ct) == 0) ndvp = realvp; - return (nfs3rename(odvp, onm, ndvp, nnm, cr)); + return (nfs3rename(odvp, onm, ndvp, nnm, cr, ct)); } /* * nfs3rename does the real work of renaming in NFS Version 3. */ static int -nfs3rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) +nfs3rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr, + caller_context_t *ct) { int error; RENAME3args args; @@ -3106,10 +3143,10 @@ nfs3rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) * the server removing the file completely. */ tmpname = newname(); - error = nfs3_link(ndvp, nvp, tmpname, cr); + error = nfs3_link(ndvp, nvp, tmpname, cr, NULL, 0); if (error == EOPNOTSUPP) { error = nfs3_rename(ndvp, nnm, ndvp, tmpname, - cr); + cr, NULL, 0); } if (error) { kmem_free(tmpname, MAXNAMELEN); @@ -3253,12 +3290,12 @@ nfs3rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) if (error == 0) { if (nvp) - vnevent_rename_dest(nvp, ndvp, nnm); + vnevent_rename_dest(nvp, ndvp, nnm, ct); if (odvp != ndvp) - vnevent_rename_dest_dir(ndvp); + vnevent_rename_dest_dir(ndvp, ct); ASSERT(ovp != NULL); - vnevent_rename_src(ovp, odvp, onm); + vnevent_rename_src(ovp, odvp, onm, ct); } if (nvp) { @@ -3272,8 +3309,10 @@ nfs3rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) return (error); } +/* ARGSUSED */ static int -nfs3_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, cred_t *cr) +nfs3_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, cred_t *cr, + caller_context_t *ct, int flags, vsecattr_t *vsecp) { int error; MKDIR3args args; @@ -3375,8 +3414,10 @@ nfs3_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, cred_t *cr) return (error); } +/* ARGSUSED */ static int -nfs3_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) +nfs3_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr, + caller_context_t *ct, int flags) { int error; RMDIR3args args; @@ -3469,7 +3510,7 @@ nfs3_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) } if (error == 0) { - vnevent_rmdir(vp, dvp, nm); + vnevent_rmdir(vp, dvp, nm, ct); } VN_RELE(vp); @@ -3478,8 +3519,10 @@ nfs3_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) return (error); } +/* ARGSUSED */ static int -nfs3_symlink(vnode_t *dvp, char *lnm, struct vattr *tva, char *tnm, cred_t *cr) +nfs3_symlink(vnode_t *dvp, char *lnm, struct vattr *tva, char *tnm, cred_t *cr, + caller_context_t *ct, int flags) { int error; SYMLINK3args args; @@ -3607,8 +3650,10 @@ static int nfs3_shrinkreaddir = 0; * may return only one block's worth of entries. Entries may be compressed * on the server. */ +/* ARGSUSED */ static int -nfs3_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp) +nfs3_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) { int error; size_t count; @@ -4335,8 +4380,9 @@ nfs3_bio(struct buf *bp, stable_how *stab_comm, cred_t *cr) return (error); } +/* ARGSUSED */ static int -nfs3_fid(vnode_t *vp, fid_t *fidp) +nfs3_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct) { rnode_t *rp; @@ -4386,7 +4432,7 @@ nfs3_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ctp) /* ARGSUSED */ static int -nfs3_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) +nfs3_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) { /* @@ -4413,10 +4459,11 @@ static int nfs3_lostpage = 0; /* number of times we lost original page */ /* * Return all the pages from [off..off+len) in file */ +/* ARGSUSED */ static int nfs3_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp, page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr, - enum seg_rw rw, cred_t *cr) + enum seg_rw rw, cred_t *cr, caller_context_t *ct) { rnode_t *rp; int error; @@ -4879,8 +4926,10 @@ nfs3_readahead(vnode_t *vp, u_offset_t blkoff, caddr_t addr, struct seg *seg, * len == MAXBSIZE (from segmap_release actions), and len == PAGESIZE * (from pageout). */ +/* ARGSUSED */ static int -nfs3_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr) +nfs3_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr, + caller_context_t *ct) { int error; rnode_t *rp; @@ -5078,7 +5127,7 @@ nfs3_sync_putapage(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, */ if (!(flags & B_ASYNC)) { error = nfs3_putpage(vp, io_off, io_len, - B_INVAL | B_FORCE, cr); + B_INVAL | B_FORCE, cr, NULL); } } else { if (error) @@ -5096,9 +5145,11 @@ nfs3_sync_putapage(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, return (error); } +/* ARGSUSED */ static int nfs3_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, - size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) + size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, + cred_t *cr, caller_context_t *ct) { struct segvn_crargs vn_a; int error; @@ -5197,7 +5248,8 @@ nfs3_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, /* ARGSUSED */ static int nfs3_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, - size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) + size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, + cred_t *cr, caller_context_t *ct) { rnode_t *rp; @@ -5220,9 +5272,11 @@ nfs3_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, return (0); } +/* ARGSUSED */ static int nfs3_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, - offset_t offset, struct flk_callback *flk_cbp, cred_t *cr) + offset_t offset, struct flk_callback *flk_cbp, cred_t *cr, + caller_context_t *ct) { netobj lm_fh3; int rc; @@ -5277,7 +5331,7 @@ nfs3_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, if (!lm_safelock(vp, bfp, cr)) return (EAGAIN); } - return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr)); + return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct)); } rp = VTOR(vp); @@ -5326,7 +5380,7 @@ nfs3_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, mutex_exit(&rp->r_statelock); if (rc != 0) goto done; - error = nfs3_putpage(vp, (offset_t)0, 0, B_INVAL, cr); + error = nfs3_putpage(vp, (offset_t)0, 0, B_INVAL, cr, ct); if (error) { if (error == ENOSPC || error == EDQUOT) { mutex_enter(&rp->r_statelock); @@ -5407,7 +5461,7 @@ nfs3_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, /* ARGSUSED */ static int -nfs3_realvp(vnode_t *vp, vnode_t **vpp) +nfs3_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct) { return (EINVAL); @@ -5426,7 +5480,8 @@ nfs3_realvp(vnode_t *vp, vnode_t **vpp) /* ARGSUSED */ static int nfs3_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, - size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr) + size_t len, uint_t prot, uint_t maxprot, uint_t flags, + cred_t *cr, caller_context_t *ct) { int caller_found; int error; @@ -5556,7 +5611,7 @@ nfs3_delmap_callback(struct as *as, void *arg, uint_t event) if ((mi->mi_flags & MI_NOCTO) || nfs_zone() != mi->mi_zone) error = nfs3_putpage(dmapp->vp, dmapp->off, dmapp->len, - B_ASYNC, dmapp->cr); + B_ASYNC, dmapp->cr, NULL); else error = nfs3_putpage_commit(dmapp->vp, dmapp->off, dmapp->len, dmapp->cr); @@ -5571,7 +5626,7 @@ nfs3_delmap_callback(struct as *as, void *arg, uint_t event) if ((rp->r_flags & RDIRECTIO) || (mi->mi_flags & MI_DIRECTIO)) (void) nfs3_putpage(dmapp->vp, dmapp->off, dmapp->len, - B_INVAL, dmapp->cr); + B_INVAL, dmapp->cr, NULL); dmapp->caller->error = error; (void) as_delete_callback(as, arg); @@ -5585,8 +5640,10 @@ static int nfs3_pathconf_cache_hits = 0; static int nfs3_pathconf_cache_misses = 0; #endif +/* ARGSUSED */ static int -nfs3_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) +nfs3_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, + caller_context_t *ct) { int error; PATHCONF3args args; @@ -5795,9 +5852,10 @@ nfs3_sync_pageio(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, return (error); } +/* ARGSUSED */ static int nfs3_pageio(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, - int flags, cred_t *cr) + int flags, cred_t *cr, caller_context_t *ct) { int error; rnode_t *rp; @@ -5824,8 +5882,10 @@ nfs3_pageio(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, return (error); } +/* ARGSUSED */ static void -nfs3_dispose(vnode_t *vp, page_t *pp, int fl, int dn, cred_t *cr) +nfs3_dispose(vnode_t *vp, page_t *pp, int fl, int dn, cred_t *cr, + caller_context_t *ct) { int error; rnode_t *rp; @@ -6362,7 +6422,7 @@ nfs3_putpage_commit(vnode_t *vp, offset_t poff, size_t plen, cred_t *cr) write_verf = rp->r_verf; mutex_exit(&rp->r_statelock); - error = nfs3_putpage(vp, poff, plen, B_ASYNC, cr); + error = nfs3_putpage(vp, poff, plen, B_ASYNC, cr, NULL); if (error == EAGAIN) error = 0; @@ -6373,7 +6433,7 @@ nfs3_putpage_commit(vnode_t *vp, offset_t poff, size_t plen, cred_t *cr) * the asynchronous i/o's in that range are done as well. */ if (!error) - error = nfs3_putpage(vp, poff, plen, 0, cr); + error = nfs3_putpage(vp, poff, plen, 0, cr, NULL); if (error) return (error); @@ -6510,8 +6570,10 @@ nfs3_async_commit(vnode_t *vp, page_t *plist, offset3 offset, count3 count, (void) nfs3_sync_commit(vp, plist, offset, count, cr); } +/* ARGSUSED */ static int -nfs3_setsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr) +nfs3_setsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr, + caller_context_t *ct) { int error; mntinfo_t *mi; @@ -6530,8 +6592,10 @@ nfs3_setsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr) return (ENOSYS); } +/* ARGSUSED */ static int -nfs3_getsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr) +nfs3_getsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr, + caller_context_t *ct) { int error; mntinfo_t *mi; @@ -6547,11 +6611,13 @@ nfs3_getsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr) return (error); } - return (fs_fab_acl(vp, vsecattr, flag, cr)); + return (fs_fab_acl(vp, vsecattr, flag, cr, ct)); } +/* ARGSUSED */ static int -nfs3_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr) +nfs3_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr, + caller_context_t *ct) { int error; struct shrlock nshr; @@ -6580,7 +6646,7 @@ nfs3_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr) * request off to the local share code. */ if (VTOMI(vp)->mi_flags & MI_LLOCK) - return (fs_shrlock(vp, cmd, shr, flag, cr)); + return (fs_shrlock(vp, cmd, shr, flag, cr, ct)); switch (cmd) { case F_SHARE: diff --git a/usr/src/uts/common/fs/nfs/nfs4_acl.c b/usr/src/uts/common/fs/nfs/nfs4_acl.c index 77c9506dcb91..7f5d28f0373a 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_acl.c +++ b/usr/src/uts/common/fs/nfs/nfs4_acl.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -782,6 +782,7 @@ vs_aent_to_ace4(vsecattr_t *aclentacl, vsecattr_t *vs_ace4, vs_ace4->vsa_aclcnt = 0; vs_ace4->vsa_dfaclentp = NULL; vs_ace4->vsa_dfaclcnt = 0; + vs_ace4->vsa_aclentsz = 0; if (! (aclentacl->vsa_mask & (VSA_ACL | VSA_ACLCNT | VSA_DFACL | VSA_DFACLCNT))) { @@ -1922,10 +1923,11 @@ vs_ace4_to_acet(vsecattr_t *vs_ace4, vsecattr_t *vs_acet, if ((vs_ace4->vsa_aclcnt == 0) || (vs_ace4->vsa_aclentp == NULL)) return (0); - if (vs_ace4->vsa_aclcnt > 0) + if (vs_ace4->vsa_aclcnt > 0) { vs_acet->vsa_aclentp = kmem_alloc(vs_ace4->vsa_aclcnt * sizeof (ace_t), KM_SLEEP); - else + vs_acet->vsa_aclentsz = vs_ace4->vsa_aclcnt * sizeof (ace_t); + } else vs_acet->vsa_aclentp = NULL; vs_acet->vsa_aclcnt = vs_ace4->vsa_aclcnt; vs_acet->vsa_mask = VSA_ACE | VSA_ACECNT; diff --git a/usr/src/uts/common/fs/nfs/nfs4_callback.c b/usr/src/uts/common/fs/nfs/nfs4_callback.c index 2bc2249e12e7..4c7d4e9b8c37 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_callback.c +++ b/usr/src/uts/common/fs/nfs/nfs4_callback.c @@ -1628,7 +1628,7 @@ nfs4delegreturn_impl(rnode4_t *rp, int flags, struct nfs4_callback_globals *ncg) * before doing DELEGRETURN. */ if (flags & NFS4_DR_PUSH) - (void) VOP_PUTPAGE(vp, 0, 0, 0, cr); + (void) VOP_PUTPAGE(vp, 0, 0, 0, cr, NULL); /* * Take r_deleg_recall_lock in WRITE mode, this will prevent @@ -1989,7 +1989,7 @@ deleg_reopen(vnode_t *vp, bool_t *recovp, struct nfs4_callback_globals *ncg, * We have already taken the 'r_deleg_recall_lock' as WRITER, which * prevents new OPENs from going OTW (as start_fop takes this * lock in READ mode); thus, no new open streams can be created - * (which inheretly means no new delegation open streams are + * (which inherently means no new delegation open streams are * being created). */ @@ -2096,7 +2096,7 @@ nfs4delegreturn_thread(struct cb_recall_pass *args) mutex_exit(&rp->r_statelock); if (rdirty) { - error = VOP_PUTPAGE(vp, 0, 0, 0, cr); + error = VOP_PUTPAGE(vp, 0, 0, 0, cr, NULL); if (error) CB_WARN1("nfs4delegreturn_thread:" @@ -2114,7 +2114,7 @@ nfs4delegreturn_thread(struct cb_recall_pass *args) if (rip) { - error = VOP_PUTPAGE(vp, 0, 0, B_INVAL, cr); + error = VOP_PUTPAGE(vp, 0, 0, B_INVAL, cr, NULL); if (error) CB_WARN1("nfs4delegreturn_thread: VOP_PUTPAGE: %d\n", diff --git a/usr/src/uts/common/fs/nfs/nfs4_client.c b/usr/src/uts/common/fs/nfs/nfs4_client.c index 85f68c51233c..f18e46d3c3cd 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_client.c +++ b/usr/src/uts/common/fs/nfs/nfs4_client.c @@ -351,7 +351,7 @@ flush_pages(vnode_t *vp, cred_t *cr) int error; rnode4_t *rp = VTOR4(vp); - error = VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_INVAL, cr); + error = VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_INVAL, cr, NULL); if (error == ENOSPC || error == EDQUOT) { mutex_enter(&rp->r_statelock); if (!rp->r_error) @@ -858,7 +858,7 @@ nfs4_getattr_otw_norecovery(vnode_t *vp, nfs4_ga_res_t *garp, /* getattr */ /* * Unlike nfs version 2 and 3, where getattr returns all the - * attributes, nfs version 4 returns only the ones explicitely + * attributes, nfs version 4 returns only the ones explicitly * asked for. This creates problems, as some system functions * (e.g. cache check) require certain attributes and if the * cached node lacks some attributes such as uid/gid, it can @@ -1566,7 +1566,7 @@ nfs4_async_stop(struct vfs *vfsp) * Wait for all outstanding putpage operations and the inactive thread to * complete. If a signal is delivered we will abort and return non-zero; * otherwise return 0. Since this routine is called from nfs4_unmount, we - * need to make it interruptable. + * need to make it interruptible. */ int nfs4_async_stop_sig(struct vfs *vfsp) @@ -2086,7 +2086,7 @@ nfs4_async_inactive(vnode_t *vp, cred_t *cr) * set nfs4_max_threads to zero in /etc/system. * * The manager thread knows about this and is willing to create - * at least one thread to accomodate us. + * at least one thread to accommodate us. */ mutex_enter(&mi->mi_async_lock); if (mi->mi_inactive_thread == NULL) { @@ -2404,7 +2404,7 @@ nfs4_putpages(vnode_t *vp, u_offset_t off, size_t len, int flags, cred_t *cr) flags, cr); /* - * If an error occured and the file was marked as dirty + * If an error occurred and the file was marked as dirty * before and we aren't forcibly invalidating pages, then * reset the R4DIRTY flag. */ @@ -2716,7 +2716,7 @@ nfs4_map_lost_lock_conflict(vnode_t *vp) if (lrp->lr_op != OP_LOCK && lrp->lr_op != OP_LOCKU) continue; ASSERT(lrp->lr_vp != NULL); - if (!VOP_CMP(lrp->lr_vp, vp)) + if (!VOP_CMP(lrp->lr_vp, vp, NULL)) continue; /* different file */ if (!SAFE_LOCK(*lrp->lr_flk)) { conflict = TRUE; diff --git a/usr/src/uts/common/fs/nfs/nfs4_deleg_ops.c b/usr/src/uts/common/fs/nfs/nfs4_deleg_ops.c index 27c9a9f300bf..b285807b3f86 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_deleg_ops.c +++ b/usr/src/uts/common/fs/nfs/nfs4_deleg_ops.c @@ -54,16 +54,21 @@ int deleg_rdopen( femarg_t *arg, int mode, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { clock_t rc; rfs4_file_t *fp; /* + * Now that the NFSv4 server calls VOP_OPEN, we need to check to + * to make sure it is not us calling open (like for DELEG_CUR) or + * we will end up panicing the system. * Since this monitor is for a read delegated file, we know that * only an open for write will cause a conflict. */ - if (mode & (FWRITE|FTRUNC)) { + if ((ct == NULL || ct->cc_caller_id != nfs4_srv_caller_id) && + (mode & (FWRITE|FTRUNC))) { fp = (rfs4_file_t *)arg->fa_fnode->fn_available; rfs4_recall_deleg(fp, FALSE, NULL); rfs4_dbe_lock(fp->dbe); @@ -79,7 +84,7 @@ deleg_rdopen( rfs4_dbe_unlock(fp->dbe); } - return (vnext_open(arg, mode, cr)); + return (vnext_open(arg, mode, cr, ct)); } /* monitor for open on write delegated file */ @@ -87,31 +92,36 @@ int deleg_wropen( femarg_t *arg, int mode, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { clock_t rc; rfs4_file_t *fp; - fp = (rfs4_file_t *)arg->fa_fnode->fn_available; - /* + * Now that the NFSv4 server calls VOP_OPEN, we need to check to + * to make sure it is not us calling open (like for DELEG_CUR) or + * we will end up panicing the system. * Since this monitor is for a write delegated file, we know that * any open will cause a conflict. */ - rfs4_recall_deleg(fp, FALSE, NULL); - rfs4_dbe_lock(fp->dbe); - while (fp->dinfo->dtype != OPEN_DELEGATE_NONE) { - rc = rfs4_dbe_twait(fp->dbe, - lbolt + SEC_TO_TICK(rfs4_lease_time)); - if (rc == -1) { /* timed out */ - rfs4_dbe_unlock(fp->dbe); - rfs4_recall_deleg(fp, FALSE, NULL); - rfs4_dbe_lock(fp->dbe); + if (ct == NULL || ct->cc_caller_id != nfs4_srv_caller_id) { + fp = (rfs4_file_t *)arg->fa_fnode->fn_available; + rfs4_recall_deleg(fp, FALSE, NULL); + rfs4_dbe_lock(fp->dbe); + while (fp->dinfo->dtype != OPEN_DELEGATE_NONE) { + rc = rfs4_dbe_twait(fp->dbe, + lbolt + SEC_TO_TICK(rfs4_lease_time)); + if (rc == -1) { /* timed out */ + rfs4_dbe_unlock(fp->dbe); + rfs4_recall_deleg(fp, FALSE, NULL); + rfs4_dbe_lock(fp->dbe); + } } + rfs4_dbe_unlock(fp->dbe); } - rfs4_dbe_unlock(fp->dbe); - return (vnext_open(arg, mode, cr)); + return (vnext_open(arg, mode, cr, ct)); } /* @@ -319,7 +329,8 @@ deleg_setsecattr( femarg_t *arg, vsecattr_t *vsap, int flag, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { clock_t rc; rfs4_file_t *fp; @@ -340,7 +351,7 @@ deleg_setsecattr( } rfs4_dbe_unlock(fp->dbe); - return (vnext_setsecattr(arg, vsap, flag, cr)); + return (vnext_setsecattr(arg, vsap, flag, cr, ct)); } /* ARGSUSED */ @@ -349,7 +360,8 @@ deleg_vnevent( femarg_t *arg, vnevent_t vnevent, vnode_t *dvp, - char *name) + char *name, + caller_context_t *ct) { clock_t rc; rfs4_file_t *fp; @@ -380,5 +392,5 @@ deleg_vnevent( default: break; } - return (vnext_vnevent(arg, vnevent, dvp, name)); + return (vnext_vnevent(arg, vnevent, dvp, name, ct)); } diff --git a/usr/src/uts/common/fs/nfs/nfs4_rnode.c b/usr/src/uts/common/fs/nfs/nfs4_rnode.c index 35d48fd75033..5ae6dce3c833 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_rnode.c +++ b/usr/src/uts/common/fs/nfs/nfs4_rnode.c @@ -101,7 +101,7 @@ * freelist and then trying to place them back on the freelist * when their reference is released. This means that the when an * rnode is looked up in the hash queues, then either the rnode - * is removed from the freelist and that reference is tranfered to + * is removed from the freelist and that reference is transferred to * the new reference or the vnode reference count must be incremented * accordingly. The mutex for the freelist must be held in order to * accurately test to see if the rnode is on the freelist or not. @@ -211,7 +211,7 @@ r4flushpages(rnode4_t *rp, cred_t *cr) if (nfs4_has_pages(vp)) { ASSERT(vp->v_type != VCHR); if ((rp->r_flags & R4DIRTY) && !rp->r_error) { - error = VOP_PUTPAGE(vp, (u_offset_t)0, 0, 0, cr); + error = VOP_PUTPAGE(vp, (u_offset_t)0, 0, 0, cr, NULL); if (error && (error == ENOSPC || error == EDQUOT)) { mutex_enter(&rp->r_statelock); if (!rp->r_error) @@ -1324,7 +1324,7 @@ r4flush(struct vfs *vfsp, cred_t *cr) */ while (cnt-- > 0) { vp = vplist[cnt]; - (void) VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_ASYNC, cr); + (void) VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_ASYNC, cr, NULL); VN_RELE(vp); } diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv.c b/usr/src/uts/common/fs/nfs/nfs4_srv.c index 4857af785b08..c830396935f1 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_srv.c +++ b/usr/src/uts/common/fs/nfs/nfs4_srv.c @@ -922,7 +922,8 @@ do_rfs4_op_secinfo(struct compound_state *cs, char *nm, SECINFO4res *resp) /* * Get the vnode for the component "nm". */ - error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cs->cr); + error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cs->cr, + NULL, NULL, NULL); if (error) return (puterrno4(error)); @@ -1253,7 +1254,7 @@ rfs4_op_access(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, * as well be reflected to the server during the open. */ va.va_mask = AT_MODE; - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); if (error) { *cs->statusp = resp->status = puterrno4(error); return; @@ -1283,7 +1284,7 @@ rfs4_op_access(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, } if (args->access & ACCESS4_READ) { - error = VOP_ACCESS(vp, VREAD, 0, cr); + error = VOP_ACCESS(vp, VREAD, 0, cr, NULL); if (!error && !MANDLOCK(vp, va.va_mode) && (!is_system_labeled() || admin_low_client || bldominates(clabel, slabel))) @@ -1291,7 +1292,7 @@ rfs4_op_access(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, resp->supported |= ACCESS4_READ; } if ((args->access & ACCESS4_LOOKUP) && vp->v_type == VDIR) { - error = VOP_ACCESS(vp, VEXEC, 0, cr); + error = VOP_ACCESS(vp, VEXEC, 0, cr, NULL); if (!error && (!is_system_labeled() || admin_low_client || bldominates(clabel, slabel))) resp->access |= ACCESS4_LOOKUP; @@ -1299,7 +1300,7 @@ rfs4_op_access(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, } if (checkwriteperm && (args->access & (ACCESS4_MODIFY|ACCESS4_EXTEND))) { - error = VOP_ACCESS(vp, VWRITE, 0, cr); + error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL); if (!error && !MANDLOCK(vp, va.va_mode) && (!is_system_labeled() || admin_low_client || blequal(clabel, slabel))) @@ -1310,14 +1311,14 @@ rfs4_op_access(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, if (checkwriteperm && (args->access & ACCESS4_DELETE) && vp->v_type == VDIR) { - error = VOP_ACCESS(vp, VWRITE, 0, cr); + error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL); if (!error && (!is_system_labeled() || admin_low_client || blequal(clabel, slabel))) resp->access |= ACCESS4_DELETE; resp->supported |= ACCESS4_DELETE; } if (args->access & ACCESS4_EXECUTE && vp->v_type != VDIR) { - error = VOP_ACCESS(vp, VEXEC, 0, cr); + error = VOP_ACCESS(vp, VEXEC, 0, cr, NULL); if (!error && !MANDLOCK(vp, va.va_mode) && (!is_system_labeled() || admin_low_client || bldominates(clabel, slabel))) @@ -1358,7 +1359,7 @@ rfs4_op_commit(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, } va.va_mask = AT_UID; - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); /* * If we can't get the attributes, then we can't do the @@ -1383,14 +1384,14 @@ rfs4_op_commit(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, } if (crgetuid(cr) != va.va_uid && - (error = VOP_ACCESS(vp, VWRITE, 0, cs->cr))) { + (error = VOP_ACCESS(vp, VWRITE, 0, cs->cr, NULL))) { *cs->statusp = resp->status = puterrno4(error); return; } - error = VOP_PUTPAGE(vp, args->offset, args->count, 0, cr); + error = VOP_PUTPAGE(vp, args->offset, args->count, 0, cr, NULL); if (!error) - error = VOP_FSYNC(vp, FNODSYNC, cr); + error = VOP_FSYNC(vp, FNODSYNC, cr, NULL); if (error) { *cs->statusp = resp->status = puterrno4(error); @@ -1455,7 +1456,7 @@ do_rfs4_op_mknod(CREATE4args *args, CREATE4res *resp, struct svc_req *req, mode = 0; - error = VOP_CREATE(dvp, nm, vap, excl, mode, &vp, cr, 0); + error = VOP_CREATE(dvp, nm, vap, excl, mode, &vp, cr, 0, NULL, NULL); if (error) { *cs->statusp = resp->status = puterrno4(error); return (NULL); @@ -1571,7 +1572,7 @@ rfs4_op_create(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, /* Get "before" change value */ bva.va_mask = AT_CTIME|AT_SEQ; - error = VOP_GETATTR(dvp, &bva, 0, cr); + error = VOP_GETATTR(dvp, &bva, 0, cr, NULL); if (error) { *cs->statusp = resp->status = puterrno4(error); kmem_free(nm, len); @@ -1604,7 +1605,7 @@ rfs4_op_create(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, vap->va_mode = 0700; /* default: owner rwx only */ vap->va_mask |= AT_MODE; } - error = VOP_MKDIR(dvp, nm, vap, &vp, cr); + error = VOP_MKDIR(dvp, nm, vap, &vp, cr, NULL, 0, NULL); if (error) break; @@ -1613,7 +1614,7 @@ rfs4_op_create(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, * set to zero */ iva.va_mask = AT_SEQ; - if (VOP_GETATTR(dvp, &iva, 0, cs->cr)) + if (VOP_GETATTR(dvp, &iva, 0, cs->cr, NULL)) iva.va_seq = 0; break; case NF4LNK: @@ -1645,7 +1646,7 @@ rfs4_op_create(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, return; } - error = VOP_SYMLINK(dvp, nm, vap, lnm, cr); + error = VOP_SYMLINK(dvp, nm, vap, lnm, cr, NULL, 0); if (lnm != NULL) kmem_free(lnm, llen); if (error) @@ -1656,10 +1657,11 @@ rfs4_op_create(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, * set to zero */ iva.va_mask = AT_SEQ; - if (VOP_GETATTR(dvp, &iva, 0, cs->cr)) + if (VOP_GETATTR(dvp, &iva, 0, cs->cr, NULL)) iva.va_seq = 0; - error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cr); + error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cr, + NULL, NULL, NULL); if (error) break; @@ -1668,7 +1670,7 @@ rfs4_op_create(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, * if it has changed zero out iva to force atomic = FALSE. */ iva2.va_mask = AT_SEQ; - if (VOP_GETATTR(dvp, &iva2, 0, cs->cr) || + if (VOP_GETATTR(dvp, &iva2, 0, cs->cr, NULL) || iva2.va_seq != iva.va_seq) iva.va_seq = 0; break; @@ -1698,7 +1700,7 @@ rfs4_op_create(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, * set to zero */ iva.va_mask = AT_SEQ; - if (VOP_GETATTR(dvp, &iva, 0, cs->cr)) + if (VOP_GETATTR(dvp, &iva, 0, cs->cr, NULL)) iva.va_seq = 0; break; @@ -1712,7 +1714,7 @@ rfs4_op_create(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(dvp, 0, cr); + (void) VOP_FSYNC(dvp, 0, cr, NULL); if (resp->status != NFS4_OK) { if (vp != NULL) @@ -1728,7 +1730,7 @@ rfs4_op_create(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, * before value. */ ava.va_mask = AT_CTIME|AT_SEQ; - if (VOP_GETATTR(dvp, &ava, 0, cr)) { + if (VOP_GETATTR(dvp, &ava, 0, cr, NULL)) { ava.va_ctime = bva.va_ctime; ava.va_seq = 0; } @@ -1774,7 +1776,7 @@ rfs4_op_create(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, else resp->cinfo.atomic = FALSE; - (void) VOP_FSYNC(vp, syncval, cr); + (void) VOP_FSYNC(vp, syncval, cr, NULL); if (resp->status != NFS4_OK) { VN_RELE(vp); @@ -2357,7 +2359,7 @@ rfs4_op_link(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, /* Get "before" change value */ bdva.va_mask = AT_CTIME|AT_SEQ; - error = VOP_GETATTR(dvp, &bdva, 0, cs->cr); + error = VOP_GETATTR(dvp, &bdva, 0, cs->cr, NULL); if (error) { *cs->statusp = resp->status = puterrno4(error); kmem_free(nm, len); @@ -2366,7 +2368,7 @@ rfs4_op_link(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, NFS4_SET_FATTR4_CHANGE(resp->cinfo.before, bdva.va_ctime) - error = VOP_LINK(dvp, vp, nm, cs->cr); + error = VOP_LINK(dvp, vp, nm, cs->cr, NULL, 0); kmem_free(nm, len); @@ -2374,14 +2376,14 @@ rfs4_op_link(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, * Get the initial "after" sequence number, if it fails, set to zero */ idva.va_mask = AT_SEQ; - if (VOP_GETATTR(dvp, &idva, 0, cs->cr)) + if (VOP_GETATTR(dvp, &idva, 0, cs->cr, NULL)) idva.va_seq = 0; /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cs->cr); - (void) VOP_FSYNC(dvp, 0, cs->cr); + (void) VOP_FSYNC(vp, FNODSYNC, cs->cr, NULL); + (void) VOP_FSYNC(dvp, 0, cs->cr, NULL); if (error) { *cs->statusp = resp->status = puterrno4(error); @@ -2393,7 +2395,7 @@ rfs4_op_link(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, * before value. */ adva.va_mask = AT_CTIME|AT_SEQ; - if (VOP_GETATTR(dvp, &adva, 0, cs->cr)) { + if (VOP_GETATTR(dvp, &adva, 0, cs->cr, NULL)) { adva.va_ctime = bdva.va_ctime; adva.va_seq = 0; } @@ -2484,7 +2486,8 @@ do_rfs4_op_lookup(char *nm, uint_t buflen, struct svc_req *req, } } - error = VOP_LOOKUP(cs->vp, nm, &vp, NULL, 0, NULL, cs->cr); + error = VOP_LOOKUP(cs->vp, nm, &vp, NULL, 0, NULL, cs->cr, + NULL, NULL, NULL); if (error) return (puterrno4(error)); @@ -2806,14 +2809,15 @@ rfs4_op_openattr(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, * checks from copen). */ - if ((cs->vp->v_vfsp->vfs_flag & VFS_XATTR) == 0) { + if ((cs->vp->v_vfsp->vfs_flag & VFS_XATTR) == 0 && + !vfs_has_feature(cs->vp->v_vfsp, VFSFT_XVATTR)) { error = ENOTSUP; goto error_out; } - if ((VOP_ACCESS(cs->vp, VREAD, 0, cs->cr) != 0) && - (VOP_ACCESS(cs->vp, VWRITE, 0, cs->cr) != 0) && - (VOP_ACCESS(cs->vp, VEXEC, 0, cs->cr) != 0)) { + if ((VOP_ACCESS(cs->vp, VREAD, 0, cs->cr, NULL) != 0) && + (VOP_ACCESS(cs->vp, VWRITE, 0, cs->cr, NULL) != 0) && + (VOP_ACCESS(cs->vp, VEXEC, 0, cs->cr, NULL) != 0)) { error = EACCES; goto error_out; } @@ -2835,7 +2839,8 @@ rfs4_op_openattr(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, if (args->createdir && ! (exp_ro = rdonly4(cs->exi, cs->vp, req))) lookup_flags |= CREATE_XATTR_DIR; - error = VOP_LOOKUP(cs->vp, "", &avp, NULL, lookup_flags, NULL, cs->cr); + error = VOP_LOOKUP(cs->vp, "", &avp, NULL, lookup_flags, NULL, cs->cr, + NULL, NULL, NULL); if (error) { if (error == ENOENT && args->createdir && exp_ro) @@ -2873,12 +2878,12 @@ rfs4_op_openattr(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, } static int -do_io(int direction, vnode_t *vp, struct uio *uio, int ioflag, cred_t *cred) +do_io(int direction, vnode_t *vp, struct uio *uio, int ioflag, cred_t *cred, + caller_context_t *ct) { int error; int i; clock_t delaytime; - caller_context_t ct; delaytime = MSEC_TO_TICK_ROUNDUP(rfs4_lock_delay); @@ -2888,21 +2893,17 @@ do_io(int direction, vnode_t *vp, struct uio *uio, int ioflag, cred_t *cred) */ uio->uio_fmode = FNONBLOCK; - ct.cc_sysid = 0; - ct.cc_pid = 0; - ct.cc_caller_id = nfs4_srv_caller_id; - for (i = 0; i < rfs4_maxlock_tries; i++) { if (direction == FREAD) { - (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &ct); - error = VOP_READ(vp, uio, ioflag, cred, &ct); - VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct); + (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, ct); + error = VOP_READ(vp, uio, ioflag, cred, ct); + VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, ct); } else { - (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, &ct); - error = VOP_WRITE(vp, uio, ioflag, cred, &ct); - VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, &ct); + (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, ct); + error = VOP_WRITE(vp, uio, ioflag, cred, ct); + VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, ct); } if (error != EAGAIN) @@ -2936,6 +2937,7 @@ rfs4_op_read(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, int in_crit = 0; mblk_t *mp; int alloc_err = 0; + caller_context_t ct; vp = cs->vp; if (vp == NULL) { @@ -2947,6 +2949,12 @@ rfs4_op_read(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, return; } + if ((stat = rfs4_check_stateid(FREAD, vp, &args->stateid, FALSE, + deleg, TRUE, &ct)) != NFS4_OK) { + *cs->statusp = resp->status = stat; + goto out; + } + /* * Enter the critical region before calling VOP_RWLOCK * to avoid a deadlock with write requests. @@ -2954,20 +2962,21 @@ rfs4_op_read(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, if (nbl_need_check(vp)) { nbl_start_crit(vp, RW_READER); in_crit = 1; - if (nbl_conflict(vp, NBL_READ, args->offset, args->count, 0)) { + if (nbl_conflict(vp, NBL_READ, args->offset, args->count, 0, + &ct)) { *cs->statusp = resp->status = NFS4ERR_LOCKED; goto out; } } if ((stat = rfs4_check_stateid(FREAD, vp, &args->stateid, FALSE, - deleg, TRUE)) != NFS4_OK) { + deleg, TRUE, &ct)) != NFS4_OK) { *cs->statusp = resp->status = stat; goto out; } va.va_mask = AT_MODE|AT_SIZE|AT_UID; - verror = VOP_GETATTR(vp, &va, 0, cs->cr); + verror = VOP_GETATTR(vp, &va, 0, cs->cr, &ct); /* * If we can't get the attributes, then we can't do the @@ -2985,8 +2994,8 @@ rfs4_op_read(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, } if (crgetuid(cs->cr) != va.va_uid && - (error = VOP_ACCESS(vp, VREAD, 0, cs->cr)) && - (error = VOP_ACCESS(vp, VEXEC, 0, cs->cr))) { + (error = VOP_ACCESS(vp, VREAD, 0, cs->cr, &ct)) && + (error = VOP_ACCESS(vp, VEXEC, 0, cs->cr, &ct))) { *cs->statusp = resp->status = puterrno4(error); goto out; } @@ -3051,10 +3060,10 @@ rfs4_op_read(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, uio.uio_loffset = args->offset; uio.uio_resid = args->count; - error = do_io(FREAD, vp, &uio, 0, cs->cr); + error = do_io(FREAD, vp, &uio, 0, cs->cr, &ct); va.va_mask = AT_SIZE; - verror = VOP_GETATTR(vp, &va, 0, cs->cr); + verror = VOP_GETATTR(vp, &va, 0, cs->cr, &ct); if (error) { freeb(mp); @@ -3477,7 +3486,7 @@ rfs4_op_readlink(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, } va.va_mask = AT_MODE; - error = VOP_GETATTR(vp, &va, 0, cs->cr); + error = VOP_GETATTR(vp, &va, 0, cs->cr, NULL); if (error) { *cs->statusp = resp->status = puterrno4(error); return; @@ -3499,7 +3508,7 @@ rfs4_op_readlink(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, uio.uio_loffset = 0; uio.uio_resid = MAXPATHLEN; - error = VOP_READLINK(vp, &uio, cs->cr); + error = VOP_READLINK(vp, &uio, cs->cr, NULL); if (error) { kmem_free((caddr_t)data, (uint_t)MAXPATHLEN + 1); @@ -3683,7 +3692,8 @@ rfs4_lookup_and_findfile(vnode_t *dvp, char *nm, vnode_t **vpp, if (vpp) *vpp = NULL; - if ((error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cr)) == 0) { + if ((error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cr, NULL, NULL, + NULL)) == 0) { if (vp->v_type == VREG) fp = rfs4_findfile(vp, NULL, &fcreate); if (vpp) @@ -3799,7 +3809,7 @@ rfs4_op_remove(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, if (nbl_need_check(vp)) { nbl_start_crit(vp, RW_READER); in_crit = 1; - if (nbl_conflict(vp, NBL_REMOVE, 0, 0, 0)) { + if (nbl_conflict(vp, NBL_REMOVE, 0, 0, 0, NULL)) { *cs->statusp = resp->status = NFS4ERR_FILE_OPEN; kmem_free(nm, len); nbl_end_crit(vp); @@ -3837,7 +3847,7 @@ rfs4_op_remove(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, /* Get dir "before" change value */ bdva.va_mask = AT_CTIME|AT_SEQ; - error = VOP_GETATTR(dvp, &bdva, 0, cs->cr); + error = VOP_GETATTR(dvp, &bdva, 0, cs->cr, NULL); if (error) { *cs->statusp = resp->status = puterrno4(error); kmem_free(nm, len); @@ -3860,12 +3870,12 @@ rfs4_op_remove(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, * NFS4ERR_EXIST to NFS4ERR_NOTEMPTY to * transmit over the wire. */ - if ((error = VOP_RMDIR(dvp, nm, rootdir, cs->cr)) - == EEXIST) + if ((error = VOP_RMDIR(dvp, nm, rootdir, cs->cr, + NULL, 0)) == EEXIST) error = ENOTEMPTY; } } else { - if ((error = VOP_REMOVE(dvp, nm, cs->cr)) == 0 && + if ((error = VOP_REMOVE(dvp, nm, cs->cr, NULL, 0)) == 0 && fp != NULL) { struct vattr va; vnode_t *tvp; @@ -3882,7 +3892,7 @@ rfs4_op_remove(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, * manipulating dvp. */ va.va_mask = AT_NLINK; - if (!VOP_GETATTR(tvp, &va, 0, cs->cr) && + if (!VOP_GETATTR(tvp, &va, 0, cs->cr, NULL) && va.va_nlink == 0) { /* Remove state on file remove */ if (in_crit) { @@ -3915,20 +3925,20 @@ rfs4_op_remove(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, * Get the initial "after" sequence number, if it fails, set to zero */ idva.va_mask = AT_SEQ; - if (VOP_GETATTR(dvp, &idva, 0, cs->cr)) + if (VOP_GETATTR(dvp, &idva, 0, cs->cr, NULL)) idva.va_seq = 0; /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(dvp, 0, cs->cr); + (void) VOP_FSYNC(dvp, 0, cs->cr, NULL); /* * Get "after" change value, if it fails, simply return the * before value. */ adva.va_mask = AT_CTIME|AT_SEQ; - if (VOP_GETATTR(dvp, &adva, 0, cs->cr)) { + if (VOP_GETATTR(dvp, &adva, 0, cs->cr, NULL)) { adva.va_ctime = bdva.va_ctime; adva.va_seq = 0; } @@ -4120,7 +4130,7 @@ rfs4_op_rename(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, if (nbl_need_check(srcvp)) { nbl_start_crit(srcvp, RW_READER); in_crit_src = 1; - if (nbl_conflict(srcvp, NBL_RENAME, 0, 0, 0)) { + if (nbl_conflict(srcvp, NBL_RENAME, 0, 0, 0, NULL)) { *cs->statusp = resp->status = NFS4ERR_FILE_OPEN; goto err_out; } @@ -4129,7 +4139,7 @@ rfs4_op_rename(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, if (targvp && nbl_need_check(targvp)) { nbl_start_crit(targvp, RW_READER); in_crit_targ = 1; - if (nbl_conflict(targvp, NBL_REMOVE, 0, 0, 0)) { + if (nbl_conflict(targvp, NBL_REMOVE, 0, 0, 0, NULL)) { *cs->statusp = resp->status = NFS4ERR_FILE_OPEN; goto err_out; } @@ -4137,10 +4147,10 @@ rfs4_op_rename(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, /* Get source "before" change value */ obdva.va_mask = AT_CTIME|AT_SEQ; - error = VOP_GETATTR(odvp, &obdva, 0, cs->cr); + error = VOP_GETATTR(odvp, &obdva, 0, cs->cr, NULL); if (!error) { nbdva.va_mask = AT_CTIME|AT_SEQ; - error = VOP_GETATTR(ndvp, &nbdva, 0, cs->cr); + error = VOP_GETATTR(ndvp, &nbdva, 0, cs->cr, NULL); } if (error) { *cs->statusp = resp->status = puterrno4(error); @@ -4150,7 +4160,7 @@ rfs4_op_rename(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, NFS4_SET_FATTR4_CHANGE(resp->source_cinfo.before, obdva.va_ctime) NFS4_SET_FATTR4_CHANGE(resp->target_cinfo.before, nbdva.va_ctime) - if ((error = VOP_RENAME(odvp, onm, ndvp, nnm, cs->cr)) == 0 && + if ((error = VOP_RENAME(odvp, onm, ndvp, nnm, cs->cr, NULL, 0)) == 0 && fp != NULL) { struct vattr va; vnode_t *tvp; @@ -4163,7 +4173,7 @@ rfs4_op_rename(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, if (tvp) { va.va_mask = AT_NLINK; - if (!VOP_GETATTR(tvp, &va, 0, cs->cr) && + if (!VOP_GETATTR(tvp, &va, 0, cs->cr, NULL) && va.va_nlink == 0) { /* The file is gone and so should the state */ if (in_crit_targ) { @@ -4213,18 +4223,18 @@ rfs4_op_rename(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, * Get the initial "after" sequence number, if it fails, set to zero */ oidva.va_mask = AT_SEQ; - if (VOP_GETATTR(odvp, &oidva, 0, cs->cr)) + if (VOP_GETATTR(odvp, &oidva, 0, cs->cr, NULL)) oidva.va_seq = 0; nidva.va_mask = AT_SEQ; - if (VOP_GETATTR(ndvp, &nidva, 0, cs->cr)) + if (VOP_GETATTR(ndvp, &nidva, 0, cs->cr, NULL)) nidva.va_seq = 0; /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(odvp, 0, cs->cr); - (void) VOP_FSYNC(ndvp, 0, cs->cr); + (void) VOP_FSYNC(odvp, 0, cs->cr, NULL); + (void) VOP_FSYNC(ndvp, 0, cs->cr, NULL); if (error) { *cs->statusp = resp->status = puterrno4(error); @@ -4236,13 +4246,13 @@ rfs4_op_rename(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, * before value. */ oadva.va_mask = AT_CTIME|AT_SEQ; - if (VOP_GETATTR(odvp, &oadva, 0, cs->cr)) { + if (VOP_GETATTR(odvp, &oadva, 0, cs->cr, NULL)) { oadva.va_ctime = obdva.va_ctime; oadva.va_seq = 0; } nadva.va_mask = AT_CTIME|AT_SEQ; - if (VOP_GETATTR(odvp, &nadva, 0, cs->cr)) { + if (VOP_GETATTR(odvp, &nadva, 0, cs->cr, NULL)) { nadva.va_ctime = nbdva.va_ctime; nadva.va_seq = 0; } @@ -4282,7 +4292,8 @@ rfs4_op_rename(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, /* * Already know that nnm will be a valid string */ - error = VOP_LOOKUP(ndvp, nnm, &vp, NULL, 0, NULL, cs->cr); + error = VOP_LOOKUP(ndvp, nnm, &vp, NULL, 0, NULL, cs->cr, + NULL, NULL, NULL); kmem_free(nnm, nlen); if (!error) { add_volrnm_fh(cs->exi, vp); @@ -4435,7 +4446,7 @@ rfs4_verify_attr(struct nfs4_svgetit_arg *sargp, * on the incoming values. */ ret_error = VOP_GETATTR(sargp->cs->vp, sargp->vap, 0, - sargp->cs->cr); + sargp->cs->cr, NULL); if (ret_error) { if (resp == NULL) return (ret_error); @@ -4710,7 +4721,7 @@ do_rfs4_op_setattr(bitmap4 *resp, fattr4 *fattrp, struct compound_state *cs, vattr_t va; va.va_mask = AT_MODE; - error = VOP_GETATTR(vp, &va, 0, cs->cr); + error = VOP_GETATTR(vp, &va, 0, cs->cr, NULL); if (error) { status = puterrno4(error); goto done; @@ -4726,15 +4737,15 @@ do_rfs4_op_setattr(bitmap4 *resp, fattr4 *fattrp, struct compound_state *cs, if (sarg.vap->va_mask & AT_SIZE) { trunc = (sarg.vap->va_size == 0); status = rfs4_check_stateid(FWRITE, cs->vp, stateid, - trunc, &cs->deleg, sarg.vap->va_mask & AT_SIZE); + trunc, &cs->deleg, sarg.vap->va_mask & AT_SIZE, &ct); if (status != NFS4_OK) goto done; + } else { + ct.cc_sysid = 0; + ct.cc_pid = 0; + ct.cc_caller_id = nfs4_srv_caller_id; } - ct.cc_sysid = 0; - ct.cc_pid = 0; - ct.cc_caller_id = nfs4_srv_caller_id; - /* XXX start of possible race with delegations */ /* @@ -4776,7 +4787,7 @@ do_rfs4_op_setattr(bitmap4 *resp, fattr4 *fattrp, struct compound_state *cs, } bva.va_mask = AT_UID|AT_SIZE; - if (error = VOP_GETATTR(vp, &bva, 0, cr)) { + if (error = VOP_GETATTR(vp, &bva, 0, cr, &ct)) { status = puterrno4(error); goto done; } @@ -4789,7 +4800,8 @@ do_rfs4_op_setattr(bitmap4 *resp, fattr4 *fattrp, struct compound_state *cs, offset = bva.va_size; length = sarg.vap->va_size - bva.va_size; } - if (nbl_conflict(vp, NBL_WRITE, offset, length, 0)) { + if (nbl_conflict(vp, NBL_WRITE, offset, length, 0, + &ct)) { status = NFS4ERR_LOCKED; goto done; } @@ -4857,7 +4869,7 @@ do_rfs4_op_setattr(bitmap4 *resp, fattr4 *fattrp, struct compound_state *cs, /* * Force modified metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cr); + (void) VOP_FSYNC(vp, FNODSYNC, cr, &ct); /* * Set response bitmap */ @@ -5067,6 +5079,7 @@ rfs4_op_write(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, bool_t *deleg = &cs->deleg; nfsstat4 stat; int in_crit = 0; + caller_context_t ct; vp = cs->vp; if (vp == NULL) { @@ -5080,6 +5093,12 @@ rfs4_op_write(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, cr = cs->cr; + if ((stat = rfs4_check_stateid(FWRITE, vp, &args->stateid, FALSE, + deleg, TRUE, &ct)) != NFS4_OK) { + *cs->statusp = resp->status = stat; + goto out; + } + /* * We have to enter the critical region before calling VOP_RWLOCK * to avoid a deadlock with ufs. @@ -5088,20 +5107,14 @@ rfs4_op_write(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, nbl_start_crit(vp, RW_READER); in_crit = 1; if (nbl_conflict(vp, NBL_WRITE, - args->offset, args->data_len, 0)) { + args->offset, args->data_len, 0, &ct)) { *cs->statusp = resp->status = NFS4ERR_LOCKED; goto out; } } - if ((stat = rfs4_check_stateid(FWRITE, vp, &args->stateid, FALSE, - deleg, TRUE)) != NFS4_OK) { - *cs->statusp = resp->status = stat; - goto out; - } - bva.va_mask = AT_MODE | AT_UID; - error = VOP_GETATTR(vp, &bva, 0, cr); + error = VOP_GETATTR(vp, &bva, 0, cr, &ct); /* * If we can't get the attributes, then we can't do the @@ -5124,7 +5137,7 @@ rfs4_op_write(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, } if (crgetuid(cr) != bva.va_uid && - (error = VOP_ACCESS(vp, VWRITE, 0, cr))) { + (error = VOP_ACCESS(vp, VWRITE, 0, cr, &ct))) { *cs->statusp = resp->status = puterrno4(error); goto out; } @@ -5210,7 +5223,7 @@ rfs4_op_write(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, */ savecred = curthread->t_cred; curthread->t_cred = cr; - error = do_io(FWRITE, vp, &uio, ioflag, cr); + error = do_io(FWRITE, vp, &uio, ioflag, cr, &ct); curthread->t_cred = savecred; if (iovp != iov) @@ -5478,6 +5491,8 @@ static void lock_print(char *str, int operation, struct flock64 *flk) break; case F_SETLK: op = "F_SETLK"; break; + case F_SETLK_NBMAND: op = "F_SETLK_NBMAND"; + break; default: op = "F_UNKNOWN"; break; } @@ -5562,7 +5577,7 @@ rfs4_lookupfile(component4 *component, struct svc_req *req, /* Get "before" change value */ bva.va_mask = AT_CTIME|AT_SEQ; - error = VOP_GETATTR(dvp, &bva, 0, cs->cr); + error = VOP_GETATTR(dvp, &bva, 0, cs->cr, NULL); if (error) return (puterrno4(error)); @@ -5580,7 +5595,7 @@ rfs4_lookupfile(component4 *component, struct svc_req *req, * before value. */ ava.va_mask = AT_CTIME|AT_SEQ; - if (VOP_GETATTR(dvp, &ava, 0, cs->cr)) { + if (VOP_GETATTR(dvp, &ava, 0, cs->cr, NULL)) { ava.va_ctime = bva.va_ctime; ava.va_seq = 0; } @@ -5590,7 +5605,7 @@ rfs4_lookupfile(component4 *component, struct svc_req *req, * Validate the file is a file */ fva.va_mask = AT_TYPE|AT_MODE; - error = VOP_GETATTR(cs->vp, &fva, 0, cs->cr); + error = VOP_GETATTR(cs->vp, &fva, 0, cs->cr, NULL); if (error) return (puterrno4(error)); @@ -5638,7 +5653,7 @@ create_vnode(vnode_t *dvp, char *nm, vattr_t *vap, createmode4 mode, */ *created = TRUE; - error = VOP_CREATE(dvp, nm, vap, EXCL, VWRITE, vpp, cr, 0); + error = VOP_CREATE(dvp, nm, vap, EXCL, VWRITE, vpp, cr, 0, NULL, NULL); if (error) { *created = FALSE; @@ -5659,7 +5674,8 @@ create_vnode(vnode_t *dvp, char *nm, vattr_t *vap, createmode4 mode, status = puterrno4(error); return (status); } - error = VOP_LOOKUP(dvp, nm, vpp, NULL, 0, NULL, cr); + error = VOP_LOOKUP(dvp, nm, vpp, NULL, 0, NULL, cr, + NULL, NULL, NULL); if (error) { /* @@ -5693,7 +5709,7 @@ create_vnode(vnode_t *dvp, char *nm, vattr_t *vap, createmode4 mode, /* Check for duplicate request */ ASSERT(mtime != 0); va.va_mask = AT_MTIME; - error = VOP_GETATTR(*vpp, &va, 0, cr); + error = VOP_GETATTR(*vpp, &va, 0, cr, NULL); if (!error) { /* We found the file */ if (va.va_mtime.tv_sec != mtime->tv_sec || @@ -5740,14 +5756,14 @@ check_open_access(uint32_t access, return (NFS4ERR_ROFS); if (access & OPEN4_SHARE_ACCESS_READ) { - if ((VOP_ACCESS(vp, VREAD, 0, cr) != 0) && - (VOP_ACCESS(vp, VEXEC, 0, cr) != 0)) { + if ((VOP_ACCESS(vp, VREAD, 0, cr, NULL) != 0) && + (VOP_ACCESS(vp, VEXEC, 0, cr, NULL) != 0)) { return (NFS4ERR_ACCESS); } } if (access & OPEN4_SHARE_ACCESS_WRITE) { - error = VOP_ACCESS(vp, VWRITE, 0, cr); + error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL); if (error) return (NFS4ERR_ACCESS); } @@ -5821,7 +5837,7 @@ rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs, } bva.va_mask = AT_TYPE|AT_CTIME|AT_SEQ; - error = VOP_GETATTR(dvp, &bva, 0, cs->cr); + error = VOP_GETATTR(dvp, &bva, 0, cs->cr, NULL); if (error) { kmem_free(nm, buflen); return (puterrno4(error)); @@ -5968,7 +5984,7 @@ rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs, * set to zero, time to before. */ iva.va_mask = AT_CTIME|AT_SEQ; - if (VOP_GETATTR(dvp, &iva, 0, cs->cr)) { + if (VOP_GETATTR(dvp, &iva, 0, cs->cr, NULL)) { iva.va_seq = 0; iva.va_ctime = bva.va_ctime; } @@ -5992,14 +6008,14 @@ rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs, * The entry was created, we need to sync the * directory metadata. */ - (void) VOP_FSYNC(dvp, 0, cs->cr); + (void) VOP_FSYNC(dvp, 0, cs->cr, NULL); /* * Get "after" change value, if it fails, simply return the * before value. */ ava.va_mask = AT_CTIME|AT_SEQ; - if (VOP_GETATTR(dvp, &ava, 0, cs->cr)) { + if (VOP_GETATTR(dvp, &ava, 0, cs->cr, NULL)) { ava.va_ctime = bva.va_ctime; ava.va_seq = 0; } @@ -6027,7 +6043,7 @@ rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs, /* Assume the worst */ cs->mandlock = TRUE; - if (VOP_GETATTR(vp, &cva, 0, cs->cr) == 0) { + if (VOP_GETATTR(vp, &cva, 0, cs->cr, NULL) == 0) { cs->mandlock = MANDLOCK(cs->vp, cva.va_mode); /* @@ -6065,7 +6081,7 @@ rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs, nbl_start_crit(vp, RW_READER); if (nbl_conflict(vp, NBL_WRITE, 0, - cva.va_size, 0)) { + cva.va_size, 0, NULL)) { in_crit = 0; nbl_end_crit(vp); VN_RELE(vp); @@ -6090,7 +6106,7 @@ rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs, /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cs->cr); + (void) VOP_FSYNC(vp, FNODSYNC, cs->cr, NULL); if (error) { VN_RELE(vp); @@ -6127,7 +6143,7 @@ static void rfs4_do_open(struct compound_state *cs, struct svc_req *req, rfs4_openowner_t *oo, delegreq_t deleg, uint32_t access, uint32_t deny, - OPEN4res *resp) + OPEN4res *resp, int deleg_cur) { /* XXX Currently not using req */ rfs4_state_t *state; @@ -6141,9 +6157,11 @@ rfs4_do_open(struct compound_state *cs, struct svc_req *req, struct shr_locowner shr_loco; sysid_t sysid; nfsstat4 status; + caller_context_t ct; int fflags = 0; int recall = 0; int err; + int cmd; /* get the file struct and hold a lock on it during initial open */ file = rfs4_findfile_withlock(cs->vp, &cs->fh, &fcreate); @@ -6175,6 +6193,12 @@ rfs4_do_open(struct compound_state *cs, struct svc_req *req, return; } + /* Calculate the fflags for this OPEN. */ + if (access & OPEN4_SHARE_ACCESS_READ) + fflags |= FREAD; + if (access & OPEN4_SHARE_ACCESS_WRITE) + fflags |= FWRITE; + /* * Calculate the new deny and access mode that this open is adding to * the file for this open owner; @@ -6203,13 +6227,8 @@ rfs4_do_open(struct compound_state *cs, struct svc_req *req, shr.s_owner = (caddr_t)&shr_loco; shr.s_own_len = sizeof (shr_loco); - fflags = 0; - if (access & OPEN4_SHARE_ACCESS_READ) - fflags |= FREAD; - if (access & OPEN4_SHARE_ACCESS_WRITE) - fflags |= FWRITE; - - if ((err = vop_shrlock(cs->vp, F_SHARE, &shr, fflags)) != 0) { + cmd = nbl_need_check(cs->vp) ? F_SHARE_NBMAND : F_SHARE; + if ((err = vop_shrlock(cs->vp, cmd, &shr, fflags)) != 0) { resp->status = err == EAGAIN ? NFS4ERR_SHARE_DENIED : puterrno4(err); @@ -6243,12 +6262,6 @@ rfs4_do_open(struct compound_state *cs, struct svc_req *req, rfs4_dbe_unlock(state->dbe); rfs4_file_rele(file); rfs4_update_lease(state->owner->client); - /* recalculate flags to match what was added */ - fflags = 0; - if (amodes & OPEN4_SHARE_ACCESS_READ) - fflags |= FREAD; - if (amodes & OPEN4_SHARE_ACCESS_WRITE) - fflags |= FWRITE; (void) vop_shrlock(cs->vp, F_UNSHARE, &shr, fflags); /* Not a fully formed open; "close" it */ if (screate == TRUE) @@ -6258,6 +6271,49 @@ rfs4_do_open(struct compound_state *cs, struct svc_req *req, return; } } + /* + * the share check passed and any delegation conflict has been + * taken care of, now call vop_open. + * if this is the first open then call vop_open with fflags. + * if not, call vn_open_upgrade with just the upgrade flags. + * + * if the file has been opened already, it will have the current + * access mode in the state struct. if it has no share access, then + * this is a new open. + * + * However, if this is open with CLAIM_DLEGATE_CUR, then don't + * call VOP_OPEN(), just do the open upgrade. + */ + if (((state->share_access & OPEN4_SHARE_ACCESS_BOTH) == 0) && + !deleg_cur) { + ct.cc_sysid = sysid; + ct.cc_pid = shr.s_pid; + ct.cc_caller_id = nfs4_srv_caller_id; + err = VOP_OPEN(&cs->vp, fflags, cs->cr, &ct); + if (err) { + rfs4_dbe_unlock(file->dbe); + rfs4_dbe_unlock(state->dbe); + rfs4_file_rele(file); + (void) vop_shrlock(cs->vp, F_UNSHARE, &shr, fflags); + /* Not a fully formed open; "close" it */ + if (screate == TRUE) + rfs4_state_close(state, FALSE, FALSE, cs->cr); + rfs4_state_rele(state); + resp->status = NFS4ERR_SERVERFAULT; + return; + } + } else { /* open upgrade */ + /* + * calculate the fflags for the new mode that is being added + * by this upgrade. + */ + fflags = 0; + if (amodes & OPEN4_SHARE_ACCESS_READ) + fflags |= FREAD; + if (amodes & OPEN4_SHARE_ACCESS_WRITE) + fflags |= FWRITE; + vn_open_upgrade(cs->vp, fflags); + } if (dmodes & OPEN4_SHARE_DENY_READ) file->deny_read++; @@ -6328,7 +6384,7 @@ rfs4_do_opennull(struct compound_state *cs, struct svc_req *req, /* cs->vp cs->fh now reference the desired file */ rfs4_do_open(cs, req, oo, DELEG_ANY, args->share_access, - args->share_deny, resp); + args->share_deny, resp, 0); /* * If rfs4_createfile set attrset, we must @@ -6367,7 +6423,7 @@ rfs4_do_openprev(struct compound_state *cs, struct svc_req *req, } va.va_mask = AT_MODE|AT_UID; - error = VOP_GETATTR(cs->vp, &va, 0, cs->cr); + error = VOP_GETATTR(cs->vp, &va, 0, cs->cr, NULL); if (error) { resp->status = puterrno4(error); return; @@ -6398,7 +6454,7 @@ rfs4_do_openprev(struct compound_state *cs, struct svc_req *req, rfs4_do_open(cs, req, oo, NFS4_DELEG4TYPE2REQTYPE(args->open_claim4_u.delegate_type), - args->share_access, args->share_deny, resp); + args->share_access, args->share_deny, resp, 0); } static void @@ -6452,7 +6508,7 @@ rfs4_do_opendelcur(struct compound_state *cs, struct svc_req *req, dsp->finfo->dinfo->time_lastwrite = gethrestime_sec(); rfs4_deleg_state_rele(dsp); rfs4_do_open(cs, req, oo, DELEG_NONE, - args->share_access, args->share_deny, resp); + args->share_access, args->share_deny, resp, 1); } /*ARGSUSED*/ @@ -7043,6 +7099,7 @@ rfs4_op_open_downgrade(nfs_argop4 *argop, nfs_resop4 *resop, nfsstat4 status; rfs4_state_t *sp; rfs4_file_t *fp; + int fflags = 0; if (cs->vp == NULL) { *cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE; @@ -7121,7 +7178,7 @@ rfs4_op_open_downgrade(nfs_argop4 *argop, nfs_resop4 *resop, * Check that no invalid bits are set. */ if ((access & ~(OPEN4_SHARE_ACCESS_READ | OPEN4_SHARE_ACCESS_WRITE)) || - (deny & ~(OPEN4_SHARE_DENY_READ | OPEN4_SHARE_DENY_READ))) { + (deny & ~(OPEN4_SHARE_DENY_READ | OPEN4_SHARE_DENY_WRITE))) { *cs->statusp = resp->status = NFS4ERR_INVAL; rfs4_update_open_sequence(sp->owner); rfs4_dbe_unlock(sp->dbe); @@ -7193,26 +7250,30 @@ rfs4_op_open_downgrade(nfs_argop4 *argop, nfs_resop4 *resop, * If the current mode has access read and the new mode * does not, decrement the number of access read mode bits * and if it goes to zero turn off the access read bit - * on the file. + * on the file. set fflags to FREAD for the call to + * vn_open_downgrade(). */ if ((sp->share_access & OPEN4_SHARE_ACCESS_READ) && (access & OPEN4_SHARE_ACCESS_READ) == 0) { fp->access_read--; if (fp->access_read == 0) fp->share_access &= ~OPEN4_SHARE_ACCESS_READ; + fflags |= FREAD; } /* * If the current mode has access write and the new mode * does not, decrement the number of access write mode bits * and if it goes to zero turn off the access write bit - * on the file. + * on the file. set fflags to FWRITE for the call to + * vn_open_downgrade(). */ if ((sp->share_access & OPEN4_SHARE_ACCESS_WRITE) && (access & OPEN4_SHARE_ACCESS_WRITE) == 0) { fp->access_write--; if (fp->access_write == 0) fp->share_deny &= ~OPEN4_SHARE_ACCESS_WRITE; + fflags |= FWRITE; } /* Set the new access and deny modes */ @@ -7224,12 +7285,21 @@ rfs4_op_open_downgrade(nfs_argop4 *argop, nfs_resop4 *resop, rfs4_dbe_unlock(fp->dbe); rfs4_dbe_unlock(sp->dbe); + if ((status = rfs4_share(sp)) != NFS4_OK) { *cs->statusp = resp->status = NFS4ERR_SERVERFAULT; rfs4_update_open_sequence(sp->owner); goto end; } + /* + * we successfully downgraded the share lock, now we need to downgrade + * the open. it is possible that the downgrade was only for a deny + * mode and we have nothing else to do. + */ + if ((fflags & (FREAD|FWRITE)) != 0) + vn_open_downgrade(cs->vp, fflags); + rfs4_dbe_lock(sp->dbe); /* Update the stateid */ @@ -7719,7 +7789,7 @@ rfs4_release_share_lock_state(rfs4_state_t *sp, cred_t *cr, flk.l_pid = 0; (void) VOP_FRLOCK(sp->finfo->vp, F_SETLK, &flk, F_REMOTELOCK | FREAD | FWRITE, - (u_offset_t)0, NULL, CRED()); + (u_offset_t)0, NULL, CRED(), NULL); } sp->owner->client->unlksys_completed = TRUE; @@ -7757,6 +7827,8 @@ rfs4_release_share_lock_state(rfs4_state_t *sp, cred_t *cr, shr.s_own_len = sizeof (shr_loco); (void) vop_shrlock(sp->finfo->vp, F_UNSHARE, &shr, fflags); } + + (void) VOP_CLOSE(fp->vp, fflags, 1, (offset_t)0, cr, NULL); } /* @@ -7819,14 +7891,16 @@ setlock(vnode_t *vp, struct flock64 *flock, int flag, cred_t *cred) struct flock64 flk; int i; clock_t delaytime; + int cmd; + cmd = nbl_need_check(vp) ? F_SETLK_NBMAND : F_SETLK; retry: delaytime = MSEC_TO_TICK_ROUNDUP(rfs4_lock_delay); for (i = 0; i < rfs4_maxlock_tries; i++) { - LOCK_PRINT(rfs4_debug, "setlock", F_SETLK, flock); - error = VOP_FRLOCK(vp, F_SETLK, - flock, flag, (u_offset_t)0, NULL, cred); + LOCK_PRINT(rfs4_debug, "setlock", cmd, flock); + error = VOP_FRLOCK(vp, cmd, + flock, flag, (u_offset_t)0, NULL, cred, NULL); if (error != EAGAIN && error != EACCES) break; @@ -7841,8 +7915,8 @@ setlock(vnode_t *vp, struct flock64 *flock, int flag, cred_t *cred) /* Get the owner of the lock */ flk = *flock; LOCK_PRINT(rfs4_debug, "setlock", F_GETLK, &flk); - if (VOP_FRLOCK(vp, F_GETLK, - &flk, flag, (u_offset_t)0, NULL, cred) == 0) { + if (VOP_FRLOCK(vp, F_GETLK, &flk, flag, + (u_offset_t)0, NULL, cred, NULL) == 0) { if (flk.l_type == F_UNLCK) { /* No longer locked, retry */ goto retry; @@ -8618,7 +8692,7 @@ rfs4_op_lockt(nfs_argop4 *argop, nfs_resop4 *resop, goto out; } error = VOP_FRLOCK(cs->vp, F_GETLK, &flk, flag, (u_offset_t)0, - NULL, cs->cr); + NULL, cs->cr, NULL); /* * N.B. We map error values to nfsv4 errors. This is differrent @@ -8661,11 +8735,11 @@ vop_shrlock(vnode_t *vp, int cmd, struct shrlock *sp, int fflags) if (cmd == F_UNSHARE && sp->s_deny == 0 && sp->s_access == 0) return (0); - err = VOP_SHRLOCK(vp, cmd, sp, fflags, CRED()); + err = VOP_SHRLOCK(vp, cmd, sp, fflags, CRED(), NULL); NFS4_DEBUG(rfs4_shrlock_debug, (CE_NOTE, "rfs4_shrlock %s vp=%p acc=%d dny=%d sysid=%d " - "pid=%d err=%d\n", cmd == F_SHARE ? "SHARE" : "UNSHR", + "pid=%d err=%d\n", cmd == F_UNSHARE ? "UNSHR" : "SHARE", (void *) vp, sp->s_access, sp->s_deny, sp->s_sysid, sp->s_pid, err)); @@ -8706,7 +8780,9 @@ rfs4_shrlock(rfs4_state_t *sp, int cmd) static int rfs4_share(rfs4_state_t *sp) { - return (rfs4_shrlock(sp, F_SHARE)); + int cmd; + cmd = nbl_need_check(sp->finfo->vp) ? F_SHARE_NBMAND : F_SHARE; + return (rfs4_shrlock(sp, cmd)); } void diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c b/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c index 9b5d9e230113..11a940f199da 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c +++ b/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -37,7 +37,7 @@ void rfs4_init_compound_state(struct compound_state *); bitmap4 rfs4_supported_attrs; int MSG_PRT_DEBUG = FALSE; -/* If building with DEBUG enabled, enable mandattr tuneable by default */ +/* If building with DEBUG enabled, enable mandattr tunable by default */ #ifdef DEBUG #ifndef RFS4_SUPPORT_MANDATTR_ONLY #define RFS4_SUPPORT_MANDATTR_ONLY @@ -575,7 +575,7 @@ rfs4_fattr4_named_attr(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, */ if (sarg->cs->vp->v_vfsp->vfs_flag & VFS_XATTR) { error = VOP_PATHCONF(sarg->cs->vp, _PC_XATTR_EXISTS, - &val, sarg->cs->cr); + &val, sarg->cs->cr, NULL); if (error) break; } else @@ -592,7 +592,7 @@ rfs4_fattr4_named_attr(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, ASSERT(sarg->cs->vp != NULL); if (sarg->cs->vp->v_vfsp->vfs_flag & VFS_XATTR) { error = VOP_PATHCONF(sarg->cs->vp, _PC_XATTR_EXISTS, - &val, sarg->cs->cr); + &val, sarg->cs->cr, NULL); if (error) break; } else @@ -863,7 +863,7 @@ rfs4_fattr4_acl(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, /* see which ACLs fs supports */ error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &whichacl, - sarg->cs->cr); + sarg->cs->cr, NULL); if (error != 0) { /* * If we got an error, then the filesystem @@ -902,7 +902,7 @@ rfs4_fattr4_acl(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, /* get the ACL, and translate it into nfsace4 style */ error = VOP_GETSECATTR(vp, &vs_native, - 0, sarg->cs->cr); + 0, sarg->cs->cr, NULL); if (error != 0) break; if (whichacl & _ACL_ACE_ENABLED) { @@ -942,7 +942,7 @@ rfs4_fattr4_acl(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, vs_ace4.vsa_mask = VSA_ACE | VSA_ACECNT; vs_ace4.vsa_aclcnt = na->acl.fattr4_acl_len; vs_ace4.vsa_aclentp = na->acl.fattr4_acl_val; - + vs_ace4.vsa_aclentsz = vs_ace4.vsa_aclcnt * sizeof (ace_t); /* make sure we have correct owner/group */ if ((vap->va_mask & (AT_UID | AT_GID)) != (AT_UID | AT_GID)) { @@ -956,7 +956,7 @@ rfs4_fattr4_acl(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, /* see which ACLs the fs supports */ error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &whichacl, - sarg->cs->cr); + sarg->cs->cr, NULL); if (error != 0) { /* * If we got an error, then the filesystem @@ -991,7 +991,7 @@ rfs4_fattr4_acl(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, break; (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); error = VOP_SETSECATTR(vp, &vs_native, - 0, sarg->cs->cr); + 0, sarg->cs->cr, NULL); VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); vs_acet_destroy(&vs_native); } else if (whichacl & _ACL_ACLENT_ENABLED) { @@ -1002,7 +1002,7 @@ rfs4_fattr4_acl(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, break; (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); error = VOP_SETSECATTR(vp, &vs_native, - 0, sarg->cs->cr); + 0, sarg->cs->cr, NULL); VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); vs_aent_destroy(&vs_native); } @@ -1097,7 +1097,7 @@ rfs4_fattr4_cansettime(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, /* * XXX - need VOP extension to ask file system (e.g. pcfs) if it supports - * case insenstive. + * case insensitive. */ /* ARGSUSED */ static int @@ -1191,7 +1191,7 @@ rfs4_fattr4_chown_restricted(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, } ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, - _PC_CHOWN_RESTRICTED, &val, sarg->cs->cr); + _PC_CHOWN_RESTRICTED, &val, sarg->cs->cr, NULL); if (error) break; @@ -1206,7 +1206,7 @@ rfs4_fattr4_chown_restricted(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, case NFS4ATTR_VERIT: ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, - _PC_CHOWN_RESTRICTED, &val, sarg->cs->cr); + _PC_CHOWN_RESTRICTED, &val, sarg->cs->cr, NULL); if (error) break; if (na->chown_restricted != (val == 1)) @@ -1559,7 +1559,7 @@ rfs4_fattr4_maxfilesize(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, } ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, _PC_FILESIZEBITS, &val, - sarg->cs->cr); + sarg->cs->cr, NULL); if (error) break; if (val >= (sizeof (uint64_t) * 8)) @@ -1576,7 +1576,7 @@ rfs4_fattr4_maxfilesize(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, case NFS4ATTR_VERIT: ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, _PC_FILESIZEBITS, &val, - sarg->cs->cr); + sarg->cs->cr, NULL); if (error) break; if (val >= (sizeof (uint64_t) * 8)) @@ -1615,7 +1615,7 @@ rfs4_fattr4_maxlink(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, } ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, _PC_LINK_MAX, &val, - sarg->cs->cr); + sarg->cs->cr, NULL); if (error == 0) { na->maxlink = val; } @@ -1629,7 +1629,7 @@ rfs4_fattr4_maxlink(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, case NFS4ATTR_VERIT: ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, _PC_LINK_MAX, &val, - sarg->cs->cr); + sarg->cs->cr, NULL); if (!error && (na->maxlink != (uint32_t)val)) error = -1; /* no match */ break; @@ -1662,7 +1662,7 @@ rfs4_fattr4_maxname(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, } ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, _PC_NAME_MAX, &val, - sarg->cs->cr); + sarg->cs->cr, NULL); if (error == 0) { na->maxname = val; } @@ -1676,7 +1676,7 @@ rfs4_fattr4_maxname(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, case NFS4ATTR_VERIT: ASSERT(sarg->cs->vp != NULL); error = VOP_PATHCONF(sarg->cs->vp, _PC_NAME_MAX, &val, - sarg->cs->cr); + sarg->cs->cr, NULL); if (!error && (na->maxname != val)) error = -1; /* no match */ break; diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv_deleg.c b/usr/src/uts/common/fs/nfs/nfs4_srv_deleg.c index 691711e5a5ee..4c150e248fe0 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_srv_deleg.c +++ b/usr/src/uts/common/fs/nfs/nfs4_srv_deleg.c @@ -805,7 +805,7 @@ rfs4_vop_getattr(vnode_t *vp, vattr_t *vap, int flag, cred_t *cr) int error; mask = vap->va_mask; - error = VOP_GETATTR(vp, vap, flag, cr); + error = VOP_GETATTR(vp, vap, flag, cr, NULL); /* * Some file systems clobber va_mask. it is probably wrong of * them to do so, nonethless we practice defensive coding. @@ -825,7 +825,7 @@ rfs4_vop_getattr(vnode_t *vp, vattr_t *vap, int flag, cred_t *cr) int rfs4_delegated_getattr(vnode_t *vp, vattr_t *vap, int flag, cred_t *cr) { - return (VOP_GETATTR(vp, vap, flag, cr)); + return (VOP_GETATTR(vp, vap, flag, cr, NULL)); } /* @@ -1547,6 +1547,8 @@ rfs4_deleg_state(rfs4_state_t *sp, open_delegation_type4 dtype, int *recall) rfs4_deleg_state_t *dsp; vnode_t *vp; int open_prev = *recall; + int ret; + int fflags = 0; ASSERT(rfs4_dbe_islocked(sp->dbe)); ASSERT(rfs4_dbe_islocked(fp->dbe)); @@ -1605,14 +1607,27 @@ rfs4_deleg_state(rfs4_state_t *sp, open_delegation_type4 dtype, int *recall) vp = fp->vp; /* vnevent_support returns 0 if file system supports vnevents */ - if (vnevent_support(vp)) { + if (vnevent_support(vp, NULL)) { rfs4_deleg_state_rele(dsp); return (NULL); } + /* Calculate the fflags for this OPEN. */ + if (sp->share_access & OPEN4_SHARE_ACCESS_READ) + fflags |= FREAD; + if (sp->share_access & OPEN4_SHARE_ACCESS_WRITE) + fflags |= FWRITE; + *recall = 0; + /* + * Before granting a delegation we need to know if anyone else has + * opened the file in a conflicting mode. However, first we need to + * know how we opened the file to check the counts properly. + */ if (dtype == OPEN_DELEGATE_READ) { - if (vn_is_opened(vp, V_WRITE) || vn_is_mapped(vp, V_WRITE)) { + if (((fflags & FWRITE) && vn_has_other_opens(vp, V_WRITE)) || + (((fflags & FWRITE) == 0) && vn_is_opened(vp, V_WRITE)) || + vn_is_mapped(vp, V_WRITE)) { if (open_prev) { *recall = 1; } else { @@ -1620,9 +1635,11 @@ rfs4_deleg_state(rfs4_state_t *sp, open_delegation_type4 dtype, int *recall) return (NULL); } } - (void) fem_install(vp, deleg_rdops, (void *)fp, OPUNIQ, + ret = fem_install(vp, deleg_rdops, (void *)fp, OPUNIQ, rfs4_mon_hold, rfs4_mon_rele); - if (vn_is_opened(vp, V_WRITE) || vn_is_mapped(vp, V_WRITE)) { + if (((fflags & FWRITE) && vn_has_other_opens(vp, V_WRITE)) || + (((fflags & FWRITE) == 0) && vn_is_opened(vp, V_WRITE)) || + vn_is_mapped(vp, V_WRITE)) { if (open_prev) { *recall = 1; } else { @@ -1632,8 +1649,24 @@ rfs4_deleg_state(rfs4_state_t *sp, open_delegation_type4 dtype, int *recall) return (NULL); } } + /* + * Because a client can hold onto a delegation after the + * file has been closed, we need to keep track of the + * access to this file. Otherwise the CIFS server would + * not know about the client accessing the file and could + * inappropriately grant an OPLOCK. + * fem_install() returns EBUSY when asked to install a + * OPUNIQ monitor more than once. Therefore, check the + * return code because we only want this done once. + */ + if (ret == 0) + vn_open_upgrade(vp, FREAD); } else { /* WRITE */ - if (vn_is_opened(vp, V_RDORWR) || vn_is_mapped(vp, V_RDORWR)) { + if (((fflags & FWRITE) && vn_has_other_opens(vp, V_WRITE)) || + (((fflags & FWRITE) == 0) && vn_is_opened(vp, V_WRITE)) || + ((fflags & FREAD) && vn_has_other_opens(vp, V_READ)) || + (((fflags & FREAD) == 0) && vn_is_opened(vp, V_READ)) || + vn_is_mapped(vp, V_RDORWR)) { if (open_prev) { *recall = 1; } else { @@ -1641,9 +1674,13 @@ rfs4_deleg_state(rfs4_state_t *sp, open_delegation_type4 dtype, int *recall) return (NULL); } } - (void) fem_install(vp, deleg_wrops, (void *)fp, OPUNIQ, + ret = fem_install(vp, deleg_wrops, (void *)fp, OPUNIQ, rfs4_mon_hold, rfs4_mon_rele); - if (vn_is_opened(vp, V_RDORWR) || vn_is_mapped(vp, V_RDORWR)) { + if (((fflags & FWRITE) && vn_has_other_opens(vp, V_WRITE)) || + (((fflags & FWRITE) == 0) && vn_is_opened(vp, V_WRITE)) || + ((fflags & FREAD) && vn_has_other_opens(vp, V_READ)) || + (((fflags & FREAD) == 0) && vn_is_opened(vp, V_READ)) || + vn_is_mapped(vp, V_RDORWR)) { if (open_prev) { *recall = 1; } else { @@ -1653,6 +1690,18 @@ rfs4_deleg_state(rfs4_state_t *sp, open_delegation_type4 dtype, int *recall) return (NULL); } } + /* + * Because a client can hold onto a delegation after the + * file has been closed, we need to keep track of the + * access to this file. Otherwise the CIFS server would + * not know about the client accessing the file and could + * inappropriately grant an OPLOCK. + * fem_install() returns EBUSY when asked to install a + * OPUNIQ monitor more than once. Therefore, check the + * return code because we only want this done once. + */ + if (ret == 0) + vn_open_upgrade(vp, FREAD|FWRITE); } /* Place on delegation list for file */ insque(&dsp->delegationlist, fp->delegationlist.prev); @@ -1697,12 +1746,22 @@ rfs4_return_deleg(rfs4_deleg_state_t *dsp, bool_t revoked) /* if file system was unshared, the vp will be NULL */ if (fp->vp != NULL) { - if (dtypewas == OPEN_DELEGATE_READ) + /* + * Once a delegation is no longer held by any client, + * the monitor is uninstalled. At this point, the + * client must send OPEN otw, so we don't need the + * reference on the vnode anymore. The open + * downgrade removes the reference put on earlier. + */ + if (dtypewas == OPEN_DELEGATE_READ) { (void) fem_uninstall(fp->vp, deleg_rdops, (void *)fp); - else + vn_open_downgrade(fp->vp, FREAD); + } else if (dtypewas == OPEN_DELEGATE_WRITE) { (void) fem_uninstall(fp->vp, deleg_wrops, (void *)fp); + vn_open_downgrade(fp->vp, FREAD|FWRITE); + } } } diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c b/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c index 2d3cd81fbab7..d10f010be05c 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c +++ b/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c @@ -53,7 +53,7 @@ vop_fid_pseudo(vnode_t *vp, fid_t *fidp) struct vattr va; int error; - error = VOP_FID(vp, fidp); + error = VOP_FID(vp, fidp, NULL); /* * XXX nfs4_fid() does nothing and returns EREMOTE. @@ -68,7 +68,7 @@ vop_fid_pseudo(vnode_t *vp, fid_t *fidp) (error == 0 && fidp->fid_len > NFS_FH4MAXDATA)) { va.va_mask = AT_NODEID; - error = VOP_GETATTR(vp, &va, 0, CRED()); + error = VOP_GETATTR(vp, &va, 0, CRED(), NULL); if (error) return (error); @@ -449,7 +449,7 @@ less_visible(struct exportinfo *exi, struct exp_visible *vis_head) * PSEUDO - a pseudo node * vis - visible list * f# - security flavor# - * (f#) - security flavor# propagated from its decendents + * (f#) - security flavor# propagated from its descendents * "" - covered vnode * * @@ -557,7 +557,7 @@ treeclimb_export(struct exportinfo *exip) * for this vnode. */ va.va_mask = AT_NODEID; - error = VOP_GETATTR(vp, &va, 0, CRED()); + error = VOP_GETATTR(vp, &va, 0, CRED(), NULL); if (error) break; @@ -579,7 +579,8 @@ treeclimb_export(struct exportinfo *exip) /* * Now, do a ".." to find parent dir of vp. */ - error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED()); + error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED(), + NULL, NULL, NULL); if (error == ENOTDIR && exportdir) { dvp = exip->exi_dvp; @@ -709,7 +710,8 @@ treeclimb_unexport(struct exportinfo *exip) /* * Do a ".." to find parent dir of vp. */ - error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED()); + error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED(), + NULL, NULL, NULL); if (error == ENOTDIR && exportdir) { dvp = exip->exi_dvp; @@ -813,7 +815,8 @@ get_root_export(struct exportinfo *exip) /* * Now, do a ".." to find parent dir of vp. */ - error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED()); + error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED(), + NULL, NULL, NULL); if (error) { exi = NULL; diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c b/usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c index 70852fbbe0b9..3f810b96b3d1 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c +++ b/usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -115,7 +114,8 @@ nfs4_readdir_getvp(vnode_t *dvp, char *d_name, vnode_t **vpp, *vpp = vp = NULL; - if (error = VOP_LOOKUP(dvp, d_name, &vp, NULL, 0, NULL, cs->cr)) + if (error = VOP_LOOKUP(dvp, d_name, &vp, NULL, 0, NULL, cs->cr, + NULL, NULL, NULL)) return (error); /* Is this object mounted upon? */ @@ -152,7 +152,7 @@ nfs4_readdir_getvp(vnode_t *dvp, char *d_name, vnode_t **vpp, * etc.), then return attrs for stub instead of VROOT object. * If it fails for any other reason, then return the error. */ - if (error = VOP_FID(vp, &fid)) { + if (error = VOP_FID(vp, &fid, NULL)) { if (ismntpt == 0) { VN_RELE(vp); return (error); @@ -232,7 +232,8 @@ rfs4_get_pc_encode(vnode_t *vp, rfs4_pc_encode_t *pce, bitmap4 ar, cred_t *cr) if (ar & FATTR4_MAXFILESIZE_MASK) { /* Maximum File Size */ - if (error = VOP_PATHCONF(vp, _PC_FILESIZEBITS, &pc_val, cr)) + if (error = VOP_PATHCONF(vp, _PC_FILESIZEBITS, &pc_val, cr, + NULL)) return (error); if (pc_val >= (sizeof (uint64_t) * 8)) @@ -243,7 +244,7 @@ rfs4_get_pc_encode(vnode_t *vp, rfs4_pc_encode_t *pce, bitmap4 ar, cred_t *cr) if (ar & FATTR4_MAXLINK_MASK) { /* Maximum Link Count */ - if (error = VOP_PATHCONF(vp, _PC_LINK_MAX, &pc_val, cr)) + if (error = VOP_PATHCONF(vp, _PC_LINK_MAX, &pc_val, cr, NULL)) return (error); pce->maxlink = pc_val; @@ -251,7 +252,7 @@ rfs4_get_pc_encode(vnode_t *vp, rfs4_pc_encode_t *pce, bitmap4 ar, cred_t *cr) if (ar & FATTR4_MAXNAME_MASK) { /* Maximum Name Length */ - if (error = VOP_PATHCONF(vp, _PC_NAME_MAX, &pc_val, cr)) + if (error = VOP_PATHCONF(vp, _PC_NAME_MAX, &pc_val, cr, NULL)) return (error); pce->maxname = pc_val; @@ -441,7 +442,7 @@ rfs4_op_readdir(nfs_argop4 *argop, nfs_resop4 *resop, return; } - error = VOP_ACCESS(dvp, VREAD, 0, cs->cr); + error = VOP_ACCESS(dvp, VREAD, 0, cs->cr, NULL); if (error) { *cs->statusp = resp->status = puterrno4(error); return; @@ -610,7 +611,7 @@ rfs4_op_readdir(nfs_argop4 *argop, nfs_resop4 *resop, (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL); - error = VOP_READDIR(dvp, &uio, cs->cr, &iseofdir); + error = VOP_READDIR(dvp, &uio, cs->cr, &iseofdir, NULL, 0); VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL); @@ -788,7 +789,7 @@ rfs4_op_readdir(nfs_argop4 *argop, nfs_resop4 *resop, } else { va.va_mask = AT_ALL; rddirattr_error = - VOP_GETATTR(vp, &va, 0, cs->cr); + VOP_GETATTR(vp, &va, 0, cs->cr, NULL); if (rddirattr_error) ae = ar & (FATTR4_RDATTR_ERROR_MASK | FATTR4_MOUNTED_ON_FILEID_MASK); @@ -877,7 +878,7 @@ rfs4_op_readdir(nfs_argop4 *argop, nfs_resop4 *resop, } else { (void) VOP_PATHCONF(vp, _PC_XATTR_EXISTS, - &pc_val, cs->cr); + &pc_val, cs->cr, NULL); } isit = (pc_val ? TRUE : FALSE); IXDR_PUT_U_INT32(ptr, isit); @@ -965,7 +966,7 @@ rfs4_op_readdir(nfs_argop4 *argop, nfs_resop4 *resop, pc_val = FALSE; (void) VOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, - &pc_val, cs->cr); + &pc_val, cs->cr, NULL); isit = (pc_val ? TRUE : FALSE); IXDR_PUT_U_INT32(ptr, isit); } diff --git a/usr/src/uts/common/fs/nfs/nfs4_state.c b/usr/src/uts/common/fs/nfs/nfs4_state.c index 2674fa8de7d6..5417ab3326aa 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_state.c +++ b/usr/src/uts/common/fs/nfs/nfs4_state.c @@ -41,6 +41,7 @@ #include #include +extern u_longlong_t nfs4_srv_caller_id; extern time_t rfs4_start_time; extern uint_t nfs4_srv_vkey; @@ -493,17 +494,17 @@ rfs4_ss_getstate(vnode_t *dvp, rfs4_ss_pn_t *ss_pn) } if (vp->v_type != VREG) { - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); return (NULL); } - err = VOP_ACCESS(vp, VREAD, 0, CRED()); + err = VOP_ACCESS(vp, VREAD, 0, CRED(), NULL); if (err) { /* * We don't have read access? better get the heck out. */ - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); return (NULL); } @@ -513,17 +514,17 @@ rfs4_ss_getstate(vnode_t *dvp, rfs4_ss_pn_t *ss_pn) * get the file size to do some basic validation */ va.va_mask = AT_SIZE; - err = VOP_GETATTR(vp, &va, 0, CRED()); + err = VOP_GETATTR(vp, &va, 0, CRED(), NULL); kill_file = (va.va_size == 0 || va.va_size < (NFS4_VERIFIER_SIZE + sizeof (uint_t)+1)); if (err || kill_file) { VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); if (kill_file) { - (void) VOP_REMOVE(dvp, ss_pn->leaf, CRED()); + (void) VOP_REMOVE(dvp, ss_pn->leaf, CRED(), NULL, 0); } return (NULL); } @@ -548,7 +549,7 @@ rfs4_ss_getstate(vnode_t *dvp, rfs4_ss_pn_t *ss_pn) if (err = VOP_READ(vp, &uio, FREAD, CRED(), NULL)) { VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); kmem_free(cl_ss, sizeof (rfs4_oldstate_t)); return (NULL); @@ -565,11 +566,11 @@ rfs4_ss_getstate(vnode_t *dvp, rfs4_ss_pn_t *ss_pn) if (err || kill_file) { VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); kmem_free(cl_ss, sizeof (rfs4_oldstate_t)); if (kill_file) { - (void) VOP_REMOVE(dvp, ss_pn->leaf, CRED()); + (void) VOP_REMOVE(dvp, ss_pn->leaf, CRED(), NULL, 0); } return (NULL); } @@ -588,7 +589,7 @@ rfs4_ss_getstate(vnode_t *dvp, rfs4_ss_pn_t *ss_pn) if (err = VOP_READ(vp, &uio, FREAD, CRED(), NULL)) { VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); kmem_free(cl_ss->cl_id4.id_val, id_len); kmem_free(cl_ss, sizeof (rfs4_oldstate_t)); @@ -596,7 +597,7 @@ rfs4_ss_getstate(vnode_t *dvp, rfs4_ss_pn_t *ss_pn) } VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); return (cl_ss); } @@ -629,7 +630,7 @@ rfs4_ss_oldstate(rfs4_oldstate_t *oldstate, char *statedir, char *destdir) if (vn_open(statedir, UIO_SYSSPACE, FREAD, 0, &dvp, 0, 0)) return; - if (dvp->v_type != VDIR || VOP_ACCESS(dvp, VREAD, 0, CRED())) + if (dvp->v_type != VDIR || VOP_ACCESS(dvp, VREAD, 0, CRED(), NULL)) goto out; dirt = kmem_alloc(RFS4_SS_DIRSIZE, KM_SLEEP); @@ -647,7 +648,7 @@ rfs4_ss_oldstate(rfs4_oldstate_t *oldstate, char *statedir, char *destdir) uio.uio_loffset = dirchunk_offset; uio.uio_resid = RFS4_SS_DIRSIZE; - err = VOP_READDIR(dvp, &uio, CRED(), &dir_eof); + err = VOP_READDIR(dvp, &uio, CRED(), &dir_eof, NULL, 0); VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL); if (err) goto out; @@ -690,7 +691,7 @@ rfs4_ss_oldstate(rfs4_oldstate_t *oldstate, char *statedir, char *destdir) } out: - (void) VOP_CLOSE(dvp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(dvp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(dvp); if (dirt) kmem_free((caddr_t)dirt, RFS4_SS_DIRSIZE); @@ -1047,7 +1048,7 @@ rfs4_ss_clid_write_one(rfs4_client_t *cp, char *dss_path, char *leaf) (void) VOP_WRITE(vp, &uio, ioflag, CRED(), NULL); VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); - (void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); } @@ -3425,11 +3426,11 @@ rfs4_state_has_access(rfs4_state_t *sp, int mode, vnode_t *vp) /* * Given the I/O mode (FREAD or FWRITE), the vnode, the stateid and whether - * the file is being truncated, return NFS4_OK if allowed or approriate + * the file is being truncated, return NFS4_OK if allowed or appropriate * V4 error if not. Note NFS4ERR_DELAY will be returned and a recall on * the associated file will be done if the I/O is not consistent with any * delegation in effect on the file. Should be holding VOP_RWLOCK, either - * as reader or writer as appropriate. rfs4_op_open will accquire the + * as reader or writer as appropriate. rfs4_op_open will acquire the * VOP_RWLOCK as writer when setting up delegation. If the stateid is bad * this routine will return NFS4ERR_BAD_STATEID. In addition, through the * deleg parameter, we will return whether a write delegation is held by @@ -3441,7 +3442,7 @@ rfs4_state_has_access(rfs4_state_t *sp, int mode, vnode_t *vp) nfsstat4 rfs4_check_stateid(int mode, vnode_t *vp, stateid4 *stateid, bool_t trunc, bool_t *deleg, - bool_t do_access) + bool_t do_access, caller_context_t *ct) { rfs4_file_t *fp; bool_t create = FALSE; @@ -3451,6 +3452,12 @@ rfs4_check_stateid(int mode, vnode_t *vp, stateid_t *id = (stateid_t *)stateid; nfsstat4 stat = NFS4_OK; + if (ct != NULL) { + ct->cc_sysid = 0; + ct->cc_pid = 0; + ct->cc_caller_id = nfs4_srv_caller_id; + } + if (ISSPECIAL(stateid)) { fp = rfs4_findfile(vp, NULL, &create); if (fp == NULL) @@ -3504,6 +3511,10 @@ rfs4_check_stateid(int mode, vnode_t *vp, return (NFS4ERR_BAD_STATEID); } } + if (ct != NULL) { + ct->cc_sysid = lsp->locker->client->sysidt; + ct->cc_pid = lsp->locker->pid; + } rfs4_lo_state_rele(lsp, FALSE); } @@ -3832,13 +3843,19 @@ rfs4_file_walk_callout(rfs4_entry_t u_entry, void *e) if (fp->vp) { vnode_t *vp = fp->vp; - /* don't leak monitors */ - if (fp->dinfo->dtype == OPEN_DELEGATE_READ) + /* + * don't leak monitors and remove the reference + * put on the vnode when the delegation was granted. + */ + if (fp->dinfo->dtype == OPEN_DELEGATE_READ) { (void) fem_uninstall(vp, deleg_rdops, (void *)fp); - else if (fp->dinfo->dtype == OPEN_DELEGATE_WRITE) + vn_open_downgrade(vp, FREAD); + } else if (fp->dinfo->dtype == OPEN_DELEGATE_WRITE) { (void) fem_uninstall(vp, deleg_wrops, (void *)fp); + vn_open_downgrade(vp, FREAD|FWRITE); + } mutex_enter(&vp->v_lock); (void) vsd_set(vp, nfs4_srv_vkey, NULL); mutex_exit(&vp->v_lock); diff --git a/usr/src/uts/common/fs/nfs/nfs4_stub_vnops.c b/usr/src/uts/common/fs/nfs/nfs4_stub_vnops.c index c6893a13f31a..92686ef99dc8 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_stub_vnops.c +++ b/usr/src/uts/common/fs/nfs/nfs4_stub_vnops.c @@ -152,40 +152,52 @@ typedef struct domount_args { /* * The vnode ops functions for a trigger stub vnode */ -static int nfs4_trigger_open(vnode_t **, int, cred_t *); -static int nfs4_trigger_getattr(vnode_t *, struct vattr *, int, cred_t *); -static int nfs4_trigger_setattr(vnode_t *, struct vattr *, int, cred_t *, - caller_context_t *); -static int nfs4_trigger_access(vnode_t *, int, int, cred_t *); -static int nfs4_trigger_readlink(vnode_t *, struct uio *, cred_t *); -static int nfs4_trigger_lookup(vnode_t *, char *, vnode_t **, - struct pathname *, int, vnode_t *, cred_t *); -static int nfs4_trigger_create(vnode_t *, char *, struct vattr *, - enum vcexcl, int, vnode_t **, cred_t *, int); -static int nfs4_trigger_remove(vnode_t *, char *, cred_t *); -static int nfs4_trigger_link(vnode_t *, vnode_t *, char *, cred_t *); -static int nfs4_trigger_rename(vnode_t *, char *, vnode_t *, char *, - cred_t *); -static int nfs4_trigger_mkdir(vnode_t *, char *, struct vattr *, - vnode_t **, cred_t *); -static int nfs4_trigger_rmdir(vnode_t *, char *, vnode_t *, cred_t *); -static int nfs4_trigger_symlink(vnode_t *, char *, struct vattr *, char *, - cred_t *); -static int nfs4_trigger_cmp(vnode_t *, vnode_t *); +static int nfs4_trigger_open(vnode_t **, int, cred_t *, caller_context_t *); +static int nfs4_trigger_getattr(vnode_t *, struct vattr *, int, cred_t *, + caller_context_t *); +static int nfs4_trigger_setattr(vnode_t *, struct vattr *, int, cred_t *, + caller_context_t *); +static int nfs4_trigger_access(vnode_t *, int, int, cred_t *, + caller_context_t *); +static int nfs4_trigger_readlink(vnode_t *, struct uio *, cred_t *, + caller_context_t *); +static int nfs4_trigger_lookup(vnode_t *, char *, vnode_t **, + struct pathname *, int, vnode_t *, cred_t *, caller_context_t *, + int *, pathname_t *); +static int nfs4_trigger_create(vnode_t *, char *, struct vattr *, + enum vcexcl, int, vnode_t **, cred_t *, int, caller_context_t *, + vsecattr_t *); +static int nfs4_trigger_remove(vnode_t *, char *, cred_t *, caller_context_t *, + int); +static int nfs4_trigger_link(vnode_t *, vnode_t *, char *, cred_t *, + caller_context_t *, int); +static int nfs4_trigger_rename(vnode_t *, char *, vnode_t *, char *, + cred_t *, caller_context_t *, int); +static int nfs4_trigger_mkdir(vnode_t *, char *, struct vattr *, + vnode_t **, cred_t *, caller_context_t *, int, vsecattr_t *vsecp); +static int nfs4_trigger_rmdir(vnode_t *, char *, vnode_t *, cred_t *, + caller_context_t *, int); +static int nfs4_trigger_symlink(vnode_t *, char *, struct vattr *, char *, + cred_t *, caller_context_t *, int); +static int nfs4_trigger_cmp(vnode_t *, vnode_t *, caller_context_t *); /* * Regular NFSv4 vnodeops that we need to reference directly */ -extern int nfs4_getattr(vnode_t *, struct vattr *, int, cred_t *); -extern void nfs4_inactive(vnode_t *, cred_t *); +extern int nfs4_getattr(vnode_t *, struct vattr *, int, cred_t *, + caller_context_t *); +extern void nfs4_inactive(vnode_t *, cred_t *, caller_context_t *); extern int nfs4_rwlock(vnode_t *, int, caller_context_t *); extern void nfs4_rwunlock(vnode_t *, int, caller_context_t *); extern int nfs4_lookup(vnode_t *, char *, vnode_t **, - struct pathname *, int, vnode_t *, cred_t *); -extern int nfs4_pathconf(vnode_t *, int, ulong_t *, cred_t *); -extern int nfs4_getsecattr(vnode_t *, vsecattr_t *, int, cred_t *); -extern int nfs4_fid(vnode_t *, fid_t *); -extern int nfs4_realvp(vnode_t *, vnode_t **); + struct pathname *, int, vnode_t *, cred_t *, + caller_context_t *, int *, pathname_t *); +extern int nfs4_pathconf(vnode_t *, int, ulong_t *, cred_t *, + caller_context_t *); +extern int nfs4_getsecattr(vnode_t *, vsecattr_t *, int, cred_t *, + caller_context_t *); +extern int nfs4_fid(vnode_t *, fid_t *, caller_context_t *); +extern int nfs4_realvp(vnode_t *, vnode_t **, caller_context_t *); static int nfs4_trigger_mount(vnode_t *, vnode_t **); static int nfs4_trigger_domount(vnode_t *, domount_args_t *, vfs_t **, @@ -305,7 +317,7 @@ const fs_operation_def_t nfs4_trigger_vnodeops_template[] = { */ static int -nfs4_trigger_open(vnode_t **vpp, int flag, cred_t *cr) +nfs4_trigger_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { int error; vnode_t *newvp; @@ -321,7 +333,7 @@ nfs4_trigger_open(vnode_t **vpp, int flag, cred_t *cr) *vpp = newvp; /* return with VN_HELD(newvp) */ - return (VOP_OPEN(vpp, flag, cr)); + return (VOP_OPEN(vpp, flag, cr, ct)); } /* @@ -332,7 +344,8 @@ nfs4_trigger_open(vnode_t **vpp, int flag, cred_t *cr) * testing. */ static int -nfs4_trigger_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) +nfs4_trigger_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, + caller_context_t *ct) { int error; @@ -343,10 +356,10 @@ nfs4_trigger_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) if (error) return (error); - error = VOP_GETATTR(newvp, vap, flags, cr); + error = VOP_GETATTR(newvp, vap, flags, cr, ct); VN_RELE(newvp); } else { - error = nfs4_getattr(vp, vap, flags, cr); + error = nfs4_getattr(vp, vap, flags, cr, ct); } return (error); @@ -370,7 +383,8 @@ nfs4_trigger_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, } static int -nfs4_trigger_access(vnode_t *vp, int mode, int flags, cred_t *cr) +nfs4_trigger_access(vnode_t *vp, int mode, int flags, cred_t *cr, + caller_context_t *ct) { int error; vnode_t *newvp; @@ -379,15 +393,16 @@ nfs4_trigger_access(vnode_t *vp, int mode, int flags, cred_t *cr) if (error) return (error); - error = VOP_ACCESS(newvp, mode, flags, cr); + error = VOP_ACCESS(newvp, mode, flags, cr, ct); VN_RELE(newvp); return (error); } static int -nfs4_trigger_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, - int flags, vnode_t *rdir, cred_t *cr) +nfs4_trigger_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, + struct pathname *pnp, int flags, vnode_t *rdir, cred_t *cr, + caller_context_t *ct, int *deflags, pathname_t *rpnp) { int error; vnode_t *newdvp; @@ -404,13 +419,15 @@ nfs4_trigger_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, * we've triggered a mount. */ if (strcmp(nm, "..") == 0) - return (nfs4_lookup(dvp, nm, vpp, pnp, flags, rdir, cr)); + return (nfs4_lookup(dvp, nm, vpp, pnp, flags, rdir, cr, + ct, deflags, rpnp)); error = nfs4_trigger_mount(dvp, &newdvp); if (error) return (error); - error = VOP_LOOKUP(newdvp, nm, vpp, pnp, flags, rdir, cr); + error = VOP_LOOKUP(newdvp, nm, vpp, pnp, flags, rdir, cr, ct, + deflags, rpnp); VN_RELE(newdvp); return (error); @@ -418,8 +435,8 @@ nfs4_trigger_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, static int nfs4_trigger_create(vnode_t *dvp, char *nm, struct vattr *va, - enum vcexcl exclusive, int mode, vnode_t **vpp, cred_t *cr, - int flags) + enum vcexcl exclusive, int mode, vnode_t **vpp, cred_t *cr, + int flags, caller_context_t *ct, vsecattr_t *vsecp) { int error; vnode_t *newdvp; @@ -428,14 +445,16 @@ nfs4_trigger_create(vnode_t *dvp, char *nm, struct vattr *va, if (error) return (error); - error = VOP_CREATE(newdvp, nm, va, exclusive, mode, vpp, cr, flags); + error = VOP_CREATE(newdvp, nm, va, exclusive, mode, vpp, cr, + flags, ct, vsecp); VN_RELE(newdvp); return (error); } static int -nfs4_trigger_remove(vnode_t *dvp, char *nm, cred_t *cr) +nfs4_trigger_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct, + int flags) { int error; vnode_t *newdvp; @@ -444,14 +463,15 @@ nfs4_trigger_remove(vnode_t *dvp, char *nm, cred_t *cr) if (error) return (error); - error = VOP_REMOVE(newdvp, nm, cr); + error = VOP_REMOVE(newdvp, nm, cr, ct, flags); VN_RELE(newdvp); return (error); } static int -nfs4_trigger_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) +nfs4_trigger_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr, + caller_context_t *ct, int flags) { int error; vnode_t *newtdvp; @@ -464,7 +484,7 @@ nfs4_trigger_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) * We don't check whether svp is a stub. Let the NFSv4 code * detect that error, and return accordingly. */ - error = VOP_LINK(newtdvp, svp, tnm, cr); + error = VOP_LINK(newtdvp, svp, tnm, cr, ct, flags); VN_RELE(newtdvp); return (error); @@ -472,7 +492,7 @@ nfs4_trigger_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) static int nfs4_trigger_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, - cred_t *cr) + cred_t *cr, caller_context_t *ct, int flags) { int error; vnode_t *newsdvp; @@ -501,16 +521,17 @@ nfs4_trigger_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, if (error) return (error); - error = VOP_RENAME(newsdvp, snm, tdvp, tnm, cr); + error = VOP_RENAME(newsdvp, snm, tdvp, tnm, cr, ct, flags); VN_RELE(newsdvp); return (error); } +/* ARGSUSED */ static int nfs4_trigger_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, - cred_t *cr) + cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp) { int error; vnode_t *newdvp; @@ -519,14 +540,15 @@ nfs4_trigger_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, if (error) return (error); - error = VOP_MKDIR(newdvp, nm, va, vpp, cr); + error = VOP_MKDIR(newdvp, nm, va, vpp, cr, ct, flags, vsecp); VN_RELE(newdvp); return (error); } static int -nfs4_trigger_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) +nfs4_trigger_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr, + caller_context_t *ct, int flags) { int error; vnode_t *newdvp; @@ -535,7 +557,7 @@ nfs4_trigger_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) if (error) return (error); - error = VOP_RMDIR(newdvp, nm, cdir, cr); + error = VOP_RMDIR(newdvp, nm, cdir, cr, ct, flags); VN_RELE(newdvp); return (error); @@ -543,7 +565,7 @@ nfs4_trigger_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) static int nfs4_trigger_symlink(vnode_t *dvp, char *lnm, struct vattr *tva, char *tnm, - cred_t *cr) + cred_t *cr, caller_context_t *ct, int flags) { int error; vnode_t *newdvp; @@ -552,14 +574,15 @@ nfs4_trigger_symlink(vnode_t *dvp, char *lnm, struct vattr *tva, char *tnm, if (error) return (error); - error = VOP_SYMLINK(newdvp, lnm, tva, tnm, cr); + error = VOP_SYMLINK(newdvp, lnm, tva, tnm, cr, ct, flags); VN_RELE(newdvp); return (error); } static int -nfs4_trigger_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr) +nfs4_trigger_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr, + caller_context_t *ct) { int error; vnode_t *newvp; @@ -568,7 +591,7 @@ nfs4_trigger_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr) if (error) return (error); - error = VOP_READLINK(newvp, uiop, cr); + error = VOP_READLINK(newvp, uiop, cr, ct); VN_RELE(newvp); return (error); diff --git a/usr/src/uts/common/fs/nfs/nfs4_subr.c b/usr/src/uts/common/fs/nfs/nfs4_subr.c index 16ce81e27075..52a482f642bf 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_subr.c +++ b/usr/src/uts/common/fs/nfs/nfs4_subr.c @@ -2319,7 +2319,7 @@ nfs4_printfhandle(nfs4_fhandle_t *fhp) * We provide a set of interfaces to allow the rest of the system to utilize * a caching mechanism while encapsulating the details of the actual * implementation. This should allow for better maintainability and - * extensibilty by consolidating the implementation details in one location. + * extensibility by consolidating the implementation details in one location. */ /* @@ -2875,7 +2875,7 @@ nfs4_directio(vnode_t *vp, int cmd, cred_t *cr) if (nfs4_has_pages(vp) && ((rp->r_flags & R4DIRTY) || rp->r_awcount > 0)) { error = VOP_PUTPAGE(vp, (offset_t)0, (uint_t)0, - B_INVAL, cr); + B_INVAL, cr, NULL); if (error) { if (error == ENOSPC || error == EDQUOT) { mutex_enter(&rp->r_statelock); diff --git a/usr/src/uts/common/fs/nfs/nfs4_vnops.c b/usr/src/uts/common/fs/nfs/nfs4_vnops.c index f788f16bef63..d945efe97a0e 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_vnops.c +++ b/usr/src/uts/common/fs/nfs/nfs4_vnops.c @@ -126,7 +126,8 @@ static int nfs4mknod(vnode_t *, char *, struct vattr *, enum vcexcl, int, vnode_t **, cred_t *); static int nfs4open_otw(vnode_t *, char *, struct vattr *, vnode_t **, cred_t *, int, int, enum createmode4, int); -static int nfs4rename(vnode_t *, char *, vnode_t *, char *, cred_t *); +static int nfs4rename(vnode_t *, char *, vnode_t *, char *, cred_t *, + caller_context_t *); static int nfs4rename_persistent_fh(vnode_t *, char *, vnode_t *, vnode_t *, char *, cred_t *, nfsstat4 *); static int nfs4rename_volatile_fh(vnode_t *, char *, vnode_t *, @@ -197,65 +198,82 @@ static void nfs4args_write(nfs_argop4 *, stable_how4, rnode4_t *, cred_t *, * These are the vnode ops functions that implement the vnode interface to * the networked file system. See more comments below at nfs4_vnodeops. */ -static int nfs4_open(vnode_t **, int, cred_t *); -static int nfs4_close(vnode_t *, int, int, offset_t, cred_t *); +static int nfs4_open(vnode_t **, int, cred_t *, caller_context_t *); +static int nfs4_close(vnode_t *, int, int, offset_t, cred_t *, + caller_context_t *); static int nfs4_read(vnode_t *, struct uio *, int, cred_t *, caller_context_t *); static int nfs4_write(vnode_t *, struct uio *, int, cred_t *, caller_context_t *); -static int nfs4_ioctl(vnode_t *, int, intptr_t, int, cred_t *, int *); +static int nfs4_ioctl(vnode_t *, int, intptr_t, int, cred_t *, int *, + caller_context_t *); static int nfs4_setattr(vnode_t *, struct vattr *, int, cred_t *, caller_context_t *); -static int nfs4_access(vnode_t *, int, int, cred_t *); -static int nfs4_readlink(vnode_t *, struct uio *, cred_t *); -static int nfs4_fsync(vnode_t *, int, cred_t *); +static int nfs4_access(vnode_t *, int, int, cred_t *, caller_context_t *); +static int nfs4_readlink(vnode_t *, struct uio *, cred_t *, + caller_context_t *); +static int nfs4_fsync(vnode_t *, int, cred_t *, caller_context_t *); static int nfs4_create(vnode_t *, char *, struct vattr *, enum vcexcl, - int, vnode_t **, cred_t *, int); -static int nfs4_remove(vnode_t *, char *, cred_t *); -static int nfs4_link(vnode_t *, vnode_t *, char *, cred_t *); -static int nfs4_rename(vnode_t *, char *, vnode_t *, char *, cred_t *); -static int nfs4_mkdir(vnode_t *, char *, struct vattr *, - vnode_t **, cred_t *); -static int nfs4_rmdir(vnode_t *, char *, vnode_t *, cred_t *); + int, vnode_t **, cred_t *, int, caller_context_t *, + vsecattr_t *); +static int nfs4_remove(vnode_t *, char *, cred_t *, caller_context_t *, + int); +static int nfs4_link(vnode_t *, vnode_t *, char *, cred_t *, + caller_context_t *, int); +static int nfs4_rename(vnode_t *, char *, vnode_t *, char *, cred_t *, + caller_context_t *, int); +static int nfs4_mkdir(vnode_t *, char *, struct vattr *, vnode_t **, + cred_t *, caller_context_t *, int, vsecattr_t *); +static int nfs4_rmdir(vnode_t *, char *, vnode_t *, cred_t *, + caller_context_t *, int); static int nfs4_symlink(vnode_t *, char *, struct vattr *, char *, - cred_t *); -static int nfs4_readdir(vnode_t *, struct uio *, cred_t *, int *); -static int nfs4_seek(vnode_t *, offset_t, offset_t *); + cred_t *, caller_context_t *, int); +static int nfs4_readdir(vnode_t *, struct uio *, cred_t *, int *, + caller_context_t *, int); +static int nfs4_seek(vnode_t *, offset_t, offset_t *, caller_context_t *); static int nfs4_getpage(vnode_t *, offset_t, size_t, uint_t *, page_t *[], size_t, struct seg *, caddr_t, - enum seg_rw, cred_t *); -static int nfs4_putpage(vnode_t *, offset_t, size_t, int, cred_t *); -static int nfs4_map(vnode_t *, offset_t, struct as *, caddr_t *, - size_t, uchar_t, uchar_t, uint_t, cred_t *); -static int nfs4_addmap(vnode_t *, offset_t, struct as *, caddr_t, - size_t, uchar_t, uchar_t, uint_t, cred_t *); -static int nfs4_cmp(vnode_t *, vnode_t *); + enum seg_rw, cred_t *, caller_context_t *); +static int nfs4_putpage(vnode_t *, offset_t, size_t, int, cred_t *, + caller_context_t *); +static int nfs4_map(vnode_t *, offset_t, struct as *, caddr_t *, size_t, + uchar_t, uchar_t, uint_t, cred_t *, caller_context_t *); +static int nfs4_addmap(vnode_t *, offset_t, struct as *, caddr_t, size_t, + uchar_t, uchar_t, uint_t, cred_t *, caller_context_t *); +static int nfs4_cmp(vnode_t *, vnode_t *, caller_context_t *); static int nfs4_frlock(vnode_t *, int, struct flock64 *, int, offset_t, - struct flk_callback *, cred_t *); + struct flk_callback *, cred_t *, caller_context_t *); static int nfs4_space(vnode_t *, int, struct flock64 *, int, offset_t, cred_t *, caller_context_t *); -static int nfs4_delmap(vnode_t *, offset_t, struct as *, caddr_t, - size_t, uint_t, uint_t, uint_t, cred_t *); +static int nfs4_delmap(vnode_t *, offset_t, struct as *, caddr_t, size_t, + uint_t, uint_t, uint_t, cred_t *, caller_context_t *); static int nfs4_pageio(vnode_t *, page_t *, u_offset_t, size_t, int, - cred_t *); -static void nfs4_dispose(vnode_t *, page_t *, int, int, cred_t *); -static int nfs4_setsecattr(vnode_t *, vsecattr_t *, int, cred_t *); -static int nfs4_shrlock(vnode_t *, int, struct shrlock *, int, cred_t *); + cred_t *, caller_context_t *); +static void nfs4_dispose(vnode_t *, page_t *, int, int, cred_t *, + caller_context_t *); +static int nfs4_setsecattr(vnode_t *, vsecattr_t *, int, cred_t *, + caller_context_t *); /* * These vnode ops are required to be called from outside this source file, * e.g. by ephemeral mount stub vnode ops, and so may not be declared * as static. */ -int nfs4_getattr(vnode_t *, struct vattr *, int, cred_t *); -void nfs4_inactive(vnode_t *, cred_t *); +int nfs4_getattr(vnode_t *, struct vattr *, int, cred_t *, + caller_context_t *); +void nfs4_inactive(vnode_t *, cred_t *, caller_context_t *); int nfs4_lookup(vnode_t *, char *, vnode_t **, - struct pathname *, int, vnode_t *, cred_t *); -int nfs4_fid(vnode_t *, fid_t *); + struct pathname *, int, vnode_t *, cred_t *, + caller_context_t *, int *, pathname_t *); +int nfs4_fid(vnode_t *, fid_t *, caller_context_t *); int nfs4_rwlock(vnode_t *, int, caller_context_t *); void nfs4_rwunlock(vnode_t *, int, caller_context_t *); -int nfs4_realvp(vnode_t *, vnode_t **); -int nfs4_pathconf(vnode_t *, int, ulong_t *, cred_t *); -int nfs4_getsecattr(vnode_t *, vsecattr_t *, int, cred_t *); +int nfs4_realvp(vnode_t *, vnode_t **, caller_context_t *); +int nfs4_pathconf(vnode_t *, int, ulong_t *, cred_t *, + caller_context_t *); +int nfs4_getsecattr(vnode_t *, vsecattr_t *, int, cred_t *, + caller_context_t *); +int nfs4_shrlock(vnode_t *, int, struct shrlock *, int, cred_t *, + caller_context_t *); /* * Used for nfs4_commit_vp() to indicate if we should @@ -580,11 +598,10 @@ nfs4_getvnodeops(void) /* * The OPEN operation opens a regular file. - * - * ARGSUSED */ +/*ARGSUSED3*/ static int -nfs4_open(vnode_t **vpp, int flag, cred_t *cr) +nfs4_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { vnode_t *dvp = NULL; rnode4_t *rp, *drp; @@ -862,10 +879,10 @@ nfs4open_otw(vnode_t *dvp, char *file_name, struct vattr *in_va, */ if (VTOR4(vpi)->r_deleg_type != OPEN_DELEGATE_NONE) { if (open_flag & FREAD && - nfs4_access(vpi, VREAD, 0, cr) == 0) + nfs4_access(vpi, VREAD, 0, cr, NULL) == 0) acc |= VREAD; if (open_flag & FWRITE && - nfs4_access(vpi, VWRITE, 0, cr) == 0) + nfs4_access(vpi, VWRITE, 0, cr, NULL) == 0) acc |= VWRITE; } } @@ -1538,7 +1555,7 @@ nfs4open_otw(vnode_t *dvp, char *file_name, struct vattr *in_va, "nfs4open_otw: EXCLUSIVE4: error %d on SETATTR:" " remove file", e.error)); VN_RELE(vp); - (void) nfs4_remove(dvp, file_name, cr); + (void) nfs4_remove(dvp, file_name, cr, NULL, 0); /* * Since we've reled the vnode and removed * the file we now need to return the error. @@ -1635,7 +1652,7 @@ nfs4open_otw(vnode_t *dvp, char *file_name, struct vattr *in_va, * - failed_reopen : same as above, except that the file has already been * marked dead, so no need to do it again. * - bailout : reopen failed but we are able to recover and retry the reopen - - * either within this function immediatley or via the calling function. + * either within this function immediately or via the calling function. */ void @@ -2210,8 +2227,10 @@ nfs4_open_non_reg_file(vnode_t **vpp, int flag, cred_t *cr) /* * CLOSE a file */ +/* ARGSUSED */ static int -nfs4_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) +nfs4_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, + caller_context_t *ct) { rnode4_t *rp; int error = 0; @@ -3513,7 +3532,8 @@ nfs4read(vnode_t *vp, caddr_t base, offset_t offset, int count, /* ARGSUSED */ static int -nfs4_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) +nfs4_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp, + caller_context_t *ct) { if (nfs_zone() != VTOMI4(vp)->mi_zone) return (EIO); @@ -3525,8 +3545,10 @@ nfs4_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) } } +/* ARGSUSED */ int -nfs4_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) +nfs4_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, + caller_context_t *ct) { int error; rnode4_t *rp = VTOR4(vp); @@ -3575,7 +3597,7 @@ nfs4_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) mutex_exit(&rp->r_statelock); error = nfs4_putpage(vp, (u_offset_t)0, - 0, 0, cr); + 0, 0, cr, NULL); mutex_enter(&rp->r_statelock); if (error && (error == ENOSPC || error == EDQUOT)) { @@ -3696,7 +3718,7 @@ nfs4setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, rp->r_count > 0 || rp->r_mapcnt > 0)) { ASSERT(vp->v_type != VCHR); - e.error = nfs4_putpage(vp, (offset_t)0, 0, 0, cr); + e.error = nfs4_putpage(vp, (offset_t)0, 0, 0, cr, NULL); if (e.error && (e.error == ENOSPC || e.error == EDQUOT)) { mutex_enter(&rp->r_statelock); if (!rp->r_error) @@ -4144,7 +4166,7 @@ nfs4setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, /* ARGSUSED */ static int -nfs4_access(vnode_t *vp, int mode, int flags, cred_t *cr) +nfs4_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct) { COMPOUND4args_clnt args; COMPOUND4res_clnt res; @@ -4358,8 +4380,9 @@ nfs4_access(vnode_t *vp, int mode, int flags, cred_t *cr) return (e.error); } +/* ARGSUSED */ static int -nfs4_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr) +nfs4_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr, caller_context_t *ct) { COMPOUND4args_clnt args; COMPOUND4res_clnt res; @@ -4521,8 +4544,9 @@ nfs4_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr) * metadata changes are not cached on the client before being * sent to the server. */ +/* ARGSUSED */ static int -nfs4_fsync(vnode_t *vp, int syncflag, cred_t *cr) +nfs4_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) { int error; @@ -4541,8 +4565,9 @@ nfs4_fsync(vnode_t *vp, int syncflag, cred_t *cr) * operation while it was open, it got renamed instead. Here we * remove the renamed file. */ +/* ARGSUSED */ void -nfs4_inactive(vnode_t *vp, cred_t *cr) +nfs4_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { rnode4_t *rp; @@ -4709,7 +4734,7 @@ nfs4_inactive_otw(vnode_t *vp, cred_t *cr) if (nfs4_has_pages(vp) && ((rp->r_flags & R4DIRTY) || rp->r_count > 0)) { ASSERT(vp->v_type != VCHR); - e.error = nfs4_putpage(vp, (u_offset_t)0, 0, 0, cr); + e.error = nfs4_putpage(vp, (u_offset_t)0, 0, 0, cr, NULL); if (e.error) { mutex_enter(&rp->r_statelock); if (!rp->r_error) @@ -4827,7 +4852,8 @@ nfs4_inactive_otw(vnode_t *vp, cred_t *cr) /* ARGSUSED3 */ int nfs4_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, - int flags, vnode_t *rdir, cred_t *cr) + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp) { int error; vnode_t *vp, *avp = NULL; @@ -4903,7 +4929,8 @@ nfs4lookup_xattr(vnode_t *dvp, char *nm, vnode_t **vpp, int flags, cred_t *cr) mntinfo4_t *mi; mi = VTOMI4(dvp); - if (!(mi->mi_vfsp->vfs_flag & VFS_XATTR)) + if (!(mi->mi_vfsp->vfs_flag & VFS_XATTR) && + !vfs_has_feature(mi->mi_vfsp, VFSFT_XVATTR)) return (EINVAL); drp = VTOR4(dvp); @@ -4983,7 +5010,7 @@ nfs4lookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int skipdnlc) * just need to check access. */ if (nm[0] == '.' && nm[1] == '\0') { - error = nfs4_access(dvp, VEXEC, 0, cr); + error = nfs4_access(dvp, VEXEC, 0, cr, NULL); if (error) return (error); VN_HOLD(dvp); @@ -5046,7 +5073,7 @@ nfs4lookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int skipdnlc) /* * The access cache should almost always hit */ - error = nfs4_access(dvp, VEXEC, 0, cr); + error = nfs4_access(dvp, VEXEC, 0, cr, NULL); if (error) { VN_RELE(*vpp); @@ -5346,7 +5373,7 @@ nfs4lookupvalidate_otw(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr) * Somehow we must not have asked for enough * so try a singleton ACCESS, should never happen. */ - e.error = nfs4_access(dvp, VEXEC, 0, cr); + e.error = nfs4_access(dvp, VEXEC, 0, cr, NULL); if (e.error) { VN_RELE(*vpp); *vpp = NULL; @@ -5488,7 +5515,7 @@ nfs4lookupvalidate_otw(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr) * and dnlc entry, we may not have access. * This should almost always hit the cache. */ - e.error = nfs4_access(dvp, VEXEC, 0, cr); + e.error = nfs4_access(dvp, VEXEC, 0, cr, NULL); if (e.error) { VN_RELE(*vpp); *vpp = NULL; @@ -5819,7 +5846,7 @@ nfs4lookupnew_otw(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr) * Somehow we must not have asked for enough * so try a singleton ACCESS should never happen */ - e.error = nfs4_access(dvp, VEXEC, 0, cr); + e.error = nfs4_access(dvp, VEXEC, 0, cr, NULL); if (e.error) { sfh4_rele(&sfhp); goto exit; @@ -5855,7 +5882,7 @@ nfs4lookupnew_otw(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr) * we may not have access. * This should almost always hit the cache. */ - e.error = nfs4_access(dvp, VEXEC, 0, cr); + e.error = nfs4_access(dvp, VEXEC, 0, cr, NULL); if (e.error) { sfh4_rele(&sfhp); goto exit; @@ -6375,7 +6402,8 @@ nfs4openattr(vnode_t *dvp, vnode_t **avp, int cflag, cred_t *cr) /* ARGSUSED */ static int nfs4_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, - int mode, vnode_t **vpp, cred_t *cr, int flags) + int mode, vnode_t **vpp, cred_t *cr, int flags, caller_context_t *ct, + vsecattr_t *vsecp) { int error; vnode_t *vp = NULL; @@ -6461,7 +6489,7 @@ nfs4_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, vp = specvp(vp, vp->v_rdev, vp->v_type, cr); VN_RELE(tempvp); } - if (!(error = VOP_ACCESS(vp, mode, 0, cr))) { + if (!(error = VOP_ACCESS(vp, mode, 0, cr, ct))) { if ((vattr.va_mask & AT_SIZE) && vp->v_type == VREG) { rp = VTOR4(vp); @@ -6500,7 +6528,8 @@ nfs4_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, rp->r_count > 0 || rp->r_mapcnt > 0)) { error = nfs4_putpage(vp, - (offset_t)0, 0, 0, cr); + (offset_t)0, 0, 0, cr, + ct); if (error && (error == ENOSPC || error == EDQUOT)) { mutex_enter( @@ -6537,7 +6566,7 @@ nfs4_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, if (IS_SHADOW(vp, trp)) tvp = RTOV4(trp); } - vnevent_create(tvp); + vnevent_create(tvp, ct); *vpp = vp; } return (error); @@ -6642,7 +6671,7 @@ nfs4_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, trp = VTOR4(tvp); if (IS_SHADOW(tvp, trp)) tvp = RTOV4(trp); - vnevent_create(tvp); + vnevent_create(tvp, ct); } return (error); } @@ -6695,7 +6724,7 @@ call_nfs4_create_req(vnode_t *dvp, char *nm, void *data, struct vattr *va, va->va_mode &= ~VSGID; dva.va_mask = AT_MODE | AT_GID; - if (VOP_GETATTR(dvp, &dva, 0, cr) == 0) { + if (VOP_GETATTR(dvp, &dva, 0, cr, NULL) == 0) { /* * If the parent's directory has the setgid bit set @@ -7073,8 +7102,9 @@ nfs4mknod(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, * we rename it instead of removing it and nfs_inactive * will remove the new name. */ +/* ARGSUSED */ static int -nfs4_remove(vnode_t *dvp, char *nm, cred_t *cr) +nfs4_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct, int flags) { COMPOUND4args_clnt args; COMPOUND4res_clnt res, *resp = NULL; @@ -7152,7 +7182,7 @@ nfs4_remove(vnode_t *dvp, char *nm, cred_t *cr) (rp->r_unldvp == NULL || strcmp(nm, rp->r_unlname) == 0)) { mutex_exit(&rp->r_statelock); tmpname = newname(); - e.error = nfs4rename(dvp, nm, dvp, tmpname, cr); + e.error = nfs4rename(dvp, nm, dvp, tmpname, cr, ct); if (e.error) kmem_free(tmpname, MAXNAMELEN); else { @@ -7188,7 +7218,7 @@ nfs4_remove(vnode_t *dvp, char *nm, cred_t *cr) */ if (nfs4_has_pages(vp) && ((rp->r_flags & R4DIRTY) || rp->r_count > 0)) { - e.error = nfs4_putpage(vp, (u_offset_t)0, 0, 0, cr); + e.error = nfs4_putpage(vp, (u_offset_t)0, 0, 0, cr, ct); if (e.error && (e.error == ENOSPC || e.error == EDQUOT)) { mutex_enter(&rp->r_statelock); if (!rp->r_error) @@ -7293,7 +7323,7 @@ nfs4_remove(vnode_t *dvp, char *nm, cred_t *cr) tvp = vp; if (IS_SHADOW(vp, trp)) tvp = RTOV4(trp); - vnevent_remove(tvp, dvp, nm); + vnevent_remove(tvp, dvp, nm, ct); } VN_RELE(vp); return (e.error); @@ -7306,8 +7336,10 @@ nfs4_remove(vnode_t *dvp, char *nm, cred_t *cr) * PUTFH(file), SAVEFH, PUTFH(targetdir), LINK, RESTOREFH, * GETATTR(file) */ +/* ARGSUSED */ static int -nfs4_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) +nfs4_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr, + caller_context_t *ct, int flags) { COMPOUND4args_clnt args; COMPOUND4res_clnt res, *resp = NULL; @@ -7332,7 +7364,7 @@ nfs4_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) if (nfs_zone() != VTOMI4(tdvp)->mi_zone) return (EPERM); - if (VOP_REALVP(svp, &realvp) == 0) { + if (VOP_REALVP(svp, &realvp, ct) == 0) { svp = realvp; ASSERT(nfs4_consistent_type(svp)); } @@ -7514,7 +7546,7 @@ nfs4_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) tvp = svp; if (IS_SHADOW(svp, trp)) tvp = RTOV4(trp); - vnevent_link(tvp); + vnevent_link(tvp, ct); } out: kmem_free(argop, argoplist_size); @@ -7526,17 +7558,19 @@ nfs4_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) return (e.error); } +/* ARGSUSED */ static int -nfs4_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) +nfs4_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr, + caller_context_t *ct, int flags) { vnode_t *realvp; if (nfs_zone() != VTOMI4(odvp)->mi_zone) return (EPERM); - if (VOP_REALVP(ndvp, &realvp) == 0) + if (VOP_REALVP(ndvp, &realvp, ct) == 0) ndvp = realvp; - return (nfs4rename(odvp, onm, ndvp, nnm, cr)); + return (nfs4rename(odvp, onm, ndvp, nnm, cr, ct)); } /* @@ -7547,7 +7581,8 @@ nfs4_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) * based on the likelihood of the filehandle to change during rename. */ static int -nfs4rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) +nfs4rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr, + caller_context_t *ct) { int error; mntinfo4_t *mi; @@ -7712,11 +7747,12 @@ nfs4rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) error = 0; if (do_link) { - error = nfs4_link(ndvp, nvp, tmpname, cr); + error = nfs4_link(ndvp, nvp, tmpname, cr, + NULL, 0); } if (error == EOPNOTSUPP || !do_link) { error = nfs4_rename(ndvp, nnm, ndvp, tmpname, - cr); + cr, NULL, 0); did_link = 0; } else { did_link = 1; @@ -7843,7 +7879,7 @@ nfs4rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) */ VN_HOLD(nvp); - (void) nfs4_remove(ndvp, tmpname, cr); + (void) nfs4_remove(ndvp, tmpname, cr, NULL, 0); /* Undo the unlinked file naming stuff we just did */ mutex_enter(&rp->r_statelock); @@ -7924,7 +7960,7 @@ nfs4rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) tvp = nvp; if (IS_SHADOW(nvp, trp)) tvp = RTOV4(trp); - vnevent_rename_dest(tvp, ndvp, nnm); + vnevent_rename_dest(tvp, ndvp, nnm, ct); } /* @@ -7936,14 +7972,14 @@ nfs4rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) tvp = ndvp; if (IS_SHADOW(ndvp, trp)) tvp = RTOV4(trp); - vnevent_rename_dest_dir(tvp); + vnevent_rename_dest_dir(tvp, ct); } trp = VTOR4(ovp); tvp = ovp; if (IS_SHADOW(ovp, trp)) tvp = RTOV4(trp); - vnevent_rename_src(tvp, odvp, onm); + vnevent_rename_src(tvp, odvp, onm, ct); } if (nvp) { @@ -7967,7 +8003,7 @@ nfs4rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) * The compound op structure for persistent fh rename is: * PUTFH(sourcdir), SAVEFH, PUTFH(targetdir), RENAME * Rather than bother with the directory postop args, we'll simply - * update that a change occured in the cache, so no post-op getattrs. + * update that a change occurred in the cache, so no post-op getattrs. */ static int nfs4rename_persistent_fh(vnode_t *odvp, char *onm, vnode_t *renvp, @@ -8432,8 +8468,10 @@ nfs4rename_volatile_fh(vnode_t *odvp, char *onm, vnode_t *ovp, return (e.error); } +/* ARGSUSED */ static int -nfs4_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, cred_t *cr) +nfs4_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, cred_t *cr, + caller_context_t *ct, int flags, vsecattr_t *vsecp) { int error; vnode_t *vp; @@ -8469,8 +8507,10 @@ nfs4_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, cred_t *cr) * The compound op structure is: * PUTFH(targetdir), REMOVE */ +/*ARGSUSED4*/ static int -nfs4_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) +nfs4_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr, + caller_context_t *ct, int flags) { int need_end_op = FALSE; COMPOUND4args_clnt args; @@ -8664,7 +8704,7 @@ nfs4_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) tvp = vp; if (IS_SHADOW(vp, trp)) tvp = RTOV4(trp); - vnevent_rmdir(tvp, dvp, nm); + vnevent_rmdir(tvp, dvp, nm, ct); } VN_RELE(vp); @@ -8672,8 +8712,10 @@ nfs4_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) return (e.error); } +/* ARGSUSED */ static int -nfs4_symlink(vnode_t *dvp, char *lnm, struct vattr *tva, char *tnm, cred_t *cr) +nfs4_symlink(vnode_t *dvp, char *lnm, struct vattr *tva, char *tnm, cred_t *cr, + caller_context_t *ct, int flags) { int error; vnode_t *vp; @@ -8726,8 +8768,10 @@ nfs4_symlink(vnode_t *dvp, char *lnm, struct vattr *tva, char *tnm, cred_t *cr) * may return only one block's worth of entries. Entries may be compressed * on the server. */ +/* ARGSUSED */ static int -nfs4_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp) +nfs4_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) { int error; uint_t count; @@ -8809,7 +8853,7 @@ nfs4_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp) nfs4readdir(vp, rdc, cr); /* - * Reaquire the lock, so that we can continue + * Reacquire the lock, so that we can continue */ mutex_enter(&rp->r_statelock); /* @@ -9483,7 +9527,7 @@ nfs4_bio(struct buf *bp, stable_how4 *stab_comm, cred_t *cr, bool_t readahead) /* ARGSUSED */ int -nfs4_fid(vnode_t *vp, fid_t *fidp) +nfs4_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct) { return (EREMOTE); } @@ -9522,7 +9566,7 @@ nfs4_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ctp) /* ARGSUSED */ static int -nfs4_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) +nfs4_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) { if (nfs_zone() != VTOMI4(vp)->mi_zone) return (EIO); @@ -9543,10 +9587,11 @@ nfs4_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) /* * Return all the pages from [off..off+len) in file */ +/* ARGSUSED */ static int nfs4_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp, page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr, - enum seg_rw rw, cred_t *cr) + enum seg_rw rw, cred_t *cr, caller_context_t *ct) { rnode4_t *rp; int error; @@ -10022,8 +10067,10 @@ nfs4_readahead(vnode_t *vp, u_offset_t blkoff, caddr_t addr, struct seg *seg, * len == MAXBSIZE (from segmap_release actions), and len == PAGESIZE * (from pageout). */ +/* ARGSUSED */ static int -nfs4_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr) +nfs4_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr, + caller_context_t *ct) { int error; rnode4_t *rp; @@ -10228,7 +10275,7 @@ nfs4_sync_putapage(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, */ if (!(flags & B_ASYNC)) { error = nfs4_putpage(vp, io_off, io_len, - B_INVAL | B_FORCE, cr); + B_INVAL | B_FORCE, cr, NULL); } } else { if (error) @@ -10251,9 +10298,11 @@ nfs4_sync_putapage(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, int nfs4_force_open_before_mmap = 0; #endif +/* ARGSUSED */ static int nfs4_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, - size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) + size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct) { struct segvn_crargs vn_a; int error = 0; @@ -10529,7 +10578,8 @@ open_and_get_osp(vnode_t *map_vp, cred_t *cr, nfs4_open_stream_t **ospp) /* ARGSUSED */ static int nfs4_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, - size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) + size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct) { rnode4_t *rp; int error = 0; @@ -10633,16 +10683,19 @@ nfs4_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, return (error); } +/* ARGSUSED */ static int -nfs4_cmp(vnode_t *vp1, vnode_t *vp2) +nfs4_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct) { return (VTOR4(vp1) == VTOR4(vp2)); } +/* ARGSUSED */ static int nfs4_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, - offset_t offset, struct flk_callback *flk_cbp, cred_t *cr) + offset_t offset, struct flk_callback *flk_cbp, cred_t *cr, + caller_context_t *ct) { int rc; u_offset_t start, end; @@ -10698,7 +10751,7 @@ nfs4_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, if (!nfs4_safelock(vp, bfp, cr)) return (EAGAIN); } - return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr)); + return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct)); } rp = VTOR4(vp); @@ -10748,7 +10801,7 @@ nfs4_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, mutex_exit(&rp->r_statelock); if (rc != 0) goto done; - error = nfs4_putpage(vp, (offset_t)0, 0, B_INVAL, cr); + error = nfs4_putpage(vp, (offset_t)0, 0, B_INVAL, cr, ct); if (error) { if (error == ENOSPC || error == EDQUOT) { mutex_enter(&rp->r_statelock); @@ -10820,7 +10873,7 @@ nfs4_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, /* ARGSUSED */ int -nfs4_realvp(vnode_t *vp, vnode_t **vpp) +nfs4_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct) { rnode4_t *rp; rp = VTOR4(vp); @@ -10845,7 +10898,8 @@ nfs4_realvp(vnode_t *vp, vnode_t **vpp) /* ARGSUSED */ static int nfs4_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, - size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr) + size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct) { int caller_found; int error; @@ -11043,7 +11097,7 @@ nfs4_delmap_callback(struct as *as, void *arg, uint_t event) if ((rp->r_flags & R4DIRECTIO) || (mi->mi_flags & MI4_DIRECTIO)) (void) nfs4_putpage(dmapp->vp, dmapp->off, dmapp->len, - B_INVAL, dmapp->cr); + B_INVAL, dmapp->cr, NULL); if (e.error) { e.stat = puterrno4(e.error); @@ -11111,8 +11165,10 @@ fattr4_maxfilesize_to_bits(uint64_t ll) return (l); } +/* ARGSUSED */ int -nfs4_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) +nfs4_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, + caller_context_t *ct) { int error; hrtime_t t; @@ -11140,7 +11196,7 @@ nfs4_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) * going otw with GETATTR(FATTR4_NAMED_ATTR). For now * just drive the OTW getattr. This is required because * _PC_XATTR_EXISTS can only return true if attributes - * exist -- simply checking for existance of the attrdir + * exist -- simply checking for existence of the attrdir * is not sufficient. * * pc4_xattr_valid can be only be trusted when r_xattr_dir @@ -11262,9 +11318,10 @@ nfs4_sync_pageio(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, return (error); } +/* ARGSUSED */ static int nfs4_pageio(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, - int flags, cred_t *cr) + int flags, cred_t *cr, caller_context_t *ct) { int error; rnode4_t *rp; @@ -11292,8 +11349,10 @@ nfs4_pageio(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, return (error); } +/* ARGSUSED */ static void -nfs4_dispose(vnode_t *vp, page_t *pp, int fl, int dn, cred_t *cr) +nfs4_dispose(vnode_t *vp, page_t *pp, int fl, int dn, cred_t *cr, + caller_context_t *ct) { int error; rnode4_t *rp; @@ -11899,7 +11958,7 @@ nfs4_putpage_commit(vnode_t *vp, offset_t poff, size_t plen, cred_t *cr) write_verf = rp->r_writeverf; mutex_exit(&rp->r_statelock); - error = nfs4_putpage(vp, poff, plen, B_ASYNC, cr); + error = nfs4_putpage(vp, poff, plen, B_ASYNC, cr, NULL); if (error == EAGAIN) error = 0; @@ -11910,7 +11969,7 @@ nfs4_putpage_commit(vnode_t *vp, offset_t poff, size_t plen, cred_t *cr) * the asynchronous i/o's in that range are done as well. */ if (!error) - error = nfs4_putpage(vp, poff, plen, 0, cr); + error = nfs4_putpage(vp, poff, plen, 0, cr, NULL); if (error) return (error); @@ -12067,7 +12126,8 @@ do_nfs4_async_commit(vnode_t *vp, page_t *plist, offset3 offset, count3 count, /*ARGSUSED*/ static int -nfs4_setsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr) +nfs4_setsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr, + caller_context_t *ct) { int error = 0; mntinfo4_t *mi; @@ -12113,8 +12173,10 @@ nfs4_setsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr) return (ENOSYS); } +/* ARGSUSED */ int -nfs4_getsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr) +nfs4_getsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr, + caller_context_t *ct) { int error; mntinfo4_t *mi; @@ -12167,7 +12229,7 @@ nfs4_getsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr) if (error) { vs_ace4_destroy(&gar.n4g_vsa); if (error == ENOTSUP || error == EOPNOTSUPP) - error = fs_fab_acl(vp, vsecattr, flag, cr); + error = fs_fab_acl(vp, vsecattr, flag, cr, ct); return (error); } @@ -12177,7 +12239,7 @@ nfs4_getsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr) * bitmap, neither was an acl. */ vs_ace4_destroy(&gar.n4g_vsa); - error = fs_fab_acl(vp, vsecattr, flag, cr); + error = fs_fab_acl(vp, vsecattr, flag, cr, ct); return (error); } @@ -12193,11 +12255,11 @@ nfs4_getsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr) if ((error) && (vsecattr->vsa_mask & (VSA_ACL | VSA_ACLCNT | VSA_DFACL | VSA_DFACLCNT)) && (error != EACCES)) { - error = fs_fab_acl(vp, vsecattr, flag, cr); + error = fs_fab_acl(vp, vsecattr, flag, cr, ct); } return (error); } - error = fs_fab_acl(vp, vsecattr, flag, cr); + error = fs_fab_acl(vp, vsecattr, flag, cr, ct); return (error); } @@ -12322,8 +12384,10 @@ nfs4_create_getsecattr_return(vsecattr_t *filled_vsap, vsecattr_t *vsap, return (0); } -static int -nfs4_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr) +/* ARGSUSED */ +int +nfs4_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr, + caller_context_t *ct) { int error; @@ -12348,7 +12412,7 @@ nfs4_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr) * request off to the local share code. */ if (VTOMI4(vp)->mi_flags & MI4_LLOCK) - return (fs_shrlock(vp, cmd, shr, flag, cr)); + return (fs_shrlock(vp, cmd, shr, flag, cr, ct)); switch (cmd) { case F_SHARE: @@ -13568,7 +13632,7 @@ nfs4frlock_recovery(int needrecov, nfs4_error_t *ep, } /* - * Handles the succesful reply from the server for nfs4frlock. + * Handles the successful reply from the server for nfs4frlock. */ static void nfs4frlock_results_ok(nfs4_lock_call_type_t ctype, int cmd, flock64_t *flk, @@ -13855,7 +13919,7 @@ nfs4frlock_final_cleanup(nfs4_lock_call_type_t ctype, COMPOUND4args_clnt *argsp, int error; error = VOP_PUTPAGE(vp, (u_offset_t)0, - 0, B_INVAL, cred); + 0, B_INVAL, cred, NULL); if (error && (error == ENOSPC || error == EDQUOT)) { rnode4_t *rp = VTOR4(vp); @@ -14345,7 +14409,7 @@ nfs4_safelock(vnode_t *vp, const struct flock64 *bfp, cred_t *cr) /* mandatory locking and mapping don't mix */ va.va_mask = AT_MODE; - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); if (error != 0) { NFS4_DEBUG(nfs4_client_lock_debug, (CE_NOTE, "nfs4_safelock: " "getattr error %d", error)); @@ -14502,7 +14566,8 @@ nfs4_lockrelease(vnode_t *vp, int flag, offset_t offset, cred_t *cr) ld.l_start = 0; ld.l_len = 0; /* do entire file */ - ret = VOP_FRLOCK(vp, F_SETLK, &ld, flag, offset, NULL, cr); + ret = VOP_FRLOCK(vp, F_SETLK, &ld, flag, offset, NULL, + cr, NULL); if (ret != 0) { /* @@ -15552,7 +15617,7 @@ locks_intersect(flock64_t *llfp, flock64_t *curfp) } /* - * Determine what the interseting lock region is, and add that to the + * Determine what the intersecting lock region is, and add that to the * 'nl_llpp' locklist in increasing order (by l_start). */ static void diff --git a/usr/src/uts/common/fs/nfs/nfs4_xdr.c b/usr/src/uts/common/fs/nfs/nfs4_xdr.c index 34fd0f11ddfc..ac08add50229 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_xdr.c +++ b/usr/src/uts/common/fs/nfs/nfs4_xdr.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. + * Copyright 2007 Sun Microsystems, Inc. * All rights reserved. Use is subject to license terms. */ @@ -190,7 +190,7 @@ xdr_inline_decode_nfs_fh4(uint32_t *ptr, nfs_fh4_fmt_t *fhp, uint32_t fhsize) fhsize -= 2 * BYTES_PER_XDR_UNIT + sizeof (ushort_t); /* - * For backwards compatability, the fid length may be less than + * For backwards compatibility, the fid length may be less than * NFS_FHMAXDATA, but it was always encoded as NFS_FHMAXDATA bytes. */ dsize = fhp->fh4_len < NFS_FHMAXDATA ? NFS_FHMAXDATA : fhp->fh4_len; @@ -849,6 +849,7 @@ xdr_ga_fattr_res(XDR *xdrs, struct nfs4_ga_res *garp, bitmap4 resbmap, vsap->vsa_aclcnt = acl.fattr4_acl_len; vsap->vsa_aclentp = acl.fattr4_acl_val; vsap->vsa_mask = VSA_ACE | VSA_ACECNT; + vsap->vsa_aclentsz = vsap->vsa_aclcnt * sizeof (ace_t); } if (resbmap & FATTR4_ACLSUPPORT_MASK) { diff --git a/usr/src/uts/common/fs/nfs/nfs_acl_srv.c b/usr/src/uts/common/fs/nfs/nfs_acl_srv.c index b8d90196ef30..f2ccc525fac3 100644 --- a/usr/src/uts/common/fs/nfs/nfs_acl_srv.c +++ b/usr/src/uts/common/fs/nfs/nfs_acl_srv.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. + * Copyright 2007 Sun Microsystems, Inc. * All rights reserved. * Use is subject to license terms. */ @@ -94,7 +94,7 @@ acl2_getacl(GETACL2args *args, GETACL2res *resp, struct exportinfo *exi, resp->resok.acl.vsa_mask = args->mask; - error = VOP_GETSECATTR(vp, &resp->resok.acl, 0, cr); + error = VOP_GETSECATTR(vp, &resp->resok.acl, 0, cr, NULL); if (error == ENOSYS) { /* @@ -112,7 +112,7 @@ acl2_getacl(GETACL2args *args, GETACL2res *resp, struct exportinfo *exi, * Note: if the fs_fab_acl() fails, we have other problems. * This error should be returned to the caller. */ - error = fs_fab_acl(vp, &resp->resok.acl, 0, cr); + error = fs_fab_acl(vp, &resp->resok.acl, 0, cr, NULL); } if (error) { @@ -211,7 +211,7 @@ acl2_setacl(SETACL2args *args, SETACL2res *resp, struct exportinfo *exi, } (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); - error = VOP_SETSECATTR(vp, &args->acl, 0, cr); + error = VOP_SETSECATTR(vp, &args->acl, 0, cr, NULL); if (error) { VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); VN_RELE(vp); @@ -317,7 +317,7 @@ acl2_access(ACCESS2args *args, ACCESS2res *resp, struct exportinfo *exi, * as well be reflected to the server during the open. */ va.va_mask = AT_MODE; - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); if (error) { VN_RELE(vp); resp->status = puterrno(error); @@ -327,30 +327,30 @@ acl2_access(ACCESS2args *args, ACCESS2res *resp, struct exportinfo *exi, resp->resok.access = 0; if (args->access & ACCESS2_READ) { - error = VOP_ACCESS(vp, VREAD, 0, cr); + error = VOP_ACCESS(vp, VREAD, 0, cr, NULL); if (!error && !MANDLOCK(vp, va.va_mode)) resp->resok.access |= ACCESS2_READ; } if ((args->access & ACCESS2_LOOKUP) && vp->v_type == VDIR) { - error = VOP_ACCESS(vp, VEXEC, 0, cr); + error = VOP_ACCESS(vp, VEXEC, 0, cr, NULL); if (!error) resp->resok.access |= ACCESS2_LOOKUP; } if (checkwriteperm && (args->access & (ACCESS2_MODIFY|ACCESS2_EXTEND))) { - error = VOP_ACCESS(vp, VWRITE, 0, cr); + error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL); if (!error && !MANDLOCK(vp, va.va_mode)) resp->resok.access |= (args->access & (ACCESS2_MODIFY|ACCESS2_EXTEND)); } if (checkwriteperm && (args->access & ACCESS2_DELETE) && (vp->v_type == VDIR)) { - error = VOP_ACCESS(vp, VWRITE, 0, cr); + error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL); if (!error) resp->resok.access |= ACCESS2_DELETE; } if (args->access & ACCESS2_EXECUTE) { - error = VOP_ACCESS(vp, VEXEC, 0, cr); + error = VOP_ACCESS(vp, VEXEC, 0, cr, NULL); if (!error && !MANDLOCK(vp, va.va_mode)) resp->resok.access |= ACCESS2_EXECUTE; } @@ -399,7 +399,7 @@ acl2_getxattrdir(GETXATTRDIR2args *args, GETXATTRDIR2res *resp, flags |= CREATE_XATTR_DIR; else { ulong_t val = 0; - error = VOP_PATHCONF(vp, _PC_XATTR_EXISTS, &val, cr); + error = VOP_PATHCONF(vp, _PC_XATTR_EXISTS, &val, cr, NULL); if (!error && val == 0) { VN_RELE(vp); resp->status = NFSERR_NOENT; @@ -407,7 +407,8 @@ acl2_getxattrdir(GETXATTRDIR2args *args, GETXATTRDIR2res *resp, } } - error = VOP_LOOKUP(vp, "", &avp, NULL, flags, NULL, cr); + error = VOP_LOOKUP(vp, "", &avp, NULL, flags, NULL, cr, + NULL, NULL, NULL); if (!error && avp == vp) { /* lookup of "" on old FS? */ error = EINVAL; VN_RELE(avp); @@ -472,7 +473,7 @@ acl3_getacl(GETACL3args *args, GETACL3res *resp, struct exportinfo *exi, resp->resok.acl.vsa_mask = args->mask; - error = VOP_GETSECATTR(vp, &resp->resok.acl, 0, cr); + error = VOP_GETSECATTR(vp, &resp->resok.acl, 0, cr, NULL); if (error == ENOSYS) { /* @@ -490,7 +491,7 @@ acl3_getacl(GETACL3args *args, GETACL3res *resp, struct exportinfo *exi, * Note: if the fs_fab_acl() fails, we have other problems. * This error should be returned to the caller. */ - error = fs_fab_acl(vp, &resp->resok.acl, 0, cr); + error = fs_fab_acl(vp, &resp->resok.acl, 0, cr, NULL); } if (error) @@ -602,7 +603,7 @@ acl3_setacl(SETACL3args *args, SETACL3res *resp, struct exportinfo *exi, goto out1; } - error = VOP_SETSECATTR(vp, &args->acl, 0, cr); + error = VOP_SETSECATTR(vp, &args->acl, 0, cr, NULL); #ifdef DEBUG if (rfs3_do_post_op_attr) { @@ -666,7 +667,7 @@ acl3_getxattrdir(GETXATTRDIR3args *args, GETXATTRDIR3res *resp, flags |= CREATE_XATTR_DIR; else { ulong_t val = 0; - error = VOP_PATHCONF(vp, _PC_XATTR_EXISTS, &val, cr); + error = VOP_PATHCONF(vp, _PC_XATTR_EXISTS, &val, cr, NULL); if (!error && val == 0) { VN_RELE(vp); resp->status = NFS3ERR_NOENT; @@ -674,7 +675,8 @@ acl3_getxattrdir(GETXATTRDIR3args *args, GETXATTRDIR3res *resp, } } - error = VOP_LOOKUP(vp, "", &avp, NULL, flags, NULL, cr); + error = VOP_LOOKUP(vp, "", &avp, NULL, flags, NULL, cr, + NULL, NULL, NULL); if (!error && avp == vp) { /* lookup of "" on old FS? */ error = EINVAL; VN_RELE(avp); diff --git a/usr/src/uts/common/fs/nfs/nfs_client.c b/usr/src/uts/common/fs/nfs/nfs_client.c index b6a8a96f01e0..17ea954eeaff 100644 --- a/usr/src/uts/common/fs/nfs/nfs_client.c +++ b/usr/src/uts/common/fs/nfs/nfs_client.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T. @@ -232,7 +232,7 @@ nfs_purge_caches(vnode_t *vp, int purge_dnlc, cred_t *cr) * Flush the page cache. */ if (vn_has_cached_data(vp)) { - error = VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_INVAL, cr); + error = VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_INVAL, cr, NULL); if (error && (error == ENOSPC || error == EDQUOT)) { mutex_enter(&rp->r_statelock); if (!rp->r_error) @@ -1807,7 +1807,7 @@ nfs_async_commit(vnode_t *vp, page_t *plist, offset3 offset, count3 count, void nfs_async_inactive(vnode_t *vp, cred_t *cr, - void (*inactive)(vnode_t *, cred_t *)) + void (*inactive)(vnode_t *, cred_t *, caller_context_t *)) { mntinfo_t *mi; struct nfs_async_reqs *args; @@ -1832,7 +1832,7 @@ nfs_async_inactive(vnode_t *vp, cred_t *cr, * set nfs3_max_threads/nfs_max_threads to zero in /etc/system. * * The manager thread knows about this and is willing to create - * at least one thread to accomodate us. + * at least one thread to accommodate us. */ mutex_enter(&mi->mi_async_lock); if (mi->mi_manager_thread == NULL) { @@ -2029,7 +2029,7 @@ nfs_async_start(struct vfs *vfsp) args->a_nfs_offset, args->a_nfs_count, args->a_cred); } else if (args->a_io == NFS_INACTIVE) { - (*args->a_nfs_inactive)(args->a_vp, args->a_cred); + (*args->a_nfs_inactive)(args->a_vp, args->a_cred, NULL); } /* @@ -2066,7 +2066,7 @@ nfs_async_stop(struct vfs *vfsp) * Wait for all outstanding putpage operation to complete. If a signal * is deliver we will abort and return non-zero. If we can put all the * pages we will return 0. This routine is called from nfs_unmount and - * nfs3_unmount to make these operations interruptable. + * nfs3_unmount to make these operations interruptible. */ int nfs_async_stop_sig(struct vfs *vfsp) @@ -2329,7 +2329,7 @@ nfs_putpages(vnode_t *vp, u_offset_t off, size_t len, int flags, cred_t *cr) flags, cr); /* - * If an error occured and the file was marked as dirty + * If an error occurred and the file was marked as dirty * before and we aren't forcibly invalidating pages, then * reset the RDIRTY flag. */ @@ -2762,7 +2762,8 @@ nfs_lockrelease(vnode_t *vp, int flag, offset_t offset, cred_t *cr) ld.l_whence = 0; /* unlock from start of file */ ld.l_start = 0; ld.l_len = 0; /* do entire file */ - ret = VOP_FRLOCK(vp, F_SETLK, &ld, flag, offset, NULL, cr); + ret = VOP_FRLOCK(vp, F_SETLK, &ld, flag, offset, NULL, cr, + NULL); if (ret != 0) { /* @@ -2802,7 +2803,7 @@ nfs_lockrelease(vnode_t *vp, int flag, offset_t offset, cred_t *cr) shr.s_sysid = 0; shr.s_pid = curproc->p_pid; - ret = VOP_SHRLOCK(vp, F_UNSHARE, &shr, flag, cr); + ret = VOP_SHRLOCK(vp, F_UNSHARE, &shr, flag, cr, NULL); #ifdef DEBUG if (ret != 0) { nfs_perror(ret, @@ -2975,7 +2976,7 @@ nfs_add_locking_id(vnode_t *vp, pid_t pid, int type, char *id, int len) nowners++; } else { cmn_err(CE_PANIC, "nfs_add_locking_id: " - "unrecognised lmpl_type %d", + "unrecognized lmpl_type %d", cur->lmpl_type); } } @@ -3055,7 +3056,7 @@ nfs_remove_locking_id(vnode_t *vp, int type, char *id, char *rid, int *rlen) nowners++; } else { cmn_err(CE_PANIC, - "nrli: unrecognised lmpl_type %d", + "nrli: unrecognized lmpl_type %d", cur->lmpl_type); } } diff --git a/usr/src/uts/common/fs/nfs/nfs_common.c b/usr/src/uts/common/fs/nfs/nfs_common.c index ded7c5075cbb..c980d55aeddd 100644 --- a/usr/src/uts/common/fs/nfs/nfs_common.c +++ b/usr/src/uts/common/fs/nfs/nfs_common.c @@ -70,7 +70,7 @@ #include /* - * The psuedo NFS filesystem to allow diskless booting to dynamically + * The pseudo NFS filesystem to allow diskless booting to dynamically * mount either a NFS V2, NFS V3, or NFS V4 filesystem. This only implements * the VFS_MOUNTROOT op and is only intended to be used by the * diskless booting code until the real root filesystem is mounted. @@ -273,7 +273,7 @@ _info(struct modinfo *modinfop) */ /* - * Returns the prefered transfer size in bytes based on + * Returns the preferred transfer size in bytes based on * what network interfaces are available. */ int @@ -287,7 +287,7 @@ nfstsize(void) } /* - * Returns the prefered transfer size in bytes based on + * Returns the preferred transfer size in bytes based on * what network interfaces are available. */ @@ -607,7 +607,7 @@ nfs_directio(vnode_t *vp, int cmd, cred_t *cr) if (vn_has_cached_data(vp) && ((rp->r_flags & RDIRTY) || rp->r_awcount > 0)) { error = VOP_PUTPAGE(vp, (offset_t)0, (uint_t)0, - B_INVAL, cr); + B_INVAL, cr, NULL); if (error) { if (error == ENOSPC || error == EDQUOT) { mutex_enter(&rp->r_statelock); diff --git a/usr/src/uts/common/fs/nfs/nfs_dump.c b/usr/src/uts/common/fs/nfs/nfs_dump.c index 17cb8ba1611e..8fe224936d67 100644 --- a/usr/src/uts/common/fs/nfs/nfs_dump.c +++ b/usr/src/uts/common/fs/nfs/nfs_dump.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -96,8 +95,9 @@ nd_log(const char *fmt, ...) } } +/* ARGSUSED */ int -nfs_dump(vnode_t *dumpvp, caddr_t addr, int bn, int count) +nfs_dump(vnode_t *dumpvp, caddr_t addr, int bn, int count, caller_context_t *ct) { static TIUSER *tiptr; XDR xdrs; diff --git a/usr/src/uts/common/fs/nfs/nfs_export.c b/usr/src/uts/common/fs/nfs/nfs_export.c index 3b7725dd3422..377b5bad1847 100644 --- a/usr/src/uts/common/fs/nfs/nfs_export.c +++ b/usr/src/uts/common/fs/nfs/nfs_export.c @@ -617,7 +617,7 @@ pseudo_secinfo_remove(exportinfo_t *ancexi, exp_visible_t *ancpseudo) * * If there is more than 1 export reference to an old flavor (i.e. some * of its children shared with this flavor), this flavor information - * needs to be transfered to the new exportdata struct. A flavor in + * needs to be transferred to the new exportdata struct. A flavor in * the old exportdata has descendant refs when s_refcnt > 1. * * Transferring descendant flavor refcnts happens in 2 passes: @@ -749,7 +749,7 @@ srv_secinfo_exp2exp(exportdata_t *curdata, secinfo_t *oldsecinfo, int ocnt) * When unsharing an old export node and the old node becomes a pseudo node, * if there is more than 1 export reference to an old flavor (i.e. some of * its children shared with this flavor), this flavor information needs to - * be transfered to the new shared node. + * be transferred to the new shared node. * * This routine is used under the protection of exported_lock (RW_WRITER). */ @@ -928,7 +928,8 @@ srv_secinfo_treeclimb(exportinfo_t *exip, secinfo_t *sec, int seccnt, int isadd) /* * Now, do a ".." to find parent dir of vp. */ - error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED()); + error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED(), + NULL, NULL, NULL); if (error == ENOTDIR && exportdir) { dvp = exip->exi_dvp; @@ -1039,8 +1040,8 @@ nfs_exportinit(void) } /* - * Finalization routine for export routines. Called to cleanup previoulsy - * initializtion work when the NFS server module could not be loaded correctly. + * Finalization routine for export routines. Called to cleanup previously + * initialization work when the NFS server module could not be loaded correctly. */ void nfs_exportfini(void) @@ -1243,7 +1244,7 @@ exportfs(struct exportfs_args *args, model_t model, cred_t *cr) * intended filesystem, so we can share the intended * filesystem instead of the AUTOFS filesystem. */ - (void) VOP_ACCESS(vp, 0, 0, cr); + (void) VOP_ACCESS(vp, 0, 0, cr, NULL); /* * We're interested in the top most filesystem. @@ -1266,7 +1267,7 @@ exportfs(struct exportfs_args *args, model_t model, cred_t *cr) */ bzero(&fid, sizeof (fid)); fid.fid_len = MAXFIDSZ; - error = VOP_FID(vp, &fid); + error = VOP_FID(vp, &fid, NULL); fsid = vp->v_vfsp->vfs_fsid; if (error) { VN_RELE(vp); @@ -1914,7 +1915,7 @@ nfs_getfh(struct nfs_getfh_args *args, model_t model, cred_t *cr) * intended filesystem, so we can share the intended * filesystem instead of the AUTOFS filesystem. */ - (void) VOP_ACCESS(vp, 0, 0, cr); + (void) VOP_ACCESS(vp, 0, 0, cr, NULL); /* * We're interested in the top most filesystem. @@ -1952,7 +1953,7 @@ nfs_getfh(struct nfs_getfh_args *args, model_t model, cred_t *cr) i += sz; /* - * For backwards compatability, the + * For backwards compatibility, the * fid length may be less than * NFS_FHMAXDATA, but it was always * encoded as NFS_FHMAXDATA bytes. @@ -2091,7 +2092,8 @@ nfs_vptoexi(vnode_t *dvp, vnode_t *vp, cred_t *cr, int *walk, * otherwise, look it up. */ if (dvp == NULL) { - error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, cr); + error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, cr, + NULL, NULL, NULL); if (error) break; } @@ -2141,7 +2143,7 @@ makefh(fhandle_t *fh, vnode_t *vp, exportinfo_t *exi) *fh = exi->exi_fh; /* struct copy */ - error = VOP_FID(vp, (fid_t *)&fh->fh_len); + error = VOP_FID(vp, (fid_t *)&fh->fh_len, NULL); if (error) { /* * Should be something other than EREMOTE @@ -2245,7 +2247,7 @@ makefh3(nfs_fh3 *fh, vnode_t *vp, struct exportinfo *exi) bzero(&fid, sizeof (fid)); fid.fid_len = MAXFIDSZ; - error = VOP_FID(vp, &fid); + error = VOP_FID(vp, &fid, NULL); if (error) return (EREMOTE); diff --git a/usr/src/uts/common/fs/nfs/nfs_log.c b/usr/src/uts/common/fs/nfs/nfs_log.c index 49d9d78f1094..a52a6937a914 100644 --- a/usr/src/uts/common/fs/nfs/nfs_log.c +++ b/usr/src/uts/common/fs/nfs/nfs_log.c @@ -461,7 +461,7 @@ log_file_create(caddr_t origname, struct log_file **lfpp) rfsl_log_file++; va.va_mask = AT_SIZE; - error = VOP_GETATTR(vp, &va, 0, CRED()); + error = VOP_GETATTR(vp, &va, 0, CRED(), NULL); if (error) { nfs_cmn_err(error, CE_WARN, "log_file_create: Can not stat %s - error = %m", name); @@ -510,7 +510,7 @@ log_file_create(caddr_t origname, struct log_file **lfpp) if (vp != NULL) { int error1; error1 = VOP_CLOSE(vp, FCREAT|FWRITE|FOFFMAX, 1, (offset_t)0, - CRED()); + CRED(), NULL); if (error1) { nfs_cmn_err(error1, CE_WARN, "log_file_create: Can not close %s - " @@ -561,7 +561,7 @@ log_file_rele(struct log_file *lfp) ASSERT(lfp->lf_writers == 0); if (error = VOP_CLOSE(lfp->lf_vp, FCREAT|FWRITE|FOFFMAX, 1, (offset_t)0, - CRED())) { + CRED(), NULL)) { nfs_cmn_err(error, CE_WARN, "NFS: Could not close log buffer %s - error = %m", lfp->lf_path); @@ -825,7 +825,7 @@ nfslog_write_logrecords(struct log_file *lfp, va.va_mask = AT_SIZE; (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); /* UIO_WRITE */ - if ((error = VOP_GETATTR(vp, &va, 0, CRED())) == 0) { + if ((error = VOP_GETATTR(vp, &va, 0, CRED(), NULL)) == 0) { if ((len + va.va_size) < (MAXOFF32_T)) { error = VOP_WRITE(vp, &uio, ioflag, CRED(), NULL); VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); @@ -1173,7 +1173,7 @@ nfsl_flush(struct nfsl_flush_args *args, model_t model) * This is where buffer flushing would occur, but there is no buffering * at this time. * Possibly rename the log buffer for processing. - * Sets tparams->ta_error equal to the value of the error that occured, + * Sets tparams->ta_error equal to the value of the error that occurred, * 0 otherwise. * Returns ENOENT if the buffer is not found. */ diff --git a/usr/src/uts/common/fs/nfs/nfs_server.c b/usr/src/uts/common/fs/nfs/nfs_server.c index a65ae500d80b..a99299aea3d8 100644 --- a/usr/src/uts/common/fs/nfs/nfs_server.c +++ b/usr/src/uts/common/fs/nfs/nfs_server.c @@ -2555,7 +2555,7 @@ rfs_publicfh_mclookup(char *p, vnode_t *dvp, cred_t *cr, vnode_t **vpp, * filesystem, so we can perform the lookup in the * intended filesystem. */ - (void) VOP_ACCESS(*vpp, 0, 0, cr); + (void) VOP_ACCESS(*vpp, 0, 0, cr, NULL); /* * If vnode is covered, get the @@ -2569,7 +2569,8 @@ rfs_publicfh_mclookup(char *p, vnode_t *dvp, cred_t *cr, vnode_t **vpp, } } - if (VOP_REALVP(*vpp, &realvp) == 0 && realvp != *vpp) { + if (VOP_REALVP(*vpp, &realvp, NULL) == 0 && + realvp != *vpp) { /* * If realvp is different from *vpp * then release our reference on *vpp, so that @@ -2603,7 +2604,8 @@ rfs_publicfh_mclookup(char *p, vnode_t *dvp, cred_t *cr, vnode_t **vpp, } } - if (VOP_REALVP(mc_dvp, &realvp) == 0 && realvp != mc_dvp) { + if (VOP_REALVP(mc_dvp, &realvp, NULL) == 0 && + realvp != mc_dvp) { /* * *vpp is a file, obtain realvp of the parent * directory vnode. @@ -2800,7 +2802,7 @@ MCLpath(char **path) ((c >= 'a' && c <= 'f') ? (c - 'a' + 10) : 0))) /* - * The implementation of URLparse gaurantees that the final string will + * The implementation of URLparse guarantees that the final string will * fit in the original one. Replaces '%' occurrences followed by 2 characters * with its corresponding hexadecimal character. */ diff --git a/usr/src/uts/common/fs/nfs/nfs_srv.c b/usr/src/uts/common/fs/nfs/nfs_srv.c index e75bd68a7c3c..d2969930be2d 100644 --- a/usr/src/uts/common/fs/nfs/nfs_srv.c +++ b/usr/src/uts/common/fs/nfs/nfs_srv.c @@ -249,7 +249,7 @@ rfs_setattr(struct nfssaargs *args, struct nfsattrstat *ns, bva.va_mask = AT_UID | AT_SIZE; TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_START, "vop_getattr_start:"); - error = VOP_GETATTR(vp, &bva, 0, cr); + error = VOP_GETATTR(vp, &bva, 0, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_END, "vop_getattr_end:"); if (error) { if (in_crit) @@ -272,7 +272,8 @@ rfs_setattr(struct nfssaargs *args, struct nfsattrstat *ns, offset = bva.va_size; length = va.va_size - bva.va_size; } - if (nbl_conflict(vp, NBL_WRITE, offset, length, 0)) { + if (nbl_conflict(vp, NBL_WRITE, offset, length, 0, + NULL)) { error = EACCES; } } @@ -322,7 +323,7 @@ rfs_setattr(struct nfssaargs *args, struct nfsattrstat *ns, /* * Force modified metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cr); + (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL); VN_RELE(vp); @@ -426,7 +427,8 @@ rfs_lookup(struct nfsdiropargs *da, struct nfsdiropres *dr, * Do a normal single component lookup. */ TRACE_0(TR_FAC_NFS, TR_VOP_LOOKUP_START, "vop_lookup_start:"); - error = VOP_LOOKUP(dvp, da->da_name, &vp, NULL, 0, NULL, cr); + error = VOP_LOOKUP(dvp, da->da_name, &vp, NULL, 0, NULL, cr, + NULL, NULL, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_LOOKUP_END, "vop_lookup_end:"); } @@ -512,7 +514,7 @@ rfs_readlink(fhandle_t *fhp, struct nfsrdlnres *rl, struct exportinfo *exi, va.va_mask = AT_MODE; TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_START, "vop_getattr_start:"); - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_END, "vop_getattr_end:"); if (error) { @@ -567,7 +569,7 @@ rfs_readlink(fhandle_t *fhp, struct nfsrdlnres *rl, struct exportinfo *exi, * Do the readlink. */ TRACE_0(TR_FAC_NFS, TR_VOP_READLINK_START, "vop_readlink_start:"); - error = VOP_READLINK(vp, &uio, cr); + error = VOP_READLINK(vp, &uio, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_READLINK_END, "vop_readlink_end:"); #if 0 /* notyet */ @@ -579,7 +581,7 @@ rfs_readlink(fhandle_t *fhp, struct nfsrdlnres *rl, struct exportinfo *exi, /* * Force modified metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cr); + (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL); #endif VN_RELE(vp); @@ -673,7 +675,7 @@ rfs_read(struct nfsreadargs *ra, struct nfsrdresult *rr, if (nbl_need_check(vp)) { nbl_start_crit(vp, RW_READER); if (nbl_conflict(vp, NBL_READ, ra->ra_offset, ra->ra_count, - 0)) { + 0, NULL)) { nbl_end_crit(vp); VN_RELE(vp); rr->rr_data = NULL; @@ -691,7 +693,7 @@ rfs_read(struct nfsreadargs *ra, struct nfsrdresult *rr, va.va_mask = AT_ALL; TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_START, "vop_getattr_start:"); - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_END, "vop_getattr_end:"); if (error) { @@ -716,7 +718,7 @@ rfs_read(struct nfsreadargs *ra, struct nfsrdresult *rr, */ if (crgetuid(cr) != va.va_uid) { TRACE_0(TR_FAC_NFS, TR_VOP_ACCESS_START, "vop_access_start:"); - error = VOP_ACCESS(vp, VREAD, 0, cr); + error = VOP_ACCESS(vp, VREAD, 0, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_ACCESS_END, "vop_access_end:"); if (error) { /* @@ -725,7 +727,7 @@ rfs_read(struct nfsreadargs *ra, struct nfsrdresult *rr, */ TRACE_0(TR_FAC_NFS, TR_VOP_ACCESS_START, "vop_access_start:"); - error = VOP_ACCESS(vp, VEXEC, 0, cr); + error = VOP_ACCESS(vp, VEXEC, 0, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_ACCESS_END, "vop_access_end:"); } @@ -824,7 +826,7 @@ rfs_read(struct nfsreadargs *ra, struct nfsrdresult *rr, */ va.va_mask = AT_ALL; TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_START, "vop_getattr_start:"); - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_END, "vop_getattr_end:"); if (error) { freeb(mp); @@ -868,7 +870,7 @@ rfs_read(struct nfsreadargs *ra, struct nfsrdresult *rr, /* * Force modified metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cr); + (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL); #endif VN_RELE(vp); @@ -970,7 +972,7 @@ rfs_write_sync(struct nfswriteargs *wa, struct nfsattrstat *ns, va.va_mask = AT_UID|AT_MODE; TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_START, "vop_getattr_start:"); - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_END, "vop_getattr_end:"); if (error) { @@ -988,7 +990,7 @@ rfs_write_sync(struct nfswriteargs *wa, struct nfsattrstat *ns, * is always allowed to write it. */ TRACE_0(TR_FAC_NFS, TR_VOP_ACCESS_START, "vop_access_start:"); - error = VOP_ACCESS(vp, VWRITE, 0, cr); + error = VOP_ACCESS(vp, VWRITE, 0, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_ACCESS_END, "vop_access_end:"); if (error) { VN_RELE(vp); @@ -1020,7 +1022,7 @@ rfs_write_sync(struct nfswriteargs *wa, struct nfsattrstat *ns, nbl_start_crit(vp, RW_READER); in_crit = 1; if (nbl_conflict(vp, NBL_WRITE, wa->wa_offset, - wa->wa_count, 0)) { + wa->wa_count, 0, NULL)) { error = EACCES; goto out; } @@ -1125,7 +1127,7 @@ rfs_write_sync(struct nfswriteargs *wa, struct nfsattrstat *ns, */ va.va_mask = AT_ALL; /* now we want everything */ TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_START, "vop_getattr_start:"); - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_END, "vop_getattr_end:"); /* check for overflows */ if (!error) { @@ -1413,7 +1415,7 @@ rfs_write(struct nfswriteargs *wa, struct nfsattrstat *ns, va.va_mask = AT_UID|AT_MODE; TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_START, "vop_getattr_start:"); - error = VOP_GETATTR(vp, &va, 0, rp->cr); + error = VOP_GETATTR(vp, &va, 0, rp->cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_END, "vop_getattr_end:"); if (!error) { if (crgetuid(rp->cr) != va.va_uid) { @@ -1425,7 +1427,7 @@ rfs_write(struct nfswriteargs *wa, struct nfsattrstat *ns, */ TRACE_0(TR_FAC_NFS, TR_VOP_ACCESS_START, "vop_access_start:"); - error = VOP_ACCESS(vp, VWRITE, 0, rp->cr); + error = VOP_ACCESS(vp, VWRITE, 0, rp->cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_ACCESS_END, "vop_access_end:"); } @@ -1437,7 +1439,7 @@ rfs_write(struct nfswriteargs *wa, struct nfsattrstat *ns, * Check for a conflict with a nbmand-locked region. */ if (in_crit && nbl_conflict(vp, NBL_WRITE, rp->wa->wa_offset, - rp->wa->wa_count, 0)) { + rp->wa->wa_count, 0, NULL)) { error = EACCES; } @@ -1598,7 +1600,7 @@ rfs_write(struct nfswriteargs *wa, struct nfsattrstat *ns, va.va_mask = AT_ALL; /* now we want everything */ TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_START, "vop_getattr_start:"); - error = VOP_GETATTR(vp, &va, 0, rp->cr); + error = VOP_GETATTR(vp, &va, 0, rp->cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_END, "vop_getattr_end:"); if (!error) @@ -1629,12 +1631,12 @@ rfs_write(struct nfswriteargs *wa, struct nfsattrstat *ns, */ if (data_written) { TRACE_0(TR_FAC_NFS, TR_VOP_PUTPAGE_START, "vop_putpage_start:"); - error = VOP_PUTPAGE(vp, (u_offset_t)off, len, 0, cr); + error = VOP_PUTPAGE(vp, (u_offset_t)off, len, 0, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_PUTPAGE_END, "vop_putpage_end:"); if (!error) { TRACE_0(TR_FAC_NFS, TR_VOP_FSYNC_START, "vop_fsync_start:"); - error = VOP_FSYNC(vp, FNODSYNC, cr); + error = VOP_FSYNC(vp, FNODSYNC, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_FSYNC_END, "vop_fsync_end:"); } } @@ -1777,7 +1779,8 @@ rfs_create(struct nfscreatargs *args, struct nfsdiropres *dr, mode = VWRITE; if (!(va.va_mask & AT_SIZE) || va.va_type != VREG) { TRACE_0(TR_FAC_NFS, TR_VOP_LOOKUP_START, "vop_lookup_start:"); - error = VOP_LOOKUP(dvp, name, &tvp, NULL, 0, NULL, cr); + error = VOP_LOOKUP(dvp, name, &tvp, NULL, 0, NULL, cr, + NULL, NULL, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_LOOKUP_END, "vop_lookup_end:"); if (!error) { struct vattr at; @@ -1786,7 +1789,7 @@ rfs_create(struct nfscreatargs *args, struct nfsdiropres *dr, at.va_mask = AT_MODE; TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_START, "vop_getattr_start:"); - error = VOP_GETATTR(tvp, &at, 0, cr); + error = VOP_GETATTR(tvp, &at, 0, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_END, "vop_getattr_end:"); if (!error) @@ -1814,7 +1817,8 @@ rfs_create(struct nfscreatargs *args, struct nfsdiropres *dr, * are conflicting locks. */ if (!error && (va.va_type == VREG) && (va.va_mask & AT_SIZE)) { - lookuperr = VOP_LOOKUP(dvp, name, &tvp, NULL, 0, NULL, cr); + lookuperr = VOP_LOOKUP(dvp, name, &tvp, NULL, 0, NULL, cr, + NULL, NULL, NULL); if (!lookuperr && rfs4_check_delegated(FWRITE, tvp, va.va_size == 0)) { @@ -1837,7 +1841,7 @@ rfs_create(struct nfscreatargs *args, struct nfsdiropres *dr, in_crit = 1; bva.va_mask = AT_SIZE; - error = VOP_GETATTR(tvp, &bva, 0, cr); + error = VOP_GETATTR(tvp, &bva, 0, cr, NULL); if (!error) { if (va.va_size < bva.va_size) { offset = va.va_size; @@ -1848,7 +1852,7 @@ rfs_create(struct nfscreatargs *args, struct nfsdiropres *dr, } if (length) { if (nbl_conflict(tvp, NBL_WRITE, - offset, length, 0)) { + offset, length, 0, NULL)) { error = EACCES; } } @@ -1873,7 +1877,8 @@ rfs_create(struct nfscreatargs *args, struct nfsdiropres *dr, va.va_mode &= ~(VSUID | VSGID); TRACE_0(TR_FAC_NFS, TR_VOP_CREATE_START, "vop_create_start:"); - error = VOP_CREATE(dvp, name, &va, NONEXCL, mode, &vp, cr, 0); + error = VOP_CREATE(dvp, name, &va, NONEXCL, mode, &vp, cr, 0, + NULL, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_CREATE_END, "vop_create_end:"); if (!error) { @@ -1891,7 +1896,7 @@ rfs_create(struct nfscreatargs *args, struct nfsdiropres *dr, va.va_mask = AT_ALL; TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_START, "vop_getattr_start:"); - error = VOP_GETATTR(vp, &va, 0, cr); + error = VOP_GETATTR(vp, &va, 0, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_END, "vop_getattr_end:"); /* check for overflows */ @@ -1906,7 +1911,7 @@ rfs_create(struct nfscreatargs *args, struct nfsdiropres *dr, /* * Force modified metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cr); + (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL); VN_RELE(vp); } @@ -1919,7 +1924,7 @@ rfs_create(struct nfscreatargs *args, struct nfsdiropres *dr, /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(dvp, 0, cr); + (void) VOP_FSYNC(dvp, 0, cr, NULL); out: @@ -1979,7 +1984,8 @@ rfs_remove(struct nfsdiropargs *da, enum nfsstat *status, /* * Check for a conflict with a non-blocking mandatory share reservation. */ - error = VOP_LOOKUP(vp, da->da_name, &targvp, NULL, 0, NULL, cr); + error = VOP_LOOKUP(vp, da->da_name, &targvp, NULL, 0, + NULL, cr, NULL, NULL, NULL); if (error != 0) { VN_RELE(vp); *status = puterrno(error); @@ -2004,20 +2010,20 @@ rfs_remove(struct nfsdiropargs *da, enum nfsstat *status, if (nbl_need_check(targvp)) { nbl_start_crit(targvp, RW_READER); in_crit = 1; - if (nbl_conflict(targvp, NBL_REMOVE, 0, 0, 0)) { + if (nbl_conflict(targvp, NBL_REMOVE, 0, 0, 0, NULL)) { error = EACCES; goto out; } } TRACE_0(TR_FAC_NFS, TR_VOP_REMOVE_START, "vop_remove_start:"); - error = VOP_REMOVE(vp, da->da_name, cr); + error = VOP_REMOVE(vp, da->da_name, cr, NULL, 0); TRACE_0(TR_FAC_NFS, TR_VOP_REMOVE_END, "vop_remove_end:"); /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(vp, 0, cr); + (void) VOP_FSYNC(vp, 0, cr, NULL); out: if (in_crit) @@ -2126,7 +2132,7 @@ rfs_rename(struct nfsrnmargs *args, enum nfsstat *status, * Check for a conflict with a non-blocking mandatory share reservation. */ error = VOP_LOOKUP(fromvp, args->rna_from.da_name, &srcvp, NULL, 0, - NULL, cr); + NULL, cr, NULL, NULL, NULL); if (error != 0) { VN_RELE(tovp); VN_RELE(fromvp); @@ -2147,8 +2153,8 @@ rfs_rename(struct nfsrnmargs *args, enum nfsstat *status, /* Check for delegation on the file being renamed over, if it exists */ if (rfs4_deleg_policy != SRV_NEVER_DELEGATE && - VOP_LOOKUP(tovp, args->rna_to.da_name, &targvp, NULL, 0, NULL, cr) - == 0) { + VOP_LOOKUP(tovp, args->rna_to.da_name, &targvp, NULL, 0, NULL, cr, + NULL, NULL, NULL) == 0) { if (rfs4_check_delegated(FWRITE, targvp, TRUE)) { VN_RELE(tovp); @@ -2165,7 +2171,7 @@ rfs_rename(struct nfsrnmargs *args, enum nfsstat *status, if (nbl_need_check(srcvp)) { nbl_start_crit(srcvp, RW_READER); in_crit = 1; - if (nbl_conflict(srcvp, NBL_RENAME, 0, 0, 0)) { + if (nbl_conflict(srcvp, NBL_RENAME, 0, 0, 0, NULL)) { error = EACCES; goto out; } @@ -2173,7 +2179,7 @@ rfs_rename(struct nfsrnmargs *args, enum nfsstat *status, TRACE_0(TR_FAC_NFS, TR_VOP_RENAME_START, "vop_rename_start:"); error = VOP_RENAME(fromvp, args->rna_from.da_name, - tovp, args->rna_to.da_name, cr); + tovp, args->rna_to.da_name, cr, NULL, 0); TRACE_0(TR_FAC_NFS, TR_VOP_RENAME_END, "vop_rename_end:"); if (error == 0) { @@ -2193,8 +2199,8 @@ rfs_rename(struct nfsrnmargs *args, enum nfsstat *status, /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(tovp, 0, cr); - (void) VOP_FSYNC(fromvp, 0, cr); + (void) VOP_FSYNC(tovp, 0, cr, NULL); + (void) VOP_FSYNC(fromvp, 0, cr, NULL); out: if (in_crit) @@ -2295,14 +2301,14 @@ rfs_link(struct nfslinkargs *args, enum nfsstat *status, } TRACE_0(TR_FAC_NFS, TR_VOP_LINK_START, "vop_link_start:"); - error = VOP_LINK(tovp, fromvp, args->la_to.da_name, cr); + error = VOP_LINK(tovp, fromvp, args->la_to.da_name, cr, NULL, 0); TRACE_0(TR_FAC_NFS, TR_VOP_LINK_END, "vop_link_end:"); /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(tovp, 0, cr); - (void) VOP_FSYNC(fromvp, FNODSYNC, cr); + (void) VOP_FSYNC(tovp, 0, cr, NULL); + (void) VOP_FSYNC(fromvp, FNODSYNC, cr, NULL); VN_RELE(tovp); VN_RELE(fromvp); @@ -2381,7 +2387,8 @@ rfs_symlink(struct nfsslargs *args, enum nfsstat *status, va.va_mask |= AT_TYPE; TRACE_0(TR_FAC_NFS, TR_VOP_SYMLINK_START, "vop_symlink_start:"); - error = VOP_SYMLINK(vp, args->sla_from.da_name, &va, args->sla_tnm, cr); + error = VOP_SYMLINK(vp, args->sla_from.da_name, &va, args->sla_tnm, cr, + NULL, 0); TRACE_0(TR_FAC_NFS, TR_VOP_SYMLINK_END, "vop_symlink_end:"); /* @@ -2389,17 +2396,17 @@ rfs_symlink(struct nfsslargs *args, enum nfsstat *status, */ TRACE_0(TR_FAC_NFS, TR_VOP_LOOKUP_START, "vop_lookup_start:"); lerror = VOP_LOOKUP(vp, args->sla_from.da_name, &svp, NULL, - 0, NULL, cr); + 0, NULL, cr, NULL, NULL, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_LOOKUP_END, "vop_lookup_end:"); if (!lerror) { - (void) VOP_FSYNC(svp, 0, cr); + (void) VOP_FSYNC(svp, 0, cr, NULL); VN_RELE(svp); } /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(vp, 0, cr); + (void) VOP_FSYNC(vp, 0, cr, NULL); VN_RELE(vp); @@ -2477,7 +2484,7 @@ rfs_mkdir(struct nfscreatargs *args, struct nfsdiropres *dr, va.va_mask |= AT_TYPE; TRACE_0(TR_FAC_NFS, TR_VOP_MKDIR_START, "vop_mkdir_start:"); - error = VOP_MKDIR(vp, name, &va, &dvp, cr); + error = VOP_MKDIR(vp, name, &va, &dvp, cr, NULL, 0, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_MKDIR_END, "vop_mkdir_end:"); if (!error) { @@ -2487,7 +2494,7 @@ rfs_mkdir(struct nfscreatargs *args, struct nfsdiropres *dr, */ va.va_mask = AT_ALL; /* We want everything */ TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_START, "vop_getattr_start:"); - error = VOP_GETATTR(dvp, &va, 0, cr); + error = VOP_GETATTR(dvp, &va, 0, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_GETATTR_END, "vop_getattr_end:"); /* check for overflows */ if (!error) { @@ -2500,14 +2507,14 @@ rfs_mkdir(struct nfscreatargs *args, struct nfsdiropres *dr, /* * Force new data and metadata out to stable storage. */ - (void) VOP_FSYNC(dvp, 0, cr); + (void) VOP_FSYNC(dvp, 0, cr, NULL); VN_RELE(dvp); } /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(vp, 0, cr); + (void) VOP_FSYNC(vp, 0, cr, NULL); VN_RELE(vp); @@ -2570,13 +2577,13 @@ rfs_rmdir(struct nfsdiropargs *da, enum nfsstat *status, * remove. */ TRACE_0(TR_FAC_NFS, TR_VOP_RMDIR_START, "vop_rmdir_start:"); - error = VOP_RMDIR(vp, da->da_name, rootdir, cr); + error = VOP_RMDIR(vp, da->da_name, rootdir, cr, NULL, 0); TRACE_0(TR_FAC_NFS, TR_VOP_RMDIR_END, "vop_rmdir_end:"); /* * Force modified data and metadata out to stable storage. */ - (void) VOP_FSYNC(vp, 0, cr); + (void) VOP_FSYNC(vp, 0, cr, NULL); VN_RELE(vp); @@ -2635,7 +2642,7 @@ rfs_readdir(struct nfsrddirargs *rda, struct nfsrddirres *rd, TRACE_0(TR_FAC_NFS, TR_VOP_RWLOCK_END, "vop_rwlock_end:"); TRACE_0(TR_FAC_NFS, TR_VOP_ACCESS_START, "vop_access_start:"); - error = VOP_ACCESS(vp, VREAD, 0, cr); + error = VOP_ACCESS(vp, VREAD, 0, cr, NULL); TRACE_0(TR_FAC_NFS, TR_VOP_ACCESS_END, "vop_access_end:"); if (error) { rd->rd_entries = NULL; @@ -2673,7 +2680,7 @@ rfs_readdir(struct nfsrddirargs *rda, struct nfsrddirres *rd, * read directory */ TRACE_0(TR_FAC_NFS, TR_VOP_READDIR_START, "vop_readdir_start:"); - error = VOP_READDIR(vp, &uio, cr, &iseof); + error = VOP_READDIR(vp, &uio, cr, &iseof, NULL, 0); TRACE_0(TR_FAC_NFS, TR_VOP_READDIR_END, "vop_readdir_end:"); /* @@ -2707,7 +2714,7 @@ rfs_readdir(struct nfsrddirargs *rda, struct nfsrddirres *rd, /* * Force modified metadata out to stable storage. */ - (void) VOP_FSYNC(vp, FNODSYNC, cr); + (void) VOP_FSYNC(vp, FNODSYNC, cr, NULL); #endif VN_RELE(vp); @@ -2969,7 +2976,7 @@ acl_perm(struct vnode *vp, struct exportinfo *exi, struct vattr *va, cred_t *cr) /* dont care default acl */ vsa.vsa_mask = (VSA_ACL | VSA_ACLCNT); - error = VOP_GETSECATTR(vp, &vsa, 0, cr); + error = VOP_GETSECATTR(vp, &vsa, 0, cr, NULL); if (!error) { aclcnt = vsa.vsa_aclcnt; diff --git a/usr/src/uts/common/fs/nfs/nfs_subr.c b/usr/src/uts/common/fs/nfs/nfs_subr.c index d9ae64ddf236..10391a7f4207 100644 --- a/usr/src/uts/common/fs/nfs/nfs_subr.c +++ b/usr/src/uts/common/fs/nfs/nfs_subr.c @@ -105,7 +105,7 @@ * freelist and then trying to place them back on the freelist * when their reference is released. This means that the when an * rnode is looked up in the hash queues, then either the rnode - * is removed from the freelist and that reference is tranfered to + * is removed from the freelist and that reference is transferred to * the new reference or the vnode reference count must be incremented * accordingly. The mutex for the freelist must be held in order to * accurately test to see if the rnode is on the freelist or not. @@ -2095,7 +2095,7 @@ setdirgid(vnode_t *dvp, gid_t *gidp, cred_t *cr) struct vattr va; va.va_mask = AT_MODE | AT_GID; - error = VOP_GETATTR(dvp, &va, 0, cr); + error = VOP_GETATTR(dvp, &va, 0, cr, NULL); if (error) return (error); @@ -2123,7 +2123,7 @@ setdirmode(vnode_t *dvp, mode_t *omp, cred_t *cr) struct vattr va; va.va_mask = AT_MODE; - error = VOP_GETATTR(dvp, &va, 0, cr); + error = VOP_GETATTR(dvp, &va, 0, cr, NULL); if (error) return (error); @@ -2189,7 +2189,7 @@ rinactive(rnode_t *rp, cred_t *cr) if (vn_has_cached_data(vp)) { ASSERT(vp->v_type != VCHR); if ((rp->r_flags & RDIRTY) && !rp->r_error) { - error = VOP_PUTPAGE(vp, (u_offset_t)0, 0, 0, cr); + error = VOP_PUTPAGE(vp, (u_offset_t)0, 0, 0, cr, NULL); if (error && (error == ENOSPC || error == EDQUOT)) { mutex_enter(&rp->r_statelock); if (!rp->r_error) @@ -3084,7 +3084,7 @@ rflush(struct vfs *vfsp, cred_t *cr) */ while (cnt-- > 0) { vp = vplist[cnt]; - (void) VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_ASYNC, cr); + (void) VOP_PUTPAGE(vp, (u_offset_t)0, 0, B_ASYNC, cr, NULL); VN_RELE(vp); } diff --git a/usr/src/uts/common/fs/nfs/nfs_vnops.c b/usr/src/uts/common/fs/nfs/nfs_vnops.c index 96b7044e6a39..befefa580fe9 100644 --- a/usr/src/uts/common/fs/nfs/nfs_vnops.c +++ b/usr/src/uts/common/fs/nfs/nfs_vnops.c @@ -91,7 +91,8 @@ static int nfsread(vnode_t *, caddr_t, uint_t, int, size_t *, cred_t *); static int nfssetattr(vnode_t *, struct vattr *, int, cred_t *); static int nfslookup_dnlc(vnode_t *, char *, vnode_t **, cred_t *); static int nfslookup_otw(vnode_t *, char *, vnode_t **, cred_t *, int); -static int nfsrename(vnode_t *, char *, vnode_t *, char *, cred_t *); +static int nfsrename(vnode_t *, char *, vnode_t *, char *, cred_t *, + caller_context_t *); static int nfsreaddir(vnode_t *, rddir_cache *, cred_t *); static int nfs_bio(struct buf *, cred_t *); static int nfs_getapage(vnode_t *, u_offset_t, size_t, uint_t *, @@ -123,59 +124,75 @@ static void nfs_delmap_callback(struct as *, void *, uint_t); * more details on rnode locking. */ -static int nfs_open(vnode_t **, int, cred_t *); -static int nfs_close(vnode_t *, int, int, offset_t, cred_t *); +static int nfs_open(vnode_t **, int, cred_t *, caller_context_t *); +static int nfs_close(vnode_t *, int, int, offset_t, cred_t *, + caller_context_t *); static int nfs_read(vnode_t *, struct uio *, int, cred_t *, caller_context_t *); static int nfs_write(vnode_t *, struct uio *, int, cred_t *, caller_context_t *); -static int nfs_ioctl(vnode_t *, int, intptr_t, int, cred_t *, int *); -static int nfs_getattr(vnode_t *, struct vattr *, int, cred_t *); +static int nfs_ioctl(vnode_t *, int, intptr_t, int, cred_t *, int *, + caller_context_t *); +static int nfs_getattr(vnode_t *, struct vattr *, int, cred_t *, + caller_context_t *); static int nfs_setattr(vnode_t *, struct vattr *, int, cred_t *, caller_context_t *); -static int nfs_access(vnode_t *, int, int, cred_t *); +static int nfs_access(vnode_t *, int, int, cred_t *, caller_context_t *); static int nfs_accessx(void *, int, cred_t *); -static int nfs_readlink(vnode_t *, struct uio *, cred_t *); -static int nfs_fsync(vnode_t *, int, cred_t *); -static void nfs_inactive(vnode_t *, cred_t *); +static int nfs_readlink(vnode_t *, struct uio *, cred_t *, + caller_context_t *); +static int nfs_fsync(vnode_t *, int, cred_t *, caller_context_t *); +static void nfs_inactive(vnode_t *, cred_t *, caller_context_t *); static int nfs_lookup(vnode_t *, char *, vnode_t **, struct pathname *, - int, vnode_t *, cred_t *); + int, vnode_t *, cred_t *, caller_context_t *, + int *, pathname_t *); static int nfs_create(vnode_t *, char *, struct vattr *, enum vcexcl, - int, vnode_t **, cred_t *, int); -static int nfs_remove(vnode_t *, char *, cred_t *); -static int nfs_link(vnode_t *, vnode_t *, char *, cred_t *); -static int nfs_rename(vnode_t *, char *, vnode_t *, char *, cred_t *); -static int nfs_mkdir(vnode_t *, char *, struct vattr *, - vnode_t **, cred_t *); -static int nfs_rmdir(vnode_t *, char *, vnode_t *, cred_t *); + int, vnode_t **, cred_t *, int, caller_context_t *, + vsecattr_t *); +static int nfs_remove(vnode_t *, char *, cred_t *, caller_context_t *, + int); +static int nfs_link(vnode_t *, vnode_t *, char *, cred_t *, + caller_context_t *, int); +static int nfs_rename(vnode_t *, char *, vnode_t *, char *, cred_t *, + caller_context_t *, int); +static int nfs_mkdir(vnode_t *, char *, struct vattr *, vnode_t **, + cred_t *, caller_context_t *, int, vsecattr_t *); +static int nfs_rmdir(vnode_t *, char *, vnode_t *, cred_t *, + caller_context_t *, int); static int nfs_symlink(vnode_t *, char *, struct vattr *, char *, - cred_t *); -static int nfs_readdir(vnode_t *, struct uio *, cred_t *, int *); -static int nfs_fid(vnode_t *, fid_t *); + cred_t *, caller_context_t *, int); +static int nfs_readdir(vnode_t *, struct uio *, cred_t *, int *, + caller_context_t *, int); +static int nfs_fid(vnode_t *, fid_t *, caller_context_t *); static int nfs_rwlock(vnode_t *, int, caller_context_t *); static void nfs_rwunlock(vnode_t *, int, caller_context_t *); -static int nfs_seek(vnode_t *, offset_t, offset_t *); +static int nfs_seek(vnode_t *, offset_t, offset_t *, caller_context_t *); static int nfs_getpage(vnode_t *, offset_t, size_t, uint_t *, page_t *[], size_t, struct seg *, caddr_t, - enum seg_rw, cred_t *); -static int nfs_putpage(vnode_t *, offset_t, size_t, int, cred_t *); -static int nfs_map(vnode_t *, offset_t, struct as *, caddr_t *, - size_t, uchar_t, uchar_t, uint_t, cred_t *); -static int nfs_addmap(vnode_t *, offset_t, struct as *, caddr_t, - size_t, uchar_t, uchar_t, uint_t, cred_t *); + enum seg_rw, cred_t *, caller_context_t *); +static int nfs_putpage(vnode_t *, offset_t, size_t, int, cred_t *, + caller_context_t *); +static int nfs_map(vnode_t *, offset_t, struct as *, caddr_t *, size_t, + uchar_t, uchar_t, uint_t, cred_t *, caller_context_t *); +static int nfs_addmap(vnode_t *, offset_t, struct as *, caddr_t, size_t, + uchar_t, uchar_t, uint_t, cred_t *, caller_context_t *); static int nfs_frlock(vnode_t *, int, struct flock64 *, int, offset_t, - struct flk_callback *, cred_t *); + struct flk_callback *, cred_t *, caller_context_t *); static int nfs_space(vnode_t *, int, struct flock64 *, int, offset_t, cred_t *, caller_context_t *); -static int nfs_realvp(vnode_t *, vnode_t **); -static int nfs_delmap(vnode_t *, offset_t, struct as *, caddr_t, - size_t, uint_t, uint_t, uint_t, cred_t *); -static int nfs_pathconf(vnode_t *, int, ulong_t *, cred_t *); +static int nfs_realvp(vnode_t *, vnode_t **, caller_context_t *); +static int nfs_delmap(vnode_t *, offset_t, struct as *, caddr_t, size_t, + uint_t, uint_t, uint_t, cred_t *, caller_context_t *); +static int nfs_pathconf(vnode_t *, int, ulong_t *, cred_t *, + caller_context_t *); static int nfs_pageio(vnode_t *, page_t *, u_offset_t, size_t, int, - cred_t *); -static int nfs_setsecattr(vnode_t *, vsecattr_t *, int, cred_t *); -static int nfs_getsecattr(vnode_t *, vsecattr_t *, int, cred_t *); -static int nfs_shrlock(vnode_t *, int, struct shrlock *, int, cred_t *); + cred_t *, caller_context_t *); +static int nfs_setsecattr(vnode_t *, vsecattr_t *, int, cred_t *, + caller_context_t *); +static int nfs_getsecattr(vnode_t *, vsecattr_t *, int, cred_t *, + caller_context_t *); +static int nfs_shrlock(vnode_t *, int, struct shrlock *, int, cred_t *, + caller_context_t *); struct vnodeops *nfs_vnodeops; @@ -233,7 +250,7 @@ nfs_getvnodeops(void) /* ARGSUSED */ static int -nfs_open(vnode_t **vpp, int flag, cred_t *cr) +nfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { int error; struct vattr va; @@ -282,8 +299,10 @@ nfs_open(vnode_t **vpp, int flag, cred_t *cr) return (error); } +/* ARGSUSED */ static int -nfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) +nfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, + caller_context_t *ct) { rnode_t *rp; int error; @@ -357,11 +376,12 @@ nfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) */ if ((flag & FWRITE) && vn_has_cached_data(vp)) { if ((VTOMI(vp)->mi_flags & MI_NOCTO)) { - error = nfs_putpage(vp, (offset_t)0, 0, B_ASYNC, cr); + error = nfs_putpage(vp, (offset_t)0, 0, B_ASYNC, + cr, ct); if (error == EAGAIN) error = 0; } else - error = nfs_putpage(vp, (offset_t)0, 0, 0, cr); + error = nfs_putpage(vp, (offset_t)0, 0, 0, cr, ct); if (!error) { mutex_enter(&rp->r_statelock); error = rp->r_error; @@ -478,10 +498,10 @@ nfs_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, * Copy data. */ error = vpm_data_copy(vp, off + on, n, uiop, - 1, NULL, 0, S_READ); + 1, NULL, 0, S_READ); } else { base = segmap_getmapflt(segkmap, vp, off + on, n, - 1, S_READ); + 1, S_READ); error = uiomove(base + on, n, UIO_READ, uiop); } @@ -679,23 +699,23 @@ nfs_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, if (segmap_kpm) { int pon = uiop->uio_loffset & PAGEOFFSET; size_t pn = MIN(PAGESIZE - pon, - uiop->uio_resid); + uiop->uio_resid); int pagecreate; mutex_enter(&rp->r_statelock); pagecreate = (pon == 0) && (pn == PAGESIZE || - uiop->uio_loffset + pn >= rp->r_size); + uiop->uio_loffset + pn >= rp->r_size); mutex_exit(&rp->r_statelock); base = segmap_getmapflt(segkmap, vp, off + on, - pn, !pagecreate, S_WRITE); + pn, !pagecreate, S_WRITE); error = writerp(rp, base + pon, n, uiop, - pagecreate); + pagecreate); } else { base = segmap_getmapflt(segkmap, vp, off + on, - n, 0, S_READ); + n, 0, S_READ); error = writerp(rp, base + on, n, uiop, 0); } } @@ -856,9 +876,10 @@ nfswrite(vnode_t *vp, caddr_t base, uint_t offset, int count, cred_t *cr) offset += tsize; if (mi->mi_io_kstats) { mutex_enter(&mi->mi_lock); - KSTAT_IO_PTR(mi->mi_io_kstats)->writes++; - KSTAT_IO_PTR(mi->mi_io_kstats)->nwritten += - tsize; + KSTAT_IO_PTR(mi->mi_io_kstats)-> + writes++; + KSTAT_IO_PTR(mi->mi_io_kstats)-> + nwritten += tsize; mutex_exit(&mi->mi_lock); } lwp_stat_update(LWP_STAT_OUBLK, 1); @@ -995,7 +1016,8 @@ nfsread(vnode_t *vp, caddr_t base, uint_t offset, int count, size_t *residp, /* ARGSUSED */ static int -nfs_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) +nfs_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp, + caller_context_t *ct) { if (nfs_zone() != VTOMI(vp)->mi_zone) @@ -1008,8 +1030,10 @@ nfs_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) } } +/* ARGSUSED */ static int -nfs_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) +nfs_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, + caller_context_t *ct) { int error; rnode_t *rp; @@ -1052,7 +1076,7 @@ nfs_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) mutex_enter(&rp->r_statelock); rp->r_gcount++; mutex_exit(&rp->r_statelock); - error = nfs_putpage(vp, (offset_t)0, 0, 0, cr); + error = nfs_putpage(vp, (offset_t)0, 0, 0, cr, ct); mutex_enter(&rp->r_statelock); if (error && (error == ENOSPC || error == EDQUOT)) { if (!rp->r_error) @@ -1096,7 +1120,7 @@ nfs_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, return (error); error = secpolicy_vnode_setattr(cr, vp, vap, &va, flags, nfs_accessx, - vp); + vp); if (error) return (error); @@ -1141,7 +1165,7 @@ nfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) rp->r_count > 0 || rp->r_mapcnt > 0)) { ASSERT(vp->v_type != VCHR); - error = nfs_putpage(vp, (offset_t)0, 0, 0, cr); + error = nfs_putpage(vp, (offset_t)0, 0, 0, cr, NULL); if (error && (error == ENOSPC || error == EDQUOT)) { mutex_enter(&rp->r_statelock); if (!rp->r_error) @@ -1324,11 +1348,12 @@ static int nfs_accessx(void *vp, int mode, cred_t *cr) { ASSERT(nfs_zone() == VTOMI((vnode_t *)vp)->mi_zone); - return (nfs_access(vp, mode, 0, cr)); + return (nfs_access(vp, mode, 0, cr, NULL)); } +/* ARGSUSED */ static int -nfs_access(vnode_t *vp, int mode, int flags, cred_t *cr) +nfs_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct) { struct vattr va; int error; @@ -1387,8 +1412,9 @@ nfs_access(vnode_t *vp, int mode, int flags, cred_t *cr) static int nfs_do_symlink_cache = 1; +/* ARGSUSED */ static int -nfs_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr) +nfs_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr, caller_context_t *ct) { int error; struct nfsrdlnres rl; @@ -1483,8 +1509,9 @@ nfs_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr) * metadata changes are not cached on the client before being * sent to the server. */ +/* ARGSUSED */ static int -nfs_fsync(vnode_t *vp, int syncflag, cred_t *cr) +nfs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) { int error; @@ -1494,7 +1521,7 @@ nfs_fsync(vnode_t *vp, int syncflag, cred_t *cr) if (nfs_zone() != VTOMI(vp)->mi_zone) return (EIO); - error = nfs_putpage(vp, (offset_t)0, 0, 0, cr); + error = nfs_putpage(vp, (offset_t)0, 0, 0, cr, ct); if (!error) error = VTOR(vp)->r_error; return (error); @@ -1506,8 +1533,9 @@ nfs_fsync(vnode_t *vp, int syncflag, cred_t *cr) * operation while it was open, it got renamed instead. Here we * remove the renamed file. */ +/* ARGSUSED */ static void -nfs_inactive(vnode_t *vp, cred_t *cr) +nfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { rnode_t *rp; @@ -1566,7 +1594,8 @@ nfs_inactive(vnode_t *vp, cred_t *cr) if (vn_has_cached_data(vp) && ((rp->r_flags & RDIRTY) || rp->r_count > 0)) { ASSERT(vp->v_type != VCHR); - error = nfs_putpage(vp, (offset_t)0, 0, 0, cr); + error = nfs_putpage(vp, (offset_t)0, 0, 0, + cr, ct); if (error) { mutex_enter(&rp->r_statelock); if (!rp->r_error) @@ -1609,9 +1638,11 @@ nfs_inactive(vnode_t *vp, cred_t *cr) * Remote file system operations having to do with directory manipulation. */ +/* ARGSUSED */ static int nfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, - int flags, vnode_t *rdir, cred_t *cr) + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp) { int error; vnode_t *vp; @@ -1734,7 +1765,7 @@ nfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, * just need to check access. */ if (strcmp(nm, ".") == 0) { - error = nfs_access(dvp, VEXEC, 0, cr); + error = nfs_access(dvp, VEXEC, 0, cr, NULL); if (error) return (error); VN_HOLD(dvp); @@ -1789,7 +1820,7 @@ nfslookup_dnlc(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr) return (error); vp = dnlc_lookup(dvp, nm); if (vp != NULL) { - error = nfs_access(dvp, VEXEC, 0, cr); + error = nfs_access(dvp, VEXEC, 0, cr, NULL); if (error) { VN_RELE(vp); return (error); @@ -1884,7 +1915,8 @@ nfslookup_otw(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, /* ARGSUSED */ static int nfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, - int mode, vnode_t **vpp, cred_t *cr, int lfaware) + int mode, vnode_t **vpp, cred_t *cr, int lfaware, caller_context_t *ct, + vsecattr_t *vsecp) { int error; struct nfscreatargs args; @@ -1925,7 +1957,7 @@ nfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, * just need to check access. */ } else if (strcmp(nm, ".") == 0) { - error = nfs_access(dvp, VEXEC, 0, cr); + error = nfs_access(dvp, VEXEC, 0, cr, ct); if (error) { nfs_rw_exit(&drp->r_rwlock); return (error); @@ -1954,7 +1986,7 @@ nfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, vp = specvp(vp, vp->v_rdev, vp->v_type, cr); VN_RELE(tempvp); } - if (!(error = VOP_ACCESS(vp, mode, 0, cr))) { + if (!(error = VOP_ACCESS(vp, mode, 0, cr, ct))) { if ((vattr.va_mask & AT_SIZE) && vp->v_type == VREG) { vattr.va_mask = AT_SIZE; @@ -1969,7 +2001,7 @@ nfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, /* * existing file got truncated, notify. */ - vnevent_create(vp); + vnevent_create(vp, ct); *vpp = vp; } return (error); @@ -2130,8 +2162,9 @@ nfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, * we rename it instead of removing it and nfs_inactive * will remove the new name. */ +/* ARGSUSED */ static int -nfs_remove(vnode_t *dvp, char *nm, cred_t *cr) +nfs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct, int flags) { int error; struct nfsdiropargs da; @@ -2184,7 +2217,7 @@ nfs_remove(vnode_t *dvp, char *nm, cred_t *cr) (rp->r_unldvp == NULL || strcmp(nm, rp->r_unlname) == 0)) { mutex_exit(&rp->r_statelock); tmpname = newname(); - error = nfsrename(dvp, nm, dvp, tmpname, cr); + error = nfsrename(dvp, nm, dvp, tmpname, cr, ct); if (error) kmem_free(tmpname, MAXNAMELEN); else { @@ -2213,7 +2246,7 @@ nfs_remove(vnode_t *dvp, char *nm, cred_t *cr) */ if (vn_has_cached_data(vp) && ((rp->r_flags & RDIRTY) || rp->r_count > 0)) { - error = nfs_putpage(vp, (offset_t)0, 0, 0, cr); + error = nfs_putpage(vp, (offset_t)0, 0, 0, cr, ct); if (error && (error == ENOSPC || error == EDQUOT)) { mutex_enter(&rp->r_statelock); if (!rp->r_error) @@ -2253,7 +2286,7 @@ nfs_remove(vnode_t *dvp, char *nm, cred_t *cr) } if (error == 0) { - vnevent_remove(vp, dvp, nm); + vnevent_remove(vp, dvp, nm, ct); } VN_RELE(vp); @@ -2262,8 +2295,10 @@ nfs_remove(vnode_t *dvp, char *nm, cred_t *cr) return (error); } +/* ARGSUSED */ static int -nfs_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) +nfs_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr, + caller_context_t *ct, int flags) { int error; struct nfslinkargs args; @@ -2274,7 +2309,7 @@ nfs_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) if (nfs_zone() != VTOMI(tdvp)->mi_zone) return (EPERM); - if (VOP_REALVP(svp, &realvp) == 0) + if (VOP_REALVP(svp, &realvp, ct) == 0) svp = realvp; args.la_from = VTOFH(svp); @@ -2310,29 +2345,32 @@ nfs_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr) /* * Notify the source file of this link operation. */ - vnevent_link(svp); + vnevent_link(svp, ct); } return (error); } +/* ARGSUSED */ static int -nfs_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) +nfs_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr, + caller_context_t *ct, int flags) { vnode_t *realvp; if (nfs_zone() != VTOMI(odvp)->mi_zone) return (EPERM); - if (VOP_REALVP(ndvp, &realvp) == 0) + if (VOP_REALVP(ndvp, &realvp, ct) == 0) ndvp = realvp; - return (nfsrename(odvp, onm, ndvp, nnm, cr)); + return (nfsrename(odvp, onm, ndvp, nnm, cr, ct)); } /* * nfsrename does the real work of renaming in NFS Version 2. */ static int -nfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) +nfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr, + caller_context_t *ct) { int error; enum nfsstat status; @@ -2465,10 +2503,10 @@ nfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) * the server removing the file completely. */ tmpname = newname(); - error = nfs_link(ndvp, nvp, tmpname, cr); + error = nfs_link(ndvp, nvp, tmpname, cr, NULL, 0); if (error == EOPNOTSUPP) { error = nfs_rename(ndvp, nnm, ndvp, tmpname, - cr); + cr, NULL, 0); } if (error) { kmem_free(tmpname, MAXNAMELEN); @@ -2570,7 +2608,7 @@ nfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) if (rp->r_unldvp != NULL) { if (strcmp(rp->r_unlname, onm) == 0) { (void) strncpy(rp->r_unlname, - nnm, MAXNAMELEN); + nnm, MAXNAMELEN); rp->r_unlname[MAXNAMELEN - 1] = '\0'; if (ndvp != rp->r_unldvp) { @@ -2595,13 +2633,13 @@ nfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) if (error == 0) { if (nvp) - vnevent_rename_dest(nvp, ndvp, nnm); + vnevent_rename_dest(nvp, ndvp, nnm, ct); if (odvp != ndvp) - vnevent_rename_dest_dir(ndvp); + vnevent_rename_dest_dir(ndvp, ct); ASSERT(ovp != NULL); - vnevent_rename_src(ovp, odvp, onm); + vnevent_rename_src(ovp, odvp, onm, ct); } if (nvp) { @@ -2615,8 +2653,10 @@ nfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr) return (error); } +/* ARGSUSED */ static int -nfs_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, cred_t *cr) +nfs_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, cred_t *cr, + caller_context_t *ct, int flags, vsecattr_t *vsecp) { int error; struct nfscreatargs args; @@ -2701,8 +2741,10 @@ nfs_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, cred_t *cr) return (error); } +/* ARGSUSED */ static int -nfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) +nfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr, + caller_context_t *ct, int flags) { int error; enum nfsstat status; @@ -2789,7 +2831,7 @@ nfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) } if (error == 0) { - vnevent_rmdir(vp, dvp, nm); + vnevent_rmdir(vp, dvp, nm, ct); } VN_RELE(vp); @@ -2798,8 +2840,10 @@ nfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr) return (error); } +/* ARGSUSED */ static int -nfs_symlink(vnode_t *dvp, char *lnm, struct vattr *tva, char *tnm, cred_t *cr) +nfs_symlink(vnode_t *dvp, char *lnm, struct vattr *tva, char *tnm, cred_t *cr, + caller_context_t *ct, int flags) { int error; struct nfsslargs args; @@ -2868,8 +2912,10 @@ static int nfs_shrinkreaddir = 0; * may return only one block's worth of entries. Entries may be compressed * on the server. */ +/* ARGSUSED */ static int -nfs_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp) +nfs_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) { int error; size_t count; @@ -2980,7 +3026,7 @@ nfs_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp) */ mutex_exit(&rp->r_statelock); (void) nfs_rw_enter_sig(&rp->r_rwlock, - RW_READER, FALSE); + RW_READER, FALSE); rddir_cache_rele(rdc); if (nrdc != NULL) rddir_cache_rele(nrdc); @@ -2988,7 +3034,7 @@ nfs_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp) } mutex_exit(&rp->r_statelock); (void) nfs_rw_enter_sig(&rp->r_rwlock, - RW_READER, FALSE); + RW_READER, FALSE); rddir_cache_rele(rdc); goto top; } @@ -3434,8 +3480,9 @@ nfs_bio(struct buf *bp, cred_t *cr) return (error); } +/* ARGSUSED */ static int -nfs_fid(vnode_t *vp, fid_t *fidp) +nfs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct) { struct nfs_fid *fp; rnode_t *rp; @@ -3486,7 +3533,7 @@ nfs_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ctp) /* ARGSUSED */ static int -nfs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) +nfs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) { /* @@ -3514,10 +3561,11 @@ static int nfs_lostpage = 0; /* number of times we lost original page */ /* * Return all the pages from [off..off+len) in file */ +/* ARGSUSED */ static int nfs_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp, page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr, - enum seg_rw rw, cred_t *cr) + enum seg_rw rw, cred_t *cr, caller_context_t *ct) { rnode_t *rp; int error; @@ -3661,8 +3709,8 @@ nfs_getapage(vnode_t *vp, u_offset_t off, size_t len, uint_t *protp, else if (blkoff == rp->r_nextr) readahead = nfs_nra; else if (rp->r_nextr > blkoff && - ((ra_window = (rp->r_nextr - blkoff) / bsize) - <= (nfs_nra - 1))) + ((ra_window = (rp->r_nextr - blkoff) / bsize) + <= (nfs_nra - 1))) readahead = nfs_nra - ra_window; else readahead = 0; @@ -3736,7 +3784,7 @@ nfs_getapage(vnode_t *vp, u_offset_t off, size_t len, uint_t *protp, } else blksize = rp->r_size - blkoff; } else if ((off == 0) || - (off != rp->r_nextr && !readahead_issued)) { + (off != rp->r_nextr && !readahead_issued)) { blksize = PAGESIZE; blkoff = off; /* block = page here */ } else @@ -3818,9 +3866,9 @@ nfs_getapage(vnode_t *vp, u_offset_t off, size_t len, uint_t *protp, } if (!readahead_issued && !error) { - mutex_enter(&rp->r_statelock); - rp->r_nextr = io_off + io_len; - mutex_exit(&rp->r_statelock); + mutex_enter(&rp->r_statelock); + rp->r_nextr = io_off + io_len; + mutex_exit(&rp->r_statelock); } } } @@ -3970,8 +4018,10 @@ nfs_readahead(vnode_t *vp, u_offset_t blkoff, caddr_t addr, struct seg *seg, * len == MAXBSIZE (from segmap_release actions), and len == PAGESIZE * (from pageout). */ +/* ARGSUSED */ static int -nfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr) +nfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr, + caller_context_t *ct) { int error; rnode_t *rp; @@ -4172,7 +4222,7 @@ nfs_sync_putapage(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, */ if (!(flags & B_ASYNC)) { error = nfs_putpage(vp, io_off, io_len, - B_INVAL | B_FORCE, cr); + B_INVAL | B_FORCE, cr, NULL); } } else { if (error) @@ -4188,9 +4238,11 @@ nfs_sync_putapage(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, return (error); } +/* ARGSUSED */ static int nfs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, - size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) + size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct) { struct segvn_crargs vn_a; int error; @@ -4292,7 +4344,8 @@ nfs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, /* ARGSUSED */ static int nfs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, - size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) + size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct) { rnode_t *rp; @@ -4315,9 +4368,10 @@ nfs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, return (0); } +/* ARGSUSED */ static int -nfs_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, - offset_t offset, struct flk_callback *flk_cbp, cred_t *cr) +nfs_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, offset_t offset, + struct flk_callback *flk_cbp, cred_t *cr, caller_context_t *ct) { netobj lm_fh; int rc; @@ -4374,7 +4428,7 @@ nfs_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, if (!lm_safelock(vp, bfp, cr)) return (EAGAIN); } - return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr)); + return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct)); } rp = VTOR(vp); @@ -4404,26 +4458,27 @@ nfs_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, if (cmd != F_GETLK) { mutex_enter(&rp->r_statelock); while (rp->r_count > 0) { - if (intr) { - klwp_t *lwp = ttolwp(curthread); + if (intr) { + klwp_t *lwp = ttolwp(curthread); - if (lwp != NULL) - lwp->lwp_nostop++; - if (cv_wait_sig(&rp->r_cv, &rp->r_statelock) == 0) { + if (lwp != NULL) + lwp->lwp_nostop++; + if (cv_wait_sig(&rp->r_cv, &rp->r_statelock) + == 0) { + if (lwp != NULL) + lwp->lwp_nostop--; + rc = EINTR; + break; + } if (lwp != NULL) lwp->lwp_nostop--; - rc = EINTR; - break; - } - if (lwp != NULL) - lwp->lwp_nostop--; - } else + } else cv_wait(&rp->r_cv, &rp->r_statelock); } mutex_exit(&rp->r_statelock); if (rc != 0) goto done; - error = nfs_putpage(vp, (offset_t)0, 0, B_INVAL, cr); + error = nfs_putpage(vp, (offset_t)0, 0, B_INVAL, cr, ct); if (error) { if (error == ENOSPC || error == EDQUOT) { mutex_enter(&rp->r_statelock); @@ -4512,7 +4567,7 @@ nfs_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, /* ARGSUSED */ static int -nfs_realvp(vnode_t *vp, vnode_t **vpp) +nfs_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct) { return (EINVAL); @@ -4531,7 +4586,8 @@ nfs_realvp(vnode_t *vp, vnode_t **vpp) /* ARGSUSED */ static int nfs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, - size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr) + size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct) { int caller_found; int error; @@ -4661,10 +4717,10 @@ nfs_delmap_callback(struct as *as, void *arg, uint_t event) if ((mi->mi_flags & MI_NOCTO) || nfs_zone() != mi->mi_zone) error = nfs_putpage(dmapp->vp, dmapp->off, dmapp->len, - B_ASYNC, dmapp->cr); + B_ASYNC, dmapp->cr, NULL); else error = nfs_putpage(dmapp->vp, dmapp->off, dmapp->len, - 0, dmapp->cr); + 0, dmapp->cr, NULL); if (!error) { mutex_enter(&rp->r_statelock); error = rp->r_error; @@ -4676,7 +4732,7 @@ nfs_delmap_callback(struct as *as, void *arg, uint_t event) if ((rp->r_flags & RDIRECTIO) || (mi->mi_flags & MI_DIRECTIO)) (void) nfs_putpage(dmapp->vp, dmapp->off, dmapp->len, - B_INVAL, dmapp->cr); + B_INVAL, dmapp->cr, NULL); dmapp->caller->error = error; (void) as_delete_callback(as, arg); @@ -4685,7 +4741,8 @@ nfs_delmap_callback(struct as *as, void *arg, uint_t event) /* ARGSUSED */ static int -nfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) +nfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, + caller_context_t *ct) { int error = 0; @@ -4748,7 +4805,7 @@ nfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) break; } return (error ? EINVAL : 0); - } + } case _PC_XATTR_EXISTS: *valp = 0; @@ -4806,9 +4863,10 @@ nfs_sync_pageio(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, return (error); } +/* ARGSUSED */ static int nfs_pageio(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, - int flags, cred_t *cr) + int flags, cred_t *cr, caller_context_t *ct) { int error; rnode_t *rp; @@ -4837,8 +4895,10 @@ nfs_pageio(vnode_t *vp, page_t *pp, u_offset_t io_off, size_t io_len, return (error); } +/* ARGSUSED */ static int -nfs_setsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr) +nfs_setsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr, + caller_context_t *ct) { int error; mntinfo_t *mi; @@ -4856,8 +4916,10 @@ nfs_setsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr) return (ENOSYS); } +/* ARGSUSED */ static int -nfs_getsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr) +nfs_getsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr, + caller_context_t *ct) { int error; mntinfo_t *mi; @@ -4872,11 +4934,13 @@ nfs_getsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr) return (error); } - return (fs_fab_acl(vp, vsecattr, flag, cr)); + return (fs_fab_acl(vp, vsecattr, flag, cr, ct)); } +/* ARGSUSED */ static int -nfs_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr) +nfs_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr, + caller_context_t *ct) { int error; struct shrlock nshr; @@ -4905,7 +4969,7 @@ nfs_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr) * request off to the local share code. */ if (VTOMI(vp)->mi_flags & MI_LLOCK) - return (fs_shrlock(vp, cmd, shr, flag, cr)); + return (fs_shrlock(vp, cmd, shr, flag, cr, ct)); switch (cmd) { case F_SHARE: diff --git a/usr/src/uts/common/fs/objfs/objfs_common.c b/usr/src/uts/common/fs/objfs/objfs_common.c index 35252c0d91c5..bdce50353ed3 100644 --- a/usr/src/uts/common/fs/objfs/objfs_common.c +++ b/usr/src/uts/common/fs/objfs/objfs_common.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -37,7 +36,8 @@ */ /* ARGSUSED */ int -objfs_dir_open(vnode_t **cpp, int flag, cred_t *cr) +objfs_dir_open(vnode_t **cpp, int flag, cred_t *cr, + caller_context_t *ct) { if ((flag & (FOFFMAX | FWRITE)) != FOFFMAX) return (EINVAL); @@ -50,17 +50,19 @@ objfs_dir_open(vnode_t **cpp, int flag, cred_t *cr) */ /* ARGSUSED */ int -objfs_common_close(vnode_t *vp, int flag, int count, offset_t off, cred_t *cr) +objfs_common_close(vnode_t *vp, int flag, int count, offset_t off, cred_t *cr, + caller_context_t *ct) { return (0); } /* - * For directories, ensure we're not open for writting. + * For directories, ensure we're not open for writing. */ /* ARGSUSED */ int -objfs_dir_access(vnode_t *vp, int mode, int flags, cred_t *cr) +objfs_dir_access(vnode_t *vp, int mode, int flags, cred_t *cr, + caller_context_t *ct) { if (mode & VWRITE) return (EACCES); diff --git a/usr/src/uts/common/fs/objfs/objfs_data.c b/usr/src/uts/common/fs/objfs/objfs_data.c index 4e050112fbd8..6777cd469e06 100644 --- a/usr/src/uts/common/fs/objfs/objfs_data.c +++ b/usr/src/uts/common/fs/objfs/objfs_data.c @@ -455,7 +455,8 @@ objfs_create_data(vnode_t *pvp) /* ARGSUSED */ static int -objfs_data_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +objfs_data_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { struct module *mp; timestruc_t now; @@ -480,7 +481,8 @@ objfs_data_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) /* ARGSUSED */ static int -objfs_data_access(vnode_t *vp, int mode, int flags, cred_t *cr) +objfs_data_access(vnode_t *vp, int mode, int flags, cred_t *cr, + caller_context_t *ct) { if (mode & (VWRITE|VEXEC)) return (EACCES); @@ -490,7 +492,8 @@ objfs_data_access(vnode_t *vp, int mode, int flags, cred_t *cr) /* ARGSUSED */ int -objfs_data_open(vnode_t **cpp, int flag, cred_t *cr) +objfs_data_open(vnode_t **cpp, int flag, cred_t *cr, + caller_context_t *ct) { if (flag & FWRITE) return (EINVAL); @@ -556,7 +559,7 @@ read_symtab(void *addr, size_t size, off_t offset, uio_t *uio) /* ARGSUSED */ static int objfs_data_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, - caller_context_t *ct) + caller_context_t *ct) { int error = 0; objfs_datanode_t *dnode = vp->v_data; @@ -745,7 +748,8 @@ objfs_data_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, /* ARGSUSED */ static int -objfs_data_seek(vnode_t *vp, offset_t off, offset_t *offp) +objfs_data_seek(vnode_t *vp, offset_t off, offset_t *offp, + caller_context_t *ct) { return (0); } diff --git a/usr/src/uts/common/fs/objfs/objfs_odir.c b/usr/src/uts/common/fs/objfs/objfs_odir.c index a7ef0ed50285..9558061e83c9 100644 --- a/usr/src/uts/common/fs/objfs/objfs_odir.c +++ b/usr/src/uts/common/fs/objfs/objfs_odir.c @@ -63,7 +63,8 @@ objfs_create_odirnode(vnode_t *pvp, struct modctl *mp) /* ARGSUSED */ static int -objfs_odir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +objfs_odir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { timestruc_t now; diff --git a/usr/src/uts/common/fs/objfs/objfs_root.c b/usr/src/uts/common/fs/objfs/objfs_root.c index 1e9bb8c1f033..06f06d35f974 100644 --- a/usr/src/uts/common/fs/objfs/objfs_root.c +++ b/usr/src/uts/common/fs/objfs/objfs_root.c @@ -38,7 +38,8 @@ extern int last_module_id; -static int objfs_root_do_lookup(vnode_t *, const char *, vnode_t **, ino64_t *); +static int objfs_root_do_lookup(vnode_t *, const char *, vnode_t **, ino64_t *, + cred_t *); static int objfs_root_do_readdir(vnode_t *, struct dirent64 *, int *, offset_t *, offset_t *, void *); @@ -54,7 +55,8 @@ objfs_create_root(vfs_t *vfsp) /* ARGSUSED */ static int -objfs_root_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +objfs_root_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { vap->va_type = VDIR; vap->va_mode = 0555; @@ -67,8 +69,10 @@ objfs_root_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) return (objfs_common_getattr(vp, vap)); } +/* ARGSUSED */ static int -objfs_root_do_lookup(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop) +objfs_root_do_lookup(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop, + cred_t *cr) { int result = ENOENT; struct modctl *mp; @@ -148,11 +152,12 @@ objfs_root_do_readdir(vnode_t *vp, struct dirent64 *dp, int *eofp, /* ARGSUSED */ static int -objfs_root_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp) +objfs_root_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) { struct modctl *mp = &modules; - return (gfs_dir_readdir(vp, uiop, eofp, &mp)); + return (gfs_dir_readdir(vp, uiop, eofp, &mp, cr, ct)); } const fs_operation_def_t objfs_tops_root[] = { diff --git a/usr/src/uts/common/fs/pathname.c b/usr/src/uts/common/fs/pathname.c index 904477f3a41f..82d2e369736c 100644 --- a/usr/src/uts/common/fs/pathname.c +++ b/usr/src/uts/common/fs/pathname.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -210,7 +209,7 @@ pn_getsymlink(vnode_t *vp, struct pathname *pnp, cred_t *crp) auio.uio_segflg = UIO_SYSSPACE; auio.uio_extflg = UIO_COPY_CACHED; auio.uio_resid = pnp->pn_bufsize; - if ((error = VOP_READLINK(vp, &auio, crp)) == 0) { + if ((error = VOP_READLINK(vp, &auio, crp, NULL)) == 0) { pnp->pn_pathlen = pnp->pn_bufsize - auio.uio_resid; if (pnp->pn_pathlen == pnp->pn_bufsize) error = ENAMETOOLONG; diff --git a/usr/src/uts/common/fs/pcfs/pc_dir.c b/usr/src/uts/common/fs/pcfs/pc_dir.c index 616efe23cb4b..cc6d1a8fe4bc 100644 --- a/usr/src/uts/common/fs/pcfs/pc_dir.c +++ b/usr/src/uts/common/fs/pcfs/pc_dir.c @@ -387,7 +387,8 @@ pc_dirremove( struct pcnode *dp, char *namep, struct vnode *cdir, - enum vtype type) + enum vtype type, + caller_context_t *ctp) { struct pcslot slot; struct pcnode *pcp; @@ -467,9 +468,9 @@ pc_dirremove( if (error == 0) { if (type == VDIR) { - vnevent_rmdir(PCTOV(pcp), vp, namep); + vnevent_rmdir(PCTOV(pcp), vp, namep, ctp); } else { - vnevent_remove(PCTOV(pcp), vp, namep); + vnevent_remove(PCTOV(pcp), vp, namep, ctp); } } @@ -556,7 +557,8 @@ pc_rename( struct pcnode *dp, /* parent directory */ struct pcnode *tdp, /* target directory */ char *snm, /* source file name */ - char *tnm) /* target file name */ + char *tnm, /* target file name */ + caller_context_t *ctp) { struct pcnode *pcp; /* pcnode we are trying to rename */ struct pcnode *tpcp; /* pcnode that's in our way */ @@ -633,7 +635,7 @@ pc_rename( newisdir = tpcp->pc_entry.pcd_attr & PCA_DIR; brelse(slot.sl_bp); - vnevent_rename_dest(PCTOV(tpcp), PCTOV(tdp), tnm); + vnevent_rename_dest(PCTOV(tpcp), PCTOV(tdp), tnm, ctp); VN_RELE(PCTOV(tpcp)); /* @@ -648,7 +650,7 @@ pc_rename( } else { /* nondir/nondir, remove target */ error = pc_dirremove(tdp, tnm, - (struct vnode *)NULL, VREG); + (struct vnode *)NULL, VREG, ctp); if (error == 0) { VN_RELE(PCTOV(pcp)); goto top; @@ -657,7 +659,7 @@ pc_rename( } else if (oldisdir) { /* dir/dir, remove target */ error = pc_dirremove(tdp, tnm, - (struct vnode *)NULL, VDIR); + (struct vnode *)NULL, VDIR, ctp); if (error == 0) { VN_RELE(PCTOV(pcp)); goto top; @@ -815,9 +817,9 @@ pc_rename( } } out: - vnevent_rename_src(PCTOV(pcp), PCTOV(dp), snm); + vnevent_rename_src(PCTOV(pcp), PCTOV(dp), snm, ctp); if (dp != tdp) { - vnevent_rename_dest_dir(PCTOV(tdp)); + vnevent_rename_dest_dir(PCTOV(tdp), ctp); } VN_RELE(PCTOV(pcp)); diff --git a/usr/src/uts/common/fs/pcfs/pc_node.c b/usr/src/uts/common/fs/pcfs/pc_node.c index 6dbc28dbbae7..33d6fd2659ff 100644 --- a/usr/src/uts/common/fs/pcfs/pc_node.c +++ b/usr/src/uts/common/fs/pcfs/pc_node.c @@ -207,7 +207,8 @@ syncpcp(struct pcnode *pcp, int flags) if (!vn_has_cached_data(PCTOV(pcp))) err = 0; else - err = VOP_PUTPAGE(PCTOV(pcp), 0, 0, flags, kcred); + err = VOP_PUTPAGE(PCTOV(pcp), 0, 0, flags, + kcred, NULL); return (err); } diff --git a/usr/src/uts/common/fs/pcfs/pc_vfsops.c b/usr/src/uts/common/fs/pcfs/pc_vfsops.c index 378fb159547a..3697041c88d2 100644 --- a/usr/src/uts/common/fs/pcfs/pc_vfsops.c +++ b/usr/src/uts/common/fs/pcfs/pc_vfsops.c @@ -415,7 +415,7 @@ pcfs_device_identify( error = ENXIO; if ((error != 0) || - (error = VOP_ACCESS(bvp, aflag, 0, cr)) != 0 || + (error = VOP_ACCESS(bvp, aflag, 0, cr, NULL)) != 0 || (error = secpolicy_spec_open(cr, bvp, oflag)) != 0) { VN_RELE(bvp); return (error); @@ -695,7 +695,7 @@ pcfs_mount( return (EBUSY); } error = VOP_OPEN(&devvp, - (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr); + (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr, NULL); if (error) { VN_RELE(devvp); return (error); @@ -770,7 +770,7 @@ pcfs_mount( errout: (void) VOP_CLOSE(devvp, vfsp->vfs_flag & VFS_RDONLY ? FREAD : FREAD | FWRITE, - 1, (offset_t)0, cr); + 1, (offset_t)0, cr, NULL); VN_RELE(devvp); mutex_destroy(&fsp->pcfs_lock); kmem_free(fsp, sizeof (*fsp)); @@ -1100,7 +1100,7 @@ pc_invalfat(struct pcfs *fsp) */ (void) VOP_CLOSE(fsp->pcfs_devvp, (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, - 1, (offset_t)0, CRED()); + 1, (offset_t)0, CRED(), NULL); } void diff --git a/usr/src/uts/common/fs/pcfs/pc_vnops.c b/usr/src/uts/common/fs/pcfs/pc_vnops.c index 0becbc9b3c2a..9f77542636aa 100644 --- a/usr/src/uts/common/fs/pcfs/pc_vnops.c +++ b/usr/src/uts/common/fs/pcfs/pc_vnops.c @@ -69,45 +69,57 @@ #include -static int pcfs_open(struct vnode **, int, struct cred *); -static int pcfs_close(struct vnode *, int, int, offset_t, struct cred *); +static int pcfs_open(struct vnode **, int, struct cred *, caller_context_t *ct); +static int pcfs_close(struct vnode *, int, int, offset_t, struct cred *, + caller_context_t *ct); static int pcfs_read(struct vnode *, struct uio *, int, struct cred *, - struct caller_context *); + caller_context_t *); static int pcfs_write(struct vnode *, struct uio *, int, struct cred *, - struct caller_context *); -static int pcfs_getattr(struct vnode *, struct vattr *, int, struct cred *); + caller_context_t *); +static int pcfs_getattr(struct vnode *, struct vattr *, int, struct cred *, + caller_context_t *ct); static int pcfs_setattr(struct vnode *, struct vattr *, int, struct cred *, caller_context_t *); -static int pcfs_access(struct vnode *, int, int, struct cred *); +static int pcfs_access(struct vnode *, int, int, struct cred *, + caller_context_t *ct); static int pcfs_lookup(struct vnode *, char *, struct vnode **, - struct pathname *, int, struct vnode *, struct cred *); + struct pathname *, int, struct vnode *, struct cred *, + caller_context_t *, int *, pathname_t *); static int pcfs_create(struct vnode *, char *, struct vattr *, - enum vcexcl, int mode, struct vnode **, struct cred *, int); -static int pcfs_remove(struct vnode *, char *, struct cred *); + enum vcexcl, int mode, struct vnode **, struct cred *, int, + caller_context_t *, vsecattr_t *); +static int pcfs_remove(struct vnode *, char *, struct cred *, + caller_context_t *, int); static int pcfs_rename(struct vnode *, char *, struct vnode *, char *, - struct cred *); + struct cred *, caller_context_t *, int); static int pcfs_mkdir(struct vnode *, char *, struct vattr *, struct vnode **, - struct cred *); -static int pcfs_rmdir(struct vnode *, char *, struct vnode *, struct cred *); -static int pcfs_readdir(struct vnode *, struct uio *, struct cred *, int *); -static int pcfs_fsync(struct vnode *, int, struct cred *); -static void pcfs_inactive(struct vnode *, struct cred *); -static int pcfs_fid(struct vnode *vp, struct fid *fidp); + struct cred *, caller_context_t *, int, vsecattr_t *); +static int pcfs_rmdir(struct vnode *, char *, struct vnode *, struct cred *, + caller_context_t *, int); +static int pcfs_readdir(struct vnode *, struct uio *, struct cred *, int *, + caller_context_t *, int); +static int pcfs_fsync(struct vnode *, int, struct cred *, caller_context_t *); +static void pcfs_inactive(struct vnode *, struct cred *, caller_context_t *); +static int pcfs_fid(struct vnode *vp, struct fid *fidp, caller_context_t *); static int pcfs_space(struct vnode *, int, struct flock64 *, int, offset_t, cred_t *, caller_context_t *); static int pcfs_getpage(struct vnode *, offset_t, size_t, uint_t *, page_t *[], - size_t, struct seg *, caddr_t, enum seg_rw, struct cred *); + size_t, struct seg *, caddr_t, enum seg_rw, struct cred *, + caller_context_t *); static int pcfs_getapage(struct vnode *, u_offset_t, size_t, uint_t *, page_t *[], size_t, struct seg *, caddr_t, enum seg_rw, struct cred *); -static int pcfs_putpage(struct vnode *, offset_t, size_t, int, struct cred *); +static int pcfs_putpage(struct vnode *, offset_t, size_t, int, struct cred *, + caller_context_t *); static int pcfs_map(struct vnode *, offset_t, struct as *, caddr_t *, size_t, - uchar_t, uchar_t, uint_t, struct cred *); + uchar_t, uchar_t, uint_t, struct cred *, caller_context_t *); static int pcfs_addmap(struct vnode *, offset_t, struct as *, caddr_t, - size_t, uchar_t, uchar_t, uint_t, struct cred *); + size_t, uchar_t, uchar_t, uint_t, struct cred *, caller_context_t *); static int pcfs_delmap(struct vnode *, offset_t, struct as *, caddr_t, - size_t, uint_t, uint_t, uint_t, struct cred *); -static int pcfs_seek(struct vnode *, offset_t, offset_t *); -static int pcfs_pathconf(struct vnode *, int, ulong_t *, struct cred *); + size_t, uint_t, uint_t, uint_t, struct cred *, caller_context_t *); +static int pcfs_seek(struct vnode *, offset_t, offset_t *, + caller_context_t *); +static int pcfs_pathconf(struct vnode *, int, ulong_t *, struct cred *, + caller_context_t *); int pcfs_putapage(struct vnode *, page_t *, u_offset_t *, size_t *, int, struct cred *); @@ -175,7 +187,8 @@ static int pcfs_open( struct vnode **vpp, int flag, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { return (0); } @@ -191,7 +204,8 @@ pcfs_close( int flag, int count, offset_t offset, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { return (0); } @@ -537,7 +551,8 @@ pcfs_getattr( struct vnode *vp, struct vattr *vap, int flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct pcnode *pcp; struct pcfs *fsp; @@ -556,7 +571,7 @@ pcfs_getattr( /* * Note that we don't check for "invalid node" (PC_INVAL) here * only in order to make stat() succeed. We allow no I/O on such - * a node, but do allow to check for its existance. + * a node, but do allow to check for its existence. */ if ((pcp = VTOPC(vp)) == NULL) { pc_unlockfs(fsp); @@ -816,7 +831,8 @@ pcfs_access( struct vnode *vp, int mode, int flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct pcnode *pcp; struct pcfs *fsp; @@ -847,7 +863,8 @@ static int pcfs_fsync( struct vnode *vp, int syncflag, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct pcfs *fsp; struct pcnode *pcp; @@ -875,7 +892,8 @@ pcfs_fsync( static void pcfs_inactive( struct vnode *vp, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct pcnode *pcp; struct pcfs *fsp; @@ -946,7 +964,10 @@ pcfs_lookup( struct pathname *pnp, int flags, struct vnode *rdir, - struct cred *cr) + struct cred *cr, + caller_context_t *ct, + int *direntflags, + pathname_t *realpnp) { struct pcfs *fsp; struct pcnode *pcp; @@ -1001,7 +1022,9 @@ pcfs_create( int mode, struct vnode **vpp, struct cred *cr, - int flag) + int flag, + caller_context_t *ct, + vsecattr_t *vsecp) { int error; struct pcnode *pcp; @@ -1055,7 +1078,7 @@ pcfs_create( error = EISDIR; } else if (mode) { error = pcfs_access(PCTOV(pcp), mode, 0, - cr); + cr, ct); } else { error = 0; } @@ -1068,7 +1091,7 @@ pcfs_create( if (error) { VN_RELE(PCTOV(pcp)); } else { - vnevent_create(PCTOV(pcp)); + vnevent_create(PCTOV(pcp), ct); } } } @@ -1087,7 +1110,9 @@ static int pcfs_remove( struct vnode *vp, char *nm, - struct cred *cr) + struct cred *cr, + caller_context_t *ct, + int flags) { struct pcfs *fsp; struct pcnode *pcp; @@ -1109,7 +1134,7 @@ pcfs_remove( return (EACCES); } } - error = pc_dirremove(pcp, nm, (struct vnode *)0, VREG); + error = pc_dirremove(pcp, nm, (struct vnode *)0, VREG, ct); pc_unlockfs(fsp); return (error); } @@ -1126,7 +1151,9 @@ pcfs_rename( char *snm, /* old (source) entry name */ struct vnode *tdvp, /* new (target) parent vnode */ char *tnm, /* new (target) entry name */ - struct cred *cr) + struct cred *cr, + caller_context_t *ct, + int flags) { struct pcfs *fsp; struct pcnode *dp; /* parent pcnode */ @@ -1140,7 +1167,7 @@ pcfs_rename( /* * make sure we can muck with this directory. */ - error = pcfs_access(sdvp, VWRITE, 0, cr); + error = pcfs_access(sdvp, VWRITE, 0, cr, ct); if (error) { return (error); } @@ -1152,7 +1179,7 @@ pcfs_rename( pc_unlockfs(fsp); return (EIO); } - error = pc_rename(dp, tdp, snm, tnm); + error = pc_rename(dp, tdp, snm, tnm, ct); pc_unlockfs(fsp); return (error); } @@ -1164,7 +1191,10 @@ pcfs_mkdir( char *nm, struct vattr *vap, struct vnode **vpp, - struct cred *cr) + struct cred *cr, + caller_context_t *ct, + int flags, + vsecattr_t *vsecp) { struct pcfs *fsp; struct pcnode *pcp; @@ -1206,7 +1236,9 @@ pcfs_rmdir( struct vnode *dvp, char *nm, struct vnode *cdir, - struct cred *cr) + struct cred *cr, + caller_context_t *ct, + int flags) { struct pcfs *fsp; struct pcnode *pcp; @@ -1230,7 +1262,7 @@ pcfs_rmdir( } } - error = pc_dirremove(pcp, nm, cdir, VDIR); + error = pc_dirremove(pcp, nm, cdir, VDIR, ct); pc_unlockfs(fsp); return (error); } @@ -1246,7 +1278,9 @@ pcfs_readdir( struct vnode *dvp, struct uio *uiop, struct cred *cr, - int *eofp) + int *eofp, + caller_context_t *ct, + int flags) { struct pcnode *pcp; struct pcfs *fsp; @@ -1517,6 +1551,7 @@ pcfs_getapage( /* * Return all the pages from [off..off+len] in given file */ +/* ARGSUSED */ static int pcfs_getpage( struct vnode *vp, @@ -1528,7 +1563,8 @@ pcfs_getpage( struct seg *seg, caddr_t addr, enum seg_rw rw, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct pcfs *fsp = VFSTOPCFS(vp->v_vfsp); int err; @@ -1574,7 +1610,8 @@ pcfs_putpage( offset_t off, size_t len, int flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct pcnode *pcp; page_t *pp; @@ -1814,7 +1851,8 @@ pcfs_map( uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct segvn_crargs vn_a; int error; @@ -1861,7 +1899,8 @@ static int pcfs_seek( struct vnode *vp, offset_t ooff, - offset_t *noffp) + offset_t *noffp, + caller_context_t *ct) { if (*noffp < 0) return (EINVAL); @@ -1882,7 +1921,8 @@ pcfs_addmap( uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { if (vp->v_flag & VNOMAP) return (ENOSYS); @@ -1900,7 +1940,8 @@ pcfs_delmap( uint_t prot, uint_t maxprot, uint_t flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { if (vp->v_flag & VNOMAP) return (ENOSYS); @@ -1916,7 +1957,8 @@ pcfs_pathconf( struct vnode *vp, int cmd, ulong_t *valp, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { ulong_t val; int error = 0; @@ -2018,7 +2060,7 @@ pcfs_space( return (EINVAL); vattr.va_mask = AT_SIZE; vattr.va_size = bfp->l_start; - error = VOP_SETATTR(vp, &vattr, 0, cr, ct); + error = VOP_SETATTR(vp, (vattr_t *)&vattr, 0, cr, ct); } return (error); } @@ -2373,8 +2415,9 @@ pc_read_short_fn(struct vnode *dvp, struct uio *uiop, struct pc_dirent *ld, return (0); } +/* ARGSUSED */ static int -pcfs_fid(struct vnode *vp, struct fid *fidp) +pcfs_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct) { struct pc_fid *pcfid; struct pcnode *pcp; diff --git a/usr/src/uts/common/fs/portfs/port_fd.c b/usr/src/uts/common/fs/portfs/port_fd.c index dd26b0af9cb6..c1f868ad117a 100644 --- a/usr/src/uts/common/fs/portfs/port_fd.c +++ b/usr/src/uts/common/fs/portfs/port_fd.c @@ -171,7 +171,7 @@ port_cache_lookup_fp(port_fdcache_t *pcp, int fd, file_t *fp) * will be submitted to the event port with port_send_event(). * Otherwise VOP_POLL does not return events but it delivers a pointer to a * pollhead_t structure. In such a case the corresponding file system behind - * VOP_POLL will use the pollwakeup() function to notify about exisiting + * VOP_POLL will use the pollwakeup() function to notify about existing * events. */ int @@ -314,7 +314,7 @@ port_associate_fd(port_t *pp, int source, uintptr_t object, int events, * poll routine to change. */ curthread->t_pollcache = (pollcache_t *)pcp; - error = VOP_POLL(fp->f_vnode, events, 0, &revents, &php); + error = VOP_POLL(fp->f_vnode, events, 0, &revents, &php, NULL); curthread->t_pollcache = NULL; /* @@ -512,7 +512,7 @@ port_bind_pollhead(pollhead_t **php, polldat_t *pdp, short *revents) pdp->pd_php = *php; fp = pdp->pd_fp; curthread->t_pollcache = (pollcache_t *)pdp->pd_pcache; - error = VOP_POLL(fp->f_vnode, pdp->pd_events, 0, revents, php); + error = VOP_POLL(fp->f_vnode, pdp->pd_events, 0, revents, php, NULL); curthread->t_pollcache = NULL; return (error); } diff --git a/usr/src/uts/common/fs/portfs/port_fop.c b/usr/src/uts/common/fs/portfs/port_fop.c index 8e07046236eb..d3530280d21c 100644 --- a/usr/src/uts/common/fs/portfs/port_fop.c +++ b/usr/src/uts/common/fs/portfs/port_fop.c @@ -57,7 +57,7 @@ * The time stamps collected by a stat(2) call are passed in fo_atime, * fo_mtime, fo_ctime. At the time a file events watch is registered, the * time stamps passed in are compared with the current time stamps of the - * file. If it has changed, relavant events are sent immediately. If the time + * file. If it has changed, relevant events are sent immediately. If the time * stamps are all '0', they will not be compared. * * @@ -206,33 +206,40 @@ static void port_close_fop(void *arg, int port, pid_t pid, int lastclose); /* * port fop functions that will be the fem hooks. */ -static int port_fop_open(femarg_t *vf, int mode, cred_t *cr); +static int port_fop_open(femarg_t *vf, int mode, cred_t *cr, + caller_context_t *); static int port_fop_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr, - struct caller_context *ct); + struct caller_context *ct); static int port_fop_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr, - caller_context_t *ct); + caller_context_t *ct); static int port_fop_map(femarg_t *vf, offset_t off, struct as *as, - caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxport, - uint_t flags, cred_t *cr); + caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxport, + uint_t flags, cred_t *cr, caller_context_t *ct); static int port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr, - caller_context_t *ct); + caller_context_t *ct); static int port_fop_create(femarg_t *vf, char *name, vattr_t *vap, - vcexcl_t excl, int mode, vnode_t **vpp, cred_t *cr, - int flag); -static int port_fop_remove(femarg_t *vf, char *nm, cred_t *cr); -static int port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr); + vcexcl_t excl, int mode, vnode_t **vpp, cred_t *cr, int flag, + caller_context_t *ct, vsecattr_t *vsecp); +static int port_fop_remove(femarg_t *vf, char *nm, cred_t *cr, + caller_context_t *ct, int flags); +static int port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr, + caller_context_t *ct, int flags); static int port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, - cred_t *cr); + cred_t *cr, caller_context_t *ct, int flags); static int port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, - vnode_t **vpp, cred_t *cr); -static int port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr); -static int port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp); + vnode_t **vpp, cred_t *cr, caller_context_t *ct, int flags, + vsecattr_t *vsecp); +static int port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr, + caller_context_t *ct, int flags); +static int port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags); static int port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap, - char *target, cred_t *cr); + char *target, cred_t *cr, caller_context_t *ct, int flags); static int port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, - cred_t *cr); + cred_t *cr, caller_context_t *ct); + static int port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, - char *cname); + char *cname, caller_context_t *ct); static int port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr); @@ -812,7 +819,7 @@ port_check_timestamp(vnode_t *vp, portfop_t *pfp, void *objptr) if (fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec || fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec || fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) { - if (VOP_GETATTR(vp, &vatt, 0, CRED())) { + if (VOP_GETATTR(vp, &vatt, 0, CRED(), NULL)) { return; } } else { @@ -828,7 +835,7 @@ port_check_timestamp(vnode_t *vp, portfop_t *pfp, void *objptr) if (fobj32->fo_atime.tv_sec || fobj32->fo_atime.tv_nsec || fobj32->fo_mtime.tv_sec || fobj32->fo_mtime.tv_nsec || fobj32->fo_ctime.tv_sec || fobj32->fo_ctime.tv_nsec) { - if (VOP_GETATTR(vp, &vatt, 0, CRED())) { + if (VOP_GETATTR(vp, &vatt, 0, CRED(), NULL)) { return; } } else { @@ -1182,7 +1189,7 @@ port_resolve_vp(vnode_t *vp) * This should take care of lofs mounted fs systems and nfs4 * hardlinks. */ - if ((VOP_REALVP(vp, &rvp) == 0) && vp != rvp) { + if ((VOP_REALVP(vp, &rvp, NULL) == 0) && vp != rvp) { VN_HOLD(rvp); VN_RELE(vp); vp = rvp; @@ -1263,7 +1270,7 @@ port_associate_fop(port_t *pp, int source, uintptr_t object, int events, vp = port_resolve_vp(vp); - if (vp != NULL && vnevent_support(vp)) { + if (vp != NULL && vnevent_support(vp, NULL)) { error = ENOTSUP; goto errout; } @@ -1547,7 +1554,7 @@ port_close_fop(void *arg, int port, pid_t pid, int lastclose) /* * Given the list of associations(watches), it will send exception events, * if still active, and discard them. The exception events are handled - * seperately because, the pfp needs to be removed from the port cache and + * separately because, the pfp needs to be removed from the port cache and * freed as the vnode's identity is changing or being removed. To remove * the pfp from the port's cache, we need to hold the cache lock (pfc_lock). * The lock order is pfc_lock -> pvp_mutex(vnode's) mutex and that is why @@ -1809,7 +1816,7 @@ port_fop(vnode_t *vp, int op, int retval) return; /* - * These events occuring on the watched file. + * These events occurring on the watched file. */ if (op & FOP_MODIFIED_MASK) { event = FILE_MODIFIED; @@ -1907,12 +1914,12 @@ port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr) * The O_TRUNC operation is caught with the VOP_SETATTR(AT_SIZE) call. */ static int -port_fop_open(femarg_t *vf, int mode, cred_t *cr) +port_fop_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct) { int retval; vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available; - retval = vnext_open(vf, mode, cr); + retval = vnext_open(vf, mode, cr, ct); port_fop(vp, FOP_FILE_OPEN, retval); return (retval); } @@ -1931,12 +1938,14 @@ port_fop_write(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr, static int port_fop_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp, - size_t len, uchar_t prot, uchar_t maxport, uint_t flags, cred_t *cr) + size_t len, uchar_t prot, uchar_t maxport, uint_t flags, cred_t *cr, + caller_context_t *ct) { int retval; vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available; - retval = vnext_map(vf, off, as, addrp, len, prot, maxport, flags, cr); + retval = vnext_map(vf, off, as, addrp, len, prot, maxport, + flags, cr, ct); port_fop(vp, FOP_FILE_MAP, retval); return (retval); } @@ -1980,7 +1989,8 @@ port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr, int port_fop_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl, - int mode, vnode_t **vpp, cred_t *cr, int flag) + int mode, vnode_t **vpp, cred_t *cr, int flag, + caller_context_t *ct, vsecattr_t *vsecp) { int retval, got = 1; vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available; @@ -1993,13 +2003,14 @@ port_fop_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl, * file was actually created. */ vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME; - if (VOP_GETATTR(vp, &vatt, 0, CRED())) { + if (VOP_GETATTR(vp, &vatt, 0, CRED(), ct)) { got = 0; } - retval = vnext_create(vf, name, vap, excl, mode, vpp, cr, flag); + retval = vnext_create(vf, name, vap, excl, mode, vpp, cr, + flag, ct, vsecp); vatt1.va_mask = AT_ATIME|AT_MTIME|AT_CTIME; - if (got && !VOP_GETATTR(vp, &vatt1, 0, CRED())) { + if (got && !VOP_GETATTR(vp, &vatt1, 0, CRED(), ct)) { if ((vatt1.va_mtime.tv_sec > vatt.va_mtime.tv_sec || (vatt1.va_mtime.tv_sec = vatt.va_mtime.tv_sec && vatt1.va_mtime.tv_nsec > vatt.va_mtime.tv_nsec))) { @@ -2013,23 +2024,25 @@ port_fop_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl, } int -port_fop_remove(femarg_t *vf, char *nm, cred_t *cr) +port_fop_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct, + int flags) { int retval; vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available; - retval = vnext_remove(vf, nm, cr); + retval = vnext_remove(vf, nm, cr, ct, flags); port_fop(vp, FOP_FILE_REMOVE, retval); return (retval); } int -port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr) +port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr, + caller_context_t *ct, int flags) { int retval; vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available; - retval = vnext_link(vf, svp, tnm, cr); + retval = vnext_link(vf, svp, tnm, cr, ct, flags); port_fop(vp, FOP_FILE_LINK, retval); return (retval); } @@ -2041,58 +2054,61 @@ port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr) * if the source dir != target dir. */ int -port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) +port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr, + caller_context_t *ct, int flags) { int retval; vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available; - retval = vnext_rename(vf, snm, tdvp, tnm, cr); + retval = vnext_rename(vf, snm, tdvp, tnm, cr, ct, flags); port_fop(vp, FOP_FILE_RENAMESRC, retval); return (retval); } int port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, vnode_t **vpp, - cred_t *cr) + cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp) { int retval; vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available; - retval = vnext_mkdir(vf, dirname, vap, vpp, cr); + retval = vnext_mkdir(vf, dirname, vap, vpp, cr, ct, flags, vsecp); port_fop(vp, FOP_FILE_MKDIR, retval); return (retval); } int -port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr) +port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr, + caller_context_t *ct, int flags) { int retval; vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available; - retval = vnext_rmdir(vf, nm, cdir, cr); + retval = vnext_rmdir(vf, nm, cdir, cr, ct, flags); port_fop(vp, FOP_FILE_RMDIR, retval); return (retval); } int -port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp) +port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) { int retval; vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available; - retval = vnext_readdir(vf, uiop, cr, eofp); + retval = vnext_readdir(vf, uiop, cr, eofp, ct, flags); port_fop(vp, FOP_FILE_READDIR, retval); return (retval); } int port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target, - cred_t *cr) + cred_t *cr, caller_context_t *ct, int flags) { int retval; vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available; - retval = vnext_symlink(vf, linkname, vap, target, cr); + retval = vnext_symlink(vf, linkname, vap, target, cr, ct, flags); port_fop(vp, FOP_FILE_SYMLINK, retval); return (retval); } @@ -2101,11 +2117,12 @@ port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target, * acl, facl call this. */ int -port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flags, cred_t *cr) +port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flags, cred_t *cr, + caller_context_t *ct) { int retval; vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available; - retval = vnext_setsecattr(vf, vsap, flags, cr); + retval = vnext_setsecattr(vf, vsap, flags, cr, ct); port_fop(vp, FOP_FILE_SETSECATTR, retval); return (retval); } @@ -2114,11 +2131,11 @@ port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flags, cred_t *cr) * these are events on the watched file/directory */ int -port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name) +port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name, + caller_context_t *ct) { vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available; - switch (vnevent) { case VE_RENAME_SRC: port_fop_sendevent(vp, FILE_RENAME_FROM, dvp, name); @@ -2151,5 +2168,5 @@ port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name) default: break; } - return (vnext_vnevent(vf, vnevent, dvp, name)); + return (vnext_vnevent(vf, vnevent, dvp, name, ct)); } diff --git a/usr/src/uts/common/fs/portfs/port_vnops.c b/usr/src/uts/common/fs/portfs/port_vnops.c index aa8bc952d20b..00c1044dfaa9 100644 --- a/usr/src/uts/common/fs/portfs/port_vnops.c +++ b/usr/src/uts/common/fs/portfs/port_vnops.c @@ -36,13 +36,16 @@ #include /* local functions */ -static int port_open(struct vnode **, int, cred_t *); -static int port_close(struct vnode *, int, int, offset_t, cred_t *); -static int port_getattr(struct vnode *, struct vattr *, int, cred_t *); -static int port_access(struct vnode *, int, int, cred_t *); -static int port_realvp(vnode_t *, vnode_t **); -static int port_poll(vnode_t *, short, int, short *, struct pollhead **); -static void port_inactive(struct vnode *, cred_t *); +static int port_open(struct vnode **, int, cred_t *, caller_context_t *); +static int port_close(struct vnode *, int, int, offset_t, cred_t *, + caller_context_t *); +static int port_getattr(struct vnode *, struct vattr *, int, cred_t *, + caller_context_t *); +static int port_access(struct vnode *, int, int, cred_t *, caller_context_t *); +static int port_realvp(vnode_t *, vnode_t **, caller_context_t *); +static int port_poll(vnode_t *, short, int, short *, struct pollhead **, + caller_context_t *); +static void port_inactive(struct vnode *, cred_t *, caller_context_t *); const fs_operation_def_t port_vnodeops_template[] = { VOPNAME_OPEN, { .vop_open = port_open }, @@ -62,7 +65,7 @@ const fs_operation_def_t port_vnodeops_template[] = { /* ARGSUSED */ static int -port_open(struct vnode **vpp, int flag, cred_t *cr) +port_open(struct vnode **vpp, int flag, cred_t *cr, caller_context_t *ct) { return (0); } @@ -146,7 +149,8 @@ port_close_events(port_queue_t *portq) */ /* ARGSUSED */ static int -port_close(struct vnode *vp, int flag, int count, offset_t offset, cred_t *cr) +port_close(struct vnode *vp, int flag, int count, offset_t offset, cred_t *cr, + caller_context_t *ct) { port_t *pp; port_queue_t *portq; @@ -276,7 +280,7 @@ port_close(struct vnode *vp, int flag, int count, offset_t offset, cred_t *cr) /*ARGSUSED*/ static int port_poll(vnode_t *vp, short events, int anyyet, short *reventsp, - struct pollhead **phpp) + struct pollhead **phpp, caller_context_t *ct) { port_t *pp; port_queue_t *portq; @@ -308,7 +312,8 @@ port_poll(vnode_t *vp, short events, int anyyet, short *reventsp, /* ARGSUSED */ static int -port_getattr(struct vnode *vp, struct vattr *vap, int flags, cred_t *cr) +port_getattr(struct vnode *vp, struct vattr *vap, int flags, cred_t *cr, + caller_context_t *ct) { port_t *pp; extern dev_t portdev; @@ -340,7 +345,7 @@ port_getattr(struct vnode *vp, struct vattr *vap, int flags, cred_t *cr) */ /* ARGSUSED */ static void -port_inactive(struct vnode *vp, cred_t *cr) +port_inactive(struct vnode *vp, cred_t *cr, caller_context_t *ct) { port_t *pp = VTOEP(vp); extern port_kstat_t port_kstat; @@ -359,14 +364,15 @@ port_inactive(struct vnode *vp, cred_t *cr) /* ARGSUSED */ static int -port_access(struct vnode *vp, int mode, int flags, cred_t *cr) +port_access(struct vnode *vp, int mode, int flags, cred_t *cr, + caller_context_t *ct) { return (0); } /* ARGSUSED */ static int -port_realvp(vnode_t *vp, vnode_t **vpp) +port_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct) { *vpp = vp; return (0); diff --git a/usr/src/uts/common/fs/proc/prcontrol.c b/usr/src/uts/common/fs/proc/prcontrol.c index 9bbf929b5f97..f4b20a404553 100644 --- a/usr/src/uts/common/fs/proc/prcontrol.c +++ b/usr/src/uts/common/fs/proc/prcontrol.c @@ -1460,7 +1460,7 @@ pr_setsig(prnode_t *pnp, siginfo_t *sip) } thread_lock(t); if (ISWAKEABLE(t) || ISWAITING(t)) { - /* Set signalled sleeping/waiting lwp running */ + /* Set signaled sleeping/waiting lwp running */ setrun_locked(t); } else if (t->t_state == TS_STOPPED && sig == SIGKILL) { /* If SIGKILL, set stopped lwp running */ diff --git a/usr/src/uts/common/fs/proc/prioctl.c b/usr/src/uts/common/fs/proc/prioctl.c index 1eb2861b0f85..45fddda6f65d 100644 --- a/usr/src/uts/common/fs/proc/prioctl.c +++ b/usr/src/uts/common/fs/proc/prioctl.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -127,14 +127,27 @@ prctioctl(prnode_t *pnp, int cmd, intptr_t arg, int flag, cred_t *cr) /* * Control operations (lots). */ +/*ARGSUSED*/ #ifdef _SYSCALL32_IMPL static int -prioctl64(struct vnode *vp, int cmd, intptr_t arg, int flag, - cred_t *cr, int *rvalp) +prioctl64( + struct vnode *vp, + int cmd, + intptr_t arg, + int flag, + cred_t *cr, + int *rvalp, + caller_context_t *ct) #else int -prioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, - cred_t *cr, int *rvalp) +prioctl( + struct vnode *vp, + int cmd, + intptr_t arg, + int flag, + cred_t *cr, + int *rvalp, + caller_context_t *ct) #endif /* _SYSCALL32_IMPL */ { caddr_t cmaddr = (caddr_t)arg; @@ -1657,9 +1670,16 @@ oprgetpsinfo32(proc_t *p, prpsinfo32_t *psp, kthread_t *tp) } } +/*ARGSUSED*/ static int -prioctl32(struct vnode *vp, int cmd, intptr_t arg, int flag, - cred_t *cr, int *rvalp) +prioctl32( + struct vnode *vp, + int cmd, + intptr_t arg, + int flag, + cred_t *cr, + int *rvalp, + caller_context_t *ct) { caddr_t cmaddr = (caddr_t)arg; proc_t *p; @@ -3126,7 +3146,7 @@ propenm(prnode_t *pnp, caddr_t cmaddr, caddr_t va, int *rvalp, cred_t *cr) prunlock(pnp); if (error == 0) { - if ((error = VOP_ACCESS(xvp, VREAD, 0, cr)) == 0) + if ((error = VOP_ACCESS(xvp, VREAD, 0, cr, NULL)) == 0) error = fassign(&xvp, FREAD, &n); if (error) { VN_RELE(xvp); @@ -3876,16 +3896,23 @@ oprpdread32(struct as *as, uint_t hatid, struct uio *uiop) } #endif /* _SYSCALL32_IMPL */ +/*ARGSUSED*/ #ifdef _SYSCALL32_IMPL int -prioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, - cred_t *cr, int *rvalp) +prioctl( + struct vnode *vp, + int cmd, + intptr_t arg, + int flag, + cred_t *cr, + int *rvalp, + caller_context_t *ct) { switch (curproc->p_model) { case DATAMODEL_ILP32: - return (prioctl32(vp, cmd, arg, flag, cr, rvalp)); + return (prioctl32(vp, cmd, arg, flag, cr, rvalp, ct)); case DATAMODEL_LP64: - return (prioctl64(vp, cmd, arg, flag, cr, rvalp)); + return (prioctl64(vp, cmd, arg, flag, cr, rvalp, ct)); default: return (ENOSYS); } diff --git a/usr/src/uts/common/fs/proc/prsubr.c b/usr/src/uts/common/fs/proc/prsubr.c index 60e541bb038a..e716d3b0e3b5 100644 --- a/usr/src/uts/common/fs/proc/prsubr.c +++ b/usr/src/uts/common/fs/proc/prsubr.c @@ -1681,7 +1681,7 @@ prgetmap(proc_t *p, int reserved, list_t *iolhead) if (seg->s_ops == &segvn_ops && SEGOP_GETVP(seg, saddr, &vp) == 0 && vp != NULL && vp->v_type == VREG && - VOP_GETATTR(vp, &vattr, 0, CRED()) == 0) { + VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) == 0) { if (vp == p->p_exec) (void) strcpy(mp->pr_mapname, "a.out"); else @@ -1793,7 +1793,7 @@ prgetmap32(proc_t *p, int reserved, list_t *iolhead) if (seg->s_ops == &segvn_ops && SEGOP_GETVP(seg, saddr, &vp) == 0 && vp != NULL && vp->v_type == VREG && - VOP_GETATTR(vp, &vattr, 0, CRED()) == 0) { + VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) == 0) { if (vp == p->p_exec) (void) strcpy(mp->pr_mapname, "a.out"); else @@ -1996,7 +1996,7 @@ prpdread(proc_t *p, uint_t hatid, struct uio *uiop) if (seg->s_ops == &segvn_ops && SEGOP_GETVP(seg, saddr, &vp) == 0 && vp != NULL && vp->v_type == VREG && - VOP_GETATTR(vp, &vattr, 0, CRED()) == 0) { + VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) == 0) { if (vp == p->p_exec) (void) strcpy(pmp->pr_mapname, "a.out"); else @@ -2143,7 +2143,7 @@ prpdread32(proc_t *p, uint_t hatid, struct uio *uiop) if (seg->s_ops == &segvn_ops && SEGOP_GETVP(seg, saddr, &vp) == 0 && vp != NULL && vp->v_type == VREG && - VOP_GETATTR(vp, &vattr, 0, CRED()) == 0) { + VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) == 0) { if (vp == p->p_exec) (void) strcpy(pmp->pr_mapname, "a.out"); else @@ -3678,7 +3678,7 @@ pr_getsegsize(struct seg *seg, int reserved) if (SEGOP_GETVP(seg, seg->s_base, &vp) == 0 && vp != NULL && vp->v_type == VREG && - VOP_GETATTR(vp, &vattr, 0, CRED()) == 0) { + VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) == 0) { u_offset_t fsize = vattr.va_size; u_offset_t offset = SEGOP_GETOFFSET(seg, seg->s_base); @@ -3981,7 +3981,8 @@ prgetxmap(proc_t *p, list_t *iolhead) if (seg->s_ops == &segvn_ops && SEGOP_GETVP(seg, saddr, &vp) == 0 && vp != NULL && vp->v_type == VREG && - VOP_GETATTR(vp, &vattr, 0, CRED()) == 0) { + VOP_GETATTR(vp, &vattr, 0, CRED(), + NULL) == 0) { mp->pr_dev = vattr.va_fsid; mp->pr_ino = vattr.va_nodeid; if (vp == p->p_exec) @@ -4164,7 +4165,8 @@ prgetxmap32(proc_t *p, list_t *iolhead) if (seg->s_ops == &segvn_ops && SEGOP_GETVP(seg, saddr, &vp) == 0 && vp != NULL && vp->v_type == VREG && - VOP_GETATTR(vp, &vattr, 0, CRED()) == 0) { + VOP_GETATTR(vp, &vattr, 0, CRED(), + NULL) == 0) { (void) cmpldev(&mp->pr_dev, vattr.va_fsid); mp->pr_ino = vattr.va_nodeid; diff --git a/usr/src/uts/common/fs/proc/prvnops.c b/usr/src/uts/common/fs/proc/prvnops.c index a8be6c151d3d..4b61f1cdcc4c 100644 --- a/usr/src/uts/common/fs/proc/prvnops.c +++ b/usr/src/uts/common/fs/proc/prvnops.c @@ -216,10 +216,10 @@ static prdirent_t lwpiddir[] = { static void rebuild_objdir(struct as *); static void prfreecommon(prcommon_t *); -static int praccess(vnode_t *, int, int, cred_t *); +static int praccess(vnode_t *, int, int, cred_t *, caller_context_t *); static int -propen(vnode_t **vpp, int flag, cred_t *cr) +propen(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { vnode_t *vp = *vpp; prnode_t *pnp = VTOP(vp); @@ -259,7 +259,7 @@ propen(vnode_t **vpp, int flag, cred_t *cr) * Need to hold rvp since VOP_OPEN() may release it. */ VN_HOLD(rvp); - error = VOP_OPEN(&rvp, flag, cr); + error = VOP_OPEN(&rvp, flag, cr, ct); if (error) { VN_RELE(rvp); } else { @@ -403,7 +403,8 @@ propen(vnode_t **vpp, int flag, cred_t *cr) /* ARGSUSED */ static int -prclose(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) +prclose(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, + caller_context_t *ct) { prnode_t *pnp = VTOP(vp); prcommon_t *pcp = pnp->pr_pcommon; @@ -2692,7 +2693,8 @@ prwrite(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, caller_context_t *ct) } static int -prgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +prgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { prnode_t *pnp = VTOP(vp); prnodetype_t type = pnp->pr_type; @@ -2742,13 +2744,13 @@ prgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) if (!(flags & ATTR_REAL)) break; /* restrict full knowledge of the attributes to owner or root */ - if ((error = praccess(vp, 0, 0, cr)) != 0) + if ((error = praccess(vp, 0, 0, cr, ct)) != 0) return (error); /* FALLTHROUGH */ case PR_OBJECT: case PR_FD: rvp = pnp->pr_realvp; - error = VOP_GETATTR(rvp, vap, flags, cr); + error = VOP_GETATTR(rvp, vap, flags, cr, ct); if (error) return (error); if (type == PR_FD) { @@ -3093,7 +3095,7 @@ prgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) } static int -praccess(vnode_t *vp, int mode, int flags, cred_t *cr) +praccess(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct) { prnode_t *pnp = VTOP(vp); prnodetype_t type = pnp->pr_type; @@ -3126,7 +3128,7 @@ praccess(vnode_t *vp, int mode, int flags, cred_t *cr) (type == PR_FD && (vmode & mode) != mode && secpolicy_proc_access(cr) != 0)) return (EACCES); - return (VOP_ACCESS(rvp, mode, flags, cr)); + return (VOP_ACCESS(rvp, mode, flags, cr, ct)); case PR_PSINFO: /* these files can be read by anyone */ case PR_LPSINFO: @@ -3166,7 +3168,7 @@ praccess(vnode_t *vp, int mode, int flags, cred_t *cr) */ VN_HOLD(xvp); prunlock(pnp); - error = VOP_ACCESS(xvp, VREAD, 0, cr); + error = VOP_ACCESS(xvp, VREAD, 0, cr, ct); VN_RELE(xvp); } if (error) @@ -3178,7 +3180,7 @@ praccess(vnode_t *vp, int mode, int flags, cred_t *cr) /* * Final access check on the underlying directory vnode. */ - return (VOP_ACCESS(pnp->pr_realvp, mode, flags, cr)); + return (VOP_ACCESS(pnp->pr_realvp, mode, flags, cr, ct)); } /* @@ -3255,7 +3257,8 @@ static vnode_t *(*pr_lookup_function[PR_NFILES])() = { static int prlookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pathp, - int flags, vnode_t *rdir, cred_t *cr) + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp) { prnode_t *pnp = VTOP(dp); prnodetype_t type = pnp->pr_type; @@ -3281,20 +3284,22 @@ prlookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pathp, case PR_CURDIR: case PR_ROOTDIR: /* restrict lookup permission to owner or root */ - if ((error = praccess(dp, VEXEC, 0, cr)) != 0) + if ((error = praccess(dp, VEXEC, 0, cr, ct)) != 0) return (error); /* FALLTHROUGH */ case PR_FD: dp = pnp->pr_realvp; - return (VOP_LOOKUP(dp, comp, vpp, pathp, flags, rdir, cr)); + return (VOP_LOOKUP(dp, comp, vpp, pathp, flags, rdir, cr, ct, + direntflags, realpnp)); default: break; } if ((type == PR_OBJECTDIR || type == PR_FDDIR || type == PR_PATHDIR) && - (error = praccess(dp, VEXEC, 0, cr)) != 0) + (error = praccess(dp, VEXEC, 0, cr, ct)) != 0) return (error); + /* XXX - Do we need to pass ct, direntflags, or realpnp? */ *vpp = (pr_lookup_function[type](dp, comp)); return ((*vpp == NULL) ? ENOENT : 0); @@ -3303,11 +3308,13 @@ prlookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pathp, /* ARGSUSED */ static int prcreate(vnode_t *dp, char *comp, vattr_t *vap, vcexcl_t excl, - int mode, vnode_t **vpp, cred_t *cr, int flag) + int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct, + vsecattr_t *vsecp) { int error; - if ((error = prlookup(dp, comp, vpp, NULL, 0, NULL, cr)) != 0) { + if ((error = prlookup(dp, comp, vpp, NULL, 0, NULL, cr, + ct, NULL, NULL)) != 0) { if (error == ENOENT) /* can't O_CREAT nonexistent files */ error = EACCES; /* unwriteable directories */ } else { @@ -3326,7 +3333,7 @@ prcreate(vnode_t *dp, char *comp, vattr_t *vap, vcexcl_t excl, vp = VTOP(vp)->pr_realvp; mask = vap->va_mask; vap->va_mask = AT_SIZE; - error = VOP_SETATTR(vp, vap, 0, cr, NULL); + error = VOP_SETATTR(vp, vap, 0, cr, ct); vap->va_mask = mask; } } @@ -3615,7 +3622,7 @@ pr_lookup_objectdir(vnode_t *dp, char *comp) if (seg->s_ops == &segvn_ops && SEGOP_GETVP(seg, seg->s_base, &vp) == 0 && vp != NULL && vp->v_type == VREG && - VOP_GETATTR(vp, &vattr, 0, CRED()) == 0) { + VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) == 0) { char name[64]; if (vp == p->p_exec) /* "a.out" */ @@ -4067,8 +4074,8 @@ pr_lookup_pathdir(vnode_t *dp, char *comp) SEGOP_GETVP(seg, seg->s_base, &vp) == 0 && vp != NULL && vp->v_type == VREG && - VOP_GETATTR(vp, &vattr, 0, CRED()) - == 0) { + VOP_GETATTR(vp, &vattr, 0, CRED(), + NULL) == 0) { char name[64]; if (vp == p->p_exec) @@ -4513,7 +4520,7 @@ prfreenode(prnode_t *pnp) } /* - * Free a prcommon structure, if the refrence count reaches zero. + * Free a prcommon structure, if the reference count reaches zero. */ static void prfreecommon(prcommon_t *pcp) @@ -4596,12 +4603,14 @@ static int (*pr_readdir_function[PR_NFILES])() = { /* ARGSUSED */ static int -prreaddir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp) +prreaddir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) { prnode_t *pnp = VTOP(vp); ASSERT(pnp->pr_type < PR_NFILES); + /* XXX - Do we need to pass ct and flags? */ return (pr_readdir_function[pnp->pr_type](pnp, uiop, eofp)); } @@ -4764,7 +4773,7 @@ rebuild_objdir(struct as *as) if (seg->s_ops == &segvn_ops && SEGOP_GETVP(seg, seg->s_base, &vp) == 0 && vp != NULL && vp->v_type == VREG && - VOP_GETATTR(vp, &vattr, 0, CRED()) == 0) { + VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) == 0) { for (i = 0; i < nentries; i++) if (vp == dir[i]) break; @@ -4920,7 +4929,8 @@ pr_readdir_objectdir(prnode_t *pnp, uio_t *uiop, int *eofp) */ vattr.va_mask = AT_FSID | AT_NODEID; while (n < objdirsize && (((vp = obj_entry(as, n)) == NULL) || - (VOP_GETATTR(vp, &vattr, 0, CRED()) != 0))) { + (VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) + != 0))) { vattr.va_mask = AT_FSID | AT_NODEID; n++; } @@ -5275,7 +5285,7 @@ pr_readdir_pathdir(prnode_t *pnp, uio_t *uiop, int *eofp) if ((vp = obj_entry(as, obj)) == NULL) continue; vattr.va_mask = AT_FSID|AT_NODEID; - if (VOP_GETATTR(vp, &vattr, 0, CRED()) != 0) + if (VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) != 0) continue; if (vp == p->p_exec) (void) strcpy(dirent->d_name, "a.out"); @@ -5429,7 +5439,7 @@ pr_readdir_ctdir(prnode_t *pnp, uio_t *uiop, int *eofp) /* ARGSUSED */ static int -prfsync(vnode_t *vp, int syncflag, cred_t *cr) +prfsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) { return (0); } @@ -5456,7 +5466,7 @@ pr_list_unlink(vnode_t *pvp, vnode_t **listp) /* ARGSUSED */ static void -prinactive(vnode_t *vp, cred_t *cr) +prinactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { prnode_t *pnp = VTOP(vp); prnodetype_t type = pnp->pr_type; @@ -5574,7 +5584,7 @@ prinactive(vnode_t *vp, cred_t *cr) /* ARGSUSED */ static int -prseek(vnode_t *vp, offset_t ooff, offset_t *noffp) +prseek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) { return (0); } @@ -5706,7 +5716,7 @@ prreadlink_lookup(prnode_t *pnp, char *buf, size_t size, cred_t *cr) /* ARGSUSED */ static int -prreadlink(vnode_t *vp, uio_t *uiop, cred_t *cr) +prreadlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ctp) { prnode_t *pnp = VTOP(vp); char *buf; @@ -5754,8 +5764,9 @@ prreadlink(vnode_t *vp, uio_t *uiop, cred_t *cr) return (ret); } +/*ARGSUSED2*/ static int -prcmp(vnode_t *vp1, vnode_t *vp2) +prcmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct) { prnode_t *pp1, *pp2; @@ -5783,13 +5794,13 @@ prcmp(vnode_t *vp1, vnode_t *vp2) } static int -prrealvp(vnode_t *vp, vnode_t **vpp) +prrealvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct) { vnode_t *rvp; if ((rvp = VTOP(vp)->pr_realvp) != NULL) { vp = rvp; - if (VOP_REALVP(vp, &rvp) == 0) + if (VOP_REALVP(vp, &rvp, ct) == 0) vp = rvp; } @@ -5805,9 +5816,10 @@ prrealvp(vnode_t *vp, vnode_t **vpp) * POLLERR /proc file descriptor is invalid * POLLHUP process or lwp has terminated */ +/*ARGSUSED5*/ static int prpoll(vnode_t *vp, short events, int anyyet, short *reventsp, - pollhead_t **phpp) + pollhead_t **phpp, caller_context_t *ct) { prnode_t *pnp = VTOP(vp); prcommon_t *pcp = pnp->pr_common; @@ -5922,7 +5934,8 @@ prpoll(vnode_t *vp, short events, int anyyet, short *reventsp, } /* in prioctl.c */ -extern int prioctl(vnode_t *, int, intptr_t, int, cred_t *, int *); +extern int prioctl(vnode_t *, int, intptr_t, int, cred_t *, int *, + caller_context_t *); /* * /proc vnode operations vector diff --git a/usr/src/uts/common/fs/sharefs/sharefs_vnops.c b/usr/src/uts/common/fs/sharefs/sharefs_vnops.c index 1818c4ee039c..cbafc3815023 100644 --- a/usr/src/uts/common/fs/sharefs/sharefs_vnops.c +++ b/usr/src/uts/common/fs/sharefs/sharefs_vnops.c @@ -158,7 +158,8 @@ sharefs_snap_create(shnode_t *sft) /* ARGSUSED */ static int -sharefs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +sharefs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { timestruc_t now; shnode_t *sft = VTOSH(vp); @@ -204,7 +205,8 @@ sharefs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) /* ARGSUSED */ static int -sharefs_access(vnode_t *vp, int mode, int flags, cred_t *cr) +sharefs_access(vnode_t *vp, int mode, int flags, cred_t *cr, + caller_context_t *ct) { if (mode & (VWRITE|VEXEC)) return (EROFS); @@ -214,7 +216,7 @@ sharefs_access(vnode_t *vp, int mode, int flags, cred_t *cr) /* ARGSUSED */ int -sharefs_open(vnode_t **vpp, int flag, cred_t *cr) +sharefs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { vnode_t *vp; vnode_t *ovp = *vpp; @@ -267,7 +269,7 @@ sharefs_open(vnode_t **vpp, int flag, cred_t *cr) /* ARGSUSED */ int sharefs_close(vnode_t *vp, int flag, int count, - offset_t off, cred_t *cr) + offset_t off, cred_t *cr, caller_context_t *ct) { shnode_t *sft = VTOSH(vp); @@ -338,7 +340,7 @@ sharefs_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, /* ARGSUSED */ static void -sharefs_inactive(vnode_t *vp, cred_t *cr) +sharefs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *tx) { gfs_file_t *fp = vp->v_data; shnode_t *sft; diff --git a/usr/src/uts/common/fs/smbsrv/smb_acl.c b/usr/src/uts/common/fs/smbsrv/smb_acl.c new file mode 100644 index 000000000000..2617268d9ece --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_acl.c @@ -0,0 +1,1310 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Platform SDK: Security + * + * ACE Inheritance Rules + * + * The system propagates inheritable ACEs to child objects according to a + * set of inheritance rules. The system places inherited ACEs in the child's + * DACL according to the preferred order of ACEs in a DACL. For Windows + * 2000 or later, the system sets the INHERITED_ACE flag in all inherited ACEs. + * + * The following table shows the ACEs inherited by container and noncontainer + * child objects for different combinations of inheritance flags. These + * inheritance rules work the same for both DACLs and SACLs. + * + * Parent ACE type Effect on Child ACL + * ----------------------- ------------------- + * OBJECT_INHERIT_ACE only Noncontainer child objects: + * Inherited as an effective ACE. + * Container child objects: + * Containers inherit an inherit-only ACE + * unless the NO_PROPAGATE_INHERIT_ACE bit + * flag is also set. + * + * CONTAINER_INHERIT_ACE only Noncontainer child objects: + * No effect on the child object. + * Container child objects: + * The child object inherits an effective ACE. + * The inherited ACE is inheritable unless the + * NO_PROPAGATE_INHERIT_ACE bit flag is also set. + * + * CONTAINER_INHERIT_ACE and + * OBJECT_INHERIT_ACE Noncontainer child objects: + * Inherited as an effective ACE. + * Container child objects: + * The child object inherits an effective ACE. + * The inherited ACE is inheritable unless the + * NO_PROPAGATE_INHERIT_ACE bit flag is also set + * + * No inheritance flags set No effect on child container or noncontainer + * objects. + * + * If an inherited ACE is an effective ACE for the child object, the system + * maps any generic rights to the specific rights for the child object. + * Similarly, the system maps generic SIDs, such as CREATOR_OWNER, to the + * appropriate SID. If an inherited ACE is an inherit-only ACE, any generic + * rights or generic SIDs are left unchanged so that they can be mapped + * appropriately when the ACE is inherited by the next generation of child + * objects. + * + * For a case in which a container object inherits an ACE that is both + * effective on the container and inheritable by its descendants, the + * container may inherit two ACEs. This occurs if the inheritable ACE + * contains generic information. The container inherits an inherit-only + * ACE containing the generic information and an effective-only ACE in + * which the generic information has been mapped. + */ + +#include +#include +#include +#include +#include + +#define ACE_FD_INHERIT_ACE (ACE_FILE_INHERIT_ACE | ACE_DIRECTORY_INHERIT_ACE) + +#define ZACE_IS_OWNER(zace) ((zace->a_flags & ACE_TYPE_FLAGS) == ACE_OWNER) +#define ZACE_IS_OWNGRP(zace) \ + ((zace->a_flags & ACE_TYPE_FLAGS) == (ACE_IDENTIFIER_GROUP|ACE_GROUP)) + +#define ZACE_IS_USER(zace) \ + (((zace->a_flags & ACE_TYPE_FLAGS) == 0) || (ZACE_IS_OWNER(zace))) +#define ZACE_IS_GROUP(zace) (zace->a_flags & ACE_IDENTIFIER_GROUP) +#define ZACE_IS_EVERYONE(zace) (zace->a_flags & ACE_EVERYONE) + +#define ZACE_IS_PROPAGATE(zace) \ + ((zace->a_flags & ACE_NO_PROPAGATE_INHERIT_ACE) == 0) + +#define ZACE_IS_CREATOR_OWNER(zace) \ + (ZACE_IS_USER(zace) && (zace->a_who == IDMAP_WK_CREATOR_OWNER_UID)) + +#define ZACE_IS_CREATOR_GROUP(zace) \ + (ZACE_IS_GROUP(zace) && (zace->a_who == IDMAP_WK_CREATOR_GROUP_GID)) + +#define ZACE_IS_CREATOR(zace) \ + (ZACE_IS_CREATOR_OWNER(zace) || ZACE_IS_CREATOR_GROUP(zace)) + +static int smb_ace_isvalid(smb_ace_hdr_t *ace, int which_acl); +static int smb_ace_append_generic(smb_acl_t *acl, void *generic_ace); + +static int smb_ace_common_add( + smb_acl_t *acl, + uint8_t type, + uint8_t flags, + uint32_t access_mask, + nt_sid_t *sid); + +static void smb_ace_inherit(ace_t *dir_zace, ace_t *zace, int is_dir); +static uint16_t smb_ace_flags_tozfs(uint8_t c_flags, int isdir); +static uint8_t smb_ace_flags_fromzfs(uint16_t z_flags); +static void smb_acl_init(smb_acl_t *acl, uint16_t size, uint8_t rev); + +static int +smb_ace_isvalid(smb_ace_hdr_t *ace, int which_acl) +{ + uint16_t min_len; + smb_ace_t *p; + + min_len = sizeof (smb_ace_hdr_t); + + if (ace->se_size < min_len) + return (0); + + if (smb_ace_is_access(ace->se_type) && + (which_acl != SMB_DACL_SECINFO)) { + return (0); + } + + if (smb_ace_is_audit(ace->se_type) && + (which_acl != SMB_SACL_SECINFO)) { + return (0); + } + + if (smb_ace_is_generic(ace->se_type)) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + p = (smb_ace_t *)ace; + + if (ace->se_size < sizeof (*p)) + return (0); /* won't handle empty SubAuthority[] */ + + if (nt_sid_is_valid(&p->se_sid) == 0) + return (0); + + min_len += sizeof (p->se_mask); + min_len += nt_sid_length(&p->se_sid); + + if (ace->se_size < min_len) + return (0); + } + + /* + * XXX object-specific ACE validation will be added later. + */ + return (1); +} + +int +smb_acl_isvalid(smb_acl_t *acl, int which_acl) +{ + uint16_t min_len; + unsigned char *scan; + unsigned char *scan_end; + smb_ace_hdr_t *ace; + uint16_t count = 0; + + min_len = sizeof (smb_acl_t); + + if (acl->sl_size < min_len) + return (0); + + if (acl->sl_revision != ACL_REVISION) { + /* + * XXX we are rejecting ACLs with object-specific ACEs for now + */ + return (0); + } + + scan = (unsigned char *) &acl[0]; + scan_end = scan + acl->sl_size; + scan = (unsigned char *) &acl[1]; /* skip Acl header */ + + while (count < acl->sl_acecnt && scan < scan_end) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + ace = (smb_ace_hdr_t *)scan; + + if (scan + sizeof (smb_ace_hdr_t) >= scan_end) + return (0); + + if (scan + ace->se_size > scan_end) + return (0); /* overflow */ + + if (!smb_ace_isvalid(ace, which_acl)) + return (0); + + scan += ace->se_size; + count++; + } + + return (1); +} + + +static void +smb_acl_init(smb_acl_t *acl, uint16_t size, uint8_t rev) +{ + bzero(acl, size); + acl->sl_revision = rev; + acl->sl_size = size; +} + +uint16_t +smb_acl_len(smb_acl_t *acl) +{ + smb_ace_hdr_t *ace; + unsigned char *scan_beg; + unsigned char *scan_end; + unsigned char *scan; + uint16_t length; + uint16_t count; + + scan_beg = (unsigned char *) &acl[0]; + scan_end = scan_beg + acl->sl_size; + scan = (unsigned char *) &acl[1]; + length = sizeof (smb_acl_t); + count = 0; + + while ((count < acl->sl_acecnt) && (scan < scan_end)) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + ace = (smb_ace_hdr_t *)scan; + length += ace->se_size; + scan += ace->se_size; + count++; + } + + return (length); +} + +/* + * Append the generic ACE to the ACL. This is used to put any + * kind of ACE on the ACL so the argument is declared as a void*. We cast it + * to an ACCESS_ALLOWED_ACE just because there is no sense of a generic ACE. + */ +static int +smb_ace_append_generic(smb_acl_t *acl, void *generic_ace) +{ + smb_ace_t *ace = (smb_ace_t *)generic_ace; + uint16_t acl_len = smb_acl_len(acl); + unsigned char *scan = (uchar_t *)acl; + + if ((acl_len + ace->se_header.se_size) > acl->sl_size) { + /* no room in the acl for this ace */ + return (0); + } + + /* append the ace to the acl and inc ace count */ + bcopy(ace, &scan[acl_len], ace->se_header.se_size); + acl->sl_acecnt++; + + return (1); +} + +/* + * Helper for the ACL sort routine + */ +typedef struct smb_ace_entry { + smb_ace_t *e_ace; + list_node_t e_node; +} smb_ace_entry_t; + +/* + * ACE groups within a DACL + * + * This is from lower to higher ACE order priority + */ +#define SMB_AG_START 0 +#define SMB_AG_ALW_INHRT 0 +#define SMB_AG_DNY_INHRT 1 +#define SMB_AG_ALW_DRCT 2 +#define SMB_AG_DNY_DRCT 3 +#define SMB_AG_NUM 4 + +/* + * smb_acl_do_sort + * + * Sorts the given ACL, acl, and returns the result + * in a newly allocated memory. + * + * The following is an excerpt from MSDN website. + * + * Order of ACEs in a DACL + * + * For Windows NT versions 4.0 and earlier, the preferred order of ACEs + * is simple: In a DACL, all access-denied ACEs should precede any + * access-allowed ACEs. + * + * For Windows 2000 or later, the proper order of ACEs is more complicated + * because of the introduction of object-specific ACEs and automatic + * inheritance. + * + * The following describes the preferred order: + * + * To ensure that noninherited ACEs have precedence over inherited ACEs, + * place all noninherited ACEs in a group before any inherited ACEs. This + * ordering ensures, for example, that a noninherited access-denied ACE + * is enforced regardless of any inherited ACE that allows access. + * Within the groups of noninherited ACEs and inherited ACEs, order ACEs + * according to ACE type, as the following shows: + * . Access-denied ACEs that apply to the object itself + * . Access-denied ACEs that apply to a subobject of the + * object, such as a property set or property + * . Access-allowed ACEs that apply to the object itself + * . Access-allowed ACEs that apply to a subobject of the object + * + * Of course, not all ACE types are required in an ACL. + */ +static smb_acl_t * +smb_acl_do_sort(smb_acl_t *acl, list_t *ace_grps) +{ + smb_acl_t *sorted_acl; + smb_ace_entry_t *nae; + int i; + + sorted_acl = kmem_alloc(acl->sl_size, KM_SLEEP); + *sorted_acl = *acl; + + /* start with no ACE in the sorted ACL */ + sorted_acl->sl_acecnt = 0; + + /* + * start with highest priority ACE group and append + * the ACEs to the ACL. + */ + for (i = SMB_AG_NUM - 1; i >= SMB_AG_START; i--) { + nae = list_head(&ace_grps[i]); + while (nae) { + if (!smb_ace_append_generic(sorted_acl, nae->e_ace)) { + kmem_free(sorted_acl, acl->sl_size); + return (NULL); + } + nae = list_next(&ace_grps[i], nae); + } + } + + return (sorted_acl); +} + +/* + * smb_acl_need_sort + * + * Here is the desired ACE order + * + * deny-direct, allow-direct, deny-inherited, allow-inherited + * + * If any ace has been encountered which belongs to a group + * with lower priority of the specified ace_grp then the acl + * should be sorted. + */ +static int +smb_acl_need_sort(list_t *ace_grps, int ace_grp) +{ + int i; + + for (i = SMB_AG_START; i < ace_grp; i++) + if (!list_is_empty(&ace_grps[i])) + return (1); + + return (0); +} + +/* + * smb_acl_sort + * + * Returns NULL upon failure. + * Returns pointer to the passed (original) acl if no sort is required. + * Returns pointer to a new acl upon successful sort in which case the + * caller is responsible for freeing the allocated memory. + */ +smb_acl_t * +smb_acl_sort(smb_acl_t *acl) +{ + smb_acl_t *sorted_acl; + smb_ace_t *ace; + smb_ace_entry_t *ace_list; + int ace_list_size; + list_t ace_grps[SMB_AG_NUM]; + int ag; + int do_sort = 0; + uint16_t i; + uint8_t ace_flags; + + ASSERT(acl); + + if (acl->sl_acecnt == 0) { + /* + * ACL with no entry is a valid ACL and it means + * no access for anybody. + */ + return (acl); + } + + for (i = SMB_AG_START; i < SMB_AG_NUM; i++) { + list_create(&ace_grps[i], sizeof (smb_ace_entry_t), + offsetof(smb_ace_entry_t, e_node)); + } + + /* + * Allocate the helper entries to group the ACEs based on + * the desired priorities. + */ + ace_list_size = sizeof (smb_ace_entry_t) * acl->sl_acecnt; + ace_list = kmem_alloc(ace_list_size, KM_SLEEP); + + for (i = 0; i < acl->sl_acecnt; ++i) { + ace_list[i].e_ace = smb_ace_get(acl, i); + ace = ace_list[i].e_ace; + ASSERT(ace); + + ace_flags = ace->se_header.se_flags; + + switch (ace->se_header.se_type) { + case ACCESS_DENIED_ACE_TYPE: + if (ace_flags & INHERITED_ACE) { + ag = SMB_AG_DNY_INHRT; + do_sort |= smb_acl_need_sort(ace_grps, ag); + } else { + ag = SMB_AG_DNY_DRCT; + do_sort |= smb_acl_need_sort(ace_grps, ag); + } + break; + + case ACCESS_ALLOWED_ACE_TYPE: + if (ace_flags & INHERITED_ACE) { + ag = SMB_AG_ALW_INHRT; + } else { + ag = SMB_AG_ALW_DRCT; + do_sort |= smb_acl_need_sort(ace_grps, ag); + } + break; + + default: + /* + * This is the lowest priority group so we put + * evertything unknown here. + */ + ag = SMB_AG_ALW_INHRT; + break; + } + + /* Put the element on the appropriate list */ + list_insert_tail(&ace_grps[ag], &ace_list[i]); + } + + if (do_sort) + sorted_acl = smb_acl_do_sort(acl, ace_grps); + else + sorted_acl = acl; + + for (i = SMB_AG_START; i < SMB_AG_NUM; i++) { + void *ent; + list_t *alist = &ace_grps[i]; + + while ((ent = list_head(alist)) != NULL) + list_remove(alist, ent); + list_destroy(alist); + } + + kmem_free(ace_list, ace_list_size); + + return (sorted_acl); +} + +static int +smb_ace_common_add( + smb_acl_t *acl, + uint8_t type, + uint8_t flags, + uint32_t access_mask, + nt_sid_t *sid) +{ + smb_ace_t *ace; + unsigned char *scan = (unsigned char *) acl; + uint16_t used = smb_acl_len(acl); + uint16_t sid_len = nt_sid_length(sid); + uint16_t size; + + size = sizeof (ace->se_header) + sizeof (ace->se_mask) + sid_len; + + if (size + used > acl->sl_size) { + /* won't fit */ + return (0); + } + + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + ace = (smb_ace_t *)&scan[used]; + + ace->se_header.se_type = type; + ace->se_header.se_flags = flags; + ace->se_header.se_size = size; + ace->se_mask = access_mask; + bcopy(sid, &ace->se_sid, sid_len); + + acl->sl_acecnt++; + + return (1); +} + +smb_ace_t * +smb_ace_get(smb_acl_t *acl, uint16_t idx) +{ + smb_ace_t *ace; + unsigned char *scan_beg = (unsigned char *) &acl[0]; + unsigned char *scan_end = scan_beg + acl->sl_size; + unsigned char *scan = (unsigned char *) &acl[1]; + uint16_t count = 0; + + if (idx >= acl->sl_acecnt) + return (NULL); + + while (count <= idx && scan < scan_end) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + ace = (smb_ace_t *)scan; + + if (count == idx) { + return (ace); + } + + scan += ace->se_header.se_size; + count++; + } + + return (NULL); +} + +int +smb_acl_copy(uint16_t buflen, smb_acl_t *dst_acl, smb_acl_t *src_acl) +{ + smb_ace_hdr_t *dst_ace; + smb_ace_hdr_t *src_ace; + unsigned char *scan = (unsigned char *) &src_acl[1]; + unsigned char *dest_beg = (unsigned char *) &dst_acl[0]; + unsigned char *dest_end; + unsigned char *dest = (unsigned char *) &dst_acl[1]; + uint16_t count = 0; + uint16_t n_bytes; + + n_bytes = smb_acl_len(src_acl); + if (n_bytes > buflen) + return (0); + + dest_end = dest_beg + n_bytes; + + dst_acl->sl_revision = src_acl->sl_revision; + dst_acl->sl_sbz1 = 0; + dst_acl->sl_size = n_bytes; + dst_acl->sl_acecnt = 0; + dst_acl->sl_sbz2 = 0; + + while (count < src_acl->sl_acecnt && dest < dest_end) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + src_ace = (smb_ace_hdr_t *)scan; + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + dst_ace = (smb_ace_hdr_t *)dest; + bcopy(src_ace, dst_ace, src_ace->se_size); + dest += dst_ace->se_size; + dst_acl->sl_acecnt++; + scan += src_ace->se_size; + count++; + } + + /*LINTED E_PTRDIFF_OVERFLOW*/ + return (dest - dest_beg); +} + +/* + * smb_ace_len + * + * Returns the length of an ACE with the given SID + * + * struct smb_ace { + * smb_ace_hdr_t se_header; + * uint32_t se_mask; + * nt_sid_t se_sid; + * }; + */ +uint16_t +smb_ace_len(nt_sid_t *sid) +{ + ASSERT(sid); + + return (sizeof (smb_ace_hdr_t) + + sizeof (uint32_t) + nt_sid_length(sid)); +} + +/* + * smb_ace_mask_g2s + * + * Converts generic access bits in the given mask (if any) + * to file specific bits. Generic access masks shouldn't be + * stored in filesystem ACEs. + */ +uint32_t +smb_ace_mask_g2s(DWORD mask) +{ + if (mask & GENERIC_ALL) { + mask &= ~(GENERIC_ALL | GENERIC_READ | GENERIC_WRITE + | GENERIC_EXECUTE); + + mask |= FILE_ALL_ACCESS; + return (mask); + } + + if (mask & GENERIC_READ) { + mask &= ~GENERIC_READ; + mask |= FILE_GENERIC_READ; + } + + if (mask & GENERIC_WRITE) { + mask &= ~GENERIC_WRITE; + mask |= FILE_GENERIC_WRITE; + } + + if (mask & GENERIC_EXECUTE) { + mask &= ~GENERIC_EXECUTE; + mask |= FILE_GENERIC_EXECUTE; + } + + return (mask); +} + +/* + * smb_acl_getsids + * + * Batch all the uid/gid in given ZFS ACL to get their corresponding SIDs. + */ +static idmap_stat +smb_acl_getsids(smb_idmap_batch_t *sib, acl_t *zacl, uid_t uid, gid_t gid) +{ + ace_t *zace; + idmap_stat idm_stat; + smb_idmap_t *sim; + uid_t id; + int i, idtype; + + sim = sib->sib_maps; + + for (i = 0, zace = zacl->acl_aclp; i < zacl->acl_cnt; + zace++, i++, sim++) { + switch (zace->a_flags & ACE_TYPE_FLAGS) { + case ACE_OWNER: + id = uid; + idtype = SMB_IDMAP_USER; + break; + + case (ACE_GROUP | ACE_IDENTIFIER_GROUP): + /* owning group */ + id = gid; + idtype = SMB_IDMAP_GROUP; + break; + + case ACE_IDENTIFIER_GROUP: + /* regular group */ + id = zace->a_who; + idtype = SMB_IDMAP_GROUP; + break; + + case ACE_EVERYONE: + idtype = SMB_IDMAP_EVERYONE; + break; + + default: + /* user entry */ + id = zace->a_who; + idtype = SMB_IDMAP_USER; + } + + idm_stat = smb_idmap_batch_getsid(sib->sib_idmaph, sim, + id, idtype); + + if (idm_stat != IDMAP_SUCCESS) { + return (idm_stat); + } + } + + idm_stat = smb_idmap_batch_getmappings(sib); + return (idm_stat); +} + +/* + * smb_acl_grow + * + * Grow the acl size by given number of bytes in 'grow' + * Returns pointer to the newly allocated memory. + */ +static smb_acl_t * +smb_acl_grow(smb_acl_t *acl, uint16_t grow) +{ + smb_acl_t *new_acl; + uint16_t smb_aclsz; + + ASSERT(acl); + + smb_aclsz = acl->sl_size; + new_acl = kmem_alloc(smb_aclsz + grow, KM_SLEEP); + (void) memcpy(new_acl, acl, smb_aclsz); + kmem_free(acl, smb_aclsz); + new_acl->sl_size = smb_aclsz + grow; + + return (new_acl); +} + +/* + * smb_acl_from_zfs + * + * Converts given ZFS ACL to a Windows ACL. + * + * A pointer to allocated memory for the Win ACL will be + * returned upon successful conversion. + */ +smb_acl_t * +smb_acl_from_zfs(acl_t *zacl, uid_t uid, gid_t gid) +{ + ace_t *zace; + int numaces; + smb_acl_t *acl; + uint16_t smb_aclsz; + smb_idmap_batch_t sib; + smb_idmap_t *sim; + idmap_stat idm_stat; + int status; + + idm_stat = smb_idmap_batch_create(&sib, zacl->acl_cnt, + SMB_IDMAP_ID2SID); + if (idm_stat != IDMAP_SUCCESS) + return (NULL); + + if (smb_acl_getsids(&sib, zacl, uid, gid) != IDMAP_SUCCESS) { + smb_idmap_batch_destroy(&sib); + return (NULL); + } + + smb_aclsz = sizeof (smb_acl_t); + + acl = kmem_alloc(smb_aclsz, KM_SLEEP); + smb_acl_init(acl, smb_aclsz, ACL_REVISION); + + sim = sib.sib_maps; + for (numaces = 0, zace = zacl->acl_aclp; + numaces < zacl->acl_cnt; + zace++, numaces++, sim++) { + ASSERT(sim->sim_sid); + if (sim->sim_sid == NULL) { + kmem_free(acl, acl->sl_size); + acl = NULL; + break; + } + + /* Make room for this ACE */ + acl = smb_acl_grow(acl, smb_ace_len(sim->sim_sid)); + + status = smb_ace_common_add(acl, + zace->a_type, + smb_ace_flags_fromzfs(zace->a_flags), + zace->a_access_mask, + sim->sim_sid); + + if (status == 0) { + kmem_free(acl, acl->sl_size); + acl = NULL; + break; + } + } + + smb_idmap_batch_destroy(&sib); + return (acl); +} + +/* + * SID for Everyone group: S-1-1-0. + */ +nt_sid_t everyone_sid = { + NT_SID_REVISION, + 1, + NT_SECURITY_WORLD_AUTH, + { 0 } +}; + +/* + * smb_acl_null_empty + * + * NULL DACL means everyone full-access + * Empty DACL means everyone full-deny + * + * ZFS ACL must have at least one entry so smb server has + * to simulate the aforementioned expected behavior by adding + * an entry in case the requested DACL is null or empty. Adding + * a everyone full-deny entry has proved to be problematic in + * tests since a deny entry takes precedence over allow entries. + * So, instead of adding a everyone full-deny, an owner ACE with + * owner implicit permissions will be set. + */ +acl_t * +smb_acl_null_empty(int null) +{ + acl_t *zacl; + ace_t *zace; + + zacl = smb_fsop_aclalloc(1, ACL_AUTO_INHERIT); + zace = zacl->acl_aclp; + + zace->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; + if (null) { + zace->a_access_mask = ACE_ALL_PERMS; + zace->a_flags = ACE_EVERYONE; + } else { + zace->a_access_mask = ACE_READ_ACL | ACE_WRITE_ACL | + ACE_READ_ATTRIBUTES; + zace->a_flags = ACE_OWNER; + } + + return (zacl); +} + +/* + * smb_acl_to_zfs + * + * Converts given Windows ACL to a ZFS ACL. + * + * fs_acl will contain a pointer to the created ZFS ACL. + * The allocated memory should be freed by calling + * smb_fsop_aclfree(). + * + * Since the output parameter, fs_acl, is allocated in this + * function, the caller has to make sure *fs_acl is NULL which + * means it's not pointing to any memory. + */ +uint32_t +smb_acl_to_zfs(smb_acl_t *acl, uint32_t flags, int which_acl, acl_t **fs_acl) +{ + smb_ace_t *ace; + acl_t *zacl; + ace_t *zace; + smb_idmap_batch_t sib; + smb_idmap_t *sim; + idmap_stat idm_stat; + int i, isdir; + + ASSERT(fs_acl); + ASSERT(*fs_acl == NULL); + + if (acl && !smb_acl_isvalid(acl, which_acl)) + return (NT_STATUS_INVALID_ACL); + + if ((acl == NULL) || (acl->sl_acecnt == 0)) { + if (which_acl == SMB_DACL_SECINFO) { + *fs_acl = smb_acl_null_empty(acl == NULL); + } + + return (NT_STATUS_SUCCESS); + } + + idm_stat = smb_idmap_batch_create(&sib, acl->sl_acecnt, + SMB_IDMAP_SID2ID); + if (idm_stat != IDMAP_SUCCESS) + return (NT_STATUS_INTERNAL_ERROR); + + isdir = ((flags & ACL_IS_DIR) == ACL_IS_DIR); + + zacl = smb_fsop_aclalloc(acl->sl_acecnt, flags); + + zace = zacl->acl_aclp; + sim = sib.sib_maps; + + for (i = 0; ace = smb_ace_get(acl, i); i++, zace++, sim++) { + zace->a_type = ace->se_header.se_type & ACE_ALL_TYPES; + zace->a_access_mask = smb_ace_mask_g2s(ace->se_mask); + zace->a_flags = smb_ace_flags_tozfs(ace->se_header.se_flags, + isdir); + + if (nt_sid_is_equal(&ace->se_sid, &everyone_sid)) + zace->a_flags |= ACE_EVERYONE; + else { + sim->sim_id = &zace->a_who; + idm_stat = smb_idmap_batch_getid(sib.sib_idmaph, sim, + &ace->se_sid, -1); + + if (idm_stat != IDMAP_SUCCESS) { + smb_fsop_aclfree(zacl); + smb_idmap_batch_destroy(&sib); + return (NT_STATUS_INTERNAL_ERROR); + } + } + } + + idm_stat = smb_idmap_batch_getmappings(&sib); + if (idm_stat != IDMAP_SUCCESS) { + smb_fsop_aclfree(zacl); + smb_idmap_batch_destroy(&sib); + return (NT_STATUS_NONE_MAPPED); + } + + /* + * Set the ACEs group flag based on the type of ID returned. + */ + zace = zacl->acl_aclp; + sim = sib.sib_maps; + for (i = 0; i < acl->sl_acecnt; i++, zace++, sim++) { + if (zace->a_flags & ACE_EVERYONE) + continue; + + if (sim->sim_idtype == SMB_IDMAP_GROUP) + zace->a_flags |= ACE_IDENTIFIER_GROUP; + } + + smb_idmap_batch_destroy(&sib); + + *fs_acl = zacl; + return (NT_STATUS_SUCCESS); +} + +/* + * smb_acl_inheritable + * + * Checks to see if there are any inheritable ACEs in the + * given ZFS ACL. Returns the number of inheritable ACEs. + * + * The inherited ACL could be different based on the type of + * new object (file/dir) specified by 'is_dir'. + * + * Note that the input ACL is a ZFS ACL not Windows ACL. + * + * Any ACE except creator owner/group: + * + * FI DI NP #F #D + * ---- ---- ---- ---- ---- + * - - ? 0 0 + * X - - 1 1 + * X - X 1 0 + * - X - 0 1 + * - X X 0 1 + * X X - 1 1 + * X X X 1 1 + * + * Creator owner/group ACE: + * + * FI DI NP #F #D + * ---- ---- ---- ---- ---- + * - - ? 0 0 + * X - - 1r 1c + * X - X 1r 0 + * - X - 0 2 + * - X X 0 1r + * X X - 1r 2 + * X X X 1r 1r + * + * Legend: + * + * FI: File Inherit + * DI: Dir Inherit + * NP: No Propagate + * #F: #ACE for a new file + * #D: #ACE for a new dir + * + * X: bit is set + * -: bit is not set + * ?: don't care + * + * 1r: one owner/group ACE + * 1c: one creator owner/group ACE + */ +static int +smb_acl_inheritable(acl_t *zacl, int is_dir) +{ + int numaces; + int num_inheritable = 0; + ace_t *zace; + + if (zacl == NULL) + return (0); + + for (numaces = 0, zace = zacl->acl_aclp; + numaces < zacl->acl_cnt; + zace++, numaces++) { + switch (zace->a_flags & ACE_FD_INHERIT_ACE) { + case (ACE_FILE_INHERIT_ACE | ACE_DIRECTORY_INHERIT_ACE): + /* + * Files inherit an effective ACE. + * + * Dirs inherit an effective ACE. + * The inherited ACE is inheritable unless the + * ACE_NO_PROPAGATE_INHERIT_ACE bit flag is also set + */ + num_inheritable++; + + if (is_dir && ZACE_IS_CREATOR(zace) && + (ZACE_IS_PROPAGATE(zace))) { + num_inheritable++; + } + break; + + case ACE_FILE_INHERIT_ACE: + /* + * Files inherit as an effective ACE. + * + * Dirs inherit an inherit-only ACE + * unless the ACE_NO_PROPAGATE_INHERIT_ACE bit + * flag is also set. + */ + if (is_dir == 0) + num_inheritable++; + else if (ZACE_IS_PROPAGATE(zace)) + num_inheritable++; + break; + + case ACE_DIRECTORY_INHERIT_ACE: + /* + * No effect on files + * + * Dirs inherit an effective ACE. + * The inherited ACE is inheritable unless the + * ACE_NO_PROPAGATE_INHERIT_ACE bit flag is also set. + */ + if (is_dir == 0) + break; + + num_inheritable++; + + if (ZACE_IS_CREATOR(zace) && + (ZACE_IS_PROPAGATE(zace))) + num_inheritable++; + break; + + default: + break; + } + } + + return (num_inheritable); +} + +#define DEFAULT_DACL_ACENUM 2 +/* + * Default ACL: + * owner: full access + * SYSTEM: full access + */ +static ace_t default_dacl[DEFAULT_DACL_ACENUM] = { + { (uid_t)-1, ACE_ALL_PERMS, 0, ACE_ACCESS_ALLOWED_ACE_TYPE }, + { IDMAP_WK_LOCAL_SYSTEM_GID, ACE_ALL_PERMS, ACE_IDENTIFIER_GROUP, + ACE_ACCESS_ALLOWED_ACE_TYPE } +}; + +/* + * smb_acl_inherit + * + * Manufacture the inherited ACL from the given ACL considering + * the new object type (file/dir) specified by 'is_dir'. The + * returned ACL is used in smb_fsop_create/smb_fsop_mkdir functions. + * This function implements Windows inheritance rules. + * + * Note that the in/our ACLs are ZFS ACLs not Windows ACLs + */ +acl_t * +smb_acl_inherit(acl_t *dir_zacl, int is_dir, int which_acl, uid_t owner_uid) +{ + boolean_t use_default = B_FALSE; + int num_inheritable = 0; + int numaces; + ace_t *dir_zace; + acl_t *new_zacl; + ace_t *new_zace; + + num_inheritable = smb_acl_inheritable(dir_zacl, is_dir); + + if (num_inheritable == 0) { + if (which_acl == SMB_DACL_SECINFO) { + /* No inheritable access ACEs -> default DACL */ + num_inheritable = DEFAULT_DACL_ACENUM; + use_default = B_TRUE; + } else { + return (NULL); + } + } + + new_zacl = smb_fsop_aclalloc(num_inheritable, ACL_AUTO_INHERIT); + new_zace = new_zacl->acl_aclp; + + if (use_default) { + bcopy(default_dacl, new_zacl->acl_aclp, sizeof (default_dacl)); + new_zace->a_who = owner_uid; + return (new_zacl); + } + + for (numaces = 0, dir_zace = dir_zacl->acl_aclp; + numaces < dir_zacl->acl_cnt; + dir_zace++, numaces++) { + switch (dir_zace->a_flags & ACE_FD_INHERIT_ACE) { + case (ACE_FILE_INHERIT_ACE | ACE_DIRECTORY_INHERIT_ACE): + /* + * Files inherit an effective ACE. + * + * Dirs inherit an effective ACE. + * The inherited ACE is inheritable unless the + * ACE_NO_PROPAGATE_INHERIT_ACE bit flag is also set + */ + smb_ace_inherit(dir_zace, new_zace, is_dir); + new_zace++; + + if (is_dir && ZACE_IS_CREATOR(dir_zace) && + (ZACE_IS_PROPAGATE(dir_zace))) { + *new_zace = *dir_zace; + new_zace->a_flags |= (ACE_INHERIT_ONLY_ACE | + ACE_INHERITED_ACE); + new_zace++; + } + break; + + case ACE_FILE_INHERIT_ACE: + /* + * Files inherit as an effective ACE. + * + * Dirs inherit an inherit-only ACE + * unless the ACE_NO_PROPAGATE_INHERIT_ACE bit + * flag is also set. + */ + if (is_dir == 0) { + smb_ace_inherit(dir_zace, new_zace, is_dir); + new_zace++; + } else if (ZACE_IS_PROPAGATE(dir_zace)) { + *new_zace = *dir_zace; + new_zace->a_flags |= (ACE_INHERIT_ONLY_ACE | + ACE_INHERITED_ACE); + new_zace++; + } + break; + + case ACE_DIRECTORY_INHERIT_ACE: + /* + * No effect on files + * + * Dirs inherit an effective ACE. + * The inherited ACE is inheritable unless the + * ACE_NO_PROPAGATE_INHERIT_ACE bit flag is also set. + */ + if (is_dir == 0) + break; + + smb_ace_inherit(dir_zace, new_zace, is_dir); + new_zace++; + + if (ZACE_IS_CREATOR(dir_zace) && + (ZACE_IS_PROPAGATE(dir_zace))) { + *new_zace = *dir_zace; + new_zace->a_flags |= (ACE_INHERIT_ONLY_ACE | + ACE_INHERITED_ACE); + new_zace++; + } + + break; + + default: + break; + } + } + + return (new_zacl); +} + +static void +smb_ace_inherit(ace_t *dir_zace, ace_t *zace, int is_dir) +{ + *zace = *dir_zace; + if (!(is_dir && ZACE_IS_PROPAGATE(dir_zace))) + zace->a_flags &= ~ACE_INHERIT_FLAGS; + zace->a_flags |= ACE_INHERITED_ACE; + + /* + * Replace creator owner/group ACEs with + * actual owner/group ACEs. + */ + if (ZACE_IS_CREATOR_OWNER(dir_zace)) { + zace->a_who = (uid_t)-1; + zace->a_flags |= ACE_OWNER; + } else if (ZACE_IS_CREATOR_GROUP(dir_zace)) { + zace->a_who = (uid_t)-1; + zace->a_flags |= ACE_GROUP; + } +} + +static uint16_t +smb_ace_flags_tozfs(uint8_t c_flags, int isdir) +{ + uint16_t z_flags = 0; + + if (c_flags & SUCCESSFUL_ACCESS_ACE_FLAG) + z_flags |= ACE_SUCCESSFUL_ACCESS_ACE_FLAG; + + if (c_flags & FAILED_ACCESS_ACE_FLAG) + z_flags |= ACE_FAILED_ACCESS_ACE_FLAG; + + if (c_flags & INHERITED_ACE) + z_flags |= ACE_INHERITED_ACE; + + /* + * ZFS doesn't like any inheritance flags to be set on a + * file's ACE, only directories. Windows doesn't care. + */ + if (isdir) + z_flags |= (c_flags & ACE_INHERIT_FLAGS); + + return (z_flags); +} + +static uint8_t +smb_ace_flags_fromzfs(uint16_t z_flags) +{ + uint8_t c_flags; + + c_flags = z_flags & ACE_INHERIT_FLAGS; + + if (z_flags & ACE_SUCCESSFUL_ACCESS_ACE_FLAG) + c_flags |= SUCCESSFUL_ACCESS_ACE_FLAG; + + if (z_flags & ACE_FAILED_ACCESS_ACE_FLAG) + c_flags |= FAILED_ACCESS_ACE_FLAG; + + if (z_flags & ACE_INHERITED_ACE) + c_flags |= INHERITED_ACE; + + return (c_flags); +} + +/* + * This is generic (ACL version 2) vs. object-specific + * (ACL version 4) ACE types. + */ +int +smb_ace_is_generic(int type) +{ + switch (type) { + case ACE_ACCESS_ALLOWED_ACE_TYPE: + case ACE_ACCESS_DENIED_ACE_TYPE: + case ACE_SYSTEM_AUDIT_ACE_TYPE: + case ACE_SYSTEM_ALARM_ACE_TYPE: + case ACE_ACCESS_ALLOWED_CALLBACK_ACE_TYPE: + case ACE_ACCESS_DENIED_CALLBACK_ACE_TYPE: + case ACE_SYSTEM_AUDIT_CALLBACK_ACE_TYPE: + case ACE_SYSTEM_ALARM_CALLBACK_ACE_TYPE: + return (1); + + default: + break; + } + + return (0); +} + +int +smb_ace_is_access(int type) +{ + switch (type) { + case ACE_ACCESS_ALLOWED_ACE_TYPE: + case ACE_ACCESS_DENIED_ACE_TYPE: + case ACE_ACCESS_ALLOWED_COMPOUND_ACE_TYPE: + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_ACCESS_ALLOWED_CALLBACK_ACE_TYPE: + case ACE_ACCESS_DENIED_CALLBACK_ACE_TYPE: + case ACE_ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE: + return (1); + + default: + break; + } + + return (0); +} + +int +smb_ace_is_audit(int type) +{ + switch (type) { + case ACE_SYSTEM_AUDIT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_CALLBACK_ACE_TYPE: + case ACE_SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE: + return (1); + + default: + break; + } + + return (0); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_alloc.c b/usr/src/uts/common/fs/smbsrv/smb_alloc.c new file mode 100644 index 000000000000..24de6ea71214 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_alloc.c @@ -0,0 +1,117 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include + +#include + +#define MEM_HDR_SIZE 8 +static uint32_t mem_get_size(void *ptr); + +void * +mem_malloc(uint32_t size) +{ + uint8_t *p; + + size += MEM_HDR_SIZE; + p = kmem_alloc(size, KM_SLEEP); + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + *(uint32_t *)p = size; + p += MEM_HDR_SIZE; + + return (p); +} + +void * +mem_zalloc(uint32_t size) +{ + uint8_t *p; + + p = mem_malloc(size); + (void) memset(p, 0, size); + return (p); +} + +char * +mem_strdup(const char *ptr) +{ + char *p; + size_t size; + + size = strlen(ptr) + 1; + p = mem_malloc(size); + (void) memcpy(p, ptr, size); + return (p); +} + +static uint32_t +mem_get_size(void *ptr) +{ + uint32_t *p; + + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + p = (uint32_t *)((uint8_t *)ptr - MEM_HDR_SIZE); + + return (*p); +} + +void * +mem_realloc(void *ptr, uint32_t size) +{ + void *new_ptr; + + if (ptr == NULL) + return (mem_malloc(size)); + + if (size == 0) { + smb_mem_free(ptr); + return (NULL); + } + + new_ptr = mem_malloc(size); + (void) memcpy(new_ptr, ptr, mem_get_size(ptr)); + smb_mem_free(ptr); + + return (new_ptr); +} + +void +smb_mem_free(void *ptr) +{ + uint8_t *p; + + if (ptr == 0) + return; + + p = (uint8_t *)ptr - MEM_HDR_SIZE; + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + kmem_free(p, *(uint32_t *)p); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_books.c b/usr/src/uts/common/fs/smbsrv/smb_books.c new file mode 100644 index 000000000000..5e94d8a6f13b --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_books.c @@ -0,0 +1,170 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file contains functions which destruct SMB session, tree, file, + * user and xa structures. + */ + +#include +#include +#include + +/* + * If this user is found in the user list, + * return 1 on the first occurrence + * else return 0 if end of list is reached + */ + +/* + * Return # of users + */ +uint32_t +smb_user_get_num(void) +{ + smb_session_t *sn = NULL; + smb_llist_t *ulist; + uint32_t cnt = 0; + + smb_svcstate_lock_read(&smb_info.si_svc_sm_ctx); + while ((sn = smb_svcstate_session_getnext(&smb_info.si_svc_sm_ctx, sn)) + != NULL) { + ASSERT(sn->s_magic == SMB_SESSION_MAGIC); + ulist = &sn->s_user_list; + cnt += smb_llist_get_count(ulist); + } + smb_svcstate_unlock(&smb_info.si_svc_sm_ctx); + return (cnt); +} + +static int +smb_dr_user_create(smb_dr_user_ctx_t *uinfo, uint64_t sess_id, uint16_t uid, + char *domain, char *acct, char *workstation, uint32_t ipaddr, + int32_t native_os, time_t logon_time, uint32_t flags) +{ + if (!domain || !acct || !workstation) + return (-1); + + uinfo->du_session_id = sess_id; + uinfo->du_uid = uid; + + uinfo->du_domain_len = strlen(domain) + 1; + uinfo->du_domain = smb_kstrdup(domain, uinfo->du_domain_len); + + uinfo->du_account_len = strlen(acct) + 1; + uinfo->du_account = smb_kstrdup(acct, uinfo->du_account_len); + + uinfo->du_workstation_len = strlen(workstation) + 1; + uinfo->du_workstation = smb_kstrdup(workstation, + uinfo->du_workstation_len); + + uinfo->du_native_os = native_os; + uinfo->du_ipaddr = ipaddr; + uinfo->du_logon_time = (int64_t)logon_time; + uinfo->du_flags = flags; + + return (0); +} + +void +smb_dr_user_free(smb_dr_user_ctx_t *uinfo) +{ + if (!uinfo) + return; + + if (uinfo->du_domain) + kmem_free(uinfo->du_domain, uinfo->du_domain_len); + + if (uinfo->du_account) + kmem_free(uinfo->du_account, uinfo->du_account_len); + + if (uinfo->du_workstation) + kmem_free(uinfo->du_workstation, uinfo->du_workstation_len); +} + +void +smb_dr_ulist_free(smb_dr_ulist_t *ulist) +{ + int i; + + if (!ulist) + return; + + for (i = 0; i < ulist->dul_cnt; i++) { + smb_dr_user_free(&ulist->dul_users[i]); + } +} + +int +smb_dr_ulist_get(int offset, smb_dr_ulist_t *dr_ulist) +{ + smb_session_t *sn = NULL; + smb_user_t *user; + smb_llist_t *ulist; + smb_dr_user_ctx_t *uinfo; + int cnt = 0, skip = 0; + + if (!dr_ulist) + return (-1); + + smb_svcstate_lock_read(&smb_info.si_svc_sm_ctx); + while ((sn = smb_svcstate_session_getnext(&smb_info.si_svc_sm_ctx, sn)) + != NULL && cnt < SMB_DR_MAX_USERS) { + ASSERT(sn->s_magic == SMB_SESSION_MAGIC); + ulist = &sn->s_user_list; + smb_llist_enter(ulist, RW_READER); + user = smb_llist_head(ulist); + while (user && cnt < SMB_DR_MAX_USERS) { + ASSERT(user->u_magic == SMB_USER_MAGIC); + mutex_enter(&user->u_mutex); + if (user->u_state == SMB_USER_STATE_LOGGED_IN) { + if (skip++ < offset) { + mutex_exit(&user->u_mutex); + user = smb_llist_next(ulist, user); + continue; + } + + uinfo = &dr_ulist->dul_users[cnt++]; + if (smb_dr_user_create(uinfo, sn->s_kid, + user->u_uid, user->u_domain, user->u_name, + sn->workstation, sn->ipaddr, sn->native_os, + user->u_logon_time, user->u_flags) != 0) { + cnt--; + mutex_exit(&user->u_mutex); + user = smb_llist_next(ulist, user); + continue; + } + } + mutex_exit(&user->u_mutex); + user = smb_llist_next(ulist, user); + } + smb_llist_exit(ulist); + } + smb_svcstate_unlock(&smb_info.si_svc_sm_ctx); + dr_ulist->dul_cnt = cnt; + return (cnt); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_check_directory.c b/usr/src/uts/common/fs/smbsrv/smb_check_directory.c new file mode 100644 index 000000000000..d29e55eda624 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_check_directory.c @@ -0,0 +1,126 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: check_directory + * + * This SMB is used to verify that a path exists and is a directory. No + * error is returned if the given path exists and the client has read + * access to it. Client machines which maintain a concept of a "working + * directory" will find this useful to verify the validity of a "change + * working directory" command. Note that the servers do NOT have a concept + * of working directory for a particular client. The client must always + * supply full pathnames relative to the Tid in the SMB header. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes; min = 2 + * UCHAR BufferFormat; 0x04 + * STRING DirectoryPath[]; Directory path + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes = 0 + * + * DOS clients, in particular, depend on the SMB_ERR_BAD_PATH return code + * if the directory is not found. + * + * 4.3.3.1 Errors + * + * ERRDOS/ERRbadfile + * ERRDOS/ERRbadpath + * ERRDOS/ERRnoaccess + * ERRHRD/ERRdata + * ERRSRV/ERRinvid + * ERRSRV/ERRbaduid + * ERRSRV/ERRaccess + */ + +#include +#include + +int +smb_com_check_directory(struct smb_request *sr) +{ + int rc; + struct smb_node *dnode; + + if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, + ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + if (smbsr_decode_data(sr, "%S", sr, &sr->arg.dirop.fqi.path) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->arg.dirop.fqi.srch_attr = 0; + + rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_EXIST); + if (rc) { + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + /* + * Release hold on dir_snode taken in smbd_fs_query() + */ + + smb_node_release(sr->arg.dirop.fqi.dir_snode); + + dnode = sr->arg.dirop.fqi.last_snode; + + if (sr->arg.dirop.fqi.last_attr.sa_vattr.va_type != VDIR) { + smb_node_release(dnode); + SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); + + smbsr_raise_errno(sr, ENOTDIR); + /* NOTREACHED */ + } + + rc = smb_fsop_access(sr, sr->user_cr, dnode, FILE_TRAVERSE); + + smb_node_release(dnode); + SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); + + if (rc != 0) { + smbsr_raise_cifs_error(sr, + NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + smbsr_encode_empty_result(sr); + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_close.c b/usr/src/uts/common/fs/smbsrv/smb_close.c new file mode 100644 index 000000000000..c4d129c2e4d1 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_close.c @@ -0,0 +1,148 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + + +/* + * Close a file by fid. All locks or other resources held by the + * requesting process on the file should be released by the server. + * The requesting process can no longer use the fid for further + * file access requests. + * + * If LastWriteTime is non-zero, it should be used to set the file + * timestamp. Otherwise, file system should set the timestamp. + * Failure to set the timestamp, even if requested by the client, + * should not result in an error response from the server. + */ +int +smb_com_close(struct smb_request *sr) +{ + uint32_t last_wtime; + int rc = 0; + + rc = smbsr_decode_vwv(sr, "wl", &sr->smb_fid, &last_wtime); + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + rc = smb_common_close(sr, last_wtime); + if (rc) { + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + smbsr_encode_empty_result(sr); + return (SDRC_NORMAL_REPLY); +} + +/* + * Close the file represented by fid and then disconnect the + * associated tree. + */ +int +smb_com_close_and_tree_disconnect(struct smb_request *sr) +{ + uint32_t last_wtime; + int rc = 0; + + rc = smbsr_decode_vwv(sr, "wl", &sr->smb_fid, &last_wtime); + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + rc = smb_common_close(sr, last_wtime); + smbsr_rq_notify(sr, sr->session, sr->tid_tree); + smb_tree_disconnect(sr->tid_tree); + + if (rc) { + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + smbsr_encode_empty_result(sr); + return (SDRC_NORMAL_REPLY); +} + +/* + * smb_common_close + * + * Common close function called by SmbClose, SmbWriteAndClose, + * and SMBCloseAndTreeDisconnect. + */ +int +smb_common_close(struct smb_request *sr, uint32_t last_wtime) +{ + return (smb_ofile_close(sr->fid_ofile, last_wtime)); +} + +/* + * smb_commit_delete_on_close() + * + * Check for the DeleteOnClose flag on the smb file and set it on the + * smb node if it is not already set. This will inhibit subsequent + * open requests. The delete-on-close credentials should be set to the + * user credentials of the current open file instance. + * + * When DeleteOnClose is set on an smb_node, the common open code will + * reject subsequent open requests for the file. Observation of Windows + * 2000 indicates that subsequent opens should be allowed (assuming + * there would be no sharing violation) until the file is closed using + * the fid on which the DeleteOnClose was requested. + * + * If there are multiple opens with delete-on-close create options, + * whichever the first file handle is closed will trigger the node to be + * marked as delete-on-close. The credentials of that ofile will be used + * as the delete-on-close credentials of the node. + */ +void +smb_commit_delete_on_close(struct smb_ofile *ofile) +{ + struct smb_node *node = ofile->f_node; + + if (!(node->flags & NODE_FLAGS_DELETE_ON_CLOSE) && + (ofile->f_flags & SMB_OFLAGS_SET_DELETE_ON_CLOSE)) { + node->flags |= NODE_FLAGS_DELETE_ON_CLOSE; + crhold(node->delete_on_close_cred = ofile->f_cr); + } +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_common_lock.c b/usr/src/uts/common/fs/smbsrv/smb_common_lock.c new file mode 100644 index 000000000000..a11a321a7feb --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_common_lock.c @@ -0,0 +1,424 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB Locking library functions. + */ + +#include + + +/* + * Oplock timeout configuration. + */ +#define OPLOCK_RETRIES 2 +time_t smb_oplock_timeout = OPLOCK_STD_TIMEOUT; + + +/* + * Oplock functionality enable/disable (see smb_oplock_init). + */ + +/* + * Magic 0xFF 'S' 'M' 'B' + * smb_com a byte, the "first" command + * Error a 4-byte union, ignored in a request + * smb_flg a one byte set of eight flags + * smb_flg2 a two byte set of 16 flags + * . twelve reserved bytes, have a role + * in connectionless transports (IPX, UDP?) + * smb_tid a 16-bit tree ID, a mount point sorta, + * 0xFFFF is this command does not have + * or require a tree context + * smb_pid a 16-bit process ID + * smb_uid a 16-bit user ID, specific to this "session" + * and mapped to a system (bona-fide) UID + * smb_mid a 16-bit multiplex ID, used to differentiate + * multiple simultaneous requests from the same + * process (pid) (ref RPC "xid") + * + * SMB_COM_LOCKING_ANDX allows both locking and/or unlocking of file range(s). + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 8 + * UCHAR AndXCommand; Secondary (X) command; 0xFF = none + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * USHORT Fid; File handle + * UCHAR LockType; See LockType table below + * UCHAR OplockLevel; The new oplock level + * ULONG Timeout; Milliseconds to wait for unlock + * USHORT NumberOfUnlocks; Num. unlock range structs following + * USHORT NumberOfLocks; Num. lock range structs following + * USHORT ByteCount; Count of data bytes + * LOCKING_ANDX_RANGE Unlocks[]; Unlock ranges + * LOCKING_ANDX_RANGE Locks[]; Lock ranges + * + * LockType Flag Name Value Description + * ============================ ===== ================================ + * + * LOCKING_ANDX_SHARED_LOCK 0x01 Read-only lock + * LOCKING_ANDX_OPLOCK_RELEASE 0x02 Oplock break notification + * LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 Change lock type + * LOCKING_ANDX_CANCEL_LOCK 0x08 Cancel outstanding request + * LOCKING_ANDX_LARGE_FILES 0x10 Large file locking format + * + * LOCKING_ANDX_RANGE Format + * ===================================================================== + * + * USHORT Pid; PID of process "owning" lock + * ULONG Offset; Offset to bytes to [un]lock + * ULONG Length; Number of bytes to [un]lock + * + * Large File LOCKING_ANDX_RANGE Format + * ===================================================================== + * + * USHORT Pid; PID of process "owning" lock + * USHORT Pad; Pad to DWORD align (mbz) + * ULONG OffsetHigh; Offset to bytes to [un]lock + * (high) + * ULONG OffsetLow; Offset to bytes to [un]lock (low) + * ULONG LengthHigh; Number of bytes to [un]lock + * (high) + * ULONG LengthLow; Number of bytes to [un]lock (low) + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 2 + * UCHAR AndXCommand; Secondary (X) command; 0xFF = + * none + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * USHORT ByteCount; Count of data bytes = 0 + * + */ + +/* + * smb_acquire_oplock + * + * Attempt to acquire an oplock. Note that the oplock granted may be + * none, i.e. the oplock was not granted. + * + * We may have to break an oplock in order to acquire one, and depending + * on what action is taken by the other client (unlock or close), we may + * or may not end up with an oplock. The type of oplock being requested + * is passed in level_requested, the result is returned in level_granted + * and is only valid if the status is NT_STATUS_SUCCESS. + * + * The Returns NT status codes: + * NT_STATUS_SUCCESS + * NT_STATUS_CONNECTION_DISCONNECTED + */ +DWORD +smb_acquire_oplock( + struct smb_request *sr, + struct smb_ofile *file, + unsigned int level_requested, + unsigned int *level_granted) +{ + struct smb_node *node = file->f_node; + unsigned int level; + int oplock_owner; + DWORD status; + smb_user_t *user; + + user = sr->uid_user; + ASSERT(user); + + level = level_requested & MYF_OPLOCK_MASK; + *level_granted = MYF_OPLOCK_NONE; + + if (smb_info.si.skc_oplock_enable == 0) + return (NT_STATUS_SUCCESS); + + if (fsd_chkcap(&sr->tid_tree->t_fsd, FSOLF_DISABLE_OPLOCKS) > 0) + return (NT_STATUS_SUCCESS); + +restart: + oplock_owner = 0; + + /* + * I'm not convinced the client redirector will send multiple + * opens requesting a batch oplock for the same file. I think + * the client redirector will handle the multiple instances + * and only send a single open to the server. The the original + * implementation supported it, however, so I'll leave it here + * for now. + * + * Grant an oplock to the requester if this session is the + * only one that has the file open, regardless of the number + * of instances of the file opened by this session. We grant + * any oplock requested to the owner. + */ + if (node->n_refcnt == 1 || oplock_owner == 1) { + if (MYF_IS_EXCLUSIVE_OPLOCK(level)) { + node->flags &= ~NODE_OPLOCKS_IN_FORCE; + node->flags |= NODE_EXCLUSIVE_OPLOCK; + node->n_oplock.op_ofile = file; + } else if (MYF_IS_BATCH_OPLOCK(level)) { + node->flags &= ~NODE_OPLOCKS_IN_FORCE; + node->flags |= NODE_BATCH_OPLOCK; + node->n_oplock.op_ofile = file; + } else { + level &= ~MYF_OPLOCK_MASK; + } + + *level_granted = level; + return (NT_STATUS_SUCCESS); + } + + /* + * Other clients have this file open but they do not have any + * oplocks in force, so we must reject this oplock request. + */ + if (node->n_refcnt > 1 && OPLOCKS_IN_FORCE(node) == 0) { + return (NT_STATUS_SUCCESS); + } + + /* + * Someone has an oplock, we need to break it. + */ + if ((status = smb_break_oplock(sr, node)) == NT_STATUS_SUCCESS) + goto restart; + + return (status); +} + + +/* + * smb_break_oplock + * + * The oplock break may succeed for multiple reasons: file close, oplock + * release, holder connection dropped, requesting client disconnect etc. + * Whatever the reason, the oplock should be broken when this function + * returns. The exceptions are when the client making this request gets + * disconnected or when another client is handling the break and it gets + * disconnected. + * + * Returns NT status codes: + * NT_STATUS_SUCCESS No oplock in force, i.e. the + * oplock has been broken. + * NT_STATUS_CONNECTION_DISCONNECTED Requesting client disconnected. + * NT_STATUS_INTERNAL_ERROR + */ +DWORD +smb_break_oplock(struct smb_request *sr, struct smb_node *node) +{ + struct smb_session *sent_session; + struct smb_ofile *sent_ofile; + struct mbuf_chain mbc; + int retries = 0; + int tid; + unsigned short fid; + clock_t elapsed_time; + clock_t max_time; + boolean_t flag; + smb_user_t *user; + + user = sr->uid_user; + ASSERT(user); + + smb_rwx_rwenter(&node->n_lock, RW_WRITER); + + if (node->n_oplock.op_flags & OPLOCK_FLAG_BREAKING) { + + elapsed_time = 0; + max_time = MSEC_TO_TICK(smb_oplock_timeout * OPLOCK_RETRIES); + /* + * Another client is already attempting to break the oplock. + * We wait for it to finish. If the caller was trying to + * acquire an oplock, he should retry in case the client's + * connection was dropped while trying to break the oplock. + * + * If the holder's connection has been dropped, we yield to + * allow the thread handling the break to detect it and set + * the flags. + */ + while ((node->n_oplock.op_flags & OPLOCK_FLAG_BREAKING) && + (elapsed_time < max_time)) { + clock_t timeleft; + + timeleft = smb_rwx_rwwait(&node->n_lock, max_time); + if (timeleft == -1) { + elapsed_time = max_time; + } else { + elapsed_time += max_time - timeleft; + } + } + /* + * If there are no oplocks in force we're done. + * Otherwise fall through and break the oplock. + */ + if (OPLOCKS_IN_FORCE(node) == 0) { + smb_rwx_rwexit(&node->n_lock); + return (NT_STATUS_SUCCESS); + } else { + /* + * Should we clear the + * LOCK_BREAKING_OPLOCK flag? + */ + smb_rwx_rwexit(&node->n_lock); + return (NT_STATUS_INTERNAL_ERROR); + } + } + + /* + * No oplock break is in progress so we start one. + */ + sent_ofile = node->n_oplock.op_ofile; + sent_session = sent_ofile->f_session; + ASSERT(sent_session); + /* + * If a client has an OPLOCK on a file it would not break it because + * another of its processes wants to open the same file. However, if + * a client were to behave like that it would create a deadlock in the + * code that follows. For now we leave the ASSERT(). Eventually the + * code will have to be more defensive. + */ + ASSERT(sent_session != sr->session); + node->n_oplock.op_flags |= OPLOCK_FLAG_BREAKING; + smb_rwx_rwexit(&node->n_lock); + + /* + * IR #104382 + * What we could find from this panic was that the tree field + * of sent_ofile structure points to an invalid memory page, + * but we couldn't find why exactly this happened. So, this is + * a work around till we can find the real problem. + */ + tid = sent_ofile->f_tree->t_tid; + fid = sent_ofile->f_fid; + + max_time = MSEC_TO_TICK(smb_oplock_timeout); + do { + MBC_INIT(&mbc, MLEN); + (void) smb_encode_mbc(&mbc, "Mb19.wwwwbb3.ww10.", + SMB_COM_LOCKING_ANDX, /* Command */ + tid, /* TID */ + 0xffff, /* PID */ + 0, /* UID */ + 0xffff, /* MID oplock break */ + 8, /* parameter words=8 */ + 0xff, /* 0xFF=none */ + fid, /* File handle */ + LOCKING_ANDX_OPLOCK_RELEASE); + + flag = B_TRUE; + smb_rwx_rwenter(&sent_session->s_lock, RW_WRITER); + while (flag) { + switch (sent_session->s_state) { + case SMB_SESSION_STATE_DISCONNECTED: + case SMB_SESSION_STATE_TERMINATED: + smb_rwx_rwexit(&sent_session->s_lock); + smb_rwx_rwenter(&node->n_lock, RW_WRITER); + node->flags &= ~NODE_OPLOCKS_IN_FORCE; + node->n_oplock.op_flags &= + ~OPLOCK_FLAG_BREAKING; + node->n_oplock.op_ofile = NULL; + smb_rwx_rwexit(&node->n_lock); + return (NT_STATUS_SUCCESS); + + case SMB_SESSION_STATE_OPLOCK_BREAKING: + flag = B_FALSE; + break; + + case SMB_SESSION_STATE_NEGOTIATED: + sent_session->s_state = + SMB_SESSION_STATE_OPLOCK_BREAKING; + flag = B_FALSE; + break; + + default: + (void) smb_rwx_rwwait(&sent_session->s_lock, + -1); + break; + } + } + smb_rwx_rwexit(&sent_session->s_lock); + + (void) smb_session_send(sent_session, 0, &mbc); + + elapsed_time = 0; + smb_rwx_rwenter(&node->n_lock, RW_WRITER); + while ((node->n_oplock.op_flags & OPLOCK_FLAG_BREAKING) && + (elapsed_time < max_time)) { + clock_t timeleft; + + timeleft = smb_rwx_rwwait(&node->n_lock, max_time); + if (timeleft == -1) { + elapsed_time = max_time; + } else { + elapsed_time += max_time - timeleft; + } + } + if (OPLOCKS_IN_FORCE(node) == 0) { + smb_rwx_rwexit(&node->n_lock); + return (NT_STATUS_SUCCESS); + } + } while (++retries < OPLOCK_RETRIES); + + /* + * Retries exhausted and timed out. + * Cancel the oplock and continue. + */ + node->flags &= ~NODE_OPLOCKS_IN_FORCE; + node->n_oplock.op_flags &= ~OPLOCK_FLAG_BREAKING; + node->n_oplock.op_ofile = 0; + smb_rwx_rwexit(&node->n_lock); + return (NT_STATUS_SUCCESS); +} + + +/* + * smb_release_oplock + * + * The original code supported batch oplock inheritance but I'm not + * convinced the client redirector will open multiple instances of a + * file with batch oplocks on the server (see smb_acquire_oplock). + */ +void /*ARGSUSED*/ +smb_release_oplock(struct smb_ofile *file, int reason) +{ + struct smb_node *node = file->f_node; + + smb_rwx_rwenter(&node->n_lock, RW_WRITER); + if ((node->n_oplock.op_ofile != file) || OPLOCKS_IN_FORCE(node) == 0) { + smb_rwx_rwexit(&node->n_lock); + return; + } + + node->flags &= ~NODE_OPLOCKS_IN_FORCE; + node->n_oplock.op_ofile = 0; + + if (node->n_oplock.op_flags & OPLOCK_FLAG_BREAKING) { + node->n_oplock.op_flags &= ~OPLOCK_FLAG_BREAKING; + } + smb_rwx_rwexit(&node->n_lock); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_common_open.c b/usr/src/uts/common/fs/smbsrv/smb_common_open.c new file mode 100644 index 000000000000..30058e8f824a --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_common_open.c @@ -0,0 +1,982 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module provides the common open functionality to the various + * open and create SMB interface functions. + */ + +#include +#include +#include +#include +#include +#include +#include + +extern uint32_t smb_is_executable(char *path); + +#define DENY_READ(share_access) ((share_access & FILE_SHARE_READ) == 0) + +#define DENY_WRITE(share_access) ((share_access & FILE_SHARE_WRITE) == 0) + +#define DENY_DELETE(share_access) ((share_access & FILE_SHARE_DELETE) == 0) + +#define DENY_RW(share_access) \ + ((share_access & (FILE_SHARE_READ | FILE_SHARE_WRITE)) == 0) + +#define DENY_ALL(share_access) (share_access == 0) + +#define DENY_NONE(share_access) (share_access == FILE_SHARE_ALL) + +/* + * The default stability mode is to perform the write-through + * behaviour requested by the client. + */ +int smb_stable_mode = 0; + + +/* + * This macro is used to delete a newly created object + * if any error happens after creation of object. + */ +#define SMB_DEL_NEWOBJ(obj) \ + if (created) { \ + if (is_dir) \ + (void) smb_fsop_rmdir(sr, sr->user_cr, \ + obj.dir_snode, obj.last_comp, 0); \ + else \ + (void) smb_fsop_remove(sr, sr->user_cr, \ + obj.dir_snode, obj.last_comp, 0); \ + } + +/* + * smb_set_stability + * + * Set the default stability mode. Normal (mode is zero) means perform + * the write-through behaviour requested by the client. Synchronous + * (mode is non-zero) means journal everything regardless of the write + * through behaviour requested by the client. + */ +void +smb_set_stability(int mode) +{ + smb_stable_mode = mode; +} + +/* + * smb_access_generic_to_file + * + * Search MSDN for IoCreateFile to see following mapping. + * + * GENERIC_READ STANDARD_RIGHTS_READ, FILE_READ_DATA, + * FILE_READ_ATTRIBUTES and FILE_READ_EA + * + * GENERIC_WRITE STANDARD_RIGHTS_WRITE, FILE_WRITE_DATA, + * FILE_WRITE_ATTRIBUTES, FILE_WRITE_EA, and FILE_APPEND_DATA + * + * GENERIC_EXECUTE STANDARD_RIGHTS_EXECUTE, SYNCHRONIZE, and FILE_EXECUTE. + */ +uint32_t +smb_access_generic_to_file(uint32_t desired_access) +{ + uint32_t access = 0; + + if (desired_access & GENERIC_ALL) + return (FILE_ALL_ACCESS & ~SYNCHRONIZE); + + if (desired_access & GENERIC_EXECUTE) { + desired_access &= ~GENERIC_EXECUTE; + access |= (STANDARD_RIGHTS_EXECUTE | + SYNCHRONIZE | FILE_EXECUTE); + } + + if (desired_access & GENERIC_WRITE) { + desired_access &= ~GENERIC_WRITE; + access |= (FILE_GENERIC_WRITE & ~SYNCHRONIZE); + } + + if (desired_access & GENERIC_READ) { + desired_access &= ~GENERIC_READ; + access |= FILE_GENERIC_READ; + } + + return (access | desired_access); +} + +/* + * smb_omode_to_amask + * + * This function converts open modes used by Open and Open AndX + * commands to desired access bits used by NT Create AndX command. + */ +uint32_t +smb_omode_to_amask(uint32_t desired_access) +{ + switch (desired_access & SMB_DA_ACCESS_MASK) { + case SMB_DA_ACCESS_READ: + return (FILE_GENERIC_READ); + + case SMB_DA_ACCESS_WRITE: + return (FILE_GENERIC_WRITE); + + case SMB_DA_ACCESS_READ_WRITE: + return (FILE_GENERIC_READ | FILE_GENERIC_WRITE); + + case SMB_DA_ACCESS_EXECUTE: + return (FILE_GENERIC_EXECUTE); + } + + /* invalid open mode */ + return ((uint32_t)SMB_INVALID_AMASK); +} + +/* + * smb_denymode_to_sharemode + * + * This function converts deny modes used by Open and Open AndX + * commands to share access bits used by NT Create AndX command. + */ +uint32_t +smb_denymode_to_sharemode(uint32_t desired_access, char *fname) +{ + switch (desired_access & SMB_DA_SHARE_MASK) { + case SMB_DA_SHARE_COMPATIBILITY: + if (smb_is_executable(fname)) + return (FILE_SHARE_READ | FILE_SHARE_WRITE); + else { + if ((desired_access & + SMB_DA_ACCESS_MASK) == SMB_DA_ACCESS_READ) + return (FILE_SHARE_READ); + else + return (FILE_SHARE_NONE); + } + + case SMB_DA_SHARE_EXCLUSIVE: + return (FILE_SHARE_NONE); + + case SMB_DA_SHARE_DENY_WRITE: + return (FILE_SHARE_READ); + + case SMB_DA_SHARE_DENY_READ: + return (FILE_SHARE_WRITE); + + case SMB_DA_SHARE_DENY_NONE: + return (FILE_SHARE_READ | FILE_SHARE_WRITE); + } + + /* invalid deny mode */ + return ((uint32_t)SMB_INVALID_SHAREMODE); +} + +/* + * smb_ofun_to_crdisposition + * + * This function converts open function values used by Open and Open AndX + * commands to create disposition values used by NT Create AndX command. + */ +uint32_t +smb_ofun_to_crdisposition(uint16_t ofun) +{ + static int ofun_cr_map[3][2] = + { + { -1, FILE_CREATE }, + { FILE_OPEN, FILE_OPEN_IF }, + { FILE_OVERWRITE, FILE_OVERWRITE_IF } + }; + + int row = ofun & SMB_OFUN_OPEN_MASK; + int col = (ofun & SMB_OFUN_CREATE_MASK) >> 4; + + if (row == 3) + return ((uint32_t)SMB_INVALID_CRDISPOSITION); + + return (ofun_cr_map[row][col]); +} + +/* + * smb_open_share_check + * + * check file sharing rules for current open request + * against the given existing open. + * + * Returns NT_STATUS_SHARING_VIOLATION if there is any + * sharing conflict, otherwise returns NT_STATUS_SUCCESS. + */ +uint32_t +smb_open_share_check(struct smb_request *sr, + struct smb_node *node, + struct smb_ofile *open) +{ + uint32_t desired_access; + uint32_t share_access; + + desired_access = sr->arg.open.desired_access; + share_access = sr->arg.open.share_access; + + /* + * As far as I can tell share modes are not relevant to + * directories. The check for exclusive access (Deny RW) + * remains because I don't know whether or not it was here + * for a reason. + */ + if (node->attr.sa_vattr.va_type == VDIR) { + if (DENY_RW(open->f_share_access) && + (node->n_orig_uid != crgetuid(sr->user_cr))) { + return (NT_STATUS_SHARING_VIOLATION); + } + + return (NT_STATUS_SUCCESS); + } + + /* if it's just meta data */ + if ((open->f_granted_access & FILE_DATA_ALL) == 0) + return (NT_STATUS_SUCCESS); + + /* + * Check requested share access against the + * open granted (desired) access + */ + if (DENY_DELETE(share_access) && (open->f_granted_access & DELETE)) + return (NT_STATUS_SHARING_VIOLATION); + + if (DENY_READ(share_access) && + (open->f_granted_access & (FILE_READ_DATA | FILE_EXECUTE))) + return (NT_STATUS_SHARING_VIOLATION); + + if (DENY_WRITE(share_access) && + (open->f_granted_access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) + return (NT_STATUS_SHARING_VIOLATION); + + /* check requested desired access against the open share access */ + if (DENY_DELETE(open->f_share_access) && (desired_access & DELETE)) + return (NT_STATUS_SHARING_VIOLATION); + + if (DENY_READ(open->f_share_access) && + (desired_access & (FILE_READ_DATA | FILE_EXECUTE))) + return (NT_STATUS_SHARING_VIOLATION); + + if (DENY_WRITE(open->f_share_access) && + (desired_access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) + return (NT_STATUS_SHARING_VIOLATION); + + return (NT_STATUS_SUCCESS); +} + +/* + * smb_file_share_check + * + * check file sharing rules for current open request + * against all existing opens for a file. + * + * Returns NT_STATUS_SHARING_VIOLATION if there is any + * sharing conflict, otherwise returns NT_STATUS_SUCCESS. + */ +uint32_t +smb_file_share_check(struct smb_request *sr, struct smb_node *node) +{ + struct smb_ofile *open; + uint32_t status; + + if (node == 0 || node->n_refcnt <= 1) + return (NT_STATUS_SUCCESS); + + /* if it's just meta data */ + if ((sr->arg.open.desired_access & FILE_DATA_ALL) == 0) + return (NT_STATUS_SUCCESS); + + smb_llist_enter(&node->n_ofile_list, RW_READER); + open = smb_llist_head(&node->n_ofile_list); + while (open) { + status = smb_open_share_check(sr, node, open); + if (status == NT_STATUS_SHARING_VIOLATION) { + smb_llist_exit(&node->n_ofile_list); + return (status); + } + open = smb_llist_next(&node->n_ofile_list, open); + } + smb_llist_exit(&node->n_ofile_list); + + return (NT_STATUS_SUCCESS); +} + +/* + * smb_amask_to_amode + * Converts specific read/write access rights of access mask to access + * mode flags. + */ +int +smb_amask_to_amode(unsigned long amask) +{ + if ((amask & FILE_READ_DATA) && + (amask & (FILE_WRITE_DATA | FILE_APPEND_DATA))) + return (O_RDWR); + + if (amask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) + return (O_WRONLY); + + return (O_RDONLY); +} + +/* + * smb_open_subr + * + * Notes on write-through behaviour. It looks like pre-LM0.12 versions + * of the protocol specify the write-through mode when a file is opened, + * (SmbOpen, SmbOpenAndX) so the write calls (SmbWrite, SmbWriteAndClose, + * SmbWriteAndUnlock) don't need to contain a write-through flag. + * + * With LM0.12, the open calls (SmbCreateAndX, SmbNtTransactCreate) + * don't indicate which write-through mode to use. Instead the write + * calls (SmbWriteAndX, SmbWriteRaw) specify the mode on a per call + * basis. + * + * We don't care which open call was used to get us here, we just need + * to ensure that the write-through mode flag is copied from the open + * parameters to the node. We test the omode write-through flag in all + * write functions. + * + * This function will return NT status codes but it also raises errors, + * in which case it won't return to the caller. Be careful how you + * handle things in here. + */ +uint32_t +smb_open_subr(struct smb_request *sr) +{ + int created = 0; + struct smb_node *node = 0; + struct smb_node *dnode = 0; + struct smb_node *cur_node; + struct open_param *op = &sr->arg.open; + int rc; + struct smb_ofile *of; + smb_attr_t new_attr; + int pathlen; + int max_requested = 0; + uint32_t max_allowed; + unsigned int granted_oplock; + uint32_t status = NT_STATUS_SUCCESS; + int is_dir; + smb_error_t err; + int is_stream; + int lookup_flags = SMB_FOLLOW_LINKS; + uint32_t daccess; + + is_dir = (op->create_options & FILE_DIRECTORY_FILE) ? 1 : 0; + + if (is_dir) { + /* + * The file being created or opened is a directory file. + * With this flag, the Disposition parameter must be set to + * one of FILE_CREATE, FILE_OPEN, or FILE_OPEN_IF + */ + if ((op->create_disposition != FILE_CREATE) && + (op->create_disposition != FILE_OPEN_IF) && + (op->create_disposition != FILE_OPEN)) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_PARAMETER, + ERRDOS, ERROR_INVALID_ACCESS); + /* invalid open mode */ + /* NOTREACHED */ + } + } + + if (op->desired_access & MAXIMUM_ALLOWED) { + max_requested = 1; + op->desired_access &= ~MAXIMUM_ALLOWED; + } + op->desired_access = smb_access_generic_to_file(op->desired_access); + + if (sr->session->s_file_cnt >= SMB_SESSION_OFILE_MAX) { + + ASSERT(sr->uid_user); + cmn_err(CE_NOTE, "smbd[%s\\%s]: %s", sr->uid_user->u_domain, + sr->uid_user->u_name, + xlate_nt_status(NT_STATUS_TOO_MANY_OPENED_FILES)); + + smbsr_raise_cifs_error(sr, NT_STATUS_TOO_MANY_OPENED_FILES, + ERRDOS, ERROR_TOO_MANY_OPEN_FILES); + /* NOTREACHED */ + } + + /* This must be NULL at this point */ + sr->fid_ofile = NULL; + + op->devstate = 0; + + switch (sr->tid_tree->t_res_type & STYPE_MASK) { + case STYPE_DISKTREE: + break; + + case STYPE_IPC: + /* + * No further processing for IPC, we need to either + * raise an exception or return success here. + */ + if ((rc = smb_rpc_open(sr)) != 0) { + smbsr_raise_nt_error(sr, rc); + /* NOTREACHED */ + } else { + return (NT_STATUS_SUCCESS); + } + break; + + default: + smbsr_raise_error(sr, ERRSRV, ERRinvdevice); + /* NOTREACHED */ + break; + } + + if ((pathlen = strlen(op->fqi.path)) >= MAXPATHLEN) { + smbsr_raise_error(sr, ERRSRV, ERRfilespecs); + /* NOTREACHED */ + } + + /* + * Some clients pass null file names; NT interprets this as "\". + */ + if (pathlen == 0) { + op->fqi.path = "\\"; + pathlen = 1; + } + + op->fqi.srch_attr = op->fqi.srch_attr; + + if ((status = smb_validate_object_name(op->fqi.path, is_dir)) != 0) { + smbsr_raise_cifs_error(sr, status, ERRDOS, ERROR_INVALID_NAME); + /* NOTREACHED */ + } + + cur_node = op->fqi.dir_snode ? + op->fqi.dir_snode : sr->tid_tree->t_snode; + + if (rc = smb_pathname_reduce(sr, sr->user_cr, op->fqi.path, + sr->tid_tree->t_snode, cur_node, &op->fqi.dir_snode, + op->fqi.last_comp)) { + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + /* + * If the access mask has only DELETE set (ignore + * FILE_READ_ATTRIBUTES), then assume that this + * is a request to delete the link (if a link) + * and do not follow links. Otherwise, follow + * the link to the target. + */ + + daccess = op->desired_access & ~FILE_READ_ATTRIBUTES; + + if (daccess == DELETE) + lookup_flags &= ~SMB_FOLLOW_LINKS; + + rc = smb_fsop_lookup_name(sr, kcred, lookup_flags, + sr->tid_tree->t_snode, op->fqi.dir_snode, op->fqi.last_comp, + &op->fqi.last_snode, &op->fqi.last_attr); + + if (rc == 0) { + op->fqi.last_comp_was_found = 1; + (void) strcpy(op->fqi.last_comp_od, + op->fqi.last_snode->od_name); + } else if (rc == ENOENT) { + op->fqi.last_comp_was_found = 0; + op->fqi.last_snode = NULL; + rc = 0; + } else { + smb_node_release(op->fqi.dir_snode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + if (op->fqi.last_comp_was_found) { + node = op->fqi.last_snode; + dnode = op->fqi.dir_snode; + + /* + * Reject this request if the target is a directory + * and the client has specified that it must not be + * a directory (required by Lotus Notes). + */ + if ((op->create_options & FILE_NON_DIRECTORY_FILE) && + (op->fqi.last_attr.sa_vattr.va_type == VDIR)) { + smb_node_release(node); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_cifs_error(sr, + NT_STATUS_FILE_IS_A_DIRECTORY, + ERRDOS, ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + if (op->fqi.last_attr.sa_vattr.va_type == VDIR) { + if ((sr->smb_com == SMB_COM_OPEN_ANDX) || + (sr->smb_com == SMB_COM_OPEN)) { + /* + * Directories cannot be opened + * with the above commands + */ + smb_node_release(node); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_cifs_error(sr, + NT_STATUS_FILE_IS_A_DIRECTORY, + ERRDOS, ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + } else if (op->my_flags & MYF_MUST_BE_DIRECTORY) { + smb_node_release(node); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_cifs_error(sr, NT_STATUS_NOT_A_DIRECTORY, + ERRDOS, ERROR_DIRECTORY); + /* NOTREACHED */ + } + + /* + * No more open should be accepted when "Delete on close" + * flag is set. + */ + if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { + smb_node_release(node); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_cifs_error(sr, NT_STATUS_DELETE_PENDING, + ERRDOS, ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + /* + * Specified file already exists so the operation should fail. + */ + if (op->create_disposition == FILE_CREATE) { + smb_node_release(node); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_cifs_error(sr, + NT_STATUS_OBJECT_NAME_COLLISION, ERRDOS, + ERROR_ALREADY_EXISTS); + /* NOTREACHED */ + } + + /* + * Windows seems to check read-only access before file + * sharing check. + */ + if (NODE_IS_READONLY(node)) { + /* Files data only */ + if (node->attr.sa_vattr.va_type != VDIR) { + if (op->desired_access & (FILE_WRITE_DATA | + FILE_APPEND_DATA)) { + smb_node_release(node); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_error(sr, ERRDOS, + ERRnoaccess); + /* NOTREACHED */ + } + } + } + + status = smb_file_share_check(sr, node); + if (status == NT_STATUS_SHARING_VIOLATION) { + smb_node_release(node); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + return (status); + } + + status = smb_fsop_access(sr, sr->user_cr, node, + op->desired_access); + + if (status != NT_STATUS_SUCCESS) { + smb_node_release(node); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + if (status == NT_STATUS_PRIVILEGE_NOT_HELD) { + smbsr_raise_cifs_error(sr, + status, + ERRDOS, + ERROR_PRIVILEGE_NOT_HELD); + } else { + smbsr_raise_cifs_error(sr, + NT_STATUS_ACCESS_DENIED, + ERRDOS, + ERROR_ACCESS_DENIED); + } + } + + /* + * Break the oplock before share checks. If another client + * has the file open, this will force a flush or close, + * which may affect the outcome of any share checking. + */ + if (OPLOCKS_IN_FORCE(node)) { + status = smb_break_oplock(sr, node); + + if (status != NT_STATUS_SUCCESS) { + smb_node_release(node); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_cifs_error(sr, status, + ERRDOS, ERROR_VC_DISCONNECTED); + /* NOTREACHED */ + } + } + + switch (op->create_disposition) { + case FILE_SUPERSEDE: + case FILE_OVERWRITE_IF: + case FILE_OVERWRITE: + if (node->attr.sa_vattr.va_type == VDIR) { + smb_node_release(node); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_cifs_error(sr, + NT_STATUS_ACCESS_DENIED, ERRDOS, + ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + if (node->attr.sa_vattr.va_size != op->dsize) { + node->flags &= ~NODE_FLAGS_SET_SIZE; + new_attr.sa_vattr.va_size = op->dsize; + new_attr.sa_mask = SMB_AT_SIZE; + if ((rc = smb_fsop_setattr(sr, sr->user_cr, + (&op->fqi)->last_snode, &new_attr, + &op->fqi.last_attr)) != 0) { + smb_node_release(node); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + } + + /* + * If file is being replaced, + * we should remove existing streams + */ + if (SMB_IS_STREAM(node) == 0) + (void) smb_fsop_remove_streams(sr, sr->user_cr, + node); + + op->action_taken = SMB_OACT_TRUNCATED; + break; + + default: + /* + * FILE_OPEN or FILE_OPEN_IF. + */ + op->action_taken = SMB_OACT_OPENED; + break; + } + } else { + + /* Last component was not found. */ + dnode = op->fqi.dir_snode; + + if ((op->create_disposition == FILE_OPEN) || + (op->create_disposition == FILE_OVERWRITE)) { + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + + is_stream = smb_stream_parse_name(op->fqi.path, + NULL, NULL); + /* + * The requested file not found so the operation should + * fail with these two dispositions + */ + if (is_stream) + smbsr_raise_cifs_error(sr, + NT_STATUS_OBJECT_NAME_NOT_FOUND, + ERRDOS, ERROR_FILE_NOT_FOUND); + else + smbsr_raise_error(sr, ERRDOS, ERRbadfile); + /* NOTREACHED */ + } + + /* + * lock the parent dir node in case another create + * request to the same parent directory comes in. + */ + smb_rwx_rwenter(&dnode->n_lock, RW_WRITER); + + bzero(&new_attr, sizeof (new_attr)); + if (is_dir == 0) { + new_attr.sa_vattr.va_type = VREG; + new_attr.sa_vattr.va_mode = 0666; + new_attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE; + rc = smb_fsop_create(sr, sr->user_cr, dnode, + op->fqi.last_comp, &new_attr, + &op->fqi.last_snode, &op->fqi.last_attr); + if (rc != 0) { + smb_rwx_rwexit(&dnode->n_lock); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + if (op->dsize) { + new_attr.sa_vattr.va_size = op->dsize; + new_attr.sa_mask = SMB_AT_SIZE; + rc = smb_fsop_setattr(sr, sr->user_cr, + op->fqi.last_snode, &new_attr, + &op->fqi.last_attr); + if (rc != 0) { + smb_node_release(op->fqi.last_snode); + (void) smb_fsop_remove(sr, sr->user_cr, + dnode, op->fqi.last_comp, 0); + smb_rwx_rwexit(&dnode->n_lock); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + } + + } else { + op->dattr |= SMB_FA_DIRECTORY; + new_attr.sa_vattr.va_type = VDIR; + new_attr.sa_vattr.va_mode = 0777; + new_attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE; + rc = smb_fsop_mkdir(sr, sr->user_cr, dnode, + op->fqi.last_comp, &new_attr, + &op->fqi.last_snode, &op->fqi.last_attr); + if (rc != 0) { + smb_rwx_rwexit(&dnode->n_lock); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + } + + created = 1; + op->action_taken = SMB_OACT_CREATED; + } + + if (node == 0) { + node = op->fqi.last_snode; + } + + if ((op->fqi.last_attr.sa_vattr.va_type != VREG) && + (op->fqi.last_attr.sa_vattr.va_type != VDIR) && + (op->fqi.last_attr.sa_vattr.va_type != VLNK)) { + /* not allowed to do this */ + SMB_DEL_NEWOBJ(op->fqi); + smb_node_release(node); + if (created) + smb_rwx_rwexit(&dnode->n_lock); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_error(sr, ERRDOS, ERRnoaccess); + /* NOTREACHED */ + } + + if (max_requested) { + smb_fsop_eaccess(sr, sr->user_cr, node, &max_allowed); + op->desired_access |= max_allowed; + } + + /* + * smb_ofile_open() will copy node to of->node. Hence + * the hold on node (i.e. op->fqi.last_snode) will be "transferred" + * to the "of" structure. + */ + + of = smb_ofile_open(sr->tid_tree, node, sr->smb_pid, op->desired_access, + op->create_options, op->share_access, SMB_FTYPE_DISK, NULL, 0, + &err); + + if (of == NULL) { + SMB_DEL_NEWOBJ(op->fqi); + smb_node_release(node); + if (created) + smb_rwx_rwexit(&dnode->n_lock); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + smbsr_raise_cifs_error(sr, err.status, err.errcls, err.errcode); + /* NOTREACHED */ + } + + /* + * Propagate the write-through mode from the open params + * to the node: see the notes in the function header. + * + * IR #102318 Mirroring may force synchronous + * writes regardless of what we specify here. + */ + if (smb_stable_mode || (op->create_options & FILE_WRITE_THROUGH)) + node->flags |= NODE_FLAGS_WRITE_THROUGH; + + op->fileid = op->fqi.last_attr.sa_vattr.va_nodeid; + + if (op->fqi.last_attr.sa_vattr.va_type == VDIR) { + /* We don't oplock directories */ + op->my_flags &= ~MYF_OPLOCK_MASK; + op->dsize = 0; + } else { + status = smb_acquire_oplock(sr, of, op->my_flags, + &granted_oplock); + op->my_flags &= ~MYF_OPLOCK_MASK; + + if (status != NT_STATUS_SUCCESS) { + (void) smb_ofile_close(of, 0); + smb_ofile_release(of); + if (created) + smb_rwx_rwexit(&dnode->n_lock); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + + smbsr_raise_cifs_error(sr, status, + ERRDOS, ERROR_SHARING_VIOLATION); + /* NOTREACHED */ + } + + op->my_flags |= granted_oplock; + op->dsize = op->fqi.last_attr.sa_vattr.va_size; + } + + if (created) { + node->flags |= NODE_FLAGS_CREATED; + /* + * Clients may set the DOS readonly bit on create but they + * expect subsequent write operations on the open fid to + * succeed. Thus the DOS readonly bit is not set until the + * file is closed. The NODE_CREATED_READONLY flag will + * inhibit other attempts to open the file with write access + * and act as the indicator to set the DOS readonly bit on + * close. + */ + if (op->dattr & SMB_FA_READONLY) { + node->flags |= NODE_CREATED_READONLY; + op->dattr &= ~SMB_FA_READONLY; + } + smb_node_set_dosattr(node, op->dattr | SMB_FA_ARCHIVE); + if (op->utime.tv_sec == 0 || op->utime.tv_sec == 0xffffffff) + (void) microtime(&op->utime); + smb_node_set_time(node, NULL, &op->utime, 0, 0, SMB_AT_MTIME); + (void) smb_sync_fsattr(sr, sr->user_cr, node); + } else { + /* + * If we reach here, it means that file already exists + * and if create disposition is one of: FILE_SUPERSEDE, + * FILE_OVERWRITE_IF, or FILE_OVERWRITE it + * means that client wants to overwrite (or truncate) + * the existing file. So we should overwrite the dos + * attributes of destination file with the dos attributes + * of source file. + */ + + switch (op->create_disposition) { + case FILE_SUPERSEDE: + case FILE_OVERWRITE_IF: + case FILE_OVERWRITE: + smb_node_set_dosattr(node, + op->dattr | SMB_FA_ARCHIVE); + (void) smb_sync_fsattr(sr, sr->user_cr, node); + } + op->utime = *smb_node_get_crtime(node); + op->dattr = smb_node_get_dosattr(node); + } + + /* + * Set up the file type in open_param for the response + */ + op->ftype = SMB_FTYPE_DISK; + sr->smb_fid = of->f_fid; + sr->fid_ofile = of; + + if (created) { + smb_rwx_rwexit(&dnode->n_lock); + } + smb_node_release(dnode); + SMB_NULL_FQI_NODES(op->fqi); + + return (NT_STATUS_SUCCESS); +} + +/* + * smb_validate_object_name + * + * Very basic file name validation. Directory validation is handed off + * to smb_validate_dirname. For filenames, we check for names of the + * form "AAAn:". Names that contain three characters, a single digit + * and a colon (:) are reserved as DOS device names, i.e. "COM1:". + * + * Returns NT status codes. + */ +uint32_t +smb_validate_object_name(char *path, unsigned int ftype) +{ + char *filename; + + if (path == 0) + return (0); + + if (ftype) + return (smb_validate_dirname(path)); + + /* + * Basename with backslashes. + */ + if ((filename = strrchr(path, '\\')) != 0) + ++filename; + else + filename = path; + + if (strlen(filename) == 5 && + mts_isdigit(filename[3]) && + filename[4] == ':') { + return (NT_STATUS_OBJECT_NAME_INVALID); + } + + return (0); +} + +/* + * smb_preset_delete_on_close + * + * Set the DeleteOnClose flag on the smb file. When the file is closed, + * the flag will be transferred to the smb node, which will commit the + * delete operation and inhibit subsequent open requests. + * + * When DeleteOnClose is set on an smb_node, the common open code will + * reject subsequent open requests for the file. Observation of Windows + * 2000 indicates that subsequent opens should be allowed (assuming + * there would be no sharing violation) until the file is closed using + * the fid on which the DeleteOnClose was requested. + */ +void +smb_preset_delete_on_close(smb_ofile_t *file) +{ + mutex_enter(&file->f_mutex); + file->f_flags |= SMB_OFLAGS_SET_DELETE_ON_CLOSE; + mutex_exit(&file->f_mutex); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_common_search.c b/usr/src/uts/common/fs/smbsrv/smb_common_search.c new file mode 100644 index 000000000000..a17555669b6d --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_common_search.c @@ -0,0 +1,344 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Implementation of smb_rdir_open, smb_rdir_next and smb_rdir_close. + */ + +#include +#include + +/* + * smb_rdir_open + */ +int +smb_rdir_open(smb_request_t *sr, char *path, unsigned short sattr) +{ + smb_odir_t *od; + smb_node_t *node; + char *last_component; + smb_session_t *session = sr->session; + unsigned int rc; + int erc; + + last_component = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + if ((rc = smb_pathname_reduce(sr, sr->user_cr, path, + sr->tid_tree->t_snode, sr->tid_tree->t_snode, + &node, last_component)) != 0) { + kmem_free(last_component, MAXNAMELEN); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + if ((node->vp)->v_type != VDIR) { + smb_node_release(node); + kmem_free(last_component, MAXNAMELEN); + smbsr_raise_error(sr, ERRDOS, ERRbadpath); + /* NOTREACHED */ + } + + erc = smb_fsop_access(sr, sr->user_cr, node, FILE_LIST_DIRECTORY); + if (erc != 0) { + smb_node_release(node); + kmem_free(last_component, MAXNAMELEN); + if (sr->smb_com == SMB_COM_SEARCH) { + if (session->capabilities & CAP_STATUS32) { + smbsr_setup_nt_status(sr, + ERROR_SEVERITY_WARNING, + NT_STATUS_NO_MORE_FILES); + return (SDRC_NORMAL_REPLY); + } else { + smbsr_raise_error(sr, + ERRDOS, ERROR_NO_MORE_FILES); + /* NOTREACHED */ + } + } else { + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + } + + od = smb_odir_open(sr->tid_tree, node, last_component, sr->smb_pid, + sattr); + kmem_free(last_component, sizeof (od->d_pattern)); + if (od == NULL) { + smb_node_release(node); + smbsr_raise_error(sr, ERRDOS, ERROR_NO_MORE_FILES); + /* NOTREACHED */ + } + + sr->smb_sid = od->d_sid; + sr->sid_odir = od; + + return (-1); +} + + +/* + * smb_rdir_next + * + * Returns: + * 0 Found an entry + * ENOENT There is no (more) entry + * error code An error happened + */ +int +smb_rdir_next( + smb_request_t *sr, + smb_node_t **rnode, + smb_odir_context_t *pc) +{ + struct smb_odir *dir; + ino64_t fileid; + int rc, n_name; + char last_component[MAXNAMELEN]; + char namebuf[MAXNAMELEN]; + smb_node_t *tmp_snode; + smb_node_t *dnode; + smb_node_t *fnode; + smb_attr_t ret_attr; + + ASSERT(sr->sid_odir); + dir = sr->sid_odir; + + if (dir->d_state == SMB_ODIR_STATE_CLOSED) { + return (ENOENT); + } + + if (dir->d_wildcards == 0) { + /* There are no wildcards in pattern */ + if (pc->dc_cookie != 0) { + /* Already found entry... */ + return (ENOENT); + } + + pc->dc_name[0] = '\0'; + pc->dc_shortname[0] = '\0'; + pc->dc_name83[0] = '\0'; + + rc = smb_fsop_lookup(sr, sr->user_cr, 0, + sr->tid_tree->t_snode, dir->d_dir_snode, dir->d_pattern, + &fnode, &pc->dc_attr, pc->dc_shortname, pc->dc_name83); + + if (rc != 0) + return (rc); + + /* + * We are here if there was a successful lookup of the + * name. The name may be a mangled name. If it was, + * then shortname has the copy of it. So, we may + * not need to do mangling later. + * + * dir->name will contain the case-preserved name. + * If that name is not available (this should not + * happen), then copy dir->pattern into dir->name. + */ + + if (fnode->od_name) { + (void) strcpy(pc->dc_name, fnode->od_name); + } else { + (void) strcpy(pc->dc_name, dir->d_pattern); + } + + /* Root of file system? */ + if ((strcmp(dir->d_pattern, "..") == 0) && + (dir->d_dir_snode == sr->tid_tree->t_snode)) { + smb_node_release(fnode); + smb_node_ref(sr->tid_tree->t_snode); + fnode = sr->tid_tree->t_snode; + } else if (pc->dc_attr.sa_vattr.va_type == VLNK) { + (void) strcpy(namebuf, dir->d_pattern); + + tmp_snode = fnode; + rc = smb_pathname_reduce(sr, sr->user_cr, namebuf, + sr->tid_tree->t_snode, dir->d_dir_snode, + &dnode, last_component); + + if (rc != 0) { + fnode = tmp_snode; + } else { + rc = smb_fsop_lookup(sr, sr->user_cr, + SMB_FOLLOW_LINKS, sr->tid_tree->t_snode, + dnode, last_component, &fnode, &ret_attr, + 0, 0); + + smb_node_release(dnode); + if (rc != 0) { + fnode = tmp_snode; + } else { + pc->dc_attr = ret_attr; + smb_node_release(tmp_snode); + } + } + } + + pc->dc_dattr = smb_node_get_dosattr(fnode); + /* + * If name not already mangled, do it. + * + * The name will only be mangled if smb_needs_mangle() + * determines that it is required. Mangling due to + * case-insensitive collisions is not necessary here. + */ + if (pc->dc_name83[0] == '\0') + (void) smb_mangle_name(fnode->attr.sa_vattr.va_nodeid, + pc->dc_name, pc->dc_shortname, pc->dc_name83, 0); + if (rnode) + *rnode = fnode; + else + smb_node_release(fnode); + + pc->dc_cookie = (uint32_t)-1; + return (0); + } /* No wild card search */ + + for (;;) { + if (dir->d_state == SMB_ODIR_STATE_CLOSED) { + return (ENOENT); + } + + /* sizeof dir->name == 256 */ + n_name = (sizeof (pc->dc_name)) - 1; + + rc = smb_fsop_readdir(sr, sr->user_cr, dir->d_dir_snode, + &pc->dc_cookie, pc->dc_name, &n_name, &fileid, NULL, + NULL, NULL); + if (rc != 0) { + return (rc); + } + + if (n_name == 0) /* EOF */ + break; + pc->dc_name[n_name] = '\0'; + + /* + * Don't return "." or ".." unless SMB_FA_HIDDEN bit is set + * We have to code these specially since we cannot set the + * SMB_FA_HIDDEN bits in these because they are simply links to + * the real directory and the real directory is NOT hidden. + */ + if (((dir->d_sattr & SMB_FA_HIDDEN) == 0) && + ((strcmp(pc->dc_name, ".") == 0) || + ((strcmp(pc->dc_name, "..") == 0)))) { + continue; + } + + /* may match a mangled name or "real" name */ + if (smb_component_match(sr, fileid, dir, pc) <= 0) + continue; + + /* Look up the "real" name */ + rc = smb_fsop_lookup(sr, sr->user_cr, 0, sr->tid_tree->t_snode, + dir->d_dir_snode, pc->dc_name, &fnode, &pc->dc_attr, 0, 0); + + if (rc != 0) { + if (rc != ENOENT) { + return (rc); + } + else + continue; + /* NOTREACHED */ + } + + /* Root of file system? */ + if ((strcmp(pc->dc_name, "..") == 0) && + (dir->d_dir_snode == sr->tid_tree->t_snode)) { + smb_node_release(fnode); + smb_node_ref(sr->tid_tree->t_snode); + fnode = sr->tid_tree->t_snode; + } else if (pc->dc_attr.sa_vattr.va_type == VLNK) { + (void) strcpy(namebuf, pc->dc_name); + + smb_node_release(fnode); + rc = smb_pathname_reduce(sr, sr->user_cr, namebuf, + sr->tid_tree->t_snode, dir->d_dir_snode, &dnode, + last_component); + + if (rc != 0) { + continue; + } + + rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, + sr->tid_tree->t_snode, dnode, last_component, + &fnode, &ret_attr, 0, 0); + + smb_node_release(dnode); + if (rc != 0) { + continue; + } + pc->dc_attr = ret_attr; + } + + pc->dc_dattr = smb_node_get_dosattr(fnode); + + /* Obey search attributes */ + if ((pc->dc_dattr & SMB_FA_DIRECTORY) && + !(dir->d_sattr & SMB_FA_DIRECTORY)) { + smb_node_release(fnode); + continue; + } + + if ((pc->dc_dattr & SMB_FA_HIDDEN) && + !(dir->d_sattr & SMB_FA_HIDDEN)) { + smb_node_release(fnode); + continue; + } + + if ((pc->dc_dattr & SMB_FA_SYSTEM) && + !(dir->d_sattr & SMB_FA_SYSTEM)) { + smb_node_release(fnode); + continue; + } + + if (rnode) + *rnode = fnode; + else + smb_node_release(fnode); + + return (0); + } + + return (ENOENT); +} + +/* + * smb_rdir_close + */ +void +smb_rdir_close(struct smb_request *sr) +{ + smb_odir_t *od = sr->sid_odir; + + ASSERT(od); + ASSERT(od->d_magic == SMB_ODIR_MAGIC); + + smb_odir_close(od); + smb_odir_release(od); + sr->sid_odir = NULL; +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_common_transact.c b/usr/src/uts/common/fs/smbsrv/smb_common_transact.c new file mode 100644 index 000000000000..2558716c5e28 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_common_transact.c @@ -0,0 +1,2485 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include + +extern int smb_maxbufsize; + +#define MAX_SHARE_NAME_LEN 13 +#define SHARE_INFO_1_SIZE (MAX_SHARE_NAME_LEN + sizeof (char) + \ + sizeof (short) + sizeof (uint32_t)) + +/* + * count of bytes in server response packet + * except parameters and data. Note that setup + * word count is zero. + */ +#define RESP_HEADER_LEN 24 + +/* + * NB. I started by using common functions for transaction/transaction2 + * and transaction_secondary/transaction2_secondary because they + * are respectively so similar. However, it turned out to be a bad + * idea because of quirky differences. Be sure if you modify one + * of these four functions to check and see if the modification should + * be applied to its peer. + */ + +int smb_trans_ready(struct smb_xa *xa); +int smb_trans_dispatch(struct smb_request *sr, struct smb_xa *xa); +int smb_trans2_dispatch(struct smb_request *sr, struct smb_xa *xa); +int smb_trans2_find(struct smb_request *sr, struct smb_xa *xa, int opcode); +int smb_trans2_query_fs_info(struct smb_request *sr, struct smb_xa *xa); + + +int smb_nt_transact_query_quota(struct smb_request *sr, struct smb_xa *xa); + +int +smb_com_transaction(struct smb_request *sr) +{ + int rc; + unsigned char msrcnt, suwcnt; + uint16_t tpscnt, tdscnt, mprcnt, mdrcnt, flags; + uint16_t pscnt, psoff, dscnt, dsoff; + uint32_t timeo; + struct smb_xa *xa; + char *stn; + int ready; + + rc = smbsr_decode_vwv(sr, SMB_TRANSHDR_ED_FMT, + &tpscnt, &tdscnt, &mprcnt, &mdrcnt, &msrcnt, &flags, + &timeo, &pscnt, &psoff, &dscnt, &dsoff, &suwcnt); + + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + xa = smb_xa_create(sr->session, sr, tpscnt, tdscnt, mprcnt, mdrcnt, + msrcnt, suwcnt); + if (xa == NULL) { + smbsr_raise_error(sr, ERRSRV, ERRnoroom); + /* NOTREACHED */ + } + + /* Should be some alignment stuff here in SMB? */ + if (sr->smb_flg2 & SMB_FLAGS2_UNICODE) { + rc = smbsr_decode_data(sr, "%.U", sr, &stn); + } else { + rc = smbsr_decode_data(sr, "%s", sr, &stn); + } + if (rc != 0) { + smb_xa_rele(sr->session, xa); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + xa->xa_smb_trans_name = MEM_STRDUP("smb", stn); + + xa->smb_flags = flags; + xa->smb_timeout = timeo; + xa->req_disp_param = pscnt; + xa->req_disp_data = dscnt; + + if (MBC_SHADOW_CHAIN(&xa->req_setup_mb, &sr->smb_vwv, + sr->smb_vwv.chain_offset, suwcnt * 2)) { + smb_xa_rele(sr->session, xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + if (MBC_SHADOW_CHAIN(&xa->req_param_mb, &sr->command, psoff, pscnt)) { + smb_xa_rele(sr->session, xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + if (MBC_SHADOW_CHAIN(&xa->req_data_mb, &sr->command, dsoff, dscnt)) { + smb_xa_rele(sr->session, xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + + ready = smb_trans_ready(xa); + + if (smb_xa_open(xa)) { + smb_xa_rele(sr->session, xa); + smbsr_raise_error(sr, ERRDOS, ERRsrverror); + /* NOTREACHED */ + } + sr->r_xa = xa; + + if (!ready) { + smbsr_encode_empty_result(sr); + return (SDRC_NORMAL_REPLY); + } + + if (!smb_xa_complete(xa)) { + smb_xa_close(xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + + return (smb_trans_dispatch(sr, xa)); +} + + +int +smb_com_transaction_secondary(struct smb_request *sr) +{ + uint16_t tpscnt, tdscnt, pscnt, psdisp; + uint16_t dscnt, dsoff, dsdisp, psoff; + smb_xa_t *xa; + int rc; + + if ((xa = smbsr_lookup_xa(sr)) == 0) { + smbsr_raise_error(sr, ERRSRV, ERRsrverror); + /* NOTREACHED */ + } + + if (sr->session->signing.flags & SMB_SIGNING_ENABLED) { + if (smb_sign_check_secondary(sr, xa->reply_seqnum) != 0) { + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERRnoaccess); + /* NOTREACHED */ + } + } + + if (xa->smb_com != SMB_COM_TRANSACTION) { + return (SDRC_DROP_VC); + } + + rc = smbsr_decode_vwv(sr, SMB_TRANSSHDR_ED_FMT, &tpscnt, &tdscnt, + &pscnt, &psoff, &psdisp, &dscnt, &dsoff, &dsdisp); + + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + mutex_enter(&xa->xa_mutex); + xa->smb_tpscnt = tpscnt; /* might have shrunk */ + xa->smb_tdscnt = tdscnt; /* might have shrunk */ + xa->req_disp_param = psdisp+pscnt; + xa->req_disp_data = dsdisp+dscnt; + + if (MBC_SHADOW_CHAIN(&xa->req_param_mb, &sr->command, psoff, pscnt)) { + mutex_exit(&xa->xa_mutex); + smb_xa_close(xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + if (MBC_SHADOW_CHAIN(&xa->req_data_mb, &sr->command, dsoff, dscnt)) { + mutex_exit(&xa->xa_mutex); + smb_xa_close(xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + mutex_exit(&xa->xa_mutex); + + if (!smb_trans_ready(xa)) + return (SDRC_NO_REPLY); + + if (!smb_xa_complete(xa)) + return (SDRC_NO_REPLY); + + return (smb_trans_dispatch(sr, xa)); +} + + +int +smb_com_ioctl(struct smb_request *sr) +{ + uint16_t fid, category, function, tpscnt, tdscnt, mprcnt; + uint16_t mdrcnt, pscnt, pdoff, dscnt, dsoff; + uint32_t timeout; + int rc; + + rc = smbsr_decode_vwv(sr, "wwwwwwwl2.wwww", &fid, &category, &function, + &tpscnt, &tdscnt, &mprcnt, &mdrcnt, &timeout, &pscnt, + &pdoff, &dscnt, &dsoff); + + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + return (SDRC_UNIMPLEMENTED); +} + + +int /*ARGSUSED*/ +smb_com_ioctl_secondary(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} + + +int +smb_com_transaction2(struct smb_request *sr) +{ + unsigned char msrcnt, suwcnt; + uint16_t tpscnt, tdscnt, mprcnt, mdrcnt, flags; + uint16_t pscnt, psoff, dscnt, dsoff; + uint32_t timeo; + smb_xa_t *xa; + int ready; + int rc; + + rc = smbsr_decode_vwv(sr, SMB_TRANSHDR_ED_FMT, &tpscnt, &tdscnt, + &mprcnt, &mdrcnt, &msrcnt, &flags, &timeo, &pscnt, &psoff, &dscnt, + &dsoff, &suwcnt); + + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + xa = smb_xa_create(sr->session, sr, tpscnt, tdscnt, mprcnt, mdrcnt, + msrcnt, suwcnt); + if (xa == 0) { + smbsr_raise_error(sr, ERRSRV, ERRnoroom); + /* NOTREACHED */ + } + + xa->smb_flags = flags; + xa->smb_timeout = timeo; + xa->req_disp_param = pscnt; + xa->req_disp_data = dscnt; + + if (MBC_SHADOW_CHAIN(&xa->req_setup_mb, &sr->smb_vwv, + sr->smb_vwv.chain_offset, suwcnt*2)) { + smb_xa_rele(sr->session, xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + if (MBC_SHADOW_CHAIN(&xa->req_param_mb, &sr->command, psoff, pscnt)) { + smb_xa_rele(sr->session, xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + if (MBC_SHADOW_CHAIN(&xa->req_data_mb, &sr->command, dsoff, dscnt)) { + smb_xa_rele(sr->session, xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + + ready = smb_trans_ready(xa); + + if (smb_xa_open(xa)) { + smb_xa_rele(sr->session, xa); + smbsr_raise_error(sr, ERRDOS, ERRsrverror); + /* NOTREACHED */ + } + sr->r_xa = xa; + + if (!ready) { + smbsr_encode_empty_result(sr); + return (SDRC_NORMAL_REPLY); + } + + if (!smb_xa_complete(xa)) { + smb_xa_close(xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + + return (smb_trans2_dispatch(sr, xa)); +} + + +int +smb_com_transaction2_secondary(struct smb_request *sr) +{ + uint16_t tpscnt, tdscnt, fid; + uint16_t pscnt, psoff, psdisp, dscnt, dsoff, dsdisp; + smb_xa_t *xa; + int rc; + + if ((xa = smbsr_lookup_xa(sr)) == 0) { + smbsr_raise_error(sr, ERRSRV, ERRsrverror); + /* NOTREACHED */ + } + + if (sr->session->signing.flags & SMB_SIGNING_ENABLED) { + if (smb_sign_check_secondary(sr, xa->reply_seqnum) != 0) { + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERRnoaccess); + /* NOTREACHED */ + } + } + + if (xa->smb_com != SMB_COM_TRANSACTION2) { + return (SDRC_DROP_VC); + } + + rc = smbsr_decode_vwv(sr, SMB_TRANS2SHDR_ED_FMT, &tpscnt, &tdscnt, + &pscnt, &psoff, &psdisp, &dscnt, &dsoff, &dsdisp, &fid); + + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + mutex_enter(&xa->xa_mutex); + xa->smb_tpscnt = tpscnt; /* might have shrunk */ + xa->smb_tdscnt = tdscnt; /* might have shrunk */ + xa->xa_smb_fid = fid; /* overwrite rules? */ + xa->req_disp_param = psdisp + pscnt; + xa->req_disp_data = dsdisp + dscnt; + + if (MBC_SHADOW_CHAIN(&xa->req_param_mb, &sr->command, psoff, pscnt)) { + mutex_exit(&xa->xa_mutex); + smb_xa_close(xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + if (MBC_SHADOW_CHAIN(&xa->req_data_mb, &sr->command, dsoff, dscnt)) { + mutex_exit(&xa->xa_mutex); + smb_xa_close(xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + mutex_exit(&xa->xa_mutex); + + if (!smb_trans_ready(xa)) + return (SDRC_NO_REPLY); + + if (!smb_xa_complete(xa)) + return (SDRC_NO_REPLY); + + return (smb_trans2_dispatch(sr, xa)); +} + +static int +smb_nt_trans_dispatch(struct smb_request *sr, struct smb_xa *xa) +{ + int rc; + int total_bytes, n_setup, n_param, n_data; + int param_off, param_pad, data_off, data_pad; + + n_setup = (xa->smb_msrcnt < 200) ? xa->smb_msrcnt : 200; + n_setup++; + n_setup = n_setup & ~0x0001; + n_param = (xa->smb_mprcnt < smb_maxbufsize) + ? xa->smb_mprcnt : smb_maxbufsize; + n_param++; + n_param = n_param & ~0x0001; + rc = smb_maxbufsize - (SMBHEADERSIZE + 28 + n_setup + n_param); + n_data = (xa->smb_mdrcnt < rc) ? xa->smb_mdrcnt : rc; + MBC_INIT(&xa->rep_setup_mb, n_setup * 2); + MBC_INIT(&xa->rep_param_mb, n_param); + MBC_INIT(&xa->rep_data_mb, n_data); + + switch (xa->smb_func) { + case NT_TRANSACT_CREATE: + rc = smb_nt_transact_create(sr, xa); + break; + case NT_TRANSACT_NOTIFY_CHANGE: + rc = smb_nt_transact_notify_change(sr, xa); + break; + case NT_TRANSACT_QUERY_SECURITY_DESC: + rc = smb_nt_transact_query_security_info(sr, xa); + break; + case NT_TRANSACT_SET_SECURITY_DESC: + rc = smb_nt_transact_set_security_info(sr, xa); + break; + case NT_TRANSACT_IOCTL: + rc = smb_nt_transact_ioctl(sr, xa); + break; + + case NT_TRANSACT_QUERY_QUOTA: + (void) smb_nt_transact_query_quota(sr, xa); + smbsr_raise_error(sr, ERRSRV, ERRaccess); + /* NOTREACHED */ + + case NT_TRANSACT_SET_QUOTA: + smbsr_raise_error(sr, ERRSRV, ERRaccess); + /* NOTREACHED */ + + default: + smbsr_raise_error(sr, ERRSRV, ERRsmbcmd); + /* NOTREACHED */ + } + + switch (rc) { + case SDRC_NORMAL_REPLY: + break; + + case SDRC_DROP_VC: + case SDRC_NO_REPLY: + case SDRC_ERROR_REPLY: + return (rc); + + case SDRC_UNIMPLEMENTED: + case SDRC_UNSUPPORTED: + smbsr_raise_error(sr, ERRSRV, ERRsmbcmd); + /* NOTREACHED */ + + default: + break; + } + + n_setup = MBC_LENGTH(&xa->rep_setup_mb); + n_param = MBC_LENGTH(&xa->rep_param_mb); + n_data = MBC_LENGTH(&xa->rep_data_mb); + + if (xa->smb_msrcnt < n_setup || + xa->smb_mprcnt < n_param || + xa->smb_mdrcnt < n_data) { + smbsr_raise_error(sr, ERRSRV, ERRsmbcmd); + /* NOTREACHED */ + } + + /* neato, blast it over there */ + + n_setup = (n_setup + 1) / 2; /* Conver to setup words */ + param_pad = 1; /* must be one */ + param_off = param_pad + 32 + 37 + (n_setup << 1) + 2; + data_pad = (4 - ((param_off + n_param) & 3)) % 4; /* Pad to 4 byte */ + data_off = param_off + n_param + data_pad; /* Param off from hdr */ + total_bytes = param_pad + n_param + data_pad + n_data; + + smbsr_encode_result(sr, 18+n_setup, total_bytes, + "b 3. llllllllb C w #. C #. C", + 18 + n_setup, /* wct */ + n_param, /* Total Parameter Bytes */ + n_data, /* Total Data Bytes */ + n_param, /* Total Parameter Bytes this buffer */ + param_off, /* Param offset from header start */ + 0, /* Param displacement */ + n_data, /* Total Data Bytes this buffer */ + data_off, /* Data offset from header start */ + 0, /* Data displacement */ + n_setup, /* suwcnt */ + &xa->rep_setup_mb, /* setup[] */ + total_bytes, /* Total data bytes */ + param_pad, + &xa->rep_param_mb, + data_pad, + &xa->rep_data_mb); + return (SDRC_NORMAL_REPLY); +} + + +/* + * smb_nt_transact_query_quota + * + * Stub to help debunk this function. There are 16 parameter bytes. The + * first parameter is definitely the fid. The second looks like a flags + * field. Then there are 12 bytes (probably 3 dwords) - all zero. + */ +int +smb_nt_transact_query_quota(struct smb_request *sr, struct smb_xa *xa) +{ + uint16_t fid; + uint16_t flags; + int rc; + + rc = smb_decode_mbc(&xa->req_param_mb, "%ww", sr, &fid, &flags); + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + return (SDRC_NORMAL_REPLY); +} + + +int +smb_com_nt_transact(struct smb_request *sr) +{ + uint16_t Function; + unsigned char MaxSetupCount, SetupCount; + uint32_t TotalParameterCount, TotalDataCount; + uint32_t MaxParameterCount, MaxDataCount, pscnt; + uint32_t psoff, dscnt, dsoff; + smb_xa_t *xa; + int ready; + int rc; + + rc = smbsr_decode_vwv(sr, SMB_NT_TRANSHDR_ED_FMT, &MaxSetupCount, + &TotalParameterCount, &TotalDataCount, &MaxParameterCount, + &MaxDataCount, &pscnt, &psoff, &dscnt, + &dsoff, &SetupCount, &Function); + + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + xa = smb_xa_create(sr->session, sr, TotalParameterCount, TotalDataCount, + MaxParameterCount, MaxDataCount, MaxSetupCount, SetupCount); + if (xa == 0) { + smbsr_raise_error(sr, ERRSRV, ERRnoroom); + /* NOTREACHED */ + } + + xa->smb_flags = 0; + xa->smb_timeout = 0; + xa->smb_func = Function; + xa->req_disp_param = pscnt; + xa->req_disp_data = dscnt; + + if (MBC_SHADOW_CHAIN(&xa->req_setup_mb, &sr->smb_vwv, + sr->smb_vwv.chain_offset, SetupCount * 2)) { + smb_xa_rele(sr->session, xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + if (MBC_SHADOW_CHAIN(&xa->req_param_mb, &sr->command, psoff, pscnt)) { + smb_xa_rele(sr->session, xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + if (MBC_SHADOW_CHAIN(&xa->req_data_mb, &sr->command, dsoff, dscnt)) { + smb_xa_rele(sr->session, xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + + ready = smb_trans_ready(xa); + + if (smb_xa_open(xa)) { + smb_xa_rele(sr->session, xa); + smbsr_raise_error(sr, ERRDOS, ERRsrverror); + /* NOTREACHED */ + } + sr->r_xa = xa; + + if (!ready) { + smbsr_encode_empty_result(sr); + return (SDRC_NORMAL_REPLY); + } + + if (!smb_xa_complete(xa)) { + smb_xa_close(xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + + return (smb_nt_trans_dispatch(sr, xa)); +} + + +int +smb_com_nt_transact_secondary(struct smb_request *sr) +{ + uint16_t tpscnt, tdscnt, fid; + uint16_t pscnt, psoff, psdisp, dscnt, dsoff, dsdisp; + smb_xa_t *xa; + int rc; + + if ((xa = smbsr_lookup_xa(sr)) == 0) { + smbsr_raise_error(sr, ERRSRV, ERRsrverror); + /* NOTREACHED */ + } + + if (sr->session->signing.flags & SMB_SIGNING_ENABLED) { + if (smb_sign_check_secondary(sr, xa->reply_seqnum) != 0) { + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERRnoaccess); + /* NOTREACHED */ + } + } + + if (xa->smb_com != SMB_COM_TRANSACTION2) { + return (SDRC_DROP_VC); + } + + rc = smbsr_decode_vwv(sr, SMB_TRANS2SHDR_ED_FMT, &tpscnt, &tdscnt, + &pscnt, &psoff, &psdisp, &dscnt, &dsoff, &dsdisp, &fid); + + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + mutex_enter(&xa->xa_mutex); + xa->smb_tpscnt = tpscnt; /* might have shrunk */ + xa->smb_tdscnt = tdscnt; /* might have shrunk */ + xa->xa_smb_fid = fid; /* overwrite rules? */ + xa->req_disp_param = psdisp+pscnt; + xa->req_disp_data = dsdisp+dscnt; + + if (MBC_SHADOW_CHAIN(&xa->req_param_mb, &sr->command, psoff, pscnt)) { + mutex_exit(&xa->xa_mutex); + smb_xa_close(xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + if (MBC_SHADOW_CHAIN(&xa->req_data_mb, &sr->command, dsoff, dscnt)) { + mutex_exit(&xa->xa_mutex); + smb_xa_close(xa); + smbsr_raise_error(sr, ERRDOS, ERRbadformat); + /* NOTREACHED */ + } + mutex_exit(&xa->xa_mutex); + + if (!smb_trans_ready(xa)) + return (SDRC_NO_REPLY); + + if (!smb_xa_complete(xa)) + return (SDRC_NO_REPLY); + + return (smb_nt_trans_dispatch(sr, xa)); +} + +int +smb_trans_ready(struct smb_xa *xa) +{ + int rc; + + mutex_enter(&xa->xa_mutex); + rc = xa->req_disp_data >= xa->smb_tdscnt && + xa->req_disp_param >= xa->smb_tpscnt; + mutex_exit(&xa->xa_mutex); + + return (rc); +} + + +/* + * smb_emit_SHARE_INFO_0 + * + * This function will convert unicode chars to oem chars before + * and store the result in a fixed length, MAX_SHARE_NAME_LEN, buffer. If the + * length after conversion is longer than 12, -1 will be reported + * to indicate an error. The fixed length is a limitation of the + * smb protocol. + */ +static int +smb_emit_SHARE_INFO_0(struct mbuf_chain *output, unsigned char *name) +{ + mts_wchar_t *unibuf; + char *tmpbuf; + unsigned int cpid = oem_get_smb_cpid(); + unsigned int length; + char name_buf[MAX_SHARE_NAME_LEN]; + + if (name == 0) + tmpbuf = ""; + else + tmpbuf = (char *)name; + + length = strlen(tmpbuf) + 1; + unibuf = MEM_MALLOC("smb", length * sizeof (mts_wchar_t)); + + (void) mts_mbstowcs(unibuf, tmpbuf, length); + tmpbuf = MEM_MALLOC("smb", length); + if (unicodestooems(tmpbuf, unibuf, length, cpid) == 0) + (void) strcpy(tmpbuf, (char *)name); + + if (strlen(tmpbuf) + 1 > MAX_SHARE_NAME_LEN) { + MEM_FREE("smb", unibuf); + MEM_FREE("smb", tmpbuf); + return (-1); + } + + bzero(name_buf, sizeof (name_buf)); + (void) strcpy(name_buf, tmpbuf); + (void) smb_encode_mbc(output, "13c", name_buf); + + MEM_FREE("smb", unibuf); + MEM_FREE("smb", tmpbuf); + + return (0); +} + +static int +smb_emit_SHARE_INFO_1(struct mbuf_chain *output, struct mbuf_chain *text, + unsigned char *name, uint16_t type, + unsigned char *comment) +{ + if (smb_emit_SHARE_INFO_0(output, name) < 0) + return (-1); + + (void) smb_encode_mbc(output, ".wl", type, MBC_LENGTH(text)); + (void) smb_encode_mbc(text, "s", + (comment ? comment : (unsigned char *)"No comment")); + return (0); +} + +static void /*ARGSUSED*/ +smb_emit_SHARE_INFO_2(struct mbuf_chain *output, struct mbuf_chain *text, + struct smb_request *sr, unsigned char *name, uint16_t type, + unsigned char *comment, uint16_t access, char *path, char *password) +{ + unsigned char pword[9]; + + /* + * XXX PGD. Is there a bug here? We zero pword, copy password + * into pword then ignore it and use password for smb_encode_mbc? + */ + bzero(pword, sizeof (pword)); + (void) strncpy((char *)pword, password, sizeof (pword)); + (void) smb_emit_SHARE_INFO_1(output, text, name, type, comment); + (void) smb_encode_mbc(output, "wwwl9c.", + access, + smb_info.si.skc_maxconnections, + smb_svcstate_session_count(&smb_info.si_svc_sm_ctx), + MBC_LENGTH(text), + password); + (void) smb_encode_mbc(text, "s", path); +} + +/* + * is_long_sharename + * + * This function is extracted from smb_emit_SHARE_INFO_0 only for + * finding shares that their names are longer than MAX_SHARE_NAME_LEN. + * + * The function returns 1 for long share names and 0 when the length + * is Ok. + */ +static int +is_long_sharename(unsigned char *name) +{ + mts_wchar_t *unibuf; + char *tmpbuf; + unsigned int cpid = oem_get_smb_cpid(); + unsigned int length; + + if (name == 0) + tmpbuf = ""; + else + tmpbuf = (char *)name; + + length = strlen(tmpbuf) + 1; + unibuf = MEM_MALLOC("smb", length * sizeof (mts_wchar_t)); + (void) mts_mbstowcs(unibuf, tmpbuf, length); + tmpbuf = MEM_MALLOC("smb", length); + if (unicodestooems(tmpbuf, unibuf, length, cpid) == 0) + (void) strcpy(tmpbuf, (char *)name); + + if (strlen(tmpbuf) + 1 > MAX_SHARE_NAME_LEN) { + MEM_FREE("smb", unibuf); + MEM_FREE("smb", tmpbuf); + return (1); + } + + MEM_FREE("smb", unibuf); + MEM_FREE("smb", tmpbuf); + + return (0); +} + +/* + * This structure holds information about shares which will + * fit in the specified client buffer size. + * + * sei_bufsize: Client specified buffer size + * sei_count: Maximum number of shares that can be + * sent in the buffer. + * + * The return data section consists of a number of SHARE_INFO_1 structures. + * In case there are multiple SHARE_INFO_1 data structures to return this + * function put all fixed length part of these structures in the return buffer + * and then put all the variable length data (shares' comment) at the end of + * buffer. + * + * sei_info_len: Size of fixed length part of SHARE_INFO_1 + * structures for sei_count shares + * sei_cmnt_len: Size of comments for sei_count shares + */ +typedef struct { + uint16_t sei_bufsize; + short sei_count; + int sei_infolen; + int sei_cmntlen; +} smb_share_enum_t; + +/* + * smb_share_update_info + * + * Check to see if the given buffer has enough + * room to fit the information of the given share. + * If there is enough room update the passed max_??? + * information. + * + * Return 1 if buffer is not full yet, 0 if it's full. + */ +static int +smb_share_update_info(lmshare_info_t *si, smb_share_enum_t *shr_enum_info) +{ + int cmnt_len; + int new_info_len = shr_enum_info->sei_infolen; + int new_cmnt_len = shr_enum_info->sei_cmntlen; + + if (lmshrd_is_special(si->share_name)) + cmnt_len = 1; + else + cmnt_len = (strlen(si->comment) + 1); + + new_info_len += SHARE_INFO_1_SIZE; + new_cmnt_len += cmnt_len; + + if ((new_info_len + new_cmnt_len) < shr_enum_info->sei_bufsize) { + shr_enum_info->sei_count++; + shr_enum_info->sei_infolen = new_info_len; + shr_enum_info->sei_cmntlen = new_cmnt_len; + return (1); + } + + return (0); +} + +/* + * smb_share_skip_share + * + * Determines whether the given share should be enumerated + * or not. The share will not be enumerated if its name is + * long or it's autohome share. + * + * Return 1 if the share should be skipped; otherwise returns + * 0 + */ +static int +smb_share_skip_share(lmshare_info_t *si) +{ + if (is_long_sharename((unsigned char *)si->share_name)) { + return (1); + } + + /* Skip autohome share if autohome filter is enabled */ + if (si->mode == LMSHRM_TRANS) { + return (1); + } + + return (0); +} + +/* + * smb_share_add_autohome + * + * Determines if an autohome share should be added to shares' list + * for the given user. + * Autohome will be add when all the following conditions are true: + * + * 1. Autohome feature is enabled + * 2. A share with the same name as the given user exists + * 3. The share is not a permanent share + * 4. Share name is not longer than maximum allowed + */ +static int +smb_share_add_autohome(char *username, lmshare_info_t *si) +{ + int do_add = 0; + + do_add = (lmshrd_getinfo(username, si) == NERR_Success) && + (si->mode & LMSHRM_TRANS) && + (is_long_sharename((unsigned char *)(si->share_name)) == 0); + + return (do_add); +} + +/* + * smb_share_total_info + * + * This function calculates following informations + * - Maximum number of shares that can be sent for clients + * according to its buffer size (cli_bufsize) + * - length of fixed information about above shares + * - length of comments of above shares + * - total number of shares that their names are no longer + * than MAX_SHARE_NAME_LEN. + * + * Added SMB user object to the parameter list to filter out other + * user autohome shares. + */ +static void +smb_share_total_info(smb_share_enum_t *shr_enum_info, short *tot_shares_num, + smb_user_t *user) +{ + uint64_t iterator; + lmshare_info_t *si; + struct lmshare_info *auto_si; + int more_room = 1; + + si = kmem_zalloc(sizeof (lmshare_info_t), KM_SLEEP); + auto_si = kmem_zalloc(sizeof (struct lmshare_info), KM_SLEEP); + + *tot_shares_num = 0; + shr_enum_info->sei_count = 0; + shr_enum_info->sei_infolen = 0; + shr_enum_info->sei_cmntlen = 0; + + if (smb_share_add_autohome(user->u_name, auto_si)) { + (*tot_shares_num)++; + more_room = smb_share_update_info(auto_si, shr_enum_info); + } + + iterator = lmshrd_open_iterator(LMSHRM_ALL); + if (iterator == 0) { + kmem_free(si, sizeof (lmshare_info_t)); + kmem_free(auto_si, sizeof (struct lmshare_info)); + return; + } + + /* check for door errors */ + if (lmshrd_iterate(iterator, si) != NERR_Success) { + (void) lmshrd_close_iterator(iterator); + kmem_free(si, sizeof (lmshare_info_t)); + kmem_free(auto_si, sizeof (struct lmshare_info)); + return; + } + + while (*si->share_name != 0) { + if (smb_share_skip_share(si)) { + /* check for door errors */ + if (lmshrd_iterate(iterator, si) != NERR_Success) { + (void) lmshrd_close_iterator(iterator); + kmem_free(si, sizeof (lmshare_info_t)); + kmem_free(auto_si, + sizeof (struct lmshare_info)); + return; + } + continue; + } + + (*tot_shares_num)++; + + if (more_room) { + more_room = smb_share_update_info(si, shr_enum_info); + } + + /* check for door errors */ + if (lmshrd_iterate(iterator, si) != NERR_Success) { + (void) lmshrd_close_iterator(iterator); + kmem_free(si, sizeof (lmshare_info_t)); + kmem_free(auto_si, sizeof (struct lmshare_info)); + return; + } + } + + (void) lmshrd_close_iterator(iterator); + kmem_free(si, sizeof (lmshare_info_t)); + kmem_free(auto_si, sizeof (struct lmshare_info)); +} + +/* + * smb_encode_SHARE_INFO_1 + * + * This function is extracted from smb_emit_SHARE_INFO_1 and only + * encodes fixed part of SHARE_INFO_1 structure. + * + * The function returns -1 if encoding fails and 0 on success. + */ +static int +smb_encode_SHARE_INFO_1(struct mbuf_chain *output, unsigned char *name, + uint16_t type, int cmnt_len) +{ + if (smb_emit_SHARE_INFO_0(output, name) < 0) + return (-1); + (void) smb_encode_mbc(output, ".wl", type, cmnt_len); + return (0); +} + +/* + * collect_shares_info + * + * This function encodes information of shares_num of shares + * into data_mb and cmnt_str. + * + * Added SMB user object to the parameter list to filter out other + * user autohome shares. + * + */ +static void +collect_shares_info(uint64_t iterator, int shares_num, + struct mbuf_chain *data_mb, + char *cmnt_str, int *cmnt_len, + smb_user_t *user, int first_resp) +{ + int i = 0; + lmshare_info_t *si; + struct lmshare_info *tsi; + int is_special; + + si = kmem_zalloc(sizeof (lmshare_info_t), KM_SLEEP); + tsi = kmem_zalloc(sizeof (struct lmshare_info), KM_SLEEP); + + if (first_resp && smb_share_add_autohome(user->u_name, tsi)) { + if (smb_encode_SHARE_INFO_1(data_mb, + (unsigned char *)tsi->share_name, + tsi->stype, *cmnt_len) == 0) { + (void) memcpy(cmnt_str+(*cmnt_len), + tsi->comment, strlen(tsi->comment)+1); + (*cmnt_len) += (strlen(tsi->comment) + 1); + i++; + } + } + + /* check for door errors */ + if (lmshrd_iterate(iterator, si) != NERR_Success) { + kmem_free(si, sizeof (lmshare_info_t)); + kmem_free(tsi, sizeof (struct lmshare_info)); + return; + } + + while ((i < shares_num) && (*si->share_name != 0)) { + if (smb_share_skip_share(si)) { + goto next; + } + + + is_special = lmshrd_is_special(si->share_name); + /* check for door errors */ + if (is_special == NERR_InternalError) { + kmem_free(si, sizeof (lmshare_info_t)); + kmem_free(tsi, sizeof (struct lmshare_info)); + return; + } + + if (is_special) { + si->stype |= STYPE_HIDDEN; + if (smb_encode_SHARE_INFO_1(data_mb, + (unsigned char *)si->share_name, + si->stype, *cmnt_len) < 0) { + goto next; + } + cmnt_str[*cmnt_len] = '\0'; + (*cmnt_len)++; + } else { + if (smb_encode_SHARE_INFO_1(data_mb, + (unsigned char *)si->share_name, si->stype, + *cmnt_len) < 0) { + goto next; + } + (void) memcpy(cmnt_str+(*cmnt_len), si->comment, + strlen(si->comment)+1); + (*cmnt_len) += (strlen(si->comment) + 1); + } + + next: + /* check for door errors */ + if (lmshrd_iterate(iterator, si) != NERR_Success) { + kmem_free(si, sizeof (lmshare_info_t)); + kmem_free(tsi, sizeof (struct lmshare_info)); + return; + } + } + kmem_free(si, sizeof (lmshare_info_t)); + kmem_free(tsi, sizeof (struct lmshare_info)); +} + +int +smb_trans_net_share_enum(struct smb_request *sr, struct smb_xa *xa) +{ + smb_share_enum_t shr_enum_info; + short left_shares_cnt; /* Number of shares not sent yet */ + + /* + * Number of shares that should be sent + * in the current response packet. + * It can be a number between 0 and + * max_share_scnt. + */ + short shares_scnt; + + /* + * Maximum number of shares that can be + * sent in one response packet regarding + * the maximum negotiated buffer size + * for SMB messages. + */ + short max_shares_per_packet; + + /* + * Total number of shares on the server + * that their name is not greater than + * MAX_SHARE_NAME_LEN + */ + short shares_tot_num; + + /* + * Size of total data (info + cmnt) + * that should be sent for client + */ + int shares_tot_byte; + + /* + * Maximum size of data that can be + * sent in one SMB transaction response + * according to the maximum negotiated + * buffer size for SMB packets + */ + int data_buf_limit; + + /* + * Number of comment bytes that will + * be sent in the current response + */ + uint16_t cmnt_scnt; + + /* + * Number of data bytes that will + * be sent in the current response + */ + uint16_t data_scnt; + + /* + * Total number of data bytes that + * are sent till now. This is only + * used for calculating current data + * displacement + */ + uint16_t tot_data_scnt; + + /* + * Number of parameter bytes should + * be sent for the current response. + * It is 8 for the 1st response and + * 0 for others + */ + uint16_t param_scnt; + + /* number of setup and parameter bytes */ + uint16_t n_setup, n_param; + + /* data and parameter displacement */ + uint16_t data_disp, param_disp; + + /* return status by the 1st reply */ + uint16_t ret_stat; + + /* parameter and data offset and pad */ + int param_off, param_pad, data_off, data_pad; + + /* + * total bytes of parameters and data + * in the packet, plus the pad bytes. + */ + int tot_packet_bytes; + + char first_resp; + uint16_t opcode, level, cli_bufsize; + unsigned char *r_fmt; + char fmt[10]; + uint64_t iterator; + char *cmnt_str, *cmnt_start; + int cmnt_len; + struct mbuf_chain reply; + smb_user_t *user; + + user = sr->uid_user; + ASSERT(user); + + /* + * Initialize the mbuf chain of reply to zero. If it is not + * zero, code inside the while loop will try to free the chain. + */ + bzero(&reply, sizeof (struct mbuf_chain)); + + if (smb_decode_mbc(&xa->req_param_mb, "%wss(lev)w(size)w", sr, + &opcode, &r_fmt, &r_fmt, &level, &cli_bufsize) != 0) + return (SDRC_UNSUPPORTED); + + if (level != 1) { + (void) smb_encode_mbc(&xa->rep_param_mb, "wwww", + NERR_BadTransactConfig, 0, 0, 0); + return (SDRC_NORMAL_REPLY); + } + + n_setup = 0; /* Setup count for NetShareEnum SMB is 0 */ + n_param = 8; + data_buf_limit = sr->session->smb_msg_size - + (SMB_HEADER_ED_LEN + RESP_HEADER_LEN + n_param); + + shr_enum_info.sei_bufsize = cli_bufsize; + smb_share_total_info(&shr_enum_info, &shares_tot_num, user); + + shares_tot_byte = shr_enum_info.sei_infolen + shr_enum_info.sei_cmntlen; + + /* Check buffer to have enough space */ + if (shares_tot_byte == 0) { + return (SDRC_ERROR_REPLY); + } + + max_shares_per_packet = data_buf_limit / SHARE_INFO_1_SIZE; + + shares_scnt = (shr_enum_info.sei_count > max_shares_per_packet) + ? max_shares_per_packet : shr_enum_info.sei_count; + + cmnt_str = MEM_MALLOC("smb", shr_enum_info.sei_cmntlen * sizeof (char)); + cmnt_len = 0; + /* save start of buffer to free it at the end of function */ + cmnt_start = cmnt_str; + + iterator = lmshrd_open_iterator(LMSHRM_ALL); + + if (iterator == NULL) { + MEM_FREE("smb", cmnt_str); + return (SDRC_DROP_VC); + } + + /* + * The rep_setup_mb is already initialized in smb_trans_dispatch(). + * Calling MBC_INIT() will initialized the structure and so the + * pointer to the mbuf chains will be lost. Therefore, we need + * to free the resources before calling MBC_INIT() again. + */ + m_freem(xa->rep_setup_mb.chain); + MBC_INIT(&xa->rep_setup_mb, n_setup * 2); + + left_shares_cnt = shr_enum_info.sei_count; + tot_data_scnt = 0; + cmnt_scnt = 0; + + first_resp = 1; + while (tot_data_scnt < shares_tot_byte) { + /* + * Calling MBC_INIT() will initialized the structure and so the + * pointer to the mbuf chains will be lost. Therefore, we need + * to free the resources if any before calling MBC_INIT(). + */ + m_freem(xa->rep_data_mb.chain); + MBC_INIT(&xa->rep_data_mb, data_buf_limit); + collect_shares_info(iterator, shares_scnt, &xa->rep_data_mb, + cmnt_str, &cmnt_len, user, first_resp); + data_scnt = shares_scnt * SHARE_INFO_1_SIZE; + left_shares_cnt -= shares_scnt; + if (left_shares_cnt < max_shares_per_packet) + shares_scnt = left_shares_cnt; + if (left_shares_cnt == 0) { + /* + * Now send comments. + * Append comments to the end of share_info_1 + * structures. + */ + cmnt_scnt = data_buf_limit - + MBC_LENGTH(&xa->rep_data_mb); + if (cmnt_scnt > shr_enum_info.sei_cmntlen) { + /*LINTED E_ASSIGN_NARROW_CONV*/ + cmnt_scnt = shr_enum_info.sei_cmntlen; + } + (void) sprintf(fmt, "%dc", cmnt_scnt); + (void) smb_encode_mbc(&xa->rep_data_mb, fmt, cmnt_str); + cmnt_str += cmnt_scnt; + shr_enum_info.sei_cmntlen -= cmnt_scnt; + } + data_scnt += cmnt_scnt; + tot_data_scnt += data_scnt; + + /* Only the 1st response packet contains parameters */ + param_scnt = (first_resp) ? n_param : 0; + param_pad = 1; /* always one */ + param_off = SMB_HEADER_ED_LEN + RESP_HEADER_LEN; + param_disp = (first_resp) ? 0 : n_param; + + /* + * Calling MBC_INIT() will initialized the structure and so the + * pointer to the mbuf chains will be lost. Therefore, we need + * to free the resources if any before calling MBC_INIT(). + */ + m_freem(xa->rep_param_mb.chain); + MBC_INIT(&xa->rep_param_mb, param_scnt); + if (first_resp) { + first_resp = 0; + /* Prepare parameters for the 1st response packet */ + ret_stat = (shares_tot_num > shr_enum_info.sei_count) + ? ERROR_MORE_DATA : 0; + (void) smb_encode_mbc(&xa->rep_param_mb, "wwww", + ret_stat, -shr_enum_info.sei_infolen, + shr_enum_info.sei_count, + shares_tot_num); + } + + data_pad = (param_off + n_param) & 1; /* Pad to short */ + + /* data off from hdr start */ + data_off = param_off + param_scnt + data_pad; + data_disp = tot_data_scnt - data_scnt; + tot_packet_bytes = param_pad + param_scnt + data_pad + + data_scnt; + + /* + * Calling MBC_INIT() will initialized the structure and so the + * pointer to the mbuf chains will be lost. Therefore, we need + * to free the resources if any before calling MBC_INIT(). + */ + m_freem(reply.chain); + MBC_INIT(&reply, SMB_HEADER_ED_LEN + + sizeof (uchar_t) /* word parameters count */ + + 10*sizeof (ushort_t) /* word parameters */ + + n_setup*sizeof (ushort_t) /* setup parameters */ + + sizeof (ushort_t) /* total data byte count */ + + tot_packet_bytes); + + (void) smb_encode_mbc(&reply, SMB_HEADER_ED_FMT, + sr->first_smb_com, + sr->smb_rcls, + sr->smb_reh, + sr->smb_err, + sr->smb_flg | SMB_FLAGS_REPLY, + sr->smb_flg2, + sr->smb_pid_high, + sr->smb_sig, + sr->smb_tid, + sr->smb_pid, + sr->smb_uid, + sr->smb_mid); + + (void) smb_encode_mbc(&reply, + "b ww 2. www www b . C w #. C #. C", + 10 + n_setup, /* wct */ + n_param, /* Total Parameter Bytes */ + shares_tot_byte, /* Total Data Bytes */ + param_scnt, /* Total Parameter Bytes this buffer */ + param_off, /* Param offset from header start */ + param_disp, /* Param displacement */ + data_scnt, /* Total Data Bytes this buffer */ + data_off, /* Data offset from header start */ + data_disp, /* Data displacement */ + n_setup, /* suwcnt */ + &xa->rep_setup_mb, /* setup[] */ + tot_packet_bytes, /* Total data bytes */ + param_pad, + &xa->rep_param_mb, + data_pad, + &xa->rep_data_mb); + + if (sr->session->signing.flags & SMB_SIGNING_ENABLED) + smb_sign_reply(sr, NULL); + + (void) smb_session_send(sr->session, 0, &reply); + } + + (void) lmshrd_close_iterator(iterator); + MEM_FREE("smb", cmnt_start); + return (SDRC_NO_REPLY); +} + +int +smb_trans_net_share_get_info(struct smb_request *sr, struct smb_xa *xa) +{ + uint16_t opcode, level, max_bytes, access; + uint32_t type; + unsigned char *req_fmt; + unsigned char *rep_fmt; + struct mbuf_chain str_mb; + char *share; + char *path; + char *password; + char *comment; + lmshare_info_t si; + int shr_found; + + if (smb_decode_mbc(&xa->req_param_mb, "%wsss(lev)w(size)w", sr, + &opcode, &req_fmt, &rep_fmt, &share, &level, &max_bytes) != 0) + return (SDRC_UNSUPPORTED); + + (void) utf8_strlwr(share); + shr_found = lmshrd_getinfo(share, &si); + if (strcmp(share, "ipc$") == 0) { + type = STYPE_IPC; + path = ""; + password = ""; + access = SHARE_ACCESS_ALL; + } else if (shr_found) { + path = si.directory; + type = STYPE_DISKTREE; + if (path[strlen(path)] == '$') + type |= STYPE_HIDDEN; + password = ""; + access = SHARE_ACCESS_ALL; + } else { + /* We have no idea what this share is... */ + (void) smb_encode_mbc(&xa->rep_param_mb, "www", + NERR_NetNameNotFound, 0, 0); + return (SDRC_NORMAL_REPLY); + } + + if (shr_found) + comment = si.comment; + else + comment = ""; + + password = ""; + + MBC_INIT(&str_mb, max_bytes); + + switch (level) { + case 0 : + (void) smb_emit_SHARE_INFO_0(&xa->rep_data_mb, + (unsigned char *)share); + break; + + case 1 : + (void) smb_emit_SHARE_INFO_1(&xa->rep_data_mb, &str_mb, + (unsigned char *)share, type, + (unsigned char *)comment); + break; + + case 2 : + smb_emit_SHARE_INFO_2(&xa->rep_data_mb, &str_mb, sr, + (unsigned char *)share, type, (unsigned char *)comment, + access, path, password); + default: + m_freem(str_mb.chain); + return (SDRC_UNSUPPORTED); + } + + (void) smb_encode_mbc(&xa->rep_param_mb, "www", 0, + -MBC_LENGTH(&xa->rep_data_mb), + MBC_LENGTH(&xa->rep_data_mb) + MBC_LENGTH(&str_mb)); + (void) smb_encode_mbc(&xa->rep_data_mb, "C", &str_mb); + m_freem(str_mb.chain); + return (SDRC_NORMAL_REPLY); +} + +int +smb_trans_net_workstation_get_info(struct smb_request *sr, struct smb_xa *xa) +{ + uint16_t opcode, level, max_bytes; + unsigned char *req_fmt; + unsigned char *rep_fmt; + struct mbuf_chain str_mb; + char *domain; + char *hostname; + + if ((smb_decode_mbc(&xa->req_param_mb, "%wss(lev)w(size)w", sr, + &opcode, &req_fmt, &rep_fmt, &level, &max_bytes) != 0) || + (level != 10)) { + (void) smb_encode_mbc(&xa->rep_param_mb, "wwww", + NERR_BadTransactConfig, 0, 0, 0); + return (SDRC_NORMAL_REPLY); + } + + domain = smb_info.si.skc_resource_domain; + hostname = smb_info.si.skc_hostname; + + MBC_INIT(&str_mb, max_bytes); + + (void) smb_encode_mbc(&str_mb, "."); /* Prevent NULL pointers */ + + (void) smb_encode_mbc(&xa->rep_data_mb, "l", MBC_LENGTH(&str_mb)); + (void) smb_encode_mbc(&str_mb, "s", hostname); + (void) smb_encode_mbc(&xa->rep_data_mb, "l", MBC_LENGTH(&str_mb)); + (void) smb_encode_mbc(&str_mb, "s", "nobody"); + (void) smb_encode_mbc(&xa->rep_data_mb, "l", MBC_LENGTH(&str_mb)); + (void) smb_encode_mbc(&str_mb, "s", domain); + (void) smb_encode_mbc(&xa->rep_data_mb, "bbl", + SMB_VERSION_MAJOR, SMB_VERSION_MINOR, MBC_LENGTH(&str_mb)); + (void) smb_encode_mbc(&str_mb, "s", domain); + (void) smb_encode_mbc(&xa->rep_data_mb, "l", MBC_LENGTH(&str_mb)); + (void) smb_encode_mbc(&str_mb, "s", domain); + + (void) smb_encode_mbc(&xa->rep_param_mb, "www", 0, + -MBC_LENGTH(&xa->rep_data_mb), + MBC_LENGTH(&xa->rep_data_mb) + MBC_LENGTH(&str_mb)); + (void) smb_encode_mbc(&xa->rep_data_mb, "C", &str_mb); + m_freem(str_mb.chain); + return (SDRC_NORMAL_REPLY); +} + +int +smb_trans_net_user_get_info(struct smb_request *sr, struct smb_xa *xa) +{ + uint16_t opcode, level, max_bytes; + unsigned char *req_fmt; + unsigned char *rep_fmt; + unsigned char *user; + int rc; + + rc = smb_decode_mbc(&xa->req_param_mb, "%wssww", sr, + &opcode, + &req_fmt, + &rep_fmt, + &user, + &level, + &max_bytes); + + if (rc != 0) + return (SDRC_UNSUPPORTED); + + (void) smb_encode_mbc(&xa->rep_param_mb, "www", + NERR_UserNotFound, 0, 0); + return (SDRC_NORMAL_REPLY); +} + + +int +smb_trans_server_get_info(struct smb_request *sr, struct smb_xa *xa) +{ + uint16_t opcode, level, buf_size; + char *req_fmt; + char *rep_fmt; + char server_name[16]; + struct mbuf_chain str_mb; + char *hostname; + char *comment; + + if (smb_decode_mbc(&xa->req_param_mb, "%wssww", sr, + &opcode, &req_fmt, &rep_fmt, &level, &buf_size) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + comment = smb_info.si.skc_system_comment; + hostname = smb_info.si.skc_hostname; + + MBC_INIT(&str_mb, buf_size); + + bzero(server_name, sizeof (server_name)); + (void) strncpy(server_name, hostname, sizeof (server_name)); + + switch (level) { + case 0: + (void) smb_encode_mbc(&xa->rep_data_mb, "16c", server_name); + break; + case 1: + (void) smb_encode_mbc(&str_mb, "."); /* Prevent NULL pointers */ + (void) smb_encode_mbc(&xa->rep_data_mb, "16cbbll", server_name, + SMB_VERSION_MAJOR, SMB_VERSION_MINOR, + MY_SERVER_TYPE, MBC_LENGTH(&str_mb)); + (void) smb_encode_mbc(&str_mb, "s", comment); + break; + case 2: + /* B16BBDzDDDWWzWWWWWWWB21BzWWWWWWWWWWWWWWWWWWWWWWz */ + (void) smb_encode_mbc(&str_mb, "."); /* Prevent NULL pointers */ + /* B16BBDz */ + (void) smb_encode_mbc(&xa->rep_data_mb, "16cbbll", server_name, + SMB_VERSION_MAJOR, + SMB_VERSION_MINOR, MY_SERVER_TYPE, MBC_LENGTH(&str_mb)); + (void) smb_encode_mbc(&str_mb, "s", comment); + (void) smb_encode_mbc(&xa->rep_data_mb, "lllwwl", + (uint32_t)1, + (uint32_t)2, + (uint32_t)3, + (uint16_t)4, + (uint16_t)5, + MBC_LENGTH(&str_mb)); + (void) smb_encode_mbc(&str_mb, "s", "str1"); + (void) smb_encode_mbc(&xa->rep_data_mb, "wwwwwww21cbl", + (uint16_t)6, + (uint16_t)7, + (uint16_t)8, + (uint16_t)9, + (uint16_t)10, + (uint16_t)11, + (uint16_t)12, + "21 byte comment ", + (unsigned char)13, + MBC_LENGTH(&str_mb)); + (void) smb_encode_mbc(&str_mb, "s", "str2"); + (void) smb_encode_mbc(&xa->rep_data_mb, + "wwwwwwwwwwwwwwwwwwwwwwl", + (uint16_t)14, + (uint16_t)15, + (uint16_t)16, + (uint16_t)17, + (uint16_t)18, + (uint16_t)19, + (uint16_t)20, + (uint16_t)21, + (uint16_t)22, + (uint16_t)23, + (uint16_t)24, + (uint16_t)25, + (uint16_t)26, + (uint16_t)27, + (uint16_t)28, + (uint16_t)29, + (uint16_t)20, + (uint16_t)31, + (uint16_t)32, + (uint16_t)33, + (uint16_t)34, + (uint16_t)35, + MBC_LENGTH(&str_mb)); + (void) smb_encode_mbc(&str_mb, "s", "str3"); + break; + default: + m_freem(str_mb.chain); + return (SDRC_UNSUPPORTED); + } + + (void) smb_encode_mbc(&xa->rep_param_mb, "www", 0, + -MBC_LENGTH(&xa->rep_data_mb), + (MBC_LENGTH(&xa->rep_data_mb)) + + (MBC_LENGTH(&str_mb))); + (void) smb_encode_mbc(&xa->rep_data_mb, "C", &str_mb); + m_freem(str_mb.chain); + return (SDRC_NORMAL_REPLY); +} + +/* + * 6.4 The NetServerEnum2 RAP Service + * + * The NetServerEnum2 RAP service lists all computers of the specified type + * or types that are visible in the specified domains. It may also + * enumerate domains. + * + * The following definition uses the notation and terminology defined in + * the CIFS Remote Administration Protocol specification, which is required + * in order to make it well-defined. The definition is: + * + * uint16_t NetServerEnum2 ( + * uint16_t sLevel, + * RCVBUF pbBuffer, + * RCVBUFLEN cbBuffer, + * ENTCOUNT pcEntriesRead, + * uint16_t *pcTotalAvail, + * uint32_t fServerType, + * char *pszDomain, + * ); + * + * where: + * + * sLevel specifies the level of detail (0 or 1) requested. + * + * pbBuffer points to the buffer to receive the returned data. If the + * function is successful, the buffer contains a sequence of + * server_info_x structures, where x is 0 or 1, depending on the + * level of detail requested. + * + * cbBuffer specifies the size, in bytes, of the buffer pointed to by + * the pbBuffer parameter. + * + * pcEntriesRead points to a 16 bit variable that receives a count of + * the number of servers enumerated in the buffer. This count is + * valid only if NetServerEnum2 returns the NERR_Success or + * ERROR_MORE_DATA values. + * + * pcTotal Avail points to a 16 bit variable that receives a count of + * the total number of available entries. This count is valid only if + * NetServerEnum2 returns the NERR_Success or ERROR_MORE_DATA values. + * + * fServerType specifies the type or types of computers to enumerate. + * Computers that match at least one of the specified types are + * returned in the buffer. Possible values are defined in the request + * parameters section. + * + * pszDomain points to a null-terminated string that contains the + * name of the workgroup in which to enumerate computers of the + * specified type or types. If the pszDomain parameter is a null + * string or a null pointer, servers are enumerated for the current + * domain of the computer. + * + * 6.4.1 Transaction Request Parameters section + * + * The Transaction request parameters section in this instance contains: + * . The 16 bit function number for NetServerEnum2 which is 104. + * . The parameter descriptor string which is "WrLehDz". + * . The data descriptor string for the (returned) data which is "B16" for + * level detail 0 or "B16BBDz" for level detail 1. + * . The actual parameters as described by the parameter descriptor + * string. + * + * The parameters are: + * . A 16 bit integer with a value of 0 or 1 (corresponding to the "W" in + * the parameter descriptor string. This represents the level of detail + * the server is expected to return + * . A 16 bit integer that contains the size of the receive buffer. + * . A 32 bit integer that represents the type of servers the function + * should enumerate. The possible values may be any of the following or + * a combination of the following: + * + * SV_TYPE_WORKSTATION 0x00000001 All workstations + * SV_TYPE_SERVER 0x00000002 All servers + * SV_TYPE_SQLSERVER 0x00000004 Any server running with SQL + * server + * SV_TYPE_DOMAIN_CTRL 0x00000008 Primary domain controller + * SV_TYPE_DOMAIN_BAKCTRL 0x00000010 Backup domain controller + * SV_TYPE_TIME_SOURCE 0x00000020 Server running the timesource + * service + * SV_TYPE_AFP 0x00000040 Apple File Protocol servers + * SV_TYPE_NOVELL 0x00000080 Novell servers + * SV_TYPE_DOMAIN_MEMBER 0x00000100 Domain Member + * SV_TYPE_PRINTQ_SERVER 0x00000200 Server sharing print queue + * SV_TYPE_DIALIN_SERVER 0x00000400 Server running dialin service. + * SV_TYPE_XENIX_SERVER 0x00000800 Xenix server + * SV_TYPE_NT 0x00001000 NT server + * SV_TYPE_WFW 0x00002000 Server running Windows for + * Workgroups + * SV_TYPE_SERVER_NT 0x00008000 Windows NT non DC server + * SV_TYPE_POTENTIAL_BROWSER 0x00010000 Server that can run the browser + * service + * SV_TYPE_BACKUP_BROWSER 0x00020000 Backup browser server + * SV_TYPE_MASTER_BROWSER 0x00040000 Master browser server + * SV_TYPE_DOMAIN_MASTER 0x00080000 Domain Master Browser server + * SV_TYPE_LOCAL_LIST_ONLY 0x40000000 Enumerate only entries marked + * "local" + * SV_TYPE_DOMAIN_ENUM 0x80000000 Enumerate Domains. The pszDomain + * parameter must be NULL. + * + * . A null terminated ASCII string representing the pszDomain parameter + * described above + * + * 6.4.2 Transaction Request Data section + * + * There is no data or auxiliary data to send as part of the request. + * + * 6.4.3 Transaction Response Parameters section + * + * The transaction response parameters section consists of: + * . A 16 bit word indicating the return status. The possible values are: + * + * Code Value Description + * NERR_Success 0 No errors encountered + * ERROR_MORE_DATA 234 Additional data is available + * NERR_ServerNotStarted 2114 The RAP service on the remote computer + * is not running + * NERR_BadTransactConfig 2141 The server is not configured for + * transactions, IPC$ is not shared + * + * . A 16 bit "converter" word. + * . A 16 bit number representing the number of entries returned. + * . A 16 bit number representing the total number of available entries. + * If the supplied buffer is large enough, this will equal the number of + * entries returned. + * + * 6.4.4 Transaction Response Data section + * + * The return data section consists of a number of SERVER_INFO_1 structures. + * The number of such structures present is determined by the third entry + * (described above) in the return parameters section. + * + * At level detail 0, the Transaction response data section contains a + * number of SERVER_INFO_0 data structure. The number of such structures is + * equal to the 16 bit number returned by the server in the third parameter + * in the Transaction response parameter section. The SERVER_INFO_0 data + * structure is defined as: + * + * struct SERVER_INFO_0 { + * char sv0_name[16]; + * }; + * + * where: + * + * sv0_name is a null-terminated string that specifies the name of a + * computer or domain . + * + * At level detail 1, the Transaction response data section contains a + * number of SERVER_INFO_1 data structure. The number of such structures is + * equal to the 16 bit number returned by the server in the third parameter + * in the Transaction response parameter section. The SERVER_INFO_1 data + * structure is defined as: + * + * struct SERVER_INFO_1 { + * char sv1_name[16]; + * char sv1_version_major; + * char sv1_version_minor; + * uint32_t sv1_type; + * char *sv1_comment_or_master_browser; + * }; + * + * sv1_name contains a null-terminated string that specifies the name + * of a computer, or a domain name if SV_TYPE_DOMAIN_ENUM is set in + * sv1_type. + * + * sv1_version_major whatever was specified in the HostAnnouncement + * or DomainAnnouncement frame with which the entry was registered. + * + * sv1_version_minor whatever was specified in the HostAnnouncement + * or DomainAnnouncement frame with which the entry was registered. + * + * sv1_type specifies the type of software the computer is running. + * The member can be one or a combination of the values defined above + * in the Transaction request parameters section for fServerType. + * + * + * sv1_comment_or_master_browser points to a null-terminated string. If + * the sv1_type indicates that the entry is for a domain, this + * specifies the name of server running the domain master browser; + * otherwise, it specifies a comment describing the server. The comment + * can be a null string or the pointer may be a null pointer. + * + * In case there are multiple SERVER_INFO_1 data structures to + * return, the server may put all these fixed length structures in + * the return buffer, leave some space and then put all the variable + * length data (the actual value of the sv1_comment strings) at the + * end of the buffer. + * + * There is no auxiliary data to receive. + */ + +int +smb_trans_net_server_enum2(struct smb_request *sr, struct smb_xa *xa) +{ + uint16_t opcode, level, max_bytes; + uint32_t server_type; + unsigned char *domain; + struct mbuf_chain str_mb; + char *hostname, *s; + smb_kmod_cfg_t *si; + + if (smb_decode_mbc(&xa->req_param_mb, + "%w s(request format) s(reply format) wwls", sr, &opcode, &s, &s, + &level, &max_bytes, &server_type, &domain) != 0) + return (SDRC_UNSUPPORTED); + + si = &smb_info.si; + + if (utf8_strcasecmp(si->skc_resource_domain, (char *)domain) != 0) { + (void) smb_encode_mbc(&xa->rep_param_mb, "wwww", 0, 0, 0, 0); + return (SDRC_NORMAL_REPLY); + } + + if ((server_type & MY_SERVER_TYPE) == 0) { + (void) smb_encode_mbc(&xa->rep_param_mb, "wwww", 0, 0, 0, 0); + return (SDRC_NORMAL_REPLY); + } + + MBC_INIT(&str_mb, max_bytes); + + hostname = si->skc_hostname; + + (void) smb_encode_mbc(&xa->rep_data_mb, "16c", hostname); + if (level == 1) { + (void) smb_encode_mbc(&xa->rep_data_mb, "bbll", + SMB_VERSION_MAJOR, SMB_VERSION_MINOR, + MY_SERVER_TYPE, MBC_LENGTH(&str_mb)); + (void) smb_encode_mbc(&str_mb, "s", si->skc_system_comment); + } + + (void) smb_encode_mbc(&xa->rep_param_mb, "wwww", 0, + -MBC_LENGTH(&xa->rep_data_mb), 1, 1); + (void) smb_encode_mbc(&xa->rep_data_mb, "m", str_mb.chain); + return (SDRC_NORMAL_REPLY); +} + +/* + * is_supported_pipe + * + * Currently, just return 0 if the pipe is \\PIPE\repl otherwise + * return 1. + */ +int +is_supported_pipe(char *pname) +{ + if (utf8_strcasecmp(pname, PIPE_REPL) == 0) + return (0); + + return (1); +} + +int +smb_trans_dispatch(struct smb_request *sr, struct smb_xa *xa) +{ + int rc, pos; + int total_bytes, n_setup, n_param, n_data; + int param_off, param_pad, data_off, data_pad; + uint16_t opcode; + uint16_t devstate; + char *req_fmt; + char *rep_fmt; + struct vardata_block vdb; + + n_setup = (xa->smb_msrcnt < 200) ? xa->smb_msrcnt : 200; + n_setup++; + n_setup = n_setup & ~0x0001; + n_param = (xa->smb_mprcnt < smb_maxbufsize) + ? xa->smb_mprcnt : smb_maxbufsize; + n_param++; + n_param = n_param & ~0x0001; + rc = smb_maxbufsize - (SMBHEADERSIZE + 28 + n_setup + n_param); + n_data = (xa->smb_mdrcnt < rc) ? xa->smb_mdrcnt : rc; + MBC_INIT(&xa->rep_setup_mb, n_setup * 2); + MBC_INIT(&xa->rep_param_mb, n_param); + MBC_INIT(&xa->rep_data_mb, n_data); + + if (xa->smb_suwcnt > 0 && STYPE_ISIPC(sr->tid_tree->t_res_type)) { + rc = smb_decode_mbc(&xa->req_setup_mb, "ww", &opcode, + &sr->smb_fid); + if (rc != 0) + goto trans_err_not_supported; + switch (opcode) { + case TRANS_SET_NMPIPE_STATE: + if ((rc = smb_decode_mbc(&xa->req_param_mb, "w", + &devstate)) != 0) + goto trans_err_not_supported; + + rc = SDRC_NORMAL_REPLY; + break; + + case TRANS_TRANSACT_NMPIPE: + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, + sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, + NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + rc = smb_decode_mbc(&xa->req_data_mb, "#B", + xa->smb_tdscnt, &vdb); + if (rc != 0) + goto trans_err_not_supported; + + rc = smb_rpc_transact(sr, &vdb.uio); + break; + + case TRANS_WAIT_NMPIPE: + if (is_supported_pipe(xa->xa_smb_trans_name) == 0) { + smbsr_raise_error(sr, ERRDOS, ERRbadfile); + /* NOT REACHED */ + } + rc = SDRC_NORMAL_REPLY; + break; + + default: + goto trans_err_not_supported; + } + } else { + if ((utf8_strcasecmp(xa->xa_smb_trans_name, + PIPE_LANMAN) != 0) && + (utf8_strcasecmp( + xa->xa_smb_trans_name, MAILSLOT_LANMAN) != 0) && + (utf8_strcasecmp( + xa->xa_smb_trans_name, MAILSLOT_BROWSE) != 0) && + (utf8_strcasecmp( + xa->xa_smb_trans_name, MAILSLOT_MSBROWSE) != 0)) + goto trans_err_not_supported; + + if ((rc = smb_decode_mbc(&xa->req_param_mb, "%wss\b", sr, + &opcode, &req_fmt, &rep_fmt)) != 0) + goto trans_err_not_supported; + + /* for now, only respond to the */ + switch (opcode) { + case API_WshareEnum: + rc = smb_trans_net_share_enum(sr, xa); + break; + + case API_WshareGetInfo: + rc = smb_trans_net_share_get_info(sr, xa); + break; + + case API_WserverGetInfo: + rc = smb_trans_server_get_info(sr, xa); + break; + + case API_WUserGetInfo: + rc = smb_trans_net_user_get_info(sr, xa); + break; + + case API_WWkstaGetInfo: + rc = smb_trans_net_workstation_get_info(sr, xa); + break; + + case API_NetServerEnum2: + rc = smb_trans_net_server_enum2(sr, xa); + break; + + default: + goto trans_err_not_supported; + } + } + + switch (rc) { + case SDRC_NORMAL_REPLY: + break; + + case SDRC_DROP_VC: + case SDRC_NO_REPLY: + case SDRC_ERROR_REPLY: + return (rc); + + case SDRC_UNIMPLEMENTED: + case SDRC_UNSUPPORTED: + goto trans_err_not_supported; + + default: + break; + } + + n_setup = MBC_LENGTH(&xa->rep_setup_mb); + n_param = MBC_LENGTH(&xa->rep_param_mb); + n_data = MBC_LENGTH(&xa->rep_data_mb); + + if (xa->smb_msrcnt < n_setup || + xa->smb_mprcnt < n_param || + xa->smb_mdrcnt < n_data) { + goto trans_err_too_small; + } + + /* neato, blast it over there */ + + n_setup = (n_setup + 1) / 2; /* Convert to setup words */ + param_pad = 1; /* always one */ + param_off = param_pad + 32 + 21 + (n_setup << 1) + 2; + data_pad = (param_off + n_param) & 1; /* Pad to short */ + /* Param off from hdr start */ + data_off = param_off + n_param + data_pad; + total_bytes = param_pad + n_param + data_pad + n_data; + + smbsr_encode_result(sr, 10+n_setup, total_bytes, + "b ww 2. www www b . C w #. C #. C", + 10 + n_setup, /* wct */ + n_param, /* Total Parameter Bytes */ + n_data, /* Total Data Bytes */ + n_param, /* Total Parameter Bytes this buffer */ + param_off, /* Param offset from header start */ + 0, /* Param displacement */ + n_data, /* Total Data Bytes this buffer */ + data_off, /* Data offset from header start */ + 0, /* Data displacement */ + n_setup, /* suwcnt */ + &xa->rep_setup_mb, /* setup[] */ + total_bytes, /* Total data bytes */ + param_pad, + &xa->rep_param_mb, + data_pad, + &xa->rep_data_mb); + return (SDRC_NORMAL_REPLY); + +trans_err_too_small: + rc = NERR_BufTooSmall; + goto trans_err; + +trans_err_not_supported: + rc = ERROR_NOT_SUPPORTED; + goto trans_err; + +trans_err: + pos = MBC_LENGTH(&sr->reply) + 23; + smbsr_encode_result(sr, 10, 4, "b ww 2. www www b . w ww", + 10, /* wct */ + 4, 0, /* tpscnt tdscnt */ + 4, pos, 0, /* pscnt psoff psdisp */ + 0, 0, 0, /* dscnt dsoff dsdisp */ + 0, /* suwcnt */ + 4, /* bcc */ + rc, + 0); /* converter word? */ + + return (SDRC_NORMAL_REPLY); +} + +int +smb_trans2_dispatch(struct smb_request *sr, struct smb_xa *xa) +{ + int rc, pos; + int total_bytes, n_setup, n_param, n_data; + int param_off, param_pad, data_off, data_pad; + uint16_t opcode; + uint16_t nt_unknown_secret = 0x0100; + char *fmt; + + n_setup = (xa->smb_msrcnt < 200) ? xa->smb_msrcnt : 200; + n_setup++; + n_setup = n_setup & ~0x0001; + n_param = (xa->smb_mprcnt < smb_maxbufsize) + ? xa->smb_mprcnt : smb_maxbufsize; + n_param++; + n_param = n_param & ~0x0001; + rc = smb_maxbufsize - (SMBHEADERSIZE + 28 + n_setup + n_param); + n_data = (xa->smb_mdrcnt < rc) ? xa->smb_mdrcnt : rc; + MBC_INIT(&xa->rep_setup_mb, n_setup * 2); + MBC_INIT(&xa->rep_param_mb, n_param); + MBC_INIT(&xa->rep_data_mb, n_data); + + if (smb_decode_mbc(&xa->req_setup_mb, "w", &opcode) != 0) + goto trans_err_not_supported; + + /* + * Save this for /proc to read later. + */ + xa->smb_func = opcode; + + /* for now, only respond to the */ + switch (opcode) { + case TRANS2_CREATE_DIRECTORY: + rc = smb_com_trans2_create_directory(sr, xa); + break; + + case TRANS2_FIND_FIRST2: + /* + * Should have enough room to send the response + * data back to client. + */ + if (n_data == 0) { + smbsr_raise_cifs_error(sr, + NT_STATUS_INFO_LENGTH_MISMATCH, + ERRDOS, ERROR_BAD_LENGTH); + /* NOT REACHED */ + } + rc = smb_com_trans2_find_first2(sr, xa); + break; + + case TRANS2_FIND_NEXT2: + /* + * Should have enough room to send the response + * data back to client. + */ + if (n_data == 0) { + smbsr_raise_cifs_error(sr, + NT_STATUS_INFO_LENGTH_MISMATCH, + ERRDOS, ERROR_BAD_LENGTH); + /* NOT REACHED */ + } + rc = smb_com_trans2_find_next2(sr, xa); + break; + + case TRANS2_QUERY_FS_INFORMATION: + /* + * Should have enough room to send the response + * data back to client. + */ + if (n_data == 0) { + smbsr_raise_cifs_error(sr, + NT_STATUS_INFO_LENGTH_MISMATCH, + ERRDOS, ERROR_BAD_LENGTH); + /* NOT REACHED */ + } + rc = smb_com_trans2_query_fs_information(sr, xa); + break; + + case TRANS2_QUERY_PATH_INFORMATION: + /* + * Should have enough room to send the response + * data back to client. + */ + if (n_data == 0) { + smbsr_raise_cifs_error(sr, + NT_STATUS_INFO_LENGTH_MISMATCH, + ERRDOS, ERROR_BAD_LENGTH); + /* NOT REACHED */ + } + rc = smb_com_trans2_query_path_information(sr, xa); + break; + + case TRANS2_QUERY_FILE_INFORMATION: + /* + * Should have enough room to send the response + * data back to client. + */ + if (n_data == 0) { + smbsr_raise_cifs_error(sr, + NT_STATUS_INFO_LENGTH_MISMATCH, + ERRDOS, ERROR_BAD_LENGTH); + /* NOT REACHED */ + } + rc = smb_com_trans2_query_file_information(sr, xa); + break; + + case TRANS2_SET_PATH_INFORMATION: + rc = smb_com_trans2_set_path_information(sr, xa); + break; + + case TRANS2_SET_FILE_INFORMATION: + rc = smb_com_trans2_set_file_information(sr, xa); + break; + default: + goto trans_err_not_supported; + } + + switch (rc) { + case SDRC_NORMAL_REPLY: + break; + + case SDRC_DROP_VC: + case SDRC_NO_REPLY: + case SDRC_ERROR_REPLY: + return (rc); + + case SDRC_UNIMPLEMENTED: + case SDRC_UNSUPPORTED: + goto trans_err_not_supported; + + default: + break; + } + + n_setup = MBC_LENGTH(&xa->rep_setup_mb); + n_param = MBC_LENGTH(&xa->rep_param_mb); + n_data = MBC_LENGTH(&xa->rep_data_mb); + + if (xa->smb_msrcnt < n_setup || + xa->smb_mprcnt < n_param || + xa->smb_mdrcnt < n_data) { + goto trans_err_too_small; + } + + /* neato, blast it over there */ + + n_setup = (n_setup + 1) / 2; /* Conver to setup words */ + param_pad = 1; /* must be one */ + param_off = param_pad + 32 + 21 + (n_setup << 1) + 2; + + /* + * Including the nt_unknown_secret value persuades netmon to + * display the correct data format for QueryPathInfo and + * QueryFileInfo. + */ + if (opcode == TRANS2_QUERY_FILE_INFORMATION || + opcode == TRANS2_QUERY_PATH_INFORMATION) { + data_pad = sizeof (uint16_t); + data_off = param_off + n_param + data_pad; + fmt = "b ww 2. www www b . C w #. C w C"; + nt_unknown_secret = 0x0100; + } + else + { + data_pad = (param_off + n_param) & 1; /* Pad to short */ + /* Param off from hdr start */ + data_off = param_off + n_param + data_pad; + fmt = "b ww 2. www www b . C w #. C #. C"; + /*LINTED E_ASSIGN_NARROW_CONV*/ + nt_unknown_secret = data_pad; + } + + total_bytes = param_pad + n_param + data_pad + n_data; + + smbsr_encode_result(sr, 10+n_setup, total_bytes, + fmt, + 10 + n_setup, /* wct */ + n_param, /* Total Parameter Bytes */ + n_data /* + data_pad */, /* Total Data Bytes */ + n_param, /* Total Parameter Bytes this buffer */ + param_off, /* Param offset from header start */ + 0, /* Param displacement */ + n_data /* + data_pad */, /* Total Data Bytes this buffer */ + data_off, /* Data offset from header start */ + 0, /* Data displacement */ + n_setup, /* suwcnt */ + &xa->rep_setup_mb, /* setup[] */ + total_bytes, /* Total data bytes */ + param_pad, + &xa->rep_param_mb, + nt_unknown_secret, + &xa->rep_data_mb); + return (SDRC_NORMAL_REPLY); + +trans_err_too_small: + rc = NERR_BufTooSmall; + goto trans_err; + +trans_err_not_supported: + rc = ERROR_NOT_SUPPORTED; + goto trans_err; + +trans_err: + pos = MBC_LENGTH(&sr->reply) + 23; + smbsr_encode_result(sr, 10, 4, "b ww 2. www www b . w ww", + 10, /* wct */ + 4, 0, /* tpscnt tdscnt */ + 4, pos, 0, /* pscnt psoff psdisp */ + 0, 0, 0, /* dscnt dsoff dsdisp */ + 0, /* suwcnt */ + 4, /* bcc */ + rc, + 0); /* converter word? */ + return (SDRC_NORMAL_REPLY); +} + +smb_xa_t * +smb_xa_create( + smb_session_t *session, + smb_request_t *sr, + uint32_t total_parameter_count, + uint32_t total_data_count, + uint32_t max_parameter_count, + uint32_t max_data_count, + uint32_t max_setup_count, + uint32_t setup_word_count) +{ + smb_xa_t *xa, *nxa; + smb_llist_t *xlist; + + xa = MEM_ZALLOC("xa", sizeof (smb_xa_t)); + xa->xa_refcnt = 1; + xa->smb_com = sr->smb_com; + xa->smb_flg = sr->smb_flg; + xa->smb_flg2 = sr->smb_flg2; + xa->smb_tid = sr->smb_tid; + xa->smb_pid = sr->smb_pid; + xa->smb_uid = sr->smb_uid; + xa->xa_smb_mid = sr->smb_mid; + xa->reply_seqnum = sr->reply_seqnum; + xa->smb_tpscnt = total_parameter_count; + xa->smb_tdscnt = total_data_count; + xa->smb_mprcnt = max_parameter_count; + xa->smb_mdrcnt = max_data_count; + xa->smb_msrcnt = max_setup_count; + xa->smb_suwcnt = setup_word_count; + xa->xa_session = session; + xa->xa_magic = SMB_XA_MAGIC; + + /* + * The new xa structure is checked against the current list to see + * if it exists already. + */ + xlist = &session->s_xa_list; + smb_llist_enter(xlist, RW_WRITER); + nxa = smb_llist_head(xlist); + while (nxa) { + ASSERT(nxa->xa_magic == SMB_XA_MAGIC); + if (nxa->xa_smb_mid == xa->xa_smb_mid && + nxa->smb_pid == xa->smb_pid && + !SMB_XA_CLOSED(nxa) && + !(nxa->xa_flags & SMB_XA_FLAG_COMPLETE)) { + smb_llist_exit(xlist); + MEM_FREE("xa", xa); + return (NULL); + } + nxa = smb_llist_next(xlist, nxa); + } + smb_llist_insert_tail(xlist, xa); + smb_llist_exit(xlist); + return (xa); +} + +void +smb_xa_delete(smb_xa_t *xa) +{ + ASSERT(xa->xa_refcnt == 0); + ASSERT(SMB_XA_CLOSED(xa)); + + if (xa->xa_smb_trans_name) + MEM_FREE("smb", xa->xa_smb_trans_name); + + if (xa->rep_setup_mb.chain != NULL) + m_freem(xa->rep_setup_mb.chain); + if (xa->rep_param_mb.chain != NULL) + m_freem(xa->rep_param_mb.chain); + if (xa->rep_data_mb.chain != NULL) + m_freem(xa->rep_data_mb.chain); + + xa->xa_magic = (uint32_t)~SMB_XA_MAGIC; + MEM_FREE("xa", xa); +} + +smb_xa_t * +smb_xa_hold(smb_xa_t *xa) +{ + mutex_enter(&xa->xa_mutex); + xa->xa_refcnt++; + ASSERT(xa->xa_refcnt); + mutex_exit(&xa->xa_mutex); + return (xa); +} + +void +smb_xa_rele(smb_session_t *session, smb_xa_t *xa) +{ + mutex_enter(&xa->xa_mutex); + ASSERT(xa->xa_refcnt); + xa->xa_refcnt--; + if (SMB_XA_CLOSED(xa) && (xa->xa_refcnt == 0)) { + mutex_exit(&xa->xa_mutex); + smb_llist_enter(&session->s_xa_list, RW_WRITER); + smb_llist_remove(&session->s_xa_list, xa); + smb_llist_exit(&session->s_xa_list); + smb_xa_delete(xa); + return; + } + mutex_exit(&xa->xa_mutex); +} + +int +smb_xa_open(smb_xa_t *xa) +{ + int rc; + + mutex_enter(&xa->xa_mutex); + + ASSERT((xa->xa_flags & SMB_XA_FLAG_OPEN) == 0); + + if ((xa->xa_flags & SMB_XA_FLAG_CLOSE) == 0) { + xa->xa_flags |= SMB_XA_FLAG_OPEN; + rc = 0; + } else { + rc = ERROR_INVALID_HANDLE; + } + + mutex_exit(&xa->xa_mutex); + + return (rc); +} + +void +smb_xa_close(smb_xa_t *xa) +{ + mutex_enter(&xa->xa_mutex); + xa->xa_flags |= SMB_XA_FLAG_CLOSE; + xa->xa_flags &= ~SMB_XA_FLAG_OPEN; + + if (xa->xa_refcnt == 0) { + mutex_exit(&xa->xa_mutex); + smb_llist_enter(&xa->xa_session->s_xa_list, RW_WRITER); + smb_llist_remove(&xa->xa_session->s_xa_list, xa); + smb_llist_exit(&xa->xa_session->s_xa_list); + smb_xa_delete(xa); + return; + } + + mutex_exit(&xa->xa_mutex); +} + +int +smb_xa_complete(smb_xa_t *xa) +{ + int rc; + + mutex_enter(&xa->xa_mutex); + if (xa->xa_flags & (SMB_XA_FLAG_COMPLETE | SMB_XA_FLAG_CLOSE)) { + rc = 0; + } else { + rc = 1; + xa->xa_flags |= SMB_XA_FLAG_COMPLETE; + } + mutex_exit(&xa->xa_mutex); + return (rc); +} + +smb_xa_t * +smb_xa_find( + smb_session_t *session, + uint16_t pid, + uint16_t mid) +{ + smb_xa_t *xa; + smb_llist_t *xlist; + + xlist = &session->s_xa_list; + smb_llist_enter(xlist, RW_READER); + xa = smb_llist_head(xlist); + while (xa) { + mutex_enter(&xa->xa_mutex); + if (xa->xa_smb_mid == mid && + xa->smb_pid == pid && + !SMB_XA_CLOSED(xa) && + !(xa->xa_flags & SMB_XA_FLAG_COMPLETE)) { + xa->xa_refcnt++; + ASSERT(xa->xa_refcnt); + mutex_exit(&xa->xa_mutex); + break; + } + mutex_exit(&xa->xa_mutex); + xa = smb_llist_next(xlist, xa); + } + smb_llist_exit(xlist); + return (xa); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_common_tree.c b/usr/src/uts/common/fs/smbsrv/smb_common_tree.c new file mode 100644 index 000000000000..7906233a3548 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_common_tree.c @@ -0,0 +1,397 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Common code for tree connections. + */ + +#include +#include +#include +#include +#include +#include + + +#define SMB_TREE_EMSZ 64 + +#define ADMINISTRATORS_SID "S-1-5-32-544" + +int smb_tcon_mute = 0; + + +int smbsr_setup_share(struct smb_request *, char *, int32_t, char *); +void smbsr_share_report(struct smb_request *, char *, char *, char *); +int smb_get_stype(const char *, const char *, int32_t *); + + +/* + * smbsr_connect_tree + * + * Set up a share. A Uniform Naming Convention (UNC) string is suppose to + * be in the form: \\HOST\SHARENAME. A sharename alone is also acceptable. + * We don't actually audit the host, we just ensure that the \ are present + * and extract the share name. Share names are case insensitive so we map + * the share name to lower-case. So it is important that all internal + * mechanisms (user interface, etc. use lower-case names. + */ +int +smbsr_connect_tree(struct smb_request *sr) +{ + char errmsg[SMB_TREE_EMSZ]; + char *sharename; + char *access_msg; + int32_t stype; + int rc; + + errmsg[0] = '\0'; + (void) utf8_strlwr(sr->arg.tcon.path); + sharename = sr->arg.tcon.path; + + if (sharename[0] == '\\') { + /* + * Looks like a UNC path, make sure the format is correct. + */ + if (sharename[1] != '\\') { + smbsr_raise_error(sr, ERRSRV, ERRinvnetname); + /* NOTREACHED */ + } + + if ((sharename = strchr(sharename+2, '\\')) == 0) { + smbsr_raise_error(sr, ERRSRV, ERRinvnetname); + /* NOTREACHED */ + } + + ++sharename; + } else if (strchr(sharename, '\\')) { + /* + * This should be a sharename: no embedded '\' allowed. + */ + smbsr_raise_error(sr, ERRSRV, ERRinvnetname); + /* NOTREACHED */ + } + + if (smb_get_stype(sharename, sr->arg.tcon.service, &stype) != 0) { + smbsr_raise_cifs_error(sr, NT_STATUS_BAD_DEVICE_TYPE, + ERRDOS, ERROR_BAD_DEV_TYPE); + /* NOTREACHED */ + } + + if ((rc = smbsr_setup_share(sr, sharename, stype, errmsg)) != 0) { + access_msg = "access denied"; + smbsr_share_report(sr, sharename, access_msg, errmsg); + + /* + * W2K sometimes tries to connect to user shares using an + * anonymous IPC connection. NT returns access denied. + */ + if (rc == ERRaccess) + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, + ERRSRV, ERRaccess); + else + smbsr_raise_error(sr, ERRSRV, rc); + /* NOTREACHED */ + } + + if (STYPE_ISDSK(sr->tid_tree->t_res_type)) { + if (SMB_TREE_IS_READ_ONLY(sr)) + access_msg = "ro access granted"; + else + access_msg = "rw access granted"; + + smbsr_share_report(sr, sharename, access_msg, errmsg); + } + + return (rc); +} + + +/* + * smbsr_share_report + * + * Report share access result to syslog. + */ +/*ARGSUSED*/ +void +smbsr_share_report(struct smb_request *sr, char *sharename, + char *access_msg, char *errmsg) +{ + smb_user_t *user; + + user = sr->uid_user; + ASSERT(user); + + if (smb_tcon_mute) + return; + + if (user->u_name) { + /* + * Only report normal users, i.e. ignore W2K misuse + * of the IPC connection by filtering out internal + * names such as nobody and root. + */ + if ((strcmp(user->u_name, "root") == 0) || + (strcmp(user->u_name, "nobody") == 0)) { + return; + } + } + + cmn_err(CE_NOTE, "smbd[%s\\%s]: %s %s", + user->u_domain, user->u_name, sharename, access_msg); +} + +/* + * smbsr_setup_share + * + * This is where the real of setting up share is done. The main thing + * to note is that we resolve ambiguities by assuming that a directory is + * being requested. This function returns error codes, rather than calling + * smbsr_raise_error. We return 0 on success or a non-zero error code if + * there is a problem. + */ +int +smbsr_setup_share(struct smb_request *sr, char *sharename, int32_t stype, + char *errmsg) +{ + smb_node_t *dir_snode = NULL; + smb_node_t *snode = NULL; + char last_component[MAXNAMELEN]; + smb_tree_t *tree; + char *resource; + uint16_t access = SMB_TREE_READ_WRITE; + int rc; + lmshare_info_t si; + nt_sid_t *sid; + fsvol_attr_t vol_attr; + smb_attr_t attr; + int is_admin; + smb_user_t *user = sr->uid_user; + cred_t *u_cred; + + ASSERT(user); + u_cred = user->u_cred; + ASSERT(u_cred); + + bzero(&si, sizeof (lmshare_info_t)); + + /* + * XXX Host based access control check to go here. + */ + + if (STYPE_ISIPC(stype)) { + if ((user->u_flags & SMB_USER_FLAG_IPC) && + smb_info.si.skc_restrict_anon) { + (void) strlcpy(errmsg, "anonymous access restricted", + SMB_TREE_EMSZ); + return (ERRaccess); + } + + bzero(&vol_attr, sizeof (fsvol_attr_t)); + resource = sharename; + sr->arg.tcon.service = "IPC"; + + tree = smb_tree_connect(sr->uid_user, access, + sharename, resource, stype, 0, &vol_attr); + + if (tree == NULL) + return (ERRaccess); + + sr->smb_tid = tree->t_tid; + sr->tid_tree = tree; + return (0); + } + + /* + * From here on we can assume that this is a disk share. + */ + ASSERT(STYPE_ISDSK(stype)); + + if (user->u_flags & SMB_USER_FLAG_IPC) { + (void) strlcpy(errmsg, "IPC only", SMB_TREE_EMSZ); + return (ERRaccess); + } + + /* + * Handle the default administration shares: C$, D$ etc. + * Only a user with admin rights is allowed to map these + * shares. + */ + if ((is_admin = lmshrd_is_admin(sharename)) == NERR_InternalError) { + (void) strlcpy(errmsg, "internal error", SMB_TREE_EMSZ); + return (ERRaccess); + } + + if (is_admin) { + sid = nt_sid_strtosid(ADMINISTRATORS_SID); + if (sid) { + rc = smb_cred_is_member(u_cred, sid); + MEM_FREE("smbsrv", sid); + if (rc == 0) { + (void) strlcpy(errmsg, + "not administrator", SMB_TREE_EMSZ); + return (ERRaccess); + } + } + } + + if (lmshrd_getinfo(sharename, &si) != NERR_Success) { + (void) strlcpy(errmsg, "share not found", SMB_TREE_EMSZ); + return (ERRinvnetname); + } + + resource = si.directory; + sr->arg.tcon.service = "A:"; + +#ifdef HOST_ACCESS + /* + * XXX This needs some sharemgr work + */ + if (hostaccess == APRV_ACC_RO) + access = SMB_TREE_READ_ONLY; +#endif /* HOST_ACCESS */ + + /* + * No password or password OK. Now check that the directory + * actually exists. + * + * The snode reference from smb_pathname_reduce() will not be + * released in this routine (except in an error path) because + * trees need a reference to their root node. The reference + * will be released upon tree deallocation. + */ + + rc = smb_pathname_reduce(sr, u_cred, resource, 0, 0, &dir_snode, + last_component); + + if (rc) { + (void) strlcpy(errmsg, "smb_pathname_reduce", SMB_TREE_EMSZ); + return (ERRinvnetname); + } + + rc = smb_fsop_lookup(sr, u_cred, SMB_FOLLOW_LINKS, 0, dir_snode, + last_component, &snode, &attr, 0, 0); + + smb_node_release(dir_snode); + + if (rc) { + (void) strlcpy(errmsg, "smb_fsop_lookup", SMB_TREE_EMSZ); + rc = ERRinvnetname; + goto error_out; + } + + if ((rc = fsd_getattr(&snode->tree_fsd, &vol_attr)) != 0) { + (void) strlcpy(errmsg, "fsd_getattr", SMB_TREE_EMSZ); + rc = ERRinvnetname; + goto error_out; + } + + tree = smb_tree_connect(sr->uid_user, access, + sharename, resource, stype, snode, &vol_attr); + + if (tree == NULL) { + rc = ERRaccess; + goto error_out; + } + + sr->smb_tid = tree->t_tid; + sr->tid_tree = tree; + return (0); + +error_out: + if (snode) + smb_node_release(snode); + + return (rc); +} + +/* + * smb_get_stype + * + * Map the service to a resource type. Valid values for service + * (CIFS/1.0 section 4.1.4) are: + * + * A: Disk share + * LPT1: Printer + * IPC Named pipe + * COMM Communications device + * ????? Any type of device (wildcard) + * + * We support IPC and disk shares; anything else is currently treated + * as an error. IPC$ is reserved as the named pipe share. + */ +int +smb_get_stype(const char *sharename, const char *service, int32_t *stype_ret) +{ + const char *any = "?????"; + + if ((strcmp(service, any) == 0) || (strcasecmp(service, "IPC") == 0)) { + if (strcasecmp(sharename, "IPC$") == 0) { + *stype_ret = STYPE_IPC; + return (0); + } + } + + if ((strcmp(service, any) == 0) || (strcasecmp(service, "A:") == 0)) { + if (strcasecmp(sharename, "IPC$") == 0) + return (-1); + + *stype_ret = STYPE_DISKTREE; + return (0); + } + + return (-1); +} + +/* + * smbsr_rq_notify + * + * Notify all requests, except sr, associated with the specified tree + * that it's time to complete. + * It's assumed that the tree has already been clipped from the session + * list so that no new requests can be added to the list while we're in + * here. + * + * Note that sr may be null. + * + * Returns: + */ +void +smbsr_rq_notify(smb_request_t *sr, smb_session_t *session, smb_tree_t *tree) +{ + struct smb_request *asr; + + smb_slist_enter(&session->s_req_list); + asr = smb_slist_head(&session->s_req_list); + while (asr) { + ASSERT(asr->sr_magic == SMB_REQ_MAGIC); + if ((asr != sr) && (asr->tid_tree == tree)) { + smb_request_cancel(asr); + } + asr = smb_slist_next(&session->s_req_list, asr); + } + smb_slist_exit(&session->s_req_list); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_copy.c b/usr/src/uts/common/fs/smbsrv/smb_copy.c new file mode 100644 index 000000000000..4b33818eaba4 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_copy.c @@ -0,0 +1,121 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: copy_file + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 3 + * USHORT Tid2; Second (target) path TID + * USHORT OpenFunction; What to do if target file exists + * USHORT Flags; Flags to control copy operation: + * bit 0 - target must be a file + * bit 1 - target must be a dir. + * bit 2 - copy target mode: + * 0 = binary, 1 = ASCII + * bit 3 - copy source mode: + * 0 = binary, 1 = ASCII + * bit 4 - verify all writes + * bit 5 - tree copy + * USHORT ByteCount; Count of data bytes; min = 2 + * UCHAR SourceFileNameFormat; 0x04 + * STRING SourceFileName; Pathname of source file + * UCHAR TargetFileNameFormat; 0x04 + * STRING TargetFileName; Pathname of target file + * + * The file at SourceName is copied to TargetFileName, both of which must refer + * to paths on the same server. + * + * The Tid in the header is associated with the source while Tid2 is + * associated with the destination. These fields may contain the same or + * differing valid values. Tid2 can be set to -1 indicating that this is to + * be the same Tid as in the SMB header. This allows use of the move + * protocol with SMB_TREE_CONNECT_ANDX. + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 1 + * USHORT Count; Number of files copied + * USHORT ByteCount; Count of data bytes; min = 0 + * UCHAR ErrorFileFormat; 0x04 (only if error) + * STRING ErrorFileName; + * + * The source path must refer to an existing file or files. Wildcards are + * permitted. Source files specified by wildcards are processed until an + * error is encountered. If an error is encountered, the expanded name of + * the file is returned in ErrorFileName. Wildcards are not permitted in + * TargetFileName. TargetFileName can refer to either a file or a direc- + * tory. + * + * The destination can be required to be a file or a directory by the bits + * in Flags. If neither bit0 nor bit1 are set, the destination may be + * either a file or a directory. Flags also controls the copy mode. In a + * ascii copy for the source, the copy stops the first time an EOF + * (control-Z) is encountered. In a ascii copy for the target, the server + * + * must make sure that there is exactly one EOF in the target file and that + * it is the last character of the file. + * + * If the destination is a file and the source contains wildcards, the + * destination file will either be truncated or appended to at the start of + * the operation depending on bits in OpenFunction (see section 3.7). + * Subsequent files will then be appended to the file. + * + * If the negotiated dialect is LM1.2X002 or later, bit5 of Flags is used + * to specify a tree copy on the remote server. When this option is + * selected the destination must not be an existing file and the source + * mode must be binary. A request with bit5 set and either bit0 or bit3 + * set is therefore an error. When the tree copy mode is selected, the + * Count field in the server response is undefined. + * + * 4.2.13.1 Errors + * + * ERRDOS/ERRfilexists + * ERRDOS/ERRshare + * ERRDOS/ERRnofids + * ERRDOS/ERRbadfile + * ERRDOS/ERRnoaccess + * ERRDOS/ERRnofiles + * ERRDOS/ERRbadshare + * ERRSRV/ERRnoaccess + * ERRSRV/ERRinvdevice + * ERRSRV/ERRinvid + * ERRSRV/ERRbaduid + * ERRSRV/ERRaccess + */ + +#include + +/*ARGSUSED*/ +int +smb_com_copy(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_create.c b/usr/src/uts/common/fs/smbsrv/smb_create.c new file mode 100644 index 000000000000..fccdf5ba9e70 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_create.c @@ -0,0 +1,224 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +#define SMB_CREATE_NAMEBUF_SZ 16 + +static uint32_t smb_common_create(struct smb_request *sr); + +/* + * Create a new file, or truncate an existing file to zero length, + * open the file and return a fid. The file is specified using a + * fully qualified name relative to the tree. + */ +int +smb_com_create(struct smb_request *sr) +{ + struct open_param *op = &sr->arg.open; + uint32_t status; + + bzero(op, sizeof (sr->arg.open)); + + if (smbsr_decode_vwv(sr, "wl", &op->dattr, &op->utime.tv_sec) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (smbsr_decode_data(sr, "%S", sr, &op->fqi.path) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + op->create_disposition = FILE_OVERWRITE_IF; + status = smb_common_create(sr); + + switch (status) { + case NT_STATUS_SUCCESS: + break; + + case NT_STATUS_SHARING_VIOLATION: + smbsr_raise_cifs_error(sr, NT_STATUS_SHARING_VIOLATION, + ERRDOS, ERROR_SHARING_VIOLATION); + /* NOTREACHED */ + break; + + default: + smbsr_raise_nt_error(sr, status); + /* NOTREACHED */ + break; + } + + smbsr_encode_result(sr, 1, 0, "bww", 1, sr->smb_fid, 0); + return (SDRC_NORMAL_REPLY); +} + +/* + * Create a new file and return a fid. The file is specified using + * a fully qualified name relative to the tree. + */ +int +smb_com_create_new(struct smb_request *sr) +{ + struct open_param *op = &sr->arg.open; + uint32_t status; + + bzero(op, sizeof (sr->arg.open)); + + if (smbsr_decode_vwv(sr, "wl", &op->dattr, &op->utime.tv_sec) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (smbsr_decode_data(sr, "%S", sr, &op->fqi.path) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + op->create_disposition = FILE_CREATE; + status = smb_common_create(sr); + + switch (status) { + case NT_STATUS_SUCCESS: + break; + + case NT_STATUS_SHARING_VIOLATION: + smbsr_raise_cifs_error(sr, NT_STATUS_SHARING_VIOLATION, + ERRDOS, ERROR_SHARING_VIOLATION); + /* NOTREACHED */ + break; + + default: + smbsr_raise_nt_error(sr, status); + /* NOTREACHED */ + break; + } + + smbsr_encode_result(sr, 1, 0, "bww", 1, sr->smb_fid, 0); + return (SDRC_NORMAL_REPLY); +} + + +/* + * Create a unique file in the specified directory relative to the + * current tree. No attributes are specified. + */ +int +smb_com_create_temporary(struct smb_request *sr) +{ + static uint16_t tmp_id = 10000; + struct open_param *op = &sr->arg.open; + char name[SMB_CREATE_NAMEBUF_SZ]; + char *buf; + uint32_t status; + uint16_t reserved; + uint16_t bcc; + + bzero(op, sizeof (sr->arg.open)); + + if (smbsr_decode_vwv(sr, "wl", &reserved, &op->utime.tv_sec) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (smbsr_decode_data(sr, "%S", sr, &op->fqi.path) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + ++tmp_id; + bcc = 1; /* null terminator */ + bcc += snprintf(name, SMB_CREATE_NAMEBUF_SZ, "tt%05d.tmp", tmp_id); + + buf = smbsr_malloc(&sr->request_storage, MAXPATHLEN); + (void) snprintf(buf, MAXPATHLEN, "%s\\%s", op->fqi.path, name); + op->fqi.path = buf; + op->create_disposition = FILE_CREATE; + status = smb_common_create(sr); + + switch (status) { + case NT_STATUS_SUCCESS: + break; + + case NT_STATUS_SHARING_VIOLATION: + smbsr_raise_cifs_error(sr, NT_STATUS_SHARING_VIOLATION, + ERRDOS, ERROR_SHARING_VIOLATION); + /* NOTREACHED */ + break; + + default: + smbsr_raise_nt_error(sr, status); + /* NOTREACHED */ + break; + } + + smbsr_encode_result(sr, 1, 0, "bwwwbs", 1, sr->smb_fid, bcc, 4, name); + return (SDRC_NORMAL_REPLY); +} + +/* + * Common create file function. The file is opened in compatibility + * mode with read/write access. + */ +uint32_t +smb_common_create(struct smb_request *sr) +{ + struct open_param *op = &sr->arg.open; + uint32_t status; + + op->utime.tv_sec = smb_local_time_to_gmt(op->utime.tv_sec); + op->utime.tv_nsec = 0; + op->omode = SMB_DA_ACCESS_READ_WRITE | SMB_DA_SHARE_COMPATIBILITY; + op->desired_access = smb_omode_to_amask(op->omode); + op->share_access = smb_denymode_to_sharemode(op->omode, op->fqi.path); + + if ((op->desired_access == ((uint32_t)SMB_INVALID_AMASK)) || + (op->share_access == ((uint32_t)SMB_INVALID_SHAREMODE))) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_PARAMETER, + ERRDOS, ERROR_INVALID_PARAMETER); + /* NOTREACHED */ + } + + op->dsize = 0; + + if (sr->smb_flg & SMB_FLAGS_OPLOCK) { + if (sr->smb_flg & SMB_FLAGS_OPLOCK_NOTIFY_ANY) { + op->my_flags = MYF_BATCH_OPLOCK; + } else { + op->my_flags = MYF_EXCLUSIVE_OPLOCK; + } + } + + status = smb_open_subr(sr); + + if (MYF_OPLOCK_TYPE(op->my_flags) == MYF_OPLOCK_NONE) { + sr->smb_flg &= + ~(SMB_FLAGS_OPLOCK | SMB_FLAGS_OPLOCK_NOTIFY_ANY); + } + + return (status); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_create_directory.c b/usr/src/uts/common/fs/smbsrv/smb_create_directory.c new file mode 100644 index 000000000000..e369f59b47fd --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_create_directory.c @@ -0,0 +1,272 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: create_directory + * + * The create directory message is sent to create a new directory. The + * appropriate Tid and additional pathname are passed. The directory must + * not exist for it to be created. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes; min = 2 + * UCHAR BufferFormat; 0x04 + * STRING DirectoryName[]; Directory name + * + * Servers require clients to have at least create permission for the + * subtree containing the directory in order to create a new directory. + * The creator's access rights to the new directory are be determined by + * local policy on the server. + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * + * USHORT ByteCount; Count of data bytes = 0 + */ + +#include +#include +#include +#include + +typedef struct smb_request SmbRequest; + +typedef struct { + char *sp_path; /* Original path */ + char *sp_curp; /* Current pointer into the original path */ + SmbRequest *sp_sr; /* Current request pointer */ +} SmbPath; + + +extern int smb_common_create_directory(struct smb_request *sr); + + +static int smbpath_next(SmbPath* spp); +static SmbPath* smbpath_new(SmbRequest* sr); + + +/* + * smb_com_create_directory + * + * It is possible to get a full pathname here and the client expects any + * or all of the components to be created if they don't already exist. + */ +int +smb_com_create_directory(struct smb_request *sr) +{ + SmbPath* spp; + DWORD status; + int rc = 0; + + if (smbsr_decode_data(sr, "%S", sr, &sr->arg.dirop.fqi.path) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if ((status = smb_validate_dirname(sr->arg.dirop.fqi.path)) != 0) { + if (sr->session->capabilities & CAP_STATUS32) + smbsr_raise_nt_error(sr, status); + else + smbsr_raise_error(sr, ERRDOS, ERROR_INVALID_NAME); + + /* NOTREACHED */ + } + + /* + * Try each component of the path. It is all right to get an EEXIST + * on each component except the last. + */ + spp = smbpath_new(sr); + + while (smbpath_next(spp)) { + rc = smb_common_create_directory(sr); + if (rc != 0 && rc != EEXIST) + smbsr_raise_errno(sr, rc); + } + + /* We should have created one directory successfully! */ + if (rc != 0) + smbsr_raise_errno(sr, rc); + + smbsr_encode_empty_result(sr); + return (SDRC_NORMAL_REPLY); +} + + +/* + * smb_validate_dirname + * + * Very basic directory name validation: checks for colons in a path. + * Need to skip the drive prefix since it contains a colon. + * + * Returns 0 if the name is valid, otherwise NT_STATUS_NOT_A_DIRECTORY. + */ +DWORD +smb_validate_dirname(char *path) +{ + char *name; + + if ((name = path) != 0) { + name += strspn(name, "\\"); + + if (strchr(name, ':') != 0) + return (NT_STATUS_NOT_A_DIRECTORY); + } + + return (0); +} + + +/* + * smb_common_create_directory + * + * Currently called from: + * smb_com_create_directory + * smb_com_trans2_create_directory + * + * Returns errno values. + */ +int +smb_common_create_directory(struct smb_request *sr) +{ + int rc; + smb_attr_t new_attr; + struct smb_node *dnode; + struct smb_node *node; + + if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + sr->arg.dirop.fqi.srch_attr = 0; + + rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_NOT_EXIST); + if (rc) + return (rc); + + /* + * Because of FQM_PATH_MUST_NOT_EXIST and the successful return + * value, only fqi.dir_snode has a valid parameter (fqi.last_snode + * is NULL). + */ + dnode = sr->arg.dirop.fqi.dir_snode; + + bzero(&new_attr, sizeof (new_attr)); + new_attr.sa_vattr.va_type = VDIR; + new_attr.sa_vattr.va_mode = 0777; + new_attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE; + + if ((rc = smb_fsop_mkdir(sr, sr->user_cr, dnode, + sr->arg.dirop.fqi.last_comp, &new_attr, + &sr->arg.dirop.fqi.last_snode, + &sr->arg.dirop.fqi.last_attr)) != 0) { + smb_node_release(dnode); + SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); + return (rc); + } + + node = sr->arg.dirop.fqi.last_snode; + node->flags |= NODE_FLAGS_CREATED; + + sr->arg.open.create_options = FILE_DIRECTORY_FILE; + + smb_node_release(node); + smb_node_release(dnode); + SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); + return (0); +} + +SmbPath* +smbpath_new(SmbRequest* sr) +{ + int pathLen; + char *xpath; + SmbPath *spp; + + /* Malloc from the request storage area. This is freed automatically */ + /* so we don't need to worry about freeing it later */ + spp = smbsr_malloc(&sr->request_storage, sizeof (SmbPath)); + spp->sp_path = sr->arg.dirop.fqi.path; + pathLen = strlen(spp->sp_path); + spp->sp_curp = spp->sp_path; + xpath = smbsr_malloc(&sr->request_storage, pathLen + 1); + sr->arg.dirop.fqi.path = xpath; + spp->sp_sr = sr; + + return (spp); +} + +/* + * Perhaps somewhat dangerous since everything happens as a side effect. The + * returns 1 if there is a valid component updated to the fqi, 0 otherwise. + */ +int +smbpath_next(SmbPath* spp) +{ + char *xp; + int xlen; + + if (spp == 0) + return (0); + + /* Move the index to the "next" "\" and copy the path to the fqi */ + /* path for the next component. */ + + /* First look for the next component */ + while (*spp->sp_curp == '\\') + spp->sp_curp++; + + /* Now get to the end of the component */ + xp = spp->sp_curp; /* Remember from where we started */ + while (*spp->sp_curp != '\0' && *spp->sp_curp != '\\') { + spp->sp_curp++; + } + + /* If we made no progress, we are done */ + if (xp == spp->sp_curp) + return (0); + + /* + * Now copy the original path up to but not including our current + * pointer + */ + + /*LINTED E_PTRDIFF_OVERFLOW*/ + xlen = spp->sp_curp - spp->sp_path; + (void) strncpy(spp->sp_sr->arg.dirop.fqi.path, spp->sp_path, xlen); + + /* Now NULL terminate it */ + spp->sp_sr->arg.dirop.fqi.path[xlen] = '\0'; + return (1); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_delete.c b/usr/src/uts/common/fs/smbsrv/smb_delete.c new file mode 100644 index 000000000000..f35e3c4273f2 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_delete.c @@ -0,0 +1,354 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include + +static DWORD smb_delete_check(struct smb_request *sr, struct smb_node *node, + uint16_t dattr, smb_error_t *smberr); +static DWORD smb_delete_share_check(struct smb_node *node); + +/* + * smb_com_delete + * + * The delete file message is sent to delete a data file. The appropriate + * Tid and additional pathname are passed. Read only files may not be + * deleted, the read-only attribute must be reset prior to file deletion. + * + * NT supports a hidden permission known as File Delete Child (FDC). If + * the user has FullControl access to a directory, the user is permitted + * to delete any object in the directory regardless of the permissions + * on the object. + * + * Client Request Description + * ================================== ================================= + * UCHAR WordCount; Count of parameter words = 1 + * USHORT SearchAttributes; + * USHORT ByteCount; Count of data bytes; min = 2 + * UCHAR BufferFormat; 0x04 + * STRING FileName[]; File name + * + * Multiple files may be deleted in response to a single request as + * SMB_COM_DELETE supports wildcards + * + * SearchAttributes indicates the attributes that the target file(s) must + * have. If the attribute is zero then only normal files are deleted. If + * the system file or hidden attributes are specified then the delete is + * inclusive -both the specified type(s) of files and normal files are + * deleted. Attributes are described in the "Attribute Encoding" section + * of this document. + * + * If bit0 of the Flags2 field of the SMB header is set, a pattern is + * passed in, and the file has a long name, then the passed pattern much + * match the long file name for the delete to succeed. If bit0 is clear, a + * pattern is passed in, and the file has a long name, then the passed + * pattern must match the file's short name for the deletion to succeed. + * + * Server Response Description + * ================================== ================================= + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes = 0 + * + * 4.2.10.1 Errors + * + * ERRDOS/ERRbadpath + * ERRDOS/ERRbadfile + * ERRDOS/ERRnoaccess + * ERRDOS/ERRbadshare # returned by NT for files that are already open + * ERRHRD/ERRnowrite + * ERRSRV/ERRaccess + * ERRSRV/ERRinvdevice + * ERRSRV/ERRinvid + * ERRSRV/ERRbaduid + */ +int +smb_com_delete(struct smb_request *sr) +{ + int rc; + int od = 0; + int deleted = 0; + unsigned short sattr; + char *path; + struct smb_node *dir_snode; + struct smb_node *node = 0; + char *name; + char *fname; + char *sname; + char *fullname; + smb_error_t smberr; + int is_stream; + smb_odir_context_t *pc; + + pc = kmem_zalloc(sizeof (*pc), KM_SLEEP); + fname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + sname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + fullname = kmem_alloc(MAXPATHLEN, KM_SLEEP); + + if (smbsr_decode_vwv(sr, "w", &sattr) != 0) { + kmem_free(pc, sizeof (*pc)); + kmem_free(name, MAXNAMELEN); + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + kmem_free(fullname, MAXPATHLEN); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (smbsr_decode_data(sr, "%S", sr, &path) != 0) { + kmem_free(pc, sizeof (*pc)); + kmem_free(name, MAXNAMELEN); + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + kmem_free(fullname, MAXPATHLEN); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + is_stream = smb_stream_parse_name(path, fname, sname); + + (void) smb_rdir_open(sr, path, sattr); + dir_snode = sr->sid_odir->d_dir_snode; + + /* + * This while loop is meant to deal with wildcards. + * It is not expected that wildcards will exist for + * streams. For the streams case, it is expected + * that the below loop will be executed only once. + */ + + while ((rc = smb_rdir_next(sr, &node, pc)) == 0) { + (void) strlcpy(name, pc->dc_name, MAXNAMELEN); + + if (smb_delete_check(sr, node, pc->dc_dattr, &smberr) + != NT_STATUS_SUCCESS) { + smb_node_release(node); + goto delete_error; + } + + smb_node_release(node); + node = NULL; + + if (is_stream) { + /* + * It is assumed that fname does not contain + * any wildcards . + * smb_fsop_remove() requires filename+streamname + */ + (void) snprintf(fullname, MAXPATHLEN, "%s%s", + fname, sname); + rc = smb_fsop_remove(sr, sr->user_cr, dir_snode, + fullname, 0); + } else { + /* + * name (i.e. pc->dc_name) is the on-disk name + * unless there is a case collision, in which + * case readdir will have returned a mangled name. + */ + if (smb_maybe_mangled_name(name) == 0) + od = 1; + + rc = smb_fsop_remove(sr, sr->user_cr, dir_snode, + name, od); + } + + if (rc != 0) { + if (rc != ENOENT) { + smb_rdir_close(sr); + kmem_free(pc, sizeof (*pc)); + kmem_free(name, MAXNAMELEN); + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + kmem_free(fullname, MAXPATHLEN); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + } else { + deleted++; + } + } + + if ((rc != 0) && (rc != ENOENT)) { + /* rc returned by smb_rdir_next() */ + smb_rdir_close(sr); + kmem_free(pc, sizeof (*pc)); + kmem_free(name, MAXNAMELEN); + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + kmem_free(fullname, MAXPATHLEN); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + if (deleted == 0) { + smberr.errcls = ERRDOS; + smberr.errcode = ERROR_FILE_NOT_FOUND; + smberr.status = (sr->sid_odir->d_wildcards == 0) + ? NT_STATUS_OBJECT_NAME_NOT_FOUND : NT_STATUS_NO_SUCH_FILE; + goto delete_error; + } + + smb_rdir_close(sr); + + smbsr_encode_empty_result(sr); + + kmem_free(pc, sizeof (*pc)); + kmem_free(name, MAXNAMELEN); + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + kmem_free(fullname, MAXPATHLEN); + return (SDRC_NORMAL_REPLY); + +delete_error: + smb_rdir_close(sr); + kmem_free(pc, sizeof (*pc)); + kmem_free(name, MAXNAMELEN); + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + kmem_free(fullname, MAXPATHLEN); + smbsr_raise_cifs_error(sr, + smberr.status, smberr.errcls, smberr.errcode); + /* NOTREACHED */ + return (SDRC_NORMAL_REPLY); /* compiler complains otherwise */ +} + +static DWORD +smb_delete_check( + struct smb_request *sr, + struct smb_node *node, + uint16_t dattr, + smb_error_t *smberr) +{ + if (dattr & SMB_FA_DIRECTORY) { + smberr->errcls = ERRDOS; + smberr->errcode = ERROR_ACCESS_DENIED; + smberr->status = NT_STATUS_FILE_IS_A_DIRECTORY; + return (NT_STATUS_UNSUCCESSFUL); + } + + if ((dattr & SMB_FA_READONLY) || + (node->flags & NODE_CREATED_READONLY)) { + smberr->errcls = ERRDOS; + smberr->errcode = ERROR_ACCESS_DENIED; + smberr->status = NT_STATUS_CANNOT_DELETE; + return (NT_STATUS_UNSUCCESSFUL); + } + + /* + * NT does not always close a file immediately, which + * can cause the share and access checking to fail + * (the node refcnt is greater than one), and the file + * doesn't get deleted. Breaking the oplock before + * share and access checking gives the client a chance + * to close the file. + */ + if (OPLOCKS_IN_FORCE(node)) { + smberr->status = smb_break_oplock(sr, node); + + if (smberr->status != NT_STATUS_SUCCESS) { + smberr->errcls = ERRDOS; + smberr->errcode = ERROR_VC_DISCONNECTED; + return (NT_STATUS_UNSUCCESSFUL); + } + } + + smberr->status = smb_delete_share_check(node); + if (smberr->status == NT_STATUS_SHARING_VIOLATION) { + smberr->errcls = ERRDOS; + smberr->errcode = ERROR_SHARING_VIOLATION; + return (NT_STATUS_UNSUCCESSFUL); + } + + /* + * This should be done after Share checking due to tests with + * W2K. I got sharing violation error trying to delete a + * locked file which is basically the same error if you + * try to delete a non-locked open file. + * + * One thing that I discovered during these tests is that + * W2K rejects lock requests on open files which are opened + * with Metadata open modes. The error is STATUS_ACCESS_DENIED. + */ + if (smb_lock_range_access(sr, node, 0, 0, FILE_WRITE_DATA) != + NT_STATUS_SUCCESS) { + smberr->errcls = ERRDOS; + smberr->errcode = ERROR_ACCESS_DENIED; + smberr->status = NT_STATUS_ACCESS_DENIED; + return (NT_STATUS_UNSUCCESSFUL); + } + + + return (NT_STATUS_SUCCESS); +} + +/* + * smb_delete_share_check + * + * An open file can be deleted only if opened for + * accessing meta data. Share modes aren't important + * in this case. + * + * NOTE: there is another mechanism for deleting an + * open file that NT clients usually use this method. + * That's setting "Delete on close" flag for an open + * file, in this way the file will be deleted after + * last close. This flag can be set by SmbTrans2SetFileInfo + * with FILE_DISPOSITION_INFO information level. + * For setting this flag file should be opened by + * DELETE access in the FID that is passed in the Trans2 + * request. + */ +static DWORD +smb_delete_share_check(struct smb_node *node) +{ + smb_ofile_t *file; + + if (node == 0 || node->n_refcnt <= 1) + return (NT_STATUS_SUCCESS); + + if (node->attr.sa_vattr.va_type == VDIR) + return (NT_STATUS_SUCCESS); + + smb_llist_enter(&node->n_ofile_list, RW_READER); + file = smb_llist_head(&node->n_ofile_list); + while (file) { + ASSERT(file->f_magic == SMB_OFILE_MAGIC); + if (file->f_granted_access & + (FILE_READ_DATA | + FILE_WRITE_DATA | + FILE_APPEND_DATA | + FILE_EXECUTE | + DELETE)) { + smb_llist_exit(&node->n_ofile_list); + return (NT_STATUS_SHARING_VIOLATION); + } + file = smb_llist_next(&node->n_ofile_list, file); + } + smb_llist_exit(&node->n_ofile_list); + return (NT_STATUS_SUCCESS); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_delete_directory.c b/usr/src/uts/common/fs/smbsrv/smb_delete_directory.c new file mode 100644 index 000000000000..e959b240b82c --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_delete_directory.c @@ -0,0 +1,114 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include + +/* + * smb_com_delete_directory + * + * The delete directory message is sent to delete an empty directory. The + * appropriate Tid and additional pathname are passed. The directory must + * be empty for it to be deleted. + * + * NT supports a hidden permission known as File Delete Child (FDC). If + * the user has FullControl access to a directory, the user is permitted + * to delete any object in the directory regardless of the permissions + * on the object. + * + * Client Request Description + * ================================== ================================= + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes; min = 2 + * UCHAR BufferFormat; 0x04 + * STRING DirectoryName[]; Directory name + * + * The directory to be deleted cannot be the root of the share specified + * by Tid. + * + * Server Response Description + * ================================== ================================= + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes = 0 + */ +int +smb_com_delete_directory(struct smb_request *sr) +{ + smb_node_t *dnode; + int rc; + + if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + if (smbsr_decode_data(sr, "%S", sr, &sr->arg.dirop.fqi.path) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->arg.dirop.fqi.srch_attr = 0; + + rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_EXIST); + if (rc) { + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + dnode = sr->arg.dirop.fqi.last_snode; + + if (dnode->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) { + smb_node_release(dnode); + smb_node_release(sr->arg.dirop.fqi.dir_snode); + SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); + + smbsr_raise_cifs_error(sr, NT_STATUS_CANNOT_DELETE, + ERRDOS, ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + smb_node_release(dnode); + + dnode = sr->arg.dirop.fqi.dir_snode; + + rc = smb_fsop_rmdir(sr, sr->user_cr, dnode, + sr->arg.dirop.fqi.last_comp_od, 1); + if (rc != 0) { + smb_node_release(dnode); + SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + smb_node_release(dnode); + SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); + + smbsr_encode_empty_result(sr); + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_dispatch.c b/usr/src/uts/common/fs/smbsrv/smb_dispatch.c new file mode 100644 index 000000000000..96a23bb995ce --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_dispatch.c @@ -0,0 +1,1574 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * + * Dispatching SMB requests. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * ALMOST EVERYTHING YOU NEED TO KNOW ABOUT A SERVER MESSAGE BLOCK + * + * Request + * Header + * Magic 0xFF 'S' 'M' 'B' + * smb_com a byte, the "first" command + * Error a 4-byte union, ignored in a request + * smb_flg a one byte set of eight flags + * smb_flg2 a two byte set of 16 flags + * . twelve reserved bytes, have a role + * in connectionless transports (IPX, UDP?) + * smb_tid a 16-bit tree ID, a mount point sorta, + * 0xFFFF is this command does not have + * or require a tree context + * smb_pid a 16-bit process ID + * smb_uid a 16-bit user ID, specific to this "session" + * and mapped to a system (bona-fide) UID + * smb_mid a 16-bit multiplex ID, used to differentiate + * multiple simultaneous requests from the same + * process (pid) (ref RPC "xid") + * + * Chained (AndX) commands (0 or more) + * smb_wct a byte, number of 16-bit words containing + * command parameters, min 2 for chained command + * andx_com a byte, the "next" command, 0xFF for none + * . an unused byte + * andx_off a 16-bit offset, byte displacement from &Magic + * to the smb_wct field of the "next" command, + * ignore if andx_com is 0xFF, s/b 0 if no next + * smb_vwv[] 0 or more 16-bit (sorta) parameters for + * "this" command (i.e. smb_com if this is the + * first parameters, or the andx_com of the just + * previous block. + * smb_bcc a 16-bit count of smb_data[] bytes + * smb_data[] 0 or more bytes, format specific to commands + * padding[] Optional padding + * + * Last command + * smb_wct a byte, number of 16-bit words containing + * command parameters, min 0 for chained command + * smb_vwv[] 0 or more 16-bit (sorta) parameters for + * "this" command (i.e. smb_com if this is the + * first parameters, or the andx_com of the just + * previous block. + * smb_bcc a 16-bit count of smb_data[] bytes + * smb_data[] 0 or more bytes, format specific to commands + * + * Reply + * Header + * Magic 0xFF 'S' 'M' 'B' + * smb_com a byte, the "first" command, corresponds + * to request + * Error a 4-byte union, coding depends on dialect in use + * for "DOS" errors + * a byte for error class + * an unused byte + * a 16-bit word for error code + * for "NT" errors + * a 32-bit error code which + * is a packed class and specifier + * for "OS/2" errors + * I don't know + * The error information is specific to the + * last command in the reply chain. + * smb_flg a one byte set of eight flags, 0x80 bit set + * indicating this message is a reply + * smb_flg2 a two byte set of 16 flags + * . twelve reserved bytes, have a role + * in connectionless transports (IPX, UDP?) + * smb_tid a 16-bit tree ID, a mount point sorta, + * should be the same as the request + * smb_pid a 16-bit process ID, MUST BE the same as request + * smb_uid a 16-bit user ID, specific to this "session" + * and mapped to a system (bona-fide) UID, + * should be the same as request + * smb_mid a 16-bit multiplex ID, used to differentiate + * multiple simultaneous requests from the same + * process (pid) (ref RPC "xid"), MUST BE the + * same as request + * padding[] Optional padding + * + * Chained (AndX) commands (0 or more) + * smb_wct a byte, number of 16-bit words containing + * command parameters, min 2 for chained command, + * andx_com a byte, the "next" command, 0xFF for none, + * corresponds to request, if this is the chained + * command that had an error set to 0xFF + * . an unused byte + * andx_off a 16-bit offset, byte displacement from &Magic + * to the smb_wct field of the "next" command, + * ignore if andx_com is 0xFF, s/b 0 if no next + * smb_vwv[] 0 or more 16-bit (sorta) parameters for + * "this" command (i.e. smb_com if this is the + * first parameters, or the andx_com of the just + * previous block. Empty if an error. + * smb_bcc a 16-bit count of smb_data[] bytes + * smb_data[] 0 or more bytes, format specific to commands + * empty if an error. + * + * Last command + * smb_wct a byte, number of 16-bit words containing + * command parameters, min 0 for chained command + * smb_vwv[] 0 or more 16-bit (sorta) parameters for + * "this" command (i.e. smb_com if this is the + * first parameters, or the andx_com of the just + * previous block, empty if an error. + * smb_bcc a 16-bit count of smb_data[] bytes + * smb_data[] 0 or more bytes, format specific to commands, + * empty if an error. + */ + +#include +#include + +#define SMB_ALL_DISPATCH_STAT_INCR(stat) atomic_inc_64(&stat); + +int smb_dispatch_diags = 0; +static kstat_t *smb_dispatch_ksp = NULL; +static kstat_named_t *smb_dispatch_kstat_data = NULL; +static int smb_dispatch_kstat_size = 0; + +static int is_andx_com(unsigned char); + +extern void smbsr_decode_error(struct smb_request *sr); +extern void smbsr_encode_error(struct smb_request *sr); +extern void smbsr_check_result(struct smb_request *sr, int wct, int bcc); + +extern int smb_com_cancel_forward(struct smb_request *); +extern int smb_com_check_directory(struct smb_request *); +extern int smb_com_close(struct smb_request *); +extern int smb_com_close_and_tree_disconnect(struct smb_request *); +extern int smb_com_close_print_file(struct smb_request *); +extern int smb_com_copy(struct smb_request *); +extern int smb_com_create(struct smb_request *); +extern int smb_com_create_directory(struct smb_request *); +extern int smb_com_create_new(struct smb_request *); +extern int smb_com_create_temporary(struct smb_request *); +extern int smb_com_delete(struct smb_request *); +extern int smb_com_delete_directory(struct smb_request *); +extern int smb_com_echo(struct smb_request *); +extern int smb_com_find(struct smb_request *); +extern int smb_com_find_close(struct smb_request *); +extern int smb_com_find_close2(struct smb_request *); +extern int smb_com_find_notify_close(struct smb_request *); +extern int smb_com_find_unique(struct smb_request *); +extern int smb_com_flush(struct smb_request *); +extern int smb_com_forward_user_name(struct smb_request *); +extern int smb_com_get_machine_name(struct smb_request *); +extern int smb_com_get_print_queue(struct smb_request *); +extern int smb_com_invalid_command(struct smb_request *); +extern int smb_com_ioctl(struct smb_request *); +extern int smb_com_ioctl_secondary(struct smb_request *); +extern int smb_com_lock_and_read(struct smb_request *); +extern int smb_com_lock_byte_range(struct smb_request *); +extern int smb_com_locking_andx(struct smb_request *); +extern int smb_com_logoff_andx(struct smb_request *); +extern int smb_com_move(struct smb_request *); +extern int smb_com_negotiate(struct smb_request *); +extern int smb_com_nt_cancel(struct smb_request *); +extern int smb_com_nt_create_andx(struct smb_request *); +extern int smb_com_nt_transact(struct smb_request *); +extern int smb_com_nt_transact_secondary(struct smb_request *); +extern int smb_com_open(struct smb_request *); +extern int smb_com_open_andx(struct smb_request *); +extern int smb_com_open_print_file(struct smb_request *); +extern int smb_com_process_exit(struct smb_request *); +extern int smb_com_query_information(struct smb_request *); +extern int smb_com_query_information2(struct smb_request *); +extern int smb_com_query_information_disk(struct smb_request *); +extern int smb_com_read(struct smb_request *); +extern int smb_com_read_andx(struct smb_request *); +extern int smb_com_read_mpx(struct smb_request *); +extern int smb_com_read_mpx_secondary(struct smb_request *); +extern int smb_com_read_raw(struct smb_request *); +extern int smb_com_rename(struct smb_request *); +extern int smb_com_search(struct smb_request *); +extern int smb_com_seek(struct smb_request *); +extern int smb_com_send_broadcast_message(struct smb_request *); +extern int smb_com_send_end_mb_message(struct smb_request *); +extern int smb_com_send_single_message(struct smb_request *); +extern int smb_com_send_start_mb_message(struct smb_request *); +extern int smb_com_send_text_mb_message(struct smb_request *); +extern int smb_com_session_setup_andx(struct smb_request *); +extern int smb_com_set_information(struct smb_request *); +extern int smb_com_set_information2(struct smb_request *); +extern int smb_com_transaction(struct smb_request *); +extern int smb_com_transaction2(struct smb_request *); +extern int smb_com_transaction2_secondary(struct smb_request *); +extern int smb_com_transaction_secondary(struct smb_request *); +extern int smb_com_tree_connect(struct smb_request *); +extern int smb_com_tree_connect_andx(struct smb_request *); +extern int smb_com_tree_disconnect(struct smb_request *); +extern int smb_com_unlock_byte_range(struct smb_request *); +extern int smb_com_write(struct smb_request *); +extern int smb_com_write_and_close(struct smb_request *); +extern int smb_com_write_and_unlock(struct smb_request *); +extern int smb_com_write_andx(struct smb_request *); +extern int smb_com_write_complete(struct smb_request *); +extern int smb_com_write_mpx(struct smb_request *); +extern int smb_com_write_mpx_secondary(struct smb_request *); +extern int smb_com_write_print_file(struct smb_request *); +extern int smb_com_write_raw(struct smb_request *); + +static smb_dispatch_table_t dispatch[256] = { + { smb_com_create_directory, /* 0x00 000 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbCreateDirectory", KSTAT_DATA_UINT64 } }, + { smb_com_delete_directory, /* 0x01 001 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbDeleteDirectory", KSTAT_DATA_UINT64 } }, + { smb_com_open, /* 0x02 002 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbOpen", KSTAT_DATA_UINT64 } }, + { smb_com_create, /* 0x03 003 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbCreate", KSTAT_DATA_UINT64 } }, + { smb_com_close, /* 0x04 004 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbClose", KSTAT_DATA_UINT64 } }, + { smb_com_flush, /* 0x05 005 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbFlush", KSTAT_DATA_UINT64 } }, + { smb_com_delete, /* 0x06 006 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbDelete", KSTAT_DATA_UINT64 } }, + { smb_com_rename, /* 0x07 007 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbRename", KSTAT_DATA_UINT64 } }, + { smb_com_query_information, /* 0x08 008 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbQueryInformation", KSTAT_DATA_UINT64 } }, + { smb_com_set_information, /* 0x09 009 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbSetInformation", KSTAT_DATA_UINT64 } }, + { smb_com_read, /* 0x0A 010 */ + PC_NETWORK_PROGRAM_1_0, SDDF_SUPPRESS_SHOW, + RW_READER, + { "SmbRead", KSTAT_DATA_UINT64 } }, + { smb_com_write, /* 0x0B 011 */ + PC_NETWORK_PROGRAM_1_0, SDDF_SUPPRESS_SHOW, + RW_READER, + { "SmbWrite", KSTAT_DATA_UINT64 } }, + { smb_com_lock_byte_range, /* 0x0C 012 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbLockByteRange", KSTAT_DATA_UINT64 } }, + { smb_com_unlock_byte_range, /* 0x0D 013 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbUnlockByteRange", KSTAT_DATA_UINT64 } }, + { smb_com_create_temporary, /* 0x0E 014 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbCreateTemporary", KSTAT_DATA_UINT64 } }, + { smb_com_create_new, /* 0x0F 015 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbCreateNew", KSTAT_DATA_UINT64 } }, + { smb_com_check_directory, /* 0x10 016 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbCheckDirectory", KSTAT_DATA_UINT64 } }, + { smb_com_process_exit, /* 0x11 017 */ + PC_NETWORK_PROGRAM_1_0, SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID, + RW_READER, + { "SmbProcessExit", KSTAT_DATA_UINT64 } }, + { smb_com_seek, /* 0x12 018 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbSeek", KSTAT_DATA_UINT64 } }, + { smb_com_lock_and_read, /* 0x13 019 */ + LANMAN1_0, SDDF_SUPPRESS_SHOW, + RW_READER, + { "SmbLockAndRead", KSTAT_DATA_UINT64 } }, + { smb_com_write_and_unlock, /* 0x14 020 */ + LANMAN1_0, SDDF_SUPPRESS_SHOW, + RW_READER, + { "SmbWriteAndUnlock", KSTAT_DATA_UINT64 } }, + { 0, 0, 0, RW_READER, 0 }, /* 0x15 021 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x16 022 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x17 023 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x18 024 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x19 025 */ + { smb_com_read_raw, /* 0x1A 026 */ + LANMAN1_0, SDDF_SUPPRESS_SHOW, + RW_WRITER, + { "SmbReadRaw", KSTAT_DATA_UINT64 } }, + { smb_com_read_mpx, /* 0x1B 027 */ + LANMAN1_0, SDDF_SUPPRESS_SHOW, + RW_READER, + { "SmbReadMpx", KSTAT_DATA_UINT64 } }, + { smb_com_read_mpx_secondary, /* 0x1C 028 */ + LANMAN1_0, SDDF_SUPPRESS_SHOW, + RW_READER, + { "SmbReadMpxSecondary", KSTAT_DATA_UINT64 } }, + { smb_com_write_raw, /* 0x1D 029 */ + LANMAN1_0, SDDF_SUPPRESS_SHOW | SDDF_SUPPRESS_UNLEASH, + RW_WRITER, + { "SmbWriteRaw", KSTAT_DATA_UINT64 } }, + { smb_com_write_mpx, /* 0x1E 030 */ + LANMAN1_0, SDDF_SUPPRESS_SHOW, + RW_READER, + { "SmbWriteMpx", KSTAT_DATA_UINT64 } }, + { smb_com_write_mpx_secondary, /* 0x1F 031 */ + LANMAN1_0, SDDF_SUPPRESS_SHOW, + RW_READER, + { "SmbWriteMpxSecondary", KSTAT_DATA_UINT64 } }, + { smb_com_write_complete, /* 0x20 032 */ + LANMAN1_0, SDDF_SUPPRESS_SHOW, + RW_READER, + { "SmbWriteComplete", KSTAT_DATA_UINT64 } }, + { 0, 0, 0, 0, 0 }, /* 0x21 033 */ + { smb_com_set_information2, /* 0x22 034 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbSetInformation2", KSTAT_DATA_UINT64 } }, + { smb_com_query_information2, /* 0x23 035 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbQueryInformation2", KSTAT_DATA_UINT64 } }, + { smb_com_locking_andx, /* 0x24 036 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbLockingX", KSTAT_DATA_UINT64 } }, + { smb_com_transaction, /* 0x25 037 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbTransaction", KSTAT_DATA_UINT64 } }, + { smb_com_transaction_secondary, /* 0x26 038 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbTransactionSecondary", KSTAT_DATA_UINT64 } }, + { smb_com_ioctl, /* 0x27 039 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbIoctl", KSTAT_DATA_UINT64 } }, + { smb_com_ioctl_secondary, /* 0x28 040 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbIoctlSecondary", KSTAT_DATA_UINT64 } }, + { smb_com_copy, /* 0x29 041 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbCopy", KSTAT_DATA_UINT64 } }, + { smb_com_move, /* 0x2A 042 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbMove", KSTAT_DATA_UINT64 } }, + { smb_com_echo, /* 0x2B 043 */ + LANMAN1_0, SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID, + RW_READER, + { "SmbEcho", KSTAT_DATA_UINT64 } }, + { smb_com_write_and_close, /* 0x2C 044 */ + LANMAN1_0, SDDF_SUPPRESS_SHOW, + RW_READER, + { "SmbWriteAndClose", KSTAT_DATA_UINT64 } }, + { smb_com_open_andx, /* 0x2D 045 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbOpenX", KSTAT_DATA_UINT64 } }, + { smb_com_read_andx, /* 0x2E 046 */ + LANMAN1_0, SDDF_SUPPRESS_SHOW, + RW_READER, + { "SmbReadX", KSTAT_DATA_UINT64 } }, + { smb_com_write_andx, /* 0x2F 047 */ + LANMAN1_0, SDDF_SUPPRESS_SHOW, + RW_READER, + { "SmbWriteX", KSTAT_DATA_UINT64 } }, + { 0, 0, 0, 0, 0 }, /* 0x30 048 */ + { smb_com_close_and_tree_disconnect, /* 0x31 049 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbCloseAndTreeDisconnect", KSTAT_DATA_UINT64 } }, + { smb_com_transaction2, /* 0x32 050 */ + LM1_2X002, SDDF_NO_FLAGS, + RW_READER, + { "SmbTransaction2", KSTAT_DATA_UINT64 } }, + { smb_com_transaction2_secondary, /* 0x33 051 */ + LM1_2X002, SDDF_NO_FLAGS, + RW_READER, + { "SmbTransaction2Secondary", KSTAT_DATA_UINT64 } }, + { smb_com_find_close2, /* 0x34 052 */ + LM1_2X002, SDDF_NO_FLAGS, + RW_READER, + { "SmbFindClose2", KSTAT_DATA_UINT64 } }, + { smb_com_find_notify_close, /* 0x35 053 */ + LM1_2X002, SDDF_NO_FLAGS, + RW_READER, + { "SmbFindNotifyClose", KSTAT_DATA_UINT64 } }, + { 0, 0, 0, RW_READER, 0 }, /* 0x36 054 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x37 055 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x38 056 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x39 057 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x3A 058 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x3B 059 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x3C 060 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x3D 061 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x3E 062 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x3F 063 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x40 064 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x41 065 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x42 066 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x43 067 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x44 068 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x45 069 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x46 070 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x47 071 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x48 072 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x49 073 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x4A 074 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x4B 075 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x4C 076 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x4D 077 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x4E 078 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x4F 079 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x50 080 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x51 081 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x52 082 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x53 083 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x54 084 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x55 085 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x56 086 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x57 087 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x58 088 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x59 089 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x5A 090 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x5B 091 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x5C 092 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x5D 093 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x5E 094 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x5F 095 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x60 096 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x61 097 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x62 098 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x63 099 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x64 100 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x65 101 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x66 102 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x67 103 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x68 104 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x69 105 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x6A 106 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x6B 107 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x6C 108 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x6D 109 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x6E 110 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x6F 111 */ + { smb_com_tree_connect, /* 0x70 112 */ + PC_NETWORK_PROGRAM_1_0, SDDF_SUPPRESS_TID, + RW_READER, + { "SmbTreeConnect", KSTAT_DATA_UINT64 } }, + { smb_com_tree_disconnect, /* 0x71 113 */ + PC_NETWORK_PROGRAM_1_0, SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID, + RW_READER, + { "SmbTreeDisconnect", KSTAT_DATA_UINT64 } }, + { smb_com_negotiate, /* 0x72 114 */ + PC_NETWORK_PROGRAM_1_0, SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID, + RW_WRITER, + { "SmbNegotiate", KSTAT_DATA_UINT64 } }, + { smb_com_session_setup_andx, /* 0x73 115 */ + LANMAN1_0, SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID, + RW_READER, + { "SmbSessionSetupX", KSTAT_DATA_UINT64 } }, + { smb_com_logoff_andx, /* 0x74 116 */ + LM1_2X002, SDDF_SUPPRESS_TID, + RW_READER, + { "SmbLogoffX", KSTAT_DATA_UINT64 } }, + { smb_com_tree_connect_andx, /* 0x75 117 */ + LANMAN1_0, SDDF_SUPPRESS_TID, + RW_READER, + { "SmbTreeConnectX", KSTAT_DATA_UINT64 } }, + { 0, 0, 0, RW_READER, 0 }, /* 0x76 118 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x77 119 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x78 120 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x79 121 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x7A 122 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x7B 123 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x7C 124 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x7D 125 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x7E 126 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x7F 127 */ + { smb_com_query_information_disk, /* 0x80 128 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbQueryInformationDisk", KSTAT_DATA_UINT64 } }, + { smb_com_search, /* 0x81 129 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbSearch", KSTAT_DATA_UINT64 } }, + { smb_com_find, /* 0x82 130 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbFind", KSTAT_DATA_UINT64 } }, + { smb_com_find_unique, /* 0x83 131 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbFindUnique", KSTAT_DATA_UINT64 } }, + { smb_com_find_close, /* 0x84 132 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbFindClose", KSTAT_DATA_UINT64 } }, + { 0, 0, 0, RW_READER, 0 }, /* 0x85 133 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x86 134 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x87 135 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x88 136 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x89 137 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x8A 138 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x8B 139 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x8C 140 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x8D 141 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x8E 142 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x8F 143 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x90 144 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x91 145 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x92 146 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x93 147 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x94 148 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x95 149 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x96 150 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x97 151 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x98 152 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x99 153 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x9A 154 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x9B 155 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x9C 156 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x9D 157 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x9E 158 */ + { 0, 0, 0, RW_READER, 0 }, /* 0x9F 159 */ + { smb_com_nt_transact, /* 0xA0 160 */ + NT_LM_0_12, SDDF_NO_FLAGS, + RW_READER, + { "SmbNtTransact", KSTAT_DATA_UINT64 } }, + { smb_com_nt_transact_secondary, /* 0xA1 161 */ + NT_LM_0_12, SDDF_NO_FLAGS, + RW_READER, + { "SmbNtTransactSecondary", KSTAT_DATA_UINT64 } }, + { smb_com_nt_create_andx, /* 0xA2 162 */ + NT_LM_0_12, SDDF_NO_FLAGS, + RW_READER, + { "SmbNtCreateX", KSTAT_DATA_UINT64 } }, + { 0, 0, 0, 0, 0 }, /* 0xA3 163 */ + { smb_com_nt_cancel, /* 0xA4 164 */ + NT_LM_0_12, SDDF_NO_FLAGS, + RW_READER, + { "SmbNtCancel", KSTAT_DATA_UINT64 } }, + { 0, 0, 0, RW_READER, 0 }, /* 0xA5 165 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xA6 166 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xA7 167 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xA8 168 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xA9 169 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xAA 170 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xAB 171 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xAC 172 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xAD 173 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xAE 174 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xAF 175 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xB0 176 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xB1 177 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xB2 178 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xB3 179 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xB4 180 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xB5 181 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xB6 182 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xB7 183 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xB8 184 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xB9 185 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xBA 186 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xBB 187 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xBC 188 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xBD 189 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xBE 190 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xBF 191 */ + { smb_com_open_print_file, /* 0xC0 192 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbOpenPrintFile", KSTAT_DATA_UINT64 } }, + { smb_com_write_print_file, /* 0xC1 193 */ + PC_NETWORK_PROGRAM_1_0, SDDF_SUPPRESS_SHOW, + RW_READER, + { "SmbWritePrintFile", KSTAT_DATA_UINT64 } }, + { smb_com_close_print_file, /* 0xC2 194 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbClosePrintFile", KSTAT_DATA_UINT64 } }, + { smb_com_get_print_queue, /* 0xC3 195 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbGetPrintQueue", KSTAT_DATA_UINT64 } }, + { 0, 0, 0, RW_READER, 0 }, /* 0xC4 196 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xC5 197 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xC6 198 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xC7 199 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xC8 200 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xC9 201 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xCA 202 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xCB 203 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xCC 204 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xCD 205 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xCE 206 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xCF 207 */ + { smb_com_send_single_message, /* 0xD0 208 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbSendSingleMessage", KSTAT_DATA_UINT64 } }, + { smb_com_send_broadcast_message, /* 0xD1 209 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbSendBroadcastMessage", KSTAT_DATA_UINT64 } }, + { smb_com_forward_user_name, /* 0xD2 210 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbForwardUserName", KSTAT_DATA_UINT64 } }, + { smb_com_cancel_forward, /* 0xD3 211 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbCancelForward", KSTAT_DATA_UINT64 } }, + { smb_com_get_machine_name, /* 0xD4 212 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbGetMachineName", KSTAT_DATA_UINT64 } }, + { smb_com_send_start_mb_message, /* 0xD5 213 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbSendStartMbMessage", KSTAT_DATA_UINT64 } }, + { smb_com_send_end_mb_message, /* 0xD6 214 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbSendEndMbMessage", KSTAT_DATA_UINT64 } }, + { smb_com_send_text_mb_message, /* 0xD7 215 */ + PC_NETWORK_PROGRAM_1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbSendTextMbMessage", KSTAT_DATA_UINT64 } }, + { 0, 0, 0, RW_READER, 0 }, /* 0xD8 216 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xD9 217 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xDA 218 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xDB 219 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xDC 220 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xDD 221 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xDE 222 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xDF 223 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xE0 224 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xE1 225 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xE2 226 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xE3 227 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xE4 228 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xE5 229 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xE6 230 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xE7 231 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xE8 232 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xE9 233 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xEA 234 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xEB 235 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xEC 236 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xED 237 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xEE 238 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xEF 239 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xF0 240 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xF1 241 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xF2 242 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xF3 243 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xF4 244 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xF5 245 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xF6 246 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xF7 247 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xF8 248 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xF9 249 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xFA 250 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xFB 251 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xFC 252 */ + { 0, 0, 0, RW_READER, 0 }, /* 0xFD 253 */ + { smb_com_invalid_command, /* 0xFE 254 */ + LANMAN1_0, SDDF_NO_FLAGS, + RW_READER, + { "SmbInvalidCommand", KSTAT_DATA_UINT64 } }, + { 0, 0, 0, RW_READER, 0 } /* 0xFF 255 */ +}; + +int smb_watch = -1; +int smb_emit_sending = 0; + +/* + * smbsr_cleanup + * + * If any user/tree/file is used by given request then + * the reference count for that resource has been incremented. + * This function decrements the reference count and close + * the resource if it's needed. + */ + +void +smbsr_cleanup(struct smb_request *sr) +{ + ASSERT((sr->sr_state != SMB_REQ_STATE_CLEANED_UP) && + (sr->sr_state != SMB_REQ_STATE_COMPLETED)); + + if (sr->fid_ofile) + smbsr_disconnect_file(sr); + + if (sr->sid_odir) + smbsr_disconnect_dir(sr); + + if (sr->tid_tree) { + smb_tree_release(sr->tid_tree); + sr->tid_tree = NULL; + } + + if (sr->uid_user) { + smb_user_release(sr->uid_user); + sr->uid_user = NULL; + } + + if (sr->r_xa) { + if (sr->r_xa->xa_flags & SMB_XA_FLAG_COMPLETE) + smb_xa_close(sr->r_xa); + smb_xa_rele(sr->session, sr->r_xa); + sr->r_xa = NULL; + } + + /* + * Mark this request so we know that we've already cleaned it up. + * A request should only get cleaned up once so multiple calls to + * smbsr_cleanup for the same request indicate a bug. + */ + mutex_enter(&sr->sr_mutex); + if (sr->sr_state != SMB_REQ_STATE_CANCELED) + sr->sr_state = SMB_REQ_STATE_CLEANED_UP; + mutex_exit(&sr->sr_mutex); +} + +int +smb_dispatch_request(struct smb_request *sr) +{ + int rc; + smb_dispatch_table_t *sdd; + + ASSERT(sr->tid_tree == 0); + ASSERT(sr->uid_user == 0); + ASSERT(sr->fid_ofile == 0); + ASSERT(sr->sid_odir == 0); + sr->smb_fid = (uint16_t)-1; + sr->smb_sid = (uint16_t)-1; + + /* temporary until we identify a user */ + sr->user_cr = kcred; + sr->orig_request_hdr = sr->command.chain_offset; + + /* If this connection is shutting down just kill request */ + if (smb_decode_mbc(&sr->command, SMB_HEADER_ED_FMT, + &sr->smb_com, + &sr->smb_rcls, + &sr->smb_reh, + &sr->smb_err, + &sr->smb_flg, + &sr->smb_flg2, + &sr->smb_pid_high, + sr->smb_sig, + &sr->smb_tid, + &sr->smb_pid, + &sr->smb_uid, + &sr->smb_mid) != 0) { + return (-1); + } + + /* + * The reply "header" is filled in now even though + * it most likely will be rewritten under reply_ready: + * below. Could just reserve the space. But this + * (for now) is convenient incase the dialect dispatcher + * has to send a special reply (like TRANSACT). + * + * Ensure that the 32-bit error code flag is turned off. + * Clients seem to set it in transact requests and they may + * get confused if we return success or a 16-bit SMB code. + */ + sr->smb_rcls = 0; + sr->smb_reh = 0; + sr->smb_err = 0; + sr->smb_flg2 &= ~SMB_FLAGS2_NT_STATUS; + + (void) smb_encode_mbc(&sr->reply, SMB_HEADER_ED_FMT, + sr->smb_com, + sr->smb_rcls, + sr->smb_reh, + sr->smb_err, + sr->smb_flg, + sr->smb_flg2, + sr->smb_pid_high, + sr->smb_sig, + sr->smb_tid, + sr->smb_pid, + sr->smb_uid, + sr->smb_mid); + sr->first_smb_com = sr->smb_com; + + /* + * Verify SMB signature if signing is enabled, + * dialiect is NT LM 0.12, + * signing was negotiated and authentication has occurred. + */ + if (sr->session->signing.flags & SMB_SIGNING_ENABLED) { + if (smb_sign_check_request(sr) != 0) { + /* Reply with ACCESS_DENIED */ + if (sr->session->capabilities & CAP_STATUS32) + smbsr_setup_nt_status(sr, ERROR_SEVERITY_ERROR, + NT_STATUS_ACCESS_DENIED); + else { + sr->smb_rcls = ERRDOS; + sr->smb_err = ERRnoaccess; + } + rc = -1; + smb_rwx_rwenter(&sr->session->s_lock, RW_READER); + goto reply_error; + } + } + +andx_more: + sdd = &dispatch[sr->smb_com]; + + smb_rwx_rwenter(&sr->session->s_lock, sdd->sdt_slock_mode); + + if (smb_decode_mbc(&sr->command, "b", &sr->smb_wct) != 0) { + rc = -3; + goto cant_decode; + } + + (void) MBC_SHADOW_CHAIN(&sr->smb_vwv, &sr->command, + sr->command.chain_offset, sr->smb_wct * 2); + + if (smb_decode_mbc(&sr->command, "#.w", + sr->smb_wct*2, &sr->smb_bcc) != 0) { + rc = -5; + goto cant_decode; + } + + (void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command, + sr->command.chain_offset, sr->smb_bcc); + + sr->command.chain_offset += sr->smb_bcc; + if (sr->command.chain_offset > sr->command.max_bytes) { + rc = -6; + goto cant_decode; + } + + /* Store pointers for later */ + sr->cur_reply_offset = sr->reply.chain_offset; + + if (is_andx_com(sr->smb_com)) { + /* Peek ahead and don't disturb vwv */ + if (smb_peek_mbc(&sr->smb_vwv, sr->smb_vwv.chain_offset, "b.w", + &sr->andx_com, &sr->andx_off) < 0) { + rc = -7; + goto cant_decode; + } + } else { + sr->andx_com = (unsigned char)-1; + } + + mutex_enter(&sr->sr_mutex); + switch (sr->sr_state) { + case SMB_REQ_STATE_SUBMITTED: + case SMB_REQ_STATE_CLEANED_UP: + sr->sr_state = SMB_REQ_STATE_ACTIVE; + break; + case SMB_REQ_STATE_CANCELED: + break; + default: + ASSERT(0); + break; + } + mutex_exit(&sr->sr_mutex); + + if (sdd->sdt_function) { + + if ((rc = setjmp(&sr->exjb))) { + /* + * Handle any errors from raw write. + */ + if (sr->session->s_state == + SMB_SESSION_STATE_WRITE_RAW_ACTIVE) { + /* + * Set state so that the netbios session + * daemon will start accepting data again. + */ + sr->session->s_write_raw_status = 0; + sr->session->s_state = + SMB_SESSION_STATE_NEGOTIATED; + } + + /* + * We should never have sr->sr_keep set here + * since this is the error path. + */ + ASSERT(sr->sr_keep == 0); + + smbsr_cleanup(sr); + + if (sr->smb_com == smb_watch) { + smb_emit_sending = 1; + } + if (rc < 0) { + rc -= 1000; + goto cant_decode; + } + goto reply_error; + } + + /* + * Setup UID and TID information (if required). Both functions + * will set the sr credentials. In domain mode, the user and + * tree credentials should be the same. In share mode, the + * tree credentials (defined in the share definition) should + * override the user credentials. + */ + if (!(sdd->sdt_flags & SDDF_SUPPRESS_UID)) { + sr->uid_user = smb_user_lookup_by_uid(sr->session, + &sr->user_cr, sr->smb_uid); + if (sr->uid_user == NULL) { + smbsr_raise_error(sr, ERRSRV, ERRbaduid); + /* NOTREACHED */ + } + if (!(sdd->sdt_flags & SDDF_SUPPRESS_TID)) { + sr->tid_tree = smb_tree_lookup_by_tid( + sr->uid_user, sr->smb_tid); + if (sr->tid_tree == NULL) { + smbsr_raise_error(sr, ERRSRV, + ERRinvnid); + /* NOTREACHED */ + } + } + } + + /* + * If the command is not a read raw request we can set the + * state of the session back to SMB_SESSION_STATE_NEGOTIATED + * (if the current state is SMB_SESSION_STATE_OPLOCK_BREAKING). + * Otherwise we let the read raw handler to deal with it. + */ + if ((sr->session->s_state == + SMB_SESSION_STATE_OPLOCK_BREAKING) && + (sr->smb_com != SMB_COM_READ_RAW)) { + krw_t mode; + /* + * The lock may have to be upgraded because, at this + * point, we don't know how it was entered. We just + * know that it has to be entered in writer mode here. + * Whatever mode was used to enter the lock, it will + * be restored. + */ + mode = smb_rwx_rwupgrade(&sr->session->s_lock); + if (sr->session->s_state == + SMB_SESSION_STATE_OPLOCK_BREAKING) { + sr->session->s_state = + SMB_SESSION_STATE_NEGOTIATED; + } + smb_rwx_rwdowngrade(&sr->session->s_lock, mode); + } + + DTRACE_PROBE1(smb__dispatch__com, struct smb_request_t *, sr); + + /* + * Increment method invocation count. This value is exposed + * via kstats, and it represents a count of all the dispatched + * requests, including the ones that have a return value, other + * than SDRC_NORMAL_REPLY. + */ + SMB_ALL_DISPATCH_STAT_INCR(sdd->sdt_dispatch_stats.value.ui64); + + rc = (*sdd->sdt_function)(sr); + + /* + * Only call smbsr_cleanup if smb->sr_keep is not set. The + * smb_nt_transact_notify_change function will set + * smb->sr_keep if it retains control of the request when + * it returns. In that case the notify change code + * will call smbsr_cleanup later when the request is finally + * completed. + */ + if (sr->sr_keep == 0) + smbsr_cleanup(sr); + } else { + rc = SDRC_UNIMPLEMENTED; /* Unknown? */ + } + + if (rc != SDRC_NORMAL_REPLY) { /* normal case special & fast */ + switch (rc) { + case SDRC_NORMAL_REPLY: + break; + + case SDRC_ERROR_REPLY: + goto reply_error; + + case SDRC_DROP_VC: + switch (sr->session->s_state) { + case SMB_SESSION_STATE_DISCONNECTED: + case SMB_SESSION_STATE_TERMINATED: + break; + default: + smb_soshutdown(sr->session->sock); + break; + } + goto reply_error; + + case SDRC_NO_REPLY: + /* tricky. */ + smb_rwx_rwexit(&sr->session->s_lock); + return (0); + + case SDRC_UNIMPLEMENTED: + sr->smb_rcls = ERRDOS; + sr->smb_err = ERRbadfunc; + goto reply_error; + + default: + sr->smb_rcls = ERRDOS; + sr->smb_err = ERRerror; /* need better */ + goto reply_error; + } + } + + if (sr->andx_com == 0xff) + goto reply_ready; + + /* have to back-patch the AndXCommand and AndXOffset */ + sr->andx_prev_wct = sr->cur_reply_offset; + (void) smb_poke_mbc(&sr->reply, sr->andx_prev_wct + 1, "b.w", + sr->andx_com, MBC_LENGTH(&sr->reply)); + + smb_rwx_rwexit(&sr->session->s_lock); + + /* now it gets interesting */ + sr->command.chain_offset = sr->orig_request_hdr + sr->andx_off; + + sr->smb_com = sr->andx_com; + + goto andx_more; + +reply_ready: + + if (SMB_TREE_CASE_INSENSITIVE(sr)) { + sr->smb_flg |= SMB_FLAGS_CASE_INSENSITIVE; + } else { + sr->smb_flg &= ~SMB_FLAGS_CASE_INSENSITIVE; + } + + (void) smb_poke_mbc(&sr->reply, 0, SMB_HEADER_ED_FMT, + sr->first_smb_com, + sr->smb_rcls, + sr->smb_reh, + sr->smb_err, + sr->smb_flg | SMB_FLAGS_REPLY, + sr->smb_flg2, + sr->smb_pid_high, + sr->smb_sig, + sr->smb_tid, + sr->smb_pid, + sr->smb_uid, + sr->smb_mid); + + if (sr->session->signing.flags & SMB_SIGNING_ENABLED) + smb_sign_reply(sr, NULL); + + if ((rc = smb_session_send(sr->session, 0, &sr->reply)) == 0) + sr->reply.chain = 0; + + smb_rwx_rwexit(&sr->session->s_lock); + + return (rc); + +cant_decode: +reply_error: + sr->reply.chain_offset = sr->cur_reply_offset; + (void) smb_encode_mbc(&sr->reply, "bw", 0, 0); + + sr->smb_wct = 0; + sr->smb_bcc = 0; + + if (sr->smb_rcls == 0) { + sr->smb_rcls = ERRSRV; + sr->smb_err = ERRerror; + } + goto reply_ready; +} + + +void +smbsr_encode_result(struct smb_request *sr, int wct, + int bcc, char *fmt, ...) +{ + va_list ap; + + if (MBC_LENGTH(&sr->reply) != sr->cur_reply_offset) { + smbsr_encode_error(sr); + } + + va_start(ap, fmt); + (void) smb_mbc_encode(&sr->reply, fmt, ap); + va_end(ap); + + sr->smb_wct = (unsigned char)wct; + sr->smb_bcc = (uint16_t)bcc; + + smbsr_check_result(sr, wct, bcc); +} + +void +smbsr_check_result(struct smb_request *sr, int wct, int bcc) +{ + int offset = sr->cur_reply_offset; + int total_bytes; + unsigned char temp, temp1; + struct mbuf *m; + + total_bytes = 0; + m = sr->reply.chain; + while (m != 0) { + total_bytes += m->m_len; + m = m->m_next; + } + + if ((offset + 3) > total_bytes) { + smbsr_encode_error(sr); + /* NOTREACHED */ + } + + (void) smb_peek_mbc(&sr->reply, offset, "b", &temp); + if (temp != wct) { + smbsr_encode_error(sr); + /* NOTREACHED */ + } + + if ((offset + (wct * 2 + 1)) > total_bytes) { + smbsr_encode_error(sr); + /* NOTREACHED */ + } + + /* reply wct & vwv seem ok, consider data now */ + offset += wct * 2 + 1; + + if ((offset + 2) > total_bytes) { + smbsr_encode_error(sr); + } + + (void) smb_peek_mbc(&sr->reply, offset, "bb", &temp, &temp1); + if (bcc == VAR_BCC) { + if ((temp != 0xFF) || (temp1 != 0xFF)) { + smbsr_encode_error(sr); + /* NOTREACHED */ + } else { + bcc = (total_bytes - offset) - 2; + (void) smb_poke_mbc(&sr->reply, offset, "bb", + bcc, bcc >> 8); + } + } else { + if ((temp != (bcc&0xFF)) || (temp1 != ((bcc>>8)&0xFF))) { + smbsr_encode_error(sr); + } + } + + offset += bcc + 2; + + if (offset != total_bytes) { + smbsr_encode_error(sr); + } + + sr->smb_wct = (unsigned char)wct; + sr->smb_bcc = (uint16_t)bcc; +} + +int +smbsr_decode_vwv(struct smb_request *sr, char *fmt, ...) +{ + int rc; + va_list ap; + + va_start(ap, fmt); + rc = smb_mbc_decode(&sr->smb_vwv, fmt, ap); + va_end(ap); + + return (rc); +} + +int +smbsr_decode_data(struct smb_request *sr, char *fmt, ...) +{ + int r; + va_list ap; + va_start(ap, fmt); + r = smb_mbc_decode(&sr->smb_data, fmt, ap); + va_end(ap); + return (r); +} + +void +smbsr_send_reply(struct smb_request *sr) +{ + (void) smb_poke_mbc(&sr->reply, 0, SMB_HEADER_ED_FMT, + sr->first_smb_com, + sr->smb_rcls, + sr->smb_reh, + sr->smb_err, + sr->smb_flg | SMB_FLAGS_REPLY, + sr->smb_flg2, + sr->smb_pid_high, + sr->smb_sig, + sr->smb_tid, + sr->smb_pid, + sr->smb_uid, + sr->smb_mid); + + if (sr->session->signing.flags & SMB_SIGNING_ENABLED) + smb_sign_reply(sr, NULL); + + (void) smb_session_send(sr->session, 0, &sr->reply); +} + + +void +smbsr_decode_error(struct smb_request *sr) +{ + longjmp(&sr->exjb); +} + +void +smbsr_encode_error(struct smb_request *sr) +{ + longjmp(&sr->exjb); +} + +void +smbsr_encode_empty_result(struct smb_request *sr) +{ + smbsr_encode_result(sr, 0, 0, "bw", 0, 0); +} + +/* + * cifs_raise_error + * + * Temporary workaround to the NT status versus Win32/SMB error codes + * decision: just report them both here. + */ +void +smbsr_raise_cifs_error(struct smb_request *sr, + DWORD status, + int error_class, + int error_code) +{ + if (sr->session->capabilities & CAP_STATUS32) + smbsr_raise_nt_error(sr, status); + else + smbsr_raise_error(sr, error_class, error_code); + + /* NOTREACHED */ +} + +void +smbsr_raise_error(struct smb_request *sr, int errcls, int errcod) +{ + sr->smb_rcls = (unsigned char)errcls; + sr->smb_err = (uint16_t)errcod; + longjmp(&sr->exjb); +} + +/* + * smbsr_setup_nt_status + * + * Set up an NT status in the smb_request but don't long jump or try + * to do any error handling. There are times when we need a status set + * up in the response to indicate that the request has either failed + * or, at least, is only partially complete (possibly indicated by the + * severity) but we also need to return some information to the client. + */ +void +smbsr_setup_nt_status(struct smb_request *sr, + uint32_t severity, + uint32_t nt_status) +{ + nt_status |= severity; + sr->smb_rcls = nt_status & 0xff; + sr->smb_reh = (nt_status >> 8) & 0xff; + sr->smb_err = nt_status >> 16; + sr->smb_flg2 |= SMB_FLAGS2_NT_STATUS; +} + +void +smbsr_raise_nt_error(struct smb_request *sr, uint32_t errcod) +{ + errcod |= 0xc0000000; + sr->smb_rcls = errcod & 0xff; + sr->smb_reh = (errcod >> 8) & 0xff; + sr->smb_err = errcod >> 16; + sr->smb_flg2 |= SMB_FLAGS2_NT_STATUS; + longjmp(&sr->exjb); +} + + +/* + * Attempt to map errno values to SMB and NT status values. + * Note: ESRCH is used as special case to handle a lookup + * failure on streams. + */ +static struct { + int unix_errno; + int smb_error_class; + int smb_error_value; + DWORD nt_status; +} +smb_errno_map[] = { + { ENOSPC, ERRDOS, ERROR_DISK_FULL, NT_STATUS_DISK_FULL }, + { EDQUOT, ERRDOS, ERROR_DISK_FULL, NT_STATUS_DISK_FULL }, + { EPERM, ERRSRV, ERRaccess, NT_STATUS_ACCESS_DENIED }, + { ENOTDIR, ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND }, + { EISDIR, ERRDOS, ERRbadpath, NT_STATUS_FILE_IS_A_DIRECTORY }, + { ENOENT, ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE }, + { ENOTEMPTY, ERRDOS, ERROR_DIR_NOT_EMPTY, + NT_STATUS_DIRECTORY_NOT_EMPTY }, + { EACCES, ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED }, + { ENOMEM, ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY }, + { EIO, ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR }, + { EXDEV, ERRSRV, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE }, + { EROFS, ERRHRD, ERRnowrite, NT_STATUS_ACCESS_DENIED }, + { ESTALE, ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, + { EBADF, ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, + { EEXIST, ERRDOS, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION}, + { ENXIO, ERRSRV, ERRinvdevice, NT_STATUS_BAD_DEVICE_TYPE}, + { ESRCH, ERRDOS, ERROR_FILE_NOT_FOUND, + NT_STATUS_OBJECT_NAME_NOT_FOUND }, + /* + * It's not clear why smb_read_common effectively returns + * ERRnoaccess if a range lock prevents access and smb_write_common + * effectively returns ERRaccess. This table entry is used by + * smb_read_common and preserves the behavior that was there before. + */ + { ERANGE, ERRDOS, ERRnoaccess, NT_STATUS_FILE_LOCK_CONFLICT } +}; + +void +smb_errmap_unix2smb(int en, smb_error_t *smberr) +{ + int i; + + smberr->status = NT_STATUS_UNSUCCESSFUL; + smberr->errcls = ERRDOS; + smberr->errcode = ERROR_GEN_FAILURE; + + for (i = 0; i < sizeof (smb_errno_map)/sizeof (smb_errno_map[0]); ++i) { + if (smb_errno_map[i].unix_errno == en) { + smberr->status = smb_errno_map[i].nt_status; + smberr->errcls = smb_errno_map[i].smb_error_class; + smberr->errcode = smb_errno_map[i].smb_error_value; + return; + } + } +} + +int +smbsr_set_errno(struct smb_request *sr, int en) +{ + int i; + + ASSERT(en != -1); + + /* + * If the client supports 32-bit NT status values, check for + * an appropriate mapping and raise an NT error, control won't + * return here due to the longjmp in smbsr_raise_nt_error. + */ + if (sr->session->capabilities & CAP_STATUS32) { + for (i = 0; + i < sizeof (smb_errno_map)/sizeof (smb_errno_map[0]); + ++i) { + if (smb_errno_map[i].unix_errno == en) { + smbsr_raise_nt_error(sr, + smb_errno_map[i].nt_status); + /* NOTREACHED */ + } + } + } else { + for (i = 0; + i < sizeof (smb_errno_map)/sizeof (smb_errno_map[0]); + ++i) { + if (smb_errno_map[i].unix_errno == en) { + sr->smb_rcls = smb_errno_map[i].smb_error_class; + sr->smb_err = smb_errno_map[i].smb_error_value; + return (0); + } + } + } + + sr->smb_rcls = ERRSRV; + sr->smb_err = ERRerror; + return (-1); +} + +void +smbsr_raise_errno(struct smb_request *sr, int en) +{ + if (smbsr_set_errno(sr, en) != 0) { + if (smb_dispatch_diags) { + cmn_err(CE_NOTE, "SmbErrno: errno=%d", en); + } + } + + longjmp(&sr->exjb); + /* no return */ +} + +smb_xa_t * +smbsr_lookup_xa(smb_request_t *sr) +{ + ASSERT(sr->r_xa == 0); + + sr->r_xa = smb_xa_find(sr->session, sr->smb_pid, sr->smb_mid); + return (sr->r_xa); +} + +void +smbsr_disconnect_file(smb_request_t *sr) +{ + smb_ofile_t *of = sr->fid_ofile; + + sr->fid_ofile = NULL; + (void) smb_ofile_release(of); +} + +void +smbsr_disconnect_dir(smb_request_t *sr) +{ + smb_odir_t *od = sr->sid_odir; + + sr->sid_odir = NULL; + smb_odir_release(od); +} + +static int +is_andx_com(unsigned char com) +{ + switch (com) { + case SMB_COM_LOCKING_ANDX: + case SMB_COM_OPEN_ANDX: + case SMB_COM_READ_ANDX: + case SMB_COM_WRITE_ANDX: + case SMB_COM_SESSION_SETUP_ANDX: + case SMB_COM_LOGOFF_ANDX: + case SMB_COM_TREE_CONNECT_ANDX: + case SMB_COM_NT_CREATE_ANDX: + return (1); + } + return (0); +} + +/* + * Invalid command stub. + */ +/*ARGSUSED*/ +int +smb_com_invalid_command(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} + +/* + * smb_kstat_update_dispatch + * + * This callback function updates the smb_dispatch_kstat_data when kstat + * command is invoked. + */ +/*ARGSUSED*/ +static int +smb_kstat_update_dispatch(kstat_t *ksp, int rw) +{ + int i = 0, j = 0; + + if (rw == KSTAT_WRITE) { + return (EACCES); + } else { + for (i = 0; i < 256; i++) { + if (dispatch[i].sdt_function) { + (void) memcpy(&smb_dispatch_kstat_data[j], + &(dispatch[i].sdt_dispatch_stats), + sizeof (kstat_named_t)); + j++; + } + } + } + return (0); +} + +/* + * smb_initialize_dispatch_kstat + * + * Initialize dispatch kstats. + */ +void +smb_initialize_dispatch_kstat() +{ + int i = 0, alloc_size = 0; + + for (i = 0; i < 256; i++) { + if (dispatch[i].sdt_function) + smb_dispatch_kstat_size++; + } + + alloc_size = smb_dispatch_kstat_size * sizeof (kstat_named_t); + smb_dispatch_kstat_data = (kstat_named_t *) + kmem_zalloc(alloc_size, KM_SLEEP); + + smb_dispatch_ksp = kstat_create("smb", 0, "smb_dispatch_all", "misc", + KSTAT_TYPE_NAMED, alloc_size/sizeof (kstat_named_t), + KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_WRITABLE); + if (smb_dispatch_ksp) { + smb_dispatch_ksp->ks_data = smb_dispatch_kstat_data; + smb_dispatch_ksp->ks_update = smb_kstat_update_dispatch; + kstat_install(smb_dispatch_ksp); + } +} + +/* + * smb_remove_dispatch_kstat + * + * Remove dispatch kstats. + */ +void +smb_remove_dispatch_kstat() +{ + if (smb_dispatch_kstat_data != NULL) + kmem_free(smb_dispatch_kstat_data, + smb_dispatch_kstat_size * sizeof (kstat_named_t)); + + if (smb_dispatch_ksp != NULL) { + kstat_delete(smb_dispatch_ksp); + smb_dispatch_ksp = NULL; + } +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_echo.c b/usr/src/uts/common/fs/smbsrv/smb_echo.c new file mode 100644 index 000000000000..8a386c99593c --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_echo.c @@ -0,0 +1,132 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +/* + * The echo request is used to test the connection to the server, + * and to see if the server is still responding. The tid is ignored, + * so this request may be sent to the server even if there are no + * tree connections to the server. + * + * Each response echoes the data sent, though ByteCount may indicate + * no data. If echo-count is zero, no response is sent. + */ +int +smb_com_echo(struct smb_request *sr) +{ + unsigned short necho; + unsigned short nbytes; + unsigned short i; + struct mbuf_chain reply; + char *data; + + if (smbsr_decode_vwv(sr, "w", &necho) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + nbytes = sr->smb_bcc; + + data = smbsr_malloc(&sr->request_storage, nbytes); + + (void) smb_decode_mbc(&sr->smb_data, "#c", nbytes, data); + + for (i = 1; i <= necho; ++i) { + + MBC_INIT(&reply, SMB_HEADER_ED_LEN + 10 + nbytes); + + (void) smb_encode_mbc(&reply, SMB_HEADER_ED_FMT, + sr->first_smb_com, + sr->smb_rcls, + sr->smb_reh, + sr->smb_err, + sr->smb_flg | SMB_FLAGS_REPLY, + sr->smb_flg2, + sr->smb_pid_high, + sr->smb_sig, + sr->smb_tid, + sr->smb_pid, + sr->smb_uid, + sr->smb_mid); + + (void) smb_encode_mbc(&reply, "bww#c", 1, i, + nbytes, nbytes, data); + + if (sr->session->signing.flags & SMB_SIGNING_ENABLED) + smb_sign_reply(sr, &reply); + + (void) smb_session_send(sr->session, 0, &reply); + } + + return (SDRC_NO_REPLY); +} + +/* + * Broadcast messages are not supported. + */ +int /*ARGSUSED*/ +smb_com_send_broadcast_message(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} + +/* + * Multi-block messages are not supported. + */ +int /*ARGSUSED*/ +smb_com_send_end_mb_message(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} + +/* + * Single-block messages are not supported. + */ +int /*ARGSUSED*/ +smb_com_send_single_message(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} + +/* + * Multi-block messages are not supported. + */ +int /*ARGSUSED*/ +smb_com_send_start_mb_message(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} + +/* + * Multi-block messages are not supported. + */ +int /*ARGSUSED*/ +smb_com_send_text_mb_message(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_fem.c b/usr/src/uts/common/fs/smbsrv/smb_fem.c new file mode 100644 index 000000000000..1248a0a66b34 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_fem.c @@ -0,0 +1,285 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include + +fem_t *smb_fcn_ops = NULL; + +int smb_fem_fcn_create(femarg_t *, char *, vattr_t *, vcexcl_t, int, + vnode_t **, cred_t *, int, caller_context_t *, vsecattr_t *); +int smb_fem_fcn_remove(femarg_t *, char *, cred_t *, + caller_context_t *, int); +int smb_fem_fcn_rename(femarg_t *, char *, vnode_t *, char *, + cred_t *, caller_context_t *, int); +int smb_fem_fcn_mkdir(femarg_t *, char *, vattr_t *, vnode_t **, + cred_t *, caller_context_t *, int, vsecattr_t *); +int smb_fem_fcn_rmdir(femarg_t *, char *, vnode_t *, cred_t *, + caller_context_t *, int); +int smb_fem_fcn_link(femarg_t *, vnode_t *, char *, cred_t *, + caller_context_t *, int); +int smb_fem_fcn_symlink(femarg_t *, char *, vattr_t *, + char *, cred_t *, caller_context_t *, int); + +static const fs_operation_def_t smb_fcn_tmpl[] = { + VOPNAME_CREATE, { .femop_create = smb_fem_fcn_create }, + VOPNAME_REMOVE, {.femop_remove = smb_fem_fcn_remove}, + VOPNAME_RENAME, {.femop_rename = smb_fem_fcn_rename}, + VOPNAME_MKDIR, {.femop_mkdir = smb_fem_fcn_mkdir}, + VOPNAME_RMDIR, {.femop_rmdir = smb_fem_fcn_rmdir}, + VOPNAME_LINK, {.femop_link = smb_fem_fcn_link}, + VOPNAME_SYMLINK, {.femop_symlink = smb_fem_fcn_symlink}, + NULL, NULL +}; + +int +smb_fem_init() +{ + return (fem_create("smb_fcn_ops", smb_fcn_tmpl, &smb_fcn_ops)); +} + +void +smb_fem_shutdown() +{ + if (smb_fcn_ops) + fem_free(smb_fcn_ops); +} + +void +smb_fem_fcn_install(smb_node_t *node) +{ + (void) fem_install(node->vp, smb_fcn_ops, (void *)node, OPARGUNIQ, + (fem_func_t)smb_node_ref, (fem_func_t)smb_node_release); +} + +void +smb_fem_fcn_uninstall(smb_node_t *node) +{ + (void) fem_uninstall(node->vp, smb_fcn_ops, (void *)node); +} + +/* + * smb_fem_fcn_create() + * + * This monitor will catch only changes to VREG files and not to extended + * attribute files. This is fine because, for CIFS files, stream creates + * should not trigger any file change notification on the VDIR directory + * being monitored. Creates of any other kind of extended attribute in + * the directory will also not trigger any file change notification on the + * VDIR directory being monitored. + */ + +int +smb_fem_fcn_create( + femarg_t *arg, + char *name, + vattr_t *vap, + vcexcl_t excl, + int mode, + vnode_t **vpp, + cred_t *cr, + int flag, + caller_context_t *ct, + vsecattr_t *vsecp) +{ + smb_node_t *dnode; + int error; + + dnode = (smb_node_t *)arg->fa_fnode->fn_available; + + ASSERT(dnode); + + error = vnext_create(arg, name, vap, excl, mode, vpp, cr, flag, + ct, vsecp); + + if (error == 0) + smb_process_node_notify_change_queue(dnode); + + return (error); +} + +/* + * smb_fem_fcn_remove() + * + * This monitor will catch only changes to VREG files and to not extended + * attribute files. This is fine because, for CIFS files, stream deletes + * should not trigger any file change notification on the VDIR directory + * being monitored. Deletes of any other kind of extended attribute in + * the directory will also not trigger any file change notification on the + * VDIR directory being monitored. + */ + +int +smb_fem_fcn_remove( + femarg_t *arg, + char *name, + cred_t *cr, + caller_context_t *ct, + int flags) +{ + smb_node_t *dnode; + int error; + + dnode = (smb_node_t *)arg->fa_fnode->fn_available; + + ASSERT(dnode); + + error = vnext_remove(arg, name, cr, ct, flags); + + if (error == 0) + smb_process_node_notify_change_queue(dnode); + + return (error); +} + +int +smb_fem_fcn_rename( + femarg_t *arg, + char *snm, + vnode_t *tdvp, + char *tnm, + cred_t *cr, + caller_context_t *ct, + int flags) +{ + smb_node_t *dnode; + int error; + + dnode = (smb_node_t *)arg->fa_fnode->fn_available; + + ASSERT(dnode); + + error = vnext_rename(arg, snm, tdvp, tnm, cr, ct, flags); + + if (error == 0) + smb_process_node_notify_change_queue(dnode); + + return (error); +} + +int +smb_fem_fcn_mkdir( + femarg_t *arg, + char *name, + vattr_t *vap, + vnode_t **vpp, + cred_t *cr, + caller_context_t *ct, + int flags, + vsecattr_t *vsecp) +{ + smb_node_t *dnode; + int error; + + dnode = (smb_node_t *)arg->fa_fnode->fn_available; + + ASSERT(dnode); + + error = vnext_mkdir(arg, name, vap, vpp, cr, ct, flags, vsecp); + + if (error == 0) + smb_process_node_notify_change_queue(dnode); + + return (error); +} + +int +smb_fem_fcn_rmdir( + femarg_t *arg, + char *name, + vnode_t *cdir, + cred_t *cr, + caller_context_t *ct, + int flags) +{ + smb_node_t *dnode; + int error; + + dnode = (smb_node_t *)arg->fa_fnode->fn_available; + + ASSERT(dnode); + + error = vnext_rmdir(arg, name, cdir, cr, ct, flags); + + if (error == 0) + smb_process_node_notify_change_queue(dnode); + + return (error); +} + +int +smb_fem_fcn_link( + femarg_t *arg, + vnode_t *svp, + char *tnm, + cred_t *cr, + caller_context_t *ct, + int flags) +{ + smb_node_t *dnode; + int error; + + dnode = (smb_node_t *)arg->fa_fnode->fn_available; + + ASSERT(dnode); + + error = vnext_link(arg, svp, tnm, cr, ct, flags); + + if (error == 0) + smb_process_node_notify_change_queue(dnode); + + return (error); +} + +int +smb_fem_fcn_symlink( + femarg_t *arg, + char *linkname, + vattr_t *vap, + char *target, + cred_t *cr, + caller_context_t *ct, + int flags) +{ + smb_node_t *dnode; + int error; + + dnode = (smb_node_t *)arg->fa_fnode->fn_available; + + ASSERT(dnode); + + error = vnext_symlink(arg, linkname, vap, target, cr, ct, flags); + + if (error == 0) + smb_process_node_notify_change_queue(dnode); + + return (error); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_find.c b/usr/src/uts/common/fs/smbsrv/smb_find.c new file mode 100644 index 000000000000..149221971459 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_find.c @@ -0,0 +1,440 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + + +/* + * smb_com_find + * + * Request Format: (same as core Search Protocol - "Find First" form) + * + * Client Request Description + * ================================== ================================= + * + * BYTE smb_wct; value = 2 + * WORD smb_count; max number of entries to find + * WORD smb_attr; search attribute + * WORD smb_bcc; minimum value = 5 + * BYTE smb_ident1; ASCII (04) + * BYTE smb_pathname[]; filename (may contain global characters) + * BYTE smb_ident2; Variable Block (05) + * WORD smb_keylen; resume key length (zero if "Find First") + * BYTE smb_resumekey[*]; "Find Next" key, * = value of smb_keylen + * + * Response Format: (same as core Search Protocol) + * + * Server Response Description + * ================================== ================================= + * BYTE smb_wct; value = 1 + * WORD smb_count; number of entries found + * WORD smb_bcc; minimum value = 3 + * BYTE smb_ident; Variable Block (05) + * WORD smb_datalen; data length + * BYTE smb_data[*]; directory entries + * + * Directory Information Entry (dir_info) Format: (same as core Search Protocol) + * + * BYTE find_buf_reserved[21]; reserved (resume_key) + * BYTE find_buf_attr; attribute + * WORD find_buf_time; modification time (hhhhh mmmmmm xxxxx) + * where 'xxxxx' is in 2 second increments + * WORD find_buf_date; modification date (yyyyyyy mmmm ddddd) + * DWORD find_buf_size; file size + * STRING find_buf_pname[13]; file name -- ASCII (null terminated) + * + * The resume_key has the following format: + * + * BYTE sr_res; reserved: + * bit 7 - reserved for consumer use + * bit 5,6 - reserved for system use + * (must be preserved) + * bits 0-4 - reserved for server + * (must be preserved) + * BYTE sr_name[11]; pathname sought. + * Format: 1-8 character file name, + * left justified 0-3 character extension, + * BYTE sr_findid[1]; uniquely identifies find through + * find_close + * BYTE sr_server[4]; available for server use + * (must be non-zero) + * BYTE sr_res[4]; reserved for consumer use + * + * Service: + * + * The Find protocol finds the directory entry or group of entries matching the + * specified file pathname. The filename portion of the pathname may contain + * global (wild card) characters. + * + * The Find protocol is used to match the find OS/2 system call. The protocols + * "Find", "Find_Unique" and "Find_Close" are methods of reading (or searching) + * a directory. These protocols may be used in place of the core "Search" + * protocol when LANMAN 1.0 dialect has been negotiated. There may be cases + * where the Search protocol will still be used. + * + * The format of the Find protocol is the same as the core "Search" protocol. + * The difference is that the directory is logically Opened with a Find protocol + * and logically closed with the Find Close protocol. This allows the Server to + * make better use of its resources. Search buffers are thus held (allowing + * search resumption via presenting a "resume_key") until a Find Close protocol + * is received. The sr_findid field of each resume key is a unique identifier + * (within the session) of the search from "Find" through "Find close". Thus if + * the consumer does "Find ahead", any find buffers containing resume keys with + * the matching find id may be released when the Find Close is requested. + * + * As is true of a failing open, if a Find request (Find "first" request where + * resume_key is null) fails (no entries are found), no find close protocol is + * expected. + * + * If no global characters are present, a "Find Unique" protocol should be used + * (only one entry is expected and find close need not be sent). + * + * The file path name in the request specifies the file to be sought. The + * attribute field indicates the attributes that the file must have. If the + * attribute is zero then only normal files are returned. If the system file, + * hidden or directory attributes are specified then the search is inclusive -- + * both the specified type(s) of files and normal files are returned. If the + * volume label attribute is specified then the search is exclusive, and only + * the volume label entry is returned + * + * The max-count field specifies the number of directory entries to be returned. + * The response will contain zero or more directory entries as determined by the + * count-returned field. No more than max-count entries will be returned. Only + * entries that match the sought filename/attribute will be returned. + * + * The resume_key field must be null (length = 0) on the initial ("Find First") + * find request. Subsequent find requests intended to continue a search must + * contain the resume_key field extracted from the last directory entry of the + * previous response. The resume_key field is self-contained, for on calls + * containing a resume_key neither the attribute or pathname fields will be + * valid in the request. A find request will terminate when either the + * requested maximum number of entries that match the named file are found, or + * the end of directory is reached without the maximum number of matches being + * found. A response containing no entries indicates that no matching entries + * were found between the starting point of the search and the end of directory. + * + * There may be multiple matching entries in response to a single request as + * Find supports "wild cards" in the file name (last component of the pathname). + * "?" is the wild single characters, "*" or "null" will match any number of + * filename characters within a single part of the filename component. The + * filename is divided into two parts -- an eight character name and a three + * character extension. The name and extension are divided by a ".". + * + * If a filename part commences with one or more "?"s then exactly that number + * of characters will be matched by the Wild Cards, e.g., "??x" will equal "abx" + * but not "abcx" or "ax". When a filename part has trailing "?"s then it will + * match the specified number of characters or less, e.g., "x??" will match + * "xab", "xa" and "x", but not "xabc". If only "?"s are present in the filename + * part, then it is handled as for trailing "?"s "*" or "null" match entire + * pathname parts, thus "*.abc" or ".abc" will match any file with an extension + * of "abc". "*.*", "*" or "null" will match all files in a directory. + * + * Unprotected servers require the requester to have read permission on the + * subtree containing the directory searched (the share specifies read + * permission). + * + * Protected servers require the requester to have permission to search the + * specified directory. + * + * If a Find requests more data than can be placed in a message of the + * max-xmit-size for the TID specified, the server will return only the number + * of entries which will fit. + * + * The number of entries returned will be the minimum of: + * 1. The number of entries requested. + * 2. The number of (complete) entries that will fit in the negotiated SMB + * buffer. + * 3. The number of entries that match the requested name pattern and + * attributes. + * + * The error ERRnofiles set in smb_err field of the response header or a zero + * value in smb_count of the response indicates no matching entry was found. + * + * The resume search key returned along with each directory entry is a server + * defined key which when returned in the Find Next protocol, allows the + * directory search to be resumed at the directory entry fol lowing the one + * denoted by the resume search key. + * + * The date is in the following format: + * bits: + * 1 1 1 1 1 1 + * 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * y y y y y y y m m m m d d d d d + * where: + * y - bit of year 0-119 (1980-2099) + * m - bit of month 1-12 + * d - bit of day 1-31 + * + * The time is in the following format: + * bits: + * 1 1 1 1 1 1 + * 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * h h h h h m m m m m m x x x x x + * where: + * h - bit of hour (0-23) + * m - bit of minute (0-59) + * x - bit of 2 second increment + * + * Find may generate the following errors. + * ERRDOS/ERRnofiles + * ERRDOS/ERRbadpath + * ERRDOS/ERRnoaccess + * ERRDOS/ERRbadaccess + * ERRDOS/ERRbadshare + * ERRSRV/ERRerror + * ERRSRV/ERRaccess + * ERRSRV/ERRinvnid + */ +int +smb_com_find(struct smb_request *sr) +{ + int rc; + unsigned short sattr, count, maxcount; + char *path; + char filename[14]; + uint32_t cookie; + struct smb_node *node; + unsigned char type; + unsigned short key_len; + smb_odir_context_t *pc; + + if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if ((smbsr_decode_data(sr, "%Abw", sr, &path, &type, &key_len) != 0) || + (type != 0x05)) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (key_len == 0) { /* begin search */ + (void) smb_rdir_open(sr, path, sattr); + cookie = 0; + } else if (key_len == 21) { + sr->smb_sid = 0; + if (smb_decode_mbc(&sr->smb_data, SMB_RESUME_KEY_FMT, + filename, &sr->smb_sid, &cookie) != 0) { + /* We don't know which rdir to close */ + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->sid_odir = smb_odir_lookup_by_sid(sr->tid_tree, + sr->smb_sid); + if (sr->sid_odir == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + cookie--; /* +1 when returned */ + } else { + /* We don't know which rdir to close */ + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + (void) smb_encode_mbc(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0); + + pc = MEM_ZALLOC("smb", sizeof (*pc)); + pc->dc_cookie = cookie; + count = 0; + node = (struct smb_node *)0; + rc = 0; + while (count < maxcount) { + if ((rc = smb_rdir_next(sr, &node, pc)) != 0) + break; + + (void) smb_encode_mbc(&sr->reply, ".8c3cbl4.bYl13c", + pc->dc_name83, pc->dc_name83+9, sr->smb_sid, + pc->dc_cookie+1, pc->dc_dattr, + smb_gmt_to_local_time(pc->dc_attr.sa_vattr.va_mtime.tv_sec), + (int32_t)smb_node_get_size(node, &pc->dc_attr), + (*pc->dc_shortname) ? pc->dc_shortname : pc->dc_name); + smb_node_release(node); + node = (struct smb_node *)0; + count++; + } + MEM_FREE("smb", pc); + + if ((rc != 0) && (rc != ENOENT)) { + /* returned error by smb_rdir_next() */ + smb_rdir_close(sr); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + if (count == 0) { + smb_rdir_close(sr); + smbsr_raise_error(sr, ERRDOS, ERRnofiles); + /* NOTREACHED */ + } + + rc = (MBC_LENGTH(&sr->reply) - sr->cur_reply_offset) - 8; + if (smb_poke_mbc(&sr->reply, sr->cur_reply_offset, + "bwwbw", 1, count, rc+3, 5, rc) < 0) { + smb_rdir_close(sr); + smbsr_encode_error(sr); + /* NOTREACHED */ + } + + return (SDRC_NORMAL_REPLY); +} + +/* + * smb_com_find_close + * + * Request Format: (same as core Search Protocol - "Find Next" form) + * + * Client Request Description + * ================================== ================================= + * + * BYTE smb_wct; value = 2 + * WORD smb_count; max number of entries to find + * WORD smb_attr; search attribute + * WORD smb_bcc; minimum value = 5 + * BYTE smb_ident1; ASCII (04) + * BYTE smb_pathname[]; null (may contain only null) + * BYTE smb_ident2; Variable Block (05) + * WORD smb_keylen; resume (close) key length + * (may not be zero) + * BYTE smb_resumekey[*]; "Find Close" key + * (* = value of smb_keylen) + * + * Response Format: (same format as core Search Protocol) + * + * Server Response Description + * ================================== ================================= + * + * BYTE smb_wct; value = 1 + * WORD smb_reserved; reserved + * WORD smb_bcc; value = 3 + * BYTE smb_ident; Variable Block (05) + * WORD smb_datalen; data length (value = 0) + * + * The resume_key (or close key) has the following format: + * + * BYTE sr_res; reserved: + * bit 7 - reserved for consumer use + * bit 5,6 - reserved for system use + * (must be preserved) + * bits 0-4 - rsvd for server + * (must be preserved by consumer) + * BYTE sr_name[11]; pathname sought. + * Format: 1-8 character file name, + * left justified 0-3 character extension, + * left justified (in last 3 chars) + * BYTE sr_findid[1]; uniquely identifies find + * through find_close + * BYTE sr_server[4]; available for server use + * (must be non-zero) + * BYTE sr_res[4]; reserved for consumer use + * + * Service: + * + * The Find_Close protocol closes the association between a Find id + * returned (in the resume_key) by the Find protocol and the directory + * search. + * + * Whereas the First Find protocol logically opens the directory, + * subsequent find protocols presenting a resume_key further "read" the + * directory, the Find Close protocol "closes" the directory allowing the + * server to free any resources held in support of the directory search. + * + * The Find Close protocol is used to match the find Close OS/2 + * system call. The protocols "Find", "Find Unique" and "Find Close" are + * methods of reading (or searching) a directory. These protocols may + * be used in place of the core "Search" protocol when LANMAN 1.0 dialect has + * been negotiated. There may be cases where the Search protocol will still be + * used. + * + * Although only the find id portion the resume key should be + * required to identify the search being ter minated, the entire + * resume_key as returned in the previous Find, either a "Find First" or "Find + * Next" is sent to the server in this protocol. + * + * Find Close may generate the following errors: + * + * ERRDOS/ERRbadfid + * ERRSRV/ERRerror + * ERRSRV/ERRinvnid + */ +int +smb_com_find_close(struct smb_request *sr) +{ + unsigned short sattr, maxcount; + char *path; + char filename[14]; + uint32_t cookie; + unsigned char type; + unsigned short key_len; + int rc; + + if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + rc = smbsr_decode_data(sr, "%Abw", sr, &path, &type, &key_len); + if ((rc != 0) || (type != 0x05)) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (key_len == 0) { /* begin search */ + smbsr_raise_error(sr, ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + if (key_len == 21) { + sr->smb_sid = 0; + if (smb_decode_mbc(&sr->smb_data, SMB_RESUME_KEY_FMT, + filename, &sr->smb_sid, &cookie) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->sid_odir = smb_odir_lookup_by_sid(sr->tid_tree, + sr->smb_sid); + if (sr->sid_odir == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + cookie--; /* +1 when returned */ + } else { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + smb_rdir_close(sr); + smbsr_encode_result(sr, 1, 3, "bwwbw", 1, 0, 3, 5, 0); + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_find_notify_close.c b/usr/src/uts/common/fs/smbsrv/smb_find_notify_close.c new file mode 100644 index 000000000000..46a9953b5528 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_find_notify_close.c @@ -0,0 +1,78 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Client Request Description + * ================================== ================================= + * + * BYTE smb_cmd; FIND_NOTIFY_CLOSE + * BYTE smb_wct; value = 1 + * WORD smb_handle; Find notify handle + * WORD smb_bcc; value = 0 + * + * + * Server Response Description + * ================================== ================================= + * + * BYTE smb_cmd; FIND_NOTIFY_CLOSE + * BYTE smb_wct; value = 0 + * WORD smb_bcc; value = 0 + * + * The FIND_NOTIFY_CLOSE request closes the association between a + * directory handle returned following a resource monitor, established + * using a TRANS2_FIND_NOTIFY_FIRST request to the server, and the + * resulting system directory monitor. This request allows the server + * to free any resources held in support of the open handle. + * + * The Find Close protocol is used to match the DosFindNotifyClose + * OS/2 system call. + * + * Find Notify Close may generate the following errors. + * + * ERRDOS/ERRbadfid + * ERRDOS/ + * ERRSRV/ERRerror + * ERRSRV/ERRinvnid + * ERRSRV/ + * ERRHRD/ + */ + + +#include + + +/* + * smb_com_find_notify_close + * + * As far as I can tell, this part of the protocol is not implemented + * by NT server. + */ +int /*ARGSUSED*/ +smb_com_find_notify_close(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_find_unique.c b/usr/src/uts/common/fs/smbsrv/smb_find_unique.c new file mode 100644 index 000000000000..59f9ba12b2b7 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_find_unique.c @@ -0,0 +1,286 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +/* + * Request Format: (same as core Search Protocol - "Find First" form) + * + * Client Request Description + * ================================== ================================= + * + * BYTE smb_wct; value = 2 + * WORD smb_count; max number of entries to find + * WORD smb_attr; search attribute + * WORD smb_bcc; minimum value = 5 + * BYTE smb_ident1; ASCII (04) + * BYTE smb_pathname[]; filename (may contain global characters) + * BYTE smb_ident2; Variable Block (05) + * WORD smb_keylen; must be zero ("Find First" only) + * + * Response Format: (same as core Search Protocol) + * + * Server Response Description + * ================================== ================================= + * BYTE smb_wct; value = 1 + * WORD smb_count; number of entries found + * WORD smb_bcc; minimum value = 3 + * BYTE smb_ident; Variable Block (05) + * WORD smb_datalen; data length + * BYTE smb_data[*]; directory entries + * + * Directory Information Entry (dir_info) Format: (same as core Search Protocol) + * + * BYTE find_buf_reserved[21]; reserved (resume_key) + * BYTE find_buf_attr; attribute + * WORD find_buf_time; modification time (hhhhh mmmmmm xxxxx) + * where 'xxxxx' is in 2 second increments + * WORD find_buf_date; modification date (yyyyyyy mmmm ddddd) + * DWORD find_buf_size; file size + * STRING find_buf_pname[13]; file name -- ASCII (null terminated) + * + * The resume_key has the following format: + * + * BYTE sr_res; reserved: + * bit 7 - reserved for consumer use + * bit 5,6 - reserved for system use + * (must be preserved) + * bits 0-4 - reserved for server + * (must be preserved) + * BYTE sr_name[11]; pathname sought. + * Format: 1-8 character file name, + * left justified 0-3 character extension, + * BYTE sr_findid[1]; uniquely identifies find through + * find_close + * BYTE sr_server[4]; available for server use + * (must be non-zero) + * BYTE sr_res[4]; reserved for consumer use + * + * Service: + * + * The Find protocol finds the directory entry or group of entries matching the + * specified file pathname. The filename portion of the pathname may contain + * global (wild card) characters. The search may not be resumed and no Find + * Close protocol is expected. + * + * The Find protocol is used to match the find OS/2 system call. The protocols + * "Find", "Find_Unique" and "Find_Close" are methods of reading (or searching) + * a directory. These protocols may be used in place of the core "Search" + * protocol when LANMAN 1.0 dialect has been negotiated. There may be cases + * where the Search protocol will still be used. + * + * The format of the Find Unique protocol is the same as the core "Search" + * protocol. The difference is that the directory is logically opened , + * any matching entries returned, and then the directory is logically + * closed. + * + * This allows the Server to make better use of its resources. No Search buffers + * are held (search resumption via presenting a "resume_key" will not be + * allowed). + * + * Only one buffer of entries is expected and find close need not be sent). + * + * The file path name in the request specifies the file to be + * sought. The attribute field indicates the attributes that the file + * must have. If the attribute is zero then only normal files are + * returned. If the system file, hidden or directory attributes are + * specified then the search is inclusive -- both the specified type(s) + * of files and normal files are returned. If the volume label attribute + * is specified then the search is exclusive, and only the volume label entry + * is returned + * + * The max-count field specifies the number of directory entries to be + * returned. The response will contain zero or more directory entries + * as determined by the count-returned field. No more than max-count + * entries will be returned. Only entries that match the sought + * filename/attribute will be returned. + * + * The resume_key field must be null (length = 0). + * + * A Find_Unique request will terminate when either the requested maximum + * number of entries that match the named file are found, or the end + * of directory is reached without the maximum number of matches being + * found. A response containing no entries indicates that no matching + * entries were found between the starting point of the search and the end of + * directory. + * + * There may be multiple matching entries in response to a single + * request as Find Unique supports "wild cards" in the file name (last + * component of the pathname). "?" is the wild card for single + * characters, "*" or "null" will match any number of filename characters + * within a single part of the filename component. The filename is + * divided into two parts -- an eight character name and a three + * character extension. The name and extension are divided by a ".". + * + * If a filename part commences with one or more "?"s then exactly + * that number of characters will be matched by the Wild Cards, e.g., + * "??x" will equal "abx" but not "abcx" or "ax". When a filename part has + * trailing "?"s then it will match the specified number of characters + * or less, e.g., "x??" will match "xab", "xa" and "x", but not "xabc". If + * only "?"s are present in the filename part, then it is handled as + * for trailing "?"s + * + * "*" or "null" match entire pathname parts, thus "*.abc" or ".abc" will + * match any file with an extension of "abc". "*.*", "*" or "null" will + * match all files in a directory. + * + * Unprotected servers require the requester to have read permission on the + * subtree containing the directory searched, the share specifies read + * permission. + * + * Protected servers require the requester to have permission to search the + * specified directory. + * + * If a Find Unique requests more data than can be placed in a + * message of the max-xmit-size for the TID specified, the server will + * abort the virtual circuit to the consumer. + * + * The number of entries returned will be the minimum of: + * + * 1. The number of entries requested. + * 2. The number of complete entries that will fit in the + * negotiated SMB buffer. + * 3. The number of entries that match the requested name pattern and + * attributes. + * + * The error ERRnofiles set in smb_err field of the response header or a zero + * value in smb_count of the response indicates no matching entry was found. + * + * The resume search key returned along with each directory entry is a server + * defined key. This key will be returned as in the Find protocol and Search + * protocol however it may NOT be returned to continue the search. + * + * The date is in the following format: + * bits: + * 1 1 1 1 1 1 + * 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * y y y y y y y m m m m d d d d d + * where: + * y - bit of year 0-119 (1980-2099) + * m - bit of month 1-12 + * d - bit of day 1-31 + * + * The time is in the following format: + * bits: + * 1 1 1 1 1 1 + * 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * h h h h h m m m m m m x x x x x + * where: + * h - bit of hour (0-23) + * m - bit of minute (0-59) + * x - bit of 2 second increment + * + * Find Unique may generate the following errors. + * ERRDOS/ERRnofiles + * ERRDOS/ERRbadpath + * ERRDOS/ERRnoaccess + * ERRDOS/ERRbadaccess + * ERRDOS/ERRbadshare + * ERRSRV/ERRerror + * ERRSRV/ERRaccess + * ERRSRV/ERRinvnid + */ +int +smb_com_find_unique(struct smb_request *sr) +{ + int rc; + unsigned short sattr, count, maxcount; + char *path; + struct vardata_block *vdb; + struct smb_node *node; + smb_odir_context_t *pc; + + vdb = kmem_alloc(sizeof (struct vardata_block), KM_SLEEP); + if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0) { + kmem_free(vdb, sizeof (struct vardata_block)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (smbsr_decode_data(sr, "%AV", sr, &path, vdb) != 0) { + kmem_free(vdb, sizeof (struct vardata_block)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (vdb->len != 0) { + kmem_free(vdb, sizeof (struct vardata_block)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + (void) smb_encode_mbc(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0); + + /* begin search */ + (void) smb_rdir_open(sr, path, sattr); + + pc = kmem_zalloc(sizeof (*pc), KM_SLEEP); + pc->dc_cookie = 0; + count = 0; + node = (struct smb_node *)0; + rc = 0; + while (count < maxcount) { + if ((rc = smb_rdir_next(sr, &node, pc)) != 0) + break; + + (void) smb_encode_mbc(&sr->reply, ".8c3cbl4.bYl13c", + pc->dc_name83, pc->dc_name83+9, sr->smb_sid, + pc->dc_cookie+1, pc->dc_dattr, + smb_gmt_to_local_time(pc->dc_attr.sa_vattr.va_mtime.tv_sec), + (int32_t)smb_node_get_size(node, &pc->dc_attr), + (*pc->dc_shortname) ? pc->dc_shortname : pc->dc_name); + smb_node_release(node); + node = (struct smb_node *)0; + count++; + } + kmem_free(pc, sizeof (*pc)); + + smb_rdir_close(sr); + + if ((rc != 0) && (rc != ENOENT)) { + /* returned error by smb_rdir_next() */ + kmem_free(vdb, sizeof (struct vardata_block)); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + if (count == 0) { + kmem_free(vdb, sizeof (struct vardata_block)); + smbsr_raise_error(sr, ERRDOS, ERRnofiles); + /* NOTREACHED */ + } + + rc = (MBC_LENGTH(&sr->reply) - sr->cur_reply_offset) - 8; + if (smb_poke_mbc(&sr->reply, sr->cur_reply_offset, + "bwwbw", 1, count, rc+3, 5, rc) < 0) { + kmem_free(vdb, sizeof (struct vardata_block)); + smbsr_encode_error(sr); + /* NOTREACHED */ + } + kmem_free(vdb, sizeof (struct vardata_block)); + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_flush.c b/usr/src/uts/common/fs/smbsrv/smb_flush.c new file mode 100644 index 000000000000..b9cfb8c34993 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_flush.c @@ -0,0 +1,129 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * The flush SMB is sent to ensure all data and allocation information + * for the corresponding file has been written to stable storage. This + * is a synchronous request. The response should not be sent until the + * writes are complete. + * + * The SmbFlush request is described in CIFS/1.0 1996 Section 3.9.14. + * + * CIFS/1.0 June 13, 1996 + * Heizer, et al + * draft-heizer-cifs-v1-spec-00.txt + */ + +#include +#include + + +static void smb_flush_file(struct smb_request *sr, struct smb_ofile *ofile); + + +int smb_flush_required = 1; + + +/* + * smb_commit_required + * + * Specify whether or not SmbFlush should send commit requests to the + * file system. If state is non-zero, commit requests will be sent to + * the file system. If state is zero, SmbFlush is a no-op. + */ +void +smb_commit_required(int state) +{ + smb_flush_required = state; +} + + +/* + * smb_com_flush + * + * Flush any cached data for a specified file, or for all files that + * this client has open, to stable storage. If the fid is valid (i.e. + * not 0xFFFF), we flush only that file. Otherwise we flush all files + * associated with this client. + * + * We need to protect the list because there's a good chance we'll + * block during the flush operation. + */ +int +smb_com_flush(smb_request_t *sr) +{ + smb_ofile_t *file; + smb_llist_t *flist; + + if (smbsr_decode_vwv(sr, "w", &sr->smb_fid) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (smb_flush_required == 0) { + smbsr_encode_empty_result(sr); + return (SDRC_NORMAL_REPLY); + } + + if (sr->smb_fid != 0xffff) { + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, + sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + smb_flush_file(sr, sr->fid_ofile); + } else { + flist = &sr->tid_tree->t_ofile_list; + smb_llist_enter(flist, RW_READER); + file = smb_llist_head(flist); + while (file) { + mutex_enter(&file->f_mutex); + smb_flush_file(sr, file); + mutex_exit(&file->f_mutex); + file = smb_llist_next(flist, file); + } + smb_llist_exit(flist); + } + smbsr_encode_empty_result(sr); + return (SDRC_NORMAL_REPLY); +} + + +/* + * smb_flush_file + * + * If writes on this file are not synchronous, flush it using the NFSv3 + * commit interface. + */ +static void smb_flush_file(struct smb_request *sr, struct smb_ofile *ofile) +{ + if ((ofile->f_node->flags & NODE_FLAGS_WRITE_THROUGH) == 0) + (void) smb_fsop_commit(sr, sr->user_cr, ofile->f_node); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_forward.c b/usr/src/uts/common/fs/smbsrv/smb_forward.c new file mode 100644 index 000000000000..498e3bcaa76c --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_forward.c @@ -0,0 +1,148 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module provides the SMB ForwardUserName interface: + * smb_com_forward_user_name + * smb_com_cancel_forward + * smb_com_get_machine_name + * + * The description of this interface is taken verbatim from the netmon + * SMB protocol help. These functions are currently empty stubs that + * return SDRC_UNIMPLEMENTED. + */ + + +#include + + +/* + * smb_com_forward_user_name + * + * This command informs the server that it should accept messages sent + * to the forwarded name. The name specified in this message does not + * include the one byte suffix ("03" or "05"). + * + * Client Request Description + * ================================== ================================= + * BYTE smb_com SMBfwdname + * BYTE smb_wct 0 + * BYTE smb_bcc min = 2 + * BYTE smb_buf[] ASCII -- 04 + * forwarded name (max 15 bytes) + * + * Server Response Description + * ================================== ================================= + * BYTE smb_com SMBfwdname + * BYTE smb_wct 0 + * BYTE smb_bcc 0 + * + * ForwardUserName may generate the following errors. + * ERRDOS/ + * ERRSRV/ERRerror + * ERRSRV/ERRinvnid + * ERRSRV/ERRrmuns + * ERRSRV/ + * ERRHRD/ + */ +int /*ARGSUSED*/ +smb_com_forward_user_name(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} + + +/* + * smb_com_cancel_forward + * + * The CancelForward command cancels the effect of a prior ForwardUserName + * command. The addressed server will no longer accept messages for the + * designated user name. The name specified in this message does not + * include the one byte suffix ("05"). + * + * Client Request Description + * ================================== ================================= + * BYTE smb_com SMBcancelf + * BYTE smb_wct 0 + * BYTE smb_bcc min = 2 + * BYTE smb_buf[] ASCII -- 04 + * forwarded name (max 15 bytes) + * + * Server Response Description + * ================================== ================================= + * BYTE smb_com SMBcancelf + * BYTE smb_wct 0 + * BYTE smb_bcc 0 + * + * CancelForward may generate the following errors. + * ERRDOS/ + * ERRSRV/ERRerror + * ERRSRV/ERRinvnid + * ERRSRV/ + * ERRHRD/ + */ +int /*ARGSUSED*/ +smb_com_cancel_forward(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} + + +/* + * smb_com_get_machine_name + * + * The GetMachineName command obtains the machine name of the target machine. + * It is used prior to the CancelForward command to determine to which + * machine the CancelForward command should be sent. GetMachineName is sent + * to the forwarded name to be canceled, and the server then returns the + * machine name to which the CancelForward command must be sent. + * + * Client Request Description + * ================================== ================================= + * BYTE smb_com SMBgetmac + * BYTE smb_wct 0 + * BYTE smb_bcc 0 + * + * Server Response Description + * ================================== ================================= + * BYTE smb_com SMBgetmac + * BYTE smb_wct 0 + * BYTE smb_bcc min = 2 + * BYTE smb_buf[] ASCII -- 04 + * machine name (max 15 bytes) + * + * GetMachineName may return the following errors. + * ERRRDOS/ + * ERRSRV/ERRerror + * ERRSRV/ERRinvnid + * ERRSRV/ + */ +int /*ARGSUSED*/ +smb_com_get_machine_name(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_fsd.c b/usr/src/uts/common/fs/smbsrv/smb_fsd.c new file mode 100644 index 000000000000..354b7d348759 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_fsd.c @@ -0,0 +1,276 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include + +fs_desc_t null_fsd = {0, 0}; +extern vfs_t *rootvfs; + +static void fsd_getname(vfs_t *vfsp, fsvol_attr_t *vol_attr); +static void fsd_getflags(vfs_t *vfsp, unsigned *flags); +static void fsd_getseq(vfs_t *vfsp, uint32_t *fs_sequence); +static void fsd_name_from_mntpoint(char *, const char *, size_t); + +/* + * The "pathname" parameter may be either a path name or a volume name. + * Kernel caller must call fsd_rele(vfsp). + * User space caller will not have a hold on the fs. + */ + +void * +fsd_lookup(char *name, unsigned flags, fs_desc_t *fsd) +{ + int (*func)(const char *, const char *); + struct vfs *vfsp, *vfs_found = NULL; + refstr_t *vfs_mntpoint; + char vfs_volname[VOL_NAME_MAX]; + char volname[VOL_NAME_MAX]; + + if (fsd) + *fsd = null_fsd; + + vfs_list_read_lock(); + vfsp = rootvfs; + + func = (flags & FSOLF_CASE_INSENSITIVE) ? utf8_strcasecmp : strcmp; + + fsd_name_from_mntpoint(volname, name, VOL_NAME_MAX); + + do { + vfs_mntpoint = vfsp->vfs_mntpt; + refstr_hold(vfs_mntpoint); + + fsd_name_from_mntpoint(vfs_volname, vfs_mntpoint->rs_string, + VOL_NAME_MAX); + + if (func(volname, vfs_volname) == 0) { + VFS_HOLD(vfsp); + vfs_found = vfsp; + + if (fsd) { + *fsd = vfsp->vfs_fsid; + } + refstr_rele(vfs_mntpoint); + break; + } else { + refstr_rele(vfs_mntpoint); + } + vfsp = vfsp->vfs_next; + + } while (vfsp != rootvfs); + + vfs_list_unlock(); + return (vfs_found); +} + +/* + * Compare two volume descriptors to determine whether or not they + * refer to the same volume. Returns 0 if the descriptors refer to + * the same volume. Otherwise returns 1. + */ + +int +fsd_cmp(fs_desc_t *fsd1, fs_desc_t *fsd2) +{ + if ((fsd1->val[0] == fsd2->val[0]) && (fsd1->val[1] == fsd2->val[1])) { + return (0); + } + return (1); +} + +/* + * fsd_getattr + * + * Obtain volume attributes. If the volume is mounted, the attributes + * are copied to vol_attr. Otherwise, vol_attr is zero'd. + * + * Returns 0 on success. Otherwise an errno is returned to indicate + * the error. + */ + +int +fsd_getattr(fs_desc_t *fsd, fsvol_attr_t *vol_attr) +{ + vfs_t *vfsp; + + ASSERT(fsd); + ASSERT(vol_attr); + + if ((vfsp = getvfs(fsd)) == NULL) + return (ESTALE); + + bzero(vol_attr, sizeof (fsvol_attr_t)); + fsd_getname(vfsp, vol_attr); + fsd_getflags(vfsp, &vol_attr->flags); + fsd_getseq(vfsp, &vol_attr->fs_sequence); + + VFS_RELE(vfsp); + return (0); +} + +/* + * Check whether or not a file system supports the features identified + * by flags. Flags can be any combination of the FSOLF flags. + * Returns 1 if all of the features are supported. Otherwise returns 0. + * Exception: Returns -1 if stale fsd. + */ + +int +fsd_chkcap(fs_desc_t *fsd, unsigned flags) +{ + vfs_t *vfsp = getvfs(fsd); + unsigned getflags = 0; + + if (!vfsp) { + return (-1); + } + + fsd_getflags(vfsp, &getflags); + + VFS_RELE(vfsp); + + if ((flags != 0) && (getflags & flags) == flags) { + return (1); + } + + return (0); +} + +void * +fsd_hold(fs_desc_t *fsd) +{ + void *vfsp = getvfs(fsd); + + return (vfsp); +} + +void +fsd_rele(void *vfsp) +{ + ASSERT(vfsp); + + VFS_RELE((vfs_t *)vfsp); +} + +/* + * Returns volume name. + * Also fills in vol_attr->fs_typename (needed for fsd_getattr()). + * + * File system types are hardcoded in uts/common/os/vfs_conf.c . + */ + +static void +fsd_getname(vfs_t *vfsp, fsvol_attr_t *vol_attr) +{ + refstr_t *vfs_mntpoint; + + (void) strlcpy(vol_attr->fs_typename, + vfssw[vfsp->vfs_fstype].vsw_name, VOL_NAME_MAX); + + vfs_mntpoint = vfs_getmntpoint(vfsp); + fsd_name_from_mntpoint(vol_attr->name, vfs_mntpoint->rs_string, + VOL_NAME_MAX); + + refstr_rele(vfs_mntpoint); +} + +/* + * Always set supports ACLs because the VFS will fake ACLs + * for file systems that don't support them. + */ +static void +fsd_getflags(vfs_t *vfsp, uint_t *flags_ret) +{ + char *fsname = vfssw[vfsp->vfs_fstype].vsw_name; + uint_t flags = FSOLF_SUPPORTS_ACLS; + + if (vfsp->vfs_flag & VFS_RDONLY) + flags |= FSOLF_READONLY; + + if (vfsp->vfs_flag & VFS_XATTR) + flags |= FSOLF_STREAMS; + + if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL)) + flags |= FSOLF_NO_ATIME; + + if (strcmp(fsname, "tmpfs") == 0) + flags |= FSOLF_NOEXPORT; + + if (vfs_has_feature(vfsp, VFSFT_XVATTR)) + flags |= FSOLF_XVATTR; + + if (vfs_has_feature(vfsp, VFSFT_CASEINSENSITIVE)) + flags |= FSOLF_CASE_INSENSITIVE; + + if (vfs_has_feature(vfsp, VFSFT_NOCASESENSITIVE)) + flags |= FSOLF_NO_CASE_SENSITIVE; + + if (vfs_has_feature(vfsp, VFSFT_DIRENTFLAGS)) + flags |= FSOLF_DIRENTFLAGS; + + DTRACE_PROBE1(smb__vfs__getflags, uint_t, flags); + *flags_ret = flags; +} + + +/* ARGSUSED */ +static void +fsd_getseq(vfs_t *vfsp, uint32_t *fs_sequence) +{ + /* + * This function is more complicated if there is + * DAVE support, but we have excised that code for + * the moment. + */ + *fs_sequence = 0; +} + +/* + * This function parses out the first conponent of a mount path, + * used elsewhere as the volume name. + * + * For "/", the volume name is "" (i.e. ROOTVOL). + */ + +static void +fsd_name_from_mntpoint(char *name, const char *mntpnt, size_t name_sz) +{ + const char *s = mntpnt; + char *tmp = name; + + s += strspn(s, "/"); + (void) strlcpy(name, s, name_sz); + (void) strsep((char **)&tmp, "/"); + + DTRACE_PROBE2(smb__vfs__volume, char *, mntpnt, char *, name); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_fsops.c b/usr/src/uts/common/fs/smbsrv/smb_fsops.c new file mode 100644 index 000000000000..655e5ad3ff6c --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_fsops.c @@ -0,0 +1,2595 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include + +u_longlong_t smb_caller_id; + +static int smb_fsop_amask_to_omode(uint32_t granted_access); + +extern uint32_t smb_sd_tofs(smb_sdbuf_t *sr_sd, smb_fssd_t *fs_sd); + +extern uint32_t smb_sd_write(smb_request_t *sr, smb_sdbuf_t *sr_sd, + uint32_t secinfo); + +extern int smb_vop_acl_to_vsa(acl_t *acl_info, vsecattr_t *vsecattr, + int *aclbsize); + +static int smb_fsop_sdinherit(smb_request_t *sr, smb_node_t *dnode, + smb_fssd_t *fs_sd); + +static void smb_fsop_aclsplit(acl_t *zacl, acl_t **dacl, acl_t **sacl, + int which_acl); +static acl_t *smb_fsop_aclmerge(acl_t *dacl, acl_t *sacl); + +/* + * The smb_fsop_* functions have knowledge of CIFS semantics. + * + * The smb_vop_* functions have minimal knowledge of CIFS semantics and + * serve as an interface to the VFS layer. + * + * Hence, smb_request_t and smb_node_t structures should not be passed + * from the smb_fsop_* layer to the smb_vop_* layer. + * + * In general, CIFS service code should only ever call smb_fsop_* + * functions directly, and never smb_vop_* functions directly. + * + * smb_fsop_* functions should call smb_vop_* functions where possible, instead + * of their smb_fsop_* counterparts. However, there are times when + * this cannot be avoided. + */ + +/* + * Note: Stream names cannot be mangled. + */ + +int +smb_fsop_start() +{ + int error; + + smb_caller_id = fs_new_caller_id(); + error = smb_node_root_init(); + + if (error == 0) + error = smb_fem_init(); + + return (error); +} + +void +smb_fsop_stop() +{ + smb_fem_shutdown(); + smb_vfs_rele_all(); + smb_node_root_fini(); +} + +int +smb_fsop_open(smb_ofile_t *of) +{ + caller_context_t ct; + int mode; + + mode = smb_fsop_amask_to_omode(of->f_granted_access); + + smb_get_caller_context(NULL, &ct); + + /* + * Assuming that same vnode is returned as we had before + * (i.e. no special vnodes) + */ + + return (smb_vop_open(&of->f_node->vp, mode, of->f_cr, &ct)); +} + +int +smb_fsop_close(smb_ofile_t *of) +{ + caller_context_t ct; + int mode; + + mode = smb_fsop_amask_to_omode(of->f_granted_access); + + smb_get_caller_context(NULL, &ct); + + return (smb_vop_close(of->f_node->vp, mode, of->f_cr, &ct)); +} + +static int +smb_fsop_amask_to_omode(uint32_t granted_access) +{ + int mode = 0; + + if (granted_access & (ACE_READ_DATA | ACE_EXECUTE)) + mode |= FREAD; + + if (granted_access & (ACE_WRITE_DATA | ACE_APPEND_DATA)) + mode |= FWRITE; + + if (granted_access & ACE_APPEND_DATA) + mode |= FAPPEND; + + return (mode); +} + +static int +smb_fsop_create_with_sd( + struct smb_request *sr, + cred_t *cr, + smb_node_t *snode, + char *name, + smb_attr_t *attr, + smb_node_t **ret_snode, + smb_attr_t *ret_attr, + smb_fssd_t *fs_sd) +{ + caller_context_t ct; + vsecattr_t *vsap; + vsecattr_t vsecattr; + acl_t *acl, *dacl, *sacl; + smb_attr_t set_attr; + vnode_t *vp; + int aclbsize = 0; /* size of acl list in bytes */ + int flags = 0; + int is_dir; + int rc; + + ASSERT(fs_sd); + + if (SMB_TREE_CASE_INSENSITIVE(sr)) + flags = SMB_IGNORE_CASE; + + ASSERT(cr); + smb_get_caller_context(sr, &ct); + + is_dir = ((fs_sd->sd_flags & SMB_FSSD_FLAGS_DIR) != 0); + + if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACLONCREATE) { + if (fs_sd->sd_secinfo & SMB_ACL_SECINFO) { + dacl = fs_sd->sd_zdacl; + sacl = fs_sd->sd_zsacl; + ASSERT(dacl || sacl); + if (dacl && sacl) { + acl = smb_fsop_aclmerge(dacl, sacl); + } else if (dacl) { + acl = dacl; + } else { + acl = sacl; + } + + rc = smb_vop_acl_to_vsa(acl, &vsecattr, &aclbsize); + + if (dacl && sacl) + acl_free(acl); + + if (rc) + return (rc); + + vsap = &vsecattr; + } + else + vsap = NULL; + + if (is_dir) { + rc = smb_vop_mkdir(snode->vp, name, attr, &vp, flags, + cr, &ct, vsap); + } else { + rc = smb_vop_create(snode->vp, name, attr, &vp, flags, + cr, &ct, vsap); + } + + if (vsap != NULL) + kmem_free(vsap->vsa_aclentp, aclbsize); + + if (rc != 0) + return (rc); + + set_attr.sa_mask = 0; + + /* + * Ideally we should be able to specify the owner and owning + * group at create time along with the ACL. Since we cannot + * do that right now, kcred is passed to smb_vop_setattr so it + * doesn't fail due to lack of permission. + */ + if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) { + set_attr.sa_vattr.va_uid = fs_sd->sd_uid; + set_attr.sa_mask |= SMB_AT_UID; + } + + if (fs_sd->sd_secinfo & SMB_GROUP_SECINFO) { + set_attr.sa_vattr.va_gid = fs_sd->sd_gid; + set_attr.sa_mask |= SMB_AT_GID; + } + + if (set_attr.sa_mask) { + rc = smb_vop_setattr(snode->vp, NULL, &set_attr, + 0, kcred, &ct); + } + + } else { + /* + * For filesystems that don't support ACL-on-create, try + * to set the specified SD after create, which could actually + * fail because of conflicts between inherited security + * attributes upon creation and the specified SD. + * + * Passing kcred to smb_fsop_sdwrite() to overcome this issue. + */ + + if (is_dir) { + rc = smb_vop_mkdir(snode->vp, name, attr, &vp, flags, + cr, &ct, NULL); + } else { + rc = smb_vop_create(snode->vp, name, attr, &vp, flags, + cr, &ct, NULL); + } + + if (rc == 0) + rc = smb_fsop_sdwrite(sr, kcred, snode, fs_sd, 1); + } + + if (rc == 0) { + *ret_snode = smb_node_lookup(sr, &sr->arg.open, cr, vp, name, + snode, NULL, ret_attr); + + if (*ret_snode == NULL) { + VN_RELE(vp); + rc = ENOMEM; + } + } + + return (rc); +} + + +/* + * smb_fsop_create + * + * All SMB functions should use this wrapper to ensure that + * all the smb_vop_creates are performed with the appropriate credentials. + * Please document any direct calls to explain the reason + * for avoiding this wrapper. + * + * It is assumed that a reference exists on snode coming into this routine. + * + * *ret_snode is returned with a reference upon success. No reference is + * taken if an error is returned. + */ + +int +smb_fsop_create( + struct smb_request *sr, + cred_t *cr, + smb_node_t *dir_snode, + char *name, + smb_attr_t *attr, + smb_node_t **ret_snode, + smb_attr_t *ret_attr) +{ + struct open_param *op = &sr->arg.open; + smb_node_t *fnode; + smb_attr_t file_attr; + caller_context_t ct; + vnode_t *xattrdirvp; + vnode_t *vp; + char *longname = NULL; + char *namep; + char *fname; + char *sname; + int is_stream; + int flags = 0; + int rc = 0; + smb_fssd_t fs_sd; + uint32_t secinfo; + uint32_t status; + + ASSERT(cr); + ASSERT(dir_snode); + ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); + ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING); + + ASSERT(ret_snode); + *ret_snode = 0; + + ASSERT(name); + if (*name == 0) + return (EINVAL); + + if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0) + return (EACCES); + + ASSERT(sr); + ASSERT(sr->tid_tree); + if (SMB_TREE_IS_READ_ONLY(sr)) + return (EROFS); + + if (SMB_TREE_CASE_INSENSITIVE(sr)) + flags = SMB_IGNORE_CASE; + + fname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + sname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + is_stream = smb_stream_parse_name(name, fname, sname); + + if (is_stream) + namep = fname; + else + namep = name; + + if (smb_maybe_mangled_name(namep)) { + longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + rc = smb_unmangle_name(sr, cr, dir_snode, namep, longname, + MAXNAMELEN, NULL, NULL, 1); + + if ((is_stream == 0) && (rc == 0)) + rc = EEXIST; + + if ((is_stream && rc) || + ((is_stream == 0) && (rc != ENOENT))) { + kmem_free(longname, MAXNAMELEN); + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + return (rc); + } + + if (is_stream) + namep = longname; + else + kmem_free(longname, MAXNAMELEN); + } + + if (is_stream) { + /* + * Look up the unnamed stream. + * + * Mangle processing in smb_fsop_lookup() for the unnamed + * stream won't be needed (as it was done above), but + * it may be needed on any link target (which + * smb_fsop_lookup() will provide). + */ + rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS, + sr->tid_tree->t_snode, dir_snode, namep, &fnode, &file_attr, + 0, 0); + + if (longname) { + kmem_free(longname, MAXNAMELEN); + namep = NULL; + } + + if (rc != 0) { + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + return (rc); + } + + smb_get_caller_context(sr, &ct); + + rc = smb_vop_stream_create(fnode->vp, sname, attr, &vp, + &xattrdirvp, flags, cr, &ct); + + if (rc != 0) { + smb_node_release(fnode); + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + return (rc); + } + + *ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp, + vp, sname, ret_attr); + + smb_node_release(fnode); + + if (*ret_snode == NULL) { + VN_RELE(xattrdirvp); + VN_RELE(vp); + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + return (ENOMEM); + } + } else { + if (op->sd_buf) { + /* + * SD sent by client in Windows format. Needs to be + * converted to FS format. No inheritance. + */ + secinfo = smb_sd_get_secinfo((smb_sdbuf_t *)op->sd_buf); + smb_fsop_sdinit(&fs_sd, secinfo, 0); + + status = smb_sd_tofs(op->sd_buf, &fs_sd); + if (status == NT_STATUS_SUCCESS) { + rc = smb_fsop_create_with_sd(sr, cr, dir_snode, + name, attr, ret_snode, ret_attr, &fs_sd); + } + else + rc = EINVAL; + smb_fsop_sdterm(&fs_sd); + } else if (sr->tid_tree->t_acltype == ACE_T) { + /* + * No incoming SD and filesystem is ZFS + * Server applies Windows inheritance rules, + * see smb_fsop_sdinherit() comments as to why. + */ + smb_fsop_sdinit(&fs_sd, SMB_ACL_SECINFO, 0); + rc = smb_fsop_sdinherit(sr, dir_snode, &fs_sd); + if (rc == 0) { + rc = smb_fsop_create_with_sd(sr, cr, dir_snode, + name, attr, ret_snode, ret_attr, &fs_sd); + } + + smb_fsop_sdterm(&fs_sd); + } else { + /* + * No incoming SD and filesystem is not ZFS + * let the filesystem handles the inheritance. + */ + smb_get_caller_context(sr, &ct); + rc = smb_vop_create(dir_snode->vp, name, attr, &vp, + flags, cr, &ct, NULL); + + if (rc == 0) { + *ret_snode = smb_node_lookup(sr, op, cr, vp, + name, dir_snode, NULL, ret_attr); + + if (*ret_snode == NULL) { + VN_RELE(vp); + rc = ENOMEM; + } + } + + } + } + + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + return (rc); +} + +/* + * smb_fsop_mkdir + * + * All SMB functions should use this wrapper to ensure that + * the the calls are performed with the appropriate credentials. + * Please document any direct call to explain the reason + * for avoiding this wrapper. + * + * It is assumed that a reference exists on snode coming into this routine. + * + * *ret_snode is returned with a reference upon success. No reference is + * taken if an error is returned. + */ +int +smb_fsop_mkdir( + struct smb_request *sr, + cred_t *cr, + smb_node_t *dir_snode, + char *name, + smb_attr_t *attr, + smb_node_t **ret_snode, + smb_attr_t *ret_attr) +{ + struct open_param *op = &sr->arg.open; + caller_context_t ct; + char *longname; + vnode_t *vp; + int flags = 0; + smb_fssd_t fs_sd; + uint32_t secinfo; + uint32_t status; + int rc; + + ASSERT(cr); + ASSERT(dir_snode); + ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); + ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING); + + ASSERT(ret_snode); + *ret_snode = 0; + + ASSERT(name); + if (*name == 0) + return (EINVAL); + + if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0) + return (EACCES); + + ASSERT(sr); + ASSERT(sr->tid_tree); + if (SMB_TREE_IS_READ_ONLY(sr)) + return (EROFS); + + if (smb_maybe_mangled_name(name)) { + longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + rc = smb_unmangle_name(sr, cr, dir_snode, name, longname, + MAXNAMELEN, NULL, NULL, 1); + + kmem_free(longname, MAXNAMELEN); + + /* + * If the name passed in by the client has an unmangled + * equivalent that is found in the specified directory, + * then the mkdir cannot succeed. Return EEXIST. + * + * Only if ENOENT is returned will a mkdir be attempted. + */ + + if (rc == 0) + rc = EEXIST; + + if (rc != ENOENT) + return (rc); + } + + if (SMB_TREE_CASE_INSENSITIVE(sr)) + flags = SMB_IGNORE_CASE; + + smb_get_caller_context(sr, &ct); + + if (op->sd_buf) { + /* + * SD sent by client in Windows format. Needs to be + * converted to FS format. No inheritance. + */ + secinfo = smb_sd_get_secinfo((smb_sdbuf_t *)op->sd_buf); + smb_fsop_sdinit(&fs_sd, secinfo, SMB_FSSD_FLAGS_DIR); + + status = smb_sd_tofs(op->sd_buf, &fs_sd); + if (status == NT_STATUS_SUCCESS) { + rc = smb_fsop_create_with_sd(sr, cr, dir_snode, + name, attr, ret_snode, ret_attr, &fs_sd); + } + else + rc = EINVAL; + smb_fsop_sdterm(&fs_sd); + } else if (sr->tid_tree->t_acltype == ACE_T) { + /* + * No incoming SD and filesystem is ZFS + * Server applies Windows inheritance rules, + * see smb_fsop_sdinherit() comments as to why. + */ + smb_fsop_sdinit(&fs_sd, SMB_ACL_SECINFO, SMB_FSSD_FLAGS_DIR); + rc = smb_fsop_sdinherit(sr, dir_snode, &fs_sd); + if (rc == 0) { + rc = smb_fsop_create_with_sd(sr, cr, dir_snode, + name, attr, ret_snode, ret_attr, &fs_sd); + } + + smb_fsop_sdterm(&fs_sd); + + } else { + rc = smb_vop_mkdir(dir_snode->vp, name, attr, &vp, flags, cr, + &ct, NULL); + + if (rc == 0) { + *ret_snode = smb_node_lookup(sr, op, cr, vp, name, + dir_snode, NULL, ret_attr); + + if (*ret_snode == NULL) { + VN_RELE(vp); + rc = ENOMEM; + } + } + } + + return (rc); +} + +/* + * smb_fsop_remove + * + * All SMB functions should use this wrapper to ensure that + * the the calls are performed with the appropriate credentials. + * Please document any direct call to explain the reason + * for avoiding this wrapper. + * + * It is assumed that a reference exists on snode coming into this routine. + * + * od: This means that the name passed in is an on-disk name. + * A null smb_request might be passed to this function. + */ + +int +smb_fsop_remove( + struct smb_request *sr, + cred_t *cr, + smb_node_t *dir_snode, + char *name, + int od) +{ + smb_node_t *fnode; + smb_attr_t file_attr; + caller_context_t ct; + char *longname; + char *fname; + char *sname; + int flags = 0; + int rc; + + ASSERT(cr); + /* + * The state of the node could be SMB_NODE_STATE_DESTROYING if this + * function is called during the deletion of the node (because of + * DELETE_ON_CLOSE). + */ + ASSERT(dir_snode); + ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); + + if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0) + return (EACCES); + + if (SMB_TREE_IS_READ_ONLY(sr)) + return (EROFS); + + smb_get_caller_context(sr, &ct); + + fname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + sname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + if (smb_stream_parse_name(name, fname, sname)) { + + ASSERT(od == 0); + + if (SMB_TREE_CASE_INSENSITIVE(sr)) + flags = SMB_IGNORE_CASE; + + /* + * Look up the unnamed stream (i.e. fname). + * Unmangle processing will be done on fname + * as well as any link target. + */ + + rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS, + sr->tid_tree->t_snode, dir_snode, fname, &fnode, &file_attr, + 0, 0); + + if (rc != 0) { + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + return (rc); + } + + /* + * XXX + * Need to find out what permission is required by NTFS + * to remove a stream. + */ + rc = smb_vop_stream_remove(fnode->vp, sname, flags, cr, &ct); + + smb_node_release(fnode); + } else { + /* + * If the passed-in name is an on-disk name, + * then we need to do a case-sensitive remove. + * This is important if the on-disk name + * corresponds to a mangled name passed in by + * the client. We want to make sure to remove + * the exact file specified by the client, + * instead of letting the underlying file system + * do a remove on the "first match." + */ + + if ((od == 0) && SMB_TREE_CASE_INSENSITIVE(sr)) + flags = SMB_IGNORE_CASE; + + rc = smb_vop_remove(dir_snode->vp, name, flags, cr, &ct); + + if (rc == ENOENT) { + if (smb_maybe_mangled_name(name) == 0) { + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + return (rc); + } + longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + rc = smb_unmangle_name(sr, cr, dir_snode, name, + longname, MAXNAMELEN, NULL, NULL, 1); + + if (rc == 0) { + /* + * We passed "1" as the "od" parameter + * to smb_unmangle_name(), such that longname + * is the real (case-sensitive) on-disk name. + * We make sure we do a remove on this exact + * name, as the name was mangled and denotes + * a unique file. + */ + flags &= ~SMB_IGNORE_CASE; + rc = smb_vop_remove(dir_snode->vp, longname, + flags, cr, &ct); + } + + kmem_free(longname, MAXNAMELEN); + } + } + + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + return (rc); +} + +/* + * smb_fsop_remove_streams + * + * This function removes a file's streams without removing the + * file itself. + * + * It is assumed that snode is not a link. + */ +int +smb_fsop_remove_streams(struct smb_request *sr, cred_t *cr, + smb_node_t *fnode) +{ + struct fs_stream_info stream_info; + caller_context_t ct; + uint32_t cookie = 0; + int flags = 0; + int rc; + + ASSERT(cr); + ASSERT(fnode); + ASSERT(fnode->n_magic == SMB_NODE_MAGIC); + ASSERT(fnode->n_state != SMB_NODE_STATE_DESTROYING); + + if (SMB_TREE_ROOT_FS(sr, fnode) == 0) + return (EACCES); + + if (SMB_TREE_IS_READ_ONLY(sr)) + return (EROFS); + + if (SMB_TREE_CASE_INSENSITIVE(sr)) + flags = SMB_IGNORE_CASE; + + smb_get_caller_context(sr, &ct); + + for (;;) { + rc = smb_vop_stream_readdir(fnode->vp, &cookie, &stream_info, + NULL, NULL, flags, cr, &ct); + + if ((rc != 0) || (cookie == SMB_EOF)) + break; + + (void) smb_vop_stream_remove(fnode->vp, stream_info.name, flags, + cr, &ct); + } + return (rc); +} + +/* + * smb_fsop_rmdir + * + * All SMB functions should use this wrapper to ensure that + * the the calls are performed with the appropriate credentials. + * Please document any direct call to explain the reason + * for avoiding this wrapper. + * + * It is assumed that a reference exists on snode coming into this routine. + * + * od: This means that the name passed in is an on-disk name. + */ + +int +smb_fsop_rmdir( + struct smb_request *sr, + cred_t *cr, + smb_node_t *dir_snode, + char *name, + int od) +{ + caller_context_t ct; + int rc; + int flags = 0; + char *longname; + + ASSERT(cr); + /* + * The state of the node could be SMB_NODE_STATE_DESTROYING if this + * function is called during the deletion of the node (because of + * DELETE_ON_CLOSE). + */ + ASSERT(dir_snode); + ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); + + if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0) + return (EACCES); + + if (SMB_TREE_IS_READ_ONLY(sr)) + return (EROFS); + + /* + * If the passed-in name is an on-disk name, + * then we need to do a case-sensitive rmdir. + * This is important if the on-disk name + * corresponds to a mangled name passed in by + * the client. We want to make sure to remove + * the exact directory specified by the client, + * instead of letting the underlying file system + * do a rmdir on the "first match." + */ + + if ((od == 0) && SMB_TREE_CASE_INSENSITIVE(sr)) + flags = SMB_IGNORE_CASE; + + smb_get_caller_context(sr, &ct); + + rc = smb_vop_rmdir(dir_snode->vp, name, flags, cr, &ct); + + if (rc == ENOENT) { + if (smb_maybe_mangled_name(name) == 0) + return (rc); + + longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + rc = smb_unmangle_name(sr, cr, dir_snode, + name, longname, MAXNAMELEN, NULL, + NULL, 1); + + if (rc == 0) { + /* + * We passed "1" as the "od" parameter + * to smb_unmangle_name(), such that longname + * is the real (case-sensitive) on-disk name. + * We make sure we do a rmdir on this exact + * name, as the name was mangled and denotes + * a unique directory. + */ + flags &= ~SMB_IGNORE_CASE; + rc = smb_vop_rmdir(dir_snode->vp, longname, flags, cr, + &ct); + } + + kmem_free(longname, MAXNAMELEN); + } + + return (rc); +} + +/* + * smb_fsop_getattr + * + * All SMB functions should use this wrapper to ensure that + * the the calls are performed with the appropriate credentials. + * Please document any direct call to explain the reason + * for avoiding this wrapper. + * + * It is assumed that a reference exists on snode coming into this routine. + */ +int +smb_fsop_getattr(struct smb_request *sr, cred_t *cr, smb_node_t *snode, + smb_attr_t *attr) +{ + smb_node_t *unnamed_node; + vnode_t *unnamed_vp = NULL; + caller_context_t ct; + uint32_t status; + uint32_t access = 0; + int flags = 0; + + ASSERT(cr); + ASSERT(snode); + ASSERT(snode->n_magic == SMB_NODE_MAGIC); + ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); + + if (SMB_TREE_ROOT_FS(sr, snode) == 0) + return (EACCES); + + if (sr->fid_ofile) { + /* if uid and/or gid is requested */ + if (attr->sa_mask & (SMB_AT_UID|SMB_AT_GID)) + access |= READ_CONTROL; + + /* if anything else is also requested */ + if (attr->sa_mask & ~(SMB_AT_UID|SMB_AT_GID)) + access |= FILE_READ_ATTRIBUTES; + + status = smb_ofile_access(sr->fid_ofile, cr, access); + if (status != NT_STATUS_SUCCESS) + return (EACCES); + + if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS) + flags = ATTR_NOACLCHECK; + } + + smb_get_caller_context(sr, &ct); + + unnamed_node = SMB_IS_STREAM(snode); + + if (unnamed_node) { + ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); + ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); + unnamed_vp = unnamed_node->vp; + } + + return (smb_vop_getattr(snode->vp, unnamed_vp, attr, flags, cr, &ct)); +} + +/* + * smb_fsop_readdir + * + * All SMB functions should use this smb_fsop_readdir wrapper to ensure that + * the smb_vop_readdir is performed with the appropriate credentials. + * Please document any direct call to smb_vop_readdir to explain the reason + * for avoiding this wrapper. + * + * It is assumed that a reference exists on snode coming into this routine. + */ +int +smb_fsop_readdir( + struct smb_request *sr, + cred_t *cr, + smb_node_t *dir_snode, + uint32_t *cookie, + char *name, + int *namelen, + ino64_t *fileid, + struct fs_stream_info *stream_info, + smb_node_t **ret_snode, + smb_attr_t *ret_attr) +{ + caller_context_t ct; + smb_node_t *ret_snodep; + smb_node_t *fnode; + smb_attr_t tmp_attr; + vnode_t *xattrdirvp; + vnode_t *fvp; + vnode_t *vp = NULL; + char *od_name; + int rc; + int flags = 0; + + ASSERT(cr); + ASSERT(dir_snode); + ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); + ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING); + + if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0) + return (EACCES); + + if (*cookie == SMB_EOF) { + *namelen = 0; + return (0); + } + + if (SMB_TREE_CASE_INSENSITIVE(sr)) + flags = SMB_IGNORE_CASE; + + smb_get_caller_context(sr, &ct); + + od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + if (stream_info) { + rc = smb_vop_lookup(dir_snode->vp, name, &fvp, od_name, + SMB_FOLLOW_LINKS, sr->tid_tree->t_snode->vp, cr, &ct); + + if (rc != 0) { + kmem_free(od_name, MAXNAMELEN); + return (rc); + } + + fnode = smb_node_lookup(sr, NULL, cr, fvp, od_name, dir_snode, + NULL, ret_attr); + + kmem_free(od_name, MAXNAMELEN); + + if (fnode == NULL) { + VN_RELE(fvp); + return (ENOMEM); + } + + /* + * XXX + * Need to find out what permission(s) NTFS requires for getting + * a file's streams list. + * + * Might have to use kcred. + */ + rc = smb_vop_stream_readdir(fvp, cookie, stream_info, &vp, + &xattrdirvp, flags, cr, &ct); + + if ((rc != 0) || (*cookie == SMB_EOF)) { + smb_node_release(fnode); + return (rc); + } + + ret_snodep = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp, + vp, stream_info->name, &tmp_attr); + + smb_node_release(fnode); + + if (ret_snodep == NULL) { + VN_RELE(xattrdirvp); + VN_RELE(vp); + return (ENOMEM); + } + + stream_info->size = tmp_attr.sa_vattr.va_size; + + if (ret_attr) + *ret_attr = tmp_attr; + + if (ret_snode) + *ret_snode = ret_snodep; + else + smb_node_release(ret_snodep); + + } else { + rc = smb_vop_readdir(dir_snode->vp, cookie, name, namelen, + fileid, &vp, od_name, flags, cr, &ct); + + if (rc != 0) { + kmem_free(od_name, MAXNAMELEN); + return (rc); + } + + if (*namelen) { + ASSERT(vp); + if (ret_attr || ret_snode) { + ret_snodep = smb_node_lookup(sr, NULL, cr, vp, + od_name, dir_snode, NULL, &tmp_attr); + + if (ret_snodep == NULL) { + kmem_free(od_name, MAXNAMELEN); + VN_RELE(vp); + return (ENOMEM); + } + + if (ret_attr) + *ret_attr = tmp_attr; + + if (ret_snode) + *ret_snode = ret_snodep; + else + smb_node_release(ret_snodep); + } + } + + kmem_free(od_name, MAXNAMELEN); + } + + return (rc); +} + +/* + * smb_fsop_getdents + * + * All SMB functions should use this smb_vop_getdents wrapper to ensure that + * the smb_vop_getdents is performed with the appropriate credentials. + * Please document any direct call to smb_vop_getdents to explain the reason + * for avoiding this wrapper. + * + * It is assumed that a reference exists on snode coming into this routine. + */ +/*ARGSUSED*/ +int +smb_fsop_getdents( + struct smb_request *sr, + cred_t *cr, + smb_node_t *dir_snode, + uint32_t *cookie, + uint64_t *verifierp, + int32_t *maxcnt, + char *args, + char *pattern) +{ + caller_context_t ct; + int flags = 0; + + ASSERT(cr); + ASSERT(dir_snode); + ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); + ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING); + + if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0) + return (EACCES); + + if (SMB_TREE_CASE_INSENSITIVE(sr)) + flags = SMB_IGNORE_CASE; + + smb_get_caller_context(sr, &ct); + + return (smb_vop_getdents(dir_snode, cookie, 0, maxcnt, args, pattern, + flags, sr, cr, &ct)); +} + +/* + * smb_fsop_rename + * + * All SMB functions should use this smb_vop_rename wrapper to ensure that + * the smb_vop_rename is performed with the appropriate credentials. + * Please document any direct call to smb_vop_rename to explain the reason + * for avoiding this wrapper. + * + * It is assumed that references exist on from_dir_snode and to_dir_snode coming + * into this routine. + */ +int +smb_fsop_rename( + struct smb_request *sr, + cred_t *cr, + smb_node_t *from_dir_snode, + char *from_name, + smb_node_t *to_dir_snode, + char *to_name) +{ + smb_node_t *from_snode; + caller_context_t ct; + smb_attr_t tmp_attr; + vnode_t *from_vp; + int flags = 0; + int rc; + + ASSERT(cr); + ASSERT(from_dir_snode); + ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC); + ASSERT(from_dir_snode->n_state != SMB_NODE_STATE_DESTROYING); + + ASSERT(to_dir_snode); + ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC); + ASSERT(to_dir_snode->n_state != SMB_NODE_STATE_DESTROYING); + + if (SMB_TREE_ROOT_FS(sr, from_dir_snode) == 0) + return (EACCES); + + if (SMB_TREE_ROOT_FS(sr, to_dir_snode) == 0) + return (EACCES); + + ASSERT(sr); + ASSERT(sr->tid_tree); + if (SMB_TREE_IS_READ_ONLY(sr)) + return (EROFS); + + /* + * Note: There is no need to check SMB_TREE_CASE_INSENSITIVE(sr) + * here. + * + * A case-sensitive rename is always done in this routine + * because we are using the on-disk name from an earlier lookup. + * If a mangled name was passed in by the caller (denoting a + * deterministic lookup), then the exact file must be renamed + * (i.e. SMB_IGNORE_CASE must not be passed to VOP_RENAME, or + * else the underlying file system might return a "first-match" + * on this on-disk name, possibly resulting in the wrong file). + */ + + /* + * XXX: Lock required through smb_node_release() below? + */ + + smb_get_caller_context(sr, &ct); + + rc = smb_vop_lookup(from_dir_snode->vp, from_name, &from_vp, NULL, 0, + NULL, cr, &ct); + + if (rc != 0) + return (rc); + + rc = smb_vop_rename(from_dir_snode->vp, from_name, to_dir_snode->vp, + to_name, flags, cr, &ct); + + if (rc == 0) { + from_snode = smb_node_lookup(sr, NULL, cr, from_vp, from_name, + from_dir_snode, NULL, &tmp_attr); + + if (from_snode == NULL) { + VN_RELE(from_vp); + return (ENOMEM); + } + + (void) smb_node_rename(from_dir_snode, from_snode, to_dir_snode, + to_name); + + smb_node_release(from_snode); + } else { + VN_RELE(from_vp); + } + + /* XXX: unlock */ + + return (rc); +} + +/* + * smb_fsop_setattr + * + * All SMB functions should use this wrapper to ensure that + * the the calls are performed with the appropriate credentials. + * Please document any direct call to explain the reason + * for avoiding this wrapper. + * + * It is assumed that a reference exists on snode coming into this routine. + * A null smb_request might be passed to this function. + */ +int +smb_fsop_setattr( + smb_request_t *sr, + cred_t *cr, + smb_node_t *snode, + smb_attr_t *set_attr, + smb_attr_t *ret_attr) +{ + smb_node_t *unnamed_node; + vnode_t *unnamed_vp = NULL; + caller_context_t ct; + uint32_t status; + uint32_t access = 0; + int rc = 0; + int flags = 0; + + ASSERT(cr); + ASSERT(snode); + ASSERT(snode->n_magic == SMB_NODE_MAGIC); + ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); + + if (SMB_TREE_ROOT_FS(sr, snode) == 0) + return (EACCES); + + if (SMB_TREE_IS_READ_ONLY(sr)) + return (EROFS); + + /* sr could be NULL in some cases */ + if (sr && sr->fid_ofile) { + /* if uid and/or gid is requested */ + if (set_attr->sa_mask & (SMB_AT_UID|SMB_AT_GID)) + access |= WRITE_OWNER; + + /* if anything else is also requested */ + if (set_attr->sa_mask & ~(SMB_AT_UID|SMB_AT_GID)) + access |= FILE_WRITE_ATTRIBUTES; + + status = smb_ofile_access(sr->fid_ofile, cr, access); + if (status != NT_STATUS_SUCCESS) + return (EACCES); + + if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS) + flags = ATTR_NOACLCHECK; + } + + smb_get_caller_context(sr, &ct); + + unnamed_node = SMB_IS_STREAM(snode); + + if (unnamed_node) { + ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); + ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); + unnamed_vp = unnamed_node->vp; + } + + rc = smb_vop_setattr(snode->vp, unnamed_vp, set_attr, flags, cr, &ct); + + if ((rc == 0) && ret_attr) { + /* + * This is an operation on behalf of CIFS service (to update + * smb node's attr) not on behalf of the user so it's done + * using kcred and the return value is intentionally ignored. + */ + ret_attr->sa_mask = SMB_AT_ALL; + (void) smb_vop_getattr(snode->vp, unnamed_vp, ret_attr, 0, + kcred, &ct); + } + + return (rc); +} + +/* + * smb_fsop_read + * + * All SMB functions should use this wrapper to ensure that + * the the calls are performed with the appropriate credentials. + * Please document any direct call to explain the reason + * for avoiding this wrapper. + * + * It is assumed that a reference exists on snode coming into this routine. + */ +int +smb_fsop_read( + struct smb_request *sr, + cred_t *cr, + smb_node_t *snode, + uio_t *uio, + smb_attr_t *ret_attr) +{ + smb_node_t *unnamed_node; + vnode_t *unnamed_vp = NULL; + caller_context_t ct; + int rc; + + ASSERT(cr); + ASSERT(snode); + ASSERT(snode->n_magic == SMB_NODE_MAGIC); + ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); + + ASSERT(sr); + ASSERT(sr->fid_ofile); + + rc = smb_ofile_access(sr->fid_ofile, cr, FILE_READ_DATA); + if (rc != NT_STATUS_SUCCESS) { + rc = smb_ofile_access(sr->fid_ofile, cr, FILE_EXECUTE); + if (rc != NT_STATUS_SUCCESS) + return (EACCES); + } + + unnamed_node = SMB_IS_STREAM(snode); + if (unnamed_node) { + ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); + ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); + unnamed_vp = unnamed_node->vp; + /* + * Streams permission are checked against the unnamed stream, + * but in FS level they have their own permissions. To avoid + * rejection by FS due to lack of permission on the actual + * extended attr kcred is passed for streams. + */ + cr = kcred; + } + + smb_get_caller_context(sr, &ct); + rc = smb_vop_read(snode->vp, uio, cr, &ct); + + if (rc == 0) { + /* + * This is an operation on behalf of CIFS service (to update + * smb node's attr) not on behalf of the user so it's done + * using kcred and the return value is intentionally ignored. + */ + ret_attr->sa_mask = SMB_AT_ALL; + (void) smb_vop_getattr(snode->vp, unnamed_vp, ret_attr, 0, + kcred, &ct); + } + + return (rc); +} + +/* + * smb_fsop_write + * + * This is a wrapper function used for smb_write and smb_write_raw operations. + * + * It is assumed that a reference exists on snode coming into this routine. + */ +int +smb_fsop_write( + struct smb_request *sr, + cred_t *cr, + smb_node_t *snode, + uio_t *uio, + uint32_t *lcount, + smb_attr_t *ret_attr, + uint32_t *flag) +{ + smb_node_t *unnamed_node; + vnode_t *unnamed_vp = NULL; + caller_context_t ct; + int rc; + + ASSERT(cr); + ASSERT(snode); + ASSERT(snode->n_magic == SMB_NODE_MAGIC); + ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); + + ASSERT(sr); + ASSERT(sr->tid_tree); + ASSERT(sr->fid_ofile); + + if (SMB_TREE_IS_READ_ONLY(sr)) + return (EROFS); + /* + * XXX what if the file has been opened only with + * FILE_APPEND_DATA? + */ + rc = smb_ofile_access(sr->fid_ofile, cr, FILE_WRITE_DATA); + if (rc != NT_STATUS_SUCCESS) + return (EACCES); + + smb_get_caller_context(sr, &ct); + + unnamed_node = SMB_IS_STREAM(snode); + + if (unnamed_node) { + ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); + ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); + unnamed_vp = unnamed_node->vp; + /* + * Streams permission are checked against the unnamed stream, + * but in FS level they have their own permissions. To avoid + * rejection by FS due to lack of permission on the actual + * extended attr kcred is passed for streams. + */ + cr = kcred; + } + + rc = smb_vop_write(snode->vp, uio, flag, lcount, cr, &ct); + + if (rc == 0) { + /* + * This is an operation on behalf of CIFS service (to update + * smb node's attr) not on behalf of the user so it's done + * using kcred and the return value is intentionally ignored. + */ + ret_attr->sa_mask = SMB_AT_ALL; + (void) smb_vop_getattr(snode->vp, unnamed_vp, ret_attr, 0, + kcred, &ct); + } + + return (rc); +} + +/* + * smb_fsop_statfs + * + * This is a wrapper function used for stat operations. + */ +int +smb_fsop_statfs( + cred_t *cr, + smb_node_t *snode, + struct statvfs64 *statp) +{ + ASSERT(cr); + ASSERT(snode); + ASSERT(snode->n_magic == SMB_NODE_MAGIC); + ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); + + return (smb_vop_statfs(snode->vp, statp, cr)); +} + +/* + * smb_fsop_access + */ +int +smb_fsop_access(smb_request_t *sr, cred_t *cr, smb_node_t *snode, + uint32_t faccess) +{ + int access = 0; + int error; + vnode_t *dir_vp; + boolean_t acl_check = B_TRUE; + smb_node_t *unnamed_node; + + ASSERT(cr); + ASSERT(snode); + ASSERT(snode->n_magic == SMB_NODE_MAGIC); + ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); + + if (faccess == 0) + return (NT_STATUS_SUCCESS); + + if (SMB_TREE_IS_READ_ONLY(sr)) { + if (faccess & (FILE_WRITE_DATA|FILE_APPEND_DATA| + FILE_WRITE_EA|FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES| + DELETE|WRITE_DAC|WRITE_OWNER)) { + return (NT_STATUS_ACCESS_DENIED); + } + } + + unnamed_node = SMB_IS_STREAM(snode); + if (unnamed_node) { + ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); + ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); + /* + * Streams authorization should be performed against the + * unnamed stream. + */ + snode = unnamed_node; + } + + if (faccess & ACCESS_SYSTEM_SECURITY) { + /* + * This permission is required for reading/writing SACL and + * it's not part of DACL. It's only granted via proper + * privileges. + */ + if ((sr->uid_user->u_privileges & + (SMB_USER_PRIV_BACKUP | + SMB_USER_PRIV_RESTORE | + SMB_USER_PRIV_SECURITY)) == 0) + return (NT_STATUS_PRIVILEGE_NOT_HELD); + + faccess &= ~ACCESS_SYSTEM_SECURITY; + } + + /* Links don't have ACL */ + if (((sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS) == 0) || + (snode->attr.sa_vattr.va_type == VLNK)) + acl_check = B_FALSE; + + if (acl_check) { + dir_vp = (snode->dir_snode) ? snode->dir_snode->vp : NULL; + error = smb_vop_access(snode->vp, faccess, V_ACE_MASK, dir_vp, + cr); + } else { + /* + * FS doesn't understand 32-bit mask, need to map + */ + if (faccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)) + access |= VWRITE; + + if (faccess & FILE_READ_DATA) + access |= VREAD; + + if (faccess & FILE_EXECUTE) + access |= VEXEC; + + error = smb_vop_access(snode->vp, access, 0, NULL, cr); + } + + return ((error) ? NT_STATUS_ACCESS_DENIED : NT_STATUS_SUCCESS); +} + +/* + * smb_fsop_lookup_name() + * + * Sanity checks on dir_snode done in smb_fsop_lookup(). + * + * Note: This function is called only from the open path. + * It will check if the file is a stream. + * It will also return an error if the looked-up file is in + * a child mount. + */ + +int +smb_fsop_lookup_name( + struct smb_request *sr, + cred_t *cr, + int flags, + smb_node_t *root_node, + smb_node_t *dir_snode, + char *name, + smb_node_t **ret_snode, + smb_attr_t *ret_attr) +{ + smb_node_t *fnode; + smb_attr_t file_attr; + caller_context_t ct; + vnode_t *xattrdirvp; + vnode_t *vp; + char *od_name; + char *fname; + char *sname; + int rc; + + ASSERT(cr); + ASSERT(dir_snode); + ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); + ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING); + + /* + * The following check is required for streams processing, below + */ + + if (SMB_TREE_CASE_INSENSITIVE(sr)) + flags |= SMB_IGNORE_CASE; + + fname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + sname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + if (smb_stream_parse_name(name, fname, sname)) { + /* + * Look up the unnamed stream (i.e. fname). + * Unmangle processing will be done on fname + * as well as any link target. + */ + rc = smb_fsop_lookup(sr, cr, flags, root_node, dir_snode, fname, + &fnode, &file_attr, NULL, NULL); + + if (rc != 0) { + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + return (rc); + } + + od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + /* + * od_name is the on-disk name of the stream, except + * without the prepended stream prefix (SMB_STREAM_PREFIX) + */ + + /* + * XXX + * What permissions NTFS requires for stream lookup if any? + */ + rc = smb_vop_stream_lookup(fnode->vp, sname, &vp, od_name, + &xattrdirvp, flags, root_node->vp, cr, &ct); + + if (rc != 0) { + smb_node_release(fnode); + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + kmem_free(od_name, MAXNAMELEN); + return (rc); + } + + *ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp, + vp, od_name, ret_attr); + + kmem_free(od_name, MAXNAMELEN); + smb_node_release(fnode); + + if (*ret_snode == NULL) { + VN_RELE(xattrdirvp); + VN_RELE(vp); + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + return (ENOMEM); + } + } else { + rc = smb_fsop_lookup(sr, cr, flags, root_node, dir_snode, name, + ret_snode, ret_attr, NULL, NULL); + } + + if (rc == 0) { + ASSERT(ret_snode); + if (SMB_TREE_ROOT_FS(sr, *ret_snode) == 0) { + smb_node_release(*ret_snode); + *ret_snode = NULL; + rc = EACCES; + } + } + + kmem_free(fname, MAXNAMELEN); + kmem_free(sname, MAXNAMELEN); + + return (rc); +} + +/* + * smb_fsop_lookup + * + * All SMB functions should use this smb_vop_lookup wrapper to ensure that + * the smb_vop_lookup is performed with the appropriate credentials and using + * case insensitive compares. Please document any direct call to smb_vop_lookup + * to explain the reason for avoiding this wrapper. + * + * It is assumed that a reference exists on dir_snode coming into this routine + * (and that it is safe from deallocation). + * + * Same with the root_node. + * + * *ret_snode is returned with a reference upon success. No reference is + * taken if an error is returned. + * + * Note: The returned ret_snode may be in a child mount. This is ok for + * readdir and getdents. + * + * Other smb_fsop_* routines will call SMB_TREE_ROOT_FS() to prevent + * operations on files not in the parent mount. + */ +int +smb_fsop_lookup( + struct smb_request *sr, + cred_t *cr, + int flags, + smb_node_t *root_node, + smb_node_t *dir_snode, + char *name, + smb_node_t **ret_snode, + smb_attr_t *ret_attr, + char *ret_shortname, /* Must be at least MANGLE_NAMELEN chars */ + char *ret_name83) /* Must be at least MANGLE_NAMELEN chars */ +{ + smb_node_t *lnk_target_node; + smb_node_t *lnk_dnode; + caller_context_t ct; + char *longname; + char *od_name; + vnode_t *vp; + int rc; + + ASSERT(cr); + ASSERT(dir_snode); + ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC); + ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING); + + if (name == NULL) + return (EINVAL); + + if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0) + return (EACCES); + + if (SMB_TREE_CASE_INSENSITIVE(sr)) + flags |= SMB_IGNORE_CASE; + + smb_get_caller_context(sr, &ct); + + od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + rc = smb_vop_lookup(dir_snode->vp, name, &vp, od_name, flags, + root_node ? root_node->vp : NULL, cr, &ct); + + if (rc != 0) { + if (smb_maybe_mangled_name(name) == 0) { + kmem_free(od_name, MAXNAMELEN); + return (rc); + } + + longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + rc = smb_unmangle_name(sr, cr, dir_snode, name, longname, + MAXNAMELEN, ret_shortname, ret_name83, 1); + + if (rc != 0) { + kmem_free(od_name, MAXNAMELEN); + kmem_free(longname, MAXNAMELEN); + return (rc); + } + + /* + * We passed "1" as the "od" parameter + * to smb_unmangle_name(), such that longname + * is the real (case-sensitive) on-disk name. + * We make sure we do a lookup on this exact + * name, as the name was mangled and denotes + * a unique file. + */ + + if (flags & SMB_IGNORE_CASE) + flags &= ~SMB_IGNORE_CASE; + + rc = smb_vop_lookup(dir_snode->vp, longname, &vp, od_name, + flags, root_node ? root_node->vp : NULL, cr, &ct); + + kmem_free(longname, MAXNAMELEN); + + if (rc != 0) { + kmem_free(od_name, MAXNAMELEN); + return (rc); + } + } + + if ((flags & SMB_FOLLOW_LINKS) && (vp->v_type == VLNK)) { + + rc = smb_pathname(sr, od_name, FOLLOW, root_node, dir_snode, + &lnk_dnode, &lnk_target_node, cr); + + if (rc != 0) { + /* + * The link is assumed to be for the last component + * of a path. Hence any ENOTDIR error will be returned + * as ENOENT. + */ + if (rc == ENOTDIR) + rc = ENOENT; + + VN_RELE(vp); + kmem_free(od_name, MAXNAMELEN); + return (rc); + } + + /* + * Release the original VLNK vnode + */ + + VN_RELE(vp); + vp = lnk_target_node->vp; + + rc = smb_vop_traverse_check(&vp); + + if (rc != 0) { + smb_node_release(lnk_dnode); + smb_node_release(lnk_target_node); + kmem_free(od_name, MAXNAMELEN); + return (rc); + } + + /* + * smb_vop_traverse_check() may have returned a different vnode + */ + + if (lnk_target_node->vp == vp) { + *ret_snode = lnk_target_node; + *ret_attr = (*ret_snode)->attr; + } else { + *ret_snode = smb_node_lookup(sr, NULL, cr, vp, + lnk_target_node->od_name, lnk_dnode, NULL, + ret_attr); + + if (*ret_snode == NULL) { + VN_RELE(vp); + rc = ENOMEM; + } + smb_node_release(lnk_target_node); + } + + smb_node_release(lnk_dnode); + + } else { + + rc = smb_vop_traverse_check(&vp); + if (rc) { + VN_RELE(vp); + kmem_free(od_name, MAXNAMELEN); + return (rc); + } + + *ret_snode = smb_node_lookup(sr, NULL, cr, vp, od_name, + dir_snode, NULL, ret_attr); + + if (*ret_snode == NULL) { + VN_RELE(vp); + rc = ENOMEM; + } + } + + kmem_free(od_name, MAXNAMELEN); + return (rc); +} + +/* + * smb_fsop_stream_readdir() + * + * ret_snode and ret_attr are optional parameters (i.e. NULL may be passed in) + * + * This routine will return only NTFS streams. If an NTFS stream is not + * found at the offset specified, the directory will be read until an NTFS + * stream is found or until EOF. + * + * Note: Sanity checks done in caller + * (smb_fsop_readdir(), smb_fsop_remove_streams()) + */ + +int +smb_fsop_stream_readdir(struct smb_request *sr, cred_t *cr, smb_node_t *fnode, + uint32_t *cookiep, struct fs_stream_info *stream_info, + smb_node_t **ret_snode, smb_attr_t *ret_attr) +{ + smb_node_t *ret_snodep = NULL; + caller_context_t ct; + smb_attr_t tmp_attr; + vnode_t *xattrdirvp; + vnode_t *vp; + int rc = 0; + int flags = 0; + + /* + * XXX NTFS permission requirements if any? + */ + ASSERT(cr); + ASSERT(fnode); + ASSERT(fnode->n_magic == SMB_NODE_MAGIC); + ASSERT(fnode->n_state != SMB_NODE_STATE_DESTROYING); + + if (SMB_TREE_CASE_INSENSITIVE(sr)) + flags = SMB_IGNORE_CASE; + + smb_get_caller_context(sr, &ct); + + rc = smb_vop_stream_readdir(fnode->vp, cookiep, stream_info, &vp, + &xattrdirvp, flags, cr, &ct); + + if ((rc != 0) || *cookiep == SMB_EOF) + return (rc); + + ret_snodep = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp, vp, + stream_info->name, &tmp_attr); + + if (ret_snodep == NULL) { + VN_RELE(xattrdirvp); + VN_RELE(vp); + return (ENOMEM); + } + + stream_info->size = tmp_attr.sa_vattr.va_size; + + if (ret_attr) + *ret_attr = tmp_attr; + + if (ret_snode) + *ret_snode = ret_snodep; + else + smb_node_release(ret_snodep); + + return (rc); +} + +int /*ARGSUSED*/ +smb_fsop_commit(smb_request_t *sr, cred_t *cr, smb_node_t *snode) +{ + caller_context_t ct; + + ASSERT(cr); + ASSERT(snode); + ASSERT(snode->n_magic == SMB_NODE_MAGIC); + ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); + + ASSERT(sr); + ASSERT(sr->tid_tree); + if (SMB_TREE_IS_READ_ONLY(sr)) + return (EROFS); + + smb_get_caller_context(sr, &ct); + + return (smb_vop_commit(snode->vp, cr, &ct)); +} + +/* + * smb_fsop_sdinit + * + * Initializes the given FS SD structure. + */ +void +smb_fsop_sdinit(smb_fssd_t *fs_sd, uint32_t secinfo, uint32_t flags) +{ + bzero(fs_sd, sizeof (smb_fssd_t)); + fs_sd->sd_secinfo = secinfo; + fs_sd->sd_flags = flags; +} + +/* + * smb_fsop_sdterm + * + * Frees allocated memory for acl fields. + */ +void +smb_fsop_sdterm(smb_fssd_t *fs_sd) +{ + ASSERT(fs_sd); + + smb_fsop_aclfree(fs_sd->sd_zdacl); + smb_fsop_aclfree(fs_sd->sd_zsacl); + bzero(fs_sd, sizeof (smb_fssd_t)); +} + +/* + * smb_fsop_aclread + * + * Retrieve filesystem ACL. Depends on requested ACLs in + * fs_sd->sd_secinfo, it'll set DACL and SACL pointers in + * fs_sd. Note that requesting a DACL/SACL doesn't mean that + * the corresponding field in fs_sd should be non-NULL upon + * return, since the target ACL might not contain that type of + * entries. + * + * Returned ACL is always in ACE_T (aka ZFS) format. + * If successful the allocated memory for the ACL should be freed + * using smb_fsop_aclfree() or smb_fsop_sdterm() + */ +int +smb_fsop_aclread(smb_request_t *sr, cred_t *cr, smb_node_t *snode, + smb_fssd_t *fs_sd) +{ + int error = 0; + int flags = 0; + int access = 0; + acl_t *acl; + caller_context_t ct; + smb_node_t *unnamed_node; + + ASSERT(cr); + + if (sr->fid_ofile) { + if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) + access = READ_CONTROL; + + if (fs_sd->sd_secinfo & SMB_SACL_SECINFO) + access |= ACCESS_SYSTEM_SECURITY; + + error = smb_ofile_access(sr->fid_ofile, cr, access); + if (error != NT_STATUS_SUCCESS) { + return (EACCES); + } + } + + unnamed_node = SMB_IS_STREAM(snode); + if (unnamed_node) { + ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); + ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); + /* + * Streams don't have ACL, any read ACL attempt on a stream + * should be performed on the unnamed stream. + */ + snode = unnamed_node; + } + + if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS) + flags = ATTR_NOACLCHECK; + + smb_get_caller_context(sr, &ct); + error = smb_vop_acl_read(snode->vp, &acl, flags, + sr->tid_tree->t_acltype, cr, &ct); + if (error != 0) { + return (error); + } + + error = acl_translate(acl, _ACL_ACE_ENABLED, + (snode->vp->v_type == VDIR), fs_sd->sd_uid, fs_sd->sd_gid); + + if (error == 0) { + smb_fsop_aclsplit(acl, &fs_sd->sd_zdacl, &fs_sd->sd_zsacl, + fs_sd->sd_secinfo); + } + + acl_free(acl); + return (error); +} + +/* + * smb_fsop_aclwrite + * + * Stores the filesystem ACL provided in fs_sd->sd_acl. + */ +int +smb_fsop_aclwrite(smb_request_t *sr, cred_t *cr, smb_node_t *snode, + smb_fssd_t *fs_sd) +{ + int target_flavor; + int error = 0; + int flags = 0; + int access = 0; + caller_context_t ct; + acl_t *acl, *dacl, *sacl; + smb_node_t *unnamed_node; + + ASSERT(cr); + + ASSERT(sr); + ASSERT(sr->tid_tree); + if (SMB_TREE_IS_READ_ONLY(sr)) + return (EROFS); + + if (sr->fid_ofile) { + if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) + access = WRITE_DAC; + + if (fs_sd->sd_secinfo & SMB_SACL_SECINFO) + access |= ACCESS_SYSTEM_SECURITY; + + error = smb_ofile_access(sr->fid_ofile, cr, access); + if (error != NT_STATUS_SUCCESS) + return (EACCES); + } + + switch (sr->tid_tree->t_acltype) { + case ACLENT_T: + target_flavor = _ACL_ACLENT_ENABLED; + break; + + case ACE_T: + target_flavor = _ACL_ACE_ENABLED; + break; + default: + return (EINVAL); + } + + unnamed_node = SMB_IS_STREAM(snode); + if (unnamed_node) { + ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); + ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); + /* + * Streams don't have ACL, any write ACL attempt on a stream + * should be performed on the unnamed stream. + */ + snode = unnamed_node; + } + + dacl = fs_sd->sd_zdacl; + sacl = fs_sd->sd_zsacl; + + ASSERT(dacl || sacl); + if ((dacl == NULL) && (sacl == NULL)) + return (EINVAL); + + if (dacl && sacl) + acl = smb_fsop_aclmerge(dacl, sacl); + else if (dacl) + acl = dacl; + else + acl = sacl; + + error = acl_translate(acl, target_flavor, (snode->vp->v_type == VDIR), + fs_sd->sd_uid, fs_sd->sd_gid); + if (error == 0) { + smb_get_caller_context(sr, &ct); + if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS) + flags = ATTR_NOACLCHECK; + + error = smb_vop_acl_write(snode->vp, acl, flags, cr, &ct); + } + + if (dacl && sacl) + acl_free(acl); + + return (error); +} + +acl_t * +smb_fsop_aclalloc(int acenum, int flags) +{ + acl_t *acl; + + acl = acl_alloc(ACE_T); + acl->acl_cnt = acenum; + acl->acl_aclp = kmem_zalloc(acl->acl_entry_size * acenum, KM_SLEEP); + acl->acl_flags = flags; + return (acl); +} + +void +smb_fsop_aclfree(acl_t *acl) +{ + if (acl) + acl_free(acl); +} + +/* + * smb_fsop_aclmerge + * + * smb_fsop_aclread/write routines which interact with filesystem + * work with single ACL. This routine merges given DACL and SACL + * which might have been created during CIFS to FS conversion into + * one single ACL. + */ +static acl_t * +smb_fsop_aclmerge(acl_t *dacl, acl_t *sacl) +{ + acl_t *acl; + int dacl_size; + + ASSERT(dacl); + ASSERT(sacl); + + acl = smb_fsop_aclalloc(dacl->acl_cnt + sacl->acl_cnt, dacl->acl_flags); + dacl_size = dacl->acl_cnt * dacl->acl_entry_size; + bcopy(dacl->acl_aclp, acl->acl_aclp, dacl_size); + bcopy(sacl->acl_aclp, (char *)acl->acl_aclp + dacl_size, + sacl->acl_cnt * sacl->acl_entry_size); + + return (acl); +} + +/* + * smb_fsop_aclsplit + * + * splits the given ACE_T ACL (zacl) to one or two ACLs (DACL/SACL) based on + * the 'which_acl' parameter. Note that output dacl/sacl parameters could be + * NULL even if they're specified in 'which_acl', which means the target + * doesn't have any access and/or audit ACEs. + */ +static void +smb_fsop_aclsplit(acl_t *zacl, acl_t **dacl, acl_t **sacl, int which_acl) +{ + ace_t *zace; + ace_t *access_ace; + ace_t *audit_ace; + int naccess, naudit; + int get_dacl, get_sacl; + int i; + + *dacl = *sacl = NULL; + naccess = naudit = 0; + get_dacl = (which_acl & SMB_DACL_SECINFO); + get_sacl = (which_acl & SMB_SACL_SECINFO); + + for (i = 0, zace = zacl->acl_aclp; i < zacl->acl_cnt; zace++, i++) { + if (get_dacl && smb_ace_is_access(zace->a_type)) + naccess++; + else if (get_sacl && smb_ace_is_audit(zace->a_type)) + naudit++; + } + + if (naccess) { + *dacl = smb_fsop_aclalloc(naccess, zacl->acl_flags); + access_ace = (*dacl)->acl_aclp; + } + + if (naudit) { + *sacl = smb_fsop_aclalloc(naudit, zacl->acl_flags); + audit_ace = (*sacl)->acl_aclp; + } + + for (i = 0, zace = zacl->acl_aclp; i < zacl->acl_cnt; zace++, i++) { + if (get_dacl && smb_ace_is_access(zace->a_type)) { + *access_ace = *zace; + access_ace++; + } else if (get_sacl && smb_ace_is_audit(zace->a_type)) { + *audit_ace = *zace; + audit_ace++; + } + } +} + +acl_type_t +smb_fsop_acltype(smb_node_t *snode) +{ + return (smb_vop_acl_type(snode->vp)); +} + +/* + * smb_fsop_sdread + * + * Read the requested security descriptor items from filesystem. + * The items are specified in fs_sd->sd_secinfo. + */ +int +smb_fsop_sdread(smb_request_t *sr, cred_t *cr, smb_node_t *snode, + smb_fssd_t *fs_sd) +{ + int error = 0; + int getowner = 0; + cred_t *ga_cred; + smb_attr_t attr; + + ASSERT(cr); + ASSERT(fs_sd); + + /* + * File's uid/gid is fetched in two cases: + * + * 1. it's explicitly requested + * + * 2. target ACL is ACE_T (ZFS ACL). They're needed for + * owner@/group@ entries. In this case kcred should be used + * because uid/gid are fetched on behalf of smb server. + */ + if (fs_sd->sd_secinfo & (SMB_OWNER_SECINFO | SMB_GROUP_SECINFO)) { + getowner = 1; + ga_cred = cr; + } else if (sr->tid_tree->t_acltype == ACE_T) { + getowner = 1; + ga_cred = kcred; + } + + if (getowner) { + /* + * Windows require READ_CONTROL to read owner/group SID since + * they're part of Security Descriptor. + * ZFS only requires read_attribute. Need to have a explicit + * access check here. + */ + if (sr->fid_ofile == NULL) { + error = smb_fsop_access(sr, ga_cred, snode, + READ_CONTROL); + if (error) + return (error); + } + + attr.sa_mask = SMB_AT_UID | SMB_AT_GID; + error = smb_fsop_getattr(sr, ga_cred, snode, &attr); + if (error == 0) { + fs_sd->sd_uid = attr.sa_vattr.va_uid; + fs_sd->sd_gid = attr.sa_vattr.va_gid; + } else { + return (error); + } + } + + if (fs_sd->sd_secinfo & SMB_ACL_SECINFO) { + error = smb_fsop_aclread(sr, cr, snode, fs_sd); + } + + return (error); +} + +/* + * smb_fsop_sdmerge + * + * From SMB point of view DACL and SACL are two separate list + * which can be manipulated independently without one affecting + * the other, but entries for both DACL and SACL will end up + * in the same ACL if target filesystem supports ACE_T ACLs. + * + * So, if either DACL or SACL is present in the client set request + * the entries corresponding to the non-present ACL shouldn't + * be touched in the FS ACL. + * + * fs_sd parameter contains DACL and SACL specified by SMB + * client to be set on a file/directory. The client could + * specify both or one of these ACLs (if none is specified + * we don't get this far). When both DACL and SACL are given + * by client the existing ACL should be overwritten. If only + * one of them is specified the entries corresponding to the other + * ACL should not be touched. For example, if only DACL + * is specified in input fs_sd, the function reads audit entries + * of the existing ACL of the file and point fs_sd->sd_zsdacl + * pointer to the fetched SACL, this way when smb_fsop_sdwrite() + * function is called the passed fs_sd would point to the specified + * DACL by client and fetched SACL from filesystem, so the file + * will end up with correct ACL. + */ +static int +smb_fsop_sdmerge(smb_request_t *sr, smb_node_t *snode, smb_fssd_t *fs_sd) +{ + smb_fssd_t cur_sd; + int error = 0; + + if (sr->tid_tree->t_acltype != ACE_T) + /* Don't bother if target FS doesn't support ACE_T */ + return (0); + + if ((fs_sd->sd_secinfo & SMB_ACL_SECINFO) != SMB_ACL_SECINFO) { + if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) { + /* + * Don't overwrite existing audit entries + */ + smb_fsop_sdinit(&cur_sd, SMB_SACL_SECINFO, + fs_sd->sd_flags); + + error = smb_fsop_sdread(sr, kcred, snode, &cur_sd); + if (error == 0) { + ASSERT(fs_sd->sd_zsacl == NULL); + fs_sd->sd_zsacl = cur_sd.sd_zsacl; + if (fs_sd->sd_zsacl && fs_sd->sd_zdacl) + fs_sd->sd_zsacl->acl_flags = + fs_sd->sd_zdacl->acl_flags; + } + } else { + /* + * Don't overwrite existing access entries + */ + smb_fsop_sdinit(&cur_sd, SMB_DACL_SECINFO, + fs_sd->sd_flags); + + error = smb_fsop_sdread(sr, kcred, snode, &cur_sd); + if (error == 0) { + ASSERT(fs_sd->sd_zdacl == NULL); + fs_sd->sd_zdacl = cur_sd.sd_zdacl; + if (fs_sd->sd_zdacl && fs_sd->sd_zsacl) + fs_sd->sd_zdacl->acl_flags = + fs_sd->sd_zsacl->acl_flags; + } + } + + if (error) + smb_fsop_sdterm(&cur_sd); + } + + return (error); +} + +/* + * smb_fsop_sdwrite + * + * Stores the given uid, gid and acl in filesystem. + * Provided items in fs_sd are specified by fs_sd->sd_secinfo. + * + * A SMB security descriptor could contain owner, primary group, + * DACL and SACL. Setting an SD should be atomic but here it has to + * be done via two separate FS operations: VOP_SETATTR and + * VOP_SETSECATTR. Therefore, this function has to simulate the + * atomicity as well as it can. + */ +int +smb_fsop_sdwrite(smb_request_t *sr, cred_t *cr, smb_node_t *snode, + smb_fssd_t *fs_sd, int overwrite) +{ + int error = 0; + int access = 0; + smb_attr_t set_attr; + smb_attr_t orig_attr; + + ASSERT(cr); + ASSERT(fs_sd); + + ASSERT(sr); + ASSERT(sr->tid_tree); + if (SMB_TREE_IS_READ_ONLY(sr)) + return (EROFS); + + bzero(&set_attr, sizeof (smb_attr_t)); + + if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) { + set_attr.sa_vattr.va_uid = fs_sd->sd_uid; + set_attr.sa_mask |= SMB_AT_UID; + } + + if (fs_sd->sd_secinfo & SMB_GROUP_SECINFO) { + set_attr.sa_vattr.va_gid = fs_sd->sd_gid; + set_attr.sa_mask |= SMB_AT_GID; + } + + if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) + access |= WRITE_DAC; + + if (fs_sd->sd_secinfo & SMB_SACL_SECINFO) + access |= ACCESS_SYSTEM_SECURITY; + + if (sr->fid_ofile) + error = smb_ofile_access(sr->fid_ofile, cr, access); + else + error = smb_fsop_access(sr, cr, snode, access); + + if (error) + return (EACCES); + + if (set_attr.sa_mask) { + /* + * Get the current uid, gid so if smb_fsop_aclwrite fails + * we can revert uid, gid changes. + * + * We use root cred here so the operation doesn't fail + * due to lack of permission for the user to read the attrs + */ + + orig_attr.sa_mask = SMB_AT_UID | SMB_AT_GID; + error = smb_fsop_getattr(sr, kcred, snode, &orig_attr); + if (error == 0) + error = smb_fsop_setattr(sr, cr, snode, &set_attr, + NULL); + + if (error) + return (error); + } + + if (fs_sd->sd_secinfo & SMB_ACL_SECINFO) { + if (overwrite == 0) { + error = smb_fsop_sdmerge(sr, snode, fs_sd); + if (error) + return (error); + } + + error = smb_fsop_aclwrite(sr, cr, snode, fs_sd); + if (error) { + /* + * Revert uid/gid changes if required. + */ + if (set_attr.sa_mask) { + orig_attr.sa_mask = set_attr.sa_mask; + (void) smb_fsop_setattr(sr, kcred, snode, + &orig_attr, NULL); + } + } + } + + return (error); +} + +/*ARGSUSED*/ +void +smb_get_caller_context(smb_request_t *sr, caller_context_t *ct) +{ + ct->cc_caller_id = smb_caller_id; + ct->cc_pid = 0; /* TBD */ + ct->cc_sysid = 0; /* TBD */ +} + +/* + * smb_fsop_sdinherit + * + * Inherit the security descriptor from the parent container. + * This function is called after FS has created the file/folder + * so if this doesn't do anything it means FS inheritance is + * in place. + * + * Do inheritance for ZFS internally. + * + * If we want to let ZFS does the inheritance the + * following setting should be true: + * + * - aclinherit = passthrough + * - aclmode = passthrough + * - smbd umask = 0777 + * + * This will result in right effective permissions but + * ZFS will always add 6 ACEs for owner, owning group + * and others to be POSIX compliant. This is not what + * Windows clients/users expect, so we decided that CIFS + * implements Windows rules and overwrite whatever ZFS + * comes up with. This way we also don't have to care + * about ZFS aclinherit and aclmode settings. + */ +static int +smb_fsop_sdinherit(smb_request_t *sr, smb_node_t *dnode, smb_fssd_t *fs_sd) +{ + int is_dir; + acl_t *dacl; + acl_t *sacl; + ksid_t *owner_sid; + int error; + + ASSERT(fs_sd); + + if (sr->tid_tree->t_acltype != ACE_T) { + /* + * No forced inheritance for non-ZFS filesystems. + */ + fs_sd->sd_secinfo = 0; + return (0); + } + + + /* Fetch parent directory's ACL */ + error = smb_fsop_sdread(sr, kcred, dnode, fs_sd); + if (error) { + return (error); + } + + is_dir = (fs_sd->sd_flags & SMB_FSSD_FLAGS_DIR); + owner_sid = crgetsid(sr->user_cr, KSID_OWNER); + ASSERT(owner_sid); + dacl = smb_acl_inherit(fs_sd->sd_zdacl, is_dir, SMB_DACL_SECINFO, + owner_sid->ks_id); + sacl = smb_acl_inherit(fs_sd->sd_zsacl, is_dir, SMB_SACL_SECINFO, + (uid_t)-1); + + smb_fsop_aclfree(fs_sd->sd_zdacl); + smb_fsop_aclfree(fs_sd->sd_zsacl); + + fs_sd->sd_zdacl = dacl; + fs_sd->sd_zsacl = sacl; + + return (0); +} + +/* + * smb_fsop_eaccess + * + * Returns the effective permission of the given credential for the + * specified object. + * + * This is just a workaround. We need VFS/FS support for this. + */ +void +smb_fsop_eaccess(smb_request_t *sr, cred_t *cr, smb_node_t *snode, + uint32_t *eaccess) +{ + int access = 0; + vnode_t *dir_vp; + smb_node_t *unnamed_node; + + ASSERT(cr); + ASSERT(snode); + ASSERT(snode->n_magic == SMB_NODE_MAGIC); + ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); + + unnamed_node = SMB_IS_STREAM(snode); + if (unnamed_node) { + ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); + ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); + /* + * Streams authorization should be performed against the + * unnamed stream. + */ + snode = unnamed_node; + } + + if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS) { + dir_vp = (snode->dir_snode) ? snode->dir_snode->vp : NULL; + smb_vop_eaccess(snode->vp, (int *)eaccess, V_ACE_MASK, dir_vp, + cr); + return; + } + + /* + * FS doesn't understand 32-bit mask + */ + smb_vop_eaccess(snode->vp, &access, 0, NULL, cr); + + *eaccess = READ_CONTROL | FILE_READ_EA | FILE_READ_ATTRIBUTES; + + if (access & VREAD) + *eaccess |= FILE_READ_DATA; + + if (access & VEXEC) + *eaccess |= FILE_EXECUTE; + + if (access & VWRITE) + *eaccess |= FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | + FILE_WRITE_EA | FILE_APPEND_DATA | FILE_DELETE_CHILD; +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_init.c b/usr/src/uts/common/fs/smbsrv/smb_init.c new file mode 100644 index 000000000000..1d3d8f39cb68 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_init.c @@ -0,0 +1,796 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * DDI entry points. + */ +static int smb_drv_attach(dev_info_t *, ddi_attach_cmd_t); +static int smb_drv_detach(dev_info_t *, ddi_detach_cmd_t); +static int smb_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); +static int smb_drv_open(dev_t *, int, int, cred_t *); +static int smb_drv_close(dev_t, int, int, cred_t *); +static int smb_drv_busy(void); +static int smb_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); + +/* + * module linkage info for the kernel + */ +static struct cb_ops cbops = { + smb_drv_open, /* cb_open */ + smb_drv_close, /* cb_close */ + nodev, /* cb_strategy */ + nodev, /* cb_print */ + nodev, /* cb_dump */ + nodev, /* cb_read */ + nodev, /* cb_write */ + smb_drv_ioctl, /* cb_ioctl */ + nodev, /* cb_devmap */ + nodev, /* cb_mmap */ + nodev, /* cb_segmap */ + nochpoll, /* cb_chpoll */ + ddi_prop_op, /* cb_prop_op */ + NULL, /* cb_streamtab */ + D_MP, /* cb_flag */ + CB_REV, /* cb_rev */ + nodev, /* cb_aread */ + nodev, /* cb_awrite */ +}; + +static struct dev_ops devops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + smb_drv_getinfo, /* devo_getinfo */ + nulldev, /* devo_identify */ + nulldev, /* devo_probe */ + smb_drv_attach, /* devo_attach */ + smb_drv_detach, /* devo_detach */ + nodev, /* devo_reset */ + &cbops, /* devo_cb_ops */ + NULL, /* devo_bus_ops */ + NULL, /* devo_power */ +}; + +static struct modldrv modldrv = { + &mod_driverops, /* drv_modops */ + "CIFS Server Protocol %I%", /* drv_linkinfo */ + &devops, +}; + +static struct modlinkage modlinkage = { + + MODREV_1, /* revision of the module, must be: MODREV_1 */ + &modldrv, /* ptr to linkage structures */ + NULL, +}; + +static int smb_info_init(struct smb_info *si); +static void smb_info_fini(struct smb_info *si); + +extern int smb_fsop_start(void); +extern void smb_fsop_stop(void); + +extern int nt_mapk_start(void); +extern void nt_mapk_stop(void); + + +extern int smb_get_kconfig(smb_kmod_cfg_t *cfg); + +extern void smb_notify_change_daemon(smb_thread_t *thread, void *arg); +extern void smb_nbt_daemon(smb_thread_t *thread, void *arg); +extern void smb_tcp_daemon(smb_thread_t *thread, void *arg); +extern void smb_timers(smb_thread_t *thread, void *arg); +extern void smb_session_worker(void *arg); + +extern int smb_maxbufsize; + +extern time_t smb_oplock_timeout; + +/* Debug logging level: 0=Disabled, 1=Quiet, 2=Verbose */ +int smbsrv_debug_level; + +struct smb_info smb_info; + +static dev_info_t *smb_drv_dip = NULL; +static kmutex_t smb_drv_opencount_lock; +static int smb_drv_opencount = 0; + +/* + * Kstat smb_info statistics. + */ +static struct smbinfo_stats { + kstat_named_t state; + kstat_named_t open_files; + kstat_named_t open_trees; + kstat_named_t open_users; +} smbinfo_stats = { + { "state", KSTAT_DATA_UINT32 }, + { "open_files", KSTAT_DATA_UINT32 }, + { "connections", KSTAT_DATA_UINT32 }, + { "sessions", KSTAT_DATA_UINT32 } +}; + +static int smb_kstat_init(void); +static void smb_kstat_fini(void); +static int smb_kstat_update_info(kstat_t *ksp, int rw); +extern void smb_initialize_dispatch_kstat(void); +extern void smb_remove_dispatch_kstat(void); + +static kstat_t *smbinfo_ksp = NULL; + +/* + * SMB pseudo-driver entry points + */ + + + +int +_init(void) +{ + int rc; + + mutex_init(&smb_drv_opencount_lock, NULL, MUTEX_DRIVER, NULL); + + if ((rc = mod_install(&modlinkage)) != 0) { + mutex_destroy(&smb_drv_opencount_lock); + cmn_err(CE_NOTE, "init: %d\n", rc); + return (rc); + } + + return (0); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +int +_fini(void) +{ + int rc; + + mutex_enter(&smb_drv_opencount_lock); + if (smb_drv_busy()) { + mutex_exit(&smb_drv_opencount_lock); + return (EBUSY); + } + mutex_exit(&smb_drv_opencount_lock); + + if ((rc = mod_remove(&modlinkage)) == 0) + mutex_destroy(&smb_drv_opencount_lock); + + return (rc); +} + +/* + * DDI entry points. + */ + +/* ARGSUSED */ +static int +smb_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) +{ + ulong_t instance = getminor((dev_t)arg); + + switch (cmd) { + case DDI_INFO_DEVT2DEVINFO: + *result = smb_drv_dip; + return (DDI_SUCCESS); + + case DDI_INFO_DEVT2INSTANCE: + *result = (void *)instance; + return (DDI_SUCCESS); + + default: + break; + } + + return (DDI_FAILURE); +} + + +static int +smb_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + if (cmd != DDI_ATTACH) { + return (DDI_FAILURE); + } + + if (ddi_get_instance(dip) != 0) { + /* we only allow instance 0 to attach */ + return (DDI_FAILURE); + } + + smb_drv_dip = dip; + + /* create the minor node */ + if (ddi_create_minor_node(dip, "smbsrv", S_IFCHR, 0, + DDI_PSEUDO, 0) != DDI_SUCCESS) { + cmn_err(CE_WARN, "smb_drv_attach: failed creating minor node"); + ddi_remove_minor_node(dip, NULL); + return (DDI_FAILURE); + } + + if (smb_service_init() != 0) { + ddi_remove_minor_node(dip, NULL); + cmn_err(CE_WARN, "smb_drv_attach: failed to initialize " + "SMB service"); + return (DDI_FAILURE); + } + + return (DDI_SUCCESS); +} + +static int +smb_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + if (cmd != DDI_DETACH) + return (DDI_FAILURE); + + mutex_enter(&smb_drv_opencount_lock); + /* + * Service state value is not protected by a lock in this case but + * it shouldn't be possible for the service state machine to transition + * TO a busy state at a time when smb_drv_busy() would return false. + */ + if (smb_drv_busy() || smb_svcstate_sm_busy()) { + mutex_exit(&smb_drv_opencount_lock); + return (DDI_FAILURE); + } + mutex_exit(&smb_drv_opencount_lock); + + smb_service_fini(); + + smb_drv_dip = NULL; + ddi_remove_minor_node(dip, NULL); + + return (DDI_SUCCESS); +} + +/* ARGSUSED */ +static int +smb_drv_ioctl(dev_t drv, int cmd, intptr_t argp, int flag, cred_t *cred, + int *retval) +{ + int gmtoff; + + switch (cmd) { + + case SMB_IOC_GMTOFF: + if (ddi_copyin((int *)argp, &gmtoff, sizeof (int), flag)) + return (EFAULT); + (void) smb_set_gmtoff((uint32_t)gmtoff); + break; + + case SMB_IOC_CONFIG_REFRESH: +#if 0 + smb_svcstate_event(SMB_SVCEVT_CONFIG, NULL); +#endif + break; + + default: + break; + } + + return (0); +} + +/* ARGSUSED */ +static int +smb_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp) +{ + int rc = 0; + + /* + * Only allow one open at a time + */ + mutex_enter(&smb_drv_opencount_lock); + if (smb_drv_busy()) { + mutex_exit(&smb_drv_opencount_lock); + return (EBUSY); + } + smb_drv_opencount++; + mutex_exit(&smb_drv_opencount_lock); + + /* + * Check caller's privileges. + */ + if (secpolicy_smb(credp) != 0) { + mutex_enter(&smb_drv_opencount_lock); + smb_drv_opencount--; + mutex_exit(&smb_drv_opencount_lock); + return (EPERM); + } + + /* + * Start SMB service state machine + */ + rc = smb_svcstate_sm_start(&smb_info.si_svc_sm_ctx); + + if (rc != 0) { + mutex_enter(&smb_drv_opencount_lock); + smb_drv_opencount--; + mutex_exit(&smb_drv_opencount_lock); + return (rc); + } + + return (0); +} + +/* ARGSUSED */ +static int +smb_drv_close(dev_t dev, int flag, int otyp, cred_t *credp) +{ + mutex_enter(&smb_drv_opencount_lock); + if (!smb_drv_busy()) { + mutex_exit(&smb_drv_opencount_lock); + return (0); + } + mutex_exit(&smb_drv_opencount_lock); + + smb_svcstate_event(SMB_SVCEVT_CLOSE, NULL); + + mutex_enter(&smb_drv_opencount_lock); + smb_drv_opencount--; + mutex_exit(&smb_drv_opencount_lock); + + return (0); +} + +/* + * Convenience function - must be called with smb_drv_opencount_lock held. + */ +static int +smb_drv_busy(void) +{ + ASSERT(mutex_owned(&smb_drv_opencount_lock)); + return (smb_drv_opencount); +} + +/* + * SMB Service initialization and startup functions + */ + +int +smb_service_init(void) +{ + int rc; + + rc = smb_info_init(&smb_info); + if (rc != 0) { + return (rc); + } + + rc = smb_svcstate_sm_init(&smb_info.si_svc_sm_ctx); + if (rc != 0) { + smb_info_fini(&smb_info); + return (rc); + } + + rc = smb_kstat_init(); + if (rc != 0) { + smb_kstat_fini(); + return (rc); + } + + smb_winpipe_init(); + + return (0); +} + +void +smb_service_fini(void) +{ + smb_winpipe_fini(); + + smb_kstat_fini(); + + smb_svcstate_sm_fini(&smb_info.si_svc_sm_ctx); + + smb_info_fini(&smb_info); +} + +/* + * Progress bits for smb_info.si_open_progress. For use only by + * smb_service_open/smb_service_close. + */ +#define SMB_FS_STARTED 0x01 +#define LMSHRD_KCLIENT_STARTED 0x02 +#define SMB_KDOOR_CLNT_STARTED 0x04 +#define SMB_KDOOR_SRV_STARTED 0x08 +#define SMB_THREADS_STARTED 0x10 + +int +smb_service_open(struct smb_info *si) +{ + int rc; + int size; /* XXX TEMPORARY (remove when kconfig is removed) */ + + /* Track progress so we can cleanup from a partial failure */ + si->si_open_progress = 0; + si->si_connect_progress = 0; + + /* XXX TEMPORARY */ + if (smb_get_kconfig(&si->si) == 0) { + if (si->si.skc_sync_enable) + smb_set_stability(1); + + if (si->si.skc_flush_required) + smb_commit_required(0); + + if (si->si.skc_maxconnections == 0) + si->si.skc_maxconnections = 0xFFFFFFFF; + + size = si->si.skc_maxbufsize; + if (size != 0) { + if (size < 37 || size > 64) + size = 37; + smb_maxbufsize = SMB_NT_MAXBUF(size); + } + + /* + * XXX should not override configuration. + * For now, this disables server side + * signing regardless of configuration. + */ + si->si.skc_signing_enable = 0; + si->si.skc_signing_required = 0; + si->si.skc_signing_check = 0; + + smb_correct_keep_alive_values(si->si.skc_keepalive); + + /* + * XXX The following code was pulled from smb_oplock_init. + * It should be combined with with the config process if + * this info will be stored with the configuration or with + * the smb_fsop_start function if the data will be stored + * in the root of the fs. + */ + + /* + * XXX oplock enable flag. + * Should be stored in extended attribute in root of fs + * or a ZFS user-defined property. + */ + if (si->si.skc_oplock_enable == 0) { + cmn_err(CE_NOTE, "SmbOplocks: disabled"); + } + + smb_oplock_timeout = si->si.skc_oplock_timeout; + + /* + * XXX oplock timeout. Can a customer configure this? + */ + if (si->si.skc_oplock_timeout < OPLOCK_MIN_TIMEOUT) + smb_oplock_timeout = OPLOCK_MIN_TIMEOUT; + + } else { + return (EIO); /* XXX Errno? */ + } + + if ((rc = smb_fsop_start()) != 0) { + return (rc); + } + si->si_open_progress |= SMB_FS_STARTED; + + if ((rc = lmshrd_kclient_start()) != 0) { + return (rc); + } + si->si_open_progress |= LMSHRD_KCLIENT_STARTED; + + if ((rc = smb_kdoor_clnt_start()) != 0) { + return (rc); + } + si->si_open_progress |= SMB_KDOOR_CLNT_STARTED; + + if ((rc = smb_kdoor_srv_start()) != 0) { + return (rc); + } + si->si_open_progress |= SMB_KDOOR_SRV_STARTED; + + if ((rc = smb_service_start_threads(si)) != 0) { + return (rc); + } + si->si_open_progress |= SMB_THREADS_STARTED; + + return (0); +} + +void +smb_service_close(struct smb_info *si) +{ + if (si->si_open_progress & SMB_THREADS_STARTED) + smb_service_stop_threads(si); + + if (si->si_open_progress & SMB_KDOOR_SRV_STARTED) + smb_kdoor_srv_stop(); + + if (si->si_open_progress & SMB_KDOOR_CLNT_STARTED) + smb_kdoor_clnt_stop(); + + if (si->si_open_progress & LMSHRD_KCLIENT_STARTED) + lmshrd_kclient_stop(); + + if (si->si_open_progress & SMB_FS_STARTED) + smb_fsop_stop(); +} + +/* + * Start the Netbios and TCP services. + * + * Awaken arguments are not known until thread starts. + * + * XXX We give up the NET_MAC_AWARE privilege because it keeps us from + * re-opening the connection when there are leftover TCP connections in + * TCPS_TIME_WAIT state. There seem to be some security ramifications + * around reestablishing a connection while possessing the NET_MAC_AWARE + * privilege. + * + * This approach may cause problems when we try to support zones. An + * alternative would be to retry the connection setup for a fixed period + * of time until the stale connections clear up but that implies we + * would be offline for a couple minutes every time the service is + * restarted with active connections. + */ +int +smb_service_connect(struct smb_info *si) +{ + int rc1, rc2; + + if ((rc1 = setpflags(NET_MAC_AWARE, 0, CRED())) != 0) { + cmn_err(CE_WARN, "Cannot remove NET_MAC_AWARE privilege"); + smb_svcstate_event(SMB_SVCEVT_DISCONNECT, (uintptr_t)rc1); + return (rc1); + } + + rc1 = smb_thread_start(&si->si_nbt_daemon); + rc2 = smb_thread_start(&si->si_tcp_daemon); + if (rc2 != 0) + rc1 = rc2; + return (rc1); +} + +void +smb_service_disconnect(struct smb_info *si) +{ + smb_thread_stop(&si->si_nbt_daemon); + smb_thread_stop(&si->si_tcp_daemon); +} + +/* + * Start any service-related kernel threads except for the NBT and TCP + * daemon threads. Those service daemon threads are handled separately. + * + * Returns 0 for success, non-zero for failure. If failure is returned the + * caller should call smb_service_stop_threads to cleanup any threads that + * were successfully started. + */ +int +smb_service_start_threads(struct smb_info *si) +{ + int rval; + + si->thread_pool = taskq_create( + "smb_workers", + si->si.skc_maxworkers, + SMB_WORKER_PRIORITY, + si->si.skc_maxworkers, + INT_MAX, + TASKQ_DYNAMIC|TASKQ_PREPOPULATE); + ASSERT(si->thread_pool != NULL); + + rval = smb_thread_start(&si->si_thread_notify_change); + if (rval != 0) + return (rval); + + rval = smb_thread_start(&si->si_thread_timers); + if (rval != 0) { + smb_thread_stop(&si->si_thread_notify_change); + return (rval); + } + + return (0); +} + +void +smb_service_stop_threads(struct smb_info *si) +{ + smb_thread_stop(&si->si_thread_timers); + smb_thread_stop(&si->si_thread_notify_change); + taskq_destroy(si->thread_pool); +} + +static int +smb_info_init(struct smb_info *si) +{ + int i; + + bzero(si, sizeof (smb_info)); + + for (i = 0; i <= SMBND_HASH_MASK; i++) { + smb_llist_constructor(&si->node_hash_table[i], + sizeof (smb_node_t), offsetof(smb_node_t, n_lnd)); + } + + smb_llist_constructor(&si->si_vfs_list, + sizeof (smb_vfs_t), offsetof(smb_vfs_t, sv_lnd)); + + smb_slist_constructor(&si->si_ncr_list, sizeof (smb_request_t), + offsetof(smb_request_t, sr_ncr.nc_lnd)); + + smb_slist_constructor(&si->si_nce_list, sizeof (smb_request_t), + offsetof(smb_request_t, sr_ncr.nc_lnd)); + + si->si_cache_vfs = kmem_cache_create("smb_vfs_cache", + sizeof (smb_vfs_t), 8, NULL, NULL, NULL, NULL, NULL, 0); + si->si_cache_request = kmem_cache_create("smb_request_cache", + sizeof (smb_request_t), 8, NULL, NULL, NULL, NULL, NULL, 0); + si->si_cache_session = kmem_cache_create("smb_session_cache", + sizeof (smb_session_t), 8, NULL, NULL, NULL, NULL, NULL, 0); + si->si_cache_user = kmem_cache_create("smb_user_cache", + sizeof (smb_user_t), 8, NULL, NULL, NULL, NULL, NULL, 0); + si->si_cache_tree = kmem_cache_create("smb_tree_cache", + sizeof (smb_tree_t), 8, NULL, NULL, NULL, NULL, NULL, 0); + si->si_cache_ofile = kmem_cache_create("smb_ofile_cache", + sizeof (smb_ofile_t), 8, NULL, NULL, NULL, NULL, NULL, 0); + si->si_cache_odir = kmem_cache_create("smb_odir_cache", + sizeof (smb_odir_t), 8, NULL, NULL, NULL, NULL, NULL, 0); + si->si_cache_node = kmem_cache_create("smb_smb_node_cache", + sizeof (smb_node_t), 8, NULL, NULL, NULL, NULL, NULL, 0); + + smb_thread_init(&si->si_nbt_daemon, "smb_nbt_daemon", smb_nbt_daemon, + si, NULL, NULL); + smb_thread_init(&si->si_tcp_daemon, "smb_tcp_daemon", smb_tcp_daemon, + si, NULL, NULL); + smb_thread_init(&si->si_thread_notify_change, + "smb_notify_change_daemon", smb_notify_change_daemon, &smb_info, + NULL, NULL); + smb_thread_init(&si->si_thread_timers, "smb_timers", smb_timers, + si, NULL, NULL); + + return (0); +} + +static void +smb_info_fini(struct smb_info *si) +{ + int i; + + for (i = 0; i <= SMBND_HASH_MASK; i++) { + smb_node_t *node; + + /* + * The following sequence is just intended for sanity check. + * This will have to be modified when the code goes into + * production. + * + * The SMB node hash table should be emtpy at this point. If the + * hash table is not empty all the nodes remaining are displayed + * (it should help figure out what actions led to this state) + * and "oops" will be set to B_TRUE which will trigger the + * ASSERT that follows. + * + * The reason why SMB nodes are still remaining in the hash + * table is problably due to a mismatch between calls to + * smb_node_lookup() and smb_node_release(). You must track that + * down. + * + * Now if you are reading this comment because you actually hit + * the ASSERT, the temptation to ignore it is going to be very + * strong. To help you make the right decision you should know + * that when the ASSERT happened a message containing you SunID + * has been sent to cifsgate. By now it has been logged into a + * special database. + * + * You are being watched... + */ + node = smb_llist_head(&si->node_hash_table[i]); + ASSERT(node == NULL); + } + + for (i = 0; i <= SMBND_HASH_MASK; i++) { + smb_llist_destructor(&si->node_hash_table[i]); + } + + smb_llist_destructor(&si->si_vfs_list); + + kmem_cache_destroy(si->si_cache_vfs); + kmem_cache_destroy(si->si_cache_request); + kmem_cache_destroy(si->si_cache_session); + kmem_cache_destroy(si->si_cache_user); + kmem_cache_destroy(si->si_cache_tree); + kmem_cache_destroy(si->si_cache_ofile); + kmem_cache_destroy(si->si_cache_odir); + kmem_cache_destroy(si->si_cache_node); + + smb_thread_destroy(&si->si_nbt_daemon); + smb_thread_destroy(&si->si_tcp_daemon); + smb_thread_destroy(&si->si_thread_notify_change); + smb_thread_destroy(&si->si_thread_timers); +} + +static int +smb_kstat_init() +{ + + /* create and initialize smb kstats - smb_info stats */ + smbinfo_ksp = kstat_create("smb", 0, "smb_info", "misc", + KSTAT_TYPE_NAMED, sizeof (smbinfo_stats) / sizeof (kstat_named_t), + KSTAT_FLAG_VIRTUAL); + if (smbinfo_ksp) { + smbinfo_ksp->ks_data = (void *) &smbinfo_stats; + smbinfo_ksp->ks_update = smb_kstat_update_info; + kstat_install(smbinfo_ksp); + } + + /* create and initialize smb kstats - smb_dispatch stats */ + smb_initialize_dispatch_kstat(); + + return (0); +} + +static void +smb_kstat_fini() +{ + if (smbinfo_ksp != NULL) { + kstat_delete(smbinfo_ksp); + smbinfo_ksp = NULL; + } + + smb_remove_dispatch_kstat(); +} + +/* ARGSUSED */ +static int +smb_kstat_update_info(kstat_t *ksp, int rw) +{ + if (rw == KSTAT_WRITE) { + return (EACCES); + } else { + smbinfo_stats.state.value.ui32 = + smb_info.si_svc_sm_ctx.ssc_state; + smbinfo_stats.open_files.value.ui32 = smb_info.open_files; + smbinfo_stats.open_trees.value.ui32 = smb_info.open_trees; + smbinfo_stats.open_users.value.ui32 = smb_info.open_users; + } + return (0); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_kdoor_clnt.c b/usr/src/uts/common/fs/smbsrv/smb_kdoor_clnt.c new file mode 100644 index 000000000000..df2cd5368b28 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_kdoor_clnt.c @@ -0,0 +1,137 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +door_handle_t smb_kdoor_clnt_dh; + +/* + * smb_kdoor_clnt_free + * + * This function should be invoked to free both the argument/result door buffer + * regardless of the status of the up-call. + * + * The doorfs allocates a new buffer if the result buffer passed by the client + * is too small. This function will deallocate that buffer as well. + */ +void +smb_kdoor_clnt_free(char *argp, size_t arg_size, char *rbufp, size_t rbuf_size) +{ + if (argp) { + if (argp == rbufp) { + kmem_free(argp, arg_size); + } else if (rbufp) { + kmem_free(argp, arg_size); + kmem_free(rbufp, rbuf_size); + } + } else { + if (rbufp) + kmem_free(rbufp, rbuf_size); + } +} + +/* + * smb_kdoor_clnt_start + * + * The SMB kernel module should invoke this function upon startup. + */ +int +smb_kdoor_clnt_start() +{ + int rc = 0; + + rc = door_ki_open(SMB_DR_SVC_NAME, &smb_kdoor_clnt_dh); + + return (rc); +} + +/* + * smb_kdoor_clnt_stop + * + * The SMB kernel module should invoke this function upon unload. + */ +void +smb_kdoor_clnt_stop() +{ + door_ki_rele(smb_kdoor_clnt_dh); +} + +/* + * smb_kdoor_clnt_upcall + * + * This function will make a door up-call to the server function + * associated with the door descriptor fp. The specified door + * request buffer (i.e. argp) will be passed as the argument to the + * door_ki_upcall(). Upon success, the result buffer is returned. Otherwise, + * NULL pointer is returned. The size of the result buffer is returned + * via rbufsize. + */ +char * +smb_kdoor_clnt_upcall(char *argp, size_t arg_size, door_desc_t *dp, + uint_t desc_num, size_t *rbufsize) +{ + door_arg_t door_arg; + int err; + + if (!argp) { + cmn_err(CE_WARN, "smb_kdoor_clnt_upcall: invalid parameter"); + return (NULL); + } + + door_arg.data_ptr = argp; + door_arg.data_size = arg_size; + door_arg.desc_ptr = dp; + door_arg.desc_num = desc_num; + door_arg.rbuf = argp; + door_arg.rsize = arg_size; + + if ((err = door_ki_upcall(smb_kdoor_clnt_dh, &door_arg)) != 0) { + cmn_err(CE_WARN, "smb_kdoor_clnt_upcall: failed(%d)", err); + kmem_free(argp, arg_size); + argp = NULL; + return (NULL); + } + + if (smb_dr_get_res_stat(door_arg.data_ptr, door_arg.rsize) != + SMB_DR_OP_SUCCESS) { + smb_kdoor_clnt_free(argp, arg_size, door_arg.rbuf, + door_arg.rsize); + *rbufsize = 0; + return (NULL); + } + *rbufsize = door_arg.rsize; + return (door_arg.data_ptr); + +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_kdoor_encdec.c b/usr/src/uts/common/fs/smbsrv/smb_kdoor_encdec.c new file mode 100644 index 000000000000..701e587fbc77 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_kdoor_encdec.c @@ -0,0 +1,266 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include + + +/* + * smb_kdr_decode_common + * + * This function can be used for decoding both door request and result buffer. + * pre-condition: data is non-null pointer, and is bzero'd. + */ +int +smb_kdr_decode_common(char *buf, size_t len, xdrproc_t proc, void *data) +{ + XDR xdrs; + int rc = 0; + + if (!data) { + cmn_err(CE_WARN, "smb_kdr_decode_common: invalid param"); + return (-1); + } + + xdrmem_create(&xdrs, buf, len, XDR_DECODE); + if (!proc(&xdrs, data)) + rc = -1; + + xdr_destroy(&xdrs); + return (rc); +} + +/* + * smb_kdr_encode_common + * + * This function is used for encoding both request/result door buffer. + * This function will first encode integer value 'reserved' (opcode/status), + * followed by the data (which will be encoded via the specified XDR routine. + * + * Returns encoded buffer upon success. Otherwise, returns NULL. + */ +char * +smb_kdr_encode_common(uint_t reserved, void *data, xdrproc_t proc, size_t *len) +{ + XDR xdrs; + char *buf; + + if (proc && !data) { + cmn_err(CE_WARN, "smb_kdr_encode_common: invalid param"); + *len = 0; + return (NULL); + } + + *len = xdr_sizeof(xdr_uint32_t, &reserved); + if (proc) + *len += xdr_sizeof(proc, data); + buf = kmem_alloc(*len, KM_SLEEP); + xdrmem_create(&xdrs, buf, *len, XDR_ENCODE); + if (!xdr_uint32_t(&xdrs, &reserved)) { + cmn_err(CE_WARN, "smb_kdr_encode_common: encode error 1"); + kmem_free(buf, *len); + *len = 0; + xdr_destroy(&xdrs); + return (NULL); + } + + if (proc && !proc(&xdrs, data)) { + cmn_err(CE_WARN, "smb_kdr_encode_common: encode error 2"); + kmem_free(buf, *len); + buf = NULL; + *len = 0; + } + + xdr_destroy(&xdrs); + return (buf); +} + +/* + * Get the opcode of the door argument buffer. + */ +int +smb_dr_get_opcode(char *argp, size_t arg_size) +{ + int opcode; + + if (smb_kdr_decode_common(argp, arg_size, xdr_uint32_t, &opcode) != 0) + opcode = -1; + return (opcode); +} + +/* + * Set the opcode of the door argument buffer. + */ +char * +smb_dr_set_opcode(uint32_t opcode, size_t *len) +{ + char *buf; + + buf = smb_kdr_encode_common(opcode, NULL, NULL, len); + return (buf); +} + +/* + * Get the status of the door result buffer. + */ +int +smb_dr_get_res_stat(char *rbufp, size_t rbuf_size) +{ + int stat; + if (smb_kdr_decode_common(rbufp, rbuf_size, xdr_uint32_t, &stat) != 0) + stat = -1; + return (stat); +} + +/* + * Set the status of the door result buffer. + */ +char * +smb_dr_set_res_stat(uint32_t stat, size_t *len) +{ + char *buf; + + buf = smb_kdr_encode_common(stat, NULL, NULL, len); + return (buf); +} + +char * +smb_dr_encode_arg_get_token(netr_client_t *clnt_info, size_t *len) +{ + + char *buf; + smb_dr_bytes_t arg; + uint_t opcode = SMB_DR_USER_AUTH_LOGON; + + arg.bytes_val = netr_client_mkselfrel(clnt_info, + &arg.bytes_len); + + buf = smb_kdr_encode_common(opcode, &arg, xdr_smb_dr_bytes_t, len); + kmem_free(arg.bytes_val, arg.bytes_len); + return (buf); +} + +smb_token_t * +smb_dr_decode_res_token(char *buf, size_t len) +{ + smb_dr_bytes_t res; + smb_token_t *token; + + bzero(&res, sizeof (smb_dr_bytes_t)); + if (smb_kdr_decode_common(buf, len, xdr_smb_dr_bytes_t, &res) != + 0) { + cmn_err(CE_WARN, "smb_dr_decode_res_token: failed"); + xdr_free(xdr_smb_dr_bytes_t, (char *)&res); + return (NULL); + } + token = smb_token_mkabsolute(res.bytes_val, res.bytes_len); + xdr_free(xdr_smb_dr_bytes_t, (char *)&res); + + return (token); +} + +char * +smb_dr_encode_string(uint32_t reserved, char *str, size_t *len) +{ + char *buf = NULL; + smb_dr_string_t res; + + if (!str) { + *len = 0; + return (buf); + } + + res.buf = str; + if ((buf = smb_kdr_encode_common(reserved, &res, + xdr_smb_dr_string_t, len)) == 0) + cmn_err(CE_WARN, "smb_dr_encode_string: failed"); + return (buf); +} + +/* + * smb_dr_decode_kshare() + * + * The kshare information arrives encoded in a flat buffer, so retrieve + * the flat buffer and convert it to an smb_dr_kshare structure. + */ + +smb_dr_kshare_t * +smb_dr_decode_kshare(char *buf, size_t len) +{ + smb_dr_bytes_t res; + smb_dr_kshare_t *kshare; + + bzero(&res, sizeof (smb_dr_bytes_t)); + if (smb_kdr_decode_common(buf, len, xdr_smb_dr_bytes_t, &res) != + 0) { + cmn_err(CE_WARN, "smb_dr_decode_kshare: failed"); + xdr_free(xdr_smb_dr_bytes_t, (char *)&res); + return (NULL); + } + kshare = smb_share_mkabsolute(res.bytes_val, res.bytes_len); + xdr_free(xdr_smb_dr_bytes_t, (char *)&res); + + return (kshare); +} + +/* + * smb_share_mkabsolute + * + * decode: flat buffer -> structure + */ + +smb_dr_kshare_t * +smb_share_mkabsolute(uint8_t *buf, uint32_t len) +{ + smb_dr_kshare_t *obj; + XDR xdrs; + + xdrmem_create(&xdrs, (const caddr_t)buf, len, XDR_DECODE); + obj = kmem_zalloc(sizeof (smb_dr_kshare_t), KM_SLEEP); + + if (!xdr_smb_dr_kshare_t(&xdrs, obj)) { + kmem_free(obj, sizeof (smb_dr_kshare_t)); + obj = NULL; + } + + xdr_destroy(&xdrs); + return (obj); +} + +void +smb_dr_kshare_free(smb_dr_kshare_t *kshare) +{ + if (!kshare) + return; + + xdr_free(xdr_smb_dr_kshare_t, (char *)kshare); + kmem_free(kshare, sizeof (smb_dr_kshare_t)); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_kdoor_ops.c b/usr/src/uts/common/fs/smbsrv/smb_kdoor_ops.c new file mode 100644 index 000000000000..d6be91418209 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_kdoor_ops.c @@ -0,0 +1,159 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Kernel door operations + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* SMB kernel module's door operation table */ +smb_kdr_op_t smb_kdoorsrv_optab[] = +{ + smb_kdr_op_user_num, + smb_kdr_op_users, + smb_kdr_op_share +}; + +int +smb_kdr_is_valid_opcode(int opcode) +{ + if (opcode < 0 || + opcode > (sizeof (smb_kdoorsrv_optab) / sizeof (smb_kdr_op_t))) + return (-1); + else + return (0); +} + +/*ARGSUSED*/ +char * +smb_kdr_op_user_num(char *argp, size_t arg_size, size_t *rbufsize, int *errno) +{ + uint32_t num; + char *rbuf; + + *errno = SMB_DR_OP_SUCCESS; + num = smb_user_get_num(); + rbuf = smb_kdr_encode_common(SMB_DR_OP_SUCCESS, &num, xdr_uint32_t, + rbufsize); + if (!rbuf) { + *errno = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + return (NULL); + } + + return (rbuf); +} + +char * +smb_kdr_op_users(char *argp, size_t arg_size, size_t *rbufsize, int *errno) +{ + smb_dr_ulist_t *ulist; + uint32_t offset; + char *rbuf = NULL; + + *errno = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + if (smb_kdr_decode_common(argp, arg_size, xdr_uint32_t, &offset) != 0) { + *errno = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + ulist = kmem_zalloc(sizeof (smb_dr_ulist_t), KM_SLEEP); + (void) smb_dr_ulist_get(offset, ulist); + + if ((rbuf = smb_kdr_encode_common(SMB_DR_OP_SUCCESS, ulist, + xdr_smb_dr_ulist_t, rbufsize)) == NULL) { + *errno = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + } + + smb_dr_ulist_free(ulist); + kmem_free(ulist, sizeof (smb_dr_ulist_t)); + return (rbuf); +} + +/* + * smb_kdr_op_share() + * + * This function decodes an smb_dr_kshare_t structure from userland and + * calls smb_share() to take action depending on whether a share is being + * enabled or disabled. + */ + +char * +smb_kdr_op_share(char *argp, size_t arg_size, size_t *rbufsize, int *errno) +{ + smb_dr_kshare_t *kshare; + char *rbuf = NULL; + int error = 0; + + *errno = SMB_DR_OP_SUCCESS; + *rbufsize = 0; + + kshare = smb_dr_decode_kshare(argp, arg_size); + + if (kshare == NULL) { + *errno = SMB_DR_OP_ERR_DECODE; + return (NULL); + } + + switch (kshare->k_op) { + case LMSHR_ADD: + error = smb_share_export(kshare->k_path); + break; + case LMSHR_DELETE: + error = smb_share_unexport(kshare->k_path, kshare->k_sharename); + break; + default: + ASSERT(0); + error = EINVAL; + break; + } + + smb_dr_kshare_free(kshare); + + if (error) + return (NULL); + + rbuf = smb_kdr_encode_common(SMB_DR_OP_SUCCESS, &error, xdr_int32_t, + rbufsize); + + if (!rbuf) { + *errno = SMB_DR_OP_ERR_ENCODE; + *rbufsize = 0; + return (NULL); + } + + return (rbuf); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_kdoor_srv.c b/usr/src/uts/common/fs/smbsrv/smb_kdoor_srv.c new file mode 100644 index 000000000000..7199aa62f4a4 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_kdoor_srv.c @@ -0,0 +1,217 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Kernel door service + * It has dependency on the kernel door client interface because the downcall + * descriptor is required to be passed up to SMB daemon via door up-call. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +door_handle_t smb_kdoor_hdl = NULL; + +/* + * Since the action performed by smb_kdoor_srv_callback might vary + * according to request type/opcode, the smb_kdoor_cookie will + * be set to the request type in the server procedure + * (i.e. smb_kdoor_svc). It will then be passed to the callback + * function when the kernel is done with the copyout operation. + */ +int smb_kdoor_cookie = -1; + +extern smb_kdr_op_t smb_kdoorsrv_optab[]; + +/* forward declaration */ +void smb_kdoor_svc(void *data, door_arg_t *dap, void (**destfnp)(void *, + void *), void **destarg, int *error); + +/* + * smb_kdoor_srv_start + * + * When driver is opened, this function should be called to create the + * kernel door. The door descriptor will then be passed up to the + * user-space SMB daemon. + * + * Returns 0 upon success otherwise non-zero + */ +int +smb_kdoor_srv_start() +{ + door_desc_t smb_kdoor_desc; + int err; + int res; + int opcode = SMB_DR_SET_DWNCALL_DESC; + + if ((err = door_ki_create(smb_kdoor_svc, + &smb_kdoor_cookie, 0, &smb_kdoor_hdl)) != 0) { + cmn_err(CE_WARN, "SmbKdoorInit: door_create" + " failed(%d)", err); + return (err); + } + + smb_kdoor_desc.d_attributes = DOOR_HANDLE; + smb_kdoor_desc.d_data.d_handle = smb_kdoor_hdl; + + res = smb_upcall_set_dwncall_desc(opcode, &smb_kdoor_desc, 1); + if (res != SMB_DR_OP_SUCCESS) { + cmn_err(CE_WARN, "SmbKdoorInit: smbd failed to set the" + " downcall descriptor res=%d", res); + smb_kdoor_srv_stop(); + return (EIO); + } + + return (0); +} + +/* + * smb_kdoor_srv_stop + * + * This function will stop the kernel door service when the driver is closed. + */ +void +smb_kdoor_srv_stop() +{ + if (smb_kdoor_hdl) { + door_ki_rele(smb_kdoor_hdl); + smb_kdoor_hdl = NULL; + } +} + +/* + * smb_kdoor_srv_callback + * + * This callback function will be executed by the kernel after copyout() + * completes. Currently, this function only free the server buffer that + * was previously allocated in the smb_kdoor_srv(). It can be enhanced + * to perform any action based on the opcode if there is a need in the + * future. + */ +static void +smb_kdoor_srv_callback(void *cookie, void *arg) +{ + /*LINTED E_FUNC_VAR_UNUSED*/ + int *opcode; + smb_kdoor_cb_arg_t *cbarg; + + if (cookie) + opcode = (int *)cookie; + + if (!arg) + return; + + cbarg = (smb_kdoor_cb_arg_t *)arg; + if (cbarg->rbuf) + kmem_free(cbarg->rbuf, cbarg->rbuf_size); + + kmem_free(cbarg, sizeof (smb_kdoor_cb_arg_t)); +} + + +void +smb_kdoor_svc(void *cookie, door_arg_t *dap, void (**destfnp)(void *, + void *), void **destarg, int *error) +{ + int opcode; + smb_kdoor_cb_arg_t *cbarg; + size_t arg_size; + char *argp = NULL; + smb_kdr_op_t smbop; + + /* + * Be aware that *destfnp cannot be NULL even if there isn't + * any additional work after the kernel completes copyout() operation. + */ + *destfnp = smb_kdoor_srv_callback; + *destarg = NULL; + *error = 0; + + if (!dap) { + cmn_err(CE_WARN, "SmbKdoorSvc: invalid arguments"); + *error = EINVAL; + return; + } + + arg_size = dap->data_size; + argp = kmem_alloc(arg_size, KM_SLEEP); + /* The data_ptr points to user data */ + (void) copyin(dap->data_ptr, argp, dap->data_size); + /* initialize the returned data size to be 0 */ + dap->data_size = 0; + + opcode = smb_dr_get_opcode(argp, arg_size); + *((int *)cookie) = opcode; + + if (smb_kdr_is_valid_opcode(opcode) != 0) { + cmn_err(CE_WARN, "SmbKdoorSvc: invalid opcode(%d)", opcode); + *error = EINVAL; + kmem_free(argp, arg_size); + return; + + } + + smbop = smb_kdoorsrv_optab[opcode]; + cbarg = kmem_alloc(sizeof (smb_kdoor_cb_arg_t), KM_SLEEP); + if ((cbarg->rbuf = smbop(argp + sizeof (opcode), + arg_size - sizeof (opcode), &cbarg->rbuf_size, error)) == NULL) { + cmn_err(CE_WARN, "SmbKdoorSvc: door op failed"); + + switch (*error) { + case SMB_DR_OP_ERR_ENCODE: + *error = EINVAL; + cmn_err(CE_WARN, "SmbKdoorSvc: encode error"); + break; + case SMB_DR_OP_ERR_DECODE: + *error = EINVAL; + cmn_err(CE_WARN, "SmbKdoorSvc: decode error"); + break; + case SMB_DR_OP_ERR_EMPTYBUF: + if ((cbarg->rbuf = smb_dr_set_res_stat( + SMB_DR_OP_ERR_EMPTYBUF, &cbarg->rbuf_size)) + == NULL) { + cmn_err(CE_WARN, "SmbKdoorSvc: return nothing"); + *error = EINVAL; + } + *error = 0; + break; + default: + cmn_err(CE_WARN, "SmbKdoorSvc: unknown error"); + } + } + + kmem_free(argp, arg_size); + dap->data_size = cbarg->rbuf_size; + dap->rbuf = cbarg->rbuf; + *destarg = cbarg; +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_lock_byte_range.c b/usr/src/uts/common/fs/smbsrv/smb_lock_byte_range.c new file mode 100644 index 000000000000..53e8d0326ebf --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_lock_byte_range.c @@ -0,0 +1,100 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: lock_byte_range + * + * The lock record message is sent to lock the given byte range. More than + * one non-overlapping byte range may be locked in a given file. Locks + * prevent attempts to lock, read or write the locked portion of the file + * by other clients or Pids. Overlapping locks are not allowed. Offsets + * beyond the current end of file may be locked. Such locks will not cause + * allocation of file space. + * + * Since Offset is a 32 bit quantity, this request is inappropriate for + * general locking within a very large file. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 5 + * USHORT Fid; File handle + * ULONG Count; Count of bytes to lock + * ULONG Offset; Offset from start of file + * USHORT ByteCount; Count of data bytes = 0 + * + * Locks may only be unlocked by the Pid that performed the lock. + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes = 0 + * + * This client request does not wait for the lock to be granted. If the + * lock can not be immediately granted (within 200-300 ms), the server + * should return failure to the client + */ + +#include + +int +smb_com_lock_byte_range(struct smb_request *sr) +{ + uint32_t count; + uint32_t off; + DWORD result; + + if (smbsr_decode_vwv(sr, "wll", &sr->smb_fid, &count, &off) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + /* + * The last parameter is lock type. This is dependent on + * lock flag (3rd parameter). Since the lock flag is + * set to be exclusive, lock type is passed as + * normal lock (write lock). + */ + result = smb_lock_range(sr, sr->fid_ofile, + (off_t)off, (uint64_t)count, 0, SMB_LOCK_TYPE_READWRITE); + if (result != NT_STATUS_SUCCESS) { + smb_lock_range_raise_error(sr, result); + /* NOT REACHED */ + } + + smbsr_encode_empty_result(sr); + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_lock_svc.c b/usr/src/uts/common/fs/smbsrv/smb_lock_svc.c new file mode 100644 index 000000000000..e043472af530 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_lock_svc.c @@ -0,0 +1,709 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module provides range lock functionality for CIFS/SMB clients. + * Lock range service functions process SMB lock and and unlock + * requests for a file by applying lock rules and marks file range + * as locked if the lock is successful otherwise return proper + * error code. + */ + +#include + +static int +smb_lock_range_overlap(struct smb_lock *lock, uint64_t start, uint64_t length); + +static uint32_t smb_lock_range_lckrules(struct smb_request *sr, + smb_ofile_t *file, struct smb_node *node, smb_lock_t *dlock, + smb_lock_t **clockp); + +static uint32_t +smb_lock_wait(struct smb_request *sr, smb_lock_t *b_lock, smb_lock_t *c_lock); + +static uint32_t +smb_lock_range_ulckrules(struct smb_request *sr, + struct smb_node *node, + uint64_t start, + uint64_t length, + struct smb_lock **nodelock); + +static smb_lock_t *smb_lock_create(smb_request_t *sr, + uint64_t start, uint64_t length, uint32_t locktype, uint32_t timeout); +static void smb_lock_destroy(smb_lock_t *lock); +static void smb_lock_free(smb_lock_t *lock); + +/* + * smb_lock_range_overlap + * + * Checks if lock range(start, length) overlaps + * range in lock structure. + * + * return values: + * 0 - Lock range doesn't overlap + * 1 - Lock range overlaps. + */ + +#define RANGE_NO_OVERLAP 0 +#define RANGE_OVERLAP 1 + +static int +smb_lock_range_overlap(struct smb_lock *lock, uint64_t start, uint64_t length) +{ + /* A zero-length range doesn't overlap anything */ + if (length == 0 || lock->l_length == 0) + return (RANGE_NO_OVERLAP); + + if (start < lock->l_start) { + if (start + length > lock->l_start) + return (RANGE_OVERLAP); + } else if (start < lock->l_start + lock->l_length) + return (RANGE_OVERLAP); + + if (start + length > lock->l_start + lock->l_length) { + if (start < lock->l_start + lock->l_length) + return (RANGE_OVERLAP); + } else if (start + length > lock->l_start) + return (RANGE_OVERLAP); + + /* Lock range doen't overlap */ + return (RANGE_NO_OVERLAP); +} + +/* + * smb_lock_range_lckrules + * + * Lock range rules: + * 1. Overlapping read locks are allowed if the + * current locks in the region are only read locks + * irrespective of pid of smb client issuing lock request. + * + * 2. Read lock in the overlapped region of write lock + * are allowed if the pervious lock is performed by the + * same pid and connection. + * + * return status: + * NT_STATUS_SUCCESS - Input lock range adapts to lock rules. + * NT_STATUS_LOCK_NOT_GRANTED - Input lock conflicts lock rules. + * NT_STATUS_CANCELLED - Error in processing lock rules + */ +static uint32_t +smb_lock_range_lckrules( + struct smb_request *sr, + smb_ofile_t *file, + struct smb_node *node, + smb_lock_t *dlock, + smb_lock_t **clockp) +{ + smb_lock_t *lock; + uint32_t status = NT_STATUS_SUCCESS; + + /* Check if file is closed */ + if (!smb_ofile_is_open(file)) { + return (NT_STATUS_RANGE_NOT_LOCKED); + } + + /* Caller must hold lock for node->n_lock_list */ + for (lock = smb_llist_head(&node->n_lock_list); + lock != NULL; + lock = smb_llist_next(&node->n_lock_list, lock)) { + + if (!smb_lock_range_overlap(lock, dlock->l_start, + dlock->l_length)) + continue; + + /* + * Check to see if lock in the overlapping record + * is only read lock. Current finding is read + * locks can overlapped irrespective of pids. + */ + if ((lock->l_type == SMB_LOCK_TYPE_READONLY) && + (dlock->l_type == SMB_LOCK_TYPE_READONLY)) { + continue; + } + + /* + * When the read lock overlaps write lock, check if + * allowed. + */ + if ((dlock->l_type == SMB_LOCK_TYPE_READONLY) && + !(lock->l_type == SMB_LOCK_TYPE_READONLY)) { + if (lock->l_file == sr->fid_ofile && + lock->l_session_kid == sr->session->s_kid && + lock->l_pid == sr->smb_pid && + lock->l_uid == sr->smb_uid) { + continue; + } + } + + /* Conflict in overlapping lock element */ + *clockp = lock; + status = NT_STATUS_LOCK_NOT_GRANTED; + break; + } + + return (status); +} + +/* + * smb_lock_wait + * + * Wait operation for smb overlapping lock to be released. Caller must hold + * write lock for node->n_lock_list so that the set of active locks can't + * change unexpectedly. The lock for node->n_lock_list will be released + * within this function during the sleep after the lock dependency has + * been recorded. + * + * return value + * + * NT_STATUS_CANCELLED Error occurred during wait operation + * NT_STATUS_SUCCESS Wait completed. + */ +static uint32_t +smb_lock_wait(smb_request_t *sr, smb_lock_t *b_lock, smb_lock_t *c_lock) +{ + clock_t result; + uint32_t status = NT_STATUS_SUCCESS; + + ASSERT(sr->sr_awaiting == NULL); + + mutex_enter(&sr->sr_mutex); + + switch (sr->sr_state) { + case SMB_REQ_STATE_ACTIVE: + /* + * Wait up till the timeout time keeping track of actual + * time waited for possible retry failure. + */ + sr->sr_state = SMB_REQ_STATE_WAITING_LOCK; + sr->sr_awaiting = c_lock; + mutex_exit(&sr->sr_mutex); + + mutex_enter(&c_lock->l_mutex); + /* + * The conflict list (l_conflict_list) for a lock contains + * all the locks that are blocked by and in conflict with + * that lock. Add the new lock to the conflict list for the + * active lock. + * + * l_conflict_list is currently a fancy way of representing + * the references/dependencies on a lock. It could be + * replaced with a reference count but this approach + * has the advantage that MDB can display the lock + * dependencies at any point in time. In the future + * we should be able to leverage the list to implement + * an asynchronous locking model. + * + * l_blocked_by is the reverse of the conflict list. It + * points to the lock that the new lock conflicts with. + * As currently implemented this value is purely for + * debug purposes -- there are windows of time when + * l_blocked_by may be non-NULL even though there is no + * conflict list + */ + b_lock->l_blocked_by = c_lock; + smb_slist_insert_tail(&c_lock->l_conflict_list, b_lock); + smb_llist_exit(&c_lock->l_file->f_node->n_lock_list); + + /* + * XXX Hack.. drop s_lock to avoid blocking subsequent SMBs + * that might affect the state of this lock (i.e. + * smb_com_close). We shouldn't sleep while holding + * locks anyway. + */ + smb_rwx_rwexit(&sr->session->s_lock); + + if (SMB_LOCK_INDEFINITE_WAIT(b_lock)) { + cv_wait(&c_lock->l_cv, &c_lock->l_mutex); + } else { + result = cv_timedwait(&c_lock->l_cv, + &c_lock->l_mutex, b_lock->l_end_time); + if (result == -1) { + status = NT_STATUS_CANCELLED; + } + } + + /* + * XXX Hack continued from above... re-acquire s_lock + * OK to hardcode RW_READER since this is just a hack and + * we really should yank it out and do something else. + */ + smb_rwx_rwenter(&sr->session->s_lock, RW_READER); + + mutex_exit(&c_lock->l_mutex); + + smb_llist_enter(&c_lock->l_file->f_node->n_lock_list, + RW_WRITER); + smb_slist_remove(&c_lock->l_conflict_list, b_lock); + + mutex_enter(&sr->sr_mutex); + sr->sr_awaiting = NULL; + if (sr->sr_state == SMB_REQ_STATE_CANCELED) { + status = NT_STATUS_CANCELLED; + } else { + sr->sr_state = SMB_REQ_STATE_ACTIVE; + } + break; + + case SMB_REQ_STATE_CANCELED: + status = NT_STATUS_CANCELLED; + mutex_exit(&sr->sr_mutex); + break; + + default: + ASSERT(0); + break; + } + + mutex_exit(&sr->sr_mutex); + + return (status); +} + +/* + * smb_lock_range_ulckrules + * + * 1. Unlock should be performed at exactly matching ends. + * This has been changed because overlapping ends is + * allowed and there is no other precise way of locating + * lock entity in node lock list. + * + * 2. Unlock is failed if there is no corresponding lock exists. + * + * Return values + * + * NT_STATUS_SUCCESS Unlock request matches lock record + * pointed by 'nodelock' lock structure. + * + * NT_STATUS_RANGE_NOT_LOCKED Unlock request doen't match any + * of lock record in node lock request or + * error in unlock range processing. + */ +static uint32_t +smb_lock_range_ulckrules( + struct smb_request *sr, + struct smb_node *node, + uint64_t start, + uint64_t length, + struct smb_lock **nodelock) +{ + smb_lock_t *lock; + uint32_t status = NT_STATUS_RANGE_NOT_LOCKED; + + /* Caller must hold lock for node->n_lock_list */ + for (lock = smb_llist_head(&node->n_lock_list); + lock != NULL; + lock = smb_llist_next(&node->n_lock_list, lock)) { + + if ((start == lock->l_start) && + (length == lock->l_length) && + lock->l_file == sr->fid_ofile && + lock->l_session_kid == sr->session->s_kid && + lock->l_pid == sr->smb_pid && + lock->l_uid == sr->smb_uid) { + *nodelock = lock; + status = NT_STATUS_SUCCESS; + break; + } + } + + return (status); +} + + +/* + * smb_unlock_range + * + * locates lock range performed for corresponding to unlock request. + * + * NT_STATUS_SUCCESS - Lock range performed successfully. + * !NT_STATUS_SUCCESS - Error in unlock range operation. + */ +uint32_t +smb_unlock_range( + struct smb_request *sr, + struct smb_node *node, + uint64_t start, + uint64_t length) +{ + struct smb_lock *lock = 0; + uint32_t status; + + /* Apply unlocking rules */ + smb_llist_enter(&node->n_lock_list, RW_WRITER); + status = smb_lock_range_ulckrules(sr, node, start, length, &lock); + if (status != NT_STATUS_SUCCESS) { + /* + * If lock range is not matching in the list + * return error. + */ + ASSERT(lock == 0); + smb_llist_exit(&node->n_lock_list); + return (status); + } + + smb_llist_remove(&node->n_lock_list, lock); + smb_llist_exit(&node->n_lock_list); + + smb_lock_destroy(lock); + + return (status); +} + +/* + * smb_lock_range + * + * checks for integrity of + * file lock operation for the given range of file data. + * This is performed by applying lock rules with all + * the elements of the node lock list. + * + * The function returns with new lock added if lock + * request is non-conflicting with existing range + * lock for the file. Otherwise smb request is filed + * without returning. + * + * NT_STATUS_SUCCESS - Lock range performed successfully. + * !NT_STATUS_SUCCESS - Error in lock range operation. + */ +uint32_t +smb_lock_range( + struct smb_request *sr, + smb_ofile_t *file, + uint64_t start, + uint64_t length, + uint32_t timeout, + uint32_t locktype) +{ + smb_node_t *node = file->f_node; + smb_lock_t *lock; + smb_lock_t *clock = 0; + uint32_t result = NT_STATUS_SUCCESS; + + lock = smb_lock_create(sr, start, length, locktype, timeout); + + smb_llist_enter(&node->n_lock_list, RW_WRITER); + for (;;) { + /* Apply locking rules */ + result = smb_lock_range_lckrules(sr, file, node, lock, &clock); + + if ((result == NT_STATUS_CANCELLED) || + (result == NT_STATUS_SUCCESS) || + (result == NT_STATUS_RANGE_NOT_LOCKED)) { + ASSERT(clock == 0); + break; + } else { + ASSERT(result == NT_STATUS_LOCK_NOT_GRANTED); + ASSERT(clock); + /* + * Call smb_lock_wait holding write lock for + * node lock list. smb_lock_wait will release + * this lock if it blocks. + */ + ASSERT(node == clock->l_file->f_node); + if ((timeout == 0) || + ((result = smb_lock_wait(sr, lock, clock)) != + NT_STATUS_SUCCESS)) { + break; + } + clock = 0; + } + } + + lock->l_blocked_by = NULL; + + if (result != NT_STATUS_SUCCESS) { + /* + * Under certain conditions NT_STATUS_FILE_LOCK_CONFLICT + * should be returned instead of NT_STATUS_LOCK_NOT_GRANTED. + */ + if (result == NT_STATUS_LOCK_NOT_GRANTED) { + /* + * Locks with timeouts always return + * NT_STATUS_FILE_LOCK_CONFLICT + */ + if (timeout != 0) { + result = NT_STATUS_FILE_LOCK_CONFLICT; + } + + /* + * Locks starting higher than 0xef000000 that do not + * have the MSB set always return + * NT_STATUS_FILE_LOCK_CONFLICT + */ + if ((lock->l_start >= 0xef000000) && + !(lock->l_start & (1ULL << 63))) { + result = NT_STATUS_FILE_LOCK_CONFLICT; + } + + /* + * If the last lock attempt to fail on this file handle + * started at the same offset as this one then return + * NT_STATUS_FILE_LOCK_CONFLICT + */ + mutex_enter(&file->f_mutex); + if ((file->f_flags & SMB_OFLAGS_LLF_POS_VALID) && + (lock->l_start == file->f_llf_pos)) { + result = NT_STATUS_FILE_LOCK_CONFLICT; + } + mutex_exit(&file->f_mutex); + } + + /* Update last lock failed offset */ + mutex_enter(&file->f_mutex); + file->f_llf_pos = lock->l_start; + file->f_flags |= SMB_OFLAGS_LLF_POS_VALID; + mutex_exit(&file->f_mutex); + + smb_lock_free(lock); + } else { + smb_llist_insert_tail(&node->n_lock_list, lock); + } + smb_llist_exit(&node->n_lock_list); + + return (result); +} + + +/* + * smb_lock_range_access + * + * scans node lock list + * to check if there is any overlapping lock. Overlapping + * lock is allowed only under same session and client pid. + * + * Return values + * NT_STATUS_SUCCESS lock access granted. + * NT_STATUS_FILE_LOCK_CONFLICT access denied due to lock conflict. + */ +int +smb_lock_range_access( + struct smb_request *sr, + struct smb_node *node, + uint64_t start, + uint64_t length, + uint32_t desired_access) +{ + smb_lock_t *lock; + smb_llist_t *llist; + int status = NT_STATUS_SUCCESS; + + ASSERT((desired_access & ~(FILE_READ_DATA | FILE_WRITE_DATA)) == 0); + ASSERT((desired_access & (FILE_READ_DATA | FILE_WRITE_DATA)) != 0); + + llist = &node->n_lock_list; + smb_llist_enter(llist, RW_READER); + /* Search for any applicable lock */ + for (lock = smb_llist_head(llist); + lock != 0; + lock = smb_llist_next(llist, lock)) { + + if (!smb_lock_range_overlap(lock, start, length)) + /* Lock does not overlap */ + continue; + + if (lock->l_type == SMB_LOCK_TYPE_READONLY && + desired_access == FILE_READ_DATA) + continue; + + if (lock->l_type == SMB_LOCK_TYPE_READWRITE && + lock->l_session_kid == sr->session->s_kid && + lock->l_pid == sr->smb_pid) + continue; + + status = NT_STATUS_FILE_LOCK_CONFLICT; + break; + } + smb_llist_exit(llist); + return (status); +} + +static smb_lock_t * +smb_lock_create( + smb_request_t *sr, + uint64_t start, + uint64_t length, + uint32_t locktype, + uint32_t timeout) +{ + smb_lock_t *lock; + + ASSERT(locktype == SMB_LOCK_TYPE_READWRITE || + locktype == SMB_LOCK_TYPE_READONLY); + + lock = kmem_zalloc(sizeof (smb_lock_t), KM_SLEEP); + lock->l_magic = SMB_LOCK_MAGIC; + lock->l_sr = sr; /* Invalid after lock is active */ + lock->l_session_kid = sr->session->s_kid; + lock->l_session = sr->session; + lock->l_file = sr->fid_ofile; + lock->l_uid = sr->smb_uid; + lock->l_pid = sr->smb_pid; + lock->l_type = locktype; + lock->l_start = start; + lock->l_length = length; + /* + * Calculate the absolute end time so that we can use it + * in cv_timedwait. + */ + lock->l_end_time = lbolt + MSEC_TO_TICK(timeout); + if (timeout == 0xffffffff) { + lock->l_flags |= SMB_LOCK_FLAG_INDEFINITE; + } + mutex_init(&lock->l_mutex, NULL, MUTEX_DEFAULT, NULL); + cv_init(&lock->l_cv, NULL, CV_DEFAULT, NULL); + smb_slist_constructor(&lock->l_conflict_list, sizeof (smb_lock_t), + offsetof(smb_lock_t, l_conflict_lnd)); + + return (lock); +} + +static void +smb_lock_free(smb_lock_t *lock) +{ + smb_slist_destructor(&lock->l_conflict_list); + cv_destroy(&lock->l_cv); + mutex_destroy(&lock->l_mutex); + + kmem_free(lock, sizeof (smb_lock_t)); +} + +/* + * smb_lock_destroy + * + * Caller must hold node->n_lock_list + */ +static void +smb_lock_destroy(smb_lock_t *lock) +{ + /* + * Caller must hold node->n_lock_list lock. + */ + mutex_enter(&lock->l_mutex); + cv_broadcast(&lock->l_cv); + mutex_exit(&lock->l_mutex); + + /* + * The cv_broadcast above should wake up any locks that previous + * had conflicts with this lock. Wait for the locking threads + * to remove their references to this lock. + */ + smb_slist_wait_for_empty(&lock->l_conflict_list); + + smb_lock_free(lock); +} + +void +smb_node_destroy_lock_by_ofile(smb_node_t *node, smb_ofile_t *file) +{ + smb_lock_t *lock; + smb_lock_t *nxtl; + list_t destroy_list; + + ASSERT(node); + ASSERT(node->n_magic == SMB_NODE_MAGIC); + ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); + ASSERT(node->n_refcnt); + + /* + * Move locks matching the specified file from the node->n_lock_list + * to a temporary list (holding the lock the entire time) then + * destroy all the matching locks. We can't call smb_lock_destroy + * while we are holding the lock for node->n_lock_list because we will + * deadlock and we can't drop the lock because the list contents might + * change (for example nxtl might get removed on another thread). + */ + list_create(&destroy_list, sizeof (smb_lock_t), + offsetof(smb_lock_t, l_lnd)); + + smb_llist_enter(&node->n_lock_list, RW_WRITER); + lock = smb_llist_head(&node->n_lock_list); + while (lock) { + nxtl = smb_llist_next(&node->n_lock_list, lock); + if (lock->l_file == file) { + smb_llist_remove(&node->n_lock_list, lock); + list_insert_tail(&destroy_list, lock); + } + lock = nxtl; + } + smb_llist_exit(&node->n_lock_list); + + lock = list_head(&destroy_list); + while (lock) { + nxtl = list_next(&destroy_list, lock); + list_remove(&destroy_list, lock); + smb_lock_destroy(lock); + lock = nxtl; + } + + list_destroy(&destroy_list); +} + +void +smb_lock_range_raise_error(smb_request_t *sr, uint32_t ntstatus) +{ + switch (ntstatus) { + case NT_STATUS_CANCELLED: + /* + * XXX What is the proper error here? + */ + smbsr_raise_error(sr, ERRDOS, ERRlock); + /* NOTREACHED */ + case NT_STATUS_FILE_LOCK_CONFLICT: + smbsr_raise_cifs_error(sr, NT_STATUS_FILE_LOCK_CONFLICT, + ERRDOS, ERRlock); + /* NOTREACHED */ + case NT_STATUS_LOCK_NOT_GRANTED: + smbsr_raise_cifs_error(sr, NT_STATUS_LOCK_NOT_GRANTED, + ERRDOS, ERRlock); + /* NOTREACHED */ + case NT_STATUS_RANGE_NOT_LOCKED: + smbsr_raise_cifs_error(sr, NT_STATUS_RANGE_NOT_LOCKED, + ERRDOS, ERRlock); + /* NOTREACHED */ + default: + ASSERT(0); + smbsr_raise_error(sr, ERRDOS, ntstatus); + /* NOTREACHED */ + } +} + + +void +smb_unlock_range_raise_error(smb_request_t *sr, uint32_t ntstatus) +{ + switch (ntstatus) { + case NT_STATUS_RANGE_NOT_LOCKED: + smbsr_raise_cifs_error(sr, NT_STATUS_RANGE_NOT_LOCKED, + ERRDOS, ERRnotlocked); + /* NOTREACHED */ + default: + ASSERT(0); + smbsr_raise_error(sr, ERRDOS, ntstatus); + /* NOTREACHED */ + } +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_locking_andx.c b/usr/src/uts/common/fs/smbsrv/smb_locking_andx.c new file mode 100644 index 000000000000..0acf7922c445 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_locking_andx.c @@ -0,0 +1,378 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: locking_andx + * + * SMB_COM_LOCKING_ANDX allows both locking and/or unlocking of file range(s). + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 8 + * UCHAR AndXCommand; Secondary (X) command; 0xFF = none + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * USHORT Fid; File handle + * UCHAR LockType; See LockType table below + * UCHAR OplockLevel; The new oplock level + * ULONG Timeout; Milliseconds to wait for unlock + * USHORT NumberOfUnlocks; Num. unlock range structs following + * USHORT NumberOfLocks; Num. lock range structs following + * USHORT ByteCount; Count of data bytes + * LOCKING_ANDX_RANGE Unlocks[]; Unlock ranges + * LOCKING_ANDX_RANGE Locks[]; Lock ranges + * + * LockType Flag Name Value Description + * ============================ ===== ================================ + * + * LOCKING_ANDX_SHARED_LOCK 0x01 Read-only lock + * LOCKING_ANDX_OPLOCK_RELEASE 0x02 Oplock break notification + * LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 Change lock type + * LOCKING_ANDX_CANCEL_LOCK 0x08 Cancel outstanding request + * LOCKING_ANDX_LARGE_FILES 0x10 Large file locking format + * + * LOCKING_ANDX_RANGE Format + * ===================================================================== + * + * USHORT Pid; PID of process "owning" lock + * ULONG Offset; Offset to bytes to [un]lock + * ULONG Length; Number of bytes to [un]lock + * + * Large File LOCKING_ANDX_RANGE Format + * ===================================================================== + * + * USHORT Pid; PID of process "owning" lock + * USHORT Pad; Pad to DWORD align (mbz) + * ULONG OffsetHigh; Offset to bytes to [un]lock + * (high) + * ULONG OffsetLow; Offset to bytes to [un]lock (low) + * ULONG LengthHigh; Number of bytes to [un]lock + * (high) + * ULONG LengthLow; Number of bytes to [un]lock (low) + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 2 + * UCHAR AndXCommand; Secondary (X) command; 0xFF = + * none + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * USHORT ByteCount; Count of data bytes = 0 + * + * Locking is a simple mechanism for excluding other processes read/write + * access to regions of a file. The locked regions can be anywhere in the + * logical file. Locking beyond end-of-file is permitted. Any process + * using the Fid specified in this request's Fid has access to the locked + * bytes, other processes will be denied the locking of the same bytes. + * + * The proper method for using locks is not to rely on being denied read or + * write access on any of the read/write protocols but rather to attempt + * the locking protocol and proceed with the read/write only if the locks + * succeeded. + * + * Locking a range of bytes will fail if any subranges or overlapping + * ranges are locked. In other words, if any of the specified bytes are + * already locked, the lock will fail. + * + * If NumberOfUnlocks is non-zero, the Unlocks vector contains + * NumberOfUnlocks elements. Each element requests that a lock at Offset + * of Length be released. If NumberOfLocks is nonzero, the Locks vector + * contains NumberOfLocks elements. Each element requests the acquisition + * of a lock at Offset of Length. + * + * Timeout is the maximum amount of time to wait for the byte range(s) + * specified to become unlocked. A timeout value of 0 indicates that the + * server should fail immediately if any lock range specified is locked. A + * + * timeout value of -1 indicates that the server should wait as long as it + * takes for each byte range specified to become unlocked so that it may be + * again locked by this protocol. Any other value of smb_timeout specifies + * the maximum number of milliseconds to wait for all lock range(s) + * specified to become available. + * + * If any of the lock ranges timeout because of the area to be locked is + * already locked (or the lock fails), the other ranges in the protocol + * request which were successfully locked as a result of this protocol will + * be unlocked (either all requested ranges will be locked when this + * protocol returns to the client or none). + * + * If LockType has the LOCKING_ANDX_SHARED_LOCK flag set, the lock is + * specified as a shared lock. Locks for both read and write (where + * LOCKING_ANDX_SHARED_LOCK is clear) should be prohibited, but other + * shared locks should be permitted. If shared locks can not be supported + * by a server, the server should map the lock to a lock for both read and + * write. Closing a file with locks still in force causes the locks to be + * released in no defined order. + * + * If LockType has the LOCKING_ANDX_LARGE_FILES flag set and if the + * negotiated protocol is NT LM 0.12 or later, then the Locks and Unlocks + * vectors are in the Large File LOCKING_ANDX_RANGE format. This allows + * specification of 64 bit offsets for very large files. + * + * If the one and only member of the Locks vector has the + * LOCKING_ANDX_CANCEL_LOCK flag set in the LockType field, the client is + * requesting the server to cancel a previously requested, but not yet + * responded to, lock. + * + * If LockType has the LOCKING_ANDX_CHANGE_LOCKTYPE flag set, the client is + * requesting that the server atomically change the lock type from a shared + * lock to an exclusive lock or vice versa. If the server can not do this + * in an atomic fashion, the server must reject this request. NT and W95 + * servers do not support this capability. + * + * Oplocks are described in the "Opportunistic Locks" section elsewhere in + * this document. A client requests an oplock by setting the appropriate + * bit in the SMB_COM_OPEN_ANDX request when the file is being opened in a + * mode which is not exclusive. The server responds by setting the + * appropriate bit in the response SMB indicating whether or not the oplock + * was granted. By granting the oplock, the server tells the client the + * file is currently only being used by this one client process at the + * current time. The client can therefore safely do read ahead and write + * behind as well as local caching of file locks knowing that the file will + * not be accessed/changed in any way by another process while the oplock + * is in effect. The client will be notified when any other process + * attempts to open or modify the oplocked file. + * + * When another user attempts to open or otherwise modify the file which a + * client has oplocked, the server delays the second attempt and notifies + * the client via an SMB_LOCKING_ANDX SMB asynchronously sent from the + * server to the client. This message has the LOCKING_ANDX_OPLOCK_RELEASE + * flag set indicating to the client that the oplock is being broken. + * + * OplockLevel indicates the type of oplock the client now owns. If + * OplockLevel is 0, the client possesses no oplocks on the file at all, if + * OplockLevel is 1 the client possesses a Level II oplock. The client is + * expected to flush any dirty buffers to the server, submit any file locks + * and respond to the server with either an SMB_LOCKING_ANDX SMB having the + * LOCKING_ANDX_OPLOCK_RELEASE flag set, or with a file close if the file + * is no longer in use by the client. If the client sends an + * SMB_LOCKING_ANDX SMB with the LOCKING_ANDX_OPLOCK_RELEASE flag set and + * NumberOfLocks is zero, the server does not send a response. Since a + * close being sent to the server and break oplock notification from the + * server could cross on the wire, if the client gets an oplock + * notification on a file which it does not have open, that notification + * should be ignored. + * + * Due to timing, the client could get an "oplock broken" notification in a + * user's data buffer as a result of this notification crossing on the wire + * with a SMB_COM_READ_RAW request. The client must detect this (use + * length of msg, "FFSMB", MID of -1 and Command of SMB_COM_LOCKING_ANDX) + * and honor the "oplock broken" notification as usual. The server must + * also note on receipt of an SMB_COM_READ_RAW request that there is an + * outstanding (unanswered) "oplock broken" notification to the client and + * return a zero length response denoting failure of the read raw request. + * The client should (after responding to the "oplock broken" + * notification), use a standard read protocol to redo the read request. + * This allows a file to actually contain data matching an "oplock broken" + * notification and still be read correctly. + * + * The entire message sent and received including the optional second + * protocol must fit in the negotiated maximum transfer size. The + * following are the only valid SMB commands for AndXCommand for + * SMB_COM_LOCKING_ANDX: + * + * SMB_COM_READ SMB_COM_READ_ANDX + * SMB_COM_WRITE SMB_COM_WRITE_ANDX + * SMB_COM_FLUSH + * + * 4.2.6.1 Errors + * + * ERRDOS/ERRbadfile + * ERRDOS/ERRbadfid + * ERRDOS/ERRlock + * ERRDOS/ERRinvdevice + * ERRSRV/ERRinvid + * ERRSRV/ERRbaduid + */ + +#include + +int +smb_com_locking_andx(struct smb_request *sr) +{ + unsigned short i; + unsigned char lock_type; /* See lock_type table above */ + unsigned char oplock_level; /* The new oplock level */ + uint32_t timeout; /* Milliseconds to wait for lock */ + unsigned short unlock_num; /* # unlock range structs */ + unsigned short lock_num; /* # lock range structs */ + unsigned short pid; /* Process Id of owner */ + uint32_t offset32, length32; + uint64_t offset64; + uint64_t length64; + DWORD result; + int rc; + uint32_t ltype; + + rc = smbsr_decode_vwv(sr, "4.wbblww", &sr->smb_fid, &lock_type, + &oplock_level, &timeout, &unlock_num, &lock_num); + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + if (lock_type & LOCKING_ANDX_SHARED_LOCK) + ltype = SMB_LOCK_TYPE_READONLY; + else + ltype = SMB_LOCK_TYPE_READWRITE; + + pid = sr->smb_pid; /* Save the original pid */ + + if (lock_type & LOCKING_ANDX_OPLOCK_RELEASE) { + smb_release_oplock(sr->fid_ofile, OPLOCK_RELEASE_LOCK_RELEASED); + + /* + * According to the protocol: + * + * If the client sends an SMB_LOCKING_ANDX request with the + * LOCKING_ANDX_OPLOCK_RELEASE flag set + * and NumberOfLocks is zero, + * the server does not send a response. + * + * I'm not sure if it's going to break anything if I change + * it according to the protocol. So, I leave it unchanged + * for now. + */ + if (unlock_num == 0 && lock_num == 0) + return (SDRC_NO_REPLY); + } + + /* + * No support for changing locktype (although we could probably + * implement this) + */ + if (lock_type & LOCKING_ANDX_CHANGE_LOCK_TYPE) { + smbsr_raise_error(sr, ERRDOS, ERRnoatomiclocks); + /* NOT REACHED */ + } + + /* + * No support for cancel lock (smbtorture expects this) + */ + if (lock_type & LOCKING_ANDX_CANCEL_LOCK) { + smbsr_raise_cifs_error(sr, + NT_STATUS_INVALID_PARAMETER, + ERRDOS, ERROR_INVALID_PARAMETER); + /* NOT REACHED */ + } + + if (lock_type & LOCKING_ANDX_LARGE_FILES) { + /* + * negotiated protocol should be NT LM 0.12 or later + */ + if (sr->session->dialect < NT_LM_0_12) { + smbsr_raise_cifs_error(sr, + NT_STATUS_INVALID_PARAMETER, + ERRDOS, ERROR_INVALID_PARAMETER); + /* NOT REACHED */ + } + + for (i = 0; i < unlock_num; i++) { + rc = smb_decode_mbc(&sr->smb_data, "w2.QQ", + &sr->smb_pid, &offset64, &length64); + if (rc) { + /* + * This is the error returned by a W2K system + * even when NT Status is negotiated. + */ + smbsr_raise_error(sr, ERRSRV, ERRerror); + /* NOT REACHED */ + } + + result = smb_unlock_range(sr, sr->fid_ofile->f_node, + offset64, length64); + if (result != NT_STATUS_SUCCESS) { + smb_unlock_range_raise_error(sr, result); + /* NOT REACHED */ + } + } + + for (i = 0; i < lock_num; i++) { + rc = smb_decode_mbc(&sr->smb_data, "w2.QQ", + &sr->smb_pid, &offset64, &length64); + if (rc) { + smbsr_raise_error(sr, ERRSRV, ERRerror); + /* NOT REACHED */ + } + + result = smb_lock_range(sr, sr->fid_ofile, + offset64, length64, timeout, ltype); + if (result != NT_STATUS_SUCCESS) { + smb_lock_range_raise_error(sr, result); + /* NOT REACHED */ + } + } + } else { + for (i = 0; i < unlock_num; i++) { + rc = smb_decode_mbc(&sr->smb_data, "wll", &sr->smb_pid, + &offset32, &length32); + if (rc) { + smbsr_raise_error(sr, ERRSRV, ERRerror); + /* NOT REACHED */ + } + + result = smb_unlock_range(sr, sr->fid_ofile->f_node, + (uint64_t)offset32, (uint64_t)length32); + if (result != NT_STATUS_SUCCESS) { + smb_unlock_range_raise_error(sr, result); + /* NOT REACHED */ + } + } + + for (i = 0; i < lock_num; i++) { + rc = smb_decode_mbc(&sr->smb_data, "wll", &sr->smb_pid, + &offset32, &length32); + if (rc) { + smbsr_raise_error(sr, ERRSRV, ERRerror); + /* NOT REACHED */ + } + + result = smb_lock_range(sr, sr->fid_ofile, + (uint64_t)offset32, + (uint64_t)length32, + timeout, ltype); + if (result != NT_STATUS_SUCCESS) { + smb_lock_range_raise_error(sr, result); + /* NOT REACHED */ + } + } + } + + sr->smb_pid = pid; + smbsr_encode_result(sr, 2, 0, "bb.ww", 2, sr->andx_com, 7, 0); + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_logoff_andx.c b/usr/src/uts/common/fs/smbsrv/smb_logoff_andx.c new file mode 100644 index 000000000000..278916b8fd9d --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_logoff_andx.c @@ -0,0 +1,78 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + + +/* + * smb_com_logoff_andx + * + * This SMB is the inverse of SMB_COM_SESSION_SETUP_ANDX. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 2 + * UCHAR AndXCommand; Secondary (X) command; 0xFF = none + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * USHORT ByteCount; Count of data bytes = 0 + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 2 + * UCHAR AndXCommand; Secondary (X) command; 0xFF = none + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * USHORT ByteCount; Count of data bytes = 0 + * + * The user represented by Uid in the SMB header is logged off. The server + * closes all files currently open by this user, and invalidates any + * outstanding requests with this Uid. + * + * SMB_COM_SESSION_SETUP_ANDX is the only valid AndX command for this SMB. + * + * 4.1.3.1 Errors + * + * ERRSRV/invnid - TID was invalid + * ERRSRV/baduid - UID was invalid + */ +int +smb_com_logoff_andx(struct smb_request *sr) +{ + if (sr->uid_user == NULL) { + cmn_err(CE_WARN, "SmbLogoffAndX: bad uid"); + smbsr_raise_error(sr, ERRSRV, ERRbaduid); + /* NOTREACHED */ + } + + smb_user_logoff(sr->uid_user); + + smbsr_encode_result(sr, 2, 0, "bb.ww", 2, sr->andx_com, -1, 0); + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_mangle_name.c b/usr/src/uts/common/fs/smbsrv/smb_mangle_name.c new file mode 100644 index 000000000000..21a4b42939c2 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_mangle_name.c @@ -0,0 +1,777 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int smb_match_unknown(char *name, char *pattern); +static int smb_is_reserved_dos_name(char *name); +static int smb_match_reserved(char *name, char *rsrv); + +/* + * smb_match_name + * + * This function will mangle the "name" field and save the resulted + * shortname to the "shortname" field and 8.3 name to "name83" field. + * The three fields, "name", "shortname" and "name83" will then be + * sent for pattern match with "pattern" field. + * + * The 0 is returned when the name is a reserved dos name, no match + * for the pattern or any type of failure. The 1 is returned when + * there is a match. + */ +int +smb_match_name(ino64_t fileid, char *name, char *shortname, + char *name83, char *pattern, int ignore_case) +{ + int rc = 0; + int force; + + /* Leading or trailing dots are disallowed */ + if (smb_is_reserved_dos_name(name)) + return (0); + + for (force = 0; (force < 2 && rc == 0); force++) { + (void) smb_mangle_name(fileid, name, shortname, name83, force); + + rc = smb_match_ci(pattern, name); + + /* If no match, check for shortname (if any) */ + + if (rc == 0 && strchr(pattern, '~')) + if (*shortname != 0) + rc = smb_match_ci(pattern, shortname); + + /* + * Sigh... DOS Shells use short name + * interchangeably with long case sensitive + * names. So check that too... + */ + if ((rc == 0) && !ignore_case) + rc = smb_match83(pattern, name83); + + /* + * Still not found and potentially a premangled name... + * Check to see if the butt-head programmer is + * assuming that we mangle names in the same manner + * as NT... + */ + if (rc == 0) + rc = smb_match_unknown(name, pattern); + } + + return (rc); +} + +/* + * smb_match_unknown + * + * I couldn't figure out what the assumptions of this peice of + * code about the format of pattern and name are and so how + * it's trying to match them. I just cleaned it up a little bit! + * + * If anybody could figure out what this is doing, please put + * comment here and change the function's name! + */ +static int +smb_match_unknown(char *name, char *pattern) +{ + int rc; + char nc, pc; + char *np, *pp; + + rc = 0; + if (utf8_isstrupr(pattern) <= 0) + return (rc); + + np = name; + pp = pattern; + + pc = *pattern; + while ((nc = *np++) != 0) { + if (nc == ' ') + continue; + + nc = mts_toupper(nc); + if ((pc = *pp++) != nc) + break; + } + + if ((pc == '~') && + (pp != (pattern + 1)) && + ((pc = *pp++) != 0)) { + while (mts_isdigit(pc)) + pc = *pp++; + + if (pc == '.') { + while ((nc = *np++) != 0) { + if (nc == '.') + break; + } + + while ((nc = *np++) != 0) { + nc = mts_toupper(nc); + if ((pc = *pp++) != nc) + break; + } + } + + if (pc == 0) + rc = 1; + } + + return (rc); +} + +/* + * smb_match_reserved + * + * Checks if the given name matches given + * DOS reserved name prefix. + * + * Returns 1 if match, 0 otherwise + */ +static int +smb_match_reserved(char *name, char *rsrv) +{ + char ch; + + int len = strlen(rsrv); + return (!utf8_strncasecmp(rsrv, name, len) && + ((ch = *(name + len)) == 0 || ch == '.')); +} + +/* + * smb_is_reserved_dos_name + * + * This function checks if the name is a reserved dos name. + * + * The function returns 1 when the name is a reserved dos name; + * otherwise, it returns 0. + */ +static int +smb_is_reserved_dos_name(char *name) +{ + char ch; + + /* + * Eliminate all names reserved by DOS and Windows. + */ + ch = mts_toupper(*name); + + switch (ch) { + case 'A': + if (smb_match_reserved(name, "AUX")) + return (1); + break; + + case 'C': + if (smb_match_reserved(name, "CLOCK$") || + smb_match_reserved(name, "COM1") || + smb_match_reserved(name, "COM2") || + smb_match_reserved(name, "COM3") || + smb_match_reserved(name, "COM4") || + smb_match_reserved(name, "CON")) { + return (1); + } + + break; + + case 'L': + if ((utf8_strncasecmp("LPT1", name, 4) == 0) || + (utf8_strncasecmp("LPT2", name, 4) == 0) || + (utf8_strncasecmp("LPT3", name, 4) == 0)) + return (1); + break; + + case 'N': + if (smb_match_reserved(name, "NUL")) + return (1); + break; + + case 'P': + if (smb_match_reserved(name, "PRN")) + return (1); + } + + /* + * If the server is configured to support Catia Version 5 + * deployments, any filename that contains backslash will + * have already been translated to the UTF-8 encoding of + * Latin Small Letter Y with Diaeresis. Thus, the check + * for backslash in the filename is not necessary. + */ +#ifdef CATIA_SUPPORT + /* XXX Catia support */ + if ((get_caps() & NFCAPS_CATIA) == 0) { + while (*name != 0) { + if (*name == '\\') + return (1); + name++; + } + } +#endif /* CATIA_SUPPORT */ + + return (0); +} + +/* + * Characters we don't allow in DOS file names. + * If a filename contains any of these chars, it should + * get mangled. + * + * '.' is also an invalid DOS char but since it's a special + * case it doesn't appear in the list. + */ +static char *invalid_dos_chars = + "\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017" + "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" + " \"/\\:|<>*?"; + +/* + * According to MSKB article #142982, Windows deletes invalid chars and + * spaces from file name in mangling process; and invalid chars include: + * ."/\[]:;=, + * + * But some of these chars and some other chars (e.g. +) are replaced + * with underscore (_). They are introduced here as special chars. + */ +static char *special_chars = "[];=,+"; + +#define isinvalid(c) (strchr(invalid_dos_chars, c) || (c & 0x80)) + +/* + * smb_needs_mangle + * + * Determines whether the given name needs to get mangled. + * + * Here are the (known) rules: + * + * 1st char is dot (.) + * name length > 12 chars + * # dots > 1 + * # dots == 0 and length > 8 + * # dots == 1 and name isn't 8.3 + * contains illegal chars + */ +int +smb_needs_mangle(char *name, char **dot_pos) +{ + int len, ndots; + char *namep; + char *last_dot; + + /* + * Returning (1) for these cases forces consistency with how + * these names are treated (smb_mangle_name() will produce an 8.3 name + * for these) + */ + if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) + return (1); + + /* skip the leading dots (if any) */ + for (namep = name; *namep == '.'; namep++) + ; + + len = ndots = 0; + last_dot = 0; + for (; *namep; namep++) { + len++; + if (*namep == '.') { + /* keep the position of last dot */ + last_dot = namep; + ndots++; + } + } + *dot_pos = last_dot; + + /* Windows mangles names like .a, .abc, or .abcd */ + if (*name == '.') + return (1); + + if (len > 12) + return (1); + + switch (ndots) { + case 0: + /* no dot */ + if (len > 8) + return (1); + break; + + case 1: + /* just one dot */ + /*LINTED E_PTR_DIFF_OVERFLOW*/ + if (((last_dot - name) > 8) || /* name length > 8 */ + (strlen(last_dot + 1) > 3)) /* extention > 3 */ + return (1); + break; + + default: + /* more than one dot */ + return (1); + } + + for (namep = name; *namep; namep++) { + if (!mts_isascii(*namep) || + strchr(special_chars, *namep) || + strchr(invalid_dos_chars, *namep)) + return (1); + } + + return (0); +} + +/* + * smb_needs_shortname + * + * Determine whether a shortname should be generated for a file name that is + * already in 8.3 format. + * + * Paramters: + * name - original file name + * + * Return: + * 1 - Shortname is required to be generated. + * 0 - No shortname needs to be generated. + * + * Note + * ======= + * Windows NT server: shortname is created only if either + * the filename or extension portion of + * a file is made up of mixed case. + * Windows 2000 server: shortname is not created regardless + * of the case. + * Windows 2003 server: [Same as Windows NT server.] + * + * StorEdge will conform to the rule used by Windows NT/2003 server. + * + * For instance: + * File | Create shortname? + * ================================ + * nf.txt | N + * NF.TXT | N + * NF.txt | N + * nf | N + * NF | N + * nF.txt | Y + * nf.TxT | Y + * Nf | Y + * nF | Y + * + */ +static int +smb_needs_shortname(char *name) +{ + char buf[9]; + int len; + int create = 0; + const char *dot_pos = 0; + + dot_pos = strrchr(name, '.'); + /*LINTED E_PTRDIFF_OVERFLOW*/ + len = (!dot_pos) ? strlen(name) : (dot_pos - name); + /* First, examine the name portion of the file */ + if (len) { + (void) snprintf(buf, len + 1, "%s", name); + /* if the name contains both lower and upper cases */ + if (utf8_isstrupr(buf) == 0 && utf8_isstrlwr(buf) == 0) { + /* create shortname */ + create = 1; + } else if (dot_pos) { + /* Next, examine the extension portion of the file */ + (void) snprintf(buf, sizeof (buf), "%s", dot_pos + 1); + /* + * if the extension contains both lower and upper + * cases + */ + if (utf8_isstrupr(buf) == 0 && utf8_isstrlwr(buf) == 0) + /* create shortname */ + create = 1; + } + } + + return (create); +} + +/* + * smb_mangle_char + * + * If given char is an invalid DOS character or it's not an + * ascii char, it should be deleted from mangled and 8.3 name. + * + * If given char is one of special chars, it should be replaced + * with '_'. + * + * Otherwise just make it upper case. + */ +static unsigned char +smb_mangle_char(unsigned char ch) +{ + if (isinvalid(ch)) + return (0); + + if (strchr(special_chars, ch)) + return ('_'); + + return (mts_toupper(ch)); +} + +/* + * smb_generate_mangle + * + * Generates a mangle string which contains + * at least 2 (considering fileid cannot be 0) + * and at most 7 chars. + * + * Returns the number of chars in the generated mangle. + */ +static int +smb_generate_mangle(ino64_t fileid, unsigned char *mangle_buf) +{ + /* + * 36**6 = 2176782336: more than enough to express inodes in 6 + * chars + */ + static char *base36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + unsigned char *manglep = mangle_buf; + + for (*manglep++ = '~'; fileid > 0; fileid /= 36) + *manglep++ = base36[fileid % 36]; + *manglep = 0; + + /*LINTED E_PTRDIFF_OVERFLOW*/ + return (manglep - mangle_buf); +} + +/* + * smb_maybe_mangled_name + * + * returns true if the passed name can possibly be a mangled name. + * mangled names should be valid dos file names hence less than 12 characters + * long and should contain at least one tilde character. + * + * note that this function can be further enhanced to check for invalid + * dos characters/character patterns (such as "file..1.c") but this version + * should be sufficient in most cases. + */ +int +smb_maybe_mangled_name(char *name) +{ + int i, has_tilde = 0; + + for (i = 0; *name && (i < 12); i++, name++) { + if ((*name == '~') && (i < 8)) + has_tilde = 1; + + if (*name == '.' && has_tilde == 0) + return (0); + } + + return ((*name == 0) && has_tilde); +} + +/* + * smb_mangle_name + * + * Microsoft knowledge base article #142982 describes how Windows + * generates 8.3 filenames from long file names. Some other details + * can be found in article #114816. + * + * The function first checks to see whether the given name needs mangling. + * If not, and the force parameter is not set, then no mangling is done, + * but both the shortname (if needed) and the 8.3 name are produced and + * returned. + * + * If the "force" parameter is set (as will be the case for case-insensitive + * collisions), then the name will be mangled. + * + * Whenever mangling is needed, both the shortname and the 8.3 names are + * produced and returned. + * + * For example, the xxx.xy in 8.3 format will be "xxx .xy ". + */ + +int smb_mangle_name( + ino64_t fileid, /* inode number to generate unique mangle */ + char *name, /* original file name */ + char *shortname, /* mangled name (if applicable) */ + char *name83, /* (mangled) name in 8.3 format */ + int force) /* force mangling even if mangling is not */ + /* needed according to standard algorithm */ +{ + int avail; + unsigned char ch; + unsigned char mangle_buf[8]; + unsigned char *namep; + unsigned char *manglep; + unsigned char *out_short; + unsigned char *out_83; + char *dot_pos = NULL; + + /* + * NOTE: + * This function used to consider filename case + * in order to mangle. I removed those checks. + */ + + *shortname = *name83 = 0; + + /* Allow dot and dot dot up front */ + if (strcmp(name, ".") == 0) { + /* no shortname */ + (void) strcpy(name83, ". . "); + return (1); + } + + if (strcmp(name, "..") == 0) { + /* no shortname */ + (void) strcpy(name83, ".. . "); + return (1); + } + + out_short = (unsigned char *)shortname; + out_83 = (unsigned char *)name83; + + if ((smb_needs_mangle(name, &dot_pos) == 0) && (force == 0)) { + /* no mangle */ + + /* check if shortname is required or not */ + if (smb_needs_shortname(name)) { + namep = (unsigned char *)name; + while (*namep) + *out_short++ = mts_toupper(*namep++); + *out_short = '\0'; + } + + out_83 = (unsigned char *)name83; + (void) strcpy((char *)out_83, " . "); + while (*name && *name != '.') + *out_83++ = mts_toupper(*name++); + + if (*name == '.') { + /* copy extension */ + name++; + out_83 = (unsigned char *)name83 + 9; + while (*name) + *out_83++ = mts_toupper(*name++); + } + return (1); + } + + avail = 8 - smb_generate_mangle(fileid, mangle_buf); + + /* + * generated mangle part has always less than 8 chars, so + * use the chars before the first dot in filename + * and try to generate a full 8 char name. + */ + + /* skip the leading dots (if any) */ + for (namep = (unsigned char *)name; *namep == '.'; namep++) + ; + + for (; avail && *namep && (*namep != '.'); namep++) { + ch = smb_mangle_char(*namep); + if (ch == 0) + continue; + *out_short++ = *out_83++ = ch; + avail--; + } + + /* Copy in mangled part */ + manglep = mangle_buf; + + while (*manglep) + *out_short++ = *out_83++ = *(manglep++); + + /* Pad any leftover in 8.3 name with spaces */ + while (avail--) + *out_83++ = ' '; + + /* Work on extension now */ + avail = 3; + *out_83++ = '.'; + if (dot_pos) { + namep = (unsigned char *)dot_pos + 1; + if (*namep != 0) { + *out_short++ = '.'; + for (; avail && *namep; namep++) { + ch = smb_mangle_char(*namep); + if (ch == 0) + continue; + + *out_short++ = *out_83++ = ch; + avail--; + } + } + } + + while (avail--) + *out_83++ = ' '; + + *out_short = *out_83 = '\0'; + + return (1); +} + +/* + * smb_unmangle_name + * + * Given a mangled name, try to find the real file name as it appears + * in the directory entry. If the name does not contain a ~, it is most + * likely not a mangled name but the caller can still try to get the + * actual on-disk name by setting the "od" parameter. + * + * Returns 0 if a name has been returned in real_name. There are three + * possible scenarios: + * 1. Name did not contain a ~ and "od" was not set, in which + * case, real_name contains name. + * 2. Name did not contain a ~ and "od" was set, in which + * case, real_name contains the actual directory entry name. + * 3. Name did contain a ~, in which case, name was mangled and + * real_name contains the actual directory entry name. + * + * EINVAL: a parameter was invalid. + * ENOENT: an unmangled name could not be found. + */ + +int +smb_unmangle_name(struct smb_request *sr, cred_t *cred, smb_node_t *dir_node, + char *name, char *real_name, int realname_size, char *shortname, + char *name83, int od) +{ + int err; + int len; + int force = 0; + ino64_t inode; + uint32_t cookie; + struct smb_node *snode = NULL; + smb_attr_t ret_attr; + char *dot_pos = NULL; + char *readdir_name; + char *shortp; + char xxx[MANGLE_NAMELEN]; + + if (dir_node == NULL || name == NULL || real_name == NULL || + realname_size == 0) + return (EINVAL); + + *real_name = '\0'; + snode = NULL; + + if (smb_maybe_mangled_name(name) == 0) { + if (od == 0) { + (void) strlcpy(real_name, name, realname_size); + return (0); + } + + err = smb_fsop_lookup(sr, cred, 0, sr->tid_tree->t_snode, + dir_node, name, &snode, &ret_attr, NULL, NULL); + + if (err != 0) + return (err); + + (void) strlcpy(real_name, snode->od_name, realname_size); + smb_node_release(snode); + return (0); + } + + if (shortname == 0) + shortname = xxx; + if (name83 == 0) + name83 = xxx; + + cookie = 0; + + readdir_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + snode = NULL; + while (cookie != 0x7FFFFFFF) { + + len = realname_size - 1; + + err = smb_fsop_readdir(sr, cred, dir_node, &cookie, + readdir_name, &len, &inode, NULL, &snode, &ret_attr); + + if (err || (cookie == 0x7FFFFFFF)) + break; + + readdir_name[len] = 0; + + /* + * smb_fsop_readdir() may return a mangled name if the + * name has a case collision. + * + * If readdir_name is not a mangled name, we mangle + * readdir_name to see if it will match the name the + * client passed in. + * + * If smb_needs_mangle() does not succeed, we try again + * using the force flag. It is possible that the client + * is using a mangled name that resulted from a prior + * case collision which no longer exists in the directory. + * smb_needs_mangle(), with the force flag, will produce + * a mangled name regardless of whether the name passed in + * meets standard DOS criteria for name mangling. + */ + + if (smb_maybe_mangled_name(readdir_name)) { + shortp = readdir_name; + } else { + if (smb_needs_mangle(readdir_name, &dot_pos) == 0) + force = 1; + (void) smb_mangle_name(inode, readdir_name, shortname, + name83, force); + shortp = shortname; + } + + if (utf8_strcasecmp(name, shortp) == 0) { + kmem_free(readdir_name, MAXNAMELEN); + (void) strlcpy(real_name, snode->od_name, + realname_size); + + smb_node_release(snode); + + return (0); + } else { + smb_node_release(snode); + snode = NULL; + } + } + + kmem_free(readdir_name, MAXNAMELEN); + + return (ENOENT); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c b/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c new file mode 100644 index 000000000000..d97f410e8b7c --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c @@ -0,0 +1,1659 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB mbuf marshaling encode/decode. + */ + +#include + +#define MALLOC_QUANTUM 80 + +#define DECODE_NO_ERROR 0 +#define DECODE_NO_MORE_DATA 1 +#define DECODE_ALLOCATION_ERROR 2 +#define DECODE_CONVERSION_ERROR 3 + + +/* + * Put data into mbuf chain allocating as needed. + * Adds room to end of mbuf chain if needed. + */ + +int +mbc_marshal_make_room(struct mbuf_chain *mbc, int32_t bytes_needed) +{ + struct mbuf *m; + struct mbuf *l; + int32_t bytes_available; + + bytes_needed += mbc->chain_offset; + if (bytes_needed > mbc->max_bytes) + return (EMSGSIZE); + + if ((m = mbc->chain) == 0) { + MGET(m, M_WAIT, MT_DATA); + m->m_len = 0; + if (mbc->max_bytes > MLEN) + MCLGET(m, M_WAIT); + mbc->chain = m; + /* xxxx */ + /* ^ */ + } + + /* ---- ----- --xx ---xxx */ + /* ^ */ + + l = 0; + while ((m != 0) && (bytes_needed >= m->m_len)) { + l = m; + bytes_needed -= m->m_len; + m = m->m_next; + } + + if ((bytes_needed == 0) || (m != 0)) { + /* We have enough room already */ + return (0); + } + + /* ---- ----- --xx ---xxx */ + /* ^ */ + /* Back up to start of last mbuf */ + m = l; + bytes_needed += m->m_len; + + /* ---- ----- --xx ---xxx */ + /* ^ */ + + bytes_available = (m->m_flags & M_EXT) ? + m->m_ext.ext_size : MLEN; + + /* ---- ----- --xx ---xxx */ + /* ^ */ + while ((bytes_needed != 0) && (bytes_needed > bytes_available)) { + m->m_len = bytes_available; + bytes_needed -= m->m_len; + /* ---- ----- --xx ------ */ + /* ^ */ + + MGET(m->m_next, M_WAIT, MT_DATA); + m = m->m_next; + m->m_len = 0; + if (bytes_needed > MLEN) + MCLGET(m, M_WAIT); + + bytes_available = (m->m_flags & M_EXT) ? + m->m_ext.ext_size : MLEN; + + /* ---- ----- --xx ------ xxxx */ + /* ^ */ + } + + /* ---- ----- --xx ------ xxxx */ + /* ^ */ + /* Expand last tail as needed */ + if (m->m_len <= bytes_needed) { + m->m_len = bytes_needed; + /* ---- ----- --xx ------ --xx */ + /* ^ */ + } + + return (0); +} + + +void +mbc_marshal_store_byte(struct mbuf_chain *mbc, unsigned char data) +{ + struct mbuf *m = mbc->chain; + int32_t cur_offset = mbc->chain_offset; + + /* + * Scan forward looking for the last data currently in chain. + */ + while (cur_offset >= m->m_len) { + cur_offset -= m->m_len; + m = m->m_next; + } + ((char *)m->m_data)[cur_offset] = data; + mbc->chain_offset++; +} + + +int +mbc_marshal_put_char(struct mbuf_chain *mbc, unsigned char data) +{ + if (mbc_marshal_make_room(mbc, sizeof (char)) != 0) + return (DECODE_NO_MORE_DATA); + mbc_marshal_store_byte(mbc, data); + return (0); +} + + +int +mbc_marshal_put_short(struct mbuf_chain *mbc, unsigned short data) +{ + if (mbc_marshal_make_room(mbc, sizeof (short))) + return (DECODE_NO_MORE_DATA); + mbc_marshal_store_byte(mbc, data); + mbc_marshal_store_byte(mbc, data >> 8); + return (0); +} + + +int +mbc_marshal_put_long(struct mbuf_chain *mbc, uint32_t data) +{ + if (mbc_marshal_make_room(mbc, sizeof (int32_t))) + return (DECODE_NO_MORE_DATA); + mbc_marshal_store_byte(mbc, data); + mbc_marshal_store_byte(mbc, data >> 8); + mbc_marshal_store_byte(mbc, data >> 16); + mbc_marshal_store_byte(mbc, data >> 24); + return (0); +} + + +int +mbc_marshal_put_long_long(struct mbuf_chain *mbc, uint64_t data) +{ + if (mbc_marshal_make_room(mbc, sizeof (int64_t))) + return (DECODE_NO_MORE_DATA); + + mbc_marshal_store_byte(mbc, data); + mbc_marshal_store_byte(mbc, data >> 8); + mbc_marshal_store_byte(mbc, data >> 16); + mbc_marshal_store_byte(mbc, data >> 24); + mbc_marshal_store_byte(mbc, data >> 32); + mbc_marshal_store_byte(mbc, data >> 40); + mbc_marshal_store_byte(mbc, data >> 48); + mbc_marshal_store_byte(mbc, data >> 56); + return (0); +} + + +/* + * When need to convert from UTF-8 (internal format) to a single + * byte string (external format ) when marshalling a string. + */ +int +mbc_marshal_put_ascii_string(struct mbuf_chain *mbc, char *mbs, int repc) +{ + mts_wchar_t wide_char; + int nbytes; + int length; + + if ((length = mts_sbequiv_strlen(mbs)) == -1) + return (DECODE_NO_MORE_DATA); + + length += sizeof (char); + + if ((repc > 1) && (repc < length)) + length = repc; + if (mbc_marshal_make_room(mbc, length)) + return (DECODE_NO_MORE_DATA); + + while (*mbs) { + /* + * We should restore oem chars here. + */ + nbytes = mts_mbtowc(&wide_char, mbs, MTS_MB_CHAR_MAX); + if (nbytes == -1) + return (DECODE_NO_MORE_DATA); + + mbc_marshal_store_byte(mbc, (unsigned char)wide_char); + + if (wide_char & 0xFF00) + mbc_marshal_store_byte(mbc, wide_char >> 8); + + mbs += nbytes; + } + + mbc_marshal_store_byte(mbc, 0); + return (0); +} + + +int +mbc_marshal_put_alignment(struct mbuf_chain *mbc, unsigned int align) +{ + int32_t delta = mbc->chain_offset % align; + + if (delta != 0) { + align -= delta; + if (mbc_marshal_make_room(mbc, align)) + return (DECODE_NO_MORE_DATA); + while (align-- > 0) + mbc_marshal_store_byte(mbc, 0); + } + return (0); +} + + +int +mbc_marshal_put_unicode_string(struct mbuf_chain *mbc, char *ascii, int repc) +{ + mts_wchar_t wchar; + int consumed; + int length; + + if ((length = mts_wcequiv_strlen(ascii)) == -1) + return (DECODE_NO_MORE_DATA); + + length += sizeof (mts_wchar_t); + +#if 0 + if (mbc_marshal_put_alignment(mbc, sizeof (mts_wchar_t)) != 0) + return (DECODE_NO_MORE_DATA); +#endif + if ((repc > 1) && (repc < length)) + length = repc; + + if (mbc_marshal_make_room(mbc, length)) + return (DECODE_NO_MORE_DATA); + while (length > 0) { + consumed = mts_mbtowc(&wchar, ascii, MTS_MB_CHAR_MAX); + if (consumed == -1) + break; /* Invalid sequence */ + /* + * Note that consumed will be 0 when the null terminator + * is encountered and ascii will not be advanced beyond + * that point. Length will continue to be decremented so + * we won't get stuck here. + */ + ascii += consumed; + mbc_marshal_store_byte(mbc, wchar); + mbc_marshal_store_byte(mbc, wchar >> 8); + length -= sizeof (mts_wchar_t); + } + return (0); +} + + +int +mbc_marshal_put_uio(struct mbuf_chain *mbc, struct uio *uio) +{ + struct mbuf **t; + struct mbuf *m = 0; + struct iovec *iov = uio->uio_iov; + int32_t i, iov_cnt = uio->uio_iovcnt; + + iov = uio->uio_iov; + t = &mbc->chain; + for (i = 0; i < iov_cnt; i++) { + MGET(m, M_WAIT, MT_DATA); + m->m_ext.ext_buf = iov->iov_base; + m->m_ext.ext_ref = smb_noop; + m->m_data = m->m_ext.ext_buf; + m->m_flags |= M_EXT; + m->m_len = m->m_ext.ext_size = iov->iov_len; + mbc->max_bytes += m->m_len; + m->m_next = 0; + *t = m; + t = &m->m_next; + iov++; + } + return (0); +} + +int +mbc_marshal_put_mbufs(struct mbuf_chain *mbc, struct mbuf *m) +{ + struct mbuf *mt; + struct mbuf **t; + int bytes; + + if (m != 0) { + mt = m; + bytes = mt->m_len; + while (mt->m_next != 0) { + mt = mt->m_next; + bytes += mt->m_len; + } + if (bytes != 0) { + t = &mbc->chain; + while (*t != 0) { + bytes += (*t)->m_len; + t = &(*t)->m_next; + } + *t = m; + mbc->chain_offset = bytes; + } else { + m_freem(m); + } + } + return (0); +} + +int +mbc_marshal_put_mbuf_chain(struct mbuf_chain *mbc, struct mbuf_chain *nmbc) +{ + if (nmbc->chain != 0) { + if (mbc_marshal_put_mbufs(mbc, nmbc->chain)) + return (DECODE_NO_MORE_DATA); + MBC_SETUP(nmbc, nmbc->max_bytes); + } + return (0); +} + +int +mbc_marshal_put_SID(struct mbuf_chain *mbc, nt_sid_t *pSid) +{ + int i; + + if (mbc_marshal_put_char(mbc, pSid->Revision) != 0) + return (DECODE_NO_MORE_DATA); + + if (mbc_marshal_put_char(mbc, pSid->SubAuthCount) != 0) + return (DECODE_NO_MORE_DATA); + + for (i = 0; i < 6; i++) { + if (mbc_marshal_put_char(mbc, + pSid->Authority[i]) != 0) + return (DECODE_NO_MORE_DATA); + + } + + for (i = 0; i < pSid->SubAuthCount; i++) { + if (mbc_marshal_put_long(mbc, pSid->SubAuthority[i]) != 0) + return (DECODE_NO_MORE_DATA); + } + return (0); +} + + +int +mbc_marshal_put_skip(struct mbuf_chain *mbc, unsigned int skip) +{ + if (mbc_marshal_make_room(mbc, skip)) + return (DECODE_NO_MORE_DATA); + while (skip-- > 0) + mbc_marshal_store_byte(mbc, 0); + return (0); +} + +unsigned char +mbc_marshal_fetch_byte(struct mbuf_chain *mbc) +{ + unsigned char data; + struct mbuf *m = mbc->chain; + int32_t offset = mbc->chain_offset; + + while (offset >= m->m_len) { + offset -= m->m_len; + m = m->m_next; + } + data = ((unsigned char *)m->m_data)[offset]; + mbc->chain_offset++; + return (data); +} + + +int +mbc_marshal_get_char(struct mbuf_chain *mbc, unsigned char *data) +{ + if (MBC_ROOM_FOR(mbc, sizeof (char)) == 0) { + /* Data will never be available */ + return (DECODE_NO_MORE_DATA); + } + *data = mbc_marshal_fetch_byte(mbc); + return (0); +} + + +int +mbc_marshal_get_short(struct mbuf_chain *mbc, unsigned short *data) +{ + unsigned short tmp; + struct mbuf *m = mbc->chain; + int32_t offset = mbc->chain_offset; + + if (MBC_ROOM_FOR(mbc, sizeof (short)) == 0) { + /* Data will never be available */ + return (DECODE_NO_MORE_DATA); + } + + while (offset >= m->m_len) { + offset -= m->m_len; + m = m->m_next; + } + if ((m->m_len - offset) >= sizeof (short)) { + *data = LE_IN16(m->m_data + offset); + mbc->chain_offset += sizeof (short); + } else { + tmp = (unsigned short)mbc_marshal_fetch_byte(mbc); + tmp |= ((unsigned short)mbc_marshal_fetch_byte(mbc)) << 8; + *data = tmp; + } + return (0); +} + + +int +mbc_marshal_get_long(struct mbuf_chain *mbc, uint32_t *data) +{ + uint32_t tmp; + struct mbuf *m = mbc->chain; + int32_t offset = mbc->chain_offset; + + if (MBC_ROOM_FOR(mbc, sizeof (int32_t)) == 0) { + /* Data will never be available */ + return (DECODE_NO_MORE_DATA); + } + while (offset >= m->m_len) { + offset -= m->m_len; + m = m->m_next; + } + if ((m->m_len - offset) >= sizeof (int32_t)) { + *data = LE_IN32(m->m_data + offset); + mbc->chain_offset += sizeof (int32_t); + } else { + tmp = (uint32_t)mbc_marshal_fetch_byte(mbc); + tmp |= ((uint32_t)mbc_marshal_fetch_byte(mbc)) << 8; + tmp |= ((uint32_t)mbc_marshal_fetch_byte(mbc)) << 16; + tmp |= ((uint32_t)mbc_marshal_fetch_byte(mbc)) << 24; + *data = tmp; + } + return (0); +} + +uint64_t +qswap(uint64_t ll) +{ + uint64_t v; + + v = ll >> 32; + v |= ll << 32; + + return (v); +} + +int +mbc_marshal_get_odd_long_long(struct mbuf_chain *mbc, uint64_t *data) +{ + uint64_t tmp; + struct mbuf *m = mbc->chain; + int32_t offset = mbc->chain_offset; + + if (MBC_ROOM_FOR(mbc, sizeof (int64_t)) == 0) { + /* Data will never be available */ + return (DECODE_NO_MORE_DATA); + } + while (offset >= m->m_len) { + offset -= m->m_len; + m = m->m_next; + } + + if ((m->m_len - offset) >= sizeof (int64_t)) { + *data = qswap(LE_IN64(m->m_data + offset)); + mbc->chain_offset += sizeof (int64_t); + } else { + tmp = (uint64_t)mbc_marshal_fetch_byte(mbc) << 32; + tmp |= (uint64_t)mbc_marshal_fetch_byte(mbc) << 40; + tmp |= (uint64_t)mbc_marshal_fetch_byte(mbc) << 48; + tmp |= (uint64_t)mbc_marshal_fetch_byte(mbc) << 56; + tmp |= (uint64_t)mbc_marshal_fetch_byte(mbc); + tmp |= (uint64_t)mbc_marshal_fetch_byte(mbc) << 8; + tmp |= (uint64_t)mbc_marshal_fetch_byte(mbc) << 16; + tmp |= (uint64_t)mbc_marshal_fetch_byte(mbc) << 24; + + *(uint64_t *)data = tmp; + } + return (0); +} + +int +mbc_marshal_get_long_long(struct mbuf_chain *mbc, uint64_t *data) +{ + uint64_t tmp; + struct mbuf *m = mbc->chain; + int32_t offset = mbc->chain_offset; + + if (MBC_ROOM_FOR(mbc, sizeof (int64_t)) == 0) { + /* Data will never be available */ + return (DECODE_NO_MORE_DATA); + } + while (offset >= m->m_len) { + offset -= m->m_len; + m = m->m_next; + } + if ((m->m_len - offset) >= sizeof (int64_t)) { + *data = LE_IN64(m->m_data + offset); + mbc->chain_offset += sizeof (int64_t); + } else { + tmp = (uint32_t)mbc_marshal_fetch_byte(mbc); + tmp |= ((uint64_t)mbc_marshal_fetch_byte(mbc)) << 8; + tmp |= ((uint64_t)mbc_marshal_fetch_byte(mbc)) << 16; + tmp |= ((uint64_t)mbc_marshal_fetch_byte(mbc)) << 24; + tmp |= ((uint64_t)mbc_marshal_fetch_byte(mbc)) << 32; + tmp |= ((uint64_t)mbc_marshal_fetch_byte(mbc)) << 40; + tmp |= ((uint64_t)mbc_marshal_fetch_byte(mbc)) << 48; + tmp |= ((uint64_t)mbc_marshal_fetch_byte(mbc)) << 56; + *(uint64_t *)data = tmp; + } + return (0); +} + +/* + * mbc_marshal_get_ascii_string + * + * The ascii string in smb includes oem chars. Since the + * system needs utf8 encodes unicode char, conversion is + * required to convert the oem char to unicode and then + * to encode the converted wchars to utf8 format. + * Therefore, the **ascii returned will be in such format + * instead of the real ASCII format. + */ +static int +mbc_marshal_get_ascii_string( + struct smb_malloc_list *ml, + struct mbuf_chain *mbc, + unsigned char **ascii, + int max_ascii) +{ + char *rcvbuf; + char *ch; + mts_wchar_t *wtmpbuf; + int max; + int length = 0; + unsigned int cpid = oem_get_smb_cpid(); + + max = MALLOC_QUANTUM; + rcvbuf = smbsr_malloc(ml, max); + + if (max_ascii == 0) + max_ascii = 0xffff; + + ch = rcvbuf; + for (;;) { + while (length < max) { + if (max_ascii-- <= 0) { + *ch++ = 0; + goto multibyte_encode; + } + if (MBC_ROOM_FOR(mbc, sizeof (char)) == 0) { + /* Data will never be available */ + return (DECODE_NO_MORE_DATA); + } + if ((*ch++ = mbc_marshal_fetch_byte(mbc)) == 0) + goto multibyte_encode; + length++; + } + max += MALLOC_QUANTUM; + rcvbuf = smbsr_realloc(rcvbuf, max); + ch = rcvbuf + length; + } + +multibyte_encode: + /* + * UTF-8 encode the string for internal system use. + */ + length = strlen(rcvbuf) + 1; + wtmpbuf = smbsr_malloc(ml, length*sizeof (mts_wchar_t)); + *ascii = smbsr_malloc(ml, length * MTS_MB_CHAR_MAX); + + if (oemstounicodes(wtmpbuf, rcvbuf, length, cpid) > 0) + (void) mts_wcstombs((char *)*ascii, wtmpbuf, + length * MTS_MB_CHAR_MAX); + else + (void) mts_stombs((char *)*ascii, rcvbuf, length * 2); + return (0); +} + + +int +mbc_marshal_get_unicode_string(struct smb_malloc_list *ml, + struct mbuf_chain *mbc, unsigned char **ascii, int max_unicode) +{ + int max; + unsigned short wchar; + char *ch; + int emitted; + int length = 0; + + if (max_unicode == 0) + max_unicode = 0xffff; + + max = MALLOC_QUANTUM; + *ascii = smbsr_malloc(ml, max); + + ch = (char *)*ascii; + for (;;) { + while ((length + MTS_MB_CHAR_MAX) < max) { + if (max_unicode <= 0) + goto done; + max_unicode -= 2; + + if (mbc_marshal_get_short(mbc, &wchar) != 0) + return (DECODE_NO_MORE_DATA); + + if (wchar == 0) goto done; + + emitted = mts_wctomb(ch, wchar); + length += emitted; + ch += emitted; + } + max += MALLOC_QUANTUM; + *ascii = smbsr_realloc(*ascii, max); + ch = (char *)*ascii + length; + } +done: *ch = 0; + return (0); +} + + +int /*ARGSUSED*/ +mbc_marshal_get_mbufs(struct mbuf_chain *mbc, int32_t bytes, struct mbuf **m) +{ + if (MBC_ROOM_FOR(mbc, bytes) == 0) { + /* Data will never be available */ + return (DECODE_NO_MORE_DATA); + } + return (0); +} + +int +mbc_marshal_get_mbuf_chain(struct mbuf_chain *mbc, + int32_t bytes, struct mbuf_chain *nmbc) +{ + int rc; + struct mbuf *m; + + if (bytes == 0) { + /* Get all the rest */ + bytes = mbc->max_bytes - mbc->chain_offset; + } + + MBC_SETUP(nmbc, mbc->max_bytes); + if ((rc = mbc_marshal_get_mbufs(mbc, bytes, &m)) != 0) { + if (m) + m_freem(m); + return (rc); + } + nmbc->chain = m; + while (m != 0) { + bytes += m->m_len; + m = m->m_next; + } + nmbc->max_bytes = bytes; + return (0); +} + + +int +mbc_marshal_get_uio(struct mbuf_chain *mbc, struct uio *uio) +{ + int i, offset; + int32_t bytes = uio->uio_resid; + int32_t remainder; + struct iovec *iov; + struct mbuf *m; + + /* + * The residual count is tested because in the case of write requests + * with no data (smbtorture RAW-WRITE test will generate that type of + * request) this function is called with a residual count of zero + * bytes. + */ + if (bytes) { + iov = uio->uio_iov; + uio->uio_segflg = UIO_SYSSPACE; + + if (MBC_ROOM_FOR(mbc, bytes) == 0) { + /* Data will never be available */ + return (DECODE_NO_MORE_DATA); + } + + m = mbc->chain; + offset = mbc->chain_offset; + while (offset >= m->m_len) { + offset -= m->m_len; + m = m->m_next; + ASSERT((offset == 0) || (offset && m)); + } + + for (i = 0; (bytes > 0) && (i < uio->uio_iovcnt); i++) { + iov[i].iov_base = &m->m_data[offset]; + remainder = m->m_len - offset; + if (remainder >= bytes) { + iov[i].iov_len = bytes; + mbc->chain_offset += bytes; + break; + } + iov[i].iov_len = remainder; + mbc->chain_offset += remainder; + bytes -= remainder; + m = m->m_next; + offset = 0; + } + if (i == uio->uio_iovcnt) { + return (DECODE_NO_MORE_DATA); + } + uio->uio_iovcnt = i; + } + return (0); +} + + +int +mbc_marshal_get_SID(struct mbuf_chain *mbc, nt_sid_t *pSid) +{ + int i; + + if (mbc_marshal_get_char(mbc, &pSid->Revision) != 0) + return (DECODE_NO_MORE_DATA); + + if (mbc_marshal_get_char(mbc, &pSid->SubAuthCount) != 0) + return (DECODE_NO_MORE_DATA); + + for (i = 0; i < 6; i++) { + if (mbc_marshal_get_char(mbc, + &pSid->Authority[i]) != 0) + return (DECODE_NO_MORE_DATA); + } + + for (i = 0; i < pSid->SubAuthCount; i++) { + if (mbc_marshal_get_long(mbc, &pSid->SubAuthority[i]) != 0) + return (DECODE_NO_MORE_DATA); + } + return (0); +} + +int +mbc_marshal_get_skip(struct mbuf_chain *mbc, unsigned int skip) +{ + if (MBC_ROOM_FOR(mbc, skip) == 0) + return (DECODE_NO_MORE_DATA); + mbc->chain_offset += skip; + return (0); +} + +int +mbc_marshal_get_alignment(struct mbuf_chain *mbc, unsigned int align) +{ + int32_t delta = mbc->chain_offset % align; + + if (delta != 0) { + align -= delta; + return (mbc_marshal_get_skip(mbc, delta)); + } + return (0); +} + +/* + * The mbuf chain passed in contains the data to be decoded. + * + * The format string provides a description of the parameters passed in as well + * as an action to be taken by smb_mbc_decode(). + * + * \b Restore the mbuf chain offset to its initial value. + * + * % Pointer to an SMB request structure (smb_request_t *). There + * should be only one of these in the string. + * + * C Pointer to an mbuf chain. Copy to that mbuf chain the number of + * bytes specified (number preceding C). + * + * m Pointer to an mbuf. Copy to that mbuf the number of bytes + * specified (number preceding m). + * + * M Read the 32 bit value at the current location of the mbuf chain + * and check if it matches the signature of an SMB request (SMBX). + * + * b Pointer to a buffer. Copy to that buffer the number of bytes + * specified (number preceding b). + * + * c Same as 'b'. + * + * w Pointer to a word (16bit value). Copy the next 16bit value into + * that location. + * + * l Pointer to a long (32bit value). Copy the next 32bit value into + * that location. + * + * q Pointer to a quad (64bit value). Copy the next 64bit value into + * that location. + * + * Q Same as above with a call to qswap(). + * + * B Pointer to a vardata_block structure. That structure is used to + * retrieve data from the mbuf chain (an iovec type structure is + * embedded in a vardata_block). + * + * D Pointer to a vardata_block structure. That structure is used to + * retrieve data from the mbuf chain, however, two fields of the + * vardata_block structure (tag and len) are first initialized + * using the mbuf chain itself. + * + * V Same as 'D'. + * + * L + * + * A + * + * P Same as 'A' + * + * S Same as 'A' + * + * u Pointer to a string pointer. Allocate memory and retrieve the + * string at the current location in the mbuf chain. Store the + * address to the buffer allocated at the address specified by + * the pointer. In addition if an sr was passed and it indicates + * that the string is an unicode string, convert it. + * + * s Same as 'u' without convertion. + * + * U Same as 'u'. The string to retrieve is unicode. + * + * R Not used anymore. + * + * y Pointer to a 32bit value. Read the dos time at the current mbuf + * chain location, convert it to unix time and store it at the + * location indicated by the pointer. + * + * Y Same as 'y' bt the dos time coded in the mbuf chain is inverted. + * + * . Skip the number of bytes indicated by the number preceding '.'. + * + * , Same as '.' but take in account it is an unicode string. + * + * The parameters can be named in the format string. They have to appear between + * parenthesis (indicating they should be ignored bu the decoder). + */ +int +smb_mbc_decode(struct mbuf_chain *mbc, char *fmt, va_list ap) +{ + unsigned char c, cval; + unsigned char *cvalp; + unsigned char **cvalpp; + unsigned short *wvalp; + unsigned int *ivalp; + uint32_t *lvalp; + uint64_t *llvalp; + struct vardata_block *vdp; + unsigned char name[32]; + struct smb_request *sr = NULL; + uint32_t lval; + int unicode = 0; + int repc; + /*LINTED E_FUNC_SET_NOT_USED*/ + enum {EVEN, UNALIGNED, ODD} alignment; + int32_t saved_chain_offset = mbc->chain_offset; + + name[0] = 0; + while ((c = *fmt++) != 0) { + repc = 1; + alignment = EVEN; + + if (c == ' ' || c == '\t') continue; + if (c == '(') { + char *nm = (char *)name; + + while (((c = *fmt++) != 0) && c != ')') { + *nm++ = c; + } + *nm = 0; + if (!c) fmt--; + continue; + } + + if (c == '{') { + unsigned char op[8]; + char *nm = (char *)op; + + while (((c = *fmt++) != 0) && c != '}') { + *nm++ = c; + } + *nm = 0; + if (!c) fmt--; + if (strcmp((char *)op, "SID") == 0) { + nt_sid_t *sidp; + + sidp = va_arg(ap, nt_sid_t *); + (void) mbc_marshal_get_SID(mbc, sidp); + } + continue; + } + + if ('0' <= c && c <= '9') { + repc = 0; + do { + repc = repc * 10 + c - '0'; + c = *fmt++; + } while ('0' <= c && c <= '9'); + } else if (c == '*') { + ivalp = va_arg(ap, unsigned int *); + repc = *(ivalp++); + c = *fmt++; + } else if (c == '!') { + alignment = ODD; + c = *fmt++; + } else if (c == '^') { + alignment = UNALIGNED; + c = *fmt++; + } else if (c == '#') { + repc = va_arg(ap, int); + c = *fmt++; + } + + switch (c) { + default: + goto format_mismatch; + + case '\b': + mbc->chain_offset = saved_chain_offset; + break; + + case '%': + sr = va_arg(ap, struct smb_request *); + unicode = sr->smb_flg2 & SMB_FLAGS2_UNICODE; + break; + + case 'C': /* Mbuf_chain */ + if (mbc_marshal_get_mbuf_chain(mbc, repc, + va_arg(ap, struct mbuf_chain *)) != 0) + goto underflow; + break; + + case 'm': /* struct_mbuf */ + if (mbc_marshal_get_mbufs(mbc, repc, + va_arg(ap, struct mbuf **)) != 0) + goto underflow; + break; + + case 'M': + if (mbc_marshal_get_long(mbc, &lval) != 0) { + /* Data will never be available */ + goto underflow; + } + if (lval != 0x424D53FF) /* 0xFF S M B */ + goto underflow; + break; + + case 'b': + case 'c': + cvalp = va_arg(ap, unsigned char *); + if (MBC_ROOM_FOR(mbc, repc) == 0) { + /* Data will never be available */ + goto underflow; + } + while (repc-- > 0) + *cvalp++ = mbc_marshal_fetch_byte(mbc); + break; + + case 'w': + wvalp = va_arg(ap, unsigned short *); + while (repc-- > 0) + if (mbc_marshal_get_short(mbc, wvalp++) != 0) + goto underflow; + break; + + case 'l': + lvalp = va_arg(ap, uint32_t *); + while (repc-- > 0) + if (mbc_marshal_get_long(mbc, lvalp++) != 0) + goto underflow; + break; + + case 'q': + llvalp = va_arg(ap, uint64_t *); + while (repc-- > 0) + if (mbc_marshal_get_long_long( + mbc, llvalp++) != 0) + goto underflow; + break; + + case 'Q': + llvalp = va_arg(ap, uint64_t *); + while (repc-- > 0) + if (mbc_marshal_get_odd_long_long( + mbc, llvalp++) != 0) + goto underflow; + break; + + case 'B': + vdp = va_arg(ap, struct vardata_block *); + vdp->tag = 0; + + /*LINTED E_ASSIGN_NARROW_CONV (BYTE)*/ + vdp->len = repc; + vdp->uio.uio_iov = &vdp->iovec[0]; + vdp->uio.uio_iovcnt = MAX_IOVEC; + vdp->uio.uio_resid = repc; + if (mbc_marshal_get_uio(mbc, &vdp->uio) != 0) + goto underflow; + break; + + case 'D': case 'V': + vdp = va_arg(ap, struct vardata_block *); + if (mbc_marshal_get_char(mbc, &vdp->tag) != 0) + goto underflow; + if (mbc_marshal_get_short(mbc, &vdp->len) != 0) + goto underflow; + vdp->uio.uio_iov = &vdp->iovec[0]; + vdp->uio.uio_iovcnt = MAX_IOVEC; + vdp->uio.uio_resid = vdp->len; + if (vdp->len != 0) { + if (mbc_marshal_get_uio(mbc, &vdp->uio) != 0) + goto underflow; + } + break; + + case 'L': + if (mbc_marshal_get_char(mbc, &cval) != 0) + goto underflow; + if (cval != 2) + goto format_mismatch; + goto ascii_conversion; + + case 'A': case 'P': case 'S': + if (mbc_marshal_get_char(mbc, &cval) != 0) + goto underflow; + if (((c == 'A' || c == 'S') && cval != 4) || + (c == 'L' && cval != 2) || (c == 'P' && cval != 3)) + goto format_mismatch; + /* FALLTHROUGH */ + + case 'u': /* Convert from unicode if flags are set */ + if (unicode) + goto unicode_translation; + /* FALLTHROUGH */ + + case 's': +ascii_conversion: + ASSERT(sr != NULL); + cvalpp = va_arg(ap, unsigned char **); + if (repc <= 1) + repc = 0; + if (mbc_marshal_get_ascii_string(&sr->request_storage, + mbc, cvalpp, repc) != 0) + goto underflow; + break; + + case 'U': /* Convert from unicode */ +unicode_translation: + ASSERT(sr != 0); + cvalpp = va_arg(ap, unsigned char **); + if (repc <= 1) + repc = 0; + if (mbc->chain_offset & 1) + mbc->chain_offset++; + if (mbc_marshal_get_unicode_string(&sr->request_storage, + mbc, cvalpp, repc) != 0) + goto underflow; + break; + + case 'R': + /* + * This was used to decode RPC format unicode strings + * prior to having a DCE RPC support. It is no longer + * required. + */ + ASSERT(0); + break; + + case 'Y': /* dos time to unix time tt/dd */ + lvalp = va_arg(ap, uint32_t *); + while (repc-- > 0) { + short d, t; + + if (mbc_marshal_get_short(mbc, + (unsigned short *)&t) != 0) + goto underflow; + if (mbc_marshal_get_short(mbc, + (unsigned short *)&d) != 0) + goto underflow; + *lvalp++ = dosfs_dos_to_ux_time(d, t); + } + break; + + case 'y': /* dos time to unix time dd/tt */ + lvalp = va_arg(ap, uint32_t *); + while (repc-- > 0) { + short d, t; + + if (mbc_marshal_get_short(mbc, + (unsigned short *)&d) != 0) + goto underflow; + if (mbc_marshal_get_short(mbc, + (unsigned short *)&t) != 0) + goto underflow; + *lvalp++ = dosfs_dos_to_ux_time(d, t); + } + break; + + case ',': + if (unicode) + repc *= 2; + /* FALLTHROUGH */ + + case '.': + if (mbc_marshal_get_skip(mbc, repc) != 0) + goto underflow; + break; + } + } + return (0); + + +format_mismatch: + return (-1); + +underflow: + return (-1); +} + + +int +smb_decode_mbc(struct mbuf_chain *mbc, char *fmt, ...) +{ + int xx; + va_list ap; + + va_start(ap, fmt); + xx = smb_mbc_decode(mbc, fmt, ap); + va_end(ap); + return (xx); +} + + +int +smb_decode_buf(unsigned char *buf, int n_buf, char *fmt, ...) +{ + int rc; + struct mbuf_chain mbc; + va_list ap; + + va_start(ap, fmt); + + MBC_ATTACH_BUF(&mbc, buf, n_buf); + rc = smb_mbc_decode(&mbc, fmt, ap); + m_freem(mbc.chain); + va_end(ap); + return (rc); +} + +/* + * The mbuf chain passed in will receive the encoded data. + * + * The format string provides a description of the parameters passed in as well + * as an action to be taken by smb_mbc_encode(). + * + * \b Restore the mbuf chain offset to its initial value. + * + * % Pointer to an SMB request structure (smb_request_t *). There + * should be only one of these in the string. If an sr in present + * it will be used to determine if unicode conversion should be + * applied to the strings. + * + * C Pointer to an mbuf chain. Copy that mbuf chain into the + * destination mbuf chain. + * + * D Pointer to a vardata_block structure. Copy the data described + * by that structure into the mbuf chain. The tag field is hard + * coded to '1'. + * + * M Write the SMB request signature ('SMBX') into the mbuf chain. + * + * T Pointer to a timestruc_t. Convert the content of the structure + * into NT time and store the result of the conversion in the + * mbuf chain. + * + * V Same as 'D' but the tag field is hard coded to '5'. + * + * b Byte. Store the byte or the nymber of bytes specified into the + * the mbuf chain. A format string like this "2b" would require 2 + * bytes to be passed in. + * + * m Pointer to an mbuf. Copy the contents of the mbuf into the mbuf + * chain. + * + * c Pointer to a buffer. Copy the buffer into the mbuf chain. The + * size of the buffer is indicated by the number preceding 'c'. + * + * w Word (16bit value). Store the word or the number of words + * specified into the the mbuf chain. A format string like this + * "2w" would require 2 words to be passed in. + * + * l Long (32bit value). Store the long or the number of longs + * specified into the the mbuf chain. A format string like this + * "2l" would require 2 longs to be passed in. + * + * q Quad (64bit value). Store the quad or the number of quads + * specified into the the mbuf chain. A format string like this + * "2q" would require 2 quads to be passed in. + * + * L Pointer to a string. Store the string passed in into the mbuf + * chain preceded with a tag value of '2'. + * + * S Pointer to a string. Store the string passed in into the mbuf + * chain preceded with a tag value of '4'. Applied a unicode + * conversion is appropriate. + * + * A Same as 'S' + * + * P Pointer to a string. Store the string passed in into the mbuf + * chain preceded with a tag value of '5'. Applied a unicode + * conversion is appropriate. + * + * u Pointer to a string. Store the string passed in into the mbuf + * chain. Applied a unicode conversion is appropriate. + * + * s Pointer to a string. Store the string passed in into the mbuf + * chain. + * + * Y Date/Time. Store the Date/Time or the number of Date/Time(s) + * specified into the the mbuf chain. A format string like this + * "2Y" would require 2 Date/Time values. The Date/Time is + * converted to DOS before storing. + * + * y Same as 'Y'. The order of Date and Time is reversed. + * + * , Character. Store the character or number of character specified + * into the mbuf chain. A format string like this "2c" would + * require 2 characters to be passed in. A unicode conversion is + * applied if appropriate. + * + * . Same as '`' without unicode conversion. + * + * U Align the offset of the mbuf chain on a 16bit boundary. + * + * Z Unicode string. Store the unicode string into the mbuf chain + * without alignment considerations. + * + * The parameters can be named in the format string. They have to appear between + * parenthesis (indicating they should be ignored bu the encoder). + */ +int +smb_mbc_encode(struct mbuf_chain *mbc, char *fmt, va_list ap) +{ + unsigned char name[32]; + unsigned char cval, c; + unsigned short wval; + uint64_t llval; + uint32_t lval; + unsigned int tag; + unsigned char *cvalp; + unsigned int *ivalp; + timestruc_t *tvp; + int64_t nt_time; + struct vardata_block *vdp; + struct smb_request *sr = 0; + int unicode = 0; + int repc = 1; + /*LINTED E_FUNC_SET_NOT_USED*/ + enum {EVEN, UNALIGNED, ODD} alignment; + + while ((c = *fmt++) != 0) { + name[0] = 0; + repc = 1; + alignment = EVEN; + if (c == ' ' || c == '\t') continue; + if (c == '(') { + char *nm = (char *)name; + + while (((c = *fmt++) != 0) && c != ')') { + *nm++ = c; + } + *nm = 0; + if (!c) fmt--; + continue; + } + + if (c == '{') { + unsigned char op[8]; + char *nm = (char *)op; + + while (((c = *fmt++) != 0) && c != '}') { + *nm++ = c; + } + *nm = 0; + if (!c) fmt--; + if (strcmp((char *)op, "SID") == 0) { + nt_sid_t *sidp; + + sidp = va_arg(ap, nt_sid_t *); + (void) mbc_marshal_put_SID(mbc, sidp); + } + continue; + } + + if ('0' <= c && c <= '9') { + repc = 0; + do { + repc = repc * 10 + c - '0'; + c = *fmt++; + } while ('0' <= c && c <= '9'); + } else if (c == '*') { + ivalp = va_arg(ap, unsigned int *); + + repc = *ivalp; + c = *fmt++; + } else if (c == '!') { + alignment = ODD; + c = *fmt++; + } else if (c == '^') { + alignment = UNALIGNED; + c = *fmt++; + } else if (c == '#') { + repc = va_arg(ap, int); + c = *fmt++; + } + + switch (c) { + default: + goto format_mismatch; + + case '%': + sr = va_arg(ap, struct smb_request *); + unicode = sr->smb_flg2 & SMB_FLAGS2_UNICODE; + break; + + case 'C': /* Mbuf_chain */ + if (mbc_marshal_put_mbuf_chain(mbc, + va_arg(ap, struct mbuf_chain *)) != 0) + return (DECODE_NO_MORE_DATA); + break; + + case 'D': + vdp = va_arg(ap, struct vardata_block *); + + if (mbc_marshal_put_char(mbc, 1) != 0) + return (DECODE_NO_MORE_DATA); + if (mbc_marshal_put_short(mbc, vdp->len) != 0) + return (DECODE_NO_MORE_DATA); + if (mbc_marshal_put_uio(mbc, &vdp->uio) != 0) + return (DECODE_NO_MORE_DATA); + break; + + case 'M': + /* 0xFF S M B */ + if (mbc_marshal_put_long(mbc, 0x424D53FF)) + return (DECODE_NO_MORE_DATA); + break; + + case 'T': + tvp = va_arg(ap, timestruc_t *); + nt_time = unix_to_nt_time(tvp); + if (mbc_marshal_put_long_long(mbc, nt_time) != 0) + return (DECODE_NO_MORE_DATA); + break; + + case 'V': + vdp = va_arg(ap, struct vardata_block *); + + if (mbc_marshal_put_char(mbc, 5) != 0) + return (DECODE_NO_MORE_DATA); + if (mbc_marshal_put_short(mbc, vdp->len) != 0) + return (DECODE_NO_MORE_DATA); + if (mbc_marshal_put_uio(mbc, &vdp->uio) != 0) + return (DECODE_NO_MORE_DATA); + break; + + case 'b': + while (repc-- > 0) { + cval = va_arg(ap, int); + if (mbc_marshal_put_char(mbc, cval) != 0) + return (DECODE_NO_MORE_DATA); + } + break; + + case 'm': /* struct_mbuf */ + if (mbc_marshal_put_mbufs(mbc, + va_arg(ap, struct mbuf *)) != 0) + return (DECODE_NO_MORE_DATA); + break; + + case 'c': + cvalp = va_arg(ap, unsigned char *); + while (repc-- > 0) { + if (mbc_marshal_put_char(mbc, + *cvalp++) != 0) + return (DECODE_NO_MORE_DATA); + } + break; + + case 'w': + while (repc-- > 0) { + wval = va_arg(ap, int); + if (mbc_marshal_put_short(mbc, wval) != 0) + return (DECODE_NO_MORE_DATA); + } + break; + + case 'l': + while (repc-- > 0) { + lval = va_arg(ap, uint32_t); + if (mbc_marshal_put_long(mbc, lval) != 0) + return (DECODE_NO_MORE_DATA); + } + break; + + case 'q': + while (repc-- > 0) { + llval = va_arg(ap, uint64_t); + if (mbc_marshal_put_long_long(mbc, llval) != 0) + return (DECODE_NO_MORE_DATA); + } + break; + + + case 'L': + tag = 2; + goto ascii_conversion; + + case 'S': + case 'A': tag = 4; goto tagged_str; + case 'P': tag = 3; goto tagged_str; + tagged_str: + if (mbc_marshal_put_char(mbc, tag) != 0) + return (DECODE_NO_MORE_DATA); + /* FALLTHROUGH */ + + case 'u': /* Convert from unicode if flags are set */ + if (unicode) + goto unicode_translation; + /* FALLTHROUGH */ + + case 's': /* ASCII/multibyte string */ +ascii_conversion: cvalp = va_arg(ap, unsigned char *); + if (mbc_marshal_put_ascii_string(mbc, + (char *)cvalp, repc) != 0) + return (DECODE_NO_MORE_DATA); + break; + + case 'Y': /* int32_t, encode dos date/time */ + while (repc-- > 0) { + unsigned short d, t; + + lval = va_arg(ap, uint32_t); + (void) dosfs_ux_to_dos_time(lval, + (short *)&d, (short *)&t); + if (mbc_marshal_put_short(mbc, t) != 0) + return (DECODE_NO_MORE_DATA); + if (mbc_marshal_put_short(mbc, d) != 0) + return (DECODE_NO_MORE_DATA); + } + break; + + case 'y': /* int32_t, encode dos date/time */ + while (repc-- > 0) { + unsigned short d, t; + + lval = va_arg(ap, uint32_t); + (void) dosfs_ux_to_dos_time(lval, + (short *)&d, (short *)&t); + if (mbc_marshal_put_short(mbc, d) != 0) + return (DECODE_NO_MORE_DATA); + if (mbc_marshal_put_short(mbc, t) != 0) + return (DECODE_NO_MORE_DATA); + } + break; + + case ',': + if (unicode) + repc *= 2; + /* FALLTHROUGH */ + + case '.': + while (repc-- > 0) + if (mbc_marshal_put_char(mbc, 0) != 0) + return (DECODE_NO_MORE_DATA); + break; + + case 'R': + /* + * This was used to encode RPC format unicode strings + * prior to having a DCE RPC support. It is no longer + * required. + */ + ASSERT(0); + break; + + case 'U': /* Convert to unicode, align to word boundary */ +unicode_translation: + if (mbc->chain_offset & 1) + mbc->chain_offset++; + /* FALLTHROUGH */ + + case 'Z': /* Convert to unicode, no alignment adjustment */ + cvalp = va_arg(ap, unsigned char *); + if (mbc_marshal_put_unicode_string(mbc, + (char *)cvalp, repc) != 0) + return (DECODE_NO_MORE_DATA); + break; + } + } + return (0); + +format_mismatch: + return (-1); +} + + +int +smb_encode_mbc(struct mbuf_chain *mbc, char *fmt, ...) +{ + int rc; + va_list ap; + + va_start(ap, fmt); + rc = smb_mbc_encode(mbc, fmt, ap); + va_end(ap); + return (rc); +} + + +int +smb_encode_buf(unsigned char *buf, int n_buf, char *fmt, ...) +{ + int rc; + struct mbuf_chain mbc; + va_list ap; + + va_start(ap, fmt); + + MBC_ATTACH_BUF(&mbc, buf, n_buf); + rc = smb_mbc_encode(&mbc, fmt, ap); + m_freem(mbc.chain); + va_end(ap); + return (rc); +} + + +int +smb_decode_vwv(struct smb_request *sr, char *fmt, ...) +{ + int rc; + va_list ap; + + va_start(ap, fmt); + rc = smb_mbc_decode(&sr->smb_vwv, fmt, ap); + va_end(ap); + return (rc); +} + + +int +smb_decode_data(struct smb_request *sr, char *fmt, ...) +{ + if (smb_decode_mbc(&sr->smb_data, fmt, (int *)(&fmt + 1)) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + return (0); +} + + +void +smb_encode_header(struct smb_request *sr, int wct, + int bcc, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (smb_mbc_encode(&sr->reply, fmt, ap) != 0) { + va_end(ap); + smbsr_encode_error(sr); + /* NOTREACHED */ + } + va_end(ap); + /*LINTED E_ASSIGN_NARROW_CONV*/ + sr->smb_wct = wct; + /*LINTED E_ASSIGN_NARROW_CONV*/ + sr->smb_bcc = bcc; +} + +int +smb_peek_mbc(struct mbuf_chain *mbc, int offset, char *fmt, ...) +{ + int xx; + struct mbuf_chain tmp; + va_list ap; + + va_start(ap, fmt); + + (void) MBC_SHADOW_CHAIN(&tmp, mbc, offset, mbc->max_bytes - offset); + xx = smb_mbc_decode(&tmp, fmt, ap); + va_end(ap); + return (xx); +} + + +int +smb_poke_mbc(struct mbuf_chain *mbc, int offset, char *fmt, ...) +{ + int xx; + struct mbuf_chain tmp; + va_list ap; + + (void) MBC_SHADOW_CHAIN(&tmp, mbc, offset, mbc->max_bytes - offset); + va_start(ap, fmt); + xx = smb_mbc_encode(&tmp, fmt, ap); + va_end(ap); + return (xx); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_mbuf_util.c b/usr/src/uts/common/fs/smbsrv/smb_mbuf_util.c new file mode 100644 index 000000000000..b8e45289cded --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_mbuf_util.c @@ -0,0 +1,334 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright (c) 1982, 1986, 1988, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * MLRPC server-side library processing. This is where we decode the + * request header to figure out what type of request is being made, + * call the server stub, if appropriate, and build the response header. + */ + +#include +#include + +/* + * Fragment size (5680: NT style). Tuning parameter + * used during development of multiple fragment support. + */ + +#define MLRPC_FRAG_SIZE 5680 +#define MLRPC_RSP_HDR_SIZE 24 + +/* + * smb_mbuf_get + * + * Allocate mbufs to hold the amount of data specified. + * A pointer to the head of the mbuf list is returned. + */ +struct mbuf * +smb_mbuf_get(uchar_t *buf, int nbytes) +{ + struct mbuf *mhead = 0; + struct mbuf *m = 0; + int count; + int offset = 0; + + while (nbytes) { + count = (nbytes > MCLBYTES) ? MCLBYTES : nbytes; + nbytes -= count; + + if (mhead == 0) { + MGET(mhead, M_WAIT, MT_DATA); + m = mhead; + } else { + MGET(m->m_next, M_WAIT, MT_DATA); + m = m->m_next; + } + + if (count > MLEN) { + MCLGET(m, M_WAIT); + } + + m->m_len = count; + bcopy(buf + offset, m->m_data, count); + offset += count; + } + return (mhead); +} + +/* + * Allocate enough mbufs to accommodate the residual count in a uio. + */ +struct mbuf * +smb_mbuf_allocate(struct uio *uio) +{ + struct iovec *iovp; + struct mbuf *mhead = 0; + struct mbuf *m = 0; + int count, iovs, resid; + + iovp = uio->uio_iov; + iovs = uio->uio_iovcnt; + resid = uio->uio_resid; + + while ((resid > 0) && (iovs > 0)) { + count = (resid > MCLBYTES) ? MCLBYTES : resid; + resid -= count; + + if (mhead == 0) { + MGET(mhead, M_WAIT, MT_DATA); + m = mhead; + } else { + MGET(m->m_next, M_WAIT, MT_DATA); + m = m->m_next; + } + + if (count > MLEN) { + MCLGET(m, M_WAIT); + } + + iovp->iov_base = m->m_data; + iovp->iov_len = m->m_len = count; + iovs--; + iovp++; + } + + uio->uio_iovcnt -= iovs; + return (mhead); +} + +/* + * Trim an mbuf chain to nbytes. + */ +void +smb_mbuf_trim(struct mbuf *mhead, int nbytes) +{ + struct mbuf *m = mhead; + + while (m != 0) { + if (nbytes <= m->m_len) { + m->m_len = nbytes; + if (m->m_next != 0) { + m_freem(m->m_next); + m->m_next = 0; + } + break; + } + nbytes -= m->m_len; + m = m->m_next; + } +} + +int +MBC_LENGTH(struct mbuf_chain *MBC) +{ + struct mbuf *m = (MBC)->chain; + int used = 0; + + while (m != 0) { + used += m->m_len; + m = m->m_next; + } + return (used); +} + +void +MBC_SETUP(struct mbuf_chain *MBC, uint32_t max_bytes) +{ + bzero((MBC), sizeof (struct mbuf_chain)); + (MBC)->max_bytes = max_bytes ? max_bytes : smb_maxbufsize; +} + +void +MBC_INIT(struct mbuf_chain *MBC, uint32_t max_bytes) +{ + struct mbuf *m; + + bzero((MBC), sizeof (struct mbuf_chain)); + + if (max_bytes != 0) { + MGET(m, M_WAIT, MT_DATA); + m->m_len = 0; + (MBC)->chain = m; + if (max_bytes > MINCLSIZE) + MCLGET(m, M_WAIT); + } + (MBC)->max_bytes = max_bytes; +} + +void +MBC_FLUSH(struct mbuf_chain *MBC) +{ + extern void m_freem(struct mbuf *); + struct mbuf *m; + + while ((m = (MBC)->chain) != 0) { + (MBC)->chain = m->m_nextpkt; + m->m_nextpkt = 0; + m_freem(m); + } + MBC_SETUP(MBC, (MBC)->max_bytes); +} + +void +MBC_ATTACH_MBUF(struct mbuf_chain *MBC, struct mbuf *MBUF) +{ + if (MBC->chain != 0) + MBC_FLUSH(MBC); + + (MBC)->chain_offset = 0; + (MBC)->chain = (MBUF); +} + +void +MBC_APPEND_MBUF(struct mbuf_chain *MBC, struct mbuf *MBUF) +{ + struct mbuf *m; + + if ((MBC)->chain == 0) { + (MBC)->chain = (MBUF); + } else { + m = (MBC)->chain; + while (m->m_next != 0) + m = m->m_next; + m->m_next = (MBUF); + } +} + + +void +MBC_ATTACH_BUF(struct mbuf_chain *MBC, unsigned char *BUF, int LEN) +{ + MGET((MBC)->chain, M_WAIT, MT_DATA); + (MBC)->chain_offset = 0; + (MBC)->chain->m_flags |= M_EXT; + (MBC)->chain->m_data = (caddr_t)(BUF); + (MBC)->chain->m_ext.ext_buf = (caddr_t)(BUF); + (MBC)->chain->m_len = (LEN); + (MBC)->chain->m_ext.ext_size = (LEN); + (MBC)->chain->m_ext.ext_ref = smb_noop; + (MBC)->max_bytes = (LEN); +} + + +int +MBC_SHADOW_CHAIN(struct mbuf_chain *SUBMBC, struct mbuf_chain *MBC, + int OFF, int LEN) +{ + if (((OFF) + (LEN)) > (MBC)->max_bytes) + return (EMSGSIZE); + + *(SUBMBC) = *(MBC); + (SUBMBC)->chain_offset = (OFF); + (SUBMBC)->max_bytes = (OFF) + (LEN); + (SUBMBC)->shadow_of = (MBC); + return (0); +} + +/* + * Free a single mbuf structure. Calls m->m_ext.ext_ref() to free any + * associated external buffers if present (indicated by m->m_flags & M_EXT) + */ +struct mbuf * +m_free(struct mbuf *m) +{ + struct mbuf *n; + + MFREE(m, n); + return (n); +} + +/* + * Free a list of mbufs. Each mbuf in the list is freed similarly to m_free. + */ +void +m_freem(struct mbuf *m) +{ + struct mbuf *n; + + if (m == NULL) + return; + /* + * Lint doesn't like the m = n assignment at the close of the loop + * but it is correct. MFREE assigns n = (m)->m_next so the loop + * is effectively assigning m = (m)->m_next then exiting when + * m == NULL + */ + do { + MFREE(m, n); + } while ((m = n) != 0); +} + +/* + * Mbuffer utility routines. + */ + +int /*ARGSUSED*/ +mclref(caddr_t p, int size, int adj) /* size, adj are unused */ +{ + MEM_FREE("mbuf", p); + return (0); +} + +int /*ARGSUSED*/ +mclrefnoop(caddr_t p, int size, int adj) /* p, size, adj are unused */ +{ + return (0); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_memory_manager.c b/usr/src/uts/common/fs/smbsrv/smb_memory_manager.c new file mode 100644 index 000000000000..61e106638812 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_memory_manager.c @@ -0,0 +1,107 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Memory management functions. + */ + +#include + +/* + * smbsr_malloc + * + * allocate a block of memory with the given size and + * add it to the given linked list. This function is + * used to allocate temporary memories which are needed + * during processing of a SMB request. These memories + * get freed when request processing is finished. + */ +void * +smbsr_malloc(smb_malloc_list *list, size_t size) +{ + smb_malloc_list *element; + + size += sizeof (smb_malloc_list); + element = MEM_MALLOC("smb", size); + element->forw = list->forw; + element->back = list; + list->forw->back = element; + list->forw = element; + return (void *)(element + 1); /* return address of data */ +} + +/* + * smbsr_realloc + * + * This function is used in conjunction with smbsr_malloc to + * resize an already allocated entity. + */ +void * +smbsr_realloc(void *mem, size_t size) +{ + smb_malloc_list *element = (smb_malloc_list *)mem; + smb_malloc_list *new_entry; + smb_malloc_list *list; + + element--; + list = element->back; + QUEUE_CLIP(element); + size += sizeof (smb_malloc_list); + + new_entry = MEM_REALLOC("smb", element, size); + new_entry->forw = list->forw; + new_entry->back = list; + list->forw->back = new_entry; + list->forw = new_entry; + return (void *)(new_entry + 1); /* return address of new data */ +} + +/* + * smbsr_free_malloc_list + * + * Frees all memory block in the given linked list. + */ +void +smbsr_free_malloc_list(smb_malloc_list *root) +{ + smb_malloc_list *element; + + /* + * we initialize smb_request structure in smb_nt_notify_change + * function, so we should check root->forw to make sure it's + * not NULL. + */ + while (root->forw && root->forw != root) { + element = root->forw; + + element->forw->back = element->back; + element->back->forw = element->forw; + + /* and release it... */ + MEM_FREE("smb", element); + } +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_move.c b/usr/src/uts/common/fs/smbsrv/smb_move.c new file mode 100644 index 000000000000..eb44e5a84d57 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_move.c @@ -0,0 +1,114 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: move + * + * The source file is copied to the destination and the source is + * subsequently deleted. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 3 + * USHORT Tid2; Second (target) file id + * USHORT OpenFunction; what to do if target file exists + * USHORT Flags; Flags to control move operations: + * 0 - target must be a file + * 1 - target must be a directory + * 2 - reserved (must be 0) + * 3 - reserved (must be 0) + * 4 - verify all writes + * USHORT ByteCount; Count of data bytes; min = 2 + * UCHAR Format1; 0x04 + * STRING OldFileName[]; Old file name + * UCHAR FormatNew; 0x04 + * STRING NewFileName[]; New file name + * + * OldFileName is copied to NewFileName, then OldFileName is deleted. Both + * OldFileName and NewFileName must refer to paths on the same server. + * NewFileName can refer to either a file or a directory. All file + * components except the last must exist; directories will not be created. + * + * NewFileName can be required to be a file or a directory by the Flags + * field. + * + * The Tid in the header is associated with the source while Tid2 is + * associated with the destination. These fields may contain the same or + * differing valid values. Tid2 can be set to -1 indicating that this is to + * + * be the same Tid as in the SMB header. This allows use of the move + * protocol with SMB_TREE_CONNECT_ANDX. + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 1 + * USHORT Count; Number of files moved + * USHORT ByteCount; Count of data bytes; min = 0 + * UCHAR ErrorFileFormat; 0x04 (only if error) + * STRING ErrorFileName[]; Pathname of file where error + * occurred + * + * The source path must refer to an existing file or files. Wildcards are + * permitted. Source files specified by wildcards are processed until an + * error is encountered. If an error is encountered, the expanded name of + * the file is returned in ErrorFileName. Wildcards are not permitted in + * NewFileName. + * + * OpenFunction controls what should happen if the destination file exists. + * If (OpenFunction & 0x30) == 0, the operation should fail if the + * destination exists. If (OpenFunction & 0x30) == 0x20, the destination + * file should be overwritten. + * + * 4.2.12.1 Errors + * + * ERRDOS/ERRfilexists + * ERRDOS/ERRbadfile + * ERRDOS/ERRnoaccess + * ERRDOS/ERRnofiles + * ERRDOS/ERRbadshare + * ERRHRD/ERRnowrite + * ERRSRV/ERRnoaccess + * ERRSRV/ERRinvdevice + * ERRSRV/ERRinvid + * ERRSRV/ERRbaduid + * ERRSRV/ERRnosupport + * ERRSRV/ERRaccess + */ + +#include +/*ARGSUSED*/ +int +smb_com_move(struct smb_request *sr) +{ + /* TODO move */ + /* TODO move wildcards */ + /* TODO move */ + + return (SDRC_UNIMPLEMENTED); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_negotiate.c b/usr/src/uts/common/fs/smbsrv/smb_negotiate.c new file mode 100644 index 000000000000..844e84a88926 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_negotiate.c @@ -0,0 +1,478 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Notes on the virtual circuit (VC) values in the SMB Negotiate + * response and SessionSetupAndx request. + * + * A virtual circuit (VC) represents a connection between a client and a + * server using a reliable, session oriented transport protocol, such as + * NetBIOS or TCP/IP. Originally, each SMB session was restricted to a + * single underlying transport connection, i.e. a single NetBIOS session, + * which limited performance for raw data transfers. + * + * The intention behind multiple VCs was to improve performance by + * allowing parallelism over each NetBIOS session. For example, raw data + * could be transmitted using a different VC from other types of SMB + * requests to remove the interleaving restriction while a raw transfer + * is in progress. So the MaxNumberVcs field was added to the negotiate + * response to make the number of VCs configurable and to allow servers + * to specify how many they were prepared to support per session + * connection. This turned out to be difficult to manage and, with + * technology improvements, it has become obsolete. + * + * Servers should set the MaxNumberVcs value in the Negotiate response + * to 1. Clients should probably ignore it. If a server receives a + * SessionSetupAndx with a VC value of 0, it should close all other + * VCs to that client. If it receives a non-zero VC, it should leave + * other VCs in tact. + * + */ + +/* + * SMB: negotiate + * + * Client Request Description + * ============================ ======================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes; min = 2 + * struct { + * UCHAR BufferFormat; 0x02 -- Dialect + * UCHAR DialectName[]; ASCII null-terminated string + * } Dialects[]; + * + * The Client sends a list of dialects that it can communicate with. The + * response is a selection of one of those dialects (numbered 0 through n) + * or -1 (hex FFFF) indicating that none of the dialects were acceptable. + * The negotiate message is binding on the virtual circuit and must be + * sent. One and only one negotiate message may be sent, subsequent + * negotiate requests will be rejected with an error response and no action + * will be taken. + * + * The protocol does not impose any particular structure to the dialect + * strings. Implementors of particular protocols may choose to include, + * for example, version numbers in the string. + * + * If the server does not understand any of the dialect strings, or if PC + * NETWORK PROGRAM 1.0 is the chosen dialect, the response format is + * + * Server Response Description + * ============================ ======================================= + * + * UCHAR WordCount; Count of parameter words = 1 + * USHORT DialectIndex; Index of selected dialect + * USHORT ByteCount; Count of data bytes = 0 + * + * If the chosen dialect is greater than core up to and including + * LANMAN2.1, the protocol response format is + * + * Server Response Description + * ============================ ======================================= + * + * UCHAR WordCount; Count of parameter words = 13 + * USHORT DialectIndex; Index of selected dialect + * USHORT SecurityMode; Security mode: + * bit 0: 0 = share, 1 = user + * bit 1: 1 = use challenge/response + * authentication + * USHORT MaxBufferSize; Max transmit buffer size (>= 1024) + * USHORT MaxMpxCount; Max pending multiplexed requests + * USHORT MaxNumberVcs; Max VCs between client and server + * USHORT RawMode; Raw modes supported: + * bit 0: 1 = Read Raw supported + * bit 1: 1 = Write Raw supported + * ULONG SessionKey; Unique token identifying this session + * SMB_TIME ServerTime; Current time at server + * SMB_DATE ServerDate; Current date at server + * USHORT ServerTimeZone; Current time zone at server + * USHORT EncryptionKeyLength; MBZ if this is not LM2.1 + * USHORT Reserved; MBZ + * USHORT ByteCount Count of data bytes + * UCHAR EncryptionKey[]; The challenge encryption key + * STRING PrimaryDomain[]; The server's primary domain + * + * MaxBufferSize is the size of the largest message which the client can + * legitimately send to the server + * + * If bit0 of the Flags field is set in the negotiate response, this + * indicates the server supports the SMB_COM_LOCK_AND_READ and + * SMB_COM_WRITE_AND_UNLOCK client requests. + * + * If the SecurityMode field indicates the server is running in user mode, + * the client must send appropriate SMB_COM_SESSION_SETUP_ANDX requests + * before the server will allow the client to access resources. If the + * SecurityMode fields indicates the client should use challenge/response + * authentication, the client should use the authentication mechanism + * specified in section 2.10. + * + * Clients should submit no more than MaxMpxCount distinct unanswered SMBs + * to the server when using multiplexed reads or writes (see sections 5.13 + * and 5.25) + * + * Clients using the "MICROSOFT NETWORKS 1.03" dialect use a different + * form of raw reads than documented here, and servers are better off + * setting RawMode in this response to 0 for such sessions. + * + * If the negotiated dialect is "DOS LANMAN2.1" or "LANMAN2.1", then + * PrimaryDomain string should be included in this response. + * + * If the negotiated dialect is NT LM 0.12, the response format is + * + * Server Response Description + * ========================== ========================================= + * + * UCHAR WordCount; Count of parameter words = 17 + * USHORT DialectIndex; Index of selected dialect + * UCHAR SecurityMode; Security mode: + * bit 0: 0 = share, 1 = user + * bit 1: 1 = encrypt passwords + * USHORT MaxMpxCount; Max pending multiplexed requests + * USHORT MaxNumberVcs; Max VCs between client and server + * ULONG MaxBufferSize; Max transmit buffer size + * ULONG MaxRawSize; Maximum raw buffer size + * ULONG SessionKey; Unique token identifying this session + * ULONG Capabilities; Server capabilities + * ULONG SystemTimeLow; System (UTC) time of the server (low). + * ULONG SystemTimeHigh; System (UTC) time of the server (high). + * USHORT ServerTimeZone; Time zone of server (min from UTC) + * UCHAR EncryptionKeyLength; Length of encryption key. + * USHORT ByteCount; Count of data bytes + * UCHAR EncryptionKey[]; The challenge encryption key + * UCHAR OemDomainName[]; The name of the domain (in OEM chars) + * + * In addition to the definitions above, MaxBufferSize is the size of the + * largest message which the client can legitimately send to the server. + * If the client is using a connectionless protocol, MaxBufferSize must be + * set to the smaller of the server's internal buffer size and the amount + * of data which can be placed in a response packet. + * + * MaxRawSize specifies the maximum message size the server can send or + * receive for SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. + * + * Connectionless clients must set Sid to 0 in the SMB request header. + * + * Capabilities allows the server to tell the client what it supports. + * The bit definitions defined in cifs.h. Bit 0x2000 used to be set in + * the negotiate response capabilities but it caused problems with + * Windows 2000. It is probably not valid, it doesn't appear in the + * CIFS spec. + * + * 4.1.1.1 Errors + * + * SUCCESS/SUCCESS + * ERRSRV/ERRerror + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * Maximum buffer size for DOS: chosen to be the same as NT. + * Do not change this value, DOS is very sensitive to it. + */ +#define SMB_DOS_MAXBUF 0x1104 + +/* + * Maximum buffer size for NT: configurable based on the client environment. + * IR104720 Experiments with Windows 2000 indicate that we achieve better + * SmbWriteX performance with a buffer size of 64KB instead of the 37KB + * used with Windows NT4.0. Previous experiments with NT4.0 resulted in + * directory listing problems so this buffer size is configurable based + * on the end-user environment. When in doubt use 37KB. + */ +int smb_maxbufsize = SMB_NT_MAXBUF(37); + +/* + * The DOS TCP rcvbuf is set to 8700 because DOS 6.1 seems to have problems + * with other values. DOS 6.1 seems to depend on a window value of 8700 to + * send the next set of data. If we return a window value of 40KB, after + * sending 8700 bytes of data, it will start the next set of data from 40KB + * instead of 8.7k. Why 8.7k? We have no idea; it is the value that NT uses. + * September 2000. + * + * IR104720 Increased smb_nt_tcp_rcvbuf from 40KB to just under 1MB to allow + * for a larger TCP window sizei based on observations of Windows 2000 and + * performance testing. March 2003. + */ +uint32_t smb_dos_tcp_rcvbuf = 8700; +uint32_t smb_nt_tcp_rcvbuf = 1048560; /* scale factor of 4 */ + +static void smb_get_security_info( + struct smb_request *sr, + unsigned short *secmode, + unsigned char *key, + unsigned char *keylen, + uint32_t *sesskey); + +/* + * Function: int smb_com_negotiate(struct smb_request *) + */ + +int +smb_com_negotiate(struct smb_request *sr) +{ + int dialect = 0; + int this_dialect; + unsigned char keylen; + int sel_pos = -1; + int pos; + char key[32]; + char *p; + timestruc_t time_val; + unsigned short secmode; + uint32_t sesskey; + uint32_t capabilities = 0; + + unsigned short max_mpx_count; + WORD tz_correction; + char ipaddr_buf[INET_ADDRSTRLEN]; + + if (sr->session->s_state != SMB_SESSION_STATE_ESTABLISHED) { + /* The protocol has already been negotiated. */ + smbsr_raise_error(sr, ERRSRV, ERRerror); + /* NOTREACHED */ + } + + for (pos = 0; + sr->smb_data.chain_offset < sr->smb_data.max_bytes; + pos++) { + if (smb_decode_mbc(&sr->smb_data, "%L", sr, &p) != 0) { + smbsr_raise_error(sr, ERRSRV, ERRerror); + /* NOTREACHED */ + } + + this_dialect = smb_xlate_dialect_str_to_cd(p); + + if (this_dialect < 0) + continue; + + if (dialect < this_dialect) { + dialect = this_dialect; + sel_pos = pos; + } + } + if (sel_pos < 0) { + smbsr_raise_error(sr, ERRSRV, ERRerror); + /* NOTREACHED */ + } + + smb_get_security_info(sr, &secmode, (unsigned char *)key, + &keylen, &sesskey); + + (void) microtime(&time_val); + + tz_correction = -(WORD)(smb_get_gmtoff() / 60); /* tz correct. (min) */ + + switch (dialect) { + case DIALECT_UNKNOWN: + case PC_NETWORK_PROGRAM_1_0: /* core */ + (void) sosetsockopt(sr->session->sock, SOL_SOCKET, SO_RCVBUF, + (const void *)&smb_dos_tcp_rcvbuf, + sizeof (smb_dos_tcp_rcvbuf)); + smbsr_encode_result(sr, 1, 0, "bww", 1, sel_pos, 0); + break; + + case Windows_for_Workgroups_3_1a: + case PCLAN1_0: + case MICROSOFT_NETWORKS_1_03: + case MICROSOFT_NETWORKS_3_0: + case LANMAN1_0: + case LM1_2X002: + case DOS_LM1_2X002: + (void) sosetsockopt(sr->session->sock, SOL_SOCKET, SO_RCVBUF, + (const void *)&smb_dos_tcp_rcvbuf, + sizeof (smb_dos_tcp_rcvbuf)); + sr->smb_flg |= SMB_FLAGS_LOCK_AND_READ_OK; + smbsr_encode_result(sr, 13, VAR_BCC, + "(wct) b" "(dix) w" "(sec) w" "(mbs) w" + "(mmc) w" "(mnv) w" "(raw) w" "(key) l" + "(tim/dat) Y" "(tz) w" "(ekl) w" + "(mbz) 2.""(bcc) w" "(key) #c", + 13, /* wct */ + sel_pos, /* dialect index */ + secmode, /* security mode */ + SMB_DOS_MAXBUF, /* max buffer size */ + 1, /* max MPX (temporary) */ + 1, /* max VCs (temporary, ambiguous) */ + 3, /* raw mode (s/b 3) */ + sesskey, /* session key */ + time_val.tv_sec, /* server time/date */ + tz_correction, /* see smb_get_gmtoff */ + (short)keylen, /* Encryption Key Length */ + /* reserved field handled 2. */ + VAR_BCC, + (int)keylen, + key); /* encryption key */ + break; + + case DOS_LANMAN2_1: + case LANMAN2_1: + (void) sosetsockopt(sr->session->sock, SOL_SOCKET, SO_RCVBUF, + (const void *)&smb_dos_tcp_rcvbuf, + sizeof (smb_dos_tcp_rcvbuf)); + sr->smb_flg |= SMB_FLAGS_LOCK_AND_READ_OK; + smbsr_encode_result(sr, 13, VAR_BCC, + "(wct) b" "(dix) w" "(sec) w" "(mbs) w" + "(mmc) w" "(mnv) w" "(raw) w" "(key) l" + "(tim/dat) Y" "(tz) w" "(ekl) w" + "(mbz) 2.""(bcc) w" "(key) #c" "(dom) s", + 13, /* wct */ + sel_pos, /* dialect index */ + secmode, /* security mode */ + SMB_DOS_MAXBUF, /* max buffer size */ + 1, /* max MPX (temporary) */ + 1, /* max VCs (temporary, ambiguous) */ + 3, /* raw mode (s/b 3) */ + sesskey, /* session key */ + time_val.tv_sec, /* server time/date */ + tz_correction, + (short)keylen, /* Encryption Key Length */ + /* reserved field handled 2. */ + VAR_BCC, + (int)keylen, + key, /* encryption key */ + smb_info.si.skc_resource_domain); + break; + + case NT_LM_0_12: + (void) sosetsockopt(sr->session->sock, SOL_SOCKET, SO_RCVBUF, + (const void *)&smb_nt_tcp_rcvbuf, + sizeof (smb_nt_tcp_rcvbuf)); + capabilities = CAP_LARGE_FILES + | CAP_NT_SMBS + | CAP_STATUS32 + | CAP_NT_FIND + | CAP_RAW_MODE + | CAP_LEVEL_II_OPLOCKS + | CAP_LOCK_AND_READ + | CAP_RPC_REMOTE_APIS + | CAP_LARGE_READX; + + /* + * UNICODE support is required to enable support for long + * share names and long file names and streams. + */ + + capabilities |= CAP_UNICODE; + + + /* + * Turn off Extended Security Negotiation + */ + sr->smb_flg2 &= ~SMB_FLAGS2_EXT_SEC; + + /* + * Allow SMB signatures if security challenge response enabled + */ + if ((secmode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && + smb_info.si.skc_signing_enable) { + secmode |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED; + if (smb_info.si.skc_signing_required) + secmode |= + NEGOTIATE_SECURITY_SIGNATURES_REQUIRED; + + sr->session->secmode = secmode; + } + + (void) inet_ntop(AF_INET, (char *)&sr->session->ipaddr, + ipaddr_buf, sizeof (ipaddr_buf)); + /*LINTED E_ASSIGN_NARROW_CONV (uint16_t)*/ + max_mpx_count = smb_info.si.skc_maxworkers; + + smbsr_encode_result(sr, 17, VAR_BCC, + "(wct) b" "(dix) w" "(sec) b" "(mmc) w" + "(mnv) w" "(mbs) l" "(raw) l" "(key) l" + "(cap) l" "(tim) T" "(tz) w" "(ekl) b" + "(bcc) w" "(key) #c" "(dom) Z", + 17, /* wct */ + sel_pos, /* dialect index */ + secmode, /* security mode */ + max_mpx_count, /* max MPX (temporary) */ + 1, /* max VCs (temporary, ambiguous) */ + (DWORD)smb_maxbufsize, /* max buffer size */ + 0xFFFF, /* max raw size */ + sesskey, /* session key */ + capabilities, + &time_val, /* system time */ + tz_correction, + keylen, /* Encryption Key Length */ + VAR_BCC, + (int)keylen, + key, /* encryption key */ + smb_info.si.skc_resource_domain); + break; + + default: + /* Just to make sure. */ + ASSERT(0); + smbsr_raise_error(sr, ERRSRV, ERRerror); + /* NOTREACHED */ + } + + /* + * Save the agreed dialect. Note that this value is also + * used to detect and reject attempts to re-negotiate. + */ + sr->session->dialect = dialect; + sr->session->s_state = SMB_SESSION_STATE_NEGOTIATED; + return (SDRC_NORMAL_REPLY); +} + +static void +smb_get_security_info( + struct smb_request *sr, + unsigned short *secmode, + unsigned char *key, + unsigned char *keylen, + uint32_t *sesskey) +{ + uchar_t tmp_key[8]; + + (void) random_get_pseudo_bytes(tmp_key, 8); + bcopy(tmp_key, &sr->session->challenge_key, 8); + sr->session->challenge_len = 8; + *keylen = 8; + bcopy(tmp_key, key, 8); + + sr->session->secmode = NEGOTIATE_SECURITY_CHALLENGE_RESPONSE| + NEGOTIATE_SECURITY_USER_LEVEL; + + (void) random_get_pseudo_bytes(tmp_key, 4); + sr->session->sesskey = tmp_key[0] | tmp_key[1] << 8 | + tmp_key[2] << 16 | tmp_key[3] << 24; + + *secmode = sr->session->secmode; + *sesskey = sr->session->sesskey; +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_net.c b/usr/src/uts/common/fs/smbsrv/smb_net.c new file mode 100644 index 000000000000..0c8f0a9db3a7 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_net.c @@ -0,0 +1,281 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef mem_free /* XXX Remove this after we convert everything to kmem_alloc */ + +#include +#include +#include +#include +#include + +/* + * SMB Network Socket API + * + * smb_socreate: Creates an socket based on domain/type. + * smb_soshutdown: Disconnect a socket created with smb_socreate + * smb_sodestroy: Release resources associated with a socket + * smb_sosend: Send the contents of a buffer on a socket + * smb_sorecv: Receive data into a buffer from a socket + * smb_iov_sosend: Send the contents of an iovec on a socket + * smb_iov_sorecv: Receive data into an iovec from a socket + */ + +struct sonode * +smb_socreate(int domain, int type, int protocol) +{ + vnode_t *dvp = NULL; + vnode_t *vp = NULL; + struct snode *csp = NULL; + int err = 0; + major_t maj; + + if ((vp = solookup(domain, type, protocol, NULL, &err)) == NULL) { + + /* + * solookup calls sogetvp if the vp is not found in the cache. + * Since the call to sogetvp is hardwired to use USERSPACE + * and declared static we'll do the work here instead. + */ + err = lookupname(type == SOCK_STREAM ? "/dev/tcp" : "/dev/udp", + UIO_SYSSPACE, FOLLOW, NULLVPP, &vp); + if (err) + return (NULL); + + /* Check that it is the correct vnode */ + if (vp->v_type != VCHR) { + VN_RELE(vp); + return (NULL); + } + + csp = VTOS(VTOS(vp)->s_commonvp); + if (!(csp->s_flag & SDIPSET)) { + char *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP); + err = ddi_dev_pathname(vp->v_rdev, S_IFCHR, + pathname); + if (err == 0) { + err = devfs_lookupname(pathname, NULLVPP, + &dvp); + } + VN_RELE(vp); + kmem_free(pathname, MAXPATHLEN); + if (err != 0) { + return (NULL); + } + vp = dvp; + } + + maj = getmajor(vp->v_rdev); + if (!STREAMSTAB(maj)) { + VN_RELE(vp); + return (NULL); + } + } + + return (socreate(vp, domain, type, protocol, SOV_DEFAULT, NULL, &err)); +} + +/* + * smb_soshutdown will disconnect the socket and prevent subsequent PDU + * reception and transmission. The sonode still exists but its state + * gets modified to indicate it is no longer connected. Calls to + * smb_sorecv/smb_iov_sorecv will return so smb_soshutdown can be used + * regain control of a thread stuck in smb_sorecv. + */ +void +smb_soshutdown(struct sonode *so) +{ + (void) soshutdown(so, SHUT_RDWR); +} + +/* + * smb_sodestroy releases all resources associated with a socket previously + * created with smb_socreate. The socket must be shutdown using smb_soshutdown + * before the socket is destroyed with smb_sodestroy, otherwise undefined + * behavior will result. + */ +void +smb_sodestroy(struct sonode *so) +{ + vnode_t *vp = SOTOV(so); + + (void) VOP_CLOSE(vp, 0, 1, 0, kcred, NULL); + VN_RELE(vp); +} + +int +smb_sosend(struct sonode *so, void *msg, size_t len) +{ + iovec_t iov; + int err; + + ASSERT(so != NULL); + ASSERT(len != 0); + + /* + * Fill in iovec and receive data + */ + iov.iov_base = msg; + iov.iov_len = len; + + if ((err = smb_iov_sosend(so, &iov, 1, len)) != 0) { + return (err); + } + + /* Successful receive */ + return (0); +} + +int +smb_sorecv(struct sonode *so, void *msg, size_t len) +{ + iovec_t iov; + int err; + + ASSERT(so != NULL); + ASSERT(len != 0); + + /* + * Fill in iovec and receive data + */ + iov.iov_base = msg; + iov.iov_len = len; + + if ((err = smb_iov_sorecv(so, &iov, 1, len)) != 0) { + return (err); + } + + /* Successful receive */ + return (0); +} + +/* + * smb_iov_sosend - Sends an iovec on a connection. + * + * This function puts the data provided on the wire by calling sosendmsg. + * It will return only when all the data has been sent or if an error + * occurs. + * + * Returns 0 for success, the socket errno value if sosendmsg fails, and + * -1 if sosendmsg returns success but uio_resid != 0 + */ +int +smb_iov_sosend(struct sonode *so, iovec_t *iop, int iovlen, size_t total_len) +{ + struct msghdr msg; + struct uio uio; + int error; + + ASSERT(iop != NULL); + + /* Initialization of the message header. */ + bzero(&msg, sizeof (msg)); + msg.msg_iov = iop; + msg.msg_flags = MSG_WAITALL; + msg.msg_iovlen = iovlen; + + /* Initialization of the uio structure. */ + bzero(&uio, sizeof (uio)); + uio.uio_iov = iop; + uio.uio_iovcnt = iovlen; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_resid = total_len; + + if ((error = sosendmsg(so, &msg, &uio)) == 0) { + /* Data sent */ + if (uio.uio_resid == 0) { + /* All data sent. Success. */ + return (0); + } else { + /* Not all data was sent. Failure */ + return (-1); + } + } + + /* Send failed */ + return (error); +} + +/* + * smb_iov_sorecv - Receives an iovec from a connection + * + * This function gets the data asked for from the socket. It will return + * only when all the requested data has been retrieved or if an error + * occurs. + * + * Returns 0 for success, the socket errno value if sorecvmsg fails, and + * -1 if sorecvmsg returns success but uio_resid != 0 + */ +int +smb_iov_sorecv(struct sonode *so, iovec_t *iop, int iovlen, size_t total_len) +{ + struct msghdr msg; + struct uio uio; + int error; + + ASSERT(iop != NULL); + + /* Initialization of the message header. */ + bzero(&msg, sizeof (msg)); + msg.msg_iov = iop; + msg.msg_flags = MSG_WAITALL; + msg.msg_iovlen = iovlen; + + /* Initialization of the uio structure. */ + bzero(&uio, sizeof (uio)); + uio.uio_iov = iop; + uio.uio_iovcnt = iovlen; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_resid = total_len; + + if ((error = sorecvmsg(so, &msg, &uio)) == 0) { + /* Received data */ + if (uio.uio_resid == 0) { + /* All requested data received. Success */ + return (0); + } else { + /* Not all data was sent. Failure */ + return (-1); + } + } + + /* Receive failed */ + return (error); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_node.c b/usr/src/uts/common/fs/smbsrv/smb_node.c new file mode 100644 index 000000000000..5ea8c5f1c368 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_node.c @@ -0,0 +1,770 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB Node State Machine + * ---------------------- + * + * +----------------------------+ T0 + * | SMB_NODE_STATE_AVAILABLE |<----------- Creation/Allocation + * +----------------------------+ + * | + * | T1 + * | + * v + * +-----------------------------+ T2 + * | SMB_NODE_STATE_DESTROYING |----------> Deletion/Free + * +-----------------------------+ + * + * Transition T0 + * + * This transition occurs in smb_node_lookup(). If the node looked for is + * not found in the has table a new node is created. The reference count is + * initialized to 1 and the state initialized to SMB_NODE_STATE_AVAILABLE. + * + * Transition T1 + * + * This transition occurs in smb_node_release(). If the reference count + * drops to zero the state is moved to SMB_NODE_STATE_DESTROYING and no more + * reference count will be given out for that node. + * + * Transition T2 + * + * This transition occurs in smb_node_release(). The structure is deleted. + * + * Comments + * -------- + * + * The reason the smb node has 2 states is the following synchronization + * rule: + * + * There's a mutex embedded in the node used to protect its fields and + * there's a lock embedded in the bucket of the hash table the node belongs + * to. To increment or to decrement the reference count the mutex must be + * entered. To insert the node into the bucket and to remove it from the + * bucket the lock must be entered in RW_WRITER mode. When both (mutex and + * lock) have to be entered, the lock has always to be entered first then + * the mutex. This prevents a deadlock between smb_node_lookup() and + * smb_node_release() from occurring. However, in smb_node_release() when the + * reference count drops to zero and triggers the deletion of the node, the + * mutex has to be released before entering the lock of the bucket (to + * remove the node). This creates a window during which the node that is + * about to be freed could be given out by smb_node_lookup(). To close that + * window the node is moved to the state SMB_NODE_STATE_DESTROYING before + * releasing the mutex. That way, even if smb_node_lookup() finds it, the + * state will indicate that the node should be treated as non existent (of + * course the state of the node should be tested/updated under the + * protection of the mutex). + */ +#include +#include +#include +#include + +uint32_t smb_is_executable(char *path); +static void smb_node_delete_on_close(smb_node_t *node); + +uint32_t smb_node_hit = 0; +uint32_t smb_node_miss = 0; +uint32_t smb_node_alloc = 0; +uint32_t smb_node_free = 0; + +#define VALIDATE_DIR_NODE(_dir_, _node_) \ + ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \ + ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \ + ASSERT((_dir_)->dir_snode != (_node_)); + +/* + * smb_node_lookup() + * + * NOTE: This routine should only be called by the file system interface layer, + * and not by SMB. + * + * smb_node_lookup() is called upon successful lookup, mkdir, and create + * (for both non-streams and streams). In each of these cases, a held vnode is + * passed into this routine. If an smb_node already exists for this vnode, + * the vp is released. Otherwise, a new smb_node will be created and the + * reference will be held until the refcnt on the node goes to 0 (see + * smb_node_release()). + * + * A reference is taken on the smb_node whether found in the hash table + * or newly created. + * + * If an smb_node needs to be created, a reference is also taken on the + * dir_snode (if passed in). + * + * See smb_node_release() for details on the release of these references. + */ + +/*ARGSUSED*/ +smb_node_t * +smb_node_lookup( + struct smb_request *sr, + struct open_param *op, + cred_t *cred, + vnode_t *vp, + char *od_name, + smb_node_t *dir_snode, + smb_node_t *unnamed_node, + smb_attr_t *attr) +{ + smb_llist_t *node_hdr; + smb_node_t *node; + uint32_t hashkey = 0; + fs_desc_t fsd; + int error; + krw_t lock_mode; + caller_context_t ct; + vnode_t *unnamed_vp = NULL; + + /* + * smb_vop_getattr() is called here instead of smb_fsop_getattr(), + * because the node may not yet exist. We also do not want to call + * it with the list lock held. + */ + + if (unnamed_node) + unnamed_vp = unnamed_node->vp; + + /* + * This getattr is performed on behalf of the server + * that's why kcred is used not the user's cred + */ + smb_get_caller_context(sr, &ct); + attr->sa_mask = SMB_AT_ALL; + error = smb_vop_getattr(vp, unnamed_vp, attr, 0, kcred, &ct); + if (error) + return (NULL); + + if (sr) { + if (sr->tid_tree) { + /* + * The fsd for a file is that of the tree, even + * if the file resides in a different mountpoint + * under the share. + */ + fsd = sr->tid_tree->t_fsd; + } else { + /* + * This should be getting executed only for the + * tree's root smb_node. + */ + fsd = vp->v_vfsp->vfs_fsid; + } + } else { + fsd = vp->v_vfsp->vfs_fsid; + } + + hashkey = fsd.val[0] + attr->sa_vattr.va_nodeid; + hashkey += (hashkey >> 24) + (hashkey >> 16) + (hashkey >> 8); + node_hdr = &smb_info.node_hash_table[(hashkey & SMBND_HASH_MASK)]; + lock_mode = RW_READER; + + smb_llist_enter(node_hdr, lock_mode); + for (;;) { + node = list_head(&node_hdr->ll_list); + while (node) { + ASSERT(node->n_magic == SMB_NODE_MAGIC); + ASSERT(node->n_hash_bucket == node_hdr); + if ((node->n_hashkey == hashkey) && (node->vp == vp)) { + smb_rwx_xenter(&node->n_lock); + DTRACE_PROBE1(smb_node_lookup_hit, + smb_node_t *, node); + switch (node->n_state) { + case SMB_NODE_STATE_AVAILABLE: + /* The node was found. */ + node->n_refcnt++; + if ((node->dir_snode == NULL) && + (dir_snode != NULL) && + (strcmp(od_name, "..") != 0) && + (strcmp(od_name, ".") != 0)) { + VALIDATE_DIR_NODE(dir_snode, + node); + node->dir_snode = dir_snode; + smb_node_ref(dir_snode); + } + node->attr = *attr; + + smb_audit_node(node); + smb_rwx_xexit(&node->n_lock); + smb_llist_exit(node_hdr); + VN_RELE(vp); + return (node); + + case SMB_NODE_STATE_DESTROYING: + /* + * Although the node exists it is about + * to be destroyed. We act as it hasn't + * been found. + */ + smb_rwx_xexit(&node->n_lock); + break; + default: + /* + * Although the node exists it is in an + * unknown state. We act as it hasn't + * been found. + */ + ASSERT(0); + smb_rwx_xexit(&node->n_lock); + break; + } + } + node = smb_llist_next(node_hdr, node); + } + if ((lock_mode == RW_READER) && smb_llist_upgrade(node_hdr)) { + lock_mode = RW_WRITER; + continue; + } + break; + } + node = kmem_cache_alloc(smb_info.si_cache_node, KM_SLEEP); + smb_node_alloc++; + + bzero(node, sizeof (smb_node_t)); + + node->n_state = SMB_NODE_STATE_AVAILABLE; + node->n_hash_bucket = node_hdr; + + if (fsd_chkcap(&fsd, FSOLF_READONLY) > 0) { + node->flags |= NODE_READ_ONLY; + } + + smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t), + offsetof(smb_ofile_t, f_nnd)); + smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t), + offsetof(smb_lock_t, l_lnd)); + node->n_hashkey = hashkey; + node->n_refcnt = 1; + + if (sr) { + node->n_orig_session_id = sr->session->s_kid; + node->n_orig_uid = crgetuid(sr->user_cr); + } + + node->vp = vp; + + ASSERT(od_name); + (void) strlcpy(node->od_name, od_name, sizeof (node->od_name)); + node->tree_fsd = fsd; + + if (op) + node->flags |= smb_is_executable(op->fqi.last_comp); + + if (dir_snode) { + smb_node_ref(dir_snode); + node->dir_snode = dir_snode; + ASSERT(dir_snode->dir_snode != node); + ASSERT((dir_snode->vp->v_xattrdir) || + (dir_snode->vp->v_type == VDIR)); + } + + if (unnamed_node) { + smb_node_ref(unnamed_node); + node->unnamed_stream_node = unnamed_node; + } + + node->attr = *attr; + node->flags |= NODE_FLAGS_ATTR_VALID; + node->n_size = node->attr.sa_vattr.va_size; + + smb_rwx_init(&node->n_lock); + node->n_magic = SMB_NODE_MAGIC; + smb_audit_buf_node_create(node); + + DTRACE_PROBE1(smb_node_lookup_miss, smb_node_t *, node); + smb_audit_node(node); + smb_llist_insert_head(node_hdr, node); + smb_llist_exit(node_hdr); + return (node); +} + +/* + * smb_stream_node_lookup() + * + * Note: stream_name (the name that will be stored in the "od_name" field + * of a stream's smb_node) is the same as the on-disk name for the stream + * except that it does not have SMB_STREAM_PREFIX prepended. + */ + +smb_node_t * +smb_stream_node_lookup(struct smb_request *sr, cred_t *cr, smb_node_t *fnode, + vnode_t *xattrdirvp, vnode_t *vp, char *stream_name, smb_attr_t *ret_attr) +{ + smb_node_t *xattrdir_node; + smb_node_t *snode; + smb_attr_t tmp_attr; + + xattrdir_node = smb_node_lookup(sr, NULL, cr, xattrdirvp, XATTR_DIR, + fnode, NULL, &tmp_attr); + + if (xattrdir_node == NULL) + return (NULL); + + snode = smb_node_lookup(sr, NULL, cr, vp, stream_name, xattrdir_node, + fnode, ret_attr); + + /* + * The following VN_HOLD is necessary because the caller will VN_RELE + * xattrdirvp in the case of an error. (xattrdir_node has the original + * hold on the vnode, which the smb_node_release() call below will + * release.) + */ + if (snode == NULL) { + VN_HOLD(xattrdirvp); + } + (void) smb_node_release(xattrdir_node); + return (snode); +} + + +/* + * This function should be called whenever a reference is needed on an + * smb_node pointer. The copy of an smb_node pointer from one non-local + * data structure to another requires a reference to be taken on the smb_node + * (unless the usage is localized). Each data structure deallocation routine + * will call smb_node_release() on its smb_node pointers. + * + * In general, an smb_node pointer residing in a structure should never be + * stale. A node pointer may be NULL, however, and care should be taken + * prior to calling smb_node_ref(), which ASSERTs that the pointer is valid. + * Care also needs to be taken with respect to racing deallocations of a + * structure. + */ + +void +smb_node_ref(smb_node_t *node) +{ + ASSERT(node); + ASSERT(node->n_magic == SMB_NODE_MAGIC); + ASSERT(node->n_state == SMB_NODE_STATE_AVAILABLE); + + smb_rwx_xenter(&node->n_lock); + node->n_refcnt++; + ASSERT(node->n_refcnt); + DTRACE_PROBE1(smb_node_ref_exit, smb_node_t *, node); + smb_audit_node(node); + smb_rwx_xexit(&node->n_lock); +} + +/* + * smb_node_lookup() takes a hold on an smb_node, whether found in the + * hash table or newly created. This hold is expected to be released + * in the following manner. + * + * smb_node_lookup() takes an address of an smb_node pointer. This should + * be getting passed down via a lookup (whether path name or component), mkdir, + * create. If the original smb_node pointer resides in a data structure, then + * the deallocation routine for the data structure is responsible for calling + * smb_node_release() on the smb_node pointer. Alternatively, + * smb_node_release() can be called as soon as the smb_node pointer is no longer + * needed. In this case, callers are responsible for setting an embedded + * pointer to NULL if it is known that the last reference is being released. + * + * If the passed-in address of the smb_node pointer belongs to a local variable, + * then the caller with the local variable should call smb_node_release() + * directly. + * + * smb_node_release() itself will call smb_node_release() on a node's dir_snode, + * as smb_node_lookup() takes a hold on dir_snode. + */ + +void +smb_node_release(smb_node_t *node) +{ + ASSERT(node); + ASSERT(node->n_magic == SMB_NODE_MAGIC); + + smb_rwx_xenter(&node->n_lock); + ASSERT(node->n_refcnt); + DTRACE_PROBE1(smb_node_release, smb_node_t *, node); + if (--node->n_refcnt == 0) { + switch (node->n_state) { + + case SMB_NODE_STATE_AVAILABLE: + node->n_state = SMB_NODE_STATE_DESTROYING; + smb_rwx_xexit(&node->n_lock); + + smb_llist_enter(node->n_hash_bucket, RW_WRITER); + smb_llist_remove(node->n_hash_bucket, node); + smb_llist_exit(node->n_hash_bucket); + + /* + * Check if the file was deleted + */ + smb_node_delete_on_close(node); + node->n_magic = (uint32_t)~SMB_NODE_MAGIC; + + /* These lists should be empty. */ + smb_llist_destructor(&node->n_ofile_list); + smb_llist_destructor(&node->n_lock_list); + + if (node->dir_snode) { + ASSERT(node->dir_snode->n_magic == + SMB_NODE_MAGIC); + smb_node_release(node->dir_snode); + } + + if (node->unnamed_stream_node) { + ASSERT(node->unnamed_stream_node->n_magic == + SMB_NODE_MAGIC); + smb_node_release(node->unnamed_stream_node); + } + + ASSERT(node->vp); + VN_RELE(node->vp); + + smb_audit_buf_node_destroy(node); + smb_rwx_destroy(&node->n_lock); + kmem_cache_free(smb_info.si_cache_node, node); + ++smb_node_free; + return; + + default: + ASSERT(0); + break; + } + } + smb_audit_node(node); + smb_rwx_xexit(&node->n_lock); +} + +static void +smb_node_delete_on_close(smb_node_t *node) +{ + smb_node_t *d_snode; + int rc = 0; + + d_snode = node->dir_snode; + if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { + + node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; + ASSERT(node->od_name != NULL); + if (node->attr.sa_vattr.va_type == VDIR) + rc = smb_fsop_rmdir(0, node->delete_on_close_cred, + d_snode, node->od_name, 1); + else + rc = smb_fsop_remove(0, node->delete_on_close_cred, + d_snode, node->od_name, 1); + smb_cred_rele(node->delete_on_close_cred); + } + if (rc != 0) + cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n", + node->od_name, rc); + DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node); +} + +/* + * smb_node_rename() + * + */ +int +smb_node_rename( + smb_node_t *from_dir_snode, + smb_node_t *ret_snode, + smb_node_t *to_dir_snode, + char *to_name) +{ + ASSERT(from_dir_snode); + ASSERT(to_dir_snode); + ASSERT(ret_snode); + ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC); + ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC); + ASSERT(ret_snode->n_magic == SMB_NODE_MAGIC); + ASSERT(from_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE); + ASSERT(to_dir_snode->n_state == SMB_NODE_STATE_AVAILABLE); + ASSERT(ret_snode->n_state == SMB_NODE_STATE_AVAILABLE); + + smb_node_ref(to_dir_snode); + smb_rwx_xenter(&ret_snode->n_lock); + ret_snode->dir_snode = to_dir_snode; + smb_rwx_xexit(&ret_snode->n_lock); + ASSERT(to_dir_snode->dir_snode != ret_snode); + ASSERT((to_dir_snode->vp->v_xattrdir) || + (to_dir_snode->vp->v_type == VDIR)); + smb_node_release(from_dir_snode); + + (void) strcpy(ret_snode->od_name, to_name); + + /* + * XXX Need to update attributes? + */ + + return (0); +} + +int +smb_node_root_init() +{ + smb_attr_t va; + + /* + * Take an explicit hold on rootdir. This goes with the + * corresponding release in smb_node_root_fini()/smb_node_release(). + */ + + VN_HOLD(rootdir); + + if ((smb_info.si_root_smb_node = smb_node_lookup(NULL, NULL, kcred, + rootdir, ROOTVOL, NULL, NULL, &va)) == NULL) + return (-1); + else + return (0); +} + +void +smb_node_root_fini() +{ + if (smb_info.si_root_smb_node) { + smb_node_release(smb_info.si_root_smb_node); + smb_info.si_root_smb_node = NULL; + } +} + +/* + * smb_node_get_size + */ +uint64_t +smb_node_get_size( + smb_node_t *node, + smb_attr_t *attr) +{ + uint64_t size; + + if (attr->sa_vattr.va_type == VDIR) + return (0); + + smb_rwx_xenter(&node->n_lock); + if (node && (node->flags & NODE_FLAGS_SET_SIZE)) + size = node->n_size; + else + size = attr->sa_vattr.va_size; + smb_rwx_xexit(&node->n_lock); + return (size); +} + +static int +timeval_cmp(timestruc_t *a, timestruc_t *b) +{ + if (a->tv_sec < b->tv_sec) + return (-1); + if (a->tv_sec > b->tv_sec) + return (1); + /* Seconds are equal compare tv_nsec */ + if (a->tv_nsec < b->tv_nsec) + return (-1); + return (a->tv_nsec > b->tv_nsec); +} + +/* + * smb_node_set_time + * + * This function will update the time stored in the node and + * set the appropriate flags. If there is nothing to update + * or the node is readonly, the function would return without + * any updates. The update is only in the node level and the + * attribute in the file system will be updated when client + * close the file. + */ +void +smb_node_set_time(struct smb_node *node, struct timestruc *crtime, + struct timestruc *mtime, struct timestruc *atime, + struct timestruc *ctime, unsigned int what) +{ + smb_rwx_xenter(&node->n_lock); + if (node->flags & NODE_READ_ONLY || what == 0) { + smb_rwx_xexit(&node->n_lock); + return; + } + + if ((what & SMB_AT_CRTIME && crtime == 0) || + (what & SMB_AT_MTIME && mtime == 0) || + (what & SMB_AT_ATIME && atime == 0) || + (what & SMB_AT_CTIME && ctime == 0)) { + smb_rwx_xexit(&node->n_lock); + return; + } + + if ((what & SMB_AT_CRTIME) && + timeval_cmp((timestruc_t *)&node->attr.sa_crtime, + crtime) != 0) { + node->what |= SMB_AT_CRTIME; + node->attr.sa_crtime = *((timestruc_t *)crtime); + } + + if ((what & SMB_AT_MTIME) && + timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_mtime, + mtime) != 0) { + node->what |= SMB_AT_MTIME; + node->attr.sa_vattr.va_mtime = *((timestruc_t *)mtime); + } + + if ((what & SMB_AT_ATIME) && + timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_atime, + atime) != 0) { + node->what |= SMB_AT_ATIME; + node->attr.sa_vattr.va_atime = *((timestruc_t *)atime); + } + + /* + * The ctime handling is trickier. It has three scenarios. + * 1. Only ctime need to be set and it is the same as the ctime + * stored in the node. (update not necessary) + * 2. The ctime is the same as the ctime stored in the node but + * is not the only time need to be set. (update required) + * 3. The ctime need to be set and is not the same as the ctime + * stored in the node. (update required) + * Unlike other time setting, the ctime needs to be set even when + * it is the same as the ctime in the node if there are other time + * needs to be set (#2). This will ensure the ctime not being + * updated when other times are being updated in the file system. + * + * Retained file rules: + * + * 1. Don't add SMB_AT_CTIME to node->what by default because the + * request will be rejected by filesystem + * 2. 'what' SMB_AT_CTIME shouldn't be set for retained files, i.e. + * any request for changing ctime on these files should have + * been already rejected + */ + node->what |= SMB_AT_CTIME; + if (what & SMB_AT_CTIME) { + if ((what == SMB_AT_CTIME) && + timeval_cmp((timestruc_t *)&node->attr.sa_vattr.va_ctime, + ctime) == 0) { + node->what &= ~SMB_AT_CTIME; + } else { + gethrestime(&node->attr.sa_vattr.va_ctime); + } + } else { + gethrestime(&node->attr.sa_vattr.va_ctime); + } + smb_rwx_xexit(&node->n_lock); +} + + +timestruc_t * +smb_node_get_crtime(smb_node_t *node) +{ + return ((timestruc_t *)&node->attr.sa_crtime); +} + +timestruc_t * +smb_node_get_atime(smb_node_t *node) +{ + return ((timestruc_t *)&node->attr.sa_vattr.va_atime); +} + +timestruc_t * +smb_node_get_ctime(smb_node_t *node) +{ + return ((timestruc_t *)&node->attr.sa_vattr.va_ctime); +} + +timestruc_t * +smb_node_get_mtime(smb_node_t *node) +{ + return ((timestruc_t *)&node->attr.sa_vattr.va_mtime); +} + +/* + * smb_node_set_dosattr + * + * Parse the specified DOS attributes and, if they have been modified, + * update the node cache. This call should be followed by a + * smb_sync_fsattr() call to write the attribute changes to filesystem. + */ +void +smb_node_set_dosattr(smb_node_t *node, uint32_t dos_attr) +{ + unsigned int mode; /* New mode */ + + mode = 0; + + /* Handle the archive bit */ + if (dos_attr & SMB_FA_ARCHIVE) + mode |= FILE_ATTRIBUTE_ARCHIVE; + + /* Handle the readonly bit */ + if (dos_attr & SMB_FA_READONLY) + mode |= FILE_ATTRIBUTE_READONLY; + + /* Handle the hidden bit */ + if (dos_attr & SMB_FA_HIDDEN) + mode |= FILE_ATTRIBUTE_HIDDEN; + + /* Handle the system bit */ + if (dos_attr & SMB_FA_SYSTEM) + mode |= FILE_ATTRIBUTE_SYSTEM; + + smb_rwx_xenter(&node->n_lock); + if (node->attr.sa_dosattr != mode) { + node->attr.sa_dosattr = mode; + node->what |= SMB_AT_DOSATTR; + } + smb_rwx_xexit(&node->n_lock); +} + +/* + * smb_node_get_dosattr + * + * This function will get dos attribute using the node. + */ +uint32_t +smb_node_get_dosattr(smb_node_t *node) +{ + return (smb_mode_to_dos_attributes(&node->attr)); +} + +int +smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr) +{ + int rc = -1; + + smb_rwx_xenter(&node->n_lock); + if (!(node->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) && + !(node->flags & NODE_FLAGS_DELETE_ON_CLOSE)) { + crhold(cr); + node->delete_on_close_cred = cr; + node->flags |= NODE_FLAGS_DELETE_ON_CLOSE; + rc = 0; + } + smb_rwx_xexit(&node->n_lock); + return (rc); +} + +void +smb_node_reset_delete_on_close(smb_node_t *node) +{ + smb_rwx_xenter(&node->n_lock); + if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { + node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; + crfree(node->delete_on_close_cred); + node->delete_on_close_cred = NULL; + } + smb_rwx_xexit(&node->n_lock); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_cancel.c b/usr/src/uts/common/fs/smbsrv/smb_nt_cancel.c new file mode 100644 index 000000000000..79334395f030 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_nt_cancel.c @@ -0,0 +1,77 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: nt_cancel + * + * This SMB allows a client to cancel a request currently pending at the + * server. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; No words are sent (== 0) + * USHORT ByteCount; No bytes (==0) + * + * The Sid, Uid, Pid, Tid, and Mid fields of the SMB are used to locate an + * pending server request from this session. If a pending request is + * found, it is "hurried along" which may result in success or failure of + * the original request. No other response is generated for this SMB. + */ + +#include + +int +smb_com_nt_cancel(struct smb_request *sr) +{ + struct smb_request *req; + struct smb_session *session; + + session = sr->session; + + smb_slist_enter(&session->s_req_list); + req = smb_slist_head(&session->s_req_list); + while (req) { + ASSERT(req->sr_magic == SMB_REQ_MAGIC); + if ((req != sr) && + (req->smb_sid == sr->smb_sid) && + (req->smb_uid == sr->smb_uid) && + (req->smb_pid == sr->smb_pid) && + (req->smb_tid == sr->smb_tid) && + (req->smb_mid == sr->smb_mid)) { + smb_request_cancel(req); + } + req = smb_slist_next(&session->s_req_list, req); + } + smb_slist_exit(&session->s_req_list); + + /* Now, search the notify change queue to find the request */ + + smb_reply_specific_cancel_request(sr); + + return (SDRC_NO_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c b/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c new file mode 100644 index 000000000000..36a3df599222 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c @@ -0,0 +1,373 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This command is used to create or open a file or directory. + */ + + +#include +#include +#include + +/* + * smb_com_nt_create_andx + * + * This command is used to create or open a file or directory. + * + * Client Request Description + * ================================= ================================== + * + * UCHAR WordCount; Count of parameter words = 24 + * UCHAR AndXCommand; Secondary command; 0xFF = None + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * UCHAR Reserved; Reserved (must be 0) + * USHORT NameLength; Length of Name[] in bytes + * ULONG Flags; Create bit set: + * 0x02 - Request an oplock + * 0x04 - Request a batch oplock + * 0x08 - Target of open must be + * directory + * ULONG RootDirectoryFid; If non-zero, open is relative to + * this directory + * ACCESS_MASK DesiredAccess; access desired + * LARGE_INTEGER AllocationSize; Initial allocation size + * ULONG ExtFileAttributes; File attributes + * ULONG ShareAccess; Type of share access + * ULONG CreateDisposition; Action to take if file exists or + * not + * ULONG CreateOptions; Options to use if creating a file + * ULONG ImpersonationLevel; Security QOS information + * UCHAR SecurityFlags; Security tracking mode flags: + * 0x1 - SECURITY_CONTEXT_TRACKING + * 0x2 - SECURITY_EFFECTIVE_ONLY + * USHORT ByteCount; Length of byte parameters + * STRING Name[]; File to open or create + * + * The DesiredAccess parameter is specified in section 3.7 on Access Mask + * Encoding. + * + * If no value is specified, it still allows an application to query + * attributes without actually accessing the file. + * + * The ExtFIleAttributes parameter specifies the file attributes and flags + * for the file. The parameter's value is the sum of allowed attributes and + * flags defined in section 3.11 on Extended File Attribute Encoding + * + * The ShareAccess field Specifies how this file can be shared. This + * parameter must be some combination of the following values: + * + * Name Value Meaning + * 0 Prevents the file from being shared. + * FILE_SHARE_READ 0x00000001 Other open operations can be performed on + * the file for read access. + * FILE_SHARE_WRITE 0x00000002 Other open operations can be performed on + * the file for write access. + * FILE_SHARE_DELETE 0x00000004 Other open operations can be performed on + * the file for delete access. + * + * The CreateDisposition parameter can contain one of the following values: + * + * CREATE_NEW Creates a new file. The function fails if the + * specified file already exists. + * CREATE_ALWAYS Creates a new file. The function overwrites the file + * if it exists. + * OPEN_EXISTING Opens the file. The function fails if the file does + * not exist. + * OPEN_ALWAYS Opens the file, if it exists. If the file does not + * exist, act like CREATE_NEW. + * TRUNCATE_EXISTING Opens the file. Once opened, the file is truncated so + * that its size is zero bytes. The calling process must + * open the file with at least GENERIC_WRITE access. The + * function fails if the file does not exist. + * + * The ImpersonationLevel parameter can contain one or more of the + * following values: + * + * SECURITY_ANONYMOUS Specifies to impersonate the client at the + * Anonymous impersonation level. + * SECURITY_IDENTIFICATION Specifies to impersonate the client at the + * Identification impersonation level. + * SECURITY_IMPERSONATION Specifies to impersonate the client at the + * Impersonation impersonation level. + * SECURITY_DELEGATION Specifies to impersonate the client at the + * Delegation impersonation level. + * + * The SecurityFlags parameter can have either of the following two flags + * set: + * + * SECURITY_CONTEXT_TRACKING Specifies that the security tracking mode is + * dynamic. If this flag is not specified, + * Security Tracking Mode is static. + * SECURITY_EFFECTIVE_ONLY Specifies that only the enabled aspects of + * the client's security context are available + * to the server. If you do not specify this + * flag, all aspects of the client's security + * context are available. This flag allows the + * client to limit the groups and privileges + * that a server can use while impersonating the + * client. + * + * The response is as follows: + * + * Server Response Description + * ================================= ================================== + * + * UCHAR WordCount; Count of parameter words = 26 + * UCHAR AndXCommand; Secondary 0xFF = None + * command; + * UCHAR AndXReserved; MBZ + * USHORT AndXOffset; Offset to next command WordCount + * UCHAR OplockLevel; The oplock level granted + * 0 - No oplock granted + * 1 - Exclusive oplock granted + * 2 - Batch oplock granted + * 3 - Level II oplock granted + * USHORT Fid; The file ID + * ULONG CreateAction; The action taken + * TIME CreationTime; The time the file was created + * TIME LastAccessTime; The time the file was accessed + * TIME LastWriteTime; The time the file was last written + * TIME ChangeTime; The time the file was last changed + * ULONG ExtFileAttributes; The file attributes + * LARGE_INTEGER AllocationSize; The number of bytes allocated + * LARGE_INTEGER EndOfFile; The end of file offset + * USHORT FileType; + * USHORT DeviceState; state of IPC device (e.g. pipe) + * BOOLEAN Directory; TRUE if this is a directory + * USHORT ByteCount; = 0 + * + * The following SMBs may follow SMB_COM_NT_CREATE_ANDX: + * + * SMB_COM_READ SMB_COM_READ_ANDX + * SMB_COM_IOCTL + */ +int +smb_com_nt_create_andx(struct smb_request *sr) +{ + struct open_param *op = &sr->arg.open; + unsigned char OplockLevel; + unsigned char DirFlag; + unsigned char SecurityFlags; + uint32_t ExtFileAttributes; + uint32_t Flags; + uint32_t ImpersonationLevel; + uint32_t RootDirFid; + unsigned short NameLength; + smb_attr_t new_attr; + smb_node_t *node; + DWORD status; + int count; + int rc; + + op->dsize = 0; + + rc = smbsr_decode_vwv(sr, "5.wlllqlllllb", + &NameLength, + &Flags, + &RootDirFid, + &op->desired_access, + &op->dsize, + &ExtFileAttributes, + &op->share_access, + &op->create_disposition, + &op->create_options, + &ImpersonationLevel, + &SecurityFlags); + + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (NameLength >= MAXPATHLEN) { + smbsr_raise_nt_error(sr, NT_STATUS_OBJECT_PATH_NOT_FOUND); + /* NOTREACHED */ + } + + if (smbsr_decode_data(sr, "%#u", sr, NameLength, &op->fqi.path) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if ((op->create_options & FILE_DELETE_ON_CLOSE) && + !(op->desired_access & DELETE)) { + smbsr_raise_nt_error(sr, NT_STATUS_INVALID_PARAMETER); + /* NOTREACHED */ + } + + op->fqi.srch_attr = 0; + op->omode = 0; + op->utime.tv_sec = op->utime.tv_nsec = 0; + op->my_flags = 0; + op->dattr = ExtFileAttributes; + + if (Flags) { + if (Flags & NT_CREATE_FLAG_REQUEST_OPLOCK) { + if (Flags & NT_CREATE_FLAG_REQUEST_OPBATCH) { + op->my_flags = MYF_BATCH_OPLOCK; + } else { + op->my_flags = MYF_EXCLUSIVE_OPLOCK; + } + } + if (Flags & NT_CREATE_FLAG_OPEN_TARGET_DIR) + op->my_flags |= MYF_MUST_BE_DIRECTORY; + } + + if (ExtFileAttributes & FILE_FLAG_WRITE_THROUGH) + op->create_options |= FILE_WRITE_THROUGH; + + if (ExtFileAttributes & FILE_FLAG_DELETE_ON_CLOSE) + op->create_options |= FILE_DELETE_ON_CLOSE; + + if (RootDirFid == 0) { + op->fqi.dir_snode = sr->tid_tree->t_snode; + } else { + sr->smb_fid = (ushort_t)RootDirFid; + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, + sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + op->fqi.dir_snode = sr->fid_ofile->f_node; + smbsr_disconnect_file(sr); + } + + status = NT_STATUS_SUCCESS; + /* + * According to NT, when exclusive share access failed, + * instead of raising "access deny" error immediately, + * we should wait for the client holding the exclusive + * file to close the file. If the wait timed out, we + * report a sharing violation; otherwise, we grant access. + * smb_open_subr returns NT_STATUS_SHARING_VIOLATION when + * it encounters an exclusive share access deny: we wait + * and retry. + */ + for (count = 0; count <= 4; count++) { + if (count) { + delay(MSEC_TO_TICK(400)); + } + + if ((status = smb_open_subr(sr)) == NT_STATUS_SUCCESS) + break; + } + + if (status != NT_STATUS_SUCCESS) { + if (status == NT_STATUS_SHARING_VIOLATION) + smbsr_raise_cifs_error(sr, + NT_STATUS_SHARING_VIOLATION, + ERRDOS, ERROR_SHARING_VIOLATION); + else + smbsr_raise_nt_error(sr, status); + + /* NOTREACHED */ + } + + if (STYPE_ISDSK(sr->tid_tree->t_res_type)) { + switch (MYF_OPLOCK_TYPE(op->my_flags)) { + case MYF_EXCLUSIVE_OPLOCK : + OplockLevel = 1; + break; + case MYF_BATCH_OPLOCK : + OplockLevel = 2; + break; + case MYF_LEVEL_II_OPLOCK : + OplockLevel = 3; + break; + case MYF_OPLOCK_NONE : + default: + OplockLevel = 0; + break; + } + + if (op->create_options & FILE_DELETE_ON_CLOSE) + smb_preset_delete_on_close(sr->fid_ofile); + + /* + * Set up the directory flag and ensure that + * we don't return a stale file size. + */ + node = sr->fid_ofile->f_node; + if (node->attr.sa_vattr.va_type == VDIR) { + DirFlag = 1; + new_attr.sa_vattr.va_size = 0; + } else { + DirFlag = 0; + new_attr.sa_mask = SMB_AT_SIZE; + (void) smb_fsop_getattr(sr, kcred, node, &new_attr); + node->attr.sa_vattr.va_size = new_attr.sa_vattr.va_size; + } + + smbsr_encode_result(sr, 34, 0, "bb.wbwlTTTTlqqwwbw", + 34, + sr->andx_com, + 0x67, + OplockLevel, + sr->smb_fid, + op->action_taken, + &node->attr.sa_crtime, + &node->attr.sa_vattr.va_atime, + &node->attr.sa_vattr.va_mtime, + &node->attr.sa_vattr.va_ctime, + op->dattr & FILE_ATTRIBUTE_MASK, + new_attr.sa_vattr.va_size, + new_attr.sa_vattr.va_size, + op->ftype, + op->devstate, + DirFlag, + 0); + } else { + /* Named PIPE */ + OplockLevel = 0; + smbsr_encode_result(sr, 34, 0, "bb.wbwlqqqqlqqwwbw", + 34, + sr->andx_com, + 0x67, + OplockLevel, + sr->smb_fid, + op->action_taken, + 0LL, + 0LL, + 0LL, + 0LL, + SMB_FA_NORMAL, + 0x1000LL, + 0LL, + op->ftype, + op->devstate, + 0, + 0); + } + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c new file mode 100644 index 000000000000..63f1f814409f --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c @@ -0,0 +1,255 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This command is used to create or open a file or directory, when EAs + * or an SD must be applied to the file. The functionality is similar + * to SmbNtCreateAndx with the option to supply extended attributes or + * a security descriptor. + * + * Note: we don't decode the extended attributes because we don't + * support them at this time. + */ + +#include +#include +#include + +/* + * smb_nt_transact_create + * + * This command is used to create or open a file or directory, when EAs + * or an SD must be applied to the file. The request parameter block + * encoding, data block encoding and output parameter block encoding are + * described in CIFS section 4.2.2. + * + * The format of the command is SmbNtTransact but it is basically the same + * as SmbNtCreateAndx with the option to supply extended attributes or a + * security descriptor. For information not defined in CIFS section 4.2.2 + * see section 4.2.1 (NT_CREATE_ANDX). + */ +int +smb_nt_transact_create(struct smb_request *sr, struct smb_xa *xa) +{ + struct open_param *op = &sr->arg.open; + uint8_t OplockLevel; + uint8_t DirFlag; + uint8_t SecurityFlags; + uint32_t ExtFileAttributes; + uint32_t sd_len; + uint32_t EaLength; + uint32_t Flags; + uint32_t ImpersonationLevel; + uint32_t RootDirFid; + uint32_t NameLength; + smb_attr_t new_attr; + smb_node_t *node; + DWORD status; + int rc; + + rc = smb_decode_mbc(&xa->req_param_mb, "%lllqllllllllb", + sr, + &Flags, + &RootDirFid, + &op->desired_access, + &op->dsize, + &ExtFileAttributes, + &op->share_access, + &op->create_disposition, + &op->create_options, + &sd_len, + &EaLength, + &NameLength, + &ImpersonationLevel, + &SecurityFlags); + + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + /* + * If name length is zero, interpret as "\". + */ + if (NameLength == 0) { + op->fqi.path = "\\"; + } else { + rc = smb_decode_mbc(&xa->req_param_mb, "%#u", + sr, NameLength, &op->fqi.path); + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + } + + if ((op->create_options & FILE_DELETE_ON_CLOSE) && + !(op->desired_access & DELETE)) { + smbsr_raise_nt_error(sr, NT_STATUS_INVALID_PARAMETER); + /* NOTREACHED */ + } + + if (sd_len >= sizeof (smb_sdbuf_t)) { + /* ignore security setting for files on Unix volumes */ + op->sd_buf = kmem_alloc(sd_len, KM_SLEEP); + + if ((smb_decode_mbc(&xa->req_data_mb, "#c", sd_len, + (char *)op->sd_buf)) != 0) { + kmem_free(op->sd_buf, sd_len); + smbsr_raise_nt_error(sr, NT_STATUS_BUFFER_TOO_SMALL); + /* NOTREACHED */ + } + } else { + op->sd_buf = 0; + } + + op->fqi.srch_attr = 0; + op->omode = 0; + + op->utime.tv_sec = op->utime.tv_nsec = 0; + op->my_flags = 0; + + op->dattr = ExtFileAttributes; + + if (Flags) { + if (Flags & NT_CREATE_FLAG_REQUEST_OPLOCK) { + if (Flags & NT_CREATE_FLAG_REQUEST_OPBATCH) { + op->my_flags = MYF_BATCH_OPLOCK; + } else { + op->my_flags = MYF_EXCLUSIVE_OPLOCK; + } + } + if (Flags & NT_CREATE_FLAG_OPEN_TARGET_DIR) + op->my_flags |= MYF_MUST_BE_DIRECTORY; + } + + if (ExtFileAttributes & FILE_FLAG_WRITE_THROUGH) + op->create_options |= FILE_WRITE_THROUGH; + + if (ExtFileAttributes & FILE_FLAG_DELETE_ON_CLOSE) + op->create_options |= FILE_DELETE_ON_CLOSE; + + if (RootDirFid == 0) { + op->fqi.dir_snode = sr->tid_tree->t_snode; + } else { + sr->smb_fid = (ushort_t)RootDirFid; + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, + sr->smb_fid); + /* + * XXX: ASSERT() for now but we should understand if the test + * of the return value is missing because it cannot happen. + */ + ASSERT(sr->fid_ofile != NULL); + op->fqi.dir_snode = sr->fid_ofile->f_node; + smbsr_disconnect_file(sr); + } + + status = smb_open_subr(sr); + if (op->sd_buf) + kmem_free(op->sd_buf, sd_len); + + if (status != NT_STATUS_SUCCESS) { + if (status == NT_STATUS_SHARING_VIOLATION) + smbsr_raise_cifs_error(sr, + NT_STATUS_SHARING_VIOLATION, + ERRDOS, ERROR_SHARING_VIOLATION); + else + smbsr_raise_nt_error(sr, status); + + /* NOTREACHED */ + } + + if (STYPE_ISDSK(sr->tid_tree->t_res_type)) { + switch (MYF_OPLOCK_TYPE(op->my_flags)) { + case MYF_EXCLUSIVE_OPLOCK : + OplockLevel = 1; + break; + case MYF_BATCH_OPLOCK : + OplockLevel = 2; + break; + case MYF_LEVEL_II_OPLOCK : + OplockLevel = 3; + break; + case MYF_OPLOCK_NONE : + default: + OplockLevel = 0; + break; + } + + if (op->create_options & FILE_DELETE_ON_CLOSE) + smb_preset_delete_on_close(sr->fid_ofile); + + /* + * Set up the directory flag and ensure that + * we don't return a stale file size. + */ + node = sr->fid_ofile->f_node; + if (node->attr.sa_vattr.va_type == VDIR) { + DirFlag = 1; + new_attr.sa_vattr.va_size = 0; + } else { + DirFlag = 0; + new_attr.sa_mask = SMB_AT_SIZE; + (void) smb_fsop_getattr(sr, kcred, node, &new_attr); + node->attr.sa_vattr.va_size = new_attr.sa_vattr.va_size; + } + + (void) smb_encode_mbc(&xa->rep_param_mb, "b.wllTTTTlqqwwb", + OplockLevel, + sr->smb_fid, + op->action_taken, + 0, /* EaErrorOffset */ + &node->attr.sa_crtime, + &node->attr.sa_vattr.va_atime, + &node->attr.sa_vattr.va_mtime, + &node->attr.sa_vattr.va_ctime, + op->dattr & FILE_ATTRIBUTE_MASK, + new_attr.sa_vattr.va_size, + new_attr.sa_vattr.va_size, + op->ftype, + op->devstate, + DirFlag); + } else { + /* Named PIPE */ + (void) smb_encode_mbc(&xa->rep_param_mb, "b.wllTTTTlqqwwb", + 0, + sr->smb_fid, + op->action_taken, + 0, /* EaErrorOffset */ + 0LL, + 0LL, + 0LL, + 0LL, + op->dattr, + 0x1000LL, + 0LL, + op->ftype, + op->devstate, + 0); + } + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c new file mode 100644 index 000000000000..5725814a0f88 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c @@ -0,0 +1,110 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include + + +/* + * This table defines the list of IOCTL/FSCTL values for which we'll + * return a specific NT status code, based on observation of NT. + */ +static struct { + uint32_t fcode; + DWORD status; +} ioctl_ret_tbl[] = { + {FSCTL_GET_OBJECT_ID, NT_STATUS_INVALID_PARAMETER}, + {FSCTL_QUERY_ALLOCATED_RANGES, NT_STATUS_INVALID_PARAMETER} +}; + + +/* + * smb_nt_transact_ioctl + * + * This command allows device and file system control functions to be + * transferred transparently from client to server. This is currently + * a stub to work out whether or not we need to return an NT status + * code. + * + * Setup Words Encoding Description + * =========================== ========================================= + * ULONG FunctionCode; NT device or file system control code + * USHORT Fid; Handle for io or fs control. Unless BIT0 + * of ISFLAGS is set. + * BOOLEAN IsFsctl; Indicates whether the command is a device + * control (FALSE) or a file system control + * (TRUE). + * UCHAR IsFlags; BIT0 - command is to be applied to share + * root handle. Share must be a DFS share. + * + * Data Block Encoding Description + * =========================== ========================================= + * Data[ TotalDataCount ] Passed to the Fsctl or Ioctl + * + * Server Response Description + * =========================== ================================== + * SetupCount 1 + * Setup[0] Length of information returned by + * io or fs control. + * DataCount Length of information returned by + * io or fs control. + * Data[ DataCount ] The results of the io or fs control. + */ +int +smb_nt_transact_ioctl(struct smb_request *sr, struct smb_xa *xa) +{ + DWORD status = NT_STATUS_SUCCESS; + uint32_t fcode; + unsigned short fid; + unsigned char is_fsctl; + unsigned char is_flags; + int i; + + if (smb_decode_mbc(&xa->req_setup_mb, "lwbb", + &fcode, + &fid, + &is_fsctl, + &is_flags) != 0) { + smbsr_raise_nt_error(sr, NT_STATUS_INVALID_PARAMETER); + } + + for (i = 0; + i < sizeof (ioctl_ret_tbl) / sizeof (ioctl_ret_tbl[0]); + i++) { + if (ioctl_ret_tbl[i].fcode == fcode) { + status = ioctl_ret_tbl[i].status; + break; + } + } + + if (status != NT_STATUS_SUCCESS) + smbsr_raise_nt_error(sr, status); + + (void) smb_encode_mbc(&xa->rep_param_mb, "l", 0); + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_notify_change.c b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_notify_change.c new file mode 100644 index 000000000000..d70ddf4fc00a --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_notify_change.c @@ -0,0 +1,613 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * File Change Notification (FCN) + */ + +/* + * SMB: nt_transact_notify_change + * + * Client Setup Words Description + * ================================== ================================= + * + * ULONG CompletionFilter; Specifies operation to monitor + * USHORT Fid; Fid of directory to monitor + * BOOLEAN WatchTree; TRUE = watch all subdirectories too + * UCHAR Reserved; MBZ + * + * This command notifies the client when the directory specified by Fid is + * modified. It also returns the name(s) of the file(s) that changed. The + * command completes once the directory has been modified based on the + * supplied CompletionFilter. The command is a "single shot" and therefore + * needs to be reissued to watch for more directory changes. + * + * A directory file must be opened before this command may be used. Once + * the directory is open, this command may be used to begin watching files + * and subdirectories in the specified directory for changes. The first + * time the command is issued, the MaxParameterCount field in the transact + * header determines the size of the buffer that will be used at the server + * to buffer directory change information between issuances of the notify + * change commands. + * + * When a change that is in the CompletionFilter is made to the directory, + * the command completes. The names of the files that have changed since + * the last time the command was issued are returned to the client. The + * ParameterCount field of the response indicates the number of bytes that + * are being returned. If too many files have changed since the last time + * the command was issued, then zero bytes are returned and an alternate + * status code is returned in the Status field of the response. + * + * The CompletionFilter is a mask created as the sum of any of the + * following flags: + * + * FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 + * FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 + * FILE_NOTIFY_CHANGE_NAME 0x00000003 + * FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 + * FILE_NOTIFY_CHANGE_SIZE 0x00000008 + * FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 + * FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 + * FILE_NOTIFY_CHANGE_CREATION 0x00000040 + * FILE_NOTIFY_CHANGE_EA 0x00000080 + * FILE_NOTIFY_CHANGE_SECURITY 0x00000100 + * FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 + * FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 + * FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 + * + * Server Response Description + * ================================== ================================ + * ParameterCount # of bytes of change data + * Parameters[ ParameterCount ] FILE_NOTIFY_INFORMATION + * structures + * + * The response contains FILE_NOTIFY_INFORMATION structures, as defined + * below. The NextEntryOffset field of the structure specifies the offset, + * in bytes, from the start of the current entry to the next entry in the + * list. If this is the last entry in the list, this field is zero. Each + * entry in the list must be longword aligned, so NextEntryOffset must be a + * multiple of four. + * + * typedef struct { + * ULONG NextEntryOffset; + * ULONG Action; + * ULONG FileNameLength; + * WCHAR FileName[1]; + * } FILE_NOTIFY_INFORMATION; + * + * Where Action describes what happened to the file named FileName: + * + * FILE_ACTION_ADDED 0x00000001 + * FILE_ACTION_REMOVED 0x00000002 + * FILE_ACTION_MODIFIED 0x00000003 + * FILE_ACTION_RENAMED_OLD_NAME 0x00000004 + * FILE_ACTION_RENAMED_NEW_NAME 0x00000005 + * FILE_ACTION_ADDED_STREAM 0x00000006 + * FILE_ACTION_REMOVED_STREAM 0x00000007 + * FILE_ACTION_MODIFIED_STREAM 0x00000008 + */ + +#include +#include + +/* + * smb_nt_transact_notify_change + * + * This function is responsible for processing NOTIFY CHANGE requests. + * Requests are stored in a global queue. This queue is processed when + * a monitored directory is changed or client cancels one of its already + * sent requests. + */ +int +smb_nt_transact_notify_change(struct smb_request *sr, struct smb_xa *xa) +{ + uint32_t CompletionFilter; + unsigned char WatchTree; + smb_node_t *node; + + if (smb_decode_mbc(&xa->req_setup_mb, "lwb", + &CompletionFilter, &sr->smb_fid, &WatchTree) != 0) + return (SDRC_UNSUPPORTED); + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + node = sr->fid_ofile->f_node; + + if (node->attr.sa_vattr.va_type != VDIR) { + /* + * notify change requests are only valid for + * directories + */ + smbsr_raise_nt_error(sr, NT_STATUS_NOT_A_DIRECTORY); + /* NOTREACHED */ + } + + mutex_enter(&sr->sr_mutex); + switch (sr->sr_state) { + case SMB_REQ_STATE_ACTIVE: + node->waiting_event++; + node->flags |= NODE_FLAGS_NOTIFY_CHANGE; + if ((node->flags & NODE_FLAGS_CHANGED) == 0) { + sr->sr_ncr.nc_node = node; + sr->sr_ncr.nc_flags = CompletionFilter; + if (WatchTree) + sr->sr_ncr.nc_flags |= NODE_FLAGS_WATCH_TREE; + + sr->sr_keep = B_TRUE; + sr->sr_state = SMB_REQ_STATE_WAITING_EVENT; + smb_slist_insert_tail(&smb_info.si_ncr_list, sr); + + /* + * Monitor events system-wide. + * + * XXX: smb_node_ref() and smb_node_release() + * take &node->n_lock. May need alternate forms + * of these routines if node->n_lock is taken + * around calls to smb_fem_fcn_install() and + * smb_fem_fcn_uninstall(). + */ + + smb_fem_fcn_install(node); + + mutex_exit(&sr->sr_mutex); + return (SDRC_NO_REPLY); + } else { + /* node already changed, reply immediately */ + if (--node->waiting_event == 0) + node->flags &= + ~(NODE_FLAGS_NOTIFY_CHANGE | + NODE_FLAGS_CHANGED); + mutex_exit(&sr->sr_mutex); + return (SDRC_NORMAL_REPLY); + } + + case SMB_REQ_STATE_CANCELED: + mutex_exit(&sr->sr_mutex); + smbsr_raise_nt_error(sr, NT_STATUS_CANCELLED); + /* NOTREACHED */ + default: + ASSERT(0); + mutex_exit(&sr->sr_mutex); + return (SDRC_NORMAL_REPLY); + } +} + +/* + * smb_reply_notify_change_request + * + * This function sends appropriate response to an already queued NOTIFY CHANGE + * request. If node is changed (reply == NODE_FLAGS_CHANGED), a normal reply is + * sent. + * If client cancels the request or session dropped, an NT_STATUS_CANCELED + * is sent in reply. + */ +int +smb_reply_notify_change_request( + smb_request_t *sr) +{ + smb_node_t *node; + int total_bytes, n_setup, n_param, n_data; + int param_off, param_pad, data_off, data_pad; + struct smb_xa *xa; + + xa = sr->r_xa; + node = sr->sr_ncr.nc_node; + + if (--node->waiting_event == 0) { + node->flags &= ~(NODE_FLAGS_NOTIFY_CHANGE | NODE_FLAGS_CHANGED); + smb_fem_fcn_uninstall(node); + } + + mutex_enter(&sr->sr_mutex); + switch (sr->sr_state) { + + case SMB_REQ_STATE_EVENT_OCCURRED: + sr->sr_state = SMB_REQ_STATE_ACTIVE; + + /* many things changed */ + + (void) smb_encode_mbc(&xa->rep_data_mb, "l", 0L); + + /* setup the NT transact reply */ + + n_setup = MBC_LENGTH(&xa->rep_setup_mb); + n_param = MBC_LENGTH(&xa->rep_param_mb); + n_data = MBC_LENGTH(&xa->rep_data_mb); + + n_setup = (n_setup + 1) / 2; /* Convert to setup words */ + param_pad = 1; /* must be one */ + param_off = param_pad + 32 + 37 + (n_setup << 1) + 2; + /* Pad to 4 bytes */ + data_pad = (4 - ((param_off + n_param) & 3)) % 4; + /* Param off from hdr */ + data_off = param_off + n_param + data_pad; + total_bytes = param_pad + n_param + data_pad + n_data; + + smbsr_encode_result(sr, 18+n_setup, total_bytes, + "b 3. llllllllb C w #. C #. C", + 18 + n_setup, /* wct */ + n_param, /* Total Parameter Bytes */ + n_data, /* Total Data Bytes */ + n_param, /* Total Parameter Bytes this buffer */ + param_off, /* Param offset from header start */ + 0, /* Param displacement */ + n_data, /* Total Data Bytes this buffer */ + data_off, /* Data offset from header start */ + 0, /* Data displacement */ + n_setup, /* suwcnt */ + &xa->rep_setup_mb, /* setup[] */ + total_bytes, /* Total data bytes */ + param_pad, + &xa->rep_param_mb, + data_pad, + &xa->rep_data_mb); + break; + + case SMB_REQ_STATE_CANCELED: + /* + * an STATUS should be sent, + * we need an implementation of nt_raise_error + * but without long jump. + */ + smbsr_setup_nt_status(sr, 0xc0000000, NT_STATUS_CANCELLED); + (void) smb_encode_mbc(&sr->reply, "bwbw", + (short)0, 0L, (short)0, 0L); + sr->smb_wct = 0; + sr->smb_bcc = 0; + break; + default: + ASSERT(0); + } + mutex_exit(&sr->sr_mutex); + + /* Setup the header */ + (void) smb_poke_mbc(&sr->reply, 0, SMB_HEADER_ED_FMT, + sr->first_smb_com, + sr->smb_rcls, + sr->smb_reh, + sr->smb_err, + sr->smb_flg | SMB_FLAGS_REPLY, + sr->smb_flg2, + sr->smb_pid_high, + sr->smb_sig, + sr->smb_tid, + sr->smb_pid, + sr->smb_uid, + sr->smb_mid); + + if (sr->session->signing.flags & SMB_SIGNING_ENABLED) + smb_sign_reply(sr, NULL); + + /* send the reply */ + DTRACE_PROBE1(ncr__reply, struct smb_request *, sr) + (void) smb_session_send(sr->session, 0, &sr->reply); + smbsr_cleanup(sr); + + mutex_enter(&sr->sr_mutex); + sr->sr_state = SMB_REQ_STATE_COMPLETED; + mutex_exit(&sr->sr_mutex); + smb_request_free(sr); + return (0); +} + +/* + * smb_process_session_notify_change_queue + * + * This function traverses notify change request queue and sends + * cancel replies to all of requests that are related to a specific + * session. + */ +void +smb_process_session_notify_change_queue(struct smb_session *session) +{ + smb_request_t *sr; + smb_request_t *tmp; + boolean_t sig = B_FALSE; + + smb_slist_enter(&smb_info.si_ncr_list); + smb_slist_enter(&smb_info.si_nce_list); + sr = smb_slist_head(&smb_info.si_ncr_list); + while (sr) { + ASSERT(sr->sr_magic == SMB_REQ_MAGIC); + tmp = smb_slist_next(&smb_info.si_ncr_list, sr); + if (sr->session == session) { + mutex_enter(&sr->sr_mutex); + switch (sr->sr_state) { + case SMB_REQ_STATE_WAITING_EVENT: + smb_slist_obj_move( + &smb_info.si_nce_list, + &smb_info.si_ncr_list, + sr); + sr->sr_state = SMB_REQ_STATE_CANCELED; + sig = B_TRUE; + break; + default: + ASSERT(0); + break; + } + mutex_exit(&sr->sr_mutex); + } + sr = tmp; + } + smb_slist_exit(&smb_info.si_nce_list); + smb_slist_exit(&smb_info.si_ncr_list); + if (sig) { + smb_thread_signal(&smb_info.si_thread_notify_change); + } +} + +/* + * smb_process_file_notify_change_queue + * + * This function traverses notify change request queue and sends + * cancel replies to all of requests that are related to the + * specified file. + */ +void +smb_process_file_notify_change_queue(struct smb_ofile *of) +{ + smb_request_t *sr; + smb_request_t *tmp; + boolean_t sig = B_FALSE; + + smb_slist_enter(&smb_info.si_ncr_list); + smb_slist_enter(&smb_info.si_nce_list); + sr = smb_slist_head(&smb_info.si_ncr_list); + while (sr) { + ASSERT(sr->sr_magic == SMB_REQ_MAGIC); + tmp = smb_slist_next(&smb_info.si_ncr_list, sr); + if (sr->fid_ofile == of) { + mutex_enter(&sr->sr_mutex); + switch (sr->sr_state) { + case SMB_REQ_STATE_WAITING_EVENT: + smb_slist_obj_move( + &smb_info.si_nce_list, + &smb_info.si_ncr_list, + sr); + sr->sr_state = SMB_REQ_STATE_CANCELED; + sig = B_TRUE; + break; + default: + ASSERT(0); + break; + } + mutex_exit(&sr->sr_mutex); + } + sr = tmp; + } + smb_slist_exit(&smb_info.si_nce_list); + smb_slist_exit(&smb_info.si_ncr_list); + if (sig) { + smb_thread_signal(&smb_info.si_thread_notify_change); + } +} + +/* + * smb_reply_specific_cancel_request + * + * This function searches global request list for a specific request. If found, + * moves the request to event queue and kicks the notify change daemon. + */ + +void +smb_reply_specific_cancel_request(struct smb_request *zsr) +{ + smb_request_t *sr; + smb_request_t *tmp; + boolean_t sig = B_FALSE; + + smb_slist_enter(&smb_info.si_ncr_list); + smb_slist_enter(&smb_info.si_nce_list); + sr = smb_slist_head(&smb_info.si_ncr_list); + while (sr) { + ASSERT(sr->sr_magic == SMB_REQ_MAGIC); + tmp = smb_slist_next(&smb_info.si_ncr_list, sr); + if ((sr->session == zsr->session) && + (sr->smb_sid == zsr->smb_sid) && + (sr->smb_uid == zsr->smb_uid) && + (sr->smb_pid == zsr->smb_pid) && + (sr->smb_tid == zsr->smb_tid) && + (sr->smb_mid == zsr->smb_mid)) { + mutex_enter(&sr->sr_mutex); + switch (sr->sr_state) { + case SMB_REQ_STATE_WAITING_EVENT: + smb_slist_obj_move( + &smb_info.si_nce_list, + &smb_info.si_ncr_list, + sr); + sr->sr_state = SMB_REQ_STATE_CANCELED; + sig = B_TRUE; + break; + default: + ASSERT(0); + break; + } + mutex_exit(&sr->sr_mutex); + } + sr = tmp; + } + smb_slist_exit(&smb_info.si_nce_list); + smb_slist_exit(&smb_info.si_ncr_list); + if (sig) { + smb_thread_signal(&smb_info.si_thread_notify_change); + } +} + +/* + * smb_process_node_notify_change_queue + * + * This function searches notify change request queue and sends + * 'NODE MODIFIED' reply to all requests which are related to a + * specific node. + * WatchTree flag: We handle this flag in a special manner just + * for DAVE clients. When something is changed, we notify all + * requests which came from DAVE clients on the same volume which + * has been modified. We don't care about the tree that they wanted + * us to monitor. any change in any part of the volume will lead + * to notifying all notify change requests from DAVE clients on the + * different parts of the volume hierarchy. + */ +void +smb_process_node_notify_change_queue(struct smb_node *node) +{ + smb_request_t *sr; + smb_request_t *tmp; + boolean_t sig = B_FALSE; + + if (!(node->flags & NODE_FLAGS_NOTIFY_CHANGE)) + return; + + node->flags |= NODE_FLAGS_CHANGED; + + smb_slist_enter(&smb_info.si_ncr_list); + smb_slist_enter(&smb_info.si_nce_list); + sr = smb_slist_head(&smb_info.si_ncr_list); + while (sr) { + ASSERT(sr->sr_magic == SMB_REQ_MAGIC); + tmp = smb_slist_next(&smb_info.si_ncr_list, sr); + /* + * send notify if: + * - it's a request for the same node or + * - it's a request from a DAVE client, its 'watch tree' + * flag is set and monitors a tree on the same volume. + */ + if ((sr->sr_ncr.nc_node == node) || + ((sr->sr_ncr.nc_flags & NODE_FLAGS_WATCH_TREE) && + (sr->session->native_os == NATIVE_OS_MACOS) && + !fsd_cmp(&sr->sr_ncr.nc_node->tree_fsd, &node->tree_fsd))) { + mutex_enter(&sr->sr_mutex); + switch (sr->sr_state) { + case SMB_REQ_STATE_WAITING_EVENT: + smb_slist_obj_move( + &smb_info.si_nce_list, + &smb_info.si_ncr_list, + sr); + sr->sr_state = SMB_REQ_STATE_EVENT_OCCURRED; + sig = B_TRUE; + break; + default: + ASSERT(0); + break; + } + mutex_exit(&sr->sr_mutex); + } + sr = tmp; + } + smb_slist_exit(&smb_info.si_nce_list); + smb_slist_exit(&smb_info.si_ncr_list); + if (sig) { + smb_thread_signal(&smb_info.si_thread_notify_change); + } +} + +/* + * smb_notify_change_daemon + * + * This function processes notify change event list and send appropriate + * responses to the requests. This function executes in the system as an + * indivdual thread. + */ + +void +smb_notify_change_daemon(smb_thread_t *thread, void *si_void) +{ + smb_request_t *sr; + smb_request_t *tmp; + list_t sr_list; + smb_info_t *si = si_void; + + list_create(&sr_list, sizeof (smb_request_t), + offsetof(smb_request_t, sr_ncr.nc_lnd)); + + ASSERT(si != NULL); + + while (smb_thread_continue(thread)) { + + while (smb_slist_move_tail(&sr_list, &si->si_nce_list)) { + sr = list_head(&sr_list); + while (sr) { + ASSERT(sr->sr_magic == SMB_REQ_MAGIC); + tmp = list_next(&sr_list, sr); + list_remove(&sr_list, sr); + (void) smb_reply_notify_change_request(sr); + sr = tmp; + } + } + } + + list_destroy(&sr_list); +} + +/* + * smb_notify_change_event_queue_dump + * + * Dumps all requests in NCE queue to the system log. + */ +void +smb_notify_change_event_queue_dump() +{ + smb_request_t *sr; + int i = 0; + + smb_slist_enter(&smb_info.si_nce_list); + sr = smb_slist_head(&smb_info.si_nce_list); + while (sr) { + ASSERT(sr->sr_magic == SMB_REQ_MAGIC); + ASSERT((sr->sr_state == SMB_REQ_STATE_CANCELED) || + (sr->sr_state == SMB_REQ_STATE_EVENT_OCCURRED)); + i++; + sr = smb_slist_next(&smb_info.si_nce_list, sr); + } + smb_slist_exit(&smb_info.si_nce_list); +} + +/* + * smb_notify_change_req_queue_dump + * + * Dumps all requests in NCR queue to the system log. + */ +void +smb_notify_change_req_queue_dump() +{ + smb_request_t *sr; + int i = 0; + + smb_slist_enter(&smb_info.si_ncr_list); + sr = smb_slist_head(&smb_info.si_ncr_list); + while (sr) { + ASSERT(sr->sr_magic == SMB_REQ_MAGIC); + ASSERT(sr->sr_state == SMB_REQ_STATE_WAITING_EVENT); + i++; + sr = smb_slist_next(&smb_info.si_ncr_list, sr); + } + smb_slist_exit(&smb_info.si_ncr_list); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_security.c b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_security.c new file mode 100644 index 000000000000..70d8437cdf13 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_security.c @@ -0,0 +1,213 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include + +extern uint32_t smb_sd_read(smb_request_t *sr, smb_sdbuf_t **sr_sd, + uint32_t secinfo, uint32_t *buflen); +extern uint32_t smb_sd_write(smb_request_t *sr, smb_sdbuf_t *sr_sd, + uint32_t secinfo); + +/* + * smb_nt_transact_query_security_info + * + * This command allows the client to retrieve the security descriptor + * on a file. The result of the call is returned to the client in the + * Data part of the transaction response. + * + * Some clients specify a non-zero maximum data return size (mdrcnt) + * for the SD and some specify zero. In either case, if the mdrcnt is + * too small we need to return NT_STATUS_BUFFER_TOO_SMALL and a buffer + * size hint. The client should then retry with the appropriate buffer + * size. + * + * Client Parameter Block Description + * ================================== ================================= + * + * USHORT Fid; FID of target + * USHORT Reserved; MBZ + * ULONG secinfo; Fields of descriptor to set + * + * Data Block Encoding Description + * ================================== ================================== + * + * Data[TotalDataCount] Security Descriptor information + */ + +int +smb_nt_transact_query_security_info(struct smb_request *sr, struct smb_xa *xa) +{ + smb_sdbuf_t *sr_sd; + uint32_t secinfo; + uint32_t sr_sdlen; + uint32_t status; + + if (smb_decode_mbc(&xa->req_param_mb, "w2.l", + &sr->smb_fid, &secinfo) != 0) { + /* + * It's not clear why ERRnomem is returned here. + * This should rarely happen and we're not sure if + * it's going to break something if we change this + * error code, so we're going to keep it for now. + */ + smbsr_raise_error(sr, ERRSRV, ERRnomem); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + + if ((sr->fid_ofile->f_node == NULL) || + (sr->fid_ofile->f_ftype != SMB_FTYPE_DISK)) { + smbsr_raise_nt_error(sr, NT_STATUS_ACCESS_DENIED); + /* NOTREACHED */ + } + + if (sr->tid_tree->t_acltype != ACE_T) { + /* + * If target filesystem doesn't support ACE_T acls then + * don't process SACL + */ + secinfo &= ~SMB_SACL_SECINFO; + } + + sr_sdlen = xa->smb_mdrcnt; + status = smb_sd_read(sr, &sr_sd, secinfo, &sr_sdlen); + + if (status != NT_STATUS_SUCCESS) { + if (status == NT_STATUS_BUFFER_TOO_SMALL) { + /* + * The maximum data return count specified by the + * client is not big enough to hold the security + * descriptor. We have to return an error but we + * can provide a buffer size hint for the client. + */ + (void) smb_encode_mbc(&xa->rep_param_mb, "l", sr_sdlen); + smbsr_setup_nt_status(sr, ERROR_SEVERITY_ERROR, + NT_STATUS_BUFFER_TOO_SMALL); + return (SDRC_NORMAL_REPLY); + } + + smbsr_raise_nt_error(sr, status); + /* NOTREACHED */ + } + + (void) smb_encode_mbc(&xa->rep_data_mb, "#c", (int)sr_sdlen, sr_sd); + (void) smb_encode_mbc(&xa->rep_param_mb, "l", sr_sdlen); + + kmem_free(sr_sd, sr_sdlen); + return (SDRC_NORMAL_REPLY); +} + +/* + * smb_nt_transact_set_security_info + * + * This command allows the client to change the security descriptor on a + * file. All we do here is decode the parameters and the data. The data + * is passed directly to smb_nt_set_security_object, with the security + * information describing the information to set. There are no response + * parameters or data. + * + * Client Parameter Block Encoding Description + * ================================== ================================== + * USHORT Fid; FID of target + * USHORT Reserved; MBZ + * ULONG SecurityInformation; Fields of SD that to set + * + * Data Block Encoding Description + * ================================== ================================== + * Data[TotalDataCount] Security Descriptor information + */ +int +smb_nt_transact_set_security_info(struct smb_request *sr, struct smb_xa *xa) +{ + smb_sdbuf_t *sd_buf; + uint32_t sec_info; + uint32_t status; + + if (smb_decode_mbc(&xa->req_param_mb, "w2.l", + &sr->smb_fid, &sec_info) != 0) { + smbsr_raise_nt_error(sr, NT_STATUS_INVALID_PARAMETER); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + if ((sr->fid_ofile->f_node == NULL) || + (sr->fid_ofile->f_ftype != SMB_FTYPE_DISK)) { + smbsr_raise_nt_error(sr, NT_STATUS_ACCESS_DENIED); + /* NOTREACHED */ + } + + if (sr->fid_ofile->f_node->flags & NODE_READ_ONLY) { + smbsr_raise_nt_error(sr, NT_STATUS_MEDIA_WRITE_PROTECTED); + /* NOTREACHED */ + } + + if (sr->tid_tree->t_acltype != ACE_T) { + /* + * If target filesystem doesn't support ACE_T acls then + * don't process SACL + */ + sec_info &= ~SMB_SACL_SECINFO; + } + + if ((sec_info & SMB_ALL_SECINFO) == 0) { + return (NT_STATUS_SUCCESS); + } + + sd_buf = kmem_alloc(xa->smb_tdscnt, KM_SLEEP); + + if ((smb_decode_mbc(&xa->req_data_mb, "#c", + xa->smb_tdscnt, (char *)sd_buf)) != 0) { + kmem_free(sd_buf, xa->smb_tdscnt); + smbsr_raise_nt_error(sr, NT_STATUS_BUFFER_TOO_SMALL); + /* NOTREACHED */ + } + + status = smb_sd_write(sr, sd_buf, sec_info); + kmem_free(sd_buf, xa->smb_tdscnt); + + if (status != NT_STATUS_SUCCESS) { + smbsr_raise_nt_error(sr, status); + /* NOTREACHED */ + } + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_odir.c b/usr/src/uts/common/fs/smbsrv/smb_odir.c new file mode 100644 index 000000000000..43ecf7b023a1 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_odir.c @@ -0,0 +1,456 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * General Structures Layout + * ------------------------- + * + * This is a simplified diagram showing the relationship between most of the + * main structures. + * + * +-------------------+ + * | SMB_INFO | + * +-------------------+ + * | + * | + * v + * +-------------------+ +-------------------+ +-------------------+ + * | SESSION |<----->| SESSION |......| SESSION | + * +-------------------+ +-------------------+ +-------------------+ + * | + * | + * v + * +-------------------+ +-------------------+ +-------------------+ + * | USER |<----->| USER |......| USER | + * +-------------------+ +-------------------+ +-------------------+ + * | + * | + * v + * +-------------------+ +-------------------+ +-------------------+ + * | TREE |<----->| TREE |......| TREE | + * +-------------------+ +-------------------+ +-------------------+ + * | | + * | | + * | v + * | +-------+ +-------+ +-------+ + * | | OFILE |<----->| OFILE |......| OFILE | + * | +-------+ +-------+ +-------+ + * | + * | + * v + * +-------+ +------+ +------+ + * | ODIR |<----->| ODIR |......| ODIR | + * +-------+ +------+ +------+ + * + * + * Odir State Machine + * ------------------ + * + * +-------------------------+ T0 + * | SMB_ODIR_STATE_OPEN |<----------- Creation/Allocation + * +-------------------------+ + * | + * | T1 + * | + * v + * +-------------------------+ + * | SMB_ODIR_STATE_CLOSING | + * +-------------------------+ + * | + * | T2 + * | + * v + * +-------------------------+ T3 + * | SMB_ODIR_STATE_CLOSED |----------> Deletion/Free + * +-------------------------+ + * + * SMB_ODIR_STATE_OPEN + * + * While in this state: + * - The odir is queued in the list of odirs of its tree. + * - References will be given out if the odir is looked up. + * + * SMB_ODIR_STATE_CLOSING + * + * While in this state: + * - The odir is queued in the list of odirs of its tree. + * - References will not be given out if the odir is looked up. + * - The odir is closed. + * - The resources associated with the odir remain. + * + * SMB_ODIR_STATE_CLOSED + * + * While in this state: + * - The odir is queued in the list of odirs of its tree. + * - References will not be given out if the odir is looked up. + * - The resources associated with the odir remain. + * + * Transition T0 + * + * This transition occurs in smb_odir_open(). A new odir is created and + * added to the list of odirs of a tree. + * + * Transition T1 + * + * This transition occurs in smb_odir_close(). + * + * Transition T2 + * + * This transition occurs in smb_odir_release(). The resources associated + * with the odir are freed as well as the odir structure. For the + * transition to occur, the odir must be in the SMB_ODIR_STATE_CLOSED + * state and the reference count be zero. + * + * Comments + * -------- + * + * The state machine of the odir structures is controlled by 3 elements: + * - The list of odirs of the tree it belongs to. + * - The mutex embedded in the structure itself. + * - The reference count. + * + * There's a mutex embedded in the odir structure used to protect its fields + * and there's a lock embedded in the list of odirs of a tree. To + * increment or to decrement the reference count the mutex must be entered. + * To insert the odir into the list of odirs of the tree and to remove + * the odir from it, the lock must be entered in RW_WRITER mode. + * + * Rules of access to a odir structure: + * + * 1) In order to avoid deadlocks, when both (mutex and lock of the odir + * list) have to be entered, the lock must be entered first. + * + * 2) All actions applied to an odir require a reference count. + * + * 3) There are 2 ways of getting a reference count. One is when the odir + * is opened. The other when the odir is looked up. This translates + * into 2 functions: smb_odir_open() and smb_odir_lookup_by_fid(). + * + * It should be noted that the reference count of an odir registers the + * number of references to the odir in other structures (such as an smb + * request). The reference count is not incremented in these 2 instances: + * + * 1) The odir is open. An odir is anchored by his state. If there's + * no activity involving an odir currently open, the reference count + * of that odir is zero. + * + * 2) The odir is queued in the list of odirs of its tree. The fact of + * being queued in that list is NOT registered by incrementing the + * reference count. + */ +#include +#include +#include + +/* Static functions defined further down this file. */ +static void smb_odir_delete(smb_odir_t *of); +static smb_odir_t *smb_odir_close_and_next(smb_odir_t *od); + +#include +#include + +/* + * smb_odir_open + */ +smb_odir_t * +smb_odir_open( + smb_tree_t *tree, + smb_node_t *node, + char *pattern, + uint16_t pid, + unsigned short sattr) +{ + smb_odir_t *dir; + + ASSERT(tree); + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + ASSERT(node); + ASSERT(node->n_magic == SMB_NODE_MAGIC); + ASSERT(pattern); + + if (strlen(pattern) >= sizeof (dir->d_pattern)) { + return (NULL); + } + + dir = kmem_cache_alloc(smb_info.si_cache_odir, KM_SLEEP); + bzero(dir, sizeof (smb_odir_t)); + dir->d_refcnt = 1; + dir->d_session = tree->t_session; + dir->d_user = tree->t_user; + dir->d_tree = tree; + (void) strlcpy(dir->d_pattern, pattern, sizeof (dir->d_pattern)); + dir->d_wildcards = smb_convert_unicode_wildcards(pattern); + dir->d_state = SMB_ODIR_STATE_OPEN; + + if (smb_idpool_alloc(&dir->d_tree->t_sid_pool, &dir->d_sid)) { + kmem_cache_free(smb_info.si_cache_odir, dir); + return (NULL); + } + mutex_init(&dir->d_mutex, NULL, MUTEX_DEFAULT, NULL); + dir->d_sattr = sattr; + dir->d_opened_by_pid = pid; + dir->d_dir_snode = node; + dir->d_state = SMB_ODIR_STATE_OPEN; + dir->d_magic = SMB_ODIR_MAGIC; + + smb_llist_enter(&tree->t_odir_list, RW_WRITER); + smb_llist_insert_tail(&tree->t_odir_list, dir); + smb_llist_exit(&tree->t_odir_list); + + atomic_inc_32(&tree->t_session->s_dir_cnt); + return (dir); +} + +/* + * smb_odir_close + */ +void +smb_odir_close( + smb_odir_t *od) +{ + ASSERT(od); + ASSERT(od->d_magic == SMB_ODIR_MAGIC); + + mutex_enter(&od->d_mutex); + ASSERT(od->d_refcnt); + switch (od->d_state) { + case SMB_ODIR_STATE_OPEN: + od->d_state = SMB_ODIR_STATE_CLOSED; + break; + case SMB_ODIR_STATE_CLOSING: + case SMB_ODIR_STATE_CLOSED: + break; + default: + ASSERT(0); + break; + } + mutex_exit(&od->d_mutex); +} + +/* + * smb_odir_close_all + * + * + */ +void +smb_odir_close_all( + smb_tree_t *tree) +{ + smb_odir_t *od; + + ASSERT(tree); + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + + smb_llist_enter(&tree->t_odir_list, RW_READER); + od = smb_llist_head(&tree->t_odir_list); + while (od) { + ASSERT(od->d_magic == SMB_ODIR_MAGIC); + ASSERT(od->d_tree == tree); + od = smb_odir_close_and_next(od); + } + smb_llist_exit(&tree->t_odir_list); +} + +/* + * smb_odir_close_all_by_pid + * + * + */ +void +smb_odir_close_all_by_pid( + smb_tree_t *tree, + uint16_t pid) +{ + smb_odir_t *od; + + ASSERT(tree); + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + + smb_llist_enter(&tree->t_odir_list, RW_READER); + od = smb_llist_head(&tree->t_odir_list); + while (od) { + ASSERT(od->d_magic == SMB_ODIR_MAGIC); + ASSERT(od->d_tree == tree); + if (od->d_opened_by_pid == pid) { + od = smb_odir_close_and_next(od); + } else { + od = smb_llist_next(&tree->t_odir_list, od); + } + } + smb_llist_exit(&tree->t_odir_list); +} + +/* + * smb_odir_release + */ +void +smb_odir_release( + smb_odir_t *od) +{ + ASSERT(od); + ASSERT(od->d_magic == SMB_ODIR_MAGIC); + + mutex_enter(&od->d_mutex); + ASSERT(od->d_refcnt); + od->d_refcnt--; + switch (od->d_state) { + case SMB_ODIR_STATE_CLOSING: + case SMB_ODIR_STATE_OPEN: + break; + + case SMB_ODIR_STATE_CLOSED: + if (od->d_refcnt == 0) { + mutex_exit(&od->d_mutex); + smb_odir_delete(od); + return; + } + break; + + default: + ASSERT(0); + break; + } + mutex_exit(&od->d_mutex); +} + +/* + * smb_odir_lookup_by_sid + */ +smb_odir_t * +smb_odir_lookup_by_sid( + smb_tree_t *tree, + uint16_t sid) +{ + smb_llist_t *od_list; + smb_odir_t *od; + + ASSERT(tree); + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + + od_list = &tree->t_odir_list; + + smb_llist_enter(od_list, RW_READER); + od = smb_llist_head(od_list); + while (od) { + ASSERT(od->d_magic == SMB_ODIR_MAGIC); + ASSERT(od->d_tree == tree); + if (od->d_sid == sid) { + mutex_enter(&od->d_mutex); + if (od->d_state != SMB_ODIR_STATE_OPEN) { + mutex_exit(&od->d_mutex); + smb_llist_exit(od_list); + return (NULL); + } + od->d_refcnt++; + mutex_exit(&od->d_mutex); + break; + } + od = smb_llist_next(od_list, od); + } + smb_llist_exit(od_list); + return (od); +} + +/* *************************** Static Functions ***************************** */ + +/* + * smb_odir_close_and_next + * + * This function closes the directory passed in (if appropriate) and returns the + * next directory in the list of directories of the tree of the directory passed + * in. It requires that the list of directories of the tree be entered in + * RW_READER mode before being called. + */ +static smb_odir_t * +smb_odir_close_and_next( + smb_odir_t *od) +{ + smb_odir_t *next_od; + smb_tree_t *tree; + + ASSERT(od); + ASSERT(od->d_magic == SMB_ODIR_MAGIC); + + mutex_enter(&od->d_mutex); + switch (od->d_state) { + case SMB_ODIR_STATE_OPEN: + /* The directory is still opened. */ + od->d_refcnt++; + ASSERT(od->d_refcnt); + tree = od->d_tree; + mutex_exit(&od->d_mutex); + smb_llist_exit(&od->d_tree->t_odir_list); + smb_odir_close(od); + smb_odir_release(od); + smb_llist_enter(&tree->t_odir_list, RW_READER); + next_od = smb_llist_head(&tree->t_odir_list); + break; + case SMB_ODIR_STATE_CLOSING: + case SMB_ODIR_STATE_CLOSED: + /* + * The odir exists but is closed or is in the process + * of being closed. + */ + mutex_exit(&od->d_mutex); + next_od = smb_llist_next(&od->d_tree->t_odir_list, od); + break; + default: + ASSERT(0); + mutex_exit(&od->d_mutex); + next_od = smb_llist_next(&od->d_tree->t_odir_list, od); + break; + } + return (next_od); +} + +/* + * smb_odir_delete + */ +static void +smb_odir_delete( + smb_odir_t *od) +{ + ASSERT(od); + ASSERT(od->d_magic == SMB_ODIR_MAGIC); + ASSERT(od->d_state == SMB_ODIR_STATE_CLOSED); + ASSERT(od->d_refcnt == 0); + + /* + * Let's remove the odir from the list of odirs of the tree. This has + * to be done before any resources associated with the odir are + * released. + */ + smb_llist_enter(&od->d_tree->t_odir_list, RW_WRITER); + smb_llist_remove(&od->d_tree->t_odir_list, od); + smb_llist_exit(&od->d_tree->t_odir_list); + + smb_node_release(od->d_dir_snode); + atomic_dec_32(&od->d_tree->t_session->s_dir_cnt); + smb_idpool_free(&od->d_tree->t_sid_pool, od->d_sid); + mutex_destroy(&od->d_mutex); + kmem_cache_free(smb_info.si_cache_odir, od); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_ofile.c b/usr/src/uts/common/fs/smbsrv/smb_ofile.c new file mode 100644 index 000000000000..150262e28b0d --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_ofile.c @@ -0,0 +1,726 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * General Structures Layout + * ------------------------- + * + * This is a simplified diagram showing the relationship between most of the + * main structures. + * + * +-------------------+ + * | SMB_INFO | + * +-------------------+ + * | + * | + * v + * +-------------------+ +-------------------+ +-------------------+ + * | SESSION |<----->| SESSION |......| SESSION | + * +-------------------+ +-------------------+ +-------------------+ + * | + * | + * v + * +-------------------+ +-------------------+ +-------------------+ + * | USER |<----->| USER |......| USER | + * +-------------------+ +-------------------+ +-------------------+ + * | + * | + * v + * +-------------------+ +-------------------+ +-------------------+ + * | TREE |<----->| TREE |......| TREE | + * +-------------------+ +-------------------+ +-------------------+ + * | | + * | | + * | v + * | +-------+ +-------+ +-------+ + * | | OFILE |<----->| OFILE |......| OFILE | + * | +-------+ +-------+ +-------+ + * | + * | + * v + * +-------+ +------+ +------+ + * | ODIR |<----->| ODIR |......| ODIR | + * +-------+ +------+ +------+ + * + * + * Ofile State Machine + * ------------------ + * + * +-------------------------+ T0 + * | SMB_OFILE_STATE_OPEN |<----------- Creation/Allocation + * +-------------------------+ + * | + * | T1 + * | + * v + * +-------------------------+ + * | SMB_OFILE_STATE_CLOSING | + * +-------------------------+ + * | + * | T2 + * | + * v + * +-------------------------+ T3 + * | SMB_OFILE_STATE_CLOSED |----------> Deletion/Free + * +-------------------------+ + * + * SMB_OFILE_STATE_OPEN + * + * While in this state: + * - The ofile is queued in the list of ofiles of its tree. + * - References will be given out if the ofile is looked up. + * + * SMB_OFILE_STATE_CLOSING + * + * While in this state: + * - The ofile is queued in the list of ofiles of its tree. + * - References will not be given out if the ofile is looked up. + * - The file is closed and the locks held are being released. + * - The resources associated with the ofile remain. + * + * SMB_OFILE_STATE_CLOSED + * + * While in this state: + * - The ofile is queued in the list of ofiles of its tree. + * - References will not be given out if the ofile is looked up. + * - The resources associated with the ofile remain. + * + * Transition T0 + * + * This transition occurs in smb_ofile_open(). A new ofile is created and + * added to the list of ofiles of a tree. + * + * Transition T1 + * + * This transition occurs in smb_ofile_close(). + * + * Transition T2 + * + * This transition occurs in smb_ofile_release(). The resources associated + * with the ofile are freed as well as the ofile structure. For the + * transition to occur, the ofile must be in the SMB_OFILE_STATE_CLOSED + * state and the reference count be zero. + * + * Comments + * -------- + * + * The state machine of the ofile structures is controlled by 3 elements: + * - The list of ofiles of the tree it belongs to. + * - The mutex embedded in the structure itself. + * - The reference count. + * + * There's a mutex embedded in the ofile structure used to protect its fields + * and there's a lock embedded in the list of ofiles of a tree. To + * increment or to decrement the reference count the mutex must be entered. + * To insert the ofile into the list of ofiles of the tree and to remove + * the ofile from it, the lock must be entered in RW_WRITER mode. + * + * Rules of access to a ofile structure: + * + * 1) In order to avoid deadlocks, when both (mutex and lock of the ofile + * list) have to be entered, the lock must be entered first. + * + * 2) All actions applied to an ofile require a reference count. + * + * 3) There are 2 ways of getting a reference count. One is when the ofile + * is opened. The other one when the ofile is looked up. This translates + * into 2 functions: smb_ofile_open() and smb_ofile_lookup_by_fid(). + * + * It should be noted that the reference count of an ofile registers the + * number of references to the ofile in other structures (such as an smb + * request). The reference count is not incremented in these 2 instances: + * + * 1) The ofile is open. An ofile is anchored by his state. If there's + * no activity involving an ofile currently open, the reference count + * of that ofile is zero. + * + * 2) The ofile is queued in the list of ofiles of its tree. The fact of + * being queued in that list is NOT registered by incrementing the + * reference count. + */ +#include +#include +#include + +/* Static functions defined further down this file. */ +static void smb_ofile_delete(smb_ofile_t *of); +static smb_ofile_t *smb_ofile_close_and_next(smb_ofile_t *of); + +/* + * smb_ofile_open + * + * + */ +smb_ofile_t * +smb_ofile_open( + smb_tree_t *tree, + smb_node_t *node, + uint16_t pid, + uint32_t access_granted, + uint32_t create_options, + uint32_t share_access, + uint16_t ftype, + char *pipe_name, + uint32_t rpc_fid, + smb_error_t *err) +{ + smb_ofile_t *of; + uint16_t fid; + + if (smb_idpool_alloc(&tree->t_fid_pool, &fid)) { + err->status = NT_STATUS_TOO_MANY_OPENED_FILES; + err->errcls = ERRDOS; + err->errcode = ERROR_TOO_MANY_OPEN_FILES; + return (NULL); + } + + of = kmem_cache_alloc(smb_info.si_cache_ofile, KM_SLEEP); + bzero(of, sizeof (smb_ofile_t)); + of->f_magic = SMB_OFILE_MAGIC; + of->f_refcnt = 1; + of->f_fid = fid; + of->f_opened_by_pid = pid; + of->f_granted_access = access_granted; + of->f_share_access = share_access; + of->f_create_options = create_options; + of->f_cr = tree->t_user->u_cred; + crhold(of->f_cr); + of->f_ftype = ftype; + of->f_session = tree->t_user->u_session; + of->f_user = tree->t_user; + of->f_tree = tree; + of->f_node = node; + mutex_init(&of->f_mutex, NULL, MUTEX_DEFAULT, NULL); + of->f_state = SMB_OFILE_STATE_OPEN; + + if (ftype == SMB_FTYPE_MESG_PIPE) { + of->f_pipe_info = kmem_alloc(sizeof (mlsvc_pipe_t), KM_SLEEP); + bzero(of->f_pipe_info, sizeof (mlsvc_pipe_t)); + of->f_pipe_info->pipe_name = pipe_name; + of->f_pipe_info->fid = rpc_fid; + mutex_init(&of->f_pipe_info->mutex, NULL, MUTEX_DEFAULT, NULL); + cv_init(&of->f_pipe_info->cv, NULL, CV_DEFAULT, NULL); + } else { + ASSERT(ftype == SMB_FTYPE_DISK); /* Regular file, not a pipe */ + ASSERT(node); + if (crgetuid(of->f_cr) == node->attr.sa_vattr.va_uid) { + /* + * Add this bit for the file's owner even if it's not + * specified in the request (Windows behavior). + */ + of->f_granted_access |= FILE_READ_ATTRIBUTES; + } + + if ((node->vp->v_type == VREG) && (smb_fsop_open(of) != 0)) { + of->f_magic = 0; + mutex_destroy(&of->f_mutex); + crfree(of->f_cr); + smb_idpool_free(&tree->t_fid_pool, of->f_fid); + kmem_cache_free(smb_info.si_cache_ofile, of); + err->status = NT_STATUS_ACCESS_DENIED; + err->errcls = ERRDOS; + err->errcode = ERROR_ACCESS_DENIED; + return (NULL); + } + smb_llist_enter(&node->n_ofile_list, RW_WRITER); + smb_llist_insert_tail(&node->n_ofile_list, of); + smb_llist_exit(&node->n_ofile_list); + } + smb_llist_enter(&tree->t_ofile_list, RW_WRITER); + smb_llist_insert_tail(&tree->t_ofile_list, of); + smb_llist_exit(&tree->t_ofile_list); + atomic_inc_32(&smb_info.open_files); + atomic_inc_32(&of->f_session->s_file_cnt); + + return (of); +} + +/* + * smb_ofile_close + * + * + */ +int +smb_ofile_close( + smb_ofile_t *of, + uint32_t last_wtime) +{ + int rc = 0; + + + ASSERT(of); + ASSERT(of->f_magic == SMB_OFILE_MAGIC); + + mutex_enter(&of->f_mutex); + ASSERT(of->f_refcnt); + switch (of->f_state) { + case SMB_OFILE_STATE_OPEN: { + + of->f_state = SMB_OFILE_STATE_CLOSING; + mutex_exit(&of->f_mutex); + + if (of->f_ftype == SMB_FTYPE_MESG_PIPE) { + smb_rpc_close(of); + } else { + if (of->f_node->vp->v_type == VREG) + (void) smb_fsop_close(of); + + if (of->f_node->flags & NODE_CREATED_READONLY) { + smb_node_set_dosattr(of->f_node, + of->f_node->attr.sa_dosattr | + SMB_FA_READONLY); + of->f_node->flags &= ~NODE_CREATED_READONLY; + } + + smb_ofile_close_timestamp_update(of, last_wtime); + rc = smb_sync_fsattr(NULL, of->f_cr, of->f_node); + smb_commit_delete_on_close(of); + smb_release_oplock(of, OPLOCK_RELEASE_FILE_CLOSED); + smb_commit_delete_on_close(of); + /* + * if there is any notify change request for + * this file then see if any of them is related + * to this open instance. If there is any then + * cancel them. + */ + if (of->f_node->flags & NODE_FLAGS_NOTIFY_CHANGE) + smb_process_file_notify_change_queue(of); + smb_node_destroy_lock_by_ofile(of->f_node, of); + } + atomic_dec_32(&smb_info.open_files); + + mutex_enter(&of->f_mutex); + ASSERT(of->f_refcnt); + ASSERT(of->f_state == SMB_OFILE_STATE_CLOSING); + of->f_state = SMB_OFILE_STATE_CLOSED; + mutex_exit(&of->f_mutex); + return (rc); + } + case SMB_OFILE_STATE_CLOSED: + case SMB_OFILE_STATE_CLOSING: + break; + + default: + ASSERT(0); + break; + } + mutex_exit(&of->f_mutex); + return (rc); +} + +/* + * smb_ofile_close_all + * + * + */ +void +smb_ofile_close_all( + smb_tree_t *tree) +{ + smb_ofile_t *of; + + ASSERT(tree); + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + + smb_llist_enter(&tree->t_ofile_list, RW_READER); + of = smb_llist_head(&tree->t_ofile_list); + while (of) { + ASSERT(of->f_magic == SMB_OFILE_MAGIC); + ASSERT(of->f_tree == tree); + of = smb_ofile_close_and_next(of); + } + smb_llist_exit(&tree->t_ofile_list); +} + +/* + * smb_ofiles_close_by_pid + * + * + */ +void +smb_ofile_close_all_by_pid( + smb_tree_t *tree, + uint16_t pid) +{ + smb_ofile_t *of; + + ASSERT(tree); + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + + smb_llist_enter(&tree->t_ofile_list, RW_READER); + of = smb_llist_head(&tree->t_ofile_list); + while (of) { + ASSERT(of->f_magic == SMB_OFILE_MAGIC); + ASSERT(of->f_tree == tree); + if (of->f_opened_by_pid == pid) { + of = smb_ofile_close_and_next(of); + } else { + of = smb_llist_next(&tree->t_ofile_list, of); + } + } + smb_llist_exit(&tree->t_ofile_list); +} + +/* + * smb_ofile_release + * + */ +void +smb_ofile_release( + smb_ofile_t *of) +{ + ASSERT(of); + ASSERT(of->f_magic == SMB_OFILE_MAGIC); + + mutex_enter(&of->f_mutex); + ASSERT(of->f_refcnt); + of->f_refcnt--; + switch (of->f_state) { + case SMB_OFILE_STATE_OPEN: + case SMB_OFILE_STATE_CLOSING: + break; + + case SMB_OFILE_STATE_CLOSED: + if (of->f_refcnt == 0) { + mutex_exit(&of->f_mutex); + smb_ofile_delete(of); + return; + } + break; + + default: + ASSERT(0); + break; + } + mutex_exit(&of->f_mutex); +} + +/* + * smb_ofile_lookup_by_fid + * + * Find the open file whose fid matches the one specified in the request. + * If we can't find the fid or the shares (trees) don't match, we have a + * bad fid. + */ +smb_ofile_t * +smb_ofile_lookup_by_fid( + smb_tree_t *tree, + uint16_t fid) +{ + smb_llist_t *of_list; + smb_ofile_t *of; + + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + + of_list = &tree->t_ofile_list; + + smb_llist_enter(of_list, RW_READER); + of = smb_llist_head(of_list); + while (of) { + ASSERT(of->f_magic == SMB_OFILE_MAGIC); + ASSERT(of->f_tree == tree); + if (of->f_fid == fid) { + mutex_enter(&of->f_mutex); + if (of->f_state != SMB_OFILE_STATE_OPEN) { + mutex_exit(&of->f_mutex); + smb_llist_exit(of_list); + return (NULL); + } + of->f_refcnt++; + mutex_exit(&of->f_mutex); + break; + } + of = smb_llist_next(of_list, of); + } + smb_llist_exit(of_list); + return (of); +} + +/* + * smb_ofile_set_flags + * + * Return value: + * + * Current flags value + * + */ +void +smb_ofile_set_flags( + smb_ofile_t *of, + uint32_t flags) +{ + ASSERT(of); + ASSERT(of->f_magic == SMB_OFILE_MAGIC); + ASSERT(of->f_refcnt); + + mutex_enter(&of->f_mutex); + of->f_flags |= flags; + mutex_exit(&of->f_mutex); +} +/* + * smb_ofile_seek + * + * Return value: + * + * 0 Success + * EINVAL Unknown mode + * EOVERFLOW offset too big + * + */ +int +smb_ofile_seek( + smb_ofile_t *of, + ushort_t mode, + int32_t off, + uint32_t *retoff) +{ + off_t newoff = 0; + int rc = 0; + + ASSERT(of); + ASSERT(of->f_magic == SMB_OFILE_MAGIC); + ASSERT(of->f_refcnt); + + mutex_enter(&of->f_mutex); + switch (mode) { + case SMB_SEEK_SET: + if (off < 0) + newoff = 0; + else + newoff = (off_t)off; + break; + + case SMB_SEEK_CUR: + if (off < 0 && (-off) > of->f_seek_pos) + newoff = 0; + else + newoff = of->f_seek_pos + (off_t)off; + break; + + case SMB_SEEK_END: + if (off < 0 && (-off) > of->f_node->attr.sa_vattr.va_size) + newoff = 0; + else + newoff = of->f_node->attr.sa_vattr.va_size + (off_t)off; + break; + + default: + mutex_exit(&of->f_mutex); + return (EINVAL); + } + + if (newoff > ULONG_MAX) { + rc = EOVERFLOW; + } else { + of->f_seek_pos = newoff; + *retoff = (uint32_t)newoff; + } + mutex_exit(&of->f_mutex); + return (rc); +} + +/* + * smb_ofile_close_timestamp_update + * + * + */ +void +smb_ofile_close_timestamp_update( + smb_ofile_t *of, + uint32_t last_wtime) +{ + smb_node_t *node; + timestruc_t mtime, atime; + unsigned int what = 0; + + mtime.tv_sec = last_wtime; + mtime.tv_nsec = 0; + + if (mtime.tv_sec != 0 && mtime.tv_sec != 0xFFFFFFFF) { + mtime.tv_sec = smb_local_time_to_gmt(mtime.tv_sec); + what |= SMB_AT_MTIME; + } + + /* + * NODE_FLAGS_SYNCATIME is set whenever something is + * written to a file. Compliant volumes don't update + * atime upon write, so don't update the atime if the + * volume is compliant. + */ + node = of->f_node; + if (node->flags & NODE_FLAGS_SYNCATIME) { + node->flags &= ~NODE_FLAGS_SYNCATIME; + what |= SMB_AT_ATIME; + (void) microtime(&atime); + } + + smb_node_set_time(node, 0, &mtime, &atime, 0, what); +} + +/* + * smb_ofile_is_open + * + */ +boolean_t +smb_ofile_is_open( + smb_ofile_t *of) +{ + boolean_t rc = B_FALSE; + + ASSERT(of); + ASSERT(of->f_magic == SMB_OFILE_MAGIC); + + mutex_enter(&of->f_mutex); + if (of->f_state == SMB_OFILE_STATE_OPEN) { + rc = B_TRUE; + } + mutex_exit(&of->f_mutex); + return (rc); +} + +/* *************************** Static Functions ***************************** */ + +/* + * smb_ofile_close_and_next + * + * This function closes the file passed in (if appropriate) and returns the + * next open file in the list of open files of the tree of the open file passed + * in. It requires that the list of open files of the tree be entered in + * RW_READER mode before being called. + */ +static smb_ofile_t * +smb_ofile_close_and_next( + smb_ofile_t *of) +{ + smb_ofile_t *next_of; + smb_tree_t *tree; + + ASSERT(of); + ASSERT(of->f_magic == SMB_OFILE_MAGIC); + + mutex_enter(&of->f_mutex); + switch (of->f_state) { + case SMB_OFILE_STATE_OPEN: + /* The file is still open. */ + of->f_refcnt++; + ASSERT(of->f_refcnt); + tree = of->f_tree; + mutex_exit(&of->f_mutex); + smb_llist_exit(&of->f_tree->t_ofile_list); + (void) smb_ofile_close(of, 0); + smb_ofile_release(of); + smb_llist_enter(&tree->t_ofile_list, RW_READER); + next_of = smb_llist_head(&tree->t_ofile_list); + break; + case SMB_OFILE_STATE_CLOSING: + case SMB_OFILE_STATE_CLOSED: + /* + * The ofile exists but is closed or + * in the process being closed. + */ + mutex_exit(&of->f_mutex); + next_of = smb_llist_next(&of->f_tree->t_ofile_list, of); + break; + default: + ASSERT(0); + mutex_exit(&of->f_mutex); + next_of = smb_llist_next(&of->f_tree->t_ofile_list, of); + break; + } + return (next_of); +} + +/* + * smb_ofile_delete + * + * + */ +static void +smb_ofile_delete( + smb_ofile_t *of) +{ + ASSERT(of); + ASSERT(of->f_magic == SMB_OFILE_MAGIC); + ASSERT(of->f_refcnt == 0); + ASSERT(of->f_state == SMB_OFILE_STATE_CLOSED); + + /* + * Let's remove the ofile from the list of ofiles of the tree. This has + * to be done before any resources associated with the ofile are + * released. + */ + smb_llist_enter(&of->f_tree->t_ofile_list, RW_WRITER); + smb_llist_remove(&of->f_tree->t_ofile_list, of); + smb_llist_exit(&of->f_tree->t_ofile_list); + atomic_dec_32(&of->f_session->s_file_cnt); + + if (of->f_ftype == SMB_FTYPE_MESG_PIPE) { + kmem_free(of->f_pipe_info, sizeof (mlsvc_pipe_t)); + of->f_pipe_info = NULL; + } else { + ASSERT(of->f_ftype == SMB_FTYPE_DISK); + ASSERT(of->f_node != NULL); + smb_llist_enter(&of->f_node->n_ofile_list, RW_WRITER); + smb_llist_remove(&of->f_node->n_ofile_list, of); + smb_llist_exit(&of->f_node->n_ofile_list); + smb_node_release(of->f_node); + } + + of->f_magic = (uint32_t)~SMB_OFILE_MAGIC; + mutex_destroy(&of->f_mutex); + crfree(of->f_cr); + smb_idpool_free(&of->f_tree->t_fid_pool, of->f_fid); + kmem_cache_free(smb_info.si_cache_ofile, of); +} + +/* + * smb_ofile_access + * + * This function will check to see if the access requested is granted. + * Returns NT status codes. + */ +uint32_t +smb_ofile_access(smb_ofile_t *of, cred_t *cr, uint32_t access) +{ + + if ((of == NULL) || (cr == kcred)) + return (NT_STATUS_SUCCESS); + + /* + * If the request is for something + * I don't grant it is an error + */ + if (~(of->f_granted_access) & access) { + if (!(of->f_granted_access & ACCESS_SYSTEM_SECURITY) && + (access & ACCESS_SYSTEM_SECURITY)) { + return (NT_STATUS_PRIVILEGE_NOT_HELD); + } + return (NT_STATUS_ACCESS_DENIED); + } + + return (NT_STATUS_SUCCESS); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_open_andx.c b/usr/src/uts/common/fs/smbsrv/smb_open_andx.c new file mode 100644 index 000000000000..aa0227422409 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_open_andx.c @@ -0,0 +1,431 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +/* + * This module provides the common open functionality to the various + * open and create SMB interface functions. + */ + +/* + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 15 + * UCHAR AndXCommand; Secondary (X) command; 0xFF = + * none + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * USHORT Flags; Additional information: bit set- + * 0 - return additional info + * 1 - exclusive oplock requested + * 2 - batch oplock requested + * USHORT DesiredAccess; File open mode + * USHORT SearchAttributes; + * USHORT FileAttributes; + * UTIME CreationTime; Creation timestamp for file if it + * gets created + * USHORT OpenFunction; Action to take if file exists + * ULONG AllocationSize; Bytes to reserve on create or + * truncate + * ULONG Reserved[2]; Must be 0 + * USHORT ByteCount; Count of data bytes; min = 1 + * UCHAR BufferFormat 0x04 + * STRING FileName; + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 15 + * UCHAR AndXCommand; Secondary (X) command; 0xFF = + * none + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * USHORT Fid; File handle + * USHORT FileAttributes; + * UTIME LastWriteTime; + * ULONG DataSize; Current file size + * USHORT GrantedAccess; Access permissions actually + * allowed + * USHORT FileType; Type of file opened + * USHORT DeviceState; State of the named pipe + * USHORT Action; Action taken + * ULONG ServerFid; Server unique file id + * USHORT Reserved; Reserved (must be 0) + * USHORT ByteCount; Count of data bytes = 0 + * + * DesiredAccess describes the access the client desires for the file (see + * section 3.6 - Access Mode Encoding). + * + * OpenFunction specifies the action to be taken depending on whether or + * not the file exists (see section 3.8 - Open Function Encoding). Action + * + * in the response specifies the action as a result of the Open request + * (see section 3.9 - Open Action Encoding). + * + * SearchAttributes indicates the attributes that the file must have to be + * found while searching to see if it exists. The encoding of this field + * is described in the "File Attribute Encoding" section elsewhere in this + * document. If SearchAttributes is zero then only normal files are + * returned. If the system file, hidden or directory attributes are + * specified then the search is inclusive -- both the specified type(s) of + * files and normal files are returned. + * + * FileType returns the kind of resource actually opened: + * + * Name Value Description + * ========================== ====== ================================== + * + * FileTypeDisk 0 Disk file or directory as defined + * in the attribute field + * FileTypeByteModePipe 1 Named pipe in byte mode + * FileTypeMessageModePipe 2 Named pipe in message mode + * FileTypePrinter 3 Spooled printer + * FileTypeUnknown 0xFFFF Unrecognized resource type + * + * If bit0 of Flags is clear, the FileAttributes, LastWriteTime, DataSize, + * FileType, and DeviceState have indeterminate values in the response. + * + * This SMB can request an oplock on the opened file. Oplocks are fully + * described in the "Oplocks" section elsewhere in this document, and there + * is also discussion of oplocks in the SMB_COM_LOCKING_ANDX SMB + * description. Bit1 and bit2 of the Flags field are used to request + * oplocks during open. + * + * The following SMBs may follow SMB_COM_OPEN_ANDX: + * + * SMB_COM_READ SMB_COM_READ_ANDX + * SMB_COM_IOCTL + */ + +#include + +/* + * SMB: open + * + * This message is sent to obtain a file handle for a data file. This + * returned Fid is used in subsequent client requests such as read, write, + * close, etc. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 2 + * USHORT DesiredAccess; Mode - read/write/share + * USHORT SearchAttributes; + * USHORT ByteCount; Count of data bytes; min = 2 + * UCHAR BufferFormat; 0x04 + * STRING FileName[]; File name + * + * FileName is the fully qualified file name, relative to the root of the + * share specified in the Tid field of the SMB header. If Tid in the SMB + * header refers to a print share, this SMB creates a new file which will + * be spooled to the printer when closed. In this case, FileName is + * ignored. + * + * SearchAttributes specifies the type of file desired. The encoding is + * described in the "File Attribute Encoding" section. + * + * DesiredAccess controls the mode under which the file is opened, and the + * file will be opened only if the client has the appropriate permissions. + * The encoding of DesiredAccess is discussed in the section entitled + * "Access Mode Encoding". + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 7 + * USHORT Fid; File handle + * USHORT FileAttributes; Attributes of opened file + * UTIME LastWriteTime; Time file was last written + * ULONG DataSize; File size + * USHORT GrantedAccess; Access allowed + * USHORT ByteCount; Count of data bytes = 0 + * + * Fid is the handle value which should be used for subsequent file + * operations. + * + * FileAttributes specifies the type of file obtained. The encoding is + * described in the "File Attribute Encoding" section. + * + * GrantedAccess indicates the access permissions actually allowed, and may + * have one of the following values: + * + * 0 read-only + * 1 write-only + * 2 read/write + * + * File Handles (Fids) are scoped per client. A Pid may reference any Fid + * established by itself or any other Pid on the client (so far as the + * server is concerned). The actual accesses allowed through the Fid + * depends on the open and deny modes specified when the file was opened + * (see below). + * + * The MS-DOS compatibility mode of file open provides exclusion at the + * client level. A file open in compatibility mode may be opened (also in + * compatibility mode) any number of times for any combination of reading + * and writing (subject to the user's permissions) by any Pid on the same + * client. If the first client has the file open for writing, then the + * file may not be opened in any way by any other client. If the first + * client has the file open only for reading, then other clients may open + * the file, in compatibility mode, for reading.. The above + * notwithstanding, if the filename has an extension of .EXE, .DLL, .SYM, + * or .COM other clients are permitted to open the file regardless of + * read/write open modes of other compatibility mode opens. However, once + * multiple clients have the file open for reading, no client is permitted + * to open the file for writing and no other client may open the file in + * any mode other than compatibility mode. + * + * The other file exclusion modes (Deny read/write, Deny write, Deny read, + * Deny none) provide exclusion at the file level. A file opened in any + * "Deny" mode may be opened again only for the accesses allowed by the + * Deny mode (subject to the user's permissions). This is true regardless + * of the identity of the second opener -a different client, a Pid from the + * same client, or the Pid that already has the file open. For example, if + * a file is open in "Deny write" mode a second open may only obtain read + * permission to the file. + * + * Although Fids are available to all Pids on a client, Pids other than the + * owner may not have the full access rights specified in the open mode by + * the Fid's creator. If the open creating the Fid specified a deny mode, + * then any Pid using the Fid, other than the creating Pid, will have only + * those access rights determined by "anding" the open mode rights and the + * deny mode rights, i.e., the deny mode is checked on all file accesses. + * For example, if a file is opened for Read/Write in Deny write mode, then + * other clients may only read the file and cannot write; if a file is + * opened for Read in Deny read mode, then the other clients can neither + * read nor write the file. + */ + +int +smb_com_open(struct smb_request *sr) +{ + struct open_param *op = &sr->arg.open; + uint16_t file_attr; + DWORD status; + + bzero(op, sizeof (sr->arg.open)); + if (smbsr_decode_vwv(sr, "ww", &op->omode, &op->fqi.srch_attr) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (smbsr_decode_data(sr, "%S", sr, &op->fqi.path) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + op->desired_access = smb_omode_to_amask(op->omode); + op->share_access = smb_denymode_to_sharemode(op->omode, op->fqi.path); + + if ((op->desired_access == ((uint32_t)SMB_INVALID_AMASK)) || + (op->share_access == ((uint32_t)SMB_INVALID_SHAREMODE))) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_PARAMETER, + ERRDOS, ERROR_INVALID_PARAMETER); + /* NOTREACHED */ + } + + op->dsize = 0; /* Don't set spurious size */ + op->utime.tv_sec = op->utime.tv_nsec = 0; + op->create_disposition = FILE_OPEN; + op->create_options = (op->omode & SMB_DA_WRITE_THROUGH) + ? FILE_WRITE_THROUGH : 0; + + if (sr->smb_flg & SMB_FLAGS_OPLOCK) { + if (sr->smb_flg & SMB_FLAGS_OPLOCK_NOTIFY_ANY) { + op->my_flags = MYF_BATCH_OPLOCK; + } else { + op->my_flags = MYF_EXCLUSIVE_OPLOCK; + } + } + + if ((status = smb_open_subr(sr)) != NT_STATUS_SUCCESS) { + if (status == NT_STATUS_SHARING_VIOLATION) + smbsr_raise_cifs_error(sr, + NT_STATUS_SHARING_VIOLATION, + ERRDOS, ERROR_SHARING_VIOLATION); + else + smbsr_raise_nt_error(sr, status); + + /* NOTREACHED */ + } + + if (MYF_OPLOCK_TYPE(op->my_flags) == MYF_OPLOCK_NONE) { + sr->smb_flg &= + ~(SMB_FLAGS_OPLOCK | SMB_FLAGS_OPLOCK_NOTIFY_ANY); + } + + if (op->dsize > 0xffffffff) + smbsr_raise_error(sr, ERRDOS, ERRbadfunc); + + file_attr = op->dattr & FILE_ATTRIBUTE_MASK; + + smbsr_encode_result(sr, 7, 0, "bwwllww", + 7, + sr->smb_fid, + file_attr, + smb_gmt_to_local_time(op->utime.tv_sec), + (uint32_t)op->dsize, + op->omode & SMB_DA_ACCESS_MASK, + (uint16_t)0); /* bcc */ + + return (SDRC_NORMAL_REPLY); +} + +int +smb_com_open_andx(struct smb_request *sr) +{ + struct open_param *op = &sr->arg.open; + uint16_t flags; + uint32_t CreationTime; + uint16_t granted_access; + uint16_t ofun; + uint16_t file_attr; + int count; + DWORD status; + int rc; + + bzero(op, sizeof (sr->arg.open)); + op->dsize = 0; + rc = smbsr_decode_vwv(sr, "b.wwwwwlwll4.", &sr->andx_com, + &sr->andx_off, &flags, &op->omode, &op->fqi.srch_attr, + &file_attr, &CreationTime, &ofun, &op->dsize, &op->timeo); + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (smbsr_decode_data(sr, "%u", sr, &op->fqi.path) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + op->desired_access = smb_omode_to_amask(op->omode); + op->share_access = smb_denymode_to_sharemode(op->omode, op->fqi.path); + + if ((op->desired_access == ((uint32_t)SMB_INVALID_AMASK)) || + (op->share_access == ((uint32_t)SMB_INVALID_SHAREMODE))) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_PARAMETER, + ERRDOS, ERROR_INVALID_PARAMETER); + /* NOTREACHED */ + } + + op->dattr = file_attr; + op->create_disposition = smb_ofun_to_crdisposition(ofun); + if (op->create_disposition == ((uint32_t)SMB_INVALID_CRDISPOSITION)) { + smbsr_raise_error(sr, ERRDOS, ERROR_INVALID_PARAMETER); + /* NOTREACHED */ + } + + op->create_options = (op->omode & SMB_DA_WRITE_THROUGH) + ? FILE_WRITE_THROUGH : 0; + + if (flags & 2) + op->my_flags = MYF_EXCLUSIVE_OPLOCK; + else if (flags & 4) + op->my_flags = MYF_BATCH_OPLOCK; + + if ((CreationTime != 0) && (CreationTime != 0xffffffff)) + op->utime.tv_sec = smb_local_time_to_gmt(CreationTime); + op->utime.tv_nsec = 0; + + status = NT_STATUS_SUCCESS; + /* + * According to NT, when exclusive share access failed, + * instead of raising "access deny" error immediately, + * we should wait for the client holding the exclusive + * file to close the file. If the wait timed out, we + * report a sharing violation; otherwise, we grant access. + * smb_open_subr returns NT_STATUS_SHARING_VIOLATION when + * it encounters an exclusive share access deny: we wait + * and retry. + */ + for (count = 0; count <= 4; count++) { + if (count) { + delay(MSEC_TO_TICK(400)); + } + + if ((status = smb_open_subr(sr)) == NT_STATUS_SUCCESS) + break; + } + + if (status != NT_STATUS_SUCCESS) { + if (status == NT_STATUS_SHARING_VIOLATION) + smbsr_raise_cifs_error(sr, + NT_STATUS_SHARING_VIOLATION, + ERRDOS, ERROR_SHARING_VIOLATION); + else + smbsr_raise_nt_error(sr, status); + + /* NOTREACHED */ + } + + if (op->dsize > 0xffffffff) + smbsr_raise_error(sr, ERRDOS, ERRbadfunc); + + if (MYF_OPLOCK_TYPE(op->my_flags) != MYF_OPLOCK_NONE) { + op->action_taken |= SMB_OACT_LOCK; + } else { + op->action_taken &= ~SMB_OACT_LOCK; + } + + granted_access = (sr->tid_tree->t_access == SMB_TREE_READ_ONLY) + ? SMB_DA_ACCESS_READ : op->omode & SMB_DA_ACCESS_MASK; + + file_attr = op->dattr & FILE_ATTRIBUTE_MASK; + if (STYPE_ISDSK(sr->tid_tree->t_res_type)) { + smb_node_t *node = sr->fid_ofile->f_node; + smbsr_encode_result(sr, 15, 0, + "b b.w w wll www wl 2. w", + 15, + sr->andx_com, VAR_BCC, + sr->smb_fid, + file_attr, + smb_gmt_to_local_time(node->attr.sa_vattr.va_mtime.tv_sec), + (uint32_t)op->dsize, + granted_access, op->ftype, + op->devstate, + op->action_taken, op->fileid, + 0); + } else { + smbsr_encode_result(sr, 15, 0, + "b b.w w wll www wl 2. w", + 15, + sr->andx_com, VAR_BCC, + sr->smb_fid, + file_attr, + 0L, + 0L, + granted_access, op->ftype, + op->devstate, + op->action_taken, op->fileid, + 0); + } + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_path_name_reduction.c b/usr/src/uts/common/fs/smbsrv/smb_path_name_reduction.c new file mode 100644 index 000000000000..cacbbb3e9dae --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_path_name_reduction.c @@ -0,0 +1,539 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include + +uint32_t +smb_is_executable(char *path) +{ + char extension[5]; + int len = strlen(path); + + if ((len >= 4) && (path[len - 4] == '.')) { + (void) strcpy(extension, &path[len - 3]); + (void) utf8_strupr(extension); + + if (strcmp(extension, "EXE") == 0) + return (NODE_FLAGS_EXECUTABLE); + + if (strcmp(extension, "COM") == 0) + return (NODE_FLAGS_EXECUTABLE); + + if (strcmp(extension, "DLL") == 0) + return (NODE_FLAGS_EXECUTABLE); + + if (strcmp(extension, "SYM") == 0) + return (NODE_FLAGS_EXECUTABLE); + } + + return (0); +} + +/* + * smbd_fs_query + * + * This function has been changed to return errors instead of using + * smbsr_raise_errno to longjmp round the calling code. This allows + * the caller to release resources when an error occurs. + * + * Upon success, the caller will need to call smb_node_release() on + * fqi.last_snode (if it isn't already set to NULL by this routine) and + * and fqi.dir_snode. These pointers will not be used after the caller + * is done with them and should be released immediately. (The position + * of smb_fqi in a union in the smb_request structure makes it difficult + * to free these pointers at smb_request deallocation time.) + * + * If smbd_fs_query() returns error, no smb_nodes will need to be released + * by callers as a result of references taken in this routine, and + * fqi.last_snode and fqi.dir_snode will be set to NULL. + */ + +int +smbd_fs_query(struct smb_request *sr, struct smb_fqi *fqi, int fqm) +{ + int rc; + + fqi->last_comp_was_found = 0; + + rc = smb_pathname_reduce(sr, sr->user_cr, fqi->path, + sr->tid_tree->t_snode, sr->tid_tree->t_snode, &fqi->dir_snode, + fqi->last_comp); + + if (rc) + return (rc); + + rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, + sr->tid_tree->t_snode, fqi->dir_snode, fqi->last_comp, + &fqi->last_snode, &fqi->last_attr, 0, 0); + + if (rc == 0) { + fqi->last_comp_was_found = 1; + (void) strcpy(fqi->last_comp_od, + fqi->last_snode->od_name); + + if (fqm == FQM_PATH_MUST_NOT_EXIST) { + smb_node_release(fqi->dir_snode); + smb_node_release(fqi->last_snode); + SMB_NULL_FQI_NODES(*fqi); + return (EEXIST); + } + + return (0); + } + + if (fqm == FQM_PATH_MUST_EXIST) { + smb_node_release(fqi->dir_snode); + SMB_NULL_FQI_NODES(*fqi); + return (rc); + } + + if (rc == ENOENT) { + fqi->last_snode = NULL; + return (0); + } + + smb_node_release(fqi->dir_snode); + SMB_NULL_FQI_NODES(*fqi); + + return (rc); +} + +/* + * smb_pathname_reduce + * + * smb_pathname_reduce() takes a path and returns the smb_node for the + * second-to-last component of the path. It also returns the name of the last + * component. Pointers for both of these fields must be supplied by the caller. + * + * Upon success, 0 is returned. + * + * Upon error, *dir_node will be set to 0. + * + * *sr (in) + * --- + * smb_request structure pointer + * + * *cred (in) + * ----- + * credential + * + * *path (in) + * ----- + * pathname to be looked up + * + * *share_root_node (in) + * ---------------- + * File operations which are share-relative should pass sr->tid_tree->t_snode. + * If the call is not for a share-relative operation, this parameter must be 0 + * (e.g. the call from smbsr_setup_share()). (Such callers will have path + * operations done using root_smb_node.) This parameter is used to determine + * whether mount points can be crossed. + * + * share_root_node should have at least one reference on it. This reference + * will stay intact throughout this routine. + * + * *cur_node (in) + * --------- + * The smb_node for the current directory (for relative paths). + * cur_node should have at least one reference on it. + * This reference will stay intact throughout this routine. + * + * **dir_node (out) + * ---------- + * Directory for the penultimate component of the original path. + * (Note that this is not the same as the parent directory of the ultimate + * target in the case of a link.) + * + * The directory smb_node is returned held. The caller will need to release + * the hold or otherwise make sure it will get released (e.g. in a destroy + * routine if made part of a global structure). + * + * last_component (out) + * -------------- + * The last component of the path. (This may be different from the name of any + * link target to which the last component may resolve.) + * + * + * ____________________________ + * + * The CIFS server lookup path needs to have logic equivalent to that of + * smb_fsop_lookup(), smb_vop_lookup() and other smb_vop_*() routines in the + * following areas: + * + * - non-traversal of child mounts (handled by smb_pathname_reduce) + * - unmangling (handled in smb_pathname) + * - "chroot" behavior of share root (handled by lookuppnvp) + * + * In addition, it needs to replace backslashes with forward slashes. It also + * ensures that link processing is done correctly, and that directory + * information requested by the caller is correctly returned (i.e. for paths + * with a link in the last component, the directory information of the + * link and not the target needs to be returned). + */ + +int +smb_pathname_reduce( + smb_request_t *sr, + cred_t *cred, + const char *path, + smb_node_t *share_root_node, + smb_node_t *cur_node, + smb_node_t **dir_node, + char *last_component) +{ + smb_node_t *root_node; + struct pathname ppn; + char *usepath; + int lookup_flags = FOLLOW; + int trailing_slash = 0; + int err = 0; + int len; + + ASSERT(dir_node); + ASSERT(last_component); + + *dir_node = NULL; + *last_component = '\0'; + + if (SMB_TREE_CASE_INSENSITIVE(sr)) + lookup_flags |= FIGNORECASE; + + if (path == NULL) + return (EINVAL); + + if (*path == '\0') + return (ENOENT); + + usepath = kmem_alloc(MAXPATHLEN, KM_SLEEP); + + if ((len = strlcpy(usepath, path, MAXPATHLEN)) >= MAXPATHLEN) { + kmem_free(usepath, MAXPATHLEN); + return (ENAMETOOLONG); + } + + (void) strsubst(usepath, '\\', '/'); + + if (usepath[len - 1] == '/') + trailing_slash = 1; + + (void) strcanon(usepath, "/"); + + if (share_root_node) + root_node = share_root_node; + else + root_node = smb_info.si_root_smb_node; + + if (cur_node == NULL) + cur_node = root_node; + + (void) pn_alloc(&ppn); + + if ((err = pn_set(&ppn, usepath)) != 0) { + (void) pn_free(&ppn); + kmem_free(usepath, MAXPATHLEN); + return (err); + } + + /* + * If a path does not have a trailing slash, strip off the + * last component. (We only need to return an smb_node for + * the second to last component; a name is returned for the + * last component.) + */ + + if (trailing_slash) { + (void) strlcpy(last_component, ".", MAXNAMELEN); + } else { + (void) pn_setlast(&ppn); + (void) strlcpy(last_component, ppn.pn_path, MAXNAMELEN); + ppn.pn_path[0] = '\0'; + } + + if (strcmp(ppn.pn_buf, "/") == 0) { + smb_node_ref(root_node); + *dir_node = root_node; + } else if (ppn.pn_buf[0] == '\0') { + smb_node_ref(cur_node); + *dir_node = cur_node; + } else { + err = smb_pathname(sr, ppn.pn_buf, lookup_flags, root_node, + cur_node, NULL, dir_node, cred); + } + + (void) pn_free(&ppn); + kmem_free(usepath, MAXPATHLEN); + + /* + * Prevent access to anything outside of the share root, except + * when mapping a share because that may require traversal from + * / to a mounted file system. share_root_node is NULL when + * mapping a share. + * + * Note that we disregard whether the traversal of the path went + * outside of the file system and then came back (say via a link). + */ + + if ((err == 0) && share_root_node) { + if (share_root_node->vp->v_vfsp != (*dir_node)->vp->v_vfsp) + err = EACCES; + } + + if (err) { + if (*dir_node) { + (void) smb_node_release(*dir_node); + *dir_node = NULL; + } + *last_component = 0; + } + + return (err); +} + +/* + * smb_pathname() - wrapper to lookuppnvp(). Handles name unmangling. + * + * *dir_node is the true directory of the target *node. + * + * If any component but the last in the path is not found, ENOTDIR instead of + * ENOENT will be returned. + * + * Path components are processed one at a time so that smb_nodes can be + * created for each component. This allows the dir_snode field in the + * smb_node to be properly populated. + * + * Mangle checking is also done on each component. + */ + +int +smb_pathname( + smb_request_t *sr, + char *path, + int flags, + smb_node_t *root_node, + smb_node_t *cur_node, + smb_node_t **dir_node, + smb_node_t **ret_node, + cred_t *cred) +{ + char *component = NULL; + char *real_name = NULL; + char *namep; + struct pathname pn; + struct pathname rpn; + struct pathname upn; + struct pathname link_pn; + smb_node_t *dnode = NULL; + smb_node_t *fnode = NULL; + vnode_t *rootvp; + vnode_t *dvp; + vnode_t *vp = NULL; + smb_attr_t attr; + size_t pathleft; + int err = 0; + int nlink = 0; + int local_flags; + + if (path == NULL) + return (EINVAL); + + ASSERT(root_node); + ASSERT(cur_node); + ASSERT(ret_node); + + *ret_node = NULL; + + if (dir_node) + *dir_node = NULL; + + (void) pn_alloc(&upn); + + if ((err = pn_set(&upn, path)) != 0) { + (void) pn_free(&upn); + return (err); + } + + (void) pn_alloc(&pn); + (void) pn_alloc(&rpn); + + component = kmem_alloc(MAXNAMELEN, KM_SLEEP); + real_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + dnode = cur_node; + smb_node_ref(dnode); + + rootvp = (vnode_t *)root_node->vp; + + /* + * Instead of passing the FOLLOW flag to lookuppnvp(), process links in + * this routine. This allows smb_nodes to be created for each component + * of a link. + */ + local_flags = flags & FIGNORECASE; + + /* + * Path components are processed one at a time so that smb_nodes + * can be created for each component. This allows the dir_snode + * field in the smb_node to be properly populated. + * + * Mangle checking is also done on each component. + */ + while ((pathleft = pn_pathleft(&upn)) != 0) { + if (fnode) { + smb_node_release(dnode); + dnode = fnode; + fnode = NULL; + } + + if ((err = pn_getcomponent(&upn, component)) != 0) + break; + + if (smb_maybe_mangled_name(component)) { + if ((err = smb_unmangle_name(sr, cred, dnode, + component, real_name, MAXNAMELEN, 0, 0, + 1)) != 0) + break; + + namep = real_name; + } else { + namep = component; + } + + if ((err = pn_set(&pn, namep)) != 0) + break; + + /* + * Holds on dvp and rootvp (if not rootdir) are + * required by lookuppnvp() and will be released within + * that routine. + */ + vp = NULL; + dvp = dnode->vp; + + VN_HOLD(dvp); + if (rootvp != rootdir) + VN_HOLD(rootvp); + + err = lookuppnvp(&pn, &rpn, local_flags, NULL, &vp, rootvp, dvp, + cred); + + if (err) + break; + + if ((vp->v_type == VLNK) && + ((flags & FOLLOW) || pn_pathleft(&upn))) { + + if (++nlink > MAXSYMLINKS) { + err = ELOOP; + break; + } + + (void) pn_alloc(&link_pn); + err = pn_getsymlink(vp, &link_pn, cred); + + if (err) { + (void) pn_free(&link_pn); + break; + } + + if (pn_pathleft(&link_pn) == 0) + (void) pn_set(&link_pn, "."); + err = pn_insert(&upn, &link_pn, strlen(namep)); + pn_free(&link_pn); + + if (err) + break; + + if (upn.pn_pathlen == 0) { + err = ENOENT; + break; + } + + if (upn.pn_path[0] == '/') { + fnode = root_node; + smb_node_ref(fnode); + } + + if (pn_fixslash(&upn)) + flags |= FOLLOW; + + } else { + if (flags & FIGNORECASE) { + if (strcmp(rpn.pn_path, "/") != 0) + pn_setlast(&rpn); + + namep = rpn.pn_path; + } else + namep = pn.pn_path; + + fnode = smb_node_lookup(sr, NULL, cred, vp, namep, + dnode, NULL, &attr); + + if (fnode == NULL) { + err = ENOMEM; + break; + } + } + + while (upn.pn_path[0] == '/') { + upn.pn_path++; + upn.pn_pathlen--; + } + } + + /* + * Since no parent vp was passed to lookuppnvp(), all + * ENOENT errors are returned as ENOENT + */ + + if ((pathleft) && (err == ENOENT)) + err = ENOTDIR; + + if (err) { + if (fnode) + smb_node_release(fnode); + if (dnode) + smb_node_release(dnode); + } else { + *ret_node = fnode; + + if (dir_node) + *dir_node = dnode; + else + smb_node_release(dnode); + } + + kmem_free(component, MAXNAMELEN); + kmem_free(real_name, MAXNAMELEN); + (void) pn_free(&pn); + (void) pn_free(&rpn); + (void) pn_free(&upn); + + return (err); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_print.c b/usr/src/uts/common/fs/smbsrv/smb_print.c new file mode 100644 index 000000000000..13f3746b0659 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_print.c @@ -0,0 +1,253 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB print interface. + */ + +#include + + +/* + * smb_com_open_print_file + * + * This message is sent to create a new printer file which will be deleted + * once it has been closed and printed. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 2 + * USHORT SetupLength; Length of printer setup data + * USHORT Mode; 0 = Text mode (DOS expands TABs) + * 1 = Graphics mode + * USHORT ByteCount; Count of data bytes; min = 2 + * UCHAR BufferFormat; 0x04 + * STRING IdentifierString[]; Identifier string + * + * Tid in the SMB header must refer to a printer resource type. + * + * SetupLength is the number of bytes in the first part of the resulting + * print spool file which contains printer-specific control strings. + * + * Mode can have the following values: + * + * 0 Text mode. The server may optionally + * expand tabs to a series of spaces. + * 1 Graphics mode. No conversion of data + * should be done by the server. + * + * IdentifierString can be used by the server to provide some sort of per- + * client identifying component to the print file. + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 1 + * USHORT Fid; File handle + * USHORT ByteCount; Count of data bytes = 0 + * + * Fid is the returned handle which may be used by subsequent write and + * close operations. When the file is finally closed, it will be sent to + * the spooler and printed. + * + * 4.5.1.1 Errors + * + * ERRDOS/ERRnoaccess + * ERRDOS/ERRnofids + * ERRSRV/ERRinvdevice + * ERRSRV/ERRbaduid + * ERRSRV/ERRqfull + * ERRSRV/ERRqtoobig + */ +int /*ARGSUSED*/ +smb_com_open_print_file(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} + + +/* + * smb_com_close_print_file + * + * + * This message invalidates the specified file handle and queues the file + * for printing. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 1 + * USHORT Fid; File handle + * USHORT ByteCount; Count of data bytes = 0 + * + * Fid refers to a file previously created with SMB_COM_OPEN_PRINT_FILE. + * On successful completion of this request, the file is queued for + * printing by the server. + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes = 0 + * + * Servers which negotiate dialects of LANMAN1.0 and newer allow all the + * other types of Fid closing requests to invalidate the Fid and begin + * spooling. + */ +int /*ARGSUSED*/ +smb_com_close_print_file(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} + + +/* + * smb_com_get_print_queue + * + * This message obtains a list of the elements currently in the print queue + * on the server. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 2 + * USHORT MaxCount; Max number of entries to return + * USHORT StartIndex; First queue entry to return + * USHORT ByteCount; Count of data bytes = 0 + * + * StartIndex specifies the first entry in the queue to return. + * + * MaxCount specifies the maximum number of entries to return, this may be + * a positive or negative number. A positive number requests a forward + * search, a negative number indicates a backward search. + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 2 + * USHORT Count; Number of entries returned + * USHORT RestartIndex; Index of entry after last + * returned + * USHORT ByteCount; Count of data bytes; min = 3 + * UCHAR BufferFormat; 0x01 -- Data block + * USHORT DataLength; Length of data + * UCHAR Data[]; Queue elements + * + * Count indicates how many entries were actually returned. RestartIndex + * is the index of the entry following the last entry returned; it may be + * used as the StartIndex in a subsequent request to resume the queue + * listing. + * + * The format of each returned queue element is: + * + * Queue Element Member Description + * ================================ =================================== + * + * SMB_DATE FileDate; Date file was queued + * SMB_TIME FileTime; Time file was queued + * UCHAR Status; Entry status. One of: + * 01 = held or stopped + * 02 = printing + * 03 = awaiting print + * 04 = in intercept + * 05 = file had error + * 06 = printer error + * 07-FF = reserved + * USHORT SpoolFileNumber; Assigned by the spooler + * ULONG SpoolFileSize; Number of bytes in spool file + * UCHAR Reserved; + * UCHAR SpoolFileName[16]; Client which created the spool file + * + * SMB_COM_GET_PRINT_QUEUE will return less than the requested number of + * elements only when the top or end of the queue is encountered. + * + * Support for this SMB is server optional. In particular, no current + * Microsoft client software issues this request. + * + * 4.5.2.1 Errors + * + * ERRHRD/ERRnotready + * ERRHRD/ERRerror + * ERRSRV/ERRbaduid + */ +int +smb_com_get_print_queue(struct smb_request *sr) +{ + unsigned short max_count, start_ix; + + if (smbsr_decode_vwv(sr, "ww", &max_count, &start_ix) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + smbsr_encode_result(sr, 2, 3, "bwwwbw", 2, 0, 0, 3, 1, 0); + return (SDRC_NORMAL_REPLY); +} + + +/* + * smb_com_write_print_file + * + * This message is sent to write bytes into a print spool file. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 1 + * USHORT Fid; File handle + * USHORT ByteCount; Count of data bytes; min = 4 + * UCHAR BufferFormat; 0x01 -- Data block + * USHORT DataLength; Length of data + * UCHAR Data[]; Data + * + * Fid indicates the print spool file to be written, it must refer to a + * print spool file. + * + * ByteCount specifies the number of bytes to be written, and must be less + * than MaxBufferSize for the Tid specified. + * + * Data contains the bytes to append to the print spool file. The first + * SetupLength bytes in the resulting print spool file contain printer + * setup data. SetupLength is specified in the SMB_COM_OPEN_PRINT_FILE SMB + * request. + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes = 0 + * + * Servers which negotiate a protocol dialect of LANMAN1.0 or later also + * support the application of normal write requests to print spool files. + * + */ +int /*ARGSUSED*/ +smb_com_write_print_file(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_process_exit.c b/usr/src/uts/common/fs/smbsrv/smb_process_exit.c new file mode 100644 index 000000000000..40d49ea17f2e --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_process_exit.c @@ -0,0 +1,84 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: process_exit + * + * This command informs the server that a client process has terminated. + * The server must close all files opened by Pid in the SMB header. This + * must automatically release all locks the process holds. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes = 0 + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes = 0 + * + * This SMB should not generate any errors from the server, unless the + * server is a user mode server and Uid in the SMB header is invalid. + * + * Clients are not required to send this SMB, they can do all cleanup + * necessary by sending close SMBs to the server to release resources. In + * fact, clients who have negotiated LANMAN 1.0 and later probably do not + * send this message at all. + */ + +#include + +int +smb_com_process_exit(struct smb_request *sr) +{ + sr->uid_user = smb_user_lookup_by_uid(sr->session, &sr->user_cr, + sr->smb_uid); + if (sr->uid_user == NULL) { + smbsr_encode_empty_result(sr); + return (SDRC_NORMAL_REPLY); + } + + /* + * If request has a valid tree ID, only look for the PID within + * that tree. Otherwise look in all the trees. smbtorture seems + * to be the only thing that sends this request these days and + * it doesn't provide a TID. + */ + sr->tid_tree = smb_tree_lookup_by_tid(sr->uid_user, sr->smb_tid); + if (sr->tid_tree != NULL) { + smb_ofile_close_all_by_pid(sr->tid_tree, sr->smb_pid); + smb_odir_close_all_by_pid(sr->tid_tree, sr->smb_pid); + } else { + smb_tree_close_all_by_pid(sr->uid_user, sr->smb_pid); + } + + smbsr_encode_empty_result(sr); + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_query_information.c b/usr/src/uts/common/fs/smbsrv/smb_query_information.c new file mode 100644 index 000000000000..8cb636aaa98b --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_query_information.c @@ -0,0 +1,128 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: query_information + * + * This request is sent to obtain information about a file. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes; min = 2 + * UCHAR BufferFormat; 0x04 + * STRING FileName[]; File name + * + * FileName is the fully qualified name of the file relative to the Tid in + * the header. + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 10 + * USHORT FileAttributes; + * UTIME LastWriteTime; Time of last write + * ULONG FileSize; File size + * USHORT Reserved [5]; Reserved - client should ignore + * USHORT ByteCount; Count of data bytes = 0 + * + * FileAttributes are as described in the "Attributes Encoding" section of + * this document. + * + * Note that FileSize is limited to 32 bits, this request is inappropriate + * for files whose size is too large. + * + * NOTES: + * Some clients send a NULL file name. Right now we return ERRbadfile + * until we find out what a MS client would send... + */ + +#include +#include + +int +smb_com_query_information(struct smb_request *sr) +{ + int rc; + unsigned short dattr; + uint32_t write_time, file_size; + char *path; + struct smb_node *dir_node; + struct smb_node *node; + smb_attr_t attr; + char *name; + timestruc_t *mtime; + + name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + if (smbsr_decode_data(sr, "%S", sr, &path) != 0) { + kmem_free(name, MAXNAMELEN); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + /* + * Some MS clients pass NULL file names + * NT interprets this as "\" + */ + if (strlen(path) == 0) path = "\\"; + + if ((rc = smb_pathname_reduce(sr, sr->user_cr, path, + sr->tid_tree->t_snode, sr->tid_tree->t_snode, &dir_node, name)) + != 0) { + kmem_free(name, MAXNAMELEN); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + if ((rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, + sr->tid_tree->t_snode, dir_node, name, &node, &attr, 0, 0)) != 0) { + smb_node_release(dir_node); + kmem_free(name, MAXNAMELEN); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + smb_node_release(dir_node); + + dattr = smb_node_get_dosattr(node); + mtime = smb_node_get_mtime(node); + write_time = smb_gmt_to_local_time(mtime->tv_sec); + file_size = (uint32_t)smb_node_get_size(node, &node->attr); + + smb_node_release(node); + + smbsr_encode_result(sr, 10, 0, "bwll10.w", + 10, /* wct */ + dattr, + write_time, /* Last write time */ + file_size, /* FileSize */ + 0); /* bcc */ + + kmem_free(name, MAXNAMELEN); + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_query_information2.c b/usr/src/uts/common/fs/smbsrv/smb_query_information2.c new file mode 100644 index 000000000000..a1324aa74e80 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_query_information2.c @@ -0,0 +1,111 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: query_information2 + * + * This SMB is gets information about the file represented by Fid. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 2 + * USHORT Fid; File handle + * USHORT ByteCount; Count of data bytes = 0 + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 11 + * SMB_DATE CreationDate; + * SMB_TIME CreationTime; + * SMB_DATE LastAccessDate; + * SMB_TIME LastAccessTime; + * SMB_DATE LastWriteDate; + * SMB_TIME LastWriteTime; + * ULONG FileDataSize; File end of data + * ULONG FileAllocationSize; File allocation size + * USHORT FileAttributes; + * USHORT ByteCount; Count of data bytes; min = 0 + * + * The file being interrogated is specified by Fid, which must possess at + * least read permission. + * + * FileAttributes are described in the "File Attribute Encoding" section + * elsewhere in this document. + */ + +#include + +int +smb_com_query_information2(struct smb_request *sr) +{ + smb_node_t *node; + smb_attr_t *attr; + uint32_t dsize, dasize; + unsigned short dattr; + + if (smbsr_decode_vwv(sr, "w", &sr->smb_fid) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + + if (sr->fid_ofile->f_ftype != SMB_FTYPE_DISK) { + cmn_err(CE_NOTE, "SmbQueryInfo2: access denied"); + smbsr_raise_error(sr, ERRDOS, ERRnoaccess); + /* NOTREACHED */ + } + + node = sr->fid_ofile->f_node; + attr = &node->attr; + + dattr = smb_node_get_dosattr(node); + dasize = attr->sa_vattr.va_blksize * attr->sa_vattr.va_nblocks; + dsize = (dattr & SMB_FA_DIRECTORY) ? 0 : attr->sa_vattr.va_size; + + smbsr_encode_result(sr, 11, 0, "byyyllww", + 11, /* wct */ + smb_gmt_to_local_time(attr->sa_crtime.tv_sec), + /* LastAccessTime */ + smb_gmt_to_local_time(attr->sa_vattr.va_atime.tv_sec), + /* LastWriteTime */ + smb_gmt_to_local_time(attr->sa_vattr.va_mtime.tv_sec), + dsize, + dasize, + dattr, /* FileAttributes */ + 0); /* bcc */ + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_query_information_disk.c b/usr/src/uts/common/fs/smbsrv/smb_query_information_disk.c new file mode 100644 index 000000000000..4c69d8516ed7 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_query_information_disk.c @@ -0,0 +1,129 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: query_information_disk + * + * The SMB_COM_QUERY_INFORMATION_DISK command is used to determine the + * capacity and remaining free space on the drive hosting the directory + * structure indicated by Tid in the SMB header. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes = 0 + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 5 + * USHORT TotalUnits; Total allocation units per server + * USHORT BlocksPerUnit; Blocks per allocation unit + * USHORT BlockSize; Block size (in bytes) + * USHORT FreeUnits; Number of free units + * USHORT Reserved; Reserved (client should ignore) + * USHORT ByteCount; Count of data bytes = 0 + * + * The blocking/allocation units used in this response may be independent + * of the actual physical or logical blocking/allocation algorithm(s) used + * internally by the server. However, they must accurately reflect the + * amount of space on the server. + * + * This SMB only returns 16 bits of information for each field, which may + * not be large enough for some disk systems. In particular TotalUnits is + * commonly > 64K. Fortunately, it turns out the all the client cares + * about is the total disk size, in bytes, and the free space, in bytes. + * So, it is reasonable for a server to adjust the relative values of + * BlocksPerUnit and BlockSize to accommodate. If after all adjustment, + * the numbers are still too high, the largest possible values for + * TotalUnit or FreeUnits (i.e. 0xFFFF) should be returned. + */ + +#include +#include + +int +smb_com_query_information_disk(struct smb_request *sr) +{ + int rc; + struct statvfs64 df; + fsblkcnt64_t total_blocks, free_blocks; + unsigned long block_size, unit_size; + unsigned short blocks_per_unit, bytes_per_block; + unsigned short total_units, free_units; + + if ((rc = smb_fsop_statfs(sr->user_cr, sr->tid_tree->t_snode, &df)) + != 0) + smbsr_raise_errno(sr, rc); + + unit_size = 1; + block_size = df.f_frsize; + total_blocks = df.f_blocks; + free_blocks = df.f_bavail; + + /* + * It seems that DOS clients cannot handle block sizes + * bigger than 512 KB. So we have to set the block size at + * most to 512 + */ + + while (block_size > 512) { + block_size >>= 1; + unit_size <<= 1; + } + + /* adjust blocks and sizes until they fit into a word */ + + while (total_blocks >= 0xFFFF) { + total_blocks >>= 1; + free_blocks >>= 1; + if ((unit_size <<= 1) > 0xFFFF) { + unit_size >>= 1; + total_blocks = 0xFFFF; + free_blocks <<= 1; + break; + } + } + + total_units = (total_blocks >= 0xFFFF) ? + 0xFFFF : (unsigned short)total_blocks; + free_units = (free_blocks >= 0xFFFF) ? + 0xFFFF : (unsigned short)free_blocks; + bytes_per_block = (unsigned short)block_size; + blocks_per_unit = (unsigned short)unit_size; + + smbsr_encode_result(sr, 5, 0, "bwwww2.w", + 5, + total_units, /* total_units */ + blocks_per_unit, /* blocks_per_unit */ + bytes_per_block, /* blocksize */ + free_units, /* free_units */ + 0); /* bcc */ + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_read.c b/usr/src/uts/common/fs/smbsrv/smb_read.c new file mode 100644 index 000000000000..6554c070141e --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_read.c @@ -0,0 +1,456 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include + + +typedef struct smb_read_param { + uint64_t r_offset; + uint16_t r_count; + uint16_t r_mincnt; +} smb_read_param_t; + + +int smb_common_read(struct smb_request *sr, smb_read_param_t *param); + + +/* + * Read bytes from a file or named pipe (SMB Core). + * + * The requested count specifies the number of bytes desired. Offset + * is limited to 32 bits, so this client request is inappropriate for + * files with 64 bit offsets. + * + * On return, count is the number of bytes actually being returned, which + * may be less than the count requested only if a read specifies bytes + * beyond the current file size. In this case only the bytes that exist + * are returned. A read completely beyond the end of file results in a + * response of length zero. This is the only circumstance when a zero + * length response is generated. A count returned which is less than the + * count requested is the end of file indicator. + */ +int +smb_com_read(struct smb_request *sr) +{ + smb_read_param_t param; + uint32_t off_low; + uint16_t remcnt; + int rc; + + rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, + ¶m.r_count, &off_low, &remcnt); + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + param.r_offset = (uint64_t)off_low; + param.r_mincnt = 0; + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + if ((rc = smb_common_read(sr, ¶m)) != 0) { + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + smbsr_encode_result(sr, 5, VAR_BCC, "bw8.wbwC", + 5, param.r_count, VAR_BCC, 0x01, param.r_count, &sr->raw_data); + + return (SDRC_NORMAL_REPLY); +} + +/* + * Lock and read bytes from a file (SMB Core Plus). The SmbLockAndRead/ + * SmbLockAndWrite sub-dialect is only valid on disk files: reject any + * attempt to use it on non-disk shares. + * + * The requested count specifies the number of bytes desired. Offset + * specifies the offset in the file of the first byte to be locked then + * read. Note that offset is limited to 32 bits, so this client request + * is inappropriate for files with 64 bit offsets. + * + * As with SMB_LOCK_BYTE_RANGE request, if the lock cannot be granted + * immediately an error should be returned to the client. If an error + * occurs on the lock, the bytes should not be read. + * + * On return, count is the number of bytes actually being returned, which + * may be less than the count requested only if a read specifies bytes + * beyond the current file size. In this case only the bytes that exist + * are returned. A read completely beyond the end of file results in a + * response of length zero. This is the only circumstance when a zero + * length response is generated. A count returned which is less than the + * count requested is the end of file indicator. + */ +int +smb_com_lock_and_read(struct smb_request *sr) +{ + smb_read_param_t param; + uint16_t remcnt; + uint32_t off_low; + DWORD result; + int rc; + + if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) { + smbsr_raise_error(sr, ERRDOS, ERRnoaccess); + /* NOTREACHED */ + } + + rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, + ¶m.r_count, &off_low, &remcnt); + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + param.r_offset = (uint64_t)off_low; + param.r_mincnt = 0; + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + result = smb_lock_range(sr, sr->fid_ofile, param.r_offset, + (uint64_t)param.r_count, 0xffffffff, SMB_LOCK_TYPE_READWRITE); + if (result != NT_STATUS_SUCCESS) { + smb_lock_range_raise_error(sr, result); + } + + if ((rc = smb_common_read(sr, ¶m)) != 0) { + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + smbsr_encode_result(sr, 5, VAR_BCC, "bw8.wbwC", + 5, param.r_count, VAR_BCC, 0x1, param.r_count, &sr->raw_data); + + return (SDRC_NORMAL_REPLY); +} + +/* + * The SMB_COM_READ_RAW protocol is a negotiated option introduced in + * SMB Core Plus to maximize performance when reading a large block + * of data from a server. This request was extended in LM 0.12 to + * support 64-bit offsets; the server can indicate support by setting + * CAP_LARGE_FILES in the negotiated capabilities. + * + * The client must guarantee that there is (and will be) no other request + * to the server for the duration of the SMB_COM_READ_RAW, since the + * server response has no header or trailer. To help ensure that there + * are no interruptions, we block all I/O for the session during read raw. + * + * If this is the first SMB request received since we sent an oplock break + * to this client, we don't know if it's safe to send the raw data because + * the requests may have crossed on the wire and the client may have + * interpreted the oplock break as part of the raw data. To avoid problems, + * we send a zero length session packet, which will force the client to + * retry the read. + * + * Read errors are handled by sending a zero length response. + */ +int +smb_com_read_raw(struct smb_request *sr) +{ + smb_read_param_t param; + smb_node_t *node; + uint32_t off_low; + uint32_t off_high; + uint32_t timeout; + int rc; + + switch (sr->session->s_state) { + case SMB_SESSION_STATE_NEGOTIATED: + if (sr->smb_wct == 8) { + rc = smbsr_decode_vwv(sr, "wlwwl2.", &sr->smb_fid, + &off_low, ¶m.r_count, ¶m.r_mincnt, + &timeout); + param.r_offset = (uint64_t)off_low; + } else { + rc = smbsr_decode_vwv(sr, "wlwwl2.l", &sr->smb_fid, + &off_low, ¶m.r_count, ¶m.r_mincnt, &timeout, + &off_high); + param.r_offset = ((uint64_t)off_high << 32) | off_low; + } + + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, + sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + rc = smb_common_read(sr, ¶m); + /* + * XXX Do we need to handle errors here? What if we have an + * access error (either permissions or range lock violations? + */ + if (STYPE_ISDSK(sr->tid_tree->t_res_type)) { + node = sr->fid_ofile->f_node; + if (node->n_oplock.op_flags & OPLOCK_FLAG_BREAKING) { + rc = EAGAIN; + } + } + + if (rc != 0) { + (void) smb_session_send(sr->session, 0, NULL); + m_freem(sr->raw_data.chain); + sr->raw_data.chain = 0; + } else { + (void) smb_session_send(sr->session, 0, &sr->raw_data); + } + return (SDRC_NO_REPLY); + + case SMB_SESSION_STATE_OPLOCK_BREAKING: + (void) smb_session_send(sr->session, 0, NULL); + sr->session->s_state = SMB_SESSION_STATE_NEGOTIATED; + return (SDRC_NO_REPLY); + + case SMB_SESSION_STATE_WRITE_RAW_ACTIVE: + ASSERT(0); + return (SDRC_DROP_VC); + + case SMB_SESSION_STATE_TERMINATED: + ASSERT(0); + return (SDRC_NO_REPLY); + + case SMB_SESSION_STATE_DISCONNECTED: + return (SDRC_NO_REPLY); + + case SMB_SESSION_STATE_CONNECTED: + case SMB_SESSION_STATE_ESTABLISHED: + default: + ASSERT(0); + return (SDRC_DROP_VC); + } +} + +/* + * Read bytes from a file (SMB Core). This request was extended in + * LM 0.12 to support 64-bit offsets, indicated by sending a wct of + * 12 and including additional offset information. + */ +int +smb_com_read_andx(struct smb_request *sr) +{ + smb_read_param_t param; + uint32_t off_low; + uint32_t off_high; + uint16_t remcnt; + uint16_t offset2; + uint8_t secondary; + int rc; + + if (sr->smb_wct == 12) { + rc = smbsr_decode_vwv(sr, "b3.wlw6.wl", &secondary, + &sr->smb_fid, &off_low, ¶m.r_count, &remcnt, &off_high); + + param.r_offset = ((uint64_t)off_high << 32) | off_low; + } else { + rc = smbsr_decode_vwv(sr, "b3.wlw6.w", &secondary, + &sr->smb_fid, &off_low, ¶m.r_count, &remcnt); + + param.r_offset = (uint64_t)off_low; + } + + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + param.r_mincnt = 0; + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + if ((rc = smb_common_read(sr, ¶m)) != 0) { + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + /* + * Ensure that the next response offset is zero + * if there is no secondary command. + */ + offset2 = (secondary == 0xFF) ? 0 : param.r_count + 59; + + /* + * The STYPE_IPC response format is different. + * The unknown value (2) may be to indicate that it + * is a follow-up to an earlier RPC transaction. + */ + if (STYPE_ISIPC(sr->tid_tree->t_res_type)) { + smbsr_encode_result(sr, 12, VAR_BCC, "bb1.ww4.ww10.wbC", + 12, /* wct */ + secondary, /* Secondary andx command */ + offset2, /* offset to next */ + 0, /* must be 0 */ + param.r_count, /* data byte count */ + 60, /* Offset from start to data */ + VAR_BCC, /* BCC marker */ + 0x02, /* unknown */ + &sr->raw_data); + } else { + smbsr_encode_result(sr, 12, VAR_BCC, "bb1.ww4.ww10.wC", + 12, /* wct */ + secondary, /* Secondary andx command */ + offset2, /* offset to next */ + -1, /* must be -1 */ + param.r_count, /* data byte count */ + 59, /* Offset from start to data */ + VAR_BCC, /* BCC marker */ + &sr->raw_data); + } + + return (SDRC_NORMAL_REPLY); +} + +/* + * Common function for reading files or IPC/MSRPC named pipes. All + * protocol read functions should lookup the fid before calling this + * function. We can't move the fid lookup here because lock-and-read + * requires the fid to do locking before attempting the read. + * + * Returns errno values. + */ +int +smb_common_read(struct smb_request *sr, smb_read_param_t *param) +{ + smb_ofile_t *ofile = sr->fid_ofile; + smb_node_t *node; + struct vardata_block *vdb; + struct mbuf *top; + int rc; + + vdb = kmem_alloc(sizeof (struct vardata_block), KM_SLEEP); + vdb->tag = 0; + vdb->uio.uio_iov = &vdb->iovec[0]; + vdb->uio.uio_iovcnt = MAX_IOVEC; + vdb->uio.uio_resid = param->r_count; + vdb->uio.uio_offset = param->r_offset; + vdb->uio.uio_segflg = UIO_SYSSPACE; + + switch (sr->tid_tree->t_res_type & STYPE_MASK) { + case STYPE_DISKTREE: + node = ofile->f_node; + + if (node->attr.sa_vattr.va_type != VDIR) { + rc = smb_lock_range_access(sr, node, param->r_offset, + param->r_count, FILE_READ_DATA); + if (rc != NT_STATUS_SUCCESS) { + rc = ERANGE; + break; + } + } + + (void) smb_sync_fsattr(sr, sr->user_cr, node); + + sr->raw_data.max_bytes = vdb->uio.uio_resid; + top = smb_mbuf_allocate(&vdb->uio); + + rc = smb_fsop_read(sr, sr->user_cr, node, &vdb->uio, + &node->attr); + + sr->raw_data.max_bytes -= vdb->uio.uio_resid; + smb_mbuf_trim(top, sr->raw_data.max_bytes); + MBC_ATTACH_MBUF(&sr->raw_data, top); + break; + + case STYPE_IPC: + rc = smb_rpc_read(sr, &vdb->uio); + break; + + default: + rc = EACCES; + break; + } + + param->r_count -= vdb->uio.uio_resid; + kmem_free(vdb, sizeof (struct vardata_block)); + + if (rc != 0) + return (rc); + + if (param->r_mincnt != 0 && param->r_count < param->r_mincnt) { + /* + * mincnt is only used by read-raw and is typically + * zero. If mincnt is greater than zero and the + * number of bytes read is less than mincnt, tell + * the client that we read nothing. + */ + param->r_count = 0; + } + + param->r_offset += param->r_count; + mutex_enter(&sr->fid_ofile->f_mutex); + ofile->f_seek_pos = param->r_offset; + mutex_exit(&sr->fid_ofile->f_mutex); + return (rc); +} + +/* + * The Read Block Multiplexed protocol is used to maximize performance + * when reading a large block of data from server to client while still + * allowing other operations to take place between the client and server + * in parallel. + * + * The mpx sub protocol is not supported because we support only + * connection oriented transports and NT supports SMB_COM_READ_MPX + * only over connectionless transports. + */ +/*ARGSUSED*/ +int +smb_com_read_mpx(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} + +/*ARGSUSED*/ +int +smb_com_read_mpx_secondary(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_rename.c b/usr/src/uts/common/fs/smbsrv/smb_rename.c new file mode 100644 index 000000000000..f22e7187de54 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_rename.c @@ -0,0 +1,347 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include + +static int smb_do_rename(struct smb_request *sr, + struct smb_fqi *src_fqi, + struct smb_fqi *dst_fqi); + + +/* + * smb_com_rename + * + * Rename a file. Files OldFileName must exist and NewFileName must not. + * Both pathnames must be relative to the Tid specified in the request. + * Open files may be renamed. + * + * Multiple files may be renamed in response to a single request as Rename + * File supports wildcards in the file name (last component of the path). + * NOTE: we don't support rename with wildcards. + * + * SearchAttributes indicates the attributes that the target file(s) must + * have. If SearchAttributes is zero then only normal files are renamed. + * If the system file or hidden attributes are specified then the rename + * is inclusive - both the specified type(s) of files and normal files are + * renamed. The encoding of SearchAttributes is described in section 3.10 + * - File Attribute Encoding. + */ +int +smb_com_rename(struct smb_request *sr) +{ + static kmutex_t mutex; + struct smb_fqi *src_fqi; + struct smb_fqi *dst_fqi; + struct smb_node *dst_node; + int rc; + + if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + src_fqi = &sr->arg.dirop.fqi; + dst_fqi = &sr->arg.dirop.dst_fqi; + + if (smbsr_decode_vwv(sr, "w", &src_fqi->srch_attr) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + rc = smbsr_decode_data(sr, "%SS", sr, &src_fqi->path, &dst_fqi->path); + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + dst_fqi->srch_attr = 0; + + mutex_enter(&mutex); + rc = smb_do_rename(sr, src_fqi, dst_fqi); + mutex_exit(&mutex); + + if (rc != 0) { + /* + * ERROR_FILE_EXISTS doesn't work for Windows98 clients. + * + * Windows95 clients don't see this problem because the target + * is deleted before the rename request. + * + * The following values are based on observed WFWG, Win9x, + * NT and W2K client behaviour. + */ + if (rc == EEXIST) { + smbsr_raise_cifs_error(sr, + NT_STATUS_OBJECT_NAME_COLLISION, + ERRDOS, ERROR_ALREADY_EXISTS); + /* NOTREACHED */ + } + + if (rc == EPIPE) { + smbsr_raise_cifs_error(sr, NT_STATUS_SHARING_VIOLATION, + ERRDOS, ERROR_SHARING_VIOLATION); + /* NOTREACHED */ + } + + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + if (src_fqi->dir_snode) + smb_node_release(src_fqi->dir_snode); + + dst_node = dst_fqi->dir_snode; + if (dst_node) { + if (dst_node->flags & NODE_FLAGS_NOTIFY_CHANGE) { + dst_node->flags |= NODE_FLAGS_CHANGED; + smb_process_node_notify_change_queue(dst_node); + } + smb_node_release(dst_node); + } + + SMB_NULL_FQI_NODES(*src_fqi); + SMB_NULL_FQI_NODES(*dst_fqi); + + smbsr_encode_empty_result(sr); + + return (SDRC_NORMAL_REPLY); +} + +/* + * smb_rename_share_check + * + * An open file can be renamed if + * + * 1. isn't opened for data writing or deleting + * + * 2. Opened with "Deny Delete" share mode + * But not opened for data reading or executing + * (opened for accessing meta data) + */ +DWORD +smb_rename_share_check(struct smb_node *node) +{ + struct smb_ofile *open; + + if (node == 0 || node->n_refcnt <= 1) + return (NT_STATUS_SUCCESS); + + smb_llist_enter(&node->n_ofile_list, RW_READER); + open = smb_llist_head(&node->n_ofile_list); + while (open) { + if (open->f_granted_access & + (FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE)) { + smb_llist_exit(&node->n_ofile_list); + return (NT_STATUS_SHARING_VIOLATION); + } + + if ((open->f_share_access & FILE_SHARE_DELETE) == 0) { + if (open->f_granted_access & + (FILE_READ_DATA | FILE_EXECUTE)) { + smb_llist_exit(&node->n_ofile_list); + return (NT_STATUS_SHARING_VIOLATION); + } + } + open = smb_llist_next(&node->n_ofile_list, open); + } + smb_llist_exit(&node->n_ofile_list); + return (NT_STATUS_SUCCESS); +} + + +/* + * smb_do_rename + * + * Backend to smb_com_rename to ensure that the rename operation is atomic. + * This function should be called within a mutual exclusion region. If the + * source and destination are identical, we don't actually do a rename, we + * just check that the conditions are right. If the source and destination + * files differ only in case, we a case-sensitive rename. Otherwise, we do + * a full case-insensitive rename. + * + * This function should always return errno values. + * + * Upon success, the last_snode's and dir_snode's of both src_fqi and dst_fqi + * are not released in this routine but in smb_com_rename(). + */ +static int +smb_do_rename( + struct smb_request *sr, + struct smb_fqi *src_fqi, + struct smb_fqi *dst_fqi) +{ + struct smb_node *src_node; + char *dstname; + DWORD status; + int rc; + int count; + + if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0) { + return (rc); + } + + src_node = src_fqi->last_snode; + + /* + * Break the oplock before access checks. If a client + * has a file open, this will force a flush or close, + * which may affect the outcome of any share checking. + */ + if (OPLOCKS_IN_FORCE(src_node)) { + status = smb_break_oplock(sr, src_node); + + if (status != NT_STATUS_SUCCESS) { + smb_node_release(src_node); + smb_node_release(src_fqi->dir_snode); + + SMB_NULL_FQI_NODES(*src_fqi); + SMB_NULL_FQI_NODES(*dst_fqi); + return (EACCES); + } + } + + status = smb_lock_range_access(sr, src_node, 0, 0, FILE_WRITE_DATA); + if (status != NT_STATUS_SUCCESS) { + smb_node_release(src_node); + smb_node_release(src_fqi->dir_snode); + + SMB_NULL_FQI_NODES(*src_fqi); + SMB_NULL_FQI_NODES(*dst_fqi); + return (EACCES); + } + + + for (count = 0; count <= 3; count++) { + if (count) + delay(MSEC_TO_TICK(400)); + status = smb_rename_share_check(src_node); + if (status != NT_STATUS_SHARING_VIOLATION) + break; + } + + smb_node_release(src_node); + + if (status == NT_STATUS_SHARING_VIOLATION) { + smb_node_release(src_fqi->dir_snode); + + SMB_NULL_FQI_NODES(*src_fqi); + SMB_NULL_FQI_NODES(*dst_fqi); + return (EPIPE); /* = ERRbadshare */ + } + + if (utf8_strcasecmp(src_fqi->path, dst_fqi->path) == 0) { + if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) { + smb_node_release(src_fqi->dir_snode); + + SMB_NULL_FQI_NODES(*src_fqi); + SMB_NULL_FQI_NODES(*dst_fqi); + return (rc); + } + + /* + * Because the fqm parameter to smbd_fs_query() was 0, + * a successful return value means that dst_fqi->last_snode + * may be NULL. + */ + if (dst_fqi->last_snode) + smb_node_release(dst_fqi->last_snode); + + rc = strcmp(src_fqi->last_comp_od, dst_fqi->last_comp); + if (rc == 0) { + smb_node_release(src_fqi->dir_snode); + smb_node_release(dst_fqi->dir_snode); + + SMB_NULL_FQI_NODES(*src_fqi); + SMB_NULL_FQI_NODES(*dst_fqi); + return (0); + } + + rc = smb_fsop_rename(sr, sr->user_cr, + src_fqi->dir_snode, + src_fqi->last_comp_od, + dst_fqi->dir_snode, + dst_fqi->last_comp); + + if (rc != 0) { + smb_node_release(src_fqi->dir_snode); + smb_node_release(dst_fqi->dir_snode); + + SMB_NULL_FQI_NODES(*src_fqi); + SMB_NULL_FQI_NODES(*dst_fqi); + } + return (rc); + } + + rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST); + if (rc != 0) { + smb_node_release(src_fqi->dir_snode); + + SMB_NULL_FQI_NODES(*src_fqi); + SMB_NULL_FQI_NODES(*dst_fqi); + return (rc); + } + + /* + * Because of FQM_PATH_MUST_NOT_EXIST and the successful return + * value, only dst_fqi->dir_snode is valid (dst_fqi->last_snode + * is NULL). + */ + + /* + * Use the unmangled form of the destination name if the + * source and destination names are the same and the source + * name is mangled. (We are taking a chance here, assuming + * that this is what the user wants.) + */ + + if ((smb_maybe_mangled_name(src_fqi->last_comp)) && + (strcmp(src_fqi->last_comp, dst_fqi->last_comp) == 0)) { + dstname = src_fqi->last_comp_od; + } else { + dstname = dst_fqi->last_comp; + } + + rc = smb_fsop_rename(sr, sr->user_cr, + src_fqi->dir_snode, + src_fqi->last_comp_od, + dst_fqi->dir_snode, + dstname); + + if (rc != 0) { + smb_node_release(src_fqi->dir_snode); + smb_node_release(dst_fqi->dir_snode); + + SMB_NULL_FQI_NODES(*src_fqi); + SMB_NULL_FQI_NODES(*dst_fqi); + } + + return (rc); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_rpc.c b/usr/src/uts/common/fs/smbsrv/smb_rpc.c new file mode 100644 index 000000000000..394c5addbe1a --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_rpc.c @@ -0,0 +1,532 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module provides a set of wrapper functions to interface to the + * RPC layer. Although this interface was originally implemented as a + * single transaction using an input buffer and an output buffer, it + * turns out that it really should have been a stream/pipe interface. + * This was first discovered when we noticed that Windows2000 was using + * smb_rpc_write and smb_rpc_read instead of smb_rpc_transact and then + * later when we tried to return a larger number of shares than would + * fit in a single transaction buffer. + * + * The interface is still limited by the buffers passed between this + * module and the RPC module but now it will support a buffer overflow + * and allow the client to read the remaining data on subsequent + * requests. Also note that the smb_rpc_write and smb_rpc_read calls + * are basically emulating the smb_rpc_transact function. + */ + +#include +#include +#include + + +/* + * This is the list of well-known RPC named pipes that we support. + * The full pipe path will be in the form \\PIPE\\SERVICE. The first + * part can be assumed, so all we need here are the service names. + */ +static char *rpc_named_pipes[] = { + "\\LSARPC", + "\\NETLOGON", + "\\SAMR", + "\\SPOOLSS", + "\\SRVSVC", + "\\SVCCTL", + "\\WINREG", + "\\WKSSVC", + "\\EVENTLOG" +}; + + +/* + * This is a list of the port addresses for the named pipes above. + * We need to check that these are correct but nothing appears to + * rely on them so this is low priority. + */ +#if 0 +static char *rpc_np_ports[] = { + "\\PIPE\\", + "\\PIPE\\lsass", + "\\PIPE\\lsass", + "\\PIPE\\spoolss", + "\\PIPE\\ntsvcs", + "\\PIPE\\ntsvcs", + "\\PIPE\\winreg", + "\\PIPE\\ntsvcs", + "\\PIPE\\ntsvcs" +}; +#endif + + +static int smb_rpc_initialize(struct smb_request *sr, char *pipe_name); +static uint32_t smb_rpc_fid(void); + + +/* + * Named pipe I/O is serialized to ensure that each request has exclusive + * access to the in and out pipe data for the duration of the request. + */ +static void +smb_rpc_enter(mlsvc_pipe_t *pi) +{ + mutex_enter(&pi->mutex); + + while (pi->busy) + cv_wait(&pi->cv, &pi->mutex); + + pi->busy = 1; + mutex_exit(&pi->mutex); +} + +static void +smb_rpc_exit(mlsvc_pipe_t *pi) +{ + mutex_enter(&pi->mutex); + pi->busy = 0; + cv_signal(&pi->cv); + mutex_exit(&pi->mutex); +} + +/* + * smb_rpc_lookup + * + * Lookup a path to see if it's a well-known RPC named pipe. + * + * Returns a pointer to the pipe name (without any leading \'s) if the path + * refers to a well-known RPC named pipe. Otherwise returns a null pointer. + */ +char * +smb_rpc_lookup(char *path) +{ + int i; + char *pipe_name; + + if (path == 0) { + cmn_err(CE_WARN, "smb_rpc_lookup: invalid parameter"); + return (0); + } + + /* + * Skip past the static part of the pipe + * name if it appears in the path. + */ + if (utf8_strncasecmp(path, "\\PIPE\\", 6) == 0) + path += 5; + + for (i = 0; + i < sizeof (rpc_named_pipes) / sizeof (rpc_named_pipes[0]); + ++i) { + if (utf8_strcasecmp(path, rpc_named_pipes[i]) == 0) { + pipe_name = rpc_named_pipes[i]; + pipe_name += strspn(pipe_name, "\\"); + + return (pipe_name); + } + } + return (0); +} + +/* + * smb_rpc_open + * + * Open a well-known RPC named pipe. This routine should be called if + * a file open is requested on a share of type STYPE_IPC. If we + * recognize the pipe, we initialize the session data. This will setup + * a new ofile and insert it into the session file list. + * + * Returns 0 on success, Otherwise an NT status is returned to indicate + * an error. + */ +int +smb_rpc_open(struct smb_request *sr) +{ + struct open_param *op; + char *pipe_name; + int status; + + if (smb_winpipe_open() != 0) + return (NT_STATUS_INTERNAL_ERROR); + + op = &sr->arg.open; + + if ((pipe_name = smb_rpc_lookup(op->fqi.path)) != 0) { + if ((status = smb_rpc_initialize(sr, pipe_name)) != 0) + return (status); + + return (NT_STATUS_SUCCESS); + } + + return (NT_STATUS_OBJECT_NAME_NOT_FOUND); +} + + +/* + * smb_rpc_initialize + * + * Initialize various parts of the session data for a named pipe: open + * parameters and the ofile. There are a number of magic numbers in + * here that we need to identify but largely these values are ignored + * by the rest of the code. Insert the ofile into the session file list. + * + * Returns 0 on success, Otherwise an NT status is returned to indicate + * an error. + */ +static int +smb_rpc_initialize(struct smb_request *sr, char *pipe_name) +{ + struct open_param *op; + struct smb_ofile *of; + smb_error_t err; + + op = &sr->arg.open; + of = smb_ofile_open(sr->tid_tree, NULL, sr->smb_pid, + op->desired_access, 0, op->share_access, + SMB_FTYPE_MESG_PIPE, pipe_name, smb_rpc_fid(), &err); + if (of == NULL) + return (err.status); + + op->dsize = 0x01000; + op->utime.tv_sec = 0; + op->utime.tv_nsec = 0; + op->dattr = SMB_FA_NORMAL; + op->ftype = SMB_FTYPE_MESG_PIPE; + op->action_taken = SMB_OACT_LOCK | SMB_OACT_OPENED; /* 0x8001 */ + op->devstate = SMB_PIPE_READMODE_MESSAGE + | SMB_PIPE_TYPE_MESSAGE + | SMB_PIPE_UNLIMITED_INSTANCES; /* 0x05ff */ + op->fileid = of->f_fid; + op->create_options = 0; + + sr->smb_fid = of->f_fid; + sr->fid_ofile = of; + return (0); +} + +/* + * smb_rpc_transact + * + * This is the entry point for RPC transactions to provide a wrapper for + * the RPC layer. The SMB decoding and encoding is handled here so that + * the RPC layer doesn't have to deal with it. Both bind operations and + * RPC requests are handled here. The connection_fid is an arbitrary id + * used to associate RPC requests with a particular binding handle. + * + * The RPC library expects the input stream to contain the request data. + * It will build the output stream. + * + * If the data to be returned is larger than the client expects, we + * return as much as the client can handle and report a buffer overflow + * warning to inform the client that we have more data to return. The + * residual data remains in the output stream until the client claims + * it or closes the pipe. + */ +int +smb_rpc_transact(struct smb_request *sr, struct uio *uio) +{ + struct smb_xa *xa; + mlsvc_pipe_t *pipe_info; + mlsvc_stream_t *streamin; + struct mbuf *mhead; + int mdrcnt; + int nbytes; + int rc; + + ASSERT(sr->fid_ofile); + ASSERT(sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE); + ASSERT(sr->fid_ofile->f_pipe_info != NULL); + + xa = sr->r_xa; + mdrcnt = xa->smb_mdrcnt; + pipe_info = sr->fid_ofile->f_pipe_info; + + smb_rpc_enter(pipe_info); + + if (pipe_info->fid == 0) { + smb_rpc_exit(pipe_info); + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERROR_INVALID_HANDLE); + /* NOTREACHED */ + } + + streamin = &pipe_info->input; + streamin->uio.uio_iov = uio->uio_iov; + streamin->uio.uio_iovcnt = uio->uio_iovcnt; + streamin->uio.uio_offset = 0; + streamin->uio.uio_resid = uio->uio_resid; + streamin->uio.uio_segflg = UIO_SYSSPACE; + + nbytes = mdrcnt; + + rc = smb_winpipe_call(sr, pipe_info, streamin, SMB_RPC_TRANSACT, + (uint32_t *)&nbytes); + + if (rc != 0) { + smb_rpc_exit(pipe_info); + smbsr_raise_nt_error(sr, + NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID); + /* NOTREACHED */ + } + + /* + * We need to zero the input stream so that we don't try to + * flush it on close: the mbuf chain belongs to the SMB XA. + * Then reassign the stream to refer to the output/response. + */ + if (nbytes > mdrcnt) { + /* + * We have more data to return than the client expects in the + * response to this request. So we send as much as the client + * can handle, mdrcnt, and store the rest in the output chain. + * The buffer overflow warning informs the client that we + * have more data to send. Typically, the client will call + * SmbRead&X, which will call smb_rpc_read, to get the data. + */ + + mhead = smb_mbuf_get(pipe_info->output, mdrcnt); + xa->rep_data_mb.max_bytes = mdrcnt; + MBC_ATTACH_MBUF(&xa->rep_data_mb, mhead); + + if (sr->session->capabilities & CAP_STATUS32) + smbsr_setup_nt_status(sr, ERROR_SEVERITY_WARNING, + NT_STATUS_BUFFER_OVERFLOW); + else { + sr->smb_rcls = ERRDOS; + sr->smb_err = ERRmoredata; + } + } else { + /* + * The client has provided enough buffer space, all + * we have to do is attach the output stream to the + * transaction response and zero out the stream. + */ + if (nbytes != 0) { + mhead = smb_mbuf_get(pipe_info->output, nbytes); + xa->rep_data_mb.max_bytes = nbytes; + MBC_ATTACH_MBUF(&xa->rep_data_mb, mhead); + } + } + + if (pipe_info->output) { + kmem_free(pipe_info->output, pipe_info->outlen); + pipe_info->output = NULL; + pipe_info->outlen = 0; + } + + smb_rpc_exit(pipe_info); + return (SDRC_NORMAL_REPLY); +} + + +/* + * smb_rpc_fid + * + * The connection_fid is an arbitrary id used to associate RPC requests + * with a particular binding handle. This routine provides a new fid on + * each call. It will not assign 0 or -1 so that those values can + * remain available as sentinels. + */ +static uint32_t +smb_rpc_fid(void) +{ + static uint32_t connection_fid; + static kmutex_t smb_rpc_fid_mutex; + + mutex_enter(&smb_rpc_fid_mutex); + + if (connection_fid == 0) + connection_fid = lbolt << 11; + + do { + ++connection_fid; + } while (connection_fid == 0 || connection_fid == (uint32_t)-1); + + mutex_exit(&smb_rpc_fid_mutex); + + return (connection_fid); +} + + +/* + * smb_rpc_close + * + * This function should be called whenever an IPC file/pipe is closed. + * All remaining I/O is flushed and the RPC layer is informed so that + * it can release the resources being used for this connection. + */ +void +smb_rpc_close(struct smb_ofile *of) +{ + mlsvc_pipe_t *pipe_info; + uint32_t nbytes = 0; + + ASSERT(of); + ASSERT(of->f_ftype == SMB_FTYPE_MESG_PIPE); + ASSERT(of->f_pipe_info != NULL); + + pipe_info = of->f_pipe_info; + smb_rpc_enter(pipe_info); + + if (pipe_info->fid != 0) { + (void) smb_winpipe_call(0, pipe_info, 0, SMB_RPC_FLUSH, + &nbytes); + pipe_info->fid = 0; + } + + if (pipe_info->output) { + kmem_free(pipe_info->output, pipe_info->outlen); + pipe_info->output = NULL; + pipe_info->outlen = 0; + } + + smb_rpc_exit(pipe_info); + + cv_destroy(&pipe_info->cv); + mutex_destroy(&pipe_info->mutex); +} + +/* + * smb_rpc_write + * + * This interface is an alternative to smb_rpc_transact. We set up the + * connection fid, as required, and copy the input data to the input + * stream. The input stream is created by allocating enough mbufs to + * hold the incoming data and doing a uio transfer. It is then up + * to the client to call smb_rpc_read to actually make the transaction + * happen. + * + * Returns 0 on success or an errno on failure. + */ +int +smb_rpc_write(struct smb_request *sr, struct uio *uio) +{ + mlsvc_pipe_t *pipe_info; + mlsvc_stream_t *streamin; + uint32_t mdrcnt; + int rc; + + ASSERT(sr->fid_ofile); + ASSERT(sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE); + ASSERT(sr->fid_ofile->f_pipe_info != NULL); + + pipe_info = sr->fid_ofile->f_pipe_info; + smb_rpc_enter(pipe_info); + + if (pipe_info->fid == 0) { + smb_rpc_exit(pipe_info); + return (EBADF); + } + + streamin = &pipe_info->input; + streamin->uio.uio_iov = uio->uio_iov; + streamin->uio.uio_iovcnt = uio->uio_iovcnt; + streamin->uio.uio_offset = 0; + streamin->uio.uio_resid = uio->uio_resid; + streamin->uio.uio_segflg = UIO_SYSSPACE; + mdrcnt = (uint32_t)uio->uio_resid; + + rc = smb_winpipe_call(sr, pipe_info, streamin, SMB_RPC_WRITE, + &mdrcnt); + + smb_rpc_exit(pipe_info); + + return ((rc == 0) ? 0 : EIO); +} + +/* + * smb_rpc_read + * + * This interface may be called because smb_rpc_transact could not return + * all of the data in the original transaction or to form the second half + * of a transaction set up using smb_rpc_write. If there is data in the + * output stream, we return it. Otherwise we assume that there is data + * in the input stream that will provide the context to perform an RPC + * transaction. The connection fid (pipe_info->fid) will provide the + * context for mlsvc_rpc_process. + * + * The response data is encoded into raw_data as required by the smb_read + * functions. The uio_resid value indicates the number of bytes read. + */ +/*ARGSUSED*/ +int +smb_rpc_read(struct smb_request *sr, struct uio *uio) +{ + mlsvc_pipe_t *pinfo; + mlsvc_stream_t *streamin; + struct mbuf *mhead; + int mdrcnt; + int nbytes; + int rc = 0; + + ASSERT(sr->fid_ofile); + ASSERT(sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE); + ASSERT(sr->fid_ofile->f_pipe_info != NULL); + + pinfo = sr->fid_ofile->f_pipe_info; + smb_rpc_enter(pinfo); + + if (pinfo->fid == 0) { + rc = EBADF; + goto smb_rpc_read_exit; + } + + /* + * if there is data left in the outpipe return it now + */ + streamin = 0; + mdrcnt = uio->uio_resid; + nbytes = mdrcnt; + + rc = smb_winpipe_call(sr, pinfo, streamin, SMB_RPC_READ, + (uint32_t *)&nbytes); + + if (rc != 0 || nbytes == 0) { + rc = EIO; + goto smb_rpc_read_exit; + } + if (nbytes > mdrcnt) { + nbytes = mdrcnt; + } + + mhead = smb_mbuf_get(pinfo->output, nbytes); + MBC_SETUP(&sr->raw_data, nbytes); + MBC_ATTACH_MBUF(&sr->raw_data, mhead); + + uio->uio_resid -= nbytes; + +smb_rpc_read_exit: + if (pinfo->output) { + kmem_free(pinfo->output, pinfo->outlen); + pinfo->output = NULL; + pinfo->outlen = 0; + } + + smb_rpc_exit(pinfo); + return (rc); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_sd.c b/usr/src/uts/common/fs/smbsrv/smb_sd.c new file mode 100644 index 000000000000..4afa258b9a97 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_sd.c @@ -0,0 +1,862 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module provides Security Descriptor handling functions. + */ + +#include +#include +#include + +#define AS_DWORD(X) (*(uint32_t *)&(X)) +#define SELF_REL(P, M, T) (T *)(((char *)(P)) + AS_DWORD((P)->M)) + +void smb_fmt_sid(char *buf, nt_sid_t *sid); + +void +smb_sd_init(smb_sd_t *sd, uint8_t revision) +{ + bzero(sd, sizeof (smb_sd_t)); + sd->sd_hdr.sd_revision = revision; +} + +/* + * smb_sd_term + * + * Free non-NULL members of 'sd' which has to be in + * absolute (pointer) form. + */ +void +smb_sd_term(smb_sd_t *sd) +{ + ASSERT(sd); + ASSERT((sd->sd_hdr.sd_control & SE_SELF_RELATIVE) == 0); + + if (sd->sd_owner) + MEM_FREE("libnt", sd->sd_owner); + + if (sd->sd_group) + MEM_FREE("libnt", sd->sd_group); + + if (sd->sd_dacl) + kmem_free(sd->sd_dacl, sd->sd_dacl->sl_size); + + if (sd->sd_sacl) + kmem_free(sd->sd_sacl, sd->sd_sacl->sl_size); + + bzero(sd, sizeof (smb_sd_t)); +} + +/* + * Hmmm. For all of these smb_sd_set_xxx() functions, + * what do we do if the affected member is already set? + * Should we free() it? For now, punt and risk a memory leak. + */ + +void +smb_sd_set_owner(smb_sd_t *sd, nt_sid_t *owner, int defaulted) +{ + ASSERT((sd->sd_hdr.sd_control & SE_SELF_RELATIVE) == 0); + + sd->sd_owner = owner; + if (defaulted) + sd->sd_hdr.sd_control |= SE_OWNER_DEFAULTED; + else + sd->sd_hdr.sd_control &= ~SE_OWNER_DEFAULTED; +} + +void +smb_sd_set_group(smb_sd_t *sd, nt_sid_t *group, int defaulted) +{ + ASSERT((sd->sd_hdr.sd_control & SE_SELF_RELATIVE) == 0); + + sd->sd_group = group; + if (defaulted) + sd->sd_hdr.sd_control |= SE_GROUP_DEFAULTED; + else + sd->sd_hdr.sd_control &= ~SE_GROUP_DEFAULTED; +} + +void +smb_sd_set_dacl(smb_sd_t *sd, int present, smb_acl_t *acl, int flags) +{ + ASSERT((sd->sd_hdr.sd_control & SE_SELF_RELATIVE) == 0); + + sd->sd_dacl = acl; + + if (flags & ACL_DEFAULTED) + sd->sd_hdr.sd_control |= SE_DACL_DEFAULTED; + if (flags & ACL_AUTO_INHERIT) + sd->sd_hdr.sd_control |= SE_DACL_AUTO_INHERITED; + if (flags & ACL_PROTECTED) + sd->sd_hdr.sd_control |= SE_DACL_PROTECTED; + + if (present) + sd->sd_hdr.sd_control |= SE_DACL_PRESENT; +} + +void +smb_sd_set_sacl(smb_sd_t *sd, int present, smb_acl_t *acl, int flags) +{ + ASSERT((sd->sd_hdr.sd_control & SE_SELF_RELATIVE) == 0); + + sd->sd_sacl = acl; + + if (flags & ACL_DEFAULTED) + sd->sd_hdr.sd_control |= SE_SACL_DEFAULTED; + if (flags & ACL_AUTO_INHERIT) + sd->sd_hdr.sd_control |= SE_SACL_AUTO_INHERITED; + if (flags & ACL_PROTECTED) + sd->sd_hdr.sd_control |= SE_SACL_PROTECTED; + + if (present) + sd->sd_hdr.sd_control |= SE_SACL_PRESENT; +} + +nt_sid_t * +smb_sd_get_owner(void *sd, int *defaulted) +{ + smb_sdbuf_t *sr_sd; + smb_sd_hdr_t *sd_hdr; + nt_sid_t *sid; + + sd_hdr = (smb_sd_hdr_t *)sd; + if (defaulted != NULL) + *defaulted = (sd_hdr->sd_control & SE_OWNER_DEFAULTED) ? 1 : 0; + + if (sd_hdr->sd_control & SE_SELF_RELATIVE) { + sr_sd = ((smb_sdbuf_t *)sd); + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + sid = SELF_REL(sr_sd, sd_owner_offs, nt_sid_t); + } + else + sid = ((smb_sd_t *)sd)->sd_owner; + + return (sid); +} + +nt_sid_t * +smb_sd_get_group(void *sd, int *defaulted) +{ + smb_sdbuf_t *sr_sd; + smb_sd_hdr_t *sd_hdr; + nt_sid_t *sid; + + sd_hdr = (smb_sd_hdr_t *)sd; + if (defaulted != NULL) + *defaulted = (sd_hdr->sd_control & SE_GROUP_DEFAULTED) ? 1 : 0; + + if (sd_hdr->sd_control & SE_SELF_RELATIVE) { + sr_sd = ((smb_sdbuf_t *)sd); + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + sid = SELF_REL(sr_sd, sd_group_offs, nt_sid_t); + } + else + sid = ((smb_sd_t *)sd)->sd_group; + + return (sid); +} + +smb_acl_t * +smb_sd_get_dacl(void *sd, int *present, int *defaulted) +{ + smb_sdbuf_t *sr_sd; + smb_sd_hdr_t *sd_hdr; + smb_acl_t *acl = NULL; + + sd_hdr = (smb_sd_hdr_t *)sd; + if (present != NULL) + *present = (sd_hdr->sd_control & SE_DACL_PRESENT) ? 1 : 0; + + if (defaulted != NULL) + *defaulted = (sd_hdr->sd_control & SE_DACL_DEFAULTED) ? 1 : 0; + + if (sd_hdr->sd_control & SE_SELF_RELATIVE) { + sr_sd = ((smb_sdbuf_t *)sd); + if (sr_sd->sd_dacl_offs) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + acl = SELF_REL(sr_sd, sd_dacl_offs, smb_acl_t); + } + } + else + acl = ((smb_sd_t *)sd)->sd_dacl; + + return (acl); +} + +smb_acl_t * +smb_sd_get_sacl(void *sd, int *present, int *defaulted) +{ + smb_sdbuf_t *sr_sd; + smb_sd_hdr_t *sd_hdr; + smb_acl_t *acl = NULL; + + sd_hdr = (smb_sd_hdr_t *)sd; + if (present != NULL) + *present = (sd_hdr->sd_control & SE_SACL_PRESENT) ? 1 : 0; + + if (defaulted != NULL) + *defaulted = (sd_hdr->sd_control & SE_SACL_DEFAULTED) ? 1 : 0; + + if (sd_hdr->sd_control & SE_SELF_RELATIVE) { + sr_sd = ((smb_sdbuf_t *)sd); + if (sr_sd->sd_sacl_offs) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + acl = SELF_REL(sr_sd, sd_sacl_offs, smb_acl_t); + } + } + else + acl = ((smb_sd_t *)sd)->sd_sacl; + + return (acl); +} + +uint32_t +smb_sd_len(void *sd, uint32_t secinfo) +{ + uint32_t length = 0; + nt_sid_t *sid; + smb_acl_t *acl; + int present; + + /* SD Header */ + length += sizeof (smb_sdbuf_t); + + /* Owner */ + if (secinfo & SMB_OWNER_SECINFO) { + sid = smb_sd_get_owner(sd, NULL); + if (sid) + length += nt_sid_length(sid); + } + + + /* Group */ + if (secinfo & SMB_GROUP_SECINFO) { + sid = smb_sd_get_group(sd, NULL); + if (sid) + length += nt_sid_length(sid); + } + + + /* DACL */ + if (secinfo & SMB_DACL_SECINFO) { + acl = smb_sd_get_dacl(sd, &present, NULL); + if (present && acl) + length += smb_acl_len(acl); + } + + /* SACL */ + if (secinfo & SMB_SACL_SECINFO) { + acl = smb_sd_get_sacl(sd, &present, NULL); + if (present && acl) + length += smb_acl_len(acl); + } + + return (length); +} + +/* + * smb_sd_get_secinfo + * + * Return the security information mask for the specified security + * descriptor. + */ +uint32_t +smb_sd_get_secinfo(void *sd) +{ + uint32_t sec_info = 0; + smb_acl_t *acl; + int present; + + if (sd == 0) + return (0); + + if (smb_sd_get_owner(sd, NULL) != 0) + sec_info |= SMB_OWNER_SECINFO; + + if (smb_sd_get_group(sd, NULL) != 0) + sec_info |= SMB_GROUP_SECINFO; + + acl = smb_sd_get_dacl(sd, &present, NULL); + if (acl && present) + sec_info |= SMB_DACL_SECINFO; + + acl = smb_sd_get_sacl(sd, &present, NULL); + if (acl && present) + sec_info |= SMB_SACL_SECINFO; + + return (sec_info); +} + +/* + * smb_sd_abs2selfrel + * + * This function takes an absolute SD (sd) and make a self relative + * SD which will be returned in srel_sd. + * + * srel_sdsz contains the size of buffer which srel_sd points to. + * + * Do not add new error codes here without checking the impact on + * all callers of this function. + * + * Returns NT status codes: + * NT_STATUS_SUCCESS + * NT_STATUS_BUFFER_TOO_SMALL + * NT_STATUS_INVALID_SECURITY_DESCR + */ +static uint32_t +smb_sd_abs2selfrel( + smb_sd_t *sd, + uint32_t secinfo, + smb_sdbuf_t *srel_sd, + uint32_t srel_sdsz) +{ + uint32_t avail_len = srel_sdsz; + uint32_t length = 0; + unsigned char *scan_beg = (unsigned char *) srel_sd; + unsigned char *scan = scan_beg; + unsigned char *scan_end; + nt_sid_t *sid; + smb_acl_t *acl; + int present, defaulted; + + length = smb_sd_len(sd, secinfo); + + if (length == 0) + return (NT_STATUS_INVALID_SECURITY_DESCR); + + if (avail_len < length) + return (NT_STATUS_BUFFER_TOO_SMALL); + + bzero(srel_sd, length); + scan_end = scan_beg + length; + + /* SD Header */ + length = sizeof (smb_sdbuf_t); + srel_sd->sd_hdr.sd_revision = sd->sd_hdr.sd_revision; + srel_sd->sd_hdr.sd_control = SE_SELF_RELATIVE; + scan += length; + + if (secinfo & SMB_OWNER_SECINFO) { + /* Owner */ + sid = smb_sd_get_owner(sd, &defaulted); + + if (defaulted) + srel_sd->sd_hdr.sd_control |= SE_OWNER_DEFAULTED; + + if (sid) { + /*LINTED E_PTRDIFF_OVERFLOW*/ + length = nt_sid_copy((void*)scan, sid, scan_end - scan); + if (length == 0) + goto fail; + /*LINTED E_PTRDIFF_OVERFLOW*/ + srel_sd->sd_owner_offs = scan - scan_beg; + scan += length; + } + } + + if (secinfo & SMB_GROUP_SECINFO) { + /* Group */ + sid = smb_sd_get_group(sd, &defaulted); + + if (defaulted) + srel_sd->sd_hdr.sd_control |= SE_GROUP_DEFAULTED; + + if (sid) { + /*LINTED E_PTRDIFF_OVERFLOW*/ + length = nt_sid_copy((void*)scan, sid, scan_end - scan); + if (length == 0) + goto fail; + /*LINTED E_PTRDIFF_OVERFLOW*/ + srel_sd->sd_group_offs = scan - scan_beg; + scan += length; + } + } + + + if (secinfo & SMB_DACL_SECINFO) { + /* Dacl */ + acl = smb_sd_get_dacl(sd, &present, &defaulted); + + srel_sd->sd_hdr.sd_control |= + (sd->sd_hdr.sd_control & SE_DACL_INHERITANCE_MASK); + + if (defaulted) + srel_sd->sd_hdr.sd_control |= SE_DACL_DEFAULTED; + + if (present) + srel_sd->sd_hdr.sd_control |= SE_DACL_PRESENT; + + if (present && acl) { + /*LINTED E_PTRDIFF_OVERFLOW*/ + length = smb_acl_copy(scan_end - scan, + (void*) scan, acl); + if (length == 0) + goto fail; + /*LINTED E_PTRDIFF_OVERFLOW*/ + srel_sd->sd_dacl_offs = scan - scan_beg; + /*LINTED E_PTRDIFF_OVERFLOW*/ + acl = (smb_acl_t *)scan; + acl->sl_size = (WORD)length; /* set the size */ + scan += length; + } + } + + if (secinfo & SMB_SACL_SECINFO) { + /* Sacl */ + acl = smb_sd_get_sacl(sd, &present, &defaulted); + + srel_sd->sd_hdr.sd_control |= + (sd->sd_hdr.sd_control & SE_SACL_INHERITANCE_MASK); + + if (defaulted) + srel_sd->sd_hdr.sd_control |= SE_SACL_DEFAULTED; + + if (present) + srel_sd->sd_hdr.sd_control |= SE_SACL_PRESENT; + + if (present && acl) { + /*LINTED E_PTRDIFF_OVERFLOW*/ + length = smb_acl_copy(scan_end - scan, + (void*) scan, acl); + if (length == 0) + goto fail; + /*LINTED E_PTRDIFF_OVERFLOW*/ + srel_sd->sd_sacl_offs = scan - scan_beg; + /*LINTED E_PTRDIFF_OVERFLOW*/ + acl = (smb_acl_t *)scan; + acl->sl_size = (WORD)length; /* set the size */ + scan += length; + } + } + + return (NT_STATUS_SUCCESS); + +fail: + return (NT_STATUS_INVALID_SECURITY_DESCR); +} + +/* + * smb_sd_fromfs + * + * Makes an Windows style security descriptor in absolute form + * based on the given filesystem security information. + * + * Should call smb_sd_term() for the returned sd to free allocated + * members. + */ +static uint32_t +smb_sd_fromfs(smb_fssd_t *fs_sd, smb_sd_t *sd) +{ + uint32_t status = NT_STATUS_SUCCESS; + smb_acl_t *acl = NULL; + smb_acl_t *sorted_acl; + nt_sid_t *sid; + idmap_stat idm_stat; + + ASSERT(fs_sd); + ASSERT(sd); + + smb_sd_init(sd, SECURITY_DESCRIPTOR_REVISION); + + /* Owner */ + if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) { + idm_stat = smb_idmap_getsid(fs_sd->sd_uid, + SMB_IDMAP_USER, &sid); + + if (idm_stat != IDMAP_SUCCESS) { + return (NT_STATUS_NONE_MAPPED); + } + + smb_sd_set_owner(sd, sid, 0); + } + + /* Group */ + if (fs_sd->sd_secinfo & SMB_GROUP_SECINFO) { + idm_stat = smb_idmap_getsid(fs_sd->sd_gid, + SMB_IDMAP_GROUP, &sid); + + if (idm_stat != IDMAP_SUCCESS) { + smb_sd_term(sd); + return (NT_STATUS_NONE_MAPPED); + } + + smb_sd_set_group(sd, sid, 0); + } + + /* DACL */ + if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) { + if (fs_sd->sd_zdacl != NULL) { + acl = smb_acl_from_zfs(fs_sd->sd_zdacl, fs_sd->sd_uid, + fs_sd->sd_gid); + if (acl == NULL) { + smb_sd_term(sd); + return (NT_STATUS_INTERNAL_ERROR); + } + + /* + * Need to sort the ACL before send it to Windows + * clients. Winodws GUI is sensitive about the order + * of ACEs. + */ + sorted_acl = smb_acl_sort(acl); + if (sorted_acl && (sorted_acl != acl)) { + kmem_free(acl, acl->sl_size); + acl = sorted_acl; + } + smb_sd_set_dacl(sd, 1, acl, fs_sd->sd_zdacl->acl_flags); + } else { + smb_sd_set_dacl(sd, 0, NULL, 0); + } + } + + /* SACL */ + if (fs_sd->sd_secinfo & SMB_SACL_SECINFO) { + if (fs_sd->sd_zsacl != NULL) { + acl = smb_acl_from_zfs(fs_sd->sd_zsacl, fs_sd->sd_uid, + fs_sd->sd_gid); + if (acl == NULL) { + smb_sd_term(sd); + return (NT_STATUS_INTERNAL_ERROR); + } + + smb_sd_set_sacl(sd, 1, acl, fs_sd->sd_zsacl->acl_flags); + } else { + smb_sd_set_sacl(sd, 0, NULL, 0); + } + } + + return (status); +} + +/* + * smb_sd_tofs + * + * Creates a filesystem security structure based on the given + * Windows security descriptor. + */ +uint32_t +smb_sd_tofs(smb_sdbuf_t *sr_sd, smb_fssd_t *fs_sd) +{ + nt_sid_t *sid; + smb_acl_t *acl; + uint32_t status = NT_STATUS_SUCCESS; + uint16_t sd_control; + idmap_stat idm_stat; + int present; + int idtype; + int flags = 0; + + sd_control = sr_sd->sd_hdr.sd_control; + + /* + * ZFS only has one set of flags so for now only + * Windows DACL flags are taken into account. + */ + if (sd_control & SE_DACL_DEFAULTED) + flags |= ACL_DEFAULTED; + if (sd_control & SE_DACL_AUTO_INHERITED) + flags |= ACL_AUTO_INHERIT; + if (sd_control & SE_DACL_PROTECTED) + flags |= ACL_PROTECTED; + + if (fs_sd->sd_flags & SMB_FSSD_FLAGS_DIR) + flags |= ACL_IS_DIR; + + /* Owner */ + if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) { + sid = smb_sd_get_owner(sr_sd, NULL); + if (nt_sid_is_valid(sid) == 0) { + return (NT_STATUS_INVALID_SID); + } + + idtype = SMB_IDMAP_UNKNOWN; + idm_stat = smb_idmap_getid(sid, &fs_sd->sd_uid, &idtype); + if (idm_stat != IDMAP_SUCCESS) { + return (NT_STATUS_NONE_MAPPED); + } + } + + /* Group */ + if (fs_sd->sd_secinfo & SMB_GROUP_SECINFO) { + sid = smb_sd_get_group(sr_sd, NULL); + if (nt_sid_is_valid(sid) == 0) { + return (NT_STATUS_INVALID_SID); + } + + idtype = SMB_IDMAP_UNKNOWN; + idm_stat = smb_idmap_getid(sid, &fs_sd->sd_gid, &idtype); + if (idm_stat != IDMAP_SUCCESS) { + return (NT_STATUS_NONE_MAPPED); + } + } + + /* DACL */ + if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) { + acl = smb_sd_get_dacl(sr_sd, &present, NULL); + if (present) { + status = smb_acl_to_zfs(acl, flags, + SMB_DACL_SECINFO, &fs_sd->sd_zdacl); + if (status != NT_STATUS_SUCCESS) + return (status); + } + else + return (NT_STATUS_INVALID_ACL); + } + + /* SACL */ + if (fs_sd->sd_secinfo & SMB_SACL_SECINFO) { + acl = smb_sd_get_sacl(sr_sd, &present, NULL); + if (present) { + status = smb_acl_to_zfs(acl, flags, + SMB_SACL_SECINFO, &fs_sd->sd_zsacl); + if (status != NT_STATUS_SUCCESS) { + return (status); + } + } else { + return (NT_STATUS_INVALID_ACL); + } + } + + return (status); +} + +/* + * smb_sd_read + * + * Read uid, gid and ACL from filesystem. The returned ACL from read + * routine is always in ZFS format. Convert the ZFS acl to a Win acl + * and return the Win SD in relative form. + * + * NOTE: upon successful return caller MUST free the memory allocated + * for the returned SD by calling kmem_free(). The length of the allocated + * buffer is returned in 'buflen'. + */ +uint32_t +smb_sd_read(smb_request_t *sr, smb_sdbuf_t **sr_sd, + uint32_t secinfo, uint32_t *buflen) +{ + smb_sd_t sd; + smb_fssd_t fs_sd; + smb_error_t smb_err; + smb_sdbuf_t *sdbuf; + smb_node_t *node; + uint32_t sdlen; + uint32_t status = NT_STATUS_SUCCESS; + uint32_t sd_flags; + int error; + + *sr_sd = NULL; + + node = sr->fid_ofile->f_node; + sd_flags = (node->vp->v_type == VDIR) ? SMB_FSSD_FLAGS_DIR : 0; + smb_fsop_sdinit(&fs_sd, secinfo, sd_flags); + + error = smb_fsop_sdread(sr, sr->user_cr, node, &fs_sd); + if (error) { + smb_errmap_unix2smb(error, &smb_err); + return (smb_err.status); + } + + status = smb_sd_fromfs(&fs_sd, &sd); + smb_fsop_sdterm(&fs_sd); + + if (status != NT_STATUS_SUCCESS) + return (status); + + sdlen = smb_sd_len(&sd, secinfo); + + if (*buflen < sdlen) { + /* return the required size */ + *buflen = sdlen; + smb_sd_term(&sd); + return (NT_STATUS_BUFFER_TOO_SMALL); + } + + sdbuf = kmem_alloc(sdlen, KM_SLEEP); + status = smb_sd_abs2selfrel(&sd, secinfo, sdbuf, sdlen); + smb_sd_term(&sd); + + if (status == NT_STATUS_SUCCESS) { + *sr_sd = sdbuf; + *buflen = sdlen; + } + else + kmem_free(sdbuf, sdlen); + + return (status); +} + +/* + * smb_sd_write + * + * Takes a Win SD in self-relative form, convert it to + * ZFS format and write it to filesystem. The write routine + * converts ZFS acl to Posix acl if required. + */ +uint32_t +smb_sd_write(smb_request_t *sr, smb_sdbuf_t *sr_sd, uint32_t secinfo) +{ + smb_node_t *node; + smb_fssd_t fs_sd; + smb_error_t smb_err; + uint32_t status; + uint32_t sd_flags; + int error; + + node = sr->fid_ofile->f_node; + sd_flags = (node->vp->v_type == VDIR) ? SMB_FSSD_FLAGS_DIR : 0; + smb_fsop_sdinit(&fs_sd, secinfo, sd_flags); + + status = smb_sd_tofs(sr_sd, &fs_sd); + if (status != NT_STATUS_SUCCESS) { + smb_fsop_sdterm(&fs_sd); + return (status); + } + + error = smb_fsop_sdwrite(sr, sr->user_cr, node, &fs_sd, 0); + smb_fsop_sdterm(&fs_sd); + + if (error) { + smb_errmap_unix2smb(error, &smb_err); + return (smb_err.status); + } + + return (NT_STATUS_SUCCESS); +} + +/* + * smb_fmt_sid + * + * Make an string SID and copy the result into the specified buffer. + */ +void +smb_fmt_sid(char *buf, nt_sid_t *sid) +{ + char *sid_str; + + sid_str = nt_sid_format(sid); + if (sid_str) { + (void) strcpy(buf, sid_str); + MEM_FREE("smb", sid_str); + } else { + (void) strcpy(buf, ""); + } +} + +/* + * smb_sd_log + * + * log the given Windows style security descriptor information + * in system log. This is for debugging purposes. + */ +void +smb_sd_log(void *sd) +{ + smb_acl_t *acl; + smb_ace_t *ace; + nt_sid_t *sid; + int present, defaulted; + char entry[128]; + char *inherit; + char *type; + int ix_dacl; + + sid = smb_sd_get_owner(sd, &defaulted); + if (sid) + smb_fmt_sid(entry, sid); + else + (void) strcpy(entry, "NULL"); + + cmn_err(CE_NOTE, " Owner: %s", entry); + + sid = smb_sd_get_group(sd, &defaulted); + if (sid) + smb_fmt_sid(entry, sid); + else + (void) strcpy(entry, "NULL"); + + cmn_err(CE_NOTE, " Primary Group: %s", entry); + + acl = smb_sd_get_dacl(sd, &present, &defaulted); + + if (!present || !acl) { + cmn_err(CE_NOTE, " No DACL"); + return; + } + + for (ix_dacl = 0; + ace = smb_ace_get(acl, ix_dacl); + ix_dacl++) { + /* + * Make sure the ACE type is something we grok. + * All ACE, now and in the future, have a valid + * header. Can't access fields passed the Header + * until we're sure it's right. + */ + switch (ace->se_header.se_type) { + case ACCESS_ALLOWED_ACE_TYPE: + type = "(Allow)"; + break; + case ACCESS_DENIED_ACE_TYPE: + type = "(Deny)"; + break; + + case SYSTEM_AUDIT_ACE_TYPE: + default: + /* Ignore unrecognized/misplaced ACE */ + continue; + } + + smb_fmt_sid(entry, &ace->se_sid); + + switch (ace->se_header.se_flags & INHERIT_MASK_ACE) { + case OBJECT_INHERIT_ACE: + inherit = "(OI)"; + break; + case CONTAINER_INHERIT_ACE: + inherit = "(CI)"; + break; + case INHERIT_ONLY_ACE: + inherit = "(IO)"; + break; + case NO_PROPOGATE_INHERIT_ACE: + inherit = "(NP)"; + break; + default: + inherit = ""; + } + + (void) snprintf(entry + strlen(entry), sizeof (entry), + ":%s 0x%X %s", inherit, ace->se_mask, type); + + cmn_err(CE_NOTE, " %s", entry); + } + + cmn_err(CE_NOTE, " %d ACE(s)", ix_dacl); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_search.c b/usr/src/uts/common/fs/smbsrv/smb_search.c new file mode 100644 index 000000000000..0700b8fd2d33 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_search.c @@ -0,0 +1,289 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: search + * + * This command is used to search directories. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 2 + * USHORT MaxCount; Number of dir. entries to return + * USHORT SearchAttributes; + * USHORT ByteCount; Count of data bytes; min = 5 + * UCHAR BufferFormat1; 0x04 -- ASCII + * UCHAR FileName[]; File name, may be null + * UCHAR BufferFormat2; 0x05 -- Variable block + * USHORT ResumeKeyLength; Length of resume key, may be 0 + * UCHAR ResumeKey[]; Resume key + * + * FileName specifies the file to be sought. SearchAttributes indicates + * the attributes that the file must have, and is described in the "File + * Attribute Encoding" section of this document. If SearchAttributes is + * zero then only normal files are returned. If the system file, hidden or + * directory attributes are specified then the search is inclusive@both the + * specified type(s) of files and normal files are returned. If the volume + * label attribute is specified then the search is exclusive, and only the + * volume label entry is returned. + * + * MaxCount specifies the number of directory entries to be returned. + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 1 + * USHORT Count; Number of entries returned + * USHORT ByteCount; Count of data bytes; min = 3 + * UCHAR BufferFormat; 0x05 -- Variable block + * USHORT DataLength; Length of data + * UCHAR DirectoryInformationData[]; Data + * + * The response will contain one or more directory entries as determined by + * the Count field. No more than MaxCount entries will be returned. Only + * entries that match the sought FileName and SearchAttributes combination + * will be returned. + * + * ResumeKey must be null (length = 0) on the initial search request. + * Subsequent search requests intended to continue a search must contain + * the ResumeKey field extracted from the last directory entry of the + * previous response. ResumeKey is self-contained, for on calls containing + * a non-zero ResumeKey neither the SearchAttributes or FileName fields + * will be valid in the request. ResumeKey has the following format: + * + * Resume Key Field Description + * ================================== ================================= + * + * UCHAR Reserved; bit 7 - consumer use + * bits 5,6 - system use (must + * preserve) + * bits 0-4 - server use (must + * preserve) + * UCHAR FileName[11]; Name of the returned file + * UCHAR ReservedForServer[5]; Client must not modify + * UCHAR ReservedForConsumer[4]; Server must not modify + * + * FileName is 8.3 format, with the three character extension left + * justified into FileName[9-11]. If the client is prior to the LANMAN1.0 + * dialect, the returned FileName should be uppercased. + * + * SMB_COM_SEARCH terminates when either the requested maximum number of + * entries that match the named file are found, or the end of directory is + * reached without the maximum number of matches being found. A response + * containing no entries indicates that no matching entries were found + * between the starting point of the search and the end of directory. + * + * There may be multiple matching entries in response to a single request + * as SMB_COM_SEARCH supports wildcards in the last component of FileName + * of the initial request. + * + * Returned directory entries in the DirectoryInformationData field of the + * response each have the following format: + * + * Directory Information Field Description + * ================================== ================================= + * + * SMB_RESUME_KEY ResumeKey; Described above + * UCHAR FileAttributes; Attributes of the found file + * SMB_TIME LastWriteTime; Time file was last written + * SMB_DATE LastWriteDate; Date file was last written + * ULONG FileSize; Size of the file + * UCHAR FileName[13]; ASCII, space-filled null + * terminated + * + * FileName must conform to 8.3 rules, and is padded after the extension + * with 0x20 characters if necessary. If the client has negotiated a + * dialect prior to the LANMAN1.0 dialect, or if bit0 of the Flags2 SMB + * header field of the request is clear, the returned FileName should be + * uppercased. + * + * As can be seen from the above structure, SMB_COM_SEARCH can not return + * long filenames, and can not return UNICODE filenames. Files which have + * a size greater than 2^32 bytes should have the least significant 32 bits + * of their size returned in FileSize. + */ + +#include + +int +smb_com_search(struct smb_request *sr) +{ + int rc; + unsigned short sattr, count, maxcount; + char *path; + uint32_t cookie; + char name[14]; + unsigned char resume_char; + uint32_t resume_key; + struct smb_node *node; + unsigned char type; + unsigned short key_len; + fsvol_attr_t vol_attr; + smb_odir_context_t *pc; + + /* We only handle 8.3 name here */ + sr->smb_flg2 &= ~SMB_FLAGS2_KNOWS_LONG_NAMES; + sr->smb_flg &= ~SMB_FLAGS_CASE_INSENSITIVE; + + if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if ((smbsr_decode_data(sr, "%Abw", sr, &path, &type, &key_len) != 0) || + (type != 0x05)) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if ((rc = fsd_getattr(&sr->tid_tree->t_fsd, &vol_attr)) != 0) { + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + count = 0; + + if ((sattr == SMB_FA_VOLUME) && (key_len != 21)) { + (void) memset(name, ' ', sizeof (name)); + (void) strncpy(name, vol_attr.name, sizeof (name)); + + if (key_len >= 21) { + (void) smb_decode_mbc(&sr->smb_data, "17.l", + &resume_key); + } else { + resume_key = 0; + } + + (void) smb_encode_mbc(&sr->reply, "bwwbwb11c5.lb8.13c", + 1, 0, VAR_BCC, 5, 0, 0, path+1, + resume_key, sattr, name); + count++; + } else { + cookie = 0; + if (key_len == 0) { /* begin search */ + /* + * Some MS clients pass NULL file names + * NT interprets this as "\" + */ + if (strlen(path) == 0) path = "\\"; + + rc = smb_rdir_open(sr, path, sattr); + if (rc == SDRC_NORMAL_REPLY) { + sr->reply.chain_offset = sr->cur_reply_offset; + (void) smb_encode_mbc(&sr->reply, "bw", 0, 0); + return (rc); + } + resume_char = 0; + resume_key = 0; + } else if (key_len == 21) { + if (smb_decode_mbc(&sr->smb_data, "b12.wwl", + &resume_char, &cookie, &sr->smb_sid, + &resume_key) != 0) { + /* We don't know which search to close! */ + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->sid_odir = smb_odir_lookup_by_sid(sr->tid_tree, + sr->smb_sid); + if (sr->sid_odir == NULL) { + smbsr_raise_cifs_error(sr, + NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + } else { + /* We don't know which search to close! */ + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + (void) smb_encode_mbc(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0); + + pc = MEM_ZALLOC("smb", sizeof (*pc)); + pc->dc_cookie = cookie; + node = (struct smb_node *)0; + rc = 0; + while (count < maxcount) { + if ((rc = smb_rdir_next(sr, &node, pc)) != 0) + break; + if ((strcmp(pc->dc_name, ".") == 0) || + (strcmp(pc->dc_name, "..") == 0)) { + if (node) { + smb_node_release(node); + node = (struct smb_node *)0; + } + continue; + } + + (void) memset(name, ' ', sizeof (name)); + if (*pc->dc_shortname) + (void) strncpy(name, pc->dc_shortname, 13); + else { + (void) strncpy(name, pc->dc_name, 13); + if ((sr->session->dialect <= LANMAN1_0) || + ((sr->smb_flg2 & + SMB_FLAGS2_KNOWS_LONG_NAMES) == 0)) + (void) utf8_strupr(name); + } + + (void) smb_encode_mbc(&sr->reply, "b8c3c.wwlbYl13c", + resume_char, + pc->dc_name83, pc->dc_name83+9, + pc->dc_cookie, sr->smb_sid, + resume_key, + pc->dc_dattr & 0xff, + pc->dc_attr.sa_vattr.va_mtime.tv_sec, + (int32_t)smb_node_get_size(node, &pc->dc_attr), + name); + smb_node_release(node); + node = (struct smb_node *)0; + count++; + } + MEM_FREE("smb", pc); + + if ((rc != 0) && (rc != ENOENT)) { + /* returned error by smb_rdir_next() */ + smb_rdir_close(sr); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + if (count == 0) { + smb_rdir_close(sr); + smbsr_raise_error(sr, ERRDOS, ERRnofiles); + /* NOTREACHED */ + } + } + + rc = (sr->reply.chain_offset - sr->cur_reply_offset) - 8; + (void) smb_poke_mbc(&sr->reply, sr->cur_reply_offset, "bwwbw", + 1, count, rc+3, 5, rc); + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_seek.c b/usr/src/uts/common/fs/smbsrv/smb_seek.c new file mode 100644 index 000000000000..a120a26a9dd3 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_seek.c @@ -0,0 +1,129 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * The seek message is sent to set the current file pointer for FID. + * This request should generally only be used by clients wishing to + * find the size of a file, since all read and write requests include + * the read or write file position as part of the SMB. This request + * is inappropriate for large files, as the offsets specified are only + * 32 bits. + * + * The CIFS/1.0 (1996) spec contains the following incomplete statement: + * + * "A seek which results in an Offset which can not be expressed + * in 32 bits returns the least significant." + * + * It would probably be a mistake to make an assumption about what this + * statement means. So, for now, we return an error if the resultant + * file offset is beyond the 32-bit limit. + */ + +#include + + +/* + * smb_com_seek + * + * Client Request Description + * ================================== ================================= + * UCHAR WordCount; Count of parameter words = 4 + * USHORT Fid; File handle + * USHORT Mode; Seek mode: 0, 1 or 2 + * LONG Offset; Relative offset + * USHORT ByteCount; Count of data bytes = 0 + * + * The starting point of the seek is set by Mode: + * + * 0 seek from start of file + * 1 seek from current current position + * 2 seek from end of file + * + * The "current position" reflects the offset plus data length specified in + * the previous read, write or seek request, and the pointer set by this + * command will be replaced by the offset specified in the next read, write + * or seek command. + * + * Server Response Description + * ================================== ================================= + * UCHAR WordCount; Count of parameter words = 2 + * ULONG Offset; Offset from start of file + * USHORT ByteCount; Count of data bytes = 0 + * + * The response returns the new file pointer in Offset, which is expressed + * as the offset from the start of the file, and may be beyond the current + * end of file. An attempt to seek before the start of the file sets the + * current file pointer to the start of the file. + */ +int +smb_com_seek(struct smb_request *sr) +{ + ushort_t mode; + int32_t off; + uint32_t off_ret; + int rc; + + if (smbsr_decode_vwv(sr, "wwl", &sr->smb_fid, &mode, &off) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + if (mode == SMB_SEEK_END) { + (void) smb_set_file_size(sr); + } + + rc = smb_ofile_seek(sr->fid_ofile, mode, off, &off_ret); + if (rc == 0) { + smbsr_encode_result(sr, 2, 0, "blw", 2, off_ret, 0); + return (SDRC_NORMAL_REPLY); + } + if (rc == EINVAL) { + smbsr_raise_error(sr, ERRDOS, ERRbadfunc); + /* NOTREACHED */ + } + if (rc == EOVERFLOW) { + smbsr_raise_error(sr, ERRSRV, ERRerror); + /* NOTREACHED */ + } + ASSERT(0); + smbsr_raise_error(sr, ERRSRV, ERRerror); + /* NOTREACHED */ + + /* + * Although smbsr_raise_error() doesn't return and the compiler is + * told so in smb_kproto.h it still has a problem if it doesn't + * find here a return instruction with a value. + */ + return (0); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_session.c b/usr/src/uts/common/fs/smbsrv/smb_session.c new file mode 100644 index 000000000000..3be7eaab1f55 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_session.c @@ -0,0 +1,1252 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int smb_maxbufsize; + +extern unsigned int smb_nt_tcp_rcvbuf; + +uint32_t smb_keep_alive = SSN_KEEP_ALIVE_TIMEOUT; +uint32_t smb_send_retries = 0; +uint32_t smb_receive_retries = 0; + +static int smb_session_message(smb_session_t *); +static int smb_session_xprt_puthdr(smb_session_t *, smb_xprt_t *, + uint8_t *, size_t); + +void smb_request_init_command_mbuf(smb_request_t *sr); +static void smb_session_wakeup_daemon(smb_thread_t *thread, void *so_void); + + +void +smb_timers(smb_thread_t *thread, void *si_void) +{ + smb_info_t *si = si_void; + smb_session_t *sn; + + ASSERT(si != NULL); + + while (smb_thread_continue_timedwait(thread, 1 /* Seconds */)) { + /* + * Walk through the table and decrement each keep_alive + * timer that has not timed out yet. (keepalive > 0) + */ + smb_svcstate_lock_read(&si->si_svc_sm_ctx); + + sn = NULL; + while ((sn = smb_svcstate_session_getnext(&si->si_svc_sm_ctx, + sn)) != NULL) { + ASSERT(sn->s_magic == SMB_SESSION_MAGIC); + if (sn->keep_alive && (sn->keep_alive != (uint32_t)-1)) + sn->keep_alive--; + } + smb_svcstate_unlock(&smb_info.si_svc_sm_ctx); + + } +} + +/* + * smb_reconnection_check + * + * This function is called when a client indicates its current connection + * should be the only one it has with the server, as indicated by VC=0 in + * a SessionSetupX request. We go through the session list and destroy any + * stale connections for that client. + * + * Clients don't associate IP addresses and servers. So a client may make + * independent connections (i.e. with VC=0) to a server with multiple + * IP addresses. So, when checking for a reconnection, we need to include + * the local IP address, to which the client is connecting, when checking + * for stale sessions. + * + * Also check the server's NetBIOS name to support simultaneous access by + * multiple clients behind a NAT server. This will only work for SMB over + * NetBIOS on TCP port 139, it will not work SMB over TCP port 445 because + * there is no NetBIOS name. See also Knowledge Base article Q301673. + */ +void +smb_reconnection_check(struct smb_session *session) +{ + smb_info_t *si = &smb_info; + smb_session_t *sn; + + smb_svcstate_lock_read(&si->si_svc_sm_ctx); + + sn = NULL; + while ((sn = smb_svcstate_session_getnext(&si->si_svc_sm_ctx, sn)) + != NULL) { + + ASSERT(sn->s_magic == SMB_SESSION_MAGIC); + if ((sn != session) && + (sn->ipaddr == session->ipaddr) && + (sn->local_ipaddr == session->local_ipaddr) && + (strcasecmp(sn->workstation, session->workstation) == 0) && + (sn->opentime <= session->opentime) && + (sn->s_kid < session->s_kid)) { + smb_thread_stop(&sn->s_thread); + } + } + + smb_svcstate_unlock(&smb_info.si_svc_sm_ctx); +} + + +void +smb_correct_keep_alive_values(uint32_t new_keep_alive) +{ + smb_info_t *si = &smb_info; + smb_session_t *sn; + + if (new_keep_alive == smb_keep_alive) + return; + /* + * keep alive == 0 means do not drop connection if it's idle + */ + smb_keep_alive = (new_keep_alive) ? new_keep_alive : -1; + + /* + * Walk through the table and set each session to the new keep_alive + * value if they have not already timed out. Block clock interrupts. + */ + smb_svcstate_lock_read(&si->si_svc_sm_ctx); + + sn = NULL; + while ((sn = smb_svcstate_session_getnext(&si->si_svc_sm_ctx, sn)) + != NULL) { + if (sn->keep_alive) + sn->keep_alive = new_keep_alive; + } + + smb_svcstate_unlock(&smb_info.si_svc_sm_ctx); +} + +/* + * Send a session message - supports SMB-over-NBT and SMB-over-TCP. + * + * The mbuf chain is copied into a contiguous buffer so that the whole + * message is submitted to smb_sosend as a single request. This should + * help Ethereal/Wireshark delineate the packets correctly even though + * TCP_NODELAY has been set on the socket. + * + * If an mbuf chain is provided, it will be freed and set to NULL here. + */ +int +smb_session_send(smb_session_t *session, uint8_t type, struct mbuf_chain *mbc) +{ + struct mbuf *m = 0; + uint8_t *buf; + smb_xprt_t hdr; + int count = 0; + int rc; + + switch (session->s_state) { + case SMB_SESSION_STATE_DISCONNECTED: + case SMB_SESSION_STATE_TERMINATED: + if ((mbc != NULL) && (mbc->chain != NULL)) { + m_freem(mbc->chain); + mbc->chain = NULL; + mbc->flags = 0; + } + return (ENOTCONN); + default: + break; + } + + buf = kmem_alloc(NETBIOS_REQ_MAX_SIZE, KM_SLEEP); + + if ((mbc != NULL) && (mbc->chain != NULL)) { + count = NETBIOS_HDR_SZ; /* Account for the NBT header. */ + m = mbc->chain; + + while (m) { + if ((count + m->m_len) > NETBIOS_REQ_MAX_SIZE) { + kmem_free(buf, NETBIOS_REQ_MAX_SIZE); + m_freem(mbc->chain); + mbc->chain = NULL; + mbc->flags = 0; + return (EMSGSIZE); + } + bcopy(m->m_data, buf + count, m->m_len); + count += m->m_len; + m = m->m_next; + } + + m_freem(mbc->chain); + mbc->chain = NULL; + mbc->flags = 0; + count -= NETBIOS_HDR_SZ; + } + + hdr.xh_type = type; + hdr.xh_length = count; + + rc = smb_session_xprt_puthdr(session, &hdr, buf, NETBIOS_HDR_SZ); + if (rc == 0) { + count += NETBIOS_HDR_SZ; + rc = smb_sosend(session->sock, buf, count); + } + + kmem_free(buf, NETBIOS_REQ_MAX_SIZE); + return (rc); +} + +/* + * Read, process and respond to a NetBIOS session request. + * + * A NetBIOS session must be established for SMB-over-NetBIOS. Validate + * the calling and called name format and save the client NetBIOS name, + * which is used when a NetBIOS session is established to check for and + * cleanup leftover state from a previous session. + * + * Session requests are not valid for SMB-over-TCP, which is unfortunate + * because without the client name leftover state cannot be cleaned up + * if the client is behind a NAT server. + */ +static int +smb_session_request(struct smb_session *session) +{ + int rc; + char *calling_name; + char *called_name; + char client_name[NETBIOS_NAME_SZ]; + struct mbuf_chain mbc; + char *names = NULL; + mts_wchar_t *wbuf = NULL; + smb_xprt_t hdr; + char *p; + unsigned int cpid = oem_get_smb_cpid(); + int rc1, rc2; + + session->keep_alive = smb_keep_alive; + + if (smb_session_xprt_gethdr(session, &hdr) != 0) + return (EINVAL); + + DTRACE_PROBE2(receive__session__req__xprthdr, struct session *, session, + smb_xprt_t *, &hdr); + + if ((hdr.xh_type != SESSION_REQUEST) || + (hdr.xh_length != NETBIOS_SESSION_REQUEST_DATA_LENGTH)) { + DTRACE_PROBE1(receive__session__req__failed, + struct session *, session); + return (EINVAL); + } + + names = kmem_alloc(hdr.xh_length, KM_SLEEP); + + if ((rc = smb_sorecv(session->sock, names, hdr.xh_length)) != 0) { + kmem_free(names, hdr.xh_length); + DTRACE_PROBE1(receive__session__req__failed, + struct session *, session); + return (rc); + } + + DTRACE_PROBE3(receive__session__req__data, struct session *, session, + char *, names, uint32_t, hdr.xh_length); + + called_name = &names[0]; + calling_name = &names[NETBIOS_ENCODED_NAME_SZ + 2]; + + rc1 = netbios_name_isvalid(called_name, 0); + rc2 = netbios_name_isvalid(calling_name, client_name); + + if (rc1 == 0 || rc2 == 0) { + + DTRACE_PROBE3(receive__invalid__session__req, + struct session *, session, char *, names, + uint32_t, hdr.xh_length); + + kmem_free(names, hdr.xh_length); + MBC_INIT(&mbc, MAX_DATAGRAM_LENGTH); + (void) smb_encode_mbc(&mbc, "b", + DATAGRAM_INVALID_SOURCE_NAME_FORMAT); + (void) smb_session_send(session, NEGATIVE_SESSION_RESPONSE, + &mbc); + return (EINVAL); + } + + DTRACE_PROBE3(receive__session__req__calling__decoded, + struct session *, session, + char *, calling_name, char *, client_name); + + /* + * The client NetBIOS name is in oem codepage format. + * We need to convert it to unicode and store it in + * multi-byte format. We also need to strip off any + * spaces added as part of the NetBIOS name encoding. + */ + wbuf = kmem_alloc((SMB_PI_MAX_HOST * sizeof (mts_wchar_t)), KM_SLEEP); + (void) oemstounicodes(wbuf, client_name, SMB_PI_MAX_HOST, cpid); + (void) mts_wcstombs(session->workstation, wbuf, SMB_PI_MAX_HOST); + kmem_free(wbuf, (SMB_PI_MAX_HOST * sizeof (mts_wchar_t))); + + if ((p = strchr(session->workstation, ' ')) != 0) + *p = '\0'; + + kmem_free(names, hdr.xh_length); + return (smb_session_send(session, POSITIVE_SESSION_RESPONSE, NULL)); +} + +/* + * Read 4-byte header from the session socket and build an in-memory + * session transport header. See smb_xprt_t definition for header + * format information. + * + * Direct hosted NetBIOS-less SMB (SMB-over-TCP) uses port 445. The + * first byte of the four-byte header must be 0 and the next three + * bytes contain the length of the remaining data. + */ +int +smb_session_xprt_gethdr(smb_session_t *session, smb_xprt_t *ret_hdr) +{ + unsigned char buf[NETBIOS_HDR_SZ]; + + if (smb_sorecv(session->sock, buf, NETBIOS_HDR_SZ) != 0) + return (-1); + + switch (session->s_local_port) { + case SSN_SRVC_TCP_PORT: + ret_hdr->xh_type = buf[0]; + ret_hdr->xh_length = (((uint32_t)buf[1] & 1) << 16) | + ((uint32_t)buf[2] << 8) | + ((uint32_t)buf[3]); + break; + + case SMB_SRVC_TCP_PORT: + ret_hdr->xh_type = buf[0]; + + if (ret_hdr->xh_type != 0) { + cmn_err(CE_WARN, "0x%08x: invalid type (%u)", + session->ipaddr, ret_hdr->xh_type); + return (-1); + } + + ret_hdr->xh_length = ((uint32_t)buf[1] << 16) | + ((uint32_t)buf[2] << 8) | + ((uint32_t)buf[3]); + break; + + default: + cmn_err(CE_WARN, "0x%08x: invalid port %u", + session->ipaddr, session->s_local_port); + return (-1); + } + + return (0); +} + +/* + * Encode a transport session packet header into a 4-byte buffer. + * See smb_xprt_t definition for header format information. + */ +static int +smb_session_xprt_puthdr(smb_session_t *session, smb_xprt_t *hdr, + uint8_t *buf, size_t buflen) +{ + if (session == NULL || hdr == NULL || + buf == NULL || buflen < NETBIOS_HDR_SZ) { + return (-1); + } + + switch (session->s_local_port) { + case SSN_SRVC_TCP_PORT: + buf[0] = hdr->xh_type; + buf[1] = ((hdr->xh_length >> 16) & 1); + buf[2] = (hdr->xh_length >> 8) & 0xff; + buf[3] = hdr->xh_length & 0xff; + break; + + case SMB_SRVC_TCP_PORT: + buf[0] = hdr->xh_type; + buf[1] = (hdr->xh_length >> 16) & 0xff; + buf[2] = (hdr->xh_length >> 8) & 0xff; + buf[3] = hdr->xh_length & 0xff; + break; + + default: + cmn_err(CE_WARN, "0x%08x: invalid port (%u)", + session->ipaddr, session->s_local_port); + return (-1); + } + + return (0); +} + +/* + * smb_request_alloc + * + * Allocate an smb_request_t structure from the kmem_cache. Partially + * initialize the found/new request. + * + * Returns pointer to a request + */ +smb_request_t * +smb_request_alloc(struct smb_session *session, int req_length) +{ + struct smb_request *sr; + + sr = kmem_cache_alloc(smb_info.si_cache_request, KM_SLEEP); + + /* + * Future: Use constructor to pre-initialize some fields. For now + * there are so many fields that it is easiest just to zero the + * whole thing and start over. + */ + bzero(sr, sizeof (smb_request_t)); + + mutex_init(&sr->sr_mutex, NULL, MUTEX_DEFAULT, NULL); + sr->session = session; + sr->request_storage.forw = &sr->request_storage; + sr->request_storage.back = &sr->request_storage; + sr->command.max_bytes = req_length; + sr->reply.max_bytes = smb_maxbufsize; + sr->sr_req_length = req_length; + sr->sr_request_buf = kmem_alloc(req_length, KM_SLEEP); + sr->sr_magic = SMB_REQ_MAGIC; + sr->sr_state = SMB_REQ_STATE_INITIALIZING; + smb_slist_insert_tail(&session->s_req_list, sr); + return (sr); +} + +void +smb_request_init_command_mbuf(smb_request_t *sr) +{ + MGET(sr->command.chain, 0, MT_DATA); + + /* + * Setup mbuf, mimic MCLGET but use the complete packet buffer. + */ + sr->command.chain->m_ext.ext_buf = sr->sr_request_buf; + sr->command.chain->m_data = sr->command.chain->m_ext.ext_buf; + sr->command.chain->m_len = sr->sr_req_length; + sr->command.chain->m_flags |= M_EXT; + sr->command.chain->m_ext.ext_size = sr->sr_req_length; + sr->command.chain->m_ext.ext_ref = &mclrefnoop; + + /* + * Initialize the rest of the mbuf_chain fields + */ + sr->command.flags = 0; + sr->command.shadow_of = 0; + sr->command.max_bytes = sr->sr_req_length; + sr->command.chain_offset = 0; +} + +/* + * smb_request_cancel + * + * Handle a cancel for a request properly depending on the current request + * state. + */ +void +smb_request_cancel(smb_request_t *sr) +{ + mutex_enter(&sr->sr_mutex); + switch (sr->sr_state) { + + case SMB_REQ_STATE_SUBMITTED: + case SMB_REQ_STATE_ACTIVE: + case SMB_REQ_STATE_CLEANED_UP: + sr->sr_state = SMB_REQ_STATE_CANCELED; + break; + + case SMB_REQ_STATE_WAITING_LOCK: + /* + * This request is waiting on a lock. Wakeup everything + * waiting on the lock so that the relevant thread regains + * control and notices that is has been canceled. The + * other lock request threads waiting on this lock will go + * back to sleep when they discover they are still blocked. + */ + sr->sr_state = SMB_REQ_STATE_CANCELED; + + ASSERT(sr->sr_awaiting != NULL); + mutex_enter(&sr->sr_awaiting->l_mutex); + cv_broadcast(&sr->sr_awaiting->l_cv); + mutex_exit(&sr->sr_awaiting->l_mutex); + + break; + + case SMB_REQ_STATE_WAITING_EVENT: + case SMB_REQ_STATE_EVENT_OCCURRED: + /* + * Cancellations for these states are handled by the + * notify-change code + */ + break; + + case SMB_REQ_STATE_COMPLETED: + case SMB_REQ_STATE_CANCELED: + /* + * No action required for these states since the request + * is completing. + */ + break; + /* + * Cases included: + * SMB_REQ_STATE_FREE: + * SMB_REQ_STATE_INITIALIZING: + */ + default: + ASSERT(0); + break; + } + mutex_exit(&sr->sr_mutex); +} + +/* + * smb_request_free + * + * release the memories which have been allocated for a smb request. + */ +void +smb_request_free(smb_request_t *sr) +{ + ASSERT(sr->session); + + ASSERT(sr->fid_ofile == NULL); + ASSERT(sr->sid_odir == NULL); + ASSERT(sr->tid_tree == NULL); + ASSERT(sr->uid_user == NULL); + ASSERT(sr->r_xa == NULL); + + smb_slist_remove(&sr->session->s_req_list, sr); + + sr->session = 0; + + /* Release any temp storage */ + smbsr_free_malloc_list(&sr->request_storage); + + if (sr->sr_request_buf) + kmem_free(sr->sr_request_buf, sr->sr_req_length); + if (sr->command.chain) + m_freem(sr->command.chain); + if (sr->reply.chain) + m_freem(sr->reply.chain); + if (sr->raw_data.chain) + m_freem(sr->raw_data.chain); + + sr->sr_magic = (uint32_t)~SMB_REQ_MAGIC; + mutex_destroy(&sr->sr_mutex); + kmem_cache_free(smb_info.si_cache_request, sr); +} + +/*ARGSUSED*/ +void +smb_wakeup_session_daemon(smb_thread_t *thread, void *session_void) +{ + struct smb_session *session = session_void; + + ASSERT(session); + ASSERT(session->s_magic == SMB_SESSION_MAGIC); + + smb_rwx_rwenter(&session->s_lock, RW_WRITER); + switch (session->s_state) { + case SMB_SESSION_STATE_TERMINATED: + case SMB_SESSION_STATE_DISCONNECTED: + break; + default: + smb_soshutdown(session->sock); + break; + } + smb_rwx_rwexit(&session->s_lock); +} + +/* + * This is the entry point for processing SMB messages over NetBIOS or + * SMB-over-TCP. + * + * NetBIOS connections require a session request to establish a session + * on which to send session messages. + * + * Session requests are not valid on SMB-over-TCP. We don't need to do + * anything here as session requests will be treated as an error when + * handling session messages. + */ +/*ARGSUSED*/ +void +smb_session_daemon(smb_thread_t *thread, void *session_void) +{ + struct smb_session *session = session_void; + int rc = 0; + + ASSERT(session != NULL); + + if (session->s_local_port == SSN_SRVC_TCP_PORT) + rc = smb_session_request(session); + + smb_rwx_rwenter(&session->s_lock, RW_WRITER); + + if ((rc == 0) || (session->s_local_port == SMB_SRVC_TCP_PORT)) + session->s_state = SMB_SESSION_STATE_ESTABLISHED; + else + session->s_state = SMB_SESSION_STATE_DISCONNECTED; + + while (session->s_state != SMB_SESSION_STATE_DISCONNECTED) { + smb_rwx_rwexit(&session->s_lock); + + rc = smb_session_message(session); + + smb_rwx_rwenter(&session->s_lock, RW_WRITER); + + if (rc != 0) + break; + } + + smb_soshutdown(session->sock); + session->s_state = SMB_SESSION_STATE_DISCONNECTED; + smb_rwx_rwexit(&session->s_lock); + + DTRACE_PROBE2(session__drop, struct session *, session, int, rc); + + smb_session_cancel(session); + + /* + * At this point everything related to the session should have been + * cleaned up and we expect that nothing will attempt to use the + * socket. + */ + smb_rwx_rwenter(&session->s_lock, RW_WRITER); + session->s_state = SMB_SESSION_STATE_TERMINATED; + smb_sodestroy(session->sock); + session->sock = NULL; + smb_rwx_rwexit(&session->s_lock); + + /* + * Notify SMB service state machine so it can cleanup the session + */ + smb_svcstate_event(SMB_SVCEVT_SESSION_DELETE, (uintptr_t)session); +} + +/* + * Read and process SMB requests. + * + * Returns: + * 0 Success + * 1 Unable to read transport header + * 2 Invalid transport header type + * 3 Invalid SMB length (too small) + * 4 Unable to read SMB header + * 5 Invalid SMB header (bad magic number) + * 6 Unable to read SMB data + * 2x Write raw failed + */ +static int +smb_session_message(smb_session_t *session) +{ + struct smb_request *sr = NULL; + smb_xprt_t hdr; + uint8_t *req_buf; + uint32_t resid; + int rc; + + if (smb_session_xprt_gethdr(session, &hdr) != 0) + return (1); + + DTRACE_PROBE2(session__receive__xprthdr, struct session *, session, + smb_xprt_t *, &hdr); + + if (hdr.xh_type != SESSION_MESSAGE) { + /* + * Anything other than SESSION_MESSAGE or SESSION_KEEP_ALIVE + * is an error. A SESSION_REQUEST may indicate a new session + * request but we need to close this session and we can treat + * it as an error here. + */ + if (hdr.xh_type == SESSION_KEEP_ALIVE) { + session->keep_alive = smb_keep_alive; + return (0); + } + + return (2); + } + + if (hdr.xh_length < SMB_HEADER_LEN) + return (3); + + session->keep_alive = smb_keep_alive; + + /* + * Allocate a request context, read the SMB header and validate it. + * The sr includes a buffer large enough to hold the SMB request + * payload. If the header looks valid, read any remaining data. + */ + sr = smb_request_alloc(session, hdr.xh_length); + + req_buf = (uint8_t *)sr->sr_request_buf; + resid = hdr.xh_length; + + if (smb_sorecv(session->sock, req_buf, SMB_HEADER_LEN) != 0) { + smb_request_free(sr); + return (4); + } + + if (SMB_PROTOCOL_MAGIC_INVALID(sr)) { + smb_request_free(sr); + return (5); + } + + if (resid > SMB_HEADER_LEN) { + req_buf += SMB_HEADER_LEN; + resid -= SMB_HEADER_LEN; + + if (smb_sorecv(session->sock, req_buf, resid) != 0) { + smb_request_free(sr); + return (6); + } + } + + /* + * Initialize command MBC to represent the received data. + */ + smb_request_init_command_mbuf(sr); + + DTRACE_PROBE1(session__receive__smb, smb_request_t *, sr); + + /* + * If this is a raw write, hand off the request. The handler + * will retrieve the remaining raw data and process the request. + */ + if (SMB_IS_WRITERAW(sr)) { + rc = smb_handle_write_raw(session, sr); + /* XXX smb_request_free(sr); ??? */ + return (rc); + } + + sr->sr_state = SMB_REQ_STATE_SUBMITTED; + (void) taskq_dispatch(smb_info.thread_pool, smb_session_worker, + sr, TQ_SLEEP); + return (0); +} + +/* + * smb_session_wakeup_daemon + * + * When the smbsrv kernel module/driver gets unloaded, chances are the + * smb_nbt_daemon and smb_tcp_daemon threads are blocked in soaccept. + * We can't get control of the threads until they return from soaccept. + * This function will attempt to connect to the SMB service via + * "localhost" to wake up the threads. + */ +/*ARGSUSED*/ +static void +smb_session_wakeup_daemon(smb_thread_t *thread, void *so_void) +{ + struct sonode *so = so_void; + + ASSERT(so != NULL); + + mutex_enter(&so->so_lock); + so->so_error = EINTR; + cv_signal(&so->so_connind_cv); + mutex_exit(&so->so_lock); +} + +/* + * SMB-over-NetBIOS service. + * + * Traditional SMB service over NetBIOS (port 139), which requires + * that a NetBIOS session be established. + */ +void +smb_nbt_daemon(smb_thread_t *thread, void *arg) +{ + /* XXX Defaults for these values should come from smbd and SMF */ + uint32_t txbuf_size = 128*1024; + uint32_t on = 1; + struct smb_session *session; + struct sonode *l_so, *s_so; + struct sockaddr_in sin; + int error; + smb_info_t *si = arg; + + ASSERT(si != NULL); + sin.sin_family = AF_INET; + sin.sin_port = htons(SSN_SRVC_TCP_PORT); + sin.sin_addr.s_addr = htonl(INADDR_ANY); + + l_so = smb_socreate(AF_INET, SOCK_STREAM, 0); + if (l_so == NULL) { + cmn_err(CE_WARN, "NBT: socket create failed"); + smb_svcstate_event(SMB_SVCEVT_DISCONNECT, (uintptr_t)ENOMEM); + return; + } + + (void) sosetsockopt(l_so, SOL_SOCKET, SO_REUSEADDR, + (const void *)&on, sizeof (on)); + + if ((error = sobind(l_so, (struct sockaddr *)&sin, sizeof (sin), + 0, 0)) != 0) { + cmn_err(CE_WARN, "NBT: bind failed"); + smb_soshutdown(l_so); + smb_sodestroy(l_so); + smb_svcstate_event(SMB_SVCEVT_DISCONNECT, (uintptr_t)error); + return; + } + if ((error = solisten(l_so, 20)) < 0) { + cmn_err(CE_WARN, "NBT: listen failed"); + smb_soshutdown(l_so); + smb_sodestroy(l_so); + smb_svcstate_event(SMB_SVCEVT_DISCONNECT, (uintptr_t)error); + return; + } + + smb_thread_set_awaken(thread, smb_session_wakeup_daemon, l_so); + si->si_connect_progress |= SMB_SI_NBT_CONNECTED; + smb_svcstate_event(SMB_SVCEVT_CONNECT, NULL); + + while (smb_thread_continue_nowait(thread)) { + DTRACE_PROBE1(so__wait__accept, struct sonode *, l_so); + + error = soaccept(l_so, 0, &s_so); + if (error) { + DTRACE_PROBE1(so__accept__error, int, error); + if (error == EINTR) { + continue; + } + + break; + } + + DTRACE_PROBE1(so__accept, struct sonode *, s_so); + + (void) sosetsockopt(s_so, IPPROTO_TCP, TCP_NODELAY, + (const void *)&on, sizeof (on)); + (void) sosetsockopt(s_so, SOL_SOCKET, SO_KEEPALIVE, + (const void *)&on, sizeof (on)); + (void) sosetsockopt(s_so, SOL_SOCKET, SO_SNDBUF, + (const void *)&txbuf_size, sizeof (txbuf_size)); + + /* + * Create a session for this connection and notify the SMB + * service state machine. The service state machine may + * start a session thread or reject the session depending + * on the current service state or number of connections. + */ + session = smb_session_create(s_so, SSN_SRVC_TCP_PORT); + smb_svcstate_event(SMB_SVCEVT_SESSION_CREATE, + (uintptr_t)session); + + } + + smb_soshutdown(l_so); + smb_sodestroy(l_so); + si->si_connect_progress &= ~SMB_SI_NBT_CONNECTED; + smb_svcstate_event(SMB_SVCEVT_DISCONNECT, (uintptr_t)error); +} + +/* + * SMB-over-TCP (or NetBIOS-less SMB) service. + * + * SMB service natively over TCP (port 445), i.e. no NetBIOS support. + */ +void +smb_tcp_daemon(smb_thread_t *thread, void *arg) +{ + /* XXX Defaults for these values should come from smbd and SMF */ + uint32_t txbuf_size = 128*1024; + uint32_t on = 1; + struct smb_session *session; + struct sonode *l_so, *s_so; + struct sockaddr_in sin; + int error; + smb_info_t *si = arg; + + ASSERT(si != NULL); + sin.sin_family = AF_INET; + sin.sin_port = htons(SMB_SRVC_TCP_PORT); + sin.sin_addr.s_addr = htonl(INADDR_ANY); + + l_so = smb_socreate(AF_INET, SOCK_STREAM, 0); + if (l_so == NULL) { + cmn_err(CE_WARN, "TCP: socket create failed"); + smb_svcstate_event(SMB_SVCEVT_DISCONNECT, (uintptr_t)ENOMEM); + return; + } + + (void) sosetsockopt(l_so, SOL_SOCKET, SO_REUSEADDR, + (const void *)&on, sizeof (on)); + + if ((error = sobind(l_so, (struct sockaddr *)&sin, sizeof (sin), + 0, 0)) != 0) { + cmn_err(CE_WARN, "TCP: bind failed"); + smb_soshutdown(l_so); + smb_sodestroy(l_so); + smb_svcstate_event(SMB_SVCEVT_DISCONNECT, (uintptr_t)error); + return; + } + if ((error = solisten(l_so, 20)) < 0) { + cmn_err(CE_WARN, "TCP: listen failed"); + smb_soshutdown(l_so); + smb_sodestroy(l_so); + smb_svcstate_event(SMB_SVCEVT_DISCONNECT, (uintptr_t)error); + return; + } + + smb_thread_set_awaken(thread, smb_session_wakeup_daemon, l_so); + si->si_connect_progress |= SMB_SI_TCP_CONNECTED; + smb_svcstate_event(SMB_SVCEVT_CONNECT, NULL); + + while (smb_thread_continue_nowait(thread)) { + DTRACE_PROBE1(so__wait__accept, struct sonode *, l_so); + + error = soaccept(l_so, 0, &s_so); + if (error) { + DTRACE_PROBE1(so__accept__error, int, error); + if (error == EINTR) { + continue; + } + + break; + } + + DTRACE_PROBE1(so__accept, struct sonode *, s_so); + + (void) sosetsockopt(s_so, IPPROTO_TCP, TCP_NODELAY, + (const void *)&on, sizeof (on)); + (void) sosetsockopt(s_so, SOL_SOCKET, SO_KEEPALIVE, + (const void *)&on, sizeof (on)); + (void) sosetsockopt(s_so, SOL_SOCKET, SO_SNDBUF, + (const void *)&txbuf_size, sizeof (txbuf_size)); + + /* + * Create a session for this connection and notify the SMB + * service state machine. The service state machine may + * start a session thread or reject the session depending + * on the current service state or number of connections. + */ + session = smb_session_create(s_so, SMB_SRVC_TCP_PORT); + smb_svcstate_event(SMB_SVCEVT_SESSION_CREATE, + (uintptr_t)session); + + } + + smb_soshutdown(l_so); + smb_sodestroy(l_so); + si->si_connect_progress &= ~SMB_SI_TCP_CONNECTED; + smb_svcstate_event(SMB_SVCEVT_DISCONNECT, (uintptr_t)error); +} + +/* + * smb_session_reject + * + * Build and send a NEGATIVE_SESSION_RESPONSE on the specified socket. + * The reason is written to the log. + */ +/*ARGSUSED*/ +void +smb_session_reject(smb_session_t *session, char *reason) +{ + unsigned char reply[8]; + + smb_rwx_rwenter(&session->s_lock, RW_READER); + if (session->sock != NULL) { + reply[0] = NEGATIVE_SESSION_RESPONSE; + reply[1] = 0; + reply[2] = 0; + reply[3] = 1; + reply[4] = SESSION_INSUFFICIENT_RESOURCES; + + (void) smb_sosend(session->sock, reply, 5); + } + smb_rwx_rwexit(&session->s_lock); +} + +/* + * Port will be SSN_SRVC_TCP_PORT or SMB_SRVC_TCP_PORT. + */ +smb_session_t * +smb_session_create(struct sonode *new_so, uint16_t port) +{ + uint32_t ipaddr; + uint32_t local_ipaddr; + struct sockaddr_in sin; + smb_session_t *session; + + session = kmem_cache_alloc(smb_info.si_cache_session, KM_SLEEP); + bzero(session, sizeof (smb_session_t)); + + if (smb_idpool_constructor(&session->s_uid_pool)) { + kmem_cache_free(smb_info.si_cache_session, session); + return (NULL); + } + + session->s_kid = SMB_NEW_KID(); + session->s_state = SMB_SESSION_STATE_DISCONNECTED; + session->native_os = NATIVE_OS_UNKNOWN; + session->opentime = lbolt64; + session->keep_alive = smb_keep_alive; + session->activity_timestamp = lbolt64; + + smb_slist_constructor(&session->s_req_list, sizeof (smb_request_t), + offsetof(smb_request_t, sr_session_lnd)); + + smb_llist_constructor(&session->s_user_list, sizeof (smb_user_t), + offsetof(smb_user_t, u_lnd)); + + smb_llist_constructor(&session->s_xa_list, sizeof (smb_xa_t), + offsetof(smb_xa_t, xa_lnd)); + + smb_thread_init(&session->s_thread, "smb_session", &smb_session_daemon, + session, smb_wakeup_session_daemon, session); + + smb_rwx_init(&session->s_lock); + + bcopy(new_so->so_faddr_sa, &sin, new_so->so_faddr_len); + ipaddr = sin.sin_addr.s_addr; + + bcopy(new_so->so_laddr_sa, &sin, new_so->so_faddr_len); + local_ipaddr = sin.sin_addr.s_addr; + + session->s_local_port = port; + session->ipaddr = ipaddr; + session->local_ipaddr = local_ipaddr; + session->sock = new_so; + + session->s_magic = SMB_SESSION_MAGIC; + return (session); +} + +void +smb_session_delete(smb_session_t *session) +{ + ASSERT(session); + ASSERT(session->s_magic == SMB_SESSION_MAGIC); + + session->s_magic = (uint32_t)~SMB_SESSION_MAGIC; + + smb_rwx_destroy(&session->s_lock); + smb_thread_destroy(&session->s_thread); + smb_slist_destructor(&session->s_req_list); + smb_llist_destructor(&session->s_user_list); + smb_llist_destructor(&session->s_xa_list); + + ASSERT(session->s_tree_cnt == 0); + ASSERT(session->s_file_cnt == 0); + ASSERT(session->s_dir_cnt == 0); + + smb_idpool_destructor(&session->s_uid_pool); + kmem_cache_free(smb_info.si_cache_session, session); +} + +void +smb_session_cancel(smb_session_t *session) +{ + smb_xa_t *xa, *nextxa; + + /* All the request currently being treated must be canceled. */ + smb_session_cancel_requests(session); + + /* + * We wait for the completion of all the requests associated with + * this session. + */ + smb_slist_wait_for_empty(&session->s_req_list); + + /* + * At this point the reference count of the users, trees, files, + * directories should be zero. It should be possible to destroy them + * without any problem. + */ + xa = smb_llist_head(&session->s_xa_list); + while (xa) { + nextxa = smb_llist_next(&session->s_xa_list, xa); + smb_xa_close(xa); + xa = nextxa; + } + smb_user_logoff_all(session); +} + +void +smb_session_cancel_requests( + smb_session_t *session) +{ + smb_request_t *sr; + smb_request_t *tmp; + + /* All the SMB requests on the notification queue are canceled. */ + smb_process_session_notify_change_queue(session); + + smb_slist_enter(&session->s_req_list); + sr = smb_slist_head(&session->s_req_list); + while (sr) { + ASSERT(sr->sr_magic == SMB_REQ_MAGIC); + tmp = smb_slist_next(&session->s_req_list, sr); + + smb_request_cancel(sr); + + sr = tmp; + } + smb_slist_exit(&session->s_req_list); +} + +void +smb_session_worker( + void *arg) +{ + smb_request_t *sr; + + sr = (smb_request_t *)arg; + + ASSERT(sr != NULL); + ASSERT(sr->sr_magic == SMB_REQ_MAGIC); + + mutex_enter(&sr->sr_mutex); + switch (sr->sr_state) { + case SMB_REQ_STATE_SUBMITTED: + mutex_exit(&sr->sr_mutex); + if (smb_dispatch_request(sr) < 0) { + smb_rwx_rwenter(&sr->session->s_lock, RW_WRITER); + if (sr->session->s_state != + SMB_SESSION_STATE_DISCONNECTED) { + smb_soshutdown(sr->session->sock); + sr->session->s_state = + SMB_SESSION_STATE_DISCONNECTED; + } + smb_rwx_rwexit(&sr->session->s_lock); + } + mutex_enter(&sr->sr_mutex); + if (!sr->sr_keep) { + sr->sr_state = SMB_REQ_STATE_COMPLETED; + mutex_exit(&sr->sr_mutex); + smb_request_free(sr); + break; + } + mutex_exit(&sr->sr_mutex); + break; + + default: + ASSERT(sr->sr_state == SMB_REQ_STATE_CANCELED); + sr->sr_state = SMB_REQ_STATE_COMPLETED; + mutex_exit(&sr->sr_mutex); + smb_request_free(sr); + break; + } +} + +/* + * smb_session_disconnect_share + * + * Disconnects the specified share. This function should be called after the + * share passed in has been made unavailable by the "share manager". + */ +void +smb_session_disconnect_share(char *sharename) +{ + smb_session_t *session; + + smb_svcstate_lock_read(&smb_info.si_svc_sm_ctx); + + session = NULL; + while ((session = smb_svcstate_session_getnext(&smb_info.si_svc_sm_ctx, + session)) != NULL) { + + ASSERT(session->s_magic == SMB_SESSION_MAGIC); + smb_rwx_rwenter(&session->s_lock, RW_READER); + switch (session->s_state) { + case SMB_SESSION_STATE_NEGOTIATED: + case SMB_SESSION_STATE_OPLOCK_BREAKING: + case SMB_SESSION_STATE_WRITE_RAW_ACTIVE: { + smb_user_t *user; + smb_user_t *next; + + user = smb_user_lookup_by_state(session, NULL); + while (user) { + smb_user_disconnect_share(user, sharename); + next = smb_user_lookup_by_state(session, user); + smb_user_release(user); + user = next; + } + break; + + } + default: + break; + } + smb_rwx_rwexit(&session->s_lock); + } + smb_svcstate_unlock(&smb_info.si_svc_sm_ctx); +} + +/* + * smb_session_disconnect_volume + * + * This function is called when a volume is deleted. We need to ensure + * all trees with a reference to the volume are destroyed before we + * discard the fs_online. Before destroying each tree, we notify any + * in-progress requests and give them a chance to complete. + * + * NOTE: + * We shouldn't be accepting any new connection on this volume while + * we are in this function. + */ +void +smb_session_disconnect_volume(fs_desc_t *fsd) +{ + smb_session_t *session; + + smb_svcstate_lock_read(&smb_info.si_svc_sm_ctx); + + session = NULL; + while ((session = smb_svcstate_session_getnext(&smb_info.si_svc_sm_ctx, + session)) != NULL) { + + ASSERT(session->s_magic == SMB_SESSION_MAGIC); + smb_rwx_rwenter(&session->s_lock, RW_READER); + switch (session->s_state) { + case SMB_SESSION_STATE_NEGOTIATED: + case SMB_SESSION_STATE_OPLOCK_BREAKING: + case SMB_SESSION_STATE_WRITE_RAW_ACTIVE: { + smb_user_t *user; + smb_user_t *next; + + user = smb_user_lookup_by_state(session, NULL); + while (user) { + smb_user_disconnect_volume(user, fsd); + next = smb_user_lookup_by_state(session, user); + smb_user_release(user); + user = next; + } + break; + + } + default: + break; + } + smb_rwx_rwexit(&session->s_lock); + } + smb_svcstate_unlock(&smb_info.si_svc_sm_ctx); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_session_setup_andx.c b/usr/src/uts/common/fs/smbsrv/smb_session_setup_andx.c new file mode 100644 index 000000000000..b7217cf78b41 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_session_setup_andx.c @@ -0,0 +1,516 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +#pragma ident "%Z%%M% %I% %E% SMI" +/* + * SMB: session_setup_andx + * + * This SMB is used to further "Set up" the session normally just + * established via the negotiate protocol. + * + * One primary function is to perform a "user logon" in the case where the + * server is in user level security mode. The Uid in the SMB header is set + * by the client to be the userid desired for the AccountName and validated + * by the AccountPassword. + * + * If the negotiated protocol is prior to NT LM 0.12, the format of + * SMB_COM_SESSION_SETUP_ANDX is: + * + * Client Request Description + * ============================== ===================================== + * + * UCHAR WordCount; Count of parameter words = 10 + * UCHAR AndXCommand; Secondary (X) command; 0xFF = none + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * USHORT MaxBufferSize; Client maximum buffer size + * USHORT MaxMpxCount; Actual maximum multiplexed pending + * requests + * USHORT VcNumber; 0 = first (only), nonzero=additional + * VC number + * ULONG SessionKey; Session key (valid iff VcNumber != 0) + * USHORT PasswordLength; Account password size + * ULONG Reserved; Must be 0 + * USHORT ByteCount; Count of data bytes; min = 0 + * UCHAR AccountPassword[]; Account Password + * STRING AccountName[]; Account Name + * STRING PrimaryDomain[]; Client's primary domain + * STRING NativeOS[]; Client's native operating system + * STRING NativeLanMan[]; Client's native LAN Manager type + * + * and the response is: + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 3 + * UCHAR AndXCommand; Secondary (X) command; 0xFF = + * none + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * USHORT Action; Request mode: + * bit0 = logged in as GUEST + * USHORT ByteCount; Count of data bytes + * STRING NativeOS[]; Server's native operating system + * STRING NativeLanMan[]; Server's native LAN Manager type + * STRING PrimaryDomain[]; Server's primary domain + * + * If the server is in "share level security mode", the account name and + * passwd should be ignored by the server. + * + * If challenge/response authentication is not being used, AccountPassword + * should be a null terminated ASCII string with PasswordLength set to the + * string size including the null; the password will case insensitive. If + * challenge/response authentication is being used (see section 2.10), then + * AccountPassword will be the response to the server's challenge, and + * PasswordLength should be set to its length. + * + * The server validates the name and password supplied and if valid, it + * registers the user identifier on this session as representing the + * specified AccountName. The Uid field in the SMB header will then be + * used to validate access on subsequent SMB requests. The SMB requests + * where permission checks are required are those which refer to a + * symbolically named resource such as SMB_COM_OPEN, SMB_COM_RENAME, + * SMB_COM_DELETE, etc.. The value of the Uid is relative to a specific + * client/server session so it is possible to have the same Uid value + * represent two different users on two different sessions at the server. + * + * Multiple session setup commands may be sent to register additional users + * on this session. If the server receives an additional + * SMB_COM_SESSION_SETUP_ANDX, only the Uid, AccountName and + * AccountPassword fields need contain valid values (the server MUST ignore + * the other fields). + * + * The client writes the name of its domain in PrimaryDomain if it knows + * what the domain name is. If the domain name is unknown, the client + * either encodes it as a NULL string, or as a question mark. + * + * If bit0 of Action is set, this informs the client that although the + * server did not recognize the AccountName, it logged the user in as a + * guest. This is optional behavior by the server, and in any case one + * would ordinarily expect guest privileges to limited. + * + * Another function of the Session Set Up protocol is to inform the server + * of the maximum values which will be utilized by this client. Here + * MaxBufferSize is the maximum message size which the client can receive. + * Thus although the server may support 16k buffers (as returned in the + * SMB_COM_NEGOTIATE response), if the client only has 4k buffers, the + * value of MaxBufferSize here would be 4096. The minimum allowable value + * for MaxBufferSize is 1024. The SMB_COM_NEGOTIATE response includes the + * server buffer size supported. Thus this is the maximum SMB message size + * which the client can send to the server. This size may be larger than + * the size returned to the server from the client via the + * SMB_COM_SESSION_SETUP_AND X protocol which is the maximum SMB message + * size which the server may send to the client. Thus if the server's + * buffer size were 4k and the client's buffer size were only 2K, the + * client could send up to 4k (standard) write requests but must only + * request up to 2k for (standard) read requests. + * + * The field, MaxMpxCount informs the server of the maximum number of + * requests which the client will have outstanding to the server + * simultaneously (see sections 5.13 and 5.25). + * + * The VcNumber field specifies whether the client wants this to be the + * first VC or an additional VC. If the the SMB_COM_SESSION_SETUP_ANDX + * request contains a VcNumber of 0 and other VCs are still connected to + * that client, they should be aborted to free any resources held by the + * server. This condition could occur if the client was rebooted and + * reconnected to the server before the transport level had informed the + * server of the previous VC termination. There is more information on + * VCs in smb_negotiate.c. + * + * The values for MaxBufferSize, MaxMpxCount, and VcNumber must be less + * than or equal to the maximum values supported by the server as returned + * in the SMB_COM_NEGOTIATE response. + * + * If the negotiated SMB dialect is "NT LM 0.12" or later, the format of + * the response SMB is unchanged, but the request is: + * + * Client Request Description + * ============================== ===================================== + * + * UCHAR WordCount; Count of parameter words = 13 + * UCHAR AndXCommand; Secondary (X) command; 0xFF = none + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * USHORT MaxBufferSize; Client's maximum buffer size + * USHORT MaxMpxCount; Actual maximum multiplexed pending + * requests + * USHORT VcNumber; 0 = first (only), nonzero=additional + * VC number + * ULONG SessionKey; Session key (valid iff VcNumber != 0) + * USHORT Account password size, ANSI + * CaseInsensitivePasswordLength; + * USHORT Account password size, Unicode + * CaseSensitivePasswordLength; + * ULONG Reserved; must be 0 + * ULONG Capabilities; Client capabilities + * USHORT ByteCount; Count of data bytes; min = 0 + * UCHAR Account Password, ANSI + * CaseInsensitivePassword[]; + * UCHAR CaseSensitivePassword[]; Account Password, Unicode + * STRING AccountName[]; Account Name, Unicode + * STRING PrimaryDomain[]; Client's primary domain, Unicode + * STRING NativeOS[]; Client's native operating system, + * Unicode + * STRING NativeLanMan[]; Client's native LAN Manager type, + * Unicode + * + * The client expresses its capabilities to the server encoded in the + * Capabilities field: + * + * Capability Name Encoding Description + * ======================== ========= ================================ + * + * CAP_UNICODE 0x0004 The client can use UNICODE + * strings + * CAP_LARGE_FILES 0x0008 The client can deal with files + * having 64 bit offsets + * CAP_NT_SMBS 0x0010 The client understands the SMBs + * introduced with the NT LM 0.12 + * dialect. Implies CAP_NT_FIND. + * CAP_NT_FIND 0x0200 + * CAP_STATUS32 0x0040 The client can receive 32 bit + * errors encoded in Status.Status + * CAP_LEVEL_II_OPLOCKS 0x0080 The client understands Level II + * oplocks + * + * The entire message sent and received including the optional ANDX SMB + * must fit in the negotiated maximum transfer size. The following are the + * only valid SMB commands for AndXCommand for SMB_COM_SESSION_SETUP_ANDX + * + * SMB_COM_TREE_CONNECT_ANDX SMB_COM_OPEN + * SMB_COM_OPEN_ANDX SMB_COM_CREATE + * SMB_COM_CREATE_NEW SMB_COM_CREATE_DIRECTORY + * SMB_COM_DELETE SMB_COM_DELETE_DIRECTORY + * SMB_COM_FIND SMB_COM_FIND_UNIQUE + * SMB_COM_COPY SMB_COM_RENAME + * SMB_COM_NT_RENAME SMB_COM_CHECK_DIRECTORY + * SMB_COM_QUERY_INFORMATION SMB_COM_SET_INFORMATION + * SMB_COM_NO_ANDX_COMMAND SMB_COM_OPEN_PRINT_FILE + * SMB_COM_GET_PRINT_QUEUE SMB_COM_TRANSACTION + * + * 4.1.2.1 Errors + * + * ERRSRV/ERRerror - no NEG_PROT issued + * ERRSRV/ERRbadpw - password not correct for given user name + * ERRSRV/ERRtoomanyuids - maximum number of users per session exceeded + * ERRSRV/ERRnosupport - chaining of this request to the previous one is + * not supported + */ + +#include +#include +#include +#include +#include +#include + +int +smb_com_session_setup_andx(struct smb_request *sr) +{ + uint16_t maxbufsize, maxmpxcount, vcnumber = 0; + uint32_t sesskey; + uint32_t capabilities = 0; + char *account_name = ""; + char *primary_domain = ""; + char *native_os = ""; + char *native_lanman = ""; + char *hostname = smb_info.si.skc_hostname; + smb_token_t *usr_token = NULL; + smb_user_t *user = NULL; + int security = smb_info.si.skc_secmode; + + uint16_t ci_pwlen = 0; + unsigned char *ci_password = NULL; + uint16_t cs_pwlen = 0; + unsigned char *cs_password = NULL; + + netr_client_t clnt_info; + smb_session_key_t *session_key = NULL; + int rc; + + if (sr->session->dialect >= NT_LM_0_12) { + rc = smbsr_decode_vwv(sr, "b.wwwwlww4.l", &sr->andx_com, + &sr->andx_off, &maxbufsize, &maxmpxcount, &vcnumber, + &sesskey, &ci_pwlen, &cs_pwlen, &capabilities); + + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + ci_password = kmem_alloc(ci_pwlen + 1, KM_SLEEP); + cs_password = kmem_alloc(cs_pwlen + 1, KM_SLEEP); + + /* + * The padding between the Native OS and Native LM is a + * bit strange. On NT4.0, there is a 2 byte pad between + * the OS (Windows NT 1381) and LM (Windows NT 4.0). + * On Windows 2000, there is no padding between the OS + * (Windows 2000 2195) and LM (Windows 2000 5.0). + * + * If the padding is removed from this decode string + * the NT4.0 LM comes out as an empty string. + * + * So if the client's native OS is Win NT we consider + * the padding otherwise we don't. + */ + rc = smbsr_decode_data(sr, "%#c#cuuu", + sr, + ci_pwlen, ci_password, + cs_pwlen, cs_password, + &account_name, + &primary_domain, + &native_os); + + if (rc != 0) { + kmem_free(ci_password, ci_pwlen + 1); + kmem_free(cs_password, cs_pwlen + 1); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + ci_password[ci_pwlen] = 0; + cs_password[cs_pwlen] = 0; + + sr->session->native_os = smbnative_os_value(native_os); + + if (sr->session->native_os == NATIVE_OS_WINNT) + rc = smbsr_decode_data(sr, "%,u", sr, &native_lanman); + else + rc = smbsr_decode_data(sr, "%u", sr, &native_lanman); + + /* + * Native Lanman could be null so we really don't care + * if above decode fails, but to have a valid value for + * the field we set it to Win NT. + */ + if (rc != 0) + native_lanman = "NT LAN Manager 4.0"; + + } else { + rc = smbsr_decode_vwv(sr, "b.wwwwlw4.", &sr->andx_com, + &sr->andx_off, &maxbufsize, &maxmpxcount, + &vcnumber, &sesskey, &ci_pwlen); + + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + ci_password = kmem_alloc(ci_pwlen + 1, KM_SLEEP); + rc = smbsr_decode_data(sr, "%#c", sr, ci_pwlen, ci_password); + if (rc != 0) { + kmem_free(ci_password, ci_pwlen + 1); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + ci_password[ci_pwlen] = 0; + + /* + * Despite the CIFS/1.0 spec, the rest of this message is + * not always present. We need to try to get the account + * name and the primary domain but we don't care about the + * the native OS or native LanMan fields. + */ + if (smbsr_decode_data(sr, "%u", sr, &account_name) != 0) + account_name = ""; + + if (smbsr_decode_data(sr, "%u", sr, &primary_domain) != 0) + primary_domain = ""; + + sr->session->native_os = NATIVE_OS_UNKNOWN; + } + + /* + * If the vcnumber is zero, we can discard any + * other connections associated with this client. + */ + sr->session->vcnumber = vcnumber; + if (vcnumber == 0) + smb_reconnection_check(sr->session); + + sr->session->smb_msg_size = maxbufsize; + + bzero(&clnt_info, sizeof (netr_client_t)); + + if (*primary_domain == 0) + primary_domain = smb_info.si.skc_resource_domain; + + if ((cs_pwlen == 0) && + (ci_pwlen == 0 || (ci_pwlen == 1 && *ci_password == 0))) { + /* anonymous user */ + clnt_info.flags |= NETR_CFLG_ANON; + account_name = "nobody"; + } else if (*account_name == '\0') { + if (ci_password) + kmem_free(ci_password, ci_pwlen + 1); + if (cs_password) + kmem_free(cs_password, cs_pwlen + 1); + smbsr_raise_error(sr, ERRSRV, ERRaccess); + /* NOTREACHED */ + } else if (utf8_strcasecmp(primary_domain, hostname) == 0) { + /* + * When domain name is equal to hostname, it means + * the user is local even if system is running in + * domain mode, so perform a local logon. + */ + clnt_info.flags |= NETR_CFLG_LOCAL; + } else if (security == SMB_SECMODE_DOMAIN) { + clnt_info.flags |= NETR_CFLG_DOMAIN; + } else if (security == SMB_SECMODE_WORKGRP) { + clnt_info.flags |= NETR_CFLG_LOCAL; + } + + (void) utf8_strupr(primary_domain); + + /* + * If this is an additional setup for an existing user + * on this session, duplicate the authenticated user. + * Otherwise authenticate as new user. + */ + user = smb_user_lookup_by_name(sr->session, primary_domain, + account_name); + + if (user) { + smb_user_t *orig_user = user; + + user = smb_user_dup(orig_user); + smb_user_release(orig_user); + } else { + cred_t *cr; + uint32_t privileges; + + clnt_info.logon_level = NETR_NETWORK_LOGON; + clnt_info.domain = primary_domain; + clnt_info.username = account_name; + clnt_info.workstation = sr->session->workstation; + clnt_info.ipaddr = sr->session->ipaddr; + clnt_info.local_ipaddr = sr->session->local_ipaddr; + clnt_info.challenge_key.challenge_key_val = + sr->session->challenge_key; + clnt_info.challenge_key.challenge_key_len = + sr->session->challenge_len; + clnt_info.nt_password.nt_password_val = cs_password; + clnt_info.nt_password.nt_password_len = cs_pwlen; + clnt_info.lm_password.lm_password_val = ci_password; + clnt_info.lm_password.lm_password_len = ci_pwlen; + clnt_info.native_os = sr->session->native_os; + clnt_info.native_lm = smbnative_lm_value(native_lanman); + clnt_info.local_port = sr->session->s_local_port; + + DTRACE_PROBE1(smb__sessionsetup__clntinfo, netr_client_t *, + &clnt_info); + + usr_token = smb_upcall_get_token(&clnt_info); + if (usr_token == 0) { + if (ci_password) + kmem_free(ci_password, ci_pwlen + 1); + if (cs_password) + kmem_free(cs_password, cs_pwlen + 1); + smbsr_raise_error(sr, ERRSRV, ERRbadpw); + /* NOTREACHED */ + } + + if (usr_token->tkn_session_key) { + session_key = kmem_alloc(sizeof (smb_session_key_t), + KM_SLEEP); + (void) memcpy(session_key, usr_token->tkn_session_key, + sizeof (smb_session_key_t)); + } + + cr = smb_cred_create(usr_token, &privileges); + if (cr != NULL) { + user = smb_user_login(sr->session, cr, + usr_token->tkn_domain_name, + usr_token->tkn_account_name, + usr_token->tkn_flags, + privileges, + usr_token->tkn_audit_sid); + smb_cred_rele(cr); + } + smb_token_free(usr_token); + } + + if (ci_password) + kmem_free(ci_password, ci_pwlen + 1); + if (cs_password) + kmem_free(cs_password, cs_pwlen + 1); + + if (user == NULL) { + if (session_key) + kmem_free(session_key, sizeof (smb_session_key_t)); + smbsr_raise_error(sr, ERRDOS, ERROR_INVALID_HANDLE); + /* no return */ + } + + sr->user_cr = user->u_cred; + sr->smb_uid = user->u_uid; + sr->uid_user = user; + sr->session->capabilities = capabilities; + + /* + * Check to see if SMB signing is enable, but if it is already turned + * on leave it. + * The first authenticated logon provides the MAC key and sequence + * numbers for signing all further session on the + * same network connection. + */ + if (!(sr->session->signing.flags & SMB_SIGNING_ENABLED) && + (sr->session->secmode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED) && + (sr->smb_flg2 & SMB_FLAGS2_SMB_SECURITY_SIGNATURE) && + session_key) + smb_sign_init(sr, session_key, (char *)cs_password, cs_pwlen); + + if (session_key) + kmem_free(session_key, sizeof (smb_session_key_t)); + + /* + * NT systems use different native OS and native LanMan values + * dependent on whether they are acting as a client or a server. + * As a server, NT 4.0 responds with the following values: + * + * NativeOS: Windows NT 4.0 + * NativeLM: NT LAN Manager 4.0 + * + * We should probably use the same values as NT but this code has + * been using the product name and "Windows NT 4.0" for a long time + * and I don't know if a change would cause any problems (see the + * conditional test below). + */ + smbsr_encode_result(sr, 3, VAR_BCC, "bb.www%uuu", + 3, + sr->andx_com, + -1, /* andx_off */ + ((user->u_flags & SMB_USER_FLAG_GUEST) ? 1 : 0), + VAR_BCC, + sr, + "Windows NT 4.0", + "NT LAN Manager 4.0", + smb_info.si.skc_resource_domain); + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_set_information.c b/usr/src/uts/common/fs/smbsrv/smb_set_information.c new file mode 100644 index 000000000000..728c86b8cd00 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_set_information.c @@ -0,0 +1,128 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: set_information + * + * This message is sent to change the information about a file. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 8 + * USHORT FileAttributes; Attributes of the file + * UTIME LastWriteTime; Time of last write + * USHORT Reserved [5]; Reserved (must be 0) + * USHORT ByteCount; Count of data bytes; min = 2 + * UCHAR BufferFormat; 0x04 + * STRING FileName[]; File name + * + * FileName is the fully qualified name of the file relative to the Tid. + * + * Support of all parameters is optional. A server which does not + * implement one of the parameters will ignore that field. If the + * LastWriteTime field contain zero then the file's time is not changed. + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes = 0 + * + */ + +#include +#include + +int +smb_com_set_information(struct smb_request *sr) +{ + int rc; + unsigned short dattr; + timestruc_t utime; + char *path; + struct smb_node *dir_node; + smb_attr_t attr; + struct smb_node *node; + char *name; + + name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + if (smbsr_decode_vwv(sr, "wl10.", &dattr, &utime.tv_sec) != 0) { + kmem_free(name, MAXNAMELEN); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (smbsr_decode_data(sr, "%S", sr, &path) != 0) { + kmem_free(name, MAXNAMELEN); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + utime.tv_nsec = 0; + + if ((rc = smb_pathname_reduce(sr, sr->user_cr, path, + sr->tid_tree->t_snode, sr->tid_tree->t_snode, &dir_node, name)) + != 0) { + kmem_free(name, MAXNAMELEN); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + if ((rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, + sr->tid_tree->t_snode, dir_node, name, &node, &attr, 0, 0)) != 0) { + smb_node_release(dir_node); + kmem_free(name, MAXNAMELEN); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + smb_node_release(dir_node); + + smb_node_set_dosattr(node, dattr); + + /* + * IR101794 The behaviour when the time field is set to -1 + * is not documented, so we'll assume it should be treated + * like 0. We ignore utime.tv_nsec is assumed to be 0 here. + */ + if (utime.tv_sec != 0 && utime.tv_sec != -1) { + utime.tv_sec = smb_local_time_to_gmt(utime.tv_sec); + smb_node_set_time(node, 0, &utime, 0, 0, SMB_AT_MTIME); + } + + rc = smb_sync_fsattr(sr, sr->user_cr, node); + smb_node_release(node); + if (rc) { + kmem_free(name, MAXNAMELEN); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + smbsr_encode_empty_result(sr); + kmem_free(name, MAXNAMELEN); + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_set_information2.c b/usr/src/uts/common/fs/smbsrv/smb_set_information2.c new file mode 100644 index 000000000000..1510f7228a8b --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_set_information2.c @@ -0,0 +1,123 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: set_information2 + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 7 + * USHORT Fid; File handle + * SMB_DATE CreationDate; + * SMB_TIME CreationTime; + * SMB_DATE LastAccessDate; + * SMB_TIME LastAccessTime; + * SMB_DATE LastWriteDate; + * SMB_TIME LastWriteTime; + * USHORT ByteCount; Count of data bytes = 0 + * + * SMB_COM_SET_INFORMATION2 sets information about the file represented by + * Fid. The target file is updated from the values specified. A date or + * time value or zero indicates to leave that specific date and time + * unchanged. + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes = 0 + * + * Fid must be open with (at least) write permission. + */ + +#include + +int +smb_com_set_information2(struct smb_request *sr) +{ + unsigned short la_ddate, la_dtime; + unsigned short lw_ddate, lw_dtime; + unsigned short cr_ddate, cr_dtime; + timestruc_t crtime, mtime, atime; + unsigned int what = 0; + struct smb_node *node; + int rc; + + rc = smbsr_decode_vwv(sr, "wwwwwww", &sr->smb_fid, &cr_ddate, &cr_dtime, + &la_ddate, &la_dtime, &lw_ddate, &lw_dtime); + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + node = sr->fid_ofile->f_node; + + if (node == 0 || sr->fid_ofile->f_ftype != SMB_FTYPE_DISK) { + cmn_err(CE_NOTE, "SmbSetInfo2: access denied"); + smbsr_raise_error(sr, ERRDOS, ERRnoaccess); + /* NOTREACHED */ + } + + crtime.tv_nsec = mtime.tv_nsec = atime.tv_nsec = 0; + + if (cr_ddate || cr_dtime) { + crtime.tv_sec = smb_local_time_to_gmt( + dosfs_dos_to_ux_time(cr_ddate, cr_dtime)); + what |= SMB_AT_CRTIME; + } + + if (lw_ddate || lw_dtime) { + mtime.tv_sec = smb_local_time_to_gmt( + dosfs_dos_to_ux_time(lw_ddate, lw_dtime)); + what |= SMB_AT_MTIME; + } + + if (la_ddate || la_dtime) { + atime.tv_sec = smb_local_time_to_gmt( + dosfs_dos_to_ux_time(la_ddate, la_dtime)); + what |= SMB_AT_ATIME; + } + + smb_node_set_time(node, &crtime, &mtime, &atime, 0, what); + rc = smb_sync_fsattr(sr, sr->user_cr, node); + if (rc) { + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + smbsr_encode_empty_result(sr); + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_share_kdoor_client.c b/usr/src/uts/common/fs/smbsrv/smb_share_kdoor_client.c new file mode 100644 index 000000000000..63a7f5d31486 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_share_kdoor_client.c @@ -0,0 +1,730 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Kernel door client for LanMan share management. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +door_handle_t lmshrd_dh; +int lmshrd_init = 0; +kmutex_t lmshrd_dh_mtx; + + +char *lmshrd_desc[] = { + "", + "LmshrkOpenIter", + "LmshrkCloseIter", + "LmshrkIterate", + "LmshrkNumShares", + "", + "", + "LmshrkGetinfo", + "", + "", + "LmshrkExists", + "LmshrkIsSpecial", + "LmshrkIsRestricted", + "LmshrkIsAdmin", + "LmshrkIsValid", + "LmshrkIsDir", + "LmshrkList", + "LmshrkListTrans", + "LmshrkNumTrans", + "SmbGetKConfig", + 0 +}; + + +static int +lmshrd_kclient_open() +{ + int err = 0; + + mutex_enter(&lmshrd_dh_mtx); + if (lmshrd_init == 0) { + if ((err = door_ki_open(LMSHR_DOOR_NAME, &lmshrd_dh)) != 0) + cmn_err(CE_WARN, "lmshrd_kclient: open %s failed", + LMSHR_DOOR_NAME); + else + lmshrd_init = 1; + } + mutex_exit(&lmshrd_dh_mtx); + + return (err); +} + +static void +lmshrd_kclient_close() +{ + mutex_enter(&lmshrd_dh_mtx); + if (lmshrd_init) { + door_ki_rele(lmshrd_dh); + lmshrd_init = 0; + } + mutex_exit(&lmshrd_dh_mtx); +} + +/* + * lmshrd_kclient_start + * + * The SMB kernel module should invoke this function upon startup. + */ +int +lmshrd_kclient_start() +{ + int rc; + + mutex_init(&lmshrd_dh_mtx, NULL, MUTEX_DEFAULT, NULL); + rc = lmshrd_kclient_open(); + + return (rc); +} + +/* + * lmshrd_kclient_stop + * + * The SMB kernel module should invoke this function upon unload. + */ +void +lmshrd_kclient_stop() +{ + lmshrd_kclient_close(); + mutex_destroy(&lmshrd_dh_mtx); +} + +/* + * Return 0 upon success. Otherwise, -1. + */ +static int +lmshrd_door_check_srv_status(int opcode, smb_dr_ctx_t *dec_ctx) +{ + int status = smb_dr_get_int32(dec_ctx); + int err; + int rc = -1; + + switch (status) { + case LMSHR_DOOR_SRV_SUCCESS: + rc = 0; + break; + + case LMSHR_DOOR_SRV_ERROR: + err = smb_dr_get_uint32(dec_ctx); + cmn_err(CE_WARN, "%s: Encountered door server error %d", + lmshrd_desc[opcode], err); + break; + + default: + cmn_err(CE_WARN, "%s: Unknown door server status", + lmshrd_desc[opcode]); + } + + if (rc != 0) { + if ((err = smb_dr_decode_finish(dec_ctx)) != 0) + cmn_err(CE_WARN, "%s: Decode error %d", + lmshrd_desc[opcode], err); + } + + return (rc); +} + +uint64_t +lmshrd_open_iterator(int mode) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + unsigned int status = 0; + uint64_t lmshr_iter = 0; + + int opcode = LMSHR_DOOR_OPEN_ITERATOR; + + if (!lmshrd_init && lmshrd_kclient_open() != 0) + return (lmshr_iter); + + buf = MEM_MALLOC("lmshrd_kclient", LMSHR_DOOR_SIZE); + if (!buf) + return (lmshr_iter); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_int32(enc_ctx, mode); + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + cmn_err(CE_WARN, "%s: Encode error %d", + lmshrd_desc[opcode], status); + MEM_FREE("lmshrd_kclient", buf); + return (lmshr_iter); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_ki_upcall(lmshrd_dh, &arg) != 0) { + cmn_err(CE_WARN, "%s: Door call failed", lmshrd_desc[opcode]); + MEM_FREE("lmshrd_kclient", buf); + lmshrd_kclient_close(); + return (lmshr_iter); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + MEM_FREE("lmshrd_kclient", buf); + return (lmshr_iter); + } + + lmshr_iter = smb_dr_get_lmshr_iterator(dec_ctx); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + cmn_err(CE_WARN, "%s: Decode error %d", + lmshrd_desc[opcode], status); + MEM_FREE("lmshrd_kclient", buf); + return (lmshr_iter); + } + + MEM_FREE("lmshrd_kclient", buf); + return (lmshr_iter); +} + + +DWORD +lmshrd_close_iterator(uint64_t iterator) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + unsigned int status = 0; + int opcode = LMSHR_DOOR_CLOSE_ITERATOR; + + if (!lmshrd_init && lmshrd_kclient_open() != 0) + return (NERR_InternalError); + + buf = MEM_MALLOC("lmshrd_kclient", LMSHR_DOOR_SIZE); + if (!buf) + return (NERR_InternalError); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_lmshr_iterator(enc_ctx, iterator); + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + cmn_err(CE_WARN, "%s: Encode error %d", + lmshrd_desc[opcode], status); + MEM_FREE("lmshrd_kclient", buf); + return (NERR_InternalError); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_ki_upcall(lmshrd_dh, &arg) != 0) { + cmn_err(CE_WARN, "%s: Door call failed", lmshrd_desc[opcode]); + MEM_FREE("lmshrd_kclient", buf); + lmshrd_kclient_close(); + return (NERR_InternalError); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + MEM_FREE("lmshrd_kclient", buf); + return (NERR_InternalError); + } + + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + cmn_err(CE_WARN, "%s: Decode error %d", + lmshrd_desc[opcode], status); + MEM_FREE("lmshrd_kclient", buf); + return (NERR_InternalError); + } + + MEM_FREE("lmshrd_kclient", buf); + return (NERR_Success); +} + +DWORD +lmshrd_iterate(uint64_t iterator, lmshare_info_t *si) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + unsigned int status = 0; + int opcode = LMSHR_DOOR_ITERATE; + + if (!lmshrd_init && lmshrd_kclient_open() != 0) + return (NERR_InternalError); + + buf = MEM_MALLOC("lmshrd_kclient", LMSHR_DOOR_SIZE); + if (!buf) + return (NERR_InternalError); + + bzero(si, sizeof (lmshare_info_t)); + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_lmshr_iterator(enc_ctx, iterator); + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + cmn_err(CE_WARN, "%s: Encode error %d", + lmshrd_desc[opcode], status); + MEM_FREE("lmshrd_kclient", buf); + return (NERR_InternalError); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_ki_upcall(lmshrd_dh, &arg) != 0) { + cmn_err(CE_WARN, "%s: Door call failed", lmshrd_desc[opcode]); + MEM_FREE("lmshrd_kclient", buf); + lmshrd_kclient_close(); + return (NERR_InternalError); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + MEM_FREE("lmshrd_kclient", buf); + return (NERR_InternalError); + } + + smb_dr_get_lmshare(dec_ctx, si); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + cmn_err(CE_WARN, "%s: Decode error %d", + lmshrd_desc[opcode], status); + MEM_FREE("lmshrd_kclient", buf); + return (NERR_InternalError); + } + + MEM_FREE("lmshrd_kclient", buf); + return (NERR_Success); +} + +int +lmshrd_num_shares(void) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + unsigned int status = 0; + DWORD num_shares; + int opcode = LMSHR_DOOR_NUM_SHARES; + + if (!lmshrd_init && lmshrd_kclient_open() != 0) + return (-1); + + buf = MEM_MALLOC("lmshrd_kclient", LMSHR_DOOR_SIZE); + if (!buf) + return (-1); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + cmn_err(CE_WARN, "%s: Encode error %d", + lmshrd_desc[opcode], status); + MEM_FREE("lmshrd_kclient", buf); + return (-1); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_ki_upcall(lmshrd_dh, &arg) != 0) { + cmn_err(CE_WARN, "%s: Door call failed", lmshrd_desc[opcode]); + MEM_FREE("lmshrd_kclient", buf); + lmshrd_kclient_close(); + return (-1); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + MEM_FREE("lmshrd_kclient", buf); + return (-1); + } + + num_shares = smb_dr_get_uint32(dec_ctx); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + cmn_err(CE_WARN, "%s: Decode error %d", + lmshrd_desc[opcode], status); + MEM_FREE("lmshrd_kclient", buf); + return (-1); + } + + MEM_FREE("lmshrd_kclient", buf); + return (num_shares); +} + +DWORD +lmshrd_getinfo(char *share_name, lmshare_info_t *si) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + int status; + DWORD rc; + int opcode = LMSHR_DOOR_GETINFO; + + if (!lmshrd_init && lmshrd_kclient_open() != 0) + return (NERR_InternalError); + + buf = MEM_MALLOC("lmshrd_kclient", LMSHR_DOOR_SIZE); + if (!buf) + return (NERR_InternalError); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_string(enc_ctx, share_name); + + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + cmn_err(CE_WARN, "%s: Encode error %d", + lmshrd_desc[opcode], status); + MEM_FREE("lmshrd_kclient", buf); + return (NERR_InternalError); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_ki_upcall(lmshrd_dh, &arg) != 0) { + cmn_err(CE_WARN, "%s: Door call failed", lmshrd_desc[opcode]); + MEM_FREE("lmshrd_kclient", buf); + lmshrd_kclient_close(); + return (NERR_InternalError); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + MEM_FREE("lmshrd_kclient", buf); + return (NERR_InternalError); + } + + rc = smb_dr_get_uint32(dec_ctx); + smb_dr_get_lmshare(dec_ctx, si); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + cmn_err(CE_WARN, "%s: Decode error %d", + lmshrd_desc[opcode], status); + MEM_FREE("lmshrd_kclient", buf); + return (NERR_InternalError); + } + + MEM_FREE("lmshrd_kclient", buf); + return (rc); +} + +int +lmshrd_check(char *share_name, int opcode) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + int status, rc; + + if (!lmshrd_init && lmshrd_kclient_open() != 0) + return (NERR_InternalError); + + buf = MEM_MALLOC("lmshrd_kclient", LMSHR_DOOR_SIZE); + if (!buf) + return (NERR_InternalError); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + smb_dr_put_string(enc_ctx, share_name); + + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + cmn_err(CE_WARN, "%s: Encode error %d", + lmshrd_desc[opcode], status); + MEM_FREE("lmshrd_kclient", buf); + return (NERR_InternalError); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_ki_upcall(lmshrd_dh, &arg) != 0) { + cmn_err(CE_WARN, "%s: Door call failed", lmshrd_desc[opcode]); + MEM_FREE("lmshrd_kclient", buf); + lmshrd_kclient_close(); + return (NERR_InternalError); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + MEM_FREE("lmshrd_kclient", buf); + return (NERR_InternalError); + } + + rc = smb_dr_get_int32(dec_ctx); + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + cmn_err(CE_WARN, "%s: Decode error %d", + lmshrd_desc[opcode], status); + MEM_FREE("lmshrd_kclient", buf); + return (NERR_InternalError); + } + + MEM_FREE("lmshrd_kclient", buf); + return (rc); +} + +int +lmshrd_exists(char *share_name) +{ + return (lmshrd_check(share_name, LMSHR_DOOR_EXISTS)); +} + +int +lmshrd_is_special(char *share_name) +{ + return (lmshrd_check(share_name, LMSHR_DOOR_IS_SPECIAL)); +} + +int +lmshrd_is_restricted(char *share_name) +{ + return (lmshrd_check(share_name, LMSHR_DOOR_IS_RESTRICTED)); +} + +int +lmshrd_is_admin(char *share_name) +{ + return (lmshrd_check(share_name, LMSHR_DOOR_IS_ADMIN)); +} + +int +lmshrd_is_valid(char *share_name) +{ + return (lmshrd_check(share_name, LMSHR_DOOR_IS_VALID)); +} + +int +lmshrd_is_dir(char *path) +{ + return (lmshrd_check(path, LMSHR_DOOR_IS_DIR)); +} + +int +smb_get_kconfig(smb_kmod_cfg_t *cfg) +{ + door_arg_t arg; + char *buf; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + int status; + int opcode = SMB_GET_KCONFIG; + + if (!lmshrd_init && lmshrd_kclient_start() != 0) + return (-1); + + buf = MEM_MALLOC("lmshrd_kclient", LMSHR_DOOR_SIZE); + if (!buf) + return (-1); + + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + + if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + cmn_err(CE_WARN, "%s: Encode error %d", + lmshrd_desc[opcode], status); + MEM_FREE("lmshrd_kclient", buf); + return (-1); + } + + arg.data_ptr = buf; + arg.data_size = used; + arg.desc_ptr = NULL; + arg.desc_num = 0; + arg.rbuf = buf; + arg.rsize = LMSHR_DOOR_SIZE; + + if (door_ki_upcall(lmshrd_dh, &arg) != 0) { + cmn_err(CE_WARN, "%s: Door call failed", lmshrd_desc[opcode]); + MEM_FREE("lmshrd_kclient", buf); + lmshrd_kclient_close(); + return (-1); + } + + dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + MEM_FREE("lmshrd_kclient", buf); + return (-1); + } + + smb_dr_get_kconfig(dec_ctx, cfg); + + if ((status = smb_dr_decode_finish(dec_ctx)) != 0) { + cmn_err(CE_WARN, "%s: Decode error %d", + lmshrd_desc[opcode], status); + MEM_FREE("lmshrd_kclient", buf); + return (-1); + } + + MEM_FREE("lmshrd_kclient", buf); + return (0); +} + +/* + * This is a special interface that will be utilized by ZFS to cause + * a share to be added/removed + * + * arg is either a lmshare_info_t or share_name from userspace. + * It will need to be copied into the kernel. It is lmshare_info_t + * for add operations and share_name for delete operations. + */ +int +lmshrd_share_upcall(void *arg, boolean_t add_share) +{ + door_arg_t doorarg = { 0 }; + char *buf = NULL; + char *str = NULL; + int error; + int rc; + unsigned int used; + smb_dr_ctx_t *dec_ctx; + smb_dr_ctx_t *enc_ctx; + lmshare_info_t *lmshare = NULL; + int opcode; + + opcode = add_share == B_TRUE ? LMSHR_DOOR_ADD : LMSHR_DOOR_DELETE; + + /* Return error if server isn't up and running */ + if (!lmshrd_init) + return (NERR_ServerNotStarted); + + if (lmshrd_kclient_open()) + return (NERR_InternalError); + + buf = MEM_MALLOC("lmshrd_share_upcall", LMSHR_DOOR_SIZE); + enc_ctx = smb_dr_encode_start(buf, LMSHR_DOOR_SIZE); + smb_dr_put_uint32(enc_ctx, opcode); + + switch (opcode) { + case LMSHR_DOOR_ADD: + lmshare = MEM_MALLOC("lmshrd_share_upcall", + sizeof (lmshare_info_t)); + + if (error = xcopyin(arg, lmshare, sizeof (lmshare_info_t))) { + MEM_FREE("lmshrd_share_upcall", lmshare); + MEM_FREE("lmshrd_share_upcall", buf); + return (error); + } + smb_dr_put_lmshare(enc_ctx, lmshare); + break; + + case LMSHR_DOOR_DELETE: + str = MEM_MALLOC("lmshrd_share_upcall", MAXPATHLEN); + if (error = copyinstr(arg, str, MAXPATHLEN, NULL)) { + MEM_FREE("lmshrd_share_upcall", buf); + MEM_FREE("lmshrd_share_upcall", str); + return (error); + } + smb_dr_put_string(enc_ctx, str); + MEM_FREE("lmshrd_share_upcall", str); + break; + } + + if ((error = smb_dr_encode_finish(enc_ctx, &used)) != 0) { + MEM_FREE("lmshrd_share_upcall", buf); + if (lmshare) + MEM_FREE("lmshrd_share_upcall", lmshare); + return (NERR_InternalError); + } + + doorarg.data_ptr = buf; + doorarg.data_size = used; + doorarg.rbuf = buf; + doorarg.rsize = LMSHR_DOOR_SIZE; + + if (error = door_ki_upcall(lmshrd_dh, &doorarg)) { + MEM_FREE("lmshrd_share_upcall", buf); + if (lmshare) + MEM_FREE("lmshrd_share_upcall", lmshare); + return (error); + } + + dec_ctx = smb_dr_decode_start(doorarg.data_ptr, doorarg.data_size); + if (lmshrd_door_check_srv_status(opcode, dec_ctx) != 0) { + MEM_FREE("lmshrd_share_upcall", buf); + if (lmshare) + MEM_FREE("lmshrd_share_upcall", lmshare); + return (NERR_InternalError); + } + + rc = smb_dr_get_uint32(dec_ctx); + if (opcode == LMSHR_DOOR_ADD) + smb_dr_get_lmshare(dec_ctx, lmshare); + + if (smb_dr_decode_finish(dec_ctx)) { + MEM_FREE("lmshrd_share_upcall", buf); + if (lmshare) + MEM_FREE("lmshrd_share_upcall", lmshare); + return (NERR_InternalError); + } + + MEM_FREE("lmshrd_share_upcall", buf); + if (lmshare) + MEM_FREE("lmshrd_share_upcall", lmshare); + return ((rc == NERR_DuplicateShare && add_share) ? 0 : rc); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_signing.c b/usr/src/uts/common/fs/smbsrv/smb_signing.c new file mode 100644 index 000000000000..2b079c193b21 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_signing.c @@ -0,0 +1,418 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * These routines provide the SMB MAC signing for the SMB server. + * The routines calculate the signature of a SMB message in an mbuf chain. + * + */ + +#include +#include +#include +#include +#include +#include + +#define SMB_SIG_SIZE 8 +#define SMB_SIG_OFFS 14 + +/* This holds the MD5 mechanism */ +static crypto_mechanism_t crypto_mech = {CRYPTO_MECHANISM_INVALID, 0, 0}; + +/* + * smb_sign_init + * + * Intializes MAC key based on the user session key and + * NTLM response and store it in the signing structure. + */ +void +smb_sign_init(struct smb_request *req, smb_session_key_t *session_key, + char *resp, int resp_len) +{ + struct smb_sign *sign = &req->session->signing; + + /* + * Initialise the crypto mechanism to MD5 if it not + * already initialised. + */ + if (crypto_mech.cm_type == CRYPTO_MECHANISM_INVALID) { + crypto_mech.cm_type = crypto_mech2id(SUN_CKM_MD5); + if (crypto_mech.cm_type == CRYPTO_MECHANISM_INVALID) { + /* + * There is no MD5 crypto mechanism + * so turn off signing + */ + smb_info.si.skc_signing_enable = 0; + req->session->secmode &= + (~NEGOTIATE_SECURITY_SIGNATURES_ENABLED); + cmn_err(CE_WARN, + "SmbSignInit: signing disabled (no MD5)"); + return; + } + } + + /* MAC key = concat (SessKey, NTLMResponse) */ + + bcopy(session_key, sign->mackey, sizeof (smb_session_key_t)); + bcopy(resp, &(sign->mackey[sizeof (smb_session_key_t)]), + resp_len); + sign->mackey_len = sizeof (smb_session_key_t) + resp_len; + + req->reply_seqnum = 1; + sign->seqnum = 2; + sign->flags = SMB_SIGNING_ENABLED; + + if (smb_info.si.skc_signing_check) + sign->flags |= SMB_SIGNING_CHECK; + +} + +/* + * smb_sign_calc + * + * Calculates MAC signature for the given buffer and returns + * it in the mac_sign parameter. + * + * The sequence number is placed in the first four bytes of the signature + * field of the signature and the other 4 bytes are zeroed. + * The signature is the first 8 bytes of the MD5 result of the + * concatenated MAC key and the SMB message. + * + * MACsig = head(MD5(concat(MACKey, SMBMsg)), 8) + * + * where + * + * MACKey = concat( UserSessionKey, NTLMResp ) + * + * and + * + * SMBMsg is the SMB message containing the sequence number. + * + * Return 0 if success else -1 + * + */ +static int +smb_sign_calc(struct mbuf_chain *mbc, + struct smb_sign *sign, + uint32_t seqnum, + unsigned char *mac_sign) +{ + uint32_t seq_buf[2] = {0, 0}; + unsigned char mac[16]; + struct mbuf *mbuf = mbc->chain; + int offset = mbc->chain_offset; + int size; + int status; + + crypto_data_t data; + crypto_data_t digest; + crypto_context_t crypto_ctx; + + data.cd_format = CRYPTO_DATA_RAW; + data.cd_offset = 0; + data.cd_length = (size_t)-1; + data.cd_miscdata = 0; + + digest.cd_format = CRYPTO_DATA_RAW; + digest.cd_offset = 0; + digest.cd_length = (size_t)-1; + digest.cd_miscdata = 0; + digest.cd_raw.iov_base = (char *)mac; + digest.cd_raw.iov_len = sizeof (mac); + + status = crypto_digest_init(&crypto_mech, &crypto_ctx, 0); + if (status != CRYPTO_SUCCESS) goto error; + + /* + * Put the sequence number into the first 4 bytes + * of the signature field in little endian format. + * We are using a buffer to represent the signature + * rather than modifying the SMB message. + */ +#ifdef __sparc + { + uint32_t temp; + ((uint8_t *)&temp)[0] = ((uint8_t *)&seqnum)[3]; + ((uint8_t *)&temp)[1] = ((uint8_t *)&seqnum)[2]; + ((uint8_t *)&temp)[2] = ((uint8_t *)&seqnum)[1]; + ((uint8_t *)&temp)[3] = ((uint8_t *)&seqnum)[0]; + + seq_buf[0] = temp; + } +#else + seq_buf[0] = seqnum; +#endif + + /* Digest the MACKey */ + data.cd_raw.iov_base = (char *)sign->mackey; + data.cd_raw.iov_len = sign->mackey_len; + status = crypto_digest_update(&crypto_ctx, &data, 0); + if (status != CRYPTO_SUCCESS) goto error; + + /* Find start of data in chain */ + while (offset >= mbuf->m_len) { + offset -= mbuf->m_len; + mbuf = mbuf->m_next; + } + + /* Digest the SMB packet up to the signature field */ + size = SMB_SIG_OFFS; + while (size >= mbuf->m_len - offset) { + data.cd_raw.iov_base = &mbuf->m_data[offset]; + data.cd_raw.iov_len = mbuf->m_len - offset; + status = crypto_digest_update(&crypto_ctx, &data, 0); + if (status != CRYPTO_SUCCESS) goto error; + + size -= mbuf->m_len - offset; + mbuf = mbuf->m_next; + offset = 0; + } + if (size > 0) { + data.cd_raw.iov_base = &mbuf->m_data[offset]; + data.cd_raw.iov_len = size; + status = crypto_digest_update(&crypto_ctx, &data, 0); + if (status != CRYPTO_SUCCESS) goto error; + + offset += size; + } + + /* + * Digest in the seq_buf instead of the signature + * which has the sequence number + */ + + data.cd_raw.iov_base = (char *)seq_buf; + data.cd_raw.iov_len = SMB_SIG_SIZE; + status = crypto_digest_update(&crypto_ctx, &data, 0); + if (status != CRYPTO_SUCCESS) goto error; + + /* Find the end of the signature field */ + offset += SMB_SIG_SIZE; + while (offset >= mbuf->m_len) { + offset -= mbuf->m_len; + mbuf = mbuf->m_next; + } + /* Digest the rest of the SMB packet */ + while (mbuf) { + data.cd_raw.iov_base = &mbuf->m_data[offset]; + data.cd_raw.iov_len = mbuf->m_len - offset; + status = crypto_digest_update(&crypto_ctx, &data, 0); + if (status != CRYPTO_SUCCESS) goto error; + + mbuf = mbuf->m_next; + offset = 0; + } + + status = crypto_digest_final(&crypto_ctx, &digest, 0); + if (status != CRYPTO_SUCCESS) goto error; + + bcopy(mac, mac_sign, SMB_SIG_SIZE); + + return (0); +error: + cmn_err(CE_WARN, "SmbSignCalc: crypto error %d", status); + return (-1); + +} + + +/* + * smb_sign_check_request + * + * Calculates MAC signature for the request mbuf chain + * using the next expected sequence number and compares + * it to the given signature. + * + * Note it does not check the signature for secondary transactions + * as their sequence number is the same as the original request. + * + * Return 0 if the signature verifies, otherwise, returns -1; + * + */ +int +smb_sign_check_request(struct smb_request *req) +{ + struct mbuf_chain command = req->command; + unsigned char mac_sig[SMB_SIG_SIZE]; + struct smb_sign *sign = &req->session->signing; + int rtn = 0; + + /* + * Don't check secondary transactions - we dont know the sequence + * number. + */ + if (req->smb_com == SMB_COM_TRANSACTION_SECONDARY || + req->smb_com == SMB_COM_TRANSACTION2_SECONDARY || + req->smb_com == SMB_COM_NT_TRANSACT_SECONDARY) + return (0); + + if (sign->flags & SMB_SIGNING_CHECK) { + + /* Reset the offset to begining of header */ + command.chain_offset = req->orig_request_hdr; + + /* calculate mac signature */ + if (smb_sign_calc(&command, sign, sign->seqnum, mac_sig) != 0) + return (-1); + + /* compare the signatures */ + if (memcmp(mac_sig, req->smb_sig, SMB_SIG_SIZE) != 0) { + cmn_err(CE_WARN, "SmbSignCheckRequest: " + "bad signature %x %x %x %x %x %x %x %x", + req->smb_sig[0], req->smb_sig[1], + req->smb_sig[2], req->smb_sig[3], + req->smb_sig[4], req->smb_sig[5], + req->smb_sig[6], req->smb_sig[7]); +#ifdef DBG_VERBOSE + /* Debug code to hunt for the sequence number */ + for (i = sign->seqnum - 6; i <= sign->seqnum + 6; i++) { + smb_sign_calc(&command, sign, i, mac_sig); + if (memcmp(mac_sig, req->smb_sig, + SMB_SIG_SIZE) == 0) { + sign->seqnum = i; + goto ok; + } + } +#endif + rtn = -1; + } + } +ok: + /* + * Increament the sequence number for the reply, save the reply + * and set it for the next expect command. + * There is no reply for NT Cancel so just increament it for the + * next expected command. + */ + sign->seqnum++; + + if (req->smb_com == SMB_COM_NT_CANCEL) + req->reply_seqnum = 0; + else + req->reply_seqnum = sign->seqnum++; + + return (rtn); +} + +/* + * smb_sign_check_secondary + * + * Calculates MAC signature for the secondary transaction mbuf chain + * and compares it to the given signature. + * Return 0 if the signature verifies, otherwise, returns -1; + * + */ +int +smb_sign_check_secondary(struct smb_request *req, unsigned int reply_seqnum) +{ + struct mbuf_chain command = req->command; + unsigned char mac_sig[SMB_SIG_SIZE]; + struct smb_sign *sign = &req->session->signing; + int rtn = 0; + + if (sign->flags & SMB_SIGNING_CHECK) { + /* Reset the offset to begining of header */ + command.chain_offset = req->orig_request_hdr; + + /* calculate mac signature */ + if (smb_sign_calc(&command, sign, reply_seqnum - 1, + mac_sig) != 0) + return (-1); + + + /* compare the signatures */ + if (memcmp(mac_sig, req->smb_sig, SMB_SIG_SIZE) != 0) { + cmn_err(CE_WARN, "SmbSignCheckSecond: bad signature"); + rtn = -1; + } + } + /* Save the reply sequence number */ + req->reply_seqnum = reply_seqnum; + + return (rtn); +} + + + + +/* + * smb_sign_reply + * + * Calculates MAC signature for the given mbuf chain, + * and write it to the signature field in the mbuf. + * + */ +void +smb_sign_reply(struct smb_request *req, struct mbuf_chain *reply) +{ + struct mbuf_chain resp; + struct smb_sign *sign = &req->session->signing; + unsigned char signature[SMB_SIG_SIZE]; + struct mbuf *mbuf; + int size = SMB_SIG_SIZE; + unsigned char *sig_ptr = signature; + int offset = 0; + + if (reply) + resp = *reply; + else + resp = req->reply; + + /* Reset offset to start of reply */ + resp.chain_offset = 0; + mbuf = resp.chain; + + /* + * Calculate MAC signature + */ + if (smb_sign_calc(&resp, sign, req->reply_seqnum, signature) != 0) + return; + + /* + * Put signature in the response + * + * First find start of signature in chain (offset + signature offset) + */ + offset += SMB_SIG_OFFS; + while (offset >= mbuf->m_len) { + offset -= mbuf->m_len; + mbuf = mbuf->m_next; + } + + while (size >= mbuf->m_len - offset) { + (void) memcpy(&mbuf->m_data[offset], + sig_ptr, mbuf->m_len - offset); + offset = 0; + sig_ptr += mbuf->m_len - offset; + size -= mbuf->m_len - offset; + mbuf = mbuf->m_next; + } + if (size > 0) { + (void) memcpy(&mbuf->m_data[offset], sig_ptr, size); + } +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_svc_sm.c b/usr/src/uts/common/fs/smbsrv/smb_svc_sm.c new file mode 100644 index 000000000000..b43388cccdd7 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_svc_sm.c @@ -0,0 +1,1301 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB Service State Machine + * + * _____________ + * T21 | | T1 + * +---------------->| INIT |------------------+ + * | |_____________| | + * ______|_____ /|\ _____\|/_____ + * | | T14| T22 | | + * | ERROR | +------------ | -----------------| OPENING | + * |____________| | | |_____________| + * /|\ | ______|______ | + * | | | | T23 | + * |T20 | +-->| CLOSING |<--------+ | T2 + * | | | |_____________|<-------+| | + * _______|_______ | | /|\ || _____\|/_____ + * | |<-+ |T19 | || | | + * | ERROR CLOSING |-----+ T13| |+-| CONFIG WAIT | + * |_______________| | | |_____________| + * /|\ | | | + * | _______|_______ | | + * | | | | | + * | +---------->| SESSION CLOSE |--+ | | + * | | |_______________| | | | T3 + * | | /|\ /|\ | |T24 | + * | | T11| |T12 | | | + * | | | +-----+ | | + * | | _______|_______ | ____\|/_____ + * | | | | | | | + * | | | DISCONNECTING | +---| CONNECTING | + * |T18 |T17 |_______________| |____________| + * | | /|\ /|\ | + * | | | | | T4 + * | | | | | + * ______|____|___ | | T9 ___\|/___ + * | | | +---------------| | + * | ERROR SESSION | |T10 | ONLINE |<-+ + * +->| CLOSE | | +-------------->|_________| | + * | |_______________| | | T8 | | | | + * |T16 | /|\ /|\ ______|____|_ | | |____| T5 + * +-----+ | | T25 | | | | + * | +-------------| RECONFIGURE |<--------------+ | + * | +----|_____________| T6 | + * | | /|\ | + * | T7| | | + * | +------+ | + * | T15 | + * +--------------------------------------------------+ + * + * + * State Descriptions: + * + * Init + * + * Ready for device open. This is the initial service state and the + * service returns to this state when cleanup has completed after a + * device close. The pseudo-driver can only be unloaded or opened in + * this state. + * + * Opening + * + * The pseudo-driver has been opened and SMB initialization is underway + * + * Config Wait + * + * Waiting for smbd to provide configuration information. XXX Today + * the kernel/user sychronization is not completely implemented and + * some changes might be appropriate in the future. The current + * code pulls configuration from smbd (see smb_get_kconfig). For + * dynamic configuration updates smbd will need to push config information + * to the kernel. This could be handled with either an ioctl or a door + * call. When this change is made we should change the state machine so + * that it also relies on the pull model. One way to handle this is to + * have the state machine end up in "config wait" instead of "online" + * when the open of the pseudo-device returns. Smbd will then know + * to push the current config after it has successfully opened the + * device. Such a change would require tweaks to the handling of + * svc_sm->ssc_started. + * + * Connecting + * + * Connecting to SMB port and starting service listener thread + * + * Online + * + * Online and accepting SMB sessions + * + * Reconfiguring + * + * Updating configuration after receiving a config update from smbd. This + * state is very similar to "online" state except that new session requests + * get place on a queue and initialization for those requests is deferred + * until configuration is complete. XXX Since dymamic configuration + * updates have not been implemented this state is never exercised. + * It's possible that we could completely eliminate it by simply grabbing + * a mutex for the duration of the config update. If the config update + * will take a long time or require sleeping then this state will + * be useful. + * + * Disconnecting + * + * Disconnecting from the SMB port and stopping the service listener + * thread. + * + * Session Close + * + * Waiting for any open sessions to close + * + * Closing + * + * Quiesce service and release any associated resources. This is the + * inverse of the "opening" state. + * + * Error Session Close + * + * The connection was unexpectedly dropped due to an error of some kind. + * Waiting for any open session to close (identical to Session Close + * except that we enter this state involuntarily) + * + * Error Closing + * + * Similar to Closing except that we enter this state as the result of + * an error (either during initialization or runtime) + * + * Error + * + * An error occurred that caused the service to shutdown but the + * pseudo-device is still open. + * + * + * State Transitions: + * + * T1 - The state machine is started by a call to smb_svcstate_sm_start. This + * causes a SMB_SVCEVT_OPEN event which forces a transition to "opening" + * state. + * + * T2 - SMB_SVCEVT_OPEN_SUCCESS indicates that the open actions completed + * successfully and the state machine transitions to "config wait" state. + * + * T3 - Configuration received from smbd (SMB_SVCEVT_CONFIG_SUCCESS) + * + * T4 - SMB service listener thread started and successfully bound to the + * socket (SMB_SVCEVT_CONNECT). + * + * T5 - Any SMB_SVCEVT_SESSION_CREATE/SMB_SVCEVT_SESSION_DELETE events are + * tracked on the svc_sm->ssc_active_sessions list and reflected in + * the svc_sm->ssc_active_session_count but we stay in "online" state + * + * T6 - Reconfiguration event (SMB_SVCEVT_CONFIG) cause a transition to + * "reconfiguring" state. + * + * T7 - SMB_SVCEVT_SESSION_CREATE/SMB_SVCEVT_SESSION_DELETE + * + * T8 - Configuration received from smbd (SMB_SVCEVT_CONFIG_SUCCESS) drives us + * back to "online" state. + * + * T9 - SMB_SVCEVT_CLOSE starts the shutdown process, starting with + * "disconnecting" state. + * + * T10 - SMB_SVCEVT_CLOSE starts the shutdown process, starting with + * "disconnecting" state. + * + * T11 - After socket disconnect SMB_SVCEVT_DISCONNECT drives the state machine + * to "session close" state. + * + * T12 - SMB_SVCEVT_SESSION_CLOSE does not cause a state transition if more + * sessions remain. + * + * T13 - When no more session remain SMB_SVCEVT_SESSION_CLOSE causes a + * transition to "closing" state. + * + * T14 - All close actions completed successfully (SMB_SVCEVT_CLOSE_SUCCESS). + * Close operations are not allowed to fail so the transition from + * "closing" to "init" is guaranteed. + * + * T15 - If the SMB service connection is unexpectedly dropped, + * SMB_SVCEVT_DISCONNECT drives the state machine to + * "error session close" state. + * + * T16 - SMB_SVCEVT_SESSION_CLOSE does not cause a state transition if more + * sessions remain. + * + * T17 - SMB_SVCEVT_CLOSE causes a state change to "session close" state. The + * difference between "error session close" and "session close" state is + * whether the pseudo device is open. + * + * T18 - When no more session remain SMB_SVCEVT_SESSION_CLOSE causes a + * transition to "error closing" state. + * + * T19 - SMB_SVCEVT_CLOSE causes a state change to "closing" state. The + * difference between "error closing" and "closing" state is + * whether the pseudo device is open. + * + * T20 - All close actions completed successfully (SMB_SVCEVT_CLOSE_SUCCESS). + * Close operations are not allowed to fail so the transition from + * "error closing" to "error" is guaranteed. + * + * T21 - SMB_SVCEVT_CLOSE moves everything back to "init" state + * + * T22 - SMB_SVCEVT_OPEN_FAILED causes a state change to "error closing". + * Moving to "error closing" state instead of "closing" causes the + * state machine to ultimately stop in "error" state (instead of + * "init"). + * + * T23 - SMB_SVCEVT_CLOSE + * + * T24 - SMB_SVCEVT_CLOSE in "connecting" state causes a transition + * to "closing" state since the connection has not yet been established. + * + * T25 - If the SMB service connection is unexpectedly dropped, + * SMB_SVCEVT_DISCONNECT drives the state machine to + * "error session close" state. + * + * Overview: + * + * When the SMB pseudo-device gets open the state machine gets started with a + * call to smb_svcstate_sm_start which will block until initialization either + * succeeds or fails. + * + * SMB code external to the state machine generates events (defined above) by + * calling smb_svcstate_event and the state machine determines the new state + * based on the events and the current state. + * + * When the pseudo-device is closed, the state machine gets stopped with + * a call to smb_svcstate_sm_stop. + * + * This state machine also keeps track of the active session list. The + * list of sessions can be queried using the following services: + * + * smb_svcstate_lock_read(svc_sm); + * smb_svcstate_session_getnext(svc_sm, prev_session); + * (repeat until NULL is returned) + * smb_svcstate_unlock(svc_sm); + * + * + * Implemention Details: + * + * States are named SMB_SVCSTATE_ + * + * Events are named SMB_SVCEVT_ + * + * Each state has an associated function: smb_svcstate_ + * + * This state machine implements four types of actions: + * + * State entry actions - State-specific actions that are taken when a + * state is entered (transitions like T5 that do not result in a real + * state change are not actually coded as state transitions (no call to + * smb_svcstate_update) and therefore will not cause state entry actions. + * These actions are implemented in smb_svcstate_update. + * + * Immediate event actions - An action specific to a particular event + * that is taken immediately, before the event is placed on the task + * queue. The action does not depend on the current state. These should + * only be implemented when absolutely necessary since the code path for + * immediate actions is multithreaded (smb_svcstate_event_locked). + * + * Deferred event actions - An action specific to a particular event + * that is handled by the taskq thread. The action does not depend on + * the current state and the code implementing the actions is single + * threaded since the taskq only has one thread. Implemented in + * smb_svcstate_event_handler. + * + * State specific event actions - Actions specific to events that + * depend on the current state. These actions are implemented + * in the state-specific event handler functions + * (smb_svcstate_) + * + * The description above makes things sound more complicated than they really + * are. Deferred event actions are really just a special case of state + * specific event actions. To find out what happens when a specific event + * occurs in a specific state: + * + * 1. Look at smb_svcstate_event_locked and find matching event actions (rare) + * 2. Look at smb_svcstate_event_handler and find matching event actions + * 3. Look at smb_svcstate_ and find matching event actions + * + * If the event causes a state transition (this will always be found in + * smb_svcstate_) then look up the new state in + * smb_svcstate_update which will show the state entry actions for the new + * state. + */ + +#include +#include +#include + +static void smb_svcstate_event_locked(smb_svc_sm_ctx_t *svc_sm, + smb_svcevt_t event, uintptr_t event_info); + +static void smb_svcstate_event_handler(void *event_ctx); + +static void smb_svcstate_init(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx); + +static void smb_svcstate_opening(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx); + +static void smb_svcstate_config_wait(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx); + +static void smb_svcstate_connecting(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx); + +static void smb_svcstate_online(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx); + +static void smb_svcstate_reconfiguring(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx); + +static void smb_svcstate_disconnecting(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx); + +static void smb_svcstate_session_close(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx); + +static void smb_svcstate_error_session_close(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx); + +static void smb_svcstate_closing(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx); + +static void smb_svcstate_error_closing(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx); + +static void smb_svcstate_error(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx); + +static void smb_svcstate_update(smb_svc_sm_ctx_t *svc_sm, + smb_svcstate_t newstate); + +static void smb_svcstate_set_started(smb_svc_sm_ctx_t *svc_sm, + int started); + +static void smb_svcstate_session_start(smb_svc_sm_ctx_t *svc_sm, + smb_session_t *new_session); + +static void smb_svcstate_session_defer(smb_svc_sm_ctx_t *svc_sm, + smb_session_t *new_session); + +static void smb_svcstate_session_reject_active(smb_svc_sm_ctx_t *svc_sm, + smb_session_t *session, char *reason); + +static void +smb_svcstate_start_deferred_sessions(smb_svc_sm_ctx_t *svc_sm); + +static void +smb_svcstate_reject_deferred_sessions(smb_svc_sm_ctx_t *svc_sm); + +static void +smb_svcstate_close_active_sessions(smb_svc_sm_ctx_t *svc_sm); + +extern void smb_wakeup_session_daemon(smb_thread_t *thread, void *arg); +extern void smb_session_daemon(smb_thread_t *thread, void *arg); +extern int smb_get_kconfig(smb_kmod_cfg_t *cfg); + +char *smb_svcstate_event_name[SMB_SVCEVT_MAX_EVENT]; +char *smb_svcstate_state_name[SMB_SVCSTATE_MAX_STATE]; + +/* + * SMB Service State Machine + */ + +#define SMB_STOPPED 0 +#define SMB_START_SUCCESS 1 +#define SMB_START_FAILED 2 + +int +smb_svcstate_sm_init(smb_svc_sm_ctx_t *svc_sm) +{ + bzero(svc_sm, sizeof (*svc_sm)); + + /* Protects state context except for ssc_state and ssc_last_state */ + rw_init(&svc_sm->ssc_state_rwlock, NULL, RW_DEFAULT, NULL); + + /* Protects ssc_state and ssc_last_state */ + mutex_init(&svc_sm->ssc_state_cv_mutex, NULL, MUTEX_DEFAULT, NULL); + cv_init(&svc_sm->ssc_state_cv, NULL, CV_DEFAULT, NULL); + + svc_sm->ssc_state = SMB_SVCSTATE_INIT; + svc_sm->ssc_last_state = SMB_SVCSTATE_INIT; + + list_create(&svc_sm->ssc_active_sessions, sizeof (smb_session_t), + offsetof(smb_session_t, s_lnd)); + list_create(&svc_sm->ssc_deferred_sessions, sizeof (smb_session_t), + offsetof(smb_session_t, s_lnd)); + + /* Service state machine is single threaded by design */ + svc_sm->ssc_taskq = taskq_create("smb_svc_sm", 1, minclsyspri, + 1, 1, 0); + if (svc_sm->ssc_taskq == NULL) { + return (ENOMEM); + } + + /* + * Setup event and state name tables. Use for debug logging + * and/or dtrace scripts + */ + smb_svcstate_event_name[SMB_SVCEVT_UNDEFINED] = "UNDEFINED"; + smb_svcstate_event_name[SMB_SVCEVT_OPEN] = "OPEN"; + smb_svcstate_event_name[SMB_SVCEVT_CLOSE] = "CLOSE"; + smb_svcstate_event_name[SMB_SVCEVT_OPEN_SUCCESS] = "OPEN_SUCCESS"; + smb_svcstate_event_name[SMB_SVCEVT_OPEN_FAILED] = "OPEN_FAILED"; + smb_svcstate_event_name[SMB_SVCEVT_CLOSE_SUCCESS] = "CLOSE_SUCCESS"; + smb_svcstate_event_name[SMB_SVCEVT_CONNECT] = "CONNECT"; + smb_svcstate_event_name[SMB_SVCEVT_DISCONNECT] = "DISCONNECT"; + smb_svcstate_event_name[SMB_SVCEVT_CONFIG] = "CONFIG"; + smb_svcstate_event_name[SMB_SVCEVT_CONFIG_SUCCESS] = "CONFIG_SUCCESS"; + smb_svcstate_event_name[SMB_SVCEVT_CONFIG_FAILED] = "CONFIG_FAILED"; + smb_svcstate_event_name[SMB_SVCEVT_SESSION_CREATE] = "SESSION_CREATE"; + smb_svcstate_event_name[SMB_SVCEVT_SESSION_DELETE] = "SESSION_DELETE"; + + smb_svcstate_state_name[SMB_SVCSTATE_UNDEFINED] = "UNDEFINED"; + smb_svcstate_state_name[SMB_SVCSTATE_INIT] = "INIT"; + smb_svcstate_state_name[SMB_SVCSTATE_OPENING] = "OPENING"; + smb_svcstate_state_name[SMB_SVCSTATE_CONFIG_WAIT] = "CONFIG_WAIT"; + smb_svcstate_state_name[SMB_SVCSTATE_CONNECTING] = "CONNECTING"; + smb_svcstate_state_name[SMB_SVCSTATE_ONLINE] = "ONLINE"; + smb_svcstate_state_name[SMB_SVCSTATE_RECONFIGURING] = "RECONFIGURING"; + smb_svcstate_state_name[SMB_SVCSTATE_DISCONNECTING] = "DISCONNECTING"; + smb_svcstate_state_name[SMB_SVCSTATE_SESSION_CLOSE] = "SESSION_CLOSE"; + smb_svcstate_state_name[SMB_SVCSTATE_ERROR_SESSION_CLOSE] = + "ERROR_SESSION_CLOSE"; + smb_svcstate_state_name[SMB_SVCSTATE_CLOSING] = "CLOSING"; + smb_svcstate_state_name[SMB_SVCSTATE_ERROR_CLOSING] = "ERROR_CLOSING"; + smb_svcstate_state_name[SMB_SVCSTATE_ERROR] = "ERROR"; + + return (0); +} + +void +smb_svcstate_sm_fini(smb_svc_sm_ctx_t *svc_sm) +{ + taskq_destroy(svc_sm->ssc_taskq); + + list_destroy(&svc_sm->ssc_deferred_sessions); + list_destroy(&svc_sm->ssc_active_sessions); + + cv_destroy(&svc_sm->ssc_state_cv); + mutex_destroy(&svc_sm->ssc_state_cv_mutex); + rw_destroy(&svc_sm->ssc_state_rwlock); +} + +int +smb_svcstate_sm_start(smb_svc_sm_ctx_t *svc_sm) +{ + clock_t wait_result; + int result; + + /* + * Make sure state machine is idle and ready to be started. + */ + mutex_enter(&svc_sm->ssc_state_cv_mutex); + while (svc_sm->ssc_started) { + /* + * Already started, possibly because we're still trying to + * shutdown. Wait for up to 30 seconds then return EBUSY. + */ + wait_result = cv_timedwait(&svc_sm->ssc_state_cv, + &svc_sm->ssc_state_cv_mutex, + lbolt + SEC_TO_TICK(30)); + if (wait_result == -1) { + /* Timeout */ + mutex_exit(&svc_sm->ssc_state_cv_mutex); + return (EBUSY); + } + } + + /* + * SMB_SVCEVT_OPEN will get the state machine moving + */ + smb_svcstate_event_locked(svc_sm, SMB_SVCEVT_OPEN, NULL); + + /* + * Wait for the state machine to signal either a successful + * start or a start failure. + */ + while (!svc_sm->ssc_started) { + cv_wait(&svc_sm->ssc_state_cv, &svc_sm->ssc_state_cv_mutex); + } + + result = (svc_sm->ssc_started == SMB_START_FAILED) ? + svc_sm->ssc_start_error : 0; + + /* + * If the open failed we will end up in "Error" state. Since we + * are returning failure to the open request on the pseudo-device + * we will never see a close so we need to force the device closed. + * + * A careful look at the state machine will should that we could + * avoid this step by transitioning from opening --> closing + * instead of opening --> error_closing when there is an initialization + * problem. The reason we shouldn't do this is because + * smb_svcstate_sm_busy returns "false" (not busy) when the state + * machine is in init state and we don't want to mistakenly indicate + * that we are not busy. + */ + if (result != 0) { + smb_svcstate_event_locked(svc_sm, SMB_SVCEVT_CLOSE, NULL); + } + + mutex_exit(&svc_sm->ssc_state_cv_mutex); + + return (result); +} + +/*ARGSUSED*/ +void +smb_svcstate_sm_stop(smb_svc_sm_ctx_t *svc_sm) +{ + smb_svcstate_event(SMB_SVCEVT_CLOSE, NULL); +} + +boolean_t +smb_svcstate_sm_busy(void) +{ + return (smb_info.si_svc_sm_ctx.ssc_state != SMB_SVCSTATE_INIT); +} + +void +smb_svcstate_event(smb_svcevt_t event, uintptr_t event_info) +{ + smb_svc_sm_ctx_t *svc_sm = &smb_info.si_svc_sm_ctx; + + mutex_enter(&svc_sm->ssc_state_cv_mutex); + smb_svcstate_event_locked(svc_sm, event, event_info); + mutex_exit(&svc_sm->ssc_state_cv_mutex); +} + +void +smb_svcstate_lock_read(smb_svc_sm_ctx_t *svc_sm) +{ + rw_enter(&svc_sm->ssc_state_rwlock, RW_READER); + +} + +void +smb_svcstate_unlock(smb_svc_sm_ctx_t *svc_sm) +{ + rw_exit(&svc_sm->ssc_state_rwlock); +} + +smb_session_t * +smb_svcstate_session_getnext(smb_svc_sm_ctx_t *svc_sm, smb_session_t *prev) +{ + smb_session_t *result; + + /* Skip sessions in "terminated" state */ + do { + if (prev == NULL) { + result = list_head(&svc_sm->ssc_active_sessions); + } else { + result = list_next(&svc_sm->ssc_active_sessions, prev); + } + prev = result; + } while ((result != NULL) && + (result->s_state == SMB_SESSION_STATE_TERMINATED)); + + return (result); +} + +int +smb_svcstate_session_count(smb_svc_sm_ctx_t *svc_sm) +{ + return (svc_sm->ssc_active_session_count); +} + +/* + * Internal use only by state machine code + */ + +static void +smb_svcstate_event_locked(smb_svc_sm_ctx_t *svc_sm, + smb_svcevt_t event, uintptr_t event_info) +{ + smb_event_ctx_t *event_ctx; + + event_ctx = kmem_zalloc(sizeof (*event_ctx), KM_SLEEP); + event_ctx->sec_event = event; + event_ctx->sec_info = event_info; + + /* + * Immediate event actions that are independent of state. + * This code is multi-threaded and is in the context of + * the thread that generated the event. + * + * Don't generate events from this function (recursive mutex + * enter). + */ + switch (event_ctx->sec_event) { + case SMB_SVCEVT_SESSION_CREATE: + /* + * We don't necessarily want to reflect this session in the + * session count just yet. We do, however, want to know + * that its waiting so that we can properly close down + * all the outstanding session as we are closing the service. + * The ssc_session_creates_waiting counter represents + * the sessions that have been dispatched to the taskq + * but not yet processed. + * + * Session delete events don't have the same issue because + * the session count won't go to zero until they are all + * processed. + */ + svc_sm->ssc_session_creates_waiting++; + break; + default: + break; + } + + (void) taskq_dispatch(svc_sm->ssc_taskq, &smb_svcstate_event_handler, + event_ctx, TQ_SLEEP); +} + +/* + * Task queue gets created with only one thread so this code is inherently + * single threaded. State changes should still be protected with a mutex + * since other threads might read the state value. + */ +static void +smb_svcstate_event_handler(void *event_ctx_opaque) +{ + smb_svc_sm_ctx_t *svc_sm = &smb_info.si_svc_sm_ctx; + smb_event_ctx_t *event_ctx = event_ctx_opaque; + smb_session_t *session; + + DTRACE_PROBE2(service__event, + smb_svc_sm_ctx_t *, svc_sm, smb_event_ctx_t *, event_ctx); + + /* + * Validate event + */ + ASSERT(event_ctx->sec_event != SMB_SVCEVT_UNDEFINED); + ASSERT3U(event_ctx->sec_event, <, SMB_SVCEVT_MAX_EVENT); + + /* + * Validate current state + */ + ASSERT(svc_sm->ssc_state != SMB_SVCSTATE_UNDEFINED); + ASSERT3U(svc_sm->ssc_state, <, SMB_SVCSTATE_MAX_STATE); + + /* + * Deferred event actions that are independent of state. + * This code is single-threaded and is in the context of + * the task-queue thread. + */ + switch (event_ctx->sec_event) { + case SMB_SVCEVT_DISCONNECT: + svc_sm->ssc_disconnect_error = (int)event_ctx->sec_info; + break; + case SMB_SVCEVT_SESSION_CREATE: + mutex_enter(&svc_sm->ssc_state_cv_mutex); + svc_sm->ssc_session_creates_waiting--; + mutex_exit(&svc_sm->ssc_state_cv_mutex); + break; + case SMB_SVCEVT_SESSION_DELETE: + session = (smb_session_t *)event_ctx->sec_info; + ASSERT(session->s_state == SMB_SESSION_STATE_TERMINATED); + rw_enter(&svc_sm->ssc_state_rwlock, RW_WRITER); + list_remove(&svc_sm->ssc_active_sessions, session); + svc_sm->ssc_active_session_count--; + rw_exit(&svc_sm->ssc_state_rwlock); + + /* + * Make sure thread has exited + */ + smb_thread_stop(&session->s_thread); + + smb_session_delete(session); + + /* + * State specific handlers will also process the event + * but the event info (session) is no longer valid. + */ + event_ctx->sec_info = NULL; + break; + default: + break; + } + + /* + * Call state-specific event handler. + */ + switch (svc_sm->ssc_state) { + case SMB_SVCSTATE_INIT: + smb_svcstate_init(svc_sm, event_ctx); + break; + case SMB_SVCSTATE_OPENING: + smb_svcstate_opening(svc_sm, event_ctx); + break; + case SMB_SVCSTATE_CONFIG_WAIT: + smb_svcstate_config_wait(svc_sm, event_ctx); + break; + case SMB_SVCSTATE_CONNECTING: + smb_svcstate_connecting(svc_sm, event_ctx); + break; + case SMB_SVCSTATE_ONLINE: + smb_svcstate_online(svc_sm, event_ctx); + break; + case SMB_SVCSTATE_RECONFIGURING: + smb_svcstate_reconfiguring(svc_sm, event_ctx); + break; + case SMB_SVCSTATE_DISCONNECTING: + smb_svcstate_disconnecting(svc_sm, event_ctx); + break; + case SMB_SVCSTATE_SESSION_CLOSE: + smb_svcstate_session_close(svc_sm, event_ctx); + break; + case SMB_SVCSTATE_ERROR_SESSION_CLOSE: + smb_svcstate_error_session_close(svc_sm, event_ctx); + break; + case SMB_SVCSTATE_CLOSING: + smb_svcstate_closing(svc_sm, event_ctx); + break; + case SMB_SVCSTATE_ERROR_CLOSING: + smb_svcstate_error_closing(svc_sm, event_ctx); + break; + case SMB_SVCSTATE_ERROR: + smb_svcstate_error(svc_sm, event_ctx); + break; + default: + ASSERT(0); + break; + } + + kmem_free(event_ctx, sizeof (*event_ctx)); +} + +static void +smb_svcstate_init(smb_svc_sm_ctx_t *svc_sm, smb_event_ctx_t *event_ctx) +{ + switch (event_ctx->sec_event) { + case SMB_SVCEVT_OPEN: + smb_svcstate_update(svc_sm, SMB_SVCSTATE_OPENING); + break; + default: + break; + } +} + +static void +smb_svcstate_opening(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx) +{ + switch (event_ctx->sec_event) { + case SMB_SVCEVT_OPEN_SUCCESS: + smb_svcstate_update(svc_sm, SMB_SVCSTATE_CONFIG_WAIT); + break; + case SMB_SVCEVT_OPEN_FAILED: + /* + * Go to error_closed state, cleanup anything we did in open + * and then back to init state. We want to end up in "error" + * state instead of "init" state. + */ + smb_svcstate_update(svc_sm, SMB_SVCSTATE_ERROR_CLOSING); + smb_svcstate_set_started(svc_sm, SMB_START_FAILED); + break; + + default: + ASSERT(0); + break; + } +} + +static void +smb_svcstate_config_wait(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx) +{ + switch (event_ctx->sec_event) { + case SMB_SVCEVT_CONFIG: + /* + * Update our configuration. A successful config update + * will trigger SMB_SVCEVT_CONFIG_SUCCESS and drive us + * into online state. + */ + (void) smb_get_kconfig(&smb_info.si); /* XXX */ + break; + case SMB_SVCEVT_CONFIG_SUCCESS: + smb_svcstate_update(svc_sm, SMB_SVCSTATE_CONNECTING); + break; + case SMB_SVCEVT_CONFIG_FAILED: + /* Don't care, wait for another config attempt */ + break; + case SMB_SVCEVT_CLOSE: + smb_svcstate_update(svc_sm, + SMB_SVCSTATE_CLOSING); + break; + default: + ASSERT(0); + break; + } +} + +static void +smb_svcstate_connecting(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx) +{ + switch (event_ctx->sec_event) { + case SMB_SVCEVT_CONNECT: + /* + * Wait until both NBT and TCP transport services + * are connected before going online. + */ + if ((smb_info.si_connect_progress & SMB_SI_NBT_CONNECTED) && + (smb_info.si_connect_progress & SMB_SI_TCP_CONNECTED)) { + smb_svcstate_update(svc_sm, SMB_SVCSTATE_ONLINE); + smb_svcstate_set_started(svc_sm, SMB_START_SUCCESS); + } + break; + case SMB_SVCEVT_DISCONNECT: + svc_sm->ssc_start_error = svc_sm->ssc_disconnect_error; + smb_svcstate_update(svc_sm, SMB_SVCSTATE_ERROR_CLOSING); + smb_svcstate_set_started(svc_sm, SMB_START_FAILED); + break; + case SMB_SVCEVT_CLOSE: + smb_svcstate_update(svc_sm, SMB_SVCSTATE_CLOSING); + break; + case SMB_SVCEVT_SESSION_CREATE: + smb_svcstate_session_defer(svc_sm, + (smb_session_t *)event_ctx->sec_info); + break; + default: + ASSERT(0); + break; + } +} + +static void +smb_svcstate_online(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx) +{ + smb_session_t *new_session; + + switch (event_ctx->sec_event) { + case SMB_SVCEVT_SESSION_CREATE: + /* Event context is the new socket */ + new_session = (smb_session_t *)event_ctx->sec_info; +#if 0 /* XXX PGD */ + smb_session_config(new_session); +#endif + smb_svcstate_session_start(svc_sm, new_session); + break; + case SMB_SVCEVT_CONNECT: + case SMB_SVCEVT_SESSION_DELETE: + /* No state-specific action required */ + break; + case SMB_SVCEVT_CONFIG: + smb_svcstate_update(svc_sm, SMB_SVCSTATE_RECONFIGURING); + break; + case SMB_SVCEVT_CLOSE: + smb_svcstate_update(svc_sm, + SMB_SVCSTATE_DISCONNECTING); + break; + case SMB_SVCEVT_DISCONNECT: + /* + * The session service daemon unexpectedly stopped. Looks + * like we're done talking SMB for the day. + */ + smb_svcstate_update(svc_sm, SMB_SVCSTATE_ERROR_SESSION_CLOSE); + break; + default: + ASSERT(0); + break; + } +} + +static void +smb_svcstate_reconfiguring(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx) +{ + smb_session_t *new_session; + + switch (event_ctx->sec_event) { + case SMB_SVCEVT_SESSION_CREATE: + /* Event context is the new socket */ + new_session = (smb_session_t *)event_ctx->sec_info; + smb_svcstate_session_defer(svc_sm, new_session); + break; + case SMB_SVCEVT_SESSION_DELETE: + /* No state-specific action required */ + break; + case SMB_SVCEVT_CONFIG: + /* Hopefully this won't happen but if it does we ignore it */ + break; + case SMB_SVCEVT_CONFIG_SUCCESS: + case SMB_SVCEVT_CONFIG_FAILED: + smb_svcstate_update(svc_sm, SMB_SVCSTATE_ONLINE); + break; + case SMB_SVCEVT_CLOSE: + smb_svcstate_update(svc_sm, + SMB_SVCSTATE_DISCONNECTING); + break; + case SMB_SVCEVT_DISCONNECT: + /* + * The session service daemon unexpectedly stopped. Looks + * like we're done talking SMB for the day. + */ + smb_svcstate_update(svc_sm, SMB_SVCSTATE_ERROR_SESSION_CLOSE); + break; + default: + ASSERT(0); + break; + } +} + +static void +smb_svcstate_disconnecting(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx) +{ + smb_session_t *session; + + switch (event_ctx->sec_event) { + case SMB_SVCEVT_SESSION_CREATE: + /* Event context is the new socket */ + session = (smb_session_t *)event_ctx->sec_info; + + /* We're not online so reject the connection */ + smb_session_reject(session, "SMB service is shutting down."); + smb_session_delete(session); + break; + case SMB_SVCEVT_CONNECT: + case SMB_SVCEVT_SESSION_DELETE: + /* No state-specific action required */ + break; + case SMB_SVCEVT_DISCONNECT: + smb_svcstate_update(svc_sm, SMB_SVCSTATE_SESSION_CLOSE); + break; + default: + ASSERT(0); + break; + } +} + +static void +smb_svcstate_session_close(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx) +{ + smb_session_t *session; + + /* + * We continue to accept "session creates" because we might + * accept a connection while the SMB_SVCEVT_CLOSE is + * queued but not yet handled. + */ + switch (event_ctx->sec_event) { + case SMB_SVCEVT_SESSION_CREATE: + /* Event context is the new socket */ + session = (smb_session_t *)event_ctx->sec_info; + + /* We're not online so reject the connection */ + smb_session_reject(session, "Not configured"); + smb_session_delete(session); + /*FALLTHROUGH*/ + case SMB_SVCEVT_SESSION_DELETE: + if ((svc_sm->ssc_active_session_count == 0) && + (svc_sm->ssc_session_creates_waiting == 0)) { + smb_svcstate_update(svc_sm, + SMB_SVCSTATE_CLOSING); + } + break; + case SMB_SVCEVT_DISCONNECT: + /* No state-specific action required */ + break; + default: + ASSERT(0); + break; + } +} + +static void +smb_svcstate_error_session_close(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx) +{ + /* + * Since our connection dropped we shouldn't see any more + * "creates" in this state + */ + switch (event_ctx->sec_event) { + case SMB_SVCEVT_CLOSE: + smb_svcstate_update(svc_sm, SMB_SVCSTATE_SESSION_CLOSE); + break; + case SMB_SVCEVT_SESSION_DELETE: + if ((svc_sm->ssc_active_session_count == 0) && + (svc_sm->ssc_session_creates_waiting == 0)) { + smb_svcstate_update(svc_sm, + SMB_SVCSTATE_ERROR_CLOSING); + } + break; + default: + ASSERT(0); + break; + } +} + +static void +smb_svcstate_closing(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx) +{ + switch (event_ctx->sec_event) { + case SMB_SVCEVT_CLOSE_SUCCESS: + smb_svcstate_update(svc_sm, SMB_SVCSTATE_INIT); + break; + default: + break; + } +} + +static void +smb_svcstate_error_closing(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx) +{ + switch (event_ctx->sec_event) { + case SMB_SVCEVT_CLOSE: + smb_svcstate_update(svc_sm, SMB_SVCSTATE_CLOSING); + break; + case SMB_SVCEVT_CLOSE_SUCCESS: + smb_svcstate_update(svc_sm, SMB_SVCSTATE_ERROR); + break; + default: + break; + } +} + +static void +smb_svcstate_error(smb_svc_sm_ctx_t *svc_sm, + smb_event_ctx_t *event_ctx) +{ + ASSERT(event_ctx->sec_event == SMB_SVCEVT_CLOSE); + + switch (event_ctx->sec_event) { + case SMB_SVCEVT_CLOSE: + smb_svcstate_update(svc_sm, SMB_SVCSTATE_INIT); + break; + default: + break; + } +} + +static void +smb_svcstate_update(smb_svc_sm_ctx_t *svc_sm, smb_svcstate_t new_state_arg) +{ + smb_svcstate_t new_state; + int error; + + /* + * Validate new state + */ + ASSERT(new_state_arg != SMB_SVCSTATE_UNDEFINED); + ASSERT3U(new_state_arg, <, SMB_SVCSTATE_MAX_STATE); + + /* + * Update state in context. We protect this with a mutex + * even though the state machine code is single threaded so that + * other threads can check the state value atomically. + */ + new_state = (new_state_arg < SMB_SVCSTATE_MAX_STATE) ? + new_state_arg: SMB_SVCSTATE_UNDEFINED; + + DTRACE_PROBE2(service__state__change, + smb_svc_sm_ctx_t *, svc_sm, smb_svcstate_t, new_state); + mutex_enter(&svc_sm->ssc_state_cv_mutex); + svc_sm->ssc_last_state = svc_sm->ssc_state; + svc_sm->ssc_state = new_state; + cv_signal(&svc_sm->ssc_state_cv); + mutex_exit(&svc_sm->ssc_state_cv_mutex); + + /* + * Now perform the appropiate actions for the new state + */ + switch (new_state) { + case SMB_SVCSTATE_INIT: + smb_svcstate_set_started(svc_sm, SMB_STOPPED); + break; + case SMB_SVCSTATE_OPENING: + /* + * Start all SMB subsystems and connect to socket + */ + svc_sm->ssc_start_error = smb_service_open(&smb_info); + if (svc_sm->ssc_start_error != 0) { + smb_svcstate_event(SMB_SVCEVT_OPEN_FAILED, NULL); + } else { + /* Open actions successful */ + smb_svcstate_event(SMB_SVCEVT_OPEN_SUCCESS, NULL); + } + break; + case SMB_SVCSTATE_CONFIG_WAIT: + /* + * Nothing in particular to do here except to note the + * state change. Now we wait for smbd to provide + * our configuration. + */ + /* + * XXX For now this is done as part of smb_service_open() + * so just send the "success" event. + */ + smb_svcstate_event(SMB_SVCEVT_CONFIG_SUCCESS, NULL); + break; + case SMB_SVCSTATE_CONNECTING: + /* + * When we move to a userland thread model we will rely + * on smbd to start the SMB socket service thread. + */ + error = smb_service_connect(&smb_info); + if (error != 0) + smb_svcstate_event(SMB_SVCEVT_DISCONNECT, + (uintptr_t)error); + break; + case SMB_SVCSTATE_ONLINE: + smb_svcstate_start_deferred_sessions(svc_sm); + /* No actions */ + break; + case SMB_SVCSTATE_RECONFIGURING: + (void) smb_get_kconfig(&smb_info.si); /* XXX */ + break; + case SMB_SVCSTATE_DISCONNECTING: + smb_svcstate_reject_deferred_sessions(svc_sm); + smb_service_disconnect(&smb_info); + break; + case SMB_SVCSTATE_ERROR_SESSION_CLOSE: + smb_svcstate_reject_deferred_sessions(svc_sm); + if ((svc_sm->ssc_active_session_count == 0) && + (svc_sm->ssc_session_creates_waiting == 0)) { + smb_svcstate_update(svc_sm, + SMB_SVCSTATE_ERROR_CLOSING); + } else { + smb_svcstate_close_active_sessions(svc_sm); + } + break; + case SMB_SVCSTATE_SESSION_CLOSE: + if ((svc_sm->ssc_active_session_count == 0) && + (svc_sm->ssc_session_creates_waiting == 0)) { + smb_svcstate_update(svc_sm, SMB_SVCSTATE_CLOSING); + } else { + smb_svcstate_close_active_sessions(svc_sm); + } + break; + case SMB_SVCSTATE_ERROR_CLOSING: + case SMB_SVCSTATE_CLOSING: + smb_service_close(&smb_info); + smb_svcstate_event(SMB_SVCEVT_CLOSE_SUCCESS, NULL); + break; + case SMB_SVCSTATE_ERROR: + /* No actions */ + break; + default: + ASSERT(0); + break; + } +} + +static void +smb_svcstate_set_started(smb_svc_sm_ctx_t *svc_sm, int started) +{ + mutex_enter(&svc_sm->ssc_state_cv_mutex); + /* Make sure we have an error code if we failed to start */ + ASSERT(started != SMB_START_FAILED || svc_sm->ssc_start_error != 0); + svc_sm->ssc_started = started; + cv_signal(&svc_sm->ssc_state_cv); + mutex_exit(&svc_sm->ssc_state_cv_mutex); +} + +static void +smb_svcstate_session_start(smb_svc_sm_ctx_t *svc_sm, + smb_session_t *new_session) +{ + rw_enter(&svc_sm->ssc_state_rwlock, RW_WRITER); + if (svc_sm->ssc_active_session_count >= + smb_info.si.skc_maxconnections) { + svc_sm->ssc_error_no_resources++; + rw_exit(&svc_sm->ssc_state_rwlock); + + smb_session_reject(new_session, "Too many open sessions"); + smb_session_delete(new_session); + } else { + new_session->s_state = SMB_SESSION_STATE_CONNECTED; + list_insert_tail(&svc_sm->ssc_active_sessions, new_session); + svc_sm->ssc_active_session_count++; + rw_exit(&svc_sm->ssc_state_rwlock); + + /* + * Blocks until thread has started + */ + if (smb_thread_start(&new_session->s_thread) != 0) { + smb_svcstate_session_reject_active(svc_sm, new_session, + "Session thread creation failed"); + } else { + DTRACE_PROBE1(session__create, + struct session *, new_session); + } + } +} + +static void +smb_svcstate_session_defer(smb_svc_sm_ctx_t *svc_sm, + smb_session_t *new_session) +{ + list_insert_tail(&svc_sm->ssc_deferred_sessions, new_session); + svc_sm->ssc_deferred_session_count++; +} + +/*ARGSUSED*/ +static void +smb_svcstate_session_reject_active(smb_svc_sm_ctx_t *svc_sm, + smb_session_t *session, char *reason) +{ + smb_session_reject(session, reason); + + smb_svcstate_event(SMB_SVCEVT_SESSION_DELETE, (uintptr_t)session); +} + +static void +smb_svcstate_start_deferred_sessions(smb_svc_sm_ctx_t *svc_sm) +{ + smb_session_t *session, *next_session; + + /* + * svc_sm->ssc_deferred_sessions is private to the (single-threaded) + * state machine so we don't need to lock it. + */ + session = list_head(&svc_sm->ssc_deferred_sessions); + while (session != NULL) { + next_session = + list_next(&svc_sm->ssc_deferred_sessions, session); + list_remove(&svc_sm->ssc_deferred_sessions, session); + svc_sm->ssc_deferred_session_count--; + smb_svcstate_session_start(svc_sm, session); + session = next_session; + } +} + +static void +smb_svcstate_reject_deferred_sessions(smb_svc_sm_ctx_t *svc_sm) +{ + smb_session_t *session, *next_session; + + + /* + * svc_sm->ssc_deferred_sessions is private to the (single-threaded) + * state machine so we don't need to lock it. + */ + session = list_head(&svc_sm->ssc_deferred_sessions); + while (session != NULL) { + next_session = + list_next(&svc_sm->ssc_deferred_sessions, session); + list_remove(&svc_sm->ssc_deferred_sessions, session); + svc_sm->ssc_deferred_session_count--; + smb_svcstate_session_reject_active(svc_sm, session, + "SMB service is shutting down (deferred)"); + session = next_session; + } +} + +static void +smb_svcstate_close_active_sessions(smb_svc_sm_ctx_t *svc_sm) +{ + smb_session_t *session; + + rw_enter(&svc_sm->ssc_state_rwlock, RW_WRITER); + for (session = list_head(&svc_sm->ssc_active_sessions); + session != NULL; + session = list_next(&svc_sm->ssc_active_sessions, session)) { + ASSERT(session->s_magic == SMB_SESSION_MAGIC); + rw_exit(&svc_sm->ssc_state_rwlock); + + /* + * As each session thread terminates it will generate + * a "session delete" event. + */ + smb_thread_stop(&session->s_thread); + + rw_enter(&svc_sm->ssc_state_rwlock, RW_WRITER); + } + rw_exit(&svc_sm->ssc_state_rwlock); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_create_directory.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_create_directory.c new file mode 100644 index 000000000000..6ef6691df381 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_create_directory.c @@ -0,0 +1,97 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: trans2_create_directory + * + * This requests the server to create a directory relative to Tid in the + * SMB header, optionally assigning extended attributes to it. + * + * Client Request Value + * ========================== ========================================= + * + * WordCount 15 + * MaxSetupCount 0 + * SetupCount 1 + * Setup[0] TRANS2_CREATE_DIRECTORY + * + * Parameter Block Encoding Description + * ========================== ========================================= + * + * ULONG Reserved; Reserved--must be zero + * STRING Name[]; Directory name to create + * UCHAR Data[]; Optional FEAList for the new directory + * + * Response Parameter Block Description + * ========================== ========================================= + * + * USHORT EaErrorOffset Offset into FEAList of first error which + * occurred while setting EAs + */ + +#include +#include +#include + + +extern int smb_common_create_directory(struct smb_request *sr); + + +/* + * smb_com_trans2_create_directory + */ +int +smb_com_trans2_create_directory(struct smb_request *sr, struct smb_xa *xa) +{ + int rc; + DWORD status; + + if (smb_decode_mbc(&xa->req_param_mb, "%4.s", + sr, &sr->arg.dirop.fqi.path) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if ((status = smb_validate_dirname(sr->arg.dirop.fqi.path)) != 0) { + if (sr->session->capabilities & CAP_STATUS32) + smbsr_raise_nt_error(sr, status); + else + smbsr_raise_error(sr, ERRDOS, ERROR_INVALID_NAME); + + /* NOTREACHED */ + } + + if ((rc = smb_common_create_directory(sr)) != 0) { + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + if (smb_encode_mbc(&xa->rep_param_mb, "w", 0) < 0) + smbsr_encode_error(sr); + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_dfs.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_dfs.c new file mode 100644 index 000000000000..2afbcc678681 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_dfs.c @@ -0,0 +1,205 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + + +/* + * trans2_get_dfs_referral + * + * The client sends this request to ask the server to convert + * RequestFilename into an alternate name for this file. This request can + * be sent to the server if the server response to the NEGOTIATE SMB + * included the CAP_DFS capability. The TID of the request must be IPC$. + * Bit15 of Flags2 in the SMB header must be set, indicating this is a + * UNICODE request. + * + * Client Request Description + * ========================== ========================================= + * WordCount 15 + * TotalDataCount 0 + * SetupCount 1 + * Setup[0] TRANS2_GET_DFS_REFERRAL + * + * Parameter Block Encoding Description + * ========================== ========================================= + * USHORT MaxReferralLevel Latest referral version number understood + * WCHAR RequestFileName; DFS name of file for which referral is + * sought + * + * Response Data Block Description + * ========================== ========================================= + * USHORT PathConsumed; Number of RequestFilename bytes client + * USHORT NumberOfReferrals; Number of referrals contained in this + * response + * USHORT Flags; bit0 - The servers in Referrals are + * capable of fielding + * TRANS2_GET_DFS_REFERRAL. + * bit1 - The servers in Referrals should + * hold the storage for the requested file. + * REFERRAL_LIST Referrals[] Set of referrals for this file + * UNICODESTRINGE Strings Used to hold the strings pointed to by + * Version 2 Referrals in REFERRALS. + * + * The server response is a list of Referrals which inform the client where + * it should resubmit the request to obtain access to the file. + * PathConsumed in the response indicates to the client how many characters + * of RequestFilename have been consumed by the server. When the client + * chooses one of the referrals to use for file access, the client may need + * to strip the leading PathConsumed characters from the front of + * RequestFileName before submitting the name to the target server. + * Whether or not the pathname should be trimmed is indicated by the + * individual referral as detailed below. + * + * Flags indicates how this referral should be treated. If bit0 is clear, + * any entity in the Referrals list holds the storage for RequestFileName. + * If bit0 is set, any entity in the Referrals list has further referral + * information for RequestFilename – a TRANS2_GET_DFS_REFERRAL request + * should be sent to an entity in the Referrals list for further + * resolution. + * + * The format of an individual referral contains version and length + * information allowing the client to skip referrals it does not + * understand. MaxReferralLevel indicates to the server the latest version + * of referral which the client can digest. Since each referral has a + * uniform element, MaxReferralLevel is advisory only. Each element in + * Referrals has this envelope: + * + * REFERRAL_LIST element + * ====================================================================== + * + * USHORT VersionNumber Version of this referral element + * + * USHORT ReferralSize Size of this referral element + * + * The following referral element versions are defined: + * + * Version 1 Referral Element Format + * ====================================================================== + * + * USHORT ServerType Type of Node handling referral: + * 0 - Don't know + * 1 - SMB Server + * 2 - Netware Server + * 3 - Domain + * + * USHORT ReferralFlags Flags which describe this referral: + * 01 - Strip off PathConsumed characters + * before submitting RequestFileName to Node + * + * UNICODESTRING Node Name of entity to visit next + * + * Version 2 Referral Element Format + * ====================================================================== + * + * USHORT ServerType Type of Node handling referral: + * 0 - Don't know + * 1 - SMB Server + * 2 - Netware Server + * 3 - Domain + * + * USHORT ReferralFlags Flags which describe this referral: + * 01 - Strip off PathConsumed characters + * before submitting RequestFileName to + * Node + * + * ULONG Proximity A hint describing the proximity of this + * server to the client. 0 indicates the + * closest, higher numbers indicate + * increasingly "distant" servers. The + * number is only relevant within the + * context of the servers listed in this + * particular SMB. + * + * ULONG TimeToLive Number of seconds for which the client + * can cache this referral. + * + * USHORT DfsPathOffset Offset, in bytes from the beginning of + * this referral, of the DFS Path that + * matched PathConsumed bytes of the + * RequestFileName. + * + * USHORT DfsAlternatePathOffset Offset, in bytes from the beginning of + * this referral, of an alternate name + * (8.3 format) of the DFS Path that + * matched PathConsumed bytes of the + * RequestFileName. + * + * USHORT NetworkAddressOffset Offset, in bytes from the beginning of + * this referral, of the entity to visit + * next. + * + * The CIFS protocol imposes no referral selection policy. + */ +int /*ARGSUSED*/ +smb_com_trans2_get_dfs_referral(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} + + +/* + * SMB: trans2_report_dfs_inconsistency + * + * As part of the Distributed Name Resolution algorithm, a DFS client may + * discover a knowledge inconsistency between the referral server (i.e., + * the server that handed out a referral), and the storage server (i.e., + * the server to which the client was redirected to by the referral + * server). When such an inconsistency is discovered, the DFS client + * optionally sends this SMB to the referral server, allowing the referral + * server to take corrective action. + * + * Client Request Description + * ================================== ================================== + * WordCount 15 + * MaxParameterCount 0 + * SetupCount 1 + * Setup[0] TRANS2_REPORT_DFS_INCONSISTENCY + * + * Parameter Block Encoding Description + * ================================== ================================== + * + * UNICODESTRING RequestFileName; DFS Name of file for which + * referral was sought + * + * The data part of this request contains the referral element (Version 1 + * format only) believed to be in error. These are encoded as described in + * the TRANS2_GET_DFS_REFERRAL response. If the server returns success, + * the client can resubmit the TRANS2_GET_DFS_REFERRAL request to this + * server to get a new referral. It is not mandatory for the DFS knowledge + * to be automatically repaired – the client must be prepared to receive + * further errant referrals and must not wind up looping between this + * request and the TRANS2_GET_DFS_REFERRAL request. + * + * Bit15 of Flags2 in the SMB header must be set, indicating this is a + * UNICODE request. + */ +int /*ARGSUSED*/ +smb_com_trans2_report_dfs_inconsistency(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c new file mode 100644 index 000000000000..3ab70761027d --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c @@ -0,0 +1,1142 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module provides functions for TRANS2_FIND_FIRST2 and + * TRANS2_FIND_NEXT2 requests. The requests allow the client to search + * for the file(s) which match the file specification. The search is + * started with TRANS2_FIND_FIRST2 and can be continued if necessary with + * TRANS2_FIND_NEXT2. There are numerous levels of information which may be + * obtained for the returned files, the desired level is specified in the + * InformationLevel field of the requests. + * + * InformationLevel Name Value + * ================================= ================ + * + * SMB_INFO_STANDARD 1 + * SMB_INFO_QUERY_EA_SIZE 2 + * SMB_INFO_QUERY_EAS_FROM_LIST 3 + * SMB_FIND_FILE_DIRECTORY_INFO 0x101 + * SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 + * SMB_FIND_FILE_NAMES_INFO 0x103 + * SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 + * + * The following sections detail the data returned for each + * InformationLevel. The requested information is placed in the Data + * portion of the transaction response. Note: a client which does not + * support long names can only request SMB_INFO_STANDARD. + * + * A four-byte resume key precedes each data item (described below) if bit + * 2 in the Flags field is set, i.e. if the request indicates the server + * should return resume keys. Note: it is not always the case. If the + * data item already includes the resume key, the resume key should not be + * added again. + * + * 4.3.4.1 SMB_INFO_STANDARD + * + * Response Field Description + * ================================ ================================== + * + * SMB_DATE CreationDate; Date when file was created + * SMB_TIME CreationTime; Time when file was created + * SMB_DATE LastAccessDate; Date of last file access + * SMB_TIME LastAccessTime; Time of last file access + * SMB_DATE LastWriteDate; Date of last write to the file + * SMB_TIME LastWriteTime; Time of last write to the file + * ULONG DataSize; File Size + * ULONG AllocationSize; Size of filesystem allocation unit + * USHORT Attributes; File Attributes + * UCHAR FileNameLength; Length of filename in bytes + * STRING FileName; Name of found file + * + * 4.3.4.2 SMB_INFO_QUERY_EA_SIZE + * + * Response Field Description + * ================================= ================================== + * + * SMB_DATE CreationDate; Date when file was created + * SMB_TIME CreationTime; Time when file was created + * SMB_DATE LastAccessDate; Date of last file access + * SMB_TIME LastAccessTime; Time of last file access + * SMB_DATE LastWriteDate; Date of last write to the file + * SMB_TIME LastWriteTime; Time of last write to the file + * ULONG DataSize; File Size + * ULONG AllocationSize; Size of filesystem allocation unit + * USHORT Attributes; File Attributes + * ULONG EaSize; Size of file's EA information + * UCHAR FileNameLength; Length of filename in bytes + * STRING FileName; Name of found file + * + * 4.3.4.3 SMB_INFO_QUERY_EAS_FROM_LIST + * + * This request returns the same information as SMB_INFO_QUERY_EA_SIZE, but + * only for files which have an EA list which match the EA information in + * the Data part of the request. + * + * 4.3.4.4 SMB_FIND_FILE_DIRECTORY_INFO + * + * Response Field Description + * ================================= ================================== + * + * ULONG NextEntryOffset; Offset from this structure to + * beginning of next one + * ULONG FileIndex; + * LARGE_INTEGER CreationTime; file creation time + * LARGE_INTEGER LastAccessTime; last access time + * LARGE_INTEGER LastWriteTime; last write time + * LARGE_INTEGER ChangeTime; last attribute change time + * LARGE_INTEGER EndOfFile; file size + * LARGE_INTEGER AllocationSize; size of filesystem allocation information + * ULONG ExtFileAttributes; Extended file attributes + * (see section 3.11) + * ULONG FileNameLength; Length of filename in bytes + * STRING FileName; Name of the file + * + * 4.3.4.5 SMB_FIND_FILE_FULL_DIRECTORY_INFO + * + * Response Field Description + * ================================= ================================== + * + * ULONG NextEntryOffset; Offset from this structure to + * beginning of next one + * ULONG FileIndex; + * LARGE_INTEGER CreationTime; file creation time + * LARGE_INTEGER LastAccessTime; last access time + * LARGE_INTEGER LastWriteTime; last write time + * LARGE_INTEGER ChangeTime; last attribute change time + * LARGE_INTEGER EndOfFile; file size + * LARGE_INTEGER AllocationSize; size of filesystem allocation information + * ULONG ExtFileAttributes; Extended file attributes + * (see section 3.11) + * ULONG FileNameLength; Length of filename in bytes + * ULONG EaSize; Size of file's extended attributes + * STRING FileName; Name of the file + * + * 4.3.4.6 SMB_FIND_FILE_BOTH_DIRECTORY_INFO + * + * Response Field Description + * ================================= ================================== + * + * ULONG NextEntryOffset; Offset from this structure to + * beginning of next one + * ULONG FileIndex; + * LARGE_INTEGER CreationTime; file creation time + * LARGE_INTEGER LastAccessTime; last access time + * LARGE_INTEGER LastWriteTime; last write time + * LARGE_INTEGER ChangeTime; last attribute change time + * LARGE_INTEGER EndOfFile; file size + * LARGE_INTEGER AllocationSize; size of filesystem allocation information + * ULONG ExtFileAttributes; Extended file attributes + * (see section 3.11) + * ULONG FileNameLength; Length of FileName in bytes + * ULONG EaSize; Size of file's extended attributes + * UCHAR ShortNameLength; Length of file's short name in bytes + * UCHAR Reserved + * WCHAR ShortName[12]; File's 8.3 conformant name in Unicode + * STRING FileName; Files full length name + * + * 4.3.4.7 SMB_FIND_FILE_NAMES_INFO + * + * Response Field Description + * ================================= ================================== + * + * ULONG NextEntryOffset; Offset from this structure to + * beginning of next one + * ULONG FileIndex; + * ULONG FileNameLength; Length of FileName in bytes + * STRING FileName; Files full length name + */ + +#include +#include +#include +#include + +int smb_trans2_find_get_maxdata(struct smb_request *, unsigned short, + unsigned short); + +int smb_trans2_find_get_dents(struct smb_request *, struct smb_xa *, + unsigned short, unsigned short, int, struct smb_node *, + unsigned short, uint32_t, int, char *, uint32_t *, int *, int *); + +int smb_gather_dents_info(char *, ino_t, int, char *, uint32_t, int32_t *, + smb_attr_t *, struct smb_node *, char *, char *); + +int smb_trans2_find_process_ients(struct smb_request *, struct smb_xa *, + smb_dent_info_hdr_t *, unsigned short, unsigned short, int, + struct smb_node *, int *, uint32_t *); + +int smb_trans2_find_mbc_encode(struct smb_request *, struct smb_xa *, + smb_dent_info_t *, int, unsigned short, unsigned short, + unsigned int, struct smb_node *, struct smb_node *); + +/* + * Support for Catia Version 5 Deployment + */ +static int (*catia_callback)(unsigned char *, unsigned char *, int) = NULL; +void smb_register_catia_callback( + int (*catia_v4tov5)(unsigned char *, unsigned char *, int)); +void smb_unregister_catia_callback(); + +/* + * Patchable parameter for find maximum count + */ +int max_find_count = 64; + +/* + * smb_register_catia_callback + * + * This function will be invoked by the catia module to register its + * function that translates filename in version 4 to a format that is + * compatible to version 5. + */ +void +smb_register_catia_callback( + int (*catia_v4tov5)(unsigned char *, unsigned char *, int)) +{ + catia_callback = catia_v4tov5; +} + +/* + * smb_unregister_catia_callback + * + * This function will unregister the catia callback prior to the catia + * module gets unloaded. + */ +void +smb_unregister_catia_callback() +{ + catia_callback = 0; +} + +/* + * smb_com_trans2_find_first2 + * + * Client Request Value + * ============================ ================================== + * + * UCHAR WordCount 15 + * UCHAR TotalDataCount Total size of extended attribute list + * UCHAR SetupCount 1 + * UCHAR Setup[0] TRANS2_FIND_FIRST2 + * + * Parameter Block Encoding Description + * ============================ ================================== + * USHORT SearchAttributes; + * USHORT SearchCount; Maximum number of entries to return + * USHORT Flags; Additional information: + * Bit 0 - close search after this request + * Bit 1 - close search if end of search + * reached + * Bit 2 - return resume keys for each + * entry found + * Bit 3 - continue search from previous + * ending place + * Bit 4 - find with backup intent + * USHORT InformationLevel; See below + * ULONG SearchStorageType; + * STRING FileName; Pattern for the search + * UCHAR Data[ TotalDataCount ] FEAList if InformationLevel is + * QUERY_EAS_FROM_LIST + * + * Response Parameter Block Description + * ============================ ================================== + * + * USHORT Sid; Search handle + * USHORT SearchCount; Number of entries returned + * USHORT EndOfSearch; Was last entry returned? + * USHORT EaErrorOffset; Offset into EA list if EA error + * USHORT LastNameOffset; Offset into data to file name of last + * entry, if server needs it to resume + * search; else 0 + * UCHAR Data[ TotalDataCount ] Level dependent info about the matches + * found in the search + */ +int +smb_com_trans2_find_first2(struct smb_request *sr, struct smb_xa *xa) +{ + int more = 0, rc; + unsigned short sattr, fflag, infolev; + int maxdata; + int count, maxcount = 0, wildcards; + uint32_t cookie; + char *path; + struct smb_node *dir_snode; + char *pattern; + unsigned short sid; + + if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + if (smb_decode_mbc(&xa->req_param_mb, "%wwww4.u", sr, + &sattr, &maxcount, &fflag, &infolev, &path) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + maxdata = smb_trans2_find_get_maxdata(sr, infolev, fflag); + + if (maxdata == 0) { + smbsr_raise_error(sr, ERRDOS, ERRunknownlevel); + /* NOTREACHED */ + } + + /* Convert name to our form */ + if (sr->smb_flg2 & SMB_FLAGS2_UNICODE) { + (void) smb_convert_unicode_wildcards(path); + } + (void) smb_rdir_open(sr, path, sattr); + + /* + * Get a copy of information + */ + pattern = kmem_alloc(MAXNAMELEN, KM_SLEEP); + dir_snode = sr->sid_odir->d_dir_snode; + (void) strcpy(pattern, sr->sid_odir->d_pattern); + /* this is funky */ + if (strcmp(pattern, "*.*") == 0) + (void) strncpy(pattern, "*", sizeof (pattern)); + wildcards = sr->sid_odir->d_wildcards; + sattr = sr->sid_odir->d_sattr; + cookie = 0; + + rc = smb_trans2_find_get_dents(sr, xa, fflag, infolev, maxdata, + dir_snode, sattr, maxcount, wildcards, + pattern, &cookie, &more, &count); + + if (!count) + rc = ENOENT; + + if (rc) { + smb_rdir_close(sr); + kmem_free(pattern, MAXNAMELEN); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + /* + * Save the sid here because search might get closed + * and sr->smb_sid becomes invalid. + * This might not seem important because the search + * is going to be finished anyways, but it's just for + * the sake of compatibility with Windows. + */ + sid = sr->smb_sid; + + if (fflag & SMB_FIND_CLOSE_AFTER_REQUEST || + (!more && fflag & SMB_FIND_CLOSE_AT_EOS)) + smb_rdir_close(sr); + else { + mutex_enter(&sr->sid_odir->d_mutex); + sr->sid_odir->d_cookie = cookie; + mutex_exit(&sr->sid_odir->d_mutex); + } + + (void) smb_encode_mbc(&xa->rep_param_mb, "wwwww", + sid, count, (more ? 0 : 1), 0, 0); + + kmem_free(pattern, MAXNAMELEN); + return (SDRC_NORMAL_REPLY); +} + + + +/* + * smb_com_trans2_find_next2 + * + * Client Request Value + * ================================== ================================= + * + * WordCount 15 + * SetupCount 1 + * Setup[0] TRANS2_FIND_NEXT2 + * + * Parameter Block Encoding Description + * ================================== ================================= + * + * USHORT Sid; Search handle + * USHORT SearchCount; Maximum number of entries to + * return + * USHORT InformationLevel; Levels described in + * TRANS2_FIND_FIRST2 request + * ULONG ResumeKey; Value returned by previous find2 + * call + * USHORT Flags; Additional information: bit set- + * 0 - close search after this + * request + * 1 - close search if end of search + * reached + * 2 - return resume keys for each + * entry found + * 3 - resume/continue from previous + * ending place + * 4 - find with backup intent + * STRING FileName; Resume file name + * + * Sid is the value returned by a previous successful TRANS2_FIND_FIRST2 + * call. If Bit3 of Flags is set, then FileName may be the NULL string, + * since the search is continued from the previous TRANS2_FIND request. + * Otherwise, FileName must not be more than 256 characters long. + * + * Response Field Description + * ================================== ================================= + * + * USHORT SearchCount; Number of entries returned + * USHORT EndOfSearch; Was last entry returned? + * USHORT EaErrorOffset; Offset into EA list if EA error + * USHORT LastNameOffset; Offset into data to file name of + * last entry, if server needs it to + * resume search; else 0 + * UCHAR Data[TotalDataCount] Level dependent info about the + * matches found in the search + */ +int +smb_com_trans2_find_next2(struct smb_request *sr, struct smb_xa *xa) +{ + unsigned short fflag, infolev; + int maxdata, count, wildcards, more = 0, rc; + uint32_t cookie; + uint32_t maxcount = 0; + struct smb_node *dir_snode; + char *pattern; + unsigned short sattr; + + pattern = kmem_alloc(MAXNAMELEN, KM_SLEEP); + /* + * There is a path field as the last piece of input information: + * + * smb_decode_mbc(&xa->req_param_mb, "%www lwu", sr, + * &sr->smb_sid, &maxcount, &infolev, &cookie, &fflag, &path) + * + * This feild has been removed because it's causing problem + * with Mac OS 10 and it's not used anyways. + * The problem is that code expects to see a 2-byte null + * because the strings are supposed to be Unicode, but + * Max OS 10 sends a 1-byte null which leads to decode error. + */ + if (smb_decode_mbc(&xa->req_param_mb, "%www lw", sr, + &sr->smb_sid, &maxcount, &infolev, &cookie, &fflag) != 0) { + kmem_free(pattern, MAXNAMELEN); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + maxdata = smb_trans2_find_get_maxdata(sr, infolev, fflag); + + sr->sid_odir = smb_odir_lookup_by_sid(sr->tid_tree, sr->smb_sid); + if (sr->sid_odir == NULL) { + kmem_free(pattern, MAXNAMELEN); + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + if (maxdata == 0) { + smb_rdir_close(sr); + kmem_free(pattern, MAXNAMELEN); + smbsr_raise_error(sr, ERRDOS, ERRunknownlevel); + /* NOTREACHED */ + } + + /* + * Get a copy of information + */ + dir_snode = sr->sid_odir->d_dir_snode; + (void) strcpy(pattern, sr->sid_odir->d_pattern); + wildcards = sr->sid_odir->d_wildcards; + sattr = sr->sid_odir->d_sattr; + if (fflag & SMB_FIND_CONTINUE_FROM_LAST) { + mutex_enter(&sr->sid_odir->d_mutex); + cookie = sr->sid_odir->d_cookie; + mutex_exit(&sr->sid_odir->d_mutex); + } + + /* + * XXX this is an optimization made for SFS2 filesystem, it might + * not be required for ZFS + * + * Break the count to smaller counts (less than the default 150) + * to reduce the number of transaction failures + * which may cause excessive delays and eventually a SMB staled + * connection. + */ + maxcount = (maxcount > max_find_count) ? max_find_count : maxcount; + + rc = smb_trans2_find_get_dents(sr, xa, fflag, infolev, maxdata, + dir_snode, sattr, maxcount, wildcards, pattern, &cookie, + &more, &count); + + if (rc) { + smb_rdir_close(sr); + kmem_free(pattern, MAXNAMELEN); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + if (fflag & SMB_FIND_CLOSE_AFTER_REQUEST || + (!more && fflag & SMB_FIND_CLOSE_AT_EOS)) + smb_rdir_close(sr); + else { + mutex_enter(&sr->sid_odir->d_mutex); + sr->sid_odir->d_cookie = cookie; + mutex_exit(&sr->sid_odir->d_mutex); + } + + (void) smb_encode_mbc(&xa->rep_param_mb, "wwww", + count, (more ? 0 : 1), 0, 0); + + kmem_free(pattern, MAXNAMELEN); + return (SDRC_NORMAL_REPLY); +} + + +/* + * smb_trans2_find_get_maxdata + * + * This function calculates the minimum space requirement for the + * base on information level and fflag. + * + * When success, minimum space requirement will be returned; otherwise, + * 0 will be returned. + */ +int +smb_trans2_find_get_maxdata( + struct smb_request *sr, + unsigned short infolev, + unsigned short fflag) +{ + int maxdata; + + maxdata = smb_ascii_or_unicode_null_len(sr); + + switch (infolev) { + case SMB_INFO_STANDARD : + if (fflag & SMB_FIND_RETURN_RESUME_KEYS) + maxdata += sizeof (int32_t); + maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 1; + break; + + case SMB_INFO_QUERY_EA_SIZE: + if (fflag & SMB_FIND_RETURN_RESUME_KEYS) + maxdata += sizeof (int32_t); + maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 4 + 1; + break; + + case SMB_FIND_FILE_DIRECTORY_INFO: + maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4; + break; + + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 2 + 24; + break; + + case SMB_FIND_FILE_NAMES_INFO: + maxdata += 4 + 4 + 4; + break; + + case SMB_MAC_FIND_BOTH_HFS_INFO: + maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 1 + 1 + 2 + + 4 + 32 + 4 + 1 + 1 + 24 + 4; + break; + + default: + maxdata = 0; + } + + return (maxdata); +} + + + +/* + * smb_trans2_find_get_dents + * + * This function will get all the directory entry information and mbc + * encode it in the xa. If there is an error, it will be returned; + * otherwise, 0 is returned. + * + * The more field will be updated. If the value returned is one, it means + * there are more entries; otherwise, the returned value will be zero. The + * cookie will also be updated to indicate the next start point for the + * search. The count value will also be updated to stores the total entries + * encoded. + */ +int smb_trans2_find_get_dents( + smb_request_t *sr, + smb_xa_t *xa, + unsigned short fflag, + unsigned short infolev, + int maxdata, + smb_node_t *dir_snode, + unsigned short sattr, + uint32_t maxcount, + int wildcards, + char *pattern, + uint32_t *cookie, + int *more, + int *count) +{ + smb_dent_info_hdr_t *ihdr; + smb_dent_info_t *ient; + int dent_buf_size; + int i; + int total; + int maxentries; + int rc; + + ihdr = kmem_zalloc(sizeof (smb_dent_info_hdr_t), KM_SLEEP); + *count = 0; + + if (!wildcards) + maxentries = maxcount = 1; + else { + maxentries = (xa->rep_data_mb.max_bytes - + xa->rep_data_mb.chain_offset) / maxdata; + if (maxcount > SMB_MAX_DENTS_IOVEC) + maxcount = SMB_MAX_DENTS_IOVEC; + if (maxentries > maxcount) + maxentries = maxcount; + } + + /* Each entry will need to be aligned so add _POINTER_ALIGNMENT */ + dent_buf_size = + maxentries * (SMB_MAX_DENT_INFO_SIZE + _POINTER_ALIGNMENT); + ihdr->iov->iov_base = kmem_alloc(dent_buf_size, KM_SLEEP); + + ihdr->sattr = sattr; + ihdr->pattern = pattern; + ihdr->sr = sr; + + ihdr->uio.uio_iovcnt = maxcount; + ihdr->uio.uio_resid = dent_buf_size; + ihdr->uio.uio_iov = ihdr->iov; + ihdr->uio.uio_offset = 0; + + rc = smb_get_dents(sr, cookie, dir_snode, wildcards, ihdr, more); + if (rc != 0) { + goto out; + } + + if (ihdr->iov->iov_len == 0) + *count = 0; + else + *count = smb_trans2_find_process_ients(sr, xa, ihdr, fflag, + infolev, maxdata, dir_snode, more, cookie); + rc = 0; + +out: + + total = maxcount - ihdr->uio.uio_iovcnt; + ASSERT((total >= 0) && (total <= SMB_MAX_DENTS_IOVEC)); + for (i = 0; i < total; i++) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + ient = (smb_dent_info_t *)ihdr->iov[i].iov_base; + ASSERT(ient); + smb_node_release(ient->snode); + } + + kmem_free(ihdr->iov->iov_base, dent_buf_size); + kmem_free(ihdr, sizeof (smb_dent_info_hdr_t)); + return (0); +} + + + +/* + * smb_get_dents + * + * This function utilizes "smb_fsop_getdents()" to get dir entries. + * The "smb_gather_dents_info()" is the call back function called + * inside the file system. It is very important that the function + * does not sleep or yield since it is processed inside a file + * system transaction. + * + * The function returns 0 when successful and error code when failed. + * If more is provided, the return value of 1 is returned indicating + * more entries; otherwise, 0 is returned. + */ +int smb_get_dents( + smb_request_t *sr, + uint32_t *cookie, + smb_node_t *dir_snode, + unsigned int wildcards, + smb_dent_info_hdr_t *ihdr, + int *more) +{ + int rc; + char *namebuf; + smb_node_t *snode; + smb_attr_t file_attr; + uint32_t maxcnt = ihdr->uio.uio_iovcnt; + char shortname[MANGLE_NAMELEN], name83[MANGLE_NAMELEN]; + fsvol_attr_t vol_attr; + + namebuf = kmem_zalloc(MAXNAMELEN, KM_SLEEP); + if (more) + *more = 0; + + if ((rc = fsd_getattr(&sr->tid_tree->t_fsd, &vol_attr)) != 0) { + kmem_free(namebuf, MAXNAMELEN); + return (rc); + } + + if (!wildcards) { + /* Already found entry? */ + if (*cookie != 0) + return (0); + shortname[0] = '\0'; + + rc = smb_fsop_lookup(sr, sr->user_cr, 0, sr->tid_tree->t_snode, + dir_snode, ihdr->pattern, &snode, &file_attr, shortname, + name83); + + if (rc) { + kmem_free(namebuf, MAXNAMELEN); + return (rc); + } + + (void) strlcpy(namebuf, ihdr->pattern, MAXNAMELEN); + + /* + * It is not necessary to set the "force" flag (i.e. to + * take into account mangling for case-insensitive collisions) + */ + + if (shortname[0] == '\0') + (void) smb_mangle_name(snode->attr.sa_vattr.va_nodeid, + namebuf, shortname, name83, 0); + (void) smb_gather_dents_info((char *)ihdr, + snode->attr.sa_vattr.va_nodeid, + strlen(namebuf), namebuf, -1, (int *)&maxcnt, + &snode->attr, snode, shortname, name83); + kmem_free(namebuf, MAXNAMELEN); + return (0); + } + + if ((rc = smb_fsop_getdents(sr, sr->user_cr, dir_snode, cookie, + 0, (int *)&maxcnt, (char *)ihdr, ihdr->pattern)) != 0) { + if (rc == ENOENT) { + kmem_free(namebuf, MAXNAMELEN); + return (0); + } + kmem_free(namebuf, MAXNAMELEN); + return (rc); + } + + if (*cookie != 0x7FFFFFFF && more) + *more = 1; + + kmem_free(namebuf, MAXNAMELEN); + return (0); +} + + + + +/* + * smb_gather_dents_info + * + * The function will accept information of each directory entry and put + * the needed information in the buffer. It is passed as the call back + * function for smb_fsop_getdents() to gather trans2 find info. + * + * If the buffer space is not enough, -1 will be returned. Regardless + * of valid entry or not, 0 will be returned; however, only valid entry + * will be stored in the buffer. + */ +int /*ARGSUSED*/ +smb_gather_dents_info( + char *args, + ino_t fileid, + int namelen, + char *name, + uint32_t cookie, + int32_t *countp, + smb_attr_t *attr, + smb_node_t *snode, + char *shortname, + char *name83) +{ + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + smb_dent_info_hdr_t *ihdr = (smb_dent_info_hdr_t *)args; + smb_dent_info_t *ient; + unsigned char *v5_name = NULL; + unsigned char *np = (unsigned char *)name; + int reclen = sizeof (smb_dent_info_t) + namelen; + + v5_name = kmem_alloc(MAXNAMELEN-1, KM_SLEEP); + + if (!ihdr->uio.uio_iovcnt || ihdr->uio.uio_resid < reclen) { + kmem_free(v5_name, MAXNAMELEN-1); + smb_node_release(snode); + return (-1); + } + + if (!smb_sattr_check(attr, name, ihdr->sattr)) { + kmem_free(v5_name, MAXNAMELEN-1); + smb_node_release(snode); + return (0); + } + + /* + * If StorEdge is configured to support Catia Version 5 deployments, + * any directory entry whose name contains the special Unix character + * that is considered to be illegal in Windows environement will be + * translated based on the following + * Special Character Translation Table. + * + * --------------------------- + * Unix-char | Windows-char + * --------------------------- + * " | (0x00a8) Diaeresis + * * | (0x00a4) Currency Sign + * : | (0x00f7) Division Sign + * < | (0x00ab) Left-Pointing Double Angle Quotation Mark + * > | (0x00bb) Right-Pointing Double Angle Quotation Mark + * ? | (0x00bf) Inverted Question mark + * \ | (0x00ff) Latin Small Letter Y with Diaeresis + * | | (0x00a6) Broken Bar + */ + if (catia_callback) { + /* XXX 255 should be max name len or something */ + catia_callback(v5_name, (unsigned char *)name, 255); + np = v5_name; + reclen = sizeof (smb_dent_info_t) + strlen((char *)v5_name); + } + + ASSERT(snode); + ASSERT(snode->n_magic == SMB_NODE_MAGIC); + ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING); + + /* + * Each entry needs to be properly aligned or we may get an alignment + * fault on sparc. + */ + ihdr->uio.uio_offset = (off_t)PTRALIGN(ihdr->uio.uio_offset); + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + ient = (smb_dent_info_t *)&ihdr->iov->iov_base[ihdr->uio.uio_offset]; + + ient->cookie = cookie; + ient->attr = *attr; + ient->snode = snode; + + (void) strcpy(ient->name, (char *)np); + (void) strcpy(ient->shortname, shortname); + (void) strcpy(ient->name83, name83); + ihdr->uio.uio_iov->iov_base = (char *)ient; + ihdr->uio.uio_iov->iov_len = reclen; + + ihdr->uio.uio_iov++; + ihdr->uio.uio_iovcnt--; + ihdr->uio.uio_resid -= reclen; + ihdr->uio.uio_offset += reclen; + + kmem_free(v5_name, MAXNAMELEN-1); + return (0); +} + + + +/* + * smb_trans2_find_process_ients + * + * This function encodes the directory entry information store in + * the iov structure of the ihdr structure. + * + * The total entries encoded will be returned. If the entries encoded + * is less than the total entries in the iov, the more field will + * be updated to 1. Also, the next cookie wil be updated as well. + */ +int +smb_trans2_find_process_ients( + struct smb_request *sr, + struct smb_xa *xa, + smb_dent_info_hdr_t *ihdr, + unsigned short fflag, + unsigned short infolev, + int maxdata, + struct smb_node *dir_snode, + int *more, + uint32_t *cookie) +{ + int i, err = 0; + smb_dent_info_t *ient; + uint32_t mb_flags = (sr->smb_flg2 & SMB_FLAGS2_UNICODE) + ? SMB_MSGBUF_UNICODE : 0; + + for (i = 0; i < SMB_MAX_DENTS_IOVEC; i++) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + if ((ient = (smb_dent_info_t *)ihdr->iov[i].iov_base) == 0) + break; + + /* + * FYI: Some observed differences between our response and + * Windows response which hasn't caused problem yet! + * + * 1. The NextEntryOffset field for the last entry should be 0 + * This code always calculate the record length and put the + * result in this field. + * + * 2. The FileIndex field is always 0. This code put the cookie + * in this field. + */ + err = smb_trans2_find_mbc_encode(sr, xa, ient, maxdata, infolev, + fflag, mb_flags, dir_snode, NULL); + + if (err) + break; + } + + /* + * Not enough space to store all the entries returned; therefore, + * update the more to 1. + */ + if (more && err < 0) { + *more = 1; + + /* + * Assume the space will be at least enough for 1 entry. + */ + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + ient = (smb_dent_info_t *)ihdr->iov[i-1].iov_base; + *cookie = ient->cookie; + } + return (i); +} + + + +/* + * smb_trans2_find_mbc_encode + * + * This function encodes the mbc for one directory entry. + * + * The function returns -1 when the max data requested by client + * is reached. If the entry is valid and successful encoded, 0 + * will be returned; otherwise, 1 will be returned. + */ +int smb_trans2_find_mbc_encode( + struct smb_request *sr, + struct smb_xa *xa, + smb_dent_info_t *ient, + int maxdata, + unsigned short infolev, + unsigned short fflag, + unsigned int mb_flags, + struct smb_node *dir_snode, /*LINTED E_FUNC_ARG_UNUSED*/ + struct smb_node *sd_snode) +{ + int uni_namelen; + int sl, rl; + char buf83[26]; + smb_msgbuf_t mb; + uint32_t dattr = 0; + uint32_t size32 = 0; + uint64_t size64 = 0; + struct smb_node *lnk_snode; + smb_attr_t lnkattr; + int rc; + + uni_namelen = smb_ascii_or_unicode_strlen(sr, ient->name); + + if (uni_namelen == -1) + return (1); + + if (MBC_ROOM_FOR(&xa->rep_data_mb, (maxdata + uni_namelen)) == 0) + return (-1); + + if (ient->attr.sa_vattr.va_type == VLNK) { + + rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, + sr->tid_tree->t_snode, dir_snode, ient->name, &lnk_snode, + &lnkattr, 0, 0); + + /* + * IR 104598 + * + * We normally want to resolve the object to which a symlink + * refers so that CIFS clients can access sub-directories and + * find the correct association for files. This causes a + * problem, however, if a symlink in a sub-directory points + * to a parent directory (some UNIX GUI's create a symlink in + * $HOME/.desktop that points to the user's home directory). + * Some Windows applications (i.e. virus scanning) loop/hang + * trying to follow this recursive path and there is little + * we can do because the path is constructed on the client. + * So we've added a flag that allows an end-user to disable + * symlinks to directories. Symlinks to other object types + * should be unaffected. + */ + if (rc == 0) { + if (smb_info.si.skc_dirsymlink_enable || + (lnkattr.sa_vattr.va_type != VDIR)) { + smb_node_release(ient->snode); + ient->snode = lnk_snode; + ient->attr = lnkattr; + } else { + smb_node_release(lnk_snode); + } + } + } + + if (infolev != SMB_FIND_FILE_NAMES_INFO) { + size64 = smb_node_get_size(ient->snode, &ient->attr); + size32 = (size64 > 0xFFFFFFFF) ? 0xFFFFFFFF : (uint32_t)size64; + dattr = smb_mode_to_dos_attributes(&ient->attr); + } + + /* + * we don't send the '.stream' to client. User shouldn't + * see this directory. + */ + switch (infolev) { + case SMB_INFO_STANDARD: + if (fflag & SMB_FIND_RETURN_RESUME_KEYS) + (void) smb_encode_mbc(&xa->rep_data_mb, "l", + ient->cookie); + + (void) smb_encode_mbc(&xa->rep_data_mb, "%yyyllwbu", sr, + ient->attr.sa_crtime.tv_sec ? ient->attr.sa_crtime.tv_sec : + ient->attr.sa_vattr.va_mtime.tv_sec, + ient->attr.sa_vattr.va_atime.tv_sec, + ient->attr.sa_vattr.va_mtime.tv_sec, + size32, + size32, + dattr, + uni_namelen, + ient->name); + break; + + case SMB_INFO_QUERY_EA_SIZE: + if (fflag & SMB_FIND_RETURN_RESUME_KEYS) + (void) smb_encode_mbc(&xa->rep_data_mb, + "l", ient->cookie); + + (void) smb_encode_mbc(&xa->rep_data_mb, "%yyyllwlbu", sr, + ient->attr.sa_crtime.tv_sec ? ient->attr.sa_crtime.tv_sec : + ient->attr.sa_vattr.va_mtime.tv_sec, + ient->attr.sa_vattr.va_atime.tv_sec, + ient->attr.sa_vattr.va_mtime.tv_sec, + size32, + size32, + dattr, + 0L, /* EA Size */ + uni_namelen, + ient->name); + break; + + case SMB_FIND_FILE_DIRECTORY_INFO: + /* Use maxdata instead */ + rl = maxdata + uni_namelen; + + (void) smb_encode_mbc(&xa->rep_data_mb, "%llTTTTqqllu", sr, + rl, + ient->cookie, + ient->attr.sa_crtime.tv_sec ? &ient->attr.sa_crtime : + &ient->attr.sa_vattr.va_mtime, + &ient->attr.sa_vattr.va_atime, + &ient->attr.sa_vattr.va_mtime, + &ient->attr.sa_vattr.va_ctime, + size64, + size64, + dattr, + uni_namelen, + ient->name); + break; + + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + /* Use maxdata instead */ + rl = maxdata + uni_namelen; + bzero(buf83, sizeof (buf83)); + smb_msgbuf_init(&mb, (unsigned char *)buf83, sizeof (buf83), + mb_flags); + if (smb_msgbuf_encode(&mb, "u", ient->shortname) < 0) { + smb_msgbuf_term(&mb); + return (-1); + } + sl = smb_ascii_or_unicode_strlen(sr, ient->shortname); + + (void) smb_encode_mbc(&xa->rep_data_mb, + "%llTTTTqqlllb.24cu", sr, + rl, + ient->cookie, + ient->attr.sa_crtime.tv_sec ? &ient->attr.sa_crtime : + &ient->attr.sa_vattr.va_mtime, + &ient->attr.sa_vattr.va_atime, + &ient->attr.sa_vattr.va_mtime, + &ient->attr.sa_vattr.va_ctime, + size64, + size64, + dattr, + uni_namelen, + 0L, + sl, + buf83, + ient->name); + + smb_msgbuf_term(&mb); + break; + + case SMB_FIND_FILE_NAMES_INFO: + rl = maxdata + uni_namelen; + (void) smb_encode_mbc(&xa->rep_data_mb, "%lllu", sr, + rl, + ient->cookie, + uni_namelen, + ient->name); + break; + } + + return (0); +} + +/* + * Close a search started by a Trans2FindFirst2 request. + */ +int +smb_com_find_close2(struct smb_request *sr) +{ + if (smbsr_decode_vwv(sr, "w", &sr->smb_sid) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->sid_odir = smb_odir_lookup_by_sid(sr->tid_tree, sr->smb_sid); + if (sr->sid_odir == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + smb_rdir_close(sr); + + smbsr_encode_empty_result(sr); + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_open2.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_open2.c new file mode 100644 index 000000000000..64fc11c3fc8c --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_open2.c @@ -0,0 +1,137 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: trans2_open2 + * + * This transaction is used to open or create a file having extended + * attributes. + * + * Client Request Value + * ============================ ======================================= + * + * WordCount 15 + * TotalDataCount Total size of extended attribute list + * DataOffset Offset to extended attribute list in + * this request + * SetupCount 1 + * Setup[0] TRANS2_OPEN2 + * + * Parameter Block Encoding Description + * ============================ ======================================= + * + * USHORT Flags; Additional information: bit set- + * 0 - return additional info + * 1 - exclusive oplock requested + * 2 - batch oplock requested + * 3 - return total length of EAs + * USHORT DesiredAccess; Requested file access + * USHORT Reserved1; Ought to be zero. Ignored by the + * server. + * USHORT FileAttributes; Attributes for file if create + * SMB_TIME CreationTime; Creation time to apply to file if + * create + * SMB_DATE CreationDate; Creation date to apply to file if + * create + * USHORT OpenFunction; Open function + * ULONG AllocationSize; Bytes to reserve on create or truncate + * USHORT Reserved [5]; Must be zero + * STRING FileName; Name of file to open or create + * UCHAR Data[ TotalDataCount ] FEAList structure for file to be + * created + * + * If secondary requests are required, they must contain 0 parameter bytes, + * and the Fid in the secondary request is 0xFFFF. + * + * DesiredAccess is encoded as described in the "Access Mode Encoding" + * section elsewhere in this document. + * + * FileAttributes are encoded as described in the "File Attribute Encoding" + * section elsewhere in this document. + * + * OpenFunction specifies the action to be taken depending on whether or + * not the file exists (see section 3.7) . + * + * Action in the response specifies the action as a result of this request + * (see section 3.8). + * + * Response Parameter Block Description + * ========================== ========================================= + * + * USHORT Fid; File handle + * USHORT FileAttributes; Attributes of file + * SMB_TIME CreationTime; Last modification time + * SMB_DATE CreationDate; Last modification date + * ULONG DataSize; Current file size + * USHORT GrantedAccess; Access permissions actually allowed + * USHORT FileType; Type of file + * USHORT DeviceState; State of IPC device (e.g. pipe) + * USHORT Action; Action taken + * ULONG Reserved; + * USHORT EaErrorOffset; Offset into EA list if EA error + * ULONG EaLength; Total EA length for opened file + * + * FileType returns the kind of resource actually opened: + * + * Name Value Description + * ======================= ====== ===================================== + * + * FileTypeDisk 0 Disk file or directory as defined in + * the attribute field + * FileTypeByteModePipe 1 Named pipe in byte mode + * FileTypeMessageModePipe 2 Named pipe in message mode + * FileTypePrinter 3 Spooled printer + * FileTypeUnknown 0xFFFF Unrecognized resource type + * + * DeviceState is applicable only if the FileType is FileTypeByteModePipe + * or FileTypeMessageModePipe and is encoded as in section 3.9. + * + * If an error was detected in the incoming EA list, the offset of the + * error is returned in EaErrorOffset. + * + * If bit0 of Flags in the request is clear, the FileAttributes, + * CreationTime, CreationDate, DataSize, GrantedAccess, FileType, and + * DeviceState have indeterminate values in the response. Similarly, if + * + * bit3 of the request is clear, EaLength in the response has an + * indeterminate value in the response. + * + * This SMB can request an oplock on the opened file. Oplocks are fully + * described in the "Oplocks" section elsewhere in this document, and there + * is also discussion of oplocks in the SMB_COM_LOCKING_ANDX SMB + * description. Bit1 and bit2 of the Flags field are used to request + * oplocks during open. + */ + +#include + +int /*ARGSUSED*/ +smb_com_trans2_open2(struct smb_request *sr) +{ + /* TODO: smb_com_trans2_open2 */ + return (SDRC_UNIMPLEMENTED); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_query_file_info.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_query_file_info.c new file mode 100644 index 000000000000..2d488b7f2cb6 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_query_file_info.c @@ -0,0 +1,559 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: trans2_query_file_information + * + * This request is used to get information about a specific file or + * subdirectory given a handle to it. + * + * Client Request Value + * ========================== ========================================== + * + * WordCount 15 + * MaxSetupCount 0 + * SetupCount 1 + * Setup[0] TRANS2_QUERY_FILE_INFORMATION + * + * Parameter Block Encoding Description + * ========================== ========================================== + * + * USHORT Fid; Handle of file for request + * USHORT InformationLevel; Level of information requested + * + * The available information levels, as well as the format of the response + * are identical to TRANS2_QUERY_PATH_INFORMATION. + */ + +#include +#include +#include +#include + +uint32_t smb_pad_align(uint32_t offset, uint32_t align); + + +/* + * smb_com_trans2_query_file_information + * + * Observation of Windows 2000 indicates the following: + * + * 1) If a file is opened with delete-on-close create options, the + * delete-on-close status returned by the Trans2QueryFileInfo will not + * be set. The delete-on-close status will only be set when the above + * file handle is closed. + * + * 2) If a file is not opened with delete-on-close create options but the + * delete-on-close is set via Trans2SetFileInfo/DispositionInfo, the + * delete-on-close status returned by Trans2QueryFileInfo will be set + * immediately. + */ + +int +smb_com_trans2_query_file_information(struct smb_request *sr, struct smb_xa *xa) +{ + static smb_attr_t pipe_attr; + unsigned short infolev, dattr = 0; + off_t dsize = 0, dused = 0; + smb_attr_t *ap = NULL; + char *namep = NULL; + char *filename = NULL, *alt_nm_ptr = NULL; + int filename_len = 0; + struct smb_node *dir_snode = NULL; + timestruc_t *creation_time = NULL; + unsigned char delete_on_close = 0; + unsigned char is_dir = 0; + char *filebuf = NULL; + + /* + * buffer for mangled name and shortname are allocated + * much higher than required space. Optimization + * here should be performed along with mangled_name & shortname + * of query path information. + */ + char *mangled_name = 0; + + if (smb_decode_mbc(&xa->req_param_mb, "ww", &sr->smb_fid, + &infolev) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + switch (sr->fid_ofile->f_ftype) { + case SMB_FTYPE_DISK: + { + /* + * The node is only valid for SMB_FTYPE_DISK files. + */ + struct smb_node *node = sr->fid_ofile->f_node; + + /* + * For some reason NT will not show the security tab in the root + * directory of a mapped drive unless the filename length is + * greater than one. + * This may be a NT vs Windows9x UNICODE check. + * So we hack the length here to persuade NT to show the tab. It + * should be safe because of the null terminator character. + */ + /* be careful here we need od_name now rather than node_name */ + /* do we want to use node_name in the case of softlinks ?? */ + namep = node->od_name; + filename = namep; + filename_len = smb_ascii_or_unicode_strlen(sr, filename); + if (strcmp(namep, ".") == 0 && filename_len == 1) + filename_len = 2; + + creation_time = smb_node_get_crtime(node); + dattr = smb_node_get_dosattr(node); + + ap = &node->attr; + if (ap->sa_vattr.va_type == VDIR) { + is_dir = 1; + dsize = dused = 0; + } else { + is_dir = 0; + dsize = ap->sa_vattr.va_size; + dused = ap->sa_vattr.va_blksize * + ap->sa_vattr.va_nblocks; + } + + dir_snode = node->dir_snode; + delete_on_close = + (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) != 0; + } + break; + + case SMB_FTYPE_MESG_PIPE: + { + /* + * The pipe is only valid for SMB_FTYPE_MESG_PIPE files. + */ + mlsvc_pipe_t *pipe_info = sr->fid_ofile->f_pipe_info; + namep = pipe_info->pipe_name; + + filename = namep; + filename_len = smb_ascii_or_unicode_strlen(sr, filename); + + ap = &pipe_attr; + creation_time = (timestruc_t *)&ap->sa_vattr.va_ctime; + dattr = SMB_FA_NORMAL; + dsize = dused = 0; + + delete_on_close = 0; + is_dir = 0; + } + break; + + default: + smbsr_raise_error(sr, ERRDOS, ERRbadfile); + /* NOTREACHED */ + break; + } + + filebuf = kmem_alloc(MAXNAMELEN+1, KM_SLEEP); + mangled_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + if (infolev > SMB_INFO_PASSTHROUGH) + infolev -= SMB_INFO_PASSTHROUGH; + + switch (infolev) { + case FileAccessInformation: + (void) smb_encode_mbc(&xa->rep_data_mb, "l", + sr->fid_ofile->f_granted_access); + break; + + case SMB_INFO_STANDARD: + if (dsize > 0xffffffff) + dsize = 0xffffffff; + if (dused > 0xffffffff) + dused = 0xffffffff; + + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, + ((sr->session->native_os == NATIVE_OS_WIN95) + ? "YYYllw" : "yyyllw"), + smb_gmt_to_local_time(creation_time->tv_sec), + smb_gmt_to_local_time(ap->sa_vattr.va_atime.tv_sec), + smb_gmt_to_local_time(ap->sa_vattr.va_mtime.tv_sec), + (uint32_t)dsize, + (uint32_t)dused, + dattr); + break; + + case SMB_INFO_QUERY_EA_SIZE: + if (dsize > 0xffffffff) + dsize = 0xffffffff; + if (dused > 0xffffffff) + dused = 0xffffffff; + + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, + ((sr->session->native_os == NATIVE_OS_WIN95) + ? "YYYllwl" : "yyyllwl"), + smb_gmt_to_local_time(creation_time->tv_sec), + smb_gmt_to_local_time(ap->sa_vattr.va_atime.tv_sec), + smb_gmt_to_local_time(ap->sa_vattr.va_mtime.tv_sec), + (uint32_t)dsize, + (uint32_t)dused, + dattr, 0); + break; + + case SMB_INFO_QUERY_EAS_FROM_LIST: + case SMB_INFO_QUERY_ALL_EAS: + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "l", 0); + break; + + case SMB_INFO_IS_NAME_VALID: + break; + + case SMB_QUERY_FILE_BASIC_INFO: + /* + * NT includes 6 undocumented bytes at the end of this + * response, which are required by NetBench 5.01. + * Similar change in smb_trans2_query_path_information.c. + */ + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "TTTTw6.", + creation_time, + &ap->sa_vattr.va_atime, + &ap->sa_vattr.va_mtime, + &ap->sa_vattr.va_ctime, + dattr); + break; + + case SMB_QUERY_FILE_STANDARD_INFO: + (void) smb_encode_mbc(&xa->rep_param_mb, "w", + SMB_QUERY_FILE_STANDARD_INFO); + /* + * Add 2 bytes to pad data to long. It is + * necessary because Win2k expects the padded bytes. + */ + (void) smb_encode_mbc(&xa->rep_data_mb, "qqlbb2.", + dused, + dsize, + ap->sa_vattr.va_nlink, + delete_on_close, + is_dir); + break; + + case SMB_QUERY_FILE_EA_INFO: + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "l", 0); + break; + + case SMB_QUERY_FILE_NAME_INFO: + /* + * It looks like NT doesn't know what to do with the name "." + * so we convert it to "\\" to indicate the root directory. + * + * If the leading \ is missing, add it. + */ + if (strcmp(namep, ".") == 0) { + filename = "\\"; + filename_len = 2; + } else if (*namep != '\\') { + filename = filebuf; + (void) snprintf(filename, MAXNAMELEN + 1, "\\%s", + namep); + filename_len = + smb_ascii_or_unicode_strlen(sr, filename); + } else { + filename = namep; + filename_len = + smb_ascii_or_unicode_strlen(sr, filename); + } + + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "%lu", sr, + filename_len, filename); + break; + + case SMB_QUERY_FILE_ALL_INFO: + /* + * The reply of this information level on the + * wire doesn't match with protocol specification. + * This is what spec. needs: "TTTTwqqlbbqllqqll" + * But this is actually is sent on the wire: + * "TTTTw6.qqlbb2.l" + * So, there is a 6-byte pad between Attributes and + * AllocationSize. Also there is a 2-byte pad After + * Directory field. Between Directory and FileNameLength + * there is just 4 bytes that it seems is AlignmentRequirement. + * There are 6 other fields between Directory and + * AlignmentRequirement in spec. that aren't sent + * on the wire. + */ + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "TTTTw6.qqlbb2.l", + creation_time, + &ap->sa_vattr.va_atime, + &ap->sa_vattr.va_mtime, + &ap->sa_vattr.va_ctime, + dattr, + (int64_t)dused, + (int64_t)dsize, + ap->sa_vattr.va_nlink, + delete_on_close, + is_dir, + 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "%lu", + sr, filename_len, filename); + break; + + case SMB_QUERY_FILE_ALT_NAME_INFO: + /* + * Conform to the rule used by Windows NT/2003 servers. + * Shortname is created only if either the + * filename or extension portion of a file is made up of + * mixed case. This is handled in os/libnt/nt_mangle_name.c. + * + * If the shortname is generated, it will be returned as + * the alternative name. Otherwise, converts the original + * name to all upper-case and returns it as the alternative + * name. This is how Windows NT/2003 servers behave. However, + * Windows 2000 seems to preserve the case of the original + * name, and returns it as the alternative name. + */ + alt_nm_ptr = (*mangled_name == 0) ? + utf8_strupr(filename) : mangled_name; + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "%lu", sr, + smb_ascii_or_unicode_strlen(sr, alt_nm_ptr), alt_nm_ptr); + break; + + case SMB_QUERY_FILE_STREAM_INFO: + { + struct smb_node *node = sr->fid_ofile->f_node; + if (dir_snode == NULL) { + kmem_free(filebuf, MAXNAMELEN+1); + kmem_free(mangled_name, MAXNAMELEN); + smbsr_raise_error(sr, ERRDOS, ERRbadfile); + /* NOT REACHED */ + } + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + if (SMB_IS_STREAM(node)) { + ASSERT(node->unnamed_stream_node); + ASSERT(node->unnamed_stream_node->n_magic == + SMB_NODE_MAGIC); + ASSERT(node->unnamed_stream_node->n_state != + SMB_NODE_STATE_DESTROYING); + + (void) smb_encode_stream_info(sr, xa, + node->unnamed_stream_node, ap); + } else { + (void) smb_encode_stream_info(sr, xa, node, ap); + } + break; + } + case SMB_QUERY_FILE_COMPRESSION_INFO: + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "qwbbb3.", + dsize, 0, 0, 0, 0); + break; + + default: + kmem_free(filebuf, MAXNAMELEN+1); + kmem_free(mangled_name, MAXNAMELEN); + smbsr_raise_error(sr, ERRDOS, ERRunknownlevel); + /* NOTREACHED */ + break; + } + + kmem_free(filebuf, MAXNAMELEN+1); + kmem_free(mangled_name, MAXNAMELEN); + return (SDRC_NORMAL_REPLY); +} + +/* + * smb_encode_stream_info + * + * This function encodes the streams information for both T2QueryFileInfo + * and T2QueryPathInfo. The rules about how to do this are not documented. + * They have been derived using observed NT behaviour and the IR's listed + * below. + * + * IR101680: ArcServe2000 problem. ArcServe doesn't like the null- + * stream data on directories that don't have any associated streams. + * + * IR103484 and KB article Q234765: Citrix problem. If there are no + * streams, only return the unnamed stream data if the target is a + * file. The Citrix Metaframe cdm.sys driver crashes the Windows server, + * on which it's running, if it receives the unexpected stream data + * for a directory. + * + * If there are streams, on files or directories, we need to return + * them to support Mac/DAVE clients. Mac clients make this request + * to see if there is a comment stream. If we don't provide the + * information, the client won't try to access the comment stream. + * + * If the target is a file: + * 1. If there are no named streams, the response should still contain + * an entry for the unnamed stream. + * 2. If there are named streams, the response should contain an entry + * for the unnamed stream followed by the entries for the named + * streams. + * + * If the target is a directory: + * 1. If there are no streams, the response is complete. Directories + * do not report the unnamed stream. + * 2. If there are streams, the response should contain entries for + * those streams but there should not be an entry for the unnamed + * stream. + * + * Note that the stream name lengths exclude the null terminator but + * the field lengths (i.e. next offset calculations) need to include + * the null terminator and be padded to a multiple of 8 bytes. The + * last entry does not seem to need any padding. + */ + +void +smb_encode_stream_info( + struct smb_request *sr, + struct smb_xa *xa, + struct smb_node *snode, + smb_attr_t *attr) +{ + char *stream_name; + uint32_t next_offset; + uint32_t stream_nlen; + uint32_t pad; + off_t dsize; + int is_dir; + uint32_t cookie = 0; + struct fs_stream_info *stream_info; + struct fs_stream_info *stream_info_next; + int rc = 0; + int done = 0; + char *fname; + + stream_info = kmem_alloc(sizeof (struct fs_stream_info), KM_SLEEP); + stream_info_next = kmem_alloc(sizeof (struct fs_stream_info), KM_SLEEP); + is_dir = (attr->sa_vattr.va_type == VDIR) ? 1 : 0; + dsize = attr->sa_vattr.va_size; + fname = MEM_MALLOC("smb", MAXPATHLEN); + + rc = smb_fsop_stream_readdir(sr, kcred, snode, &cookie, stream_info, + NULL, NULL); + + if ((cookie == 0x7FFFFFFF) || (rc == EACCES) || (rc == ENOENT)) { + if (is_dir == 0) { + stream_name = "::$DATA"; + stream_nlen = + smb_ascii_or_unicode_strlen(sr, stream_name); + next_offset = 0; + + (void) smb_encode_mbc(&xa->rep_data_mb, "%llqqu", + sr, next_offset, stream_nlen, dsize, dsize, + stream_name); + } + /* No named streams, we're done */ + kmem_free(stream_info, sizeof (struct fs_stream_info)); + kmem_free(stream_info_next, sizeof (struct fs_stream_info)); + MEM_FREE("smb", fname); + return; + } + + if (is_dir == 0) { + stream_name = "::$DATA"; + stream_nlen = smb_ascii_or_unicode_strlen(sr, stream_name); + + /* + * Offset calculation: + * 2 dwords + 2 quadwords => 4 + 4 + 8 + 8 => 24 + */ + next_offset = 24 + stream_nlen + + smb_ascii_or_unicode_null_len(sr); + + (void) smb_encode_mbc(&xa->rep_data_mb, "%llqqu", sr, + next_offset, stream_nlen, dsize, dsize, stream_name); + } + + while (!done) { + /* + * Named streams. + */ + stream_nlen = smb_ascii_or_unicode_strlen(sr, + stream_info->name); + next_offset = 0; + pad = 0; + + /* + * this is a little kludgy, since we use a cookie now and last + * packet does not have a pad we need to check the next item + * before we encode the current one + */ + stream_info_next->name[0] = 0; + rc = smb_fsop_stream_readdir(sr, kcred, snode, &cookie, + stream_info_next, NULL, NULL); + if (cookie == 0x7FFFFFFF) { + done = 1; + } else { + if (cookie == 0) { + break; + } + next_offset = 24 + stream_nlen + + smb_ascii_or_unicode_null_len(sr); + pad = smb_pad_align(next_offset, 8); + next_offset += pad; + } + (void) smb_encode_mbc(&xa->rep_data_mb, "%llqqu#.", + sr, next_offset, stream_nlen, + stream_info->size, stream_info->size, + stream_info->name, pad); + + (void) memcpy(stream_info, stream_info_next, + sizeof (struct fs_stream_info)); + } + kmem_free(stream_info, sizeof (struct fs_stream_info)); + kmem_free(stream_info_next, sizeof (struct fs_stream_info)); + MEM_FREE("smb", fname); +} + +/* + * smb_pad_align + * + * Returns the number of bytes required to get pad an offset to the + * specified alignment. + */ +uint32_t +smb_pad_align(uint32_t offset, uint32_t align) +{ + uint32_t pad = offset % align; + + if (pad != 0) + pad = align - pad; + + return (pad); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_query_fs_information.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_query_fs_information.c new file mode 100644 index 000000000000..aa7cc15343c6 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_query_fs_information.c @@ -0,0 +1,434 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: trans2_query_fs_information + * + * This transaction requests information about a filesystem on the server. + * + * Client Request Value + * ================================== ================================= + * + * WordCount; 15 + * TotalParameterCount; 2 or 4 + * MaxSetupCount; 0 + * SetupCount; 1 or 2 + * Setup[0]; TRANS2_QUERY_FS_INFORMATION + * + * Parameter Block Encoding Description + * ================================== ================================= + * + * USHORT Information Level; Level of information requested + * + * The filesystem is identified by Tid in the SMB header. + * + * MaxDataCount in the transaction request must be large enough to + * accommodate the response. + * + * The encoding of the response parameter block depends on the + * InformationLevel requested. Information levels whose values are greater + * than 0x102 are mapped to corresponding calls to + * NtQueryVolumeInformationFile calls by the server. The two levels below + * 0x102 are described below. The requested information is placed in the + * Data portion of the transaction response. + * + * InformationLevel Value + * + * ============================= ====== + * + * SMB_INFO_ALLOCATION 1 + * SMB_INFO_VOLUME 2 + * SMB_QUERY_FS_VOLUME_INFO 0x102 + * SMB_QUERY_FS_SIZE_INFO 0x103 + * SMB_QUERY_FS_DEVICE_INFO 0x104 + * SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 + * + * The following sections describe the InformationLevel dependent encoding + * of the data part of the transaction response. + * + * 4.1.6.1 SMB_INFO_ALLOCATION + * + * Data Block Encoding Description + * =================== ================================================ + * + * ULONG idFileSystem; File system identifier. NT server always + * returns 0 + * ULONG cSectorUnit; Number of sectors per allocation unit + * ULONG cUnit; Total number of allocation units + * ULONG cUnitAvail; Total number of available allocation units + * USHORT cbSector; Number of bytes per sector + * + * 4.1.6.2 SMB_INFO_VOLUME + * + * Data Block Encoding Description + * =================== ================================================ + * + * ULONG ulVsn; Volume serial number + * UCHAR cch; Number of characters in Label + * STRING Label; The volume label + * + * 4.1.6.3 SMB_QUERY_FS_VOLUME_INFO + * + * Data Block Encoding Description + * =================== ================================================ + * + * LARGE_INTEGER Volume Creation Time + * ULONG Volume Serial Number + * ULONG Length of Volume Label in bytes + * + * BYTE Reserved + * + * BYTE Reserved + * + * STRING Label; The volume label + * + * 4.1.6.4 SMB_QUERY_FS_SIZE_INFO + * + * Data Block Encoding Description + * =================== ================================================ + * + * LARGE_INTEGER Total Number of Allocation units on the Volume + * LARGE_INTEGER Number of free Allocation units on the Volume + * ULONG Number of sectors in each Allocation unit + * + * ULONG Number of bytes in each sector + * + * 4.1.6.5 SMB_QUERY_FS_DEVICE_INFO + * + * Data Block Encoding Value + * ==================== =============================================== + * + * ULONG DeviceType; Values as specified below + * ULONG Characteristics of the device; Values as + * specified below + * + * For DeviceType, note that the values 0-32767 are reserved for the + * exclusive use of Microsoft Corporation. The following device types are + * currently defined: + * + * FILE_DEVICE_BEEP 0x00000001 + * + * FILE_DEVICE_CD_ROM 0x00000002 + * FILE_DEVICE_CD_ROM_FILE_SYST 0x00000003 + * EM + * FILE_DEVICE_CONTROLLER 0x00000004 + * FILE_DEVICE_DATALINK 0x00000005 + * FILE_DEVICE_DFS 0x00000006 + * FILE_DEVICE_DISK 0x00000007 + * FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 + * FILE_DEVICE_FILE_SYSTEM 0x00000009 + * FILE_DEVICE_INPORT_PORT 0x0000000a + * FILE_DEVICE_KEYBOARD 0x0000000b + * FILE_DEVICE_MAILSLOT 0x0000000c + * FILE_DEVICE_MIDI_IN 0x0000000d + * FILE_DEVICE_MIDI_OUT 0x0000000e + * FILE_DEVICE_MOUSE 0x0000000f + * FILE_DEVICE_MULTI_UNC_PROVID 0x00000010 + * ER + * FILE_DEVICE_NAMED_PIPE 0x00000011 + * FILE_DEVICE_NETWORK 0x00000012 + * FILE_DEVICE_NETWORK_BROWSER 0x00000013 + * FILE_DEVICE_NETWORK_FILE_SYS 0x00000014 + * TEM + * FILE_DEVICE_NULL 0x00000015 + * FILE_DEVICE_PARALLEL_PORT 0x00000016 + * FILE_DEVICE_PHYSICAL_NETCARD 0x00000017 + * FILE_DEVICE_PRINTER 0x00000018 + * FILE_DEVICE_SCANNER 0x00000019 + * FILE_DEVICE_SERIAL_MOUSE_POR 0x0000001a + * T + * FILE_DEVICE_SERIAL_PORT 0x0000001b + * FILE_DEVICE_SCREEN 0x0000001c + * FILE_DEVICE_SOUND 0x0000001d + * FILE_DEVICE_STREAMS 0x0000001e + * FILE_DEVICE_TAPE 0x0000001f + * FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 + * FILE_DEVICE_TRANSPORT 0x00000021 + * FILE_DEVICE_UNKNOWN 0x00000022 + * FILE_DEVICE_VIDEO 0x00000023 + * FILE_DEVICE_VIRTUAL_DISK 0x00000024 + * FILE_DEVICE_WAVE_IN 0x00000025 + * FILE_DEVICE_WAVE_OUT 0x00000026 + * FILE_DEVICE_8042_PORT 0x00000027 + * FILE_DEVICE_NETWORK_REDIRECT 0x00000028 + * OR + * FILE_DEVICE_BATTERY 0x00000029 + * FILE_DEVICE_BUS_EXTENDER 0x0000002a + * FILE_DEVICE_MODEM 0x0000002b + * FILE_DEVICE_VDM 0x0000002c + * + * Some of these device types are not currently accessible over the network + * and may never be accessible over the network. Some may change to be + * + * accessible over the network. The values for device types that may never + * be accessible over the network may be redefined to be just reserved at + * some date in the future. + * + * Characteristics is the sum of any of the following: + * + * FILE_REMOVABLE_MEDIA 0x00000001 + * FILE_READ_ONLY_DEVICE 0x00000002 + * FILE_FLOPPY_DISKETTE 0x00000004 + * FILE_WRITE_ONE_MEDIA 0x00000008 + * FILE_REMOTE_DEVICE 0x00000010 + * FILE_DEVICE_IS_MOUNTED 0x00000020 + * FILE_VIRTUAL_VOLUME 0x00000040 + * + * 4.1.6.6 SMB_QUERY_FS_ATTRIBUTE_INFO + * + * Data Block Encoding Description + * =================== ================================================ + * + * ULONG File System Attributes; possible values + * described below + * LONG Maximum length of each file name component in + * number of bytes + * ULONG Length, in bytes, of the name of the file system + * + * STRING Name of the file system + * + * Where FileSystemAttributes is the sum of any of the following: + * + * FILE_CASE_SENSITIVE_SEARCH 0x00000001 + * FILE_CASE_PRESERVED_NAMES 0x00000002 + * FILE_PRSISTENT_ACLS 0x00000004 + * FILE_FILE_COMPRESSION 0x00000008 + * FILE_VOLUME_QUOTAS 0x00000010 + * FILE_DEVICE_IS_MOUNTED 0x00000020 + * FILE_VOLUME_IS_COMPRESSED 0x00008000 + * + * 4.1.6.7 Errors + * + * ERRSRV/invnid - TID was invalid + * ERRSRV/baduid - UID was invalid + * ERRHRD/ERRnotready - the file system has been removed + * ERRHRD/ERRdata - disk I/O error + * ERRSRV/ERRaccess - user does not have the right to perform this + * operation + * ERRSRV/ERRinvdevice - resource identified by TID is not a file system + */ + +#include +#include +#include + +char ntfs[] = "NTFS"; + + +/* + * is_dot_or_dotdot + * + * Inline function to detect the "." and ".." entries in a directory. + * Returns 1 is the name is "." or "..". Otherwise returns 0. + */ +int +is_dot_or_dotdot(char *name) +{ + if (*name != '.') + return (0); + + if ((name[1] == 0) || (name[1] == '.' && name[2] == 0)) + return (1); + + return (0); +} + + +/* + * smb_com_trans2_query_fs_information + */ +int +smb_com_trans2_query_fs_information(struct smb_request *sr, struct smb_xa *xa) +{ + int rc; + uint32_t flags; + char *encode_str; + uint64_t max_int; + unsigned short infolev; + struct statvfs64 df; + int sect_per_unit, length; + uint32_t total_units, avail_units; + struct smb_node *snode; + char *fsname = "NTFS"; + fsvol_attr_t vol_attr; + + if (smb_decode_mbc(&xa->req_param_mb, "w", &infolev) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + snode = sr->tid_tree->t_snode; + if (fsd_getattr(&sr->tid_tree->t_fsd, &vol_attr) != 0) { + smbsr_raise_errno(sr, ESTALE); + /* NOTREACHED */ + } + + switch (infolev) { + case SMB_INFO_ALLOCATION: + if ((rc = smb_fsop_statfs(sr->user_cr, snode, &df)) != 0) { + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + max_int = 0xffffffffLL; + + if (df.f_blocks > max_int) + df.f_blocks = max_int; + + if (df.f_bavail > max_int) + df.f_bavail = max_int; + + total_units = (uint32_t)df.f_blocks; + avail_units = (uint32_t)df.f_bavail; + length = 512; + sect_per_unit = df.f_frsize >> 9; + + if (avail_units > total_units) + avail_units = 0; + + (void) smb_encode_mbc(&xa->rep_data_mb, "llllw", + 0, /* file system ID. NT rets 0 */ + sect_per_unit, /* sectors/unit */ + total_units, /* total units */ + avail_units, /* avail units */ + length); /* bytes/sector */ + break; + + case SMB_INFO_VOLUME: + length = strlen(vol_attr.name); + encode_str = "%lbs"; + /* + * tree_fsd.val[0] is the 32-bit dev for the file system + * of the share's root smb_node. + * + * Together with tree_fsd.val[1] (the file system type), it + * comprises a system-wide unique file system ID. + */ + + (void) smb_encode_mbc(&xa->rep_data_mb, encode_str, sr, + snode->tree_fsd.val[0], length, vol_attr.name); + break; + + case SMB_QUERY_FS_VOLUME_INFO: + if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) || + (sr->session->native_os == NATIVE_OS_WIN95)) { + length = mts_wcequiv_strlen(vol_attr.name); + encode_str = "%qllb.U"; + } else { + length = strlen(vol_attr.name); /* label length */ + encode_str = "%qllb.s"; + } + + /* + * NT has the "supports objects" flag set to 1. + */ + + /* + * tree_fsd.val[0] is the 32-bit dev for the file system + * of the share's root smb_node. + * + * Together with tree_fsd.val[1] (the file system type), it + * comprises a system-wide unique file system ID. + */ + + (void) smb_encode_mbc(&xa->rep_data_mb, encode_str, sr, + 0ll, /* Volume creation time */ + snode->tree_fsd.val[0], /* Volume serial number */ + length, /* label length */ + 0, /* Supports objects */ + vol_attr.name); + break; + + case SMB_QUERY_FS_SIZE_INFO: + if ((rc = smb_fsop_statfs(sr->user_cr, snode, &df)) != 0) { + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + length = 512; + sect_per_unit = df.f_frsize >> 9; + + if (df.f_bavail > df.f_blocks) + df.f_bavail = 0; + + (void) smb_encode_mbc(&xa->rep_data_mb, "qqll", + df.f_blocks, /* total units */ + df.f_bavail, /* avail units */ + sect_per_unit, /* sectors/unit */ + length); /* bytes/sector */ + break; + case SMB_QUERY_FS_DEVICE_INFO: + (void) smb_encode_mbc(&xa->rep_data_mb, "ll", + FILE_DEVICE_FILE_SYSTEM, + FILE_DEVICE_IS_MOUNTED); + break; + + case SMB_QUERY_FS_ATTRIBUTE_INFO: + + if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) || + (sr->session->native_os == NATIVE_OS_WINNT) || + (sr->session->native_os == NATIVE_OS_WIN2000) || + (sr->session->native_os == NATIVE_OS_WIN95) || + (sr->session->native_os == NATIVE_OS_MACOS)) { + length = mts_wcequiv_strlen(fsname); + encode_str = "%lllU"; + sr->smb_flg2 |= SMB_FLAGS2_UNICODE; + } else { + length = strlen(fsname); + encode_str = "%llls"; + } + + flags = FILE_CASE_PRESERVED_NAMES; + /* flags |= FILE_UNICODE_ON_DISK; */ + + if (vol_attr.flags & FSOLF_SUPPORTS_ACLS) + flags |= FILE_PERSISTENT_ACLS; + + if ((vol_attr.flags & FSOLF_CASE_INSENSITIVE) == 0) + flags |= FILE_CASE_SENSITIVE_SEARCH; + + if (vol_attr.flags & FSOLF_STREAMS) + flags |= FILE_NAMED_STREAMS; + + if (smb_info.si.skc_announce_quota) + flags |= FILE_VOLUME_QUOTAS; + + (void) smb_encode_mbc(&xa->rep_data_mb, encode_str, sr, + flags, + MAXNAMELEN, /* max name */ + length, /* label length */ + fsname); + break; + + default: + smbsr_raise_error(sr, ERRDOS, ERRunknownlevel); + /* NOTREACHED */ + break; + } + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_query_path_info.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_query_path_info.c new file mode 100644 index 000000000000..1a80d6a2c420 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_query_path_info.c @@ -0,0 +1,588 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: trans2_query_path_information + * + * This request is used to get information about a specific file or + * subdirectory. + * + * Client Request Value + * ========================== ========================================= + * + * WordCount 15 + * MaxSetupCount 0 + * SetupCount 1 + * Setup[0] TRANS2_QUERY_PATH_INFORMATION + * + * Parameter Block Encoding Description + * ========================== ========================================= + * + * USHORT InformationLevel; Level of information requested + * ULONG Reserved; Must be zero + * STRING FileName; File or directory name + * + * The following InformationLevels may be requested: + * + * Information Level Value + * + * ================================ ===== + * + * SMB_INFO_STANDARD 1 + * SMB_INFO_QUERY_EA_SIZE 2 + * SMB_INFO_QUERY_EAS_FROM_LIST 3 + * SMB_INFO_QUERY_ALL_EAS 4 + * SMB_INFO_IS_NAME_VALID 6 + * SMB_QUERY_FILE_BASIC_INFO 0x101 + * SMB_QUERY_FILE_STANDARD_INFO 0x102 + * SMB_QUERY_FILE_EA_INFO 0x103 + * SMB_QUERY_FILE_NAME_INFO 0x104 + * SMB_QUERY_FILE_ALL_INFO 0x107 + * SMB_QUERY_FILE_ALT_NAME_INFO 0x108 + * SMB_QUERY_FILE_STREAM_INFO 0x109 + * SMB_QUERY_FILE_COMPRESSION_INFO 0x10B + * + * The requested information is placed in the Data portion of the + * transaction response. For the information levels greater than 0x100, + * the transaction response has 1 parameter word which should be ignored by + * the client. + * + * The following sections describe the InformationLevel dependent encoding + * of the data part of the transaction response. + * + * 4.2.14.1 SMB_INFO_STANDARD & SMB_INFO_QUERY_EA_SIZE + * + * Data Block Encoding Description + * =============================== ==================================== + * + * SMB_DATE CreationDate; Date when file was created + * SMB_TIME CreationTime; Time when file was created + * SMB_DATE LastAccessDate; Date of last file access + * SMB_TIME LastAccessTime; Time of last file access + * SMB_DATE LastWriteDate; Date of last write to the file + * SMB_TIME LastWriteTime; Time of last write to the file + * ULONG DataSize; File Size + * ULONG AllocationSize; Size of filesystem allocation unit + * USHORT Attributes; File Attributes + * ULONG EaSize; Size of file's EA information + * (SMB_INFO_QUERY_EA_SIZE) + * + * 4.2.14.2 SMB_INFO_QUERY_EAS_FROM_LIST & SMB_INFO_QUERY_ALL_EAS + * + * Response Field Value + * ==================== =============================================== + * + * MaxDataCount Length of EAlist found (minimum value is 4) + * + * Parameter Block Description + * Encoding =============================================== + * ==================== + * + * USHORT EaErrorOffset Offset into EAList of EA error + * + * Data Block Encoding Description + * ==================== =============================================== + * + * ULONG ListLength; Length of the remaining data + * UCHAR EaList[] The extended attributes list + * + * 4.2.14.3 SMB_INFO_IS_NAME_VALID + * + * This requests checks to see if the name of the file contained in the + * request's Data field has a valid path syntax. No parameters or data are + * returned on this information request. An error is returned if the syntax + * of the name is incorrect. Success indicates the server accepts the path + * syntax, but it does not ensure the file or directory actually exists. + * + * 4.2.14.4 SMB_QUERY_FILE_BASIC_INFO + * + * Data Block Encoding Description + * =============================== ==================================== + * + * LARGE_INTEGER CreationTime; Time when file was created + * LARGE_INTEGER LastAccessTime; Time of last file access + * LARGE_INTEGER LastWriteTime; Time of last write to the file + * LARGE_INTEGER ChangeTime Time when file was last changed + * USHORT Attributes; File Attributes + * + * 4.2.14.5 SMB_QUERY_FILE_STANDARD_INFO + * + * Data Block Encoding Description + * =============================== ==================================== + * + * LARGE_INTEGER AllocationSize Allocated size of the file in number + * of bytes + * LARGE_INTEGER EndofFile; Offset to the first free byte in the + * file + * ULONG NumberOfLinks Number of hard links to the file + * BOOLEAN DeletePending Indicates whether the file is marked + * for deletion + * BOOLEAN Directory Indicates whether the file is a + * directory + * + * 4.2.14.6 SMB_QUERY_FILE_EA_INFO + * + * Data Block Encoding Description + * =============================== ==================================== + * + * ULONG EASize Size of the file's extended + * attributes in number of bytes + * + * 4.2.14.7 SMB_QUERY_FILE_NAME_INFO + * + * Data Block Encoding Description + * =============================== ==================================== + * + * ULONG FileNameLength Length of the file name in number of + * bytes + * STRING FileName Name of the file + * + * 4.2.14.8 SMB_QUERY_FILE_ALL_INFO + * + * Data Block Encoding Description + * =============================== ==================================== + * + * LARGE_INTEGER CreationTime; Time when file was created + * LARGE_INTEGER LastAccessTime; Time of last file access + * LARGE_INTEGER LastWriteTime; Time of last write to the file + * LARGE_INTEGER ChangeTime Time when file was last changed + * USHORT Attributes; File Attributes + * LARGE_INTEGER AllocationSize Allocated size of the file in number + * of bytes + * LARGE_INTEGER EndofFile; Offset to the first free byte in the + * file + * ULONG NumberOfLinks Number of hard links to the file + * BOOLEAN DeletePending Indicates whether the file is marked + * for deletion + * BOOLEAN Directory Indicates whether the file is a + * directory + * LARGE_INTEGER Index Number A file system unique identifier + * ULONG EASize Size of the file's extended + * attributes in number of bytes + * ULONG AccessFlags Access that a caller has to the + * file; Possible values and meanings + * are specified below + * LARGE_INTEGER Index Number A file system unique identifier + * LARGE_INTEGER CurrentByteOffset Current byte offset within the file + * ULONG Mode Current Open mode of the file handle + * to the file; possible values and + * meanings are detailed below + * ULONG AlignmentRequirement Buffer Alignment required by device; + * possible values detailed below + * ULONG FileNameLength Length of the file name in number of + * bytes + * STRING FileName Name of the file + * + * The AccessFlags specifies the access permissions a caller has to the + * file and can have any suitable combination of the following values: + * + * Value Meaning + * + * ILE_READ_DATA 0x00000001 Data can be read from the file + * ILE_WRITE_DATA 0x00000002 Data can be written to the file + * ILE_APPEND_DATA 0x00000004 Data can be appended to the file + * ILE_READ_EA 0x00000008 Extended attributes associated + * with the file can be read + * ILE_WRITE_EA 0x00000010 Extended attributes associated + * with the file can be written + * ILE_EXECUTE 0x00000020 Data can be read into memory from + * the file using system paging I/O + * ILE_READ_ATTRIBUTES 0x00000080 Attributes associated with the + * file can be read + * ILE_WRITE_ATTRIBUTES 0x00000100 Attributes associated with the + * file can be written + * ELETE 0x00010000 The file can be deleted + * EAD_CONTROL 0x00020000 The access control list and + * ownership associated with the + * file can be read + * RITE_DAC 0x00040000 The access control list and + * ownership associated with the + * file can be written. + * RITE_OWNER 0x00080000 Ownership information associated + * with the file can be written + * YNCHRONIZE 0x00100000 The file handle can waited on to + * synchronize with the completion + * of an input/output request + * + * The Mode field specifies the mode in which the file is currently opened. + * The possible values may be a suitable and logical combination of the + * following: + * + * Value Meaning + * + * FILE_WRITE_THROUGH 0x00000002 File is opened in mode + * where data is written to + * file before the driver + * completes a write request + * FILE_SEQUENTIAL_ONLY 0x00000004 All access to the file is + * sequential + * FILE_SYNCHRONOUS_IO_ALERT 0x00000010 All operations on the + * file are performed + * synchronously + * FILE_SYNCHRONOUS_IO_NONALER 0x00000020 All operations on the + * T file are to be performed + * synchronously. Waits in + * the system to synchronize + * I/O queuing and + * completion are not + * subject to alerts. + * + * The AlignmentRequirement field specifies buffer alignment required by + * the device and can have any one of the following values: + * + * Value Meaning + * + * FILE_BYTE_ALIGNMENT 0x00000000 The buffer needs to be aligned + * on a byte boundary + * FILE_WORD_ALIGNMENT 0x00000001 The buffer needs to be aligned + * on a word boundary + * FILE_LONG_ALIGNMENT 0x00000003 The buffer needs to be aligned + * on a 4 byte boundary + * FILE_QUAD_ALIGNMENT 0x00000007 The buffer needs to be aligned + * on an 8 byte boundary + * FILE_OCTA_ALIGNMENT 0x0000000f The buffer needs to be aligned + * on a 16 byte boundary + * FILE_32_BYTE_ALIGNMENT 0x0000001f The buffer needs to be aligned + * on a 32 byte boundary + * FILE_64_BYTE_ALIGNMENT 0x0000003f The buffer needs to be aligned + * on a 64 byte boundary + * FILE_128_BYTE_ALIGNMENT 0x0000007f The buffer needs to be aligned + * on a 128 byte boundary + * FILE_256_BYTE_ALIGNMENT 0x000000ff The buffer needs to be aligned + * on a 256 byte boundary + * FILE_512_BYTE_ALIGNMENT 0x000001ff The buffer needs to be aligned + * on a 512 byte boundary + * + * 4.2.14.9 SMB_QUERY_FILE_ALT_NAME_INFO + * + * Data Block Encoding Description + * ===================== ================================= + * ULONG FileNameLength Length of the file name in number of bytes + * STRING FileName Name of the file + * + * 4.2.14.10 SMB_QUERY_FILE_STREAM_INFO + * + * Data Block Encoding Description + * =============================== ==================================== + * ULONG NextEntryOffset Offset to the next entry (in bytes) + * ULONG StreamNameLength Length of the stream name in # of bytes + * LARGE_INTEGER StreamSize Size of the stream in number of bytes + * LARGE_INTEGER AllocationSize Allocated size of stream in bytes + * STRING FileName Name of the stream + * + * 4.2.14.11 SMB_QUERY_FILE_COMPRESSION_INFO + * + * Data Block Encoding Description + * =============================== ==================================== + * LARGE_INTEGER Size of the compressed file in + * CompressedFileSize number of bytes + * USHORT CompressionFormat compression algorithm used + * UCHAR CompressionUnitShift Size of the stream in number of bytes + * UCHAR ChunkShift Allocated size of the stream in # of bytes + * UCHAR ClusterShift Allocated size of the stream in # of bytes + * UCHAR Reserved[3] Name of the stream + * + * typedef struct { + * LARGE_INTEGER CompressedFileSize; + * USHORT CompressionFormat; + * UCHAR CompressionUnitShift; + * UCHAR ChunkShift; + * UCHAR ClusterShift; + * UCHAR Reserved[3]; + * } FILE_COMPRESSION_INFORMATION; + */ + +#include +#include +#include +#include + +/* + * Function: int smb_com_trans2_query_path_information(struct smb_request *) + */ +int +smb_com_trans2_query_path_information(struct smb_request *sr, struct smb_xa *xa) +{ + char *path, *alt_nm_ptr; + int rc; + off_t dsize, dused; + unsigned short infolev, dattr; + smb_attr_t *ap, ret_attr; + struct smb_node *dir_node; + struct smb_node *node; + char *name; + char *short_name; + char *name83; + unsigned char is_dir; + int len; + + if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, + ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + short_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + name83 = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + if (smb_decode_mbc(&xa->req_param_mb, "%w4.u", sr, + &infolev, &path) != 0) { + kmem_free(name, MAXNAMELEN); + kmem_free(short_name, MAXNAMELEN); + kmem_free(name83, MAXNAMELEN); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + /* + * Some MS clients pass NULL file names + * NT interprets this as "\" + */ + if ((len = strlen(path)) == 0) + path = "\\"; + else { + if ((len > 1) && (path[len - 1] == '\\')) { + /* + * Remove the terminating slash to prevent + * sending back '.' instead of path name. + */ + path[len - 1] = 0; + } + } + + ap = &ret_attr; + if ((rc = smb_pathname_reduce(sr, sr->user_cr, path, + sr->tid_tree->t_snode, sr->tid_tree->t_snode, &dir_node, name)) + != 0) { + kmem_free(name, MAXNAMELEN); + kmem_free(short_name, MAXNAMELEN); + kmem_free(name83, MAXNAMELEN); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + if ((rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, + sr->tid_tree->t_snode, dir_node, name, &node, ap, short_name, + name83)) != 0) { + smb_node_release(dir_node); + kmem_free(name, MAXNAMELEN); + kmem_free(short_name, MAXNAMELEN); + kmem_free(name83, MAXNAMELEN); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + smb_node_release(dir_node); + (void) strcpy(name, node->od_name); + + dattr = smb_node_get_dosattr(node); + if (ap->sa_vattr.va_type == VDIR) { + is_dir = 1; + /* + * Win2K and NT reply with the size of directory + * file. + */ + dsize = dused = 0; + } else { + is_dir = 0; + dsize = ap->sa_vattr.va_size; + dused = ap->sa_vattr.va_blksize * ap->sa_vattr.va_nblocks; + } + + switch (infolev) { + case SMB_INFO_STANDARD: + if (dsize > 0xffffffff) + dsize = 0xffffffff; + if (dused > 0xffffffff) + dused = 0xffffffff; + + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, + ((sr->session->native_os == NATIVE_OS_WIN95) + ? "YYYllw" : "yyyllw"), + smb_gmt_to_local_time(ap->sa_crtime.tv_sec), + smb_gmt_to_local_time(ap->sa_vattr.va_atime.tv_sec), + smb_gmt_to_local_time(ap->sa_vattr.va_mtime.tv_sec), + (uint32_t)dsize, + (uint32_t)dused, + dattr); + break; + + case SMB_INFO_QUERY_EA_SIZE: + if (dsize > 0xffffffff) + dsize = 0xffffffff; + if (dused > 0xffffffff) + dused = 0xffffffff; + + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, + ((sr->session->native_os == NATIVE_OS_WIN95) + ? "YYYllwl" : "yyyllwl"), + smb_gmt_to_local_time(ap->sa_crtime.tv_sec), + smb_gmt_to_local_time(ap->sa_vattr.va_atime.tv_sec), + smb_gmt_to_local_time(ap->sa_vattr.va_mtime.tv_sec), + (uint32_t)dsize, + (uint32_t)dused, + dattr, 0); + break; + + case SMB_INFO_QUERY_EAS_FROM_LIST: + case SMB_INFO_QUERY_ALL_EAS: + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "l", 0); + break; + + case SMB_INFO_IS_NAME_VALID: + break; + + case SMB_QUERY_FILE_BASIC_INFO: + /* + * NT includes 6 undocumented bytes at the end of this + * response, which are required by NetBench 5.01. + * Similar change in smb_trans2_query_file_information.c. + */ + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "TTTTw6.", + &ap->sa_crtime, + &ap->sa_vattr.va_atime, + &ap->sa_vattr.va_mtime, + &ap->sa_vattr.va_ctime, + dattr); + break; + + case SMB_QUERY_FILE_STANDARD_INFO: + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + /* + * Add 2 bytes to pad data to long. It is + * necessary because Win2k expects the padded bytes. + */ + (void) smb_encode_mbc(&xa->rep_data_mb, "qqlbb2.", + dused, + dsize, + ap->sa_vattr.va_nlink, + (node && (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) != 0), + (char)(ap->sa_vattr.va_type == VDIR)); + break; + + case SMB_QUERY_FILE_EA_INFO: + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "l", 0); + break; + + case SMB_QUERY_FILE_NAME_INFO: + /* + * If you have problems here, see the changes + * in smb_trans2_query_file_information.c. + */ + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "%lu", sr, + smb_ascii_or_unicode_strlen(sr, name), name); + break; + + case SMB_QUERY_FILE_ALL_INFO: + /* + * The reply of this information level on the + * wire doesn't match with protocol specification. + * This is what spec. needs: "TTTTwqqlbbqllqqll" + * But this is actually is sent on the wire: + * "TTTTw6.qqlbb2.l" + * So, there is a 6-byte pad between Attributes and + * AllocationSize. Also there is a 2-byte pad After + * Directory field. Between Directory and FileNameLength + * there is just 4 bytes that it seems is AlignmentRequirement. + * There are 6 other fields between Directory and + * AlignmentRequirement in spec. that aren't sent + * on the wire. + */ + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "TTTTw6.qqlbb2.l", + &ap->sa_crtime, + &ap->sa_vattr.va_atime, + &ap->sa_vattr.va_mtime, + &ap->sa_vattr.va_ctime, + dattr, + dused, + dsize, + ap->sa_vattr.va_nlink, + 0, + is_dir, + 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "%lu", sr, + smb_ascii_or_unicode_strlen(sr, name), name); + break; + + case SMB_QUERY_FILE_ALT_NAME_INFO: + /* + * Conform to the rule used by Windows NT/2003 servers. + * Shortname is created only if either the filename or + * extension portion of a file is made up of mixed case. + * + * If the shortname is generated, it will be returned as + * the alternative name. Otherwise, converts the original + * name to all upper-case and returns it as the alternative + * name. This is how Windows NT/2003 servers behave. However, + * Windows 2000 seems to preserve the case of the original + * name, and returns it as the alternative name. + * + * Note: The shortname is returned by smb_fsop_lookup(), above. + * In the case that the name used by the client was originally + * generated in response to a case-insensitive collision, the + * short_name and the 8.3 name will reflect this. + */ + alt_nm_ptr = ((*short_name == 0) ? + utf8_strupr(name) : short_name); + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, "%lu", sr, + smb_ascii_or_unicode_strlen(sr, alt_nm_ptr), alt_nm_ptr); + break; + + case SMB_QUERY_FILE_STREAM_INFO: + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + smb_encode_stream_info(sr, xa, node, ap); + break; + + case SMB_QUERY_FILE_COMPRESSION_INFO: + (void) smb_encode_mbc(&xa->rep_param_mb, "w", 0); + (void) smb_encode_mbc(&xa->rep_data_mb, + "qwbbb3.", dsize, 0, 0, 0, 0); + break; + + default: + smb_node_release(node); + kmem_free(name, MAXNAMELEN); + kmem_free(short_name, MAXNAMELEN); + kmem_free(name83, MAXNAMELEN); + smbsr_raise_error(sr, ERRDOS, ERRunknownlevel); + /* NOTREACHED */ + break; + } + smb_node_release(node); + kmem_free(name, MAXNAMELEN); + kmem_free(short_name, MAXNAMELEN); + kmem_free(name83, MAXNAMELEN); + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_set_file_information.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_set_file_information.c new file mode 100644 index 000000000000..094032da1cfd --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_set_file_information.c @@ -0,0 +1,163 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: trans2_set_file_information + * + * This request is used to set information about a specific file or + * subdirectory given a handle to the file or subdirectory. + * + * Client Request Value + * ========================== ========================================== + * + * WordCount 15 + * MaxSetupCount 0 + * SetupCount 1 + * Setup[0] TRANS2_SET_FILE_INFORMATION + * + * Parameter Block Encoding Description + * ========================== ========================================== + * + * USHORT Fid; Handle of file for request + * USHORT InformationLevel; Level of information requested + * USHORT Reserved; Ignored by the server + * + * The following InformationLevels may be set: + * + * Information Level Value + * ================================ ===== + * + * SMB_INFO_STANDARD 1 + * SMB_INFO_QUERY_EA_SIZE 2 + * SMB_SET_FILE_BASIC_INFO 0x101 + * SMB_SET_FILE_DISPOSITION_INFO 0x102 + * SMB_SET_FILE_ALLOCATION_INFO 0x103 + * SMB_SET_FILE_END_OF_FILE_INFO 0x104 + * + * The two levels below 0x101 are as described in the + * NT_SET_PATH_INFORMATION transaction. The requested information is + * placed in the Data portion of the transaction response. For the + * information levels greater than 0x100, the transaction response has 1 + * parameter word which should be ignored by the client. + * + * 4.2.17.1 SMB_FILE_DISPOSITION_INFO + * + * Response Field Value + * ==================== =============================================== + * + * BOOLEAN A boolean which is TRUE if the file is marked + * FileIsDeleted for deletion + * + * 4.2.17.2 SMB_FILE_ALLOCATION_INFO + * + * Response Field Value + * ==================== =============================================== + * + * LARGE_INTEGER File Allocation size in number of bytes + * + * 4.2.17.3 SMB_FILE_END_OF_FILE_INFO + * + * Response Field Value + * ==================== =============================================== + * + * LARGE_INTEGER The total number of bytes that need to be + * traversed from the beginning of the file in + * order to locate the end of the file + * + * Undocumented things: + * Poorly documented information levels. Information must be infered + * from other commands. + * + * NULL Attributes means don't set them. NT sets the high bit to + * set attributes to 0. + */ + +#include + +/* + * smb_com_trans2_set_file_information + */ +int +smb_com_trans2_set_file_information(struct smb_request *sr, struct smb_xa *xa) +{ + smb_trans2_setinfo_t *info; + smb_error_t smberr; + DWORD status; + int rc; + + info = kmem_zalloc(sizeof (smb_trans2_setinfo_t), KM_SLEEP); + info->ts_xa = xa; + + rc = smb_decode_mbc(&xa->req_param_mb, "ww", &sr->smb_fid, + &info->level); + if (rc != 0) { + kmem_free(info, sizeof (smb_trans2_setinfo_t)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (!STYPE_ISDSK(sr->tid_tree->t_res_type) || + SMB_TREE_IS_READ_ONLY(sr)) { + kmem_free(info, sizeof (smb_trans2_setinfo_t)); + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + kmem_free(info, sizeof (smb_trans2_setinfo_t)); + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + info->node = sr->fid_ofile->f_node; + + if (info->node == 0 || + !SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) { + cmn_err(CE_NOTE, "SmbT2SetFileInfo: access denied"); + kmem_free(info, sizeof (smb_trans2_setinfo_t)); + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + status = smb_trans2_set_information(sr, info, &smberr); + if (status == NT_STATUS_DATA_ERROR) { + kmem_free(info, sizeof (smb_trans2_setinfo_t)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } else if (status == NT_STATUS_UNSUCCESSFUL) { + kmem_free(info, sizeof (smb_trans2_setinfo_t)); + smbsr_raise_cifs_error(sr, smberr.status, + smberr.errcls, smberr.errcode); + /* NOTREACHED */ + } + kmem_free(info, sizeof (smb_trans2_setinfo_t)); + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_set_information.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_set_information.c new file mode 100644 index 000000000000..b41e1c426d42 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_set_information.c @@ -0,0 +1,414 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file contains the common code used by + * Trans2SetFileInfo and Trans2SetPathInfo SMBs. + */ + +#include +#include + +static DWORD smb_set_standard_info(struct smb_request *sr, + smb_trans2_setinfo_t *info, smb_error_t *smberr); + +static DWORD smb_set_basic_info(struct smb_request *sr, + smb_trans2_setinfo_t *info, smb_error_t *smberr); + +static DWORD smb_set_disposition_info(struct smb_request *sr, + smb_trans2_setinfo_t *info, smb_error_t *smberr); + +static DWORD smb_set_alloc_info(struct smb_request *sr, + smb_trans2_setinfo_t *info, smb_error_t *smberr); + +/*LINTED E_STATIC_UNUSED*/ +static DWORD smb_set_mac_info(struct smb_request *sr, + smb_trans2_setinfo_t *info, smb_error_t *smberr); + +/*LINTED E_STATIC_UNUSED*/ +static DWORD smb_set_mac_addappl(struct smb_request *sr, + smb_trans2_setinfo_t *info, smb_error_t *smberr); + +/*LINTED E_STATIC_UNUSED*/ +static DWORD smb_set_mac_rmvappl(struct smb_request *sr, + smb_trans2_setinfo_t *info, smb_error_t *smberr); + +/*LINTED E_STATIC_UNUSED*/ +static DWORD smb_set_mac_addicon(struct smb_request *sr, + smb_trans2_setinfo_t *info, smb_error_t *smberr); + +static unsigned short smb_info_passthru(unsigned short infolevel); + +/* + * smb_trans2_set_information + * + * This is a common function called by both Trans2SetFileInfo + * and Trans2SetPathInfo. + */ +DWORD +smb_trans2_set_information( + struct smb_request *sr, + smb_trans2_setinfo_t *info, + smb_error_t *smberr) +{ + info->level = smb_info_passthru(info->level); + + switch (info->level) { + case SMB_INFO_STANDARD: + case SMB_INFO_QUERY_EA_SIZE: + return (smb_set_standard_info(sr, info, smberr)); + + case SMB_INFO_QUERY_ALL_EAS: + /* This info level is not supported */ + return (NT_STATUS_SUCCESS); + + case SMB_SET_FILE_BASIC_INFO: + return (smb_set_basic_info(sr, info, smberr)); + + case SMB_SET_FILE_DISPOSITION_INFO: + return (smb_set_disposition_info(sr, info, smberr)); + + case SMB_SET_FILE_END_OF_FILE_INFO: + case SMB_SET_FILE_ALLOCATION_INFO: + return (smb_set_alloc_info(sr, info, smberr)); + + default: + smberr->status = NT_STATUS_INVALID_INFO_CLASS; + smberr->errcls = ERRDOS; + smberr->errcode = ERROR_INVALID_PARAMETER; + return (NT_STATUS_UNSUCCESSFUL); + } + /*NOTREACHED*/ +} + +/* + * smb_info_passthru + * + * SMB_INFO_PASSTHROUGH + * If the server supports information level request passing through, + * the client may add the information level with SMB_INFO_PASSTHROUGH + * and submit the file information in NT data format instead of SMB + * data format. Please refer to MSDN for related NT file information + * data structure. + * + * SMB_INFO_PASSTHROUGH (1000) is defined in win32/cifs.h and the file + * information class values are defined in win32/ntifs.h. we have + * observed: + * 0x3EC = SMB_INFO_PASSTHROUGH + FileBasicInformation (4) + * 0x3F5 = SMB_INFO_PASSTHROUGH + FileDispositionInformation (13) + * 0x3FC = SMB_INFO_PASSTHROUGH + FileEndOfFileInformation (20) + * + * Based on network traces between two Win2K systems: + * FileBasicInformation <=> SMB_SET_FILE_BASIC_INFO + * FileDispositionInformation <=> SMB_SET_FILE_DISPOSITION_INFO + * FileEndOfFileInformation <=> SMB_SET_FILE_END_OF_FILE_INFO + */ +static unsigned short +smb_info_passthru(unsigned short infolevel) +{ + if (infolevel <= SMB_INFO_PASSTHROUGH) + return (infolevel); + + infolevel -= SMB_INFO_PASSTHROUGH; + + switch (infolevel) { + case FileBasicInformation: + return (SMB_SET_FILE_BASIC_INFO); + + case FileDispositionInformation: + return (SMB_SET_FILE_DISPOSITION_INFO); + + case FileEndOfFileInformation: + return (SMB_SET_FILE_END_OF_FILE_INFO); + } + + return (infolevel); +} + +/* + * smb_set_standard_info + * + * SMB_INFO_STANDARD & SMB_INFO_QUERY_EA_SIZE + * + * Data Block Encoding Description + * ================================== ================================= + * + * SMB_DATE CreationDate; Date when file was created + * SMB_TIME CreationTime; Time when file was created + * SMB_DATE LastAccessDate; Date of last file access + * SMB_TIME LastAccessTime; Time of last file access + * SMB_DATE LastWriteDate; Date of last write to the file + * SMB_TIME LastWriteTime; Time of last write to the file + * ULONG DataSize; File Size + * ULONG AllocationSize; Size of filesystem allocation + * unit + * USHORT Attributes; File Attributes + * ULONG EaSize; Size of file's EA information + * (SMB_INFO_QUERY_EA_SIZE) + */ +static DWORD +smb_set_standard_info( + struct smb_request *sr, + smb_trans2_setinfo_t *info, + smb_error_t *smberr) +{ + uint32_t Creation, LastAccess, LastWrite; /* times */ + uint32_t DataSize, AllocationSize; + unsigned short Attributes; + unsigned int what = 0; + timestruc_t crtime, mtime, atime; + DWORD status = NT_STATUS_SUCCESS; + struct smb_node *node = info->node; + int rc; + + if (smb_decode_mbc(&info->ts_xa->req_data_mb, "yyyllw", + &Creation, /* CreationDate/Time */ + &LastAccess, /* LastAccessDate/Time */ + &LastWrite, /* LastWriteDate/Time */ + &DataSize, /* File Size */ + &AllocationSize, /* Block Size */ + &Attributes) != 0) { /* File Attributes */ + return (NT_STATUS_DATA_ERROR); + } + + if (DataSize != 0) { + node->flags |= NODE_FLAGS_SET_SIZE; + node->n_size = DataSize; + } + + /* + * IR101794 The behaviour when the time field is set to -1 + * is not documented, so we'll assume it should be treated + * like 0. + */ + crtime.tv_nsec = mtime.tv_nsec = atime.tv_nsec = 0; + if (LastWrite != 0 && LastWrite != (uint32_t)-1) { + mtime.tv_sec = smb_local_time_to_gmt(LastWrite); + node->set_mtime = mtime; + what |= SMB_AT_MTIME; + } + + if (Creation != 0 && Creation != (uint32_t)-1) { + crtime.tv_sec = smb_local_time_to_gmt(Creation); + what |= SMB_AT_CRTIME; + } + + if (LastAccess != 0 && LastAccess != (uint32_t)-1) { + atime.tv_sec = smb_local_time_to_gmt(LastAccess); + what |= SMB_AT_ATIME; + } + + if (Attributes != 0) + smb_node_set_dosattr(node, Attributes); + + smb_node_set_time(node, &crtime, &mtime, &atime, 0, what); + rc = smb_sync_fsattr(sr, sr->user_cr, node); + if (rc) { + smb_errmap_unix2smb(rc, smberr); + status = NT_STATUS_UNSUCCESSFUL; + } + + return (status); +} + +/* + * smb_set_basic_info + * + * Sets basic file/path information. + */ +static DWORD +smb_set_basic_info( + struct smb_request *sr, + smb_trans2_setinfo_t *info, + smb_error_t *smberr) +{ + uint64_t NT_Creation, NT_LastAccess, NT_LastWrite, NT_Change; + unsigned short Attributes; + unsigned int what = 0; + timestruc_t crtime, mtime, atime, ctime; + struct smb_node *node = info->node; + DWORD status = NT_STATUS_SUCCESS; + int rc; + + if (smb_decode_mbc(&info->ts_xa->req_data_mb, "qqqqw", + &NT_Creation, /* CreationDate/Time */ + &NT_LastAccess, /* LastAccessDate/Time */ + &NT_LastWrite, /* LastWriteDate/Time */ + &NT_Change, /* LastWriteDate/Time */ + &Attributes) != 0) { /* File Attributes */ + return (NT_STATUS_DATA_ERROR); + } + + /* + * IR101794 The behaviour when the time field is set to -1 + * is not documented, so we'll assume it should be treated + * like 0. + */ + if (NT_Change != 0 && NT_Change != (uint64_t)-1) { + (void) nt_to_unix_time(NT_Change, &ctime); + what |= SMB_AT_CTIME; + } + + if (NT_Creation != 0 && NT_Creation != (uint64_t)-1) { + (void) nt_to_unix_time(NT_Creation, &crtime); + what |= SMB_AT_CRTIME; + } + + if (NT_LastWrite != 0 && NT_LastWrite != (uint64_t)-1) { + (void) nt_to_unix_time(NT_LastWrite, &mtime); + node->set_mtime = mtime; + what |= SMB_AT_MTIME; + } + + if (NT_LastAccess != 0 && NT_LastAccess != (uint64_t)-1) { + (void) nt_to_unix_time(NT_LastAccess, &atime); + what |= SMB_AT_ATIME; + } + + if (Attributes != 0) + smb_node_set_dosattr(node, Attributes); + + smb_node_set_time(node, &crtime, &mtime, &atime, &ctime, what); + rc = smb_sync_fsattr(sr, sr->user_cr, node); + if (rc) { + smb_errmap_unix2smb(rc, smberr); + status = NT_STATUS_UNSUCCESSFUL; + } + + return (status); +} + + +/* + * smb_set_alloc_info + * + * Sets file allocation/end_of_file info + */ +static DWORD +smb_set_alloc_info( + struct smb_request *sr, + smb_trans2_setinfo_t *info, + smb_error_t *smberr) +{ + uint64_t DataSize; + DWORD status = NT_STATUS_SUCCESS; + struct smb_node *node = info->node; + int rc; + + if (sr->fid_ofile == NULL) { + smberr->status = NT_STATUS_ACCESS_DENIED; + smberr->errcls = ERRDOS; + smberr->errcode = ERROR_ACCESS_DENIED; + return (NT_STATUS_UNSUCCESSFUL); + } + + if (smb_decode_mbc(&info->ts_xa->req_data_mb, "q", &DataSize) != 0) + return (NT_STATUS_DATA_ERROR); + + if (node->attr.sa_vattr.va_size != DataSize) { + node->flags |= NODE_FLAGS_SET_SIZE; + node->n_size = (off_t)DataSize; + + /* + * Ensure that the FS is consistent with the node cache + * because the size flag can get cleared by subsequent + * write requests without the inode ever being updated. + */ + if ((rc = smb_set_file_size(sr)) != 0) { + smb_errmap_unix2smb(rc, smberr); + status = NT_STATUS_UNSUCCESSFUL; + } + } + + return (status); +} + +/* + * smb_set_disposition_info + * + * Set/Clear DELETE_ON_CLOSE flag for an open file. + * File should have been opened with DELETE access otherwise + * the operation is not permitted. + * + * NOTE: The node should be marked delete-on-close upon the receipt + * of the Trans2SetFileInfo(SetDispositionInfo) if mark_delete is set. + * It is different than both SmbNtCreateAndX and SmbNtTransact, which + * set delete-on-close on the ofile and defer setting the flag on the + * node until the file is closed. + * + * Observation of Windows 2000 indicates the following: + * + * 1) If a file is not opened with delete-on-close create options and + * the delete-on-close is set via Trans2SetFileInfo(SetDispositionInfo) + * using that open file handle, any subsequent open requests will fail + * with DELETE_PENDING. + * + * 2) If a file is opened with delete-on-close create options and the + * client attempts to unset delete-on-close via Trans2SetFileInfo + * (SetDispositionInfo) prior to the file close, any subsequent open + * requests will still fail with DELETE_PENDING after the file is closed. + * + * 3) If a file is opened with delete-on-close create options and that + * file handle (not the last open handle and the only file handle + * with delete-on-close set) is closed. Any subsequent open requests + * will fail with DELETE_PENDING. Unsetting delete-on-close via + * Trans2SetFileInfo(SetDispositionInfo) at this time will unset the + * node delete-on-close flag, which will result in the file not being + * removed even after the last file handle is closed. + */ +static DWORD +smb_set_disposition_info( + smb_request_t *sr, + smb_trans2_setinfo_t *info, + smb_error_t *smberr) +{ + unsigned char mark_delete; + + if (smb_decode_mbc(&info->ts_xa->req_data_mb, "b", &mark_delete) != 0) + return (NT_STATUS_DATA_ERROR); + + if ((sr->fid_ofile == NULL) || + !(smb_ofile_granted_access(sr->fid_ofile) & DELETE)) { + smberr->status = NT_STATUS_ACCESS_DENIED; + smberr->errcls = ERRDOS; + smberr->errcode = ERROR_ACCESS_DENIED; + return (NT_STATUS_UNSUCCESSFUL); + } + + if (mark_delete) { + if (smb_node_set_delete_on_close(info->node, + sr->user_cr)) { + smberr->status = NT_STATUS_CANNOT_DELETE; + smberr->errcls = ERRDOS; + smberr->errcode = ERROR_ACCESS_DENIED; + return (NT_STATUS_UNSUCCESSFUL); + } + } else { + smb_node_reset_delete_on_close(info->node); + } + return (NT_STATUS_SUCCESS); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_set_path_information.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_set_path_information.c new file mode 100644 index 000000000000..02be8c53cbc9 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_set_path_information.c @@ -0,0 +1,179 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: trans2_set_path_information + * + * This request is used to set information about a specific file or + * subdirectory. + * + * Client Request Value + * ========================== ========================================= + * + * WordCount 15 + * MaxSetupCount 0 + * SetupCount 1 + * Setup[0] TRANS2_SET_PATH_INFORMATION + * + * Parameter Block Encoding Description + * ========================== ========================================= + * + * USHORT InformationLevel; Level of information to set + * ULONG Reserved; Must be zero + * STRING FileName; File or directory name + * + * The following InformationLevels may be set: + * + * Information Level Value + * ========================== ========================================= + * + * SMB_INFO_STANDARD 1 + * SMB_INFO_QUERY_EA_SIZE 2 + * SMB_INFO_QUERY_ALL_EAS 4 + * + * The response formats are: + * + * 4.2.16.1 SMB_INFO_STANDARD & SMB_INFO_QUERY_EA_SIZE + * + * Parameter Block Encoding Description + * ================================== ================================= + * + * USHORT Reserved 0 + * + * Data Block Encoding Description + * ================================== ================================= + * + * SMB_DATE CreationDate; Date when file was created + * SMB_TIME CreationTime; Time when file was created + * SMB_DATE LastAccessDate; Date of last file access + * SMB_TIME LastAccessTime; Time of last file access + * SMB_DATE LastWriteDate; Date of last write to the file + * SMB_TIME LastWriteTime; Time of last write to the file + * ULONG DataSize; File Size + * ULONG AllocationSize; Size of filesystem allocation + * unit + * USHORT Attributes; File Attributes + * ULONG EaSize; Size of file's EA information + * (SMB_INFO_QUERY_EA_SIZE) + * + * 4.2.16.2 SMB_INFO_QUERY_ALL_EAS + * + * Response Field Value + * ==================== =============================================== + * + * MaxDataCount Length of FEAlist found (minimum value is 4) + * + * Parameter Block Description + * Encoding =============================================== + * ==================== + * + * USHORT EaErrorOffset Offset into EAList of EA error + * + * Data Block Encoding Description + * ==================== =============================================== + * + * ULONG ListLength; Length of the remaining data + * UCHAR EaList[] The extended attributes list + * + * Undocumented things: + * Poorly documented information levels. Information must be infered + * from other commands. + * + * NULL Attributes means don't set them. NT sets the high bit to + * set attributes to 0. + */ + +#include +#include + +int +smb_com_trans2_set_path_information(struct smb_request *sr, struct smb_xa *xa) +{ + smb_trans2_setinfo_t *info; + smb_attr_t ret_attr; + struct smb_node *dir_node; + struct smb_node *ret_snode; + smb_error_t smberr; + DWORD status; + int rc = 0; + + info = kmem_zalloc(sizeof (smb_trans2_setinfo_t), KM_SLEEP); + info->ts_xa = xa; + + if (smb_decode_mbc(&xa->req_param_mb, "%w4.u", sr, &info->level, + &info->path) != 0) { + kmem_free(info, sizeof (smb_trans2_setinfo_t)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (!STYPE_ISDSK(sr->tid_tree->t_res_type) || + SMB_TREE_IS_READ_ONLY(sr)) { + kmem_free(info, sizeof (smb_trans2_setinfo_t)); + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + rc = smb_pathname_reduce(sr, sr->user_cr, info->path, + sr->tid_tree->t_snode, sr->tid_tree->t_snode, + &dir_node, info->name); + + if (rc != 0) { + kmem_free(info, sizeof (smb_trans2_setinfo_t)); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, + sr->tid_tree->t_snode, dir_node, info->name, &ret_snode, &ret_attr, + 0, 0); + + smb_node_release(dir_node); + + if (rc != 0) { + kmem_free(info, sizeof (smb_trans2_setinfo_t)); + smbsr_raise_errno(sr, rc); + } + + info->node = ret_snode; + status = smb_trans2_set_information(sr, info, &smberr); + info->node = NULL; + smb_node_release(ret_snode); + if (status == NT_STATUS_DATA_ERROR) { + kmem_free(info, sizeof (smb_trans2_setinfo_t)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } else if (status == NT_STATUS_UNSUCCESSFUL) { + kmem_free(info, sizeof (smb_trans2_setinfo_t)); + smbsr_raise_cifs_error(sr, smberr.status, + smberr.errcls, smberr.errcode); + /* NOTREACHED */ + } + kmem_free(info, sizeof (smb_trans2_setinfo_t)); + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_tree.c b/usr/src/uts/common/fs/smbsrv/smb_tree.c new file mode 100644 index 000000000000..85d663c88cfe --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_tree.c @@ -0,0 +1,733 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * General Structures Layout + * ------------------------- + * + * This is a simplified diagram showing the relationship between most of the + * main structures. + * + * +-------------------+ + * | SMB_INFO | + * +-------------------+ + * | + * | + * v + * +-------------------+ +-------------------+ +-------------------+ + * | SESSION |<----->| SESSION |......| SESSION | + * +-------------------+ +-------------------+ +-------------------+ + * | + * | + * v + * +-------------------+ +-------------------+ +-------------------+ + * | USER |<----->| USER |......| USER | + * +-------------------+ +-------------------+ +-------------------+ + * | + * | + * v + * +-------------------+ +-------------------+ +-------------------+ + * | TREE |<----->| TREE |......| TREE | + * +-------------------+ +-------------------+ +-------------------+ + * | | + * | | + * | v + * | +-------+ +-------+ +-------+ + * | | OFILE |<----->| OFILE |......| OFILE | + * | +-------+ +-------+ +-------+ + * | + * | + * v + * +-------+ +------+ +------+ + * | ODIR |<----->| ODIR |......| ODIR | + * +-------+ +------+ +------+ + * + * + * Tree State Machine + * ------------------ + * + * +-----------------------------+ T0 + * | SMB_TREE_STATE_CONNECTED |<----------- Creation/Allocation + * +-----------------------------+ + * | + * | T1 + * | + * v + * +------------------------------+ + * | SMB_TREE_STATE_DISCONNECTING | + * +------------------------------+ + * | + * | T2 + * | + * v + * +-----------------------------+ T3 + * | SMB_TREE_STATE_DISCONNECTED |----------> Deletion/Free + * +-----------------------------+ + * + * SMB_TREE_STATE_CONNECTED + * + * While in this state: + * - The tree is queued in the list of trees of its user. + * - References will be given out if the tree is looked up. + * - Files under that tree can be accessed. + * + * SMB_TREE_STATE_DISCONNECTING + * + * While in this state: + * - The tree is queued in the list of trees of its user. + * - References will not be given out if the tree is looked up. + * - The files and directories open under the tree are being closed. + * - The resources associated with the tree remain. + * + * SMB_TREE_STATE_DISCONNECTED + * + * While in this state: + * - The tree is queued in the list of trees of its user. + * - References will not be given out if the tree is looked up. + * - The tree has no more files and directories opened. + * - The resources associated with the tree remain. + * + * Transition T0 + * + * This transition occurs in smb_tree_connect(). A new tree is created and + * added to the list of trees of a user. + * + * Transition T1 + * + * This transition occurs in smb_tree_disconnect(). + * + * Transition T2 + * + * This transition occurs in smb_tree_release(). The resources associated + * with the tree are freed as well as the tree structure. For the transition + * to occur, the tree must be in the SMB_TREE_STATE_DISCONNECTED state and + * the reference count be zero. + * + * Comments + * -------- + * + * The state machine of the tree structures is controlled by 3 elements: + * - The list of trees of the user it belongs to. + * - The mutex embedded in the structure itself. + * - The reference count. + * + * There's a mutex embedded in the tree structure used to protect its fields + * and there's a lock embedded in the list of trees of a user. To + * increment or to decrement the reference count the mutex must be entered. + * To insert the tree into the list of trees of the user and to remove + * the tree from it, the lock must be entered in RW_WRITER mode. + * + * Rules of access to a tree structure: + * + * 1) In order to avoid deadlocks, when both (mutex and lock of the user + * list) have to be entered, the lock must be entered first. + * + * 2) All actions applied to a tree require a reference count. + * + * 3) There are 2 ways of getting a reference count. One is when the tree + * is connected. The other when the user is looked up. This translates + * into 2 functions: smb_tree_connect() and smb_tree_lookup_by_tid(). + * + * It should be noted that the reference count of a tree registers the + * number of references to the tree in other structures (such as an smb + * request). The reference count is not incremented in these 2 instances: + * + * 1) The tree is connected. An tree is anchored by his state. If there's + * no activity involving a tree currently connected, the reference + * count of that tree is zero. + * + * 2) The tree is queued in the list of trees of the user. The fact of + * being queued in that list is NOT registered by incrementing the + * reference count. + */ +#include +#include + +/* Static functions defined further down this file. */ +static void smb_tree_delete(smb_tree_t *); +static smb_tree_t *smb_tree_lookup_head(smb_llist_t *); +static smb_tree_t *smb_tree_lookup_next(smb_llist_t *, smb_tree_t *); + +/* + * smb_tree_connect + */ +smb_tree_t * +smb_tree_connect( + smb_user_t *user, + uint16_t access_flags, + char *sharename, + char *resource, + int32_t stype, + smb_node_t *snode, + fsvol_attr_t *vol_attr) +{ + smb_tree_t *tree; + uint16_t tid; + + if (smb_idpool_alloc(&user->u_tid_pool, &tid)) { + return (NULL); + } + + tree = kmem_cache_alloc(smb_info.si_cache_tree, KM_SLEEP); + bzero(tree, sizeof (smb_tree_t)); + + if (smb_idpool_constructor(&tree->t_fid_pool)) { + smb_idpool_free(&user->u_tid_pool, tid); + kmem_cache_free(smb_info.si_cache_tree, tree); + return (NULL); + } + + if (smb_idpool_constructor(&tree->t_sid_pool)) { + smb_idpool_destructor(&tree->t_fid_pool); + smb_idpool_free(&user->u_tid_pool, tid); + kmem_cache_free(smb_info.si_cache_tree, tree); + return (NULL); + } + + smb_llist_constructor(&tree->t_ofile_list, sizeof (smb_ofile_t), + offsetof(smb_ofile_t, f_lnd)); + + smb_llist_constructor(&tree->t_odir_list, sizeof (smb_odir_t), + offsetof(smb_odir_t, d_lnd)); + + (void) strlcpy(tree->t_sharename, sharename, + sizeof (tree->t_sharename)); + (void) strlcpy(tree->t_resource, resource, sizeof (tree->t_resource)); + + mutex_init(&tree->t_mutex, NULL, MUTEX_DEFAULT, NULL); + + tree->t_user = user; + tree->t_session = user->u_session; + tree->t_refcnt = 1; + tree->t_tid = tid; + tree->t_access = access_flags; + tree->t_res_type = stype; + tree->t_snode = snode; + tree->t_state = SMB_TREE_STATE_CONNECTED; + tree->t_magic = SMB_TREE_MAGIC; + + switch (stype & STYPE_MASK) { + case STYPE_DISKTREE: + tree->t_fsd = snode->tree_fsd; + + (void) strlcpy(tree->t_typename, vol_attr->fs_typename, + SMB_TREE_TYPENAME_SZ); + (void) utf8_strupr((char *)tree->t_typename); + + if (vol_attr->flags & FSOLF_READONLY) + tree->t_access = SMB_TREE_READ_ONLY; + + tree->t_acltype = smb_fsop_acltype(snode); + + if (vfs_has_feature(snode->vp->v_vfsp, VFSFT_ACLONCREATE)) { + tree->t_flags |= SMB_TREE_FLAG_ACLONCREATE; + } + + if (vfs_has_feature(snode->vp->v_vfsp, VFSFT_ACEMASKONACCESS)) { + tree->t_flags |= SMB_TREE_FLAG_ACEMASKONACCESS; + } + + if (vfs_has_feature(snode->vp->v_vfsp, VFSFT_CASEINSENSITIVE)) { + tree->t_flags |= SMB_TREE_FLAG_IGNORE_CASE; + } + break; + + case STYPE_IPC: + default: + tree->t_typename[0] = '\0'; + break; + } + + smb_llist_enter(&user->u_tree_list, RW_WRITER); + smb_llist_insert_head(&user->u_tree_list, tree); + smb_llist_exit(&user->u_tree_list); + atomic_inc_32(&user->u_session->s_tree_cnt); + atomic_inc_32(&smb_info.open_trees); + + return (tree); +} + +/* + * smb_tree_disconnect + * + * + */ +void +smb_tree_disconnect( + smb_tree_t *tree) +{ + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + + mutex_enter(&tree->t_mutex); + ASSERT(tree->t_refcnt); + switch (tree->t_state) { + case SMB_TREE_STATE_CONNECTED: { + /* + * The tree is moved into a state indicating that the disconnect + * process has started. + */ + tree->t_state = SMB_TREE_STATE_DISCONNECTING; + mutex_exit(&tree->t_mutex); + atomic_dec_32(&smb_info.open_trees); + /* + * The files opened under this tree are closed. + */ + smb_ofile_close_all(tree); + /* + * The directories opened under this tree are closed. + */ + smb_odir_close_all(tree); + mutex_enter(&tree->t_mutex); + tree->t_state = SMB_TREE_STATE_DISCONNECTED; + /*FALLTHRU*/ + } + case SMB_TREE_STATE_DISCONNECTED: + case SMB_TREE_STATE_DISCONNECTING: + break; + + default: + ASSERT(0); + break; + } + mutex_exit(&tree->t_mutex); +} + +/* + * smb_tree_disconnect_all + * + * + */ +void +smb_tree_disconnect_all( + smb_user_t *user) +{ + smb_tree_t *tree; + + ASSERT(user); + ASSERT(user->u_magic == SMB_USER_MAGIC); + + tree = smb_tree_lookup_head(&user->u_tree_list); + while (tree) { + ASSERT(tree->t_user == user); + smb_tree_disconnect(tree); + smb_tree_release(tree); + tree = smb_tree_lookup_head(&user->u_tree_list); + } +} + +/* + * smb_tree_close_all_by_pid + * + * + */ +void +smb_tree_close_all_by_pid( + smb_user_t *user, + uint16_t pid) +{ + smb_tree_t *tree; + + ASSERT(user); + ASSERT(user->u_magic == SMB_USER_MAGIC); + + tree = smb_tree_lookup_head(&user->u_tree_list); + while (tree) { + smb_tree_t *next; + ASSERT(tree->t_user == user); + smb_ofile_close_all_by_pid(tree, pid); + smb_odir_close_all_by_pid(tree, pid); + next = smb_tree_lookup_next(&user->u_tree_list, tree); + smb_tree_release(tree); + tree = next; + } +} + +/* + * smb_tree_release + * + * + */ +void +smb_tree_release( + smb_tree_t *tree) +{ + ASSERT(tree); + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + + mutex_enter(&tree->t_mutex); + ASSERT(tree->t_refcnt); + tree->t_refcnt--; + switch (tree->t_state) { + case SMB_TREE_STATE_DISCONNECTED: + if (tree->t_refcnt == 0) { + mutex_exit(&tree->t_mutex); + smb_tree_delete(tree); + return; + } + break; + + case SMB_TREE_STATE_CONNECTED: + case SMB_TREE_STATE_DISCONNECTING: + break; + + default: + ASSERT(0); + break; + } + mutex_exit(&tree->t_mutex); +} + +/* + * Find the appropriate tree for this request. The request credentials + * set here override those set during uid lookup. In domain mode, the + * user and tree credentials should be the same. In share mode, the + * tree credentials (defined in the share definition) should override + * the user credentials. + */ +smb_tree_t * +smb_tree_lookup_by_tid( + smb_user_t *user, + uint16_t tid) +{ + smb_tree_t *tree; + + ASSERT(user); + ASSERT(user->u_magic == SMB_USER_MAGIC); + + smb_llist_enter(&user->u_tree_list, RW_READER); + tree = smb_llist_head(&user->u_tree_list); + while (tree) { + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + ASSERT(tree->t_user == user); + if (tree->t_tid == tid) { + mutex_enter(&tree->t_mutex); + switch (tree->t_state) { + case SMB_TREE_STATE_CONNECTED: + /* The tree exists and is still connected. */ + tree->t_refcnt++; + mutex_exit(&tree->t_mutex); + smb_llist_exit(&user->u_tree_list); + return (tree); + case SMB_TREE_STATE_DISCONNECTING: + case SMB_TREE_STATE_DISCONNECTED: + /* + * The tree exists but is diconnected or is in + * the process of being destroyed. + */ + mutex_exit(&tree->t_mutex); + smb_llist_exit(&user->u_tree_list); + return (NULL); + default: + ASSERT(0); + mutex_exit(&tree->t_mutex); + smb_llist_exit(&user->u_tree_list); + return (NULL); + } + } + tree = smb_llist_next(&user->u_tree_list, tree); + } + smb_llist_exit(&user->u_tree_list); + return (NULL); +} + +/* + * smb_tree_lookup_first_by_name + * + * This function returns the first tree in the connected state that matches the + * sharename passed in. If the tree provided is NULL the search starts from + * the beginning of the list of trees of the user. It a tree is provided the + * search starts just after that tree. + */ +smb_tree_t * +smb_tree_lookup_by_name( + smb_user_t *user, + char *sharename, + smb_tree_t *tree) +{ + ASSERT(user); + ASSERT(user->u_magic == SMB_USER_MAGIC); + ASSERT(sharename); + + smb_llist_enter(&user->u_tree_list, RW_READER); + + if (tree) { + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + ASSERT(tree->t_user == user); + tree = smb_llist_next(&user->u_tree_list, tree); + } else { + tree = smb_llist_head(&user->u_tree_list); + } + + while (tree) { + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + ASSERT(tree->t_user == user); + if (strcmp(tree->t_sharename, sharename) == 0) { + mutex_enter(&tree->t_mutex); + switch (tree->t_state) { + case SMB_TREE_STATE_CONNECTED: + /* The tree exists and is still connected. */ + tree->t_refcnt++; + mutex_exit(&tree->t_mutex); + smb_llist_exit(&user->u_tree_list); + return (tree); + case SMB_TREE_STATE_DISCONNECTING: + case SMB_TREE_STATE_DISCONNECTED: + /* + * The tree exists but is diconnected or is in + * the process of being destroyed. + */ + mutex_exit(&tree->t_mutex); + break; + default: + ASSERT(0); + mutex_exit(&tree->t_mutex); + break; + } + } + tree = smb_llist_next(&user->u_tree_list, tree); + } + smb_llist_exit(&user->u_tree_list); + return (NULL); +} + +/* + * smb_tree_lookup_first_by_fsd + * + * This function returns the first tree in the connected state that matches the + * fsd passed in. If the tree provided is NULL the search starts from + * the beginning of the list of trees of the user. It a tree is provided the + * search starts just after that tree. + */ +smb_tree_t * +smb_tree_lookup_by_fsd( + smb_user_t *user, + fs_desc_t *fsd, + smb_tree_t *tree) +{ + ASSERT(user); + ASSERT(user->u_magic == SMB_USER_MAGIC); + ASSERT(fsd); + + smb_llist_enter(&user->u_tree_list, RW_READER); + + if (tree) { + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + ASSERT(tree->t_user == user); + tree = smb_llist_next(&user->u_tree_list, tree); + } else { + tree = smb_llist_head(&user->u_tree_list); + } + + while (tree) { + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + ASSERT(tree->t_user == user); + if (fsd_cmp(&tree->t_fsd, fsd) == 0) { + mutex_enter(&tree->t_mutex); + switch (tree->t_state) { + case SMB_TREE_STATE_CONNECTED: + /* The tree exists and is still connected. */ + tree->t_refcnt++; + mutex_exit(&tree->t_mutex); + smb_llist_exit(&user->u_tree_list); + return (tree); + case SMB_TREE_STATE_DISCONNECTING: + case SMB_TREE_STATE_DISCONNECTED: + /* + * The tree exists but is diconnected or is in + * the process of being destroyed. + */ + mutex_exit(&tree->t_mutex); + break; + default: + ASSERT(0); + mutex_exit(&tree->t_mutex); + break; + } + } + tree = smb_llist_next(&user->u_tree_list, tree); + } + smb_llist_exit(&user->u_tree_list); + return (NULL); +} + +/* *************************** Static Functions ***************************** */ + +/* + * smb_tree_delete + * + * This function releases all the resources associated with a tree. It also + * removes the tree the caller passes from the list of trees of the user. + * + * The tree to destroy must be in the "destroying state" and the reference count + * must be zero. This function assumes it's single threaded i.e. only one + * thread will attempt to destroy a specific tree (this condition should be met + * if the tree is is the "destroying state" and has a reference count of zero). + * + * Entry: + * tree Tree to destroy + * + * Exit: + * Nothing + * + * Return: + * Nothing + */ +static void +smb_tree_delete(smb_tree_t *tree) +{ + ASSERT(tree); + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED); + ASSERT(tree->t_refcnt == 0); + + /* + * Let's remove the tree from the list of trees of the + * user. This has to be done before any resources + * associated with the tree are released. + */ + smb_llist_enter(&tree->t_user->u_tree_list, RW_WRITER); + smb_llist_remove(&tree->t_user->u_tree_list, tree); + smb_llist_exit(&tree->t_user->u_tree_list); + + tree->t_magic = (uint32_t)~SMB_TREE_MAGIC; + smb_idpool_free(&tree->t_user->u_tid_pool, tree->t_tid); + atomic_dec_32(&tree->t_session->s_tree_cnt); + + if (tree->t_snode) { + smb_node_release(tree->t_snode); + } + mutex_destroy(&tree->t_mutex); + /* + * The list of open files and open directories should be empty. + */ + smb_llist_destructor(&tree->t_ofile_list); + smb_llist_destructor(&tree->t_odir_list); + smb_idpool_destructor(&tree->t_fid_pool); + smb_idpool_destructor(&tree->t_sid_pool); + kmem_cache_free(smb_info.si_cache_tree, tree); +} + +/* + * smb_tree_lookup_head + * + * This function returns the first tree in the list that is in the + * SMB_TREE_STATE_CONNECTED. A reference is taken on the tree and + * smb_tree_release() will have to be called for the tree returned. + * + * Entry: + * lst List of trees (usually the list of trees of a user) + * + * Exit: + * Nothing + * + * Return: + * NULL No tree in the SMB_TREE_STATE_CONNECTED state was found. + * !NULL First tree in the list in the SMB_TREE_STATE_CONNECTED state. + */ +static smb_tree_t * +smb_tree_lookup_head( + smb_llist_t *lst) +{ + smb_tree_t *tree; + + smb_llist_enter(lst, RW_READER); + tree = smb_llist_head(lst); + while (tree) { + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + mutex_enter(&tree->t_mutex); + if (tree->t_state == SMB_TREE_STATE_CONNECTED) { + tree->t_refcnt++; + mutex_exit(&tree->t_mutex); + break; + } else if ((tree->t_state == SMB_TREE_STATE_DISCONNECTING) || + (tree->t_state == SMB_TREE_STATE_DISCONNECTED)) { + mutex_exit(&tree->t_mutex); + tree = smb_llist_next(lst, tree); + } else { + ASSERT(0); + mutex_exit(&tree->t_mutex); + tree = smb_llist_next(lst, tree); + } + } + smb_llist_exit(lst); + + return (tree); +} + +/* + * smb_tree_lookup_next + * + * This function returns the next tree in the list that is in the + * SMB_TREE_STATE_CONNECTED. A reference is taken on the tree and + * smb_tree_release() will have to be called for the tree returned. + * + * Entry: + * lst List of trees (usually the list of trees of a user). + * tree Starting tree. + * + * Exit: + * Nothing + * + * Return: + * NULL No tree in the SMB_TREE_STATE_CONNECTED state was found. + * !NULL Next tree in the list in the SMB_TREE_STATE_CONNECTED state. + */ +static smb_tree_t * +smb_tree_lookup_next( + smb_llist_t *lst, + smb_tree_t *tree) +{ + smb_tree_t *next; + + ASSERT(lst); + ASSERT(tree); + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + ASSERT(tree->t_refcnt); + + smb_llist_enter(lst, RW_READER); + next = smb_llist_next(lst, tree); + while (next) { + ASSERT(next->t_magic == SMB_TREE_MAGIC); + mutex_enter(&next->t_mutex); + if (next->t_state == SMB_TREE_STATE_CONNECTED) { + next->t_refcnt++; + mutex_exit(&next->t_mutex); + break; + } else if ((next->t_state == SMB_TREE_STATE_DISCONNECTING) || + (next->t_state == SMB_TREE_STATE_DISCONNECTED)) { + mutex_exit(&next->t_mutex); + next = smb_llist_next(lst, next); + } else { + ASSERT(0); + mutex_exit(&next->t_mutex); + next = smb_llist_next(lst, next); + } + } + smb_llist_exit(lst); + + return (next); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_tree_connect.c b/usr/src/uts/common/fs/smbsrv/smb_tree_connect.c new file mode 100644 index 000000000000..9f8d15807ea1 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_tree_connect.c @@ -0,0 +1,107 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: tree_connect + * + * When a client connects to a server resource, an SMB_COM_TREE_CONNECT + * message is generated to the server. This command is almost exactly like + * SMB_COM_TREE_CONNECT_ANDX, except that no AndX command may follow; see + * section 4.1.4. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes; min = 4 + * UCHAR BufferFormat1; 0x04 + * STRING Path[]; Server name and share name + * UCHAR BufferFormat2; 0x04 + * STRING Password[]; Password + * UCHAR BufferFormat3; 0x04 + * STRING Service[]; Service name + * + * The CIFS server responds with: + * + * Server Response Description + * ================================ ================================= + * + * UCHAR WordCount; Count of parameter words = 2 + * USHORT MaxBufferSize; Max size message the server handles + * USHORT Tid; Tree ID + * USHORT ByteCount; Count of data bytes = 0 + * + * If the negotiated dialect is MICROSOFT NETWORKS 1.03 or earlier, + * MaxBufferSize in the response message indicates the maximum size message + * that the server can handle. The client should not generate messages, + * nor expect to receive responses, larger than this. This must be + * constant for a given server. For newer dialects, this field is ignored. + * + * Tid should be included in any future SMBs referencing this tree + * connection. + */ + +#include + +int +smb_com_tree_connect(struct smb_request *sr) +{ + /* + * I'm not sure it this should be "%A.sA" + * now that unicode is enabled. + */ + if (smbsr_decode_data(sr, "%AAA", sr, &sr->arg.tcon.path, + &sr->arg.tcon.password, &sr->arg.tcon.service) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->arg.tcon.flags = 0; + + /* + * If the negotiated dialect is MICROSOFT NETWORKS 1.03 + * or earlier, MaxBufferSize in the response message + * indicates the maximum size message that the server can + * handle. The client should not generate messages, nor + * expect to receive responses, larger than this. This + * must be constant for a given server. For newer dialects, + * this field is ignored. + * + * The reason for this is that the maximum buffer size is + * established during the NEGOTIATE. + */ + + (void) smbsr_connect_tree(sr); + + smbsr_encode_result(sr, 2, 0, "bwww", + 2, /* wct */ + (WORD)smb_maxbufsize, /* MaxBufferSize */ + sr->smb_tid, /* TID */ + 0); /* bcc */ + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_tree_connect_andx.c b/usr/src/uts/common/fs/smbsrv/smb_tree_connect_andx.c new file mode 100644 index 000000000000..436ecf906c90 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_tree_connect_andx.c @@ -0,0 +1,213 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: tree_connect_andx + * + * Client Request Description + * ================================= ================================= + * + * UCHAR WordCount; Count of parameter words = 4 + * UCHAR AndXCommand; Secondary (X) command; 0xFF = none + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * USHORT Flags; Additional information + * bit 0 set = disconnect Tid + * USHORT PasswordLength; Length of Password[] + * USHORT ByteCount; Count of data bytes; min = 3 + * UCHAR Password[]; Password + * STRING Path[]; Server name and share name + * STRING Service[]; Service name + * + * The serving machine verifies the combination and returns an error code + * or an identifier. The full name is included in this request message and + * the identifier identifying the connection is returned in the Tid field + * of the SMB header. The Tid field in the client request is ignored. The + * meaning of this identifier (Tid) is server specific; the client must not + * associate any specific meaning to it. + * + * If the negotiated dialect is LANMAN1.0 or later, then it is a protocol + * violation for the client to send this message prior to a successful + * SMB_COM_SESSION_SETUP_ANDX, and the server ignores Password. + * + * If the negotiated dialect is prior to LANMAN1.0 and the client has not + * sent a successful SMB_COM_SESSION_SETUP_ANDX request when the tree + * connect arrives, a user level security mode server must nevertheless + * validate the client's credentials as discussed earlier in this document. + * + * Path follows UNC style syntax, that is to say it is encoded as + * \\server\share and it indicates the name of the resource to which the + * client wishes to connect. + * + * Because Password may be an authentication response, it is a variable + * length field with the length specified by PasswordLength. If + * authentication is not being used, Password should be a null terminated + * ASCII string with PasswordLength set to the string size including the + * terminating null. + * + * The server can enforce whatever policy it desires to govern share + * access. Typically, if the server is paused, administrative privilege is + * required to connect to any share; if the server is not paused, + * administrative privilege is required only for administrative shares (C$, + * etc.). Other such policies may include valid times of day, software + * usage license limits, number of simultaneous server users or share + * users, etc. + * + * The Service component indicates the type of resource the client intends + * to access. Valid values are: + * + * Service Description Earliest Dialect Allowed + * ======== ======================== ================================ + * + * A: disk share PC NETWORK PROGRAM 1.0 + * LPT1: printer PC NETWORK PROGRAM 1.0 + * IPC named pipe MICROSOFT NETWORKS 3.0 + * COMM communications device MICROSOFT NETWORKS 3.0 + * ????? any type of device MICROSOFT NETWORKS 3.0 + * + * If bit0 of Flags is set, the tree connection to Tid in the SMB header + * should be disconnected. If this tree disconnect fails, the error should + * be ignored. + * + * If the negotiated dialect is earlier than DOS LANMAN2.1, the response to + * this SMB is: + * + * Server Response Description + * ================================ =================================== + * + * UCHAR WordCount; Count of parameter words = 2 + * UCHAR AndXCommand; Secondary (X) command; 0xFF = none + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * USHORT ByteCount; Count of data bytes; min = 3 + * + * If the negotiated is DOS LANMAN2.1 or later, the response to this SMB + * is: + * + * Server Response Description + * ================================ =================================== + * + * UCHAR WordCount; Count of parameter words = 3 + * UCHAR AndXCommand; Secondary (X) command; 0xFF = none + * UCHAR AndXReserved; Reserved (must be 0) + * USHORT AndXOffset; Offset to next command WordCount + * USHORT OptionalSupport; Optional support bits + * USHORT ByteCount; Count of data bytes; min = 3 + * UCHAR Service[]; Service type connected to. Always + * ANSII. + * STRING NativeFileSystem[]; Native file system for this tree + * + * NativeFileSystem is the name of the filesystem; values to be expected + * include FAT, NTFS, etc. + * + * OptionalSupport bits has the encoding: + * + * Name Encoding Description + * ============================= ========= ========================== + * + * SMB_SUPPORT_SEARCH_BITS 0x0001 + * + * SMB_SHARE_IS_IN_DFS 0x0002 + * + * Some servers negotiate "DOS LANMAN2.1" dialect or later and still send + * the "downlevel" (i.e. wordcount==2) response. Valid AndX following + * commands are + * + * SMB_COM_OPEN SMB_COM_OPEN_ANDX SMB_COM_CREATE + * SMB_COM_CREATE_NEW SMB_COM_CREATE_DIRECTORY SMB_COM_DELETE + * SMB_COM_DELETE_DIRECTORY SMB_COM_FIND SMB_COM_COPY + * SMB_COM_FIND_UNIQUE SMB_COM_RENAME + * SMB_COM_CHECK_DIRECTORY SMB_COM_QUERY_INFORMATION + * SMB_COM_GET_PRINT_QUEUE SMB_COM_OPEN_PRINT_FILE + * SMB_COM_TRANSACTION SMB_COM_NO_ANDX_CMD + * SMB_COM_SET_INFORMATION SMB_COM_NT_RENAME + * + * 4.1.4.1 Errors + * + * ERRDOS/ERRnomem + * ERRDOS/ERRbadpath + * + * ERRDOS/ERRinvdevice + * ERRSRV/ERRaccess + * ERRSRV/ERRbadpw + * ERRSRV/ERRinvnetname + */ + +#include + +int +smb_com_tree_connect_andx(struct smb_request *sr) +{ + unsigned char *pwbuf = NULL; + unsigned short pwlen = 0; + int rc; + + rc = smbsr_decode_vwv(sr, "b.www", &sr->andx_com, &sr->andx_off, + &sr->arg.tcon.flags, &pwlen); + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + if (pwlen != 0) { + pwbuf = (unsigned char *)smbsr_malloc(&sr->request_storage, + pwlen); + bzero(pwbuf, pwlen); + } + + if (smbsr_decode_data(sr, "%#cus", sr, pwlen, pwbuf, + &sr->arg.tcon.path, &sr->arg.tcon.service) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->arg.tcon.pwdlen = pwlen; + sr->arg.tcon.password = (char *)pwbuf; + + (void) smbsr_connect_tree(sr); + + if (sr->session->dialect < NT_LM_0_12) { + smbsr_encode_result(sr, 2, VAR_BCC, "bb.wwss", + (char)2, /* wct */ + sr->andx_com, + VAR_BCC, + VAR_BCC, + sr->arg.tcon.service, + sr->tid_tree->t_typename); + } else { + smbsr_encode_result(sr, 3, VAR_BCC, "bb.wwws%u", + (char)3, /* wct */ + sr->andx_com, + (short)64, + (short)SMB_TREE_SUPPORT_SEARCH_BITS, + VAR_BCC, + sr->arg.tcon.service, + sr, + sr->tid_tree->t_typename); + } + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_tree_disconnect.c b/usr/src/uts/common/fs/smbsrv/smb_tree_disconnect.c new file mode 100644 index 000000000000..9139930a4d99 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_tree_disconnect.c @@ -0,0 +1,110 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: tree_disconnect + * + * This message informs the server that the client no longer wishes to + * access the resource connected to with a prior SMB_COM_TREE_CONNECT or + * SMB_COM_TREE_CONNECT_ANDX. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes = 0 + * + * The resource sharing connection identified by Tid in the SMB header is + * logically disconnected from the server. Tid is invalidated; it will not + * be recognized if used by the client for subsequent requests. All locks, + * open files, etc. created on behalf of Tid are released. + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes = 0 + * + * 4.1.5.1 Errors + * + * ERRSRV/ERRinvnid + * ERRSRV/ERRbaduid + */ + +#include + +/* + * Function: int smb_com_tree_disconnect(struct smb_request *) + * + * Please note the SDDF_SUPPRESS_UID is set for this operation; + * therefore, the uid_user field in sr is invalid. Do not use it + * or the system would panic. + * + * Please also note that in some cases, the client would not send + * tree disconnect call. An example of that is, the return of invalid + * uid for a client request i.e. read_andx, when the used has logged + * off. This will cause a minor memory leak for the share and some + * files would remain open. When the session is destroyed, the leaked + * and remained open files will be freed/closed. We will need to + * address this problem by re-architecting user/tree structures. + * For the time being, we will leave it till we have time. + */ + +int +smb_com_tree_disconnect(struct smb_request *sr) +{ + /* + * A Tree Disconnect request requires a valid user ID as well as a + * valid tree ID. However, some clients logoff a user and then try to + * disconnect the trees connected using the user they just logged off. + * There's a problem with that behavior and the tree representation + * of the different contexts (session, user, tree, file...). In order + * to find a tree a valid user has to be provided. This means, with + * the behavior described above, a client would receive a negative + * response to the TreeDisconnect request with an error code saying + * ERRbaduid. That response breaks some clients. To prevent that + * from happening, the dispatch table indicates that, for the + * TreeDisconnect request, the UID and the TID shouldn't be looked up + * in the dispatch routine. The lookup is done here. If the user or + * the tree cannot be identified a negative response is sent back with + * the error code ERRinvnid. + */ + sr->uid_user = smb_user_lookup_by_uid(sr->session, &sr->user_cr, + sr->smb_uid); + if (sr->uid_user != NULL) + sr->tid_tree = smb_tree_lookup_by_tid(sr->uid_user, + sr->smb_tid); + + if (sr->uid_user == NULL || sr->tid_tree == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRinvnid); + } + smbsr_rq_notify(sr, sr->session, sr->tid_tree); + smb_tree_disconnect(sr->tid_tree); + smbsr_encode_empty_result(sr); + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_unlock_byte_range.c b/usr/src/uts/common/fs/smbsrv/smb_unlock_byte_range.c new file mode 100644 index 000000000000..420c26a25082 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_unlock_byte_range.c @@ -0,0 +1,87 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: unlock_byte_range + * + * This message is sent to unlock the given byte range. Offset, Count, and + * Pid must be identical to that specified in a prior successful lock. If + * + * an unlock references an address range that is not locked, no error is + * generated. + * + * Since Offset is a 32 bit quantity, this request is inappropriate for + * general locking within a very large file. + * + * Client Request Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 5 + * USHORT Fid; File handle + * ULONG Count; Count of bytes to unlock + * ULONG Offset; Offset from start of file + * USHORT ByteCount; Count of data bytes = 0 + * + * Server Response Description + * ================================== ================================= + * + * UCHAR WordCount; Count of parameter words = 0 + * USHORT ByteCount; Count of data bytes = 0 + */ + +#include + +int +smb_com_unlock_byte_range(struct smb_request *sr) +{ + uint32_t Length; + uint32_t Offset; + DWORD result; + + if (smbsr_decode_vwv(sr, "wll", &sr->smb_fid, &Length, &Offset) != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + result = smb_unlock_range(sr, sr->fid_ofile->f_node, + (off_t)Offset, (uint64_t)Length); + if (result != NT_STATUS_SUCCESS) { + smb_unlock_range_raise_error(sr, result); + /* NOT REACHED */ + } + + smbsr_encode_empty_result(sr); + + return (SDRC_NORMAL_REPLY); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_upcalls.c b/usr/src/uts/common/fs/smbsrv/smb_upcalls.c new file mode 100644 index 000000000000..a421640b1b74 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_upcalls.c @@ -0,0 +1,104 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include + + +void +smb_user_nonauth_logon(uint32_t audit_sid) +{ + char *arg, *rsp; + size_t arg_size, rsp_size; + + arg = smb_kdr_encode_common(SMB_DR_USER_NONAUTH_LOGON, + &audit_sid, xdr_uint32_t, &arg_size); + + if (arg != NULL) { + rsp = smb_kdoor_clnt_upcall(arg, arg_size, NULL, 0, &rsp_size); + smb_kdoor_clnt_free(arg, arg_size, rsp, rsp_size); + } +} + +void +smb_user_auth_logoff(uint32_t audit_sid) +{ + char *arg, *rsp; + size_t arg_size, rsp_size; + + arg = smb_kdr_encode_common(SMB_DR_USER_AUTH_LOGOFF, + &audit_sid, xdr_uint32_t, &arg_size); + + if (arg != NULL) { + rsp = smb_kdoor_clnt_upcall(arg, arg_size, NULL, 0, &rsp_size); + smb_kdoor_clnt_free(arg, arg_size, rsp, rsp_size); + } +} + +smb_token_t * +smb_upcall_get_token(netr_client_t *clnt_info) +{ + char *argp, *rbufp; + size_t arg_size, rbuf_size; + smb_token_t *token = NULL; + + argp = smb_dr_encode_arg_get_token(clnt_info, &arg_size); + rbufp = smb_kdoor_clnt_upcall(argp, arg_size, NULL, 0, &rbuf_size); + if (rbufp) + token = smb_dr_decode_res_token(rbufp + SMB_DR_DATA_OFFSET, + rbuf_size - SMB_DR_DATA_OFFSET); + + smb_kdoor_clnt_free(argp, arg_size, rbufp, rbuf_size); + return (token); + +} + +int +smb_upcall_set_dwncall_desc(uint32_t opcode, door_desc_t *dp, uint_t n_desc) +{ + char *argp, *rbufp; + size_t arg_size, rbuf_size; + + argp = smb_dr_set_opcode(opcode, &arg_size); + if (argp == NULL) { + return (SMB_DR_OP_ERR_ENCODE); + } + + rbufp = smb_kdoor_clnt_upcall(argp, arg_size, dp, n_desc, &rbuf_size); + if (rbufp == NULL) { + return (SMB_DR_OP_ERR); + } + + smb_kdoor_clnt_free(argp, arg_size, rbufp, rbuf_size); + + return (SMB_DR_OP_SUCCESS); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_user.c b/usr/src/uts/common/fs/smbsrv/smb_user.c new file mode 100644 index 000000000000..bfea00768ec8 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_user.c @@ -0,0 +1,619 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * General Structures Layout + * ------------------------- + * + * This is a simplified diagram showing the relationship between most of the + * main structures. + * + * +-------------------+ + * | SMB_INFO | + * +-------------------+ + * | + * | + * v + * +-------------------+ +-------------------+ +-------------------+ + * | SESSION |<----->| SESSION |......| SESSION | + * +-------------------+ +-------------------+ +-------------------+ + * | + * | + * v + * +-------------------+ +-------------------+ +-------------------+ + * | USER |<----->| USER |......| USER | + * +-------------------+ +-------------------+ +-------------------+ + * | + * | + * v + * +-------------------+ +-------------------+ +-------------------+ + * | TREE |<----->| TREE |......| TREE | + * +-------------------+ +-------------------+ +-------------------+ + * | | + * | | + * | v + * | +-------+ +-------+ +-------+ + * | | OFILE |<----->| OFILE |......| OFILE | + * | +-------+ +-------+ +-------+ + * | + * | + * v + * +-------+ +------+ +------+ + * | ODIR |<----->| ODIR |......| ODIR | + * +-------+ +------+ +------+ + * + * + * User State Machine + * ------------------ + * + * +-----------------------------+ T0 + * | SMB_USER_STATE_LOGGED_IN |<----------- Creation/Allocation + * +-----------------------------+ + * | + * | T1 + * | + * v + * +-----------------------------+ + * | SMB_USER_STATE_LOGGING_OFF | + * +-----------------------------+ + * | + * | T2 + * | + * v + * +-----------------------------+ T3 + * | SMB_USER_STATE_LOGGED_OFF |----------> Deletion/Free + * +-----------------------------+ + * + * SMB_USER_STATE_LOGGED_IN + * + * While in this state: + * - The user is queued in the list of users of his session. + * - References will be given out if the user is looked up. + * - The user can access files and pipes. + * + * SMB_USER_STATE_LOGGING_OFF + * + * While in this state: + * - The user is queued in the list of users of his session. + * - References will not be given out if the user is looked up. + * - The trees the user connected are being disconnected. + * - The resources associated with the user remain. + * + * SMB_USER_STATE_LOGGING_OFF + * + * While in this state: + * - The user is queued in the list of users of his session. + * - References will not be given out if the user is looked up. + * - The user has no more trees connected. + * - The resources associated with the user remain. + * + * Transition T0 + * + * This transition occurs in smb_user_login(). A new user is created and + * added to the list of users of a session. + * + * Transition T1 + * + * This transition occurs in smb_user_logoff(). + * + * Transition T2 + * + * This transition occurs in smb_user_release(). The resources associated + * with the user are deleted as well as the user. For the transition to + * occur, the user must be in the SMB_USER_STATE_LOGGED_OFF state and the + * reference count be zero. + * + * Comments + * -------- + * + * The state machine of the user structures is controlled by 3 elements: + * - The list of users of the session he belongs to. + * - The mutex embedded in the structure itself. + * - The reference count. + * + * There's a mutex embedded in the user structure used to protect its fields + * and there's a lock embedded in the list of users of a session. To + * increment or to decrement the reference count the mutex must be entered. + * To insert the user into the list of users of the session and to remove + * the user from it, the lock must be entered in RW_WRITER mode. + * + * Rules of access to a user structure: + * + * 1) In order to avoid deadlocks, when both (mutex and lock of the session + * list) have to be entered, the lock must be entered first. + * + * 2) All actions applied to a user require a reference count. + * + * 3) There are 2 ways of getting a reference count. One is when the user + * logs in. The other when the user is looked up. This translates into + * 3 functions: smb_user_login(), smb_user_lookup_by_uid() and + * smb_user_lookup_by_credentials. + * + * It should be noted that the reference count of a user registers the + * number of references to the user in other structures (such as an smb + * request). The reference count is not incremented in these 2 instances: + * + * 1) The user is logged in. An user is anchored by his state. If there's + * no activity involving a user currently logged in, the reference + * count of that user is zero. + * + * 2) The user is queued in the list of users of the session. The fact of + * being queued in that list is NOT registered by incrementing the + * reference count. + */ +#include +#include + +/* Static functions defined further down this file. */ +static void smb_user_delete(smb_user_t *user); + +/* + * smb_user_login + * + * + */ +smb_user_t * +smb_user_login( + smb_session_t *session, + cred_t *cr, + char *domain_name, + char *account_name, + uint32_t flags, + uint32_t privileges, + uint32_t audit_sid) +{ + smb_user_t *user; + + ASSERT(session); + ASSERT(session->s_magic == SMB_SESSION_MAGIC); + ASSERT(cr); + ASSERT(account_name); + ASSERT(domain_name); + + user = kmem_cache_alloc(smb_info.si_cache_user, KM_SLEEP); + bzero(user, sizeof (smb_user_t)); + user->u_refcnt = 1; + user->u_session = session; + user->u_logon_time = gethrestime_sec(); + user->u_flags = flags; + user->u_privileges = privileges; + user->u_name_len = strlen(account_name) + 1; + user->u_domain_len = strlen(domain_name) + 1; + user->u_name = smb_kstrdup(account_name, user->u_name_len); + user->u_domain = smb_kstrdup(domain_name, user->u_domain_len); + user->u_cred = cr; + user->u_audit_sid = audit_sid; + + if (!smb_idpool_alloc(&session->s_uid_pool, &user->u_uid)) { + if (!smb_idpool_constructor(&user->u_tid_pool)) { + smb_llist_constructor(&user->u_tree_list, + sizeof (smb_tree_t), offsetof(smb_tree_t, t_lnd)); + mutex_init(&user->u_mutex, NULL, MUTEX_DEFAULT, NULL); + crhold(cr); + user->u_state = SMB_USER_STATE_LOGGED_IN; + user->u_magic = SMB_USER_MAGIC; + smb_llist_enter(&session->s_user_list, RW_WRITER); + smb_llist_insert_tail(&session->s_user_list, user); + smb_llist_exit(&session->s_user_list); + atomic_inc_32(&smb_info.open_users); + return (user); + } + smb_idpool_free(&session->s_uid_pool, user->u_uid); + } + kmem_free(user->u_name, (size_t)user->u_name_len); + kmem_free(user->u_domain, (size_t)user->u_domain_len); + kmem_cache_free(smb_info.si_cache_user, user); + return (NULL); +} + +/* + * Create a new user based on an existing user, used to support + * additional SessionSetupX requests for a user on a session. + * + * Assumes the caller has a reference on the original user from + * a user_lookup_by_x call. + */ +smb_user_t * +smb_user_dup( + smb_user_t *orig_user) +{ + smb_user_t *user; + + ASSERT(orig_user->u_magic == SMB_USER_MAGIC); + ASSERT(orig_user->u_refcnt); + + user = smb_user_login(orig_user->u_session, orig_user->u_cred, + orig_user->u_domain, orig_user->u_name, orig_user->u_flags, + orig_user->u_privileges, orig_user->u_audit_sid); + + if (user) + smb_user_nonauth_logon(orig_user->u_audit_sid); + + return (user); +} + +/* + * smb_user_logoff + * + * + */ +void +smb_user_logoff( + smb_user_t *user) +{ + ASSERT(user->u_magic == SMB_USER_MAGIC); + + mutex_enter(&user->u_mutex); + ASSERT(user->u_refcnt); + switch (user->u_state) { + case SMB_USER_STATE_LOGGED_IN: { + /* + * The user is moved into a state indicating that the log off + * process has started. + */ + user->u_state = SMB_USER_STATE_LOGGING_OFF; + mutex_exit(&user->u_mutex); + atomic_dec_32(&smb_info.open_users); + /* + * All the trees hanging off of this user are disconnected. + */ + smb_tree_disconnect_all(user); + smb_user_auth_logoff(user->u_audit_sid); + mutex_enter(&user->u_mutex); + user->u_state = SMB_USER_STATE_LOGGED_OFF; + break; + } + case SMB_USER_STATE_LOGGED_OFF: + case SMB_USER_STATE_LOGGING_OFF: + break; + + default: + ASSERT(0); + break; + } + mutex_exit(&user->u_mutex); +} + +/* + * smb_user_logoff_all + * + * + */ +void +smb_user_logoff_all( + smb_session_t *session) +{ + smb_user_t *user; + + ASSERT(session); + ASSERT(session->s_magic == SMB_SESSION_MAGIC); + + smb_llist_enter(&session->s_user_list, RW_READER); + user = smb_llist_head(&session->s_user_list); + while (user) { + ASSERT(user->u_magic == SMB_USER_MAGIC); + ASSERT(user->u_session == session); + mutex_enter(&user->u_mutex); + switch (user->u_state) { + case SMB_USER_STATE_LOGGED_IN: + /* The user is still logged in. */ + user->u_refcnt++; + mutex_exit(&user->u_mutex); + smb_llist_exit(&session->s_user_list); + smb_user_logoff(user); + smb_user_release(user); + smb_llist_enter(&session->s_user_list, RW_READER); + user = smb_llist_head(&session->s_user_list); + break; + case SMB_USER_STATE_LOGGING_OFF: + case SMB_USER_STATE_LOGGED_OFF: + /* + * The user is logged off or logging off. + */ + mutex_exit(&user->u_mutex); + user = smb_llist_next(&session->s_user_list, user); + break; + default: + ASSERT(0); + mutex_exit(&user->u_mutex); + user = smb_llist_next(&session->s_user_list, user); + break; + } + } + smb_llist_exit(&session->s_user_list); +} + +/* + * smb_user_release + * + * + */ +void +smb_user_release( + smb_user_t *user) +{ + ASSERT(user->u_magic == SMB_USER_MAGIC); + + mutex_enter(&user->u_mutex); + ASSERT(user->u_refcnt); + user->u_refcnt--; + switch (user->u_state) { + case SMB_USER_STATE_LOGGED_OFF: + if (user->u_refcnt == 0) { + mutex_exit(&user->u_mutex); + smb_user_delete(user); + return; + } + break; + + case SMB_USER_STATE_LOGGED_IN: + case SMB_USER_STATE_LOGGING_OFF: + break; + + default: + ASSERT(0); + break; + } + mutex_exit(&user->u_mutex); +} + +/* + * smb_user_lookup_by_uid + * + * Find the appropriate user for this request. The request credentials + * set here may be overridden by the tree credentials. In domain mode, + * the user and tree credentials should be the same. In share mode, the + * tree credentials (defined in the share definition) should override + * the user credentials. + */ +smb_user_t * +smb_user_lookup_by_uid( + smb_session_t *session, + cred_t **cr, + uint16_t uid) +{ + smb_user_t *user; + + ASSERT(session); + ASSERT(session->s_magic == SMB_SESSION_MAGIC); + ASSERT(cr); + + smb_llist_enter(&session->s_user_list, RW_READER); + user = smb_llist_head(&session->s_user_list); + while (user) { + ASSERT(user->u_magic == SMB_USER_MAGIC); + ASSERT(user->u_session == session); + if (user->u_uid == uid) { + mutex_enter(&user->u_mutex); + switch (user->u_state) { + + case SMB_USER_STATE_LOGGED_IN: + /* The user exists and is still logged in. */ + *cr = user->u_cred; + user->u_refcnt++; + mutex_exit(&user->u_mutex); + smb_llist_exit(&session->s_user_list); + return (user); + + case SMB_USER_STATE_LOGGING_OFF: + case SMB_USER_STATE_LOGGED_OFF: + /* + * The user exists but has logged off or is in + * the process of logging off. + */ + mutex_exit(&user->u_mutex); + smb_llist_exit(&session->s_user_list); + return (NULL); + + default: + ASSERT(0); + mutex_exit(&user->u_mutex); + smb_llist_exit(&session->s_user_list); + return (NULL); + } + } + user = smb_llist_next(&session->s_user_list, user); + } + smb_llist_exit(&session->s_user_list); + return (NULL); +} + +/* + * smb_user_lookup_by_name + */ +smb_user_t * +smb_user_lookup_by_name(smb_session_t *session, char *domain, char *name) +{ + smb_user_t *user; + smb_llist_t *ulist; + + ulist = &session->s_user_list; + smb_llist_enter(ulist, RW_READER); + user = smb_llist_head(ulist); + while (user) { + ASSERT(user->u_magic == SMB_USER_MAGIC); + if (!utf8_strcasecmp(user->u_name, name) && + !utf8_strcasecmp(user->u_domain, domain)) { + mutex_enter(&user->u_mutex); + if (user->u_state == SMB_USER_STATE_LOGGED_IN) { + user->u_refcnt++; + mutex_exit(&user->u_mutex); + break; + } + mutex_exit(&user->u_mutex); + } + user = smb_llist_next(ulist, user); + } + smb_llist_exit(ulist); + + return (user); +} + +/* + * smb_user_lookup_by_state + * + * This function returns the first user in the logged in state. If the user + * provided is NULL the search starts from the beginning of the list passed + * in. It a user is provided the search starts just after that user. + */ +smb_user_t * +smb_user_lookup_by_state( + smb_session_t *session, + smb_user_t *user) +{ + smb_llist_t *lst; + smb_user_t *next; + + ASSERT(session); + ASSERT(session->s_magic == SMB_SESSION_MAGIC); + + lst = &session->s_user_list; + + smb_llist_enter(lst, RW_READER); + if (user) { + ASSERT(user); + ASSERT(user->u_magic == SMB_USER_MAGIC); + ASSERT(user->u_refcnt); + next = smb_llist_next(lst, user); + } else { + next = smb_llist_head(lst); + } + while (next) { + ASSERT(next->u_magic == SMB_USER_MAGIC); + ASSERT(next->u_session == session); + mutex_enter(&next->u_mutex); + if (next->u_state == SMB_USER_STATE_LOGGED_IN) { + next->u_refcnt++; + mutex_exit(&next->u_mutex); + break; + } else { + ASSERT((next->u_state == SMB_USER_STATE_LOGGING_OFF) || + (next->u_state == SMB_USER_STATE_LOGGED_OFF)); + mutex_exit(&next->u_mutex); + next = smb_llist_next(lst, next); + } + } + smb_llist_exit(lst); + + return (next); +} + +/* + * smb_user_disconnect_share + * + * This function disconnects all the trees that have the sharename passed in. + */ +void +smb_user_disconnect_share( + smb_user_t *user, + char *sharename) +{ + smb_tree_t *tree; + smb_tree_t *next; + + ASSERT(user); + ASSERT(user->u_magic == SMB_USER_MAGIC); + ASSERT(user->u_refcnt); + + tree = smb_tree_lookup_by_name(user, sharename, NULL); + while (tree) { + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + smb_tree_disconnect(tree); + smbsr_rq_notify(NULL, user->u_session, tree); + next = smb_tree_lookup_by_name(user, sharename, + tree); + smb_tree_release(tree); + tree = next; + } +} + +/* + * smb_user_disconnect_share + * + * This function disconnects all the trees that match fsd passed in. + */ +void +smb_user_disconnect_volume( + smb_user_t *user, + fs_desc_t *fsd) +{ + smb_tree_t *tree; + smb_tree_t *next; + + ASSERT(user); + ASSERT(user->u_magic == SMB_USER_MAGIC); + ASSERT(user->u_refcnt); + + tree = smb_tree_lookup_by_fsd(user, fsd, NULL); + while (tree) { + ASSERT(tree->t_magic == SMB_TREE_MAGIC); + smb_tree_disconnect(tree); + smbsr_rq_notify(NULL, user->u_session, tree); + next = smb_tree_lookup_by_fsd(user, fsd, tree); + smb_tree_release(tree); + tree = next; + } +} + +/* *************************** Static Functions ***************************** */ + +/* + * smb_user_delete + * + * + */ +static void +smb_user_delete( + smb_user_t *user) +{ + smb_session_t *session; + + ASSERT(user); + ASSERT(user->u_magic == SMB_USER_MAGIC); + ASSERT(user->u_refcnt == 0); + ASSERT(user->u_state == SMB_USER_STATE_LOGGED_OFF); + + session = user->u_session; + /* + * Let's remove the user from the list of users of the session. This + * has to be done before any resources associated with the user are + * deleted. + */ + smb_llist_enter(&session->s_user_list, RW_WRITER); + smb_llist_remove(&session->s_user_list, user); + smb_llist_exit(&session->s_user_list); + + user->u_magic = (uint32_t)~SMB_USER_MAGIC; + mutex_destroy(&user->u_mutex); + smb_llist_destructor(&user->u_tree_list); + smb_idpool_destructor(&user->u_tid_pool); + smb_idpool_free(&session->s_uid_pool, user->u_uid); + crfree(user->u_cred); + kmem_free(user->u_name, (size_t)user->u_name_len); + kmem_free(user->u_domain, (size_t)user->u_domain_len); + kmem_cache_free(smb_info.si_cache_user, user); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_util.c b/usr/src/uts/common/fs/smbsrv/smb_util.c new file mode 100644 index 000000000000..beb39a6e51cb --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_util.c @@ -0,0 +1,2172 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#ifdef DEBUG +uint_t smb_tsd_key; +#endif + +static boolean_t +smb_thread_continue_timedwait_locked(smb_thread_t *thread, int ticks); + +time_t tzh_leapcnt = 0; + +struct tm +*smb_gmtime_r(time_t *clock, struct tm *result); + +time_t +smb_timegm(struct tm *tm); + +struct tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +static int days_in_month[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +int +smb_ascii_or_unicode_strlen(struct smb_request *sr, char *str) +{ + if (sr->smb_flg2 & SMB_FLAGS2_UNICODE) + return (mts_wcequiv_strlen(str)); + return (strlen(str)); +} + +int +smb_ascii_or_unicode_strlen_null(struct smb_request *sr, char *str) +{ + if (sr->smb_flg2 & SMB_FLAGS2_UNICODE) + return (mts_wcequiv_strlen(str) + 2); + return (strlen(str) + 1); +} + +int +smb_ascii_or_unicode_null_len(struct smb_request *sr) +{ + if (sr->smb_flg2 & SMB_FLAGS2_UNICODE) + return (2); + return (1); +} + +void +smb_set_gmtoff(uint32_t goff) +{ + (void) atomic_swap_32(&smb_info.si_gmtoff, goff); +} + +uint32_t +smb_get_gmtoff(void) +{ + return (atomic_or_32_nv(&smb_info.si_gmtoff, 0)); +} + +uint32_t +smb_gmt_to_local_time(uint32_t gmt) +{ + return (gmt + smb_get_gmtoff()); +} + +uint32_t +smb_local_time_to_gmt(uint32_t local) +{ + return (local - smb_get_gmtoff()); +} + + +int +smb_component_match( + struct smb_request *sr, + ino64_t fileid, + struct smb_odir *od, + smb_odir_context_t *pc) +{ + int ci = (fsd_chkcap(&sr->tid_tree->t_fsd, FSOLF_CASE_INSENSITIVE) > 0); + + int ignore_case = (ci || (SMB_TREE_CASE_INSENSITIVE(sr))); + + return (smb_match_name(fileid, pc->dc_name, pc->dc_shortname, + pc->dc_name83, od->d_pattern, ignore_case)); +} + +int +smb_convert_unicode_wildcards(char *path) +{ + int wildcards = 0; + char *ptr = path; + char nch; + + /* + * Special case "<" for "dir *." + */ + if (strcmp(path, "<") == 0) { + return (1); + } + while (*ptr) { + nch = *(ptr + 1); + switch (*ptr) { + case '*' : /* Count non-unicode wildcards while we're at it */ + case '?' : + wildcards++; + break; + case '<' : + if (nch == '.') { + *(ptr++) = '*'; + wildcards++; + } + break; + case '>' : + *ptr = '?'; + wildcards++; + break; + case '\"' : + *ptr = '.'; + break; + } + ptr++; + } + /* NT DOS wildcards... */ + if (strcmp(path, "????????.???") == 0) { + (void) strcpy(path, "*"); + } else if (strncmp(path, "????????.", 9) == 0) { + *path = '*'; + (void) strcpy(path+1, path+8); + } + + return (wildcards); +} + + + +/* + * smb_mode_to_dos_attributes + * + * This function converts unix mode from smb_attr_t structure to dos attr. + * + * The reason dos_attr is returned as uint32_t, unlike sattr as + * unsigned short is the smb_trans_find_first2/next encodes dattr in + * BOTH DIR info as long. + */ +uint32_t +smb_mode_to_dos_attributes(smb_attr_t *ap) +{ + uint32_t dos_attr = 0; + + dos_attr = ap->sa_dosattr; + if (dos_attr == 0) + dos_attr = SMB_FA_NORMAL; + + return (dos_attr); +} + + + +/* + * smb_sattr_check + * + * This function checks if the file has the attributes indicated by + * the search attribute, "sattr". The normal files, which includes + * FSA_READONLY and FSA_ARCHIVE, should always pass the check. If the + * special attributes: SMB_FA_DIRECTORY, SMB_FA_HIDDEN or + * SMB_FA_SYSTEM are set, then the special mode FSA_DIR, FSA_HIDDEN, + * and FSA_SYSTEM will also pass accordingly. The following + * examples will show how this works: + * + * fileA: FSA_READONLY + * fileB: 0 (no attributes = normal file) + * fileC: FSA_READONLY, FSA_ARCHIVE + * fileD: FSA_HIDDEN + * fileE: FSA_READONLY, FSA_HIDDEN, FSA_SYSTEM + * dirA: FSA_DIRECTORY + * + * *search attribute: 0 + * Returns: fileA, fileB and fileC. + * *search attribute: SMB_FA_HIDDEN + * Returns: fileA, fileB, fileC and fileD. + * *search attribute: SMB_FA_SYSTEM + * Returns: fileA, fileB and fileC. + * *search attribute: SMB_FA_DIRECTORY + * Returns: fileA, fileB, fileC and dirA. + * *search attribute: SMB_FA_HIDDEN and SMB_FA_SYSTEM + * Returns: fileA, fileB, fileC, fileD and fileE. + * + * As you can see, the special attributes are inclusive, which means the + * files that has all their special attributes included in the search + * attribute and normal files will be returned. The FSA_READONLY and + * FSA_ARCHIVE attributes are completely ignored since they are being + * treated as normal file. + * + * If check passed, 1 is returned; otherwise, 0 is returned. + */ +int +smb_sattr_check(smb_attr_t *ap, char *name, unsigned short sattr) +{ + if (name) { + if (is_dot_or_dotdot(name) && !(sattr & SMB_FA_HIDDEN)) + return (0); + } + + /* + * The FSA_READONLY and FSA_ARCHIVE bits are being treated + * as normal file; therefore, they are ignored. + */ + + if ((ap->sa_vattr.va_type == VDIR) && !(sattr & SMB_FA_DIRECTORY)) + return (0); + + if ((ap->sa_dosattr & FILE_ATTRIBUTE_HIDDEN) && + !(sattr & SMB_FA_HIDDEN)) + return (0); + + if ((ap->sa_dosattr & FILE_ATTRIBUTE_SYSTEM) && + !(sattr & SMB_FA_SYSTEM)) + return (0); + + return (1); +} + + +/* + * smb_stream_parse_name + * + * calling function is responsible for passing valid buffers with + * adequate sizes. + * + * path is a NULL terminated string which could be a + * stream path. If it's a stream path it could be + * in one of the following formats: + * . path:stream + * . path:stream:$DATA + * unnamed stream is part of the path and there is + * exactly one ':' in between the unamed and name + * streams + * + * u_stream_name will contain the unamed stream portion upon + * successful return. + * this is the portion between last '\' and + * the first ':' + * + * stream_name will contain the named stream portion upon + * successful return. + * this is the portion between the first ':' and the + * end of the 'name' string. + * + * '::' - is a non-stream and is commonly used by Windows to designate + * the unamed stream in the form "::$DATA" + * + * on return the named stream always has a ":$DATA" appended if there + * isn't one already + * + * Return Codes: + * + * 0 - given path doesn't contain any streams + * 1 - given path had a stream + */ +int +smb_stream_parse_name(char *path, char *u_stream_name, + char *stream_name) +{ + char *colonp; + char *slashp; + + if (path == 0) + return (0); + + /* + * if there is no colon in the path or it's the last char + * then it's not a stream name + */ + colonp = strchr(path, ':'); + if ((colonp == 0) || (*(colonp+1) == 0)) + return (0); + + /* "::" always means the unamed stream */ + if (strstr(path, "::")) + return (0); + + if (stream_name) { + /* + * stream name is the portion between ':' and the + * end of 'path' string (including the starting ':') + */ + (void) strcpy(stream_name, colonp); + + if (strstr(stream_name, ":$DATA") == 0) + (void) strcat(stream_name, ":$DATA"); + } + + if (u_stream_name) { + /* + * uname stream is the portion between last '\' + * and the ':' + */ + slashp = strrchr(path, '\\'); + slashp = (slashp == 0) ? path : slashp + 1; + /*LINTED E_PTRDIFF_OVERFLOW*/ + (void) strlcpy(u_stream_name, slashp, colonp - slashp + 1); + } + return (1); +} + +int +microtime(timestruc_t *tvp) +{ + tvp->tv_sec = gethrestime_sec(); + tvp->tv_nsec = 0; + return (0); +} + +int32_t +clock_get_milli_uptime() +{ + return (TICK_TO_MSEC(lbolt)); +} + +int /*ARGSUSED*/ +smb_noop(void *p, size_t size, int foo) +{ + return (0); +} + +/* + * smb_idpool_increment + * + * This function increments the ID pool by doubling the current size. This + * function assumes the caller entered the mutex of the pool. + */ +static int +smb_idpool_increment( + smb_idpool_t *pool) +{ + uint8_t *new_pool; + uint32_t new_size; + + ASSERT(pool->id_magic == SMB_IDPOOL_MAGIC); + + new_size = pool->id_size * 2; + if (new_size <= SMB_IDPOOL_MAX_SIZE) { + new_pool = kmem_alloc(new_size / 8, KM_NOSLEEP); + if (new_pool) { + bzero(new_pool, new_size / 8); + bcopy(pool->id_pool, new_pool, pool->id_size / 8); + kmem_free(pool->id_pool, pool->id_size / 8); + pool->id_pool = new_pool; + pool->id_free_counter += new_size - pool->id_size; + pool->id_max_free_counter += new_size - pool->id_size; + pool->id_size = new_size; + pool->id_idx_msk = (new_size / 8) - 1; + if (new_size >= SMB_IDPOOL_MAX_SIZE) { + /* id -1 made unavailable */ + pool->id_pool[pool->id_idx_msk] = 0x80; + pool->id_free_counter--; + pool->id_max_free_counter--; + } + return (0); + } + } + return (-1); +} + +/* + * smb_idpool_constructor + * + * This function initializes the pool structure provided. + */ +int +smb_idpool_constructor( + smb_idpool_t *pool) +{ + + ASSERT(pool->id_magic != SMB_IDPOOL_MAGIC); + + pool->id_size = SMB_IDPOOL_MIN_SIZE; + pool->id_idx_msk = (SMB_IDPOOL_MIN_SIZE / 8) - 1; + pool->id_free_counter = SMB_IDPOOL_MIN_SIZE - 1; + pool->id_max_free_counter = SMB_IDPOOL_MIN_SIZE - 1; + pool->id_bit = 0x02; + pool->id_bit_idx = 1; + pool->id_idx = 0; + pool->id_pool = (uint8_t *)kmem_alloc((SMB_IDPOOL_MIN_SIZE / 8), + KM_SLEEP); + bzero(pool->id_pool, (SMB_IDPOOL_MIN_SIZE / 8)); + /* -1 id made unavailable */ + pool->id_pool[0] = 0x01; /* id 0 made unavailable */ + mutex_init(&pool->id_mutex, NULL, MUTEX_DEFAULT, NULL); + pool->id_magic = SMB_IDPOOL_MAGIC; + return (0); +} + +/* + * smb_idpool_destructor + * + * This function tears down and frees the resources associated with the + * pool provided. + */ +void +smb_idpool_destructor( + smb_idpool_t *pool) +{ + ASSERT(pool->id_magic == SMB_IDPOOL_MAGIC); + ASSERT(pool->id_free_counter == pool->id_max_free_counter); + pool->id_magic = (uint32_t)~SMB_IDPOOL_MAGIC; + mutex_destroy(&pool->id_mutex); + kmem_free(pool->id_pool, (size_t)(pool->id_size / 8)); +} + +/* + * smb_idpool_alloc + * + * This function allocates an ID from the pool provided. + */ +int +smb_idpool_alloc( + smb_idpool_t *pool, + uint16_t *id) +{ + uint32_t i; + uint8_t bit; + uint8_t bit_idx; + uint8_t byte; + + ASSERT(pool->id_magic == SMB_IDPOOL_MAGIC); + + mutex_enter(&pool->id_mutex); + if ((pool->id_free_counter == 0) && smb_idpool_increment(pool)) { + mutex_exit(&pool->id_mutex); + return (-1); + } + + i = pool->id_size; + while (i) { + bit = pool->id_bit; + bit_idx = pool->id_bit_idx; + byte = pool->id_pool[pool->id_idx]; + while (bit) { + if (byte & bit) { + bit = bit << 1; + bit_idx++; + continue; + } + pool->id_pool[pool->id_idx] |= bit; + *id = (uint16_t)(pool->id_idx * 8 + (uint32_t)bit_idx); + pool->id_free_counter--; + pool->id_bit = bit; + pool->id_bit_idx = bit_idx; + mutex_exit(&pool->id_mutex); + return (0); + } + pool->id_bit = 1; + pool->id_bit_idx = 0; + pool->id_idx++; + pool->id_idx &= pool->id_idx_msk; + --i; + } + /* + * This section of code shouldn't be reached. If there are IDs + * available and none could be found there's a problem. + */ + ASSERT(0); + mutex_exit(&pool->id_mutex); + return (-1); +} + +/* + * smb_idpool_free + * + * This function frees the ID provided. + */ +void +smb_idpool_free( + smb_idpool_t *pool, + uint16_t id) +{ + ASSERT(pool->id_magic == SMB_IDPOOL_MAGIC); + ASSERT(id != 0); + ASSERT(id != 0xFFFF); + + mutex_enter(&pool->id_mutex); + if (pool->id_pool[id >> 3] & (1 << (id & 7))) { + pool->id_pool[id >> 3] &= ~(1 << (id & 7)); + pool->id_free_counter++; + ASSERT(pool->id_free_counter <= pool->id_max_free_counter); + mutex_exit(&pool->id_mutex); + return; + } + /* Freeing a free ID. */ + ASSERT(0); + mutex_exit(&pool->id_mutex); +} + +/* + * smb_llist_constructor + * + * This function initializes a locked list. + */ +void +smb_llist_constructor( + smb_llist_t *ll, + size_t size, + size_t offset) +{ + rw_init(&ll->ll_lock, NULL, RW_DEFAULT, NULL); + list_create(&ll->ll_list, size, offset); + ll->ll_count = 0; + ll->ll_wrop = 0; +} + +/* + * smb_llist_destructor + * + * This function destroys a locked list. + */ +void +smb_llist_destructor( + smb_llist_t *ll) +{ + ASSERT(ll->ll_count == 0); + + rw_destroy(&ll->ll_lock); + list_destroy(&ll->ll_list); +} + +/* + * smb_llist_upgrade + * + * This function tries to upgrade the lock of the locked list. It assumes the + * locked has already been entered in RW_READER mode. It first tries using the + * Solaris function rw_tryupgrade(). If that call fails the lock is released + * and reentered in RW_WRITER mode. In that last case a window is opened during + * which the contents of the list may have changed. The return code indicates + * whether or not the list was modified when the lock was exited. + */ +int smb_llist_upgrade( + smb_llist_t *ll) +{ + uint64_t wrop; + + if (rw_tryupgrade(&ll->ll_lock) != 0) { + return (0); + } + wrop = ll->ll_wrop; + rw_exit(&ll->ll_lock); + rw_enter(&ll->ll_lock, RW_WRITER); + return (wrop != ll->ll_wrop); +} + +/* + * smb_llist_insert_head + * + * This function inserts the object passed a the beginning of the list. This + * function assumes the lock of the list has already been entered. + */ +void +smb_llist_insert_head( + smb_llist_t *ll, + void *obj) +{ + list_insert_head(&ll->ll_list, obj); + ++ll->ll_wrop; + ++ll->ll_count; +} + +/* + * smb_llist_insert_tail + * + * This function appends to the object passed to the list. This function assumes + * the lock of the list has already been entered. + * + */ +void +smb_llist_insert_tail( + smb_llist_t *ll, + void *obj) +{ + list_insert_tail(&ll->ll_list, obj); + ++ll->ll_wrop; + ++ll->ll_count; +} + +/* + * smb_llist_remove + * + * This function removes the object passed from the list. This function assumes + * the lock of the list has already been entered. + */ +void +smb_llist_remove( + smb_llist_t *ll, + void *obj) +{ + list_remove(&ll->ll_list, obj); + ++ll->ll_wrop; + --ll->ll_count; +} + +/* + * smb_llist_get_count + * + * This function returns the number of elements in the specified list. + */ +uint32_t +smb_llist_get_count( + smb_llist_t *ll) +{ + return (ll->ll_count); +} + +/* + * smb_slist_constructor + * + * Synchronized list constructor. + */ +void +smb_slist_constructor( + smb_slist_t *sl, + size_t size, + size_t offset) +{ + mutex_init(&sl->sl_mutex, NULL, MUTEX_DEFAULT, NULL); + cv_init(&sl->sl_cv, NULL, CV_DEFAULT, NULL); + list_create(&sl->sl_list, size, offset); + sl->sl_count = 0; + sl->sl_waiting = B_FALSE; +} + +/* + * smb_slist_destructor + * + * Synchronized list destructor. + */ +void +smb_slist_destructor( + smb_slist_t *sl) +{ + ASSERT(sl->sl_count == 0); + + mutex_destroy(&sl->sl_mutex); + cv_destroy(&sl->sl_cv); + list_destroy(&sl->sl_list); +} + +/* + * smb_slist_insert_head + * + * This function inserts the object passed a the beginning of the list. + */ +void +smb_slist_insert_head( + smb_slist_t *sl, + void *obj) +{ + mutex_enter(&sl->sl_mutex); + list_insert_head(&sl->sl_list, obj); + ++sl->sl_count; + mutex_exit(&sl->sl_mutex); +} + +/* + * smb_slist_insert_tail + * + * This function appends the object passed to the list. + */ +void +smb_slist_insert_tail( + smb_slist_t *sl, + void *obj) +{ + mutex_enter(&sl->sl_mutex); + list_insert_tail(&sl->sl_list, obj); + ++sl->sl_count; + mutex_exit(&sl->sl_mutex); +} + +/* + * smb_llist_remove + * + * This function removes the object passed by the caller from the list. + */ +void +smb_slist_remove( + smb_slist_t *sl, + void *obj) +{ + mutex_enter(&sl->sl_mutex); + list_remove(&sl->sl_list, obj); + if ((--sl->sl_count == 0) && (sl->sl_waiting)) { + sl->sl_waiting = B_FALSE; + cv_broadcast(&sl->sl_cv); + } + mutex_exit(&sl->sl_mutex); +} + +/* + * smb_slist_move_tail + * + * This function transfers all the contents of the synchronized list to the + * list_t provided. It returns the number of objects transferred. + */ +uint32_t +smb_slist_move_tail( + list_t *lst, + smb_slist_t *sl) +{ + uint32_t rv; + + mutex_enter(&sl->sl_mutex); + rv = sl->sl_count; + if (sl->sl_count) { + list_move_tail(lst, &sl->sl_list); + sl->sl_count = 0; + if (sl->sl_waiting) { + sl->sl_waiting = B_FALSE; + cv_broadcast(&sl->sl_cv); + } + } + mutex_exit(&sl->sl_mutex); + return (rv); +} + +/* + * smb_slist_obj_move + * + * This function moves an object from one list to the end of the other list. It + * assumes the mutex of each list has been entered. + */ +void +smb_slist_obj_move( + smb_slist_t *dst, + smb_slist_t *src, + void *obj) +{ + ASSERT(dst->sl_list.list_offset == src->sl_list.list_offset); + ASSERT(dst->sl_list.list_size == src->sl_list.list_size); + + list_remove(&src->sl_list, obj); + list_insert_tail(&dst->sl_list, obj); + dst->sl_count++; + src->sl_count--; + if ((src->sl_count == 0) && (src->sl_waiting)) { + src->sl_waiting = B_FALSE; + cv_broadcast(&src->sl_cv); + } +} + +/* + * smb_slist_wait_for_empty + * + * This function waits for a list to be emptied. + */ +void +smb_slist_wait_for_empty( + smb_slist_t *sl) +{ + mutex_enter(&sl->sl_mutex); + while (sl->sl_count) { + sl->sl_waiting = B_TRUE; + cv_wait(&sl->sl_cv, &sl->sl_mutex); + } + mutex_exit(&sl->sl_mutex); +} + +/* + * smb_slist_exit + * + * This function exits the muetx of the list and signal the condition variable + * if the list is empty. + */ +void +smb_slist_exit(smb_slist_t *sl) +{ + if ((sl->sl_count == 0) && (sl->sl_waiting)) { + sl->sl_waiting = B_FALSE; + cv_broadcast(&sl->sl_cv); + } + mutex_exit(&sl->sl_mutex); +} + +/* + * smb_thread_entry_point + * + * Common entry point for all the threads created through smb_thread_start. The + * state of teh thread is set to "running" at the beginning and moved to + * "exiting" just before calling thread_exit(). The condition variable is + * also signaled. + */ +static void +smb_thread_entry_point( + smb_thread_t *thread) +{ + ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); + mutex_enter(&thread->sth_mtx); + ASSERT(thread->sth_state == SMB_THREAD_STATE_STARTING); + thread->sth_th = curthread; + thread->sth_did = thread->sth_th->t_did; + + if (!thread->sth_kill) { + thread->sth_state = SMB_THREAD_STATE_RUNNING; + cv_signal(&thread->sth_cv); + mutex_exit(&thread->sth_mtx); + thread->sth_ep(thread, thread->sth_ep_arg); + mutex_enter(&thread->sth_mtx); + } + thread->sth_th = NULL; + thread->sth_state = SMB_THREAD_STATE_EXITING; + cv_broadcast(&thread->sth_cv); + mutex_exit(&thread->sth_mtx); + thread_exit(); +} + +/* + * smb_thread_init + */ +void +smb_thread_init( + smb_thread_t *thread, + char *name, + smb_thread_ep_t ep, + void *ep_arg, + smb_thread_aw_t aw, + void *aw_arg) +{ + ASSERT(thread->sth_magic != SMB_THREAD_MAGIC); + + bzero(thread, sizeof (*thread)); + + (void) strlcpy(thread->sth_name, name, sizeof (thread->sth_name)); + thread->sth_ep = ep; + thread->sth_ep_arg = ep_arg; + thread->sth_aw = aw; + thread->sth_aw_arg = aw_arg; + thread->sth_state = SMB_THREAD_STATE_EXITED; + mutex_init(&thread->sth_mtx, NULL, MUTEX_DEFAULT, NULL); + cv_init(&thread->sth_cv, NULL, CV_DEFAULT, NULL); + thread->sth_magic = SMB_THREAD_MAGIC; +} + +/* + * smb_thread_destroy + */ +void +smb_thread_destroy( + smb_thread_t *thread) +{ + ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); + ASSERT(thread->sth_state == SMB_THREAD_STATE_EXITED); + thread->sth_magic = 0; + mutex_destroy(&thread->sth_mtx); + cv_destroy(&thread->sth_cv); +} + +/* + * smb_thread_start + * + * This function starts a thread with the parameters provided. It waits until + * the state of the thread has been moved to running. + */ +/*ARGSUSED*/ +int +smb_thread_start( + smb_thread_t *thread) +{ + int rc = 0; + kthread_t *tmpthread; + + ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); + + mutex_enter(&thread->sth_mtx); + switch (thread->sth_state) { + case SMB_THREAD_STATE_EXITED: + thread->sth_state = SMB_THREAD_STATE_STARTING; + mutex_exit(&thread->sth_mtx); + tmpthread = thread_create(NULL, 0, smb_thread_entry_point, + thread, 0, &p0, TS_RUN, minclsyspri); + ASSERT(tmpthread != NULL); + mutex_enter(&thread->sth_mtx); + while (thread->sth_state == SMB_THREAD_STATE_STARTING) + cv_wait(&thread->sth_cv, &thread->sth_mtx); + if (thread->sth_state != SMB_THREAD_STATE_RUNNING) + rc = -1; + break; + default: + ASSERT(0); + rc = -1; + break; + } + mutex_exit(&thread->sth_mtx); + return (rc); +} + +/* + * smb_thread_stop + * + * This function signals a thread to kill itself and waits until the "exiting" + * state has been reached. + */ +void +smb_thread_stop( + smb_thread_t *thread) +{ + ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); + + mutex_enter(&thread->sth_mtx); + switch (thread->sth_state) { + case SMB_THREAD_STATE_RUNNING: + case SMB_THREAD_STATE_STARTING: + if (!thread->sth_kill) { + thread->sth_kill = B_TRUE; + if (thread->sth_aw) + thread->sth_aw(thread, thread->sth_aw_arg); + cv_broadcast(&thread->sth_cv); + while (thread->sth_state != SMB_THREAD_STATE_EXITING) + cv_wait(&thread->sth_cv, &thread->sth_mtx); + mutex_exit(&thread->sth_mtx); + thread_join(thread->sth_did); + mutex_enter(&thread->sth_mtx); + thread->sth_state = SMB_THREAD_STATE_EXITED; + thread->sth_did = 0; + thread->sth_kill = B_FALSE; + cv_broadcast(&thread->sth_cv); + break; + } + /*FALLTHRU*/ + + case SMB_THREAD_STATE_EXITING: + if (thread->sth_kill) { + while (thread->sth_state != SMB_THREAD_STATE_EXITED) + cv_wait(&thread->sth_cv, &thread->sth_mtx); + } else { + thread->sth_state = SMB_THREAD_STATE_EXITED; + thread->sth_did = 0; + } + break; + + case SMB_THREAD_STATE_EXITED: + break; + + default: + ASSERT(0); + break; + } + mutex_exit(&thread->sth_mtx); +} + +/* + * smb_thread_signal + * + * This function signals a thread. + */ +void +smb_thread_signal( + smb_thread_t *thread) +{ + ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); + + mutex_enter(&thread->sth_mtx); + switch (thread->sth_state) { + case SMB_THREAD_STATE_RUNNING: + if (thread->sth_aw) + thread->sth_aw(thread, thread->sth_aw_arg); + cv_signal(&thread->sth_cv); + break; + + default: + break; + } + mutex_exit(&thread->sth_mtx); +} + +boolean_t +smb_thread_continue(smb_thread_t *thread) +{ + boolean_t result; + + ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); + + mutex_enter(&thread->sth_mtx); + result = smb_thread_continue_timedwait_locked(thread, 0); + mutex_exit(&thread->sth_mtx); + + return (result); +} + +boolean_t +smb_thread_continue_nowait(smb_thread_t *thread) +{ + boolean_t result; + + ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); + + mutex_enter(&thread->sth_mtx); + /* + * Setting ticks=-1 requests a non-blocking check. We will + * still block if the thread is in "suspend" state. + */ + result = smb_thread_continue_timedwait_locked(thread, -1); + mutex_exit(&thread->sth_mtx); + + return (result); +} + +boolean_t +smb_thread_continue_timedwait(smb_thread_t *thread, int seconds) +{ + boolean_t result; + + ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); + + mutex_enter(&thread->sth_mtx); + result = smb_thread_continue_timedwait_locked(thread, + SEC_TO_TICK(seconds)); + mutex_exit(&thread->sth_mtx); + + return (result); +} + +/* + * smb_thread_continue_timedwait_locked + * + * Internal only. Ticks==-1 means don't block, Ticks == 0 means wait + * indefinitely + */ +static boolean_t +smb_thread_continue_timedwait_locked(smb_thread_t *thread, int ticks) +{ + boolean_t result; + clock_t finish_time = lbolt + ticks; + + /* -1 means don't block */ + if (ticks != -1 && !thread->sth_kill) { + if (ticks == 0) { + cv_wait(&thread->sth_cv, &thread->sth_mtx); + } else { + (void) cv_timedwait(&thread->sth_cv, &thread->sth_mtx, + finish_time); + } + } + result = (thread->sth_kill == 0); + + return (result); +} + +void +smb_thread_set_awaken(smb_thread_t *thread, smb_thread_aw_t new_aw_fn, + void *new_aw_arg) +{ + ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); + + mutex_enter(&thread->sth_mtx); + thread->sth_aw = new_aw_fn; + thread->sth_aw_arg = new_aw_arg; + mutex_exit(&thread->sth_mtx); +} + +/* + * smb_rwx_init + */ +void +smb_rwx_init( + smb_rwx_t *rwx) +{ + bzero(rwx, sizeof (smb_rwx_t)); + cv_init(&rwx->rwx_cv, NULL, CV_DEFAULT, NULL); + mutex_init(&rwx->rwx_mutex, NULL, MUTEX_DEFAULT, NULL); + rw_init(&rwx->rwx_lock, NULL, RW_DEFAULT, NULL); +} + +/* + * smb_rwx_destroy + */ +void +smb_rwx_destroy( + smb_rwx_t *rwx) +{ + mutex_destroy(&rwx->rwx_mutex); + cv_destroy(&rwx->rwx_cv); + rw_destroy(&rwx->rwx_lock); +} + +/* + * smb_rwx_rwexit + */ +void +smb_rwx_rwexit( + smb_rwx_t *rwx) +{ + if (rw_write_held(&rwx->rwx_lock)) { + ASSERT(rw_owner(&rwx->rwx_lock) == curthread); + mutex_enter(&rwx->rwx_mutex); + if (rwx->rwx_waiting) { + rwx->rwx_waiting = B_FALSE; + cv_broadcast(&rwx->rwx_cv); + } + mutex_exit(&rwx->rwx_mutex); + } + rw_exit(&rwx->rwx_lock); +} + +/* + * smb_rwx_rwupgrade + */ +krw_t +smb_rwx_rwupgrade( + smb_rwx_t *rwx) +{ + if (rw_write_held(&rwx->rwx_lock)) { + ASSERT(rw_owner(&rwx->rwx_lock) == curthread); + return (RW_WRITER); + } + if (!rw_tryupgrade(&rwx->rwx_lock)) { + rw_exit(&rwx->rwx_lock); + rw_enter(&rwx->rwx_lock, RW_WRITER); + } + return (RW_READER); +} + +/* + * smb_rwx_rwrestore + */ +void +smb_rwx_rwdowngrade( + smb_rwx_t *rwx, + krw_t mode) +{ + ASSERT(rw_write_held(&rwx->rwx_lock)); + ASSERT(rw_owner(&rwx->rwx_lock) == curthread); + + if (mode == RW_WRITER) { + return; + } + ASSERT(mode == RW_READER); + mutex_enter(&rwx->rwx_mutex); + if (rwx->rwx_waiting) { + rwx->rwx_waiting = B_FALSE; + cv_broadcast(&rwx->rwx_cv); + } + mutex_exit(&rwx->rwx_mutex); + rw_downgrade(&rwx->rwx_lock); +} + +/* + * smb_rwx_wait + * + * This function assumes the smb_rwx lock was enter in RW_READER or RW_WRITER + * mode. It will: + * + * 1) release the lock and save its current mode. + * 2) wait until the condition variable is signaled. This can happen for + * 2 reasons: When a writer releases the lock or when the time out (if + * provided) expires. + * 3) re-acquire the lock in the mode saved in (1). + */ +int +smb_rwx_rwwait( + smb_rwx_t *rwx, + clock_t timeout) +{ + int rc; + krw_t mode; + + mutex_enter(&rwx->rwx_mutex); + rwx->rwx_waiting = B_TRUE; + mutex_exit(&rwx->rwx_mutex); + + if (rw_write_held(&rwx->rwx_lock)) { + ASSERT(rw_owner(&rwx->rwx_lock) == curthread); + mode = RW_WRITER; + } else { + ASSERT(rw_read_held(&rwx->rwx_lock)); + mode = RW_READER; + } + rw_exit(&rwx->rwx_lock); + + mutex_enter(&rwx->rwx_mutex); + if (rwx->rwx_waiting) { + if (timeout == -1) { + rc = 1; + cv_wait(&rwx->rwx_cv, &rwx->rwx_mutex); + } else { + rc = cv_timedwait(&rwx->rwx_cv, &rwx->rwx_mutex, + lbolt + timeout); + } + } + mutex_exit(&rwx->rwx_mutex); + + rw_enter(&rwx->rwx_lock, mode); + return (rc); +} + +/* + * SMB ID mapping + * + * Solaris ID mapping service (aka Winchester) works with domain SIDs + * and RIDs where domain SIDs are in string format. CIFS service works + * with binary SIDs understandable by CIFS clients. A layer of SMB ID + * mapping functions are implemeted to hide the SID conversion details + * and also hide the handling of array of batch mapping requests. + */ + +static int smb_idmap_batch_binsid(smb_idmap_batch_t *sib); + +/* + * smb_idmap_getid + * + * Maps the given Windows SID to a Solaris ID using the + * simple mapping API. + */ +idmap_stat +smb_idmap_getid(nt_sid_t *sid, uid_t *id, int *idtype) +{ + smb_idmap_t sim; + nt_sid_t *tmpsid; + + tmpsid = nt_sid_dup(sid); + (void) nt_sid_split(tmpsid, &sim.sim_rid); + sim.sim_domsid = nt_sid_format(tmpsid); + MEM_FREE("smbsrv", tmpsid); + sim.sim_id = id; + + switch (*idtype) { + case SMB_IDMAP_USER: + sim.sim_stat = kidmap_getuidbysid(sim.sim_domsid, + sim.sim_rid, sim.sim_id); + break; + + case SMB_IDMAP_GROUP: + sim.sim_stat = kidmap_getgidbysid(sim.sim_domsid, + sim.sim_rid, sim.sim_id); + break; + + case SMB_IDMAP_UNKNOWN: + sim.sim_stat = kidmap_getpidbysid(sim.sim_domsid, + sim.sim_rid, sim.sim_id, &sim.sim_idtype); + break; + + default: + ASSERT(0); + return (IDMAP_ERR_ARG); + } + + *idtype = sim.sim_idtype; + MEM_FREE("smbsrv", sim.sim_domsid); + + return (sim.sim_stat); +} + +/* + * smb_idmap_getsid + * + * Maps the given Solaris ID to a Windows SID using the + * simple mapping API. + */ +idmap_stat +smb_idmap_getsid(uid_t id, int idtype, nt_sid_t **sid) +{ + smb_idmap_t sim; + + switch (idtype) { + case SMB_IDMAP_USER: + sim.sim_stat = kidmap_getsidbyuid(id, + (const char **)&sim.sim_domsid, &sim.sim_rid); + break; + + case SMB_IDMAP_GROUP: + sim.sim_stat = kidmap_getsidbygid(id, + (const char **)&sim.sim_domsid, &sim.sim_rid); + break; + + case SMB_IDMAP_EVERYONE: + /* Everyone S-1-1-0 */ + sim.sim_domsid = "S-1-1"; + sim.sim_rid = 0; + sim.sim_stat = IDMAP_SUCCESS; + break; + + default: + ASSERT(0); + return (IDMAP_ERR_ARG); + } + + if (sim.sim_stat != IDMAP_SUCCESS) + return (sim.sim_stat); + + if (sim.sim_domsid == NULL) { + return (IDMAP_ERR_NOMAPPING); + } + + sim.sim_sid = nt_sid_strtosid(sim.sim_domsid); + if (sim.sim_sid == NULL) { + return (IDMAP_ERR_INTERNAL); + } + + *sid = nt_sid_splice(sim.sim_sid, sim.sim_rid); + MEM_FREE("smbsrv", sim.sim_sid); + if (*sid == NULL) + sim.sim_stat = IDMAP_ERR_INTERNAL; + + return (sim.sim_stat); +} + +/* + * smb_idmap_batch_create + * + * Creates and initializes the context for batch ID mapping. + */ +idmap_stat +smb_idmap_batch_create(smb_idmap_batch_t *sib, uint16_t nmap, int flags) +{ + ASSERT(sib); + + bzero(sib, sizeof (smb_idmap_batch_t)); + + sib->sib_idmaph = kidmap_get_create(); + if (sib->sib_idmaph == NULL) + return (IDMAP_ERR_INTERNAL); + + sib->sib_flags = flags; + sib->sib_nmap = nmap; + sib->sib_size = nmap * sizeof (smb_idmap_t); + sib->sib_maps = kmem_zalloc(sib->sib_size, KM_SLEEP); + + return (IDMAP_SUCCESS); +} + +/* + * smb_idmap_batch_destroy + * + * Frees the batch ID mapping context. + * If ID mapping is Solaris -> Windows it frees memories + * allocated for binary SIDs. + */ +void +smb_idmap_batch_destroy(smb_idmap_batch_t *sib) +{ + nt_sid_t *sid; + char *domsid; + int i; + + ASSERT(sib); + ASSERT(sib->sib_maps); + + if (sib->sib_idmaph) + kidmap_get_destroy(sib->sib_idmaph); + + if (sib->sib_flags & SMB_IDMAP_ID2SID) { + /* + * SIDs are allocated only when mapping + * UID/GID to SIDs + */ + for (i = 0; i < sib->sib_nmap; i++) { + sid = sib->sib_maps[i].sim_sid; + if (sid) + MEM_FREE("smbsrv", sid); + } + } else if (sib->sib_flags & SMB_IDMAP_SID2ID) { + /* + * SID prefixes are allocated only when mapping + * SIDs to UID/GID + */ + for (i = 0; i < sib->sib_nmap; i++) { + domsid = sib->sib_maps[i].sim_domsid; + if (domsid) + MEM_FREE("smbsrv", domsid); + } + } + + if (sib->sib_size && sib->sib_maps) + kmem_free(sib->sib_maps, sib->sib_size); +} + +/* + * smb_idmap_batch_getid + * + * Queue a request to map the given SID to a UID or GID. + * + * sim->sim_id should point to variable that's supposed to + * hold the returned UID/GID. This needs to be setup by caller + * of this function. + * + * If requested ID type is known, it's passed as 'idtype', + * if it's unknown it'll be returned in sim->sim_idtype. + */ +idmap_stat +smb_idmap_batch_getid(idmap_get_handle_t *idmaph, smb_idmap_t *sim, + nt_sid_t *sid, int idtype) +{ + nt_sid_t *tmpsid; + idmap_stat idm_stat; + + ASSERT(idmaph); + ASSERT(sim); + ASSERT(sid); + + tmpsid = nt_sid_dup(sid); + (void) nt_sid_split(tmpsid, &sim->sim_rid); + sim->sim_domsid = nt_sid_format(tmpsid); + MEM_FREE("smbsrv", tmpsid); + + switch (idtype) { + case SMB_IDMAP_USER: + idm_stat = kidmap_batch_getuidbysid(idmaph, sim->sim_domsid, + sim->sim_rid, sim->sim_id, &sim->sim_stat); + break; + + case SMB_IDMAP_GROUP: + idm_stat = kidmap_batch_getgidbysid(idmaph, sim->sim_domsid, + sim->sim_rid, sim->sim_id, &sim->sim_stat); + break; + + case SMB_IDMAP_UNKNOWN: + idm_stat = kidmap_batch_getpidbysid(idmaph, sim->sim_domsid, + sim->sim_rid, sim->sim_id, &sim->sim_idtype, + &sim->sim_stat); + break; + + default: + ASSERT(0); + return (IDMAP_ERR_ARG); + } + + return (idm_stat); +} + +/* + * smb_idmap_batch_getsid + * + * Queue a request to map the given UID/GID to a SID. + * + * sim->sim_domsid and sim->sim_rid will contain the mapping + * result upon successful process of the batched request. + */ +idmap_stat +smb_idmap_batch_getsid(idmap_get_handle_t *idmaph, smb_idmap_t *sim, + uid_t id, int idtype) +{ + idmap_stat idm_stat; + + switch (idtype) { + case SMB_IDMAP_USER: + idm_stat = kidmap_batch_getsidbyuid(idmaph, id, + (const char **)&sim->sim_domsid, &sim->sim_rid, + &sim->sim_stat); + break; + + case SMB_IDMAP_GROUP: + idm_stat = kidmap_batch_getsidbygid(idmaph, id, + (const char **)&sim->sim_domsid, &sim->sim_rid, + &sim->sim_stat); + break; + + case SMB_IDMAP_EVERYONE: + /* Everyone S-1-1-0 */ + sim->sim_domsid = "S-1-1"; + sim->sim_rid = 0; + sim->sim_stat = IDMAP_SUCCESS; + idm_stat = IDMAP_SUCCESS; + break; + + default: + ASSERT(0); + return (IDMAP_ERR_ARG); + } + + return (idm_stat); +} + +/* + * smb_idmap_batch_binsid + * + * Convert sidrids to binary sids + * + * Returns 0 if successful and non-zero upon failure. + */ +static int +smb_idmap_batch_binsid(smb_idmap_batch_t *sib) +{ + nt_sid_t *sid; + smb_idmap_t *sim; + int i; + + if (sib->sib_flags & SMB_IDMAP_SID2ID) + /* This operation is not required */ + return (0); + + sim = sib->sib_maps; + for (i = 0; i < sib->sib_nmap; sim++, i++) { + ASSERT(sim->sim_domsid); + if (sim->sim_domsid == NULL) { + return (1); + } + + sid = nt_sid_strtosid(sim->sim_domsid); + if (sid == NULL) { + return (1); + } + + sim->sim_sid = nt_sid_splice(sid, sim->sim_rid); + MEM_FREE("smbsrv", sid); + } + + return (0); +} + +/* + * smb_idmap_batch_getmappings + * + * trigger ID mapping service to get the mappings for queued + * requests. + * + * Checks the result of all the queued requests. + * If this is a Solaris -> Windows mapping it generates + * binary SIDs from returned (domsid, rid) pairs. + */ +idmap_stat +smb_idmap_batch_getmappings(smb_idmap_batch_t *sib) +{ + idmap_stat idm_stat = IDMAP_SUCCESS; + int i; + + idm_stat = kidmap_get_mappings(sib->sib_idmaph); + if (idm_stat != IDMAP_SUCCESS) { + return (idm_stat); + } + + /* + * Check the status for all the queued requests + */ + for (i = 0; i < sib->sib_nmap; i++) { + if (sib->sib_maps[i].sim_stat != IDMAP_SUCCESS) { + return (sib->sib_maps[i].sim_stat); + } + } + + if (smb_idmap_batch_binsid(sib) != 0) { + idm_stat = IDMAP_ERR_OTHER; + } + + return (idm_stat); +} + +uint64_t +unix_to_nt_time(timestruc_t *unix_time) +{ + uint64_t nt_time; + + nt_time = unix_time->tv_sec; + nt_time *= 10000000; /* seconds to 100ns */ + nt_time += unix_time->tv_nsec / 100; + return (nt_time + NT_TIME_BIAS); +} + +uint32_t +nt_to_unix_time(uint64_t nt_time, timestruc_t *unix_time) +{ + uint32_t seconds; + + nt_time -= NT_TIME_BIAS; + seconds = nt_time / 10000000; + if (unix_time) { + unix_time->tv_sec = seconds; + unix_time->tv_nsec = (nt_time % 10000000) * 100; + } + return (seconds); +} + +int32_t /*ARGSUSED*/ +dosfs_dos_to_ux_time(int32_t date, int time) +{ + struct tm atm; + + atm.tm_year = ((date >> 9) & 0x3F) + 80; + atm.tm_mon = ((date >> 5) & 0x0F) - 1; + atm.tm_mday = ((date >> 0) & 0x1F); + atm.tm_hour = ((time >> 11) & 0x1F); + atm.tm_min = ((time >> 5) & 0x3F); + atm.tm_sec = ((time >> 0) & 0x1F) << 1; + + return (smb_timegm(&atm)); +} + +int32_t /*ARGSUSED*/ +dosfs_ux_to_dos_time(int32_t ux_time, short *date_p, short *time_p) +{ + struct tm atm; + int i; + time_t tmp_time; + + tmp_time = (time_t)ux_time; + (void) smb_gmtime_r(&tmp_time, &atm); + + if (date_p) { + i = 0; + i += atm.tm_year - 80; + i <<= 4; + i += atm.tm_mon + 1; + i <<= 5; + i += atm.tm_mday; + + *date_p = (short)i; + } + if (time_p) { + i = 0; + i += atm.tm_hour; + i <<= 6; + i += atm.tm_min; + i <<= 5; + i += atm.tm_sec >> 1; + + *time_p = (short)i; + } + return (ux_time); +} + + +/* + * smb_gmtime_r + * + * Thread-safe version of smb_gmtime. Returns a null pointer if either + * input parameter is a null pointer. Otherwise returns a pointer + * to result. + * + * Day of the week calculation: the Epoch was a thursday. + * + * There are no timezone corrections so tm_isdst and tm_gmtoff are + * always zero, and the zone is always WET. + */ +struct tm * +smb_gmtime_r(time_t *clock, struct tm *result) +{ + time_t tsec; + int year; + int month; + int sec_per_month; + + if (clock == 0 || result == 0) + return (0); + + bzero(result, sizeof (struct tm)); + tsec = *clock; + tsec -= tzh_leapcnt; + + result->tm_wday = tsec / SECSPERDAY; + result->tm_wday = (result->tm_wday + TM_THURSDAY) % DAYSPERWEEK; + + year = EPOCH_YEAR; + while (tsec >= (isleap(year) ? (SECSPERDAY * DAYSPERLYEAR) : + (SECSPERDAY * DAYSPERNYEAR))) { + if (isleap(year)) + tsec -= SECSPERDAY * DAYSPERLYEAR; + else + tsec -= SECSPERDAY * DAYSPERNYEAR; + + ++year; + } + + result->tm_year = year - TM_YEAR_BASE; + result->tm_yday = tsec / SECSPERDAY; + + for (month = TM_JANUARY; month <= TM_DECEMBER; ++month) { + sec_per_month = days_in_month[month] * SECSPERDAY; + + if (month == TM_FEBRUARY && isleap(year)) + sec_per_month += SECSPERDAY; + + if (tsec < sec_per_month) + break; + + tsec -= sec_per_month; + } + + result->tm_mon = month; + result->tm_mday = (tsec / SECSPERDAY) + 1; + tsec %= SECSPERDAY; + result->tm_sec = tsec % 60; + tsec /= 60; + result->tm_min = tsec % 60; + tsec /= 60; + result->tm_hour = (int)tsec; + + return (result); +} + + +/* + * smb_timegm + * + * Converts the broken-down time in tm to a time value, i.e. the number + * of seconds since the Epoch (00:00:00 UTC, January 1, 1970). This is + * not a POSIX or ANSI function. Per the man page, the input values of + * tm_wday and tm_yday are ignored and, as the input data is assumed to + * represent GMT, we force tm_isdst and tm_gmtoff to 0. + * + * Before returning the clock time, we use smb_gmtime_r to set up tm_wday + * and tm_yday, and bring the other fields within normal range. I don't + * think this is really how it should be done but it's convenient for + * now. + */ +time_t +smb_timegm(struct tm *tm) +{ + time_t tsec; + int dd; + int mm; + int yy; + int year; + + if (tm == 0) + return (-1); + + year = tm->tm_year + TM_YEAR_BASE; + tsec = tzh_leapcnt; + + for (yy = EPOCH_YEAR; yy < year; ++yy) { + if (isleap(yy)) + tsec += SECSPERDAY * DAYSPERLYEAR; + else + tsec += SECSPERDAY * DAYSPERNYEAR; + } + + for (mm = TM_JANUARY; mm < tm->tm_mon; ++mm) { + dd = days_in_month[mm] * SECSPERDAY; + + if (mm == TM_FEBRUARY && isleap(year)) + dd += SECSPERDAY; + + tsec += dd; + } + + tsec += (tm->tm_mday - 1) * SECSPERDAY; + tsec += tm->tm_sec; + tsec += tm->tm_min * SECSPERMIN; + tsec += tm->tm_hour * SECSPERHOUR; + + tm->tm_isdst = 0; + (void) smb_gmtime_r(&tsec, tm); + return (tsec); +} + +#ifdef DEBUG +uint32_t smb_audit_flags = SMB_AUDIT_NODE; +#else +uint32_t smb_audit_flags = 0; +#endif + +void +smb_audit_buf_node_create(smb_node_t *node) +{ + smb_audit_buf_node_t *abn; + + if (smb_audit_flags & SMB_AUDIT_NODE) { + abn = kmem_zalloc(sizeof (smb_audit_buf_node_t), KM_SLEEP); + abn->anb_max_index = SMB_AUDIT_BUF_MAX_REC - 1; + node->n_audit_buf = abn; + } +} + +void +smb_audit_buf_node_destroy(smb_node_t *node) +{ + smb_audit_buf_node_t *abn; + + abn = node->n_audit_buf; + + if (abn) { + node->n_audit_buf = NULL; + kmem_free(abn, sizeof (smb_audit_buf_node_t)); + } +} + +/* + * smb_cred_set_sid + * + * Initialize the ksid based on the given smb_id_t. + */ +static void +smb_cred_set_sid(smb_id_t *id, ksid_t *ksid) +{ + nt_sid_t *domain_sid = NULL; + char *domain_sid_buf = NULL; + int rc; + + ASSERT(id); + ASSERT(id->i_sidattr.sid); + + ksid->ks_id = id->i_id; + domain_sid = nt_sid_dup(id->i_sidattr.sid); + rc = nt_sid_split(domain_sid, &ksid->ks_rid); + ASSERT(rc == 0); + + ksid->ks_attr = id->i_sidattr.attrs; + domain_sid_buf = nt_sid_format(domain_sid); + ksid->ks_domain = ksid_lookupdomain(domain_sid_buf); + MEM_FREE("smbsrv", domain_sid); + MEM_FREE("smbsrv", domain_sid_buf); +} + +/* + * smb_cred_set_sidlist + * + * Allocate and initialize the ksidlist based on the Windows group list of the + * access token. + */ +static ksidlist_t * +smb_cred_set_sidlist(smb_win_grps_t *token_grps) +{ + int i; + ksidlist_t *lp; + + lp = kmem_zalloc(KSIDLIST_MEM(token_grps->wg_count), KM_SLEEP); + lp->ksl_ref = 1; + lp->ksl_nsid = token_grps->wg_count; + lp->ksl_neid = 0; + + for (i = 0; i < lp->ksl_nsid; i++) { + smb_cred_set_sid(&token_grps->wg_groups[i], + &lp->ksl_sids[i]); + if (lp->ksl_sids[i].ks_id > IDMAP_WK__MAX_GID) + lp->ksl_neid++; + } + + return (lp); +} + +/* + * smb_cred_create + * + * The credential of the given SMB user will be allocated and initialized based + * on the given access token. + */ +cred_t * +smb_cred_create(smb_token_t *token, uint32_t *privileges) +{ + ksid_t ksid; + ksidlist_t *ksidlist = NULL; + smb_posix_grps_t *posix_grps; + cred_t *cr; + + ASSERT(token); + ASSERT(token->tkn_posix_grps); + ASSERT(privileges); + + cr = crget(); + ASSERT(cr != NULL); + + posix_grps = token->tkn_posix_grps; + if (crsetugid(cr, token->tkn_user->i_id, + token->tkn_primary_grp->i_id) != 0) { + crfree(cr); + return (NULL); + } + + if (crsetgroups(cr, posix_grps->pg_ngrps, posix_grps->pg_grps) != 0) { + crfree(cr); + return (NULL); + } + + smb_cred_set_sid(token->tkn_user, &ksid); + crsetsid(cr, &ksid, KSID_USER); + smb_cred_set_sid(token->tkn_primary_grp, &ksid); + crsetsid(cr, &ksid, KSID_GROUP); + smb_cred_set_sid(token->tkn_owner, &ksid); + crsetsid(cr, &ksid, KSID_OWNER); + ksidlist = smb_cred_set_sidlist(token->tkn_win_grps); + crsetsidlist(cr, ksidlist); + + *privileges = 0; + + /* + * Support for backup and restore privileges will be disabled until + * the BACKUP_SEMANTICS and backup intent attributes are supported. + */ +#ifdef SUPPORT_FILE_OPEN_FOR_BACKUP + if (smb_token_query_privilege(token, SE_BACKUP_LUID)) { + *privileges |= SMB_USER_PRIV_BACKUP; + (void) crsetpriv(cr, PRIV_FILE_DAC_READ, + PRIV_FILE_DAC_SEARCH, PRIV_SYS_MOUNT, NULL); + } + + if (smb_token_query_privilege(token, SE_RESTORE_LUID)) { + *privileges |= SMB_USER_PRIV_RESTORE; + (void) crsetpriv(cr, PRIV_FILE_DAC_WRITE, + PRIV_FILE_CHOWN, PRIV_FILE_CHOWN_SELF, + PRIV_FILE_DAC_SEARCH, PRIV_FILE_LINK_ANY, + PRIV_FILE_OWNER, PRIV_FILE_SETID, PRIV_SYS_LINKDIR, + PRIV_SYS_MOUNT, NULL); + } +#endif /* SUPPORT_FILE_OPEN_FOR_BACKUP */ + + if (smb_token_query_privilege(token, SE_TAKE_OWNERSHIP_LUID)) { + *privileges |= SMB_USER_PRIV_TAKE_OWNERSHIP; + (void) crsetpriv(cr, PRIV_FILE_CHOWN, NULL); + } + + if (smb_token_query_privilege(token, SE_SECURITY_LUID)) { + *privileges |= SMB_USER_PRIV_SECURITY; + } + return (cr); +} + +/* + * smb_cred_rele + * + * The reference count of the user's credential will get decremented if it + * is non-zero. Otherwise, the credential will be freed. + */ +void +smb_cred_rele(cred_t *cr) +{ + ASSERT(cr); + crfree(cr); +} + +/* + * smb_cred_is_member + * + * Same as smb_token_is_member. The only difference is that + * we compare the given SID against user SID and the ksidlist + * of the user's cred. + */ +int +smb_cred_is_member(cred_t *cr, nt_sid_t *sid) +{ + ksidlist_t *ksidlist; + ksid_t ksid1, *ksid2; + smb_id_t id; + int i, rc = 0; + + ASSERT(cr); + + bzero(&id, sizeof (smb_id_t)); + id.i_sidattr.sid = sid; + smb_cred_set_sid(&id, &ksid1); + + ksidlist = crgetsidlist(cr); + ASSERT(ksidlist); + ASSERT(ksid1.ks_domain); + ASSERT(ksid1.ks_domain->kd_name); + + i = 0; + ksid2 = crgetsid(cr, KSID_USER); + do { + ASSERT(ksid2->ks_domain); + ASSERT(ksid2->ks_domain->kd_name); + + if (strcmp(ksid1.ks_domain->kd_name, + ksid2->ks_domain->kd_name) == 0 && + ksid1.ks_rid == ksid2->ks_rid) { + rc = 1; + break; + } + + ksid2 = &ksidlist->ksl_sids[i]; + } while (i++ < ksidlist->ksl_nsid); + + ksid_rele(&ksid1); + return (rc); +} + +/* + * smb_kstrdup + * + * Duplicate the given string s. + */ +char * +smb_kstrdup(const char *s, size_t n) +{ + char *s2; + + ASSERT(s); + ASSERT(n); + s2 = kmem_alloc(n, KM_SLEEP); + (void) strcpy(s2, s); + return (s2); +} + +/* + * smb_sync_fsattr + * + * Sync file's attributes with file system. + * The sync takes place based on node->what and node->flags + * values. + */ +int +smb_sync_fsattr(struct smb_request *sr, cred_t *cr, smb_node_t *node) +{ + uint32_t what; + int rc = 0; + + if (node->flags & NODE_READ_ONLY) + return (0); + + if (node->flags & NODE_FLAGS_SET_SIZE) { + node->flags &= ~NODE_FLAGS_SET_SIZE; + node->what |= SMB_AT_SIZE; + node->attr.sa_vattr.va_size = node->n_size; + } + + if (node->what) { + /* + * This is to prevent another thread from starting + * a setattr should this one go to sleep + */ + what = node->what; + node->what = 0; + + node->attr.sa_mask = what; + + rc = smb_fsop_setattr(sr, cr, node, &node->attr, &node->attr); + + if (rc) { + /* setattr failed, restore the dirty state? */ + node->what = what; + } + } + + return (rc); +} + +/* + * smb_share_export() + * + * This function handles kernel processing at share enable time. + * + * At share-enable time (LMSHRD_ADD), the file system corresponding to + * the share is checked for characteristics that are required for SMB + * sharing. If this check passes, then a hold is taken on the root vnode + * of the file system (or a reference count on the corresponding smb_vfs_t + * is bumped), preventing an unmount. (See smb_vfs_hold()). + */ + +int +smb_share_export(char *path) +{ + int error; + smb_node_t *fnode = NULL; + smb_node_t *dnode; + smb_attr_t ret_attr; + char last_comp[MAXNAMELEN]; + + error = smb_pathname_reduce(NULL, kcred, path, NULL, NULL, &dnode, + last_comp); + + if (error) + return (error); + + error = smb_fsop_lookup(NULL, kcred, SMB_FOLLOW_LINKS, NULL, dnode, + last_comp, &fnode, &ret_attr, NULL, NULL); + + smb_node_release(dnode); + + if (error) + return (error); + + ASSERT(fnode->vp && fnode->vp->v_vfsp); + +#ifdef SMB_ENFORCE_NODEV + if (vfs_optionisset(fnode->vp->v_vfsp, MNTOPT_NODEVICES, NULL) == 0) + return (EINVAL); +#endif /* SMB_ENFORCE_NODEV */ + + if (!smb_vfs_hold(fnode->vp->v_vfsp)) { + smb_node_release(fnode); + return (ENOMEM); + } + + /* + * The refcount on the smb_vfs has been incremented. + * If it wasn't already, a hold has also been taken + * on the root vnode of the file system. + */ + + smb_node_release(fnode); + return (0); +} + +/* + * smb_share_unexport() + * + * This function handles kernel processing at share disable time. + * + * At share-disable time (LMSHRD_DELETE), the reference count on the + * corresponding smb_vfs_t is decremented. If this is the last share + * on the file system, the hold on the root vnode of the file system + * will be released. (See smb_vfs_rele().) + */ + +int +smb_share_unexport(char *path, char *sharename) +{ + int error; + smb_node_t *fnode = NULL; + smb_node_t *dnode; + smb_attr_t ret_attr; + char last_comp[MAXNAMELEN]; + + error = smb_pathname_reduce(NULL, kcred, path, NULL, NULL, &dnode, + last_comp); + + if (error) + return (error); + + error = smb_fsop_lookup(NULL, kcred, SMB_FOLLOW_LINKS, NULL, dnode, + last_comp, &fnode, &ret_attr, NULL, NULL); + + smb_node_release(dnode); + + if (error) + return (error); + + ASSERT(fnode->vp && fnode->vp->v_vfsp); + + smb_session_disconnect_share(sharename); + smb_vfs_rele(fnode->vp->v_vfsp); + smb_node_release(fnode); + return (0); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_vfs.c b/usr/src/uts/common/fs/smbsrv/smb_vfs.c new file mode 100644 index 000000000000..38928bfe2a87 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_vfs.c @@ -0,0 +1,161 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include + +static smb_vfs_t *smb_vfs_lookup(vnode_t *); + +/* + * smb_vfs_hold + * + * Increments the reference count of the fs passed in. If no smb_vfs_t structure + * has been created yet for the fs passed in it is created. + */ +boolean_t +smb_vfs_hold(vfs_t *vfsp) +{ + smb_vfs_t *smb_vfs; + vnode_t *rootvp; + + if ((vfsp == NULL) || VFS_ROOT(vfsp, &rootvp)) + return (B_FALSE); + + smb_llist_enter(&smb_info.si_vfs_list, RW_WRITER); + smb_vfs = smb_vfs_lookup(rootvp); + if (smb_vfs) { + DTRACE_PROBE1(smb_vfs_hold_hit, smb_vfs_t *, smb_vfs); + smb_llist_exit(&smb_info.si_vfs_list); + VN_RELE(rootvp); + return (B_TRUE); + } + smb_vfs = kmem_cache_alloc(smb_info.si_cache_vfs, KM_SLEEP); + + bzero(smb_vfs, sizeof (smb_vfs_t)); + + smb_vfs->sv_magic = SMB_VFS_MAGIC; + smb_vfs->sv_refcnt = 1; + smb_vfs->sv_vfsp = vfsp; + /* + * We have a hold on the root vnode of the file system + * from the VFS_ROOT call above. + */ + smb_vfs->sv_rootvp = rootvp; + smb_llist_insert_head(&smb_info.si_vfs_list, smb_vfs); + DTRACE_PROBE1(smb_vfs_hold_miss, smb_vfs_t *, smb_vfs); + smb_llist_exit(&smb_info.si_vfs_list); + return (B_TRUE); +} + +/* + * smb_vfs_rele + * + * Decrements the reference count of the fs passed in. If the reference count + * drops to zero the smb_vfs_t structure associated with the fs is freed. + */ +void +smb_vfs_rele(vfs_t *vfsp) +{ + smb_vfs_t *smb_vfs; + vnode_t *rootvp; + + ASSERT(vfsp); + + if (VFS_ROOT(vfsp, &rootvp)) + return; + + smb_llist_enter(&smb_info.si_vfs_list, RW_WRITER); + smb_vfs = smb_vfs_lookup(rootvp); + DTRACE_PROBE2(smb_vfs_release, smb_vfs_t *, smb_vfs, vnode_t *, rootvp); + VN_RELE(rootvp); + if (smb_vfs) { + --smb_vfs->sv_refcnt; + ASSERT(smb_vfs->sv_refcnt); + if (--smb_vfs->sv_refcnt == 0) { + smb_llist_remove(&smb_info.si_vfs_list, smb_vfs); + smb_llist_exit(&smb_info.si_vfs_list); + ASSERT(rootvp == smb_vfs->sv_rootvp); + VN_RELE(smb_vfs->sv_rootvp); + smb_vfs->sv_magic = (uint32_t)~SMB_VFS_MAGIC; + kmem_cache_free(smb_info.si_cache_vfs, smb_vfs); + return; + } + } + smb_llist_exit(&smb_info.si_vfs_list); +} + +/* + * smb_vfs_rele_all() + * + * Release all holds on root vnodes of file systems which were taken + * due to the existence of at least one enabled share on the file system. + * Called at driver close time. + */ +void +smb_vfs_rele_all() +{ + smb_vfs_t *smb_vfs; + + smb_llist_enter(&smb_info.si_vfs_list, RW_WRITER); + while ((smb_vfs = smb_llist_head(&smb_info.si_vfs_list)) != NULL) { + + ASSERT(smb_vfs->sv_magic == SMB_VFS_MAGIC); + DTRACE_PROBE1(smb_vfs_rele_all_hit, smb_vfs_t *, smb_vfs); + smb_llist_remove(&smb_info.si_vfs_list, smb_vfs); + VN_RELE(smb_vfs->sv_rootvp); + kmem_cache_free(smb_info.si_cache_vfs, smb_vfs); + } + smb_llist_exit(&smb_info.si_vfs_list); +} + +/* + * smb_vfs_lookup + * + * Goes through the list of smb_vfs_t structure and returns the one matching + * the vnode passed in. If no match is found a NULL pointer is returned. + * + * The list of smb_vfs_t structures has to have been entered prior calling + * this function. + */ +static smb_vfs_t * +smb_vfs_lookup(vnode_t *rootvp) +{ + smb_vfs_t *smb_vfs; + + smb_vfs = smb_llist_head(&smb_info.si_vfs_list); + while (smb_vfs) { + ASSERT(smb_vfs->sv_magic == SMB_VFS_MAGIC); + if (smb_vfs->sv_rootvp == rootvp) { + smb_vfs->sv_refcnt++; + ASSERT(smb_vfs->sv_refcnt); + return (smb_vfs); + } + smb_vfs = smb_llist_next(&smb_info.si_vfs_list, smb_vfs); + } + return (NULL); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_vops.c b/usr/src/uts/common/fs/smbsrv/smb_vops.c new file mode 100644 index 000000000000..b71174e9d595 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_vops.c @@ -0,0 +1,1878 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int +smb_vop_readdir_readpage(vnode_t *vp, void *buf, uint32_t offset, int *count, + cred_t *cr, caller_context_t *ct, int flags); + +static int +smb_vop_readdir_entry(vnode_t *dvp, uint32_t *cookiep, char *name, int *namelen, + ino64_t *inop, vnode_t **vpp, char *od_name, int flags, cred_t *cr, + caller_context_t *ct, char *dirbuf, int num_bytes); + +static int +smb_vop_getdents_entries(smb_node_t *dir_snode, uint32_t *cookiep, + int32_t *dircountp, char *arg, uint32_t flags, struct smb_request *sr, + cred_t *cr, caller_context_t *ct, char *dirbuf, int *maxentries, + int num_bytes, char *); + +extern int +smb_gather_dents_info(char *args, ino_t fileid, int namelen, + char *name, uint32_t cookie, int32_t *countp, + smb_attr_t *attr, struct smb_node *snode, + char *shortname, char *name83); + +static void +smb_sa_to_va_mask(uint_t sa_mask, uint_t *va_maskp); + +#define SMB_AT_MAX 16 +static uint_t smb_attrmap[SMB_AT_MAX] = { + 0, + AT_TYPE, + AT_MODE, + AT_UID, + AT_GID, + AT_FSID, + AT_NODEID, + AT_NLINK, + AT_SIZE, + AT_ATIME, + AT_MTIME, + AT_CTIME, + AT_RDEV, + AT_BLKSIZE, + AT_NBLOCKS, + AT_SEQ +}; + +int +smb_vop_open(vnode_t **vpp, int mode, cred_t *cred, caller_context_t *ct) +{ + return (VOP_OPEN(vpp, mode, cred, ct)); +} + +int +smb_vop_close(vnode_t *vp, int mode, cred_t *cred, caller_context_t *ct) +{ + return (VOP_CLOSE(vp, mode, 1, (offset_t)0, cred, ct)); +} + +/* + * The smb_vop_* functions have minimal knowledge of CIFS semantics and + * serve as an interface to the VFS layer. + * + * Only smb_fsop_* layer functions should call smb_vop_* layer functions. + * (Higher-level CIFS service code should never skip the smb_fsop_* layer + * to call smb_vop_* layer functions directly.) + */ + +/* + * XXX - Extended attributes support in the file system assumed. + * This is needed for full NT Streams functionality. + */ + +int +smb_vop_read(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct) +{ + int error; + + (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); + error = VOP_READ(vp, uiop, 0, cr, ct); + VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); + return (error); +} + +int +smb_vop_write(vnode_t *vp, uio_t *uiop, uint32_t *flag, uint32_t *lcount, + cred_t *cr, caller_context_t *ct) +{ + int error; + int ioflag = 0; + + *lcount = uiop->uio_resid; + + if (*flag == FSSTAB_FILE_SYNC) + ioflag = FSYNC; + + uiop->uio_llimit = MAXOFFSET_T; + + (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); + error = VOP_WRITE(vp, uiop, ioflag, cr, ct); + VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); + + *lcount -= uiop->uio_resid; + + return (error); +} + +/* + * smb_vop_getattr() + * + * smb_fsop_getattr()/smb_vop_getattr() should always be called from the CIFS + * service (instead of calling VOP_GETATTR directly) to retrieve attributes + * due to special processing needed for streams files. + * + * All attributes are retrieved. + * + * A named stream's attributes (as far as CIFS is concerned) are those of the + * unnamed (i.e. data) stream (minus the size attribute), and the size of the + * named stream. Though the file system may store attributes other than size + * with the named stream, these should not be used by CIFS for any purpose. + * + * When vp denotes a named stream, then unnamed_vp should be passed in (denoting + * the corresponding unnamed stream). + */ + +int +smb_vop_getattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *ret_attr, + int flags, cred_t *cr, caller_context_t *ct) +{ + int error; + vnode_t *use_vp; + smb_attr_t tmp_attr; + xvattr_t tmp_xvattr; + xoptattr_t *xoap = NULL; + + if (unnamed_vp) + use_vp = unnamed_vp; + else + use_vp = vp; + + if (vfs_has_feature(use_vp->v_vfsp, VFSFT_XVATTR)) { + xva_init(&tmp_xvattr); + xoap = xva_getxoptattr(&tmp_xvattr); + + ASSERT(xoap); + + smb_sa_to_va_mask(ret_attr->sa_mask, + &tmp_xvattr.xva_vattr.va_mask); + + XVA_SET_REQ(&tmp_xvattr, XAT_READONLY); + XVA_SET_REQ(&tmp_xvattr, XAT_HIDDEN); + XVA_SET_REQ(&tmp_xvattr, XAT_SYSTEM); + XVA_SET_REQ(&tmp_xvattr, XAT_ARCHIVE); + XVA_SET_REQ(&tmp_xvattr, XAT_CREATETIME); + + if ((error = VOP_GETATTR(use_vp, (vattr_t *)&tmp_xvattr, flags, + cr, ct)) != 0) + return (error); + + ret_attr->sa_vattr = tmp_xvattr.xva_vattr; + + /* + * Copy special attributes to ret_attr parameter + */ + + ret_attr->sa_dosattr = 0; + + ASSERT(tmp_xvattr.xva_vattr.va_mask & AT_XVATTR); + + xoap = xva_getxoptattr(&tmp_xvattr); + ASSERT(xoap); + + if (XVA_ISSET_RTN(&tmp_xvattr, XAT_READONLY)) { + if (xoap->xoa_readonly) + ret_attr->sa_dosattr |= FILE_ATTRIBUTE_READONLY; + } + + if (XVA_ISSET_RTN(&tmp_xvattr, XAT_HIDDEN)) { + if (xoap->xoa_hidden) + ret_attr->sa_dosattr |= FILE_ATTRIBUTE_HIDDEN; + } + + if (XVA_ISSET_RTN(&tmp_xvattr, XAT_SYSTEM)) { + if (xoap->xoa_system) + ret_attr->sa_dosattr |= FILE_ATTRIBUTE_SYSTEM; + } + + if (XVA_ISSET_RTN(&tmp_xvattr, XAT_ARCHIVE)) { + if (xoap->xoa_archive) + ret_attr->sa_dosattr |= FILE_ATTRIBUTE_ARCHIVE; + } + + ret_attr->sa_crtime = xoap->xoa_createtime; + + if (unnamed_vp && (ret_attr->sa_mask & SMB_AT_SIZE)) { + /* + * Retrieve stream size attribute into temporary + * structure, in case the underlying file system + * returns attributes other than the size (we do not + * want to have ret_attr's other fields get + * overwritten). + * + * Note that vp is used here, and not use_vp. + * Also, only AT_SIZE is needed. + */ + + tmp_xvattr.xva_vattr.va_mask = AT_SIZE; + + if ((error = VOP_GETATTR(vp, (vattr_t *)&tmp_xvattr, + flags, cr, ct)) != 0) + return (error); + + ret_attr->sa_vattr.va_size = + tmp_xvattr.xva_vattr.va_size; + + } + + if (ret_attr->sa_vattr.va_type == VDIR) { + ret_attr->sa_dosattr |= FILE_ATTRIBUTE_DIRECTORY; + } + + return (error); + } + + /* + * Support for file systems without VFSFT_XVATTR + */ + + smb_sa_to_va_mask(ret_attr->sa_mask, + &ret_attr->sa_vattr.va_mask); + + error = VOP_GETATTR(use_vp, &ret_attr->sa_vattr, flags, cr, ct); + + if (error != 0) + return (error); + + /* + * "Fake" DOS attributes and create time, filesystem doesn't support + * them. + */ + + ret_attr->sa_dosattr = 0; + ret_attr->sa_crtime = ret_attr->sa_vattr.va_ctime; + + if (unnamed_vp && (ret_attr->sa_mask & SMB_AT_SIZE)) { + /* + * Retrieve stream size attribute into temporary structure, + * in case the underlying file system returns attributes + * other than the size (we do not want to have ret_attr's + * other fields get overwritten). + * + * Note that vp is used here, and not use_vp. + * Also, only AT_SIZE is needed. + */ + + tmp_attr.sa_vattr.va_mask = AT_SIZE; + error = VOP_GETATTR(vp, &tmp_attr.sa_vattr, flags, cr, ct); + + if (error != 0) + return (error); + + + ret_attr->sa_vattr.va_size = tmp_attr.sa_vattr.va_size; + } + + if (ret_attr->sa_vattr.va_type == VDIR) { + ret_attr->sa_dosattr |= FILE_ATTRIBUTE_DIRECTORY; + } + + return (error); +} + +/* + * smb_vop_setattr() + * + * smb_fsop_setattr()/smb_vop_setattr() should always be called from the CIFS + * service to set attributes due to special processing for streams files. + * + * When smb_vop_setattr() is called on a named stream file, all indicated + * attributes except the size are set on the unnamed stream file. The size + * (if indicated) is set on the named stream file. + */ + +int +smb_vop_setattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *set_attr, + int flags, cred_t *cr, caller_context_t *ct) +{ + int error = 0; + int at_size = 0; + vnode_t *use_vp; + xvattr_t tmp_xvattr; + xoptattr_t *xoap = NULL; + uint_t xva_mask; + + if (unnamed_vp) { + use_vp = unnamed_vp; + if (set_attr->sa_mask & SMB_AT_SIZE) { + at_size = 1; + set_attr->sa_mask &= ~SMB_AT_SIZE; + } + } else { + use_vp = vp; + } + + /* + * The caller should not be setting sa_vattr.va_mask, + * but rather sa_mask. + */ + + set_attr->sa_vattr.va_mask = 0; + + if (vfs_has_feature(use_vp->v_vfsp, VFSFT_XVATTR)) { + /* + * Initialize xvattr, including bzero + */ + xva_init(&tmp_xvattr); + xoap = xva_getxoptattr(&tmp_xvattr); + + ASSERT(xoap); + + /* + * Copy caller-specified classic attributes to tmp_xvattr. + * First save tmp_xvattr's mask (set in xva_init()). + * This is |'d in later. + */ + + xva_mask = tmp_xvattr.xva_vattr.va_mask; + tmp_xvattr.xva_vattr = set_attr->sa_vattr; + + smb_sa_to_va_mask(set_attr->sa_mask, + &tmp_xvattr.xva_vattr.va_mask); + + /* + * "|" in the original xva_mask. + */ + + tmp_xvattr.xva_vattr.va_mask |= xva_mask; + + if (set_attr->sa_mask & SMB_AT_DOSATTR) { + XVA_SET_REQ(&tmp_xvattr, XAT_ARCHIVE); + XVA_SET_REQ(&tmp_xvattr, XAT_SYSTEM); + XVA_SET_REQ(&tmp_xvattr, XAT_READONLY); + XVA_SET_REQ(&tmp_xvattr, XAT_HIDDEN); + + /* + * set_attr->sa_dosattr: If a given bit is not set, + * that indicates that the corresponding field needs + * to be updated with a "0" value. This is done + * implicitly as the xoap->xoa_* fields were bzero'd. + */ + + if (set_attr->sa_dosattr & FILE_ATTRIBUTE_ARCHIVE) + xoap->xoa_archive = 1; + + if (set_attr->sa_dosattr & FILE_ATTRIBUTE_SYSTEM) + xoap->xoa_system = 1; + + if (set_attr->sa_dosattr & FILE_ATTRIBUTE_READONLY) + xoap->xoa_readonly = 1; + + if (set_attr->sa_dosattr & FILE_ATTRIBUTE_HIDDEN) + xoap->xoa_hidden = 1; + } + + if (set_attr->sa_mask & SMB_AT_CRTIME) { + XVA_SET_REQ(&tmp_xvattr, XAT_CREATETIME); + xoap->xoa_createtime = set_attr->sa_crtime; + } + + if ((error = VOP_SETATTR(use_vp, (vattr_t *)&tmp_xvattr, flags, + cr, ct)) != 0) + return (error); + + /* + * If the size of the stream needs to be set, set it on + * the stream file directly. (All other indicated attributes + * are set on the stream's unnamed stream, above.) + */ + + if (at_size) { + /* + * set_attr->sa_vattr.va_size already contains the + * size as set by the caller + * + * Note that vp is used here, and not use_vp. + * Also, only AT_SIZE is needed. + */ + + set_attr->sa_vattr.va_mask = AT_SIZE; + error = VOP_SETATTR(vp, &set_attr->sa_vattr, flags, + cr, ct); + } + + return (error); + } + + /* + * Support for file systems without VFSFT_XVATTR + */ + + smb_sa_to_va_mask(set_attr->sa_mask, &set_attr->sa_vattr.va_mask); + + /* + * set_attr->sa_vattr already contains new values + * as set by the caller + */ + + error = VOP_SETATTR(use_vp, &set_attr->sa_vattr, flags, cr, ct); + + if (error != 0) + return (error); + + if (at_size) { + /* + * set_attr->sa_vattr.va_size already contains the + * size as set by the caller + * + * Note that vp is used here, and not use_vp. + * Also, only AT_SIZE is needed. + */ + + set_attr->sa_vattr.va_mask = AT_SIZE; + error = VOP_SETATTR(vp, &set_attr->sa_vattr, flags, cr, ct); + } + + return (error); +} + +/* + * smb_vop_access + * + * This is a wrapper round VOP_ACCESS. VOP_ACCESS checks the given mode + * against file's ACL or Unix permissions. CIFS on the other hand needs to + * know if the requested operation can succeed for the given object, this + * requires more checks in case of DELETE bit since permissions on the parent + * directory are important as well. Based on Windows rules if parent's ACL + * grant FILE_DELETE_CHILD a file can be delete regardless of the file's + * permissions. + */ +int +smb_vop_access(vnode_t *vp, int mode, int flags, vnode_t *dir_vp, cred_t *cr) +{ + int error = 0; + + if (mode == 0) + return (0); + + if ((flags == V_ACE_MASK) && (mode & ACE_DELETE)) { + if (dir_vp) { + error = VOP_ACCESS(dir_vp, ACE_DELETE_CHILD, flags, + cr, NULL); + + if (error == 0) + mode &= ~ACE_DELETE; + } + } + + if (mode) { + error = VOP_ACCESS(vp, mode, flags, cr, NULL); + } + + return (error); +} + +/* + * smb_vop_lookup + * + * dvp: directory vnode (in) + * name: name of file to be looked up (in) + * vpp: looked-up vnode (out) + * od_name: on-disk name of file (out). + * This parameter is optional. If a pointer is passed in, it + * must be allocated with MAXNAMELEN bytes + * rootvp: vnode of the tree root (in) + * This parameter is always passed in non-NULL except at the time + * of share set up. + */ + +int +smb_vop_lookup(vnode_t *dvp, char *name, vnode_t **vpp, char *od_name, + int flags, vnode_t *rootvp, cred_t *cr, caller_context_t *ct) +{ + int error = 0; + int option_flags = 0; + pathname_t rpn; + + if (*name == '\0') + return (EINVAL); + + ASSERT(vpp); + *vpp = NULL; + + if ((name[0] == '.') && (name[1] == '.') && (name[2] == 0)) { + if (rootvp && (dvp == rootvp)) { + VN_HOLD(dvp); + *vpp = dvp; + return (0); + } + + if (dvp->v_flag & VROOT) { + vfs_t *vfsp; + vnode_t *cvp = dvp; + + /* + * Set dvp and check for races with forced unmount + * (see lookuppnvp()) + */ + + vfsp = cvp->v_vfsp; + vfs_rlock_wait(vfsp); + if (((dvp = cvp->v_vfsp->vfs_vnodecovered) == NULL) || + (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) { + vfs_unlock(vfsp); + return (EIO); + } + vfs_unlock(vfsp); + } + } + + + + if (flags & SMB_IGNORE_CASE) + option_flags = FIGNORECASE; + + pn_alloc(&rpn); + + error = VOP_LOOKUP(dvp, name, vpp, NULL, option_flags, NULL, cr, + ct, NULL, &rpn); + + if ((error == 0) && od_name) { + bzero(od_name, MAXNAMELEN); + if (option_flags == FIGNORECASE) + (void) strlcpy(od_name, rpn.pn_buf, MAXNAMELEN); + else + (void) strlcpy(od_name, name, MAXNAMELEN); + } + + pn_free(&rpn); + return (error); +} + +int +smb_vop_create(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp, + int flags, cred_t *cr, caller_context_t *ct, vsecattr_t *vsap) +{ + int error; + int option_flags = 0; + + if (flags & SMB_IGNORE_CASE) + option_flags = FIGNORECASE; + + smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask); + + error = VOP_CREATE(dvp, name, &attr->sa_vattr, EXCL, + attr->sa_vattr.va_mode, vpp, cr, option_flags, ct, vsap); + + return (error); +} + +int +smb_vop_remove(vnode_t *dvp, char *name, int flags, cred_t *cr, + caller_context_t *ct) +{ + int error; + int option_flags = 0; + + if (flags & SMB_IGNORE_CASE) + option_flags = FIGNORECASE; + + error = VOP_REMOVE(dvp, name, cr, ct, option_flags); + + return (error); +} + +/* + * smb_vop_rename() + * + * The rename is for files in the same tree (identical TID) only. + */ + +int +smb_vop_rename(vnode_t *from_dvp, char *from_name, vnode_t *to_dvp, + char *to_name, int flags, cred_t *cr, caller_context_t *ct) +{ + int error; + int option_flags = 0; + + + if (flags & SMB_IGNORE_CASE) + option_flags = FIGNORECASE; + + error = VOP_RENAME(from_dvp, from_name, to_dvp, to_name, cr, + ct, option_flags); + + return (error); +} + +int +smb_vop_mkdir(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp, + int flags, cred_t *cr, caller_context_t *ct, vsecattr_t *vsap) +{ + int error; + int option_flags = 0; + + + + if (flags & SMB_IGNORE_CASE) + option_flags = FIGNORECASE; + + smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask); + + error = VOP_MKDIR(dvp, name, &attr->sa_vattr, vpp, cr, ct, + option_flags, vsap); + + return (error); +} + +/* + * smb_vop_rmdir() + * + * Only simple rmdir supported, consistent with NT semantics + * (can only remove an empty directory). + * + */ + +int +smb_vop_rmdir(vnode_t *dvp, char *name, int flags, cred_t *cr, + caller_context_t *ct) +{ + int error; + int option_flags = 0; + + if (flags & SMB_IGNORE_CASE) + option_flags = FIGNORECASE; + + /* + * Comments adapted from rfs_rmdir(). + * + * VOP_RMDIR now takes a new third argument (the current + * directory of the process). That's because rmdir + * wants to return EINVAL if one tries to remove ".". + * Of course, SMB servers do not know what their + * clients' current directories are. We fake it by + * supplying a vnode known to exist and illegal to + * remove. + */ + + error = VOP_RMDIR(dvp, name, rootdir, cr, ct, option_flags); + return (error); +} + +int +smb_vop_commit(vnode_t *vp, cred_t *cr, caller_context_t *ct) +{ + return (VOP_FSYNC(vp, 1, cr, ct)); +} + +/* + * smb_vop_readdir() + * + * Upon return, the "name" field will contain either the on-disk name or, if + * it needs mangling or has a case-insensitive collision, the mangled + * "shortname." + * + * vpp is an optional parameter. If non-NULL, it will contain a pointer to + * the vnode for the name that is looked up (the vnode will be returned held). + * + * od_name is an optional parameter (NULL can be passed if the on-disk name + * is not needed by the caller). + */ + +int +smb_vop_readdir(vnode_t *dvp, uint32_t *cookiep, char *name, int *namelen, + ino64_t *inop, vnode_t **vpp, char *od_name, int flags, cred_t *cr, + caller_context_t *ct) +{ + int num_bytes; + int error = 0; + char *dirbuf = NULL; + + ASSERT(dvp); + ASSERT(cookiep); + ASSERT(name); + ASSERT(namelen); + ASSERT(inop); + ASSERT(cr); + ASSERT(ct); + + if (dvp->v_type != VDIR) { + *namelen = 0; + return (ENOTDIR); + } + + if (vpp) + *vpp = NULL; + + dirbuf = kmem_zalloc(SMB_MINLEN_RDDIR_BUF, KM_SLEEP); + num_bytes = SMB_MINLEN_RDDIR_BUF; + + /* + * The goal is to retrieve the first valid entry from *cookiep + * forward. smb_vop_readdir_readpage() collects an + * SMB_MINLEN_RDDIR_BUF-size "page" of directory entry information. + * smb_vop_readdir_entry() attempts to find the first valid entry + * in that page. + */ + + while ((error = smb_vop_readdir_readpage(dvp, dirbuf, *cookiep, + &num_bytes, cr, ct, flags)) == 0) { + + if (num_bytes <= 0) + break; + + name[0] = '\0'; + + error = smb_vop_readdir_entry(dvp, cookiep, name, namelen, + inop, vpp, od_name, flags, cr, ct, dirbuf, + num_bytes); + + if (error) + break; + + if (*name) + break; + + bzero(dirbuf, SMB_MINLEN_RDDIR_BUF); + num_bytes = SMB_MINLEN_RDDIR_BUF; + } + + + if (error) { + kmem_free(dirbuf, SMB_MINLEN_RDDIR_BUF); + *namelen = 0; + return (error); + } + + if (num_bytes == 0) { /* EOF */ + kmem_free(dirbuf, SMB_MINLEN_RDDIR_BUF); + *cookiep = SMB_EOF; + *namelen = 0; + return (0); + } + + kmem_free(dirbuf, SMB_MINLEN_RDDIR_BUF); + return (0); +} + +/* + * smb_vop_readdir_readpage() + * + * Collects an SMB_MINLEN_RDDIR_BUF "page" of directory entries. (The + * directory entries are returned in an fs-independent format by the + * underlying file system. That is, the "page" of information returned is + * not literally stored on-disk in the format returned.) + * + * Much of the following is borrowed from getdents64() + * + * MAXGETDENTS_SIZE is defined in getdents.c + */ + +#define MAXGETDENTS_SIZE (64 * 1024) + +static int +smb_vop_readdir_readpage(vnode_t *vp, void *buf, uint32_t offset, int *count, + cred_t *cr, caller_context_t *ct, int flags) +{ + int error = 0; + int rdirent_flags = 0; + int sink; + struct uio auio; + struct iovec aiov; + + if (vp->v_type != VDIR) + return (ENOTDIR); + + /* entflags not working for streams so don't try to use them */ + if (!(flags & SMB_STREAM_RDDIR) && + (vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS))) { + /* + * Setting V_RDDIR_ENTFLAGS will cause the buffer to + * be filled with edirent_t structures (instead of + * dirent64_t structures). + */ + rdirent_flags = V_RDDIR_ENTFLAGS; + + if (*count < sizeof (edirent_t)) + return (EINVAL); + } else { + if (*count < sizeof (dirent64_t)) + return (EINVAL); + } + + if (*count > MAXGETDENTS_SIZE) + *count = MAXGETDENTS_SIZE; + + aiov.iov_base = buf; + aiov.iov_len = *count; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_loffset = (uint64_t)offset; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_resid = *count; + auio.uio_fmode = 0; + + (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); + error = VOP_READDIR(vp, &auio, cr, &sink, ct, rdirent_flags); + VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); + + if (error) { + if (error == ENOENT) { + /* Fake EOF if offset is bad due to dropping of lock */ + *count = 0; + return (0); + } else { + return (error); + } + } + + /* + * Windows cannot handle an offset > SMB_EOF. + * Pretend we are at EOF. + */ + + if (auio.uio_loffset > SMB_EOF) { + *count = 0; + return (0); + } + + *count = *count - auio.uio_resid; + return (0); +} + +/* + * smb_vop_readdir_entry() + * + * This function retrieves the first valid entry from the + * SMB_MINLEN_RDDIR_BUF-sized buffer returned by smb_vop_readdir_readpage() + * to smb_vop_readdir(). + * + * Both dirent64_t and edirent_t structures need to be handled. The former is + * needed for file systems that do not support VFSFT_DIRENTFLAGS. The latter + * is required for proper handling of case collisions on file systems that + * support case-insensitivity. edirent_t structures are also used for + * case-sensitive file systems if VFSFT_DIRENTFLAGS is supported. + */ + +static int +smb_vop_readdir_entry(vnode_t *dvp, uint32_t *cookiep, char *name, int *namelen, + ino64_t *inop, vnode_t **vpp, char *od_name, int flags, cred_t *cr, + caller_context_t *ct, char *dirbuf, int num_bytes) +{ + uint32_t next_cookie; + int ebufsize; + int error = 0; + int len; + int rc; + char shortname[MANGLE_NAMELEN]; + char name83[MANGLE_NAMELEN]; + char *ebuf = NULL; + edirent_t *edp; + dirent64_t *dp = NULL; + vnode_t *vp = NULL; + + ASSERT(dirbuf); + + /* + * Use edirent_t structure for both + * entflags not working for streams so don't try to use them + */ + if (!(flags & SMB_STREAM_RDDIR) && + (vfs_has_feature(dvp->v_vfsp, VFSFT_DIRENTFLAGS))) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + edp = (edirent_t *)dirbuf; + } else { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + dp = (dirent64_t *)dirbuf; + ebufsize = EDIRENT_RECLEN(MAXNAMELEN); + ebuf = kmem_zalloc(ebufsize, KM_SLEEP); + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + edp = (edirent_t *)ebuf; + } + + while (edp) { + if (dp) + DP_TO_EDP(dp, edp); + + next_cookie = (uint32_t)edp->ed_off; + if (edp->ed_ino == 0) { + *cookiep = next_cookie; + + if (dp) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + DP_ADVANCE(dp, dirbuf, num_bytes); + if (dp == NULL) + edp = NULL; + } else { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + EDP_ADVANCE(edp, dirbuf, num_bytes); + } + continue; + } + + len = strlen(edp->ed_name); + + if (*namelen < len) { + *namelen = 0; + + if (ebuf) + kmem_free(ebuf, ebufsize); + + return (EOVERFLOW); + } + + /* + * Do not pass SMB_IGNORE_CASE to smb_vop_lookup + */ + + error = smb_vop_lookup(dvp, edp->ed_name, vpp ? vpp : &vp, + od_name, 0, NULL, cr, ct); + + if (error) { + if (error == ENOENT) { + *cookiep = (uint32_t)next_cookie; + + if (dp) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + DP_ADVANCE(dp, dirbuf, num_bytes); + if (dp == NULL) + edp = NULL; + } else { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + EDP_ADVANCE(edp, dirbuf, num_bytes); + } + continue; + } + + + *namelen = 0; + + if (ebuf) + kmem_free(ebuf, ebufsize); + + return (error); + } + + if ((flags & SMB_IGNORE_CASE) && ED_CASE_CONFLICTS(edp)) { + rc = smb_mangle_name(edp->ed_ino, edp->ed_name, + shortname, name83, 1); + + if (rc == 1) { /* success */ + (void) strlcpy(name, shortname, *namelen + 1); + *namelen = strlen(shortname); + } else { + (void) strlcpy(name, edp->ed_name, + *namelen + 1); + name[*namelen] = '\0'; + } + + } else { + (void) strlcpy(name, edp->ed_name, *namelen + 1); + *namelen = len; + } + + if (vpp == NULL) + VN_RELE(vp); + + if (inop) + *inop = edp->ed_ino; + + *cookiep = (uint32_t)next_cookie; + break; + } + + if (ebuf) + kmem_free(ebuf, ebufsize); + + return (error); +} + +/* + * smb_sa_to_va_mask + * + * Set va_mask by running through the SMB_AT_* #define's and + * setting those bits that correspond to the SMB_AT_* bits + * set in sa_mask. + */ + +void +smb_sa_to_va_mask(uint_t sa_mask, uint_t *va_maskp) +{ + int i; + uint_t smask; + + smask = (sa_mask); + for (i = SMB_AT_TYPE; (i < SMB_AT_MAX) && (smask != 0); ++i) { + if (smask & 1) + *(va_maskp) |= smb_attrmap[i]; + + smask >>= 1; + } +} + +/* + * smb_vop_getdents() + * + * Upon success, the smb_node corresponding to each entry returned will + * have a reference taken on it. These will be released in + * smb_trans2_find_get_dents(). + * + * If an error is returned from this routine, a list of already processed + * entries will be returned. The smb_nodes corresponding to these entries + * will be referenced, and will be released in smb_trans2_find_get_dents(). + * + * The returned dp->d_name field will contain either the on-disk name or, if + * it needs mangling or has a case-insensitive collision, the mangled + * "shortname." In this case, the on-disk name can be retrieved from the + * smb_node's od_name (the smb_node is passed to smb_gather_dents_info()). + */ + +int /*ARGSUSED*/ +smb_vop_getdents( + smb_node_t *dir_snode, + uint32_t *cookiep, + uint64_t *verifierp, + int32_t *dircountp, + char *arg, + char *pattern, + uint32_t flags, + smb_request_t *sr, + cred_t *cr, + caller_context_t *ct) +{ + int error = 0; + int maxentries; + int num_bytes; + int resid; + char *dirbuf = NULL; + vnode_t *dvp; + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + smb_dent_info_hdr_t *ihdr = (smb_dent_info_hdr_t *)arg; + + dvp = dir_snode->vp; + + resid = ihdr->uio.uio_resid; + maxentries = resid / SMB_MAX_DENT_INFO_SIZE; + + bzero(ihdr->iov->iov_base, resid); + + dirbuf = kmem_alloc(SMB_MINLEN_RDDIR_BUF, KM_SLEEP); + + while (maxentries) { + + bzero(dirbuf, SMB_MINLEN_RDDIR_BUF); + + num_bytes = SMB_MINLEN_RDDIR_BUF; + error = smb_vop_readdir_readpage(dvp, dirbuf, *cookiep, + &num_bytes, cr, ct, flags); + + if (error || (num_bytes <= 0)) + break; + + error = smb_vop_getdents_entries(dir_snode, cookiep, dircountp, + arg, flags, sr, cr, ct, dirbuf, &maxentries, num_bytes, + pattern); + + if (error) + goto out; + } + + if (num_bytes < 0) { + error = -1; + } else if (num_bytes == 0) { + *cookiep = SMB_EOF; + error = 0; + } else { + error = 0; + } + +out: + if (dirbuf) + kmem_free(dirbuf, SMB_MINLEN_RDDIR_BUF); + + return (error); +} + +/* + * smb_vop_getdents_entries() + * + * This function retrieves names from the SMB_MINLEN_RDDIR_BUF-sized buffer + * returned by smb_vop_readdir_readpage() to smb_vop_getdents(). + * + * Both dirent64_t and edirent_t structures need to be handled. The former is + * needed for file systems that do not support VFSFT_DIRENTFLAGS. The latter + * is required for properly handling case collisions on file systems that + * support case-insensitivity. edirent_t is also used on case-sensitive + * file systems where VFSFT_DIRENTFLAGS is available. + */ + +static int +smb_vop_getdents_entries( + smb_node_t *dir_snode, + uint32_t *cookiep, + int32_t *dircountp, + char *arg, + uint32_t flags, + struct smb_request *sr, + cred_t *cr, + caller_context_t *ct, + char *dirbuf, + int *maxentries, + int num_bytes, + char *pattern) +{ + uint32_t next_cookie; + int ebufsize; + char *tmp_name; + int error; + int rc; + char shortname[MANGLE_NAMELEN]; + char name83[MANGLE_NAMELEN]; + char *ebuf = NULL; + dirent64_t *dp = NULL; + edirent_t *edp; + smb_node_t *ret_snode; + smb_attr_t ret_attr; + vnode_t *dvp; + vnode_t *fvp; + + ASSERT(dirbuf); + + dvp = dir_snode->vp; + + if (vfs_has_feature(dvp->v_vfsp, VFSFT_DIRENTFLAGS)) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + edp = (edirent_t *)dirbuf; + } else { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + dp = (dirent64_t *)dirbuf; + ebufsize = EDIRENT_RECLEN(MAXNAMELEN); + ebuf = kmem_zalloc(ebufsize, KM_SLEEP); + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + edp = (edirent_t *)ebuf; + } + + while (edp) { + if (dp) + DP_TO_EDP(dp, edp); + + if (*maxentries == 0) + break; + + next_cookie = (uint32_t)edp->ed_off; + + if (edp->ed_ino == 0) { + *cookiep = next_cookie; + if (dp) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + DP_ADVANCE(dp, dirbuf, num_bytes); + if (dp == NULL) + edp = NULL; + } else { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + EDP_ADVANCE(edp, dirbuf, num_bytes); + } + continue; + } + + error = smb_vop_lookup(dvp, edp->ed_name, &fvp, + NULL, 0, NULL, cr, ct); + + if (error) { + if (error == ENOENT) { + *cookiep = next_cookie; + if (dp) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + DP_ADVANCE(dp, dirbuf, + num_bytes); + if (dp == NULL) + edp = NULL; + } else { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + EDP_ADVANCE(edp, dirbuf, + num_bytes); + } + continue; + } + if (ebuf) + kmem_free(ebuf, ebufsize); + + return (error); + } + + ret_snode = smb_node_lookup(sr, NULL, cr, fvp, + edp->ed_name, dir_snode, NULL, &ret_attr); + + if (ret_snode == NULL) { + VN_RELE(fvp); + + if (ebuf) + kmem_free(ebuf, ebufsize); + + return (ENOMEM); + } + + if (smb_match_name(edp->ed_ino, edp->ed_name, shortname, + name83, pattern, (flags & SMB_IGNORE_CASE))) { + + tmp_name = edp->ed_name; + + if ((flags & SMB_IGNORE_CASE) && + ED_CASE_CONFLICTS(edp)) { + rc = smb_mangle_name(edp->ed_ino, edp->ed_name, + shortname, name83, 1); + if (rc == 1) + tmp_name = shortname; + } else { + rc = smb_mangle_name(edp->ed_ino, edp->ed_name, + shortname, name83, 0); + } + + if (rc != 1) { + (void) strlcpy(shortname, edp->ed_name, + MANGLE_NAMELEN); + (void) strlcpy(name83, edp->ed_name, + MANGLE_NAMELEN); + shortname[MANGLE_NAMELEN - 1] = '\0'; + name83[MANGLE_NAMELEN - 1] = '\0'; + } + + error = smb_gather_dents_info(arg, edp->ed_ino, + strlen(tmp_name), tmp_name, next_cookie, dircountp, + &ret_attr, ret_snode, shortname, name83); + + if (error > 0) { + if (ebuf) + kmem_free(ebuf, ebufsize); + return (error); + } + + /* + * Treat errors from smb_gather_dents_info() that are + * < 0 the same as EOF. + */ + if (error < 0) { + if (ebuf) + kmem_free(ebuf, ebufsize); + *maxentries = 0; + return (0); + } + (*maxentries)--; + } else { + smb_node_release(ret_snode); + } + + *cookiep = next_cookie; + + if (dp) { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + DP_ADVANCE(dp, dirbuf, num_bytes); + if (dp == NULL) + edp = NULL; + } else { + /*LINTED E_BAD_PTR_CAST_ALIGN*/ + EDP_ADVANCE(edp, dirbuf, num_bytes); + } + } + + if (ebuf) + kmem_free(ebuf, ebufsize); + + return (0); +} + +/* + * smb_vop_stream_lookup() + * + * The name returned in od_name is the on-disk name of the stream with the + * SMB_STREAM_PREFIX stripped off. od_name should be allocated to MAXNAMELEN + * by the caller. + */ + +int +smb_vop_stream_lookup(vnode_t *fvp, char *stream_name, vnode_t **vpp, + char *od_name, vnode_t **xattrdirvpp, int flags, vnode_t *rootvp, + cred_t *cr, caller_context_t *ct) +{ + char *solaris_stream_name; + char *name; + int error; + + if ((error = smb_vop_lookup_xattrdir(fvp, xattrdirvpp, + LOOKUP_XATTR | CREATE_XATTR_DIR, cr, ct)) != 0) + return (error); + + /* + * Prepend SMB_STREAM_PREFIX to stream name + */ + + solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + (void) sprintf(solaris_stream_name, "%s%s", SMB_STREAM_PREFIX, + stream_name); + + /* + * "name" will hold the on-disk name returned from smb_vop_lookup + * for the stream, including the SMB_STREAM_PREFIX. + */ + + name = kmem_zalloc(MAXNAMELEN, KM_SLEEP); + + if ((error = smb_vop_lookup(*xattrdirvpp, solaris_stream_name, vpp, + name, flags, rootvp, cr, ct)) != 0) { + VN_RELE(*xattrdirvpp); + } else { + (void) strlcpy(od_name, &(name[SMB_STREAM_PREFIX_LEN]), + MAXNAMELEN); + } + + kmem_free(solaris_stream_name, MAXNAMELEN); + kmem_free(name, MAXNAMELEN); + + return (error); +} + +int +smb_vop_stream_create(vnode_t *fvp, char *stream_name, smb_attr_t *attr, + vnode_t **vpp, vnode_t **xattrdirvpp, int flags, cred_t *cr, + caller_context_t *ct) +{ + char *solaris_stream_name; + int error; + + if ((error = smb_vop_lookup_xattrdir(fvp, xattrdirvpp, + LOOKUP_XATTR | CREATE_XATTR_DIR, cr, ct)) != 0) + return (error); + + /* + * Prepend SMB_STREAM_PREFIX to stream name + */ + + solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + (void) sprintf(solaris_stream_name, "%s%s", SMB_STREAM_PREFIX, + stream_name); + + if ((error = smb_vop_create(*xattrdirvpp, solaris_stream_name, attr, + vpp, flags, cr, ct, NULL)) != 0) + VN_RELE(*xattrdirvpp); + + kmem_free(solaris_stream_name, MAXNAMELEN); + + return (error); +} + +int +smb_vop_stream_remove(vnode_t *vp, char *stream_name, int flags, cred_t *cr, + caller_context_t *ct) +{ + char *solaris_stream_name; + vnode_t *xattrdirvp; + int error; + + if ((error = smb_vop_lookup_xattrdir(vp, &xattrdirvp, LOOKUP_XATTR, cr, + ct)) != 0) + return (error); + + /* + * Prepend SMB_STREAM_PREFIX to stream name + */ + + solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + (void) sprintf(solaris_stream_name, "%s%s", SMB_STREAM_PREFIX, + stream_name); + + /* XXX might have to use kcred */ + error = smb_vop_remove(xattrdirvp, solaris_stream_name, flags, cr, ct); + + kmem_free(solaris_stream_name, MAXNAMELEN); + + return (error); +} + +/* + * smb_vop_stream_readdir() + * + * Note: stream_info.size is not filled in in this routine. + * It needs to be filled in by the caller due to the parameters for getattr. + * + * stream_info.name is set to the on-disk stream name with the SMB_STREAM_PREFIX + * removed. + */ + +int +smb_vop_stream_readdir(vnode_t *fvp, uint32_t *cookiep, + struct fs_stream_info *stream_info, vnode_t **vpp, vnode_t **xattrdirvpp, + int flags, cred_t *cr, caller_context_t *ct) +{ + int nsize = MAXNAMELEN-1; + int error = 0; + ino64_t ino; + char *tmp_name; + vnode_t *xattrdirvp; + vnode_t *vp; + + if ((error = smb_vop_lookup_xattrdir(fvp, &xattrdirvp, LOOKUP_XATTR, + cr, ct)) != 0) + return (error); + + bzero(stream_info->name, sizeof (stream_info->name)); + stream_info->size = 0; + + tmp_name = kmem_zalloc(MAXNAMELEN, KM_SLEEP); + + for (;;) { + error = smb_vop_readdir(xattrdirvp, cookiep, tmp_name, &nsize, + &ino, &vp, NULL, flags | SMB_STREAM_RDDIR, cr, ct); + + if (error || (*cookiep == SMB_EOF)) + break; + + if (strncmp(tmp_name, SMB_STREAM_PREFIX, + SMB_STREAM_PREFIX_LEN)) { + VN_RELE(vp); + continue; + } + + tmp_name[nsize] = '\0'; + (void) strlcpy(stream_info->name, + &(tmp_name[SMB_STREAM_PREFIX_LEN]), + sizeof (stream_info->name)); + + nsize -= SMB_STREAM_PREFIX_LEN; + break; + } + + if ((error == 0) && nsize) { + if (vpp) + *vpp = vp; + else + VN_RELE(vp); + + if (xattrdirvpp) + *xattrdirvpp = xattrdirvp; + else + VN_RELE(xattrdirvp); + + } + + kmem_free(tmp_name, MAXNAMELEN); + + return (error); +} + +int +smb_vop_lookup_xattrdir(vnode_t *fvp, vnode_t **xattrdirvpp, int flags, + cred_t *cr, caller_context_t *ct) +{ + int error; + + error = VOP_LOOKUP(fvp, "", xattrdirvpp, NULL, flags, NULL, cr, ct, + NULL, NULL); + return (error); +} + +/* + * smb_vop_traverse_check() + * + * This function checks to see if the passed-in vnode has a file system + * mounted on it. If it does, the mount point is "traversed" and the + * vnode for the root of the file system is returned. + */ + +int +smb_vop_traverse_check(vnode_t **vpp) +{ + int error; + + if (vn_mountedvfs(*vpp) == 0) + return (0); + + /* + * traverse() may return a different held vnode, even in the error case. + * If it returns a different vnode, it will have released the original. + */ + + error = traverse(vpp); + + return (error); +} + +int /*ARGSUSED*/ +smb_vop_statfs(vnode_t *vp, struct statvfs64 *statp, cred_t *cr) +{ + int error; + + error = VFS_STATVFS(vp->v_vfsp, statp); + + return (error); +} + +/* + * smb_vop_acl_from_vsa + * + * Converts given vsecattr_t structure to a acl_t structure. + * + * The allocated memory for retuned acl_t should be freed by + * calling acl_free(). + */ +static acl_t * +smb_vop_acl_from_vsa(vsecattr_t *vsecattr, acl_type_t acl_type) +{ + int aclbsize = 0; /* size of acl list in bytes */ + int dfaclbsize = 0; /* size of default acl list in bytes */ + int numacls; + acl_t *acl_info; + + ASSERT(vsecattr); + + acl_info = acl_alloc(acl_type); + if (acl_info == NULL) + return (NULL); + + acl_info->acl_flags = 0; + + switch (acl_type) { + + case ACLENT_T: + numacls = vsecattr->vsa_aclcnt + vsecattr->vsa_dfaclcnt; + aclbsize = vsecattr->vsa_aclcnt * sizeof (aclent_t); + dfaclbsize = vsecattr->vsa_dfaclcnt * sizeof (aclent_t); + + acl_info->acl_cnt = numacls; + acl_info->acl_aclp = kmem_alloc(aclbsize + dfaclbsize, + KM_SLEEP); + (void) memcpy(acl_info->acl_aclp, vsecattr->vsa_aclentp, + aclbsize); + (void) memcpy((char *)acl_info->acl_aclp + aclbsize, + vsecattr->vsa_dfaclentp, dfaclbsize); + + if (acl_info->acl_cnt <= MIN_ACL_ENTRIES) + acl_info->acl_flags |= ACL_IS_TRIVIAL; + + break; + + case ACE_T: + aclbsize = vsecattr->vsa_aclcnt * sizeof (ace_t); + acl_info->acl_cnt = vsecattr->vsa_aclcnt; + acl_info->acl_flags = vsecattr->vsa_aclflags; + acl_info->acl_aclp = kmem_alloc(aclbsize, KM_SLEEP); + (void) memcpy(acl_info->acl_aclp, vsecattr->vsa_aclentp, + aclbsize); + if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0) + acl_info->acl_flags |= ACL_IS_TRIVIAL; + + break; + + default: + acl_free(acl_info); + return (NULL); + } + + if (aclbsize && vsecattr->vsa_aclentp) + kmem_free(vsecattr->vsa_aclentp, aclbsize); + if (dfaclbsize && vsecattr->vsa_dfaclentp) + kmem_free(vsecattr->vsa_dfaclentp, dfaclbsize); + + return (acl_info); +} + +/* + * smb_vop_acl_to_vsa + * + * Converts given acl_t structure to a vsecattr_t structure. + * + * IMPORTANT: + * Upon successful return the memory allocated for vsa_aclentp + * should be freed by calling kmem_free(). The size is returned + * in aclbsize. + */ +int +smb_vop_acl_to_vsa(acl_t *acl_info, vsecattr_t *vsecattr, int *aclbsize) +{ + int error = 0; + int numacls; + aclent_t *aclp; + + ASSERT(acl_info); + ASSERT(vsecattr); + ASSERT(aclbsize); + + bzero(vsecattr, sizeof (vsecattr_t)); + *aclbsize = 0; + + switch (acl_info->acl_type) { + case ACLENT_T: + numacls = acl_info->acl_cnt; + /* + * Minimum ACL size is three entries so might as well + * bail out here. Also limit request size to prevent user + * from allocating too much kernel memory. Maximum size + * is MAX_ACL_ENTRIES for the ACL part and MAX_ACL_ENTRIES + * for the default ACL part. + */ + if (numacls < 3 || numacls > (MAX_ACL_ENTRIES * 2)) { + error = EINVAL; + break; + } + + vsecattr->vsa_mask = VSA_ACL; + + vsecattr->vsa_aclcnt = numacls; + *aclbsize = numacls * sizeof (aclent_t); + vsecattr->vsa_aclentp = kmem_alloc(*aclbsize, KM_SLEEP); + (void) memcpy(vsecattr->vsa_aclentp, acl_info->acl_aclp, + *aclbsize); + + /* Sort the acl list */ + ksort((caddr_t)vsecattr->vsa_aclentp, + vsecattr->vsa_aclcnt, sizeof (aclent_t), cmp2acls); + + /* Break into acl and default acl lists */ + for (numacls = 0, aclp = vsecattr->vsa_aclentp; + numacls < vsecattr->vsa_aclcnt; + aclp++, numacls++) { + if (aclp->a_type & ACL_DEFAULT) + break; + } + + /* Find where defaults start (if any) */ + if (numacls < vsecattr->vsa_aclcnt) { + vsecattr->vsa_mask |= VSA_DFACL; + vsecattr->vsa_dfaclcnt = vsecattr->vsa_aclcnt - numacls; + vsecattr->vsa_dfaclentp = aclp; + vsecattr->vsa_aclcnt = numacls; + } + + /* Adjust if they're all defaults */ + if (vsecattr->vsa_aclcnt == 0) { + vsecattr->vsa_mask &= ~VSA_ACL; + vsecattr->vsa_aclentp = NULL; + } + + /* Only directories can have defaults */ + if (vsecattr->vsa_dfaclcnt && + (acl_info->acl_flags & ACL_IS_DIR)) { + error = ENOTDIR; + } + + break; + + case ACE_T: + if (acl_info->acl_cnt < 1 || + acl_info->acl_cnt > MAX_ACL_ENTRIES) { + error = EINVAL; + break; + } + + vsecattr->vsa_mask = VSA_ACE | VSA_ACE_ACLFLAGS; + vsecattr->vsa_aclcnt = acl_info->acl_cnt; + vsecattr->vsa_aclflags = acl_info->acl_flags & ACL_FLAGS_ALL; + *aclbsize = vsecattr->vsa_aclcnt * sizeof (ace_t); + vsecattr->vsa_aclentsz = *aclbsize; + vsecattr->vsa_aclentp = kmem_alloc(*aclbsize, KM_SLEEP); + (void) memcpy(vsecattr->vsa_aclentp, acl_info->acl_aclp, + *aclbsize); + + break; + + default: + error = EINVAL; + } + + return (error); +} + +/* + * smb_vop_acl_read + * + * Reads the ACL of the specified file into 'aclp'. + * acl_type is the type of ACL which the filesystem supports. + * + * Caller has to free the allocated memory for aclp by calling + * acl_free(). + */ +int +smb_vop_acl_read(vnode_t *vp, acl_t **aclp, int flags, acl_type_t acl_type, + cred_t *cr, caller_context_t *ct) +{ + int error; + vsecattr_t vsecattr; + + ASSERT(vp); + ASSERT(aclp); + + *aclp = NULL; + bzero(&vsecattr, sizeof (vsecattr_t)); + + switch (acl_type) { + case ACLENT_T: + vsecattr.vsa_mask = VSA_ACL | VSA_ACLCNT | VSA_DFACL | + VSA_DFACLCNT; + break; + + case ACE_T: + vsecattr.vsa_mask = VSA_ACE | VSA_ACECNT | VSA_ACE_ACLFLAGS; + break; + + default: + return (EINVAL); + } + + if (error = VOP_GETSECATTR(vp, &vsecattr, flags, cr, ct)) + return (error); + + *aclp = smb_vop_acl_from_vsa(&vsecattr, acl_type); + if (vp->v_type == VDIR) + (*aclp)->acl_flags |= ACL_IS_DIR; + + return (0); +} + +/* + * smb_vop_acl_write + * + * Writes the given ACL in aclp for the specified file. + */ +int +smb_vop_acl_write(vnode_t *vp, acl_t *aclp, int flags, cred_t *cr, + caller_context_t *ct) +{ + int error; + vsecattr_t vsecattr; + int aclbsize; + + ASSERT(vp); + ASSERT(aclp); + + error = smb_vop_acl_to_vsa(aclp, &vsecattr, &aclbsize); + + if (error == 0) { + (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); + error = VOP_SETSECATTR(vp, &vsecattr, flags, cr, ct); + VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); + } + + if (aclbsize && vsecattr.vsa_aclentp) + kmem_free(vsecattr.vsa_aclentp, aclbsize); + + return (error); +} + +/* + * smb_vop_acl_type + * + * Determines the ACL type for the given vnode. + * ACLENT_T is a Posix ACL and ACE_T is a ZFS ACL. + */ +acl_type_t +smb_vop_acl_type(vnode_t *vp) +{ + int error; + ulong_t whichacl; + + error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &whichacl, kcred, NULL); + if (error != 0) { + /* + * If we got an error, then the filesystem + * likely does not understand the _PC_ACL_ENABLED + * pathconf. In this case, we fall back to trying + * POSIX-draft (aka UFS-style) ACLs. + */ + whichacl = _ACL_ACLENT_ENABLED; + } + + if (!(whichacl & (_ACL_ACE_ENABLED | _ACL_ACLENT_ENABLED))) { + /* + * If the file system supports neither ACE nor + * ACLENT ACLs we will fall back to UFS-style ACLs + * like we did above if there was an error upon + * calling VOP_PATHCONF. + * + * ACE and ACLENT type ACLs are the only interfaces + * supported thus far. If any other bits are set on + * 'whichacl' upon return from VOP_PATHCONF, we will + * ignore them. + */ + whichacl = _ACL_ACLENT_ENABLED; + } + + if (whichacl == _ACL_ACLENT_ENABLED) + return (ACLENT_T); + + return (ACE_T); +} + +static int zfs_perms[] = { + ACE_READ_DATA, ACE_WRITE_DATA, ACE_APPEND_DATA, ACE_READ_NAMED_ATTRS, + ACE_WRITE_NAMED_ATTRS, ACE_EXECUTE, ACE_DELETE_CHILD, + ACE_READ_ATTRIBUTES, ACE_WRITE_ATTRIBUTES, ACE_DELETE, ACE_READ_ACL, + ACE_WRITE_ACL, ACE_WRITE_OWNER, ACE_SYNCHRONIZE +}; + +static int unix_perms[] = { VREAD, VWRITE, VEXEC }; +/* + * smb_vop_eaccess + * + * Returns the effective permission of the given credential for the + * specified object. + * + * This is just a workaround. We need VFS/FS support for this. + */ +void +smb_vop_eaccess(vnode_t *vp, int *mode, int flags, vnode_t *dir_vp, cred_t *cr) +{ + int error, i; + int pnum; + + *mode = 0; + + if (flags == V_ACE_MASK) { + pnum = sizeof (zfs_perms) / sizeof (int); + + for (i = 0; i < pnum; i++) { + error = smb_vop_access(vp, zfs_perms[i], flags, + dir_vp, cr); + if (error == 0) + *mode |= zfs_perms[i]; + } + } else { + pnum = sizeof (unix_perms) / sizeof (int); + + for (i = 0; i < pnum; i++) { + error = smb_vop_access(vp, unix_perms[i], flags, + dir_vp, cr); + if (error == 0) + *mode |= unix_perms[i]; + } + } +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_winpipe.c b/usr/src/uts/common/fs/smbsrv/smb_winpipe.c new file mode 100755 index 000000000000..361485783cc5 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_winpipe.c @@ -0,0 +1,431 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file contain routines to initialize the doors interfaces for + * CIFS winpipe calls. + */ + +#define START_UPDOOR_SIZE 16384 +#define START_INPIPE_SIZE 16384 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static door_handle_t *smb_winpipe_dh = NULL; +static uint64_t smb_winpipe_ncall = 0; +static kmutex_t smb_winpipe_mutex; +static kcondvar_t smb_winpipe_cv; + +static int smb_winpipe_upcall(mlsvc_pipe_t *, smb_dr_user_ctx_t *, + mlsvc_stream_t *, uint16_t, uint32_t, unsigned char *, smb_pipe_t *); + +static smb_dr_user_ctx_t *smb_winpipe_ctx_alloc(struct smb_request *); +static void smb_winpipe_ctx_free(smb_dr_user_ctx_t *); +static uint8_t *smb_winpipe_ctx_mkselfrel(smb_dr_user_ctx_t *, uint32_t *); + + +void +smb_winpipe_init(void) +{ + mutex_init(&smb_winpipe_mutex, NULL, MUTEX_DEFAULT, NULL); + cv_init(&smb_winpipe_cv, NULL, CV_DEFAULT, NULL); +} + +void +smb_winpipe_fini(void) +{ + smb_winpipe_close(); + cv_destroy(&smb_winpipe_cv); + mutex_destroy(&smb_winpipe_mutex); +} + +int +smb_winpipe_open(void) +{ + door_handle_t *dh; + int rc; + + mutex_enter(&smb_winpipe_mutex); + + if (smb_winpipe_dh == NULL) { + dh = kmem_zalloc(sizeof (door_handle_t), KM_SLEEP); + + rc = door_ki_open(SMB_WINPIPE_DOOR_UP_PATH, dh); + if (rc) { + kmem_free(dh, sizeof (door_handle_t)); + mutex_exit(&smb_winpipe_mutex); + cmn_err(CE_WARN, "smb_winpipe_open: rc=%d", rc); + return (-1); + } + + smb_winpipe_ncall = 0; + smb_winpipe_dh = dh; + } + + mutex_exit(&smb_winpipe_mutex); + return (0); +} + +void +smb_winpipe_close(void) +{ + mutex_enter(&smb_winpipe_mutex); + + while (smb_winpipe_ncall > 0) + cv_wait(&smb_winpipe_cv, &smb_winpipe_mutex); + + if (smb_winpipe_dh) { + kmem_free(smb_winpipe_dh, sizeof (door_handle_t)); + smb_winpipe_dh = NULL; + } + + mutex_exit(&smb_winpipe_mutex); +} + +/* + * Winpipe call interface: called by smb_rpc_transact and smb_rpc_read. + * Serialization and call reference accounting handled here. + * + * The sr will be null on a flush operation, which will result in ctx + * being null. A null ctx must be handled by smb_winpipe_upcall. + */ +int +smb_winpipe_call(struct smb_request *sr, + mlsvc_pipe_t *pi, + mlsvc_stream_t *streamin, + uint16_t call_type, + uint32_t *nbytes) +{ + smb_dr_user_ctx_t *ctx; + unsigned char *lbuf; + smb_pipe_t *pp; + int rc; + + mutex_enter(&smb_winpipe_mutex); + + if (smb_winpipe_dh == NULL) { + mutex_exit(&smb_winpipe_mutex); + return (-1); + } + + ++smb_winpipe_ncall; + mutex_exit(&smb_winpipe_mutex); + + lbuf = kmem_zalloc(START_UPDOOR_SIZE, KM_SLEEP); + pp = kmem_zalloc(START_INPIPE_SIZE, KM_SLEEP); + ctx = smb_winpipe_ctx_alloc(sr); + + rc = smb_winpipe_upcall(pi, ctx, streamin, call_type, *nbytes, + lbuf, pp); + + if (rc == 0) { + switch (call_type) { + case SMB_RPC_TRANSACT: + case SMB_RPC_READ: + case SMB_RPC_FLUSH: + *nbytes = pp->sp_datalen; + break; + + default: + /* + * A write just queues the data and returns. + */ + break; + } + } + + smb_winpipe_ctx_free(ctx); + kmem_free(pp, START_INPIPE_SIZE); + kmem_free(lbuf, START_UPDOOR_SIZE); + + mutex_enter(&smb_winpipe_mutex); + --smb_winpipe_ncall; + cv_signal(&smb_winpipe_cv); + mutex_exit(&smb_winpipe_mutex); + return (rc); +} + +/* + * Door upcall wrapper - handles data marshalling. + * This function should only be called by smb_winpipe_call. + */ +static int +smb_winpipe_upcall(mlsvc_pipe_t *pipe_info, + smb_dr_user_ctx_t *user_ctx, + mlsvc_stream_t *streamin, + uint16_t call_type, + uint32_t req_cnt, + unsigned char *lbuf, + smb_pipe_t *pp) +{ + door_arg_t da; + int user_ctx_bytes; + mlsvc_door_hdr_t mdhin, mdhout; + smb_pipe_t newpipe; + int total_bytes = 0; + int cnt; + int bytes_off = 0; + ulong_t save_resid; + uint32_t tmp_resid; + uint8_t *user_ctx_selfrel; + + /* + * copy the pipe hdr into flat buf, this contains the thread id, + * version and a couple of reserved fields for future expansion + */ + mdhin.md_tid = (uint64_t)curthread->t_did; + mdhin.md_version = SMB_MLSVC_DOOR_VERSION; + /* + * rpc_transact, rpc_read or rpc_write + */ + mdhin.md_call_type = call_type; + mdhin.md_length = req_cnt; + mdhin.md_reserved = 0; + bcopy(&mdhin.md_tid, lbuf, sizeof (uint64_t)); + bytes_off += sizeof (uint64_t); + bcopy(&mdhin.md_version, lbuf + bytes_off, sizeof (uint16_t)); + bytes_off += sizeof (uint16_t); + bcopy(&mdhin.md_call_type, lbuf + bytes_off, sizeof (uint16_t)); + bytes_off += sizeof (uint16_t); + bcopy(&mdhin.md_length, lbuf + bytes_off, sizeof (uint32_t)); + bytes_off += sizeof (uint32_t); + bcopy(&mdhin.md_reserved, lbuf + bytes_off, sizeof (uint64_t)); + bytes_off += sizeof (uint64_t); + total_bytes = bytes_off; + + /* + * Most of the marshalling isn't needed for flush. + * The pipe-id is needed to find the rpc_context and + * free the input and output pipes. + */ + if (call_type == SMB_RPC_FLUSH) { + bcopy(&pipe_info->fid, lbuf + total_bytes, sizeof (uint32_t)); + total_bytes += sizeof (uint32_t); + } else { + user_ctx_selfrel = smb_winpipe_ctx_mkselfrel(user_ctx, + (uint32_t *)&cnt); + + if (user_ctx_selfrel == NULL) { + return (-1); + } + + bcopy(user_ctx_selfrel, lbuf + total_bytes, cnt); + kmem_free(user_ctx_selfrel, cnt); + total_bytes += cnt; + /* + * based on uio stuff and smb_pipe_t size + * calculate size of buffer needed + */ + newpipe.sp_pipeid = pipe_info->fid; + (void) strlcpy(newpipe.sp_pipename, pipe_info->pipe_name, + SMB_MAX_PIPENAMELEN); + bcopy(newpipe.sp_pipename, lbuf + total_bytes, + SMB_MAX_PIPENAMELEN); + bcopy(&newpipe.sp_pipeid, lbuf + total_bytes + + SMB_MAX_PIPENAMELEN, sizeof (uint32_t)); + total_bytes += sizeof (uint32_t) + SMB_MAX_PIPENAMELEN; + } + + /* copy the pipe data len into flat buf */ + if ((mdhin.md_call_type == SMB_RPC_TRANSACT) || + (mdhin.md_call_type == SMB_RPC_WRITE)) { + /* we only want the least 4 significant bytes here */ + tmp_resid = (uint32_t)streamin->uio.uio_resid; + bcopy(&tmp_resid, lbuf + total_bytes, sizeof (uint32_t)); + total_bytes += sizeof (uint32_t); + save_resid = streamin->uio.uio_resid; + (void) uiomove((caddr_t)(lbuf + total_bytes), + streamin->uio.uio_resid, UIO_WRITE, &streamin->uio); + total_bytes += (save_resid - streamin->uio.uio_resid); + } else if (mdhin.md_call_type == SMB_RPC_READ) { + bzero(lbuf + total_bytes, sizeof (uint32_t)); + total_bytes += sizeof (uint32_t); + } + + da.data_ptr = (char *)lbuf; + da.data_size = total_bytes; + da.desc_ptr = NULL; + da.desc_num = 0; + da.rbuf = (char *)lbuf; + da.rsize = START_UPDOOR_SIZE; + + if (door_ki_upcall(*smb_winpipe_dh, &da) != 0) { + return (-1); + } + /* RPC_WRITE just queues the data and returns */ + if (mdhin.md_call_type == SMB_RPC_WRITE) { + return (0); + } + bytes_off = 0; + bcopy(da.data_ptr, &mdhout.md_tid, sizeof (uint64_t)); + bytes_off += sizeof (uint64_t); + bcopy(da.data_ptr+bytes_off, &mdhout.md_version, sizeof (uint16_t)); + bytes_off += sizeof (uint16_t); + bcopy(da.data_ptr+bytes_off, &mdhout.md_call_type, sizeof (uint16_t)); + bytes_off += sizeof (uint16_t); + bcopy(da.data_ptr+bytes_off, &mdhout.md_length, sizeof (uint32_t)); + bytes_off += sizeof (uint32_t); + bcopy(da.data_ptr+bytes_off, &mdhout.md_reserved, sizeof (uint64_t)); + bytes_off += sizeof (uint64_t); + user_ctx_bytes = 0; + total_bytes = user_ctx_bytes + bytes_off; + + bzero(pp, START_INPIPE_SIZE); + bcopy(da.data_ptr+total_bytes, pp->sp_pipename, SMB_MAX_PIPENAMELEN); + total_bytes += SMB_MAX_PIPENAMELEN; + bcopy(da.data_ptr+total_bytes, &(pp->sp_pipeid), sizeof (uint32_t)); + total_bytes += sizeof (uint32_t); + bcopy(da.data_ptr + total_bytes, &(pp->sp_datalen), sizeof (uint32_t)); + total_bytes += sizeof (uint32_t); + + if (pp->sp_datalen > 0) { + pipe_info->outlen = pp->sp_datalen; + pipe_info->output = kmem_alloc(pipe_info->outlen, KM_SLEEP); + bcopy((char *)(da.data_ptr + total_bytes), + pipe_info->output, pipe_info->outlen); + } + + return (0); +} + + +/* + * Allocate a user context structure and initialize it based on the + * specified SMB request data. Resources allocated here must be + * released using smb_winpipe_ctx_free. + * + * If sr is null, a null pointer is returned. + */ +static smb_dr_user_ctx_t * +smb_winpipe_ctx_alloc(struct smb_request *sr) +{ + smb_session_t *session; + smb_user_t *user; + smb_dr_user_ctx_t *ctx; + + if (sr == NULL) + return (NULL); + + user = sr->uid_user; + session = user->u_session; + + ASSERT(user); + ASSERT(user->u_domain); + ASSERT(user->u_name); + ASSERT(session); + ASSERT(session->workstation); + + ctx = kmem_zalloc(sizeof (smb_dr_user_ctx_t), KM_SLEEP); + + ctx->du_session_id = session->s_kid; + ctx->du_native_os = session->native_os; + ctx->du_ipaddr = session->ipaddr; + ctx->du_uid = user->u_uid; + ctx->du_logon_time = user->u_logon_time; + ctx->du_flags = user->u_flags; + + ctx->du_domain_len = user->u_domain_len; + ctx->du_domain = kmem_alloc(ctx->du_domain_len, KM_SLEEP); + (void) strlcpy(ctx->du_domain, user->u_domain, ctx->du_domain_len); + + ctx->du_account_len = user->u_name_len; + ctx->du_account = kmem_alloc(ctx->du_account_len, KM_SLEEP); + (void) strlcpy(ctx->du_account, user->u_name, ctx->du_account_len); + + ctx->du_workstation_len = strlen(session->workstation) + 1; + ctx->du_workstation = kmem_alloc(ctx->du_workstation_len, + KM_SLEEP); + (void) strlcpy(ctx->du_workstation, session->workstation, + ctx->du_workstation_len); + + return (ctx); +} + + +/* + * Free resources associated with a user context structure. + */ +static void +smb_winpipe_ctx_free(smb_dr_user_ctx_t *ctx) +{ + if (ctx == NULL) + return; + + ASSERT(ctx->du_domain); + ASSERT(ctx->du_account); + ASSERT(ctx->du_workstation); + + kmem_free(ctx->du_domain, ctx->du_domain_len); + kmem_free(ctx->du_account, ctx->du_account_len); + kmem_free(ctx->du_workstation, ctx->du_workstation_len); + kmem_free(ctx, sizeof (smb_dr_user_ctx_t)); +} + +/* + * Convert a user context structure from absolute to self-relative format. + * + * On success, a pointer to an allocated XDR encoded buffer is returned, + * with the buffer size in ret_len. The caller is responsible for freeing + * this buffer when it is no longer required. If the return value is NULL, + * it is not valid to interpret ret_len. + */ +static uint8_t * +smb_winpipe_ctx_mkselfrel(smb_dr_user_ctx_t *ctx, uint32_t *ret_len) +{ + XDR xdrs; + uint8_t *buf; + uint32_t len; + + if (ctx == NULL || ret_len == NULL) { + return (NULL); + } + + len = xdr_sizeof(xdr_smb_dr_user_ctx_t, ctx); + buf = kmem_zalloc(len, KM_SLEEP); + xdrmem_create(&xdrs, (const caddr_t)buf, len, XDR_ENCODE); + + if (!xdr_smb_dr_user_ctx_t(&xdrs, ctx)) { + kmem_free(buf, len); + len = 0; + buf = NULL; + } + + xdr_destroy(&xdrs); + *ret_len = len; + return (buf); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_write.c b/usr/src/uts/common/fs/smbsrv/smb_write.c new file mode 100644 index 000000000000..e6874397b459 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_write.c @@ -0,0 +1,565 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include + + +#define SMB_WRMODE_WRITE_THRU 0x0001 +#define SMB_WRMODE_IS_STABLE(M) ((M) & SMB_WRMODE_WRITE_THRU) + + +typedef struct smb_write_param { + struct vardata_block w_vdb; + uint64_t w_offset; + uint16_t w_mode; + uint16_t w_count; +} smb_write_param_t; + + +int smb_write_common(struct smb_request *sr, smb_write_param_t *param); +int smb_write_truncate(struct smb_request *sr, smb_write_param_t *param); +int smb_set_file_size(struct smb_request *sr); + + +/* + * Write count bytes at the specified offset in a file. The offset is + * limited to 32-bits. If the count is zero, the file is truncated to + * the length specified by the offset. + * + * The response count indicates the actual number of bytes written, which + * will equal the requested count on success. If request and response + * counts differ but there is no error, the client will assume that the + * server encountered a resource issue. + */ +int +smb_com_write(struct smb_request *sr) +{ + smb_write_param_t *param; + uint32_t off; + int rc; + + param = kmem_zalloc(sizeof (smb_write_param_t), KM_SLEEP); + + rc = smbsr_decode_vwv(sr, "wwl", &sr->smb_fid, ¶m->w_count, &off); + if (rc != 0) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + param->w_offset = (uint64_t)off; + param->w_vdb.uio.uio_offset = param->w_offset; + + if (param->w_count == 0) { + rc = smb_write_truncate(sr, param); + } else { + rc = smbsr_decode_data(sr, "D", ¶m->w_vdb); + + if ((rc != 0) || (param->w_vdb.len != param->w_count)) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + param->w_vdb.uio.uio_offset = param->w_offset; + + rc = smb_write_common(sr, param); + } + + if (rc != 0) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + smbsr_encode_result(sr, 1, 0, "bww", 1, param->w_count, 0); + kmem_free(param, sizeof (smb_write_param_t)); + return (SDRC_NORMAL_REPLY); +} + +/* + * Write count bytes to a file and then close the file. This function + * can only be used to write to 32-bit offsets and the client must set + * WordCount (6 or 12) correctly in order to locate the data to be + * written. If an error occurs on the write, the file should still be + * closed. If Count is 0, the file is truncated (or extended) to offset. + * + * If the last_write time is non-zero, last_write should be used to set + * the mtime. Otherwise the file system stamps the mtime. Failure to + * set mtime should not result in an error response. + */ +int +smb_com_write_and_close(struct smb_request *sr) +{ + smb_write_param_t *param; + uint32_t last_write; + uint32_t off; + int rc = 0; + + param = kmem_zalloc(sizeof (smb_write_param_t), KM_SLEEP); + + if (sr->smb_wct == 12) { + rc = smbsr_decode_vwv(sr, "wwll12.", &sr->smb_fid, + ¶m->w_count, &off, &last_write); + } else { + rc = smbsr_decode_vwv(sr, "wwll", &sr->smb_fid, + ¶m->w_count, &off, &last_write); + } + + if (rc != 0) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + param->w_offset = (uint64_t)off; + + if (param->w_count == 0) { + rc = smb_write_truncate(sr, param); + } else { + /* + * There may be a bug here: should this be "3.#B"? + */ + rc = smbsr_decode_data(sr, ".#B", param->w_count, + ¶m->w_vdb); + + if ((rc != 0) || (param->w_vdb.len != param->w_count)) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + param->w_vdb.uio.uio_offset = param->w_offset; + + rc = smb_write_common(sr, param); + } + + if (rc != 0) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + if ((rc = smb_common_close(sr, last_write)) != 0) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + smbsr_encode_result(sr, 1, 0, "bww", 1, param->w_count, 0); + kmem_free(param, sizeof (smb_write_param_t)); + return (SDRC_NORMAL_REPLY); +} + +/* + * Write count bytes to a file at the specified offset and then unlock + * them. Write behind is safe because the client should have the range + * locked and this request is allowed to extend the file - note that + * offest is limited to 32-bits. It is an error for count to be zero. + * + * The SmbLockAndRead/SmbWriteAndUnlock sub-dialect is only valid on disk + * files. Reject any attempt to use it on other shares. + * + * The response count indicates the actual number of bytes written, which + * will equal the requested count on success. If request and response + * counts differ but there is no error, the client will assume that the + * server encountered a resource issue. + */ +int +smb_com_write_and_unlock(struct smb_request *sr) +{ + smb_write_param_t *param; + uint32_t off; + uint32_t result; + uint16_t remcnt; + int rc = 0; + + if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) { + smbsr_raise_error(sr, ERRDOS, ERRnoaccess); + /* NOTREACHED */ + } + + param = kmem_zalloc(sizeof (smb_write_param_t), KM_SLEEP); + + rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, ¶m->w_count, &off, + &remcnt); + if (rc != 0) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + if (param->w_count == 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + rc = smbsr_decode_data(sr, "D", ¶m->w_vdb); + + if ((rc != 0) || (param->w_count != param->w_vdb.len)) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + param->w_offset = (uint64_t)off; + param->w_vdb.uio.uio_offset = (off_t)param->w_offset; + + if ((rc = smb_write_common(sr, param)) != 0) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + + result = smb_unlock_range(sr, sr->fid_ofile->f_node, param->w_offset, + (uint64_t)param->w_count); + if (result != NT_STATUS_SUCCESS) { + kmem_free(param, sizeof (smb_write_param_t)); + smb_unlock_range_raise_error(sr, result); + /* NOTREACHED */ + } + + smbsr_encode_result(sr, 1, 0, "bww", 1, param->w_count, 0); + kmem_free(param, sizeof (smb_write_param_t)); + return (SDRC_NORMAL_REPLY); +} + +/* + * Write bytes to a file (SMB Core). This request was extended in + * LM 0.12 to support 64-bit offsets, indicated by sending a wct of + * 14, instead of 12, and including additional offset information. + * + * A ByteCount of 0 does not truncate the file - use SMB_COM_WRITE + * to truncate a file. A zero length merely transfers zero bytes. + * + * If bit 0 of WriteMode is set, Fid must refer to a disk file and + * the data must be on stable storage before responding. + */ +int +smb_com_write_andx(struct smb_request *sr) +{ + smb_write_param_t *param; + uint32_t off_low; + uint32_t off_high; + uint16_t data_offset; + uint16_t remcnt; + int rc = 0; + + param = kmem_zalloc(sizeof (smb_write_param_t), KM_SLEEP); + + if (sr->smb_wct == 14) { + rc = smbsr_decode_vwv(sr, "4.wl4.ww2.wwl", &sr->smb_fid, + &off_low, ¶m->w_mode, &remcnt, ¶m->w_count, + &data_offset, &off_high); + + data_offset -= 63; + param->w_offset = ((uint64_t)off_high << 32) | off_low; + } else { + rc = smbsr_decode_vwv(sr, "4.wl4.ww2.ww", &sr->smb_fid, + &off_low, ¶m->w_mode, &remcnt, ¶m->w_count, + &data_offset); + + param->w_offset = (uint64_t)off_low; + data_offset -= 59; + } + + if (rc != 0) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + if (SMB_WRMODE_IS_STABLE(param->w_mode) && + STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_raise_error(sr, ERRSRV, ERRaccess); + /* NOTREACHED */ + } + + rc = smbsr_decode_data(sr, "#.#B", data_offset, param->w_count, + ¶m->w_vdb); + if ((rc != 0) || (param->w_vdb.len != param->w_count)) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + param->w_vdb.uio.uio_offset = param->w_offset; + + if (param->w_count != 0) { + if ((rc = smb_write_common(sr, param)) != 0) { + kmem_free(param, sizeof (smb_write_param_t)); + smbsr_raise_errno(sr, rc); + /* NOTREACHED */ + } + } + + smbsr_encode_result(sr, 6, 0, "bb1.ww6.w", + 6, sr->andx_com, 15, param->w_count, 0); + + kmem_free(param, sizeof (smb_write_param_t)); + return (SDRC_NORMAL_REPLY); +} + +/* + * Common function for writing files or IPC/MSRPC named pipes. + * + * Returns errno values. + */ +int +smb_write_common(struct smb_request *sr, smb_write_param_t *param) +{ + struct smb_ofile *ofile = sr->fid_ofile; + smb_node_t *node; + uint32_t stability = FSSTAB_UNSTABLE; + uint32_t lcount; + int rc = 0; + + switch (sr->tid_tree->t_res_type & STYPE_MASK) { + case STYPE_DISKTREE: + node = ofile->f_node; + + if (node->attr.sa_vattr.va_type != VDIR) { + rc = smb_lock_range_access(sr, node, param->w_offset, + param->w_count, FILE_WRITE_DATA); + if (rc != NT_STATUS_SUCCESS) { + smbsr_raise_cifs_error(sr, rc, + ERRSRV, ERRaccess); + /* NOTREACHED */ + } + } + + if (SMB_WRMODE_IS_STABLE(param->w_mode) || + (node->flags & NODE_FLAGS_WRITE_THROUGH)) { + stability = FSSTAB_FILE_SYNC; + } + + rc = smb_fsop_write(sr, sr->user_cr, node, + ¶m->w_vdb.uio, &lcount, &node->attr, &stability); + + if (rc) + return (rc); + + node->flags |= NODE_FLAGS_SYNCATIME; + + if (node->flags & NODE_FLAGS_SET_SIZE) { + if ((param->w_offset + lcount) >= node->n_size) { + node->flags &= ~NODE_FLAGS_SET_SIZE; + node->n_size = param->w_offset + lcount; + } + } + + param->w_count = (uint16_t)lcount; + break; + + case STYPE_IPC: + param->w_count = (uint16_t)param->w_vdb.uio.uio_resid; + + if ((rc = smb_rpc_write(sr, ¶m->w_vdb.uio)) != 0) + param->w_count = 0; + break; + + default: + rc = EACCES; + break; + } + + if (rc != 0) + return (rc); + + mutex_enter(&ofile->f_mutex); + ofile->f_seek_pos = param->w_offset + param->w_count; + mutex_exit(&ofile->f_mutex); + return (rc); +} + +/* + * Truncate a disk file to the specified offset. + * Typically, w_count will be zero here. + * + * Returns errno values. + */ +int +smb_write_truncate(struct smb_request *sr, smb_write_param_t *param) +{ + struct smb_ofile *ofile = sr->fid_ofile; + smb_node_t *node = ofile->f_node; + int rc; + + if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) + return (0); + + if (node->attr.sa_vattr.va_type != VDIR) { + rc = smb_lock_range_access(sr, node, param->w_offset, + param->w_count, FILE_WRITE_DATA); + if (rc != NT_STATUS_SUCCESS) { + smbsr_raise_cifs_error(sr, rc, + ERRSRV, ERRaccess); + /* NOTREACHED */ + } + } + + /* + * XXX what if the file has been opened only with + * FILE_APPEND_DATA? + */ + rc = smb_ofile_access(ofile, sr->user_cr, FILE_WRITE_DATA); + if (rc != NT_STATUS_SUCCESS) { + smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + /* NOTREACHED */ + } + + node->flags |= NODE_FLAGS_SET_SIZE; + node->n_size = param->w_offset; + + if ((rc = smb_set_file_size(sr)) != 0) + return (rc); + + mutex_enter(&ofile->f_mutex); + ofile->f_seek_pos = param->w_offset + param->w_count; + mutex_exit(&ofile->f_mutex); + return (0); +} + +/* + * Set the file size using the value in the node. The file will only be + * updated if NODE_FLAGS_SET_SIZE is set. It is safe to pass a null node + * pointer, we just return success. + * + * The node attributes are refreshed here from the file system. So any + * attributes that are affected by file size changes, i.e. the mtime, + * will be current. + * + * Note that smb_write_andx cannot be used to reduce the file size so, + * if this is required, smb_write is called with a count of zero and + * the appropriate file length in offset. The file should be resized + * to the length specified by the offset. + * + * Returns 0 on success. Otherwise returns EACCES. + */ +int +smb_set_file_size(struct smb_request *sr) +{ + struct smb_node *node; + smb_attr_t new_attr; + uint32_t dosattr; + + if ((node = sr->fid_ofile->f_node) == 0) + return (0); + + if ((node->flags & NODE_FLAGS_SET_SIZE) == 0) + return (0); + + node->flags &= ~NODE_FLAGS_SET_SIZE; + + dosattr = smb_node_get_dosattr(node); + + if (dosattr & SMB_FA_READONLY) { + if (((node->flags & NODE_FLAGS_CREATED) == 0) || + (sr->session->s_kid != node->n_orig_session_id)) + return (EACCES); + } + + bzero(&new_attr, sizeof (new_attr)); + new_attr.sa_vattr.va_size = node->n_size; + new_attr.sa_mask = SMB_AT_SIZE; + + (void) smb_fsop_setattr(sr, sr->user_cr, node, &new_attr, + &node->attr); + + return (0); +} + +/* + * write_complete is sent acknowledge completion of raw write requests. + * We never send raw write commands to other servers so, if we receive a + * write_complete, we treat it as an error. + */ +int +smb_com_write_complete(struct smb_request *sr) +{ + smbsr_decode_error(sr); + /* NOT REACHED */ + return (0); +} + +/* + * The Write Block Multiplexed protocol is used to maximize performance + * when writing a large block of data. + * + * The mpx sub protocol is not supported because we support only + * connection oriented transports and NT supports SMB_COM_READ_MPX + * only over connectionless transports. + */ +int /*ARGSUSED*/ +smb_com_write_mpx(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} + +int /*ARGSUSED*/ +smb_com_write_mpx_secondary(struct smb_request *sr) +{ + return (SDRC_UNIMPLEMENTED); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_write_raw.c b/usr/src/uts/common/fs/smbsrv/smb_write_raw.c new file mode 100644 index 000000000000..5d02179cc286 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_write_raw.c @@ -0,0 +1,606 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB: write_raw + * 5.27 WRITE_RAW: Write Raw Bytes + * + * The Write Block Raw protocol is used to maximize the performance of + * writing a large block of data from the client to the server. The Write + * Block Raw command's scope includes files, Named Pipes, and spooled + * output (can be used in place COM_WRITE_PRINT_FILE ). + * + * Client Request Description + * ========================== ========================================= + * + * UCHAR WordCount; Count of parameter words = 12 + * USHORT Fid; File handle + * USHORT Count; Total bytes, including this buffer + * USHORT Reserved; + * ULONG Offset; Offset in file to begin write + * ULONG Timeout; + * USHORT WriteMode; Write mode: + * bit 0 - complete write to disk and send + * final result response + * bit 1 - return Remaining (pipe/dev) + * (see WriteAndX for #defines) + * ULONG Reserved2; + * USHORT DataLength; Number of data bytes this buffer + * USHORT DataOffset; Offset (from header start) to data + * USHORT ByteCount; Count of data bytes + * UCHAR Pad[]; Pad to SHORT or LONG + * UCHAR Data[]; Data (# = DataLength) + * + * First Server Response Description + * ============================== ===================================== + * + * UCHAR WordCount; Count of parameter words = 1 + * USHORT Remaining; Bytes remaining to be read if pipe + * USHORT ByteCount; Count of data bytes = 0 + * + * Final Server Response Description + * ================================== ================================= + * + * UCHAR Command (in SMB header) SMB_COM_WRITE_COMPLETE + * + * UCHAR WordCount; Count of parameter words = 1 + * USHORT Count; Total number of bytes written + * USHORT ByteCount; Count of data bytes = 0 + * + * The first response format will be that of the final server response in + * the case where the server gets an error while writing the data sent + * along with the request. Thus Count is the number of bytes which did get + * written any time an error is returned. If an error occurs after the + * first response has been sent allowing the client to send the remaining + * data, the final response should not be sent unless write through is set. + * Rather the server should return this "write behind" error on the next + * access to the Fid. + * + * The client must guarantee that there is (and will be) no other request + * on the connection for the duration of this request. The server will + * reserve enough resources to receive the data and respond with a response + * SMB as defined above. The client then sends the raw data in one send. + * Thus the server is able to receive up to 65,535 bytes of data directly + * into the server buffer. The amount of data transferred is expected to + * be larger than the negotiated buffer size for this protocol. + * + * The reason that no other requests can be active on the connection for + * the duration of the request is that if other receives are present on the + * connection, there is normally no way to guarantee that the data will be + * received into the correct server buffer, rather the data may fill one + * (or more) of the other buffers. Also if the client is sending other + * requests on the connection, a request may land in the buffer that the + * server has allocated for the this SMB's data. + * + * Whether or not SMB_COM_WRITE_RAW is supported is returned in the + * response to SMB_COM_NEGOTIATE. SMB_COM_WRITE_RAW is not supported for + * connectionless clients. + * + * When write through is not specified ((WriteMode & 01) == 0) this SMB is + * assumed to be a form of write behind. The transport layer guarantees + * delivery of all secondary requests from the client. Thus no "got the + * data you sent" SMB is needed. If an error should occur at the server + * end, all bytes must be received and thrown away. If an error occurs + * while writing data to disk such as disk full, the next access of the + * file handle (another write, close, read, etc.) will return the fact that + * the error occurred. + * + * If write through is specified ((WriteMode & 01) != 0), the server will + * receive the data, write it to disk and then send a final response + * indicating the result of the write. The total number of bytes written + * is also returned in this response in the Count field. + * + * The flow for the SMB_COM_WRITE_RAW SMB is: + * + * client -----> SMB_COM_WRITE_RAW request (optional data) >-------> server + * client <------------------< OK send (more) data <---------------- server + * client ----------------------> raw data >----------------------> server + * client <---< data on disk or error (write through only) <------- server + * + * This protocol is set up such that the SMB_COM_WRITE_RAW request may also + * carry data. This is an optimization in that up to the server's buffer + * size (MaxCount from SMB_COM_NEGOTIATE response), minus the size of the + * SMB_COM_WRITE_RAW SMB request, may be sent along with the request. Thus + * if the server is busy and unable to support the raw write of the + * remaining data, the data sent along with the request has been delivered + * and need not be sent again. The server will write any data sent in the + * request (and wait for it to be on the disk or device if write through is + * set), prior to sending the response. + * + * The specific responses error class ERRSRV, error codes ERRusempx and + * ERRusestd, indicate that the server is temporarily out of the resources + * + * needed to support the raw write of the remaining data, but that any data + * sent along with the request has been successfully written. The client + * should then write the remaining data using a different type of SMB write + * request, or delay and retry using SMB_COM_WRITE_RAW. If a write error + * occurs writing the initial data, it will be returned and the write raw + * request is implicitly denied. + * + * The return field Remaining is returned for named pipes only. It is used + * to return the number of bytes currently available in the pipe. This + * information can then be used by the client to know when a subsequent + * (non blocking) read of the pipe may return some data. Of course when + * the read request is actually received by the server there may be more or + * less actual data in the pipe (more data has been written to the pipe / + * device or another reader drained it). If the information is currently + * not available or the request is NOT for a pipe or the server does not + * support this feature, a -1 value should be returned. + * + * If the negotiated dialect is NT LM 0.12 or later, and the response to + * the SMB_COM_NEGOTIATE SMB has CAP_LARGE_FILES set in the Capabilities + * field, an additional request format is allowed which accommodates very + * large files having 64 bit offsets: + * + * Client Request Description + * ================================== ================================= + * UCHAR WordCount; Count of parameter words = 14 + * USHORT Fid; File handle + * USHORT Count; Total bytes, including this + * buffer + * USHORT Reserved; + * ULONG Offset; Offset in file to begin write + * ULONG Timeout; + * USHORT WriteMode; Write mode: + * bit 0 - complete write to disk + * and send final result response + * bit 1 - return Remaining + * (pipe/dev) + * ULONG Reserved2; + * USHORT DataLength; Number of data bytes this buffer + * USHORT DataOffset; Offset (from header start) to + * data + * ULONG OffsetHigh; Upper 32 bits of offset + * USHORT ByteCount; Count of data bytes + * UCHAR Pad[]; Pad to SHORT or LONG + * UCHAR Data[]; Data (# = DataLength) + * + * In this case the final offset in the file is formed by combining + * OffsetHigh and Offset, the resulting offset must not be negative. + */ + +#include +#include +#include +#include +#include + +extern uint32_t smb_keep_alive; + +static int smb_write_raw_helper(struct smb_request *sr, struct uio *uiop, + unsigned int stability, offset_t *offp, uint32_t *lcountp); + +static int smb_transfer_write_raw_data(smb_request_t *sr, + uint16_t addl_xfer_count); + +#define WR_MODE_WR_THRU 1 + +int +smb_com_write_raw(struct smb_request *sr) +{ + int rc = 0; + int session_send_rc = 0; + unsigned short addl_xfer_count; + unsigned short count; + unsigned short write_mode, data_offset, data_length; + offset_t off; + uint32_t off_low, off_high, timeout; + uint32_t lcount = 0; + uint32_t addl_lcount = 0; + struct uio uio; + iovec_t iovec; + unsigned int stability; + struct mbuf_chain reply; + smb_node_t *fnode; + + if (sr->session->s_state != SMB_SESSION_STATE_WRITE_RAW_ACTIVE) { + return (SDRC_DROP_VC); + } + + if (sr->smb_wct == 12) { + off_high = 0; + rc = smbsr_decode_vwv(sr, "ww2.llw4.ww", &sr->smb_fid, &count, + &off_low, &timeout, &write_mode, &data_length, + &data_offset); + data_offset -= 59; + } else { + rc = smbsr_decode_vwv(sr, "ww2.llw4.wwl", &sr->smb_fid, &count, + &off_low, &timeout, &write_mode, &data_length, + &data_offset, &off_high); + data_offset -= 63; + } + + if (rc != 0) { + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + off = ((offset_t)off_high << 32) | off_low; + addl_xfer_count = count - data_length; + + sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); + if (sr->fid_ofile == NULL) { + smbsr_raise_cifs_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + /* NOTREACHED */ + } + + fnode = sr->fid_ofile->f_node; + stability = ((write_mode & WR_MODE_WR_THRU) || + (fnode->flags & NODE_FLAGS_WRITE_THROUGH)) ? + FSSTAB_FILE_SYNC : FSSTAB_UNSTABLE; + + if (STYPE_ISDSK(sr->tid_tree->t_res_type)) { + /* + * See comments in smb_write.c + */ + if (fnode->attr.sa_vattr.va_type != VDIR) { + rc = smb_lock_range_access(sr, fnode, off, + count, FILE_WRITE_DATA); + if (rc != NT_STATUS_SUCCESS) { + smbsr_raise_cifs_error(sr, rc, + ERRSRV, ERRaccess); + /* NOTREACHED */ + } + } + } + + /* + * Make sure any raw write data that is supposed to be + * contained in this SMB is actually present. + */ + if (sr->smb_data.chain_offset + data_offset + data_length > + sr->smb_data.max_bytes) { + /* Error handling code will wake up the session daemon */ + smbsr_decode_error(sr); + /* NOTREACHED */ + } + + /* + * Init uio (resid will get filled in later) + */ + uio.uio_iov = &iovec; + uio.uio_iovcnt = 1; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_loffset = off; + + /* + * Send response if there is additional data to transfer. This + * will prompt the client to send the remaining data. + */ + if (addl_xfer_count != 0) { + MBC_INIT(&reply, MLEN); + (void) smb_encode_mbc(&reply, SMB_HEADER_ED_FMT "bww", + sr->first_smb_com, + sr->smb_rcls, + sr->smb_reh, + sr->smb_err, + sr->smb_flg | SMB_FLAGS_REPLY, + sr->smb_flg2, + sr->smb_pid_high, + sr->smb_sig, + sr->smb_tid, + sr->smb_pid, + sr->smb_uid, + sr->smb_mid, 1, -1, 0); + + if (sr->session->signing.flags & SMB_SIGNING_ENABLED) + smb_sign_reply(sr, &reply); + + session_send_rc = smb_session_send(sr->session, 0, &reply); + + /* + * If the session response failed we're not going to + * return an error just yet -- we can still write the + * data we received along with the SMB even if the + * response failed. If it failed, we need to force the + * stability level to "write-through". + */ + stability = + (session_send_rc == 0) ? stability : FSSTAB_FILE_SYNC; + } + + /* + * While the response is in flight (and the data begins to arrive) + * write out the first data segment. Start by setting up the + * iovec list for the first transfer. + */ + iovec.iov_base = sr->smb_data.chain->m_data + + sr->smb_data.chain_offset + data_offset; + iovec.iov_len = data_length; + uio.uio_resid = data_length; + + /* + * smb_write_raw_helper will call smb_rpc_write or + * smb_fsop_write as appropriate, handle the NODE_FLAGS_SET_SIZE + * flag (if set) and update the other f_node fields. It's possible + * that data_length may be 0 for this transfer but we still want + * process it since it will update the file state (seek position, + * file size (possibly), etc). + */ + rc = smb_write_raw_helper(sr, &uio, stability, &off, &lcount); + + /* + * If our initial session response failed then we're done. Return + * failure. The client will know we wrote some of the data because + * of the transfer count (count - lcount) in the response. + */ + if (session_send_rc != 0) { + sr->smb_rcls = ERRSRV; + sr->smb_err = ERRusestd; + goto write_raw_transfer_failed; + } + + /* + * If we have more data to read then go get it + */ + if (addl_xfer_count) { + /* + * This is the only place where a worker thread should + * directly read from the session socket. If the data + * is read successfully then the buffer (sr->sr_raw_data_buf) + * will need to be freed after the data is written. + */ + if (smb_transfer_write_raw_data(sr, addl_xfer_count) != 0) { + /* + * Raw data transfer failed + */ + goto write_raw_transfer_failed; + } + + /* + * Fill in next iov entry + */ + iovec.iov_base = sr->sr_raw_data_buf; + iovec.iov_len = addl_xfer_count; + uio.uio_resid = addl_xfer_count; + } + + /* + * Wake up session daemon since we now have all of our data and + * it's safe for the session daemon to resume processing SMB's. + */ + sr->session->s_write_raw_status = 0; + sr->session->s_state = SMB_SESSION_STATE_NEGOTIATED; + + /* + * If we didn't write all the data from the first segment then + * there's not much point in continuing (we still wanted to + * read any additional data above since we don't necessarily + * want to drop the connection and we need to read through + * to the next SMB). + */ + if ((rc != 0) || (lcount != data_length)) { + goto notify_write_raw_complete; + } + + /* + * Write any additional data + */ + if (addl_xfer_count) { + rc = smb_write_raw_helper(sr, &uio, stability, &off, + &addl_lcount); + } + + /* + * If we were called in "Write-behind" mode ((write_mode & 1) == 0) + * and the transfer was successful then we don't need to send + * any further response. If we were called in "Write-Through" mode + * ((write_mode & 1) == 1) or if the transfer failed we need to + * send a completion notification. The "count" value will indicate + * whether the transfer was successful. + */ + if ((rc != 0) || (write_mode & WR_MODE_WR_THRU) || + (lcount + addl_lcount != count)) { + goto notify_write_raw_complete; + } + + /* + * Free raw write buffer (allocated in smb_transfer_write_raw_data) + */ + kmem_free(sr->sr_raw_data_buf, sr->sr_raw_data_length); + + (void) smb_session_send(sr->session, SESSION_KEEP_ALIVE, NULL); + return (SDRC_NO_REPLY); + +write_raw_transfer_failed: + /* + * Raw data transfer failed, wake up session + * daemon + */ + sr->session->s_write_raw_status = 20; + sr->session->s_state = SMB_SESSION_STATE_NEGOTIATED; + +notify_write_raw_complete: + /* + * If we had an error fill in the appropriate error code + */ + if (rc != 0) { + (void) smbsr_set_errno(sr, rc); + } + /* + * Free raw write buffer if present (from smb_transfer_write_raw_data) + */ + if (sr->sr_raw_data_buf != NULL) { + kmem_free(sr->sr_raw_data_buf, sr->sr_raw_data_length); + } + /* Write complete notification */ + sr->first_smb_com = SMB_COM_WRITE_COMPLETE; + smbsr_encode_result(sr, 1, 0, "bww", 1, + count - (lcount + addl_lcount), 0); + return (SDRC_NORMAL_REPLY); +} + + + +/* + * smb_write_raw_helper + * + * This function will call smb_rpc_write or smb_fsop_write as appropriate, + * handle the NODE_FLAGS_SET_SIZE flag (if set) and update the other f_node + * fields. It's possible that data_length may be 0 for this transfer but + * we still want process it since it will update the file state (seek + * position, file size (possibly), etc). + * + * Returns 0 for success, non-zero for failure + */ +static int +smb_write_raw_helper(struct smb_request *sr, struct uio *uiop, + unsigned int stability, offset_t *offp, uint32_t *lcountp) +{ + smb_node_t *fnode; + int rc = 0; + + if (STYPE_ISIPC(sr->tid_tree->t_res_type)) { + *lcountp = uiop->uio_resid; + + if ((rc = smb_rpc_write(sr, uiop)) != 0) + *lcountp = 0; + } else { + fnode = sr->fid_ofile->f_node; + rc = smb_fsop_write(sr, sr->user_cr, fnode, + uiop, lcountp, &fnode->attr, &stability); + + if (rc == 0) { + + fnode->flags |= NODE_FLAGS_SYNCATIME; + + if (fnode->flags & NODE_FLAGS_SET_SIZE) { + if ((*offp + *lcountp) >= fnode->n_size) { + fnode->flags &= ~NODE_FLAGS_SET_SIZE; + fnode->n_size = *offp + *lcountp; + } + } + } + } + + *offp += *lcountp; + mutex_enter(&sr->fid_ofile->f_mutex); + sr->fid_ofile->f_seek_pos = *offp; + mutex_exit(&sr->fid_ofile->f_mutex); + + return (rc); +} + + +/* + * smb_handle_write_raw + * + * Called from smb_session_daemon() when the SMB command is SMB_COM_WRITE_RAW. + * Dispatches the command to the worker thread and waits until the worker + * has completed processing the command. + * + * Returns 0 for success, non-zero for failure + */ +int +smb_handle_write_raw(smb_session_t *session, smb_request_t *sr) +{ + int drop_reason = 0; + + /* + * Set flag to indicate that we are waiting for raw data. The + * worker thread will actually retrieve the raw data directly + * from the socket. This should be the only case when a worker + * thread reads from the session socket. When the data is read + * the worker will clear the flag. + */ + smb_rwx_rwenter(&session->s_lock, RW_WRITER); + switch (session->s_state) { + case SMB_SESSION_STATE_NEGOTIATED: + case SMB_SESSION_STATE_OPLOCK_BREAKING: + session->s_state = SMB_SESSION_STATE_WRITE_RAW_ACTIVE; + smb_rwx_rwexit(&session->s_lock); + sr->sr_state = SMB_REQ_STATE_SUBMITTED; + (void) taskq_dispatch(smb_info.thread_pool, smb_session_worker, + sr, TQ_SLEEP); + smb_rwx_rwenter(&session->s_lock, RW_READER); + while (session->s_state == SMB_SESSION_STATE_WRITE_RAW_ACTIVE) { + (void) smb_rwx_rwwait(&session->s_lock, -1); + } + drop_reason = session->s_write_raw_status; + break; + default: + drop_reason = 21; + break; + } + smb_rwx_rwexit(&session->s_lock); + return (drop_reason); +} + +/* + * smb_transfer_write_raw_data + * + * Handles the second transfer phase of SMB_COM_WRITE_RAW. smb_com_write_raw() + * will process the parameters and data from the SMB and send the initial + * SMB response. This function reads the remaining data from the socket + * as it arrives from the client. + * + * Clients may send KEEP_ALIVE messages (when using NBT) between the first + * and second parts of write raw requests. The only session transport + * types accepted here are SESSION_MESSAGE or SESSION_KEEP_ALIVE. + * + * Returns 0 for success, non-zero for failure + */ +int +smb_transfer_write_raw_data(smb_request_t *sr, uint16_t addl_xfer_count) +{ + smb_session_t *session = sr->session; + smb_xprt_t hdr; + uint8_t *data_buf; + + do { + if (smb_session_xprt_gethdr(session, &hdr) != 0) + return (-1); + + if ((hdr.xh_type == SESSION_MESSAGE) || + (hdr.xh_type == SESSION_KEEP_ALIVE)) { + session->keep_alive = smb_keep_alive; + } else { + return (-1); + } + } while (hdr.xh_type == SESSION_KEEP_ALIVE); + + if (hdr.xh_length < addl_xfer_count) { + /* + * Less data than we were expecting. + */ + return (-1); + } + + data_buf = kmem_alloc(hdr.xh_length, KM_SLEEP); + + if (smb_sorecv(session->sock, data_buf, hdr.xh_length) != 0) { + kmem_free(data_buf, hdr.xh_length); + sr->sr_raw_data_buf = NULL; + sr->sr_raw_data_length = 0; + return (-1); + } + + sr->sr_raw_data_buf = data_buf; + sr->sr_raw_data_length = hdr.xh_length; + return (0); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_xlate.c b/usr/src/uts/common/fs/smbsrv/smb_xlate.c new file mode 100644 index 000000000000..ffe1aa199de1 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_xlate.c @@ -0,0 +1,250 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +struct xlate_table { + int code; + char *str; +}; + +struct xlate_table smb_xlate_com[] = { + { SMB_COM_CREATE_DIRECTORY, "CREATE_DIRECTORY" }, + { SMB_COM_DELETE_DIRECTORY, "DELETE_DIRECTORY" }, + { SMB_COM_OPEN, "OPEN" }, + { SMB_COM_CREATE, "COM_CREATE" }, + { SMB_COM_CLOSE, "CLOSE" }, + { SMB_COM_FLUSH, "FLUSH" }, + { SMB_COM_DELETE, "DELETE" }, + { SMB_COM_RENAME, "RENAME" }, + { SMB_COM_QUERY_INFORMATION, "QUERY_INFORMATION" }, + { SMB_COM_SET_INFORMATION, "SET_INFORMATION" }, + { SMB_COM_READ, "READ" }, + { SMB_COM_WRITE, "WRITE" }, + { SMB_COM_LOCK_BYTE_RANGE, "LOCK_BYTE_RANGE" }, + { SMB_COM_UNLOCK_BYTE_RANGE, "UNLOCK_BYTE_RANGE" }, + { SMB_COM_CREATE_TEMPORARY, "CREATE_TEMPORARY" }, + { SMB_COM_CREATE_NEW, "CREATE_NEW" }, + { SMB_COM_CHECK_DIRECTORY, "CHECK_DIRECTORY" }, + { SMB_COM_PROCESS_EXIT, "PROCESS_EXIT" }, + { SMB_COM_SEEK, "SEEK" }, + { SMB_COM_LOCK_AND_READ, "LOCK_AND_READ" }, + { SMB_COM_WRITE_AND_UNLOCK, "WRITE_AND_UNLOCK" }, + { SMB_COM_READ_RAW, "READ_RAW" }, + { SMB_COM_READ_MPX, "READ_MPX" }, + { SMB_COM_READ_MPX_SECONDARY, "READ_MPX_SECONDARY" }, + { SMB_COM_WRITE_RAW, "WRITE_RAW" }, + { SMB_COM_WRITE_MPX, "WRITE_MPX" }, + { SMB_COM_WRITE_COMPLETE, "WRITE_COMPLETE" }, + { SMB_COM_SET_INFORMATION2, "SET_INFORMATION2" }, + { SMB_COM_QUERY_INFORMATION2, "QUERY_INFORMATION2" }, + { SMB_COM_LOCKING_ANDX, "LOCKING_ANDX" }, + { SMB_COM_TRANSACTION, "TRANSACTION" }, + { SMB_COM_TRANSACTION_SECONDARY, "TRANSACTION_SECONDARY" }, + { SMB_COM_IOCTL, "IOCTL" }, + { SMB_COM_IOCTL_SECONDARY, "IOCTL_SECONDARY" }, + { SMB_COM_COPY, "COPY" }, + { SMB_COM_MOVE, "MOVE" }, + { SMB_COM_ECHO, "ECHO" }, + { SMB_COM_WRITE_AND_CLOSE, "WRITE_AND_CLOSE" }, + { SMB_COM_OPEN_ANDX, "OPEN_ANDX" }, + { SMB_COM_READ_ANDX, "READ_ANDX" }, + { SMB_COM_WRITE_ANDX, "WRITE_ANDX" }, + { SMB_COM_CLOSE_AND_TREE_DISC, "CLOSE_AND_TREE_DISC" }, + { SMB_COM_TRANSACTION2, "TRANSACTION2" }, + { SMB_COM_TRANSACTION2_SECONDARY, "TRANSACTION2_SECONDARY" }, + { SMB_COM_FIND_CLOSE2, "FIND_CLOSE2" }, + { SMB_COM_FIND_NOTIFY_CLOSE, "FIND_NOTIFY_CLOSE" }, + { SMB_COM_TREE_CONNECT, "TREE_CONNECT" }, + { SMB_COM_TREE_DISCONNECT, "TREE_DISCONNECT" }, + { SMB_COM_NEGOTIATE, "NEGOTIATE" }, + { SMB_COM_SESSION_SETUP_ANDX, "SESSION_SETUP_ANDX" }, + { SMB_COM_LOGOFF_ANDX, "LOGOFF_ANDX" }, + { SMB_COM_TREE_CONNECT_ANDX, "TREE_CONNECT_ANDX" }, + { SMB_COM_QUERY_INFORMATION_DISK, "QUERY_INFORMATION_DISK" }, + { SMB_COM_SEARCH, "SEARCH" }, + { SMB_COM_FIND, "FIND" }, + { SMB_COM_FIND_UNIQUE, "FIND_UNIQUE" }, + { SMB_COM_NT_TRANSACT, "NT_TRANSACT" }, + { SMB_COM_NT_TRANSACT_SECONDARY, "NT_TRANSACT_SECONDARY" }, + { SMB_COM_NT_CREATE_ANDX, "NT_CREATE_ANDX" }, + { SMB_COM_NT_CANCEL, "NT_CANCEL" }, + { SMB_COM_OPEN_PRINT_FILE, "OPEN_PRINT_FILE" }, + { SMB_COM_WRITE_PRINT_FILE, "WRITE_PRINT_FILE" }, + { SMB_COM_CLOSE_PRINT_FILE, "CLOSE_PRINT_FILE" }, + { SMB_COM_GET_PRINT_QUEUE, "GET_PRINT_QUEUE" }, + { 0 } +}; + +struct xlate_table smb_xlate_rcls[] = { + { SUCCESS, "SUCCESS" }, + { ERRDOS, "ERRDOS" }, + { ERRSRV, "ERRSRV" }, + { ERRHRD, "ERRHRD" }, + { ERRCMD, "ERRCMD" }, + { 0 } +}; + +struct xlate_table smb_xlate_errdos[] = { + { ERRbadfunc, "ERRbadfunc" }, + { ERRbadfile, "ERRbadfile" }, + { ERRbadpath, "ERRbadpath" }, + { ERRnofids, "ERRnofids" }, + { ERRnoaccess, "ERRnoaccess" }, + { ERRbadfid, "ERRbadfid" }, + { ERRbadmcb, "ERRbadmcb" }, + { ERRnomem, "ERRnomem" }, + { ERRbadmem, "ERRbadmem" }, + { ERRbadenv, "ERRbadenv" }, + { ERRbadformat, "ERRbadformat" }, + { ERRbadaccess, "ERRbadaccess" }, + { ERRbaddata, "ERRbaddata" }, + { ERRbaddrive, "ERRbaddrive" }, + { ERRremcd, "ERRremcd" }, + { ERRdiffdevice, "ERRdiffdevice" }, + { ERRnofiles, "ERRnofiles" }, + { ERRbadshare, "ERRbadshare" }, + { ERRlock, "ERRlock" }, + { ERRfilexists, "ERRfilexists" }, + { ERRbadpipe, "ERRbadpipe" }, + { ERRpipebusy, "ERRpipebusy" }, + { ERRpipeclosing, "ERRpipeclosing" }, + { ERRnotconnected, "ERRnotconnected" }, + { ERRmoredata, "ERRmoredata" }, + { 0 } +}; + +struct xlate_table smb_xlate_errsrv[] = { + { ERRerror, "ERRerror" }, + { ERRbadpw, "ERRbadpw" }, + { ERRaccess, "ERRaccess" }, + { ERRinvnid, "ERRinvnid" }, + { ERRinvnetname, "ERRinvnetname" }, + { ERRinvdevice, "ERRinvdevice" }, + { ERRqfull, "ERRqfull" }, + { ERRqtoobig, "ERRqtoobig" }, + { ERRqeof, "ERRqeof" }, + { ERRinvpfid, "ERRinvpfid" }, + { ERRsmbcmd, "ERRsmbcmd" }, + { ERRsrverror, "ERRsrverror" }, + { ERRfilespecs, "ERRfilespecs" }, + { ERRbadpermits, "ERRbadpermits" }, + { ERRsetattrmode, "ERRsetattrmode" }, + { ERRpaused, "ERRpaused" }, + { ERRmsgoff, "ERRmsgoff" }, + { ERRnoroom, "ERRnoroom" }, + { ERRrmuns, "ERRrmuns" }, + { ERRtimeout, "ERRtimeout" }, + { ERRnoresource, "ERRnoresource" }, + { ERRtoomanyuids, "ERRtoomanyuids" }, + { ERRbaduid, "ERRbaduid" }, + { ERRusempx, "ERRusempx" }, + { ERRusestd, "ERRusestd" }, + { ERRcontmpx, "ERRcontmpx" }, + { ERRnosupport, "ERRnosupport" }, + { 0 } +}; + +struct xlate_table smb_xlate_errhrd[] = { + { ERRnowrite, "ERRnowrite" }, + { ERRbadunit, "ERRbadunit" }, + { ERRnotready, "ERRnotready" }, + { ERRbadcmd, "ERRbadcmd" }, + { ERRdata, "ERRdata" }, + { ERRbadreq, "ERRbadreq" }, + { ERRseek, "ERRseek" }, + { ERRbadmedia, "ERRbadmedia" }, + { ERRbadsector, "ERRbadsector" }, + { ERRnopaper, "ERRnopaper" }, + { ERRwrite, "ERRwrite" }, + { ERRread, "ERRread" }, + { ERRgeneral, "ERRgeneral" }, + { ERRbadshare, "ERRbadshare" }, + { ERRlock, "ERRlock" }, + { ERRwrongdisk, "ERRwrongdisk" }, + { ERRFCBUnavail, "ERRFCBUnavail" }, + { ERRsharebufexc, "ERRsharebufexc" }, + { 0 } +}; + +struct xlate_table smb_xlate_dialect[] = { + { DIALECT_UNKNOWN, "DIALECT_UNKNOWN" }, + { PC_NETWORK_PROGRAM_1_0, "PC NETWORK PROGRAM 1.0" }, + { PCLAN1_0, "PCLAN1.0" }, + { MICROSOFT_NETWORKS_1_03, "MICROSOFT NETWORKS 1.03" }, + { MICROSOFT_NETWORKS_3_0, "MICROSOFT NETWORKS 3.0" }, + { LANMAN1_0, "LANMAN1.0" }, + { LM1_2X002, "LM1.2X002" }, + { DOS_LM1_2X002, "DOS LM1.2X002" }, + { DOS_LANMAN2_1, "DOS LANMAN2.1" }, + { LANMAN2_1, "LANMAN2.1" }, + { Windows_for_Workgroups_3_1a, "Windows for Workgroups 3.1a" }, + { NT_LM_0_12, "NT LM 0.12" }, + { 0 } +}; + +static char * +smb_xlate_cd_to_str(struct xlate_table *xl, int cd) +{ + static char no_answer[32]; + + for (; xl->str; xl++) + if (xl->code == cd) + return (xl->str); + + (void) sprintf(no_answer, "-%x-", cd); + + return (no_answer); +} + +static int +smb_xlate_str_to_cd(struct xlate_table *xl, char *str) +{ + for (; xl->str; xl++) + if (strcmp(xl->str, str) == 0) + return (xl->code); + return (-1); +} + + +char * +smb_xlate_com_cd_to_str(int com) +{ + return (smb_xlate_cd_to_str(smb_xlate_com, com)); +} + +char * +smb_xlate_dialect_cd_to_str(int dialect) +{ + return (smb_xlate_cd_to_str(smb_xlate_dialect, dialect)); +} + +int +smb_xlate_dialect_str_to_cd(char *str) +{ + return (smb_xlate_str_to_cd(smb_xlate_dialect, str)); +} diff --git a/usr/src/uts/common/fs/smbsrv/smbsrv.conf b/usr/src/uts/common/fs/smbsrv/smbsrv.conf new file mode 100644 index 000000000000..11ecccbccf01 --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smbsrv.conf @@ -0,0 +1,27 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +name="smbsrv" parent="pseudo"; diff --git a/usr/src/uts/common/fs/sockfs/nl7c.c b/usr/src/uts/common/fs/sockfs/nl7c.c index 19900ed654ff..002d111c3aba 100644 --- a/usr/src/uts/common/fs/sockfs/nl7c.c +++ b/usr/src/uts/common/fs/sockfs/nl7c.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -31,21 +31,21 @@ * (Hypertext Transfer Protocol, see HTTP/1.1 RFC2616) in a semantically * transparent manner. * - * Neither the requesting user agent (client, e.g. web broweser) nor the + * Neither the requesting user agent (client, e.g. web browser) nor the * origin server (e.g. webserver) that provided the response cached by * NL7C are impacted in any way. * * Note, currently NL7C only processes HTTP messages via the embedded * URI of scheme http (not https nor any other), additional scheme are - * intended to be supproted as is practical such that much of the NL7C - * framework may appear more gerneral purpose then would be needed just + * intended to be supported as is practical such that much of the NL7C + * framework may appear more general purpose then would be needed just * for an HTTP gateway cache. * * NL7C replaces NCA (Network Cache and Accelerator) and in the future * NCAS (NCA/SSL). * * Further, NL7C uses all NCA configuration files, see "/etc/nca/", the - * NCA socket API, "AF_NCA", and "ndd /dev/nca" for backwards compatability. + * NCA socket API, "AF_NCA", and "ndd /dev/nca" for backwards compatibility. */ #include @@ -351,7 +351,7 @@ inet_atob(char *s, nl7c_addr_t *p) * IPaddr - an IPv4 numeric dot address (e.g. 192.168.84.71) or '*' for * INADDR_ANY, or an IPv6 numeric address or "::" for IN6ADDR_ANY. * - * / - IPaddr/Port seperator. + * / - IPaddr/Port separator. * * Port - a TCP decimal port number. * @@ -517,7 +517,7 @@ ncaportconf_read(void) if (addrp != NULL) { kmem_free(addrp, sizeof (*addrp)); } - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); } @@ -620,7 +620,7 @@ ncakmodconf_read(void) } done: - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); } @@ -797,7 +797,7 @@ ncalogdconf_read(void) /* Opening delimiter, skip */ /*EMPTY*/; } else if (c == '"' || c == ' ') { - /* List delim or filename seperator */ + /* List delim or filename separator */ *fnvp++ = strdup(file); fp = file; } else if (fp < &file[sizeof (file) - 1]) { @@ -820,7 +820,7 @@ ncalogdconf_read(void) } done: - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); if (nl7c_logd_enabled) { @@ -912,7 +912,7 @@ nl7c_init() * * 2) URI scheme not reqcognized. * - * 3) A request which can't be procesed. + * 3) A request which can't be processed. * * 4) A request which could be processed but NL7C dosen't currently have * the response data. In which case NL7C will parse the returned response @@ -980,9 +980,9 @@ nl7c_process(struct sonode *so, boolean_t nonblocking) * First time through, if no data left over from a previous * kstrgetmsg() then try to get some, else just process it. * - * Thereafter, rmp = NULL after the successfull kstrgetmsg() + * Thereafter, rmp = NULL after the successful kstrgetmsg() * so try to get some new data and append to list (i.e. until - * enough fragments are collected for a successfull parse). + * enough fragments are collected for a successful parse). */ if (rmp == NULL) { diff --git a/usr/src/uts/common/fs/sockfs/nl7clogd.c b/usr/src/uts/common/fs/sockfs/nl7clogd.c index f7a1a8afe93a..1580a08c6cac 100644 --- a/usr/src/uts/common/fs/sockfs/nl7clogd.c +++ b/usr/src/uts/common/fs/sockfs/nl7clogd.c @@ -57,7 +57,7 @@ static void logit_flush(void *); * in the "/var/nca" directory. * * NL7C reuses the NCA logging APIs defined in , at - * some future date (when NCA is depricated or improvements are needed) + * some future date (when NCA is deprecated or improvements are needed) * these need to be moved into NL7C. * * NL7C implements logging differently in 2 ways, 1st the initialization @@ -278,7 +278,7 @@ logd_log_write(kmutex_t *lock, log_buf_t *lbp) mutex_exit(lock); /* Close current file */ ret = VOP_CLOSE(nca_fio_vp(&fio), FCREAT|FWRITE|FAPPEND|FTRUNC, - 1, (offset_t)0, kcred); + 1, (offset_t)0, kcred, NULL); nca_fio_vp(&fio) = NULL; if (ret) { cmn_err(CE_WARN, "nl7c_logd: close of %s failed (error %d)", @@ -319,13 +319,13 @@ logd_log_write(kmutex_t *lock, log_buf_t *lbp) /* Turn on directio */ (void) VOP_IOCTL(nca_fio_vp(&fio), _FIODIRECTIO, - DIRECTIO_ON, 0, kcred, NULL); + DIRECTIO_ON, 0, kcred, NULL, NULL); /* Start writing from the begining of the file */ nca_fio_offset(&fio) = 0; /* Remove the current symlink */ - (void) VOP_REMOVE(nca_fio_dvp(&fio), symlink, kcred); + (void) VOP_REMOVE(nca_fio_dvp(&fio), symlink, kcred, NULL, 0); attr.va_mask = AT_MODE | AT_TYPE; attr.va_mode = 0777; @@ -333,7 +333,7 @@ logd_log_write(kmutex_t *lock, log_buf_t *lbp) /* Create symlink to the new log file */ ret = VOP_SYMLINK(nca_fio_dvp(&fio), symlink, - &attr, nca_fio_name(&fio), kcred); + &attr, nca_fio_name(&fio), kcred, NULL, 0); if (ret) { cmn_err(CE_WARN, "nl7c_logd: symlink of %s to %s failed", symlink, nca_fio_name(&fio)); @@ -469,9 +469,9 @@ nl7c_logd_init(int fsz, caddr_t *fnv) uio.uio_segflg = UIO_SYSSPACE; uio.uio_loffset = 0; uio.uio_fmode = 0; - ret = VOP_READLINK(svp, &uio, kcred); + ret = VOP_READLINK(svp, &uio, kcred, NULL); if (ret) { - (void) VOP_REMOVE(dvp, symlink, kcred); + (void) VOP_REMOVE(dvp, symlink, kcred, NULL, 0); goto fresh_start; } @@ -498,9 +498,9 @@ nl7c_logd_init(int fsz, caddr_t *fnv) goto error; } nca_fio_vp(&fio) = vp; - (void) VOP_IOCTL(vp, _FIODIRECTIO, DIRECTIO_ON, 0, kcred, NULL); + (void) VOP_IOCTL(vp, _FIODIRECTIO, DIRECTIO_ON, 0, kcred, NULL, NULL); attr.va_mask = AT_SIZE; - ret = VOP_GETATTR(nca_fio_vp(&fio), &attr, 0, 0); + ret = VOP_GETATTR(nca_fio_vp(&fio), &attr, 0, NULL, NULL); if (ret) { cmn_err(CE_WARN, "nl7c_logd_init: getattr of %s failed", *fnp); goto error; @@ -520,8 +520,9 @@ nl7c_logd_init(int fsz, caddr_t *fnv) attr.va_mask = AT_MODE | AT_TYPE; attr.va_mode = 0777; attr.va_type = VLNK; - (void) VOP_REMOVE(dvp, symlink, kcred); - ret = VOP_SYMLINK(dvp, symlink, &attr, nca_fio_name(&fio), kcred); + (void) VOP_REMOVE(dvp, symlink, kcred, NULL, 0); + ret = VOP_SYMLINK(dvp, symlink, &attr, nca_fio_name(&fio), kcred, NULL, + 0); if (ret) { cmn_err(CE_WARN, "nl7c_logd_init: symlink of %s to %s failed", symlink_path, nca_fio_name(&fio)); @@ -537,7 +538,7 @@ nl7c_logd_init(int fsz, caddr_t *fnv) /* Turn on directio */ (void) VOP_IOCTL(nca_fio_vp(&fio), _FIODIRECTIO, - DIRECTIO_ON, 0, kcred, NULL); + DIRECTIO_ON, 0, kcred, NULL, NULL); finish: log_buf_kmc = kmem_cache_create("NL7C_log_buf_kmc", sizeof (log_buf_t), diff --git a/usr/src/uts/common/fs/sockfs/socksctp.c b/usr/src/uts/common/fs/sockfs/socksctp.c index fbaf68195e6f..68212f5d15d8 100644 --- a/usr/src/uts/common/fs/sockfs/socksctp.c +++ b/usr/src/uts/common/fs/sockfs/socksctp.c @@ -403,7 +403,7 @@ sosctp_create(vnode_t *accessvp, int domain, int type, int protocol, */ if ((cr = CRED()) == NULL) cr = kcred; - if ((error = VOP_OPEN(&vp, soflags, cr)) != 0) { + if ((error = VOP_OPEN(&vp, soflags, cr, NULL)) != 0) { VN_RELE(vp); *errorp = error; return (NULL); @@ -457,7 +457,7 @@ sosctp_free(struct sonode *so) mp->b_next = NULL; nso = *(struct sonode **)mp->b_rptr; - (void) VOP_CLOSE(SOTOV(nso), 0, 1, 0, CRED()); + (void) VOP_CLOSE(SOTOV(nso), 0, 1, 0, CRED(), NULL); vn_invalid(SOTOV(nso)); VN_RELE(SOTOV(nso)); @@ -531,7 +531,7 @@ sosctp_accept(struct sonode *lso, int fflag, struct sonode **nsop) if (error != 0) { vnode_t *nvp; nvp = SOTOV(nso); - (void) VOP_CLOSE(nvp, 0, 1, 0, CRED()); + (void) VOP_CLOSE(nvp, 0, 1, 0, CRED(), NULL); VN_RELE(nvp); /* @@ -1104,7 +1104,7 @@ sosctp_uiomove(mblk_t *hdr_mp, ssize_t count, ssize_t blk_size, int wroff, /* * As a message can be splitted up and sent in different * packets, each mblk will have the extra space before - * data to accomodate what SCTP wants to put in there. + * data to accommodate what SCTP wants to put in there. */ while ((mp = allocb_cred(size + wroff, cr)) == NULL) { if ((uiop->uio_fmode & (FNDELAY|FNONBLOCK)) || diff --git a/usr/src/uts/common/fs/sockfs/socksctpvnops.c b/usr/src/uts/common/fs/sockfs/socksctpvnops.c index ea3c49ce6eae..214cf4f859f9 100644 --- a/usr/src/uts/common/fs/sockfs/socksctpvnops.c +++ b/usr/src/uts/common/fs/sockfs/socksctpvnops.c @@ -64,19 +64,21 @@ /* * SCTP sockfs vnode operations */ -static int socksctpv_open(struct vnode **, int, struct cred *); +static int socksctpv_open(struct vnode **, int, struct cred *, + caller_context_t *); static int socksctpv_close(struct vnode *, int, int, offset_t, - struct cred *); + struct cred *, caller_context_t *); static int socksctpv_read(struct vnode *, struct uio *, int, struct cred *, - struct caller_context *); + caller_context_t *); static int socksctpv_write(struct vnode *, struct uio *, int, struct cred *, - struct caller_context *); + caller_context_t *); static int socksctpv_ioctl(struct vnode *, int, intptr_t, int, - struct cred *, int32_t *); -static int socksctp_setfl(vnode_t *, int, int, cred_t *); -static void socksctpv_inactive(struct vnode *, struct cred *); + struct cred *, int32_t *, caller_context_t *); +static int socksctp_setfl(vnode_t *, int, int, cred_t *, caller_context_t *); +static void socksctpv_inactive(struct vnode *, struct cred *, + caller_context_t *); static int socksctpv_poll(struct vnode *, short, int, short *, - struct pollhead **); + struct pollhead **, caller_context_t *); const fs_operation_def_t socksctp_vnodeops_template[] = { VOPNAME_OPEN, { .vop_open = socksctpv_open }, @@ -98,8 +100,10 @@ const fs_operation_def_t socksctp_vnodeops_template[] = { }; struct vnodeops *socksctp_vnodeops; +/*ARGSUSED3*/ static int -socksctpv_open(struct vnode **vpp, int flag, struct cred *cr) +socksctpv_open(struct vnode **vpp, int flag, struct cred *cr, + caller_context_t *ct) { struct sonode *so; struct sctp_sonode *ss; @@ -157,7 +161,7 @@ socksctpv_open(struct vnode **vpp, int flag, struct cred *cr) /*ARGSUSED*/ static int socksctpv_close(struct vnode *vp, int flag, int count, offset_t offset, - struct cred *cr) + struct cred *cr, caller_context_t *ct) { struct sonode *so; struct sctp_sonode *ss; @@ -244,7 +248,7 @@ socksctpv_close(struct vnode *vp, int flag, int count, offset_t offset, /*ARGSUSED2*/ static int socksctpv_read(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cr, - struct caller_context *ct) + caller_context_t *ct) { struct sonode *so = VTOSO(vp); struct nmsghdr lmsg; @@ -267,7 +271,7 @@ socksctpv_read(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cr, /*ARGSUSED2*/ static int socksctpv_write(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cr, - struct caller_context *ct) + caller_context_t *ct) { struct sctp_sonode *ss; struct sonode *so; @@ -361,7 +365,7 @@ socksctpv_write(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cr, /*ARGSUSED4*/ static int socksctpv_ioctl(struct vnode *vp, int cmd, intptr_t arg, int mode, - struct cred *cr, int32_t *rvalp) + struct cred *cr, int32_t *rvalp, caller_context_t *ct) { struct sonode *so; struct sctp_sonode *ss; @@ -720,7 +724,8 @@ socksctpv_ioctl(struct vnode *vp, int cmd, intptr_t arg, int mode, */ /* ARGSUSED */ static int -socksctp_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr) +socksctp_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr, + caller_context_t *ct) { struct sonode *so; @@ -741,7 +746,7 @@ socksctp_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr) /*ARGSUSED*/ static void -socksctpv_inactive(struct vnode *vp, struct cred *cr) +socksctpv_inactive(struct vnode *vp, struct cred *cr, caller_context_t *ct) { struct sonode *so; struct sctp_sonode *ss; @@ -804,9 +809,10 @@ socksctpv_inactive(struct vnode *vp, struct cred *cr) /* * Check socktpi_poll() on why so_lock is not held in this function. */ +/*ARGSUSED5*/ static int socksctpv_poll(struct vnode *vp, short events, int anyyet, short *reventsp, - struct pollhead **phpp) + struct pollhead **phpp, caller_context_t *ct) { struct sonode *so; struct sctp_sonode *ss; diff --git a/usr/src/uts/common/fs/sockfs/socksdp.c b/usr/src/uts/common/fs/sockfs/socksdp.c index fab7c9d86166..09ab4d0b49c6 100755 --- a/usr/src/uts/common/fs/sockfs/socksdp.c +++ b/usr/src/uts/common/fs/sockfs/socksdp.c @@ -345,7 +345,7 @@ sosdp_create(vnode_t *accessvp, int domain, int type, int protocol, */ if ((cr = CRED()) == NULL) cr = kcred; - if ((error = VOP_OPEN(&vp, soflags, cr)) != 0) { + if ((error = VOP_OPEN(&vp, soflags, cr, NULL)) != 0) { VN_RELE(vp); *errorp = error; return (NULL); @@ -391,7 +391,7 @@ sosdp_free(struct sonode *so) mp->b_next = NULL; nso = *(struct sonode **)mp->b_rptr; - (void) VOP_CLOSE(SOTOV(nso), 0, 1, 0, CRED()); + (void) VOP_CLOSE(SOTOV(nso), 0, 1, 0, CRED(), NULL); vn_invalid(SOTOV(nso)); VN_RELE(SOTOV(nso)); diff --git a/usr/src/uts/common/fs/sockfs/socksdpvnops.c b/usr/src/uts/common/fs/sockfs/socksdpvnops.c index 667010ddea21..0993bff6a453 100755 --- a/usr/src/uts/common/fs/sockfs/socksdpvnops.c +++ b/usr/src/uts/common/fs/sockfs/socksdpvnops.c @@ -63,19 +63,21 @@ /* * SDP sockfs vnode operations */ -static int socksdpv_open(struct vnode **, int, struct cred *); +static int socksdpv_open(struct vnode **, int, struct cred *, + caller_context_t *); static int socksdpv_close(struct vnode *, int, int, offset_t, - struct cred *); + struct cred *, caller_context_t *); static int socksdpv_read(struct vnode *, struct uio *, int, struct cred *, - struct caller_context *); + caller_context_t *); static int socksdpv_write(struct vnode *, struct uio *, int, struct cred *, - struct caller_context *); + caller_context_t *); static int socksdpv_ioctl(struct vnode *, int, intptr_t, int, - struct cred *, int32_t *); -static int socksdp_setfl(vnode_t *, int, int, cred_t *); -static void socksdpv_inactive(struct vnode *, struct cred *); + struct cred *, int32_t *, caller_context_t *); +static int socksdp_setfl(vnode_t *, int, int, cred_t *, caller_context_t *); +static void socksdpv_inactive(struct vnode *, struct cred *, + caller_context_t *); static int socksdpv_poll(struct vnode *, short, int, short *, - struct pollhead **); + struct pollhead **, caller_context_t *); const fs_operation_def_t socksdp_vnodeops_template[] = { VOPNAME_OPEN, { .vop_open = socksdpv_open }, @@ -97,8 +99,10 @@ const fs_operation_def_t socksdp_vnodeops_template[] = { }; struct vnodeops *socksdp_vnodeops; +/*ARGSUSED3*/ static int -socksdpv_open(struct vnode **vpp, int flag, struct cred *cr) +socksdpv_open(struct vnode **vpp, int flag, struct cred *cr, + caller_context_t *ct) { struct sonode *so; struct sdp_sonode *ss; @@ -149,7 +153,7 @@ socksdpv_open(struct vnode **vpp, int flag, struct cred *cr) /*ARGSUSED*/ static int socksdpv_close(struct vnode *vp, int flag, int count, offset_t offset, - struct cred *cr) + struct cred *cr, caller_context_t *ct) { int sendsig = 0; int error = 0; @@ -212,7 +216,7 @@ socksdpv_close(struct vnode *vp, int flag, int count, offset_t offset, /*ARGSUSED2*/ static int socksdpv_read(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cr, - struct caller_context *ct) + caller_context_t *ct) { struct sonode *so = VTOSO(vp); struct nmsghdr lmsg; @@ -235,7 +239,7 @@ socksdpv_read(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cr, /*ARGSUSED2*/ static int socksdpv_write(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cr, - struct caller_context *ct) + caller_context_t *ct) { struct sonode *so; ssize_t count; @@ -281,7 +285,7 @@ socksdpv_write(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cr, /*ARGSUSED4*/ static int socksdpv_ioctl(struct vnode *vp, int cmd, intptr_t arg, int mode, - struct cred *cr, int32_t *rvalp) + struct cred *cr, int32_t *rvalp, caller_context_t *ct) { struct sonode *so; struct sdp_sonode *ss; @@ -400,7 +404,8 @@ socksdpv_ioctl(struct vnode *vp, int cmd, intptr_t arg, int mode, */ /* ARGSUSED */ static int -socksdp_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr) +socksdp_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr, + caller_context_t *ct) { struct sonode *so; @@ -421,7 +426,7 @@ socksdp_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr) /*ARGSUSED*/ static void -socksdpv_inactive(struct vnode *vp, struct cred *cr) +socksdpv_inactive(struct vnode *vp, struct cred *cr, caller_context_t *ct) { struct sonode *so; @@ -457,9 +462,10 @@ socksdpv_inactive(struct vnode *vp, struct cred *cr) /* * Check socktpi_poll() on why so_lock is not held in this function. */ +/*ARGSUSED5*/ static int socksdpv_poll(struct vnode *vp, short events, int anyyet, short *reventsp, - struct pollhead **phpp) + struct pollhead **phpp, caller_context_t *ct) { struct sonode *so; struct sdp_sonode *ss; diff --git a/usr/src/uts/common/fs/sockfs/socksubr.c b/usr/src/uts/common/fs/sockfs/socksubr.c index ad90f21a3634..cbacac2495b3 100644 --- a/usr/src/uts/common/fs/sockfs/socksubr.c +++ b/usr/src/uts/common/fs/sockfs/socksubr.c @@ -918,7 +918,7 @@ so_ux_lookup(struct sonode *so, struct sockaddr_un *soun, int checkaccess, * vnode. This check is not done in BSD but it is required * by X/Open. */ - if (error = VOP_ACCESS(vp, VREAD|VWRITE, 0, CRED())) { + if (error = VOP_ACCESS(vp, VREAD|VWRITE, 0, CRED(), NULL)) { eprintsoline(so, error); goto done2; } diff --git a/usr/src/uts/common/fs/sockfs/socksyscalls.c b/usr/src/uts/common/fs/sockfs/socksyscalls.c index 757a353982d6..90ddae2a95b9 100644 --- a/usr/src/uts/common/fs/sockfs/socksyscalls.c +++ b/usr/src/uts/common/fs/sockfs/socksyscalls.c @@ -185,7 +185,7 @@ so_socket(int domain, int type, int protocol, char *devpath, int version) &protocol, (t_uscalar_t)sizeof (protocol)); if (error) { - (void) VOP_CLOSE(vp, 0, 1, 0, CRED()); + (void) VOP_CLOSE(vp, 0, 1, 0, CRED(), NULL); VN_RELE(vp); /* * Setsockopt often fails with ENOPROTOOPT but socket() @@ -199,7 +199,7 @@ so_socket(int domain, int type, int protocol, char *devpath, int version) } } if (error = falloc(vp, FWRITE|FREAD, &fp, &fd)) { - (void) VOP_CLOSE(vp, 0, 1, 0, CRED()); + (void) VOP_CLOSE(vp, 0, 1, 0, CRED(), NULL); VN_RELE(vp); return (set_errno(error)); } @@ -533,14 +533,14 @@ so_socketpair(int sv[2]) mutex_exit(&so2->so_lock); nvp = SOTOV(nso); if (error != 0) { - (void) VOP_CLOSE(nvp, 0, 1, 0, CRED()); + (void) VOP_CLOSE(nvp, 0, 1, 0, CRED(), NULL); VN_RELE(nvp); eprintsoline(so2, error); goto done; } if (error = falloc(nvp, FWRITE|FREAD, &nfp, &nfd)) { - (void) VOP_CLOSE(nvp, 0, 1, 0, CRED()); + (void) VOP_CLOSE(nvp, 0, 1, 0, CRED(), NULL); VN_RELE(nvp); eprintsoline(nso, error); goto done; @@ -720,13 +720,13 @@ accept(int sock, struct sockaddr *name, socklen_t *namelenp, int version) nso->so_faddr_sa, (socklen_t)nso->so_faddr_len); if (error) { setf(nfd, NULL); - (void) VOP_CLOSE(nvp, 0, 1, 0, CRED()); + (void) VOP_CLOSE(nvp, 0, 1, 0, CRED(), NULL); VN_RELE(nvp); return (set_errno(error)); } if (error = falloc(NULL, FWRITE|FREAD, &nfp, NULL)) { setf(nfd, NULL); - (void) VOP_CLOSE(nvp, 0, 1, 0, CRED()); + (void) VOP_CLOSE(nvp, 0, 1, 0, CRED(), NULL); VN_RELE(nvp); eprintsoline(so, error); return (set_errno(error)); @@ -754,7 +754,8 @@ accept(int sock, struct sockaddr *name, socklen_t *namelenp, int version) * This code is a simplification of the F_SETFL code in fcntl() * Ignore any errors from VOP_SETFL. */ - if ((error = VOP_SETFL(nvp, oflag, arg, nfp->f_cred)) != 0) { + if ((error = VOP_SETFL(nvp, oflag, arg, nfp->f_cred, NULL)) + != 0) { eprintsoline(so, error); error = 0; } else { @@ -1734,7 +1735,7 @@ sockconfig(int domain, int type, int protocol, char *devpath) * processed by a thread, it produces a number of mblk_t structures to * be consumed by the sendfile thread. snf_deque and snf_enque are * used for consuming and producing mblks. Size of the filesystem - * read is determined by the tuneable (sendfile_read_size). A single + * read is determined by the tunable (sendfile_read_size). A single * mblk holds sendfile_read_size worth of data (except the last * read of the file) which is sent down as a whole to the network. * sendfile_read_size is set to 1 MB as this seems to be the optimal @@ -1752,7 +1753,7 @@ sockconfig(int domain, int type, int protocol, char *devpath) * a) One of the threads need to clean the mblks. * b) When one thread encounters an error, the other should stop. * - * For (a), we don't want to penalise the reader thread as it could do + * For (a), we don't want to penalize the reader thread as it could do * some useful work processing other requests. For (b), the error can * be detected by examining sr_read_error or sr_write_error. * sr_lock protects sr_read_error and sr_write_error. If both reader and @@ -1795,7 +1796,7 @@ sockconfig(int domain, int type, int protocol, char *devpath) * control, it would take 25ms to get new data ready for transmission. * We have to make sure that network is not idling, while we are initiating * new transfers. So, at 100MB/sec, to keep network busy we would need - * 2.5MB of data. Roundig off, we keep the low water mark to be 3MB of data. + * 2.5MB of data. Rounding off, we keep the low water mark to be 3MB of data. * We need to pick a high water mark so that the woken up thread would * do considerable work before blocking again to prevent thrashing. Currently, * we pick this to be 10 times that of the low water mark. @@ -1946,7 +1947,7 @@ snf_async_read(snf_req_t *sr) * Ignore the error for filesystems that doesn't support DIRECTIO. */ (void) VOP_IOCTL(fp->f_vnode, _FIODIRECTIO, DIRECTIO_ON, 0, - kcred, NULL); + kcred, NULL, NULL); while ((size != 0) && (sr->sr_write_error == 0)) { @@ -1970,7 +1971,7 @@ snf_async_read(snf_req_t *sr) fileoff += ret_size; } (void) VOP_IOCTL(fp->f_vnode, _FIODIRECTIO, DIRECTIO_OFF, 0, - kcred, NULL); + kcred, NULL, NULL); mutex_enter(&sr->sr_lock); sr->sr_read_error = error; sr->sr_read_error |= SR_READ_DONE; @@ -2301,7 +2302,7 @@ snf_segmap(file_t *fp, vnode_t *fvp, u_offset_t fileoff, u_offset_t size, (void) VOP_RWLOCK(fvp, V_WRITELOCK_FALSE, NULL); va.va_mask = AT_SIZE; - error = VOP_GETATTR(fvp, &va, 0, kcred); + error = VOP_GETATTR(fvp, &va, 0, kcred, NULL); if (error) break; /* Read as much as possible. */ @@ -2401,7 +2402,7 @@ snf_cache(file_t *fp, vnode_t *fvp, u_offset_t fileoff, u_offset_t size, fileoff += iosize; (void) VOP_RWLOCK(fvp, V_WRITELOCK_FALSE, NULL); va.va_mask = AT_SIZE; - error = VOP_GETATTR(fvp, &va, 0, kcred); + error = VOP_GETATTR(fvp, &va, 0, kcred, NULL); if (error) break; /* Read as much as possible. */ @@ -2466,7 +2467,7 @@ sosendfile64(file_t *fp, file_t *rfp, const struct ksendfilevec64 *sfv, goto out; } fvp = rfp->f_vnode; - if (VOP_REALVP(fvp, &realvp) == 0) + if (VOP_REALVP(fvp, &realvp, NULL) == 0) fvp = realvp; /* * Grab the lock as a reader to prevent the file size @@ -2474,7 +2475,7 @@ sosendfile64(file_t *fp, file_t *rfp, const struct ksendfilevec64 *sfv, */ (void) VOP_RWLOCK(fvp, V_WRITELOCK_FALSE, NULL); va.va_mask = AT_SIZE; - error = VOP_GETATTR(fvp, &va, 0, kcred); + error = VOP_GETATTR(fvp, &va, 0, kcred, NULL); va_size = va.va_size; if ((error != 0) || (va_size == 0) || (sfv_off >= va_size)) { VOP_RWUNLOCK(fvp, V_WRITELOCK_FALSE, NULL); @@ -2560,7 +2561,7 @@ sendto32(int32_t sock, caddr32_t buffer, size32_t len, int32_t flags, #endif /* _SYSCALL32_IMPL */ /* - * Function wrappers (mostly arround the sonode switch) for + * Function wrappers (mostly around the sonode switch) for * backward compatibility. */ diff --git a/usr/src/uts/common/fs/sockfs/socktpi.c b/usr/src/uts/common/fs/sockfs/socktpi.c index 65a10f06909b..a198c171765e 100644 --- a/usr/src/uts/common/fs/sockfs/socktpi.c +++ b/usr/src/uts/common/fs/sockfs/socktpi.c @@ -287,14 +287,14 @@ sotpi_create(vnode_t *accessvp, int domain, int type, int protocol, int version, so->so_kssl_ent = NULL; so->so_kssl_ctx = NULL; - if (error = socktpi_open(&vp, flags, CRED())) { + if (error = socktpi_open(&vp, flags, CRED(), NULL)) { VN_RELE(vp); *errorp = error; return (NULL); } if (error = so_strinit(so, tso)) { - (void) VOP_CLOSE(vp, 0, 1, 0, CRED()); + (void) VOP_CLOSE(vp, 0, 1, 0, CRED(), NULL); VN_RELE(vp); *errorp = error; return (NULL); @@ -1753,7 +1753,7 @@ sotpi_accept(struct sonode *so, int fflag, struct sonode **nsop) */ mutex_exit(&nso->so_lock); (void) VOP_CLOSE(nvp, 0, 1, (offset_t)0, - CRED()); + CRED(), NULL); VN_RELE(nvp); goto again; } @@ -1900,7 +1900,7 @@ sotpi_accept(struct sonode *so, int fflag, struct sonode **nsop) pr_disc_vp_unl: eprintsoline(so, error); disconnect_vp_unlocked: - (void) VOP_CLOSE(nvp, 0, 1, 0, CRED()); + (void) VOP_CLOSE(nvp, 0, 1, 0, CRED(), NULL); VN_RELE(nvp); disconnect_unlocked: (void) sodisconnect(so, SEQ_number, 0); @@ -1912,7 +1912,7 @@ sotpi_accept(struct sonode *so, int fflag, struct sonode **nsop) (void) sodisconnect(so, SEQ_number, _SODISCONNECT_LOCK_HELD); so_unlock_single(so, SOLOCKED); mutex_exit(&so->so_lock); - (void) VOP_CLOSE(nvp, 0, 1, 0, CRED()); + (void) VOP_CLOSE(nvp, 0, 1, 0, CRED(), NULL); VN_RELE(nvp); return (error); diff --git a/usr/src/uts/common/fs/sockfs/sockvnops.c b/usr/src/uts/common/fs/sockfs/sockvnops.c index 3ab7626e6f4e..6c122c679d4e 100644 --- a/usr/src/uts/common/fs/sockfs/sockvnops.c +++ b/usr/src/uts/common/fs/sockfs/sockvnops.c @@ -95,16 +95,17 @@ #include -static int socktpi_close(struct vnode *, int, int, offset_t, struct cred *); +static int socktpi_close(struct vnode *, int, int, offset_t, struct cred *, + caller_context_t *); static int socktpi_read(struct vnode *, struct uio *, int, struct cred *, - struct caller_context *); + caller_context_t *); static int socktpi_write(struct vnode *, struct uio *, int, struct cred *, - struct caller_context *); + caller_context_t *); static int socktpi_plumbioctl(struct vnode *, int, intptr_t, int, struct cred *, int32_t *); -static void socktpi_inactive(struct vnode *, struct cred *); +static void socktpi_inactive(struct vnode *, struct cred *, caller_context_t *); static int socktpi_poll(struct vnode *, short, int, short *, - struct pollhead **); + struct pollhead **, caller_context_t *); struct vnodeops *socktpi_vnodeops; @@ -148,7 +149,8 @@ boolean_t socktpi_direct = B_TRUE; * open/closes for a given vnode which is probably not needed. */ int -socktpi_open(struct vnode **vpp, int flag, struct cred *cr) +socktpi_open(struct vnode **vpp, int flag, struct cred *cr, + caller_context_t *ct) { major_t maj; dev_t newdev; @@ -196,7 +198,7 @@ socktpi_open(struct vnode **vpp, int flag, struct cred *cr) * this is a post SVR4 tty driver - a socket can not * be a controlling terminal. Fail the open. */ - (void) socktpi_close(vp, flag, 1, (offset_t)0, cr); + (void) socktpi_close(vp, flag, 1, (offset_t)0, cr, ct); return (ENOTTY); /* XXX */ } @@ -249,7 +251,7 @@ socktpi_open(struct vnode **vpp, int flag, struct cred *cr) _SIOCSOCKFALLBACK, 0, 0, K_TO_K, CRED(), &rval)) != 0) { (void) socktpi_close(vp, flag, - 1, (offset_t)0, cr); + 1, (offset_t)0, cr, ct); return (error); } } @@ -273,7 +275,7 @@ socktpi_open(struct vnode **vpp, int flag, struct cred *cr) so_unlock_single(so, SOLOCKED); mutex_exit(&so->so_lock); (void) socktpi_close(vp, flag, 1, - (offset_t)0, cr); + (offset_t)0, cr, ct); return (error); /*NOTREACHED*/ } @@ -297,7 +299,8 @@ socktpi_close( int flag, int count, offset_t offset, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct sonode *so; dev_t dev; @@ -402,7 +405,7 @@ socktpi_read( struct uio *uiop, int ioflag, struct cred *cr, - struct caller_context *ct) + caller_context_t *ct) { struct sonode *so = VTOSO(vp); struct nmsghdr lmsg; @@ -428,11 +431,11 @@ socktpi_read( /* ARGSUSED2 */ static int socktpi_write( - struct vnode *vp, - struct uio *uiop, - int ioflag, - struct cred *cr, - struct caller_context *ct) + struct vnode *vp, + struct uio *uiop, + int ioflag, + struct cred *cr, + caller_context_t *ct) { struct sonode *so = VTOSO(vp); int so_state; @@ -531,9 +534,10 @@ so_copyout(const void *from, void *to, size_t size, int tokernel) return (xcopyout(from, to, size)); } +/*ARGSUSED6*/ int socktpi_ioctl(struct vnode *vp, int cmd, intptr_t arg, int mode, - struct cred *cr, int32_t *rvalp) + struct cred *cr, int32_t *rvalp, caller_context_t *ct) { struct sonode *so = VTOSO(vp); int error = 0; @@ -941,7 +945,8 @@ socktpi_plumbioctl(struct vnode *vp, int cmd, intptr_t arg, int mode, */ /* ARGSUSED */ int -socktpi_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr) +socktpi_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr, + caller_context_t *ct) { struct sonode *so; int error = 0; @@ -1003,7 +1008,8 @@ socktpi_getattr( struct vnode *vp, struct vattr *vap, int flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { dev_t fsid; struct sonode *so; @@ -1081,7 +1087,7 @@ socktpi_setattr( struct vattr *vap, int flags, struct cred *cr, - caller_context_t *ct) + caller_context_t *ct) { struct sonode *so = VTOSO(vp); @@ -1101,13 +1107,14 @@ socktpi_setattr( } int -socktpi_access(struct vnode *vp, int mode, int flags, struct cred *cr) +socktpi_access(struct vnode *vp, int mode, int flags, struct cred *cr, + caller_context_t *ct) { struct vnode *accessvp; struct sonode *so = VTOSO(vp); if ((accessvp = so->so_accessvp) != NULL) - return (VOP_ACCESS(accessvp, mode, flags, cr)); + return (VOP_ACCESS(accessvp, mode, flags, cr, ct)); else return (0); /* Allow all access. */ } @@ -1120,14 +1127,15 @@ socktpi_access(struct vnode *vp, int mode, int flags, struct cred *cr) */ /* ARGSUSED */ int -socktpi_fsync(struct vnode *vp, int syncflag, struct cred *cr) +socktpi_fsync(struct vnode *vp, int syncflag, struct cred *cr, + caller_context_t *ct) { return (EINVAL); } /* ARGSUSED */ static void -socktpi_inactive(struct vnode *vp, struct cred *cr) +socktpi_inactive(struct vnode *vp, struct cred *cr, caller_context_t *ct) { struct sonode *so = VTOSO(vp); @@ -1156,7 +1164,7 @@ socktpi_inactive(struct vnode *vp, struct cred *cr) /* ARGSUSED */ int -socktpi_fid(struct vnode *vp, struct fid *fidp) +socktpi_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct) { return (EINVAL); } @@ -1167,7 +1175,8 @@ socktpi_fid(struct vnode *vp, struct fid *fidp) */ /*ARGSUSED*/ int -socktpi_seek(struct vnode *vp, offset_t ooff, offset_t *noffp) +socktpi_seek(struct vnode *vp, offset_t ooff, offset_t *noffp, + caller_context_t *ct) { return (ESPIPE); } @@ -1202,13 +1211,15 @@ socktpi_seek(struct vnode *vp, offset_t ooff, offset_t *noffp) * for SS_HASCONNIND and set appropriate events to ensure poll_common() * will not sleep. */ +/*ARGSUSED5*/ static int socktpi_poll( struct vnode *vp, short events, int anyyet, short *reventsp, - struct pollhead **phpp) + struct pollhead **phpp, + caller_context_t *ct) { short origevents = events; struct sonode *so = VTOSO(vp); diff --git a/usr/src/uts/common/fs/specfs/specsubr.c b/usr/src/uts/common/fs/specfs/specsubr.c index 85d9089b8273..de5bf62e4438 100644 --- a/usr/src/uts/common/fs/specfs/specsubr.c +++ b/usr/src/uts/common/fs/specfs/specsubr.c @@ -142,7 +142,7 @@ specvp( * been required if the snode is in the cache. */ va.va_mask = AT_FSID | AT_TIMES; - rc = VOP_GETATTR(vp, &va, 0, cr); /* XXX may block! */ + rc = VOP_GETATTR(vp, &va, 0, cr, NULL); /* XXX may block! */ mutex_enter(&stable_lock); if ((sp = sfind(dev, type, vp)) == NULL) { @@ -465,7 +465,7 @@ devi_stillreferenced(dev_info_t *dip) /* * Given an snode, returns the open count and the dip * associated with that snode - * Assumes the caller holds the approriate locks + * Assumes the caller holds the appropriate locks * to prevent snode and/or dip from going away. * Returns: * -1 No associated dip @@ -862,7 +862,7 @@ device_close(struct vnode *vp, int flag, struct cred *cr) * can, for example, change floppy disks. */ (void) spec_putpage(cvp, (offset_t)0, - (size_t)0, B_INVAL|B_FORCE, cr); + (size_t)0, B_INVAL|B_FORCE, cr, NULL); bflush(dev); binval(dev); error = dev_close(dev, flag, OTYP_BLK, cr); diff --git a/usr/src/uts/common/fs/specfs/specvfsops.c b/usr/src/uts/common/fs/specfs/specvfsops.c index e943a700a0ee..98342f44245b 100644 --- a/usr/src/uts/common/fs/specfs/specvfsops.c +++ b/usr/src/uts/common/fs/specfs/specvfsops.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -98,7 +97,7 @@ _info(struct modinfo *modinfop) * N.B. * No _fini routine. This module cannot be unloaded once loaded. * The NO_UNLOAD_STUB in modstub.s must change if this module ever - * is modififed to become unloadable. + * is modified to become unloadable. */ kmutex_t spec_syncbusy; /* initialized in specinit() */ @@ -158,7 +157,8 @@ spec_sync(struct vfs *vfsp, for (sp = sync_list; sp != NULL; sp = spnext) { spnext = sp->s_list; vp = STOV(sp); - (void) VOP_PUTPAGE(vp, (offset_t)0, (uint_t)0, B_ASYNC, cr); + (void) VOP_PUTPAGE(vp, (offset_t)0, (uint_t)0, B_ASYNC, cr, + NULL); VN_RELE(vp); /* Release our hold on vnode */ } mutex_exit(&spec_syncbusy); diff --git a/usr/src/uts/common/fs/specfs/specvnops.c b/usr/src/uts/common/fs/specfs/specvnops.c index 07b5e6106d3d..d45224613555 100644 --- a/usr/src/uts/common/fs/specfs/specvnops.c +++ b/usr/src/uts/common/fs/specfs/specvnops.c @@ -96,50 +96,59 @@ #include -static int spec_open(struct vnode **, int, struct cred *); -static int spec_close(struct vnode *, int, int, offset_t, struct cred *); +static int spec_open(struct vnode **, int, struct cred *, caller_context_t *); +static int spec_close(struct vnode *, int, int, offset_t, struct cred *, + caller_context_t *); static int spec_read(struct vnode *, struct uio *, int, struct cred *, - struct caller_context *); + caller_context_t *); static int spec_write(struct vnode *, struct uio *, int, struct cred *, - struct caller_context *); -static int spec_ioctl(struct vnode *, int, intptr_t, int, struct cred *, int *); -static int spec_getattr(struct vnode *, struct vattr *, int, struct cred *); + caller_context_t *); +static int spec_ioctl(struct vnode *, int, intptr_t, int, struct cred *, int *, + caller_context_t *); +static int spec_getattr(struct vnode *, struct vattr *, int, struct cred *, + caller_context_t *); static int spec_setattr(struct vnode *, struct vattr *, int, struct cred *, caller_context_t *); -static int spec_access(struct vnode *, int, int, struct cred *); -static int spec_create(struct vnode *, char *, vattr_t *, enum vcexcl, - int, struct vnode **, struct cred *, int); -static int spec_fsync(struct vnode *, int, struct cred *); -static void spec_inactive(struct vnode *, struct cred *); -static int spec_fid(struct vnode *, struct fid *); -static int spec_seek(struct vnode *, offset_t, offset_t *); +static int spec_access(struct vnode *, int, int, struct cred *, + caller_context_t *); +static int spec_create(struct vnode *, char *, vattr_t *, enum vcexcl, int, + struct vnode **, struct cred *, int, caller_context_t *, vsecattr_t *); +static int spec_fsync(struct vnode *, int, struct cred *, caller_context_t *); +static void spec_inactive(struct vnode *, struct cred *, caller_context_t *); +static int spec_fid(struct vnode *, struct fid *, caller_context_t *); +static int spec_seek(struct vnode *, offset_t, offset_t *, caller_context_t *); static int spec_frlock(struct vnode *, int, struct flock64 *, int, offset_t, - struct flk_callback *, struct cred *); -static int spec_realvp(struct vnode *, struct vnode **); + struct flk_callback *, struct cred *, caller_context_t *); +static int spec_realvp(struct vnode *, struct vnode **, caller_context_t *); static int spec_getpage(struct vnode *, offset_t, size_t, uint_t *, page_t **, - size_t, struct seg *, caddr_t, enum seg_rw, struct cred *); + size_t, struct seg *, caddr_t, enum seg_rw, struct cred *, + caller_context_t *); static int spec_putapage(struct vnode *, page_t *, u_offset_t *, size_t *, int, struct cred *); static struct buf *spec_startio(struct vnode *, page_t *, u_offset_t, size_t, int); static int spec_getapage(struct vnode *, u_offset_t, size_t, uint_t *, - page_t **, size_t, struct seg *, caddr_t, enum seg_rw, struct cred *); + page_t **, size_t, struct seg *, caddr_t, enum seg_rw, struct cred *); static int spec_map(struct vnode *, offset_t, struct as *, caddr_t *, size_t, - uchar_t, uchar_t, uint_t, struct cred *); + uchar_t, uchar_t, uint_t, struct cred *, caller_context_t *); static int spec_addmap(struct vnode *, offset_t, struct as *, caddr_t, size_t, - uchar_t, uchar_t, uint_t, struct cred *); + uchar_t, uchar_t, uint_t, struct cred *, caller_context_t *); static int spec_delmap(struct vnode *, offset_t, struct as *, caddr_t, size_t, - uint_t, uint_t, uint_t, struct cred *); + uint_t, uint_t, uint_t, struct cred *, caller_context_t *); -static int spec_poll(struct vnode *, short, int, short *, struct pollhead **); -static int spec_dump(struct vnode *, caddr_t, int, int); +static int spec_poll(struct vnode *, short, int, short *, struct pollhead **, + caller_context_t *); +static int spec_dump(struct vnode *, caddr_t, int, int, caller_context_t *); static int spec_pageio(struct vnode *, page_t *, u_offset_t, size_t, int, - cred_t *); + cred_t *, caller_context_t *); -static int spec_getsecattr(struct vnode *, vsecattr_t *, int, struct cred *); -static int spec_setsecattr(struct vnode *, vsecattr_t *, int, struct cred *); -static int spec_pathconf(struct vnode *, int, ulong_t *, struct cred *); +static int spec_getsecattr(struct vnode *, vsecattr_t *, int, struct cred *, + caller_context_t *); +static int spec_setsecattr(struct vnode *, vsecattr_t *, int, struct cred *, + caller_context_t *); +static int spec_pathconf(struct vnode *, int, ulong_t *, struct cred *, + caller_context_t *); #define SN_HOLD(csp) { \ mutex_enter(&csp->s_lock); \ @@ -549,7 +558,7 @@ spec_clone(struct vnode **vpp, dev_t newdev, int vtype, struct stdata *stp) } static int -spec_open(struct vnode **vpp, int flag, struct cred *cr) +spec_open(struct vnode **vpp, int flag, struct cred *cr, caller_context_t *cc) { major_t maj; dev_t dev, newdev; @@ -785,7 +794,7 @@ spec_open(struct vnode **vpp, int flag, struct cred *cr) /* STREAM is of type S_IFCHR */ if (contract_device_open(newdev, S_IFCHR, &ct) != 0) { UNLOCK_CSP(csp); - (void) spec_close(vp, flag, 1, 0, cr); + (void) spec_close(vp, flag, 1, 0, cr, cc); return (EIO); } } @@ -807,7 +816,7 @@ spec_open(struct vnode **vpp, int flag, struct cred *cr) ASSERT(ttoproc(curthread)); (void) contract_abandon(ct, ttoproc(curthread), 0); } - (void) spec_close(vp, flag, 1, 0, cr); + (void) spec_close(vp, flag, 1, 0, cr, cc); return (EINTR); } @@ -826,7 +835,7 @@ spec_open(struct vnode **vpp, int flag, struct cred *cr) mutex_exit(&stp->sd_lock); UNLOCK_CSP(csp); - (void) spec_close(vp, flag, 1, 0, cr); + (void) spec_close(vp, flag, 1, 0, cr, cc); } else { UNLOCK_CSP(csp); SN_RELE(csp); @@ -842,7 +851,8 @@ spec_close( int flag, int count, offset_t offset, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct vnode *cvp; struct snode *sp, *csp; @@ -943,7 +953,7 @@ spec_read( struct uio *uiop, int ioflag, struct cred *cr, - struct caller_context *ct) + caller_context_t *ct) { int error; struct snode *sp = VTOS(vp); @@ -1049,7 +1059,7 @@ spec_write( struct uio *uiop, int ioflag, struct cred *cr, - struct caller_context *ct) + caller_context_t *ct) { int error; struct snode *sp = VTOS(vp); @@ -1214,9 +1224,10 @@ spec_write( return (error); } +/*ARGSUSED6*/ static int spec_ioctl(struct vnode *vp, int cmd, intptr_t arg, int mode, struct cred *cr, - int *rvalp) + int *rvalp, caller_context_t *ct) { struct snode *sp; dev_t dev; @@ -1242,7 +1253,12 @@ spec_ioctl(struct vnode *vp, int cmd, intptr_t arg, int mode, struct cred *cr, } static int -spec_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cr) +spec_getattr( + struct vnode *vp, + struct vattr *vap, + int flags, + struct cred *cr, + caller_context_t *ct) { int error; struct snode *sp; @@ -1302,7 +1318,7 @@ spec_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cr) */ vap->va_nblocks = 0; } else { - error = VOP_GETATTR(realvp, vap, flags, cr); + error = VOP_GETATTR(realvp, vap, flags, cr, ct); if (error != 0) return (error); } @@ -1331,7 +1347,7 @@ spec_setattr( struct vattr *vap, int flags, struct cred *cr, - caller_context_t *ctp) + caller_context_t *ct) { struct snode *sp = VTOS(vp); struct vnode *realvp; @@ -1353,7 +1369,7 @@ spec_setattr( if ((realvp = sp->s_realvp) == NULL) error = 0; /* no real vnode to update */ else - error = VOP_SETATTR(realvp, vap, flags, cr, ctp); + error = VOP_SETATTR(realvp, vap, flags, cr, ct); if (error == 0) { /* * If times were changed, update snode. @@ -1371,7 +1387,12 @@ spec_setattr( } static int -spec_access(struct vnode *vp, int mode, int flags, struct cred *cr) +spec_access( + struct vnode *vp, + int mode, + int flags, + struct cred *cr, + caller_context_t *ct) { struct vnode *realvp; struct snode *sp = VTOS(vp); @@ -1381,7 +1402,7 @@ spec_access(struct vnode *vp, int mode, int flags, struct cred *cr) return (ENXIO); if ((realvp = sp->s_realvp) != NULL) - return (VOP_ACCESS(realvp, mode, flags, cr)); + return (VOP_ACCESS(realvp, mode, flags, cr, ct)); else return (0); /* Allow all access. */ } @@ -1392,8 +1413,17 @@ spec_access(struct vnode *vp, int mode, int flags, struct cred *cr) */ /*ARGSUSED*/ static int -spec_create(struct vnode *dvp, char *name, vattr_t *vap, enum vcexcl excl, - int mode, struct vnode **vpp, struct cred *cr, int flag) +spec_create( + struct vnode *dvp, + char *name, + vattr_t *vap, + enum vcexcl excl, + int mode, + struct vnode **vpp, + struct cred *cr, + int flag, + caller_context_t *ct, + vsecattr_t *vsecp) { int error; struct snode *sp = VTOS(dvp); @@ -1404,7 +1434,7 @@ spec_create(struct vnode *dvp, char *name, vattr_t *vap, enum vcexcl excl, ASSERT(dvp && (dvp->v_flag & VROOT) && *name == '\0'); if (excl == NONEXCL) { - if (mode && (error = spec_access(dvp, mode, 0, cr))) + if (mode && (error = spec_access(dvp, mode, 0, cr, ct))) return (error); VN_HOLD(dvp); return (0); @@ -1418,7 +1448,11 @@ spec_create(struct vnode *dvp, char *name, vattr_t *vap, enum vcexcl excl, * already set in the vnode. */ static int -spec_fsync(struct vnode *vp, int syncflag, struct cred *cr) +spec_fsync( + struct vnode *vp, + int syncflag, + struct cred *cr, + caller_context_t *ct) { struct snode *sp = VTOS(vp); struct vnode *realvp; @@ -1440,7 +1474,7 @@ spec_fsync(struct vnode *vp, int syncflag, struct cred *cr) if (vp->v_type == VBLK && cvp != vp && vn_has_cached_data(cvp) && (cvp->v_flag & VISSWAP) == 0) - (void) VOP_PUTPAGE(cvp, (offset_t)0, 0, 0, cr); + (void) VOP_PUTPAGE(cvp, (offset_t)0, 0, 0, cr, ct); /* * For devices that support it, force write cache to stable storage. @@ -1473,7 +1507,7 @@ spec_fsync(struct vnode *vp, int syncflag, struct cred *cr) return (0); vatmp.va_mask = AT_ATIME|AT_MTIME; - if (VOP_GETATTR(realvp, &vatmp, 0, cr) == 0) { + if (VOP_GETATTR(realvp, &vatmp, 0, cr, ct) == 0) { mutex_enter(&sp->s_lock); if (vatmp.va_atime.tv_sec > sp->s_atime) @@ -1491,15 +1525,15 @@ spec_fsync(struct vnode *vp, int syncflag, struct cred *cr) mutex_exit(&sp->s_lock); va.va_mask = AT_ATIME|AT_MTIME; - (void) VOP_SETATTR(realvp, &va, 0, cr, NULL); + (void) VOP_SETATTR(realvp, &va, 0, cr, ct); } - (void) VOP_FSYNC(realvp, syncflag, cr); + (void) VOP_FSYNC(realvp, syncflag, cr, ct); return (0); } /*ARGSUSED*/ static void -spec_inactive(struct vnode *vp, struct cred *cr) +spec_inactive(struct vnode *vp, struct cred *cr, caller_context_t *ct) { struct snode *sp = VTOS(vp); struct vnode *cvp; @@ -1550,7 +1584,7 @@ spec_inactive(struct vnode *vp, struct cred *cr) * The user may not own the device, but we * want to update the attributes anyway. */ - if (VOP_GETATTR(rvp, &vatmp, 0, kcred) == 0) { + if (VOP_GETATTR(rvp, &vatmp, 0, kcred, ct) == 0) { if (vatmp.va_atime.tv_sec > sp->s_atime) va.va_atime = vatmp.va_atime; else { @@ -1565,7 +1599,7 @@ spec_inactive(struct vnode *vp, struct cred *cr) } va.va_mask = AT_ATIME|AT_MTIME; - (void) VOP_SETATTR(rvp, &va, 0, kcred, NULL); + (void) VOP_SETATTR(rvp, &va, 0, kcred, ct); } } } @@ -1619,20 +1653,24 @@ spec_inactive(struct vnode *vp, struct cred *cr) } static int -spec_fid(struct vnode *vp, struct fid *fidp) +spec_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct) { struct vnode *realvp; struct snode *sp = VTOS(vp); if ((realvp = sp->s_realvp) != NULL) - return (VOP_FID(realvp, fidp)); + return (VOP_FID(realvp, fidp, ct)); else return (EINVAL); } /*ARGSUSED1*/ static int -spec_seek(struct vnode *vp, offset_t ooff, offset_t *noffp) +spec_seek( + struct vnode *vp, + offset_t ooff, + offset_t *noffp, + caller_context_t *ct) { offset_t maxoff = spec_maxoffset(vp); @@ -1650,7 +1688,8 @@ spec_frlock( int flag, offset_t offset, struct flk_callback *flk_cbp, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct snode *sp = VTOS(vp); struct snode *csp; @@ -1662,17 +1701,17 @@ spec_frlock( if (csp->s_mapcnt > 0) return (EAGAIN); - return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr)); + return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct)); } static int -spec_realvp(struct vnode *vp, struct vnode **vpp) +spec_realvp(struct vnode *vp, struct vnode **vpp, caller_context_t *ct) { struct vnode *rvp; if ((rvp = VTOS(vp)->s_realvp) != NULL) { vp = rvp; - if (VOP_REALVP(vp, &rvp) == 0) + if (VOP_REALVP(vp, &rvp, ct) == 0) vp = rvp; } @@ -1684,6 +1723,7 @@ spec_realvp(struct vnode *vp, struct vnode **vpp) * Return all the pages from [off..off + len] in block * or character device. */ +/*ARGSUSED*/ static int spec_getpage( struct vnode *vp, @@ -1695,7 +1735,8 @@ spec_getpage( struct seg *seg, caddr_t addr, enum seg_rw rw, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct snode *sp = VTOS(vp); int err; @@ -1942,13 +1983,15 @@ spec_getapage( * len == MAXBSIZE (from segmap_release actions), and len == PAGESIZE * (from pageout). */ +/*ARGSUSED5*/ int spec_putpage( struct vnode *vp, offset_t off, size_t len, int flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct snode *sp = VTOS(vp); struct vnode *cvp; @@ -2142,13 +2185,14 @@ spec_poll( short events, int anyyet, short *reventsp, - struct pollhead **phpp) + struct pollhead **phpp, + caller_context_t *ct) { dev_t dev; int error; if (vp->v_type == VBLK) - error = fs_poll(vp, events, anyyet, reventsp, phpp); + error = fs_poll(vp, events, anyyet, reventsp, phpp, ct); else { ASSERT(vp->v_type == VCHR); dev = vp->v_rdev; @@ -2159,7 +2203,7 @@ spec_poll( } else if (devopsp[getmajor(dev)]->devo_cb_ops->cb_chpoll) { error = cdev_poll(dev, events, anyyet, reventsp, phpp); } else { - error = fs_poll(vp, events, anyyet, reventsp, phpp); + error = fs_poll(vp, events, anyyet, reventsp, phpp, ct); } } return (error); @@ -2310,6 +2354,7 @@ spec_char_map( maxprot, flags, cred)); } +/*ARGSUSED9*/ static int spec_map( struct vnode *vp, @@ -2320,7 +2365,8 @@ spec_map( uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cred) + struct cred *cred, + caller_context_t *ct) { int error = 0; struct snode *sp = VTOS(vp); @@ -2405,7 +2451,8 @@ spec_addmap( uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cred) + struct cred *cred, + caller_context_t *ct) { int error = 0; struct snode *csp = VTOS(vp); @@ -2443,7 +2490,8 @@ spec_delmap( uint_t prot, uint_t maxprot, uint_t flags, - struct cred *cred) + struct cred *cred, + caller_context_t *ct) { struct snode *csp = VTOS(vp); ulong_t npages; @@ -2498,8 +2546,14 @@ spec_delmap( return (0); } +/*ARGSUSED4*/ static int -spec_dump(struct vnode *vp, caddr_t addr, int bn, int count) +spec_dump( + struct vnode *vp, + caddr_t addr, + int bn, + int count, + caller_context_t *ct) { /* allow dump to succeed even if device fenced off */ @@ -2522,7 +2576,8 @@ spec_pageio( u_offset_t io_off, size_t io_len, int flags, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { struct buf *bp = NULL; int err = 0; @@ -2546,7 +2601,12 @@ spec_pageio( * Set ACL on underlying vnode if one exists, or return ENOSYS otherwise. */ int -spec_setsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *cr) +spec_setsecattr( + struct vnode *vp, + vsecattr_t *vsap, + int flag, + struct cred *cr, + caller_context_t *ct) { struct vnode *realvp; struct snode *sp = VTOS(vp); @@ -2564,9 +2624,9 @@ spec_setsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *cr) * here privately to avoid serializing specfs reads and writes. */ if ((realvp = sp->s_realvp) != NULL) { - (void) VOP_RWLOCK(realvp, V_WRITELOCK_TRUE, NULL); - error = VOP_SETSECATTR(realvp, vsap, flag, cr); - (void) VOP_RWUNLOCK(realvp, V_WRITELOCK_TRUE, NULL); + (void) VOP_RWLOCK(realvp, V_WRITELOCK_TRUE, ct); + error = VOP_SETSECATTR(realvp, vsap, flag, cr, ct); + (void) VOP_RWUNLOCK(realvp, V_WRITELOCK_TRUE, ct); return (error); } else return (fs_nosys()); @@ -2577,7 +2637,12 @@ spec_setsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *cr) * the permissions returned by spec_getattr() otherwise. */ int -spec_getsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *cr) +spec_getsecattr( + struct vnode *vp, + vsecattr_t *vsap, + int flag, + struct cred *cr, + caller_context_t *ct) { struct vnode *realvp; struct snode *sp = VTOS(vp); @@ -2587,13 +2652,18 @@ spec_getsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *cr) return (ENXIO); if ((realvp = sp->s_realvp) != NULL) - return (VOP_GETSECATTR(realvp, vsap, flag, cr)); + return (VOP_GETSECATTR(realvp, vsap, flag, cr, ct)); else - return (fs_fab_acl(vp, vsap, flag, cr)); + return (fs_fab_acl(vp, vsap, flag, cr, ct)); } int -spec_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) +spec_pathconf( + vnode_t *vp, + int cmd, + ulong_t *valp, + cred_t *cr, + caller_context_t *ct) { vnode_t *realvp; struct snode *sp = VTOS(vp); @@ -2603,7 +2673,7 @@ spec_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) return (ENXIO); if ((realvp = sp->s_realvp) != NULL) - return (VOP_PATHCONF(realvp, cmd, valp, cr)); + return (VOP_PATHCONF(realvp, cmd, valp, cr, ct)); else - return (fs_pathconf(vp, cmd, valp, cr)); + return (fs_pathconf(vp, cmd, valp, cr, ct)); } diff --git a/usr/src/uts/common/fs/swapfs/swap_subr.c b/usr/src/uts/common/fs/swapfs/swap_subr.c index e589c38073f5..dbbf57a50adf 100644 --- a/usr/src/uts/common/fs/swapfs/swap_subr.c +++ b/usr/src/uts/common/fs/swapfs/swap_subr.c @@ -257,7 +257,7 @@ swap_sync(struct vfs *vfsp, short flag, struct cred *cr) if (vp) { VN_HOLD(vp); (void) VOP_PUTPAGE(vp, (offset_t)0, 0, - (B_ASYNC | B_FREE), kcred); + (B_ASYNC | B_FREE), kcred, NULL); VN_RELE(vp); } } diff --git a/usr/src/uts/common/fs/swapfs/swap_vnops.c b/usr/src/uts/common/fs/swapfs/swap_vnops.c index 53bdae350cce..4e69206084c8 100644 --- a/usr/src/uts/common/fs/swapfs/swap_vnops.c +++ b/usr/src/uts/common/fs/swapfs/swap_vnops.c @@ -55,13 +55,14 @@ * Define the routines within this file. */ static int swap_getpage(struct vnode *vp, offset_t off, size_t len, - uint_t *protp, struct page **plarr, size_t plsz, - struct seg *seg, caddr_t addr, enum seg_rw rw, struct cred *cr); + uint_t *protp, struct page **plarr, size_t plsz, struct seg *seg, + caddr_t addr, enum seg_rw rw, struct cred *cr, caller_context_t *ct); static int swap_putpage(struct vnode *vp, offset_t off, size_t len, - int flags, struct cred *cr); -static void swap_inactive(struct vnode *vp, struct cred *cr); + int flags, struct cred *cr, caller_context_t *ct); +static void swap_inactive(struct vnode *vp, struct cred *cr, + caller_context_t *ct); static void swap_dispose(vnode_t *vp, page_t *pp, int fl, int dn, - cred_t *cr); + cred_t *cr, caller_context_t *ct); static int swap_getapage(struct vnode *vp, u_offset_t off, size_t len, uint_t *protp, page_t **plarr, size_t plsz, @@ -94,7 +95,8 @@ vnodeops_t *swap_vnodeops; static void swap_inactive( struct vnode *vp, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { SWAPFS_PRINT(SWAP_VOPS, "swap_inactive: vp %x\n", vp, 0, 0, 0, 0); } @@ -102,6 +104,7 @@ swap_inactive( /* * Return all the pages from [off..off+len] in given file */ +/*ARGSUSED*/ static int swap_getpage( struct vnode *vp, @@ -113,7 +116,8 @@ swap_getpage( struct seg *seg, caddr_t addr, enum seg_rw rw, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { int err; @@ -236,7 +240,7 @@ swap_getapage( flags = (pl == NULL ? B_ASYNC|B_READ : B_READ); err = VOP_PAGEIO(pvp, pp, poff, - PAGESIZE, flags, cr); + PAGESIZE, flags, cr, NULL); if (!err) { ahm = &anonhash_lock[AH_LOCK(vp, off)]; @@ -412,7 +416,8 @@ swap_getconpage( } if (pvp) { - err = VOP_PAGEIO(pvp, pp, poff, PAGESIZE, B_READ, cr); + err = VOP_PAGEIO(pvp, pp, poff, PAGESIZE, B_READ, cr, + NULL); } else { pagezero(pp, 0, PAGESIZE); } @@ -464,7 +469,8 @@ swap_putpage( offset_t off, size_t len, int flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { page_t *pp; u_offset_t io_off; @@ -709,7 +715,7 @@ swap_putapage( } err = VOP_PAGEIO(klvp, pplist, klstart, klsz, - B_WRITE | flags, cr); + B_WRITE | flags, cr, NULL); if ((flags & B_ASYNC) == 0) pvn_write_done(pp, ((err) ? B_ERROR : 0) | B_WRITE | flags); @@ -731,7 +737,13 @@ swap_putapage( } static void -swap_dispose(vnode_t *vp, page_t *pp, int fl, int dn, cred_t *cr) +swap_dispose( + vnode_t *vp, + page_t *pp, + int fl, + int dn, + cred_t *cr, + caller_context_t *ct) { int err; u_offset_t off = pp->p_offset; @@ -751,7 +763,7 @@ swap_dispose(vnode_t *vp, page_t *pp, int fl, int dn, cred_t *cr) err = swap_getphysname(vp, off, &pvp, &poff); if (!err && pvp != NULL) - VOP_DISPOSE(pvp, pp, fl, dn, cr); + VOP_DISPOSE(pvp, pp, fl, dn, cr, ct); else - fs_dispose(vp, pp, fl, dn, cr); + fs_dispose(vp, pp, fl, dn, cr, ct); } diff --git a/usr/src/uts/common/fs/tmpfs/tmp_dir.c b/usr/src/uts/common/fs/tmpfs/tmp_dir.c index 682cac326bc0..f6621c8097b6 100644 --- a/usr/src/uts/common/fs/tmpfs/tmp_dir.c +++ b/usr/src/uts/common/fs/tmpfs/tmp_dir.c @@ -237,7 +237,8 @@ tdirenter( struct tmpnode *tp, /* source tmpnode, if link/rename */ struct vattr *va, struct tmpnode **tpp, /* return tmpnode, if create/mkdir */ - struct cred *cred) + struct cred *cred, + caller_context_t *ctp) { struct tdirent *tdp; struct tmpnode *found = NULL; @@ -346,7 +347,7 @@ tdirenter( if (error == 0) { if (found != NULL) { vnevent_rename_dest(TNTOV(found), - TNTOV(dir), name); + TNTOV(dir), name, ctp); } } diff --git a/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c b/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c index f798cb8ed42e..4ee833094fcb 100644 --- a/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c +++ b/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c @@ -338,7 +338,7 @@ tmp_mount( * Get the mode, uid, and gid from the underlying mount point. */ rattr.va_mask = AT_MODE|AT_UID|AT_GID; /* Hint to getattr */ - got_attrs = VOP_GETATTR(mvp, &rattr, 0, cr); + got_attrs = VOP_GETATTR(mvp, &rattr, 0, cr, NULL); rw_enter(&tp->tn_rwlock, RW_WRITER); TNTOV(tp)->v_flag |= VROOT; @@ -371,6 +371,9 @@ tmp_mount( error = 0; out: + if (error == 0) + vfs_set_feature(vfsp, VFSFT_XVATTR); + return (error); } diff --git a/usr/src/uts/common/fs/tmpfs/tmp_vnops.c b/usr/src/uts/common/fs/tmpfs/tmp_vnops.c index c2d921bba972..7be9c2bddafb 100644 --- a/usr/src/uts/common/fs/tmpfs/tmp_vnops.c +++ b/usr/src/uts/common/fs/tmpfs/tmp_vnops.c @@ -73,7 +73,7 @@ static int tmp_putapage(struct vnode *, page_t *, u_offset_t *, size_t *, /* ARGSUSED1 */ static int -tmp_open(struct vnode **vpp, int flag, struct cred *cred) +tmp_open(struct vnode **vpp, int flag, struct cred *cred, caller_context_t *ct) { /* * swapon to a tmpfs file is not supported so access @@ -86,8 +86,13 @@ tmp_open(struct vnode **vpp, int flag, struct cred *cred) /* ARGSUSED1 */ static int -tmp_close(struct vnode *vp, int flag, int count, - offset_t offset, struct cred *cred) +tmp_close( + struct vnode *vp, + int flag, + int count, + offset_t offset, + struct cred *cred, + caller_context_t *ct) { cleanlocks(vp, ttoproc(curthread)->p_pid, 0); cleanshares(vp, ttoproc(curthread)->p_pid); @@ -269,7 +274,7 @@ wrtmp( /* * We have to drop the contents lock to allow the VM - * system to reaquire it in tmp_getpage() + * system to reacquire it in tmp_getpage() */ rw_exit(&tp->tn_contents); @@ -496,7 +501,7 @@ rdtmp( /* * We have to drop the contents lock to prevent the VM - * system from trying to reaquire it in tmp_getpage() + * system from trying to reacquire it in tmp_getpage() * should the uiomove cause a pagefault. */ rw_exit(&tp->tn_contents); @@ -621,15 +626,26 @@ tmp_write(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cred, /* ARGSUSED */ static int -tmp_ioctl(struct vnode *vp, int com, intptr_t data, int flag, - struct cred *cred, int *rvalp) +tmp_ioctl( + struct vnode *vp, + int com, + intptr_t data, + int flag, + struct cred *cred, + int *rvalp, + caller_context_t *ct) { return (ENOTTY); } /* ARGSUSED2 */ static int -tmp_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cred) +tmp_getattr( + struct vnode *vp, + struct vattr *vap, + int flags, + struct cred *cred, + caller_context_t *ct) { struct tmpnode *tp = (struct tmpnode *)VTOTN(vp); struct vnode *mvp; @@ -651,7 +667,7 @@ tmp_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cred) mutex_exit(&tp->tn_tlock); bzero(&va, sizeof (struct vattr)); va.va_mask = AT_UID|AT_GID; - attrs = VOP_GETATTR(mvp, &va, 0, cred); + attrs = VOP_GETATTR(mvp, &va, 0, cred, ct); } else { mutex_exit(&tp->tn_tlock); } @@ -703,7 +719,7 @@ tmp_setattr( /* * Cannot set these attributes */ - if (vap->va_mask & AT_NOSET) + if ((vap->va_mask & AT_NOSET) || (vap->va_mask & AT_XVATTR)) return (EINVAL); mutex_enter(&tp->tn_tlock); @@ -763,7 +779,12 @@ tmp_setattr( /* ARGSUSED2 */ static int -tmp_access(struct vnode *vp, int mode, int flags, struct cred *cred) +tmp_access( + struct vnode *vp, + int mode, + int flags, + struct cred *cred, + caller_context_t *ct) { struct tmpnode *tp = (struct tmpnode *)VTOTN(vp); int error; @@ -783,7 +804,10 @@ tmp_lookup( struct pathname *pnp, int flags, struct vnode *rdir, - struct cred *cred) + struct cred *cred, + caller_context_t *ct, + int *direntflags, + pathname_t *realpnp) { struct tmpnode *tp = (struct tmpnode *)VTOTN(dvp); struct tmpnode *ntp = NULL; @@ -795,6 +819,12 @@ tmp_lookup( struct tmpnode *xdp; struct tmount *tm; + /* + * don't allow attributes if not mounted XATTR support + */ + if (!(dvp->v_vfsp->vfs_flag & VFS_XATTR)) + return (EINVAL); + if (tp->tn_flags & ISXATTR) /* No attributes on attributes */ return (EINVAL); @@ -894,7 +924,9 @@ tmp_create( int mode, struct vnode **vpp, struct cred *cred, - int flag) + int flag, + caller_context_t *ct, + vsecattr_t *vsecp) { struct tmpnode *parent; struct tmount *tm; @@ -977,7 +1009,7 @@ tmp_create( } if (error == 0) { - vnevent_create(*vpp); + vnevent_create(*vpp, ct); } return (0); } @@ -988,7 +1020,7 @@ tmp_create( rw_enter(&parent->tn_rwlock, RW_WRITER); error = tdirenter(tm, parent, nm, DE_CREATE, (struct tmpnode *)NULL, (struct tmpnode *)NULL, - vap, &self, cred); + vap, &self, cred, ct); rw_exit(&parent->tn_rwlock); if (error) { @@ -1026,8 +1058,14 @@ tmp_create( return (0); } +/* ARGSUSED3 */ static int -tmp_remove(struct vnode *dvp, char *nm, struct cred *cred) +tmp_remove( + struct vnode *dvp, + char *nm, + struct cred *cred, + caller_context_t *ct, + int flags) { struct tmpnode *parent = (struct tmpnode *)VTOTN(dvp); int error; @@ -1047,7 +1085,7 @@ tmp_remove(struct vnode *dvp, char *nm, struct cred *cred) rw_exit(&tp->tn_rwlock); rw_exit(&parent->tn_rwlock); - vnevent_remove(TNTOV(tp), dvp, nm); + vnevent_remove(TNTOV(tp), dvp, nm, ct); tmpnode_rele(tp); TRACE_3(TR_FAC_TMPFS, TR_TMPFS_REMOVE, @@ -1055,8 +1093,15 @@ tmp_remove(struct vnode *dvp, char *nm, struct cred *cred) return (error); } +/* ARGSUSED4 */ static int -tmp_link(struct vnode *dvp, struct vnode *srcvp, char *tnm, struct cred *cred) +tmp_link( + struct vnode *dvp, + struct vnode *srcvp, + char *tnm, + struct cred *cred, + caller_context_t *ct, + int flags) { struct tmpnode *parent; struct tmpnode *from; @@ -1065,7 +1110,7 @@ tmp_link(struct vnode *dvp, struct vnode *srcvp, char *tnm, struct cred *cred) struct tmpnode *found = NULL; struct vnode *realvp; - if (VOP_REALVP(srcvp, &realvp) == 0) + if (VOP_REALVP(srcvp, &realvp, ct) == 0) srcvp = realvp; parent = (struct tmpnode *)VTOTN(dvp); @@ -1095,21 +1140,24 @@ tmp_link(struct vnode *dvp, struct vnode *srcvp, char *tnm, struct cred *cred) rw_enter(&parent->tn_rwlock, RW_WRITER); error = tdirenter(tm, parent, tnm, DE_LINK, (struct tmpnode *)NULL, - from, NULL, (struct tmpnode **)NULL, cred); + from, NULL, (struct tmpnode **)NULL, cred, ct); rw_exit(&parent->tn_rwlock); if (error == 0) { - vnevent_link(srcvp); + vnevent_link(srcvp, ct); } return (error); } +/* ARGSUSED5 */ static int tmp_rename( struct vnode *odvp, /* source parent vnode */ char *onm, /* source name */ struct vnode *ndvp, /* destination parent vnode */ char *nnm, /* destination name */ - struct cred *cred) + struct cred *cred, + caller_context_t *ct, + int flags) { struct tmpnode *fromparent; struct tmpnode *toparent; @@ -1119,7 +1167,7 @@ tmp_rename( int samedir = 0; /* set if odvp == ndvp */ struct vnode *realvp; - if (VOP_REALVP(ndvp, &realvp) == 0) + if (VOP_REALVP(ndvp, &realvp, ct) == 0) ndvp = realvp; fromparent = (struct tmpnode *)VTOTN(odvp); @@ -1178,7 +1226,7 @@ tmp_rename( rw_enter(&toparent->tn_rwlock, RW_WRITER); error = tdirenter(tm, toparent, nnm, DE_RENAME, fromparent, fromtp, (struct vattr *)NULL, - (struct tmpnode **)NULL, cred); + (struct tmpnode **)NULL, cred, ct); rw_exit(&toparent->tn_rwlock); if (error) { @@ -1191,14 +1239,14 @@ tmp_rename( error = 0; goto done; } - vnevent_rename_src(TNTOV(fromtp), odvp, onm); + vnevent_rename_src(TNTOV(fromtp), odvp, onm, ct); /* * Notify the target directory if not same as * source directory. */ if (ndvp != odvp) { - vnevent_rename_dest_dir(ndvp); + vnevent_rename_dest_dir(ndvp, ct); } /* @@ -1232,13 +1280,17 @@ tmp_rename( return (error); } +/* ARGSUSED5 */ static int tmp_mkdir( struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp, - struct cred *cred) + struct cred *cred, + caller_context_t *ct, + int flags, + vsecattr_t *vsecp) { struct tmpnode *parent = (struct tmpnode *)VTOTN(dvp); struct tmpnode *self = NULL; @@ -1269,7 +1321,7 @@ tmp_mkdir( rw_enter(&parent->tn_rwlock, RW_WRITER); error = tdirenter(tm, parent, nm, DE_MKDIR, (struct tmpnode *)NULL, (struct tmpnode *)NULL, va, - &self, cred); + &self, cred, ct); if (error) { rw_exit(&parent->tn_rwlock); if (self) @@ -1281,12 +1333,15 @@ tmp_mkdir( return (0); } +/* ARGSUSED4 */ static int tmp_rmdir( struct vnode *dvp, char *nm, struct vnode *cdir, - struct cred *cred) + struct cred *cred, + caller_context_t *ct, + int flags) { struct tmpnode *parent = (struct tmpnode *)VTOTN(dvp); struct tmpnode *self = NULL; @@ -1354,16 +1409,21 @@ tmp_rmdir( done1: rw_exit(&self->tn_rwlock); rw_exit(&parent->tn_rwlock); - vnevent_rmdir(TNTOV(self), dvp, nm); + vnevent_rmdir(TNTOV(self), dvp, nm, ct); tmpnode_rele(self); return (error); } /* ARGSUSED2 */ - static int -tmp_readdir(struct vnode *vp, struct uio *uiop, struct cred *cred, int *eofp) +tmp_readdir( + struct vnode *vp, + struct uio *uiop, + struct cred *cred, + int *eofp, + caller_context_t *ct, + int flags) { struct tmpnode *tp = (struct tmpnode *)VTOTN(vp); struct tdirent *tdp; @@ -1467,13 +1527,16 @@ tmp_readdir(struct vnode *vp, struct uio *uiop, struct cred *cred, int *eofp) return (error); } +/* ARGSUSED5 */ static int tmp_symlink( struct vnode *dvp, char *lnm, struct vattr *tva, char *tnm, - struct cred *cred) + struct cred *cred, + caller_context_t *ct, + int flags) { struct tmpnode *parent = (struct tmpnode *)VTOTN(dvp); struct tmpnode *self = (struct tmpnode *)NULL; @@ -1503,7 +1566,7 @@ tmp_symlink( rw_enter(&parent->tn_rwlock, RW_WRITER); error = tdirenter(tm, parent, lnm, DE_CREATE, (struct tmpnode *)NULL, - (struct tmpnode *)NULL, tva, &self, cred); + (struct tmpnode *)NULL, tva, &self, cred, ct); rw_exit(&parent->tn_rwlock); if (error) { @@ -1527,7 +1590,11 @@ tmp_symlink( /* ARGSUSED2 */ static int -tmp_readlink(struct vnode *vp, struct uio *uiop, struct cred *cred) +tmp_readlink( + struct vnode *vp, + struct uio *uiop, + struct cred *cred, + caller_context_t *ct) { struct tmpnode *tp = (struct tmpnode *)VTOTN(vp); int error = 0; @@ -1546,14 +1613,18 @@ tmp_readlink(struct vnode *vp, struct uio *uiop, struct cred *cred) /* ARGSUSED */ static int -tmp_fsync(struct vnode *vp, int syncflag, struct cred *cred) +tmp_fsync( + struct vnode *vp, + int syncflag, + struct cred *cred, + caller_context_t *ct) { return (0); } /* ARGSUSED */ static void -tmp_inactive(struct vnode *vp, struct cred *cred) +tmp_inactive(struct vnode *vp, struct cred *cred, caller_context_t *ct) { struct tmpnode *tp = (struct tmpnode *)VTOTN(vp); struct tmount *tm = (struct tmount *)VFSTOTM(vp->v_vfsp); @@ -1634,8 +1705,9 @@ tmp_inactive(struct vnode *vp, struct cred *cred) tmp_memfree(tp, sizeof (struct tmpnode)); } +/* ARGSUSED2 */ static int -tmp_fid(struct vnode *vp, struct fid *fidp) +tmp_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct) { struct tmpnode *tp = (struct tmpnode *)VTOTN(vp); struct tfid *tfid; @@ -1659,6 +1731,7 @@ tmp_fid(struct vnode *vp, struct fid *fidp) /* * Return all the pages from [off..off+len] in given file */ +/* ARGSUSED */ static int tmp_getpage( struct vnode *vp, @@ -1670,7 +1743,8 @@ tmp_getpage( struct seg *seg, caddr_t addr, enum seg_rw rw, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { int err = 0; struct tmpnode *tp = VTOTN(vp); @@ -1788,7 +1862,7 @@ tmp_getapage( if (pvp) { flags = (pl == NULL ? B_ASYNC|B_READ : B_READ); err = VOP_PAGEIO(pvp, pp, (u_offset_t)poff, PAGESIZE, - flags, cr); + flags, cr, NULL); if (flags & B_ASYNC) pp = NULL; } else if (rw != S_CREATE) { @@ -1820,7 +1894,8 @@ tmp_putpage( offset_t off, size_t len, int flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { register page_t *pp; u_offset_t io_off; @@ -2037,7 +2112,7 @@ tmp_putapage( /* Do i/o on the remaining kluster */ err = VOP_PAGEIO(pvp, pplist, (u_offset_t)pstart, io_len, - B_WRITE | flags, cr); + B_WRITE | flags, cr, NULL); if ((flags & B_ASYNC) == 0) { pvn_write_done(pplist, ((err) ? B_ERROR : 0) | B_WRITE | flags); @@ -2056,6 +2131,7 @@ tmp_putapage( return (err); } +/* ARGSUSED */ static int tmp_map( struct vnode *vp, @@ -2066,7 +2142,8 @@ tmp_map( uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cred) + struct cred *cred, + caller_context_t *ct) { struct segvn_crargs vn_a; struct tmpnode *tp = (struct tmpnode *)VTOTN(vp); @@ -2139,7 +2216,8 @@ tmp_addmap( uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cred) + struct cred *cred, + caller_context_t *ct) { return (0); } @@ -2155,7 +2233,8 @@ tmp_delmap( uint_t prot, uint_t maxprot, uint_t flags, - struct cred *cred) + struct cred *cred, + caller_context_t *ct) { return (0); } @@ -2243,7 +2322,11 @@ tmp_space( /* ARGSUSED */ static int -tmp_seek(struct vnode *vp, offset_t ooff, offset_t *noffp) +tmp_seek( + struct vnode *vp, + offset_t ooff, + offset_t *noffp, + caller_context_t *ct) { return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0); } @@ -2272,7 +2355,12 @@ tmp_rwunlock(struct vnode *vp, int write_lock, caller_context_t *ctp) } static int -tmp_pathconf(struct vnode *vp, int cmd, ulong_t *valp, cred_t *cr) +tmp_pathconf( + struct vnode *vp, + int cmd, + ulong_t *valp, + cred_t *cr, + caller_context_t *ct) { struct tmpnode *tp = NULL; int error; @@ -2296,8 +2384,14 @@ tmp_pathconf(struct vnode *vp, int cmd, ulong_t *valp, cred_t *cr) error = EINVAL; } break; + case _PC_SATTR_ENABLED: + case _PC_SATTR_EXISTS: + *valp = vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) && + (vp->v_type == VREG || vp->v_type == VDIR); + error = 0; + break; default: - error = fs_pathconf(vp, cmd, valp, cr); + error = fs_pathconf(vp, cmd, valp, cr, ct); } return (error); } diff --git a/usr/src/uts/common/fs/udfs/udf_dir.c b/usr/src/uts/common/fs/udfs/udf_dir.c index d470a2588a74..40b0a7d4aac2 100644 --- a/usr/src/uts/common/fs/udfs/udf_dir.c +++ b/usr/src/uts/common/fs/udfs/udf_dir.c @@ -327,9 +327,16 @@ ud_dirlook(struct ud_inode *dip, } int -ud_direnter(struct ud_inode *tdp, - char *namep, enum de_op op, struct ud_inode *sdp, struct ud_inode *sip, - struct vattr *vap, struct ud_inode **ipp, struct cred *cr) +ud_direnter( + struct ud_inode *tdp, + char *namep, + enum de_op op, + struct ud_inode *sdp, + struct ud_inode *sip, + struct vattr *vap, + struct ud_inode **ipp, + struct cred *cr, + caller_context_t *ctp) { struct udf_vfs *udf_vfsp; struct ud_inode *tip; @@ -556,11 +563,11 @@ ud_direnter(struct ud_inode *tdp, if (err == 0) { if (tip) { vnevent_rename_dest(ITOV(tip), ITOV(tdp), - namep); + namep, ctp); } if (sdp != tdp) { - vnevent_rename_dest_dir(ITOV(tdp)); + vnevent_rename_dest_dir(ITOV(tdp), ctp); } } @@ -594,9 +601,14 @@ ud_direnter(struct ud_inode *tdp, * function seems to be really weird */ int -ud_dirremove(struct ud_inode *dp, - char *namep, struct ud_inode *oip, struct vnode *cdir, - enum dr_op op, struct cred *cr) +ud_dirremove( + struct ud_inode *dp, + char *namep, + struct ud_inode *oip, + struct vnode *cdir, + enum dr_op op, + struct cred *cr, + caller_context_t *ctp) { struct udf_vfs *udf_vfsp; int32_t namelen, err = 0; @@ -852,9 +864,9 @@ ud_dirremove(struct ud_inode *dp, */ if (err == 0) { if (op == DR_REMOVE) { - vnevent_remove(ITOV(ip), ITOV(dp), namep); + vnevent_remove(ITOV(ip), ITOV(dp), namep, ctp); } else if (op == DR_RMDIR) { - vnevent_rmdir(ITOV(ip), ITOV(dp), namep); + vnevent_rmdir(ITOV(ip), ITOV(dp), namep, ctp); } } VN_RELE(ITOV(ip)); diff --git a/usr/src/uts/common/fs/udfs/udf_subr.c b/usr/src/uts/common/fs/udfs/udf_subr.c index b8ab49457feb..dd06e0d884de 100644 --- a/usr/src/uts/common/fs/udfs/udf_subr.c +++ b/usr/src/uts/common/fs/udfs/udf_subr.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -223,7 +222,7 @@ ud_xlate_to_daddr(struct udf_vfs *udf_vfsp, continue; } if ((end_req < begin_bad) || - (begin_req >= end_bad)) { + (begin_req >= end_bad)) { continue; } @@ -232,7 +231,7 @@ ud_xlate_to_daddr(struct udf_vfs *udf_vfsp, end_req = begin_bad; } else { retblkno = SWAP_32(te->sent_ml) + - begin_req - begin_bad; + begin_req - begin_bad; if (end_req < end_bad) { *count = end_req - begin_req; } else { @@ -319,10 +318,10 @@ ud_ip_off2bno(struct ud_inode *ip, uint32_t offset, uint32_t *bno) for (i = 0; i < ip->i_ext_used; i++) { iext = &ip->i_ext[i]; if ((iext->ib_offset <= offset) && - (offset < (iext->ib_offset + iext->ib_count))) { + (offset < (iext->ib_offset + iext->ib_count))) { *bno = iext->ib_block + - ((offset - iext->ib_offset) >> - ip->i_udf->udf_l2b_shift); + ((offset - iext->ib_offset) >> + ip->i_udf->udf_l2b_shift); break; } } @@ -338,8 +337,6 @@ static uint32_t cum_sec_leap[] = { 0xeff100, 0x118cf80, 0x141ae00, 0x1693b00, 0x1921980, 0x1b9a680 }; -#define SECS_PER_MIN 60 -#define SECS_PER_HOUR 3600 #define DAYS_PER_YEAR 365 #define SEC_PER_DAY 0x15180 @@ -363,8 +360,8 @@ ud_dtime2utime(struct timespec32 *utime, utime->tv_sec = cp[dtime->ts_month - 1]; utime->tv_sec += (dtime->ts_day - 1) * SEC_PER_DAY; utime->tv_sec += ((dtime->ts_hour * 60) + - dtime->ts_min) * 60 + - dtime->ts_sec; + dtime->ts_min) * 60 + + dtime->ts_sec; tzone = SWAP_16(dtime->ts_tzone); if ((tzone & TMODE) == 0x1000) { @@ -384,15 +381,15 @@ ud_dtime2utime(struct timespec32 *utime, } utime->tv_nsec = ((((dtime->ts_csec * 100) + - dtime->ts_husec) * 100) + - dtime->ts_usec) * 1000; + dtime->ts_husec) * 100) + + dtime->ts_usec) * 1000; if (year >= 1970) { utime->tv_sec += (year - 1970) * SEC_PER_YEAR; utime->tv_sec += ((year - 1969) / 4) * SEC_PER_DAY; } else { utime->tv_sec = ((1970 - year) * SEC_PER_YEAR + - ((1972 - year) / 4) * SEC_PER_DAY - - utime->tv_sec) * -1; + ((1972 - year) / 4) * SEC_PER_DAY - + utime->tv_sec) * -1; if (utime->tv_nsec) { utime->tv_sec++; utime->tv_nsec = 1000 * 1000 * 1000 - utime->tv_nsec; @@ -498,7 +495,7 @@ ud_syncip(struct ud_inode *ip, int32_t flags, int32_t waitfor) } else { rw_exit(&ip->i_contents); error = VOP_PUTPAGE(vp, (offset_t)0, - (uint32_t)0, flags, CRED()); + (uint32_t)0, flags, CRED(), NULL); rw_enter(&ip->i_contents, RW_WRITER); } @@ -561,10 +558,10 @@ ud_sbwrite(struct udf_vfs *udf_vfsp) ud_update_regid(&iu->lvidiu_regid); ud_make_tag(udf_vfsp, &lvid->lvid_tag, - UD_LOG_VOL_INT, udf_vfsp->udf_iseq_loc, - sizeof (struct log_vol_int_desc) - 8 + - 8 * udf_vfsp->udf_npart + - SWAP_32(lvid->lvid_liu)); + UD_LOG_VOL_INT, udf_vfsp->udf_iseq_loc, + sizeof (struct log_vol_int_desc) - 8 + + 8 * udf_vfsp->udf_npart + + SWAP_32(lvid->lvid_liu)); /* * Don't release the buffer after writing to the disk @@ -627,7 +624,7 @@ ud_update(int32_t flag) */ mutex_enter(&udf_vfs_mutex); for (udfsp = udf_vfs_instances; - udfsp != NULL; udfsp = udfsp->udf_next) { + udfsp != NULL; udfsp = udfsp->udf_next) { vfsp = udfsp->udf_vfs; if (vfs_lock(vfsp) != 0) { continue; @@ -762,7 +759,7 @@ ud_still_mounted(struct check_node *checkp) mutex_enter(&udf_vfs_mutex); for (udf_vfsp = udf_vfs_instances; - udf_vfsp != NULL; udf_vfsp = udf_vfsp->udf_next) { + udf_vfsp != NULL; udf_vfsp = udf_vfsp->udf_next) { if (udf_vfsp != checkp->udf_vfs) { continue; } @@ -802,7 +799,7 @@ ud_checkclean(struct vfs *vfsp, * ignore if buffers or inodes are busy */ if ((bcheck(dev, udf_vfsp->udf_iseq)) || - (ud_icheck(udf_vfsp))) { + (ud_icheck(udf_vfsp))) { return; } mutex_enter(&udf_vfsp->udf_lock); @@ -856,8 +853,8 @@ ud_flushi(int32_t flag) lip = NULL; for (ip = ih->ih_chain[0], lip = NULL; - ip && ip != (struct ud_inode *)ih; - ip = ip->i_forw) { + ip && ip != (struct ud_inode *)ih; + ip = ip->i_forw) { int flag = ip->i_flag; vp = ITOV(ip); @@ -867,9 +864,9 @@ ud_flushi(int32_t flag) * Skip read-only vnodes */ if ((flag & IREF) == 0 || - (!vn_has_cached_data(vp) && - ((flag & (IMOD|IACC|IUPD|ICHG)) == 0)) || - (vp->v_vfsp == NULL) || vn_is_readonly(vp)) { + (!vn_has_cached_data(vp) && + ((flag & (IMOD|IACC|IUPD|ICHG)) == 0)) || + (vp->v_vfsp == NULL) || vn_is_readonly(vp)) { continue; } @@ -1010,7 +1007,7 @@ ud_get_next_fid(struct ud_inode *ip, struct fbuf **fbp, uint32_t offset, if ((offset % lbsize) || - (offset == 0)) { + (offset == 0)) { sz = end - beg; } else { sz = 0; @@ -1276,14 +1273,14 @@ ud_verify_tag_and_desc(struct tag *tag, uint16_t id, uint32_t blockno, eah = (struct ext_attr_hdr *)tag; if (SWAP_32(eah->eah_aal) > desc_len) { cmn_err(CE_NOTE, - "eah_all(0x%x) exceeds desc. len(0x%x) blockno 0x%x\n", + "eah_all(0x%x) exceeds desc. len(0x%x) blockno 0x%x\n", SWAP_32(eah->eah_aal), desc_len, blockno); - return (1); + return (1); } ea_off = GET_32(&eah->eah_ial); if (ea_off >= desc_len) { cmn_err(CE_NOTE, - "ea_off(0x%x) is not less than ea_len(0x%x) blockno 0x%x\n", + "ea_off(0x%x) is not less than ea_len(0x%x) blockno 0x%x\n", ea_off, desc_len, blockno); return (1); } @@ -1293,8 +1290,8 @@ ud_verify_tag_and_desc(struct tag *tag, uint16_t id, uint32_t blockno, } if (SWAP_32(blockno) != tag->tag_loc) { cmn_err(CE_NOTE, - "Tag Location mismatch blockno %x tag_blockno %x\n", - blockno, SWAP_32(tag->tag_loc)); + "Tag Location mismatch blockno %x tag_blockno %x\n", + blockno, SWAP_32(tag->tag_loc)); return (1); } return (0); @@ -1455,41 +1452,41 @@ ud_utf82utf16(uint8_t *s_8, uint16_t *c_16, int32_t count) c_32 = *s_8 & 0x7F; } else if (extra_bytes == 1) { if (((*s_8 & 0xE0) != 0xC0) || - ((*(s_8 + 1) & 0xC0) != 0x80)) { + ((*(s_8 + 1) & 0xC0) != 0x80)) { return (0); } c_32 = *s_8 & 0x1F; } else if (extra_bytes == 2) { if (((*s_8 & 0xF0) != 0xE0) || - ((*(s_8 + 1) & 0xC0) != 0x80) || - ((*(s_8 + 2) & 0xC0) != 0x80)) { + ((*(s_8 + 1) & 0xC0) != 0x80) || + ((*(s_8 + 2) & 0xC0) != 0x80)) { return (0); } c_32 = *s_8 & 0x0F; } else if (extra_bytes == 3) { if (((*s_8 & 0xF8) != 0xF0) || - ((*(s_8 + 1) & 0xC0) != 0x80) || - ((*(s_8 + 2) & 0xC0) != 0x80) || - ((*(s_8 + 3) & 0xC0) != 0x80)) { + ((*(s_8 + 1) & 0xC0) != 0x80) || + ((*(s_8 + 2) & 0xC0) != 0x80) || + ((*(s_8 + 3) & 0xC0) != 0x80)) { return (0); } c_32 = *s_8 & 0x07; } else if (extra_bytes == 4) { if (((*s_8 & 0xFC) != 0xF8) || - ((*(s_8 + 1) & 0xC0) != 0x80) || - ((*(s_8 + 2) & 0xC0) != 0x80) || - ((*(s_8 + 3) & 0xC0) != 0x80) || - ((*(s_8 + 4) & 0xC0) != 0x80)) { + ((*(s_8 + 1) & 0xC0) != 0x80) || + ((*(s_8 + 2) & 0xC0) != 0x80) || + ((*(s_8 + 3) & 0xC0) != 0x80) || + ((*(s_8 + 4) & 0xC0) != 0x80)) { return (0); } c_32 = *s_8 & 0x03; } else if (extra_bytes == 5) { if (((*s_8 & 0xFE) != 0xFC) || - ((*(s_8 + 1) & 0xC0) != 0x80) || - ((*(s_8 + 2) & 0xC0) != 0x80) || - ((*(s_8 + 3) & 0xC0) != 0x80) || - ((*(s_8 + 4) & 0xC0) != 0x80) || - ((*(s_8 + 5) & 0xC0) != 0x80)) { + ((*(s_8 + 1) & 0xC0) != 0x80) || + ((*(s_8 + 2) & 0xC0) != 0x80) || + ((*(s_8 + 3) & 0xC0) != 0x80) || + ((*(s_8 + 4) & 0xC0) != 0x80) || + ((*(s_8 + 5) & 0xC0) != 0x80)) { return (0); } c_32 = *s_8 & 0x01; @@ -1560,7 +1557,7 @@ ud_compress(int32_t in_len, int32_t *out_len, comp_id = 8; for (in_index = 0; in_index < in_len; in_index += c_tx_sz) { if ((c_tx_sz = ud_utf82utf16(&in_str[in_index], - &w2_char, in_len - in_index)) == 0) { + &w2_char, in_len - in_index)) == 0) { error = EINVAL; goto end; } @@ -1576,7 +1573,7 @@ ud_compress(int32_t in_len, int32_t *out_len, w2_str[out_index++] = w2_char; } if (((comp_id == 0x10) && (out_index > ((out_str_len - 2)/2))) || - ((comp_id == 0x8) && (out_index > (out_str_len - 2)))) { + ((comp_id == 0x8) && (out_index > (out_str_len - 2)))) { error = ENAMETOOLONG; goto end; } @@ -1669,9 +1666,9 @@ ud_utf162utf8(uint16_t c_16, uint8_t *s_8) } /* - * Convert to a form that can be transfered to the user + * Convert to a form that can be transferred to the user * Assumption's - * in_length < 256, out_str is atleast 255 bytes long + * in_length < 256, out_str is at least 255 bytes long * The converted byte stream length is returned in out_len */ #define MAX_ALLOWABLE_STRING 250 @@ -1703,16 +1700,16 @@ ud_uncompress(int32_t in_len, int32_t *out_len, */ if (comp_id == 8) { if ((in_str[1] == DOT) && - ((in_len == 2) || ((in_len == 3) && - (in_str[2] == DOT)))) { + ((in_len == 2) || ((in_len == 3) && + (in_str[2] == DOT)))) { out_str[k++] = UNDERBAR; len_till_now = 1; goto make_append_crc; } } else if (comp_id == 0x10) { if (((in_str[1] << 8 | in_str[2]) == DOT) && - ((in_len == 3) || ((in_len == 5) && - ((in_str[3] << 8 | in_str[4]) == DOT)))) { + ((in_len == 3) || ((in_len == 5) && + ((in_str[3] << 8 | in_str[4]) == DOT)))) { out_str[k++] = UNDERBAR; len_till_now = 1; goto make_append_crc; @@ -1746,12 +1743,12 @@ ud_uncompress(int32_t in_len, int32_t *out_len, * Get rid of invalid characters */ if ((w2_char == SLASH) || - (w2_char == NULL)) { + (w2_char == NULL)) { make_crc = 1; if (((comp_id == 8) && - (lic != (index - 1))) || - (comp_id == 0x10) && - (lic != (index - 2))) { + (lic != (index - 1))) || + (comp_id == 0x10) && + (lic != (index - 2))) { w2_char = UNDERBAR; lic = index; } else { @@ -1776,13 +1773,13 @@ ud_uncompress(int32_t in_len, int32_t *out_len, * the maximum allowed string length */ if ((crc_start_loc == 0) && - ((len_till_now + c_tx_sz) > MAX_ALLOWABLE_STRING)) { + ((len_till_now + c_tx_sz) > MAX_ALLOWABLE_STRING)) { crc_start_loc = len_till_now; } if ((len_till_now + c_tx_sz) < MAXNAMELEN) { (void) strncpy((caddr_t)&out_str[len_till_now], - (caddr_t)utf8, c_tx_sz); + (caddr_t)utf8, c_tx_sz); len_till_now += c_tx_sz; } else { break; @@ -1835,7 +1832,7 @@ ud_bread(dev_t dev, daddr_t blkno, long bsize) bp = bread(dev, blkno, bsize); if (((bp->b_flags & B_ERROR) == 0) && - (bp->b_bcount != bsize)) { + (bp->b_bcount != bsize)) { /* * Buffer cache returned a * wrong number of bytes diff --git a/usr/src/uts/common/fs/udfs/udf_vfsops.c b/usr/src/uts/common/fs/udfs/udf_vfsops.c index 79e76d2715d6..95f98d62299c 100644 --- a/usr/src/uts/common/fs/udfs/udf_vfsops.c +++ b/usr/src/uts/common/fs/udfs/udf_vfsops.c @@ -282,7 +282,7 @@ udf_mount(struct vfs *vfsp, struct vnode *mvp, oflag = FREAD | FWRITE; aflag = VREAD | VWRITE; } - if ((error = VOP_ACCESS(bvp, aflag, 0, cr)) != 0 || + if ((error = VOP_ACCESS(bvp, aflag, 0, cr, NULL)) != 0 || (error = secpolicy_spec_open(cr, bvp, oflag)) != 0) { goto out; } @@ -365,8 +365,8 @@ udf_unmount(struct vfs *vfsp, int fflag, struct cred *cr) ud_destroy_fsp(udf_vfsp); - (void) VOP_PUTPAGE(bvp, (offset_t)0, (uint32_t)0, B_INVAL, cr); - (void) VOP_CLOSE(bvp, flag, 1, (offset_t)0, cr); + (void) VOP_PUTPAGE(bvp, (offset_t)0, (uint32_t)0, B_INVAL, cr, NULL); + (void) VOP_CLOSE(bvp, flag, 1, (offset_t)0, cr, NULL); (void) bfinval(vfsp->vfs_dev, 1); VN_RELE(bvp); @@ -555,7 +555,7 @@ udf_mountroot(struct vfs *vfsp, enum whymountroot why) (void) dnlc_purge_vfsp(vfsp, 0); vp = common_specvp(vp); (void) VOP_PUTPAGE(vp, (offset_t)0, - (uint32_t)0, B_INVAL, CRED()); + (uint32_t)0, B_INVAL, CRED(), NULL); binval(vfsp->vfs_dev); ovflags = vfsp->vfs_flag; @@ -566,7 +566,7 @@ udf_mountroot(struct vfs *vfsp, enum whymountroot why) ud_update(0); vp = ((struct udf_vfs *)vfsp->vfs_data)->udf_devvp; (void) VOP_CLOSE(vp, FREAD|FWRITE, 1, - (offset_t)0, CRED()); + (offset_t)0, CRED(), NULL); return (0); } @@ -630,7 +630,8 @@ ud_mountfs(struct vfs *vfsp, * operations. */ error = VOP_OPEN(&devvp, - (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, cr); + (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, + cr, NULL); if (error) { goto out; } @@ -677,7 +678,7 @@ ud_mountfs(struct vfs *vfsp, if (udf_vfsp->udf_flags & UDF_FL_RDONLY) { (void) dnlc_purge_vfsp(vfsp, 0); (void) VOP_PUTPAGE(devvp, (offset_t)0, (uint_t)0, - B_INVAL, CRED()); + B_INVAL, CRED(), NULL); (void) ud_iflush(vfsp); bflush(dev); binval(dev); @@ -769,7 +770,7 @@ ud_mountfs(struct vfs *vfsp, * they really should be using the raw device. */ (void) VOP_PUTPAGE(common_specvp(devvp), (offset_t)0, - (uint32_t)0, B_INVAL, cr); + (uint32_t)0, B_INVAL, cr, NULL); /* @@ -936,7 +937,7 @@ ud_mountfs(struct vfs *vfsp, ud_destroy_fsp(udf_vfsp); if (needclose) { (void) VOP_CLOSE(devvp, (vfsp->vfs_flag & VFS_RDONLY) ? - FREAD : FREAD|FWRITE, 1, (offset_t)0, cr); + FREAD : FREAD|FWRITE, 1, (offset_t)0, cr, NULL); bflush(dev); binval(dev); } diff --git a/usr/src/uts/common/fs/udfs/udf_vnops.c b/usr/src/uts/common/fs/udfs/udf_vnops.c index defbd544f575..2a2822eb3f5a 100644 --- a/usr/src/uts/common/fs/udfs/udf_vnops.c +++ b/usr/src/uts/common/fs/udfs/udf_vnops.c @@ -84,70 +84,79 @@ #include static int32_t udf_open(struct vnode **, - int32_t, struct cred *); + int32_t, struct cred *, caller_context_t *); static int32_t udf_close(struct vnode *, - int32_t, int32_t, offset_t, struct cred *); + int32_t, int32_t, offset_t, struct cred *, caller_context_t *); static int32_t udf_read(struct vnode *, - struct uio *, int32_t, struct cred *, struct caller_context *); + struct uio *, int32_t, struct cred *, caller_context_t *); static int32_t udf_write(struct vnode *, - struct uio *, int32_t, struct cred *, struct caller_context *); + struct uio *, int32_t, struct cred *, caller_context_t *); static int32_t udf_ioctl(struct vnode *, - int32_t, intptr_t, int32_t, struct cred *, int32_t *); + int32_t, intptr_t, int32_t, struct cred *, int32_t *, + caller_context_t *); static int32_t udf_getattr(struct vnode *, - struct vattr *, int32_t, struct cred *); + struct vattr *, int32_t, struct cred *, caller_context_t *); static int32_t udf_setattr(struct vnode *, struct vattr *, int32_t, struct cred *, caller_context_t *); static int32_t udf_access(struct vnode *, - int32_t, int32_t, struct cred *); + int32_t, int32_t, struct cred *, caller_context_t *); static int32_t udf_lookup(struct vnode *, char *, struct vnode **, struct pathname *, - int32_t, struct vnode *, struct cred *); + int32_t, struct vnode *, struct cred *, + caller_context_t *, int *, pathname_t *); static int32_t udf_create(struct vnode *, char *, struct vattr *, enum vcexcl, - int32_t, struct vnode **, struct cred *, int32_t); + int32_t, struct vnode **, struct cred *, int32_t, + caller_context_t *, vsecattr_t *); static int32_t udf_remove(struct vnode *, - char *, struct cred *); + char *, struct cred *, caller_context_t *, int); static int32_t udf_link(struct vnode *, - struct vnode *, char *, struct cred *); + struct vnode *, char *, struct cred *, caller_context_t *, int); static int32_t udf_rename(struct vnode *, - char *, struct vnode *, char *, struct cred *); + char *, struct vnode *, char *, struct cred *, caller_context_t *, int); static int32_t udf_mkdir(struct vnode *, - char *, struct vattr *, struct vnode **, struct cred *); + char *, struct vattr *, struct vnode **, struct cred *, + caller_context_t *, int, vsecattr_t *); static int32_t udf_rmdir(struct vnode *, - char *, struct vnode *, struct cred *); + char *, struct vnode *, struct cred *, caller_context_t *, int); static int32_t udf_readdir(struct vnode *, - struct uio *, struct cred *, int32_t *); + struct uio *, struct cred *, int32_t *, caller_context_t *, int); static int32_t udf_symlink(struct vnode *, - char *, struct vattr *, char *, struct cred *); + char *, struct vattr *, char *, struct cred *, caller_context_t *, int); static int32_t udf_readlink(struct vnode *, - struct uio *, struct cred *); + struct uio *, struct cred *, caller_context_t *); static int32_t udf_fsync(struct vnode *, - int32_t, struct cred *); + int32_t, struct cred *, caller_context_t *); static void udf_inactive(struct vnode *, - struct cred *); -static int32_t udf_fid(struct vnode *, struct fid *); + struct cred *, caller_context_t *); +static int32_t udf_fid(struct vnode *, struct fid *, caller_context_t *); static int udf_rwlock(struct vnode *, int32_t, caller_context_t *); static void udf_rwunlock(struct vnode *, int32_t, caller_context_t *); -static int32_t udf_seek(struct vnode *, offset_t, offset_t *); +static int32_t udf_seek(struct vnode *, offset_t, offset_t *, + caller_context_t *); static int32_t udf_frlock(struct vnode *, int32_t, - struct flock64 *, int32_t, offset_t, struct flk_callback *, cred_t *); + struct flock64 *, int32_t, offset_t, struct flk_callback *, cred_t *, + caller_context_t *); static int32_t udf_space(struct vnode *, int32_t, struct flock64 *, int32_t, offset_t, cred_t *, caller_context_t *); static int32_t udf_getpage(struct vnode *, offset_t, size_t, uint32_t *, struct page **, size_t, - struct seg *, caddr_t, enum seg_rw, struct cred *); + struct seg *, caddr_t, enum seg_rw, struct cred *, caller_context_t *); static int32_t udf_putpage(struct vnode *, offset_t, - size_t, int32_t, struct cred *); + size_t, int32_t, struct cred *, caller_context_t *); static int32_t udf_map(struct vnode *, offset_t, struct as *, - caddr_t *, size_t, uint8_t, uint8_t, uint32_t, struct cred *); + caddr_t *, size_t, uint8_t, uint8_t, uint32_t, struct cred *, + caller_context_t *); static int32_t udf_addmap(struct vnode *, offset_t, struct as *, - caddr_t, size_t, uint8_t, uint8_t, uint32_t, struct cred *); + caddr_t, size_t, uint8_t, uint8_t, uint32_t, struct cred *, + caller_context_t *); static int32_t udf_delmap(struct vnode *, offset_t, struct as *, - caddr_t, size_t, uint32_t, uint32_t, uint32_t, struct cred *); + caddr_t, size_t, uint32_t, uint32_t, uint32_t, struct cred *, + caller_context_t *); static int32_t udf_l_pathconf(struct vnode *, int32_t, - ulong_t *, struct cred *); + ulong_t *, struct cred *, caller_context_t *); static int32_t udf_pageio(struct vnode *, struct page *, - u_offset_t, size_t, int32_t, struct cred *); + u_offset_t, size_t, int32_t, struct cred *, caller_context_t *); int32_t ud_getpage_miss(struct vnode *, u_offset_t, size_t, struct seg *, caddr_t, page_t *pl[], @@ -227,7 +236,11 @@ const fs_operation_def_t udf_vnodeops_template[] = { /* ARGSUSED */ static int32_t -udf_open(struct vnode **vpp, int32_t flag, struct cred *cr) +udf_open( + struct vnode **vpp, + int32_t flag, + struct cred *cr, + caller_context_t *ct) { ud_printf("udf_open\n"); @@ -236,8 +249,13 @@ udf_open(struct vnode **vpp, int32_t flag, struct cred *cr) /* ARGSUSED */ static int32_t -udf_close(struct vnode *vp, int32_t flag, - int32_t count, offset_t offset, struct cred *cr) +udf_close( + struct vnode *vp, + int32_t flag, + int32_t count, + offset_t offset, + struct cred *cr, + caller_context_t *ct) { struct ud_inode *ip = VTOI(vp); @@ -265,9 +283,14 @@ udf_close(struct vnode *vp, int32_t flag, return (0); } +/* ARGSUSED */ static int32_t -udf_read(struct vnode *vp, struct uio *uiop, - int32_t ioflag, struct cred *cr, struct caller_context *ct) +udf_read( + struct vnode *vp, + struct uio *uiop, + int32_t ioflag, + struct cred *cr, + caller_context_t *ct) { struct ud_inode *ip = VTOI(vp); int32_t error; @@ -309,9 +332,14 @@ int32_t ud_HW = 96 * 1024; int32_t ud_LW = 64 * 1024; int32_t ud_throttles = 0; +/* ARGSUSED */ static int32_t -udf_write(struct vnode *vp, struct uio *uiop, - int32_t ioflag, struct cred *cr, struct caller_context *ct) +udf_write( + struct vnode *vp, + struct uio *uiop, + int32_t ioflag, + struct cred *cr, + caller_context_t *ct) { struct ud_inode *ip = VTOI(vp); int32_t error = 0; @@ -369,16 +397,26 @@ udf_write(struct vnode *vp, struct uio *uiop, /* ARGSUSED */ static int32_t -udf_ioctl(struct vnode *vp, int32_t cmd, intptr_t arg, - int32_t flag, struct cred *cr, int32_t *rvalp) +udf_ioctl( + struct vnode *vp, + int32_t cmd, + intptr_t arg, + int32_t flag, + struct cred *cr, + int32_t *rvalp, + caller_context_t *ct) { return (ENOTTY); } /* ARGSUSED */ static int32_t -udf_getattr(struct vnode *vp, - struct vattr *vap, int32_t flags, struct cred *cr) +udf_getattr( + struct vnode *vp, + struct vattr *vap, + int32_t flags, + struct cred *cr, + caller_context_t *ct) { struct ud_inode *ip = VTOI(vp); @@ -566,8 +604,12 @@ udf_setattr( /* ARGSUSED */ static int32_t -udf_access(struct vnode *vp, - int32_t mode, int32_t flags, struct cred *cr) +udf_access( + struct vnode *vp, + int32_t mode, + int32_t flags, + struct cred *cr, + caller_context_t *ct) { struct ud_inode *ip = VTOI(vp); int32_t error; @@ -587,9 +629,17 @@ int32_t udfs_stickyhack = 1; /* ARGSUSED */ static int32_t -udf_lookup(struct vnode *dvp, - char *nm, struct vnode **vpp, struct pathname *pnp, - int32_t flags, struct vnode *rdir, struct cred *cr) +udf_lookup( + struct vnode *dvp, + char *nm, + struct vnode **vpp, + struct pathname *pnp, + int32_t flags, + struct vnode *rdir, + struct cred *cr, + caller_context_t *ct, + int *direntflags, + pathname_t *realpnp) { int32_t error; struct vnode *vp; @@ -656,9 +706,17 @@ udf_lookup(struct vnode *dvp, /* ARGSUSED */ static int32_t -udf_create(struct vnode *dvp, - char *name, struct vattr *vap, enum vcexcl excl, - int32_t mode, struct vnode **vpp, struct cred *cr, int32_t flag) +udf_create( + struct vnode *dvp, + char *name, + struct vattr *vap, + enum vcexcl excl, + int32_t mode, + struct vnode **vpp, + struct cred *cr, + int32_t flag, + caller_context_t *ct, + vsecattr_t *vsecp) { int32_t error; struct ud_inode *ip = VTOI(dvp), *xip; @@ -679,8 +737,8 @@ udf_create(struct vnode *dvp, xip = NULL; rw_enter(&ip->i_rwlock, RW_WRITER); error = ud_direnter(ip, name, DE_CREATE, - (struct ud_inode *)0, (struct ud_inode *)0, - vap, &xip, cr); + (struct ud_inode *)0, (struct ud_inode *)0, + vap, &xip, cr, ct); rw_exit(&ip->i_rwlock); ITIMES(ip); ip = xip; @@ -732,7 +790,7 @@ udf_create(struct vnode *dvp, (void) ud_itrunc(ip, 0, 0, cr); rw_exit(&ip->i_rwlock); } - vnevent_create(ITOV(ip)); + vnevent_create(ITOV(ip), ct); } } @@ -769,8 +827,14 @@ udf_create(struct vnode *dvp, return (error); } +/* ARGSUSED */ static int32_t -udf_remove(struct vnode *vp, char *nm, struct cred *cr) +udf_remove( + struct vnode *vp, + char *nm, + struct cred *cr, + caller_context_t *ct, + int flags) { int32_t error; struct ud_inode *ip = VTOI(vp); @@ -779,16 +843,22 @@ udf_remove(struct vnode *vp, char *nm, struct cred *cr) rw_enter(&ip->i_rwlock, RW_WRITER); error = ud_dirremove(ip, nm, - (struct ud_inode *)0, (struct vnode *)0, DR_REMOVE, cr); + (struct ud_inode *)0, (struct vnode *)0, DR_REMOVE, cr, ct); rw_exit(&ip->i_rwlock); ITIMES(ip); return (error); } +/* ARGSUSED */ static int32_t -udf_link(struct vnode *tdvp, - struct vnode *svp, char *tnm, struct cred *cr) +udf_link( + struct vnode *tdvp, + struct vnode *svp, + char *tnm, + struct cred *cr, + caller_context_t *ct, + int flags) { int32_t error; struct vnode *realvp; @@ -796,7 +866,7 @@ udf_link(struct vnode *tdvp, struct ud_inode *tdp; ud_printf("udf_link\n"); - if (VOP_REALVP(svp, &realvp) == 0) { + if (VOP_REALVP(svp, &realvp, ct) == 0) { svp = realvp; } @@ -816,13 +886,13 @@ udf_link(struct vnode *tdvp, rw_enter(&tdp->i_rwlock, RW_WRITER); error = ud_direnter(tdp, tnm, DE_LINK, (struct ud_inode *)0, - sip, (struct vattr *)0, (struct ud_inode **)0, cr); + sip, (struct vattr *)0, (struct ud_inode **)0, cr, ct); rw_exit(&tdp->i_rwlock); ITIMES(sip); ITIMES(tdp); if (error == 0) { - vnevent_link(svp); + vnevent_link(svp, ct); } return (error); @@ -830,9 +900,14 @@ udf_link(struct vnode *tdvp, /* ARGSUSED */ static int32_t -udf_rename(struct vnode *sdvp, - char *snm, struct vnode *tdvp, - char *tnm, struct cred *cr) +udf_rename( + struct vnode *sdvp, + char *snm, + struct vnode *tdvp, + char *tnm, + struct cred *cr, + caller_context_t *ct, + int flags) { int32_t error = 0; struct udf_vfs *udf_vfsp; @@ -842,7 +917,7 @@ udf_rename(struct vnode *sdvp, ud_printf("udf_rename\n"); - if (VOP_REALVP(tdvp, &realvp) == 0) { + if (VOP_REALVP(tdvp, &realvp, ct) == 0) { tdvp = realvp; } @@ -906,7 +981,7 @@ udf_rename(struct vnode *sdvp, */ rw_enter(&tdp->i_rwlock, RW_WRITER); if (error = ud_direnter(tdp, tnm, DE_RENAME, sdp, sip, - (struct vattr *)0, (struct ud_inode **)0, cr)) { + (struct vattr *)0, (struct ud_inode **)0, cr, ct)) { /* * ESAME isn't really an error; it indicates that the * operation should not be done because the source and target @@ -918,7 +993,7 @@ udf_rename(struct vnode *sdvp, rw_exit(&tdp->i_rwlock); goto errout; } - vnevent_rename_src(ITOV(sip), sdvp, snm); + vnevent_rename_src(ITOV(sip), sdvp, snm, ct); rw_exit(&tdp->i_rwlock); rw_enter(&sdp->i_rwlock, RW_WRITER); @@ -930,7 +1005,7 @@ udf_rename(struct vnode *sdvp, * the source inode. */ if ((error = ud_dirremove(sdp, snm, sip, (struct vnode *)0, - DR_RENAME, cr)) == ENOENT) { + DR_RENAME, cr, ct)) == ENOENT) { error = 0; } rw_exit(&sdp->i_rwlock); @@ -943,10 +1018,17 @@ udf_rename(struct vnode *sdvp, return (error); } +/* ARGSUSED */ static int32_t -udf_mkdir(struct vnode *dvp, - char *dirname, struct vattr *vap, - struct vnode **vpp, struct cred *cr) +udf_mkdir( + struct vnode *dvp, + char *dirname, + struct vattr *vap, + struct vnode **vpp, + struct cred *cr, + caller_context_t *ct, + int flags, + vsecattr_t *vsecp) { int32_t error; struct ud_inode *ip; @@ -959,7 +1041,7 @@ udf_mkdir(struct vnode *dvp, ip = VTOI(dvp); rw_enter(&ip->i_rwlock, RW_WRITER); error = ud_direnter(ip, dirname, DE_MKDIR, - (struct ud_inode *)0, (struct ud_inode *)0, vap, &xip, cr); + (struct ud_inode *)0, (struct ud_inode *)0, vap, &xip, cr, ct); rw_exit(&ip->i_rwlock); ITIMES(ip); if (error == 0) { @@ -974,9 +1056,15 @@ udf_mkdir(struct vnode *dvp, return (error); } +/* ARGSUSED */ static int32_t -udf_rmdir(struct vnode *vp, - char *nm, struct vnode *cdir, struct cred *cr) +udf_rmdir( + struct vnode *vp, + char *nm, + struct vnode *cdir, + struct cred *cr, + caller_context_t *ct, + int flags) { int32_t error; struct ud_inode *ip = VTOI(vp); @@ -984,7 +1072,8 @@ udf_rmdir(struct vnode *vp, ud_printf("udf_rmdir\n"); rw_enter(&ip->i_rwlock, RW_WRITER); - error = ud_dirremove(ip, nm, (struct ud_inode *)0, cdir, DR_RMDIR, cr); + error = ud_dirremove(ip, nm, (struct ud_inode *)0, cdir, DR_RMDIR, + cr, ct); rw_exit(&ip->i_rwlock); ITIMES(ip); @@ -993,8 +1082,13 @@ udf_rmdir(struct vnode *vp, /* ARGSUSED */ static int32_t -udf_readdir(struct vnode *vp, - struct uio *uiop, struct cred *cr, int32_t *eofp) +udf_readdir( + struct vnode *vp, + struct uio *uiop, + struct cred *cr, + int32_t *eofp, + caller_context_t *ct, + int flags) { struct ud_inode *ip; struct dirent64 *nd; @@ -1142,9 +1236,14 @@ udf_readdir(struct vnode *vp, /* ARGSUSED */ static int32_t -udf_symlink(struct vnode *dvp, - char *linkname, struct vattr *vap, - char *target, struct cred *cr) +udf_symlink( + struct vnode *dvp, + char *linkname, + struct vattr *vap, + char *target, + struct cred *cr, + caller_context_t *ct, + int flags) { int32_t error = 0, outlen; uint32_t ioflag = 0; @@ -1161,7 +1260,7 @@ udf_symlink(struct vnode *dvp, rw_enter(&dip->i_rwlock, RW_WRITER); error = ud_direnter(dip, linkname, DE_CREATE, - (struct ud_inode *)0, (struct ud_inode *)0, vap, &ip, cr); + (struct ud_inode *)0, (struct ud_inode *)0, vap, &ip, cr, ct); rw_exit(&dip->i_rwlock); if (error == 0) { dname = kmem_zalloc(1024, KM_SLEEP); @@ -1246,7 +1345,7 @@ udf_symlink(struct vnode *dvp, rw_exit(&ip->i_contents); rw_enter(&dip->i_rwlock, RW_WRITER); (void) ud_dirremove(dip, linkname, (struct ud_inode *)0, - (struct vnode *)0, DR_REMOVE, cr); + (struct vnode *)0, DR_REMOVE, cr, ct); rw_exit(&dip->i_rwlock); goto update_inode; } @@ -1271,8 +1370,11 @@ udf_symlink(struct vnode *dvp, /* ARGSUSED */ static int32_t -udf_readlink(struct vnode *vp, - struct uio *uiop, struct cred *cr) +udf_readlink( + struct vnode *vp, + struct uio *uiop, + struct cred *cr, + caller_context_t *ct) { int32_t error = 0, off, id_len, size, len; int8_t *dname = NULL, *uname = NULL; @@ -1374,8 +1476,11 @@ udf_readlink(struct vnode *vp, /* ARGSUSED */ static int32_t -udf_fsync(struct vnode *vp, - int32_t syncflag, struct cred *cr) +udf_fsync( + struct vnode *vp, + int32_t syncflag, + struct cred *cr, + caller_context_t *ct) { int32_t error = 0; struct ud_inode *ip = VTOI(vp); @@ -1397,15 +1502,16 @@ udf_fsync(struct vnode *vp, /* ARGSUSED */ static void -udf_inactive(struct vnode *vp, struct cred *cr) +udf_inactive(struct vnode *vp, struct cred *cr, caller_context_t *ct) { ud_printf("udf_iinactive\n"); ud_iinactive(VTOI(vp), cr); } +/* ARGSUSED */ static int32_t -udf_fid(struct vnode *vp, struct fid *fidp) +udf_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct) { struct udf_fid *udfidp; struct ud_inode *ip = VTOI(vp); @@ -1466,15 +1572,21 @@ udf_rwunlock(struct vnode *vp, int32_t write_lock, caller_context_t *ctp) /* ARGSUSED */ static int32_t -udf_seek(struct vnode *vp, offset_t ooff, offset_t *noffp) +udf_seek(struct vnode *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) { return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0); } static int32_t -udf_frlock(struct vnode *vp, int32_t cmd, struct flock64 *bfp, - int32_t flag, offset_t offset, struct flk_callback *flk_cbp, - cred_t *cr) +udf_frlock( + struct vnode *vp, + int32_t cmd, + struct flock64 *bfp, + int32_t flag, + offset_t offset, + struct flk_callback *flk_cbp, + cred_t *cr, + caller_context_t *ct) { struct ud_inode *ip = VTOI(vp); @@ -1492,7 +1604,7 @@ udf_frlock(struct vnode *vp, int32_t cmd, struct flock64 *bfp, return (EAGAIN); } - return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr)); + return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct)); } /*ARGSUSED6*/ @@ -1521,10 +1633,18 @@ udf_space( /* ARGSUSED */ static int32_t -udf_getpage(struct vnode *vp, offset_t off, - size_t len, uint32_t *protp, struct page **plarr, - size_t plsz, struct seg *seg, caddr_t addr, - enum seg_rw rw, struct cred *cr) +udf_getpage( + struct vnode *vp, + offset_t off, + size_t len, + uint32_t *protp, + struct page **plarr, + size_t plsz, + struct seg *seg, + caddr_t addr, + enum seg_rw rw, + struct cred *cr, + caller_context_t *ct) { struct ud_inode *ip = VTOI(vp); int32_t error, has_holes, beyond_eof, seqmode, dolock; @@ -1798,8 +1918,13 @@ int32_t ud_delay = 1; /* ARGSUSED */ static int32_t -udf_putpage(struct vnode *vp, offset_t off, - size_t len, int32_t flags, struct cred *cr) +udf_putpage( + struct vnode *vp, + offset_t off, + size_t len, + int32_t flags, + struct cred *cr, + caller_context_t *ct) { struct ud_inode *ip; int32_t error = 0; @@ -1879,11 +2004,19 @@ udf_putpage(struct vnode *vp, offset_t off, return (error); } +/* ARGSUSED */ static int32_t -udf_map(struct vnode *vp, offset_t off, - struct as *as, caddr_t *addrp, size_t len, - uint8_t prot, uint8_t maxprot, uint32_t flags, - struct cred *cr) +udf_map( + struct vnode *vp, + offset_t off, + struct as *as, + caddr_t *addrp, + size_t len, + uint8_t prot, + uint8_t maxprot, + uint32_t flags, + struct cred *cr, + caller_context_t *ct) { struct segvn_crargs vn_a; int32_t error = 0; @@ -1949,10 +2082,16 @@ udf_map(struct vnode *vp, offset_t off, /* ARGSUSED */ static int32_t -udf_addmap(struct vnode *vp, offset_t off, - struct as *as, caddr_t addr, size_t len, - uint8_t prot, uint8_t maxprot, uint32_t flags, - struct cred *cr) +udf_addmap(struct vnode *vp, + offset_t off, + struct as *as, + caddr_t addr, + size_t len, + uint8_t prot, + uint8_t maxprot, + uint32_t flags, + struct cred *cr, + caller_context_t *ct) { struct ud_inode *ip = VTOI(vp); @@ -1971,10 +2110,16 @@ udf_addmap(struct vnode *vp, offset_t off, /* ARGSUSED */ static int32_t -udf_delmap(struct vnode *vp, offset_t off, - struct as *as, caddr_t addr, size_t len, - uint32_t prot, uint32_t maxprot, uint32_t flags, - struct cred *cr) +udf_delmap( + struct vnode *vp, offset_t off, + struct as *as, + caddr_t addr, + size_t len, + uint32_t prot, + uint32_t maxprot, + uint32_t flags, + struct cred *cr, + caller_context_t *ct) { struct ud_inode *ip = VTOI(vp); @@ -1992,9 +2137,14 @@ udf_delmap(struct vnode *vp, offset_t off, return (0); } +/* ARGSUSED */ static int32_t -udf_l_pathconf(struct vnode *vp, int32_t cmd, - ulong_t *valp, struct cred *cr) +udf_l_pathconf( + struct vnode *vp, + int32_t cmd, + ulong_t *valp, + struct cred *cr, + caller_context_t *ct) { int32_t error = 0; @@ -2010,7 +2160,7 @@ udf_l_pathconf(struct vnode *vp, int32_t cmd, */ *valp = 41; } else { - error = fs_pathconf(vp, cmd, valp, cr); + error = fs_pathconf(vp, cmd, valp, cr, ct); } return (error); @@ -2027,9 +2177,14 @@ _NOTE(SCHEME_PROTECTS_DATA("safe sharing", ud_pageio_writes)) */ /* ARGSUSED */ static int32_t -udf_pageio(struct vnode *vp, struct page *pp, - u_offset_t io_off, size_t io_len, - int32_t flags, struct cred *cr) +udf_pageio( + struct vnode *vp, + struct page *pp, + u_offset_t io_off, + size_t io_len, + int32_t flags, + struct cred *cr, + caller_context_t *ct) { daddr_t bn; struct buf *bp; diff --git a/usr/src/uts/common/fs/ufs/quotacalls.c b/usr/src/uts/common/fs/ufs/quotacalls.c index 6474acbfffcd..4c97cd914943 100644 --- a/usr/src/uts/common/fs/ufs/quotacalls.c +++ b/usr/src/uts/common/fs/ufs/quotacalls.c @@ -271,7 +271,7 @@ opendq( " from quota file\n"); rw_exit(&qip->i_contents); (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)qip->i_size, - B_INVAL, kcred); + B_INVAL, kcred, NULL); } else { rw_exit(&qip->i_contents); } @@ -642,7 +642,7 @@ setquota(int cmd, uid_t uid, struct ufsvfs *ufsvfsp, rw_exit(&qip->i_contents); (void) VOP_PUTPAGE(ITOV(qip), dqoff(dqp->dq_uid) & ~qip->i_fs->fs_bmask, - qip->i_fs->fs_bsize, B_INVAL, kcred); + qip->i_fs->fs_bsize, B_INVAL, kcred, NULL); /* * We must set the dq_mof even if not we are not logging in case diff --git a/usr/src/uts/common/fs/ufs/ufs_directio.c b/usr/src/uts/common/fs/ufs/ufs_directio.c index b60537ba752e..7e908772bc94 100644 --- a/usr/src/uts/common/fs/ufs/ufs_directio.c +++ b/usr/src/uts/common/fs/ufs/ufs_directio.c @@ -600,7 +600,8 @@ ufs_directio_write(struct inode *ip, uio_t *arg_uio, int ioflag, int rewrite, rw_exit(&ip->i_contents); rw_enter(&ip->i_contents, RW_WRITER); } - (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, B_INVAL, cr); + (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, + B_INVAL, cr, NULL); if (vn_has_cached_data(vp)) goto errout; if (!exclusive) @@ -737,7 +738,7 @@ ufs_directio_write(struct inode *ip, uio_t *arg_uio, int ioflag, int rewrite, rw_exit(&ip->i_contents); rw_enter(&ip->i_contents, RW_WRITER); (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, - B_INVAL, cr); + B_INVAL, cr, NULL); ufs_directio_kstats.nflushes.value.ui64++; rw_downgrade(&ip->i_contents); } @@ -912,7 +913,8 @@ ufs_directio_read(struct inode *ip, uio_t *uio, cred_t *cr, int *statusp) if (vn_has_cached_data(vp)) { rw_exit(&ip->i_contents); rw_enter(&ip->i_contents, RW_WRITER); - (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, B_INVAL, cr); + (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, + B_INVAL, cr, NULL); if (vn_has_cached_data(vp)) return (0); rw_downgrade(&ip->i_contents); diff --git a/usr/src/uts/common/fs/ufs/ufs_extvnops.c b/usr/src/uts/common/fs/ufs/ufs_extvnops.c index 4533f8db07e9..08ece1de7946 100644 --- a/usr/src/uts/common/fs/ufs/ufs_extvnops.c +++ b/usr/src/uts/common/fs/ufs/ufs_extvnops.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -244,7 +243,7 @@ ufs_alloc_data( size_t done_len, io_len; int contig; u_offset_t uoff, io_off; - int error = 0; /* No error occured */ + int error = 0; /* No error occurred */ int offsetn; /* Start point this IO */ int nbytes; /* Number bytes in this IO */ daddr_t bn; @@ -502,7 +501,7 @@ ufs_alloc_data( * was written. * */ - (void) VOP_PUTPAGE(vnodep, 0, 0, B_INVAL, credp); + (void) VOP_PUTPAGE(vnodep, 0, 0, B_INVAL, credp, NULL); rw_exit(&ip->i_contents); rw_exit(&ip->i_ufsvfs->vfs_dqrwlock); diff --git a/usr/src/uts/common/fs/ufs/ufs_filio.c b/usr/src/uts/common/fs/ufs/ufs_filio.c index 6a6bc4c42163..04a68104ffa9 100644 --- a/usr/src/uts/common/fs/ufs/ufs_filio.c +++ b/usr/src/uts/common/fs/ufs/ufs_filio.c @@ -171,10 +171,10 @@ ufs_fioio( * Adapted from vn_open: check access and then open the file */ vpio = ITOV(ipio); - if (error = VOP_ACCESS(vpio, VREAD, 0, cr)) + if (error = VOP_ACCESS(vpio, VREAD, 0, cr, NULL)) goto errout; - if (error = VOP_OPEN(&vpio, FREAD, cr)) + if (error = VOP_OPEN(&vpio, FREAD, cr, NULL)) goto errout; /* diff --git a/usr/src/uts/common/fs/ufs/ufs_lockfs.c b/usr/src/uts/common/fs/ufs/ufs_lockfs.c index 605553200005..b7a7179c28a6 100644 --- a/usr/src/uts/common/fs/ufs/ufs_lockfs.c +++ b/usr/src/uts/common/fs/ufs/ufs_lockfs.c @@ -367,7 +367,7 @@ ufs_flush(struct vfs *vfsp) * flush w/invalidate block device pages and buf cache */ if ((error = VOP_PUTPAGE(common_specvp(ufsvfsp->vfs_devvp), - (offset_t)0, 0, B_INVAL, CRED())) > 0) + (offset_t)0, 0, B_INVAL, CRED(), NULL)) > 0) saverror = error; (void) bflush((dev_t)vfsp->vfs_dev); diff --git a/usr/src/uts/common/fs/ufs/ufs_subr.c b/usr/src/uts/common/fs/ufs/ufs_subr.c index f6daee5dc194..e2d1f6577b9e 100644 --- a/usr/src/uts/common/fs/ufs/ufs_subr.c +++ b/usr/src/uts/common/fs/ufs/ufs_subr.c @@ -489,7 +489,8 @@ ufs_syncip(struct inode *ip, int flags, int waitfor, top_t topid) TRANS_BEGIN_ASYNC(ufsvfsp, TOP_PUTPAGE, TOP_PUTPAGE_SIZE(ip)); } - error = VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, flags, CRED()); + error = VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, + flags, CRED(), NULL); if (dotrans) { TRANS_END_ASYNC(ufsvfsp, TOP_PUTPAGE, TOP_PUTPAGE_SIZE(ip)); diff --git a/usr/src/uts/common/fs/ufs/ufs_vfsops.c b/usr/src/uts/common/fs/ufs/ufs_vfsops.c index 23c2d1798ae4..80151eaa3271 100644 --- a/usr/src/uts/common/fs/ufs/ufs_vfsops.c +++ b/usr/src/uts/common/fs/ufs/ufs_vfsops.c @@ -361,7 +361,7 @@ ufs_mount(struct vfs *vfsp, struct vnode *mvp, struct mounta *uap, oflag = FREAD | FWRITE; aflag = VREAD | VWRITE; } - if ((error = VOP_ACCESS(bvp, aflag, 0, cr)) != 0 || + if ((error = VOP_ACCESS(bvp, aflag, 0, cr, NULL)) != 0 || (error = secpolicy_spec_open(cr, bvp, oflag)) != 0) { pn_free(&dpn); VN_RELE(bvp); @@ -407,6 +407,8 @@ ufs_mount(struct vfs *vfsp, struct vnode *mvp, struct mounta *uap, if (error) { VN_RELE(bvp); } + if (error == 0) + vfs_set_feature(vfsp, VFSFT_XVATTR); return (error); } /* @@ -449,7 +451,8 @@ ufs_mountroot(struct vfs *vfsp, enum whymountroot why) vp = ((struct ufsvfs *)vfsp->vfs_data)->vfs_devvp; (void) dnlc_purge_vfsp(vfsp, 0); vp = common_specvp(vp); - (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, B_INVAL, CRED()); + (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, B_INVAL, + CRED(), NULL); (void) bfinval(vfsp->vfs_dev, 0); fsp = getfs(vfsp); @@ -484,7 +487,7 @@ ufs_mountroot(struct vfs *vfsp, enum whymountroot why) vp = ((struct ufsvfs *)vfsp->vfs_data)->vfs_devvp; (void) VOP_CLOSE(vp, FREAD|FWRITE, 1, - (offset_t)0, CRED()); + (offset_t)0, CRED(), NULL); return (0); } error = vfs_lock(vfsp); @@ -496,10 +499,10 @@ ufs_mountroot(struct vfs *vfsp, enum whymountroot why) /* If RO media, don't call clkset() (see below) */ doclkset = 1; if (why == ROOT_INIT) { - error = VOP_OPEN(&devvp, FREAD|FWRITE, CRED()); + error = VOP_OPEN(&devvp, FREAD|FWRITE, CRED(), NULL); if (error == 0) { (void) VOP_CLOSE(devvp, FREAD|FWRITE, 1, - (offset_t)0, CRED()); + (offset_t)0, CRED(), NULL); } else { doclkset = 0; } @@ -788,7 +791,8 @@ mountfs(struct vfs *vfsp, enum whymountroot why, struct vnode *devvp, * operations. */ error = VOP_OPEN(&devvp, - (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, cr); + (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, + cr, NULL); if (error) goto out; needclose = 1; @@ -822,7 +826,7 @@ mountfs(struct vfs *vfsp, enum whymountroot why, struct vnode *devvp, * they really should be using the raw device. */ (void) VOP_PUTPAGE(common_specvp(devvp), (offset_t)0, - (size_t)0, B_INVAL, cr); + (size_t)0, B_INVAL, cr, NULL); /* * read in superblock @@ -1321,7 +1325,7 @@ mountfs(struct vfs *vfsp, enum whymountroot why, struct vnode *devvp, } if (needclose) { (void) VOP_CLOSE(devvp, (vfsp->vfs_flag & VFS_RDONLY) ? - FREAD : FREAD|FWRITE, 1, (offset_t)0, cr); + FREAD : FREAD|FWRITE, 1, (offset_t)0, cr, NULL); bflush(dev); (void) bfinval(dev, 1); } @@ -1628,8 +1632,8 @@ ufs_unmount(struct vfs *vfsp, int fflag, struct cred *cr) brelse(bp); /* free the superblock buf */ (void) VOP_PUTPAGE(common_specvp(bvp), (offset_t)0, (size_t)0, - B_INVAL, cr); - (void) VOP_CLOSE(bvp, flag, 1, (offset_t)0, cr); + B_INVAL, cr, NULL); + (void) VOP_CLOSE(bvp, flag, 1, (offset_t)0, cr, NULL); bflush(dev); (void) bfinval(dev, 1); VN_RELE(bvp); @@ -1696,7 +1700,7 @@ ufs_unmount(struct vfs *vfsp, int fflag, struct cred *cr) ufs_trans_onerror(); /* - * if we have a seperate /usr it will never unmount + * if we have a separate /usr it will never unmount * when halting. In order to not re-read all the * cylinder group summary info on mounting after * reboot the logging of summary info is re-enabled @@ -2099,7 +2103,7 @@ ufs_remountroot(struct vfs *vfsp) new_rootvp = makespecvp(new_rootdev, VBLK); error = VOP_OPEN(&new_rootvp, - (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, CRED()); + (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, CRED(), NULL); if (error) { cmn_err(CE_CONT, "Cannot open mirrored root device, error %d\n", error); @@ -2245,7 +2249,7 @@ ufs_remountroot(struct vfs *vfsp) vfs_unlock(vfsp); - error = VOP_CLOSE(old_rootvp, FREAD, 1, (offset_t)0, CRED()); + error = VOP_CLOSE(old_rootvp, FREAD, 1, (offset_t)0, CRED(), NULL); if (error) { cmn_err(CE_CONT, "close of root device component failed, error %d\n", diff --git a/usr/src/uts/common/fs/ufs/ufs_vnops.c b/usr/src/uts/common/fs/ufs/ufs_vnops.c index 414aa037c4a8..4c712bbd7559 100644 --- a/usr/src/uts/common/fs/ufs/ufs_vnops.c +++ b/usr/src/uts/common/fs/ufs/ufs_vnops.c @@ -103,64 +103,80 @@ static struct instats ins; static int ufs_getpage_ra(struct vnode *, u_offset_t, struct seg *, caddr_t); static int ufs_getpage_miss(struct vnode *, u_offset_t, size_t, struct seg *, caddr_t, struct page **, size_t, enum seg_rw, int); -static int ufs_open(struct vnode **, int, struct cred *); -static int ufs_close(struct vnode *, int, int, offset_t, struct cred *); +static int ufs_open(struct vnode **, int, struct cred *, caller_context_t *); +static int ufs_close(struct vnode *, int, int, offset_t, struct cred *, + caller_context_t *); static int ufs_read(struct vnode *, struct uio *, int, struct cred *, - struct caller_context *); + struct caller_context *); static int ufs_write(struct vnode *, struct uio *, int, struct cred *, - struct caller_context *); -static int ufs_ioctl(struct vnode *, int, intptr_t, int, struct cred *, int *); -static int ufs_getattr(struct vnode *, struct vattr *, int, struct cred *); + struct caller_context *); +static int ufs_ioctl(struct vnode *, int, intptr_t, int, struct cred *, + int *, caller_context_t *); +static int ufs_getattr(struct vnode *, struct vattr *, int, struct cred *, + caller_context_t *); static int ufs_setattr(struct vnode *, struct vattr *, int, struct cred *, - caller_context_t *); -static int ufs_access(struct vnode *, int, int, struct cred *); + caller_context_t *); +static int ufs_access(struct vnode *, int, int, struct cred *, + caller_context_t *); static int ufs_lookup(struct vnode *, char *, struct vnode **, - struct pathname *, int, struct vnode *, struct cred *); + struct pathname *, int, struct vnode *, struct cred *, + caller_context_t *, int *, pathname_t *); static int ufs_create(struct vnode *, char *, struct vattr *, enum vcexcl, - int, struct vnode **, struct cred *, int); -static int ufs_remove(struct vnode *, char *, struct cred *); -static int ufs_link(struct vnode *, struct vnode *, char *, struct cred *); + int, struct vnode **, struct cred *, int, + caller_context_t *, vsecattr_t *); +static int ufs_remove(struct vnode *, char *, struct cred *, + caller_context_t *, int); +static int ufs_link(struct vnode *, struct vnode *, char *, struct cred *, + caller_context_t *, int); static int ufs_rename(struct vnode *, char *, struct vnode *, char *, - struct cred *); + struct cred *, caller_context_t *, int); static int ufs_mkdir(struct vnode *, char *, struct vattr *, struct vnode **, - struct cred *); -static int ufs_rmdir(struct vnode *, char *, struct vnode *, struct cred *); -static int ufs_readdir(struct vnode *, struct uio *, struct cred *, int *); + struct cred *, caller_context_t *, int, vsecattr_t *); +static int ufs_rmdir(struct vnode *, char *, struct vnode *, struct cred *, + caller_context_t *, int); +static int ufs_readdir(struct vnode *, struct uio *, struct cred *, int *, + caller_context_t *, int); static int ufs_symlink(struct vnode *, char *, struct vattr *, char *, - struct cred *); -static int ufs_readlink(struct vnode *, struct uio *, struct cred *); -static int ufs_fsync(struct vnode *, int, struct cred *); -static void ufs_inactive(struct vnode *, struct cred *); -static int ufs_fid(struct vnode *, struct fid *); + struct cred *, caller_context_t *, int); +static int ufs_readlink(struct vnode *, struct uio *, struct cred *, + caller_context_t *); +static int ufs_fsync(struct vnode *, int, struct cred *, caller_context_t *); +static void ufs_inactive(struct vnode *, struct cred *, caller_context_t *); +static int ufs_fid(struct vnode *, struct fid *, caller_context_t *); static int ufs_rwlock(struct vnode *, int, caller_context_t *); static void ufs_rwunlock(struct vnode *, int, caller_context_t *); -static int ufs_seek(struct vnode *, offset_t, offset_t *); +static int ufs_seek(struct vnode *, offset_t, offset_t *, caller_context_t *); static int ufs_frlock(struct vnode *, int, struct flock64 *, int, offset_t, - struct flk_callback *, struct cred *); + struct flk_callback *, struct cred *, + caller_context_t *); static int ufs_space(struct vnode *, int, struct flock64 *, int, offset_t, cred_t *, caller_context_t *); static int ufs_getpage(struct vnode *, offset_t, size_t, uint_t *, struct page **, size_t, struct seg *, caddr_t, - enum seg_rw, struct cred *); -static int ufs_putpage(struct vnode *, offset_t, size_t, int, struct cred *); + enum seg_rw, struct cred *, caller_context_t *); +static int ufs_putpage(struct vnode *, offset_t, size_t, int, struct cred *, + caller_context_t *); static int ufs_putpages(struct vnode *, offset_t, size_t, int, struct cred *); static int ufs_map(struct vnode *, offset_t, struct as *, caddr_t *, size_t, - uchar_t, uchar_t, uint_t, struct cred *); + uchar_t, uchar_t, uint_t, struct cred *, caller_context_t *); static int ufs_addmap(struct vnode *, offset_t, struct as *, caddr_t, size_t, - uchar_t, uchar_t, uint_t, struct cred *); + uchar_t, uchar_t, uint_t, struct cred *, caller_context_t *); static int ufs_delmap(struct vnode *, offset_t, struct as *, caddr_t, size_t, - uint_t, uint_t, uint_t, struct cred *); -static int ufs_poll(vnode_t *, short, int, short *, struct pollhead **); -static int ufs_dump(vnode_t *, caddr_t, int, int); -static int ufs_l_pathconf(struct vnode *, int, ulong_t *, struct cred *); + uint_t, uint_t, uint_t, struct cred *, caller_context_t *); +static int ufs_poll(vnode_t *, short, int, short *, struct pollhead **, + caller_context_t *); +static int ufs_dump(vnode_t *, caddr_t, int, int, caller_context_t *); +static int ufs_l_pathconf(struct vnode *, int, ulong_t *, struct cred *, + caller_context_t *); static int ufs_pageio(struct vnode *, struct page *, u_offset_t, size_t, int, - struct cred *); -static int ufs_dump(vnode_t *, caddr_t, int, int); -static int ufs_dumpctl(vnode_t *, int, int *); + struct cred *, caller_context_t *); +static int ufs_dumpctl(vnode_t *, int, int *, caller_context_t *); static daddr32_t *save_dblks(struct inode *, struct ufsvfs *, daddr32_t *, - daddr32_t *, int, int); -static int ufs_getsecattr(struct vnode *, vsecattr_t *, int, struct cred *); -static int ufs_setsecattr(struct vnode *, vsecattr_t *, int, struct cred *); + daddr32_t *, int, int); +static int ufs_getsecattr(struct vnode *, vsecattr_t *, int, struct cred *, + caller_context_t *); +static int ufs_setsecattr(struct vnode *, vsecattr_t *, int, struct cred *, + caller_context_t *); extern int as_map_locked(struct as *, caddr_t, size_t, int ((*)()), void *); @@ -246,7 +262,7 @@ static struct dump *dump_info = NULL; /* ARGSUSED */ static int -ufs_open(struct vnode **vpp, int flag, struct cred *cr) +ufs_open(struct vnode **vpp, int flag, struct cred *cr, caller_context_t *ct) { return (0); } @@ -254,7 +270,7 @@ ufs_open(struct vnode **vpp, int flag, struct cred *cr) /*ARGSUSED*/ static int ufs_close(struct vnode *vp, int flag, int count, offset_t offset, - struct cred *cr) + struct cred *cr, caller_context_t *ct) { cleanlocks(vp, ttoproc(curthread)->p_pid, 0); cleanshares(vp, ttoproc(curthread)->p_pid); @@ -966,7 +982,7 @@ wrip(struct inode *ip, struct uio *uio, int ioflag, struct cred *cr) * 2) uiomove() causes a page fault. * * We have to drop the contents lock to prevent the VM - * system from trying to reaquire it in ufs_getpage() + * system from trying to reacquire it in ufs_getpage() * should the uiomove cause a pagefault. * * We have to drop the reader vfs_dqrwlock here as well. @@ -1521,7 +1537,8 @@ ufs_ioctl( intptr_t arg, int flag, struct cred *cr, - int *rvalp) + int *rvalp, + caller_context_t *ct) { struct lockfs lockfs, lockfs_out; struct ufsvfs *ufsvfsp = VTOI(vp)->i_ufsvfs; @@ -1916,7 +1933,7 @@ ufs_ioctl( /* ARGSUSED */ static int ufs_getattr(struct vnode *vp, struct vattr *vap, int flags, - struct cred *cr) + struct cred *cr, caller_context_t *ct) { struct inode *ip = VTOI(vp); struct ufsvfs *ufsvfsp; @@ -2042,10 +2059,8 @@ ufs_setattr( /* * Cannot set these attributes. */ - if (mask & AT_NOSET) { - error = EINVAL; - goto out; - } + if ((mask & AT_NOSET) || (mask & AT_XVATTR)) + return (EINVAL); /* * check for forced unmount @@ -2349,7 +2364,8 @@ ufs_setattr( /*ARGSUSED*/ static int -ufs_access(struct vnode *vp, int mode, int flags, struct cred *cr) +ufs_access(struct vnode *vp, int mode, int flags, struct cred *cr, + caller_context_t *ct) { struct inode *ip = VTOI(vp); int error; @@ -2379,7 +2395,8 @@ ufs_access(struct vnode *vp, int mode, int flags, struct cred *cr) /* ARGSUSED */ static int -ufs_readlink(struct vnode *vp, struct uio *uiop, struct cred *cr) +ufs_readlink(struct vnode *vp, struct uio *uiop, struct cred *cr, + caller_context_t *ct) { struct inode *ip = VTOI(vp); struct ufsvfs *ufsvfsp; @@ -2505,7 +2522,7 @@ ufs_readlink(struct vnode *vp, struct uio *uiop, struct cred *cr) (void) VOP_PUTPAGE(ITOV(ip), (offset_t)0, PAGESIZE, (B_DONTNEED | B_FREE | B_FORCE | B_ASYNC), - cr); + cr, ct); } else { int i; /* error, clear garbage left behind */ @@ -2534,7 +2551,8 @@ ufs_readlink(struct vnode *vp, struct uio *uiop, struct cred *cr) /* ARGSUSED */ static int -ufs_fsync(struct vnode *vp, int syncflag, struct cred *cr) +ufs_fsync(struct vnode *vp, int syncflag, struct cred *cr, + caller_context_t *ct) { struct inode *ip = VTOI(vp); struct ufsvfs *ufsvfsp = ip->i_ufsvfs; @@ -2552,7 +2570,7 @@ ufs_fsync(struct vnode *vp, int syncflag, struct cred *cr) if (vn_has_cached_data(vp) && !(syncflag & FNODSYNC) && (vp->v_type != VCHR) && !(IS_SWAPVP(vp))) { error = VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, - 0, CRED()); + 0, CRED(), ct); if (error) goto out; } @@ -2627,7 +2645,7 @@ ufs_fsync(struct vnode *vp, int syncflag, struct cred *cr) /*ARGSUSED*/ static void -ufs_inactive(struct vnode *vp, struct cred *cr) +ufs_inactive(struct vnode *vp, struct cred *cr, caller_context_t *ct) { ufs_iinactive(VTOI(vp)); } @@ -2639,7 +2657,8 @@ int ufs_lookup_idle_count = 2; /* Number of inodes to idle each time */ /* ARGSUSED */ static int ufs_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, - struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cr) + struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cr, + caller_context_t *ct, int *direntflags, pathname_t *realpnp) { struct inode *ip; struct inode *sip; @@ -2657,6 +2676,12 @@ ufs_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, if (flags & LOOKUP_XATTR) { + /* + * If not mounted with XATTR support then return EINVAL + */ + + if (!(ip->i_ufsvfs->vfs_vfs->vfs_flag & VFS_XATTR)) + return (EINVAL); /* * We don't allow recursive attributes... * Maybe someday we will. @@ -2796,9 +2821,11 @@ ufs_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, return (error); } +/*ARGSUSED*/ static int ufs_create(struct vnode *dvp, char *name, struct vattr *vap, enum vcexcl excl, - int mode, struct vnode **vpp, struct cred *cr, int flag) + int mode, struct vnode **vpp, struct cred *cr, int flag, + caller_context_t *ct, vsecattr_t *vsecp) { struct inode *ip; struct inode *xip; @@ -2988,7 +3015,7 @@ ufs_create(struct vnode *dvp, char *name, struct vattr *vap, enum vcexcl excl, } if (error == 0) { - vnevent_create(ITOV(ip)); + vnevent_create(ITOV(ip), ct); } } } @@ -3082,7 +3109,8 @@ ufs_create(struct vnode *dvp, char *name, struct vattr *vap, enum vcexcl excl, extern int ufs_idle_max; /*ARGSUSED*/ static int -ufs_remove(struct vnode *vp, char *nm, struct cred *cr) +ufs_remove(struct vnode *vp, char *nm, struct cred *cr, + caller_context_t *ct, int flags) { struct inode *ip = VTOI(vp); struct ufsvfs *ufsvfsp = ip->i_ufsvfs; @@ -3135,7 +3163,7 @@ ufs_remove(struct vnode *vp, char *nm, struct cred *cr) if (rmvp != NULL) { /* Only send the event if there were no errors */ if (error == 0) - vnevent_remove(rmvp, vp, nm); + vnevent_remove(rmvp, vp, nm, ct); VN_RELE(rmvp); } out: @@ -3146,8 +3174,10 @@ ufs_remove(struct vnode *vp, char *nm, struct cred *cr) * Link a file or a directory. Only privileged processes are allowed to * make links to directories. */ +/*ARGSUSED*/ static int -ufs_link(struct vnode *tdvp, struct vnode *svp, char *tnm, struct cred *cr) +ufs_link(struct vnode *tdvp, struct vnode *svp, char *tnm, struct cred *cr, + caller_context_t *ct, int flags) { struct inode *sip; struct inode *tdp = VTOI(tdvp); @@ -3169,7 +3199,7 @@ ufs_link(struct vnode *tdvp, struct vnode *svp, char *tnm, struct cred *cr) TRANS_BEGIN_CSYNC(ufsvfsp, issync, TOP_LINK, trans_size = (int)TOP_LINK_SIZE(VTOI(tdvp))); - if (VOP_REALVP(svp, &realvp) == 0) + if (VOP_REALVP(svp, &realvp, ct) == 0) svp = realvp; /* @@ -3216,7 +3246,7 @@ ufs_link(struct vnode *tdvp, struct vnode *svp, char *tnm, struct cred *cr) } if (!error) { - vnevent_link(svp); + vnevent_link(svp, ct); } out: return (error); @@ -3247,7 +3277,9 @@ ufs_rename( char *snm, /* old (source) entry name */ struct vnode *tdvp, /* new (target) parent vnode */ char *tnm, /* new (target) entry name */ - struct cred *cr) + struct cred *cr, + caller_context_t *ct, + int flags) { struct inode *sip = NULL; /* source inode */ struct inode *ip = NULL; /* check inode */ @@ -3278,7 +3310,7 @@ ufs_rename( TRANS_BEGIN_CSYNC(ufsvfsp, issync, TOP_RENAME, trans_size = (int)TOP_RENAME_SIZE(sdp)); - if (VOP_REALVP(tdvp, &realvp) == 0) + if (VOP_REALVP(tdvp, &realvp, ct) == 0) tdvp = realvp; tdp = VTOI(tdvp); @@ -3598,14 +3630,14 @@ ufs_rename( */ if (error == 0) { if (tvp != NULL) - vnevent_rename_dest(tvp, tdvp, tnm); + vnevent_rename_dest(tvp, tdvp, tnm, ct); /* * Notify the target directory of the rename event * if source and target directories are not same. */ if (sdvp != tdvp) - vnevent_rename_dest_dir(tdvp); + vnevent_rename_dest_dir(tdvp, ct); /* * Note that if ufs_direnter_lr() returned ESAME then @@ -3613,7 +3645,7 @@ ufs_rename( * to be a problem for anticipated usage by consumers. */ if (sip != NULL) - vnevent_rename_src(ITOV(sip), sdvp, snm); + vnevent_rename_src(ITOV(sip), sdvp, snm, ct); } if (tvp != NULL) @@ -3629,7 +3661,8 @@ ufs_rename( /*ARGSUSED*/ static int ufs_mkdir(struct vnode *dvp, char *dirname, struct vattr *vap, - struct vnode **vpp, struct cred *cr) + struct vnode **vpp, struct cred *cr, caller_context_t *ct, int flags, + vsecattr_t *vsecp) { struct inode *ip; struct inode *xip; @@ -3705,7 +3738,8 @@ ufs_mkdir(struct vnode *dvp, char *dirname, struct vattr *vap, /*ARGSUSED*/ static int -ufs_rmdir(struct vnode *vp, char *nm, struct vnode *cdir, struct cred *cr) +ufs_rmdir(struct vnode *vp, char *nm, struct vnode *cdir, struct cred *cr, + caller_context_t *ct, int flags) { struct inode *ip = VTOI(vp); struct ufsvfs *ufsvfsp = ip->i_ufsvfs; @@ -3759,7 +3793,7 @@ ufs_rmdir(struct vnode *vp, char *nm, struct vnode *cdir, struct cred *cr) if (rmvp != NULL) { /* Only send the event if there were no errors */ if (error == 0) - vnevent_rmdir(rmvp, vp, nm); + vnevent_rmdir(rmvp, vp, nm, ct); VN_RELE(rmvp); } out: @@ -3772,7 +3806,9 @@ ufs_readdir( struct vnode *vp, struct uio *uiop, struct cred *cr, - int *eofp) + int *eofp, + caller_context_t *ct, + int flags) { struct iovec *iovp; struct inode *ip; @@ -3980,7 +4016,9 @@ ufs_symlink( char *linkname, /* name of symbolic link */ struct vattr *vap, /* attributes */ char *target, /* target path */ - struct cred *cr) /* user credentials */ + struct cred *cr, /* user credentials */ + caller_context_t *ct, + int flags) { struct inode *ip, *dip = VTOI(dvp); struct ufsvfs *ufsvfsp = dip->i_ufsvfs; @@ -4203,10 +4241,9 @@ ufs_rdwri(enum uio_rw rw, int ioflag, struct inode *ip, caddr_t base, return (error); } +/*ARGSUSED*/ static int -ufs_fid(vp, fidp) - struct vnode *vp; - struct fid *fidp; +ufs_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct) { struct ufid *ufid; struct inode *ip = VTOI(vp); @@ -4297,7 +4334,8 @@ ufs_rwunlock(struct vnode *vp, int write_lock, caller_context_t *ctp) /* ARGSUSED */ static int -ufs_seek(struct vnode *vp, offset_t ooff, offset_t *noffp) +ufs_seek(struct vnode *vp, offset_t ooff, offset_t *noffp, + caller_context_t *ct) { return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0); } @@ -4305,7 +4343,8 @@ ufs_seek(struct vnode *vp, offset_t ooff, offset_t *noffp) /* ARGSUSED */ static int ufs_frlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag, - offset_t offset, struct flk_callback *flk_cbp, struct cred *cr) + offset_t offset, struct flk_callback *flk_cbp, struct cred *cr, + caller_context_t *ct) { struct inode *ip = VTOI(vp); @@ -4321,7 +4360,7 @@ ufs_frlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag, */ if (ip->i_mapcnt > 0 && MANDLOCK(vp, ip->i_mode)) return (EAGAIN); - return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr)); + return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct)); } /* ARGSUSED */ @@ -4383,10 +4422,11 @@ ufs_space(struct vnode *vp, int cmd, struct flock64 *bfp, int flag, * the time this thread tests the i_nextrio value and then reads it * again to use it as the offset for the read ahead. */ +/*ARGSUSED*/ static int ufs_getpage(struct vnode *vp, offset_t off, size_t len, uint_t *protp, page_t *plarr[], size_t plsz, struct seg *seg, caddr_t addr, - enum seg_rw rw, struct cred *cr) + enum seg_rw rw, struct cred *cr, caller_context_t *ct) { u_offset_t uoff = (u_offset_t)off; /* type conversion */ u_offset_t pgoff; @@ -4966,7 +5006,7 @@ int ufs_delay = 1; /*ARGSUSED*/ static int ufs_putpage(struct vnode *vp, offset_t off, size_t len, int flags, - struct cred *cr) + struct cred *cr, caller_context_t *ct) { struct inode *ip = VTOI(vp); int err = 0; @@ -5435,7 +5475,8 @@ ufs_map(struct vnode *vp, uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct segvn_crargs vn_a; struct ufsvfs *ufsvfsp = VTOI(vp)->i_ufsvfs; @@ -5538,7 +5579,8 @@ ufs_addmap(struct vnode *vp, uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cr) + struct cred *cr, + caller_context_t *ct) { struct inode *ip = VTOI(vp); @@ -5556,7 +5598,7 @@ ufs_addmap(struct vnode *vp, static int ufs_delmap(struct vnode *vp, offset_t off, struct as *as, caddr_t addr, size_t len, uint_t prot, uint_t maxprot, uint_t flags, - struct cred *cr) + struct cred *cr, caller_context_t *ct) { struct inode *ip = VTOI(vp); @@ -5577,7 +5619,8 @@ struct pollhead ufs_pollhd; /* ARGSUSED */ int -ufs_poll(vnode_t *vp, short ev, int any, short *revp, struct pollhead **phpp) +ufs_poll(vnode_t *vp, short ev, int any, short *revp, struct pollhead **phpp, + caller_context_t *ct) { struct ufsvfs *ufsvfsp; @@ -5622,7 +5665,8 @@ ufs_poll(vnode_t *vp, short ev, int any, short *revp, struct pollhead **phpp) /* ARGSUSED */ static int -ufs_l_pathconf(struct vnode *vp, int cmd, ulong_t *valp, struct cred *cr) +ufs_l_pathconf(struct vnode *vp, int cmd, ulong_t *valp, struct cred *cr, + caller_context_t *ct) { struct ufsvfs *ufsvfsp = VTOI(vp)->i_ufsvfs; struct ulockfs *ulp = NULL; @@ -5694,7 +5738,7 @@ ufs_l_pathconf(struct vnode *vp, int cmd, ulong_t *valp, struct cred *cr) error = 0; } } else { - error = fs_pathconf(vp, cmd, valp, cr); + error = fs_pathconf(vp, cmd, valp, cr, ct); } break; @@ -5706,8 +5750,14 @@ ufs_l_pathconf(struct vnode *vp, int cmd, ulong_t *valp, struct cred *cr) *valp = (ulong_t)ip->i_fs->fs_bsize; break; + case _PC_SATTR_ENABLED: + case _PC_SATTR_EXISTS: + *valp = vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) && + (vp->v_type == VREG || vp->v_type == VDIR); + break; + default: - error = fs_pathconf(vp, cmd, valp, cr); + error = fs_pathconf(vp, cmd, valp, cr, ct); } if (ulp != NULL) { @@ -5721,7 +5771,7 @@ int ufs_pageio_writes, ufs_pageio_reads; /*ARGSUSED*/ static int ufs_pageio(struct vnode *vp, page_t *pp, u_offset_t io_off, size_t io_len, - int flags, struct cred *cr) + int flags, struct cred *cr, caller_context_t *ct) { struct inode *ip = VTOI(vp); struct ufsvfs *ufsvfsp; @@ -5940,8 +5990,9 @@ ufs_pageio(struct vnode *vp, page_t *pp, u_offset_t io_off, size_t io_len, * directly to the device. It uses a private dump data structure, * set up by dump_ctl, to locate the correct disk block to which to dump. */ +/*ARGSUSED*/ static int -ufs_dump(vnode_t *vp, caddr_t addr, int ldbn, int dblks) +ufs_dump(vnode_t *vp, caddr_t addr, int ldbn, int dblks, caller_context_t *ct) { u_offset_t file_size; struct inode *ip = VTOI(vp); @@ -6019,8 +6070,9 @@ ufs_dump(vnode_t *vp, caddr_t addr, int ldbn, int dblks) * if found, the starting file-relative DEV_BSIZE lbn is written * to *bklp; that lbn is intended for use with VOP_DUMP() */ +/*ARGSUSED*/ static int -ufs_dumpctl(vnode_t *vp, int action, int *blkp) +ufs_dumpctl(vnode_t *vp, int action, int *blkp, caller_context_t *ct) { struct inode *ip = VTOI(vp); ufsvfs_t *ufsvfsp = ip->i_ufsvfs; @@ -6199,7 +6251,7 @@ save_dblks(struct inode *ip, struct ufsvfs *ufsvfsp, daddr32_t *storeblk, /* ARGSUSED */ static int ufs_getsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, - struct cred *cr) + struct cred *cr, caller_context_t *ct) { struct inode *ip = VTOI(vp); struct ulockfs *ulp; @@ -6230,7 +6282,8 @@ ufs_getsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, /* ARGSUSED */ static int -ufs_setsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *cr) +ufs_setsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *cr, + caller_context_t *ct) { struct inode *ip = VTOI(vp); struct ulockfs *ulp = NULL; diff --git a/usr/src/uts/common/fs/vfs.c b/usr/src/uts/common/fs/vfs.c index a1b7059e9ba1..3b0f7afec073 100644 --- a/usr/src/uts/common/fs/vfs.c +++ b/usr/src/uts/common/fs/vfs.c @@ -83,6 +83,7 @@ #include #include #include +#include #include @@ -120,6 +121,8 @@ static kmutex_t vfs_miplist_mutex; static struct ipmnt *vfs_miplist = NULL; static struct ipmnt *vfs_miplist_end = NULL; +static kmem_cache_t *vfs_cache; /* Pointer to VFS kmem cache */ + /* * VFS global data. */ @@ -257,6 +260,21 @@ fsop_sync(vfs_t *vfsp, short flag, cred_t *cr) int fsop_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp) { + /* + * In order to handle system attribute fids in a manner + * transparent to the underlying fs, we embed the fid for + * the sysattr parent object in the sysattr fid and tack on + * some extra bytes that only the sysattr layer knows about. + * + * This guarantees that sysattr fids are larger than other fids + * for this vfs. If the vfs supports sysattrs (implied + * by VFSFT_XVATTR support), we cannot have a size collision + * with XATTR_FIDSZ. + */ + if (vfs_has_feature(vfsp, VFSFT_XVATTR) && + fidp->fid_len == XATTR_FIDSZ) + return (xattr_dir_vget(vfsp, vpp, fidp)); + return (*(vfsp)->vfs_op->vfs_vget)(vfsp, vpp, fidp); } @@ -442,7 +460,7 @@ vfs_setops(vfs_t *vfsp, vfsops_t *vfsops) op = vfsp->vfs_op; membar_consumer(); - if ((vfsp->vfs_implp == NULL || vfsp->vfs_femhead == NULL) && + if (vfsp->vfs_femhead == NULL && casptr(&vfsp->vfs_op, op, vfsops) == op) { return; } @@ -459,8 +477,7 @@ vfs_getops(vfs_t *vfsp) op = vfsp->vfs_op; membar_consumer(); - if ((vfsp->vfs_implp == NULL || vfsp->vfs_femhead == NULL) && - op == vfsp->vfs_op) { + if (vfsp->vfs_femhead == NULL && op == vfsp->vfs_op) { return (op); } else { return (fsem_getvfsops(vfsp)); @@ -494,25 +511,16 @@ vfs_can_sync(vfs_t *vfsp) void vfs_init(vfs_t *vfsp, vfsops_t *op, void *data) { + /* Other initialization has been moved to vfs_alloc() */ vfsp->vfs_count = 0; vfsp->vfs_next = vfsp; vfsp->vfs_prev = vfsp; vfsp->vfs_zone_next = vfsp; vfsp->vfs_zone_prev = vfsp; - vfsp->vfs_flag = 0; + sema_init(&vfsp->vfs_reflock, 1, NULL, SEMA_DEFAULT, NULL); + vfsimpl_setup(vfsp); vfsp->vfs_data = (data); - vfsp->vfs_resource = NULL; - vfsp->vfs_mntpt = NULL; - vfsp->vfs_mntopts.mo_count = 0; - vfsp->vfs_mntopts.mo_list = NULL; - vfsp->vfs_implp = NULL; - vfsp->vfs_zone = NULL; - /* - * Note: Don't initialize any member of the vfs_impl_t structure - * here as it could be a problem for unbundled file systems. - */ vfs_setops((vfsp), (op)); - sema_init(&vfsp->vfs_reflock, 1, NULL, SEMA_DEFAULT, NULL); } /* @@ -522,11 +530,22 @@ vfs_init(vfs_t *vfsp, vfsops_t *op, void *data) void vfsimpl_setup(vfs_t *vfsp) { + int i; + + if (vfsp->vfs_implp != NULL) { + return; + } + vfsp->vfs_implp = kmem_alloc(sizeof (vfs_impl_t), KM_SLEEP); - /* Note that this are #define'd in vfs.h */ - vfsp->vfs_femhead = NULL; + /* Note that these are #define'd in vfs.h */ vfsp->vfs_vskap = NULL; vfsp->vfs_fstypevsp = NULL; + + /* Set size of counted array, then zero the array */ + vfsp->vfs_featureset[0] = VFS_FEATURE_MAXSZ - 1; + for (i = 1; i < VFS_FEATURE_MAXSZ; i++) { + vfsp->vfs_featureset[i] = 0; + } } /* @@ -542,13 +561,6 @@ vfsimpl_teardown(vfs_t *vfsp) if (vip == NULL) return; - if (vip->vi_femhead) { - ASSERT(vip->vi_femhead->femh_list == NULL); - mutex_destroy(&vip->vi_femhead->femh_lock); - kmem_free(vip->vi_femhead, sizeof (*(vip->vi_femhead))); - vip->vi_femhead = NULL; - } - kmem_free(vfsp->vfs_implp, sizeof (vfs_impl_t)); vfsp->vfs_implp = NULL; } @@ -1308,22 +1320,21 @@ domount(char *fsname, struct mounta *uap, vnode_t *vp, struct cred *credp, goto errout; } /* - * Changing the NBMAND setting on remounts is permitted - * but logged since it can lead to unexpected behavior. - * We also counsel against using it for / and /usr. + * Disallow changing the NBMAND disposition of the file + * system on remounts. */ if ((nbmand && ((vp->v_vfsp->vfs_flag & VFS_NBMAND) == 0)) || (!nbmand && (vp->v_vfsp->vfs_flag & VFS_NBMAND))) { - cmn_err(CE_WARN, "domount: nbmand turned %s via " - "remounting %s", nbmand ? "on" : "off", - refstr_value(vp->v_vfsp->vfs_mntpt)); + vn_vfsunlock(vp); + error = EINVAL; + goto errout; } vfsp = vp->v_vfsp; ovflags = vfsp->vfs_flag; vfsp->vfs_flag |= VFS_REMOUNT; vfsp->vfs_flag &= ~VFS_RDONLY; } else { - vfsp = kmem_alloc(sizeof (vfs_t), KM_SLEEP); + vfsp = vfs_alloc(KM_SLEEP); VFS_INIT(vfsp, vfsops, NULL); } @@ -1350,9 +1361,7 @@ domount(char *fsname, struct mounta *uap, vnode_t *vp, struct cred *credp, vfsp->vfs_flag = ovflags; if (splice) vn_vfsunlock(vp); - if (vfsp->vfs_implp) - vfsimpl_teardown(vfsp); - kmem_free(vfsp, sizeof (struct vfs)); + vfs_free(vfsp); goto errout; } } else { @@ -1444,7 +1453,7 @@ domount(char *fsname, struct mounta *uap, vnode_t *vp, struct cred *credp, /* * going to mount on this vnode, so notify. */ - vnevent_mountedover(vp); + vnevent_mountedover(vp, NULL); error = VFS_MOUNT(vfsp, vp, uap, credp); if (uap->flags & MS_RDONLY) @@ -1472,9 +1481,7 @@ domount(char *fsname, struct mounta *uap, vnode_t *vp, struct cred *credp, } else { vfs_unlock(vfsp); vfs_freemnttab(vfsp); - if (vfsp->vfs_implp) - vfsimpl_teardown(vfsp); - kmem_free(vfsp, sizeof (struct vfs)); + vfs_free(vfsp); } } else { /* @@ -2597,7 +2604,8 @@ vfs_mntdummywrite(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cred, */ /* ARGSUSED */ static int -vfs_mntdummygetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +vfs_mntdummygetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { bzero(vap, sizeof (vattr_t)); vap->va_type = VREG; @@ -3996,12 +4004,14 @@ vfsinit(void) NULL, NULL }; - /* Initialize the vnode cache (file systems may use it during init). */ + /* Create vfs cache */ + vfs_cache = kmem_cache_create("vfs_cache", sizeof (struct vfs), + sizeof (uintptr_t), NULL, NULL, NULL, NULL, NULL, 0); + /* Initialize the vnode cache (file systems may use it during init). */ vn_create_cache(); /* Setup event monitor framework */ - fem_init(); /* Initialize the dummy stray file system type. */ @@ -4044,6 +4054,52 @@ vfsinit(void) EIO_vfs.vfs_vskap = NULL; EIO_vfs.vfs_flag |= VFS_STATS; } + + xattr_init(); +} + +vfs_t * +vfs_alloc(int kmflag) +{ + vfs_t *vfsp; + + vfsp = kmem_cache_alloc(vfs_cache, kmflag); + + /* + * Do the simplest initialization here. + * Everything else gets done in vfs_init() + */ + bzero(vfsp, sizeof (vfs_t)); + return (vfsp); +} + +void +vfs_free(vfs_t *vfsp) +{ + /* + * One would be tempted to assert that "vfsp->vfs_count == 0". + * The problem is that this gets called out of domount() with + * a partially initialized vfs and a vfs_count of 1. This is + * also called from vfs_rele() with a vfs_count of 0. We can't + * call VFS_RELE() from domount() if VFS_MOUNT() hasn't successfully + * returned. This is because VFS_MOUNT() fully initializes the + * vfs structure and its associated data. VFS_RELE() will call + * VFS_FREEVFS() which may panic the system if the data structures + * aren't fully initialized from a successful VFS_MOUNT()). + */ + + /* If FEM was in use, make sure everything gets cleaned up */ + if (vfsp->vfs_femhead) { + ASSERT(vfsp->vfs_femhead->femh_list == NULL); + mutex_destroy(&vfsp->vfs_femhead->femh_lock); + kmem_free(vfsp->vfs_femhead, sizeof (*(vfsp->vfs_femhead))); + vfsp->vfs_femhead = NULL; + } + + if (vfsp->vfs_implp) + vfsimpl_teardown(vfsp); + sema_destroy(&vfsp->vfs_reflock); + kmem_cache_free(vfs_cache, vfsp); } /* @@ -4070,10 +4126,7 @@ vfs_rele(vfs_t *vfsp) if (vfsp->vfs_zone) zone_rele(vfsp->vfs_zone); vfs_freemnttab(vfsp); - if (vfsp->vfs_implp) - vfsimpl_teardown(vfsp); - sema_destroy(&vfsp->vfs_reflock); - kmem_free(vfsp, sizeof (*vfsp)); + vfs_free(vfsp); } } @@ -4346,3 +4399,40 @@ getrootfs(char **fstypp, char **fsmodp) *fsmodp = "nfs"; } #endif + +/* + * VFS feature routines + */ + +#define VFTINDEX(feature) (((feature) >> 32) & 0xFFFFFFFF) +#define VFTBITS(feature) ((feature) & 0xFFFFFFFFLL) + +/* Register a feature in the vfs */ +void +vfs_set_feature(vfs_t *vfsp, vfs_feature_t feature) +{ + /* Note that vfs_featureset[] is found in *vfsp->vfs_implp */ + if (vfsp->vfs_implp == NULL) + return; + + vfsp->vfs_featureset[VFTINDEX(feature)] |= VFTBITS(feature); +} + +/* + * Query a vfs for a feature. + * Returns 1 if feature is present, 0 if not + */ +int +vfs_has_feature(vfs_t *vfsp, vfs_feature_t feature) +{ + int ret = 0; + + /* Note that vfs_featureset[] is found in *vfsp->vfs_implp */ + if (vfsp->vfs_implp == NULL) + return (ret); + + if (vfsp->vfs_featureset[VFTINDEX(feature)] & VFTBITS(feature)) + ret = 1; + + return (ret); +} diff --git a/usr/src/uts/common/fs/vnode.c b/usr/src/uts/common/fs/vnode.c index 2e0364a2d4d1..5431d1ed0995 100644 --- a/usr/src/uts/common/fs/vnode.c +++ b/usr/src/uts/common/fs/vnode.c @@ -361,6 +361,36 @@ static const fs_operation_trans_def_t vn_ops_table[] = { NULL, 0, NULL, NULL }; +/* Extensible attribute (xva) routines. */ + +/* + * Zero out the structure, set the size of the requested/returned bitmaps, + * set AT_XVATTR in the embedded vattr_t's va_mask, and set up the pointer + * to the returned attributes array. + */ +void +xva_init(xvattr_t *xvap) +{ + bzero(xvap, sizeof (xvattr_t)); + xvap->xva_mapsize = XVA_MAPSIZE; + xvap->xva_magic = XVA_MAGIC; + xvap->xva_vattr.va_mask = AT_XVATTR; + xvap->xva_rtnattrmapp = &(xvap->xva_rtnattrmap)[0]; +} + +/* + * If AT_XVATTR is set, returns a pointer to the embedded xoptattr_t + * structure. Otherwise, returns NULL. + */ +xoptattr_t * +xva_getxoptattr(xvattr_t *xvap) +{ + xoptattr_t *xoap = NULL; + if (xvap->xva_vattr.va_mask & AT_XVATTR) + xoap = &xvap->xva_xoptattrs; + return (xoap); +} + /* * Used by the AVL routines to compare two vsk_anchor_t structures in the tree. * We use the f_fsid reported by VFS_STATVFS() since we use that for the @@ -740,7 +770,7 @@ vn_rdwr( if (error != 0) goto done; if (nbl_conflict(vp, rw == UIO_WRITE ? NBL_WRITE : NBL_READ, - uio.uio_offset, uio.uio_resid, svmand)) { + uio.uio_offset, uio.uio_resid, svmand, NULL)) { error = EACCES; goto done; } @@ -757,8 +787,8 @@ vn_rdwr( uio.uio_extflg = UIO_COPY_CACHED; error = VOP_READ(vp, &uio, ioflag, cr, NULL); } - VOP_RWUNLOCK(vp, rw == UIO_WRITE ? V_WRITELOCK_TRUE : V_WRITELOCK_FALSE, - NULL); + VOP_RWUNLOCK(vp, + rw == UIO_WRITE ? V_WRITELOCK_TRUE : V_WRITELOCK_FALSE, NULL); if (residp) *residp = uio.uio_resid; else if (uio.uio_resid) @@ -789,7 +819,7 @@ vn_rele(vnode_t *vp) mutex_enter(&vp->v_lock); if (vp->v_count == 1) { mutex_exit(&vp->v_lock); - VOP_INACTIVE(vp, CRED()); + VOP_INACTIVE(vp, CRED(), NULL); } else { vp->v_count--; mutex_exit(&vp->v_lock); @@ -812,7 +842,7 @@ vn_rele_stream(vnode_t *vp) vp->v_stream = NULL; if (vp->v_count == 1) { mutex_exit(&vp->v_lock); - VOP_INACTIVE(vp, CRED()); + VOP_INACTIVE(vp, CRED(), NULL); } else { vp->v_count--; mutex_exit(&vp->v_lock); @@ -829,8 +859,8 @@ vn_open( enum create crwhy, mode_t umask) { - return (vn_openat(pnamep, seg, filemode, - createmode, vpp, crwhy, umask, NULL)); + return (vn_openat(pnamep, seg, filemode, createmode, vpp, crwhy, + umask, NULL, -1)); } @@ -849,21 +879,30 @@ vn_openat( struct vnode **vpp, enum create crwhy, mode_t umask, - struct vnode *startvp) + struct vnode *startvp, + int fd) { struct vnode *vp; int mode; + int accessflags; int error; int in_crit = 0; + int open_done = 0; + int shrlock_done = 0; struct vattr vattr; enum symfollow follow; int estale_retry = 0; + struct shrlock shr; + struct shr_locowner shr_own; mode = 0; + accessflags = 0; if (filemode & FREAD) mode |= VREAD; if (filemode & (FWRITE|FTRUNC)) mode |= VWRITE; + if (filemode & FXATTRDIROPEN) + mode |= VEXEC; /* symlink interpretation */ if (filemode & FNOFOLLOW) @@ -871,6 +910,9 @@ vn_openat( else follow = FOLLOW; + if (filemode & FAPPEND) + accessflags |= V_APPEND; + top: if (filemode & FCREAT) { enum vcexcl excl; @@ -914,7 +956,8 @@ vn_openat( if (!(filemode & FOFFMAX) && (vp->v_type == VREG)) { vattr.va_mask = AT_SIZE; - if ((error = VOP_GETATTR(vp, &vattr, 0, CRED()))) { + if ((error = VOP_GETATTR(vp, &vattr, 0, + CRED(), NULL))) { goto out; } if (vattr.va_size > (u_offset_t)MAXOFF32_T) { @@ -944,36 +987,19 @@ vn_openat( goto out; } /* - * Can't truncate files on which mandatory locking - * or non-blocking mandatory locking is in effect. + * Can't truncate files on which + * sysv mandatory locking is in effect. */ if (filemode & FTRUNC) { vnode_t *rvp; - if (VOP_REALVP(vp, &rvp) != 0) + if (VOP_REALVP(vp, &rvp, NULL) != 0) rvp = vp; - if (nbl_need_check(vp)) { - nbl_start_crit(vp, RW_READER); - in_crit = 1; - vattr.va_mask = AT_MODE|AT_SIZE; - if ((error = VOP_GETATTR(vp, &vattr, 0, - CRED())) == 0) { - if (rvp->v_filocks != NULL) - if (MANDLOCK(vp, - vattr.va_mode)) - error = EAGAIN; - if (!error) { - if (nbl_conflict(vp, - NBL_WRITE, 0, - vattr.va_size, 0)) - error = EACCES; - } - } - } else if (rvp->v_filocks != NULL) { + if (rvp->v_filocks != NULL) { vattr.va_mask = AT_MODE; - if ((error = VOP_GETATTR(vp, &vattr, - 0, CRED())) == 0 && MANDLOCK(vp, - vattr.va_mode)) + if ((error = VOP_GETATTR(vp, + &vattr, 0, CRED(), NULL)) == 0 && + MANDLOCK(vp, vattr.va_mode)) error = EAGAIN; } } @@ -983,7 +1009,7 @@ vn_openat( /* * Check permissions. */ - if (error = VOP_ACCESS(vp, mode, 0, CRED())) + if (error = VOP_ACCESS(vp, mode, accessflags, CRED(), NULL)) goto out; } @@ -996,7 +1022,7 @@ vn_openat( } if (filemode & FNOLINKS) { vattr.va_mask = AT_NLINK; - if ((error = VOP_GETATTR(vp, &vattr, 0, CRED()))) { + if ((error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL))) { goto out; } if (vattr.va_nlink != 1) { @@ -1020,25 +1046,66 @@ vn_openat( if (vp->v_type == VSOCK) { struct vnode *nvp; - error = VOP_REALVP(vp, &nvp); + error = VOP_REALVP(vp, &nvp, NULL); if (error != 0 || nvp == NULL || nvp == vp || nvp->v_type != VSOCK) { error = EOPNOTSUPP; goto out; } } + + if ((vp->v_type == VREG) && nbl_need_check(vp)) { + /* get share reservation */ + shr.s_access = 0; + if (filemode & FWRITE) + shr.s_access |= F_WRACC; + if (filemode & FREAD) + shr.s_access |= F_RDACC; + shr.s_deny = 0; + shr.s_sysid = 0; + shr.s_pid = ttoproc(curthread)->p_pid; + shr_own.sl_pid = shr.s_pid; + shr_own.sl_id = fd; + shr.s_own_len = sizeof (shr_own); + shr.s_owner = (caddr_t)&shr_own; + error = VOP_SHRLOCK(vp, F_SHARE_NBMAND, &shr, filemode, CRED(), + NULL); + if (error) + goto out; + shrlock_done = 1; + + /* nbmand conflict check if truncating file */ + if ((filemode & FTRUNC) && !(filemode & FCREAT)) { + nbl_start_crit(vp, RW_READER); + in_crit = 1; + + vattr.va_mask = AT_SIZE; + if (error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL)) + goto out; + if (nbl_conflict(vp, NBL_WRITE, 0, vattr.va_size, 0, + NULL)) { + error = EACCES; + goto out; + } + } + } + /* * Do opening protocol. */ - error = VOP_OPEN(&vp, filemode, CRED()); + error = VOP_OPEN(&vp, filemode, CRED(), NULL); + if (error) + goto out; + open_done = 1; + /* * Truncate if required. */ - if (error == 0 && (filemode & FTRUNC) && !(filemode & FCREAT)) { + if ((filemode & FTRUNC) && !(filemode & FCREAT)) { vattr.va_size = 0; vattr.va_mask = AT_SIZE; if ((error = VOP_SETATTR(vp, &vattr, 0, CRED(), NULL)) != 0) - (void) VOP_CLOSE(vp, filemode, 1, (offset_t)0, CRED()); + goto out; } out: ASSERT(vp->v_count > 0); @@ -1048,6 +1115,18 @@ vn_openat( in_crit = 0; } if (error) { + if (open_done) { + (void) VOP_CLOSE(vp, filemode, 1, (offset_t)0, CRED(), + NULL); + open_done = 0; + shrlock_done = 0; + } + if (shrlock_done) { + (void) VOP_SHRLOCK(vp, F_UNSHARE, &shr, 0, CRED(), + NULL); + shrlock_done = 0; + } + /* * The following clause was added to handle a problem * with NFS consistency. It is possible that a lookup @@ -1068,6 +1147,49 @@ vn_openat( return (error); } +/* + * The following two accessor functions are for the NFSv4 server. Since there + * is no VOP_OPEN_UP/DOWNGRADE we need a way for the NFS server to keep the + * vnode open counts correct when a client "upgrades" an open or does an + * open_downgrade. In NFS, an upgrade or downgrade can not only change the + * open mode (add or subtract read or write), but also change the share/deny + * modes. However, share reservations are not integrated with OPEN, yet, so + * we need to handle each separately. These functions are cleaner than having + * the NFS server manipulate the counts directly, however, nobody else should + * use these functions. + */ +void +vn_open_upgrade( + vnode_t *vp, + int filemode) +{ + ASSERT(vp->v_type == VREG); + + if (filemode & FREAD) + atomic_add_32(&(vp->v_rdcnt), 1); + if (filemode & FWRITE) + atomic_add_32(&(vp->v_wrcnt), 1); + +} + +void +vn_open_downgrade( + vnode_t *vp, + int filemode) +{ + ASSERT(vp->v_type == VREG); + + if (filemode & FREAD) { + ASSERT(vp->v_rdcnt > 0); + atomic_add_32(&(vp->v_rdcnt), -1); + } + if (filemode & FWRITE) { + ASSERT(vp->v_wrcnt > 0); + atomic_add_32(&(vp->v_wrcnt), -1); + } + +} + int vn_create( char *pnamep, @@ -1080,8 +1202,8 @@ vn_create( int flag, mode_t umask) { - return (vn_createat(pnamep, seg, vap, excl, mode, vpp, - why, flag, umask, NULL)); + return (vn_createat(pnamep, seg, vap, excl, mode, vpp, why, flag, + umask, NULL)); } /* @@ -1171,7 +1293,7 @@ vn_createat( vsec.vsa_dfaclcnt = 0; vsec.vsa_dfaclentp = NULL; vsec.vsa_mask = VSA_DFACLCNT; - error = VOP_GETSECATTR(dvp, &vsec, 0, CRED()); + error = VOP_GETSECATTR(dvp, &vsec, 0, CRED(), NULL); /* * If error is ENOSYS then treat it as no error * Don't want to force all file systems to support @@ -1230,7 +1352,7 @@ vn_createat( * applied, return error. */ vp = *vpp; - if (VOP_REALVP(vp, &rvp) != 0) + if (VOP_REALVP(vp, &rvp, NULL) != 0) rvp = vp; if ((vap->va_mask & AT_SIZE) && nbl_need_check(vp)) { nbl_start_crit(vp, RW_READER); @@ -1238,7 +1360,7 @@ vn_createat( } if (rvp->v_filocks != NULL || rvp->v_shrlocks != NULL) { vattr.va_mask = AT_MODE|AT_SIZE; - if (error = VOP_GETATTR(vp, &vattr, 0, CRED())) { + if (error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL)) { goto out; } if (MANDLOCK(vp, vattr.va_mode)) { @@ -1259,7 +1381,7 @@ vn_createat( vap->va_size - vattr.va_size : vattr.va_size - vap->va_size; if (nbl_conflict(vp, NBL_WRITE, offset, - length, 0)) { + length, 0, NULL)) { error = EACCES; goto out; } @@ -1281,9 +1403,8 @@ vn_createat( */ if (vp->v_flag & VROOT) { ASSERT(why != CRMKDIR); - error = - VOP_CREATE(vp, "", vap, excl, mode, vpp, CRED(), - flag); + error = VOP_CREATE(vp, "", vap, excl, mode, vpp, + CRED(), flag, NULL, NULL); /* * If the create succeeded, it will have created * a new reference to the vnode. Give up the @@ -1307,7 +1428,8 @@ vn_createat( !(flag & FOFFMAX) && (vp->v_type == VREG)) { vattr.va_mask = AT_SIZE; - if ((error = VOP_GETATTR(vp, &vattr, 0, CRED()))) { + if ((error = VOP_GETATTR(vp, &vattr, 0, + CRED(), NULL))) { goto out; } if ((vattr.va_size > (u_offset_t)MAXOFF32_T)) { @@ -1324,10 +1446,17 @@ vn_createat( int must_be_dir = pn_fixslash(&pn); /* trailing '/'? */ if (why == CRMKDIR) - error = VOP_MKDIR(dvp, pn.pn_path, vap, vpp, CRED()); + /* + * N.B., if vn_createat() ever requests + * case-insensitive behavior then it will need + * to be passed to VOP_MKDIR(). VOP_CREATE() + * will already get it via "flag" + */ + error = VOP_MKDIR(dvp, pn.pn_path, vap, vpp, CRED(), + NULL, 0, NULL); else if (!must_be_dir) error = VOP_CREATE(dvp, pn.pn_path, vap, - excl, mode, vpp, CRED(), flag); + excl, mode, vpp, CRED(), flag, NULL, NULL); else error = ENOTDIR; } @@ -1389,11 +1518,11 @@ vn_link(char *from, char *to, enum uio_seg seg) * in the same vfs and that it is writeable. */ vattr.va_mask = AT_FSID; - if (error = VOP_GETATTR(fvp, &vattr, 0, CRED())) + if (error = VOP_GETATTR(fvp, &vattr, 0, CRED(), NULL)) goto out; fsid = vattr.va_fsid; vattr.va_mask = AT_FSID; - if (error = VOP_GETATTR(tdvp, &vattr, 0, CRED())) + if (error = VOP_GETATTR(tdvp, &vattr, 0, CRED(), NULL)) goto out; if (fsid != vattr.va_fsid) { error = EXDEV; @@ -1407,7 +1536,7 @@ vn_link(char *from, char *to, enum uio_seg seg) * Do the link. */ (void) pn_fixslash(&pn); - error = VOP_LINK(tdvp, fvp, pn.pn_path, CRED()); + error = VOP_LINK(tdvp, fvp, pn.pn_path, CRED(), NULL, 0); out: pn_free(&pn); if (fvp) @@ -1434,13 +1563,14 @@ vn_renameat(vnode_t *fdvp, char *fname, vnode_t *tdvp, struct pathname fpn; /* from pathname */ struct pathname tpn; /* to pathname */ dev_t fsid; - int in_crit = 0; + int in_crit_src, in_crit_targ; vnode_t *fromvp, *fvp; - vnode_t *tovp; + vnode_t *tovp, *targvp; int estale_retry = 0; top: - fvp = fromvp = tovp = NULL; + fvp = fromvp = tovp = targvp = NULL; + in_crit_src = in_crit_targ = 0; /* * Get to and from pathnames. */ @@ -1483,7 +1613,7 @@ vn_renameat(vnode_t *fdvp, char *fname, vnode_t *tdvp, if (audit_active) audit_setfsat_path(3); #endif /* C2_AUDIT */ - if (error = lookuppnat(&tpn, NULL, NO_FOLLOW, &tovp, NULLVPP, tdvp)) { + if (error = lookuppnat(&tpn, NULL, NO_FOLLOW, &tovp, &targvp, tdvp)) { goto out; } @@ -1494,11 +1624,11 @@ vn_renameat(vnode_t *fdvp, char *fname, vnode_t *tdvp, */ if (fromvp != tovp) { vattr.va_mask = AT_FSID; - if (error = VOP_GETATTR(fromvp, &vattr, 0, CRED())) + if (error = VOP_GETATTR(fromvp, &vattr, 0, CRED(), NULL)) goto out; fsid = vattr.va_fsid; vattr.va_mask = AT_FSID; - if (error = VOP_GETATTR(tovp, &vattr, 0, CRED())) + if (error = VOP_GETATTR(tovp, &vattr, 0, CRED(), NULL)) goto out; if (fsid != vattr.va_fsid) { error = EXDEV; @@ -1511,10 +1641,19 @@ vn_renameat(vnode_t *fdvp, char *fname, vnode_t *tdvp, goto out; } + if (targvp && (fvp != targvp)) { + nbl_start_crit(targvp, RW_READER); + in_crit_targ = 1; + if (nbl_conflict(targvp, NBL_REMOVE, 0, 0, 0, NULL)) { + error = EACCES; + goto out; + } + } + if (nbl_need_check(fvp)) { nbl_start_crit(fvp, RW_READER); - in_crit = 1; - if (nbl_conflict(fvp, NBL_RENAME, 0, 0, 0)) { + in_crit_src = 1; + if (nbl_conflict(fvp, NBL_RENAME, 0, 0, 0, NULL)) { error = EACCES; goto out; } @@ -1524,19 +1663,22 @@ vn_renameat(vnode_t *fdvp, char *fname, vnode_t *tdvp, * Do the rename. */ (void) pn_fixslash(&tpn); - error = VOP_RENAME(fromvp, fpn.pn_path, tovp, tpn.pn_path, CRED()); + error = VOP_RENAME(fromvp, fpn.pn_path, tovp, tpn.pn_path, CRED(), + NULL, 0); out: pn_free(&fpn); pn_free(&tpn); - if (in_crit) { + if (in_crit_src) nbl_end_crit(fvp); - in_crit = 0; - } + if (in_crit_targ) + nbl_end_crit(targvp); if (fromvp) VN_RELE(fromvp); if (tovp) VN_RELE(tovp); + if (targvp) + VN_RELE(targvp); if (fvp) VN_RELE(fvp); if ((error == ESTALE) && fs_need_estale_retry(estale_retry++)) @@ -1691,7 +1833,7 @@ vn_removeat(vnode_t *startvp, char *fnamep, enum uio_seg seg, enum rm dirflag) if (nbl_need_check(vp)) { nbl_start_crit(vp, RW_READER); in_crit = 1; - if (nbl_conflict(vp, NBL_REMOVE, 0, 0, 0)) { + if (nbl_conflict(vp, NBL_REMOVE, 0, 0, 0, NULL)) { error = EACCES; goto out; } @@ -1715,14 +1857,15 @@ vn_removeat(vnode_t *startvp, char *fnamep, enum uio_seg seg, enum rm dirflag) cwd = PTOU(pp)->u_cdir; VN_HOLD(cwd); mutex_exit(&pp->p_lock); - error = VOP_RMDIR(dvp, pn.pn_path, cwd, CRED()); + error = VOP_RMDIR(dvp, pn.pn_path, cwd, CRED(), + NULL, 0); VN_RELE(cwd); } } else { /* * Unlink(2) can be applied to anything. */ - error = VOP_REMOVE(dvp, pn.pn_path, CRED()); + error = VOP_REMOVE(dvp, pn.pn_path, CRED(), NULL, 0); } out: @@ -1750,9 +1893,9 @@ vn_compare(vnode_t *vp1, vnode_t *vp2) { vnode_t *realvp; - if (vp1 != NULL && VOP_REALVP(vp1, &realvp) == 0) + if (vp1 != NULL && VOP_REALVP(vp1, &realvp, NULL) == 0) vp1 = realvp; - if (vp2 != NULL && VOP_REALVP(vp2, &realvp) == 0) + if (vp2 != NULL && VOP_REALVP(vp2, &realvp, NULL) == 0) vp2 = realvp; return (VN_CMP(vp1, vp2)); } @@ -2170,6 +2313,7 @@ vn_reinit(vnode_t *vp) vp->v_msflags = 0; vp->v_msnext = NULL; vp->v_msprev = NULL; + vp->v_xattrdir = NULL; /* Handles v_femhead, v_path, and the r/w/map counts */ vn_recycle(vp); @@ -2194,6 +2338,9 @@ vn_alloc(int kmflag) void vn_free(vnode_t *vp) { + ASSERT(vp->v_shrlocks == NULL); + ASSERT(vp->v_filocks == NULL); + /* * Some file systems call vn_free() with v_count of zero, * some with v_count of 1. In any case, the value should @@ -2275,84 +2422,85 @@ vn_invalid(vnode_t *vp) /* Vnode event notification */ int -vnevent_support(vnode_t *vp) +vnevent_support(vnode_t *vp, caller_context_t *ct) { if (vp == NULL) return (EINVAL); - return (VOP_VNEVENT(vp, VE_SUPPORT, NULL, NULL)); + return (VOP_VNEVENT(vp, VE_SUPPORT, NULL, NULL, ct)); } void -vnevent_rename_src(vnode_t *vp, vnode_t *dvp, char *name) +vnevent_rename_src(vnode_t *vp, vnode_t *dvp, char *name, caller_context_t *ct) { if (vp == NULL || vp->v_femhead == NULL) { return; } - (void) VOP_VNEVENT(vp, VE_RENAME_SRC, dvp, name); + (void) VOP_VNEVENT(vp, VE_RENAME_SRC, dvp, name, ct); } void -vnevent_rename_dest(vnode_t *vp, vnode_t *dvp, char *name) +vnevent_rename_dest(vnode_t *vp, vnode_t *dvp, char *name, + caller_context_t *ct) { if (vp == NULL || vp->v_femhead == NULL) { return; } - (void) VOP_VNEVENT(vp, VE_RENAME_DEST, dvp, name); + (void) VOP_VNEVENT(vp, VE_RENAME_DEST, dvp, name, ct); } void -vnevent_rename_dest_dir(vnode_t *vp) +vnevent_rename_dest_dir(vnode_t *vp, caller_context_t *ct) { if (vp == NULL || vp->v_femhead == NULL) { return; } - (void) VOP_VNEVENT(vp, VE_RENAME_DEST_DIR, NULL, NULL); + (void) VOP_VNEVENT(vp, VE_RENAME_DEST_DIR, NULL, NULL, ct); } void -vnevent_remove(vnode_t *vp, vnode_t *dvp, char *name) +vnevent_remove(vnode_t *vp, vnode_t *dvp, char *name, caller_context_t *ct) { if (vp == NULL || vp->v_femhead == NULL) { return; } - (void) VOP_VNEVENT(vp, VE_REMOVE, dvp, name); + (void) VOP_VNEVENT(vp, VE_REMOVE, dvp, name, ct); } void -vnevent_rmdir(vnode_t *vp, vnode_t *dvp, char *name) +vnevent_rmdir(vnode_t *vp, vnode_t *dvp, char *name, caller_context_t *ct) { if (vp == NULL || vp->v_femhead == NULL) { return; } - (void) VOP_VNEVENT(vp, VE_RMDIR, dvp, name); + (void) VOP_VNEVENT(vp, VE_RMDIR, dvp, name, ct); } void -vnevent_create(vnode_t *vp) +vnevent_create(vnode_t *vp, caller_context_t *ct) { if (vp == NULL || vp->v_femhead == NULL) { return; } - (void) VOP_VNEVENT(vp, VE_CREATE, NULL, NULL); + (void) VOP_VNEVENT(vp, VE_CREATE, NULL, NULL, ct); } void -vnevent_link(vnode_t *vp) +vnevent_link(vnode_t *vp, caller_context_t *ct) { if (vp == NULL || vp->v_femhead == NULL) { return; } - (void) VOP_VNEVENT(vp, VE_LINK, NULL, NULL); + (void) VOP_VNEVENT(vp, VE_LINK, NULL, NULL, ct); } void -vnevent_mountedover(vnode_t *vp) +vnevent_mountedover(vnode_t *vp, caller_context_t *ct) { if (vp == NULL || vp->v_femhead == NULL) { return; } - (void) VOP_VNEVENT(vp, VE_MOUNTEDOVER, NULL, NULL); + (void) VOP_VNEVENT(vp, VE_MOUNTEDOVER, NULL, NULL, ct); } /* @@ -2400,7 +2548,7 @@ vn_can_change_zones(vnode_t *vp) /* * We always want to look at the underlying vnode if there is one. */ - if (VOP_REALVP(vp, &rvp) != 0) + if (VOP_REALVP(vp, &rvp, NULL) != 0) rvp = vp; /* * Some pseudo filesystems (including doorfs) don't actually register @@ -2432,6 +2580,45 @@ vn_mountedvfs(vnode_t *vp) return (vp->v_vfsmountedhere); } +/* + * vn_has_other_opens() checks whether a particular file is opened by more than + * just the caller and whether the open is for read and/or write. + * This routine is for calling after the caller has already called VOP_OPEN() + * and the caller wishes to know if they are the only one with it open for + * the mode(s) specified. + * + * Vnode counts are only kept on regular files (v_type=VREG). + */ +int +vn_has_other_opens( + vnode_t *vp, + v_mode_t mode) +{ + + ASSERT(vp != NULL); + + switch (mode) { + case V_WRITE: + if (vp->v_wrcnt > 1) + return (V_TRUE); + break; + case V_RDORWR: + if ((vp->v_rdcnt > 1) || (vp->v_wrcnt > 1)) + return (V_TRUE); + break; + case V_RDANDWR: + if ((vp->v_rdcnt > 1) && (vp->v_wrcnt > 1)) + return (V_TRUE); + break; + case V_READ: + if (vp->v_rdcnt > 1) + return (V_TRUE); + break; + } + + return (V_FALSE); +} + /* * vn_is_opened() checks whether a particular file is opened and * whether the open is for read and/or write. @@ -2821,7 +3008,8 @@ int fop_open( vnode_t **vpp, int mode, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int ret; vnode_t *vp = *vpp; @@ -2848,7 +3036,7 @@ fop_open( VOPXID_MAP_CR(vp, cr); - ret = (*(*(vpp))->v_op->vop_open)(vpp, mode, cr); + ret = (*(*(vpp))->v_op->vop_open)(vpp, mode, cr, ct); if (ret) { /* @@ -2892,13 +3080,14 @@ fop_close( int flag, int count, offset_t offset, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; VOPXID_MAP_CR(vp, cr); - err = (*(vp)->v_op->vop_close)(vp, flag, count, offset, cr); + err = (*(vp)->v_op->vop_close)(vp, flag, count, offset, cr, ct); VOPSTATS_UPDATE(vp, close); /* * Check passed in count to handle possible dups. Vnode counts are only @@ -2923,7 +3112,7 @@ fop_read( uio_t *uiop, int ioflag, cred_t *cr, - struct caller_context *ct) + caller_context_t *ct) { int err; ssize_t resid_start = uiop->uio_resid; @@ -2942,7 +3131,7 @@ fop_write( uio_t *uiop, int ioflag, cred_t *cr, - struct caller_context *ct) + caller_context_t *ct) { int err; ssize_t resid_start = uiop->uio_resid; @@ -2962,13 +3151,14 @@ fop_ioctl( intptr_t arg, int flag, cred_t *cr, - int *rvalp) + int *rvalp, + caller_context_t *ct) { int err; VOPXID_MAP_CR(vp, cr); - err = (*(vp)->v_op->vop_ioctl)(vp, cmd, arg, flag, cr, rvalp); + err = (*(vp)->v_op->vop_ioctl)(vp, cmd, arg, flag, cr, rvalp, ct); VOPSTATS_UPDATE(vp, ioctl); return (err); } @@ -2978,13 +3168,14 @@ fop_setfl( vnode_t *vp, int oflags, int nflags, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; VOPXID_MAP_CR(vp, cr); - err = (*(vp)->v_op->vop_setfl)(vp, oflags, nflags, cr); + err = (*(vp)->v_op->vop_setfl)(vp, oflags, nflags, cr, ct); VOPSTATS_UPDATE(vp, setfl); return (err); } @@ -2994,13 +3185,30 @@ fop_getattr( vnode_t *vp, vattr_t *vap, int flags, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; VOPXID_MAP_CR(vp, cr); - err = (*(vp)->v_op->vop_getattr)(vp, vap, flags, cr); + /* + * If this file system doesn't understand the xvattr extensions + * then turn off the xvattr bit. + */ + if (vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) == 0) { + vap->va_mask &= ~AT_XVATTR; + } + + /* + * We're only allowed to skip the ACL check iff we used a 32 bit + * ACE mask with VOP_ACCESS() to determine permissions. + */ + if ((flags & ATTR_NOACLCHECK) && + vfs_has_feature(vp->v_vfsp, VFSFT_ACEMASKONACCESS) == 0) { + return (EINVAL); + } + err = (*(vp)->v_op->vop_getattr)(vp, vap, flags, cr, ct); VOPSTATS_UPDATE(vp, getattr); return (err); } @@ -3017,6 +3225,22 @@ fop_setattr( VOPXID_MAP_CR(vp, cr); + /* + * If this file system doesn't understand the xvattr extensions + * then turn off the xvattr bit. + */ + if (vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) == 0) { + vap->va_mask &= ~AT_XVATTR; + } + + /* + * We're only allowed to skip the ACL check iff we used a 32 bit + * ACE mask with VOP_ACCESS() to determine permissions. + */ + if ((flags & ATTR_NOACLCHECK) && + vfs_has_feature(vp->v_vfsp, VFSFT_ACEMASKONACCESS) == 0) { + return (EINVAL); + } err = (*(vp)->v_op->vop_setattr)(vp, vap, flags, cr, ct); VOPSTATS_UPDATE(vp, setattr); return (err); @@ -3027,13 +3251,19 @@ fop_access( vnode_t *vp, int mode, int flags, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; + if ((flags & V_ACE_MASK) && + vfs_has_feature(vp->v_vfsp, VFSFT_ACEMASKONACCESS) == 0) { + return (EINVAL); + } + VOPXID_MAP_CR(vp, cr); - err = (*(vp)->v_op->vop_access)(vp, mode, flags, cr); + err = (*(vp)->v_op->vop_access)(vp, mode, flags, cr, ct); VOPSTATS_UPDATE(vp, access); return (err); } @@ -3046,13 +3276,32 @@ fop_lookup( pathname_t *pnp, int flags, vnode_t *rdir, - cred_t *cr) + cred_t *cr, + caller_context_t *ct, + int *deflags, /* Returned per-dirent flags */ + pathname_t *ppnp) /* Returned case-preserved name in directory */ { int ret; + /* + * If this file system doesn't support case-insensitive access + * and said access is requested, fail quickly. It is required + * that if the vfs supports case-insensitive lookup, it also + * supports extended dirent flags. + */ + if (flags & FIGNORECASE && + (vfs_has_feature(dvp->v_vfsp, VFSFT_CASEINSENSITIVE) == 0 && + vfs_has_feature(dvp->v_vfsp, VFSFT_NOCASESENSITIVE) == 0)) + return (EINVAL); + VOPXID_MAP_CR(dvp, cr); - ret = (*(dvp)->v_op->vop_lookup)(dvp, nm, vpp, pnp, flags, rdir, cr); + if ((flags & LOOKUP_XATTR) && (flags & LOOKUP_HAVE_SYSATTR_DIR) == 0) { + ret = xattr_dir_lookup(dvp, vpp, flags, cr); + } else { + ret = (*(dvp)->v_op->vop_lookup) + (dvp, nm, vpp, pnp, flags, rdir, cr, ct, deflags, ppnp); + } if (ret == 0 && *vpp) { VOPSTATS_UPDATE(*vpp, lookup); if ((*vpp)->v_path == NULL) { @@ -3072,14 +3321,29 @@ fop_create( int mode, vnode_t **vpp, cred_t *cr, - int flag) + int flags, + caller_context_t *ct, + vsecattr_t *vsecp) /* ACL to set during create */ { int ret; + if (vsecp != NULL && + vfs_has_feature(dvp->v_vfsp, VFSFT_ACLONCREATE) == 0) { + return (EINVAL); + } + /* + * If this file system doesn't support case-insensitive access + * and said access is requested, fail quickly. + */ + if (flags & FIGNORECASE && + (vfs_has_feature(dvp->v_vfsp, VFSFT_CASEINSENSITIVE) == 0 && + vfs_has_feature(dvp->v_vfsp, VFSFT_NOCASESENSITIVE) == 0)) + return (EINVAL); + VOPXID_MAP_CR(dvp, cr); ret = (*(dvp)->v_op->vop_create) - (dvp, name, vap, excl, mode, vpp, cr, flag); + (dvp, name, vap, excl, mode, vpp, cr, flags, ct, vsecp); if (ret == 0 && *vpp) { VOPSTATS_UPDATE(*vpp, create); if ((*vpp)->v_path == NULL) { @@ -3094,13 +3358,24 @@ int fop_remove( vnode_t *dvp, char *nm, - cred_t *cr) + cred_t *cr, + caller_context_t *ct, + int flags) { int err; + /* + * If this file system doesn't support case-insensitive access + * and said access is requested, fail quickly. + */ + if (flags & FIGNORECASE && + (vfs_has_feature(dvp->v_vfsp, VFSFT_CASEINSENSITIVE) == 0 && + vfs_has_feature(dvp->v_vfsp, VFSFT_NOCASESENSITIVE) == 0)) + return (EINVAL); + VOPXID_MAP_CR(dvp, cr); - err = (*(dvp)->v_op->vop_remove)(dvp, nm, cr); + err = (*(dvp)->v_op->vop_remove)(dvp, nm, cr, ct, flags); VOPSTATS_UPDATE(dvp, remove); return (err); } @@ -3110,13 +3385,24 @@ fop_link( vnode_t *tdvp, vnode_t *svp, char *tnm, - cred_t *cr) + cred_t *cr, + caller_context_t *ct, + int flags) { int err; + /* + * If the target file system doesn't support case-insensitive access + * and said access is requested, fail quickly. + */ + if (flags & FIGNORECASE && + (vfs_has_feature(tdvp->v_vfsp, VFSFT_CASEINSENSITIVE) == 0 && + vfs_has_feature(tdvp->v_vfsp, VFSFT_NOCASESENSITIVE) == 0)) + return (EINVAL); + VOPXID_MAP_CR(tdvp, cr); - err = (*(tdvp)->v_op->vop_link)(tdvp, svp, tnm, cr); + err = (*(tdvp)->v_op->vop_link)(tdvp, svp, tnm, cr, ct, flags); VOPSTATS_UPDATE(tdvp, link); return (err); } @@ -3127,13 +3413,25 @@ fop_rename( char *snm, vnode_t *tdvp, char *tnm, - cred_t *cr) + cred_t *cr, + caller_context_t *ct, + int flags) { int err; + /* + * If the file system involved does not support + * case-insensitive access and said access is requested, fail + * quickly. + */ + if (flags & FIGNORECASE && + ((vfs_has_feature(sdvp->v_vfsp, VFSFT_CASEINSENSITIVE) == 0 && + vfs_has_feature(sdvp->v_vfsp, VFSFT_NOCASESENSITIVE) == 0))) + return (EINVAL); + VOPXID_MAP_CR(tdvp, cr); - err = (*(sdvp)->v_op->vop_rename)(sdvp, snm, tdvp, tnm, cr); + err = (*(sdvp)->v_op->vop_rename)(sdvp, snm, tdvp, tnm, cr, ct, flags); VOPSTATS_UPDATE(sdvp, rename); return (err); } @@ -3144,13 +3442,30 @@ fop_mkdir( char *dirname, vattr_t *vap, vnode_t **vpp, - cred_t *cr) + cred_t *cr, + caller_context_t *ct, + int flags, + vsecattr_t *vsecp) /* ACL to set during create */ { int ret; + if (vsecp != NULL && + vfs_has_feature(dvp->v_vfsp, VFSFT_ACLONCREATE) == 0) { + return (EINVAL); + } + /* + * If this file system doesn't support case-insensitive access + * and said access is requested, fail quickly. + */ + if (flags & FIGNORECASE && + (vfs_has_feature(dvp->v_vfsp, VFSFT_CASEINSENSITIVE) == 0 && + vfs_has_feature(dvp->v_vfsp, VFSFT_NOCASESENSITIVE) == 0)) + return (EINVAL); + VOPXID_MAP_CR(dvp, cr); - ret = (*(dvp)->v_op->vop_mkdir)(dvp, dirname, vap, vpp, cr); + ret = (*(dvp)->v_op->vop_mkdir) + (dvp, dirname, vap, vpp, cr, ct, flags, vsecp); if (ret == 0 && *vpp) { VOPSTATS_UPDATE(*vpp, mkdir); if ((*vpp)->v_path == NULL) { @@ -3167,13 +3482,24 @@ fop_rmdir( vnode_t *dvp, char *nm, vnode_t *cdir, - cred_t *cr) + cred_t *cr, + caller_context_t *ct, + int flags) { int err; + /* + * If this file system doesn't support case-insensitive access + * and said access is requested, fail quickly. + */ + if (flags & FIGNORECASE && + (vfs_has_feature(dvp->v_vfsp, VFSFT_CASEINSENSITIVE) == 0 && + vfs_has_feature(dvp->v_vfsp, VFSFT_NOCASESENSITIVE) == 0)) + return (EINVAL); + VOPXID_MAP_CR(dvp, cr); - err = (*(dvp)->v_op->vop_rmdir)(dvp, nm, cdir, cr); + err = (*(dvp)->v_op->vop_rmdir)(dvp, nm, cdir, cr, ct, flags); VOPSTATS_UPDATE(dvp, rmdir); return (err); } @@ -3183,14 +3509,24 @@ fop_readdir( vnode_t *vp, uio_t *uiop, cred_t *cr, - int *eofp) + int *eofp, + caller_context_t *ct, + int flags) { int err; ssize_t resid_start = uiop->uio_resid; + /* + * If this file system doesn't support retrieving directory + * entry flags and said access is requested, fail quickly. + */ + if (flags & V_RDDIR_ENTFLAGS && + vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS) == 0) + return (EINVAL); + VOPXID_MAP_CR(vp, cr); - err = (*(vp)->v_op->vop_readdir)(vp, uiop, cr, eofp); + err = (*(vp)->v_op->vop_readdir)(vp, uiop, cr, eofp, ct, flags); VOPSTATS_UPDATE_IO(vp, readdir, readdir_bytes, (resid_start - uiop->uio_resid)); return (err); @@ -3202,13 +3538,25 @@ fop_symlink( char *linkname, vattr_t *vap, char *target, - cred_t *cr) + cred_t *cr, + caller_context_t *ct, + int flags) { int err; + /* + * If this file system doesn't support case-insensitive access + * and said access is requested, fail quickly. + */ + if (flags & FIGNORECASE && + (vfs_has_feature(dvp->v_vfsp, VFSFT_CASEINSENSITIVE) == 0 && + vfs_has_feature(dvp->v_vfsp, VFSFT_NOCASESENSITIVE) == 0)) + return (EINVAL); + VOPXID_MAP_CR(dvp, cr); - err = (*(dvp)->v_op->vop_symlink) (dvp, linkname, vap, target, cr); + err = (*(dvp)->v_op->vop_symlink) + (dvp, linkname, vap, target, cr, ct, flags); VOPSTATS_UPDATE(dvp, symlink); return (err); } @@ -3217,13 +3565,14 @@ int fop_readlink( vnode_t *vp, uio_t *uiop, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; VOPXID_MAP_CR(vp, cr); - err = (*(vp)->v_op->vop_readlink)(vp, uiop, cr); + err = (*(vp)->v_op->vop_readlink)(vp, uiop, cr, ct); VOPSTATS_UPDATE(vp, readlink); return (err); } @@ -3232,13 +3581,14 @@ int fop_fsync( vnode_t *vp, int syncflag, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; VOPXID_MAP_CR(vp, cr); - err = (*(vp)->v_op->vop_fsync)(vp, syncflag, cr); + err = (*(vp)->v_op->vop_fsync)(vp, syncflag, cr, ct); VOPSTATS_UPDATE(vp, fsync); return (err); } @@ -3246,24 +3596,26 @@ fop_fsync( void fop_inactive( vnode_t *vp, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { /* Need to update stats before vop call since we may lose the vnode */ VOPSTATS_UPDATE(vp, inactive); VOPXID_MAP_CR(vp, cr); - (*(vp)->v_op->vop_inactive)(vp, cr); + (*(vp)->v_op->vop_inactive)(vp, cr, ct); } int fop_fid( vnode_t *vp, - fid_t *fidp) + fid_t *fidp, + caller_context_t *ct) { int err; - err = (*(vp)->v_op->vop_fid)(vp, fidp); + err = (*(vp)->v_op->vop_fid)(vp, fidp, ct); VOPSTATS_UPDATE(vp, fid); return (err); } @@ -3295,11 +3647,12 @@ int fop_seek( vnode_t *vp, offset_t ooff, - offset_t *noffp) + offset_t *noffp, + caller_context_t *ct) { int err; - err = (*(vp)->v_op->vop_seek)(vp, ooff, noffp); + err = (*(vp)->v_op->vop_seek)(vp, ooff, noffp, ct); VOPSTATS_UPDATE(vp, seek); return (err); } @@ -3307,11 +3660,12 @@ fop_seek( int fop_cmp( vnode_t *vp1, - vnode_t *vp2) + vnode_t *vp2, + caller_context_t *ct) { int err; - err = (*(vp1)->v_op->vop_cmp)(vp1, vp2); + err = (*(vp1)->v_op->vop_cmp)(vp1, vp2, ct); VOPSTATS_UPDATE(vp1, cmp); return (err); } @@ -3324,14 +3678,15 @@ fop_frlock( int flag, offset_t offset, struct flk_callback *flk_cbp, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; VOPXID_MAP_CR(vp, cr); err = (*(vp)->v_op->vop_frlock) - (vp, cmd, bfp, flag, offset, flk_cbp, cr); + (vp, cmd, bfp, flag, offset, flk_cbp, cr, ct); VOPSTATS_UPDATE(vp, frlock); return (err); } @@ -3358,11 +3713,12 @@ fop_space( int fop_realvp( vnode_t *vp, - vnode_t **vpp) + vnode_t **vpp, + caller_context_t *ct) { int err; - err = (*(vp)->v_op->vop_realvp)(vp, vpp); + err = (*(vp)->v_op->vop_realvp)(vp, vpp, ct); VOPSTATS_UPDATE(vp, realvp); return (err); } @@ -3378,14 +3734,15 @@ fop_getpage( struct seg *seg, caddr_t addr, enum seg_rw rw, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; VOPXID_MAP_CR(vp, cr); err = (*(vp)->v_op->vop_getpage) - (vp, off, len, protp, plarr, plsz, seg, addr, rw, cr); + (vp, off, len, protp, plarr, plsz, seg, addr, rw, cr, ct); VOPSTATS_UPDATE(vp, getpage); return (err); } @@ -3396,13 +3753,14 @@ fop_putpage( offset_t off, size_t len, int flags, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; VOPXID_MAP_CR(vp, cr); - err = (*(vp)->v_op->vop_putpage)(vp, off, len, flags, cr); + err = (*(vp)->v_op->vop_putpage)(vp, off, len, flags, cr, ct); VOPSTATS_UPDATE(vp, putpage); return (err); } @@ -3417,14 +3775,15 @@ fop_map( uchar_t prot, uchar_t maxprot, uint_t flags, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; VOPXID_MAP_CR(vp, cr); err = (*(vp)->v_op->vop_map) - (vp, off, as, addrp, len, prot, maxprot, flags, cr); + (vp, off, as, addrp, len, prot, maxprot, flags, cr, ct); VOPSTATS_UPDATE(vp, map); return (err); } @@ -3439,7 +3798,8 @@ fop_addmap( uchar_t prot, uchar_t maxprot, uint_t flags, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int error; u_longlong_t delta; @@ -3447,7 +3807,7 @@ fop_addmap( VOPXID_MAP_CR(vp, cr); error = (*(vp)->v_op->vop_addmap) - (vp, off, as, addr, len, prot, maxprot, flags, cr); + (vp, off, as, addr, len, prot, maxprot, flags, cr, ct); if ((!error) && (vp->v_type == VREG)) { delta = (u_longlong_t)btopr(len); @@ -3488,7 +3848,8 @@ fop_delmap( uint_t prot, uint_t maxprot, uint_t flags, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int error; u_longlong_t delta; @@ -3496,7 +3857,7 @@ fop_delmap( VOPXID_MAP_CR(vp, cr); error = (*(vp)->v_op->vop_delmap) - (vp, off, as, addr, len, prot, maxprot, flags, cr); + (vp, off, as, addr, len, prot, maxprot, flags, cr, ct); /* * NFS calls into delmap twice, the first time @@ -3539,11 +3900,12 @@ fop_poll( short events, int anyyet, short *reventsp, - struct pollhead **phpp) + struct pollhead **phpp, + caller_context_t *ct) { int err; - err = (*(vp)->v_op->vop_poll)(vp, events, anyyet, reventsp, phpp); + err = (*(vp)->v_op->vop_poll)(vp, events, anyyet, reventsp, phpp, ct); VOPSTATS_UPDATE(vp, poll); return (err); } @@ -3553,11 +3915,12 @@ fop_dump( vnode_t *vp, caddr_t addr, int lbdn, - int dblks) + int dblks, + caller_context_t *ct) { int err; - err = (*(vp)->v_op->vop_dump)(vp, addr, lbdn, dblks); + err = (*(vp)->v_op->vop_dump)(vp, addr, lbdn, dblks, ct); VOPSTATS_UPDATE(vp, dump); return (err); } @@ -3567,13 +3930,14 @@ fop_pathconf( vnode_t *vp, int cmd, ulong_t *valp, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; VOPXID_MAP_CR(vp, cr); - err = (*(vp)->v_op->vop_pathconf)(vp, cmd, valp, cr); + err = (*(vp)->v_op->vop_pathconf)(vp, cmd, valp, cr, ct); VOPSTATS_UPDATE(vp, pathconf); return (err); } @@ -3585,13 +3949,14 @@ fop_pageio( u_offset_t io_off, size_t io_len, int flags, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; VOPXID_MAP_CR(vp, cr); - err = (*(vp)->v_op->vop_pageio)(vp, pp, io_off, io_len, flags, cr); + err = (*(vp)->v_op->vop_pageio)(vp, pp, io_off, io_len, flags, cr, ct); VOPSTATS_UPDATE(vp, pageio); return (err); } @@ -3600,10 +3965,11 @@ int fop_dumpctl( vnode_t *vp, int action, - int *blkp) + int *blkp, + caller_context_t *ct) { int err; - err = (*(vp)->v_op->vop_dumpctl)(vp, action, blkp); + err = (*(vp)->v_op->vop_dumpctl)(vp, action, blkp, ct); VOPSTATS_UPDATE(vp, dumpctl); return (err); } @@ -3614,14 +3980,15 @@ fop_dispose( page_t *pp, int flag, int dn, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { /* Must do stats first since it's possible to lose the vnode */ VOPSTATS_UPDATE(vp, dispose); VOPXID_MAP_CR(vp, cr); - (*(vp)->v_op->vop_dispose)(vp, pp, flag, dn, cr); + (*(vp)->v_op->vop_dispose)(vp, pp, flag, dn, cr, ct); } int @@ -3629,13 +3996,22 @@ fop_setsecattr( vnode_t *vp, vsecattr_t *vsap, int flag, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; VOPXID_MAP_CR(vp, cr); - err = (*(vp)->v_op->vop_setsecattr) (vp, vsap, flag, cr); + /* + * We're only allowed to skip the ACL check iff we used a 32 bit + * ACE mask with VOP_ACCESS() to determine permissions. + */ + if ((flag & ATTR_NOACLCHECK) && + vfs_has_feature(vp->v_vfsp, VFSFT_ACEMASKONACCESS) == 0) { + return (EINVAL); + } + err = (*(vp)->v_op->vop_setsecattr) (vp, vsap, flag, cr, ct); VOPSTATS_UPDATE(vp, setsecattr); return (err); } @@ -3645,13 +4021,23 @@ fop_getsecattr( vnode_t *vp, vsecattr_t *vsap, int flag, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; + /* + * We're only allowed to skip the ACL check iff we used a 32 bit + * ACE mask with VOP_ACCESS() to determine permissions. + */ + if ((flag & ATTR_NOACLCHECK) && + vfs_has_feature(vp->v_vfsp, VFSFT_ACEMASKONACCESS) == 0) { + return (EINVAL); + } + VOPXID_MAP_CR(vp, cr); - err = (*(vp)->v_op->vop_getsecattr) (vp, vsap, flag, cr); + err = (*(vp)->v_op->vop_getsecattr) (vp, vsap, flag, cr, ct); VOPSTATS_UPDATE(vp, getsecattr); return (err); } @@ -3662,23 +4048,25 @@ fop_shrlock( int cmd, struct shrlock *shr, int flag, - cred_t *cr) + cred_t *cr, + caller_context_t *ct) { int err; VOPXID_MAP_CR(vp, cr); - err = (*(vp)->v_op->vop_shrlock)(vp, cmd, shr, flag, cr); + err = (*(vp)->v_op->vop_shrlock)(vp, cmd, shr, flag, cr, ct); VOPSTATS_UPDATE(vp, shrlock); return (err); } int -fop_vnevent(vnode_t *vp, vnevent_t vnevent, vnode_t *dvp, char *fnm) +fop_vnevent(vnode_t *vp, vnevent_t vnevent, vnode_t *dvp, char *fnm, + caller_context_t *ct) { int err; - err = (*(vp)->v_op->vop_vnevent)(vp, vnevent, dvp, fnm); + err = (*(vp)->v_op->vop_vnevent)(vp, vnevent, dvp, fnm, ct); VOPSTATS_UPDATE(vp, vnevent); return (err); } diff --git a/usr/src/uts/common/fs/xattr.c b/usr/src/uts/common/fs/xattr.c new file mode 100644 index 000000000000..65e0cd80d0b5 --- /dev/null +++ b/usr/src/uts/common/fs/xattr.c @@ -0,0 +1,1428 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + gfs_file_t gfs_private; + xattr_view_t xattr_view; +} xattr_file_t; + +/* ARGSUSED */ +static int +xattr_file_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct) +{ + xattr_file_t *np = (*vpp)->v_data; + + if ((np->xattr_view == XATTR_VIEW_READONLY) && (flags & FWRITE)) + return (EACCES); + + return (0); +} + +/* ARGSUSED */ +static int +xattr_file_access(vnode_t *vp, int mode, int flags, cred_t *cr, + caller_context_t *ct) +{ + xattr_file_t *np = vp->v_data; + + if ((np->xattr_view == XATTR_VIEW_READONLY) && (mode & VWRITE)) + return (EACCES); + + return (0); +} + +/* ARGSUSED */ +static int +xattr_file_close(vnode_t *vp, int flags, int count, offset_t off, + cred_t *cr, caller_context_t *ct) +{ + cleanlocks(vp, ddi_get_pid(), 0); + cleanshares(vp, ddi_get_pid()); + return (0); +} + +static int +xattr_common_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct) +{ + xattr_fid_t *xfidp; + vnode_t *pvp, *savevp; + int error; + uint16_t orig_len; + + if (fidp->fid_len < XATTR_FIDSZ) { + fidp->fid_len = XATTR_FIDSZ; + return (ENOSPC); + } + + savevp = pvp = gfs_file_parent(vp); + mutex_enter(&savevp->v_lock); + if (pvp->v_flag & V_XATTRDIR) { + pvp = gfs_file_parent(pvp); + } + mutex_exit(&savevp->v_lock); + + xfidp = (xattr_fid_t *)fidp; + orig_len = fidp->fid_len; + fidp->fid_len = sizeof (xfidp->parent_fid); + + error = VOP_FID(pvp, fidp, ct); + if (error) { + fidp->fid_len = orig_len; + return (error); + } + + xfidp->parent_len = fidp->fid_len; + fidp->fid_len = XATTR_FIDSZ; + xfidp->dir_offset = gfs_file_inode(vp); + + return (0); +} + +/* ARGSUSED */ +static int +xattr_fill_nvlist(vnode_t *vp, xattr_view_t xattr_view, nvlist_t *nvlp, + cred_t *cr, caller_context_t *ct) +{ + int error; + f_attr_t attr; + uint64_t fsid; + dev_t mdev; + xvattr_t xvattr; + xoptattr_t *xoap; /* Pointer to optional attributes */ + vnode_t *ppvp; + const char *domain; + uint32_t rid; + + xva_init(&xvattr); + + if ((xoap = xva_getxoptattr(&xvattr)) == NULL) + return (EINVAL); + + /* + * For detecting ephemeral uid/gid + */ + xvattr.xva_vattr.va_mask |= (AT_UID|AT_GID); + + /* + * We need to access the real fs object. + * vp points to a GFS file; ppvp points to the real object. + */ + ppvp = gfs_file_parent(gfs_file_parent(vp)); + + /* + * Iterate through the attrs associated with this view + */ + + for (attr = 0; attr < F_ATTR_ALL; attr++) { + if (xattr_view != attr_to_xattr_view(attr)) { + continue; + } + + switch (attr) { + case F_SYSTEM: + XVA_SET_REQ(&xvattr, XAT_SYSTEM); + break; + case F_READONLY: + XVA_SET_REQ(&xvattr, XAT_READONLY); + break; + case F_HIDDEN: + XVA_SET_REQ(&xvattr, XAT_HIDDEN); + break; + case F_ARCHIVE: + XVA_SET_REQ(&xvattr, XAT_ARCHIVE); + break; + case F_IMMUTABLE: + XVA_SET_REQ(&xvattr, XAT_IMMUTABLE); + break; + case F_APPENDONLY: + XVA_SET_REQ(&xvattr, XAT_APPENDONLY); + break; + case F_NOUNLINK: + XVA_SET_REQ(&xvattr, XAT_NOUNLINK); + break; + case F_OPAQUE: + XVA_SET_REQ(&xvattr, XAT_OPAQUE); + break; + case F_NODUMP: + XVA_SET_REQ(&xvattr, XAT_NODUMP); + break; + case F_AV_QUARANTINED: + XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED); + break; + case F_AV_MODIFIED: + XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED); + break; + case F_AV_SCANSTAMP: + if (ppvp->v_type == VREG) + XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP); + break; + case F_CRTIME: + XVA_SET_REQ(&xvattr, XAT_CREATETIME); + break; + case F_FSID: + fsid = (((uint64_t)vp->v_vfsp->vfs_fsid.val[0] << 32) | + (uint64_t)(vp->v_vfsp->vfs_fsid.val[1] & + 0xffffffff)); + VERIFY(nvlist_add_uint64(nvlp, attr_to_name(attr), + fsid) == 0); + break; + case F_MDEV: + mdev = ((int16_t)vp->v_vfsp->vfs_dev); + VERIFY(nvlist_add_uint16(nvlp, attr_to_name(attr), + mdev) == 0); + break; + default: + break; + } + } + + error = VOP_GETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct); + if (error) + return (error); + + /* + * Process all the optional attributes together here. Notice that + * xoap was set when the optional attribute bits were set above. + */ + if ((xvattr.xva_vattr.va_mask & AT_XVATTR) && xoap) { + if (XVA_ISSET_RTN(&xvattr, XAT_READONLY)) { + VERIFY(nvlist_add_boolean_value(nvlp, + attr_to_name(F_READONLY), + xoap->xoa_readonly) == 0); + } + if (XVA_ISSET_RTN(&xvattr, XAT_HIDDEN)) { + VERIFY(nvlist_add_boolean_value(nvlp, + attr_to_name(F_HIDDEN), + xoap->xoa_hidden) == 0); + } + if (XVA_ISSET_RTN(&xvattr, XAT_SYSTEM)) { + VERIFY(nvlist_add_boolean_value(nvlp, + attr_to_name(F_SYSTEM), + xoap->xoa_system) == 0); + } + if (XVA_ISSET_RTN(&xvattr, XAT_ARCHIVE)) { + VERIFY(nvlist_add_boolean_value(nvlp, + attr_to_name(F_ARCHIVE), + xoap->xoa_archive) == 0); + } + if (XVA_ISSET_RTN(&xvattr, XAT_IMMUTABLE)) { + VERIFY(nvlist_add_boolean_value(nvlp, + attr_to_name(F_IMMUTABLE), + xoap->xoa_immutable) == 0); + } + if (XVA_ISSET_RTN(&xvattr, XAT_NOUNLINK)) { + VERIFY(nvlist_add_boolean_value(nvlp, + attr_to_name(F_NOUNLINK), + xoap->xoa_nounlink) == 0); + } + if (XVA_ISSET_RTN(&xvattr, XAT_APPENDONLY)) { + VERIFY(nvlist_add_boolean_value(nvlp, + attr_to_name(F_APPENDONLY), + xoap->xoa_appendonly) == 0); + } + if (XVA_ISSET_RTN(&xvattr, XAT_NODUMP)) { + VERIFY(nvlist_add_boolean_value(nvlp, + attr_to_name(F_NODUMP), + xoap->xoa_nodump) == 0); + } + if (XVA_ISSET_RTN(&xvattr, XAT_OPAQUE)) { + VERIFY(nvlist_add_boolean_value(nvlp, + attr_to_name(F_OPAQUE), + xoap->xoa_opaque) == 0); + } + if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED)) { + VERIFY(nvlist_add_boolean_value(nvlp, + attr_to_name(F_AV_QUARANTINED), + xoap->xoa_av_quarantined) == 0); + } + if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED)) { + VERIFY(nvlist_add_boolean_value(nvlp, + attr_to_name(F_AV_MODIFIED), + xoap->xoa_av_modified) == 0); + } + if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP)) { + VERIFY(nvlist_add_uint8_array(nvlp, + attr_to_name(F_AV_SCANSTAMP), + xoap->xoa_av_scanstamp, + sizeof (xoap->xoa_av_scanstamp)) == 0); + } + if (XVA_ISSET_RTN(&xvattr, XAT_CREATETIME)) { + VERIFY(nvlist_add_uint64_array(nvlp, + attr_to_name(F_CRTIME), + (uint64_t *)&(xoap->xoa_createtime), + sizeof (xoap->xoa_createtime) / + sizeof (uint64_t)) == 0); + } + } + /* + * Check for optional ownersid/groupsid + */ + + if (xvattr.xva_vattr.va_uid > MAXUID) { + nvlist_t *nvl_sid; + + if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP)) + return (ENOMEM); + + if (kidmap_getsidbyuid(xvattr.xva_vattr.va_uid, + &domain, &rid) == 0) { + VERIFY(nvlist_add_string(nvl_sid, + SID_DOMAIN, domain) == 0); + VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0); + VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_OWNERSID), + nvl_sid) == 0); + } + nvlist_free(nvl_sid); + } + if (xvattr.xva_vattr.va_gid > MAXUID) { + nvlist_t *nvl_sid; + + if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP)) + return (ENOMEM); + + if (kidmap_getsidbygid(xvattr.xva_vattr.va_gid, + &domain, &rid) == 0) { + VERIFY(nvlist_add_string(nvl_sid, + SID_DOMAIN, domain) == 0); + VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0); + VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_GROUPSID), + nvl_sid) == 0); + } + nvlist_free(nvl_sid); + } + + return (0); +} + +/* + * The size of a sysattr file is the size of the nvlist that will be + * returned by xattr_file_read(). A call to xattr_file_write() could + * change the size of that nvlist. That size is not stored persistently + * so xattr_fill_nvlist() calls VOP_GETATTR so that it can be calculated. + */ +static int +xattr_file_size(vnode_t *vp, xattr_view_t xattr_view, size_t *size, + cred_t *cr, caller_context_t *ct) +{ + nvlist_t *nvl; + + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)) { + return (ENOMEM); + } + + if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) { + nvlist_free(nvl); + return (EFAULT); + } + + VERIFY(nvlist_size(nvl, size, NV_ENCODE_XDR) == 0); + nvlist_free(nvl); + return (0); +} + +/* ARGSUSED */ +static int +xattr_file_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) +{ + xattr_file_t *np = vp->v_data; + timestruc_t now; + size_t size; + int error; + vnode_t *pvp; + vattr_t pvattr; + + vap->va_type = VREG; + vap->va_mode = MAKEIMODE(vap->va_type, + (np->xattr_view == XATTR_VIEW_READONLY ? 0444 : 0644)); + vap->va_nodeid = gfs_file_inode(vp); + vap->va_nlink = 1; + pvp = gfs_file_parent(vp); + (void) memset(&pvattr, 0, sizeof (pvattr)); + pvattr.va_mask = AT_CTIME|AT_MTIME; + error = VOP_GETATTR(pvp, &pvattr, flags, cr, ct); + if (error) { + return (error); + } + vap->va_ctime = pvattr.va_ctime; + vap->va_mtime = pvattr.va_mtime; + gethrestime(&now); + vap->va_atime = now; + vap->va_uid = 0; + vap->va_gid = 0; + vap->va_rdev = 0; + vap->va_blksize = DEV_BSIZE; + vap->va_seq = 0; + vap->va_fsid = vp->v_vfsp->vfs_dev; + error = xattr_file_size(vp, np->xattr_view, &size, cr, ct); + vap->va_size = size; + vap->va_nblocks = howmany(vap->va_size, vap->va_blksize); + return (error); +} + +/* ARGSUSED */ +static int +xattr_file_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, + caller_context_t *ct) +{ + xattr_file_t *np = vp->v_data; + xattr_view_t xattr_view = np->xattr_view; + char *buf; + size_t filesize; + nvlist_t *nvl; + int error; + + /* + * Validate file offset and fasttrack empty reads + */ + if (uiop->uio_loffset < (offset_t)0) + return (EINVAL); + + if (uiop->uio_resid == 0) + return (0); + + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)) + return (ENOMEM); + + if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) { + nvlist_free(nvl); + return (EFAULT); + } + + VERIFY(nvlist_size(nvl, &filesize, NV_ENCODE_XDR) == 0); + + if (uiop->uio_loffset >= filesize) { + nvlist_free(nvl); + return (0); + } + + buf = kmem_alloc(filesize, KM_SLEEP); + VERIFY(nvlist_pack(nvl, &buf, &filesize, NV_ENCODE_XDR, + KM_SLEEP) == 0); + + error = uiomove((caddr_t)buf, filesize, UIO_READ, uiop); + kmem_free(buf, filesize); + nvlist_free(nvl); + return (error); +} + +/* ARGSUSED */ +static int +xattr_file_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, + caller_context_t *ct) +{ + int error = 0; + char *buf; + char *domain; + uint32_t rid; + ssize_t size = uiop->uio_resid; + nvlist_t *nvp; + nvpair_t *pair = NULL; + vnode_t *ppvp; + xvattr_t xvattr; + xoptattr_t *xoap = NULL; /* Pointer to optional attributes */ + + /* + * Validate file offset and size. + */ + if (uiop->uio_loffset < (offset_t)0) + return (EINVAL); + + if (size == 0) + return (EINVAL); + + xva_init(&xvattr); + + if ((xoap = xva_getxoptattr(&xvattr)) == NULL) { + return (EINVAL); + } + + /* + * Copy and unpack the nvlist + */ + buf = kmem_alloc(size, KM_SLEEP); + if (uiomove((caddr_t)buf, size, UIO_WRITE, uiop)) { + return (EFAULT); + } + + if (nvlist_unpack(buf, size, &nvp, KM_SLEEP) != 0) { + kmem_free(buf, size); + uiop->uio_resid = size; + return (EINVAL); + } + kmem_free(buf, size); + + /* + * Fasttrack empty writes (nvlist with no nvpairs) + */ + if (nvlist_next_nvpair(nvp, NULL) == 0) + return (0); + + ppvp = gfs_file_parent(gfs_file_parent(vp)); + + while (pair = nvlist_next_nvpair(nvp, pair)) { + data_type_t type; + f_attr_t attr; + boolean_t value; + uint64_t *time, *times; + uint_t elem, nelems; + nvlist_t *nvp_sid; + uint8_t *scanstamp; + + /* + * Validate the name and type of each attribute. + * Log any unknown names and continue. This will + * help if additional attributes are added later. + */ + type = nvpair_type(pair); + if ((attr = name_to_attr(nvpair_name(pair))) == F_ATTR_INVAL) { + cmn_err(CE_WARN, "Unknown attribute %s", + nvpair_name(pair)); + continue; + } + + /* + * Verify nvlist type matches required type and view is OK + */ + + if (type != attr_to_data_type(attr) || + (attr_to_xattr_view(attr) == XATTR_VIEW_READONLY)) { + nvlist_free(nvp); + return (EINVAL); + } + + /* + * For OWNERSID/GROUPSID make sure the target + * file system support ephemeral ID's + */ + if ((attr == F_OWNERSID || attr == F_GROUPSID) && + (!(vp->v_vfsp->vfs_flag & VFS_XID))) { + nvlist_free(nvp); + return (EINVAL); + } + + /* + * Retrieve data from nvpair + */ + switch (type) { + case DATA_TYPE_BOOLEAN_VALUE: + if (nvpair_value_boolean_value(pair, &value)) { + nvlist_free(nvp); + return (EINVAL); + } + break; + case DATA_TYPE_UINT64_ARRAY: + if (nvpair_value_uint64_array(pair, ×, &nelems)) { + nvlist_free(nvp); + return (EINVAL); + } + break; + case DATA_TYPE_NVLIST: + if (nvpair_value_nvlist(pair, &nvp_sid)) { + nvlist_free(nvp); + return (EINVAL); + } + break; + case DATA_TYPE_UINT8_ARRAY: + if (nvpair_value_uint8_array(pair, + &scanstamp, &nelems)) { + nvlist_free(nvp); + return (EINVAL); + } + break; + default: + nvlist_free(nvp); + return (EINVAL); + } + + switch (attr) { + /* + * If we have several similar optional attributes to + * process then we should do it all together here so that + * xoap and the requested bitmap can be set in one place. + */ + case F_READONLY: + XVA_SET_REQ(&xvattr, XAT_READONLY); + xoap->xoa_readonly = value; + break; + case F_HIDDEN: + XVA_SET_REQ(&xvattr, XAT_HIDDEN); + xoap->xoa_hidden = value; + break; + case F_SYSTEM: + XVA_SET_REQ(&xvattr, XAT_SYSTEM); + xoap->xoa_system = value; + break; + case F_ARCHIVE: + XVA_SET_REQ(&xvattr, XAT_ARCHIVE); + xoap->xoa_archive = value; + break; + case F_IMMUTABLE: + XVA_SET_REQ(&xvattr, XAT_IMMUTABLE); + xoap->xoa_immutable = value; + break; + case F_NOUNLINK: + XVA_SET_REQ(&xvattr, XAT_NOUNLINK); + xoap->xoa_nounlink = value; + break; + case F_APPENDONLY: + XVA_SET_REQ(&xvattr, XAT_APPENDONLY); + xoap->xoa_appendonly = value; + break; + case F_NODUMP: + XVA_SET_REQ(&xvattr, XAT_NODUMP); + xoap->xoa_nodump = value; + break; + case F_AV_QUARANTINED: + XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED); + xoap->xoa_av_quarantined = value; + break; + case F_AV_MODIFIED: + XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED); + xoap->xoa_av_modified = value; + break; + case F_CRTIME: + XVA_SET_REQ(&xvattr, XAT_CREATETIME); + time = (uint64_t *)&(xoap->xoa_createtime); + for (elem = 0; elem < nelems; elem++) + *time++ = times[elem]; + break; + case F_OWNERSID: + case F_GROUPSID: + if (nvlist_lookup_string(nvp_sid, SID_DOMAIN, + &domain) || nvlist_lookup_uint32(nvp_sid, SID_RID, + &rid)) { + nvlist_free(nvp); + return (EINVAL); + } + + /* + * Now map domain+rid to ephemeral id's + * + * If mapping fails, then the uid/gid will + * be set to UID_NOBODY by Winchester. + */ + + if (attr == F_OWNERSID) { + (void) kidmap_getuidbysid(domain, rid, + &xvattr.xva_vattr.va_uid); + xvattr.xva_vattr.va_mask |= AT_UID; + } else { + (void) kidmap_getgidbysid(domain, rid, + &xvattr.xva_vattr.va_gid); + xvattr.xva_vattr.va_mask |= AT_GID; + } + break; + case F_AV_SCANSTAMP: + if (ppvp->v_type == VREG) { + XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP); + (void) memcpy(xoap->xoa_av_scanstamp, + scanstamp, nelems); + } else { + nvlist_free(nvp); + return (EINVAL); + } + break; + default: + break; + } + } + + ppvp = gfs_file_parent(gfs_file_parent(vp)); + error = VOP_SETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct); + if (error) + uiop->uio_resid = size; + + nvlist_free(nvp); + return (error); +} + +static int +xattr_file_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, + caller_context_t *ct) +{ + switch (cmd) { + case _PC_XATTR_EXISTS: + case _PC_SATTR_ENABLED: + case _PC_SATTR_EXISTS: + *valp = 0; + return (0); + default: + return (fs_pathconf(vp, cmd, valp, cr, ct)); + } +} + +vnodeops_t *xattr_file_ops; + +static const fs_operation_def_t xattr_file_tops[] = { + { VOPNAME_OPEN, { .vop_open = xattr_file_open } }, + { VOPNAME_CLOSE, { .vop_close = xattr_file_close } }, + { VOPNAME_READ, { .vop_read = xattr_file_read } }, + { VOPNAME_WRITE, { .vop_write = xattr_file_write } }, + { VOPNAME_IOCTL, { .error = fs_ioctl } }, + { VOPNAME_GETATTR, { .vop_getattr = xattr_file_getattr } }, + { VOPNAME_ACCESS, { .vop_access = xattr_file_access } }, + { VOPNAME_READDIR, { .error = fs_notdir } }, + { VOPNAME_SEEK, { .vop_seek = fs_seek } }, + { VOPNAME_INACTIVE, { .vop_inactive = gfs_vop_inactive } }, + { VOPNAME_FID, { .vop_fid = xattr_common_fid } }, + { VOPNAME_PATHCONF, { .vop_pathconf = xattr_file_pathconf } }, + { VOPNAME_PUTPAGE, { .error = fs_putpage } }, + { VOPNAME_FSYNC, { .error = fs_fsync } }, + { NULL } +}; + +vnode_t * +xattr_mkfile(vnode_t *pvp, xattr_view_t xattr_view) +{ + vnode_t *vp; + xattr_file_t *np; + + vp = gfs_file_create(sizeof (xattr_file_t), pvp, xattr_file_ops); + np = vp->v_data; + np->xattr_view = xattr_view; + vp->v_flag |= V_SYSATTR; + return (vp); +} + +vnode_t * +xattr_mkfile_ro(vnode_t *pvp) +{ + return (xattr_mkfile(pvp, XATTR_VIEW_READONLY)); +} + +vnode_t * +xattr_mkfile_rw(vnode_t *pvp) +{ + return (xattr_mkfile(pvp, XATTR_VIEW_READWRITE)); +} + +vnodeops_t *xattr_dir_ops; + +static gfs_dirent_t xattr_dirents[] = { + { VIEW_READONLY, xattr_mkfile_ro, GFS_CACHE_VNODE, }, + { VIEW_READWRITE, xattr_mkfile_rw, GFS_CACHE_VNODE, }, + { NULL }, +}; + +#define XATTRDIR_NENTS ((sizeof (xattr_dirents) / sizeof (gfs_dirent_t)) - 1) + +static int +is_sattr_name(char *s) +{ + int i; + + for (i = 0; i < XATTRDIR_NENTS; ++i) { + if (strcmp(s, xattr_dirents[i].gfse_name) == 0) { + return (1); + } + } + return (0); +} + +static int +xattr_copy(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, + cred_t *cr, caller_context_t *ct) +{ + xvattr_t xvattr; + vnode_t *pdvp; + int error; + + /* + * Only copy system attrs if the views are the same + */ + if (strcmp(snm, tnm) != 0) + return (EINVAL); + + xva_init(&xvattr); + + XVA_SET_REQ(&xvattr, XAT_SYSTEM); + XVA_SET_REQ(&xvattr, XAT_READONLY); + XVA_SET_REQ(&xvattr, XAT_HIDDEN); + XVA_SET_REQ(&xvattr, XAT_ARCHIVE); + XVA_SET_REQ(&xvattr, XAT_APPENDONLY); + XVA_SET_REQ(&xvattr, XAT_NOUNLINK); + XVA_SET_REQ(&xvattr, XAT_IMMUTABLE); + XVA_SET_REQ(&xvattr, XAT_NODUMP); + XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED); + XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED); + XVA_SET_REQ(&xvattr, XAT_CREATETIME); + + pdvp = gfs_file_parent(sdvp); + error = VOP_GETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct); + if (error) + return (error); + + pdvp = gfs_file_parent(tdvp); + error = VOP_SETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct); + return (error); +} + +static int +xattr_dir_realdir(vnode_t *dvp, vnode_t **realdvp, int lookup_flags, + cred_t *cr, caller_context_t *ct) +{ + vnode_t *pvp; + int error; + struct pathname pn; + char *startnm = ""; + + *realdvp = NULL; + + pvp = gfs_file_parent(dvp); + + error = pn_get(startnm, UIO_SYSSPACE, &pn); + if (error) { + VN_RELE(pvp); + return (error); + } + + /* + * Set the LOOKUP_HAVE_SYSATTR_DIR flag so that we don't get into an + * infinite loop with fop_lookup calling back to xattr_dir_lookup. + */ + lookup_flags |= LOOKUP_HAVE_SYSATTR_DIR; + error = VOP_LOOKUP(pvp, startnm, realdvp, &pn, lookup_flags, + rootvp, cr, ct, NULL, NULL); + pn_free(&pn); + + return (error); +} + +/* ARGSUSED */ +static int +xattr_dir_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct) +{ + if (flags & FWRITE) { + return (EACCES); + } + + return (0); +} + +/* ARGSUSED */ +static int +xattr_dir_close(vnode_t *vpp, int flags, int count, offset_t off, cred_t *cr, + caller_context_t *ct) +{ + return (0); +} + +/* ARGSUSED */ +static int +xattr_dir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) +{ + timestruc_t now; + vnode_t *pvp; + int error; + vattr_t pvattr; + + error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR, cr, ct); + if (error == 0) { + error = VOP_GETATTR(pvp, vap, 0, cr, ct); + VN_RELE(pvp); + if (error) { + return (error); + } + vap->va_nlink += XATTRDIR_NENTS; + vap->va_size += XATTRDIR_NENTS; + return (0); + } + + /* + * There is no real xattr directory. Cobble together + * an entry using info from the parent object. + */ + pvp = gfs_file_parent(vp); + (void) memset(&pvattr, 0, sizeof (pvattr)); + pvattr.va_mask = AT_UID|AT_GID|AT_RDEV|AT_CTIME|AT_MTIME; + error = VOP_GETATTR(pvp, &pvattr, 0, cr, ct); + if (error) { + return (error); + } + *vap = pvattr; + vap->va_type = VDIR; + vap->va_mode = MAKEIMODE(vap->va_type, S_ISVTX | 0777); + vap->va_fsid = vp->v_vfsp->vfs_dev; + vap->va_nodeid = gfs_file_inode(vp); + vap->va_nlink = XATTRDIR_NENTS+2; + vap->va_size = vap->va_nlink; + gethrestime(&now); + vap->va_atime = now; + vap->va_blksize = 0; + vap->va_nblocks = 0; + vap->va_seq = 0; + return (0); +} + +static int +xattr_dir_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) +{ + vnode_t *realvp; + int error; + + /* + * If there is a real xattr directory, do the setattr there. + * Otherwise, just return success. The GFS directory is transient, + * and any setattr changes can disappear anyway. + */ + error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct); + if (error == 0) { + error = VOP_SETATTR(realvp, vap, flags, cr, ct); + VN_RELE(realvp); + } + if (error == ENOENT) { + error = 0; + } + return (error); +} + +/* ARGSUSED */ +static int +xattr_dir_access(vnode_t *vp, int mode, int flags, cred_t *cr, + caller_context_t *ct) +{ + int error; + vnode_t *realvp = NULL; + + if (mode & VWRITE) { + return (EACCES); + } + + error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct); + + if (realvp) + VN_RELE(realvp); + + /* + * No real xattr dir isn't an error + * an error of EINVAL indicates attributes on attributes + * are not supported. In that case just allow access to the + * transient directory. + */ + return ((error == ENOENT || error == EINVAL) ? 0 : error); +} + +static int +xattr_dir_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, + int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct, + vsecattr_t *vsecp) +{ + vnode_t *pvp; + int error; + + *vpp = NULL; + + /* + * Don't allow creation of extended attributes with sysattr names. + */ + if (is_sattr_name(name)) { + return (gfs_dir_lookup(dvp, name, vpp, cr)); + } + + error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR, + cr, ct); + if (error == 0) { + error = VOP_CREATE(pvp, name, vap, excl, mode, vpp, cr, flag, + ct, vsecp); + VN_RELE(pvp); + } + return (error); +} + +static int +xattr_dir_remove(vnode_t *dvp, char *name, cred_t *cr, caller_context_t *ct, + int flags) +{ + vnode_t *pvp; + int error; + + if (is_sattr_name(name)) { + return (EACCES); + } + + error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct); + if (error == 0) { + error = VOP_REMOVE(pvp, name, cr, ct, flags); + VN_RELE(pvp); + } + return (error); +} + +static int +xattr_dir_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr, + caller_context_t *ct, int flags) +{ + vnode_t *pvp; + int error; + + if (svp->v_flag & V_SYSATTR) { + return (EINVAL); + } + + error = xattr_dir_realdir(tdvp, &pvp, LOOKUP_XATTR, cr, ct); + if (error == 0) { + error = VOP_LINK(pvp, svp, name, cr, ct, flags); + VN_RELE(pvp); + } + return (error); +} + +static int +xattr_dir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, + cred_t *cr, caller_context_t *ct, int flags) +{ + vnode_t *spvp, *tpvp; + int error; + int held_tgt; + + if (is_sattr_name(snm) || is_sattr_name(tnm)) + return (xattr_copy(sdvp, snm, tdvp, tnm, cr, ct)); + /* + * We know that sdvp is a GFS dir, or we wouldn't be here. + * Get the real unnamed directory. + */ + error = xattr_dir_realdir(sdvp, &spvp, LOOKUP_XATTR, cr, ct); + if (error) { + return (error); + } + + if (sdvp == tdvp) { + /* + * If the source and target are the same GFS directory, the + * underlying unnamed source and target dir will be the same. + */ + tpvp = spvp; + VN_HOLD(tpvp); + held_tgt = 1; + } else if (tdvp->v_flag & V_SYSATTR) { + /* + * If the target dir is a different GFS directory, + * find its underlying unnamed dir. + */ + error = xattr_dir_realdir(tdvp, &tpvp, LOOKUP_XATTR, cr, ct); + if (error) { + VN_RELE(spvp); + return (error); + } + held_tgt = 1; + } else { + /* + * Target dir is outside of GFS, pass it on through. + */ + tpvp = tdvp; + held_tgt = 0; + } + + error = VOP_RENAME(spvp, snm, tpvp, tnm, cr, ct, flags); + + if (held_tgt) { + VN_RELE(tpvp); + } + VN_RELE(spvp); + + return (error); +} + +static int +xattr_dir_readdir(vnode_t *dvp, uio_t *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) +{ + vnode_t *pvp; + int error; + int local_eof = 0; + int reset_off = 0; + int has_xattrs = 0; + + if (eofp == NULL) { + eofp = &local_eof; + } + + /* + * See if there is a real extended attribute directory. + */ + error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct); + if (error == 0) { + has_xattrs = 1; + } + + /* + * Start by reading up the static entries. + */ + if (uiop->uio_loffset == 0) { + if (has_xattrs) { + /* + * If there is a real xattr dir, skip . and .. + * in the GFS dir. We'll pick them up below + * when we call into the underlying fs. + */ + uiop->uio_loffset = GFS_STATIC_ENTRY_OFFSET; + } + error = gfs_dir_readdir(dvp, uiop, eofp, NULL, cr, ct); + if (error) { + if (has_xattrs) { + VN_RELE(pvp); + } + return (error); + } + /* + * We must read all of the static entries in the first + * call. Otherwise we won't know if uio_loffset in a + * subsequent call refers to the static entries or to those + * in an underlying fs. + */ + ASSERT(*eofp); + reset_off = 1; + } + + if (!has_xattrs) { + *eofp = 1; + return (0); + } + + *eofp = 0; + if (reset_off) { + uiop->uio_loffset = 0; + } + (void) VOP_RWLOCK(pvp, V_WRITELOCK_FALSE, NULL); + error = VOP_READDIR(pvp, uiop, cr, eofp, ct, flags); + VOP_RWUNLOCK(pvp, V_WRITELOCK_FALSE, NULL); + VN_RELE(pvp); + + return (error); +} + +/* ARGSUSED */ +static void +xattr_dir_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) +{ + gfs_file_t *fp; + + fp = gfs_dir_inactive(vp); + if (fp != NULL) { + kmem_free(fp, fp->gfs_size); + } +} + +static int +xattr_dir_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, + caller_context_t *ct) +{ + switch (cmd) { + case _PC_XATTR_EXISTS: + case _PC_SATTR_ENABLED: + case _PC_SATTR_EXISTS: + *valp = 0; + return (0); + default: + return (fs_pathconf(vp, cmd, valp, cr, ct)); + } +} + +static const fs_operation_def_t xattr_dir_tops[] = { + { VOPNAME_OPEN, { .vop_open = xattr_dir_open } }, + { VOPNAME_CLOSE, { .vop_close = xattr_dir_close } }, + { VOPNAME_IOCTL, { .error = fs_inval } }, + { VOPNAME_GETATTR, { .vop_getattr = xattr_dir_getattr } }, + { VOPNAME_SETATTR, { .vop_setattr = xattr_dir_setattr } }, + { VOPNAME_ACCESS, { .vop_access = xattr_dir_access } }, + { VOPNAME_READDIR, { .vop_readdir = xattr_dir_readdir } }, + { VOPNAME_LOOKUP, { .vop_lookup = gfs_vop_lookup } }, + { VOPNAME_CREATE, { .vop_create = xattr_dir_create } }, + { VOPNAME_REMOVE, { .vop_remove = xattr_dir_remove } }, + { VOPNAME_LINK, { .vop_link = xattr_dir_link } }, + { VOPNAME_RENAME, { .vop_rename = xattr_dir_rename } }, + { VOPNAME_MKDIR, { .error = fs_inval } }, + { VOPNAME_SEEK, { .vop_seek = fs_seek } }, + { VOPNAME_INACTIVE, { .vop_inactive = xattr_dir_inactive } }, + { VOPNAME_FID, { .vop_fid = xattr_common_fid } }, + { VOPNAME_PATHCONF, { .vop_pathconf = xattr_dir_pathconf } }, + { NULL, NULL } +}; + +static gfs_opsvec_t xattr_opsvec[] = { + { "xattr dir", xattr_dir_tops, &xattr_dir_ops }, + { "system attributes", xattr_file_tops, &xattr_file_ops }, + { NULL, NULL, NULL } +}; + +static int +xattr_lookup_cb(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop, + cred_t *cr) +{ + vnode_t *pvp; + struct pathname pn; + int error; + + *vpp = NULL; + *inop = 0; + + error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR, + cr, NULL); + + /* + * Return ENOENT for EACCES requests during lookup. Once an + * attribute create is attempted EACCES will be returned. + */ + if (error) { + if (error == EACCES) + return (ENOENT); + return (error); + } + + error = pn_get((char *)nm, UIO_SYSSPACE, &pn); + if (error == 0) { + error = VOP_LOOKUP(pvp, (char *)nm, vpp, &pn, 0, rootvp, + cr, NULL, NULL, NULL); + pn_free(&pn); + } + VN_RELE(pvp); + + return (error); +} + +/* ARGSUSED */ +static ino64_t +xattrdir_do_ino(vnode_t *vp, int index) +{ + /* + * We use index 0 for the directory fid. Start + * the file numbering at 1. + */ + return ((ino64_t)index+1); +} + +void +xattr_init(void) +{ + VERIFY(gfs_make_opsvec(xattr_opsvec) == 0); +} + +int +xattr_dir_lookup(vnode_t *dvp, vnode_t **vpp, int flags, cred_t *cr) +{ + int error = 0; + + *vpp = NULL; + + if (dvp->v_type != VDIR && dvp->v_type != VREG) + return (EINVAL); + + mutex_enter(&dvp->v_lock); + + /* + * If we're already in sysattr space, don't allow creation + * of another level of sysattrs. + */ + if (dvp->v_flag & V_SYSATTR) { + mutex_exit(&dvp->v_lock); + return (EINVAL); + } + + if (dvp->v_xattrdir != NULL) { + *vpp = dvp->v_xattrdir; + VN_HOLD(*vpp); + } else { + ulong_t val; + int xattrs_allowed = dvp->v_vfsp->vfs_flag & VFS_XATTR; + int sysattrs_allowed = 1; + + /* + * We have to drop the lock on dvp. gfs_dir_create will + * grab it for a VN_HOLD. + */ + mutex_exit(&dvp->v_lock); + + /* + * If dvp allows xattr creation, but not sysattr + * creation, return the real xattr dir vp. We can't + * use the vfs feature mask here because _PC_SATTR_ENABLED + * has vnode-level granularity (e.g. .zfs). + */ + error = VOP_PATHCONF(dvp, _PC_SATTR_ENABLED, &val, cr, NULL); + if (error != 0 || val == 0) + sysattrs_allowed = 0; + + if (!xattrs_allowed && !sysattrs_allowed) + return (EINVAL); + + if (!sysattrs_allowed) { + struct pathname pn; + char *nm = ""; + + error = pn_get(nm, UIO_SYSSPACE, &pn); + if (error) + return (error); + error = VOP_LOOKUP(dvp, nm, vpp, &pn, + flags|LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, NULL, + NULL, NULL); + pn_free(&pn); + return (error); + } + + /* + * Note that we act as if we were given CREATE_XATTR_DIR, + * but only for creation of the GFS directory. + */ + *vpp = gfs_dir_create( + sizeof (gfs_dir_t), dvp, xattr_dir_ops, xattr_dirents, + xattrdir_do_ino, MAXNAMELEN, NULL, xattr_lookup_cb); + mutex_enter(&dvp->v_lock); + if (dvp->v_xattrdir != NULL) { + /* + * We lost the race to create the xattr dir. + * Destroy this one, use the winner. We can't + * just call VN_RELE(*vpp), because the vnode + * is only partially initialized. + */ + gfs_dir_t *dp = (*vpp)->v_data; + + ASSERT((*vpp)->v_count == 1); + vn_free(*vpp); + + mutex_destroy(&dp->gfsd_lock); + kmem_free(dp->gfsd_static, + dp->gfsd_nstatic * sizeof (gfs_dirent_t)); + kmem_free(dp, dp->gfsd_file.gfs_size); + + /* + * There is an implied VN_HOLD(dvp) here. We should + * be doing a VN_RELE(dvp) to clean up the reference + * from *vpp, and then a VN_HOLD(dvp) for the new + * reference. Instead, we just leave the count alone. + */ + + *vpp = dvp->v_xattrdir; + VN_HOLD(*vpp); + } else { + (*vpp)->v_flag |= (V_XATTRDIR|V_SYSATTR); + dvp->v_xattrdir = *vpp; + } + } + mutex_exit(&dvp->v_lock); + + return (error); +} + +int +xattr_dir_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp) +{ + int error; + vnode_t *pvp, *dvp; + xattr_fid_t *xfidp; + struct pathname pn; + char *nm; + uint16_t orig_len; + + *vpp = NULL; + + if (fidp->fid_len < XATTR_FIDSZ) + return (EINVAL); + + xfidp = (xattr_fid_t *)fidp; + orig_len = fidp->fid_len; + fidp->fid_len = xfidp->parent_len; + + error = VFS_VGET(vfsp, &pvp, fidp); + fidp->fid_len = orig_len; + if (error) + return (error); + + /* + * Start by getting the GFS sysattr directory. We might need + * to recreate it during the VOP_LOOKUP. + */ + nm = ""; + error = pn_get(nm, UIO_SYSSPACE, &pn); + if (error) { + VN_RELE(pvp); + return (EINVAL); + } + + error = VOP_LOOKUP(pvp, nm, &dvp, &pn, LOOKUP_XATTR|CREATE_XATTR_DIR, + rootvp, CRED(), NULL, NULL, NULL); + pn_free(&pn); + VN_RELE(pvp); + if (error) + return (error); + + if (xfidp->dir_offset == 0) { + /* + * If we were looking for the directory, we're done. + */ + *vpp = dvp; + return (0); + } + + if (xfidp->dir_offset > XATTRDIR_NENTS) { + VN_RELE(dvp); + return (EINVAL); + } + + nm = xattr_dirents[xfidp->dir_offset - 1].gfse_name; + + error = pn_get(nm, UIO_SYSSPACE, &pn); + if (error) { + VN_RELE(dvp); + return (EINVAL); + } + + error = VOP_LOOKUP(dvp, nm, vpp, &pn, 0, rootvp, CRED(), NULL, + NULL, NULL); + + pn_free(&pn); + VN_RELE(dvp); + + return (error); +} diff --git a/usr/src/uts/common/fs/zfs/dmu.c b/usr/src/uts/common/fs/zfs/dmu.c index b41458bd157b..170f5cc32087 100644 --- a/usr/src/uts/common/fs/zfs/dmu.c +++ b/usr/src/uts/common/fs/zfs/dmu.c @@ -65,7 +65,7 @@ const dmu_object_type_info_t dmu_ot[DMU_OT_NUMTYPES] = { { zap_byteswap, TRUE, "DSL props" }, { byteswap_uint64_array, TRUE, "DSL dataset" }, { zfs_znode_byteswap, TRUE, "ZFS znode" }, - { zfs_acl_byteswap, TRUE, "ZFS ACL" }, + { zfs_oldacl_byteswap, TRUE, "ZFS V0 ACL" }, { byteswap_uint8_array, FALSE, "ZFS plain file" }, { zap_byteswap, TRUE, "ZFS directory" }, { zap_byteswap, TRUE, "ZFS master node" }, @@ -79,7 +79,11 @@ const dmu_object_type_info_t dmu_ot[DMU_OT_NUMTYPES] = { { byteswap_uint8_array, TRUE, "SPA history" }, { byteswap_uint64_array, TRUE, "SPA history offsets" }, { zap_byteswap, TRUE, "Pool properties" }, - { zap_byteswap, TRUE, "DSL permissions" } + { zap_byteswap, TRUE, "DSL permissions" }, + { zfs_acl_byteswap, TRUE, "ZFS ACL" }, + { byteswap_uint8_array, TRUE, "ZFS SYSACL" }, + { byteswap_uint8_array, TRUE, "FUID table" }, + { byteswap_uint8_array, TRUE, "FUID table size" }, }; int diff --git a/usr/src/uts/common/fs/zfs/dsl_deleg.c b/usr/src/uts/common/fs/zfs/dsl_deleg.c index 6f071afab82a..3a9ffa430de1 100644 --- a/usr/src/uts/common/fs/zfs/dsl_deleg.c +++ b/usr/src/uts/common/fs/zfs/dsl_deleg.c @@ -31,7 +31,7 @@ * it is a local or descendent permission. The first letter * identifies the type of entry. * - * ul$ identifies permssions granted locally for this userid. + * ul$ identifies permissions granted locally for this userid. * ud$ identifies permissions granted on descendent datasets for * this userid. * Ul$ identifies permission sets granted locally for this userid. @@ -55,7 +55,7 @@ * s-$@ permissions defined in specified set @ * S-$@ Sets defined in named set @ * - * Each of the above entiies points to another zap attribute that contains one + * Each of the above entities points to another zap attribute that contains one * attribute for each allowed permission, such as create, destroy,... * All of the "upper" case class types will specify permission set names * rather than permissions. diff --git a/usr/src/uts/common/fs/zfs/dsl_prop.c b/usr/src/uts/common/fs/zfs/dsl_prop.c index fcf756b26734..ef248f26e39c 100644 --- a/usr/src/uts/common/fs/zfs/dsl_prop.c +++ b/usr/src/uts/common/fs/zfs/dsl_prop.c @@ -44,8 +44,13 @@ dodefault(const char *propname, int intsz, int numint, void *buf) { zfs_prop_t prop; + /* + * The setonce properties are read-only, BUT they still + * have a default value that can be used as the initial + * value. + */ if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL || - zfs_prop_readonly(prop)) + (zfs_prop_readonly(prop) && !zfs_prop_setonce(prop))) return (ENOENT); if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) { @@ -93,8 +98,7 @@ dsl_prop_get_impl(dsl_dir_t *dd, const char *propname, /* * Break out of this loop for non-inheritable properties. */ - if (prop != ZPROP_INVAL && - !zfs_prop_inheritable(prop)) + if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop)) break; } if (err == ENOENT) @@ -418,14 +422,12 @@ dsl_prop_get_all(objset_t *os, nvlist_t **nvp) { dsl_dataset_t *ds = os->os->os_dsl_dataset; dsl_dir_t *dd = ds->ds_dir; + boolean_t snapshot; int err = 0; dsl_pool_t *dp; objset_t *mos; - if (dsl_dataset_is_snapshot(ds)) { - VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); - return (0); - } + snapshot = dsl_dataset_is_snapshot(ds); VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); @@ -453,6 +455,10 @@ dsl_prop_get_all(objset_t *os, nvlist_t **nvp) dd != ds->ds_dir) continue; + if (snapshot && + !zfs_prop_valid_for_type(prop, ZFS_TYPE_SNAPSHOT)) + continue; + if (nvlist_lookup_nvlist(*nvp, za.za_name, &propval) == 0) continue; diff --git a/usr/src/uts/common/fs/zfs/spa_config.c b/usr/src/uts/common/fs/zfs/spa_config.c index 599f960cb759..6ce83e0d142f 100644 --- a/usr/src/uts/common/fs/zfs/spa_config.c +++ b/usr/src/uts/common/fs/zfs/spa_config.c @@ -197,13 +197,13 @@ spa_config_sync(void) if (vn_rdwr(UIO_WRITE, vp, buf, buflen, 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, NULL) == 0 && - VOP_FSYNC(vp, FSYNC, kcred) == 0) { + VOP_FSYNC(vp, FSYNC, kcred, NULL) == 0) { (void) snprintf(pathname2, sizeof (pathname2), "%s/%s", spa_config_dir, ZPOOL_CACHE_FILE); (void) vn_rename(pathname, pathname2, UIO_SYSSPACE); } - (void) VOP_CLOSE(vp, oflags, 1, 0, kcred); + (void) VOP_CLOSE(vp, oflags, 1, 0, kcred, NULL); VN_RELE(vp); out: diff --git a/usr/src/uts/common/fs/zfs/sys/dmu.h b/usr/src/uts/common/fs/zfs/sys/dmu.h index 6e6495e2ec2b..3300e901a1d5 100644 --- a/usr/src/uts/common/fs/zfs/sys/dmu.h +++ b/usr/src/uts/common/fs/zfs/sys/dmu.h @@ -92,7 +92,7 @@ typedef enum dmu_object_type { DMU_OT_DSL_DATASET, /* UINT64 */ /* zpl: */ DMU_OT_ZNODE, /* ZNODE */ - DMU_OT_ACL, /* ACL */ + DMU_OT_OLDACL, /* Old ACL */ DMU_OT_PLAIN_FILE_CONTENTS, /* UINT8 */ DMU_OT_DIRECTORY_CONTENTS, /* ZAP */ DMU_OT_MASTER_NODE, /* ZAP */ @@ -110,6 +110,10 @@ typedef enum dmu_object_type { DMU_OT_SPA_HISTORY_OFFSETS, /* spa_his_phys_t */ DMU_OT_POOL_PROPS, /* ZAP */ DMU_OT_DSL_PERMS, /* ZAP */ + DMU_OT_ACL, /* ACL */ + DMU_OT_SYSACL, /* SYSACL */ + DMU_OT_FUID, /* FUID table (Packed NVLIST UINT8) */ + DMU_OT_FUID_SIZE, /* FUID table size UINT64 */ DMU_OT_NUMTYPES } dmu_object_type_t; @@ -128,6 +132,7 @@ void byteswap_uint32_array(void *buf, size_t size); void byteswap_uint16_array(void *buf, size_t size); void byteswap_uint8_array(void *buf, size_t size); void zap_byteswap(void *buf, size_t size); +void zfs_oldacl_byteswap(void *buf, size_t size); void zfs_acl_byteswap(void *buf, size_t size); void zfs_znode_byteswap(void *buf, size_t size); @@ -545,7 +550,7 @@ uint64_t dmu_tx_get_txg(dmu_tx_t *tx); * Synchronous write. * If a parent zio is provided this function initiates a write on the * provided buffer as a child of the parent zio. - * In the absense of a parent zio, the write is completed synchronously. + * In the absence of a parent zio, the write is completed synchronously. * At write completion, blk is filled with the bp of the written block. * Note that while the data covered by this function will be on stable * storage when the write completes this new data does not become a diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h b/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h index 1d01123c778d..a29e44e67d0c 100644 --- a/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h +++ b/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h @@ -50,6 +50,7 @@ extern "C" { #define ZFS_DELEG_PERM_RECEIVE "receive" #define ZFS_DELEG_PERM_ALLOW "allow" #define ZFS_DELEG_PERM_USERPROP "userprop" +#define ZFS_DELEG_PERM_VSCAN "vscan" /* * Note: the names of properties that are marked delegatable are also diff --git a/usr/src/uts/common/fs/zfs/sys/zap.h b/usr/src/uts/common/fs/zfs/sys/zap.h index d19470598728..b762a93da539 100644 --- a/usr/src/uts/common/fs/zfs/sys/zap.h +++ b/usr/src/uts/common/fs/zfs/sys/zap.h @@ -31,7 +31,7 @@ /* * ZAP - ZFS Attribute Processor * - * The ZAP is a module which sits on top of the DMU (Data Managemnt + * The ZAP is a module which sits on top of the DMU (Data Management * Unit) and implements a higher-level storage primitive using DMU * objects. Its primary consumer is the ZPL (ZFS Posix Layer). * @@ -90,11 +90,39 @@ extern "C" { #define ZAP_MAXNAMELEN 256 #define ZAP_MAXVALUELEN 1024 +/* + * The matchtype specifies which entry will be accessed. + * MT_EXACT: only find an exact match (non-normalized) + * MT_FIRST: find the "first" normalized (case and Unicode + * form) match; the designated "first" match will not change as long + * as the set of entries with this normalization doesn't change + * MT_BEST: if there is an exact match, find that, otherwise find the + * first normalized match + */ +typedef enum matchtype +{ + MT_EXACT, + MT_BEST, + MT_FIRST +} matchtype_t; + /* * Create a new zapobj with no attributes and return its object number. + * MT_EXACT will cause the zap object to only support MT_EXACT lookups, + * otherwise any matchtype can be used for lookups. + * + * normflags specifies what normalization will be done. values are: + * 0: no normalization (legacy on-disk format, supports MT_EXACT matching + * only) + * U8_TEXTPREP_TOLOWER: case normalization will be performed. + * MT_FIRST/MT_BEST matching will find entries that match without + * regard to case (eg. looking for "foo" can find an entry "Foo"). + * Eventually, other flags will permit unicode normalization as well. */ uint64_t zap_create(objset_t *ds, dmu_object_type_t ot, dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx); +uint64_t zap_create_norm(objset_t *ds, int normflags, dmu_object_type_t ot, + dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx); /* * Create a new zapobj with no attributes from the given (unallocated) @@ -102,6 +130,9 @@ uint64_t zap_create(objset_t *ds, dmu_object_type_t ot, */ int zap_create_claim(objset_t *ds, uint64_t obj, dmu_object_type_t ot, dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx); +int zap_create_claim_norm(objset_t *ds, uint64_t obj, + int normflags, dmu_object_type_t ot, + dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx); /* * The zapobj passed in must be a valid ZAP object for all of the @@ -140,9 +171,20 @@ int zap_destroy(objset_t *ds, uint64_t zapobj, dmu_tx_t *tx); * If the attribute is longer than the buffer, as many integers as will * fit will be transferred to 'buf'. If the entire attribute was not * transferred, the call will return EOVERFLOW. + * + * If rn_len is nonzero, realname will be set to the name of the found + * entry (which may be different from the requested name if matchtype is + * not MT_EXACT). + * + * If normalization_conflictp is not NULL, it will be set if there is + * another name with the same case/unicode normalized form. */ int zap_lookup(objset_t *ds, uint64_t zapobj, const char *name, uint64_t integer_size, uint64_t num_integers, void *buf); +int zap_lookup_norm(objset_t *ds, uint64_t zapobj, const char *name, + uint64_t integer_size, uint64_t num_integers, void *buf, + matchtype_t mt, char *realname, int rn_len, + boolean_t *normalization_conflictp); /* * Create an attribute with the given name and value. @@ -182,6 +224,8 @@ int zap_length(objset_t *ds, uint64_t zapobj, const char *name, * return ENOENT. */ int zap_remove(objset_t *ds, uint64_t zapobj, const char *name, dmu_tx_t *tx); +int zap_remove_norm(objset_t *ds, uint64_t zapobj, const char *name, + matchtype_t mt, dmu_tx_t *tx); /* * Returns (in *count) the number of attributes in the specified zap @@ -213,6 +257,11 @@ typedef struct zap_cursor { typedef struct { int za_integer_length; + /* + * za_normalization_conflict will be set if there are additional + * entries with this normalized form (eg, "foo" and "Foo"). + */ + boolean_t za_normalization_conflict; uint64_t za_num_integers; uint64_t za_first_integer; /* no sign extension for <8byte ints */ char za_name[MAXNAMELEN]; diff --git a/usr/src/uts/common/fs/zfs/sys/zap_impl.h b/usr/src/uts/common/fs/zfs/sys/zap_impl.h index 4e43f4ae49a1..8cc110884693 100644 --- a/usr/src/uts/common/fs/zfs/sys/zap_impl.h +++ b/usr/src/uts/common/fs/zfs/sys/zap_impl.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -59,7 +59,8 @@ typedef struct mzap_ent_phys { typedef struct mzap_phys { uint64_t mz_block_type; /* ZBT_MICRO */ uint64_t mz_salt; - uint64_t mz_pad[6]; + uint64_t mz_normflags; + uint64_t mz_pad[5]; mzap_ent_phys_t mz_chunk[1]; /* actually variable size depending on block size */ } mzap_phys_t; @@ -127,6 +128,7 @@ typedef struct zap_phys { uint64_t zap_num_leafs; /* number of leafs */ uint64_t zap_num_entries; /* number of entries */ uint64_t zap_salt; /* salt to stir into hash function */ + uint64_t zap_normflags; /* flags for u8_textprep_str() */ /* * This structure is followed by padding, and then the embedded * pointer table. The embedded pointer table takes up second @@ -142,7 +144,8 @@ typedef struct zap { uint64_t zap_object; struct dmu_buf *zap_dbuf; krwlock_t zap_rwlock; - int zap_ismicro; + boolean_t zap_ismicro; + int zap_normflags; uint64_t zap_salt; union { struct { @@ -165,34 +168,45 @@ typedef struct zap { } zap_u; } zap_t; +typedef struct zap_name { + zap_t *zn_zap; + const char *zn_name_orij; + uint64_t zn_hash; + matchtype_t zn_matchtype; + const char *zn_name_norm; + char zn_normbuf[ZAP_MAXNAMELEN]; +} zap_name_t; + #define zap_f zap_u.zap_fat #define zap_m zap_u.zap_micro -uint64_t zap_hash(zap_t *zap, const char *name); +boolean_t zap_match(zap_name_t *zn, const char *matchname); int zap_lockdir(objset_t *os, uint64_t obj, dmu_tx_t *tx, krw_t lti, int fatreader, zap_t **zapp); void zap_unlockdir(zap_t *zap); void zap_evict(dmu_buf_t *db, void *vmzap); +zap_name_t *zap_name_alloc(zap_t *zap, const char *name, matchtype_t mt); +void zap_name_free(zap_name_t *zn); #define ZAP_HASH_IDX(hash, n) (((n) == 0) ? 0 : ((hash) >> (64 - (n)))) void fzap_byteswap(void *buf, size_t size); int fzap_count(zap_t *zap, uint64_t *count); -int fzap_lookup(zap_t *zap, const char *name, - uint64_t integer_size, uint64_t num_integers, void *buf); -int fzap_add(zap_t *zap, const char *name, - uint64_t integer_size, uint64_t num_integers, +int fzap_lookup(zap_name_t *zn, + uint64_t integer_size, uint64_t num_integers, void *buf, + char *realname, int rn_len, boolean_t *normalization_conflictp); +int fzap_add(zap_name_t *zn, uint64_t integer_size, uint64_t num_integers, const void *val, dmu_tx_t *tx); -int fzap_update(zap_t *zap, const char *name, +int fzap_update(zap_name_t *zn, int integer_size, uint64_t num_integers, const void *val, dmu_tx_t *tx); -int fzap_length(zap_t *zap, const char *name, +int fzap_length(zap_name_t *zn, uint64_t *integer_size, uint64_t *num_integers); -int fzap_remove(zap_t *zap, const char *name, dmu_tx_t *tx); +int fzap_remove(zap_name_t *zn, dmu_tx_t *tx); int fzap_cursor_retrieve(zap_t *zap, zap_cursor_t *zc, zap_attribute_t *za); void fzap_get_stats(zap_t *zap, zap_stats_t *zs); void zap_put_leaf(struct zap_leaf *l); -int fzap_add_cd(zap_t *zap, const char *name, +int fzap_add_cd(zap_name_t *zn, uint64_t integer_size, uint64_t num_integers, const void *val, uint32_t cd, dmu_tx_t *tx); void fzap_upgrade(zap_t *zap, dmu_tx_t *tx); diff --git a/usr/src/uts/common/fs/zfs/sys/zap_leaf.h b/usr/src/uts/common/fs/zfs/sys/zap_leaf.h index 147fb7212454..6cee851f1f04 100644 --- a/usr/src/uts/common/fs/zfs/sys/zap_leaf.h +++ b/usr/src/uts/common/fs/zfs/sys/zap_leaf.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -92,6 +92,8 @@ typedef enum zap_chunk_type { ZAP_CHUNK_TYPE_MAX = 250 } zap_chunk_type_t; +#define ZLF_ENTRIES_CDSORTED (1<<0) + /* * TAKE NOTE: * If zap_leaf_phys_t is modified, zap_leaf_byteswap() must be modified. @@ -109,7 +111,8 @@ typedef struct zap_leaf_phys { /* above is accessable to zap, below is zap_leaf private */ uint16_t lh_freelist; /* chunk head of free list */ - uint8_t lh_pad2[12]; + uint8_t lh_flags; /* ZLF_* flags */ + uint8_t lh_pad2[11]; } l_hdr; /* 2 24-byte chunks */ /* @@ -174,7 +177,7 @@ typedef struct zap_entry_handle { * value must equal zap_hash(name). */ extern int zap_leaf_lookup(zap_leaf_t *l, - const char *name, uint64_t h, zap_entry_handle_t *zeh); + zap_name_t *zn, zap_entry_handle_t *zeh); /* * Return a handle to the entry with this hash+cd, or the entry with the @@ -218,13 +221,20 @@ extern int zap_entry_create(zap_leaf_t *l, uint8_t integer_size, uint64_t num_integers, const void *buf, zap_entry_handle_t *zeh); +/* + * Return true if there are additional entries with the same normalized + * form. + */ +extern boolean_t zap_entry_normalization_conflict(zap_entry_handle_t *zeh, + zap_name_t *zn, const char *name, zap_t *zap); + /* * Other stuff. */ -extern void zap_leaf_init(zap_leaf_t *l); +extern void zap_leaf_init(zap_leaf_t *l, int version); extern void zap_leaf_byteswap(zap_leaf_phys_t *buf, int len); -extern void zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl); +extern void zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl, int version); extern void zap_leaf_stats(zap_t *zap, zap_leaf_t *l, zap_stats_t *zs); #ifdef __cplusplus diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_acl.h b/usr/src/uts/common/fs/zfs/sys/zfs_acl.h index e3a653c5fa4c..6a7724cee4d9 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_acl.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_acl.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -34,6 +34,7 @@ #endif #include #include +#include #ifdef __cplusplus extern "C" { @@ -44,30 +45,129 @@ struct znode_phys; #define ACCESS_UNDETERMINED -1 #define ACE_SLOT_CNT 6 +#define ZFS_ACL_VERSION_INITIAL 0ULL +#define ZFS_ACL_VERSION_FUID 1ULL +#define ZFS_ACL_VERSION ZFS_ACL_VERSION_FUID -typedef struct zfs_znode_acl { +/* + * ZFS ACLs are store in various forms. + * Files created with ACL version ZFS_ACL_VERSION_INITIAL + * will all be created with fixed length ACEs of type + * zfs_oldace_t. + * + * Files with ACL version ZFS_ACL_VERSION_FUID will be created + * with various sized ACEs. The abstraction entries will utilize + * zfs_ace_hdr_t, normal user/group entries will use zfs_ace_t + * and some specialized CIFS ACEs will use zfs_object_ace_t. + */ + +/* + * All ACEs have a common hdr. For + * owner@, group@, and everyone@ this is all + * thats needed. + */ +typedef struct zfs_ace_hdr { + uint16_t z_type; + uint16_t z_flags; + uint32_t z_access_mask; +} zfs_ace_hdr_t; + +typedef zfs_ace_hdr_t zfs_ace_abstract_t; + +/* + * Standard ACE + */ +typedef struct zfs_ace { + zfs_ace_hdr_t z_hdr; + uint64_t z_fuid; +} zfs_ace_t; + +/* + * The following type only applies to ACE_ACCESS_ALLOWED|DENIED_OBJECT_ACE_TYPE + * and will only be set/retrieved in a CIFS context. + */ + +typedef struct zfs_object_ace { + zfs_ace_t z_ace; + uint8_t z_object_type[16]; /* object type */ + uint8_t z_inherit_type[16]; /* inherited object type */ +} zfs_object_ace_t; + +typedef struct zfs_oldace { + uint32_t z_fuid; /* "who" */ + uint32_t z_access_mask; /* access mask */ + uint16_t z_flags; /* flags, i.e inheritance */ + uint16_t z_type; /* type of entry allow/deny */ +} zfs_oldace_t; + +typedef struct zfs_acl_phys_v0 { + uint64_t z_acl_extern_obj; /* ext acl pieces */ + uint32_t z_acl_count; /* Number of ACEs */ + uint16_t z_acl_version; /* acl version */ + uint16_t z_acl_pad; /* pad */ + zfs_oldace_t z_ace_data[ACE_SLOT_CNT]; /* 6 standard ACEs */ +} zfs_acl_phys_v0_t; + +#define ZFS_ACE_SPACE (sizeof (zfs_oldace_t) * ACE_SLOT_CNT) + +typedef struct zfs_acl_phys { uint64_t z_acl_extern_obj; /* ext acl pieces */ - uint32_t z_acl_count; /* Number of ACEs */ + uint32_t z_acl_size; /* Number of bytes in ACL */ uint16_t z_acl_version; /* acl version */ - uint16_t z_acl_pad; /* pad */ - ace_t z_ace_data[ACE_SLOT_CNT]; /* 6 standard ACEs */ -} zfs_znode_acl_t; - -#define ACL_DATA_ALLOCED 0x1 + uint16_t z_acl_count; /* ace count */ + uint8_t z_ace_data[ZFS_ACE_SPACE]; /* space for embedded ACEs */ +} zfs_acl_phys_t; + + + +typedef struct acl_ops { + uint32_t (*ace_mask_get) (void *acep); /* get access mask */ + void (*ace_mask_set) (void *acep, + uint32_t mask); /* set access mask */ + uint16_t (*ace_flags_get) (void *acep); /* get flags */ + void (*ace_flags_set) (void *acep, + uint16_t flags); /* set flags */ + uint16_t (*ace_type_get)(void *acep); /* get type */ + void (*ace_type_set)(void *acep, + uint16_t type); /* set type */ + uint64_t (*ace_who_get)(void *acep); /* get who/fuid */ + void (*ace_who_set)(void *acep, + uint64_t who); /* set who/fuid */ + size_t (*ace_size)(void *acep); /* how big is this ace */ + size_t (*ace_abstract_size)(void); /* sizeof abstract entry */ + int (*ace_mask_off)(void); /* off of access mask in ace */ + int (*ace_data)(void *acep, void **datap); + /* ptr to data if any */ +} acl_ops_t; /* - * Max ACL size is prepended deny for all entries + the - * canonical six tacked on * the end. + * A zfs_acl_t structure is composed of a list of zfs_acl_node_t's. + * Each node will have one or more ACEs associated with it. You will + * only have multiple nodes during a chmod operation. Normally only + * one node is required. */ -#define MAX_ACL_SIZE (MAX_ACL_ENTRIES * 2 + 6) +typedef struct zfs_acl_node { + list_node_t z_next; /* Next chunk of ACEs */ + void *z_acldata; /* pointer into actual ACE(s) */ + void *z_allocdata; /* pointer to kmem allocated memory */ + size_t z_allocsize; /* Size of blob in bytes */ + size_t z_size; /* length of ACL data */ + int z_ace_count; /* number of ACEs in this acl node */ + int z_ace_idx; /* ace iterator positioned on */ +} zfs_acl_node_t; typedef struct zfs_acl { - int z_slots; /* number of allocated slots for ACEs */ - int z_acl_count; - uint_t z_state; - ace_t *z_acl; + int z_acl_count; /* Number of ACEs */ + size_t z_acl_bytes; /* Number of bytes in ACL */ + uint_t z_version; /* version of ACL */ + void *z_next_ace; /* pointer to next ACE */ + int z_hints; /* ACL hints (ZFS_INHERIT_ACE ...) */ + zfs_acl_node_t *z_curr_node; /* current node iterator is handling */ + list_t z_acl; /* chunks of ACE data */ + acl_ops_t z_ops; /* ACL operations */ } zfs_acl_t; +#define ACL_DATA_ALLOCED 0x1 #define ZFS_ACL_SIZE(aclcnt) (sizeof (ace_t) * (aclcnt)) /* @@ -84,24 +184,26 @@ typedef struct zfs_acl { #define ZFS_ACL_SECURE 4 struct znode; +struct zfsvfs; #ifdef _KERNEL void zfs_perm_init(struct znode *, struct znode *, int, vattr_t *, - dmu_tx_t *, cred_t *); -int zfs_getacl(struct znode *, vsecattr_t *, cred_t *); -int zfs_mode_update(struct znode *, uint64_t, dmu_tx_t *); -int zfs_setacl(struct znode *, vsecattr_t *, cred_t *); + dmu_tx_t *, cred_t *, zfs_acl_t *, zfs_fuid_info_t **); +int zfs_getacl(struct znode *, vsecattr_t *, boolean_t, cred_t *); +int zfs_setacl(struct znode *, vsecattr_t *, boolean_t, cred_t *); void zfs_acl_rele(void *); -void zfs_ace_byteswap(ace_t *, int); -extern int zfs_zaccess(struct znode *, int, cred_t *); -extern int zfs_zaccess_rwx(struct znode *, mode_t, cred_t *); +void zfs_oldace_byteswap(ace_t *, int); +void zfs_ace_byteswap(void *, size_t, boolean_t); +extern int zfs_zaccess(struct znode *, int, int, boolean_t, cred_t *); +extern int zfs_zaccess_rwx(struct znode *, mode_t, int, cred_t *); +extern int zfs_zaccess_unix(struct znode *, mode_t, cred_t *); extern int zfs_acl_access(struct znode *, int, cred_t *); int zfs_acl_chmod_setattr(struct znode *, uint64_t, dmu_tx_t *); int zfs_zaccess_delete(struct znode *, struct znode *, cred_t *); int zfs_zaccess_rename(struct znode *, struct znode *, struct znode *, struct znode *, cred_t *cr); -int zfs_zaccess_v4_perm(struct znode *, int, cred_t *); void zfs_acl_free(zfs_acl_t *); +int zfs_vsec_2_aclp(struct zfsvfs *, vtype_t, vsecattr_t *, zfs_acl_t **); #endif diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_ctldir.h b/usr/src/uts/common/fs/zfs/sys/zfs_ctldir.h index 8f1cb74d94be..78f52bf22c34 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_ctldir.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_ctldir.h @@ -58,7 +58,8 @@ int zfsctl_umount_snapshots(vfs_t *, int, cred_t *); int zfsctl_unmount_snap(vnode_t *dvp, const char *name, int force, cred_t *cr); int zfsctl_root_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp, - int flags, vnode_t *rdir, cred_t *cr); + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp); int zfsctl_make_fid(zfsvfs_t *zfsvfsp, uint64_t object, uint32_t gen, fid_t *fidp); diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_dir.h b/usr/src/uts/common/fs/zfs/sys/zfs_dir.h index f60d614953f3..0ad129092c0b 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_dir.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_dir.h @@ -28,6 +28,7 @@ #pragma ident "%Z%%M% %I% %E% SMI" +#include #include #include @@ -41,6 +42,8 @@ extern "C" { #define ZSHARED 0x0004 /* shared access (zfs_dirlook()) */ #define ZXATTR 0x0008 /* we want the xattr dir */ #define ZRENAMING 0x0010 /* znode is being renamed */ +#define ZCILOOK 0x0020 /* case-insensitive lookup requested */ +#define ZCIEXACT 0x0040 /* c-i requires c-s match (rename) */ /* mknode flags */ #define IS_ROOT_NODE 0x01 /* create a root node */ @@ -48,15 +51,18 @@ extern "C" { #define IS_REPLAY 0x04 /* we are replaying intent log */ extern int zfs_dirent_lock(zfs_dirlock_t **, znode_t *, char *, znode_t **, - int); + int, int *, pathname_t *); extern void zfs_dirent_unlock(zfs_dirlock_t *); extern int zfs_link_create(zfs_dirlock_t *, znode_t *, dmu_tx_t *, int); extern int zfs_link_destroy(zfs_dirlock_t *, znode_t *, dmu_tx_t *, int, boolean_t *); -extern int zfs_dirlook(znode_t *, char *, vnode_t **); +extern int zfs_dirlook(znode_t *, char *, vnode_t **, int, int *, + pathname_t *); extern void zfs_mknode(znode_t *, vattr_t *, uint64_t *, - dmu_tx_t *, cred_t *, uint_t, znode_t **, int); + dmu_tx_t *, cred_t *, uint_t, znode_t **, int, + zfs_acl_t *, zfs_fuid_info_t **); extern void zfs_rmnode(znode_t *); +extern void zfs_dl_name_switch(zfs_dirlock_t *dl, char *new, char **old); extern boolean_t zfs_dirempty(znode_t *); extern void zfs_unlinked_add(znode_t *, dmu_tx_t *); extern void zfs_unlinked_drain(zfsvfs_t *zfsvfs); diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_fuid.h b/usr/src/uts/common/fs/zfs/sys/zfs_fuid.h new file mode 100644 index 000000000000..3f46ff7e3e05 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/sys/zfs_fuid.h @@ -0,0 +1,131 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_FS_ZFS_FUID_H +#define _SYS_FS_ZFS_FUID_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#ifdef _KERNEL +#include +#include +#include +#include +#endif + +#ifdef _KERNEL +typedef struct zfs_fuid_hdl { + idmap_get_handle_t *z_hdl; + boolean_t z_map_needed; /* is mapping required */ + idmap_stat z_status; /* needed for kidmap interface */ +} zfs_fuid_hdl_t; + +typedef enum { + ZFS_OWNER, + ZFS_GROUP, + ZFS_ACE_USER, + ZFS_ACE_GROUP +} zfs_fuid_type_t; + +#endif + + +#define FUID_INDEX(x) (x >> 32) +#define FUID_RID(x) (x & 0xffffffff) +#define FUID_ENCODE(idx, rid) ((idx << 32) | rid) +/* + * FUIDs cause problems for the intent log + * we need to replay the creation of the FUID, + * but we can't count on the idmapper to be around + * and during replay the FUID index may be different than + * before. Also, if an ACL has 100 ACEs and 12 different + * domains we don't want to log 100 domain strings, but rather + * just the unique 12. + */ + +/* + * The FUIDs in the log will index into + * domain string table and the bottom half will be the rid. + * Used for mapping ephemeral uid/gid during ACL setting to FUIDs + */ +typedef struct zfs_fuid { + list_node_t z_next; + uint64_t z_id; /* uid/gid being converted to fuid */ + uint64_t z_domidx; /* index in AVL domain table */ + uint64_t z_logfuid; /* index for domain in log */ +} zfs_fuid_t; + +/* list of unique domains */ +typedef struct zfs_fuid_domain { + list_node_t z_next; + uint64_t z_domidx; /* AVL tree idx */ + const char *z_domain; /* domain string */ +} zfs_fuid_domain_t; + +/* + * FUID information necessary for logging create, setattr, and setacl. + */ +typedef struct zfs_fuid_info { + list_t z_fuids; + list_t z_domains; + uint64_t z_fuid_owner; + uint64_t z_fuid_group; + char **z_domain_table; /* Used during replay */ + uint32_t z_fuid_cnt; /* How many fuids in z_fuids */ + uint32_t z_domain_cnt; /* How many domains */ + size_t z_domain_str_sz; /* len of domain strings z_domain list */ +} zfs_fuid_info_t; + +#ifdef _KERNEL +struct znode; +extern void zfs_fuid_map_id(zfsvfs_t *, uint64_t, zfs_fuid_type_t, uid_t *); +extern void zfs_fuid_destroy(zfsvfs_t *); +extern uint64_t zfs_fuid_create_cred(zfsvfs_t *, uint64_t, zfs_fuid_type_t, + dmu_tx_t *, cred_t *, zfs_fuid_info_t **); +extern uint64_t zfs_fuid_create(zfsvfs_t *, uint64_t, zfs_fuid_type_t, + dmu_tx_t *, zfs_fuid_info_t **); +extern void zfs_fuid_queue_map_id(zfsvfs_t *zfsvfs, zfs_fuid_hdl_t *, + uint64_t, zfs_fuid_type_t, uid_t *); +extern void zfs_fuid_map_ids(struct znode *zp, uid_t *uid, uid_t *gid); +extern void zfs_fuid_get_mappings(zfs_fuid_hdl_t *); +extern char *zfs_fuid_find_by_idx(zfsvfs_t *, uint64_t); +int zfs_fuid_find_by_domain(zfsvfs_t *, const char *, char **, dmu_tx_t *); +extern zfs_fuid_info_t *zfs_fuid_info_alloc(void); +extern void zfs_fuid_info_free(); +extern boolean_t zfs_groupmember(zfsvfs_t *, uint64_t, cred_t *); + +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_FS_ZFS_FUID_H */ diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_i18n.h b/usr/src/uts/common/fs/zfs/sys/zfs_i18n.h new file mode 100644 index 000000000000..96fe54a2304f --- /dev/null +++ b/usr/src/uts/common/fs/zfs/sys/zfs_i18n.h @@ -0,0 +1,71 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_ZFS_I18N_H +#define _SYS_ZFS_I18N_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * z_case behaviors + * The first two describe the extent of case insensitivity. + * The third describes matching behavior when mixed sensitivity + * is allowed. + */ +#define ZFS_CI_ONLY 0x01 /* all lookups case-insensitive */ +#define ZFS_CI_MIXD 0x02 /* some lookups case-insensitive */ + +/* + * ZFS_UTF8_ONLY + * If set, the file system should reject non-utf8 characters in names. + */ +#define ZFS_UTF8_ONLY 0x04 + +enum zfs_case { + ZFS_CASE_SENSITIVE, + ZFS_CASE_INSENSITIVE, + ZFS_CASE_MIXED +}; + +enum zfs_normal { + ZFS_NORMALIZE_NONE, + ZFS_NORMALIZE_D, + ZFS_NORMALIZE_KC, + ZFS_NORMALIZE_C, + ZFS_NORMALIZE_KD +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_ZFS_I18N_H */ diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h index e1bb9f0514a9..93c8d76bc0b3 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h @@ -33,6 +33,10 @@ #include #include +#ifdef _KERNEL +#include +#endif /* _KERNEL */ + #ifdef __cplusplus extern "C" { #endif @@ -152,6 +156,11 @@ typedef struct zfs_cmd { #ifdef _KERNEL +typedef struct zfs_creat { + int zct_norm; + nvlist_t *zct_props; +} zfs_creat_t; + extern dev_info_t *zfs_dip; extern int zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr); diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h b/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h index ea55a86b9e6b..b21dadaa153d 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h @@ -49,9 +49,17 @@ struct zfsvfs { uint64_t z_unlinkedobj; /* id of unlinked zapobj */ uint64_t z_max_blksz; /* maximum block size for files */ uint64_t z_assign; /* TXG_NOWAIT or set by zil_replay() */ + uint64_t z_fuid_obj; /* fuid table object number */ + avl_tree_t z_fuid_idx; /* fuid tree keyed by index */ + avl_tree_t z_fuid_domain; /* fuid tree keyed by domain */ + krwlock_t z_fuid_lock; /* fuid lock */ + boolean_t z_fuid_loaded; /* fuid tables are loaded */ + struct zfs_fuid_info *z_fuid_replay; /* fuid info for replay */ zilog_t *z_log; /* intent log pointer */ uint_t z_acl_mode; /* acl chmod/mode behavior */ uint_t z_acl_inherit; /* acl inheritance behavior */ + uint_t z_case; /* case-insensitive behavior */ + int z_norm; /* normalization flags */ boolean_t z_atime; /* enable atimes mount option */ boolean_t z_unmounted; /* unmounted */ rrwlock_t z_teardown_lock; @@ -61,6 +69,8 @@ struct zfsvfs { vnode_t *z_ctldir; /* .zfs directory pointer */ boolean_t z_show_ctldir; /* expose .zfs in the root dir */ boolean_t z_issnap; /* true if this is a snapshot */ + boolean_t z_vscan; /* virus scan on/off */ + boolean_t z_use_fuids; /* version allows fuids */ uint64_t z_version; #define ZFS_OBJ_MTX_SZ 64 kmutex_t z_hold_mtx[ZFS_OBJ_MTX_SZ]; /* znode hold locks */ diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_znode.h b/usr/src/uts/common/fs/zfs/sys/zfs_znode.h index 8b4ee4621841..b9e0c952900d 100644 --- a/usr/src/uts/common/fs/zfs/sys/zfs_znode.h +++ b/usr/src/uts/common/fs/zfs/sys/zfs_znode.h @@ -31,6 +31,7 @@ #ifdef _KERNEL #include #include +#include #include #include #include @@ -43,22 +44,63 @@ extern "C" { #endif +/* + * Additional file level attributes, that are stored + * in the upper half of zp_flags + */ +#define ZFS_READONLY 0x0000000100000000 +#define ZFS_HIDDEN 0x0000000200000000 +#define ZFS_SYSTEM 0x0000000400000000 +#define ZFS_ARCHIVE 0x0000000800000000 +#define ZFS_IMMUTABLE 0x0000001000000000 +#define ZFS_NOUNLINK 0x0000002000000000 +#define ZFS_APPENDONLY 0x0000004000000000 +#define ZFS_NODUMP 0x0000008000000000 +#define ZFS_OPAQUE 0x0000010000000000 +#define ZFS_AV_QUARANTINED 0x0000020000000000 +#define ZFS_AV_MODIFIED 0x0000040000000000 + +#define ZFS_ATTR_SET(zp, attr, value) \ +{ \ + if (value) \ + zp->z_phys->zp_flags |= attr; \ + else \ + zp->z_phys->zp_flags &= ~attr; \ +} + /* * Define special zfs pflags */ -#define ZFS_XATTR 0x1 /* is an extended attribute */ -#define ZFS_INHERIT_ACE 0x2 /* ace has inheritable ACEs */ -#define ZFS_ACL_TRIVIAL 0x4 /* files ACL is trivial */ +#define ZFS_XATTR 0x1 /* is an extended attribute */ +#define ZFS_INHERIT_ACE 0x2 /* ace has inheritable ACEs */ +#define ZFS_ACL_TRIVIAL 0x4 /* files ACL is trivial */ +#define ZFS_ACL_OBJ_ACE 0x8 /* ACL has CMPLX Object ACE */ +#define ZFS_ACL_PROTECTED 0x10 /* ACL protected */ +#define ZFS_ACL_DEFAULTED 0x20 /* ACL should be defaulted */ +#define ZFS_ACL_AUTO_INHERIT 0x40 /* ACL should be inherited */ +#define ZFS_BONUS_SCANSTAMP 0x80 /* Scanstamp in bonus area */ + +/* + * Is ID ephemeral? + */ +#define IS_EPHEMERAL(x) (x > MAXUID) + +/* + * Should we use FUIDs? + */ +#define USE_FUIDS(version, os) (version >= ZPL_VERSION_FUID &&\ + spa_version(dmu_objset_spa(os)) >= SPA_VERSION_FUID) #define MASTER_NODE_OBJ 1 /* - * special attributes for master node. + * Special attributes for master node. */ #define ZFS_FSID "FSID" #define ZFS_UNLINKED_SET "DELETE_QUEUE" #define ZFS_ROOT_OBJ "ROOT" #define ZPL_VERSION_STR "VERSION" +#define ZFS_FUID_TABLES "FUID" #define ZFS_MAX_BLOCKSIZE (SPA_MAXBLOCKSIZE) @@ -107,8 +149,9 @@ typedef struct znode_phys { uint64_t zp_flags; /* 120 - persistent flags */ uint64_t zp_uid; /* 128 - file owner */ uint64_t zp_gid; /* 136 - owning group */ - uint64_t zp_pad[4]; /* 144 - future */ - zfs_znode_acl_t zp_acl; /* 176 - 263 ACL */ + uint64_t zp_zap; /* 144 - extra attributes */ + uint64_t zp_pad[3]; /* 152 - future */ + zfs_acl_phys_t zp_acl; /* 176 - 263 ACL */ /* * Data may pad out any remaining bytes in the znode buffer, eg: * @@ -116,7 +159,9 @@ typedef struct znode_phys { * |<-- dnode (192) --->|<----------- "bonus" buffer (320) ---------->| * |<---- znode (264) ---->|<---- data (56) ---->| * - * At present, we only use this space to store symbolic links. + * At present, we use this space for the following: + * - symbolic links + * - 32-byte anti-virus scanstamp (regular files only) */ } znode_phys_t; @@ -253,7 +298,8 @@ typedef struct znode { extern int zfs_init_fs(zfsvfs_t *, znode_t **, cred_t *); extern void zfs_set_dataprop(objset_t *); -extern void zfs_create_fs(objset_t *os, cred_t *cr, uint64_t, dmu_tx_t *tx); +extern void zfs_create_fs(objset_t *os, cred_t *cr, uint64_t, int, + dmu_tx_t *tx); extern void zfs_time_stamper(znode_t *, uint_t, dmu_tx_t *); extern void zfs_time_stamper_locked(znode_t *, uint_t, dmu_tx_t *); extern void zfs_grow_blocksize(znode_t *, uint64_t, dmu_tx_t *); @@ -271,25 +317,31 @@ extern int zfs_sync(vfs_t *vfsp, short flag, cred_t *cr); extern dev_t zfs_cmpldev(uint64_t); extern int zfs_get_version(objset_t *os, uint64_t *version); extern int zfs_set_version(const char *name, uint64_t newvers); - -extern void zfs_log_create(zilog_t *zilog, dmu_tx_t *tx, int txtype, - znode_t *dzp, znode_t *zp, char *name); -extern void zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, int txtype, +extern int zfs_get_stats(objset_t *os, nvlist_t *nv); + +extern void zfs_log_create(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, + znode_t *dzp, znode_t *zp, char *name, vsecattr_t *, zfs_fuid_info_t *, + vattr_t *vap); +extern int zfs_log_create_txtype(zil_create_t, vsecattr_t *vsecp, + vattr_t *vap); +extern void zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, znode_t *dzp, char *name); -extern void zfs_log_link(zilog_t *zilog, dmu_tx_t *tx, int txtype, +extern void zfs_log_link(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, znode_t *dzp, znode_t *zp, char *name); -extern void zfs_log_symlink(zilog_t *zilog, dmu_tx_t *tx, int txtype, +extern void zfs_log_symlink(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, znode_t *dzp, znode_t *zp, char *name, char *link); -extern void zfs_log_rename(zilog_t *zilog, dmu_tx_t *tx, int txtype, +extern void zfs_log_rename(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, znode_t *sdzp, char *sname, znode_t *tdzp, char *dname, znode_t *szp); extern void zfs_log_write(zilog_t *zilog, dmu_tx_t *tx, int txtype, znode_t *zp, offset_t off, ssize_t len, int ioflag); extern void zfs_log_truncate(zilog_t *zilog, dmu_tx_t *tx, int txtype, znode_t *zp, uint64_t off, uint64_t len); extern void zfs_log_setattr(zilog_t *zilog, dmu_tx_t *tx, int txtype, - znode_t *zp, vattr_t *vap, uint_t mask_applied); -extern void zfs_log_acl(zilog_t *zilog, dmu_tx_t *tx, int txtype, - znode_t *zp, int aclcnt, ace_t *z_ace); + znode_t *zp, vattr_t *vap, uint_t mask_applied, zfs_fuid_info_t *fuidp); +extern void zfs_log_acl(zilog_t *zilog, dmu_tx_t *tx, znode_t *zp, + vsecattr_t *vsecp, zfs_fuid_info_t *fuidp); +extern void zfs_xvattr_set(znode_t *zp, xvattr_t *xvap); +extern void zfs_upgrade(zfsvfs_t *zfsvfs, dmu_tx_t *tx); extern zil_get_data_t zfs_get_data; extern zil_replay_func_t *zfs_replay_vector[TX_MAX_TYPE]; diff --git a/usr/src/uts/common/fs/zfs/sys/zil.h b/usr/src/uts/common/fs/zfs/sys/zil.h index 55de6b6f2711..b5b665c26cff 100644 --- a/usr/src/uts/common/fs/zfs/sys/zil.h +++ b/usr/src/uts/common/fs/zfs/sys/zil.h @@ -88,22 +88,53 @@ typedef struct zil_trailer { #define ZIL_ZC_OBJSET 2 #define ZIL_ZC_SEQ 3 +typedef enum zil_create { + Z_FILE, + Z_DIR, + Z_XATTRDIR, +} zil_create_t; + +/* + * size of xvattr log section. + * its composed of lr_attr_t + xvattr bitmap + 2 64 bit timestamps + * for create time and a single 64 bit integer for all of the attributes, + * and 4 64 bit integers (32 bytes) for the scanstamp. + * + */ + +#define ZIL_XVAT_SIZE(mapsize) \ + sizeof (lr_attr_t) + (sizeof (uint32_t) * (mapsize - 1)) + \ + (sizeof (uint64_t) * 7) /* * Intent log transaction types and record structures */ -#define TX_CREATE 1 /* Create file */ -#define TX_MKDIR 2 /* Make directory */ -#define TX_MKXATTR 3 /* Make XATTR directory */ -#define TX_SYMLINK 4 /* Create symbolic link to a file */ -#define TX_REMOVE 5 /* Remove file */ -#define TX_RMDIR 6 /* Remove directory */ -#define TX_LINK 7 /* Create hard link to a file */ -#define TX_RENAME 8 /* Rename a file */ -#define TX_WRITE 9 /* File write */ -#define TX_TRUNCATE 10 /* Truncate a file */ -#define TX_SETATTR 11 /* Set file attributes */ -#define TX_ACL 12 /* Set acl */ -#define TX_MAX_TYPE 13 /* Max transaction type */ +#define TX_CREATE 1 /* Create file */ +#define TX_MKDIR 2 /* Make directory */ +#define TX_MKXATTR 3 /* Make XATTR directory */ +#define TX_SYMLINK 4 /* Create symbolic link to a file */ +#define TX_REMOVE 5 /* Remove file */ +#define TX_RMDIR 6 /* Remove directory */ +#define TX_LINK 7 /* Create hard link to a file */ +#define TX_RENAME 8 /* Rename a file */ +#define TX_WRITE 9 /* File write */ +#define TX_TRUNCATE 10 /* Truncate a file */ +#define TX_SETATTR 11 /* Set file attributes */ +#define TX_ACL_V0 12 /* Set old formatted ACL */ +#define TX_ACL 13 /* Set ACL */ +#define TX_CREATE_ACL 14 /* create with ACL */ +#define TX_CREATE_ATTR 15 /* create + attrs */ +#define TX_CREATE_ACL_ATTR 16 /* create with ACL + attrs */ +#define TX_MKDIR_ACL 17 /* mkdir with ACL */ +#define TX_MKDIR_ATTR 18 /* mkdir with attr */ +#define TX_MKDIR_ACL_ATTR 19 /* mkdir with ACL + attrs */ +#define TX_MAX_TYPE 20 /* Max transaction type */ + +/* + * The transactions for mkdir, symlink, remove, rmdir, link, and rename + * may have the following bit set, indicating the original request + * specified case-insensitive handling of names. + */ +#define TX_CI ((uint64_t)0x1 << 63) /* case-insensitive behavior requested */ /* * Format of log records. @@ -124,6 +155,23 @@ typedef struct { /* common log record header */ uint64_t lrc_seq; /* see comment above */ } lr_t; +/* + * Handle option extended vattr attributes. + * + * Whenever new attributes are added the version number + * will need to be updated as will code in + * zfs_log.c and zfs_replay.c + */ +typedef struct { + uint32_t lr_attr_masksize; /* number of elements in array */ + uint32_t lr_attr_bitmap; /* First entry of array */ + /* remainder of array and any additional fields */ +} lr_attr_t; + +/* + * log record for creates without optional ACL. + * This log record does support optional xvattr_t attributes. + */ typedef struct { lr_t lr_common; /* common portion of log record */ uint64_t lr_doid; /* object id of directory */ @@ -136,8 +184,42 @@ typedef struct { uint64_t lr_rdev; /* rdev of object to create */ /* name of object to create follows this */ /* for symlinks, link content follows name */ + /* for creates with xvattr data, the name follows the xvattr info */ } lr_create_t; +/* + * FUID ACL record will be an array of ACEs from the original ACL. + * If this array includes ephemeral IDs, the record will also include + * an array of log-specific FUIDs to replace the ephemeral IDs. + * Only one copy of each unique domain will be present, so the log-specific + * FUIDs will use an index into a compressed domain table. On replay this + * information will be used to construct real FUIDs (and bypass idmap, + * since it may not be available). + */ + +/* + * Log record for creates with optional ACL + * This log record is also used for recording any FUID + * information needed for replaying the create. If the + * file doesn't have any actual ACEs then the lr_aclcnt + * would be zero. + */ +typedef struct { + lr_create_t lr_create; /* common create portion */ + uint64_t lr_aclcnt; /* number of ACEs in ACL */ + uint64_t lr_domcnt; /* number of unique domains */ + uint64_t lr_fuidcnt; /* number of real fuids */ + uint64_t lr_acl_bytes; /* number of bytes in ACL */ + uint64_t lr_acl_flags; /* ACL flags */ + /* lr_acl_bytes number of variable sized ace's follows */ + /* if create is also setting xvattr's, then acl data follows xvattr */ + /* if ACE FUIDs are needed then they will follow the xvattr_t */ + /* Following the FUIDs will be the domain table information. */ + /* The FUIDs for the owner and group will be in the lr_create */ + /* portion of the record. */ + /* name follows ACL data */ +} lr_acl_create_t; + typedef struct { lr_t lr_common; /* common portion of log record */ uint64_t lr_doid; /* obj id of directory */ @@ -185,6 +267,7 @@ typedef struct { uint64_t lr_size; /* size to set */ uint64_t lr_atime[2]; /* access time */ uint64_t lr_mtime[2]; /* modification time */ + /* optional attribute lr_attr_t may be here */ } lr_setattr_t; typedef struct { @@ -192,6 +275,17 @@ typedef struct { uint64_t lr_foid; /* obj id of file */ uint64_t lr_aclcnt; /* number of acl entries */ /* lr_aclcnt number of ace_t entries follow this */ +} lr_acl_v0_t; + +typedef struct { + lr_t lr_common; /* common portion of log record */ + uint64_t lr_foid; /* obj id of file */ + uint64_t lr_aclcnt; /* number of ACEs in ACL */ + uint64_t lr_domcnt; /* number of unique domains */ + uint64_t lr_fuidcnt; /* number of real fuids */ + uint64_t lr_acl_bytes; /* number of bytes in ACL */ + uint64_t lr_acl_flags; /* ACL flags */ + /* lr_acl_bytes number of variable sized ace's follows */ } lr_acl_t; /* @@ -253,7 +347,7 @@ extern void zil_replay(objset_t *os, void *arg, uint64_t *txgp, extern void zil_destroy(zilog_t *zilog, boolean_t keep_first); extern void zil_rollback_destroy(zilog_t *zilog, dmu_tx_t *tx); -extern itx_t *zil_itx_create(int txtype, size_t lrsize); +extern itx_t *zil_itx_create(uint64_t txtype, size_t lrsize); extern uint64_t zil_itx_assign(zilog_t *zilog, itx_t *itx, dmu_tx_t *tx); extern void zil_commit(zilog_t *zilog, uint64_t seq, uint64_t oid); diff --git a/usr/src/uts/common/fs/zfs/vdev_file.c b/usr/src/uts/common/fs/zfs/vdev_file.c index 6f099b662902..b212161c6482 100644 --- a/usr/src/uts/common/fs/zfs/vdev_file.c +++ b/usr/src/uts/common/fs/zfs/vdev_file.c @@ -61,7 +61,7 @@ vdev_file_open_common(vdev_t *vd) */ ASSERT(vd->vdev_path != NULL && vd->vdev_path[0] == '/'); error = vn_openat(vd->vdev_path + 1, UIO_SYSSPACE, - spa_mode | FOFFMAX, 0, &vp, 0, 0, rootdir); + spa_mode | FOFFMAX, 0, &vp, 0, 0, rootdir, -1); if (error) { vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; @@ -99,7 +99,7 @@ vdev_file_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift) * Determine the physical size of the file. */ vattr.va_mask = AT_SIZE; - error = VOP_GETATTR(vf->vf_vnode, &vattr, 0, kcred); + error = VOP_GETATTR(vf->vf_vnode, &vattr, 0, kcred, NULL); if (error) { vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; return (error); @@ -120,8 +120,8 @@ vdev_file_close(vdev_t *vd) return; if (vf->vf_vnode != NULL) { - (void) VOP_PUTPAGE(vf->vf_vnode, 0, 0, B_INVAL, kcred); - (void) VOP_CLOSE(vf->vf_vnode, spa_mode, 1, 0, kcred); + (void) VOP_PUTPAGE(vf->vf_vnode, 0, 0, B_INVAL, kcred, NULL); + (void) VOP_CLOSE(vf->vf_vnode, spa_mode, 1, 0, kcred, NULL); VN_RELE(vf->vf_vnode); } @@ -233,7 +233,7 @@ vdev_file_io_start(zio_t *zio) switch (zio->io_cmd) { case DKIOCFLUSHWRITECACHE: zio->io_error = VOP_FSYNC(vf->vf_vnode, FSYNC | FDSYNC, - kcred); + kcred, NULL); dprintf("fsync(%s) = %d\n", vdev_description(vd), zio->io_error); break; diff --git a/usr/src/uts/common/fs/zfs/zap.c b/usr/src/uts/common/fs/zfs/zap.c index 7dfe44baba07..db0162b0eee5 100644 --- a/usr/src/uts/common/fs/zfs/zap.c +++ b/usr/src/uts/common/fs/zfs/zap.c @@ -102,6 +102,7 @@ fzap_upgrade(zap_t *zap, dmu_tx_t *tx) zp->zap_num_leafs = 1; zp->zap_num_entries = 0; zp->zap_salt = zap->zap_salt; + zp->zap_normflags = zap->zap_normflags; /* block 1 will be the first leaf */ for (i = 0; i < (1<zap_ptrtbl.zt_shift); i++) @@ -118,7 +119,7 @@ fzap_upgrade(zap_t *zap, dmu_tx_t *tx) l->l_dbuf = db; l->l_phys = db->db_data; - zap_leaf_init(l); + zap_leaf_init(l, spa_version(dmu_objset_spa(zap->zap_objset))); kmem_free(l, sizeof (zap_leaf_t)); dmu_buf_rele(db, FTAG); @@ -398,7 +399,7 @@ zap_create_leaf(zap_t *zap, dmu_tx_t *tx) ASSERT(winner == NULL); dmu_buf_will_dirty(l->l_dbuf, tx); - zap_leaf_init(l); + zap_leaf_init(l, spa_version(dmu_objset_spa(zap->zap_objset))); zap->zap_f.zap_phys->zap_num_leafs++; @@ -642,7 +643,7 @@ zap_expand_leaf(zap_t *zap, zap_leaf_t *l, uint64_t hash, dmu_tx_t *tx, } nl = zap_create_leaf(zap, tx); - zap_leaf_split(l, nl); + zap_leaf_split(l, nl, spa_version(dmu_objset_spa(zap->zap_objset))); /* set sibling pointers */ for (i = 0; i < (1ULL<zn_name_orij, integer_size, num_integers); if (err != 0) return (err); - hash = zap_hash(zap, name); - err = zap_deref_leaf(zap, hash, NULL, RW_READER, &l); + err = zap_deref_leaf(zn->zn_zap, zn->zn_hash, NULL, RW_READER, &l); if (err != 0) return (err); - err = zap_leaf_lookup(l, name, hash, &zeh); - if (err == 0) + err = zap_leaf_lookup(l, zn, &zeh); + if (err == 0) { err = zap_entry_read(&zeh, integer_size, num_integers, buf); + (void) zap_entry_read_name(&zeh, rn_len, realname); + if (ncp) { + *ncp = zap_entry_normalization_conflict(&zeh, + zn, NULL, zn->zn_zap); + } + } zap_put_leaf(l); return (err); } int -fzap_add_cd(zap_t *zap, const char *name, +fzap_add_cd(zap_name_t *zn, uint64_t integer_size, uint64_t num_integers, const void *val, uint32_t cd, dmu_tx_t *tx) { zap_leaf_t *l; - uint64_t hash; int err; zap_entry_handle_t zeh; + zap_t *zap = zn->zn_zap; ASSERT(RW_LOCK_HELD(&zap->zap_rwlock)); ASSERT(!zap->zap_ismicro); - ASSERT(fzap_checksize(name, integer_size, num_integers) == 0); + ASSERT(fzap_checksize(zn->zn_name_orij, + integer_size, num_integers) == 0); - hash = zap_hash(zap, name); - err = zap_deref_leaf(zap, hash, tx, RW_WRITER, &l); + err = zap_deref_leaf(zap, zn->zn_hash, tx, RW_WRITER, &l); if (err != 0) return (err); retry: - err = zap_leaf_lookup(l, name, hash, &zeh); + err = zap_leaf_lookup(l, zn, &zeh); if (err == 0) { err = EEXIST; goto out; @@ -774,13 +780,13 @@ fzap_add_cd(zap_t *zap, const char *name, if (err != ENOENT) goto out; - err = zap_entry_create(l, name, hash, cd, + err = zap_entry_create(l, zn->zn_name_orij, zn->zn_hash, cd, integer_size, num_integers, val, &zeh); if (err == 0) { zap_increment_num_entries(zap, 1, tx); } else if (err == EAGAIN) { - err = zap_expand_leaf(zap, l, hash, tx, &l); + err = zap_expand_leaf(zap, l, zn->zn_hash, tx, &l); if (err == 0) goto retry; } @@ -791,46 +797,43 @@ fzap_add_cd(zap_t *zap, const char *name, } int -fzap_add(zap_t *zap, const char *name, +fzap_add(zap_name_t *zn, uint64_t integer_size, uint64_t num_integers, const void *val, dmu_tx_t *tx) { - int err = fzap_checksize(name, integer_size, num_integers); + int err = fzap_checksize(zn->zn_name_orij, integer_size, num_integers); if (err != 0) return (err); - return (fzap_add_cd(zap, name, integer_size, num_integers, + return (fzap_add_cd(zn, integer_size, num_integers, val, ZAP_MAXCD, tx)); } int -fzap_update(zap_t *zap, const char *name, +fzap_update(zap_name_t *zn, int integer_size, uint64_t num_integers, const void *val, dmu_tx_t *tx) { zap_leaf_t *l; - uint64_t hash; int err, create; zap_entry_handle_t zeh; + zap_t *zap = zn->zn_zap; ASSERT(RW_LOCK_HELD(&zap->zap_rwlock)); - err = fzap_checksize(name, integer_size, num_integers); + err = fzap_checksize(zn->zn_name_orij, integer_size, num_integers); if (err != 0) return (err); - hash = zap_hash(zap, name); - err = zap_deref_leaf(zap, hash, tx, RW_WRITER, &l); + err = zap_deref_leaf(zap, zn->zn_hash, tx, RW_WRITER, &l); if (err != 0) return (err); retry: - err = zap_leaf_lookup(l, name, hash, &zeh); + err = zap_leaf_lookup(l, zn, &zeh); create = (err == ENOENT); ASSERT(err == 0 || err == ENOENT); - /* XXX If this leaf is chained, split it if we can. */ - if (create) { - err = zap_entry_create(l, name, hash, ZAP_MAXCD, - integer_size, num_integers, val, &zeh); + err = zap_entry_create(l, zn->zn_name_orij, zn->zn_hash, + ZAP_MAXCD, integer_size, num_integers, val, &zeh); if (err == 0) zap_increment_num_entries(zap, 1, tx); } else { @@ -838,7 +841,7 @@ fzap_update(zap_t *zap, const char *name, } if (err == EAGAIN) { - err = zap_expand_leaf(zap, l, hash, tx, &l); + err = zap_expand_leaf(zap, l, zn->zn_hash, tx, &l); if (err == 0) goto retry; } @@ -848,19 +851,17 @@ fzap_update(zap_t *zap, const char *name, } int -fzap_length(zap_t *zap, const char *name, +fzap_length(zap_name_t *zn, uint64_t *integer_size, uint64_t *num_integers) { zap_leaf_t *l; int err; - uint64_t hash; zap_entry_handle_t zeh; - hash = zap_hash(zap, name); - err = zap_deref_leaf(zap, hash, NULL, RW_READER, &l); + err = zap_deref_leaf(zn->zn_zap, zn->zn_hash, NULL, RW_READER, &l); if (err != 0) return (err); - err = zap_leaf_lookup(l, name, hash, &zeh); + err = zap_leaf_lookup(l, zn, &zeh); if (err != 0) goto out; @@ -874,25 +875,21 @@ fzap_length(zap_t *zap, const char *name, } int -fzap_remove(zap_t *zap, const char *name, dmu_tx_t *tx) +fzap_remove(zap_name_t *zn, dmu_tx_t *tx) { zap_leaf_t *l; - uint64_t hash; int err; zap_entry_handle_t zeh; - hash = zap_hash(zap, name); - err = zap_deref_leaf(zap, hash, tx, RW_WRITER, &l); + err = zap_deref_leaf(zn->zn_zap, zn->zn_hash, tx, RW_WRITER, &l); if (err != 0) return (err); - err = zap_leaf_lookup(l, name, hash, &zeh); + err = zap_leaf_lookup(l, zn, &zeh); if (err == 0) { zap_entry_remove(&zeh); - zap_increment_num_entries(zap, -1, tx); + zap_increment_num_entries(zn->zn_zap, -1, tx); } zap_put_leaf(l); - dprintf("fzap_remove: ds=%p obj=%llu name=%s err=%d\n", - zap->zap_objset, zap->zap_object, name, err); return (err); } @@ -986,6 +983,10 @@ fzap_cursor_retrieve(zap_t *zap, zap_cursor_t *zc, zap_attribute_t *za) err = zap_entry_read_name(&zeh, sizeof (za->za_name), za->za_name); ASSERT(err == 0); + + za->za_normalization_conflict = + zap_entry_normalization_conflict(&zeh, + NULL, za->za_name, zap); } rw_exit(&zc->zc_leaf->l_rwlock); return (err); diff --git a/usr/src/uts/common/fs/zfs/zap_leaf.c b/usr/src/uts/common/fs/zfs/zap_leaf.c index 5dff5145308a..d048bbc77c11 100644 --- a/usr/src/uts/common/fs/zfs/zap_leaf.c +++ b/usr/src/uts/common/fs/zfs/zap_leaf.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -38,6 +38,8 @@ #include #include +static uint16_t *zap_leaf_rehash_entry(zap_leaf_t *l, uint16_t entry); + #define CHAIN_END 0xffff /* end of the chunk chain */ /* half the (current) minimum block size */ @@ -150,7 +152,7 @@ zap_leaf_byteswap(zap_leaf_phys_t *buf, int size) } void -zap_leaf_init(zap_leaf_t *l) +zap_leaf_init(zap_leaf_t *l, int version) { int i; @@ -165,6 +167,8 @@ zap_leaf_init(zap_leaf_t *l) l->l_phys->l_hdr.lh_block_type = ZBT_LEAF; l->l_phys->l_hdr.lh_magic = ZAP_LEAF_MAGIC; l->l_phys->l_hdr.lh_nfree = ZAP_LEAF_NUMCHUNKS(l); + if (version >= SPA_VERSION_NORMALIZATION) + l->l_phys->l_hdr.lh_flags |= ZLF_ENTRIES_CDSORTED; } /* @@ -327,19 +331,30 @@ zap_leaf_array_read(zap_leaf_t *l, uint16_t chunk, /* * Only to be used on 8-bit arrays. * array_len is actual len in bytes (not encoded le_value_length). - * buf is null-terminated. + * namenorm is null-terminated. */ -static int -zap_leaf_array_equal(zap_leaf_t *l, int chunk, - int array_len, const char *buf) +static boolean_t +zap_leaf_array_match(zap_leaf_t *l, zap_name_t *zn, int chunk, int array_len) { int bseen = 0; + if (zn->zn_matchtype == MT_FIRST) { + char *thisname = kmem_alloc(array_len, KM_SLEEP); + boolean_t match; + + zap_leaf_array_read(l, chunk, 1, array_len, 1, + array_len, thisname); + match = zap_match(zn, thisname); + kmem_free(thisname, array_len); + return (match); + } + + /* Fast path for exact matching */ while (bseen < array_len) { struct zap_leaf_array *la = &ZAP_LEAF_CHUNK(l, chunk).l_array; int toread = MIN(array_len - bseen, ZAP_LEAF_ARRAY_BYTES); ASSERT3U(chunk, <, ZAP_LEAF_NUMCHUNKS(l)); - if (bcmp(la->la_array, buf + bseen, toread)) + if (bcmp(la->la_array, zn->zn_name_orij + bseen, toread)) break; chunk = la->la_next; bseen += toread; @@ -352,15 +367,15 @@ zap_leaf_array_equal(zap_leaf_t *l, int chunk, */ int -zap_leaf_lookup(zap_leaf_t *l, - const char *name, uint64_t h, zap_entry_handle_t *zeh) +zap_leaf_lookup(zap_leaf_t *l, zap_name_t *zn, zap_entry_handle_t *zeh) { uint16_t *chunkp; struct zap_leaf_entry *le; ASSERT3U(l->l_phys->l_hdr.lh_magic, ==, ZAP_LEAF_MAGIC); - for (chunkp = LEAF_HASH_ENTPTR(l, h); +again: + for (chunkp = LEAF_HASH_ENTPTR(l, zn->zn_hash); *chunkp != CHAIN_END; chunkp = &le->le_next) { uint16_t chunk = *chunkp; le = ZAP_LEAF_ENTRY(l, chunk); @@ -368,11 +383,18 @@ zap_leaf_lookup(zap_leaf_t *l, ASSERT3U(chunk, <, ZAP_LEAF_NUMCHUNKS(l)); ASSERT3U(le->le_type, ==, ZAP_CHUNK_ENTRY); - if (le->le_hash != h) + if (le->le_hash != zn->zn_hash) continue; - if (zap_leaf_array_equal(l, le->le_name_chunk, - le->le_name_length, name)) { + /* + * NB: the entry chain is always sorted by cd on + * normalized zap objects, so this will find the + * lowest-cd match for MT_FIRST. + */ + ASSERT(zn->zn_matchtype == MT_EXACT || + (l->l_phys->l_hdr.lh_flags & ZLF_ENTRIES_CDSORTED)); + if (zap_leaf_array_match(l, zn, le->le_name_chunk, + le->le_name_length)) { zeh->zeh_num_integers = le->le_value_length; zeh->zeh_integer_size = le->le_int_size; zeh->zeh_cd = le->le_cd; @@ -383,6 +405,15 @@ zap_leaf_lookup(zap_leaf_t *l, } } + /* + * NB: we could of course do this in one pass, but that would be + * a pain. We'll see if MT_BEST is even used much. + */ + if (zn->zn_matchtype == MT_BEST) { + zn->zn_matchtype = MT_FIRST; + goto again; + } + return (ENOENT); } @@ -539,22 +570,41 @@ zap_entry_create(zap_leaf_t *l, const char *name, uint64_t h, uint32_t cd, return (E2BIG); if (cd == ZAP_MAXCD) { - for (cd = 0; cd < ZAP_MAXCD; cd++) { + /* find the lowest unused cd */ + if (l->l_phys->l_hdr.lh_flags & ZLF_ENTRIES_CDSORTED) { + cd = 0; + for (chunk = *LEAF_HASH_ENTPTR(l, h); chunk != CHAIN_END; chunk = le->le_next) { le = ZAP_LEAF_ENTRY(l, chunk); - if (le->le_hash == h && - le->le_cd == cd) { + if (le->le_cd > cd) break; + if (le->le_hash == h) { + ASSERT3U(cd, ==, le->le_cd); + cd++; } } - /* If this cd is not in use, we are good. */ - if (chunk == CHAIN_END) - break; + } else { + /* old unsorted format; do it the O(n^2) way */ + for (cd = 0; cd < ZAP_MAXCD; cd++) { + for (chunk = *LEAF_HASH_ENTPTR(l, h); + chunk != CHAIN_END; chunk = le->le_next) { + le = ZAP_LEAF_ENTRY(l, chunk); + if (le->le_hash == h && + le->le_cd == cd) { + break; + } + } + /* If this cd is not in use, we are good. */ + if (chunk == CHAIN_END) + break; + } } - /* If we tried all the cd's, we lose. */ - if (cd == ZAP_MAXCD) - return (ENOSPC); + /* + * we would run out of space in a block before we could + * have ZAP_MAXCD entries + */ + ASSERT3U(cd, <, ZAP_MAXCD); } if (l->l_phys->l_hdr.lh_nfree < numchunks) @@ -574,9 +624,8 @@ zap_entry_create(zap_leaf_t *l, const char *name, uint64_t h, uint32_t cd, le->le_cd = cd; /* link it into the hash chain */ - chunkp = LEAF_HASH_ENTPTR(l, h); - le->le_next = *chunkp; - *chunkp = chunk; + /* XXX if we did the search above, we could just use that */ + chunkp = zap_leaf_rehash_entry(l, chunk); l->l_phys->l_hdr.lh_nentries++; @@ -590,17 +639,77 @@ zap_entry_create(zap_leaf_t *l, const char *name, uint64_t h, uint32_t cd, return (0); } +/* + * Determine if there is another entry with the same normalized form. + * For performance purposes, either zn or name must be provided (the + * other can be NULL). Note, there usually won't be any hash + * conflicts, in which case we don't need the concatenated/normalized + * form of the name. But all callers have one of these on hand anyway, + * so might as well take advantage. A cleaner but slower interface + * would accept neither argument, and compute the normalized name as + * needed (using zap_name_alloc(zap_entry_read_name(zeh))). + */ +boolean_t +zap_entry_normalization_conflict(zap_entry_handle_t *zeh, zap_name_t *zn, + const char *name, zap_t *zap) +{ + uint64_t chunk; + struct zap_leaf_entry *le; + boolean_t allocdzn = B_FALSE; + + if (zap->zap_normflags == 0) + return (B_FALSE); + + for (chunk = *LEAF_HASH_ENTPTR(zeh->zeh_leaf, zeh->zeh_hash); + chunk != CHAIN_END; chunk = le->le_next) { + le = ZAP_LEAF_ENTRY(zeh->zeh_leaf, chunk); + if (le->le_hash != zeh->zeh_hash) + continue; + if (le->le_cd == zeh->zeh_cd) + continue; + + if (zn == NULL) { + zn = zap_name_alloc(zap, name, MT_FIRST); + allocdzn = B_TRUE; + } + if (zap_leaf_array_match(zeh->zeh_leaf, zn, + le->le_name_chunk, le->le_name_length)) { + if (allocdzn) + zap_name_free(zn); + return (B_TRUE); + } + } + if (allocdzn) + zap_name_free(zn); + return (B_FALSE); +} + /* * Routines for transferring entries between leafs. */ -static void +static uint16_t * zap_leaf_rehash_entry(zap_leaf_t *l, uint16_t entry) { struct zap_leaf_entry *le = ZAP_LEAF_ENTRY(l, entry); - uint16_t *ptr = LEAF_HASH_ENTPTR(l, le->le_hash); - le->le_next = *ptr; - *ptr = entry; + struct zap_leaf_entry *le2; + uint16_t *chunkp; + + /* + * keep the entry chain sorted by cd + * NB: this will not cause problems for unsorted leafs, though + * it is unnecessary there. + */ + for (chunkp = LEAF_HASH_ENTPTR(l, le->le_hash); + *chunkp != CHAIN_END; chunkp = &le2->le_next) { + le2 = ZAP_LEAF_ENTRY(l, *chunkp); + if (le2->le_cd > le->le_cd) + break; + } + + le->le_next = *chunkp; + *chunkp = entry; + return (chunkp); } static uint16_t @@ -644,7 +753,7 @@ zap_leaf_transfer_entry(zap_leaf_t *l, int entry, zap_leaf_t *nl) nle = ZAP_LEAF_ENTRY(nl, chunk); *nle = *le; /* structure assignment */ - zap_leaf_rehash_entry(nl, chunk); + (void) zap_leaf_rehash_entry(nl, chunk); nle->le_name_chunk = zap_leaf_transfer_array(l, le->le_name_chunk, nl); nle->le_value_chunk = @@ -660,7 +769,7 @@ zap_leaf_transfer_entry(zap_leaf_t *l, int entry, zap_leaf_t *nl) * Transfer the entries whose hash prefix ends in 1 to the new leaf. */ void -zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl) +zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl, int version) { int i; int bit = 64 - 1 - l->l_phys->l_hdr.lh_prefix_len; @@ -674,6 +783,9 @@ zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl) /* break existing hash chains */ zap_memset(l->l_phys->l_hash, CHAIN_END, 2*ZAP_LEAF_HASH_NUMENTRIES(l)); + if (version >= SPA_VERSION_NORMALIZATION) + l->l_phys->l_hdr.lh_flags |= ZLF_ENTRIES_CDSORTED; + /* * Transfer entries whose hash bit 'bit' is set to nl; rehash * the remaining entries @@ -691,7 +803,7 @@ zap_leaf_split(zap_leaf_t *l, zap_leaf_t *nl) if (le->le_hash & (1ULL << bit)) zap_leaf_transfer_entry(l, i, nl); else - zap_leaf_rehash_entry(l, i); + (void) zap_leaf_rehash_entry(l, i); } } diff --git a/usr/src/uts/common/fs/zfs/zap_micro.c b/usr/src/uts/common/fs/zfs/zap_micro.c index 8fce0ba400fc..f185a7754fdf 100644 --- a/usr/src/uts/common/fs/zfs/zap_micro.c +++ b/usr/src/uts/common/fs/zfs/zap_micro.c @@ -33,11 +33,103 @@ #include #include #include - +#include static void mzap_upgrade(zap_t *zap, dmu_tx_t *tx); +static uint64_t +zap_hash(zap_t *zap, const char *normname) +{ + const uint8_t *cp; + uint8_t c; + uint64_t crc = zap->zap_salt; + + /* NB: name must already be normalized, if necessary */ + + ASSERT(crc != 0); + ASSERT(zfs_crc64_table[128] == ZFS_CRC64_POLY); + for (cp = (const uint8_t *)normname; (c = *cp) != '\0'; cp++) { + crc = (crc >> 8) ^ zfs_crc64_table[(crc ^ c) & 0xFF]; + } + + /* + * Only use 28 bits, since we need 4 bits in the cookie for the + * collision differentiator. We MUST use the high bits, since + * those are the ones that we first pay attention to when + * chosing the bucket. + */ + crc &= ~((1ULL << (64 - ZAP_HASHBITS)) - 1); + + return (crc); +} + +static int +zap_normalize(zap_t *zap, const char *name, char *namenorm) +{ + size_t inlen, outlen; + int err; + + inlen = strlen(name) + 1; + outlen = ZAP_MAXNAMELEN; + + err = 0; + (void) u8_textprep_str((char *)name, &inlen, namenorm, &outlen, + zap->zap_normflags | U8_TEXTPREP_IGNORE_NULL, U8_UNICODE_LATEST, + &err); + + return (err); +} + +boolean_t +zap_match(zap_name_t *zn, const char *matchname) +{ + if (zn->zn_matchtype == MT_FIRST) { + char norm[ZAP_MAXNAMELEN]; + + if (zap_normalize(zn->zn_zap, matchname, norm) != 0) + return (B_FALSE); + + return (strcmp(zn->zn_name_norm, norm) == 0); + } else { + /* MT_BEST or MT_EXACT */ + return (strcmp(zn->zn_name_orij, matchname) == 0); + } +} + +void +zap_name_free(zap_name_t *zn) +{ + kmem_free(zn, sizeof (zap_name_t)); +} + +/* XXX combine this with zap_lockdir()? */ +zap_name_t * +zap_name_alloc(zap_t *zap, const char *name, matchtype_t mt) +{ + zap_name_t *zn = kmem_alloc(sizeof (zap_name_t), KM_SLEEP); + + zn->zn_zap = zap; + zn->zn_name_orij = name; + zn->zn_matchtype = mt; + if (zap->zap_normflags) { + if (zap_normalize(zap, name, zn->zn_normbuf) != 0) { + zap_name_free(zn); + return (NULL); + } + zn->zn_name_norm = zn->zn_normbuf; + } else { + if (mt != MT_EXACT) { + zap_name_free(zn); + return (NULL); + } + zn->zn_name_norm = zn->zn_name_orij; + } + + zn->zn_hash = zap_hash(zap, zn->zn_name_norm); + return (zn); +} + static void mzap_byteswap(mzap_phys_t *buf, size_t size) { @@ -93,7 +185,6 @@ mze_insert(zap_t *zap, int chunkid, uint64_t hash, mzap_ent_phys_t *mzep) ASSERT(zap->zap_ismicro); ASSERT(RW_WRITE_HELD(&zap->zap_rwlock)); ASSERT(mzep->mze_cd < ZAP_MAXCD); - ASSERT3U(zap_hash(zap, mzep->mze_name), ==, hash); mze = kmem_alloc(sizeof (mzap_ent_t), KM_SLEEP); mze->mze_chunkid = chunkid; @@ -103,30 +194,34 @@ mze_insert(zap_t *zap, int chunkid, uint64_t hash, mzap_ent_phys_t *mzep) } static mzap_ent_t * -mze_find(zap_t *zap, const char *name, uint64_t hash) +mze_find(zap_name_t *zn) { mzap_ent_t mze_tofind; mzap_ent_t *mze; avl_index_t idx; - avl_tree_t *avl = &zap->zap_m.zap_avl; + avl_tree_t *avl = &zn->zn_zap->zap_m.zap_avl; - ASSERT(zap->zap_ismicro); - ASSERT(RW_LOCK_HELD(&zap->zap_rwlock)); - ASSERT3U(zap_hash(zap, name), ==, hash); + ASSERT(zn->zn_zap->zap_ismicro); + ASSERT(RW_LOCK_HELD(&zn->zn_zap->zap_rwlock)); - if (strlen(name) >= sizeof (mze_tofind.mze_phys.mze_name)) + if (strlen(zn->zn_name_norm) >= sizeof (mze_tofind.mze_phys.mze_name)) return (NULL); - mze_tofind.mze_hash = hash; + mze_tofind.mze_hash = zn->zn_hash; mze_tofind.mze_phys.mze_cd = 0; +again: mze = avl_find(avl, &mze_tofind, &idx); if (mze == NULL) mze = avl_nearest(avl, idx, AVL_AFTER); - for (; mze && mze->mze_hash == hash; mze = AVL_NEXT(avl, mze)) { - if (strcmp(name, mze->mze_phys.mze_name) == 0) + for (; mze && mze->mze_hash == zn->zn_hash; mze = AVL_NEXT(avl, mze)) { + if (zap_match(zn, mze->mze_phys.mze_name)) return (mze); } + if (zn->zn_matchtype == MT_BEST) { + zn->zn_matchtype = MT_FIRST; + goto again; + } return (NULL); } @@ -193,7 +288,7 @@ mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db) zap->zap_object = obj; zap->zap_dbuf = db; - if (((uint64_t *)db->db_data)[0] != ZBT_MICRO) { + if (*(uint64_t *)db->db_data != ZBT_MICRO) { mutex_init(&zap->zap_f.zap_num_entries_mtx, 0, 0, 0); zap->zap_f.zap_block_shift = highbit(db->db_size) - 1; } else { @@ -218,6 +313,7 @@ mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db) if (zap->zap_ismicro) { zap->zap_salt = zap->zap_m.zap_phys->mz_salt; + zap->zap_normflags = zap->zap_m.zap_phys->mz_normflags; zap->zap_m.zap_num_chunks = db->db_size / MZAP_ENT_LEN - 1; avl_create(&zap->zap_m.zap_avl, mze_compare, sizeof (mzap_ent_t), offsetof(mzap_ent_t, mze_node)); @@ -226,13 +322,18 @@ mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db) mzap_ent_phys_t *mze = &zap->zap_m.zap_phys->mz_chunk[i]; if (mze->mze_name[0]) { + zap_name_t *zn; + zap->zap_m.zap_num_entries++; - mze_insert(zap, i, - zap_hash(zap, mze->mze_name), mze); + zn = zap_name_alloc(zap, mze->mze_name, + MT_EXACT); + mze_insert(zap, i, zn->zn_hash, mze); + zap_name_free(zn); } } } else { zap->zap_salt = zap->zap_f.zap_phys->zap_salt; + zap->zap_normflags = zap->zap_f.zap_phys->zap_normflags; ASSERT3U(sizeof (struct zap_leaf_header), ==, 2*ZAP_LEAF_CHUNKSIZE); @@ -357,6 +458,7 @@ mzap_upgrade(zap_t *zap, dmu_tx_t *tx) dprintf("upgrading obj=%llu with %u chunks\n", zap->zap_object, nchunks); + /* XXX destroy the avl later, so we can use the stored hash value */ mze_destroy(zap); fzap_upgrade(zap, tx); @@ -364,48 +466,28 @@ mzap_upgrade(zap_t *zap, dmu_tx_t *tx) for (i = 0; i < nchunks; i++) { int err; mzap_ent_phys_t *mze = &mzp->mz_chunk[i]; + zap_name_t *zn; if (mze->mze_name[0] == 0) continue; dprintf("adding %s=%llu\n", mze->mze_name, mze->mze_value); - err = fzap_add_cd(zap, - mze->mze_name, 8, 1, &mze->mze_value, + zn = zap_name_alloc(zap, mze->mze_name, MT_EXACT); + err = fzap_add_cd(zn, 8, 1, &mze->mze_value, mze->mze_cd, tx); + zap_name_free(zn); ASSERT3U(err, ==, 0); } kmem_free(mzp, sz); } -uint64_t -zap_hash(zap_t *zap, const char *name) -{ - const uint8_t *cp; - uint8_t c; - uint64_t crc = zap->zap_salt; - - ASSERT(crc != 0); - ASSERT(zfs_crc64_table[128] == ZFS_CRC64_POLY); - for (cp = (const uint8_t *)name; (c = *cp) != '\0'; cp++) - crc = (crc >> 8) ^ zfs_crc64_table[(crc ^ c) & 0xFF]; - - /* - * Only use 28 bits, since we need 4 bits in the cookie for the - * collision differentiator. We MUST use the high bits, since - * those are the onces that we first pay attention to when - * chosing the bucket. - */ - crc &= ~((1ULL << (64 - ZAP_HASHBITS)) - 1); - - return (crc); -} - - static void -mzap_create_impl(objset_t *os, uint64_t obj, dmu_tx_t *tx) +mzap_create_impl(objset_t *os, uint64_t obj, int normflags, dmu_tx_t *tx) { dmu_buf_t *db; mzap_phys_t *zp; + ASSERT(normflags == 0 || + spa_version(dmu_objset_spa(os)) >= SPA_VERSION_NORMALIZATION); VERIFY(0 == dmu_buf_hold(os, obj, 0, FTAG, &db)); #ifdef ZFS_DEBUG @@ -420,30 +502,46 @@ mzap_create_impl(objset_t *os, uint64_t obj, dmu_tx_t *tx) zp = db->db_data; zp->mz_block_type = ZBT_MICRO; zp->mz_salt = ((uintptr_t)db ^ (uintptr_t)tx ^ (obj << 1)) | 1ULL; - ASSERT(zp->mz_salt != 0); + zp->mz_normflags = normflags; dmu_buf_rele(db, FTAG); } int zap_create_claim(objset_t *os, uint64_t obj, dmu_object_type_t ot, dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx) +{ + return (zap_create_claim_norm(os, obj, + 0, ot, bonustype, bonuslen, tx)); +} + +int +zap_create_claim_norm(objset_t *os, uint64_t obj, int normflags, + dmu_object_type_t ot, + dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx) { int err; err = dmu_object_claim(os, obj, ot, 0, bonustype, bonuslen, tx); if (err != 0) return (err); - mzap_create_impl(os, obj, tx); + mzap_create_impl(os, obj, normflags, tx); return (0); } uint64_t zap_create(objset_t *os, dmu_object_type_t ot, dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx) +{ + return (zap_create_norm(os, 0, ot, bonustype, bonuslen, tx)); +} + +uint64_t +zap_create_norm(objset_t *os, int normflags, dmu_object_type_t ot, + dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx) { uint64_t obj = dmu_object_alloc(os, ot, 0, bonustype, bonuslen, tx); - mzap_create_impl(os, obj, tx); + mzap_create_impl(os, obj, normflags, tx); return (obj); } @@ -494,36 +592,102 @@ zap_count(objset_t *os, uint64_t zapobj, uint64_t *count) } /* - * Routines for maniplulating attributes. + * zn may be NULL; if not specified, it will be computed if needed. + * See also the comment above zap_entry_normalization_conflict(). + */ +static boolean_t +mzap_normalization_conflict(zap_t *zap, zap_name_t *zn, mzap_ent_t *mze) +{ + mzap_ent_t *other; + int direction = AVL_BEFORE; + boolean_t allocdzn = B_FALSE; + + if (zap->zap_normflags == 0) + return (B_FALSE); + +again: + for (other = avl_walk(&zap->zap_m.zap_avl, mze, direction); + other && other->mze_hash == mze->mze_hash; + other = avl_walk(&zap->zap_m.zap_avl, other, direction)) { + + if (zn == NULL) { + zn = zap_name_alloc(zap, mze->mze_phys.mze_name, + MT_FIRST); + allocdzn = B_TRUE; + } + if (zap_match(zn, other->mze_phys.mze_name)) { + if (allocdzn) + zap_name_free(zn); + return (B_TRUE); + } + } + + if (direction == AVL_BEFORE) { + direction = AVL_AFTER; + goto again; + } + + if (allocdzn) + zap_name_free(zn); + return (B_FALSE); +} + +/* + * Routines for manipulating attributes. */ int zap_lookup(objset_t *os, uint64_t zapobj, const char *name, uint64_t integer_size, uint64_t num_integers, void *buf) +{ + return (zap_lookup_norm(os, zapobj, name, integer_size, + num_integers, buf, MT_EXACT, NULL, 0, NULL)); +} + +int +zap_lookup_norm(objset_t *os, uint64_t zapobj, const char *name, + uint64_t integer_size, uint64_t num_integers, void *buf, + matchtype_t mt, char *realname, int rn_len, + boolean_t *ncp) { zap_t *zap; int err; mzap_ent_t *mze; + zap_name_t *zn; err = zap_lockdir(os, zapobj, NULL, RW_READER, TRUE, &zap); if (err) return (err); + zn = zap_name_alloc(zap, name, mt); + if (zn == NULL) { + zap_unlockdir(zap); + return (ENOTSUP); + } + if (!zap->zap_ismicro) { - err = fzap_lookup(zap, name, - integer_size, num_integers, buf); + err = fzap_lookup(zn, integer_size, num_integers, buf, + realname, rn_len, ncp); } else { - mze = mze_find(zap, name, zap_hash(zap, name)); + mze = mze_find(zn); if (mze == NULL) { err = ENOENT; } else { - if (num_integers < 1) + if (num_integers < 1) { err = EOVERFLOW; - else if (integer_size != 8) + } else if (integer_size != 8) { err = EINVAL; - else + } else { *(uint64_t *)buf = mze->mze_phys.mze_value; + (void) strlcpy(realname, + mze->mze_phys.mze_name, rn_len); + if (ncp) { + *ncp = mzap_normalization_conflict(zap, + zn, mze); + } + } } } + zap_name_free(zn); zap_unlockdir(zap); return (err); } @@ -535,14 +699,20 @@ zap_length(objset_t *os, uint64_t zapobj, const char *name, zap_t *zap; int err; mzap_ent_t *mze; + zap_name_t *zn; err = zap_lockdir(os, zapobj, NULL, RW_READER, TRUE, &zap); if (err) return (err); + zn = zap_name_alloc(zap, name, MT_EXACT); + if (zn == NULL) { + zap_unlockdir(zap); + return (ENOTSUP); + } if (!zap->zap_ismicro) { - err = fzap_length(zap, name, integer_size, num_integers); + err = fzap_length(zn, integer_size, num_integers); } else { - mze = mze_find(zap, name, zap_hash(zap, name)); + mze = mze_find(zn); if (mze == NULL) { err = ENOENT; } else { @@ -552,28 +722,31 @@ zap_length(objset_t *os, uint64_t zapobj, const char *name, *num_integers = 1; } } + zap_name_free(zn); zap_unlockdir(zap); return (err); } static void -mzap_addent(zap_t *zap, const char *name, uint64_t hash, uint64_t value) +mzap_addent(zap_name_t *zn, uint64_t value) { int i; + zap_t *zap = zn->zn_zap; int start = zap->zap_m.zap_alloc_next; uint32_t cd; - dprintf("obj=%llu %s=%llu\n", zap->zap_object, name, value); + dprintf("obj=%llu %s=%llu\n", zap->zap_object, + zn->zn_name_orij, value); ASSERT(RW_WRITE_HELD(&zap->zap_rwlock)); #ifdef ZFS_DEBUG for (i = 0; i < zap->zap_m.zap_num_chunks; i++) { mzap_ent_phys_t *mze = &zap->zap_m.zap_phys->mz_chunk[i]; - ASSERT(strcmp(name, mze->mze_name) != 0); + ASSERT(strcmp(zn->zn_name_orij, mze->mze_name) != 0); } #endif - cd = mze_find_unused_cd(zap, hash); + cd = mze_find_unused_cd(zap, zn->zn_hash); /* given the limited size of the microzap, this can't happen */ ASSERT(cd != ZAP_MAXCD); @@ -583,13 +756,13 @@ mzap_addent(zap_t *zap, const char *name, uint64_t hash, uint64_t value) if (mze->mze_name[0] == 0) { mze->mze_value = value; mze->mze_cd = cd; - (void) strcpy(mze->mze_name, name); + (void) strcpy(mze->mze_name, zn->zn_name_orij); zap->zap_m.zap_num_entries++; zap->zap_m.zap_alloc_next = i+1; if (zap->zap_m.zap_alloc_next == zap->zap_m.zap_num_chunks) zap->zap_m.zap_alloc_next = 0; - mze_insert(zap, i, hash, mze); + mze_insert(zap, i, zn->zn_hash, mze); return; } } @@ -609,28 +782,33 @@ zap_add(objset_t *os, uint64_t zapobj, const char *name, int err; mzap_ent_t *mze; const uint64_t *intval = val; - uint64_t hash; + zap_name_t *zn; err = zap_lockdir(os, zapobj, tx, RW_WRITER, TRUE, &zap); if (err) return (err); + zn = zap_name_alloc(zap, name, MT_EXACT); + if (zn == NULL) { + zap_unlockdir(zap); + return (ENOTSUP); + } if (!zap->zap_ismicro) { - err = fzap_add(zap, name, integer_size, num_integers, val, tx); + err = fzap_add(zn, integer_size, num_integers, val, tx); } else if (integer_size != 8 || num_integers != 1 || strlen(name) >= MZAP_NAME_LEN) { dprintf("upgrading obj %llu: intsz=%u numint=%llu name=%s\n", zapobj, integer_size, num_integers, name); mzap_upgrade(zap, tx); - err = fzap_add(zap, name, integer_size, num_integers, val, tx); + err = fzap_add(zn, integer_size, num_integers, val, tx); } else { - hash = zap_hash(zap, name); - mze = mze_find(zap, name, hash); + mze = mze_find(zn); if (mze != NULL) { err = EEXIST; } else { - mzap_addent(zap, name, hash, *intval); + mzap_addent(zn, *intval); } } + zap_name_free(zn); zap_unlockdir(zap); return (err); } @@ -642,63 +820,77 @@ zap_update(objset_t *os, uint64_t zapobj, const char *name, zap_t *zap; mzap_ent_t *mze; const uint64_t *intval = val; - uint64_t hash; + zap_name_t *zn; int err; err = zap_lockdir(os, zapobj, tx, RW_WRITER, TRUE, &zap); if (err) return (err); - ASSERT(RW_LOCK_HELD(&zap->zap_rwlock)); + zn = zap_name_alloc(zap, name, MT_EXACT); + if (zn == NULL) { + zap_unlockdir(zap); + return (ENOTSUP); + } if (!zap->zap_ismicro) { - err = fzap_update(zap, name, - integer_size, num_integers, val, tx); + err = fzap_update(zn, integer_size, num_integers, val, tx); } else if (integer_size != 8 || num_integers != 1 || strlen(name) >= MZAP_NAME_LEN) { dprintf("upgrading obj %llu: intsz=%u numint=%llu name=%s\n", zapobj, integer_size, num_integers, name); mzap_upgrade(zap, tx); - err = fzap_update(zap, name, - integer_size, num_integers, val, tx); + err = fzap_update(zn, integer_size, num_integers, val, tx); } else { - hash = zap_hash(zap, name); - mze = mze_find(zap, name, hash); + mze = mze_find(zn); if (mze != NULL) { mze->mze_phys.mze_value = *intval; zap->zap_m.zap_phys->mz_chunk [mze->mze_chunkid].mze_value = *intval; } else { - mzap_addent(zap, name, hash, *intval); + mzap_addent(zn, *intval); } } + zap_name_free(zn); zap_unlockdir(zap); return (err); } int zap_remove(objset_t *os, uint64_t zapobj, const char *name, dmu_tx_t *tx) +{ + return (zap_remove_norm(os, zapobj, name, MT_EXACT, tx)); +} + +int +zap_remove_norm(objset_t *os, uint64_t zapobj, const char *name, + matchtype_t mt, dmu_tx_t *tx) { zap_t *zap; int err; mzap_ent_t *mze; + zap_name_t *zn; err = zap_lockdir(os, zapobj, tx, RW_WRITER, TRUE, &zap); if (err) return (err); + zn = zap_name_alloc(zap, name, mt); + if (zn == NULL) { + zap_unlockdir(zap); + return (ENOTSUP); + } if (!zap->zap_ismicro) { - err = fzap_remove(zap, name, tx); + err = fzap_remove(zn, tx); } else { - mze = mze_find(zap, name, zap_hash(zap, name)); + mze = mze_find(zn); if (mze == NULL) { - dprintf("fail: %s\n", name); err = ENOENT; } else { - dprintf("success: %s\n", name); zap->zap_m.zap_num_entries--; bzero(&zap->zap_m.zap_phys->mz_chunk[mze->mze_chunkid], sizeof (mzap_ent_phys_t)); mze_remove(zap, mze); } } + zap_name_free(zn); zap_unlockdir(zap); return (err); } @@ -795,14 +987,17 @@ zap_cursor_retrieve(zap_cursor_t *zc, zap_attribute_t *za) mze_tofind.mze_phys.mze_cd = zc->zc_cd; mze = avl_find(&zc->zc_zap->zap_m.zap_avl, &mze_tofind, &idx); - ASSERT(mze == NULL || 0 == bcmp(&mze->mze_phys, - &zc->zc_zap->zap_m.zap_phys->mz_chunk[mze->mze_chunkid], - sizeof (mze->mze_phys))); if (mze == NULL) { mze = avl_nearest(&zc->zc_zap->zap_m.zap_avl, idx, AVL_AFTER); } if (mze) { + ASSERT(0 == bcmp(&mze->mze_phys, + &zc->zc_zap->zap_m.zap_phys->mz_chunk + [mze->mze_chunkid], sizeof (mze->mze_phys))); + + za->za_normalization_conflict = + mzap_normalization_conflict(zc->zc_zap, NULL, mze); za->za_integer_length = 8; za->za_num_integers = 1; za->za_first_integer = mze->mze_phys.mze_value; diff --git a/usr/src/uts/common/fs/zfs/zfs_acl.c b/usr/src/uts/common/fs/zfs/zfs_acl.c index f78a9cd0002c..34057b6d72cf 100644 --- a/usr/src/uts/common/fs/zfs/zfs_acl.c +++ b/usr/src/uts/common/fs/zfs/zfs_acl.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -44,17 +45,19 @@ #include #include #include +#include #include #include #include #include +#include #include -#include #include "fs/fs_subr.h" #include #define ALLOW ACE_ACCESS_ALLOWED_ACE_TYPE #define DENY ACE_ACCESS_DENIED_ACE_TYPE +#define MAX_ACE_TYPE ACE_SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE #define OWNING_GROUP (ACE_GROUP|ACE_IDENTIFIER_GROUP) #define EVERYONE_ALLOW_MASK (ACE_READ_ACL|ACE_READ_ATTRIBUTES | \ @@ -63,8 +66,15 @@ ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS) #define OWNER_ALLOW_MASK (ACE_WRITE_ACL | ACE_WRITE_OWNER | \ ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS) -#define WRITE_MASK (ACE_WRITE_DATA|ACE_APPEND_DATA|ACE_WRITE_NAMED_ATTRS| \ - ACE_WRITE_ATTRIBUTES|ACE_WRITE_ACL|ACE_WRITE_OWNER) +#define WRITE_MASK_DATA (ACE_WRITE_DATA|ACE_APPEND_DATA|ACE_WRITE_NAMED_ATTRS) + +#define ZFS_CHECKED_MASKS (ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_READ_DATA| \ + ACE_READ_NAMED_ATTRS|ACE_WRITE_DATA|ACE_WRITE_ATTRIBUTES| \ + ACE_WRITE_NAMED_ATTRS|ACE_APPEND_DATA|ACE_EXECUTE|ACE_WRITE_OWNER| \ + ACE_WRITE_ACL|ACE_DELETE|ACE_DELETE_CHILD|ACE_SYNCHRONIZE) + +#define WRITE_MASK (WRITE_MASK_DATA|ACE_WRITE_ATTRIBUTES|ACE_WRITE_ACL|\ + ACE_WRITE_OWNER) #define OGE_CLEAR (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \ ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_EXECUTE) @@ -73,59 +83,634 @@ ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_EXECUTE) #define ALL_INHERIT (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE | \ - ACE_NO_PROPAGATE_INHERIT_ACE|ACE_INHERIT_ONLY_ACE) + ACE_NO_PROPAGATE_INHERIT_ACE|ACE_INHERIT_ONLY_ACE|ACE_INHERITED_ACE) #define SECURE_CLEAR (ACE_WRITE_ACL|ACE_WRITE_OWNER) -#define OGE_PAD 6 /* traditional owner/group/everyone ACES */ +#define V4_ACL_WIDE_FLAGS (ZFS_ACL_AUTO_INHERIT|ZFS_ACL_DEFAULTED|\ + ZFS_ACL_PROTECTED) + +#define ZFS_ACL_WIDE_FLAGS (V4_ACL_WIDE_FLAGS|ZFS_ACL_TRIVIAL|ZFS_INHERIT_ACE|\ + ZFS_ACL_OBJ_ACE) + +static uint16_t +zfs_ace_v0_get_type(void *acep) +{ + return (((zfs_oldace_t *)acep)->z_type); +} + +static uint16_t +zfs_ace_v0_get_flags(void *acep) +{ + return (((zfs_oldace_t *)acep)->z_flags); +} -static int zfs_ace_can_use(znode_t *zp, ace_t *); +static uint32_t +zfs_ace_v0_get_mask(void *acep) +{ + return (((zfs_oldace_t *)acep)->z_access_mask); +} + +static uint64_t +zfs_ace_v0_get_who(void *acep) +{ + return (((zfs_oldace_t *)acep)->z_fuid); +} + +static void +zfs_ace_v0_set_type(void *acep, uint16_t type) +{ + ((zfs_oldace_t *)acep)->z_type = type; +} + +static void +zfs_ace_v0_set_flags(void *acep, uint16_t flags) +{ + ((zfs_oldace_t *)acep)->z_flags = flags; +} + +static void +zfs_ace_v0_set_mask(void *acep, uint32_t mask) +{ + ((zfs_oldace_t *)acep)->z_access_mask = mask; +} + +static void +zfs_ace_v0_set_who(void *acep, uint64_t who) +{ + ((zfs_oldace_t *)acep)->z_fuid = who; +} + +/*ARGSUSED*/ +static size_t +zfs_ace_v0_size(void *acep) +{ + return (sizeof (zfs_oldace_t)); +} + +static size_t +zfs_ace_v0_abstract_size(void) +{ + return (sizeof (zfs_oldace_t)); +} + +static int +zfs_ace_v0_mask_off(void) +{ + return (offsetof(zfs_oldace_t, z_access_mask)); +} + +/*ARGSUSED*/ +static int +zfs_ace_v0_data(void *acep, void **datap) +{ + *datap = NULL; + return (0); +} + +static acl_ops_t zfs_acl_v0_ops = { + zfs_ace_v0_get_mask, + zfs_ace_v0_set_mask, + zfs_ace_v0_get_flags, + zfs_ace_v0_set_flags, + zfs_ace_v0_get_type, + zfs_ace_v0_set_type, + zfs_ace_v0_get_who, + zfs_ace_v0_set_who, + zfs_ace_v0_size, + zfs_ace_v0_abstract_size, + zfs_ace_v0_mask_off, + zfs_ace_v0_data +}; + +static uint16_t +zfs_ace_fuid_get_type(void *acep) +{ + return (((zfs_ace_hdr_t *)acep)->z_type); +} + +static uint16_t +zfs_ace_fuid_get_flags(void *acep) +{ + return (((zfs_ace_hdr_t *)acep)->z_flags); +} + +static uint32_t +zfs_ace_fuid_get_mask(void *acep) +{ + return (((zfs_ace_hdr_t *)acep)->z_access_mask); +} + +static uint64_t +zfs_ace_fuid_get_who(void *args) +{ + uint16_t entry_type; + zfs_ace_t *acep = args; + + entry_type = acep->z_hdr.z_flags & ACE_TYPE_FLAGS; + + if (entry_type == ACE_OWNER || entry_type == OWNING_GROUP || + entry_type == ACE_EVERYONE) + return (-1); + return (((zfs_ace_t *)acep)->z_fuid); +} + +static void +zfs_ace_fuid_set_type(void *acep, uint16_t type) +{ + ((zfs_ace_hdr_t *)acep)->z_type = type; +} + +static void +zfs_ace_fuid_set_flags(void *acep, uint16_t flags) +{ + ((zfs_ace_hdr_t *)acep)->z_flags = flags; +} + +static void +zfs_ace_fuid_set_mask(void *acep, uint32_t mask) +{ + ((zfs_ace_hdr_t *)acep)->z_access_mask = mask; +} + +static void +zfs_ace_fuid_set_who(void *arg, uint64_t who) +{ + zfs_ace_t *acep = arg; + + uint16_t entry_type = acep->z_hdr.z_flags & ACE_TYPE_FLAGS; + + if (entry_type == ACE_OWNER || entry_type == OWNING_GROUP || + entry_type == ACE_EVERYONE) + return; + acep->z_fuid = who; +} + +static size_t +zfs_ace_fuid_size(void *acep) +{ + zfs_ace_hdr_t *zacep = acep; + uint16_t entry_type; + + switch (zacep->z_type) { + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE: + return (sizeof (zfs_object_ace_t)); + case ALLOW: + case DENY: + entry_type = + (((zfs_ace_hdr_t *)acep)->z_flags & ACE_TYPE_FLAGS); + if (entry_type == ACE_OWNER || + entry_type == (ACE_GROUP | ACE_IDENTIFIER_GROUP) || + entry_type == ACE_EVERYONE) + return (sizeof (zfs_ace_hdr_t)); + /*FALLTHROUGH*/ + default: + return (sizeof (zfs_ace_t)); + } +} + +static size_t +zfs_ace_fuid_abstract_size(void) +{ + return (sizeof (zfs_ace_hdr_t)); +} + +static int +zfs_ace_fuid_mask_off(void) +{ + return (offsetof(zfs_ace_hdr_t, z_access_mask)); +} + +static int +zfs_ace_fuid_data(void *acep, void **datap) +{ + zfs_ace_t *zacep = acep; + zfs_object_ace_t *zobjp; + + switch (zacep->z_hdr.z_type) { + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE: + zobjp = acep; + *datap = (caddr_t)zobjp + sizeof (zfs_ace_t); + return (sizeof (zfs_object_ace_t) - sizeof (zfs_ace_t)); + default: + *datap = NULL; + return (0); + } +} + +static acl_ops_t zfs_acl_fuid_ops = { + zfs_ace_fuid_get_mask, + zfs_ace_fuid_set_mask, + zfs_ace_fuid_get_flags, + zfs_ace_fuid_set_flags, + zfs_ace_fuid_get_type, + zfs_ace_fuid_set_type, + zfs_ace_fuid_get_who, + zfs_ace_fuid_set_who, + zfs_ace_fuid_size, + zfs_ace_fuid_abstract_size, + zfs_ace_fuid_mask_off, + zfs_ace_fuid_data +}; + +static int +zfs_acl_version(int version) +{ + if (version < ZPL_VERSION_FUID) + return (ZFS_ACL_VERSION_INITIAL); + else + return (ZFS_ACL_VERSION_FUID); +} + +static int +zfs_acl_version_zp(znode_t *zp) +{ + return (zfs_acl_version(zp->z_zfsvfs->z_version)); +} static zfs_acl_t * -zfs_acl_alloc(int slots) +zfs_acl_alloc(int vers) { zfs_acl_t *aclp; aclp = kmem_zalloc(sizeof (zfs_acl_t), KM_SLEEP); - if (slots != 0) { - aclp->z_acl = kmem_alloc(ZFS_ACL_SIZE(slots), KM_SLEEP); - aclp->z_acl_count = 0; - aclp->z_state = ACL_DATA_ALLOCED; - } else { - aclp->z_state = 0; - } - aclp->z_slots = slots; + list_create(&aclp->z_acl, sizeof (zfs_acl_node_t), + offsetof(zfs_acl_node_t, z_next)); + aclp->z_version = vers; + if (vers == ZFS_ACL_VERSION_FUID) + aclp->z_ops = zfs_acl_fuid_ops; + else + aclp->z_ops = zfs_acl_v0_ops; return (aclp); } +static zfs_acl_node_t * +zfs_acl_node_alloc(size_t bytes) +{ + zfs_acl_node_t *aclnode; + + aclnode = kmem_zalloc(sizeof (zfs_acl_node_t), KM_SLEEP); + if (bytes) { + aclnode->z_acldata = kmem_alloc(bytes, KM_SLEEP); + aclnode->z_allocdata = aclnode->z_acldata; + aclnode->z_allocsize = bytes; + aclnode->z_size = bytes; + } + + return (aclnode); +} + +static void +zfs_acl_node_free(zfs_acl_node_t *aclnode) +{ + if (aclnode->z_allocsize) + kmem_free(aclnode->z_allocdata, aclnode->z_allocsize); + kmem_free(aclnode, sizeof (zfs_acl_node_t)); +} + void zfs_acl_free(zfs_acl_t *aclp) { - if (aclp->z_state == ACL_DATA_ALLOCED) { - kmem_free(aclp->z_acl, ZFS_ACL_SIZE(aclp->z_slots)); + zfs_acl_node_t *aclnode; + + while (aclnode = list_head(&aclp->z_acl)) { + list_remove(&aclp->z_acl, aclnode); + zfs_acl_node_free(aclnode); } + + list_destroy(&aclp->z_acl); kmem_free(aclp, sizeof (zfs_acl_t)); } -static uint32_t -zfs_v4_to_unix(uint32_t access_mask) +static boolean_t +zfs_ace_valid(vtype_t obj_type, zfs_acl_t *aclp, uint16_t type, uint16_t iflags) { - uint32_t new_mask = 0; + /* + * first check type of entry + */ + + switch (iflags & ACE_TYPE_FLAGS) { + case ACE_OWNER: + case (ACE_IDENTIFIER_GROUP | ACE_GROUP): + case ACE_IDENTIFIER_GROUP: + case ACE_EVERYONE: + case 0: /* User entry */ + break; + default: + return (B_FALSE); + + } /* - * This is used for mapping v4 permissions into permissions - * that can be passed to secpolicy_vnode_access() + * next check inheritance level flags */ - if (access_mask & (ACE_READ_DATA | ACE_LIST_DIRECTORY | - ACE_READ_ATTRIBUTES | ACE_READ_ACL)) - new_mask |= S_IROTH; - if (access_mask & (ACE_WRITE_DATA | ACE_APPEND_DATA | - ACE_WRITE_ATTRIBUTES | ACE_ADD_FILE | ACE_WRITE_NAMED_ATTRS)) - new_mask |= S_IWOTH; - if (access_mask & (ACE_EXECUTE | ACE_READ_NAMED_ATTRS)) - new_mask |= S_IXOTH; - return (new_mask); + if (type != ALLOW && type > MAX_ACE_TYPE) { + return (B_FALSE); + } + + switch (type) { + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE: + if (aclp->z_version < ZFS_ACL_VERSION_FUID) + return (B_FALSE); + aclp->z_hints |= ZFS_ACL_OBJ_ACE; + } + + /* + * Only directories should have inheritance flags. + */ + if (obj_type != VDIR && (iflags & + (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE| + ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE))) { + return (B_FALSE); + } + + if (iflags & (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE)) + aclp->z_hints |= ZFS_INHERIT_ACE; + + if (iflags & (ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE)) { + if ((iflags & (ACE_FILE_INHERIT_ACE| + ACE_DIRECTORY_INHERIT_ACE)) == 0) { + return (B_FALSE); + } + } + + return (B_TRUE); +} + +static void * +zfs_acl_next_ace(zfs_acl_t *aclp, void *start, uint64_t *who, + uint32_t *access_mask, uint16_t *iflags, uint16_t *type) +{ + zfs_acl_node_t *aclnode; + + if (start == NULL) { + aclnode = list_head(&aclp->z_acl); + if (aclnode == NULL) + return (NULL); + + aclp->z_next_ace = aclnode->z_acldata; + aclp->z_curr_node = aclnode; + aclnode->z_ace_idx = 0; + } + + aclnode = aclp->z_curr_node; + + if (aclnode == NULL) + return (NULL); + + if (aclnode->z_ace_idx >= aclnode->z_ace_count) { + aclnode = list_next(&aclp->z_acl, aclnode); + if (aclnode == NULL) + return (NULL); + else { + aclp->z_curr_node = aclnode; + aclnode->z_ace_idx = 0; + aclp->z_next_ace = aclnode->z_acldata; + } + } + + if (aclnode->z_ace_idx < aclnode->z_ace_count) { + void *acep = aclp->z_next_ace; + *iflags = aclp->z_ops.ace_flags_get(acep); + *type = aclp->z_ops.ace_type_get(acep); + *access_mask = aclp->z_ops.ace_mask_get(acep); + *who = aclp->z_ops.ace_who_get(acep); + aclp->z_next_ace = (caddr_t)aclp->z_next_ace + + aclp->z_ops.ace_size(acep); + aclnode->z_ace_idx++; + return ((void *)acep); + } + return (NULL); +} + +/*ARGSUSED*/ +static uint64_t +zfs_ace_walk(void *datap, uint64_t cookie, int aclcnt, + uint16_t *flags, uint16_t *type, uint32_t *mask) +{ + zfs_acl_t *aclp = datap; + zfs_ace_hdr_t *acep = (zfs_ace_hdr_t *)(uintptr_t)cookie; + uint64_t who; + + acep = zfs_acl_next_ace(aclp, acep, &who, mask, + flags, type); + return ((uint64_t)(uintptr_t)acep); +} + +static zfs_acl_node_t * +zfs_acl_curr_node(zfs_acl_t *aclp) +{ + ASSERT(aclp->z_curr_node); + return (aclp->z_curr_node); +} + +/* + * Copy ACE to internal ZFS format. + * While processing the ACL each ACE will be validated for correctness. + * ACE FUIDs will be created later. + */ +int +zfs_copy_ace_2_fuid(vtype_t obj_type, zfs_acl_t *aclp, void *datap, + zfs_ace_t *z_acl, int aclcnt, size_t *size) +{ + int i; + uint16_t entry_type; + zfs_ace_t *aceptr = z_acl; + ace_t *acep = datap; + zfs_object_ace_t *zobjacep; + ace_object_t *aceobjp; + + for (i = 0; i != aclcnt; i++) { + aceptr->z_hdr.z_access_mask = acep->a_access_mask; + aceptr->z_hdr.z_flags = acep->a_flags; + aceptr->z_hdr.z_type = acep->a_type; + entry_type = aceptr->z_hdr.z_flags & ACE_TYPE_FLAGS; + if (entry_type != ACE_OWNER && entry_type != OWNING_GROUP && + entry_type != ACE_EVERYONE) + aceptr->z_fuid = (uint64_t)acep->a_who; + /* + * Make sure ACE is valid + */ + if (zfs_ace_valid(obj_type, aclp, aceptr->z_hdr.z_type, + aceptr->z_hdr.z_flags) != B_TRUE) + return (EINVAL); + + switch (acep->a_type) { + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE: + zobjacep = (zfs_object_ace_t *)aceptr; + aceobjp = (ace_object_t *)acep; + + bcopy(aceobjp->a_obj_type, zobjacep->z_object_type, + sizeof (aceobjp->a_obj_type)); + bcopy(aceobjp->a_inherit_obj_type, + zobjacep->z_inherit_type, + sizeof (aceobjp->a_inherit_obj_type)); + acep = (ace_t *)((caddr_t)acep + sizeof (ace_object_t)); + break; + default: + acep = (ace_t *)((caddr_t)acep + sizeof (ace_t)); + } + + aceptr = (zfs_ace_t *)((caddr_t)aceptr + + aclp->z_ops.ace_size(aceptr)); + } + + *size = (caddr_t)aceptr - (caddr_t)z_acl; + + return (0); +} + +/* + * Copy ZFS ACEs to fixed size ace_t layout + */ +static void +zfs_copy_fuid_2_ace(zfsvfs_t *zfsvfs, zfs_acl_t *aclp, void *datap, int filter) +{ + uint64_t who; + uint32_t access_mask; + uint16_t iflags, type; + zfs_ace_hdr_t *zacep = NULL; + ace_t *acep = datap; + ace_object_t *objacep; + zfs_object_ace_t *zobjacep; + zfs_fuid_hdl_t hdl = { 0 }; + size_t ace_size; + uint16_t entry_type; + + while (zacep = zfs_acl_next_ace(aclp, zacep, + &who, &access_mask, &iflags, &type)) { + + switch (type) { + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE: + if (filter) { + continue; + } + zobjacep = (zfs_object_ace_t *)zacep; + objacep = (ace_object_t *)acep; + bcopy(zobjacep->z_object_type, + objacep->a_obj_type, + sizeof (zobjacep->z_object_type)); + bcopy(zobjacep->z_inherit_type, + objacep->a_inherit_obj_type, + sizeof (zobjacep->z_inherit_type)); + ace_size = sizeof (ace_object_t); + break; + default: + ace_size = sizeof (ace_t); + break; + } + + entry_type = (iflags & ACE_TYPE_FLAGS); + if ((entry_type != ACE_OWNER && + entry_type != (ACE_GROUP | ACE_IDENTIFIER_GROUP) && + entry_type != ACE_EVERYONE)) + zfs_fuid_queue_map_id(zfsvfs, &hdl, who, + (entry_type & ACE_IDENTIFIER_GROUP) ? + ZFS_ACE_GROUP : ZFS_ACE_USER, &acep->a_who); + else + acep->a_who = (uid_t)(int64_t)who; + acep->a_access_mask = access_mask; + acep->a_flags = iflags; + acep->a_type = type; + acep = (ace_t *)((caddr_t)acep + ace_size); + } + zfs_fuid_get_mappings(&hdl); +} + +static int +zfs_copy_ace_2_oldace(vtype_t obj_type, zfs_acl_t *aclp, ace_t *acep, + zfs_oldace_t *z_acl, int aclcnt, size_t *size) +{ + int i; + zfs_oldace_t *aceptr = z_acl; + + for (i = 0; i != aclcnt; i++, aceptr++) { + aceptr->z_access_mask = acep[i].a_access_mask; + aceptr->z_type = acep[i].a_type; + aceptr->z_flags = acep[i].a_flags; + aceptr->z_fuid = acep[i].a_who; + /* + * Make sure ACE is valid + */ + if (zfs_ace_valid(obj_type, aclp, aceptr->z_type, + aceptr->z_flags) != B_TRUE) + return (EINVAL); + } + *size = (caddr_t)aceptr - (caddr_t)z_acl; + return (0); +} + +/* + * convert old ACL format to new + */ +void +zfs_acl_xform(znode_t *zp, zfs_acl_t *aclp) +{ + zfs_oldace_t *oldaclp; + int i; + uint16_t type, iflags; + uint32_t access_mask; + uint64_t who; + void *cookie = NULL; + zfs_acl_node_t *aclnode, *newaclnode; + + ASSERT(aclp->z_version == ZFS_ACL_VERSION_INITIAL); + /* + * First create the ACE in a contiguous piece of memory + * for zfs_copy_ace_2_fuid(). + * + * We only convert an ACL once, so this won't happen + * everytime. + */ + oldaclp = kmem_alloc(sizeof (zfs_oldace_t) * aclp->z_acl_count, + KM_SLEEP); + i = 0; + while (cookie = zfs_acl_next_ace(aclp, cookie, &who, + &access_mask, &iflags, &type)) { + oldaclp[i].z_flags = iflags; + oldaclp[i].z_type = type; + oldaclp[i].z_fuid = who; + oldaclp[i++].z_access_mask = access_mask; + } + + newaclnode = zfs_acl_node_alloc(aclp->z_acl_count * + sizeof (zfs_object_ace_t)); + aclp->z_ops = zfs_acl_fuid_ops; + VERIFY(zfs_copy_ace_2_fuid(ZTOV(zp)->v_type, aclp, oldaclp, + newaclnode->z_acldata, aclp->z_acl_count, + &newaclnode->z_size) == 0); + aclp->z_acl_bytes = newaclnode->z_size; + newaclnode->z_ace_count = aclp->z_acl_count; + aclp->z_version = ZFS_ACL_VERSION; + kmem_free(oldaclp, aclp->z_acl_count * sizeof (zfs_oldace_t)); + + /* + * Release all previous ACL nodes + */ + + while (aclnode = list_head(&aclp->z_acl)) { + list_remove(&aclp->z_acl, aclnode); + if (aclnode->z_allocsize) + kmem_free(aclnode->z_allocdata, aclnode->z_allocsize); + kmem_free(aclnode, sizeof (zfs_acl_node_t)); + } + list_insert_head(&aclp->z_acl, newaclnode); } /* @@ -136,164 +721,207 @@ zfs_unix_to_v4(uint32_t access_mask) { uint32_t new_mask = 0; - if (access_mask & 01) - new_mask |= (ACE_EXECUTE); - if (access_mask & 02) { - new_mask |= (ACE_WRITE_DATA); - } if (access_mask & 04) { + if (access_mask & S_IXOTH) + new_mask |= ACE_EXECUTE; + if (access_mask & S_IWOTH) + new_mask |= ACE_WRITE_DATA; + if (access_mask & S_IROTH) new_mask |= ACE_READ_DATA; - } return (new_mask); } static void -zfs_set_ace(ace_t *zacep, uint32_t access_mask, int access_type, - uid_t uid, int entry_type) +zfs_set_ace(zfs_acl_t *aclp, void *acep, uint32_t access_mask, + uint16_t access_type, uint64_t fuid, uint16_t entry_type) { - zacep->a_access_mask = access_mask; - zacep->a_type = access_type; - zacep->a_who = uid; - zacep->a_flags = entry_type; + uint16_t type = entry_type & ACE_TYPE_FLAGS; + + aclp->z_ops.ace_mask_set(acep, access_mask); + aclp->z_ops.ace_type_set(acep, access_type); + aclp->z_ops.ace_flags_set(acep, entry_type); + if ((type != ACE_OWNER && type != (ACE_GROUP | ACE_IDENTIFIER_GROUP) && + type != ACE_EVERYONE)) + aclp->z_ops.ace_who_set(acep, fuid); } +/* + * Determine mode of file based on ACL. + * Also, create FUIDs for any User/Group ACEs + */ static uint64_t -zfs_mode_compute(znode_t *zp, zfs_acl_t *aclp) +zfs_mode_fuid_compute(znode_t *zp, zfs_acl_t *aclp, zfs_fuid_info_t **fuidp, + dmu_tx_t *tx) { - int i; - int entry_type; - mode_t mode = (zp->z_phys->zp_mode & - (S_IFMT | S_ISUID | S_ISGID | S_ISVTX)); - mode_t seen = 0; - ace_t *acep; + int entry_type; + mode_t mode; + mode_t seen = 0; + zfs_ace_hdr_t *acep = NULL; + uint64_t who; + uint16_t iflags, type; + uint32_t access_mask; + + mode = (zp->z_phys->zp_mode & (S_IFMT | S_ISUID | S_ISGID | S_ISVTX)); - for (i = 0, acep = aclp->z_acl; - i != aclp->z_acl_count; i++, acep++) { + while (acep = zfs_acl_next_ace(aclp, acep, &who, + &access_mask, &iflags, &type)) { + entry_type = (iflags & ACE_TYPE_FLAGS); /* * Skip over inherit only ACEs */ - if (acep->a_flags & ACE_INHERIT_ONLY_ACE) + if (entry_type == ACE_INHERIT_ONLY_ACE) continue; - entry_type = (acep->a_flags & ACE_TYPE_FLAGS); if (entry_type == ACE_OWNER) { - if ((acep->a_access_mask & ACE_READ_DATA) && + if ((access_mask & ACE_READ_DATA) && (!(seen & S_IRUSR))) { seen |= S_IRUSR; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IRUSR; } } - if ((acep->a_access_mask & ACE_WRITE_DATA) && + if ((access_mask & ACE_WRITE_DATA) && (!(seen & S_IWUSR))) { seen |= S_IWUSR; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IWUSR; } } - if ((acep->a_access_mask & ACE_EXECUTE) && + if ((access_mask & ACE_EXECUTE) && (!(seen & S_IXUSR))) { seen |= S_IXUSR; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IXUSR; } } } else if (entry_type == OWNING_GROUP) { - if ((acep->a_access_mask & ACE_READ_DATA) && + if ((access_mask & ACE_READ_DATA) && (!(seen & S_IRGRP))) { seen |= S_IRGRP; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IRGRP; } } - if ((acep->a_access_mask & ACE_WRITE_DATA) && + if ((access_mask & ACE_WRITE_DATA) && (!(seen & S_IWGRP))) { seen |= S_IWGRP; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IWGRP; } } - if ((acep->a_access_mask & ACE_EXECUTE) && + if ((access_mask & ACE_EXECUTE) && (!(seen & S_IXGRP))) { seen |= S_IXGRP; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IXGRP; } } } else if (entry_type == ACE_EVERYONE) { - if ((acep->a_access_mask & ACE_READ_DATA)) { + if ((access_mask & ACE_READ_DATA)) { if (!(seen & S_IRUSR)) { seen |= S_IRUSR; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IRUSR; } } if (!(seen & S_IRGRP)) { seen |= S_IRGRP; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IRGRP; } } if (!(seen & S_IROTH)) { seen |= S_IROTH; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IROTH; } } } - if ((acep->a_access_mask & ACE_WRITE_DATA)) { + if ((access_mask & ACE_WRITE_DATA)) { if (!(seen & S_IWUSR)) { seen |= S_IWUSR; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IWUSR; } } if (!(seen & S_IWGRP)) { seen |= S_IWGRP; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IWGRP; } } if (!(seen & S_IWOTH)) { seen |= S_IWOTH; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IWOTH; } } } - if ((acep->a_access_mask & ACE_EXECUTE)) { + if ((access_mask & ACE_EXECUTE)) { if (!(seen & S_IXUSR)) { seen |= S_IXUSR; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IXUSR; } } if (!(seen & S_IXGRP)) { seen |= S_IXGRP; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IXGRP; } } if (!(seen & S_IXOTH)) { seen |= S_IXOTH; - if (acep->a_type == ALLOW) { + if (type == ALLOW) { mode |= S_IXOTH; } } } } + /* + * Now handle FUID create for user/group ACEs + */ + if (entry_type == 0 || entry_type == ACE_IDENTIFIER_GROUP) { + aclp->z_ops.ace_who_set(acep, + zfs_fuid_create(zp->z_zfsvfs, who, + entry_type == 0 ? ZFS_ACE_USER : ZFS_ACE_GROUP, tx, + fuidp)); + } } return (mode); } static zfs_acl_t * -zfs_acl_node_read_internal(znode_t *zp) +zfs_acl_node_read_internal(znode_t *zp, boolean_t will_modify) { zfs_acl_t *aclp; + zfs_acl_node_t *aclnode; + + aclp = zfs_acl_alloc(zp->z_phys->zp_acl.z_acl_version); + + /* + * Version 0 to 1 znode_acl_phys has the size/count fields swapped. + * Version 0 didn't have a size field, only a count. + */ + if (zp->z_phys->zp_acl.z_acl_version == ZFS_ACL_VERSION_INITIAL) { + aclp->z_acl_count = zp->z_phys->zp_acl.z_acl_size; + aclp->z_acl_bytes = ZFS_ACL_SIZE(aclp->z_acl_count); + } else { + aclp->z_acl_count = zp->z_phys->zp_acl.z_acl_count; + aclp->z_acl_bytes = zp->z_phys->zp_acl.z_acl_size; + } + + aclnode = zfs_acl_node_alloc(will_modify ? aclp->z_acl_bytes : 0); + aclnode->z_ace_count = aclp->z_acl_count; + if (will_modify) { + bcopy(zp->z_phys->zp_acl.z_ace_data, aclnode->z_acldata, + aclp->z_acl_bytes); + } else { + aclnode->z_size = aclp->z_acl_bytes; + aclnode->z_acldata = &zp->z_phys->zp_acl.z_ace_data[0]; + } - aclp = zfs_acl_alloc(0); - aclp->z_acl_count = zp->z_phys->zp_acl.z_acl_count; - aclp->z_acl = &zp->z_phys->zp_acl.z_ace_data[0]; + list_insert_head(&aclp->z_acl, aclnode); return (aclp); } @@ -302,144 +930,127 @@ zfs_acl_node_read_internal(znode_t *zp) * Read an external acl object. */ static int -zfs_acl_node_read(znode_t *zp, zfs_acl_t **aclpp) +zfs_acl_node_read(znode_t *zp, zfs_acl_t **aclpp, boolean_t will_modify) { uint64_t extacl = zp->z_phys->zp_acl.z_acl_extern_obj; zfs_acl_t *aclp; + size_t aclsize; + size_t acl_count; + zfs_acl_node_t *aclnode; int error; ASSERT(MUTEX_HELD(&zp->z_acl_lock)); if (zp->z_phys->zp_acl.z_acl_extern_obj == 0) { - *aclpp = zfs_acl_node_read_internal(zp); + *aclpp = zfs_acl_node_read_internal(zp, will_modify); return (0); } - aclp = zfs_acl_alloc(zp->z_phys->zp_acl.z_acl_count); + aclp = zfs_acl_alloc(zp->z_phys->zp_acl.z_acl_version); + if (zp->z_phys->zp_acl.z_acl_version == ZFS_ACL_VERSION_INITIAL) { + zfs_acl_phys_v0_t *zacl0 = + (zfs_acl_phys_v0_t *)&zp->z_phys->zp_acl; + aclsize = ZFS_ACL_SIZE(zacl0->z_acl_count); + acl_count = zacl0->z_acl_count; + } else { + aclsize = zp->z_phys->zp_acl.z_acl_size; + acl_count = zp->z_phys->zp_acl.z_acl_count; + if (aclsize == 0) + aclsize = acl_count * sizeof (zfs_ace_t); + } + aclnode = zfs_acl_node_alloc(aclsize); + list_insert_head(&aclp->z_acl, aclnode); error = dmu_read(zp->z_zfsvfs->z_os, extacl, 0, - ZFS_ACL_SIZE(zp->z_phys->zp_acl.z_acl_count), aclp->z_acl); + aclsize, aclnode->z_acldata); + aclnode->z_ace_count = acl_count; + aclp->z_acl_count = acl_count; + aclp->z_acl_bytes = aclsize; + if (error != 0) { zfs_acl_free(aclp); return (error); } - aclp->z_acl_count = zp->z_phys->zp_acl.z_acl_count; - *aclpp = aclp; return (0); } -static boolean_t -zfs_acl_valid(znode_t *zp, ace_t *uace, int aclcnt, int *inherit) -{ - ace_t *acep; - int i; - - *inherit = 0; - - if (aclcnt > MAX_ACL_ENTRIES || aclcnt <= 0) { - return (B_FALSE); - } - - for (i = 0, acep = uace; i != aclcnt; i++, acep++) { - - /* - * first check type of entry - */ - - switch (acep->a_flags & ACE_TYPE_FLAGS) { - case ACE_OWNER: - acep->a_who = (uid_t)-1; - break; - case (ACE_IDENTIFIER_GROUP | ACE_GROUP): - case ACE_IDENTIFIER_GROUP: - if (acep->a_flags & ACE_GROUP) { - acep->a_who = (uid_t)-1; - } - break; - case ACE_EVERYONE: - acep->a_who = (uid_t)-1; - break; - } - - /* - * next check inheritance level flags - */ - - if (acep->a_type != ALLOW && acep->a_type != DENY) - return (B_FALSE); - - /* - * Only directories should have inheritance flags. - */ - if (ZTOV(zp)->v_type != VDIR && (acep->a_flags & - (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE| - ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE))) { - return (B_FALSE); - } - - if (acep->a_flags & - (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE)) - *inherit = 1; - - if (acep->a_flags & - (ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE)) { - if ((acep->a_flags & (ACE_FILE_INHERIT_ACE| - ACE_DIRECTORY_INHERIT_ACE)) == 0) { - return (B_FALSE); - } - } - } - - return (B_TRUE); -} /* - * common code for setting acl's. + * common code for setting ACLs. * * This function is called from zfs_mode_update, zfs_perm_init, and zfs_setacl. * zfs_setacl passes a non-NULL inherit pointer (ihp) to indicate that it's * already checked the acl and knows whether to inherit. */ int -zfs_aclset_common(znode_t *zp, zfs_acl_t *aclp, dmu_tx_t *tx, int *ihp) +zfs_aclset_common(znode_t *zp, zfs_acl_t *aclp, zfs_fuid_info_t **fuidp, + dmu_tx_t *tx) { - int inherit = 0; int error; znode_phys_t *zphys = zp->z_phys; - zfs_znode_acl_t *zacl = &zphys->zp_acl; - uint32_t acl_phys_size = ZFS_ACL_SIZE(aclp->z_acl_count); + zfs_acl_phys_t *zacl = &zphys->zp_acl; zfsvfs_t *zfsvfs = zp->z_zfsvfs; uint64_t aoid = zphys->zp_acl.z_acl_extern_obj; + uint64_t off = 0; + dmu_object_type_t otype; + zfs_acl_node_t *aclnode; ASSERT(MUTEX_HELD(&zp->z_lock)); ASSERT(MUTEX_HELD(&zp->z_acl_lock)); - if (ihp) - inherit = *ihp; /* already determined by caller */ - else if (!zfs_acl_valid(zp, aclp->z_acl, - aclp->z_acl_count, &inherit)) { - return (EINVAL); - } - dmu_buf_will_dirty(zp->z_dbuf, tx); + zphys->zp_mode = zfs_mode_fuid_compute(zp, aclp, fuidp, tx); + /* - * Will ACL fit internally? + * Decide which opbject type to use. If we are forced to + * use old ACL format than transform ACL into zfs_oldace_t + * layout. */ - if (aclp->z_acl_count > ACE_SLOT_CNT) { + if (!zfsvfs->z_use_fuids) { + otype = DMU_OT_OLDACL; + } else { + if ((aclp->z_version == ZFS_ACL_VERSION_INITIAL) && + (zfsvfs->z_version >= ZPL_VERSION_FUID)) + zfs_acl_xform(zp, aclp); + ASSERT(aclp->z_version >= ZFS_ACL_VERSION_FUID); + otype = DMU_OT_ACL; + } + + if (aclp->z_acl_bytes > ZFS_ACE_SPACE) { + /* + * If ACL was previously external and we are now + * converting to new ACL format then release old + * ACL object and create a new one. + */ + if (aoid && aclp->z_version != zacl->z_acl_version) { + error = dmu_object_free(zfsvfs->z_os, + zp->z_phys->zp_acl.z_acl_extern_obj, tx); + if (error) + return (error); + aoid = 0; + } if (aoid == 0) { aoid = dmu_object_alloc(zfsvfs->z_os, - DMU_OT_ACL, acl_phys_size, DMU_OT_NONE, 0, tx); + otype, aclp->z_acl_bytes, + otype == DMU_OT_ACL ? DMU_OT_SYSACL : DMU_OT_NONE, + otype == DMU_OT_ACL ? DN_MAX_BONUSLEN : 0, tx); } else { (void) dmu_object_set_blocksize(zfsvfs->z_os, aoid, - acl_phys_size, 0, tx); + aclp->z_acl_bytes, 0, tx); } zphys->zp_acl.z_acl_extern_obj = aoid; - zphys->zp_acl.z_acl_count = aclp->z_acl_count; - dmu_write(zfsvfs->z_os, aoid, 0, - acl_phys_size, aclp->z_acl, tx); + for (aclnode = list_head(&aclp->z_acl); aclnode; + aclnode = list_next(&aclp->z_acl, aclnode)) { + if (aclnode->z_ace_count == 0) + continue; + dmu_write(zfsvfs->z_os, aoid, off, + aclnode->z_size, aclnode->z_acldata, tx); + off += aclnode->z_size; + } } else { + void *start = zacl->z_ace_data; /* * Migrating back embedded? */ @@ -450,64 +1061,75 @@ zfs_aclset_common(znode_t *zp, zfs_acl_t *aclp, dmu_tx_t *tx, int *ihp) return (error); zphys->zp_acl.z_acl_extern_obj = 0; } - bcopy(aclp->z_acl, zacl->z_ace_data, - aclp->z_acl_count * sizeof (ace_t)); - zacl->z_acl_count = aclp->z_acl_count; + + for (aclnode = list_head(&aclp->z_acl); aclnode; + aclnode = list_next(&aclp->z_acl, aclnode)) { + if (aclnode->z_ace_count == 0) + continue; + bcopy(aclnode->z_acldata, start, aclnode->z_size); + start = (caddr_t)start + aclnode->z_size; + } } - zp->z_phys->zp_flags &= ~(ZFS_ACL_TRIVIAL|ZFS_INHERIT_ACE); - if (inherit) { - zp->z_phys->zp_flags |= ZFS_INHERIT_ACE; - } else if (ace_trivial(zacl->z_ace_data, zacl->z_acl_count) == 0) { - zp->z_phys->zp_flags |= ZFS_ACL_TRIVIAL; + /* + * If Old version then swap count/bytes to match old + * layout of znode_acl_phys_t. + */ + if (aclp->z_version == ZFS_ACL_VERSION_INITIAL) { + zphys->zp_acl.z_acl_size = aclp->z_acl_count; + zphys->zp_acl.z_acl_count = aclp->z_acl_bytes; + } else { + zphys->zp_acl.z_acl_size = aclp->z_acl_bytes; + zphys->zp_acl.z_acl_count = aclp->z_acl_count; } - zphys->zp_mode = zfs_mode_compute(zp, aclp); - zfs_time_stamper_locked(zp, STATE_CHANGED, tx); + zphys->zp_acl.z_acl_version = aclp->z_version; + + /* + * Replace ACL wide bits, but first clear them. + */ + zp->z_phys->zp_flags &= ~ZFS_ACL_WIDE_FLAGS; + + zp->z_phys->zp_flags |= aclp->z_hints; + if (ace_trivial_common(aclp, 0, zfs_ace_walk) == 0) + zp->z_phys->zp_flags |= ZFS_ACL_TRIVIAL; + + zfs_time_stamper_locked(zp, STATE_CHANGED, tx); return (0); } /* - * Create space for slots_needed ACEs to be append - * to aclp. + * Remove ACE from aclp */ static void -zfs_acl_append(zfs_acl_t *aclp, int slots_needed) +zfs_ace_remove(zfs_acl_t *aclp, void *acep) { - ace_t *newacep; - ace_t *oldaclp; - int slot_cnt; - int slots_left = aclp->z_slots - aclp->z_acl_count; + zfs_acl_node_t *currnode = zfs_acl_curr_node(aclp); + size_t length; - if (aclp->z_state == ACL_DATA_ALLOCED) - ASSERT(aclp->z_slots >= aclp->z_acl_count); - if (slots_left < slots_needed || aclp->z_state != ACL_DATA_ALLOCED) { - slot_cnt = aclp->z_slots + 1 + (slots_needed - slots_left); - newacep = kmem_alloc(ZFS_ACL_SIZE(slot_cnt), KM_SLEEP); - bcopy(aclp->z_acl, newacep, - ZFS_ACL_SIZE(aclp->z_acl_count)); - oldaclp = aclp->z_acl; - if (aclp->z_state == ACL_DATA_ALLOCED) - kmem_free(oldaclp, ZFS_ACL_SIZE(aclp->z_slots)); - aclp->z_acl = newacep; - aclp->z_slots = slot_cnt; - aclp->z_state = ACL_DATA_ALLOCED; + /* + * If first entry then just alter acldata ptr + * + * Otherwise split node in two + */ + if (currnode->z_ace_count > 1) { + length = currnode->z_size - ((caddr_t)aclp->z_next_ace - + (caddr_t)currnode->z_acldata); + (void) memmove(acep, aclp->z_next_ace, length); + currnode->z_size = currnode->z_size - + aclp->z_ops.ace_size(acep); + aclp->z_next_ace = acep; + aclp->z_acl_bytes -= ((caddr_t)aclp->z_next_ace - + (caddr_t)currnode->z_acldata); + } else { + list_remove(&aclp->z_acl, currnode); + aclp->z_next_ace = NULL; + aclp->z_acl_bytes = 0; } -} - -/* - * Remove "slot" ACE from aclp - */ -static void -zfs_ace_remove(zfs_acl_t *aclp, int slot) -{ - if (aclp->z_acl_count > 1) { - (void) memmove(&aclp->z_acl[slot], - &aclp->z_acl[slot +1], sizeof (ace_t) * - (--aclp->z_acl_count - slot)); - } else - aclp->z_acl_count--; + currnode->z_ace_count--; + currnode->z_ace_idx--; + aclp->z_acl_count--; } /* @@ -516,16 +1138,24 @@ zfs_ace_remove(zfs_acl_t *aclp, int slot) * This applies the "groupmask" value for aclmode property. */ static void -zfs_acl_prepend_fixup(ace_t *acep, ace_t *origacep, mode_t mode, uid_t owner) +zfs_acl_prepend_fixup(zfs_acl_t *aclp, void *acep, void *origacep, + mode_t mode, uint64_t owner) { - int rmask, wmask, xmask; int user_ace; + uint16_t aceflags; + uint32_t origmask, acepmask; + uint64_t fuid; + + aceflags = aclp->z_ops.ace_flags_get(acep); + fuid = aclp->z_ops.ace_who_get(acep); + origmask = aclp->z_ops.ace_mask_get(origacep); + acepmask = aclp->z_ops.ace_mask_get(acep); - user_ace = (!(acep->a_flags & + user_ace = (!(aceflags & (ACE_OWNER|ACE_GROUP|ACE_IDENTIFIER_GROUP))); - if (user_ace && (acep->a_who == owner)) { + if (user_ace && (fuid == owner)) { rmask = S_IRUSR; wmask = S_IWUSR; xmask = S_IXUSR; @@ -535,33 +1165,38 @@ zfs_acl_prepend_fixup(ace_t *acep, ace_t *origacep, mode_t mode, uid_t owner) xmask = S_IXGRP; } - if (origacep->a_access_mask & ACE_READ_DATA) { - if (mode & rmask) - acep->a_access_mask &= ~ACE_READ_DATA; - else - acep->a_access_mask |= ACE_READ_DATA; + if (origmask & ACE_READ_DATA) { + if (mode & rmask) { + acepmask &= ~ACE_READ_DATA; + } else { + acepmask |= ACE_READ_DATA; + } } - if (origacep->a_access_mask & ACE_WRITE_DATA) { - if (mode & wmask) - acep->a_access_mask &= ~ACE_WRITE_DATA; - else - acep->a_access_mask |= ACE_WRITE_DATA; + if (origmask & ACE_WRITE_DATA) { + if (mode & wmask) { + acepmask &= ~ACE_WRITE_DATA; + } else { + acepmask |= ACE_WRITE_DATA; + } } - if (origacep->a_access_mask & ACE_APPEND_DATA) { - if (mode & wmask) - acep->a_access_mask &= ~ACE_APPEND_DATA; - else - acep->a_access_mask |= ACE_APPEND_DATA; + if (origmask & ACE_APPEND_DATA) { + if (mode & wmask) { + acepmask &= ~ACE_APPEND_DATA; + } else { + acepmask |= ACE_APPEND_DATA; + } } - if (origacep->a_access_mask & ACE_EXECUTE) { - if (mode & xmask) - acep->a_access_mask &= ~ACE_EXECUTE; - else - acep->a_access_mask |= ACE_EXECUTE; + if (origmask & ACE_EXECUTE) { + if (mode & xmask) { + acepmask &= ~ACE_EXECUTE; + } else { + acepmask |= ACE_EXECUTE; + } } + aclp->z_ops.ace_mask_set(acep, acepmask); } /* @@ -570,116 +1205,156 @@ zfs_acl_prepend_fixup(ace_t *acep, ace_t *origacep, mode_t mode, uid_t owner) static void zfs_acl_fixup_canonical_six(zfs_acl_t *aclp, mode_t mode) { - int cnt; - ace_t *acep; + zfs_acl_node_t *aclnode = list_tail(&aclp->z_acl); + void *acep; + int maskoff = aclp->z_ops.ace_mask_off(); + size_t abstract_size = aclp->z_ops.ace_abstract_size(); - cnt = aclp->z_acl_count -1; - acep = aclp->z_acl; + ASSERT(aclnode != NULL); + + acep = (void *)((caddr_t)aclnode->z_acldata + + aclnode->z_size - (abstract_size * 6)); /* * Fixup final ACEs to match the mode */ - ASSERT(cnt >= 5); - adjust_ace_pair(&acep[cnt - 1], mode); /* everyone@ */ - adjust_ace_pair(&acep[cnt - 3], (mode & 0070) >> 3); /* group@ */ - adjust_ace_pair(&acep[cnt - 5], (mode & 0700) >> 6); /* owner@ */ + adjust_ace_pair_common(acep, maskoff, abstract_size, + (mode & 0700) >> 6); /* owner@ */ + + acep = (caddr_t)acep + (abstract_size * 2); + + adjust_ace_pair_common(acep, maskoff, abstract_size, + (mode & 0070) >> 3); /* group@ */ + + acep = (caddr_t)acep + (abstract_size * 2); + adjust_ace_pair_common(acep, maskoff, + abstract_size, mode); /* everyone@ */ } static int -zfs_acl_ace_match(ace_t *acep, int allow_deny, int type, int mask) +zfs_acl_ace_match(zfs_acl_t *aclp, void *acep, int allow_deny, + int entry_type, int accessmask) { - return (acep->a_access_mask == mask && acep->a_type == allow_deny && - ((acep->a_flags & ACE_TYPE_FLAGS) == type)); + uint32_t mask = aclp->z_ops.ace_mask_get(acep); + uint16_t type = aclp->z_ops.ace_type_get(acep); + uint16_t flags = aclp->z_ops.ace_flags_get(acep); + + return (mask == accessmask && type == allow_deny && + ((flags & ACE_TYPE_FLAGS) == entry_type)); } /* * Can prepended ACE be reused? */ static int -zfs_reuse_deny(ace_t *acep, int i) +zfs_reuse_deny(zfs_acl_t *aclp, void *acep, void *prevacep) { int okay_masks; + uint16_t prevtype; + uint16_t prevflags; + uint16_t flags; + uint32_t mask, prevmask; - if (i < 1) + if (prevacep == NULL) return (B_FALSE); - if (acep[i-1].a_type != DENY) + prevtype = aclp->z_ops.ace_type_get(prevacep); + prevflags = aclp->z_ops.ace_flags_get(prevacep); + flags = aclp->z_ops.ace_flags_get(acep); + mask = aclp->z_ops.ace_mask_get(acep); + prevmask = aclp->z_ops.ace_mask_get(prevacep); + + if (prevtype != DENY) return (B_FALSE); - if (acep[i-1].a_flags != (acep[i].a_flags & ACE_IDENTIFIER_GROUP)) + if (prevflags != (flags & ACE_IDENTIFIER_GROUP)) return (B_FALSE); - okay_masks = (acep[i].a_access_mask & OKAY_MASK_BITS); + okay_masks = (mask & OKAY_MASK_BITS); - if (acep[i-1].a_access_mask & ~okay_masks) + if (prevmask & ~okay_masks) return (B_FALSE); return (B_TRUE); } + /* - * Create space to prepend an ACE + * Insert new ACL node into chain of zfs_acl_node_t's + * + * This will result in two possible results. + * 1. If the ACL is currently just a single zfs_acl_node and + * we are prepending the entry then current acl node will have + * a new node inserted above it. + * + * 2. If we are inserting in the middle of current acl node then + * the current node will be split in two and new node will be inserted + * in between the two split nodes. */ -static void -zfs_acl_prepend(zfs_acl_t *aclp, int i) -{ - ace_t *oldaclp = NULL; - ace_t *to, *from; - int slots_left = aclp->z_slots - aclp->z_acl_count; - int oldslots; - int need_free = 0; - - if (aclp->z_state == ACL_DATA_ALLOCED) - ASSERT(aclp->z_slots >= aclp->z_acl_count); - - if (slots_left == 0 || aclp->z_state != ACL_DATA_ALLOCED) { - - to = kmem_alloc(ZFS_ACL_SIZE(aclp->z_acl_count + - OGE_PAD), KM_SLEEP); - if (aclp->z_state == ACL_DATA_ALLOCED) - need_free++; - from = aclp->z_acl; - oldaclp = aclp->z_acl; - (void) memmove(to, from, - sizeof (ace_t) * aclp->z_acl_count); - aclp->z_state = ACL_DATA_ALLOCED; - } else { - from = aclp->z_acl; - to = aclp->z_acl; +static zfs_acl_node_t * +zfs_acl_ace_insert(zfs_acl_t *aclp, void *acep) +{ + zfs_acl_node_t *newnode; + zfs_acl_node_t *trailernode = NULL; + zfs_acl_node_t *currnode = zfs_acl_curr_node(aclp); + int curr_idx = aclp->z_curr_node->z_ace_idx; + int trailer_count; + size_t oldsize; + + newnode = zfs_acl_node_alloc(aclp->z_ops.ace_size(acep)); + newnode->z_ace_count = 1; + + oldsize = currnode->z_size; + + if (curr_idx != 1) { + trailernode = zfs_acl_node_alloc(0); + trailernode->z_acldata = acep; + + trailer_count = currnode->z_ace_count - curr_idx + 1; + currnode->z_ace_count = curr_idx - 1; + currnode->z_size = (caddr_t)acep - (caddr_t)currnode->z_acldata; + trailernode->z_size = oldsize - currnode->z_size; + trailernode->z_ace_count = trailer_count; } - - (void) memmove(&to[i + 1], &from[i], - sizeof (ace_t) * (aclp->z_acl_count - i)); - - if (oldaclp) { - aclp->z_acl = to; - oldslots = aclp->z_slots; - aclp->z_slots = aclp->z_acl_count + OGE_PAD; - if (need_free) - kmem_free(oldaclp, ZFS_ACL_SIZE(oldslots)); + aclp->z_acl_count += 1; + aclp->z_acl_bytes += aclp->z_ops.ace_size(acep); + + if (curr_idx == 1) + list_insert_before(&aclp->z_acl, currnode, newnode); + else + list_insert_after(&aclp->z_acl, currnode, newnode); + if (trailernode) { + list_insert_after(&aclp->z_acl, newnode, trailernode); + aclp->z_curr_node = trailernode; + trailernode->z_ace_idx = 1; } + return (newnode); } /* * Prepend deny ACE */ -static void -zfs_acl_prepend_deny(znode_t *zp, zfs_acl_t *aclp, int i, +static void * +zfs_acl_prepend_deny(znode_t *zp, zfs_acl_t *aclp, void *acep, mode_t mode) { - ace_t *acep; - - zfs_acl_prepend(aclp, i); - - acep = aclp->z_acl; - zfs_set_ace(&acep[i], 0, DENY, acep[i + 1].a_who, - (acep[i + 1].a_flags & ACE_TYPE_FLAGS)); - zfs_acl_prepend_fixup(&acep[i], &acep[i+1], mode, zp->z_phys->zp_uid); - aclp->z_acl_count++; + zfs_acl_node_t *aclnode; + void *newacep; + uint64_t fuid; + uint16_t flags; + + aclnode = zfs_acl_ace_insert(aclp, acep); + newacep = aclnode->z_acldata; + fuid = aclp->z_ops.ace_who_get(acep); + flags = aclp->z_ops.ace_flags_get(acep); + zfs_set_ace(aclp, newacep, 0, DENY, fuid, (flags & ACE_TYPE_FLAGS)); + zfs_acl_prepend_fixup(aclp, newacep, acep, mode, zp->z_phys->zp_uid); + + return (newacep); } /* @@ -687,41 +1362,72 @@ zfs_acl_prepend_deny(znode_t *zp, zfs_acl_t *aclp, int i, * and original ACE with inheritance flags stripped off. */ static void -zfs_acl_split_ace(zfs_acl_t *aclp, int i) +zfs_acl_split_ace(zfs_acl_t *aclp, zfs_ace_hdr_t *acep) { - ace_t *acep = aclp->z_acl; - - zfs_acl_prepend(aclp, i); - acep = aclp->z_acl; - acep[i] = acep[i + 1]; - acep[i].a_flags |= ACE_INHERIT_ONLY_ACE; - acep[i + 1].a_flags &= ~ALL_INHERIT; - aclp->z_acl_count++; + zfs_acl_node_t *aclnode; + zfs_acl_node_t *currnode = zfs_acl_curr_node(aclp); + void *newacep; + uint16_t type, flags; + uint32_t mask; + uint64_t fuid; + + type = aclp->z_ops.ace_type_get(acep); + flags = aclp->z_ops.ace_flags_get(acep); + mask = aclp->z_ops.ace_mask_get(acep); + fuid = aclp->z_ops.ace_who_get(acep); + + aclnode = zfs_acl_ace_insert(aclp, acep); + newacep = aclnode->z_acldata; + + aclp->z_ops.ace_type_set(newacep, type); + aclp->z_ops.ace_flags_set(newacep, flags | ACE_INHERIT_ONLY_ACE); + aclp->z_ops.ace_mask_set(newacep, mask); + aclp->z_ops.ace_type_set(newacep, type); + aclp->z_ops.ace_who_set(newacep, fuid); + aclp->z_next_ace = acep; + flags &= ~ALL_INHERIT; + aclp->z_ops.ace_flags_set(acep, flags); + currnode->z_ace_idx -= 1; } /* * Are ACES started at index i, the canonical six ACES? */ static int -zfs_have_canonical_six(zfs_acl_t *aclp, int i) +zfs_have_canonical_six(zfs_acl_t *aclp) { - ace_t *acep = aclp->z_acl; + void *acep; + zfs_acl_node_t *aclnode = list_tail(&aclp->z_acl); + int i = 0; + size_t abstract_size = aclp->z_ops.ace_abstract_size(); + + ASSERT(aclnode != NULL); + + if (aclnode->z_ace_count < 6) + return (0); - if ((zfs_acl_ace_match(&acep[i], + acep = (void *)((caddr_t)aclnode->z_acldata + + aclnode->z_size - (aclp->z_ops.ace_abstract_size() * 6)); + + if ((zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++), DENY, ACE_OWNER, 0) && - zfs_acl_ace_match(&acep[i + 1], ALLOW, ACE_OWNER, - OWNER_ALLOW_MASK) && zfs_acl_ace_match(&acep[i + 2], - DENY, OWNING_GROUP, 0) && zfs_acl_ace_match(&acep[i + 3], - ALLOW, OWNING_GROUP, 0) && zfs_acl_ace_match(&acep[i + 4], + zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++), + ALLOW, ACE_OWNER, OWNER_ALLOW_MASK) && + zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++), DENY, + OWNING_GROUP, 0) && zfs_acl_ace_match(aclp, (caddr_t)acep + + (abstract_size * i++), + ALLOW, OWNING_GROUP, 0) && + zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++), DENY, ACE_EVERYONE, EVERYONE_DENY_MASK) && - zfs_acl_ace_match(&acep[i + 5], ALLOW, ACE_EVERYONE, - EVERYONE_ALLOW_MASK))) { + zfs_acl_ace_match(aclp, (caddr_t)acep + (abstract_size * i++), + ALLOW, ACE_EVERYONE, EVERYONE_ALLOW_MASK))) { return (1); } else { return (0); } } + /* * Apply step 1g, to group entries * @@ -731,32 +1437,36 @@ zfs_have_canonical_six(zfs_acl_t *aclp, int i) * group has. */ static void -zfs_fixup_group_entries(ace_t *acep, mode_t mode) +zfs_fixup_group_entries(zfs_acl_t *aclp, void *acep, void *prevacep, + mode_t mode) { + uint32_t prevmask = aclp->z_ops.ace_mask_get(prevacep); + uint32_t mask = aclp->z_ops.ace_mask_get(acep); + uint16_t prevflags = aclp->z_ops.ace_flags_get(prevacep); mode_t extramode = (mode >> 3) & 07; mode_t ownermode = (mode >> 6); - if (acep[0].a_flags & ACE_IDENTIFIER_GROUP) { + if (prevflags & ACE_IDENTIFIER_GROUP) { extramode &= ~ownermode; if (extramode) { - if (extramode & 04) { - acep[0].a_access_mask &= ~ACE_READ_DATA; - acep[1].a_access_mask &= ~ACE_READ_DATA; + if (extramode & S_IROTH) { + prevmask &= ~ACE_READ_DATA; + mask &= ~ACE_READ_DATA; } - if (extramode & 02) { - acep[0].a_access_mask &= - ~(ACE_WRITE_DATA|ACE_APPEND_DATA); - acep[1].a_access_mask &= - ~(ACE_WRITE_DATA|ACE_APPEND_DATA); + if (extramode & S_IWOTH) { + prevmask &= ~(ACE_WRITE_DATA|ACE_APPEND_DATA); + mask &= ~(ACE_WRITE_DATA|ACE_APPEND_DATA); } - if (extramode & 01) { - acep[0].a_access_mask &= ~ACE_EXECUTE; - acep[1].a_access_mask &= ~ACE_EXECUTE; + if (extramode & S_IXOTH) { + prevmask &= ~ACE_EXECUTE; + mask &= ~ACE_EXECUTE; } } } + aclp->z_ops.ace_mask_set(acep, mask); + aclp->z_ops.ace_mask_set(prevacep, prevmask); } /* @@ -768,58 +1478,64 @@ zfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp, dmu_tx_t *tx) { zfsvfs_t *zfsvfs = zp->z_zfsvfs; - ace_t *acep; + void *acep = NULL, *prevacep = NULL; + uint64_t who; int i; int error; int entry_type; int reuse_deny; int need_canonical_six = 1; - int inherit = 0; - int iflags; + uint16_t iflags, type; + uint32_t access_mask; ASSERT(MUTEX_HELD(&zp->z_acl_lock)); ASSERT(MUTEX_HELD(&zp->z_lock)); - i = 0; - while (i < aclp->z_acl_count) { - acep = aclp->z_acl; - entry_type = (acep[i].a_flags & ACE_TYPE_FLAGS); - iflags = (acep[i].a_flags & ALL_INHERIT); + aclp->z_hints = (zp->z_phys->zp_flags & V4_ACL_WIDE_FLAGS); + while (acep = zfs_acl_next_ace(aclp, acep, &who, &access_mask, + &iflags, &type)) { - if ((acep[i].a_type != ALLOW && acep[i].a_type != DENY) || + entry_type = (iflags & ACE_TYPE_FLAGS); + iflags = (iflags & ALL_INHERIT); + + if ((type != ALLOW && type != DENY) || (iflags & ACE_INHERIT_ONLY_ACE)) { - i++; if (iflags) - inherit = 1; - continue; + aclp->z_hints |= ZFS_INHERIT_ACE; + switch (type) { + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE: + aclp->z_hints |= ZFS_ACL_OBJ_ACE; + break; + } + goto nextace; } - if (zfsvfs->z_acl_mode == ZFS_ACL_DISCARD) { - zfs_ace_remove(aclp, i); - continue; + zfs_ace_remove(aclp, acep); + goto nextace; } - /* * Need to split ace into two? */ if ((iflags & (ACE_FILE_INHERIT_ACE| ACE_DIRECTORY_INHERIT_ACE)) && (!(iflags & ACE_INHERIT_ONLY_ACE))) { - zfs_acl_split_ace(aclp, i); - i++; - inherit = 1; - continue; + zfs_acl_split_ace(aclp, acep); + aclp->z_hints |= ZFS_INHERIT_ACE; + goto nextace; } if (entry_type == ACE_OWNER || entry_type == ACE_EVERYONE || (entry_type == OWNING_GROUP)) { - acep[i].a_access_mask &= ~OGE_CLEAR; - i++; - continue; - + access_mask &= ~OGE_CLEAR; + aclp->z_ops.ace_mask_set(acep, access_mask); + goto nextace; } else { - if (acep[i].a_type == ALLOW) { + reuse_deny = B_TRUE; + if (type == ALLOW) { /* * Check preceding ACE if any, to see @@ -829,25 +1545,27 @@ zfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp, */ if (zfsvfs->z_acl_mode == ZFS_ACL_GROUPMASK) { - reuse_deny = zfs_reuse_deny(acep, i); + reuse_deny = zfs_reuse_deny(aclp, acep, + prevacep); if (reuse_deny == B_FALSE) { - zfs_acl_prepend_deny(zp, aclp, - i, mode); - i++; - acep = aclp->z_acl; + prevacep = + zfs_acl_prepend_deny(zp, + aclp, acep, mode); } else { zfs_acl_prepend_fixup( - &acep[i - 1], - &acep[i], mode, + aclp, prevacep, + acep, mode, zp->z_phys->zp_uid); } - zfs_fixup_group_entries(&acep[i - 1], - mode); + zfs_fixup_group_entries(aclp, acep, + prevacep, mode); + } } - i++; } +nextace: + prevacep = acep; } /* @@ -855,37 +1573,46 @@ zfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp, */ if (aclp->z_acl_count >= 6) { - i = aclp->z_acl_count - 6; - - if (zfs_have_canonical_six(aclp, i)) { + if (zfs_have_canonical_six(aclp)) { need_canonical_six = 0; } } if (need_canonical_six) { - - zfs_acl_append(aclp, 6); - i = aclp->z_acl_count; - acep = aclp->z_acl; - zfs_set_ace(&acep[i++], 0, DENY, -1, ACE_OWNER); - zfs_set_ace(&acep[i++], OWNER_ALLOW_MASK, ALLOW, -1, ACE_OWNER); - zfs_set_ace(&acep[i++], 0, DENY, -1, OWNING_GROUP); - zfs_set_ace(&acep[i++], 0, ALLOW, -1, OWNING_GROUP); - zfs_set_ace(&acep[i++], EVERYONE_DENY_MASK, - DENY, -1, ACE_EVERYONE); - zfs_set_ace(&acep[i++], EVERYONE_ALLOW_MASK, - ALLOW, -1, ACE_EVERYONE); + size_t abstract_size = aclp->z_ops.ace_abstract_size(); + void *zacep; + zfs_acl_node_t *aclnode = + zfs_acl_node_alloc(abstract_size * 6); + + aclnode->z_size = abstract_size * 6; + aclnode->z_ace_count = 6; + aclp->z_acl_bytes += aclnode->z_size; + list_insert_tail(&aclp->z_acl, aclnode); + + zacep = aclnode->z_acldata; + + i = 0; + zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++), + 0, DENY, -1, ACE_OWNER); + zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++), + OWNER_ALLOW_MASK, ALLOW, -1, ACE_OWNER); + zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++), 0, + DENY, -1, OWNING_GROUP); + zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++), 0, + ALLOW, -1, OWNING_GROUP); + zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++), + EVERYONE_DENY_MASK, DENY, -1, ACE_EVERYONE); + zfs_set_ace(aclp, (caddr_t)zacep + (abstract_size * i++), + EVERYONE_ALLOW_MASK, ALLOW, -1, ACE_EVERYONE); aclp->z_acl_count += 6; } zfs_acl_fixup_canonical_six(aclp, mode); - zp->z_phys->zp_mode = mode; - error = zfs_aclset_common(zp, aclp, tx, &inherit); + error = zfs_aclset_common(zp, aclp, NULL, tx); return (error); } - int zfs_acl_chmod_setattr(znode_t *zp, uint64_t mode, dmu_tx_t *tx) { @@ -894,7 +1621,7 @@ zfs_acl_chmod_setattr(znode_t *zp, uint64_t mode, dmu_tx_t *tx) ASSERT(MUTEX_HELD(&zp->z_lock)); mutex_enter(&zp->z_acl_lock); - error = zfs_acl_node_read(zp, &aclp); + error = zfs_acl_node_read(zp, &aclp, B_TRUE); if (error == 0) error = zfs_acl_chmod(zp, mode, aclp, tx); mutex_exit(&zp->z_acl_lock); @@ -907,11 +1634,32 @@ zfs_acl_chmod_setattr(znode_t *zp, uint64_t mode, dmu_tx_t *tx) * strip off write_owner and write_acl */ static void -zfs_securemode_update(zfsvfs_t *zfsvfs, ace_t *acep) +zfs_securemode_update(zfsvfs_t *zfsvfs, zfs_acl_t *aclp, void *acep) { + uint32_t mask = aclp->z_ops.ace_mask_get(acep); + if ((zfsvfs->z_acl_inherit == ZFS_ACL_SECURE) && - (acep->a_type == ALLOW)) - acep->a_access_mask &= ~SECURE_CLEAR; + (aclp->z_ops.ace_type_get(acep) == ALLOW)) { + mask &= ~SECURE_CLEAR; + aclp->z_ops.ace_mask_set(acep, mask); + } +} + +/* + * Should ACE be inherited? + */ +static int +zfs_ace_can_use(znode_t *zp, uint16_t acep_flags) +{ + int vtype = ZTOV(zp)->v_type; + int iflags = (acep_flags & 0xf); + + if ((vtype == VDIR) && (iflags & ACE_DIRECTORY_INHERIT_ACE)) + return (1); + else if (iflags & ACE_FILE_INHERIT_ACE) + return (!((vtype == VDIR) && + (iflags & ACE_NO_PROPAGATE_INHERIT_ACE))); + return (0); } /* @@ -921,90 +1669,108 @@ static zfs_acl_t * zfs_acl_inherit(znode_t *zp, zfs_acl_t *paclp) { zfsvfs_t *zfsvfs = zp->z_zfsvfs; - ace_t *pacep; - ace_t *acep; - int ace_cnt = 0; - int pace_cnt; - int i, j; + void *pacep; + void *acep, *acep2; + zfs_acl_node_t *aclnode, *aclnode2; zfs_acl_t *aclp = NULL; - - i = j = 0; - pace_cnt = paclp->z_acl_count; - pacep = paclp->z_acl; + uint64_t who; + uint32_t access_mask; + uint16_t iflags, newflags, type; + size_t ace_size; + void *data1, *data2; + size_t data1sz, data2sz; + + pacep = NULL; + aclp = zfs_acl_alloc(zfs_acl_version_zp(zp)); if (zfsvfs->z_acl_inherit != ZFS_ACL_DISCARD) { - for (i = 0; i != pace_cnt; i++) { - - if (zfsvfs->z_acl_inherit == ZFS_ACL_NOALLOW && - pacep[i].a_type == ALLOW) - continue; - - if (zfs_ace_can_use(zp, &pacep[i])) { - ace_cnt++; - if (!(pacep[i].a_flags & - ACE_NO_PROPAGATE_INHERIT_ACE)) - ace_cnt++; - } - } - } - - aclp = zfs_acl_alloc(ace_cnt + OGE_PAD); - if (ace_cnt && zfsvfs->z_acl_inherit != ZFS_ACL_DISCARD) { - acep = aclp->z_acl; - pacep = paclp->z_acl; - for (i = 0; i != pace_cnt; i++) { + while (pacep = zfs_acl_next_ace(paclp, pacep, &who, + &access_mask, &iflags, &type)) { if (zfsvfs->z_acl_inherit == ZFS_ACL_NOALLOW && - pacep[i].a_type == ALLOW) + type == ALLOW) continue; - if (zfs_ace_can_use(zp, &pacep[i])) { + ace_size = aclp->z_ops.ace_size(pacep); - /* - * Now create entry for inherited ace - */ + if (zfs_ace_can_use(zp, iflags)) { + aclnode = + zfs_acl_node_alloc(ace_size); - acep[j] = pacep[i]; + list_insert_tail(&aclp->z_acl, aclnode); + acep = aclnode->z_acldata; + zfs_set_ace(aclp, acep, access_mask, type, + who, iflags|ACE_INHERITED_ACE); /* - * When AUDIT/ALARM a_types are supported - * they should be inherited here. + * Copy special opaque data if any */ - - if ((pacep[i].a_flags & + if ((data1sz = paclp->z_ops.ace_data(pacep, + &data1)) != 0) { + VERIFY((data2sz = + aclp->z_ops.ace_data(acep, + &data2)) == data1sz); + bcopy(data1, data2, data2sz); + } + aclp->z_acl_count++; + aclnode->z_ace_count++; + aclp->z_acl_bytes += aclnode->z_size; + newflags = aclp->z_ops.ace_flags_get(acep); + if ((iflags & ACE_NO_PROPAGATE_INHERIT_ACE) || (ZTOV(zp)->v_type != VDIR)) { - acep[j].a_flags &= ~ALL_INHERIT; - zfs_securemode_update(zfsvfs, &acep[j]); - j++; + newflags &= ~ALL_INHERIT; + aclp->z_ops.ace_flags_set(acep, + newflags|ACE_INHERITED_ACE); + zfs_securemode_update(zfsvfs, + aclp, acep); continue; } ASSERT(ZTOV(zp)->v_type == VDIR); - /* - * If we are inheriting an ACE targeted for - * only files, then make sure inherit_only - * is on for future propagation. - */ - if ((pacep[i].a_flags & (ACE_FILE_INHERIT_ACE | + newflags = aclp->z_ops.ace_flags_get(acep); + if ((iflags & (ACE_FILE_INHERIT_ACE | ACE_DIRECTORY_INHERIT_ACE)) != ACE_FILE_INHERIT_ACE) { - j++; - acep[j] = acep[j-1]; - acep[j-1].a_flags |= - ACE_INHERIT_ONLY_ACE; - acep[j].a_flags &= ~ALL_INHERIT; + aclnode2 = zfs_acl_node_alloc(ace_size); + list_insert_tail(&aclp->z_acl, + aclnode2); + acep2 = aclnode2->z_acldata; + zfs_set_ace(aclp, acep2, + access_mask, type, who, + iflags|ACE_INHERITED_ACE); + newflags |= ACE_INHERIT_ONLY_ACE; + aclp->z_ops.ace_flags_set(acep, + newflags); + newflags &= ~ALL_INHERIT; + aclp->z_ops.ace_flags_set(acep2, + newflags|ACE_INHERITED_ACE); + + /* + * Copy special opaque data if any + */ + if ((data1sz = + aclp->z_ops.ace_data(acep, + &data1)) != 0) { + VERIFY((data2sz = + aclp->z_ops.ace_data(acep2, + &data2)) == data1sz); + bcopy(data1, data2, data1sz); + } + aclp->z_acl_count++; + aclnode2->z_ace_count++; + aclp->z_acl_bytes += aclnode->z_size; + zfs_securemode_update(zfsvfs, + aclp, acep2); } else { - acep[j].a_flags |= ACE_INHERIT_ONLY_ACE; + newflags |= ACE_INHERIT_ONLY_ACE; + aclp->z_ops.ace_flags_set(acep, + newflags|ACE_INHERITED_ACE); } - zfs_securemode_update(zfsvfs, &acep[j]); - j++; + } } } - aclp->z_acl_count = j; - ASSERT(aclp->z_slots >= aclp->z_acl_count); - return (aclp); } @@ -1014,14 +1780,21 @@ zfs_acl_inherit(znode_t *zp, zfs_acl_t *paclp) */ void zfs_perm_init(znode_t *zp, znode_t *parent, int flag, - vattr_t *vap, dmu_tx_t *tx, cred_t *cr) + vattr_t *vap, dmu_tx_t *tx, cred_t *cr, + zfs_acl_t *setaclp, zfs_fuid_info_t **fuidp) { uint64_t mode; - uid_t uid; - gid_t gid; + uint64_t uid; + uint64_t gid; int error; int pull_down; - zfs_acl_t *aclp, *paclp; + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + zfs_acl_t *aclp = NULL; + zfs_acl_t *paclp; + xvattr_t *xvap = (xvattr_t *)vap; + + if (setaclp) + aclp = setaclp; mode = MAKEIMODE(vap->va_type, vap->va_mode); @@ -1030,18 +1803,25 @@ zfs_perm_init(znode_t *zp, znode_t *parent, int flag, */ if ((flag & (IS_ROOT_NODE | IS_REPLAY)) || ((flag & IS_XATTR) && (vap->va_type == VDIR))) { - uid = vap->va_uid; - gid = vap->va_gid; + uid = zfs_fuid_create(zfsvfs, vap->va_uid, + ZFS_OWNER, tx, fuidp); + gid = zfs_fuid_create(zfsvfs, vap->va_gid, + ZFS_GROUP, tx, fuidp); } else { - uid = crgetuid(cr); + uid = zfs_fuid_create_cred(zfsvfs, crgetuid(cr), + ZFS_OWNER, tx, cr, fuidp); if ((vap->va_mask & AT_GID) && ((vap->va_gid == parent->z_phys->zp_gid) || groupmember(vap->va_gid, cr) || - secpolicy_vnode_create_gid(cr) == 0)) - gid = vap->va_gid; - else + secpolicy_vnode_create_gid(cr) == 0)) { + gid = zfs_fuid_create_cred(zfsvfs, vap->va_gid, + ZFS_GROUP, tx, cr, fuidp); + } else { gid = (parent->z_phys->zp_mode & S_ISGID) ? parent->z_phys->zp_gid : crgetgid(cr); + gid = zfs_fuid_create_cred(zfsvfs, gid, + ZFS_GROUP, tx, cr, fuidp); + } } /* @@ -1063,85 +1843,133 @@ zfs_perm_init(znode_t *zp, znode_t *parent, int flag, zp->z_phys->zp_gid = gid; zp->z_phys->zp_mode = mode; - mutex_enter(&parent->z_lock); - pull_down = (parent->z_phys->zp_flags & ZFS_INHERIT_ACE); - if (pull_down) { - mutex_enter(&parent->z_acl_lock); - VERIFY(0 == zfs_acl_node_read(parent, &paclp)); - mutex_exit(&parent->z_acl_lock); - aclp = zfs_acl_inherit(zp, paclp); - zfs_acl_free(paclp); + if (aclp == NULL) { + mutex_enter(&parent->z_lock); + pull_down = (parent->z_phys->zp_flags & ZFS_INHERIT_ACE); + if (pull_down) { + mutex_enter(&parent->z_acl_lock); + VERIFY(0 == zfs_acl_node_read(parent, &paclp, B_FALSE)); + mutex_exit(&parent->z_acl_lock); + aclp = zfs_acl_inherit(zp, paclp); + zfs_acl_free(paclp); + } else { + aclp = zfs_acl_alloc(zfs_acl_version_zp(zp)); + } + mutex_exit(&parent->z_lock); + mutex_enter(&zp->z_lock); + mutex_enter(&zp->z_acl_lock); + error = zfs_acl_chmod(zp, mode, aclp, tx); } else { - aclp = zfs_acl_alloc(6); + mutex_enter(&zp->z_lock); + mutex_enter(&zp->z_acl_lock); } - mutex_exit(&parent->z_lock); - mutex_enter(&zp->z_lock); - mutex_enter(&zp->z_acl_lock); - error = zfs_acl_chmod(zp, mode, aclp, tx); + + /* Force auto_inherit on all new directory objects */ + if (vap->va_type == VDIR) + aclp->z_hints |= ZFS_ACL_AUTO_INHERIT; + + error = zfs_aclset_common(zp, aclp, fuidp, tx); + + /* Set optional attributes if any */ + if (vap->va_mask & AT_XVATTR) + zfs_xvattr_set(zp, xvap); + mutex_exit(&zp->z_lock); mutex_exit(&zp->z_acl_lock); ASSERT3U(error, ==, 0); - zfs_acl_free(aclp); -} - -/* - * Should ACE be inherited? - */ -static int -zfs_ace_can_use(znode_t *zp, ace_t *acep) -{ - int vtype = ZTOV(zp)->v_type; - int iflags = (acep->a_flags & 0xf); - - if ((vtype == VDIR) && (iflags & ACE_DIRECTORY_INHERIT_ACE)) - return (1); - else if (iflags & ACE_FILE_INHERIT_ACE) - return (!((vtype == VDIR) && - (iflags & ACE_NO_PROPAGATE_INHERIT_ACE))); - return (0); + if (aclp != setaclp) { + zfs_acl_free(aclp); + } } /* * Retrieve a files ACL */ int -zfs_getacl(znode_t *zp, vsecattr_t *vsecp, cred_t *cr) +zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) { zfs_acl_t *aclp; - ulong_t mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT); + ulong_t mask; int error; + int count = 0; + int largeace = 0; - if (error = zfs_zaccess(zp, ACE_READ_ACL, cr)) { - /* - * If owner of file then allow reading of the - * ACL. - */ - if (crgetuid(cr) != zp->z_phys->zp_uid) - return (error); - } + mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT | + VSA_ACE_ACLFLAGS | VSA_ACE_ALLTYPES); + + if (error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr)) + return (error); if (mask == 0) return (ENOSYS); mutex_enter(&zp->z_acl_lock); - error = zfs_acl_node_read(zp, &aclp); + error = zfs_acl_node_read(zp, &aclp, B_FALSE); if (error != 0) { mutex_exit(&zp->z_acl_lock); return (error); } + /* + * Scan ACL to determine number of ACEs + */ + if ((zp->z_phys->zp_flags & ZFS_ACL_OBJ_ACE) && + !(mask & VSA_ACE_ALLTYPES)) { + void *zacep = NULL; + uint64_t who; + uint32_t access_mask; + uint16_t type, iflags; + + while (zacep = zfs_acl_next_ace(aclp, zacep, + &who, &access_mask, &iflags, &type)) { + switch (type) { + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE: + largeace++; + continue; + default: + count++; + } + } + vsecp->vsa_aclcnt = count; + } else + count = aclp->z_acl_count; if (mask & VSA_ACECNT) { - vsecp->vsa_aclcnt = aclp->z_acl_count; + vsecp->vsa_aclcnt = count; } if (mask & VSA_ACE) { - vsecp->vsa_aclentp = kmem_alloc(aclp->z_acl_count * - sizeof (ace_t), KM_SLEEP); - bcopy(aclp->z_acl, vsecp->vsa_aclentp, - aclp->z_acl_count * sizeof (ace_t)); + size_t aclsz; + + zfs_acl_node_t *aclnode = list_head(&aclp->z_acl); + + aclsz = count * sizeof (ace_t) + + sizeof (ace_object_t) * largeace; + + vsecp->vsa_aclentp = kmem_alloc(aclsz, KM_SLEEP); + vsecp->vsa_aclentsz = aclsz; + + if (aclp->z_version == ZFS_ACL_VERSION_FUID) + zfs_copy_fuid_2_ace(zp->z_zfsvfs, aclp, + vsecp->vsa_aclentp, !(mask & VSA_ACE_ALLTYPES)); + else { + bcopy(aclnode->z_acldata, vsecp->vsa_aclentp, + count * sizeof (ace_t)); + } + } + if (mask & VSA_ACE_ACLFLAGS) { + vsecp->vsa_aclflags = 0; + if (zp->z_phys->zp_flags & ZFS_ACL_DEFAULTED) + vsecp->vsa_aclflags |= ACL_DEFAULTED; + if (zp->z_phys->zp_flags & ZFS_ACL_PROTECTED) + vsecp->vsa_aclflags |= ACL_PROTECTED; + if (zp->z_phys->zp_flags & ZFS_ACL_AUTO_INHERIT) + vsecp->vsa_aclflags |= ACL_AUTO_INHERIT; } mutex_exit(&zp->z_acl_lock); @@ -1151,36 +1979,99 @@ zfs_getacl(znode_t *zp, vsecattr_t *vsecp, cred_t *cr) return (0); } +int +zfs_vsec_2_aclp(zfsvfs_t *zfsvfs, vtype_t obj_type, + vsecattr_t *vsecp, zfs_acl_t **zaclp) +{ + zfs_acl_t *aclp; + zfs_acl_node_t *aclnode; + int aclcnt = vsecp->vsa_aclcnt; + int error; + + if (vsecp->vsa_aclcnt > MAX_ACL_ENTRIES || vsecp->vsa_aclcnt <= 0) + return (EINVAL); + + aclp = zfs_acl_alloc(zfs_acl_version(zfsvfs->z_version)); + + aclp->z_hints = 0; + aclnode = zfs_acl_node_alloc(aclcnt * sizeof (zfs_object_ace_t)); + if (aclp->z_version == ZFS_ACL_VERSION_INITIAL) { + if ((error = zfs_copy_ace_2_oldace(obj_type, aclp, + (ace_t *)vsecp->vsa_aclentp, aclnode->z_acldata, + aclcnt, &aclnode->z_size)) != 0) { + zfs_acl_free(aclp); + zfs_acl_node_free(aclnode); + return (error); + } + } else { + if ((error = zfs_copy_ace_2_fuid(obj_type, aclp, + vsecp->vsa_aclentp, aclnode->z_acldata, aclcnt, + &aclnode->z_size)) != 0) { + zfs_acl_free(aclp); + zfs_acl_node_free(aclnode); + return (error); + } + } + aclp->z_acl_bytes = aclnode->z_size; + aclnode->z_ace_count = aclcnt; + aclp->z_acl_count = aclcnt; + list_insert_head(&aclp->z_acl, aclnode); + + /* + * If flags are being set then add them to z_hints + */ + if (vsecp->vsa_mask & VSA_ACE_ACLFLAGS) { + if (vsecp->vsa_aclflags & ACL_PROTECTED) + aclp->z_hints |= ZFS_ACL_PROTECTED; + if (vsecp->vsa_aclflags & ACL_DEFAULTED) + aclp->z_hints |= ZFS_ACL_DEFAULTED; + if (vsecp->vsa_aclflags & ACL_AUTO_INHERIT) + aclp->z_hints |= ZFS_ACL_AUTO_INHERIT; + } + + *zaclp = aclp; + + return (0); +} + /* * Set a files ACL */ int -zfs_setacl(znode_t *zp, vsecattr_t *vsecp, cred_t *cr) +zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) { zfsvfs_t *zfsvfs = zp->z_zfsvfs; zilog_t *zilog = zfsvfs->z_log; - ace_t *acep = vsecp->vsa_aclentp; - int aclcnt = vsecp->vsa_aclcnt; ulong_t mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT); dmu_tx_t *tx; int error; - int inherit; zfs_acl_t *aclp; + zfs_fuid_info_t *fuidp = NULL; if (mask == 0) return (ENOSYS); - if (!zfs_acl_valid(zp, acep, aclcnt, &inherit)) - return (EINVAL); + if (zp->z_phys->zp_flags & ZFS_IMMUTABLE) + return (EPERM); + + if (error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr)) + return (error); + + error = zfs_vsec_2_aclp(zfsvfs, ZTOV(zp)->v_type, vsecp, &aclp); + if (error) + return (error); + + /* + * If ACL wide flags aren't being set then preserve any + * existing flags. + */ + if (!(vsecp->vsa_mask & VSA_ACE_ACLFLAGS)) { + aclp->z_hints |= (zp->z_phys->zp_flags & V4_ACL_WIDE_FLAGS); + } top: - error = zfs_zaccess_v4_perm(zp, ACE_WRITE_ACL, cr); - if (error == EACCES || error == ACCESS_UNDETERMINED) { - if ((error = secpolicy_vnode_setdac(cr, - zp->z_phys->zp_uid)) != 0) { - return (error); - } - } else if (error) { - return (error == EROFS ? error : EPERM); + if (error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr)) { + zfs_acl_free(aclp); + return (error); } mutex_enter(&zp->z_lock); @@ -1190,10 +2081,32 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, cred_t *cr) dmu_tx_hold_bonus(tx, zp->z_id); if (zp->z_phys->zp_acl.z_acl_extern_obj) { - dmu_tx_hold_write(tx, zp->z_phys->zp_acl.z_acl_extern_obj, - 0, ZFS_ACL_SIZE(aclcnt)); - } else if (aclcnt > ACE_SLOT_CNT) { - dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, ZFS_ACL_SIZE(aclcnt)); + /* Are we upgrading ACL? */ + if (zfsvfs->z_version <= ZPL_VERSION_FUID && + zp->z_phys->zp_acl.z_acl_version == + ZFS_ACL_VERSION_INITIAL) { + dmu_tx_hold_free(tx, + zp->z_phys->zp_acl.z_acl_extern_obj, + 0, DMU_OBJECT_END); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, + 0, sizeof (zfs_object_ace_t) * 2048 + 6); + } else { + dmu_tx_hold_write(tx, + zp->z_phys->zp_acl.z_acl_extern_obj, + 0, aclp->z_acl_bytes); + } + } else if (aclp->z_acl_bytes > ZFS_ACE_SPACE) { + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, aclp->z_acl_bytes); + } + if (zfsvfs->z_fuid_obj == 0) { + dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, + SPA_MAXBLOCKSIZE); + dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL); + } else { + dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); + dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, + SPA_MAXBLOCKSIZE); } error = dmu_tx_assign(tx, zfsvfs->z_assign); @@ -1207,17 +2120,18 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, cred_t *cr) goto top; } dmu_tx_abort(tx); + zfs_acl_free(aclp); return (error); } - aclp = zfs_acl_alloc(aclcnt); - bcopy(acep, aclp->z_acl, sizeof (ace_t) * aclcnt); - aclp->z_acl_count = aclcnt; - error = zfs_aclset_common(zp, aclp, tx, &inherit); + error = zfs_aclset_common(zp, aclp, &fuidp, tx); ASSERT(error == 0); + zfs_log_acl(zilog, tx, zp, vsecp, fuidp); + + if (fuidp) + zfs_fuid_info_free(fuidp); zfs_acl_free(aclp); - zfs_log_acl(zilog, tx, TX_ACL, zp, aclcnt, acep); dmu_tx_commit(tx); done: mutex_exit(&zp->z_acl_lock); @@ -1226,46 +2140,34 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, cred_t *cr) return (error); } +/* + * working_mode returns the permissions that were not granted + */ static int -zfs_ace_access(ace_t *zacep, int *working_mode) -{ - if (*working_mode == 0) { - return (0); - } - - if (zacep->a_access_mask & *working_mode) { - if (zacep->a_type == ALLOW) { - *working_mode &= - ~(*working_mode & zacep->a_access_mask); - if (*working_mode == 0) - return (0); - } else if (zacep->a_type == DENY) { - return (EACCES); - } - } - - /* - * haven't been specifcally denied at this point - * so return UNDETERMINED. - */ - - return (ACCESS_UNDETERMINED); -} - - -static int -zfs_zaccess_common(znode_t *zp, int v4_mode, int *working_mode, cred_t *cr) +zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode, + boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr) { zfs_acl_t *aclp; zfsvfs_t *zfsvfs = zp->z_zfsvfs; - ace_t *zacep; - gid_t gid; - int cnt; - int i; int error; int access_deny = ACCESS_UNDETERMINED; - uint_t entry_type; uid_t uid = crgetuid(cr); + uint64_t who; + uint16_t type, iflags; + uint16_t entry_type; + uint32_t access_mask; + zfs_ace_hdr_t *acep = NULL; + boolean_t checkit; + uid_t fowner; + uid_t gowner; + + /* + * Short circuit empty requests + */ + if (v4_mode == 0) + return (0); + + *check_privs = B_TRUE; if (zfsvfs->z_assign >= TXG_INITIAL) { /* ZIL replay */ *working_mode = 0; @@ -1277,68 +2179,109 @@ zfs_zaccess_common(znode_t *zp, int v4_mode, int *working_mode, cred_t *cr) if ((v4_mode & WRITE_MASK) && (zp->z_zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) && (!IS_DEVVP(ZTOV(zp)))) { + *check_privs = B_FALSE; return (EROFS); } + /* + * Only check for READONLY on non-directories. + */ + if ((v4_mode & WRITE_MASK_DATA) && + (((ZTOV(zp)->v_type != VDIR) && + (zp->z_phys->zp_flags & (ZFS_READONLY | ZFS_IMMUTABLE))) || + (ZTOV(zp)->v_type == VDIR && + (zp->z_phys->zp_flags & ZFS_IMMUTABLE)))) { + *check_privs = B_FALSE; + return (EPERM); + } + + if ((v4_mode & (ACE_DELETE | ACE_DELETE_CHILD)) && + (zp->z_phys->zp_flags & ZFS_NOUNLINK)) { + *check_privs = B_FALSE; + return (EPERM); + } + + if (((v4_mode & (ACE_READ_DATA|ACE_EXECUTE)) && + (zp->z_phys->zp_flags & ZFS_AV_QUARANTINED))) { + *check_privs = B_FALSE; + return (EACCES); + } + + /* + * The caller requested that the ACL check be skipped. This + * would only happen if the caller checked VOP_ACCESS() with a + * 32 bit ACE mask and already had the appropriate permissions. + */ + if (skipaclchk) { + *working_mode = 0; + return (0); + } + + zfs_fuid_map_ids(zp, &fowner, &gowner); + mutex_enter(&zp->z_acl_lock); - error = zfs_acl_node_read(zp, &aclp); + error = zfs_acl_node_read(zp, &aclp, B_FALSE); if (error != 0) { mutex_exit(&zp->z_acl_lock); return (error); } + while (acep = zfs_acl_next_ace(aclp, acep, &who, &access_mask, + &iflags, &type)) { - zacep = aclp->z_acl; - cnt = aclp->z_acl_count; - - for (i = 0; i != cnt; i++) { + if (iflags & ACE_INHERIT_ONLY_ACE) + continue; - DTRACE_PROBE2(zfs__access__common, - ace_t *, &zacep[i], int, *working_mode); + entry_type = (iflags & ACE_TYPE_FLAGS); - if (zacep[i].a_flags & ACE_INHERIT_ONLY_ACE) - continue; + checkit = B_FALSE; - entry_type = (zacep[i].a_flags & ACE_TYPE_FLAGS); switch (entry_type) { case ACE_OWNER: - if (uid == zp->z_phys->zp_uid) { - access_deny = zfs_ace_access(&zacep[i], - working_mode); - } + if (uid == fowner) + checkit = B_TRUE; break; - case (ACE_IDENTIFIER_GROUP | ACE_GROUP): + case OWNING_GROUP: + who = gowner; + /*FALLTHROUGH*/ case ACE_IDENTIFIER_GROUP: - /* - * Owning group gid is in znode not ACL - */ - if (entry_type == (ACE_IDENTIFIER_GROUP | ACE_GROUP)) - gid = zp->z_phys->zp_gid; - else - gid = zacep[i].a_who; - - if (groupmember(gid, cr)) { - access_deny = zfs_ace_access(&zacep[i], - working_mode); - } + checkit = zfs_groupmember(zfsvfs, who, cr); break; case ACE_EVERYONE: - access_deny = zfs_ace_access(&zacep[i], working_mode); + checkit = B_TRUE; break; /* USER Entry */ default: if (entry_type == 0) { - if (uid == zacep[i].a_who) { - access_deny = zfs_ace_access(&zacep[i], - working_mode); - } + uid_t newid; + + zfs_fuid_map_id(zfsvfs, who, + ZFS_ACE_USER, &newid); + if (newid != IDMAP_WK_CREATOR_OWNER_UID && + uid == newid) + checkit = B_TRUE; break; + } else { + zfs_acl_free(aclp); + mutex_exit(&zp->z_acl_lock); + return (EIO); + } + } + + if (checkit) { + if (access_mask & *working_mode) { + if (type == ALLOW) { + *working_mode &= + ~(*working_mode & access_mask); + if (*working_mode == 0) { + access_deny = 0; + } + } else if (type == DENY) { + access_deny = EACCES; + } } - zfs_acl_free(aclp); - mutex_exit(&zp->z_acl_lock); - return (EIO); } if (access_deny != ACCESS_UNDETERMINED) @@ -1347,23 +2290,35 @@ zfs_zaccess_common(znode_t *zp, int v4_mode, int *working_mode, cred_t *cr) mutex_exit(&zp->z_acl_lock); zfs_acl_free(aclp); - +out: return (access_deny); } +static int +zfs_zaccess_append(znode_t *zp, uint32_t *working_mode, boolean_t *check_privs, + cred_t *cr) +{ + if (*working_mode != ACE_WRITE_DATA) + return (EACCES); + + return (zfs_zaccess_common(zp, ACE_APPEND_DATA, working_mode, + check_privs, B_FALSE, cr)); +} /* * Determine whether Access should be granted/denied, invoking least * priv subsytem when a deny is determined. */ int -zfs_zaccess(znode_t *zp, int mode, cred_t *cr) +zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr) { - int working_mode; - int error; - int is_attr; - znode_t *xzp; - znode_t *check_zp = zp; + uint32_t working_mode; + int error; + int is_attr; + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + boolean_t check_privs; + znode_t *xzp; + znode_t *check_zp = zp; is_attr = ((zp->z_phys->zp_flags & ZFS_XATTR) && (ZTOV(zp)->v_type == VDIR)); @@ -1376,7 +2331,9 @@ zfs_zaccess(znode_t *zp, int mode, cred_t *cr) zp->z_phys->zp_parent, &xzp)) != 0) { return (error); } + check_zp = xzp; + /* * fixup mode to map to xattr perms */ @@ -1392,18 +2349,76 @@ zfs_zaccess(znode_t *zp, int mode, cred_t *cr) } } - error = zfs_zaccess_common(check_zp, mode, &working_mode, cr); + if ((error = zfs_zaccess_common(check_zp, mode, &working_mode, + &check_privs, skipaclchk, cr)) == 0) { + if (is_attr) + VN_RELE(ZTOV(xzp)); + return (0); + } - if (error == EROFS) { + if (error && check_privs == B_FALSE) { if (is_attr) VN_RELE(ZTOV(xzp)); return (error); } - if (error || working_mode) { - working_mode = (zfs_v4_to_unix(working_mode) << 6); - error = secpolicy_vnode_access(cr, ZTOV(check_zp), - check_zp->z_phys->zp_uid, working_mode); + if (error && (flags & V_APPEND)) { + error = zfs_zaccess_append(zp, &working_mode, &check_privs, cr); + } + + if (error && check_privs) { + uid_t owner; + mode_t checkmode = 0; + + zfs_fuid_map_id(zfsvfs, check_zp->z_phys->zp_uid, + ZFS_OWNER, &owner); + + /* + * First check for implicit owner permission on + * read_acl/read_attributes + */ + + error = 0; + ASSERT(working_mode != 0); + + if ((working_mode & (ACE_READ_ACL|ACE_READ_ATTRIBUTES) && + owner == crgetuid(cr))) + working_mode &= ~(ACE_READ_ACL|ACE_READ_ATTRIBUTES); + + if (working_mode & (ACE_READ_DATA|ACE_READ_NAMED_ATTRS| + ACE_READ_ACL|ACE_READ_ATTRIBUTES)) + checkmode |= VREAD; + if (working_mode & (ACE_WRITE_DATA|ACE_WRITE_NAMED_ATTRS| + ACE_APPEND_DATA|ACE_WRITE_ATTRIBUTES)) + checkmode |= VWRITE; + if (working_mode & ACE_EXECUTE) + checkmode |= VEXEC; + + if (checkmode) + error = secpolicy_vnode_access(cr, ZTOV(check_zp), + owner, checkmode); + + if (error == 0 && (working_mode & ACE_WRITE_OWNER)) + error = secpolicy_vnode_create_gid(cr); + if (error == 0 && (working_mode & ACE_WRITE_ACL)) + error = secpolicy_vnode_setdac(cr, owner); + + if (error == 0 && (working_mode & + (ACE_DELETE|ACE_DELETE_CHILD))) + error = secpolicy_vnode_remove(cr); + + if (error == 0 && (working_mode & ACE_SYNCHRONIZE)) + error = secpolicy_vnode_owner(cr, owner); + + if (error == 0) { + /* + * See if any bits other than those already checked + * for are still present. If so then return EACCES + */ + if (working_mode & ~(ZFS_CHECKED_MASKS)) { + error = EACCES; + } + } } if (is_attr) @@ -1413,38 +2428,36 @@ zfs_zaccess(znode_t *zp, int mode, cred_t *cr) } /* - * Special zaccess function to check for special nfsv4 perm. - * doesn't call secpolicy_vnode_access() for failure, since that - * would probably be the wrong policy function to call. - * instead its up to the caller to handle that situation. + * Translate traditional unix VREAD/VWRITE/VEXEC mode into + * native ACL format and call zfs_zaccess() */ - int -zfs_zaccess_v4_perm(znode_t *zp, int mode, cred_t *cr) +zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr) { - int working_mode = 0; - return (zfs_zaccess_common(zp, mode, &working_mode, cr)); + return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr)); } /* - * Translate tradition unix VREAD/VWRITE/VEXEC mode into - * native ACL format and call zfs_zaccess() + * Access function for secpolicy_vnode_setattr */ int -zfs_zaccess_rwx(znode_t *zp, mode_t mode, cred_t *cr) +zfs_zaccess_unix(znode_t *zp, mode_t mode, cred_t *cr) { int v4_mode = zfs_unix_to_v4(mode >> 6); - return (zfs_zaccess(zp, v4_mode, cr)); + return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr)); } static int zfs_delete_final_check(znode_t *zp, znode_t *dzp, cred_t *cr) { int error; + uid_t downer; + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + + zfs_fuid_map_id(zfsvfs, dzp->z_phys->zp_uid, ZFS_OWNER, &downer); - error = secpolicy_vnode_access(cr, ZTOV(zp), - dzp->z_phys->zp_uid, S_IWRITE|S_IEXEC); + error = secpolicy_vnode_access(cr, ZTOV(zp), downer, S_IWRITE|S_IEXEC); if (error == 0) error = zfs_sticky_remove_access(dzp, zp, cr); @@ -1490,9 +2503,11 @@ zfs_delete_final_check(znode_t *zp, znode_t *dzp, cred_t *cr) int zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr) { - int dzp_working_mode = 0; - int zp_working_mode = 0; + uint32_t dzp_working_mode = 0; + uint32_t zp_working_mode = 0; int dzp_error, zp_error; + boolean_t dzpcheck_privs = B_TRUE; + boolean_t zpcheck_privs = B_TRUE; /* * Arghh, this check is going to require a couple of questions @@ -1505,11 +2520,16 @@ zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr) * by secpolicy_vnode_access(). */ + if (zp->z_phys->zp_flags & (ZFS_IMMUTABLE | ZFS_NOUNLINK)) + return (EPERM); + dzp_error = zfs_zaccess_common(dzp, ACE_DELETE_CHILD, - &dzp_working_mode, cr); - zp_error = zfs_zaccess_common(zp, ACE_DELETE, &zp_working_mode, cr); + &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr); + zp_error = zfs_zaccess_common(zp, ACE_DELETE, &zp_working_mode, + &zpcheck_privs, B_FALSE, cr); - if (dzp_error == EROFS || zp_error == EROFS) + if ((dzp_error && dzpcheck_privs == B_FALSE) || + (zp_error && zpcheck_privs == B_FALSE)) return (dzp_error); /* @@ -1550,9 +2570,9 @@ zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr) */ dzp_error = zfs_zaccess_common(dzp, ACE_WRITE_DATA|ACE_EXECUTE, - &dzp_working_mode, cr); + &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr); - if (dzp_error == EROFS) + if (dzp_error && dzpcheck_privs == B_FALSE) return (dzp_error); if ((dzp_working_mode & (ACE_WRITE_DATA|ACE_EXECUTE)) == 0) @@ -1576,6 +2596,9 @@ zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp, int add_perm; int error; + if (szp->z_phys->zp_flags & ZFS_AV_QUARANTINED) + return (EACCES); + add_perm = (ZTOV(szp)->v_type == VDIR) ? ACE_ADD_SUBDIRECTORY : ACE_ADD_FILE; @@ -1604,7 +2627,7 @@ zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp, /* * Now check for add permissions */ - error = zfs_zaccess(tdzp, add_perm, cr); + error = zfs_zaccess(tdzp, add_perm, 0, B_FALSE, cr); return (error); } diff --git a/usr/src/uts/common/fs/zfs/zfs_byteswap.c b/usr/src/uts/common/fs/zfs/zfs_byteswap.c index c8450d488bdb..ab97f83eb0af 100644 --- a/usr/src/uts/common/fs/zfs/zfs_byteswap.c +++ b/usr/src/uts/common/fs/zfs/zfs_byteswap.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -33,7 +32,7 @@ #include void -zfs_ace_byteswap(ace_t *ace, int ace_cnt) +zfs_oldace_byteswap(ace_t *ace, int ace_cnt) { int i; @@ -45,9 +44,74 @@ zfs_ace_byteswap(ace_t *ace, int ace_cnt) } } +/* + * swap ace_t and ace_oject_t + */ +void +zfs_ace_byteswap(void *buf, size_t size, boolean_t zfs_layout) +{ + caddr_t end; + caddr_t ptr; + zfs_ace_t *zacep; + ace_t *acep; + uint16_t entry_type; + size_t entry_size; + int ace_type; + + end = (caddr_t)buf + size; + ptr = buf; + + while (ptr < end) { + if (zfs_layout) { + zacep = (zfs_ace_t *)ptr; + zacep->z_hdr.z_access_mask = + BSWAP_32(zacep->z_hdr.z_access_mask); + zacep->z_hdr.z_flags = BSWAP_16(zacep->z_hdr.z_flags); + ace_type = zacep->z_hdr.z_type = + BSWAP_16(zacep->z_hdr.z_type); + entry_type = zacep->z_hdr.z_flags & ACE_TYPE_FLAGS; + } else { + acep = (ace_t *)ptr; + acep->a_access_mask = BSWAP_32(acep->a_access_mask); + acep->a_flags = BSWAP_16(acep->a_flags); + ace_type = acep->a_type = BSWAP_16(acep->a_type); + acep->a_who = BSWAP_32(acep->a_who); + entry_type = acep->a_flags & ACE_TYPE_FLAGS; + } + switch (entry_type) { + case ACE_OWNER: + case ACE_EVERYONE: + case (ACE_IDENTIFIER_GROUP | ACE_GROUP): + entry_size = zfs_layout ? + sizeof (zfs_ace_hdr_t) : sizeof (ace_t); + break; + case ACE_IDENTIFIER_GROUP: + default: + if (zfs_layout) { + zacep->z_fuid = BSWAP_64(zacep->z_fuid); + } + switch (ace_type) { + case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE: + case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE: + case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE: + case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE: + entry_size = zfs_layout ? + sizeof (zfs_object_ace_t) : + sizeof (ace_object_t); + break; + default: + entry_size = zfs_layout ? sizeof (zfs_ace_t) : + sizeof (ace_t); + break; + } + } + ptr = ptr + entry_size; + } +} + /* ARGSUSED */ void -zfs_acl_byteswap(void *buf, size_t size) +zfs_oldacl_byteswap(void *buf, size_t size) { int cnt; @@ -58,7 +122,14 @@ zfs_acl_byteswap(void *buf, size_t size) cnt = size / sizeof (ace_t); - zfs_ace_byteswap((ace_t *)buf, cnt); + zfs_oldace_byteswap((ace_t *)buf, cnt); +} + +/* ARGSUSED */ +void +zfs_acl_byteswap(void *buf, size_t size) +{ + zfs_ace_byteswap(buf, size, B_TRUE); } void @@ -86,14 +157,19 @@ zfs_znode_byteswap(void *buf, size_t size) zp->zp_flags = BSWAP_64(zp->zp_flags); zp->zp_uid = BSWAP_64(zp->zp_uid); zp->zp_gid = BSWAP_64(zp->zp_gid); + zp->zp_zap = BSWAP_64(zp->zp_zap); zp->zp_pad[0] = BSWAP_64(zp->zp_pad[0]); zp->zp_pad[1] = BSWAP_64(zp->zp_pad[1]); zp->zp_pad[2] = BSWAP_64(zp->zp_pad[2]); - zp->zp_pad[3] = BSWAP_64(zp->zp_pad[3]); zp->zp_acl.z_acl_extern_obj = BSWAP_64(zp->zp_acl.z_acl_extern_obj); - zp->zp_acl.z_acl_count = BSWAP_32(zp->zp_acl.z_acl_count); + zp->zp_acl.z_acl_size = BSWAP_32(zp->zp_acl.z_acl_size); zp->zp_acl.z_acl_version = BSWAP_16(zp->zp_acl.z_acl_version); - zp->zp_acl.z_acl_pad = BSWAP_16(zp->zp_acl.z_acl_pad); - zfs_ace_byteswap(&zp->zp_acl.z_ace_data[0], ACE_SLOT_CNT); + zp->zp_acl.z_acl_count = BSWAP_16(zp->zp_acl.z_acl_count); + if (zp->zp_acl.z_acl_version == ZFS_ACL_VERSION) { + zfs_acl_byteswap((void *)&zp->zp_acl.z_ace_data[0], + ZFS_ACE_SPACE); + } else + zfs_oldace_byteswap((ace_t *)&zp->zp_acl.z_ace_data[0], + ACE_SLOT_CNT); } diff --git a/usr/src/uts/common/fs/zfs/zfs_ctldir.c b/usr/src/uts/common/fs/zfs/zfs_ctldir.c index 3b2cc409e046..b272d16ddbc0 100644 --- a/usr/src/uts/common/fs/zfs/zfs_ctldir.c +++ b/usr/src/uts/common/fs/zfs/zfs_ctldir.c @@ -243,7 +243,7 @@ zfsctl_root(znode_t *zp) */ /* ARGSUSED */ static int -zfsctl_common_open(vnode_t **vpp, int flags, cred_t *cr) +zfsctl_common_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct) { if (flags & FWRITE) return (EACCES); @@ -257,7 +257,7 @@ zfsctl_common_open(vnode_t **vpp, int flags, cred_t *cr) /* ARGSUSED */ static int zfsctl_common_close(vnode_t *vpp, int flags, int count, offset_t off, - cred_t *cr) + cred_t *cr, caller_context_t *ct) { return (0); } @@ -267,7 +267,8 @@ zfsctl_common_close(vnode_t *vpp, int flags, int count, offset_t off, */ /* ARGSUSED */ static int -zfsctl_common_access(vnode_t *vp, int mode, int flags, cred_t *cr) +zfsctl_common_access(vnode_t *vp, int mode, int flags, cred_t *cr, + caller_context_t *ct) { if (mode & VWRITE) return (EACCES); @@ -306,8 +307,9 @@ zfsctl_common_getattr(vnode_t *vp, vattr_t *vap) vap->va_mtime = vap->va_ctime = zcp->zc_cmtime; } +/*ARGSUSED*/ static int -zfsctl_common_fid(vnode_t *vp, fid_t *fidp) +zfsctl_common_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct) { zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data; zfsctl_node_t *zcp = vp->v_data; @@ -357,7 +359,8 @@ zfsctl_common_fid(vnode_t *vp, fid_t *fidp) */ /* ARGSUSED */ static int -zfsctl_root_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +zfsctl_root_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data; @@ -377,17 +380,24 @@ zfsctl_root_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) /* ARGSUSED */ int zfsctl_root_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp, - int flags, vnode_t *rdir, cred_t *cr) + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp) { zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data; int err; + /* + * No extended attributes allowed under .zfs + */ + if (flags & LOOKUP_XATTR) + return (EINVAL); + ZFS_ENTER(zfsvfs); if (strcmp(nm, "..") == 0) { err = VFS_ROOT(dvp->v_vfsp, vpp); } else { - err = gfs_dir_lookup(dvp, nm, vpp); + err = gfs_dir_lookup(dvp, nm, vpp, cr); } ZFS_EXIT(zfsvfs); @@ -449,7 +459,7 @@ zfsctl_unmount_snap(vnode_t *dvp, const char *name, int force, cred_t *cr) return (err); } ASSERT(sep->se_root->v_count == 1); - gfs_vop_inactive(sep->se_root, cr); + gfs_vop_inactive(sep->se_root, cr, NULL); avl_remove(&sdp->sd_snaps, sep); kmem_free(sep->se_name, strlen(sep->se_name) + 1); @@ -512,9 +522,10 @@ zfsctl_rename_snap(zfsctl_snapdir_t *sdp, zfs_snapentry_t *sep, const char *nm) vfs_unlock(vfsp); } +/*ARGSUSED*/ static int zfsctl_snapdir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, - cred_t *cr) + cred_t *cr, caller_context_t *ct, int flags) { zfsctl_snapdir_t *sdp = sdvp->v_data; zfs_snapentry_t search, *sep; @@ -560,7 +571,8 @@ zfsctl_snapdir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, /* ARGSUSED */ static int -zfsctl_snapdir_remove(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr) +zfsctl_snapdir_remove(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr, + caller_context_t *ct, int flags) { zfsctl_snapdir_t *sdp = dvp->v_data; char snapname[MAXNAMELEN]; @@ -594,7 +606,7 @@ zfsctl_snapdir_remove(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr) /* ARGSUSED */ static int zfsctl_snapdir_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, - cred_t *cr) + cred_t *cr, caller_context_t *cc, int flags, vsecattr_t *vsecp) { zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data; char name[MAXNAMELEN]; @@ -628,7 +640,8 @@ zfsctl_snapdir_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, /* ARGSUSED */ static int zfsctl_snapdir_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp, - int flags, vnode_t *rdir, cred_t *cr) + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp) { zfsctl_snapdir_t *sdp = dvp->v_data; objset_t *snap; @@ -642,6 +655,12 @@ zfsctl_snapdir_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp, zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data; int err; + /* + * No extended attributes allowed under .zfs + */ + if (flags & LOOKUP_XATTR) + return (EINVAL); + ASSERT(dvp->v_type == VDIR); if (gfs_lookup_dot(vpp, dvp, zfsvfs->z_ctldir, nm) == 0) @@ -814,7 +833,8 @@ zfsctl_mknode_snapdir(vnode_t *pvp) /* ARGSUSED */ static int -zfsctl_snapdir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +zfsctl_snapdir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data; zfsctl_snapdir_t *sdp = vp->v_data; @@ -830,7 +850,7 @@ zfsctl_snapdir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) /* ARGSUSED */ static void -zfsctl_snapdir_inactive(vnode_t *vp, cred_t *cr) +zfsctl_snapdir_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { zfsctl_snapdir_t *sdp = vp->v_data; void *private; @@ -883,13 +903,13 @@ zfsctl_snapshot_mknode(vnode_t *pvp, uint64_t objset) } static void -zfsctl_snapshot_inactive(vnode_t *vp, cred_t *cr) +zfsctl_snapshot_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { zfsctl_snapdir_t *sdp; zfs_snapentry_t *sep, *next; vnode_t *dvp; - VERIFY(gfs_dir_lookup(vp, "..", &dvp) == 0); + VERIFY(gfs_dir_lookup(vp, "..", &dvp, cr) == 0); sdp = dvp->v_data; mutex_enter(&sdp->sd_lock); @@ -924,7 +944,7 @@ zfsctl_snapshot_inactive(vnode_t *vp, cred_t *cr) * "active". If we lookup the same name again we will end up * creating a new vnode. */ - gfs_vop_inactive(vp, cr); + gfs_vop_inactive(vp, cr, ct); } @@ -949,7 +969,7 @@ zfsctl_lookup_objset(vfs_t *vfsp, uint64_t objsetid, zfsvfs_t **zfsvfsp) ASSERT(zfsvfs->z_ctldir != NULL); error = zfsctl_root_lookup(zfsvfs->z_ctldir, "snapshot", &dvp, - NULL, 0, NULL, kcred); + NULL, 0, NULL, kcred, NULL, NULL, NULL); if (error != 0) return (error); sdp = dvp->v_data; @@ -1008,7 +1028,7 @@ zfsctl_umount_snapshots(vfs_t *vfsp, int fflags, cred_t *cr) ASSERT(zfsvfs->z_ctldir != NULL); error = zfsctl_root_lookup(zfsvfs->z_ctldir, "snapshot", &dvp, - NULL, 0, NULL, cr); + NULL, 0, NULL, cr, NULL, NULL, NULL); if (error != 0) return (error); sdp = dvp->v_data; @@ -1046,7 +1066,7 @@ zfsctl_umount_snapshots(vfs_t *vfsp, int fflags, cred_t *cr) * would lead to an attempt to re-grab the sd_lock. */ ASSERT3U(svp->v_count, ==, 1); - gfs_vop_inactive(svp, cr); + gfs_vop_inactive(svp, cr, NULL); } sep = next; } diff --git a/usr/src/uts/common/fs/zfs/zfs_dir.c b/usr/src/uts/common/fs/zfs/zfs_dir.c index bcaa6fd4b5f8..8ae6a95e4165 100644 --- a/usr/src/uts/common/fs/zfs/zfs_dir.c +++ b/usr/src/uts/common/fs/zfs/zfs_dir.c @@ -52,7 +52,51 @@ #include #include #include +#include #include +#include +#include + +/* + * zfs_match_find() is used by zfs_dirent_lock() to peform zap lookups + * of names after deciding which is the appropriate lookup interface. + */ +static int +zfs_match_find(zfsvfs_t *zfsvfs, znode_t *dzp, char *name, boolean_t exact, + boolean_t update, int *deflags, pathname_t *rpnp, uint64_t *zoid) +{ + int error; + + if (zfsvfs->z_norm) { + matchtype_t mt = MT_FIRST; + boolean_t conflict = B_FALSE; + size_t bufsz = 0; + char *buf = NULL; + + if (rpnp) { + buf = rpnp->pn_path; + bufsz = rpnp->pn_bufsize; + } + if (exact) + mt = MT_EXACT; + /* + * In the non-mixed case we only expect there would ever + * be one match, but we need to use the normalizing lookup. + */ + error = zap_lookup_norm(zfsvfs->z_os, dzp->z_id, name, 8, 1, + zoid, mt, buf, bufsz, &conflict); + if (deflags) + *deflags = conflict ? ED_CASE_CONFLICT : 0; + } else { + error = zap_lookup(zfsvfs->z_os, dzp->z_id, name, 8, 1, zoid); + } + *zoid = ZFS_DIRENT_OBJ(*zoid); + + if (error == ENOENT && update) + dnlc_update(ZTOV(dzp), name, DNLC_NO_VNODE); + + return (error); +} /* * Lock a directory entry. A dirlock on protects that name @@ -67,24 +111,38 @@ * ZEXISTS: if the entry does not exist, fail with ENOENT. * ZSHARED: allow concurrent access with other ZSHARED callers. * ZXATTR: we want dzp's xattr directory + * ZCILOOK: On a mixed sensitivity file system, + * this lookup should be case-insensitive. + * ZCIEXACT: On a purely case-insensitive file system, + * this lookup should be case-sensitive. + * ZRENAMING: we are locking for renaming, force narrow locks * * Output arguments: * zpp - pointer to the znode for the entry (NULL if there isn't one) * dlpp - pointer to the dirlock for this entry (NULL on error) + * direntflags - (case-insensitive lookup only) + * flags if multiple case-sensitive matches exist in directory + * realpnp - (case-insensitive lookup only) + * actual name matched within the directory * * Return value: 0 on success or errno on failure. * * NOTE: Always checks for, and rejects, '.' and '..'. + * NOTE: For case-insensitive file systems we take wide locks (see below), + * but return znode pointers to a single match. */ int zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp, - int flag) + int flag, int *direntflags, pathname_t *realpnp) { zfsvfs_t *zfsvfs = dzp->z_zfsvfs; zfs_dirlock_t *dl; + boolean_t update; + boolean_t exact; uint64_t zoid; - int error; - vnode_t *vp; + vnode_t *vp = NULL; + int error = 0; + int cmpflags; *zpp = NULL; *dlpp = NULL; @@ -97,6 +155,58 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp, zfs_has_ctldir(dzp) && strcmp(name, ZFS_CTLDIR_NAME) == 0) return (EEXIST); + /* + * Case sensitivity and normalization preferences are set when + * the file system is created. These are stored in the + * zfsvfs->z_case and zfsvfs->z_norm fields. These choices + * affect what vnodes can be cached in the DNLC, how we + * perform zap lookups, and the "width" of our dirlocks. + * + * A normal dirlock locks a single name. Note that with + * normalization a name can be composed multiple ways, but + * when normalized, these names all compare equal. A wide + * dirlock locks multiple names. We need these when the file + * system is supporting mixed-mode access. It is sometimes + * necessary to lock all case permutations of file name at + * once so that simultaneous case-insensitive/case-sensitive + * behaves as rationally as possible. + */ + + /* + * Decide if exact matches should be requested when performing + * a zap lookup on file systems supporting case-insensitive + * access. + */ + exact = ((zfsvfs->z_case & ZFS_CI_ONLY) && (flag & ZCIEXACT)) || + ((zfsvfs->z_case & ZFS_CI_MIXD) && !(flag & ZCILOOK)); + + /* + * Only look in or update the DNLC if we are looking for the + * name on a file system that does not require normalization + * or case folding. We can also look there if we happen to be + * on a non-normalizing, mixed sensitivity file system IF we + * are looking for the exact name. + * + * Maybe can add TO-UPPERed version of name to dnlc in ci-only + * case for performance improvement? + */ + update = !zfsvfs->z_norm || + ((zfsvfs->z_case & ZFS_CI_MIXD) && + !(zfsvfs->z_norm & ~U8_TEXTPREP_TOUPPER) && !(flag & ZCILOOK)); + + /* + * ZRENAMING indicates we are in a situation where we should + * take narrow locks regardless of the file system's + * preferences for normalizing and case folding. This will + * prevent us deadlocking trying to grab the same wide lock + * twice if the two names happen to be case-insensitive + * matches. + */ + if (flag & ZRENAMING) + cmpflags = 0; + else + cmpflags = zfsvfs->z_norm; + /* * Wait until there are no locks on this name. */ @@ -108,9 +218,16 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp, rw_exit(&dzp->z_name_lock); return (ENOENT); } - for (dl = dzp->z_dirlocks; dl != NULL; dl = dl->dl_next) - if (strcmp(name, dl->dl_name) == 0) + for (dl = dzp->z_dirlocks; dl != NULL; dl = dl->dl_next) { + if ((u8_strcmp(name, dl->dl_name, 0, cmpflags, + U8_UNICODE_LATEST, &error) == 0) || error != 0) break; + } + if (error != 0) { + mutex_exit(&dzp->z_lock); + rw_exit(&dzp->z_name_lock); + return (ENOENT); + } if (dl == NULL) { /* * Allocate a new dirlock and add it to the list. @@ -156,7 +273,8 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp, zoid = dzp->z_phys->zp_xattr; error = (zoid == 0 ? ENOENT : 0); } else { - vp = dnlc_lookup(ZTOV(dzp), name); + if (update) + vp = dnlc_lookup(ZTOV(dzp), name); if (vp == DNLC_NO_VNODE) { VN_RELE(vp); error = ENOENT; @@ -170,11 +288,8 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp, *zpp = VTOZ(vp); return (0); } else { - error = zap_lookup(zfsvfs->z_os, dzp->z_id, name, - 8, 1, &zoid); - zoid = ZFS_DIRENT_OBJ(zoid); - if (error == ENOENT) - dnlc_update(ZTOV(dzp), name, DNLC_NO_VNODE); + error = zfs_match_find(zfsvfs, dzp, name, exact, + update, direntflags, realpnp, &zoid); } } if (error) { @@ -192,7 +307,7 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp, zfs_dirent_unlock(dl); return (error); } - if (!(flag & ZXATTR)) + if (!(flag & ZXATTR) && update) dnlc_update(ZTOV(dzp), name, ZTOV(*zpp)); } @@ -239,7 +354,8 @@ zfs_dirent_unlock(zfs_dirlock_t *dl) * special pseudo-directory. */ int -zfs_dirlook(znode_t *dzp, char *name, vnode_t **vpp) +zfs_dirlook(znode_t *dzp, char *name, vnode_t **vpp, int flags, + int *deflg, pathname_t *rpnp) { zfs_dirlock_t *dl; znode_t *zp; @@ -257,7 +373,8 @@ zfs_dirlook(znode_t *dzp, char *name, vnode_t **vpp) if (dzp->z_phys->zp_parent == dzp->z_id && zfsvfs->z_parent != zfsvfs) { error = zfsctl_root_lookup(zfsvfs->z_parent->z_ctldir, - "snapshot", vpp, NULL, 0, NULL, kcred); + "snapshot", vpp, NULL, 0, NULL, kcred, + NULL, NULL, NULL); return (error); } rw_enter(&dzp->z_parent_lock, RW_READER); @@ -268,14 +385,24 @@ zfs_dirlook(znode_t *dzp, char *name, vnode_t **vpp) } else if (zfs_has_ctldir(dzp) && strcmp(name, ZFS_CTLDIR_NAME) == 0) { *vpp = zfsctl_root(dzp); } else { - error = zfs_dirent_lock(&dl, dzp, name, &zp, ZEXISTS | ZSHARED); + int zf; + + zf = ZEXISTS | ZSHARED; + if (flags & FIGNORECASE) + zf |= ZCILOOK; + + error = zfs_dirent_lock(&dl, dzp, name, &zp, zf, deflg, rpnp); if (error == 0) { *vpp = ZTOV(zp); zfs_dirent_unlock(dl); dzp->z_zn_prefetch = B_TRUE; /* enable prefetching */ } + rpnp = NULL; } + if ((flags & FIGNORECASE) && rpnp) + (void) strlcpy(rpnp->pn_path, name, rpnp->pn_bufsize); + return (error); } @@ -633,7 +760,20 @@ zfs_link_destroy(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag, zfs_time_stamper_locked(dzp, CONTENT_MODIFIED, tx); mutex_exit(&dzp->z_lock); - error = zap_remove(zp->z_zfsvfs->z_os, dzp->z_id, dl->dl_name, tx); + if (zp->z_zfsvfs->z_norm) { + if (((zp->z_zfsvfs->z_case & ZFS_CI_ONLY) && + (flag & ZCIEXACT)) || + ((zp->z_zfsvfs->z_case & ZFS_CI_MIXD) && + !(flag & ZCILOOK))) + error = zap_remove_norm(zp->z_zfsvfs->z_os, + dzp->z_id, dl->dl_name, MT_EXACT, tx); + else + error = zap_remove_norm(zp->z_zfsvfs->z_os, + dzp->z_id, dl->dl_name, MT_FIRST, tx); + } else { + error = zap_remove(zp->z_zfsvfs->z_os, + dzp->z_id, dl->dl_name, tx); + } ASSERT(error == 0); if (unlinkedp != NULL) @@ -663,15 +803,24 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, vnode_t **xvpp, cred_t *cr) dmu_tx_t *tx; uint64_t xoid; int error; + zfs_fuid_info_t *fuidp = NULL; *xvpp = NULL; - if (error = zfs_zaccess(zp, ACE_WRITE_NAMED_ATTRS, cr)) + if (error = zfs_zaccess(zp, ACE_WRITE_NAMED_ATTRS, 0, B_FALSE, cr)) return (error); tx = dmu_tx_create(zfsvfs->z_os); dmu_tx_hold_bonus(tx, zp->z_id); dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); + if (zfsvfs->z_fuid_obj == 0) { + dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, SPA_MAXBLOCKSIZE); + dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL); + } else { + dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); + dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, SPA_MAXBLOCKSIZE); + } error = dmu_tx_assign(tx, zfsvfs->z_assign); if (error) { if (error == ERESTART && zfsvfs->z_assign == TXG_NOWAIT) @@ -679,13 +828,16 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, vnode_t **xvpp, cred_t *cr) dmu_tx_abort(tx); return (error); } - zfs_mknode(zp, vap, &xoid, tx, cr, IS_XATTR, &xzp, 0); + zfs_mknode(zp, vap, &xoid, tx, cr, IS_XATTR, &xzp, 0, NULL, &fuidp); ASSERT(xzp->z_id == xoid); ASSERT(xzp->z_phys->zp_parent == zp->z_id); dmu_buf_will_dirty(zp->z_dbuf, tx); zp->z_phys->zp_xattr = xoid; - (void) zfs_log_create(zfsvfs->z_log, tx, TX_MKXATTR, zp, xzp, ""); + (void) zfs_log_create(zfsvfs->z_log, tx, TX_MKXATTR, zp, + xzp, "", NULL, fuidp, vap); + if (fuidp) + zfs_fuid_info_free(fuidp); dmu_tx_commit(tx); *xvpp = ZTOV(xzp); @@ -715,7 +867,7 @@ zfs_get_xattrdir(znode_t *zp, vnode_t **xvpp, cred_t *cr, int flags) vattr_t va; int error; top: - error = zfs_dirent_lock(&dl, zp, "", &xzp, ZXATTR); + error = zfs_dirent_lock(&dl, zp, "", &xzp, ZXATTR, NULL, NULL); if (error) return (error); @@ -750,8 +902,7 @@ zfs_get_xattrdir(znode_t *zp, vnode_t **xvpp, cred_t *cr, int flags) va.va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID; va.va_type = VDIR; va.va_mode = S_IFDIR | S_ISVTX | 0777; - va.va_uid = (uid_t)zp->z_phys->zp_uid; - va.va_gid = (gid_t)zp->z_phys->zp_gid; + zfs_fuid_map_ids(zp, &va.va_uid, &va.va_gid); error = zfs_make_xattrdir(zp, &va, xvpp, cr); zfs_dirent_unlock(dl); @@ -781,15 +932,22 @@ int zfs_sticky_remove_access(znode_t *zdp, znode_t *zp, cred_t *cr) { uid_t uid; + uid_t downer; + uid_t fowner; + zfsvfs_t *zfsvfs = zdp->z_zfsvfs; if (zdp->z_zfsvfs->z_assign >= TXG_INITIAL) /* ZIL replay */ return (0); - if ((zdp->z_phys->zp_mode & S_ISVTX) == 0 || - (uid = crgetuid(cr)) == zdp->z_phys->zp_uid || - uid == zp->z_phys->zp_uid || + if ((zdp->z_phys->zp_mode & S_ISVTX) == 0) + return (0); + + zfs_fuid_map_id(zfsvfs, zdp->z_phys->zp_uid, ZFS_OWNER, &downer); + zfs_fuid_map_id(zfsvfs, zp->z_phys->zp_uid, ZFS_OWNER, &fowner); + + if ((uid = crgetuid(cr)) == downer || uid == fowner || (ZTOV(zp)->v_type == VREG && - zfs_zaccess(zp, ACE_WRITE_DATA, cr) == 0)) + zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr) == 0)) return (0); else return (secpolicy_vnode_remove(cr)); diff --git a/usr/src/uts/common/fs/zfs/zfs_fuid.c b/usr/src/uts/common/fs/zfs/zfs_fuid.c new file mode 100644 index 000000000000..3eb7d5661175 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/zfs_fuid.c @@ -0,0 +1,724 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * FUID Domain table(s). + * + * The FUID table is stored as a packed nvlist of an array + * of nvlists which contain an index, domain string and offset + * + * During file system initialization the nvlist(s) are read and + * two AVL trees are created. One tree is keyed by the index number + * and the other by the domain string. Nodes are never removed from + * trees, but new entries may be added. If a new entry is added then the + * on-disk packed nvlist will also be updated. + */ + +#define FUID_IDX "fuid_idx" +#define FUID_DOMAIN "fuid_domain" +#define FUID_OFFSET "fuid_offset" +#define FUID_NVP_ARRAY "fuid_nvlist" + +typedef struct fuid_domain { + avl_node_t f_node; + ksiddomain_t *f_ksid; + int f_idx; + uint32_t f_offset; +} fuid_domain_t; + +typedef struct fuid_idx { + avl_node_t f_node; + int f_idx; + fuid_domain_t *f_domain; +} fuid_idx_t; + +/* + * Compare two indexes. + */ +static int +idx_compare(const void *arg1, const void *arg2) +{ + const fuid_idx_t *node1 = arg1; + const fuid_idx_t *node2 = arg2; + + if (node1->f_idx < node2->f_idx) + return (-1); + else if (node1->f_idx > node2->f_idx) + return (1); + return (0); +} + +/* + * Compare two domain strings. + */ +static int +domain_compare(const void *arg1, const void *arg2) +{ + const fuid_domain_t *node1 = arg1; + const fuid_domain_t *node2 = arg2; + int val; + + val = strcmp(node1->f_ksid->kd_name, node2->f_ksid->kd_name); + if (val == 0) + return (0); + return (val > 0 ? 1 : -1); +} + +/* + * Load the fuid table(s) into memory. + */ +static void +zfs_fuid_init(zfsvfs_t *zfsvfs, dmu_tx_t *tx) +{ + dmu_buf_t *db; + char *packed; + size_t nvsize = 0; + int error = 0; + int i; + + rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); + + if (zfsvfs->z_fuid_loaded) { + rw_exit(&zfsvfs->z_fuid_lock); + return; + } + + if (zfsvfs->z_fuid_obj == 0) { + + /* first make sure we need to allocate object */ + + error = zap_lookup(zfsvfs->z_os, MASTER_NODE_OBJ, + ZFS_FUID_TABLES, 8, 1, &zfsvfs->z_fuid_obj); + if (error == ENOENT && tx != NULL) { + zfsvfs->z_fuid_obj = dmu_object_alloc(zfsvfs->z_os, + DMU_OT_FUID, 1 << 14, DMU_OT_FUID_SIZE, + sizeof (uint64_t), tx); + VERIFY(zap_add(zfsvfs->z_os, MASTER_NODE_OBJ, + ZFS_FUID_TABLES, sizeof (uint64_t), 1, + &zfsvfs->z_fuid_obj, tx) == 0); + } + } + + avl_create(&zfsvfs->z_fuid_idx, idx_compare, + sizeof (fuid_idx_t), offsetof(fuid_idx_t, f_node)); + avl_create(&zfsvfs->z_fuid_domain, domain_compare, + sizeof (fuid_domain_t), offsetof(fuid_domain_t, f_node)); + + if (zfsvfs->z_fuid_obj) { + VERIFY(0 == dmu_bonus_hold(zfsvfs->z_os, zfsvfs->z_fuid_obj, + FTAG, &db)); + nvsize = *(uint64_t *)db->db_data; + dmu_buf_rele(db, FTAG); + } + + if (nvsize == 0) + goto initialized; + + packed = kmem_alloc(nvsize, KM_SLEEP); + error = dmu_read(zfsvfs->z_os, zfsvfs->z_fuid_obj, 0, nvsize, packed); + if (error == 0) { + nvlist_t **fuidnvp; + nvlist_t *nvp = NULL; + uint_t count; + + VERIFY(nvlist_unpack(packed, nvsize, &nvp, 0) == 0); + VERIFY((error = nvlist_lookup_nvlist_array(nvp, FUID_NVP_ARRAY, + &fuidnvp, &count)) == 0); + + for (i = 0; i != count; i++) { + fuid_idx_t *idxnode; + fuid_domain_t *domnode; + char *domain; + avl_index_t loc; + uint64_t idx, offset; + + VERIFY(nvlist_lookup_string(fuidnvp[i], FUID_DOMAIN, + &domain) == 0); + VERIFY(nvlist_lookup_uint64(fuidnvp[i], FUID_IDX, + &idx) == 0); + VERIFY(nvlist_lookup_uint64(fuidnvp[i], FUID_OFFSET, + &offset) == 0); + + idxnode = kmem_alloc(sizeof (fuid_idx_t), KM_SLEEP); + domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP); + + domnode->f_idx = idxnode->f_idx = idx; + domnode->f_ksid = ksid_lookupdomain(domain); + idxnode->f_domain = domnode; + domnode->f_offset = offset; + if (avl_find(&zfsvfs->z_fuid_idx, + idxnode, &loc) == NULL) { + avl_insert(&zfsvfs->z_fuid_idx, idxnode, loc); + } + if (avl_find(&zfsvfs->z_fuid_domain, + domnode, &loc) == NULL) { + avl_insert(&zfsvfs->z_fuid_domain, + domnode, loc); + } + } + nvlist_free(nvp); + } + kmem_free(packed, nvsize); + +initialized: + zfsvfs->z_fuid_loaded = B_TRUE; + rw_exit(&zfsvfs->z_fuid_lock); +} + +/* + * Query domain table for a given domain. + * + * If domain isn't found it is added to AVL trees and + * the results are pushed out to disk. + */ +int +zfs_fuid_find_by_domain(zfsvfs_t *zfsvfs, const char *domain, char **retdomain, + dmu_tx_t *tx) +{ + fuid_domain_t searchnode, *findnode; + avl_index_t loc; + + searchnode.f_ksid = ksid_lookupdomain(domain); + if (retdomain) { + *retdomain = searchnode.f_ksid->kd_name; + } + if (zfsvfs->z_fuid_loaded == B_FALSE) + zfs_fuid_init(zfsvfs, tx); + + rw_enter(&zfsvfs->z_fuid_lock, RW_READER); + findnode = avl_find(&zfsvfs->z_fuid_domain, &searchnode, &loc); + rw_exit(&zfsvfs->z_fuid_lock); + + if (findnode) { + ksiddomain_rele(searchnode.f_ksid); + return (findnode->f_idx); + } else { + fuid_domain_t *domnode; + fuid_idx_t *newidxnode; + nvlist_t *nvp; + nvlist_t **fuids; + uint64_t retidx; + size_t nvsize = 0; + char *packed; + dmu_buf_t *db; + int i = 0; + + domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP); + domnode->f_ksid = searchnode.f_ksid; + domnode->f_offset = 0; + + newidxnode = kmem_alloc(sizeof (fuid_idx_t), KM_SLEEP); + newidxnode->f_domain = domnode; + + rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); + retidx = domnode->f_idx = newidxnode->f_idx = + avl_numnodes(&zfsvfs->z_fuid_idx) + 1; + + avl_add(&zfsvfs->z_fuid_domain, domnode); + avl_add(&zfsvfs->z_fuid_idx, newidxnode); + /* + * Now resync the on-disk nvlist. + */ + VERIFY(nvlist_alloc(&nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); + + domnode = avl_first(&zfsvfs->z_fuid_domain); + fuids = kmem_alloc(retidx * sizeof (void *), KM_SLEEP); + while (domnode) { + VERIFY(nvlist_alloc(&fuids[i], + NV_UNIQUE_NAME, KM_SLEEP) == 0); + VERIFY(nvlist_add_uint64(fuids[i], FUID_IDX, + domnode->f_idx) == 0); + VERIFY(nvlist_add_uint64(fuids[i], FUID_OFFSET, + domnode->f_offset) == 0); + VERIFY(nvlist_add_string(fuids[i++], FUID_DOMAIN, + domnode->f_ksid->kd_name) == 0); + domnode = AVL_NEXT(&zfsvfs->z_fuid_domain, domnode); + } + VERIFY(nvlist_add_nvlist_array(nvp, FUID_NVP_ARRAY, + fuids, retidx) == 0); + for (i = 0; i != retidx; i++) + nvlist_free(fuids[i]); + kmem_free(fuids, retidx * sizeof (void *)); + VERIFY(nvlist_size(nvp, &nvsize, NV_ENCODE_XDR) == 0); + packed = kmem_alloc(nvsize, KM_SLEEP); + VERIFY(nvlist_pack(nvp, &packed, &nvsize, + NV_ENCODE_XDR, KM_SLEEP) == 0); + nvlist_free(nvp); + dmu_write(zfsvfs->z_os, zfsvfs->z_fuid_obj, 0, nvsize, + packed, tx); + kmem_free(packed, nvsize); + VERIFY(0 == dmu_bonus_hold(zfsvfs->z_os, zfsvfs->z_fuid_obj, + FTAG, &db)); + dmu_buf_will_dirty(db, tx); + *(uint64_t *)db->db_data = nvsize; + dmu_buf_rele(db, FTAG); + + rw_exit(&zfsvfs->z_fuid_lock); + return (retidx); + } +} + +/* + * Query domain table by index, returning domain string + * + * Returns a pointer from an avl node of the domain string. + * + */ +char * +zfs_fuid_find_by_idx(zfsvfs_t *zfsvfs, uint64_t idx) +{ + fuid_idx_t searchnode, *findnode; + avl_index_t loc; + + if (idx == 0 || zfsvfs->z_use_fuids == B_FALSE) + return (NULL); + + if (zfsvfs->z_fuid_loaded == B_FALSE) + zfs_fuid_init(zfsvfs, NULL); + + searchnode.f_idx = idx; + + rw_enter(&zfsvfs->z_fuid_lock, RW_READER); + findnode = avl_find(&zfsvfs->z_fuid_idx, &searchnode, &loc); + rw_exit(&zfsvfs->z_fuid_lock); + + ASSERT(findnode); + return (findnode->f_domain->f_ksid->kd_name); +} + +void +zfs_fuid_get_mappings(zfs_fuid_hdl_t *hdl) +{ + VERIFY(hdl != NULL); + if (hdl->z_map_needed == B_FALSE) + return; + + (void) kidmap_get_mappings(hdl->z_hdl); + + kidmap_get_destroy(hdl->z_hdl); + hdl->z_hdl = NULL; + hdl->z_map_needed = B_FALSE; +} + +void +zfs_fuid_queue_map_id(zfsvfs_t *zfsvfs, zfs_fuid_hdl_t *hdl, + uint64_t fuid, zfs_fuid_type_t type, uid_t *id) +{ + uint32_t index = FUID_INDEX(fuid); + char *domain; + int status; + + VERIFY(hdl); + + if (index == 0 || zfsvfs->z_use_fuids == B_FALSE) { + *id = (uid_t)fuid; + return; + } + + if (hdl->z_hdl == NULL) { + hdl->z_hdl = kidmap_get_create(); + hdl->z_map_needed = B_TRUE; + } + + domain = zfs_fuid_find_by_idx(zfsvfs, index); + ASSERT(domain != NULL); + + if (type == ZFS_OWNER || type == ZFS_ACE_USER) + status = kidmap_batch_getuidbysid(hdl->z_hdl, domain, + FUID_RID(fuid), id, &hdl->z_status); + else + status = kidmap_batch_getgidbysid(hdl->z_hdl, domain, + FUID_RID(fuid), id, &hdl->z_status); + ASSERT(status == 0); +} + +void +zfs_fuid_map_ids(znode_t *zp, uid_t *uid, uid_t *gid) +{ + uint32_t uid_index = FUID_INDEX(zp->z_phys->zp_uid); + uint32_t gid_index = FUID_INDEX(zp->z_phys->zp_gid); + + /* Favor the common case, neither will be ephemeral */ + if (uid_index == 0 && gid_index == 0) { + *uid = zp->z_phys->zp_uid; + *gid = zp->z_phys->zp_gid; + return; + } else { + zfs_fuid_hdl_t hdl = { 0 }; + + zfs_fuid_queue_map_id(zp->z_zfsvfs, &hdl, + zp->z_phys->zp_uid, ZFS_OWNER, uid); + + zfs_fuid_queue_map_id(zp->z_zfsvfs, &hdl, + zp->z_phys->zp_gid, ZFS_GROUP, gid); + + zfs_fuid_get_mappings(&hdl); + } +} + +void +zfs_fuid_map_id(zfsvfs_t *zfsvfs, uint64_t fuid, + zfs_fuid_type_t type, uid_t *id) +{ + uint32_t index = FUID_INDEX(fuid); + char *domain; + + if (index == 0) { + *id = (uid_t)fuid; + return; + } + + domain = zfs_fuid_find_by_idx(zfsvfs, index); + ASSERT(domain != NULL); + + if (type == ZFS_OWNER || type == ZFS_ACE_USER) + (void) kidmap_getuidbysid(domain, FUID_RID(fuid), id); + else + (void) kidmap_getgidbysid(domain, FUID_RID(fuid), id); +} + +/* + * Add a FUID node to the list of fuid's being created for this + * ACL + * + * If ACL has multiple domains, then keep only one copy of each unique + * domain. + */ +static void +zfs_fuid_node_add(zfs_fuid_info_t **fuidpp, const char *domain, uint32_t rid, + uint64_t idx, uint64_t id, zfs_fuid_type_t type) +{ + zfs_fuid_t *fuid; + zfs_fuid_domain_t *fuid_domain; + zfs_fuid_info_t *fuidp; + uint64_t fuididx; + boolean_t found = B_FALSE; + + if (*fuidpp == NULL) + *fuidpp = zfs_fuid_info_alloc(); + + fuidp = *fuidpp; + /* + * First find fuid domain index in linked list + * + * If one isn't found then create an entry. + */ + + for (fuididx = 1, fuid_domain = list_head(&fuidp->z_domains); + fuid_domain; fuid_domain = list_next(&fuidp->z_domains, + fuid_domain), fuididx++) { + if (idx == fuid_domain->z_domidx) { + found = B_TRUE; + break; + } + } + + if (found == B_FALSE) { + fuid_domain = kmem_alloc(sizeof (zfs_fuid_domain_t), KM_SLEEP); + fuid_domain->z_domain = domain; + fuid_domain->z_domidx = idx; + list_insert_tail(&fuidp->z_domains, fuid_domain); + fuidp->z_domain_str_sz += strlen(domain) + 1; + fuidp->z_domain_cnt++; + } + + if (type == ZFS_ACE_USER || type == ZFS_ACE_GROUP) { + /* + * Now allocate fuid entry and add it on the end of the list + */ + + fuid = kmem_alloc(sizeof (zfs_fuid_t), KM_SLEEP); + fuid->z_id = id; + fuid->z_domidx = idx; + fuid->z_logfuid = FUID_ENCODE(fuididx, rid); + + list_insert_tail(&fuidp->z_fuids, fuid); + fuidp->z_fuid_cnt++; + } else { + if (type == ZFS_OWNER) + fuidp->z_fuid_owner = FUID_ENCODE(fuididx, rid); + else + fuidp->z_fuid_group = FUID_ENCODE(fuididx, rid); + } +} + +/* + * Create a file system FUID + * + * During a replay operation the id will be incorrect and + * will be ignored. In this case replay must be true and the + * cred will have a ksid_t attached to it. + * + * A mapped uid/gid would have a ksid_t attached to the cred. + */ +uint64_t +zfs_fuid_create_cred(zfsvfs_t *zfsvfs, uint64_t id, + zfs_fuid_type_t type, dmu_tx_t *tx, cred_t *cr, zfs_fuid_info_t **fuidp) +{ + uint64_t idx; + ksid_t *ksid; + uint32_t rid; + char *kdomain; + const char *domain; + + VERIFY(type == ZFS_OWNER || type == ZFS_GROUP); + + if (zfsvfs->z_use_fuids == B_FALSE || !IS_EPHEMERAL(id)) + return ((uint64_t)id); + + ksid = crgetsid(cr, (type == ZFS_OWNER) ? KSID_OWNER : KSID_GROUP); + + VERIFY(ksid != NULL); + rid = ksid_getrid(ksid); + domain = ksid_getdomain(ksid); + + idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, tx); + + zfs_fuid_node_add(fuidp, kdomain, rid, idx, id, type); + + return (FUID_ENCODE(idx, rid)); +} + +/* + * Create a file system FUID for an ACL ace + * or a chown/chgrp of the file. + * This is similar to zfs_fuid_create_cred, except that + * we can't find the domain + rid information in the + * cred. Instead we have to query Winchester for the + * domain and rid. + */ +uint64_t +zfs_fuid_create(zfsvfs_t *zfsvfs, uint64_t id, + zfs_fuid_type_t type, dmu_tx_t *tx, zfs_fuid_info_t **fuidpp) +{ + const char *domain; + char *kdomain; + uint32_t fuid_idx = FUID_INDEX(id); + uint32_t rid; + idmap_stat status; + uint64_t idx; + boolean_t is_replay = (zfsvfs->z_assign >= TXG_INITIAL); + zfs_fuid_t *zfuid = NULL; + zfs_fuid_info_t *fuidp; + + /* + * If POSIX ID, or entry is already a FUID then + * just return the id + */ + if (!IS_EPHEMERAL(id) || fuid_idx != 0) + return (id); + + if (is_replay) { + fuidp = zfsvfs->z_fuid_replay; + + /* + * If we are passed an ephemeral id, but no + * fuid_info was logged then return NOBODY. + * This is most likely a result of idmap service + * not being available. + */ + if (fuidp == NULL) + return (UID_NOBODY); + + switch (type) { + case ZFS_ACE_USER: + case ZFS_ACE_GROUP: + zfuid = list_head(&fuidp->z_fuids); + rid = FUID_RID(zfuid->z_logfuid); + idx = FUID_INDEX(zfuid->z_logfuid); + break; + case ZFS_OWNER: + rid = FUID_RID(fuidp->z_fuid_owner); + idx = FUID_INDEX(fuidp->z_fuid_owner); + break; + case ZFS_GROUP: + rid = FUID_RID(fuidp->z_fuid_group); + idx = FUID_INDEX(fuidp->z_fuid_group); + break; + }; + domain = fuidp->z_domain_table[idx -1]; + } else { + if (type == ZFS_OWNER || type == ZFS_ACE_USER) + status = kidmap_getsidbyuid(id, &domain, &rid); + else + status = kidmap_getsidbygid(id, &domain, &rid); + + if (status != 0) + return (UID_NOBODY); + + } + + idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, tx); + + if (is_replay == B_FALSE) + zfs_fuid_node_add(fuidpp, kdomain, rid, idx, id, type); + else if (zfuid != NULL) { + list_remove(&fuidp->z_fuids, zfuid); + kmem_free(zfuid, sizeof (zfs_fuid_t)); + } + return (FUID_ENCODE(idx, rid)); +} + +void +zfs_fuid_destroy(zfsvfs_t *zfsvfs) +{ + fuid_domain_t *domnode; + fuid_idx_t *idxnode; + void *cookie; + + rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); + if (zfsvfs->z_fuid_loaded == B_FALSE) { + rw_exit(&zfsvfs->z_fuid_lock); + return; + } + cookie = NULL; + while (domnode = avl_destroy_nodes(&zfsvfs->z_fuid_domain, &cookie)) { + ksiddomain_rele(domnode->f_ksid); + kmem_free(domnode, sizeof (fuid_domain_t)); + } + avl_destroy(&zfsvfs->z_fuid_domain); + cookie = NULL; + while (idxnode = avl_destroy_nodes(&zfsvfs->z_fuid_idx, &cookie)) + kmem_free(idxnode, sizeof (fuid_idx_t)); + avl_destroy(&zfsvfs->z_fuid_idx); + rw_exit(&zfsvfs->z_fuid_lock); +} + +/* + * Allocate zfs_fuid_info for tracking FUIDs created during + * zfs_mknode, VOP_SETATTR() or VOP_SETSECATTR() + */ +zfs_fuid_info_t * +zfs_fuid_info_alloc(void) +{ + zfs_fuid_info_t *fuidp; + + fuidp = kmem_zalloc(sizeof (zfs_fuid_info_t), KM_SLEEP); + list_create(&fuidp->z_domains, sizeof (zfs_fuid_domain_t), + offsetof(zfs_fuid_domain_t, z_next)); + list_create(&fuidp->z_fuids, sizeof (zfs_fuid_t), + offsetof(zfs_fuid_t, z_next)); + return (fuidp); +} + +/* + * Release all memory associated with zfs_fuid_info_t + */ +void +zfs_fuid_info_free(zfs_fuid_info_t *fuidp) +{ + zfs_fuid_t *zfuid; + zfs_fuid_domain_t *zdomain; + + while ((zfuid = list_head(&fuidp->z_fuids)) != NULL) { + list_remove(&fuidp->z_fuids, zfuid); + kmem_free(zfuid, sizeof (zfs_fuid_t)); + } + + if (fuidp->z_domain_table != NULL) + kmem_free(fuidp->z_domain_table, + (sizeof (char **)) * fuidp->z_domain_cnt); + + while ((zdomain = list_head(&fuidp->z_domains)) != NULL) { + list_remove(&fuidp->z_domains, zdomain); + kmem_free(zdomain, sizeof (zfs_fuid_domain_t)); + } + + kmem_free(fuidp, sizeof (zfs_fuid_info_t)); +} + +/* + * Check to see if id is a groupmember. If cred + * has ksid info then sidlist is checked first + * and if still not found then POSIX groups are checked + * + * Will use a straight FUID compare when possible. + */ +boolean_t +zfs_groupmember(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr) +{ + ksid_t *ksid = crgetsid(cr, KSID_GROUP); + uid_t gid; + + if (ksid) { + int i; + ksid_t *ksid_groups; + ksidlist_t *ksidlist = crgetsidlist(cr); + uint32_t idx = FUID_INDEX(id); + uint32_t rid = FUID_RID(id); + + ASSERT(ksidlist); + ksid_groups = ksidlist->ksl_sids; + + for (i = 0; i != ksidlist->ksl_nsid; i++) { + if (idx == 0) { + if (id != IDMAP_WK_CREATOR_GROUP_GID && + id == ksid_groups[i].ks_id) { + return (B_TRUE); + } + } else { + char *domain; + + domain = zfs_fuid_find_by_idx(zfsvfs, idx); + ASSERT(domain != NULL); + + if (strcmp(domain, + IDMAP_WK_CREATOR_SID_AUTHORITY) == 0) { + return (B_FALSE); + } + + if ((strcmp(domain, + ksid_groups[i].ks_domain->kd_name) == 0) && + rid == ksid_groups[i].ks_rid) { + return (B_TRUE); + } + } + } + } + + /* + * Not found in ksidlist, check posix groups + */ + zfs_fuid_map_id(zfsvfs, id, ZFS_GROUP, &gid); + + return (groupmember(gid, cr)); +} diff --git a/usr/src/uts/common/fs/zfs/zfs_ioctl.c b/usr/src/uts/common/fs/zfs/zfs_ioctl.c index 54158d03f2ab..72e2524646fe 100644 --- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c +++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include #include #include @@ -60,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -154,6 +157,22 @@ history_str_get(zfs_cmd_t *zc) return (buf); } +static int +zfs_check_version(const char *name, int version) +{ + + spa_t *spa; + + if (spa_open(name, &spa, FTAG) == 0) { + if (spa_version(spa) < version) { + spa_close(spa, FTAG); + return (1); + } + spa_close(spa, FTAG); + } + return (0); +} + static void zfs_log_history(zfs_cmd_t *zc) { @@ -1280,9 +1299,8 @@ zfs_set_prop_nvlist(const char *name, nvlist_t *nvl) nvpair_type(elem) != DATA_TYPE_STRING) return (EINVAL); - error = zfs_secpolicy_write_perms(name, - ZFS_DELEG_PERM_USERPROP, CRED()); - if (error) + if (error = zfs_secpolicy_write_perms(name, + ZFS_DELEG_PERM_USERPROP, CRED())) return (error); continue; } @@ -1304,35 +1322,25 @@ zfs_set_prop_nvlist(const char *name, nvlist_t *nvl) nvpair_value_uint64(elem, &intval) == 0 && intval >= ZIO_COMPRESS_GZIP_1 && intval <= ZIO_COMPRESS_GZIP_9) { - spa_t *spa; - - if (spa_open(name, &spa, FTAG) == 0) { - if (spa_version(spa) < - SPA_VERSION_GZIP_COMPRESSION) { - spa_close(spa, FTAG); - return (ENOTSUP); - } - - spa_close(spa, FTAG); - } + if (zfs_check_version(name, + SPA_VERSION_GZIP_COMPRESSION)) + return (ENOTSUP); } break; case ZFS_PROP_COPIES: - { - spa_t *spa; - - if (spa_open(name, &spa, FTAG) == 0) { - if (spa_version(spa) < - SPA_VERSION_DITTO_BLOCKS) { - spa_close(spa, FTAG); - return (ENOTSUP); - } - spa_close(spa, FTAG); - } + if (zfs_check_version(name, SPA_VERSION_DITTO_BLOCKS)) + return (ENOTSUP); break; + case ZFS_PROP_NORMALIZE: + case ZFS_PROP_UTF8ONLY: + case ZFS_PROP_CASE: + if (zfs_check_version(name, SPA_VERSION_NORMALIZATION)) + return (ENOTSUP); + } - } + if ((error = zfs_secpolicy_setprop(name, prop, CRED())) != 0) + return (error); } elem = NULL; @@ -1642,13 +1650,163 @@ zfs_get_vfs(const char *resource) static void zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) { - nvlist_t *nvprops = arg; - uint64_t version = ZPL_VERSION; + zfs_creat_t *zct = arg; + uint64_t version; + + if (spa_version(dmu_objset_spa(os)) >= SPA_VERSION_FUID) + version = ZPL_VERSION; + else + version = ZPL_VERSION_FUID - 1; - (void) nvlist_lookup_uint64(nvprops, + (void) nvlist_lookup_uint64(zct->zct_props, zfs_prop_to_name(ZFS_PROP_VERSION), &version); - zfs_create_fs(os, cr, version, tx); + zfs_create_fs(os, cr, version, zct->zct_norm, tx); +} + +/* + * zfs_prop_lookup() + * + * Look for the property first in the existing property nvlist. If + * it's already present, you're done. If it's not there, attempt to + * find the property value from a parent dataset. If that fails, fall + * back to the property's default value. In either of these two + * cases, if update is TRUE, add a value for the property to the + * property nvlist. + * + * If the rval pointer is non-NULL, copy the discovered value to rval. + * + * If we get any unexpected errors, bail and return the error number + * to the caller. + * + * If we succeed, return 0. + */ +static int +zfs_prop_lookup(const char *parentname, zfs_prop_t propnum, + nvlist_t *proplist, uint64_t *rval, boolean_t update) +{ + const char *propname; + uint64_t value; + int error = ENOENT; + + propname = zfs_prop_to_name(propnum); + if (proplist != NULL) + error = nvlist_lookup_uint64(proplist, propname, &value); + if (error == ENOENT) { + error = dsl_prop_get_integer(parentname, propname, + &value, NULL); + if (error == ENOENT) + value = zfs_prop_default_numeric(propnum); + else if (error != 0) + return (error); + if (update) { + ASSERT(proplist != NULL); + error = nvlist_add_uint64(proplist, propname, value); + } + } + if (error == 0 && rval) + *rval = value; + return (error); +} + +/* + * zfs_normalization_get + * + * Get the normalization flag value. If the properties have + * non-default values, make sure the pool version is recent enough to + * support these choices. + */ +static int +zfs_normalization_get(const char *dataset, nvlist_t *proplist, int *norm, + boolean_t update) +{ + char parentname[MAXNAMELEN]; + char poolname[MAXNAMELEN]; + char *cp; + uint64_t value; + int check = 0; + int error; + + ASSERT(norm != NULL); + *norm = 0; + + (void) strncpy(parentname, dataset, sizeof (parentname)); + cp = strrchr(parentname, '@'); + if (cp != NULL) { + cp[0] = '\0'; + } else { + cp = strrchr(parentname, '/'); + if (cp == NULL) + return (ENOENT); + cp[0] = '\0'; + } + + (void) strncpy(poolname, dataset, sizeof (poolname)); + cp = strchr(poolname, '/'); + if (cp != NULL) + cp[0] = '\0'; + + error = zfs_prop_lookup(parentname, ZFS_PROP_UTF8ONLY, + proplist, &value, update); + if (error != 0) + return (error); + if (value != zfs_prop_default_numeric(ZFS_PROP_UTF8ONLY)) + check = 1; + + error = zfs_prop_lookup(parentname, ZFS_PROP_NORMALIZE, + proplist, &value, update); + if (error != 0) + return (error); + if (value != zfs_prop_default_numeric(ZFS_PROP_NORMALIZE)) { + check = 1; + switch ((int)value) { + case ZFS_NORMALIZE_NONE: + break; + case ZFS_NORMALIZE_C: + *norm |= U8_TEXTPREP_NFC; + break; + case ZFS_NORMALIZE_D: + *norm |= U8_TEXTPREP_NFD; + break; + case ZFS_NORMALIZE_KC: + *norm |= U8_TEXTPREP_NFKC; + break; + case ZFS_NORMALIZE_KD: + *norm |= U8_TEXTPREP_NFKD; + break; + default: + ASSERT((int)value >= ZFS_NORMALIZE_NONE); + ASSERT((int)value <= ZFS_NORMALIZE_KD); + break; + } + } + + error = zfs_prop_lookup(parentname, ZFS_PROP_CASE, + proplist, &value, update); + if (error != 0) + return (error); + if (value != zfs_prop_default_numeric(ZFS_PROP_CASE)) { + check = 1; + switch ((int)value) { + case ZFS_CASE_SENSITIVE: + break; + case ZFS_CASE_INSENSITIVE: + *norm |= U8_TEXTPREP_TOUPPER; + break; + case ZFS_CASE_MIXED: + *norm |= U8_TEXTPREP_TOUPPER; + break; + default: + ASSERT((int)value >= ZFS_CASE_SENSITIVE); + ASSERT((int)value <= ZFS_CASE_MIXED); + break; + } + } + + if (check == 1) + if (zfs_check_version(poolname, SPA_VERSION_NORMALIZATION)) + return (ENOTSUP); + return (0); } static int @@ -1656,6 +1814,7 @@ zfs_ioc_create(zfs_cmd_t *zc) { objset_t *clone; int error = 0; + zfs_creat_t zct; nvlist_t *nvprops = NULL; void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); dmu_objset_type_t type = zc->zc_objset_type; @@ -1682,6 +1841,9 @@ zfs_ioc_create(zfs_cmd_t *zc) &nvprops)) != 0) return (error); + zct.zct_norm = 0; + zct.zct_props = nvprops; + if (zc->zc_value[0] != '\0') { /* * We're creating a clone of an existing snapshot. @@ -1699,6 +1861,34 @@ zfs_ioc_create(zfs_cmd_t *zc) return (error); } error = dmu_objset_create(zc->zc_name, type, clone, NULL, NULL); + if (error) { + dmu_objset_close(clone); + nvlist_free(nvprops); + return (error); + } + /* + * If caller did not provide any properties, allocate + * an nvlist for properties, as we will be adding our set-once + * properties to it. This carries the choices made on the + * original file system into the clone. + */ + if (nvprops == NULL) + VERIFY(nvlist_alloc(&nvprops, + NV_UNIQUE_NAME, KM_SLEEP) == 0); + + /* + * We have to have normalization and case-folding + * flags correct when we do the file system creation, + * so go figure them out now. All we really care about + * here is getting these values into the property list. + */ + error = zfs_normalization_get(zc->zc_value, nvprops, + &zct.zct_norm, B_TRUE); + if (error != 0) { + dmu_objset_close(clone); + nvlist_free(nvprops); + return (error); + } dmu_objset_close(clone); } else { if (cbfunc == NULL) { @@ -1737,18 +1927,38 @@ zfs_ioc_create(zfs_cmd_t *zc) } } else if (type == DMU_OST_ZFS) { uint64_t version; + int error; + + error = nvlist_lookup_uint64(nvprops, + zfs_prop_to_name(ZFS_PROP_VERSION), &version); - if (0 == nvlist_lookup_uint64(nvprops, - zfs_prop_to_name(ZFS_PROP_VERSION), &version) && - (version < ZPL_VERSION_INITIAL || + if (error == 0 && (version < ZPL_VERSION_INITIAL || version > ZPL_VERSION)) { nvlist_free(nvprops); - return (EINVAL); + return (ENOTSUP); + } else if (error == 0 && version >= ZPL_VERSION_FUID && + zfs_check_version(zc->zc_name, SPA_VERSION_FUID)) { + nvlist_free(nvprops); + return (ENOTSUP); } - } + /* + * We have to have normalization and + * case-folding flags correct when we do the + * file system creation, so go figure them out + * now. The final argument to zfs_normalization_get() + * tells that routine not to update the nvprops + * list. + */ + error = zfs_normalization_get(zc->zc_name, nvprops, + &zct.zct_norm, B_FALSE); + if (error != 0) { + nvlist_free(nvprops); + return (error); + } + } error = dmu_objset_create(zc->zc_name, type, NULL, cbfunc, - nvprops); + &zct); } /* @@ -1952,7 +2162,7 @@ zfs_ioc_recvbackup(zfs_cmd_t *zc) if (zfsvfs != NULL) VFS_RELE(zfsvfs->z_vfs); new_off = fp->f_offset + zc->zc_cookie; - if (VOP_SEEK(fp->f_vnode, fp->f_offset, &new_off) == 0) + if (VOP_SEEK(fp->f_vnode, fp->f_offset, &new_off, NULL) == 0) fp->f_offset = new_off; releasef(fd); @@ -2123,55 +2333,129 @@ zfs_ioc_promote(zfs_cmd_t *zc) /* * We don't want to have a hard dependency * against some special symbols in sharefs - * and nfs. Determine them if needed when + * nfs, and smbsrv. Determine them if needed when * the first file system is shared. - * Neither sharefs or nfs are unloadable modules. + * Neither sharefs, nfs or smbsrv are unloadable modules. */ -int (*zexport_fs)(void *arg); +int (*znfsexport_fs)(void *arg); int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t); +int (*zsmbexport_fs)(void *arg, boolean_t add_share); + +int zfs_nfsshare_inited; +int zfs_smbshare_inited; -int zfs_share_inited; ddi_modhandle_t nfs_mod; ddi_modhandle_t sharefs_mod; +ddi_modhandle_t smbsrv_mod; kmutex_t zfs_share_lock; +static int +zfs_init_sharefs() +{ + int error; + + ASSERT(MUTEX_HELD(&zfs_share_lock)); + /* Both NFS and SMB shares also require sharetab support. */ + if (sharefs_mod == NULL && ((sharefs_mod = + ddi_modopen("fs/sharefs", + KRTLD_MODE_FIRST, &error)) == NULL)) { + return (ENOSYS); + } + if (zshare_fs == NULL && ((zshare_fs = + (int (*)(enum sharefs_sys_op, share_t *, uint32_t)) + ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) { + return (ENOSYS); + } + return (0); +} + static int zfs_ioc_share(zfs_cmd_t *zc) { int error; int opcode; - if (zfs_share_inited == 0) { - mutex_enter(&zfs_share_lock); - nfs_mod = ddi_modopen("fs/nfs", KRTLD_MODE_FIRST, &error); - sharefs_mod = ddi_modopen("fs/sharefs", - KRTLD_MODE_FIRST, &error); - if (nfs_mod == NULL || sharefs_mod == NULL) { + switch (zc->zc_share.z_sharetype) { + case ZFS_SHARE_NFS: + case ZFS_UNSHARE_NFS: + if (zfs_nfsshare_inited == 0) { + mutex_enter(&zfs_share_lock); + if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs", + KRTLD_MODE_FIRST, &error)) == NULL)) { + mutex_exit(&zfs_share_lock); + return (ENOSYS); + } + if (znfsexport_fs == NULL && + ((znfsexport_fs = (int (*)(void *)) + ddi_modsym(nfs_mod, + "nfs_export", &error)) == NULL)) { + mutex_exit(&zfs_share_lock); + return (ENOSYS); + } + error = zfs_init_sharefs(); + if (error) { + mutex_exit(&zfs_share_lock); + return (ENOSYS); + } + zfs_nfsshare_inited = 1; mutex_exit(&zfs_share_lock); - return (ENOSYS); } - if (zexport_fs == NULL && ((zexport_fs = (int (*)(void *)) - ddi_modsym(nfs_mod, "nfs_export", &error)) == NULL)) { + break; + case ZFS_SHARE_SMB: + case ZFS_UNSHARE_SMB: + if (zfs_smbshare_inited == 0) { + mutex_enter(&zfs_share_lock); + if (smbsrv_mod == NULL && ((smbsrv_mod = + ddi_modopen("drv/smbsrv", + KRTLD_MODE_FIRST, &error)) == NULL)) { + mutex_exit(&zfs_share_lock); + return (ENOSYS); + } + if (zsmbexport_fs == NULL && ((zsmbexport_fs = + (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod, + "lmshrd_share_upcall", &error)) == NULL)) { + mutex_exit(&zfs_share_lock); + return (ENOSYS); + } + error = zfs_init_sharefs(); + if (error) { + mutex_exit(&zfs_share_lock); + return (ENOSYS); + } + zfs_smbshare_inited = 1; mutex_exit(&zfs_share_lock); - return (ENOSYS); } + break; + default: + return (EINVAL); + } - if (zshare_fs == NULL && ((zshare_fs = - (int (*)(enum sharefs_sys_op, share_t *, uint32_t)) - ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) { - mutex_exit(&zfs_share_lock); - return (ENOSYS); + switch (zc->zc_share.z_sharetype) { + case ZFS_SHARE_NFS: + case ZFS_UNSHARE_NFS: + if (error = + znfsexport_fs((void *) + (uintptr_t)zc->zc_share.z_exportdata)) + return (error); + break; + case ZFS_SHARE_SMB: + case ZFS_UNSHARE_SMB: + if (error = zsmbexport_fs((void *) + (uintptr_t)zc->zc_share.z_exportdata, + zc->zc_share.z_sharetype == ZFS_SHARE_SMB ? + B_TRUE : B_FALSE)) { + return (error); } - zfs_share_inited = 1; - mutex_exit(&zfs_share_lock); + break; } - if (error = zexport_fs((void *)(uintptr_t)zc->zc_share.z_exportdata)) - return (error); - - opcode = (zc->zc_share.z_sharetype == B_TRUE) ? + opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS || + zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ? SHAREFS_ADD : SHAREFS_REMOVE; + /* + * Add or remove share from sharetab + */ error = zshare_fs(opcode, (void *)(uintptr_t)zc->zc_share.z_sharedata, zc->zc_share.z_sharemax); @@ -2447,10 +2731,12 @@ _fini(void) zvol_fini(); zfs_fini(); spa_fini(); - if (zfs_share_inited) { + if (zfs_nfsshare_inited) (void) ddi_modclose(nfs_mod); + if (zfs_smbshare_inited) + (void) ddi_modclose(smbsrv_mod); + if (zfs_nfsshare_inited || zfs_smbshare_inited) (void) ddi_modclose(sharefs_mod); - } tsd_destroy(&zfs_fsyncer_key); ldi_ident_release(zfs_li); diff --git a/usr/src/uts/common/fs/zfs/zfs_log.c b/usr/src/uts/common/fs/zfs/zfs_log.c index e5e26d3a05c8..04075f47bfec 100644 --- a/usr/src/uts/common/fs/zfs/zfs_log.c +++ b/usr/src/uts/common/fs/zfs/zfs_log.c @@ -45,44 +45,277 @@ #include #include #include +#include #include /* * All the functions in this file are used to construct the log entries - * to record transactions. They allocate * a intent log transaction + * to record transactions. They allocate * an intent log transaction * structure (itx_t) and save within it all the information necessary to * possibly replay the transaction. The itx is then assigned a sequence * number and inserted in the in-memory list anchored in the zilog. */ +int +zfs_log_create_txtype(zil_create_t type, vsecattr_t *vsecp, vattr_t *vap) +{ + int isxvattr = (vap->va_mask & AT_XVATTR); + switch (type) { + case Z_FILE: + if (vsecp == NULL && !isxvattr) + return (TX_CREATE); + if (vsecp && isxvattr) + return (TX_CREATE_ACL_ATTR); + if (vsecp) + return (TX_CREATE_ACL); + else + return (TX_CREATE_ATTR); + /*NOTREACHED*/ + case Z_DIR: + if (vsecp == NULL && !isxvattr) + return (TX_MKDIR); + if (vsecp && isxvattr) + return (TX_MKDIR_ACL_ATTR); + if (vsecp) + return (TX_MKDIR_ACL); + else + return (TX_MKDIR_ATTR); + case Z_XATTRDIR: + return (TX_MKXATTR); + } + ASSERT(0); + return (TX_MAX_TYPE); +} + +/* + * build up the log data necessary for logging xvattr_t + * First lr_attr_t is initialized. following the lr_attr_t + * is the mapsize and attribute bitmap copied from the xvattr_t. + * Following the bitmap and bitmapsize two 64 bit words are reserved + * for the create time which may be set. Following the create time + * records a single 64 bit integer which has the bits to set on + * replay for the xvattr. + */ +static void +zfs_log_xvattr(lr_attr_t *lrattr, xvattr_t *xvap) +{ + uint32_t *bitmap; + uint64_t *attrs; + uint64_t *crtime; + xoptattr_t *xoap; + void *scanstamp; + int i; + + xoap = xva_getxoptattr(xvap); + ASSERT(xoap); + + lrattr->lr_attr_masksize = xvap->xva_mapsize; + bitmap = &lrattr->lr_attr_bitmap; + for (i = 0; i != xvap->xva_mapsize; i++, bitmap++) { + *bitmap = xvap->xva_reqattrmap[i]; + } + + /* Now pack the attributes up in a single uint64_t */ + attrs = (uint64_t *)bitmap; + crtime = attrs + 1; + scanstamp = (caddr_t)(crtime + 2); + *attrs = 0; + if (XVA_ISSET_REQ(xvap, XAT_READONLY)) + *attrs |= (xoap->xoa_readonly == 0) ? 0 : + XAT0_READONLY; + if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) + *attrs |= (xoap->xoa_hidden == 0) ? 0 : + XAT0_HIDDEN; + if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) + *attrs |= (xoap->xoa_system == 0) ? 0 : + XAT0_SYSTEM; + if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) + *attrs |= (xoap->xoa_archive == 0) ? 0 : + XAT0_ARCHIVE; + if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE)) + *attrs |= (xoap->xoa_immutable == 0) ? 0 : + XAT0_IMMUTABLE; + if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) + *attrs |= (xoap->xoa_nounlink == 0) ? 0 : + XAT0_NOUNLINK; + if (XVA_ISSET_REQ(xvap, XAT_APPENDONLY)) + *attrs |= (xoap->xoa_appendonly == 0) ? 0 : + XAT0_APPENDONLY; + if (XVA_ISSET_REQ(xvap, XAT_OPAQUE)) + *attrs |= (xoap->xoa_opaque == 0) ? 0 : + XAT0_APPENDONLY; + if (XVA_ISSET_REQ(xvap, XAT_NODUMP)) + *attrs |= (xoap->xoa_nodump == 0) ? 0 : + XAT0_NODUMP; + if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) + *attrs |= (xoap->xoa_av_quarantined == 0) ? 0 : + XAT0_AV_QUARANTINED; + if (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED)) + *attrs |= (xoap->xoa_av_modified == 0) ? 0 : + XAT0_AV_MODIFIED; + if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) + ZFS_TIME_ENCODE(&xoap->xoa_createtime, crtime); + if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) + bcopy(xoap->xoa_av_scanstamp, scanstamp, AV_SCANSTAMP_SZ); +} + +static void * +zfs_log_fuid_ids(zfs_fuid_info_t *fuidp, void *start) +{ + zfs_fuid_t *zfuid; + uint64_t *fuidloc = start; + + /* First copy in the ACE FUIDs */ + for (zfuid = list_head(&fuidp->z_fuids); zfuid; + zfuid = list_next(&fuidp->z_fuids, zfuid)) { + *fuidloc++ = zfuid->z_logfuid; + } + return (fuidloc); +} + + +static void * +zfs_log_fuid_domains(zfs_fuid_info_t *fuidp, void *start) +{ + zfs_fuid_domain_t *zdomain; + + /* now copy in the domain info, if any */ + if (fuidp->z_domain_str_sz != 0) { + for (zdomain = list_head(&fuidp->z_domains); zdomain; + zdomain = list_next(&fuidp->z_domains, zdomain)) { + bcopy((void *)zdomain->z_domain, start, + strlen(zdomain->z_domain) + 1); + start = (caddr_t)start + + strlen(zdomain->z_domain) + 1; + } + } + return (start); +} + /* - * zfs_log_create() is used to handle TX_CREATE, TX_MKDIR and TX_MKXATTR + * zfs_log_create() is used to handle TX_CREATE, TX_CREATE_ATTR, TX_MKDIR, + * TX_MKDIR_ATTR and TX_MKXATTR * transactions. + * + * TX_CREATE and TX_MKDIR are standard creates, but they may have FUID + * domain information appended prior to the name. In this case the + * uid/gid in the log record will be a log centric FUID. + * + * TX_CREATE_ACL_ATTR and TX_MKDIR_ACL_ATTR handle special creates that + * may contain attributes, ACL and optional fuid information. + * + * TX_CREATE_ACL and TX_MKDIR_ACL handle special creates that specify + * and ACL and normal users/groups in the ACEs. + * + * There may be an optional xvattr attribute information similar + * to zfs_log_setattr. + * + * Also, after the file name "domain" strings may be appended. */ void -zfs_log_create(zilog_t *zilog, dmu_tx_t *tx, int txtype, - znode_t *dzp, znode_t *zp, char *name) +zfs_log_create(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, + znode_t *dzp, znode_t *zp, char *name, vsecattr_t *vsecp, + zfs_fuid_info_t *fuidp, vattr_t *vap) { itx_t *itx; uint64_t seq; lr_create_t *lr; + lr_acl_create_t *lracl; + size_t aclsize; + size_t xvatsize = 0; + size_t txsize; + xvattr_t *xvap = (xvattr_t *)vap; + void *end; + size_t lrsize; + size_t namesize = strlen(name) + 1; + size_t fuidsz = 0; if (zilog == NULL) return; - itx = zil_itx_create(txtype, sizeof (*lr) + namesize); + /* + * If we have FUIDs present then add in space for + * domains and ACE fuid's if any. + */ + if (fuidp) { + fuidsz += fuidp->z_domain_str_sz; + fuidsz += fuidp->z_fuid_cnt * sizeof (uint64_t); + } + + if (vap->va_mask & AT_XVATTR) + xvatsize = ZIL_XVAT_SIZE(xvap->xva_mapsize); + + if ((int)txtype == TX_CREATE_ATTR || (int)txtype == TX_MKDIR_ATTR || + (int)txtype == TX_CREATE || (int)txtype == TX_MKDIR || + (int)txtype == TX_MKXATTR) { + txsize = sizeof (*lr) + namesize + fuidsz + xvatsize; + lrsize = sizeof (*lr); + } else { + aclsize = (vsecp) ? vsecp->vsa_aclentsz : 0; + txsize = + sizeof (lr_acl_create_t) + namesize + fuidsz + + aclsize + xvatsize; + lrsize = sizeof (lr_acl_create_t); + } + + itx = zil_itx_create(txtype, txsize); + lr = (lr_create_t *)&itx->itx_lr; lr->lr_doid = dzp->z_id; lr->lr_foid = zp->z_id; lr->lr_mode = zp->z_phys->zp_mode; - lr->lr_uid = zp->z_phys->zp_uid; - lr->lr_gid = zp->z_phys->zp_gid; + if (!IS_EPHEMERAL(zp->z_phys->zp_uid)) { + lr->lr_uid = (uint64_t)zp->z_phys->zp_uid; + } else { + lr->lr_uid = fuidp->z_fuid_owner; + } + if (!IS_EPHEMERAL(zp->z_phys->zp_gid)) { + lr->lr_gid = (uint64_t)zp->z_phys->zp_gid; + } else { + lr->lr_gid = fuidp->z_fuid_group; + } lr->lr_gen = zp->z_phys->zp_gen; lr->lr_crtime[0] = zp->z_phys->zp_crtime[0]; lr->lr_crtime[1] = zp->z_phys->zp_crtime[1]; lr->lr_rdev = zp->z_phys->zp_rdev; - bcopy(name, (char *)(lr + 1), namesize); + + /* + * Fill in xvattr info if any + */ + if (vap->va_mask & AT_XVATTR) { + zfs_log_xvattr((lr_attr_t *)((caddr_t)lr + lrsize), xvap); + end = (caddr_t)lr + lrsize + xvatsize; + } else { + end = (caddr_t)lr + lrsize; + } + + /* Now fill in any ACL info */ + + if (vsecp) { + lracl = (lr_acl_create_t *)&itx->itx_lr; + lracl->lr_aclcnt = vsecp->vsa_aclcnt; + lracl->lr_acl_bytes = aclsize; + lracl->lr_domcnt = fuidp ? fuidp->z_domain_cnt : 0; + lracl->lr_fuidcnt = fuidp ? fuidp->z_fuid_cnt : 0; + if (vsecp->vsa_aclflags & VSA_ACE_ACLFLAGS) + lracl->lr_acl_flags = (uint64_t)vsecp->vsa_aclflags; + else + lracl->lr_acl_flags = 0; + + bcopy(vsecp->vsa_aclentp, end, aclsize); + end = (caddr_t)end + aclsize; + } + + /* drop in FUID info */ + if (fuidp) { + end = zfs_log_fuid_ids(fuidp, end); + end = zfs_log_fuid_domains(fuidp, end); + } + /* + * Now place file name in log record + */ + bcopy(name, end, namesize); seq = zil_itx_assign(zilog, itx, tx); dzp->z_last_itx = seq; @@ -93,7 +326,7 @@ zfs_log_create(zilog_t *zilog, dmu_tx_t *tx, int txtype, * zfs_log_remove() handles both TX_REMOVE and TX_RMDIR transactions. */ void -zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, int txtype, +zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, znode_t *dzp, char *name) { itx_t *itx; @@ -117,7 +350,7 @@ zfs_log_remove(zilog_t *zilog, dmu_tx_t *tx, int txtype, * zfs_log_link() handles TX_LINK transactions. */ void -zfs_log_link(zilog_t *zilog, dmu_tx_t *tx, int txtype, +zfs_log_link(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, znode_t *dzp, znode_t *zp, char *name) { itx_t *itx; @@ -143,8 +376,8 @@ zfs_log_link(zilog_t *zilog, dmu_tx_t *tx, int txtype, * zfs_log_symlink() handles TX_SYMLINK transactions. */ void -zfs_log_symlink(zilog_t *zilog, dmu_tx_t *tx, int txtype, - znode_t *dzp, znode_t *zp, char *name, char *link) +zfs_log_symlink(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, + znode_t *dzp, znode_t *zp, char *name, char *link) { itx_t *itx; uint64_t seq; @@ -177,7 +410,7 @@ zfs_log_symlink(zilog_t *zilog, dmu_tx_t *tx, int txtype, * zfs_log_rename() handles TX_RENAME transactions. */ void -zfs_log_rename(zilog_t *zilog, dmu_tx_t *tx, int txtype, +zfs_log_rename(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, znode_t *sdzp, char *sname, znode_t *tdzp, char *dname, znode_t *szp) { itx_t *itx; @@ -328,25 +561,60 @@ zfs_log_truncate(zilog_t *zilog, dmu_tx_t *tx, int txtype, */ void zfs_log_setattr(zilog_t *zilog, dmu_tx_t *tx, int txtype, - znode_t *zp, vattr_t *vap, uint_t mask_applied) + znode_t *zp, vattr_t *vap, uint_t mask_applied, zfs_fuid_info_t *fuidp) { - itx_t *itx; - uint64_t seq; - lr_setattr_t *lr; + itx_t *itx; + uint64_t seq; + lr_setattr_t *lr; + xvattr_t *xvap = (xvattr_t *)vap; + size_t recsize = sizeof (lr_setattr_t); + void *start; + if (zilog == NULL || zp->z_unlinked) return; - itx = zil_itx_create(txtype, sizeof (*lr)); + /* + * If XVATTR set, then log record size needs to allow + * for lr_attr_t + xvattr mask, mapsize and create time + * plus actual attribute values + */ + if (vap->va_mask & AT_XVATTR) + recsize = sizeof (*lr) + ZIL_XVAT_SIZE(xvap->xva_mapsize); + + if (fuidp) + recsize += fuidp->z_domain_str_sz; + + itx = zil_itx_create(txtype, recsize); lr = (lr_setattr_t *)&itx->itx_lr; lr->lr_foid = zp->z_id; lr->lr_mask = (uint64_t)mask_applied; lr->lr_mode = (uint64_t)vap->va_mode; - lr->lr_uid = (uint64_t)vap->va_uid; - lr->lr_gid = (uint64_t)vap->va_gid; + if ((mask_applied & AT_UID) && IS_EPHEMERAL(vap->va_uid)) + lr->lr_uid = fuidp->z_fuid_owner; + else + lr->lr_uid = (uint64_t)vap->va_uid; + + if ((mask_applied & AT_GID) && IS_EPHEMERAL(vap->va_gid)) + lr->lr_gid = fuidp->z_fuid_group; + else + lr->lr_gid = (uint64_t)vap->va_gid; + lr->lr_size = (uint64_t)vap->va_size; ZFS_TIME_ENCODE(&vap->va_atime, lr->lr_atime); ZFS_TIME_ENCODE(&vap->va_mtime, lr->lr_mtime); + start = (lr_setattr_t *)(lr + 1); + if (vap->va_mask & AT_XVATTR) { + zfs_log_xvattr((lr_attr_t *)start, xvap); + start = (caddr_t)start + ZIL_XVAT_SIZE(xvap->xva_mapsize); + } + + /* + * Now stick on domain information if any on end + */ + + if (fuidp) + (void) zfs_log_fuid_domains(fuidp, start); itx->itx_sync = (zp->z_sync_cnt != 0); seq = zil_itx_assign(zilog, itx, tx); @@ -357,21 +625,62 @@ zfs_log_setattr(zilog_t *zilog, dmu_tx_t *tx, int txtype, * zfs_log_acl() handles TX_ACL transactions. */ void -zfs_log_acl(zilog_t *zilog, dmu_tx_t *tx, int txtype, - znode_t *zp, int aclcnt, ace_t *z_ace) +zfs_log_acl(zilog_t *zilog, dmu_tx_t *tx, znode_t *zp, + vsecattr_t *vsecp, zfs_fuid_info_t *fuidp) { itx_t *itx; uint64_t seq; + lr_acl_v0_t *lrv0; lr_acl_t *lr; + int txtype; + int lrsize; + size_t txsize; + size_t aclbytes = vsecp->vsa_aclentsz; + + txtype = (zp->z_zfsvfs->z_version == ZPL_VERSION_INITIAL) ? + TX_ACL_V0 : TX_ACL; + + if (txtype == TX_ACL) + lrsize = sizeof (*lr); + else + lrsize = sizeof (*lrv0); if (zilog == NULL || zp->z_unlinked) return; - itx = zil_itx_create(txtype, sizeof (*lr) + aclcnt * sizeof (ace_t)); + txsize = lrsize + aclbytes + (fuidp ? fuidp->z_domain_str_sz : 0) + + sizeof (uint64) * (fuidp ? fuidp->z_fuid_cnt : 0); + + itx = zil_itx_create(txtype, txsize); + lr = (lr_acl_t *)&itx->itx_lr; lr->lr_foid = zp->z_id; - lr->lr_aclcnt = (uint64_t)aclcnt; - bcopy(z_ace, (ace_t *)(lr + 1), aclcnt * sizeof (ace_t)); + if (txtype == TX_ACL) { + lr->lr_acl_bytes = aclbytes; + lr->lr_domcnt = fuidp ? fuidp->z_domain_cnt : 0; + lr->lr_fuidcnt = fuidp ? fuidp->z_fuid_cnt : 0; + if (vsecp->vsa_mask & VSA_ACE_ACLFLAGS) + lr->lr_acl_flags = (uint64_t)vsecp->vsa_aclflags; + else + lr->lr_acl_flags = 0; + } + lr->lr_aclcnt = (uint64_t)vsecp->vsa_aclcnt; + + if (txtype == TX_ACL_V0) { + lrv0 = (lr_acl_v0_t *)lr; + bcopy(vsecp->vsa_aclentp, (ace_t *)(lrv0 + 1), aclbytes); + } else { + void *start = (ace_t *)(lr + 1); + + bcopy(vsecp->vsa_aclentp, start, aclbytes); + + start = (caddr_t)start + aclbytes; + + if (fuidp) { + start = zfs_log_fuid_ids(fuidp, start); + (void) zfs_log_fuid_domains(fuidp, start); + } + } itx->itx_sync = (zp->z_sync_cnt != 0); seq = zil_itx_assign(zilog, itx, tx); diff --git a/usr/src/uts/common/fs/zfs/zfs_replay.c b/usr/src/uts/common/fs/zfs/zfs_replay.c index 4b028510b504..6b2937490f5d 100644 --- a/usr/src/uts/common/fs/zfs/zfs_replay.c +++ b/usr/src/uts/common/fs/zfs/zfs_replay.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -62,8 +63,8 @@ zfs_init_vattr(vattr_t *vap, uint64_t mask, uint64_t mode, vap->va_mask = (uint_t)mask; vap->va_type = IFTOVT(mode); vap->va_mode = mode & MODEMASK; - vap->va_uid = (uid_t)uid; - vap->va_gid = (gid_t)gid; + vap->va_uid = (uid_t)(IS_EPHEMERAL(uid)) ? -1 : uid; + vap->va_gid = (gid_t)(IS_EPHEMERAL(gid)) ? -1 : gid; vap->va_rdev = zfs_cmpldev(rdev); vap->va_nodeid = nodeid; } @@ -75,23 +76,345 @@ zfs_replay_error(zfsvfs_t *zfsvfs, lr_t *lr, boolean_t byteswap) return (ENOTSUP); } +static void +zfs_replay_xvattr(lr_attr_t *lrattr, xvattr_t *xvap) +{ + xoptattr_t *xoap = NULL; + uint64_t *attrs; + uint64_t *crtime; + void *scanstamp; + + xvap->xva_vattr.va_mask |= AT_XVATTR; + if ((xoap = xva_getxoptattr(xvap)) == NULL) { + xvap->xva_vattr.va_mask &= ~AT_XVATTR; /* shouldn't happen */ + return; + } + + ASSERT(lrattr->lr_attr_masksize == xvap->xva_mapsize); + bcopy(&lrattr->lr_attr_bitmap, xvap->xva_reqattrmap, + xvap->xva_mapsize); + attrs = (uint64_t *)(lrattr + lrattr->lr_attr_masksize - 1); + crtime = attrs + 1; + scanstamp = (caddr_t)(crtime + 2); + + if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) + xoap->xoa_hidden = ((*attrs & XAT0_HIDDEN) != 0); + if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) + xoap->xoa_system = ((*attrs & XAT0_SYSTEM) != 0); + if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) + xoap->xoa_archive = ((*attrs & XAT0_ARCHIVE) != 0); + if (XVA_ISSET_REQ(xvap, XAT_READONLY)) + xoap->xoa_readonly = ((*attrs & XAT0_READONLY) != 0); + if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE)) + xoap->xoa_immutable = ((*attrs & XAT0_IMMUTABLE) != 0); + if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) + xoap->xoa_nounlink = ((*attrs & XAT0_NOUNLINK) != 0); + if (XVA_ISSET_REQ(xvap, XAT_APPENDONLY)) + xoap->xoa_appendonly = ((*attrs & XAT0_APPENDONLY) != 0); + if (XVA_ISSET_REQ(xvap, XAT_NODUMP)) + xoap->xoa_nodump = ((*attrs & XAT0_NODUMP) != 0); + if (XVA_ISSET_REQ(xvap, XAT_OPAQUE)) + xoap->xoa_opaque = ((*attrs & XAT0_OPAQUE) != 0); + if (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED)) + xoap->xoa_av_modified = ((*attrs & XAT0_AV_MODIFIED) != 0); + if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) + xoap->xoa_av_quarantined = + ((*attrs & XAT0_AV_QUARANTINED) != 0); + if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) + ZFS_TIME_DECODE(&xoap->xoa_createtime, crtime); + if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) + bcopy(scanstamp, xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ); +} + +static int +zfs_replay_domain_cnt(uint64_t uid, uint64_t gid) +{ + uint64_t uid_idx; + uint64_t gid_idx; + int domcnt = 0; + + uid_idx = FUID_INDEX(uid); + gid_idx = FUID_INDEX(gid); + if (uid_idx) + domcnt++; + if (gid_idx > 0 && gid_idx != uid_idx) + domcnt++; + + return (domcnt); +} + +static void * +zfs_replay_fuid_domain_common(zfs_fuid_info_t *fuid_infop, void *start, + int domcnt) +{ + int i; + + for (i = 0; i != domcnt; i++) { + fuid_infop->z_domain_table[i] = start; + start = (caddr_t)start + strlen(start) + 1; + } + + return (start); +} + +/* + * Set the uid/gid in the fuid_info structure. + */ +static void +zfs_replay_fuid_ugid(zfs_fuid_info_t *fuid_infop, uint64_t uid, uint64_t gid) +{ + /* + * If owner or group are log specific FUIDs then slurp up + * domain information and build zfs_fuid_info_t + */ + if (IS_EPHEMERAL(uid)) + fuid_infop->z_fuid_owner = uid; + + if (IS_EPHEMERAL(gid)) + fuid_infop->z_fuid_group = gid; +} + +/* + * Load fuid domains into fuid_info_t + */ +static zfs_fuid_info_t * +zfs_replay_fuid_domain(void *buf, void **end, uint64_t uid, uint64_t gid) +{ + int domcnt; + + zfs_fuid_info_t *fuid_infop; + + fuid_infop = zfs_fuid_info_alloc(); + + domcnt = zfs_replay_domain_cnt(uid, gid); + + if (domcnt == 0) + return (fuid_infop); + + fuid_infop->z_domain_table = + kmem_zalloc(domcnt * sizeof (char **), KM_SLEEP); + + zfs_replay_fuid_ugid(fuid_infop, uid, gid); + + fuid_infop->z_domain_cnt = domcnt; + *end = zfs_replay_fuid_domain_common(fuid_infop, buf, domcnt); + return (fuid_infop); +} + +/* + * load zfs_fuid_t's and fuid_domains into fuid_info_t + */ +static zfs_fuid_info_t * +zfs_replay_fuids(void *start, void **end, int idcnt, int domcnt, uint64_t uid, + uint64_t gid) +{ + uint64_t *log_fuid = (uint64_t *)start; + zfs_fuid_info_t *fuid_infop; + int i; + + fuid_infop = zfs_fuid_info_alloc(); + fuid_infop->z_domain_cnt = domcnt; + + fuid_infop->z_domain_table = + kmem_zalloc(domcnt * sizeof (char **), KM_SLEEP); + + for (i = 0; i != idcnt; i++) { + zfs_fuid_t *zfuid; + + zfuid = kmem_alloc(sizeof (zfs_fuid_t), KM_SLEEP); + zfuid->z_logfuid = *log_fuid; + zfuid->z_id = -1; + zfuid->z_domidx = 0; + list_insert_tail(&fuid_infop->z_fuids, zfuid); + log_fuid++; + } + + zfs_replay_fuid_ugid(fuid_infop, uid, gid); + + *end = zfs_replay_fuid_domain_common(fuid_infop, log_fuid, domcnt); + return (fuid_infop); +} + +static void +zfs_replay_swap_attrs(lr_attr_t *lrattr) +{ + /* swap the lr_attr structure */ + byteswap_uint32_array(lrattr, sizeof (*lrattr)); + /* swap the bitmap */ + byteswap_uint32_array(lrattr + 1, lrattr->lr_attr_masksize - 1); + /* swap the attributes, create time + 64 bit word for attributes */ + byteswap_uint64_array(lrattr + (sizeof (uint32_t) * + (lrattr->lr_attr_masksize - 1)), 3 * sizeof (uint64_t)); +} + +/* + * Replay file create with optional ACL, xvattr information as well + * as option FUID information. + */ +static int +zfs_replay_create_acl(zfsvfs_t *zfsvfs, + lr_acl_create_t *lracl, boolean_t byteswap) +{ + char *name = NULL; /* location determined later */ + lr_create_t *lr = (lr_create_t *)lracl; + znode_t *dzp; + vnode_t *vp = NULL; + xvattr_t xva; + int vflg = 0; + vsecattr_t vsec = { 0 }; + lr_attr_t *lrattr; + void *aclstart; + void *fuidstart; + size_t xvatlen = 0; + uint64_t txtype; + int error; + + if (byteswap) { + byteswap_uint64_array(lracl, sizeof (*lracl)); + txtype = (int)lr->lr_common.lrc_txtype; + if (txtype == TX_CREATE_ACL_ATTR || + txtype == TX_MKDIR_ACL_ATTR) { + lrattr = (lr_attr_t *)(caddr_t)(lr + 1); + zfs_replay_swap_attrs(lrattr); + xvatlen = ZIL_XVAT_SIZE(lrattr->lr_attr_masksize); + } + + aclstart = (caddr_t)(lracl + 1) + xvatlen; + zfs_ace_byteswap(aclstart, lracl->lr_acl_bytes, B_FALSE); + /* swap fuids */ + if (lracl->lr_fuidcnt) { + byteswap_uint64_array((caddr_t)aclstart + + lracl->lr_acl_bytes, sizeof (uint64_t)); + } + } + + if ((error = zfs_zget(zfsvfs, lr->lr_doid, &dzp)) != 0) + return (error); + + xva_init(&xva); + zfs_init_vattr(&xva.xva_vattr, AT_TYPE | AT_MODE | AT_UID | AT_GID, + lr->lr_mode, lr->lr_uid, lr->lr_gid, lr->lr_rdev, lr->lr_foid); + + /* + * All forms of zfs create (create, mkdir, mkxattrdir, symlink) + * eventually end up in zfs_mknode(), which assigns the object's + * creation time and generation number. The generic VOP_CREATE() + * doesn't have either concept, so we smuggle the values inside + * the vattr's otherwise unused va_ctime and va_nblocks fields. + */ + ZFS_TIME_DECODE(&xva.xva_vattr.va_ctime, lr->lr_crtime); + xva.xva_vattr.va_nblocks = lr->lr_gen; + + error = dmu_object_info(zfsvfs->z_os, lr->lr_foid, NULL); + if (error != ENOENT) + goto bail; + + if (lr->lr_common.lrc_txtype & TX_CI) + vflg |= FIGNORECASE; + switch ((int)lr->lr_common.lrc_txtype) { + case TX_CREATE_ACL: + aclstart = (caddr_t)(lracl + 1); + fuidstart = (caddr_t)aclstart + lracl->lr_acl_bytes; + zfsvfs->z_fuid_replay = zfs_replay_fuids(fuidstart, + (void *)&name, lracl->lr_fuidcnt, lracl->lr_domcnt, + lr->lr_uid, lr->lr_gid); + /*FALLTHROUGH*/ + case TX_CREATE_ACL_ATTR: + if (name == NULL) { + lrattr = (lr_attr_t *)(caddr_t)(lracl + 1); + xvatlen = ZIL_XVAT_SIZE(lrattr->lr_attr_masksize); + xva.xva_vattr.va_mask |= AT_XVATTR; + zfs_replay_xvattr(lrattr, &xva); + } + vsec.vsa_mask = VSA_ACE | VSA_ACE_ACLFLAGS; + vsec.vsa_aclentp = (caddr_t)(lracl + 1) + xvatlen; + vsec.vsa_aclcnt = lracl->lr_aclcnt; + vsec.vsa_aclentsz = lracl->lr_acl_bytes; + vsec.vsa_aclflags = lracl->lr_acl_flags; + if (zfsvfs->z_fuid_replay == NULL) + fuidstart = (caddr_t)(lracl + 1) + xvatlen + + lracl->lr_acl_bytes; + zfsvfs->z_fuid_replay = + zfs_replay_fuids(fuidstart, + (void *)&name, lracl->lr_fuidcnt, lracl->lr_domcnt, + lr->lr_uid, lr->lr_gid); + + error = VOP_CREATE(ZTOV(dzp), name, &xva.xva_vattr, + 0, 0, &vp, kcred, vflg, NULL, &vsec); + break; + case TX_MKDIR_ACL: + aclstart = (caddr_t)(lracl + 1); + fuidstart = (caddr_t)aclstart + lracl->lr_acl_bytes; + zfsvfs->z_fuid_replay = zfs_replay_fuids(fuidstart, + (void *)&name, lracl->lr_fuidcnt, lracl->lr_domcnt, + lr->lr_uid, lr->lr_gid); + /*FALLTHROUGH*/ + case TX_MKDIR_ACL_ATTR: + if (name == NULL) { + lrattr = (lr_attr_t *)(caddr_t)(lracl + 1); + xvatlen = ZIL_XVAT_SIZE(lrattr->lr_attr_masksize); + zfs_replay_xvattr(lrattr, &xva); + } + vsec.vsa_mask = VSA_ACE | VSA_ACE_ACLFLAGS; + vsec.vsa_aclentp = (caddr_t)(lracl + 1) + xvatlen; + vsec.vsa_aclcnt = lracl->lr_aclcnt; + vsec.vsa_aclentsz = lracl->lr_acl_bytes; + vsec.vsa_aclflags = lracl->lr_acl_flags; + if (zfsvfs->z_fuid_replay == NULL) + fuidstart = (caddr_t)(lracl + 1) + xvatlen + + lracl->lr_acl_bytes; + zfsvfs->z_fuid_replay = + zfs_replay_fuids(fuidstart, + (void *)&name, lracl->lr_fuidcnt, lracl->lr_domcnt, + lr->lr_uid, lr->lr_gid); + error = VOP_MKDIR(ZTOV(dzp), name, &xva.xva_vattr, + &vp, kcred, NULL, vflg, &vsec); + break; + default: + error = ENOTSUP; + } + +bail: + if (error == 0 && vp != NULL) + VN_RELE(vp); + + VN_RELE(ZTOV(dzp)); + + zfs_fuid_info_free(zfsvfs->z_fuid_replay); + zfsvfs->z_fuid_replay = NULL; + + return (error); +} + static int zfs_replay_create(zfsvfs_t *zfsvfs, lr_create_t *lr, boolean_t byteswap) { - char *name = (char *)(lr + 1); /* name follows lr_create_t */ + char *name = NULL; /* location determined later */ char *link; /* symlink content follows name */ znode_t *dzp; vnode_t *vp = NULL; - vattr_t va; + xvattr_t xva; + int vflg = 0; + size_t lrsize = sizeof (lr_create_t); + lr_attr_t *lrattr; + void *start; + size_t xvatlen; + uint64_t txtype; int error; - if (byteswap) + if (byteswap) { byteswap_uint64_array(lr, sizeof (*lr)); + txtype = (int)lr->lr_common.lrc_txtype; + if (txtype == TX_CREATE_ATTR || txtype == TX_MKDIR_ATTR) + zfs_replay_swap_attrs((lr_attr_t *)(lr + 1)); + } + if ((error = zfs_zget(zfsvfs, lr->lr_doid, &dzp)) != 0) return (error); - zfs_init_vattr(&va, AT_TYPE | AT_MODE | AT_UID | AT_GID, + xva_init(&xva); + zfs_init_vattr(&xva.xva_vattr, AT_TYPE | AT_MODE | AT_UID | AT_GID, lr->lr_mode, lr->lr_uid, lr->lr_gid, lr->lr_rdev, lr->lr_foid); /* @@ -101,26 +424,77 @@ zfs_replay_create(zfsvfs_t *zfsvfs, lr_create_t *lr, boolean_t byteswap) * doesn't have either concept, so we smuggle the values inside * the vattr's otherwise unused va_ctime and va_nblocks fields. */ - ZFS_TIME_DECODE(&va.va_ctime, lr->lr_crtime); - va.va_nblocks = lr->lr_gen; + ZFS_TIME_DECODE(&xva.xva_vattr.va_ctime, lr->lr_crtime); + xva.xva_vattr.va_nblocks = lr->lr_gen; error = dmu_object_info(zfsvfs->z_os, lr->lr_foid, NULL); if (error != ENOENT) goto out; + if (lr->lr_common.lrc_txtype & TX_CI) + vflg |= FIGNORECASE; + + /* + * Symlinks don't have fuid info, and CIFS never creates + * symlinks. + * + * The _ATTR versions will grab the fuid info in their subcases. + */ + if ((int)lr->lr_common.lrc_txtype != TX_SYMLINK && + (int)lr->lr_common.lrc_txtype != TX_MKDIR_ATTR && + (int)lr->lr_common.lrc_txtype != TX_CREATE_ATTR) { + start = (lr + 1); + zfsvfs->z_fuid_replay = + zfs_replay_fuid_domain(start, &start, + lr->lr_uid, lr->lr_gid); + } + switch ((int)lr->lr_common.lrc_txtype) { + case TX_CREATE_ATTR: + lrattr = (lr_attr_t *)(caddr_t)(lr + 1); + xvatlen = ZIL_XVAT_SIZE(lrattr->lr_attr_masksize); + zfs_replay_xvattr((lr_attr_t *)((caddr_t)lr + lrsize), &xva); + start = (caddr_t)(lr + 1) + xvatlen; + zfsvfs->z_fuid_replay = + zfs_replay_fuid_domain(start, &start, + lr->lr_uid, lr->lr_gid); + name = (char *)start; + + /*FALLTHROUGH*/ case TX_CREATE: - error = VOP_CREATE(ZTOV(dzp), name, &va, 0, 0, &vp, kcred, 0); + if (name == NULL) + name = (char *)start; + + error = VOP_CREATE(ZTOV(dzp), name, &xva.xva_vattr, + 0, 0, &vp, kcred, vflg, NULL, NULL); break; + case TX_MKDIR_ATTR: + lrattr = (lr_attr_t *)(caddr_t)(lr + 1); + xvatlen = ZIL_XVAT_SIZE(lrattr->lr_attr_masksize); + zfs_replay_xvattr((lr_attr_t *)((caddr_t)lr + lrsize), &xva); + start = (caddr_t)(lr + 1) + xvatlen; + zfsvfs->z_fuid_replay = + zfs_replay_fuid_domain(start, &start, + lr->lr_uid, lr->lr_gid); + name = (char *)start; + + /*FALLTHROUGH*/ case TX_MKDIR: - error = VOP_MKDIR(ZTOV(dzp), name, &va, &vp, kcred); + if (name == NULL) + name = (char *)(lr + 1); + + error = VOP_MKDIR(ZTOV(dzp), name, &xva.xva_vattr, + &vp, kcred, NULL, vflg, NULL); break; case TX_MKXATTR: - error = zfs_make_xattrdir(dzp, &va, &vp, kcred); + name = (char *)(lr + 1); + error = zfs_make_xattrdir(dzp, &xva.xva_vattr, &vp, kcred); break; case TX_SYMLINK: + name = (char *)(lr + 1); link = name + strlen(name) + 1; - error = VOP_SYMLINK(ZTOV(dzp), name, &va, link, kcred); + error = VOP_SYMLINK(ZTOV(dzp), name, &xva.xva_vattr, + link, kcred, NULL, vflg); break; default: error = ENOTSUP; @@ -132,6 +506,9 @@ zfs_replay_create(zfsvfs_t *zfsvfs, lr_create_t *lr, boolean_t byteswap) VN_RELE(ZTOV(dzp)); + if (zfsvfs->z_fuid_replay) + zfs_fuid_info_free(zfsvfs->z_fuid_replay); + zfsvfs->z_fuid_replay = NULL; return (error); } @@ -141,6 +518,7 @@ zfs_replay_remove(zfsvfs_t *zfsvfs, lr_remove_t *lr, boolean_t byteswap) char *name = (char *)(lr + 1); /* name follows lr_remove_t */ znode_t *dzp; int error; + int vflg = 0; if (byteswap) byteswap_uint64_array(lr, sizeof (*lr)); @@ -148,12 +526,15 @@ zfs_replay_remove(zfsvfs_t *zfsvfs, lr_remove_t *lr, boolean_t byteswap) if ((error = zfs_zget(zfsvfs, lr->lr_doid, &dzp)) != 0) return (error); + if (lr->lr_common.lrc_txtype & TX_CI) + vflg |= FIGNORECASE; + switch ((int)lr->lr_common.lrc_txtype) { case TX_REMOVE: - error = VOP_REMOVE(ZTOV(dzp), name, kcred); + error = VOP_REMOVE(ZTOV(dzp), name, kcred, NULL, vflg); break; case TX_RMDIR: - error = VOP_RMDIR(ZTOV(dzp), name, NULL, kcred); + error = VOP_RMDIR(ZTOV(dzp), name, NULL, kcred, NULL, vflg); break; default: error = ENOTSUP; @@ -170,6 +551,7 @@ zfs_replay_link(zfsvfs_t *zfsvfs, lr_link_t *lr, boolean_t byteswap) char *name = (char *)(lr + 1); /* name follows lr_link_t */ znode_t *dzp, *zp; int error; + int vflg = 0; if (byteswap) byteswap_uint64_array(lr, sizeof (*lr)); @@ -182,7 +564,10 @@ zfs_replay_link(zfsvfs_t *zfsvfs, lr_link_t *lr, boolean_t byteswap) return (error); } - error = VOP_LINK(ZTOV(dzp), ZTOV(zp), name, kcred); + if (lr->lr_common.lrc_txtype & TX_CI) + vflg |= FIGNORECASE; + + error = VOP_LINK(ZTOV(dzp), ZTOV(zp), name, kcred, NULL, vflg); VN_RELE(ZTOV(zp)); VN_RELE(ZTOV(dzp)); @@ -197,6 +582,7 @@ zfs_replay_rename(zfsvfs_t *zfsvfs, lr_rename_t *lr, boolean_t byteswap) char *tname = sname + strlen(sname) + 1; znode_t *sdzp, *tdzp; int error; + int vflg = 0; if (byteswap) byteswap_uint64_array(lr, sizeof (*lr)); @@ -209,7 +595,11 @@ zfs_replay_rename(zfsvfs_t *zfsvfs, lr_rename_t *lr, boolean_t byteswap) return (error); } - error = VOP_RENAME(ZTOV(sdzp), sname, ZTOV(tdzp), tname, kcred); + if (lr->lr_common.lrc_txtype & TX_CI) + vflg |= FIGNORECASE; + + error = VOP_RENAME(ZTOV(sdzp), sname, ZTOV(tdzp), tname, kcred, + NULL, vflg); VN_RELE(ZTOV(tdzp)); VN_RELE(ZTOV(sdzp)); @@ -286,12 +676,20 @@ static int zfs_replay_setattr(zfsvfs_t *zfsvfs, lr_setattr_t *lr, boolean_t byteswap) { znode_t *zp; - vattr_t va; + xvattr_t xva; + vattr_t *vap = &xva.xva_vattr; int error; + void *start; - if (byteswap) + xva_init(&xva); + if (byteswap) { byteswap_uint64_array(lr, sizeof (*lr)); + if ((lr->lr_mask & AT_XVATTR) && + zfsvfs->z_version >= ZPL_VERSION_INITIAL) + zfs_replay_swap_attrs((lr_attr_t *)(lr + 1)); + } + if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0) { /* * As we can log setattrs out of order, it's possible the @@ -303,33 +701,95 @@ zfs_replay_setattr(zfsvfs_t *zfsvfs, lr_setattr_t *lr, boolean_t byteswap) return (error); } - zfs_init_vattr(&va, lr->lr_mask, lr->lr_mode, + zfs_init_vattr(vap, lr->lr_mask, lr->lr_mode, lr->lr_uid, lr->lr_gid, 0, lr->lr_foid); - va.va_size = lr->lr_size; - ZFS_TIME_DECODE(&va.va_atime, lr->lr_atime); - ZFS_TIME_DECODE(&va.va_mtime, lr->lr_mtime); + vap->va_size = lr->lr_size; + ZFS_TIME_DECODE(&vap->va_atime, lr->lr_atime); + ZFS_TIME_DECODE(&vap->va_mtime, lr->lr_mtime); + + /* + * Fill in xvattr_t portions if necessary. + */ + + start = (lr_setattr_t *)(lr + 1); + if (vap->va_mask & AT_XVATTR) { + zfs_replay_xvattr((lr_attr_t *)start, &xva); + start = (caddr_t)start + + ZIL_XVAT_SIZE(((lr_attr_t *)start)->lr_attr_masksize); + } else + xva.xva_vattr.va_mask &= ~AT_XVATTR; + + zfsvfs->z_fuid_replay = zfs_replay_fuid_domain(start, &start, + lr->lr_uid, lr->lr_gid); - error = VOP_SETATTR(ZTOV(zp), &va, 0, kcred, NULL); + error = VOP_SETATTR(ZTOV(zp), vap, 0, kcred, NULL); + zfs_fuid_info_free(zfsvfs->z_fuid_replay); + zfsvfs->z_fuid_replay = NULL; VN_RELE(ZTOV(zp)); return (error); } static int -zfs_replay_acl(zfsvfs_t *zfsvfs, lr_acl_t *lr, boolean_t byteswap) +zfs_replay_acl_v0(zfsvfs_t *zfsvfs, lr_acl_v0_t *lr, boolean_t byteswap) { ace_t *ace = (ace_t *)(lr + 1); /* ace array follows lr_acl_t */ vsecattr_t vsa; znode_t *zp; int error; + if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0) { + /* + * As we can log acls out of order, it's possible the + * file has been removed. In this case just drop the acl + * and return success. + */ + if (error == ENOENT) + error = 0; + return (error); + } + if (byteswap) { byteswap_uint64_array(lr, sizeof (*lr)); - zfs_ace_byteswap(ace, lr->lr_aclcnt); + zfs_oldace_byteswap(ace, lr->lr_aclcnt); } + bzero(&vsa, sizeof (vsa)); + vsa.vsa_mask = VSA_ACE | VSA_ACECNT; + vsa.vsa_aclcnt = lr->lr_aclcnt; + vsa.vsa_aclentp = ace; + + error = VOP_SETSECATTR(ZTOV(zp), &vsa, 0, kcred, NULL); + + VN_RELE(ZTOV(zp)); + + return (error); +} + +/* + * Replaying ACLs is complicated by FUID support. + * The log record may contain some optional data + * to be used for replaying FUID's. These pieces + * are the actual FUIDs that were created initially. + * The FUID table index may no longer be valid and + * during zfs_create() a new index may be assigned. + * Because of this the log will contain the original + * doman+rid in order to create a new FUID. + * + * The individual ACEs may contain an ephemeral uid/gid which is no + * longer valid and will need to be replaced with an actual FUID. + * + */ +static int +zfs_replay_acl(zfsvfs_t *zfsvfs, lr_acl_t *lr, boolean_t byteswap) +{ + ace_t *ace = (ace_t *)(lr + 1); + vsecattr_t vsa; + znode_t *zp; + int error; + if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0) { /* * As we can log acls out of order, it's possible the @@ -341,13 +801,36 @@ zfs_replay_acl(zfsvfs_t *zfsvfs, lr_acl_t *lr, boolean_t byteswap) return (error); } + if (byteswap) { + byteswap_uint64_array(lr, sizeof (*lr)); + zfs_ace_byteswap(ace, lr->lr_acl_bytes, B_FALSE); + if (lr->lr_fuidcnt) { + byteswap_uint64_array((caddr_t)ace + lr->lr_acl_bytes, + lr->lr_fuidcnt * sizeof (uint64_t)); + } + } + bzero(&vsa, sizeof (vsa)); - vsa.vsa_mask = VSA_ACE | VSA_ACECNT; + vsa.vsa_mask = VSA_ACE | VSA_ACECNT | VSA_ACE_ACLFLAGS; vsa.vsa_aclcnt = lr->lr_aclcnt; vsa.vsa_aclentp = ace; + vsa.vsa_aclentsz = lr->lr_acl_bytes; + vsa.vsa_aclflags = lr->lr_acl_flags; + + if (lr->lr_fuidcnt) { + void *fuidstart = (caddr_t)ace + lr->lr_acl_bytes; + + zfsvfs->z_fuid_replay = + zfs_replay_fuids(fuidstart, &fuidstart, + lr->lr_fuidcnt, lr->lr_domcnt, 0, 0); + } + + error = VOP_SETSECATTR(ZTOV(zp), &vsa, 0, kcred, NULL); - error = VOP_SETSECATTR(ZTOV(zp), &vsa, 0, kcred); + if (zfsvfs->z_fuid_replay) + zfs_fuid_info_free(zfsvfs->z_fuid_replay); + zfsvfs->z_fuid_replay = NULL; VN_RELE(ZTOV(zp)); return (error); @@ -369,5 +852,12 @@ zil_replay_func_t *zfs_replay_vector[TX_MAX_TYPE] = { zfs_replay_write, /* TX_WRITE */ zfs_replay_truncate, /* TX_TRUNCATE */ zfs_replay_setattr, /* TX_SETATTR */ + zfs_replay_acl_v0, /* TX_ACL_V0 */ zfs_replay_acl, /* TX_ACL */ + zfs_replay_create_acl, /* TX_CREATE_ACL */ + zfs_replay_create, /* TX_CREATE_ATTR */ + zfs_replay_create_acl, /* TX_CREATE_ACL_ATTR */ + zfs_replay_create_acl, /* TX_MKDIR_ACL */ + zfs_replay_create, /* TX_MKDIR_ATTR */ + zfs_replay_create_acl, /* TX_MKDIR_ACL_ATTR */ }; diff --git a/usr/src/uts/common/fs/zfs/zfs_vfsops.c b/usr/src/uts/common/fs/zfs/zfs_vfsops.c index 0736cb322468..4106ca8f2f32 100644 --- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c +++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c @@ -40,6 +40,7 @@ #include "fs/fs_subr.h" #include #include +#include #include #include #include @@ -56,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -329,6 +331,27 @@ exec_changed_cb(void *arg, uint64_t newval) } } +/* + * The nbmand mount option can be changed at mount time. + * We can't allow it to be toggled on live file systems or incorrect + * behavior may be seen from cifs clients + * + * This property isn't registered via dsl_prop_register(), but this callback + * will be called when a file system is first mounted + */ +static void +nbmand_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + if (newval == FALSE) { + vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NBMAND); + vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NONBMAND, NULL, 0); + } else { + vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NONBMAND); + vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NBMAND, NULL, 0); + } +} + static void snapdir_changed_cb(void *arg, uint64_t newval) { @@ -337,6 +360,14 @@ snapdir_changed_cb(void *arg, uint64_t newval) zfsvfs->z_show_ctldir = newval; } +static void +vscan_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + zfsvfs->z_vscan = newval; +} + static void acl_mode_changed_cb(void *arg, uint64_t newval) { @@ -353,18 +384,86 @@ acl_inherit_changed_cb(void *arg, uint64_t newval) zfsvfs->z_acl_inherit = newval; } +static int +zfs_normalization_set(char *osname, zfsvfs_t *zfsvfs) +{ + uint64_t pval; + int error; + + if (zfsvfs->z_version < ZPL_VERSION_FUID) + return (0); + + error = dsl_prop_get_integer(osname, "normalization", &pval, NULL); + if (error) + goto normquit; + switch ((int)pval) { + case ZFS_NORMALIZE_NONE: + break; + case ZFS_NORMALIZE_C: + zfsvfs->z_norm |= U8_TEXTPREP_NFC; + break; + case ZFS_NORMALIZE_KC: + zfsvfs->z_norm |= U8_TEXTPREP_NFKC; + break; + case ZFS_NORMALIZE_D: + zfsvfs->z_norm |= U8_TEXTPREP_NFD; + break; + case ZFS_NORMALIZE_KD: + zfsvfs->z_norm |= U8_TEXTPREP_NFKD; + break; + default: + ASSERT(pval <= ZFS_NORMALIZE_KD); + break; + } + + error = dsl_prop_get_integer(osname, "utf8only", &pval, NULL); + if (error) + goto normquit; + if (pval) + zfsvfs->z_case |= ZFS_UTF8_ONLY; + else + zfsvfs->z_case &= ~ZFS_UTF8_ONLY; + + error = dsl_prop_get_integer(osname, "casesensitivity", &pval, NULL); + if (error) + goto normquit; + vfs_set_feature(zfsvfs->z_vfs, VFSFT_DIRENTFLAGS); + switch ((int)pval) { + case ZFS_CASE_SENSITIVE: + break; + case ZFS_CASE_INSENSITIVE: + zfsvfs->z_norm |= U8_TEXTPREP_TOUPPER; + zfsvfs->z_case |= ZFS_CI_ONLY; + vfs_set_feature(zfsvfs->z_vfs, VFSFT_CASEINSENSITIVE); + vfs_set_feature(zfsvfs->z_vfs, VFSFT_NOCASESENSITIVE); + break; + case ZFS_CASE_MIXED: + zfsvfs->z_norm |= U8_TEXTPREP_TOUPPER; + zfsvfs->z_case |= ZFS_CI_MIXD; + vfs_set_feature(zfsvfs->z_vfs, VFSFT_CASEINSENSITIVE); + break; + default: + ASSERT(pval <= ZFS_CASE_MIXED); + break; + } + +normquit: + return (error); +} + static int zfs_register_callbacks(vfs_t *vfsp) { struct dsl_dataset *ds = NULL; objset_t *os = NULL; zfsvfs_t *zfsvfs = NULL; - int readonly, do_readonly = FALSE; - int setuid, do_setuid = FALSE; - int exec, do_exec = FALSE; - int devices, do_devices = FALSE; - int xattr, do_xattr = FALSE; - int atime, do_atime = FALSE; + uint64_t nbmand; + int readonly, do_readonly = B_FALSE; + int setuid, do_setuid = B_FALSE; + int exec, do_exec = B_FALSE; + int devices, do_devices = B_FALSE; + int xattr, do_xattr = B_FALSE; + int atime, do_atime = B_FALSE; int error = 0; ASSERT(vfsp); @@ -429,6 +528,26 @@ zfs_register_callbacks(vfs_t *vfsp) do_atime = B_TRUE; } + /* + * nbmand is a special property. It can only be changed at + * mount time. + * + * This is weird, but it is documented to only be changeable + * at mount time. + */ + if (vfs_optionisset(vfsp, MNTOPT_NONBMAND, NULL)) { + nbmand = B_FALSE; + } else if (vfs_optionisset(vfsp, MNTOPT_NBMAND, NULL)) { + nbmand = B_TRUE; + } else { + char osname[MAXNAMELEN]; + + dmu_objset_name(os, osname); + if (error = dsl_prop_get_integer(osname, "nbmand", &nbmand, + NULL)) + return (error); + } + /* * Register property callbacks. * @@ -456,6 +575,8 @@ zfs_register_callbacks(vfs_t *vfsp) "aclmode", acl_mode_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, "aclinherit", acl_inherit_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + "vscan", vscan_changed_cb, zfsvfs); if (error) goto unregister; @@ -475,6 +596,8 @@ zfs_register_callbacks(vfs_t *vfsp) if (do_atime) atime_changed_cb(zfsvfs, atime); + nbmand_changed_cb(zfsvfs, nbmand); + return (0); unregister: @@ -494,6 +617,7 @@ zfs_register_callbacks(vfs_t *vfsp) (void) dsl_prop_unregister(ds, "aclmode", acl_mode_changed_cb, zfsvfs); (void) dsl_prop_unregister(ds, "aclinherit", acl_inherit_changed_cb, zfsvfs); + (void) dsl_prop_unregister(ds, "vscan", vscan_changed_cb, zfsvfs); return (error); } @@ -643,15 +767,34 @@ zfs_domount(vfs_t *vfsp, char *osname, cred_t *cr) /* The call to zfs_init_fs leaves the vnode held, release it here. */ VN_RELE(ZTOV(zp)); + /* + * Set features for file system. + */ + zfsvfs->z_use_fuids = USE_FUIDS(zfsvfs->z_version, zfsvfs->z_os); + if (zfsvfs->z_use_fuids) { + vfs_set_feature(vfsp, VFSFT_XVATTR); + vfs_set_feature(vfsp, VFSFT_ACEMASKONACCESS); + vfs_set_feature(vfsp, VFSFT_ACLONCREATE); + } + + /* + * Set normalization regardless of whether or not the object + * set is a snapshot. Snapshots and clones need to have + * identical normalization as did the file system they + * originated from. + */ + if ((error = zfs_normalization_set(osname, zfsvfs)) != 0) + goto out; + if (dmu_objset_is_snapshot(zfsvfs->z_os)) { - uint64_t xattr; + uint64_t pval; ASSERT(mode & DS_MODE_READONLY); atime_changed_cb(zfsvfs, B_FALSE); readonly_changed_cb(zfsvfs, B_TRUE); - if (error = dsl_prop_get_integer(osname, "xattr", &xattr, NULL)) + if (error = dsl_prop_get_integer(osname, "xattr", &pval, NULL)) goto out; - xattr_changed_cb(zfsvfs, xattr); + xattr_changed_cb(zfsvfs, pval); zfsvfs->z_issnap = B_TRUE; } else { error = zfsvfs_setup(zfsvfs, B_TRUE); @@ -715,6 +858,9 @@ zfs_unregister_callbacks(zfsvfs_t *zfsvfs) VERIFY(dsl_prop_unregister(ds, "aclinherit", acl_inherit_changed_cb, zfsvfs) == 0); + + VERIFY(dsl_prop_unregister(ds, "vscan", + vscan_changed_cb, zfsvfs) == 0); } } @@ -916,7 +1062,7 @@ zfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) vattr.va_mask = AT_UID; - if (error = VOP_GETATTR(mvp, &vattr, 0, cr)) { + if (error = VOP_GETATTR(mvp, &vattr, 0, cr, NULL)) { goto out; } @@ -924,7 +1070,7 @@ zfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) goto out; } - if (error = VOP_ACCESS(mvp, VWRITE, 0, cr)) { + if (error = VOP_ACCESS(mvp, VWRITE, 0, cr, NULL)) { goto out; } @@ -1301,7 +1447,7 @@ zfs_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp) ASSERT(*vpp != NULL); if (object == ZFSCTL_INO_SNAPDIR) { VERIFY(zfsctl_root_lookup(*vpp, "snapshot", vpp, NULL, - 0, NULL, NULL) == 0); + 0, NULL, NULL, NULL, NULL, NULL) == 0); } else { VN_HOLD(*vpp); } @@ -1415,6 +1561,7 @@ zfs_freevfs(vfs_t *vfsp) list_destroy(&zfsvfs->z_all_znodes); rrw_destroy(&zfsvfs->z_teardown_lock); rw_destroy(&zfsvfs->z_teardown_inactive_lock); + zfs_fuid_destroy(zfsvfs); kmem_free(zfsvfs, sizeof (zfsvfs_t)); atomic_add_32(&zfs_active_fs_count, -1); @@ -1553,7 +1700,8 @@ static vfsdef_t vfw = { VFSDEF_VERSION, MNTTYPE_ZFS, zfs_vfsinit, - VSW_HASPROTO|VSW_CANRWRO|VSW_CANREMOUNT|VSW_VOLATILEDEV|VSW_STATS, + VSW_HASPROTO|VSW_CANRWRO|VSW_CANREMOUNT|VSW_VOLATILEDEV|VSW_STATS| + VSW_XID, &zfs_mntopts }; diff --git a/usr/src/uts/common/fs/zfs/zfs_vnops.c b/usr/src/uts/common/fs/zfs/zfs_vnops.c index 2e6405be7a52..573f746e7278 100644 --- a/usr/src/uts/common/fs/zfs/zfs_vnops.c +++ b/usr/src/uts/common/fs/zfs/zfs_vnops.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -68,8 +69,12 @@ #include #include "fs/fs_subr.h" #include +#include #include #include +#include +#include +#include /* * Programming rules. @@ -77,8 +82,8 @@ * Each vnode op performs some logical unit of work. To do this, the ZPL must * properly lock its in-core state, create a DMU transaction, do the work, * record this work in the intent log (ZIL), commit the DMU transaction, - * and wait the the intent log to commit if it's is a synchronous operation. - * Morover, the vnode ops must work in both normal and log replay context. + * and wait for the intent log to commit if it is a synchronous operation. + * Moreover, the vnode ops must work in both normal and log replay context. * The ordering of events is important to avoid deadlocks and references * to freed memory. The example below illustrates the following Big Rules: * @@ -160,19 +165,33 @@ */ /* ARGSUSED */ static int -zfs_open(vnode_t **vpp, int flag, cred_t *cr) +zfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) { znode_t *zp = VTOZ(*vpp); + if ((flag & FWRITE) && (zp->z_phys->zp_flags & ZFS_APPENDONLY) && + ((flag & FAPPEND) == 0)) { + return (EPERM); + } + + if (!zfs_has_ctldir(zp) && zp->z_zfsvfs->z_vscan && + ZTOV(zp)->v_type == VREG && + !(zp->z_phys->zp_flags & ZFS_AV_QUARANTINED) && + zp->z_phys->zp_size > 0) + if (fs_vscan(*vpp, cr, 0) != 0) + return (EACCES); + /* Keep a count of the synchronous opens in the znode */ if (flag & (FSYNC | FDSYNC)) atomic_inc_32(&zp->z_sync_cnt); + return (0); } /* ARGSUSED */ static int -zfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) +zfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, + caller_context_t *ct) { znode_t *zp = VTOZ(vp); @@ -186,6 +205,12 @@ zfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) cleanlocks(vp, ddi_get_pid(), 0); cleanshares(vp, ddi_get_pid()); + if (!zfs_has_ctldir(zp) && zp->z_zfsvfs->z_vscan && + ZTOV(zp)->v_type == VREG && + !(zp->z_phys->zp_flags & ZFS_AV_QUARANTINED) && + zp->z_phys->zp_size > 0) + VERIFY(fs_vscan(vp, cr, 1) == 0); + return (0); } @@ -235,7 +260,7 @@ zfs_holey(vnode_t *vp, int cmd, offset_t *off) /* ARGSUSED */ static int zfs_ioctl(vnode_t *vp, int com, intptr_t data, int flag, cred_t *cred, - int *rvalp) + int *rvalp, caller_context_t *ct) { offset_t off; int error; @@ -386,6 +411,7 @@ offset_t zfs_read_chunk_size = 1024 * 1024; /* Tunable */ * and return buffer. * ioflag - SYNC flags; used to provide FRSYNC semantics. * cr - credentials of caller. + * ct - caller context * * OUT: uio - updated offset and range, buffer filled. * @@ -552,6 +578,7 @@ zfs_prefault_write(ssize_t n, struct uio *uio) * and data buffer. * ioflag - FAPPEND flag set if in append mode. * cr - credentials of caller. + * ct - caller context (NFS/CIFS fem monitor only) * * OUT: uio - updated offset and range. * @@ -577,8 +604,17 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct) ssize_t n, nbytes; rl_t *rl; int max_blksz = zfsvfs->z_max_blksz; + uint64_t pflags = zp->z_phys->zp_flags; int error; + /* + * If immutable or not appending then return EPERM + */ + if ((pflags & (ZFS_IMMUTABLE | ZFS_READONLY)) || + ((pflags & ZFS_APPENDONLY) && !(ioflag & FAPPEND) && + (uio->uio_loffset < zp->z_phys->zp_size))) + return (EPERM); + /* * Fasttrack empty write */ @@ -734,6 +770,9 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct) * It would be nice to to this after all writes have * been done, but that would still expose the ISUID/ISGID * to another app after the partial write is committed. + * + * Note: we don't call zfs_fuid_map_id() here because + * user 0 is not an ephemeral uid. */ mutex_enter(&zp->z_acl_lock); if ((zp->z_phys->zp_mode & (S_IXUSR | (S_IXUSR >> 3) | @@ -905,14 +944,20 @@ zfs_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) /*ARGSUSED*/ static int -zfs_access(vnode_t *vp, int mode, int flags, cred_t *cr) +zfs_access(vnode_t *vp, int mode, int flag, cred_t *cr, + caller_context_t *ct) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; int error; ZFS_ENTER_VERIFY_ZP(zfsvfs, zp); - error = zfs_zaccess_rwx(zp, mode, cr); + + if (flag & V_ACE_MASK) + error = zfs_zaccess(zp, mode, flag, B_FALSE, cr); + else + error = zfs_zaccess_rwx(zp, mode, flag, cr); + ZFS_EXIT(zfsvfs); return (error); } @@ -927,6 +972,9 @@ zfs_access(vnode_t *vp, int mode, int flags, cred_t *cr) * flags - LOOKUP_XATTR set if looking for an attribute. * rdir - root directory vnode [UNUSED]. * cr - credentials of caller. + * ct - caller context + * direntflags - directory lookup flags + * realpnp - returned pathname. * * OUT: vpp - vnode of located entry, NULL if not found. * @@ -939,9 +987,9 @@ zfs_access(vnode_t *vp, int mode, int flags, cred_t *cr) /* ARGSUSED */ static int zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, - int flags, vnode_t *rdir, cred_t *cr) + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp) { - znode_t *zdp = VTOZ(dvp); zfsvfs_t *zfsvfs = zdp->z_zfsvfs; int error; @@ -977,8 +1025,10 @@ zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, * Do we have permission to get into attribute directory? */ - if (error = zfs_zaccess(VTOZ(*vpp), ACE_EXECUTE, cr)) { + if (error = zfs_zaccess(VTOZ(*vpp), ACE_EXECUTE, 0, + B_FALSE, cr)) { VN_RELE(*vpp); + *vpp = NULL; } ZFS_EXIT(zfsvfs); @@ -994,13 +1044,19 @@ zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, * Check accessibility of directory. */ - if (error = zfs_zaccess(zdp, ACE_EXECUTE, cr)) { + if (error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr)) { ZFS_EXIT(zfsvfs); return (error); } - if ((error = zfs_dirlook(zdp, nm, vpp)) == 0) { + if (zfsvfs->z_case & ZFS_UTF8_ONLY && u8_validate(nm, strlen(nm), + NULL, U8_VALIDATE_ENTIRE, &error) < 0) { + ZFS_EXIT(zfsvfs); + return (EILSEQ); + } + error = zfs_dirlook(zdp, nm, vpp, flags, direntflags, realpnp); + if (error == 0) { /* * Convert device special files */ @@ -1032,6 +1088,8 @@ zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, * mode - mode to open file with. * cr - credentials of caller. * flag - large file flag [UNUSED]. + * ct - caller context + * vsecp - ACL to be set * * OUT: vpp - vnode of created or trunc'd entry. * @@ -1042,10 +1100,12 @@ zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, * dvp - ctime|mtime updated if new entry created * vp - ctime|mtime always, atime if new */ + /* ARGSUSED */ static int zfs_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, - int mode, vnode_t **vpp, cred_t *cr, int flag) + int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct, + vsecattr_t *vsecp) { znode_t *zp, *dzp = VTOZ(dvp); zfsvfs_t *zfsvfs = dzp->z_zfsvfs; @@ -1055,11 +1115,36 @@ zfs_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, dmu_tx_t *tx; int error; uint64_t zoid; + zfs_acl_t *aclp = NULL; + zfs_fuid_info_t *fuidp = NULL; + + /* + * If we have an ephemeral id, ACL, or XVATTR then + * make sure file system is at proper version + */ + + if (zfsvfs->z_use_fuids == B_FALSE && + (vsecp || (vap->va_mask & AT_XVATTR) || + IS_EPHEMERAL(crgetuid(cr)) || IS_EPHEMERAL(crgetgid(cr)))) + return (EINVAL); ZFS_ENTER_VERIFY_ZP(zfsvfs, dzp); os = zfsvfs->z_os; zilog = zfsvfs->z_log; + if (zfsvfs->z_case & ZFS_UTF8_ONLY && u8_validate(name, strlen(name), + NULL, U8_VALIDATE_ENTIRE, &error) < 0) { + ZFS_EXIT(zfsvfs); + return (EILSEQ); + } + + if (vap->va_mask & AT_XVATTR) { + if ((error = secpolicy_xvattr((xvattr_t *)vap, + crgetuid(cr), cr, vap->va_type)) != 0) { + ZFS_EXIT(zfsvfs); + return (error); + } + } top: *vpp = NULL; @@ -1076,22 +1161,41 @@ zfs_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, error = 0; } else { /* possible VN_HOLD(zp) */ - if (error = zfs_dirent_lock(&dl, dzp, name, &zp, 0)) { + int zflg = 0; + + if (flag & FIGNORECASE) + zflg |= ZCILOOK; + + error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, + NULL, NULL); + if (error) { if (strcmp(name, "..") == 0) error = EISDIR; ZFS_EXIT(zfsvfs); + if (aclp) + zfs_acl_free(aclp); + return (error); + } + } + if (vsecp && aclp == NULL) { + error = zfs_vsec_2_aclp(zfsvfs, vap->va_type, vsecp, &aclp); + if (error) { + ZFS_EXIT(zfsvfs); + if (dl) + zfs_dirent_unlock(dl); return (error); } } - zoid = zp ? zp->z_id : -1ULL; if (zp == NULL) { + uint64_t txtype; + /* * Create a new file object and update the directory * to reference it. */ - if (error = zfs_zaccess(dzp, ACE_ADD_FILE, cr)) { + if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) { goto out; } @@ -1107,11 +1211,22 @@ zfs_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, tx = dmu_tx_create(os); dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); + if (zfsvfs->z_fuid_obj == 0) { + dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, + SPA_MAXBLOCKSIZE); + dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL); + } else { + dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); + dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, + SPA_MAXBLOCKSIZE); + } dmu_tx_hold_bonus(tx, dzp->z_id); dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name); - if (dzp->z_phys->zp_flags & ZFS_INHERIT_ACE) + if ((dzp->z_phys->zp_flags & ZFS_INHERIT_ACE) || aclp) { dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, SPA_MAXBLOCKSIZE); + } error = dmu_tx_assign(tx, zfsvfs->z_assign); if (error) { zfs_dirent_unlock(dl); @@ -1123,14 +1238,24 @@ zfs_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, } dmu_tx_abort(tx); ZFS_EXIT(zfsvfs); + if (aclp) + zfs_acl_free(aclp); return (error); } - zfs_mknode(dzp, vap, &zoid, tx, cr, 0, &zp, 0); + zfs_mknode(dzp, vap, &zoid, tx, cr, 0, &zp, 0, aclp, &fuidp); ASSERT(zp->z_id == zoid); (void) zfs_link_create(dl, zp, tx, ZNEW); - zfs_log_create(zilog, tx, TX_CREATE, dzp, zp, name); + txtype = zfs_log_create_txtype(Z_FILE, vsecp, vap); + if (flag & FIGNORECASE) + txtype |= TX_CI; + zfs_log_create(zilog, tx, txtype, dzp, zp, name, + vsecp, fuidp, vap); + if (fuidp) + zfs_fuid_info_free(fuidp); dmu_tx_commit(tx); } else { + int aflags = (flag & FAPPEND) ? V_APPEND : 0; + /* * A directory entry already exists for this name. */ @@ -1151,7 +1276,7 @@ zfs_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, /* * Verify requested access to file. */ - if (mode && (error = zfs_zaccess_rwx(zp, mode, cr))) { + if (mode && (error = zfs_zaccess_rwx(zp, mode, aflags, cr))) { goto out; } @@ -1174,7 +1299,7 @@ zfs_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, } if (error == 0) { - vnevent_create(ZTOV(zp)); + vnevent_create(ZTOV(zp), ct); } } } @@ -1202,6 +1327,8 @@ zfs_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, *vpp = svp; } } + if (aclp) + zfs_acl_free(aclp); ZFS_EXIT(zfsvfs); return (error); @@ -1213,6 +1340,8 @@ zfs_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, * IN: dvp - vnode of directory to remove entry from. * name - name of entry to remove. * cr - credentials of caller. + * ct - caller context + * flags - case flags * * RETURN: 0 if success * error code if failure @@ -1221,8 +1350,10 @@ zfs_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl, * dvp - ctime|mtime * vp - ctime (if nlink > 0) */ +/*ARGSUSED*/ static int -zfs_remove(vnode_t *dvp, char *name, cred_t *cr) +zfs_remove(vnode_t *dvp, char *name, cred_t *cr, caller_context_t *ct, + int flags) { znode_t *zp, *dzp = VTOZ(dvp); znode_t *xzp = NULL; @@ -1234,16 +1365,29 @@ zfs_remove(vnode_t *dvp, char *name, cred_t *cr) dmu_tx_t *tx; boolean_t may_delete_now, delete_now = FALSE; boolean_t unlinked; + uint64_t txtype; + pathname_t *realnmp = NULL; + pathname_t realnm; int error; + int zflg = ZEXISTS; ZFS_ENTER_VERIFY_ZP(zfsvfs, dzp); zilog = zfsvfs->z_log; + if (flags & FIGNORECASE) { + zflg |= ZCILOOK; + pn_alloc(&realnm); + realnmp = &realnm; + } + top: /* * Attempt to lock directory; fail if entry doesn't exist. */ - if (error = zfs_dirent_lock(&dl, dzp, name, &zp, ZEXISTS)) { + if (error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, + NULL, realnmp)) { + if (realnmp) + pn_free(realnmp); ZFS_EXIT(zfsvfs); return (error); } @@ -1262,9 +1406,12 @@ zfs_remove(vnode_t *dvp, char *name, cred_t *cr) goto out; } - vnevent_remove(vp, dvp, name); + vnevent_remove(vp, dvp, name, ct); - dnlc_remove(dvp, name); + if (realnmp) + dnlc_remove(dvp, realnmp->pn_path); + else + dnlc_remove(dvp, name); mutex_enter(&vp->v_lock); may_delete_now = vp->v_count == 1 && !vn_has_cached_data(vp); @@ -1305,6 +1452,8 @@ zfs_remove(vnode_t *dvp, char *name, cred_t *cr) dmu_tx_abort(tx); goto top; } + if (realnmp) + pn_free(realnmp); dmu_tx_abort(tx); ZFS_EXIT(zfsvfs); return (error); @@ -1313,7 +1462,7 @@ zfs_remove(vnode_t *dvp, char *name, cred_t *cr) /* * Remove the directory entry. */ - error = zfs_link_destroy(dl, zp, tx, 0, &unlinked); + error = zfs_link_destroy(dl, zp, tx, zflg, &unlinked); if (error) { dmu_tx_commit(tx); @@ -1354,10 +1503,16 @@ zfs_remove(vnode_t *dvp, char *name, cred_t *cr) zfs_unlinked_add(zp, tx); } - zfs_log_remove(zilog, tx, TX_REMOVE, dzp, name); + txtype = TX_REMOVE; + if (flags & FIGNORECASE) + txtype |= TX_CI; + zfs_log_remove(zilog, tx, txtype, dzp, name); dmu_tx_commit(tx); out: + if (realnmp) + pn_free(realnmp); + zfs_dirent_unlock(dl); if (!delete_now) { @@ -1379,6 +1534,8 @@ zfs_remove(vnode_t *dvp, char *name, cred_t *cr) * dirname - name of new directory. * vap - attributes of new directory. * cr - credentials of caller. + * ct - caller context + * vsecp - ACL to be set * * OUT: vpp - vnode of created directory. * @@ -1389,19 +1546,35 @@ zfs_remove(vnode_t *dvp, char *name, cred_t *cr) * dvp - ctime|mtime updated * vp - ctime|mtime|atime updated */ +/*ARGSUSED*/ static int -zfs_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr) +zfs_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr, + caller_context_t *ct, int flags, vsecattr_t *vsecp) { znode_t *zp, *dzp = VTOZ(dvp); zfsvfs_t *zfsvfs = dzp->z_zfsvfs; zilog_t *zilog; zfs_dirlock_t *dl; uint64_t zoid = 0; + uint64_t txtype; dmu_tx_t *tx; int error; + zfs_acl_t *aclp = NULL; + zfs_fuid_info_t *fuidp = NULL; + int zf = ZNEW; ASSERT(vap->va_type == VDIR); + /* + * If we have an ephemeral id, ACL, or XVATTR then + * make sure file system is at proper version + */ + + if (zfsvfs->z_use_fuids == B_FALSE && + (vsecp || (vap->va_mask & AT_XVATTR) || IS_EPHEMERAL(crgetuid(cr))|| + IS_EPHEMERAL(crgetgid(cr)))) + return (EINVAL); + ZFS_ENTER_VERIFY_ZP(zfsvfs, dzp); zilog = zfsvfs->z_log; @@ -1409,30 +1582,65 @@ zfs_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr) ZFS_EXIT(zfsvfs); return (EINVAL); } -top: - *vpp = NULL; + + if (zfsvfs->z_case & ZFS_UTF8_ONLY && u8_validate(dirname, + strlen(dirname), NULL, U8_VALIDATE_ENTIRE, &error) < 0) { + ZFS_EXIT(zfsvfs); + return (EILSEQ); + } + if (flags & FIGNORECASE) + zf |= ZCILOOK; + + if (vap->va_mask & AT_XVATTR) + if ((error = secpolicy_xvattr((xvattr_t *)vap, + crgetuid(cr), cr, vap->va_type)) != 0) { + ZFS_EXIT(zfsvfs); + return (error); + } /* * First make sure the new directory doesn't exist. */ - if (error = zfs_dirent_lock(&dl, dzp, dirname, &zp, ZNEW)) { +top: + *vpp = NULL; + + if (error = zfs_dirent_lock(&dl, dzp, dirname, &zp, zf, + NULL, NULL)) { ZFS_EXIT(zfsvfs); return (error); } - if (error = zfs_zaccess(dzp, ACE_ADD_SUBDIRECTORY, cr)) { + if (error = zfs_zaccess(dzp, ACE_ADD_SUBDIRECTORY, 0, B_FALSE, cr)) { zfs_dirent_unlock(dl); ZFS_EXIT(zfsvfs); return (error); } + if (vsecp && aclp == NULL) { + error = zfs_vsec_2_aclp(zfsvfs, vap->va_type, vsecp, &aclp); + if (error) { + zfs_dirent_unlock(dl); + ZFS_EXIT(zfsvfs); + return (error); + } + } /* * Add a new entry to the directory. */ tx = dmu_tx_create(zfsvfs->z_os); dmu_tx_hold_zap(tx, dzp->z_id, TRUE, dirname); dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); - if (dzp->z_phys->zp_flags & ZFS_INHERIT_ACE) + if (zfsvfs->z_fuid_obj == 0) { + dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, + SPA_MAXBLOCKSIZE); + dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL); + } else { + dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); + dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, + SPA_MAXBLOCKSIZE); + } + if ((dzp->z_phys->zp_flags & ZFS_INHERIT_ACE) || aclp) dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, SPA_MAXBLOCKSIZE); error = dmu_tx_assign(tx, zfsvfs->z_assign); @@ -1445,13 +1653,18 @@ zfs_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr) } dmu_tx_abort(tx); ZFS_EXIT(zfsvfs); + if (aclp) + zfs_acl_free(aclp); return (error); } /* * Create new node. */ - zfs_mknode(dzp, vap, &zoid, tx, cr, 0, &zp, 0); + zfs_mknode(dzp, vap, &zoid, tx, cr, 0, &zp, 0, aclp, &fuidp); + + if (aclp) + zfs_acl_free(aclp); /* * Now put new name in parent dir. @@ -1460,7 +1673,13 @@ zfs_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr) *vpp = ZTOV(zp); - zfs_log_create(zilog, tx, TX_MKDIR, dzp, zp, dirname); + txtype = zfs_log_create_txtype(Z_DIR, vsecp, vap); + if (flags & FIGNORECASE) + txtype |= TX_CI; + zfs_log_create(zilog, tx, txtype, dzp, zp, dirname, vsecp, fuidp, vap); + + if (fuidp) + zfs_fuid_info_free(fuidp); dmu_tx_commit(tx); zfs_dirent_unlock(dl); @@ -1478,6 +1697,8 @@ zfs_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr) * name - name of directory to be removed. * cwd - vnode of current working directory. * cr - credentials of caller. + * ct - caller context + * flags - case flags * * RETURN: 0 if success * error code if failure @@ -1485,8 +1706,10 @@ zfs_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr) * Timestamps: * dvp - ctime|mtime updated */ +/*ARGSUSED*/ static int -zfs_rmdir(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr) +zfs_rmdir(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr, + caller_context_t *ct, int flags) { znode_t *dzp = VTOZ(dvp); znode_t *zp; @@ -1496,17 +1719,21 @@ zfs_rmdir(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr) zfs_dirlock_t *dl; dmu_tx_t *tx; int error; + int zflg = ZEXISTS; ZFS_ENTER_VERIFY_ZP(zfsvfs, dzp); zilog = zfsvfs->z_log; + if (flags & FIGNORECASE) + zflg |= ZCILOOK; top: zp = NULL; /* * Attempt to lock directory; fail if entry doesn't exist. */ - if (error = zfs_dirent_lock(&dl, dzp, name, &zp, ZEXISTS)) { + if (error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, + NULL, NULL)) { ZFS_EXIT(zfsvfs); return (error); } @@ -1527,7 +1754,7 @@ zfs_rmdir(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr) goto out; } - vnevent_rmdir(vp, dvp, name); + vnevent_rmdir(vp, dvp, name, ct); /* * Grab a lock on the directory to make sure that noone is @@ -1561,10 +1788,14 @@ zfs_rmdir(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr) return (error); } - error = zfs_link_destroy(dl, zp, tx, 0, NULL); + error = zfs_link_destroy(dl, zp, tx, zflg, NULL); - if (error == 0) - zfs_log_remove(zilog, tx, TX_RMDIR, dzp, name); + if (error == 0) { + uint64_t txtype = TX_RMDIR; + if (flags & FIGNORECASE) + txtype |= TX_CI; + zfs_log_remove(zilog, tx, txtype, dzp, name); + } dmu_tx_commit(tx); @@ -1588,6 +1819,8 @@ zfs_rmdir(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr) * uio - structure supplying read location, range info, * and return buffer. * cr - credentials of caller. + * ct - caller context + * flags - case flags * * OUT: uio - updated offset and range, buffer filled. * eofp - set to true if end-of-file detected. @@ -1605,10 +1838,12 @@ zfs_rmdir(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr) */ /* ARGSUSED */ static int -zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp) +zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) { znode_t *zp = VTOZ(vp); iovec_t *iovp; + edirent_t *eodp; dirent64_t *odp; zfsvfs_t *zfsvfs = zp->z_zfsvfs; objset_t *os; @@ -1681,6 +1916,7 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp) bufsize = bytes_wanted; odp = (struct dirent64 *)iovp->iov_base; } + eodp = (struct edirent *)odp; /* * Transform to file-system independent format @@ -1696,12 +1932,15 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp) */ if (offset == 0) { (void) strcpy(zap.za_name, "."); + zap.za_normalization_conflict = 0; objnum = zp->z_id; } else if (offset == 1) { (void) strcpy(zap.za_name, ".."); + zap.za_normalization_conflict = 0; objnum = zp->z_phys->zp_parent; } else if (offset == 2 && zfs_show_ctldir(zp)) { (void) strcpy(zap.za_name, ZFS_CTLDIR_NAME); + zap.za_normalization_conflict = 0; objnum = ZFSCTL_INO_ROOT; } else { /* @@ -1730,7 +1969,11 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp) * uint8_t type = ZFS_DIRENT_TYPE(zap.za_first_integer); */ } - reclen = DIRENT64_RECLEN(strlen(zap.za_name)); + + if (flags & V_RDDIR_ENTFLAGS) + reclen = EDIRENT_RECLEN(strlen(zap.za_name)); + else + reclen = DIRENT64_RECLEN(strlen(zap.za_name)); /* * Will this entry fit in the buffer? @@ -1745,17 +1988,32 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp) } break; } - /* - * Add this entry: - */ - odp->d_ino = objnum; - odp->d_reclen = reclen; - /* NOTE: d_off is the offset for the *next* entry */ - next = &(odp->d_off); - (void) strncpy(odp->d_name, zap.za_name, - DIRENT64_NAMELEN(reclen)); + if (flags & V_RDDIR_ENTFLAGS) { + /* + * Add extended flag entry: + */ + eodp->ed_ino = objnum; + eodp->ed_reclen = reclen; + /* NOTE: ed_off is the offset for the *next* entry */ + next = &(eodp->ed_off); + eodp->ed_eflags = zap.za_normalization_conflict ? + ED_CASE_CONFLICT : 0; + (void) strncpy(eodp->ed_name, zap.za_name, + EDIRENT_NAMELEN(reclen)); + eodp = (edirent_t *)((intptr_t)eodp + reclen); + } else { + /* + * Add normal entry: + */ + odp->d_ino = objnum; + odp->d_reclen = reclen; + /* NOTE: d_off is the offset for the *next* entry */ + next = &(odp->d_off); + (void) strncpy(odp->d_name, zap.za_name, + DIRENT64_NAMELEN(reclen)); + odp = (dirent64_t *)((intptr_t)odp + reclen); + } outcount += reclen; - odp = (dirent64_t *)((intptr_t)odp + reclen); ASSERT(outcount <= bufsize); @@ -1805,7 +2063,7 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp) ulong_t zfs_fsync_sync_cnt = 4; static int -zfs_fsync(vnode_t *vp, int syncflag, cred_t *cr) +zfs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; @@ -1818,7 +2076,7 @@ zfs_fsync(vnode_t *vp, int syncflag, cred_t *cr) */ if (vn_has_cached_data(vp) && !(syncflag & FNODSYNC) && (vp->v_type == VREG) && !(IS_SWAPVP(vp))) - (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, B_ASYNC, cr); + (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, B_ASYNC, cr, ct); (void) tsd_set(zfs_fsyncer_key, (void *)zfs_fsync_sync_cnt); @@ -1828,14 +2086,17 @@ zfs_fsync(vnode_t *vp, int syncflag, cred_t *cr) return (0); } + /* * Get the requested file attributes and place them in the provided * vattr structure. * * IN: vp - vnode of file. * vap - va_mask identifies requested attributes. - * flags - [UNUSED] + * If AT_XVATTR set, then optional attrs are requested + * flags - ATTR_NOACLCHECK (CIFS server context) * cr - credentials of caller. + * ct - caller context * * OUT: vap - attribute values. * @@ -1843,27 +2104,46 @@ zfs_fsync(vnode_t *vp, int syncflag, cred_t *cr) */ /* ARGSUSED */ static int -zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) +zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; znode_phys_t *pzp; - int error; + int error = 0; uint64_t links; + xvattr_t *xvap = (xvattr_t *)vap; /* vap may be an xvattr_t * */ + xoptattr_t *xoap = NULL; + boolean_t skipaclchk = (flags & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE; ZFS_ENTER_VERIFY_ZP(zfsvfs, zp); pzp = zp->z_phys; + mutex_enter(&zp->z_lock); + + /* + * If ACL is trivial don't bother looking for ACE_READ_ATTRIBUTES. + * Also, if we are the owner don't bother, since owner should + * always be allowed to read basic attributes of file. + */ + if (!(pzp->zp_flags & ZFS_ACL_TRIVIAL) && + (pzp->zp_uid != crgetuid(cr))) { + if (error = zfs_zaccess(zp, ACE_READ_ATTRIBUTES, 0, + skipaclchk, cr)) { + mutex_exit(&zp->z_lock); + ZFS_EXIT(zfsvfs); + return (error); + } + } + /* * Return all attributes. It's cheaper to provide the answer * than to determine whether we were asked the question. */ - mutex_enter(&zp->z_lock); vap->va_type = vp->v_type; vap->va_mode = pzp->zp_mode & MODEMASK; - vap->va_uid = zp->z_phys->zp_uid; - vap->va_gid = zp->z_phys->zp_gid; + zfs_fuid_map_ids(zp, &vap->va_uid, &vap->va_gid); vap->va_fsid = zp->z_zfsvfs->z_vfs->vfs_dev; vap->va_nodeid = zp->z_id; if ((vp->v_flag & VROOT) && zfs_show_ctldir(zp)) @@ -1875,24 +2155,113 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) vap->va_rdev = vp->v_rdev; vap->va_seq = zp->z_seq; - ZFS_TIME_DECODE(&vap->va_atime, pzp->zp_atime); - ZFS_TIME_DECODE(&vap->va_mtime, pzp->zp_mtime); - ZFS_TIME_DECODE(&vap->va_ctime, pzp->zp_ctime); - /* - * If ACL is trivial don't bother looking for ACE_READ_ATTRIBUTES. - * Also, if we are the owner don't bother, since owner should - * always be allowed to read basic attributes of file. + * Add in any requested optional attributes and the create time. + * Also set the corresponding bits in the returned attribute bitmap. */ - if (!(zp->z_phys->zp_flags & ZFS_ACL_TRIVIAL) && - (zp->z_phys->zp_uid != crgetuid(cr))) { - if (error = zfs_zaccess(zp, ACE_READ_ATTRIBUTES, cr)) { - mutex_exit(&zp->z_lock); - ZFS_EXIT(zfsvfs); - return (error); + if ((xoap = xva_getxoptattr(xvap)) != NULL && zfsvfs->z_use_fuids) { + if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) { + xoap->xoa_archive = + ((pzp->zp_flags & ZFS_ARCHIVE) != 0); + XVA_SET_RTN(xvap, XAT_ARCHIVE); + } + + if (XVA_ISSET_REQ(xvap, XAT_READONLY)) { + xoap->xoa_readonly = + ((pzp->zp_flags & ZFS_READONLY) != 0); + XVA_SET_RTN(xvap, XAT_READONLY); + } + + if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) { + xoap->xoa_system = + ((pzp->zp_flags & ZFS_SYSTEM) != 0); + XVA_SET_RTN(xvap, XAT_SYSTEM); + } + + if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) { + xoap->xoa_hidden = + ((pzp->zp_flags & ZFS_HIDDEN) != 0); + XVA_SET_RTN(xvap, XAT_HIDDEN); + } + + if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) { + xoap->xoa_nounlink = + ((pzp->zp_flags & ZFS_NOUNLINK) != 0); + XVA_SET_RTN(xvap, XAT_NOUNLINK); + } + + if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE)) { + xoap->xoa_immutable = + ((pzp->zp_flags & ZFS_IMMUTABLE) != 0); + XVA_SET_RTN(xvap, XAT_IMMUTABLE); + } + + if (XVA_ISSET_REQ(xvap, XAT_APPENDONLY)) { + xoap->xoa_appendonly = + ((pzp->zp_flags & ZFS_APPENDONLY) != 0); + XVA_SET_RTN(xvap, XAT_APPENDONLY); + } + + if (XVA_ISSET_REQ(xvap, XAT_NODUMP)) { + xoap->xoa_nodump = + ((pzp->zp_flags & ZFS_NODUMP) != 0); + XVA_SET_RTN(xvap, XAT_NODUMP); + } + + if (XVA_ISSET_REQ(xvap, XAT_OPAQUE)) { + xoap->xoa_opaque = + ((pzp->zp_flags & ZFS_OPAQUE) != 0); + XVA_SET_RTN(xvap, XAT_OPAQUE); + } + + if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) { + xoap->xoa_av_quarantined = + ((pzp->zp_flags & ZFS_AV_QUARANTINED) != 0); + XVA_SET_RTN(xvap, XAT_AV_QUARANTINED); + } + + if (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED)) { + xoap->xoa_av_modified = + ((pzp->zp_flags & ZFS_AV_MODIFIED) != 0); + XVA_SET_RTN(xvap, XAT_AV_MODIFIED); + } + + if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP) && + vp->v_type == VREG && + (pzp->zp_flags & ZFS_BONUS_SCANSTAMP)) { + size_t len; + dmu_object_info_t doi; + + /* + * Only VREG files have anti-virus scanstamps, so we + * won't conflict with symlinks in the bonus buffer. + */ + dmu_object_info_from_db(zp->z_dbuf, &doi); + len = sizeof (xoap->xoa_av_scanstamp) + + sizeof (znode_phys_t); + if (len <= doi.doi_bonus_size) { + /* + * pzp points to the start of the + * znode_phys_t. pzp + 1 points to the + * first byte after the znode_phys_t. + */ + (void) memcpy(xoap->xoa_av_scanstamp, + pzp + 1, + sizeof (xoap->xoa_av_scanstamp)); + XVA_SET_RTN(xvap, XAT_AV_SCANSTAMP); + } + } + + if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) { + ZFS_TIME_DECODE(&xoap->xoa_createtime, pzp->zp_crtime); + XVA_SET_RTN(xvap, XAT_CREATETIME); } } + ZFS_TIME_DECODE(&vap->va_atime, pzp->zp_atime); + ZFS_TIME_DECODE(&vap->va_mtime, pzp->zp_mtime); + ZFS_TIME_DECODE(&vap->va_ctime, pzp->zp_ctime); + mutex_exit(&zp->z_lock); dmu_object_size_from_db(zp->z_dbuf, &vap->va_blksize, &vap->va_nblocks); @@ -1914,8 +2283,11 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) * * IN: vp - vnode of file to be modified. * vap - new attribute values. + * If AT_XVATTR set, then optional attrs are being set * flags - ATTR_UTIME set if non-default time values provided. + * - ATTR_NOACLCHECK (CIFS context only). * cr - credentials of caller. + * ct - caller context * * RETURN: 0 if success * error code if failure @@ -1941,6 +2313,10 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, znode_t *attrzp; int need_policy = FALSE; int err; + zfs_fuid_info_t *fuidp = NULL; + xvattr_t *xvap = (xvattr_t *)vap; /* vap may be an xvattr_t * */ + xoptattr_t *xoap; + boolean_t skipaclchk = (flags & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE; if (mask == 0) return (0); @@ -1948,15 +2324,44 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, if (mask & AT_NOSET) return (EINVAL); + ZFS_ENTER_VERIFY_ZP(zfsvfs, zp); + + pzp = zp->z_phys; + zilog = zfsvfs->z_log; + + /* + * Make sure that if we have ephemeral uid/gid or xvattr specified + * that file system is at proper version level + */ + + if (zfsvfs->z_use_fuids == B_FALSE && + (((mask & AT_UID) && IS_EPHEMERAL(vap->va_uid)) || + ((mask & AT_GID) && IS_EPHEMERAL(vap->va_gid)) || + (mask & AT_XVATTR))) + return (EINVAL); + if (mask & AT_SIZE && vp->v_type == VDIR) return (EISDIR); if (mask & AT_SIZE && vp->v_type != VREG && vp->v_type != VFIFO) return (EINVAL); - ZFS_ENTER_VERIFY_ZP(zfsvfs, zp); - pzp = zp->z_phys; - zilog = zfsvfs->z_log; + /* + * If this is an xvattr_t, then get a pointer to the structure of + * optional attributes. If this is NULL, then we have a vattr_t. + */ + xoap = xva_getxoptattr(xvap); + + /* + * Immutable files can only alter immutable bit and atime + */ + if ((pzp->zp_flags & ZFS_IMMUTABLE) && + ((mask & (AT_SIZE|AT_UID|AT_GID|AT_MTIME|AT_MODE)) || + ((mask & AT_XVATTR) && XVA_ISSET_REQ(xvap, XAT_CREATETIME)))) + return (EPERM); + + if ((mask & AT_SIZE) && (pzp->zp_flags & ZFS_READONLY)) + return (EPERM); top: attrzp = NULL; @@ -1971,7 +2376,7 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, */ if (mask & AT_SIZE) { - err = zfs_zaccess(zp, ACE_WRITE_DATA, cr); + err = zfs_zaccess(zp, ACE_WRITE_DATA, 0, skipaclchk, cr); if (err) { ZFS_EXIT(zfsvfs); return (err); @@ -1992,8 +2397,14 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, } } - if (mask & (AT_ATIME|AT_MTIME)) - need_policy = zfs_zaccess_v4_perm(zp, ACE_WRITE_ATTRIBUTES, cr); + if (mask & (AT_ATIME|AT_MTIME) || + ((mask & AT_XVATTR) && (XVA_ISSET_REQ(xvap, XAT_HIDDEN) || + XVA_ISSET_REQ(xvap, XAT_READONLY) || + XVA_ISSET_REQ(xvap, XAT_ARCHIVE) || + XVA_ISSET_REQ(xvap, XAT_CREATETIME) || + XVA_ISSET_REQ(xvap, XAT_SYSTEM)))) + need_policy = zfs_zaccess(zp, ACE_WRITE_ATTRIBUTES, 0, + skipaclchk, cr); if (mask & (AT_UID|AT_GID)) { int idmask = (mask & (AT_UID|AT_GID)); @@ -2013,7 +2424,8 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, */ take_owner = (mask & AT_UID) && (vap->va_uid == crgetuid(cr)); - take_group = (mask & AT_GID) && groupmember(vap->va_gid, cr); + take_group = (mask & AT_GID) && + zfs_groupmember(zfsvfs, vap->va_gid, cr); /* * If both AT_UID and AT_GID are set then take_owner and @@ -2027,7 +2439,8 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, if (((idmask == (AT_UID|AT_GID)) && take_owner && take_group) || ((idmask == AT_UID) && take_owner) || ((idmask == AT_GID) && take_group)) { - if (zfs_zaccess_v4_perm(zp, ACE_WRITE_OWNER, cr) == 0) { + if (zfs_zaccess(zp, ACE_WRITE_OWNER, 0, + skipaclchk, cr) == 0) { /* * Remove setuid/setgid for non-privileged users */ @@ -2043,12 +2456,37 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, mutex_enter(&zp->z_lock); oldva.va_mode = pzp->zp_mode; - oldva.va_uid = zp->z_phys->zp_uid; - oldva.va_gid = zp->z_phys->zp_gid; + zfs_fuid_map_ids(zp, &oldva.va_uid, &oldva.va_gid); + if (mask & AT_XVATTR) { + if ((need_policy == FALSE) && + (XVA_ISSET_REQ(xvap, XAT_APPENDONLY) && + xoap->xoa_appendonly != + ((pzp->zp_flags & ZFS_APPENDONLY) != 0)) || + (XVA_ISSET_REQ(xvap, XAT_NOUNLINK) && + xoap->xoa_nounlink != + ((pzp->zp_flags & ZFS_NOUNLINK) != 0)) || + (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE) && + xoap->xoa_immutable != + ((pzp->zp_flags & ZFS_IMMUTABLE) != 0)) || + (XVA_ISSET_REQ(xvap, XAT_NODUMP) && + xoap->xoa_nodump != + ((pzp->zp_flags & ZFS_NODUMP) != 0)) || + (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED) && + xoap->xoa_av_modified != + ((pzp->zp_flags & ZFS_AV_MODIFIED) != 0)) || + (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED) && + xoap->xoa_av_quarantined != + ((pzp->zp_flags & ZFS_AV_QUARANTINED) != 0)) || + (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) || + (XVA_ISSET_REQ(xvap, XAT_OPAQUE))) { + need_policy = TRUE; + } + } + mutex_exit(&zp->z_lock); if (mask & AT_MODE) { - if (zfs_zaccess_v4_perm(zp, ACE_WRITE_ACL, cr) == 0) { + if (zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr) == 0) { err = secpolicy_setid_setsticky_clear(vp, vap, &oldva, cr); if (err) { @@ -2073,10 +2511,9 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, if (trim_mask) { saved_mask = vap->va_mask; vap->va_mask &= ~trim_mask; - } err = secpolicy_vnode_setattr(cr, vp, vap, &oldva, flags, - (int (*)(void *, int, cred_t *))zfs_zaccess_rwx, zp); + (int (*)(void *, int, cred_t *))zfs_zaccess_unix, zp); if (err) { ZFS_EXIT(zfsvfs); return (err); @@ -2094,22 +2531,45 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, tx = dmu_tx_create(zfsvfs->z_os); dmu_tx_hold_bonus(tx, zp->z_id); + if (zfsvfs->z_fuid_obj == 0) { + dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, + SPA_MAXBLOCKSIZE); + dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL); + } else { + dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); + dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, + SPA_MAXBLOCKSIZE); + } if (mask & AT_MODE) { uint64_t pmode = pzp->zp_mode; new_mode = (pmode & S_IFMT) | (vap->va_mode & ~S_IFMT); - if (zp->z_phys->zp_acl.z_acl_extern_obj) - dmu_tx_hold_write(tx, - pzp->zp_acl.z_acl_extern_obj, 0, SPA_MAXBLOCKSIZE); - else + if (pzp->zp_acl.z_acl_extern_obj) { + /* Are we upgrading ACL from old V0 format to new V1 */ + if (zfsvfs->z_version <= ZPL_VERSION_FUID && + pzp->zp_acl.z_acl_version == + ZFS_ACL_VERSION_INITIAL) { + dmu_tx_hold_free(tx, + pzp->zp_acl.z_acl_extern_obj, 0, + DMU_OBJECT_END); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, + 0, sizeof (zfs_object_ace_t) * 2048 + 6); + } else { + dmu_tx_hold_write(tx, + pzp->zp_acl.z_acl_extern_obj, 0, + SPA_MAXBLOCKSIZE); + } + } else { dmu_tx_hold_write(tx, DMU_NEW_OBJECT, - 0, ZFS_ACL_SIZE(MAX_ACL_SIZE)); + 0, sizeof (zfs_object_ace_t) * 2048 + 6); + } } - if ((mask & (AT_UID | AT_GID)) && zp->z_phys->zp_xattr != 0) { - err = zfs_zget(zp->z_zfsvfs, zp->z_phys->zp_xattr, &attrzp); + if ((mask & (AT_UID | AT_GID)) && pzp->zp_xattr != 0) { + err = zfs_zget(zp->z_zfsvfs, pzp->zp_xattr, &attrzp); if (err) { dmu_tx_abort(tx); ZFS_EXIT(zfsvfs); @@ -2153,16 +2613,20 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, mutex_enter(&attrzp->z_lock); if (mask & AT_UID) { - zp->z_phys->zp_uid = (uint64_t)vap->va_uid; + pzp->zp_uid = zfs_fuid_create(zfsvfs, + vap->va_uid, ZFS_OWNER, tx, &fuidp); if (attrzp) { - attrzp->z_phys->zp_uid = (uint64_t)vap->va_uid; + attrzp->z_phys->zp_uid = zfs_fuid_create(zfsvfs, + vap->va_uid, ZFS_OWNER, tx, &fuidp); } } if (mask & AT_GID) { - zp->z_phys->zp_gid = (uint64_t)vap->va_gid; + pzp->zp_gid = zfs_fuid_create(zfsvfs, vap->va_gid, + ZFS_GROUP, tx, &fuidp); if (attrzp) - attrzp->z_phys->zp_gid = (uint64_t)vap->va_gid; + attrzp->z_phys->zp_gid = zfs_fuid_create(zfsvfs, + vap->va_gid, ZFS_GROUP, tx, &fuidp); } if (attrzp) @@ -2178,10 +2642,33 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, zfs_time_stamper_locked(zp, CONTENT_MODIFIED, tx); else if (mask != 0) zfs_time_stamper_locked(zp, STATE_CHANGED, tx); + /* + * Do this after setting timestamps to prevent timestamp + * update from toggling bit + */ + + if (xoap && (mask & AT_XVATTR)) { + if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) { + size_t len; + dmu_object_info_t doi; + + ASSERT(vp->v_type == VREG); + + /* Grow the bonus buffer if necessary. */ + dmu_object_info_from_db(zp->z_dbuf, &doi); + len = sizeof (xoap->xoa_av_scanstamp) + + sizeof (znode_phys_t); + if (len > doi.doi_bonus_size) + VERIFY(dmu_set_bonus(zp->z_dbuf, len, tx) == 0); + } + zfs_xvattr_set(zp, xvap); + } if (mask != 0) - zfs_log_setattr(zilog, tx, TX_SETATTR, zp, vap, mask); + zfs_log_setattr(zilog, tx, TX_SETATTR, zp, vap, mask, fuidp); + if (fuidp) + zfs_fuid_info_free(fuidp); mutex_exit(&zp->z_lock); if (attrzp) @@ -2298,6 +2785,8 @@ zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp) * tdvp - Target directory to contain the "new entry". * tnm - New entry name. * cr - credentials of caller. + * ct - caller context + * flags - case flags * * RETURN: 0 if success * error code if failure @@ -2305,8 +2794,10 @@ zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp) * Timestamps: * sdvp,tdvp - ctime|mtime updated */ +/*ARGSUSED*/ static int -zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) +zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr, + caller_context_t *ct, int flags) { znode_t *tdzp, *szp, *tzp; znode_t *sdzp = VTOZ(sdvp); @@ -2316,7 +2807,9 @@ zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) zfs_dirlock_t *sdl, *tdl; dmu_tx_t *tx; zfs_zlock_t *zl; - int cmp, serr, terr, error; + int cmp, serr, terr; + int error = 0; + int zflg = 0; ZFS_ENTER_VERIFY_ZP(zfsvfs, sdzp); zilog = zfsvfs->z_log; @@ -2324,7 +2817,7 @@ zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) /* * Make sure we have the real vp for the target directory. */ - if (VOP_REALVP(tdvp, &realvp) == 0) + if (VOP_REALVP(tdvp, &realvp, ct) == 0) tdvp = realvp; if (tdvp->v_vfsp != sdvp->v_vfsp) { @@ -2337,6 +2830,15 @@ zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) ZFS_EXIT(zfsvfs); return (EIO); } + if (zfsvfs->z_case & ZFS_UTF8_ONLY && u8_validate(tnm, + strlen(tnm), NULL, U8_VALIDATE_ENTIRE, &error) < 0) { + ZFS_EXIT(zfsvfs); + return (EILSEQ); + } + + if (flags & FIGNORECASE) + zflg |= ZCILOOK; + top: szp = NULL; tzp = NULL; @@ -2364,7 +2866,14 @@ zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) } else if (sdzp->z_id > tdzp->z_id) { cmp = 1; } else { - cmp = strcmp(snm, tnm); + /* + * First compare the two name arguments without + * considering any case folding. + */ + int nofold = (zfsvfs->z_norm & ~U8_TEXTPREP_TOUPPER); + + cmp = u8_strcmp(snm, tnm, 0, nofold, U8_UNICODE_LATEST, &error); + ASSERT(error == 0 || !(zfsvfs->z_case & ZFS_UTF8_ONLY)); if (cmp == 0) { /* * POSIX: "If the old argument and the new argument @@ -2375,13 +2884,48 @@ zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) ZFS_EXIT(zfsvfs); return (0); } + /* + * If the file system is case-folding, then we may + * have some more checking to do. A case-folding file + * system is either supporting mixed case sensitivity + * access or is completely case-insensitive. Note + * that the file system is always case preserving. + * + * In mixed sensitivity mode case sensitive behavior + * is the default. FIGNORECASE must be used to + * explicitly request case insensitive behavior. + * + * If the source and target names provided differ only + * by case (e.g., a request to rename 'tim' to 'Tim'), + * we will treat this as a special case in the + * case-insensitive mode: as long as the source name + * is an exact match, we will allow this to proceed as + * a name-change request. + */ + if ((zfsvfs->z_case & ZFS_CI_ONLY || + (zfsvfs->z_case & ZFS_CI_MIXD && flags & FIGNORECASE)) && + u8_strcmp(snm, tnm, 0, zfsvfs->z_norm, U8_UNICODE_LATEST, + &error) == 0) { + /* + * case preserving rename request, require exact + * name matches + */ + zflg |= ZCIEXACT; + zflg &= ~ZCILOOK; + } } + if (cmp < 0) { - serr = zfs_dirent_lock(&sdl, sdzp, snm, &szp, ZEXISTS); - terr = zfs_dirent_lock(&tdl, tdzp, tnm, &tzp, 0); + serr = zfs_dirent_lock(&sdl, sdzp, snm, &szp, + ZEXISTS | zflg, NULL, NULL); + terr = zfs_dirent_lock(&tdl, + tdzp, tnm, &tzp, ZRENAMING | zflg, NULL, NULL); } else { - terr = zfs_dirent_lock(&tdl, tdzp, tnm, &tzp, 0); - serr = zfs_dirent_lock(&sdl, sdzp, snm, &szp, ZEXISTS); + terr = zfs_dirent_lock(&tdl, + tdzp, tnm, &tzp, zflg, NULL, NULL); + serr = zfs_dirent_lock(&sdl, + sdzp, snm, &szp, ZEXISTS | ZRENAMING | zflg, + NULL, NULL); } if (serr) { @@ -2455,16 +2999,16 @@ zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) } } - vnevent_rename_src(ZTOV(szp), sdvp, snm); + vnevent_rename_src(ZTOV(szp), sdvp, snm, ct); if (tzp) - vnevent_rename_dest(ZTOV(tzp), tdvp, tnm); + vnevent_rename_dest(ZTOV(tzp), tdvp, tnm, ct); /* * notify the target directory if it is not the same * as source directory. */ if (tdvp != sdvp) { - vnevent_rename_dest_dir(tdvp); + vnevent_rename_dest_dir(tdvp, ct); } tx = dmu_tx_create(zfsvfs->z_os); @@ -2497,15 +3041,19 @@ zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) } if (tzp) /* Attempt to remove the existing target */ - error = zfs_link_destroy(tdl, tzp, tx, 0, NULL); + error = zfs_link_destroy(tdl, tzp, tx, zflg, NULL); if (error == 0) { error = zfs_link_create(tdl, szp, tx, ZRENAMING); if (error == 0) { + szp->z_phys->zp_flags |= ZFS_AV_MODIFIED; + error = zfs_link_destroy(sdl, szp, tx, ZRENAMING, NULL); ASSERT(error == 0); - zfs_log_rename(zilog, tx, TX_RENAME, sdzp, - sdl->dl_name, tdzp, tdl->dl_name, szp); + + zfs_log_rename(zilog, tx, + TX_RENAME | (flags & FIGNORECASE ? TX_CI : 0), + sdzp, sdl->dl_name, tdzp, tdl->dl_name, szp); } } @@ -2533,6 +3081,8 @@ zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) * vap - Attributes of new entry. * target - Target path of new symlink. * cr - credentials of caller. + * ct - caller context + * flags - case flags * * RETURN: 0 if success * error code if failure @@ -2540,8 +3090,10 @@ zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr) * Timestamps: * dvp - ctime|mtime updated */ +/*ARGSUSED*/ static int -zfs_symlink(vnode_t *dvp, char *name, vattr_t *vap, char *link, cred_t *cr) +zfs_symlink(vnode_t *dvp, char *name, vattr_t *vap, char *link, cred_t *cr, + caller_context_t *ct, int flags) { znode_t *zp, *dzp = VTOZ(dvp); zfs_dirlock_t *dl; @@ -2551,13 +3103,23 @@ zfs_symlink(vnode_t *dvp, char *name, vattr_t *vap, char *link, cred_t *cr) uint64_t zoid; int len = strlen(link); int error; + int zflg = ZNEW; + zfs_fuid_info_t *fuidp = NULL; ASSERT(vap->va_type == VLNK); ZFS_ENTER_VERIFY_ZP(zfsvfs, dzp); zilog = zfsvfs->z_log; + + if (zfsvfs->z_case & ZFS_UTF8_ONLY && u8_validate(name, strlen(name), + NULL, U8_VALIDATE_ENTIRE, &error) < 0) { + ZFS_EXIT(zfsvfs); + return (EILSEQ); + } + if (flags & FIGNORECASE) + zflg |= ZCILOOK; top: - if (error = zfs_zaccess(dzp, ACE_ADD_FILE, cr)) { + if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) { ZFS_EXIT(zfsvfs); return (error); } @@ -2570,7 +3132,8 @@ zfs_symlink(vnode_t *dvp, char *name, vattr_t *vap, char *link, cred_t *cr) /* * Attempt to lock directory; fail if entry already exists. */ - if (error = zfs_dirent_lock(&dl, dzp, name, &zp, ZNEW)) { + error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, NULL, NULL); + if (error) { ZFS_EXIT(zfsvfs); return (error); } @@ -2581,6 +3144,16 @@ zfs_symlink(vnode_t *dvp, char *name, vattr_t *vap, char *link, cred_t *cr) dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name); if (dzp->z_phys->zp_flags & ZFS_INHERIT_ACE) dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, SPA_MAXBLOCKSIZE); + if (zfsvfs->z_fuid_obj == 0) { + dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, + SPA_MAXBLOCKSIZE); + dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL); + } else { + dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj); + dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0, + SPA_MAXBLOCKSIZE); + } error = dmu_tx_assign(tx, zfsvfs->z_assign); if (error) { zfs_dirent_unlock(dl); @@ -2603,14 +3176,13 @@ zfs_symlink(vnode_t *dvp, char *name, vattr_t *vap, char *link, cred_t *cr) */ zoid = 0; if (sizeof (znode_phys_t) + len <= dmu_bonus_max()) { - zfs_mknode(dzp, vap, &zoid, tx, cr, 0, &zp, len); + zfs_mknode(dzp, vap, &zoid, tx, cr, 0, &zp, len, NULL, &fuidp); if (len != 0) bcopy(link, zp->z_phys + 1, len); } else { dmu_buf_t *dbp; - zfs_mknode(dzp, vap, &zoid, tx, cr, 0, &zp, 0); - + zfs_mknode(dzp, vap, &zoid, tx, cr, 0, &zp, 0, NULL, &fuidp); /* * Nothing can access the znode yet so no locking needed * for growing the znode's blocksize. @@ -2631,8 +3203,14 @@ zfs_symlink(vnode_t *dvp, char *name, vattr_t *vap, char *link, cred_t *cr) */ (void) zfs_link_create(dl, zp, tx, ZNEW); out: - if (error == 0) - zfs_log_symlink(zilog, tx, TX_SYMLINK, dzp, zp, name, link); + if (error == 0) { + uint64_t txtype = TX_SYMLINK; + if (flags & FIGNORECASE) + txtype |= TX_CI; + zfs_log_symlink(zilog, tx, txtype, dzp, zp, name, link); + } + if (fuidp) + zfs_fuid_info_free(fuidp); dmu_tx_commit(tx); @@ -2651,6 +3229,7 @@ zfs_symlink(vnode_t *dvp, char *name, vattr_t *vap, char *link, cred_t *cr) * IN: vp - vnode of symbolic link. * uoip - structure to contain the link path. * cr - credentials of caller. + * ct - caller context * * OUT: uio - structure to contain the link path. * @@ -2662,7 +3241,7 @@ zfs_symlink(vnode_t *dvp, char *name, vattr_t *vap, char *link, cred_t *cr) */ /* ARGSUSED */ static int -zfs_readlink(vnode_t *vp, uio_t *uio, cred_t *cr) +zfs_readlink(vnode_t *vp, uio_t *uio, cred_t *cr, caller_context_t *ct) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; @@ -2699,6 +3278,7 @@ zfs_readlink(vnode_t *vp, uio_t *uio, cred_t *cr) * svp - vnode of new entry. * name - name of new entry. * cr - credentials of caller. + * ct - caller context * * RETURN: 0 if success * error code if failure @@ -2709,7 +3289,8 @@ zfs_readlink(vnode_t *vp, uio_t *uio, cred_t *cr) */ /* ARGSUSED */ static int -zfs_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr) +zfs_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr, + caller_context_t *ct, int flags) { znode_t *dzp = VTOZ(tdvp); znode_t *tzp, *szp; @@ -2719,13 +3300,15 @@ zfs_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr) dmu_tx_t *tx; vnode_t *realvp; int error; + int zf = ZNEW; + uid_t owner; ASSERT(tdvp->v_type == VDIR); ZFS_ENTER_VERIFY_ZP(zfsvfs, dzp); zilog = zfsvfs->z_log; - if (VOP_REALVP(svp, &realvp) == 0) + if (VOP_REALVP(svp, &realvp, ct) == 0) svp = realvp; if (svp->v_vfsp != tdvp->v_vfsp) { @@ -2733,6 +3316,14 @@ zfs_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr) return (EXDEV); } + if (zfsvfs->z_case & ZFS_UTF8_ONLY && u8_validate(name, + strlen(name), NULL, U8_VALIDATE_ENTIRE, &error) < 0) { + ZFS_EXIT(zfsvfs); + return (EILSEQ); + } + if (flags & FIGNORECASE) + zf |= ZCILOOK; + szp = VTOZ(svp); if (!szp->z_dbuf_held) { ZFS_EXIT(zfsvfs); @@ -2760,13 +3351,14 @@ zfs_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr) return (EPERM); } - if ((uid_t)szp->z_phys->zp_uid != crgetuid(cr) && + zfs_fuid_map_id(zfsvfs, szp->z_phys->zp_uid, ZFS_OWNER, &owner); + if (owner != crgetuid(cr) && secpolicy_basic_link(cr) != 0) { ZFS_EXIT(zfsvfs); return (EPERM); } - if (error = zfs_zaccess(dzp, ACE_ADD_FILE, cr)) { + if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) { ZFS_EXIT(zfsvfs); return (error); } @@ -2774,7 +3366,8 @@ zfs_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr) /* * Attempt to lock directory; fail if entry already exists. */ - if (error = zfs_dirent_lock(&dl, dzp, name, &tzp, ZNEW)) { + error = zfs_dirent_lock(&dl, dzp, name, &tzp, zf, NULL, NULL); + if (error) { ZFS_EXIT(zfsvfs); return (error); } @@ -2797,15 +3390,19 @@ zfs_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr) error = zfs_link_create(dl, szp, tx, 0); - if (error == 0) - zfs_log_link(zilog, tx, TX_LINK, dzp, szp, name); + if (error == 0) { + uint64_t txtype = TX_LINK; + if (flags & FIGNORECASE) + txtype |= TX_CI; + zfs_log_link(zilog, tx, txtype, dzp, szp, name); + } dmu_tx_commit(tx); zfs_dirent_unlock(dl); if (error == 0) { - vnevent_link(svp); + vnevent_link(svp, ct); } ZFS_EXIT(zfsvfs); @@ -2953,6 +3550,7 @@ zfs_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp, * len - amount of data to write. * flags - flags to control the operation. * cr - credentials of caller. + * ct - caller context. * * RETURN: 0 if success * error code if failure @@ -2960,8 +3558,10 @@ zfs_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp, * Timestamps: * vp - ctime|mtime updated */ +/*ARGSUSED*/ static int -zfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr) +zfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr, + caller_context_t *ct) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; @@ -3022,8 +3622,9 @@ zfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr) return (error); } +/*ARGSUSED*/ void -zfs_inactive(vnode_t *vp, cred_t *cr) +zfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; @@ -3084,13 +3685,15 @@ zfs_inactive(vnode_t *vp, cred_t *cr) * IN: vp - vnode seeking within * ooff - old file offset * noffp - pointer to new file offset + * ct - caller context * * RETURN: 0 if success * EINVAL if new offset invalid */ /* ARGSUSED */ static int -zfs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) +zfs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, + caller_context_t *ct) { if (vp->v_type == VDIR) return (0); @@ -3103,7 +3706,7 @@ zfs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp) */ static int zfs_frlock(vnode_t *vp, int cmd, flock64_t *bfp, int flag, offset_t offset, - flk_callback_t *flk_cbp, cred_t *cr) + flk_callback_t *flk_cbp, cred_t *cr, caller_context_t *ct) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; @@ -3121,7 +3724,7 @@ zfs_frlock(vnode_t *vp, int cmd, flock64_t *bfp, int flag, offset_t offset, ZFS_EXIT(zfsvfs); return (EAGAIN); } - error = fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr); + error = fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct); ZFS_EXIT(zfsvfs); return (error); } @@ -3239,6 +3842,7 @@ zfs_fillpage(vnode_t *vp, u_offset_t off, struct seg *seg, * addr - virtual address of fault. * rw - mode of created pages. * cr - credentials of caller. + * ct - caller context. * * OUT: protp - protection mode of created pages. * pl - list of pages created. @@ -3253,7 +3857,7 @@ zfs_fillpage(vnode_t *vp, u_offset_t off, struct seg *seg, static int zfs_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp, page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr, - enum seg_rw rw, cred_t *cr) + enum seg_rw rw, cred_t *cr, caller_context_t *ct) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; @@ -3384,15 +3988,22 @@ zfs_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp, * * zfs_addmap() updates z_mapcnt */ +/*ARGSUSED*/ static int zfs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, - size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) + size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; segvn_crargs_t vn_a; int error; + if ((prot & PROT_WRITE) && + (zp->z_phys->zp_flags & (ZFS_IMMUTABLE | ZFS_READONLY | + ZFS_APPENDONLY))) + return (EPERM); + ZFS_ENTER_VERIFY_ZP(zfsvfs, zp); if (vp->v_flag & VNOMAP) { @@ -3454,7 +4065,8 @@ zfs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, /* ARGSUSED */ static int zfs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, - size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr) + size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct) { uint64_t pages = btopr(len); @@ -3486,7 +4098,8 @@ zfs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, /* ARGSUSED */ static int zfs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, - size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr) + size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct) { uint64_t pages = btopr(len); @@ -3495,7 +4108,7 @@ zfs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, if ((flags & MAP_SHARED) && (prot & PROT_WRITE) && vn_has_cached_data(vp)) - (void) VOP_PUTPAGE(vp, off, len, B_ASYNC, cr); + (void) VOP_PUTPAGE(vp, off, len, B_ASYNC, cr, ct); return (0); } @@ -3512,6 +4125,7 @@ zfs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, * flag - current file open mode flags. * offset - current file offset. * cr - credentials of caller [UNUSED]. + * ct - caller context. * * RETURN: 0 if success * error code if failure @@ -3559,8 +4173,9 @@ zfs_space(vnode_t *vp, int cmd, flock64_t *bfp, int flag, return (error); } +/*ARGSUSED*/ static int -zfs_fid(vnode_t *vp, fid_t *fidp) +zfs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; @@ -3611,7 +4226,8 @@ zfs_fid(vnode_t *vp, fid_t *fidp) } static int -zfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) +zfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, + caller_context_t *ct) { znode_t *zp, *xzp; zfsvfs_t *zfsvfs; @@ -3633,7 +4249,7 @@ zfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) ZFS_ENTER_VERIFY_ZP(zfsvfs, zp); *valp = 0; error = zfs_dirent_lock(&dl, zp, "", &xzp, - ZXATTR | ZEXISTS | ZSHARED); + ZXATTR | ZEXISTS | ZSHARED, NULL, NULL); if (error == 0) { zfs_dirent_unlock(dl); if (!zfs_dirempty(xzp)) @@ -3649,6 +4265,13 @@ zfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) ZFS_EXIT(zfsvfs); return (error); + case _PC_SATTR_ENABLED: + case _PC_SATTR_EXISTS: + zp = VTOZ(vp); + *valp = vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) && + (vp->v_type == VREG || vp->v_type == VDIR); + return (0); + case _PC_ACL_ENABLED: *valp = _ACL_ACE_ENABLED; return (0); @@ -3658,20 +4281,22 @@ zfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr) return (0); default: - return (fs_pathconf(vp, cmd, valp, cr)); + return (fs_pathconf(vp, cmd, valp, cr, ct)); } } /*ARGSUSED*/ static int -zfs_getsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr) +zfs_getsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr, + caller_context_t *ct) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; int error; + boolean_t skipaclchk = (flag & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE; ZFS_ENTER_VERIFY_ZP(zfsvfs, zp); - error = zfs_getacl(zp, vsecp, cr); + error = zfs_getacl(zp, vsecp, skipaclchk, cr); ZFS_EXIT(zfsvfs); return (error); @@ -3679,14 +4304,16 @@ zfs_getsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr) /*ARGSUSED*/ static int -zfs_setsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr) +zfs_setsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr, + caller_context_t *ct) { znode_t *zp = VTOZ(vp); zfsvfs_t *zfsvfs = zp->z_zfsvfs; int error; + boolean_t skipaclchk = (flag & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE; ZFS_ENTER_VERIFY_ZP(zfsvfs, zp); - error = zfs_setacl(zp, vsecp, cr); + error = zfs_setacl(zp, vsecp, skipaclchk, cr); ZFS_EXIT(zfsvfs); return (error); } diff --git a/usr/src/uts/common/fs/zfs/zfs_znode.c b/usr/src/uts/common/fs/zfs/zfs_znode.c index 7415a15e7493..c9c3e720bac8 100644 --- a/usr/src/uts/common/fs/zfs/zfs_znode.c +++ b/usr/src/uts/common/fs/zfs/zfs_znode.c @@ -51,7 +51,10 @@ #include #include #include +#include +#include #include +#include #endif /* _KERNEL */ #include @@ -262,13 +265,18 @@ zfs_init_fs(zfsvfs_t *zfsvfs, znode_t **zpp, cred_t *cr) */ if (dmu_object_info(os, MASTER_NODE_OBJ, &doi) == ENOENT) { dmu_tx_t *tx = dmu_tx_create(os); + uint64_t zpl_version; dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, TRUE, NULL); /* master */ dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, TRUE, NULL); /* del queue */ dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); /* root node */ error = dmu_tx_assign(tx, TXG_WAIT); ASSERT3U(error, ==, 0); - zfs_create_fs(os, cr, ZPL_VERSION, tx); + if (spa_version(dmu_objset_spa(os)) >= SPA_VERSION_FUID) + zpl_version = ZPL_VERSION; + else + zpl_version = ZPL_VERSION_FUID - 1; + zfs_create_fs(os, cr, zpl_version, 0, tx); dmu_tx_commit(tx); } @@ -327,6 +335,11 @@ zfs_init_fs(zfsvfs_t *zfsvfs, znode_t **zpp, cred_t *cr) return (error); } ASSERT3U((*zpp)->z_id, ==, zfsvfs->z_root); + error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_FUID_TABLES, 8, 1, + &zfsvfs->z_fuid_obj); + if (error == ENOENT) + error = 0; + return (0); } @@ -503,13 +516,18 @@ zfs_znode_dmu_init(znode_t *zp) * IS_ROOT_NODE - new object will be root * IS_XATTR - new object is an attribute * IS_REPLAY - intent log replay + * bonuslen - length of bonus buffer + * setaclp - File/Dir initial ACL + * fuidp - Tracks fuid allocation. * * OUT: oid - ID of created object + * zpp - allocated znode * */ void zfs_mknode(znode_t *dzp, vattr_t *vap, uint64_t *oid, dmu_tx_t *tx, cred_t *cr, - uint_t flag, znode_t **zpp, int bonuslen) + uint_t flag, znode_t **zpp, int bonuslen, zfs_acl_t *setaclp, + zfs_fuid_info_t **fuidp) { dmu_buf_t *dbp; znode_phys_t *pzp; @@ -543,13 +561,13 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, uint64_t *oid, dmu_tx_t *tx, cred_t *cr, */ if (vap->va_type == VDIR) { if (flag & IS_REPLAY) { - err = zap_create_claim(zfsvfs->z_os, *oid, - DMU_OT_DIRECTORY_CONTENTS, + err = zap_create_claim_norm(zfsvfs->z_os, *oid, + zfsvfs->z_norm, DMU_OT_DIRECTORY_CONTENTS, DMU_OT_ZNODE, sizeof (znode_phys_t) + bonuslen, tx); ASSERT3U(err, ==, 0); } else { - *oid = zap_create(zfsvfs->z_os, - DMU_OT_DIRECTORY_CONTENTS, + *oid = zap_create_norm(zfsvfs->z_os, + zfsvfs->z_norm, DMU_OT_DIRECTORY_CONTENTS, DMU_OT_ZNODE, sizeof (znode_phys_t) + bonuslen, tx); } } else { @@ -593,6 +611,9 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, uint64_t *oid, dmu_tx_t *tx, cred_t *cr, pzp->zp_rdev = zfs_expldev(vap->va_rdev); } + if (zfsvfs->z_use_fuids) + pzp->zp_flags = ZFS_ARCHIVE | ZFS_AV_MODIFIED; + if (vap->va_type == VDIR) { pzp->zp_size = 2; /* contents ("." and "..") */ pzp->zp_links = (flag & (IS_ROOT_NODE | IS_XATTR)) ? 2 : 1; @@ -622,7 +643,7 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, uint64_t *oid, dmu_tx_t *tx, cred_t *cr, pzp->zp_mode = MAKEIMODE(vap->va_type, vap->va_mode); zp = zfs_znode_alloc(zfsvfs, dbp, *oid, 0); - zfs_perm_init(zp, dzp, flag, vap, tx, cr); + zfs_perm_init(zp, dzp, flag, vap, tx, cr, setaclp, fuidp); if (zpp) { kmutex_t *hash_mtx = ZFS_OBJ_MUTEX(zp); @@ -639,6 +660,71 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, uint64_t *oid, dmu_tx_t *tx, cred_t *cr, } } +void +zfs_xvattr_set(znode_t *zp, xvattr_t *xvap) +{ + xoptattr_t *xoap; + + xoap = xva_getxoptattr(xvap); + ASSERT(xoap); + + if (XVA_ISSET_REQ(xvap, XAT_CREATETIME)) { + ZFS_TIME_ENCODE(&xoap->xoa_createtime, zp->z_phys->zp_crtime); + XVA_SET_RTN(xvap, XAT_CREATETIME); + } + if (XVA_ISSET_REQ(xvap, XAT_READONLY)) { + ZFS_ATTR_SET(zp, ZFS_READONLY, xoap->xoa_readonly); + XVA_SET_RTN(xvap, XAT_READONLY); + } + if (XVA_ISSET_REQ(xvap, XAT_HIDDEN)) { + ZFS_ATTR_SET(zp, ZFS_HIDDEN, xoap->xoa_hidden); + XVA_SET_RTN(xvap, XAT_HIDDEN); + } + if (XVA_ISSET_REQ(xvap, XAT_SYSTEM)) { + ZFS_ATTR_SET(zp, ZFS_SYSTEM, xoap->xoa_system); + XVA_SET_RTN(xvap, XAT_SYSTEM); + } + if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE)) { + ZFS_ATTR_SET(zp, ZFS_ARCHIVE, xoap->xoa_archive); + XVA_SET_RTN(xvap, XAT_ARCHIVE); + } + if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE)) { + ZFS_ATTR_SET(zp, ZFS_IMMUTABLE, xoap->xoa_immutable); + XVA_SET_RTN(xvap, XAT_IMMUTABLE); + } + if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) { + ZFS_ATTR_SET(zp, ZFS_NOUNLINK, xoap->xoa_nounlink); + XVA_SET_RTN(xvap, XAT_NOUNLINK); + } + if (XVA_ISSET_REQ(xvap, XAT_APPENDONLY)) { + ZFS_ATTR_SET(zp, ZFS_APPENDONLY, xoap->xoa_appendonly); + XVA_SET_RTN(xvap, XAT_APPENDONLY); + } + if (XVA_ISSET_REQ(xvap, XAT_NODUMP)) { + ZFS_ATTR_SET(zp, ZFS_NODUMP, xoap->xoa_nodump); + XVA_SET_RTN(xvap, XAT_NODUMP); + } + if (XVA_ISSET_REQ(xvap, XAT_OPAQUE)) { + ZFS_ATTR_SET(zp, ZFS_OPAQUE, xoap->xoa_opaque); + XVA_SET_RTN(xvap, XAT_OPAQUE); + } + if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) { + ZFS_ATTR_SET(zp, ZFS_AV_QUARANTINED, + xoap->xoa_av_quarantined); + XVA_SET_RTN(xvap, XAT_AV_QUARANTINED); + } + if (XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED)) { + ZFS_ATTR_SET(zp, ZFS_AV_MODIFIED, xoap->xoa_av_modified); + XVA_SET_RTN(xvap, XAT_AV_MODIFIED); + } + if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) { + (void) memcpy(zp->z_phys + 1, xoap->xoa_av_scanstamp, + sizeof (xoap->xoa_av_scanstamp)); + zp->z_phys->zp_flags |= ZFS_BONUS_SCANSTAMP; + XVA_SET_RTN(xvap, XAT_AV_SCANSTAMP); + } +} + int zfs_zget(zfsvfs_t *zfsvfs, uint64_t obj_num, znode_t **zpp) { @@ -861,11 +947,17 @@ zfs_time_stamper_locked(znode_t *zp, uint_t flag, dmu_tx_t *tx) if (flag & AT_ATIME) ZFS_TIME_ENCODE(&now, zp->z_phys->zp_atime); - if (flag & AT_MTIME) + if (flag & AT_MTIME) { ZFS_TIME_ENCODE(&now, zp->z_phys->zp_mtime); + if (zp->z_zfsvfs->z_use_fuids) + zp->z_phys->zp_flags |= (ZFS_ARCHIVE | ZFS_AV_MODIFIED); + } - if (flag & AT_CTIME) + if (flag & AT_CTIME) { ZFS_TIME_ENCODE(&now, zp->z_phys->zp_ctime); + if (zp->z_zfsvfs->z_use_fuids) + zp->z_phys->zp_flags |= ZFS_ARCHIVE; + } } /* @@ -958,8 +1050,13 @@ zfs_freesp(znode_t *zp, uint64_t off, uint64_t len, int flag, boolean_t log) rl_t *rl; uint64_t end = off + len; uint64_t size, new_blksz; + uint64_t pflags = zp->z_phys->zp_flags; int error; + if ((pflags & (ZFS_IMMUTABLE|ZFS_READONLY)) || + off < zp->z_phys->zp_size && (pflags & ZFS_APPENDONLY)) + return (EPERM); + if (ZTOV(zp)->v_type == VFIFO) return (0); @@ -1094,7 +1191,8 @@ zfs_freesp(znode_t *zp, uint64_t off, uint64_t len, int flag, boolean_t log) } void -zfs_create_fs(objset_t *os, cred_t *cr, uint64_t version, dmu_tx_t *tx) +zfs_create_fs(objset_t *os, cred_t *cr, uint64_t version, + int norm, dmu_tx_t *tx) { zfsvfs_t zfsvfs; uint64_t moid, doid, roid = 0; @@ -1155,12 +1253,16 @@ zfs_create_fs(objset_t *os, cred_t *cr, uint64_t version, dmu_tx_t *tx) zfsvfs.z_os = os; zfsvfs.z_assign = TXG_NOWAIT; zfsvfs.z_parent = &zfsvfs; + zfsvfs.z_version = version; + zfsvfs.z_use_fuids = USE_FUIDS(version, os); + zfsvfs.z_norm = norm; mutex_init(&zfsvfs.z_znodes_lock, NULL, MUTEX_DEFAULT, NULL); list_create(&zfsvfs.z_all_znodes, sizeof (znode_t), offsetof(znode_t, z_link_node)); - zfs_mknode(rootzp, &vattr, &roid, tx, cr, IS_ROOT_NODE, NULL, 0); + zfs_mknode(rootzp, &vattr, &roid, tx, cr, IS_ROOT_NODE, + NULL, 0, NULL, NULL); ASSERT3U(rootzp->z_id, ==, roid); error = zap_add(os, moid, ZFS_ROOT_OBJ, 8, 1, &roid, tx); ASSERT(error == 0); @@ -1168,8 +1270,8 @@ zfs_create_fs(objset_t *os, cred_t *cr, uint64_t version, dmu_tx_t *tx) ZTOV(rootzp)->v_count = 0; kmem_cache_free(znode_cache, rootzp); } -#endif /* _KERNEL */ +#endif /* _KERNEL */ /* * Given an object number, return its parent object number and whether * or not the object is an extended attribute directory. diff --git a/usr/src/uts/common/fs/zfs/zil.c b/usr/src/uts/common/fs/zfs/zil.c index fb0393f02ad9..98e494926d3c 100644 --- a/usr/src/uts/common/fs/zfs/zil.c +++ b/usr/src/uts/common/fs/zfs/zil.c @@ -857,7 +857,7 @@ zil_lwb_commit(zilog_t *zilog, itx_t *itx, lwb_t *lwb) } itx_t * -zil_itx_create(int txtype, size_t lrsize) +zil_itx_create(uint64_t txtype, size_t lrsize) { itx_t *itx; @@ -1396,6 +1396,9 @@ zil_replay_log_record(zilog_t *zilog, lr_t *lr, void *zra, uint64_t claim_txg) if (lr->lrc_seq <= zh->zh_replay_seq) /* already replayed */ return; + /* Strip case-insensitive bit, still present in log record */ + txtype &= ~TX_CI; + /* * Make a copy of the data so we can revise and extend it. */ @@ -1515,8 +1518,9 @@ zil_replay_log_record(zilog_t *zilog, lr_t *lr, void *zra, uint64_t claim_txg) name = kmem_alloc(MAXNAMELEN, KM_SLEEP); dmu_objset_name(zr->zr_os, name); cmn_err(CE_WARN, "ZFS replay transaction error %d, " - "dataset %s, seq 0x%llx, txtype %llu\n", - error, name, (u_longlong_t)lr->lrc_seq, (u_longlong_t)txtype); + "dataset %s, seq 0x%llx, txtype %llu %s\n", + error, name, (u_longlong_t)lr->lrc_seq, (u_longlong_t)txtype, + (lr->lrc_txtype & TX_CI) ? "CI" : ""); zilog->zl_stop_replay = 1; kmem_free(name, MAXNAMELEN); } diff --git a/usr/src/uts/common/fs/zfs/zvol.c b/usr/src/uts/common/fs/zfs/zvol.c index 7f7c4018319d..dd83cb7b014f 100644 --- a/usr/src/uts/common/fs/zfs/zvol.c +++ b/usr/src/uts/common/fs/zfs/zvol.c @@ -223,7 +223,8 @@ zvol_minor_lookup(const char *name) void zvol_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) { - nvlist_t *nvprops = arg; + zfs_creat_t *zct = arg; + nvlist_t *nvprops = zct->zct_props; int error; uint64_t volblocksize, volsize; diff --git a/usr/src/uts/common/idmap/idmap_cache.c b/usr/src/uts/common/idmap/idmap_cache.c index d5c5f4f3748b..3b4c4963cddb 100644 --- a/usr/src/uts/common/idmap/idmap_cache.c +++ b/usr/src/uts/common/idmap/idmap_cache.c @@ -368,8 +368,8 @@ kidmap_cache_purge_avl(idmap_avl_cache_t *cache) if (rw_tryupgrade(&cache->lock) == 0) { /* * Could not upgrade lock so release lock - * and aquire the write lock. It is valid to - * release abd re-aquire the lock as there + * and acquire the write lock. It is valid to + * release abd re-acquire the lock as there * can only be one purge routine running on an * avl tree and no other routine removes * entries. @@ -451,7 +451,7 @@ kidmap_find_sid_prefix(const char *sid_prefix) { if (rw_tryupgrade(&kidmap_sid_prefix_store->lock) == 0) { /* * Could not upgrade lock so release lock - * and aquire the write lock + * and acquire the write lock */ rw_exit(&kidmap_sid_prefix_store->lock); rw_enter(&kidmap_sid_prefix_store->lock, RW_WRITER); diff --git a/usr/src/uts/common/io/cons.c b/usr/src/uts/common/io/cons.c index 2baf58af3760..ae55f2ab61a2 100644 --- a/usr/src/uts/common/io/cons.c +++ b/usr/src/uts/common/io/cons.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -121,7 +121,7 @@ static struct dev_ops cn_ops = { * Global variables associated with the console device: * * XXX: There are too many of these! - * moved to space.c to becone resident in the kernel so that cons + * moved to space.c to become resident in the kernel so that cons * can be loadable. */ @@ -310,7 +310,7 @@ cnopen(dev_t *dev, int flag, int state, struct cred *cred) LOG_HIWAT / LOG_MSGSIZE, TASKQ_PREPOPULATE); } - if ((err = VOP_OPEN(&vp, flag, cred)) != 0) + if ((err = VOP_OPEN(&vp, flag, cred, NULL)) != 0) return (err); /* @@ -323,7 +323,7 @@ cnopen(dev_t *dev, int flag, int state, struct cred *cred) * whilst we were in the middle of the open. */ if (rconsvp == NULL) { - (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred); + (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL); return (0); } cmn_err(CE_PANIC, "cnopen: cloned open"); @@ -354,7 +354,7 @@ cnclose(dev_t dev, int flag, int state, struct cred *cred) return (0); while ((rconsopen != 0) && ((vp = rconsvp) != NULL)) { - err = VOP_CLOSE(vp, flag, 1, (offset_t)0, cred); + err = VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL); if (!err) { vp->v_stream = NULL; rconsopen--; diff --git a/usr/src/uts/common/io/devinfo.c b/usr/src/uts/common/io/devinfo.c index bc441e93a3e3..79279b4e31d6 100644 --- a/usr/src/uts/common/io/devinfo.c +++ b/usr/src/uts/common/io/devinfo.c @@ -3630,11 +3630,11 @@ di_cache_write(struct di_cache *cache) /* * Now sync the file and close it */ - if (error = VOP_FSYNC(vp, FSYNC, kcred)) { + if (error = VOP_FSYNC(vp, FSYNC, kcred, NULL)) { CACHE_DEBUG((DI_ERR, "FSYNC failed: %d", error)); } - if (error = VOP_CLOSE(vp, oflags, 1, (offset_t)0, kcred)) { + if (error = VOP_CLOSE(vp, oflags, 1, (offset_t)0, kcred, NULL)) { CACHE_DEBUG((DI_ERR, "close() failed: %d", error)); VN_RELE(vp); return; @@ -3655,7 +3655,7 @@ di_cache_write(struct di_cache *cache) return; fail: - (void) VOP_CLOSE(vp, oflags, 1, (offset_t)0, kcred); + (void) VOP_CLOSE(vp, oflags, 1, (offset_t)0, kcred, NULL); VN_RELE(vp); } diff --git a/usr/src/uts/common/io/devpoll.c b/usr/src/uts/common/io/devpoll.c index 965df2a9a13c..1e71013d1763 100644 --- a/usr/src/uts/common/io/devpoll.c +++ b/usr/src/uts/common/io/devpoll.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -128,12 +128,12 @@ static struct modlinkage modlinkage = { * This assumption is not true for /dev/poll; hence the need for extra * locking. * - * To allow more paralellism, each /dev/poll file descriptor (indexed by + * To allow more parallelism, each /dev/poll file descriptor (indexed by * minor number) has its own lock. Since read (dpioctl) is a much more * frequent operation than write, we want to allow multiple reads on same * /dev/poll fd. However, we prevent writes from being starved by giving * priority to write operation. Theoretically writes can starve reads as - * well. But in pratical sense this is not important because (1) writes + * well. But in practical sense this is not important because (1) writes * happens less often than reads, and (2) write operation defines the * content of poll fd a cache set. If writes happens so often that they * can starve reads, that means the cached set is very unstable. It may @@ -348,7 +348,7 @@ dp_pcache_poll(pollfd_t *pfdp, pollcache_t *pcp, nfds_t nfds, int *fdcntp) */ curthread->t_pollcache = pcp; error = VOP_POLL(fp->f_vnode, pdp->pd_events, 0, - &revent, &php); + &revent, &php, NULL); curthread->t_pollcache = NULL; releasef(fd); if (error != 0) { @@ -623,7 +623,7 @@ dpwrite(dev_t dev, struct uio *uiop, cred_t *credp) */ curthread->t_pollcache = pcp; error = VOP_POLL(fp->f_vnode, pfdp->events, 0, - &pfdp->revents, &php); + &pfdp->revents, &php, NULL); curthread->t_pollcache = NULL; /* * We always set the bit when this fd is cached. @@ -809,7 +809,7 @@ dpioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) continue; /* - * Sleep until we are notified, signalled, or timed out. + * Sleep until we are notified, signaled, or timed out. * Do not check for signals if we have a zero timeout. */ if (time_out == 0) /* immediate timeout */ diff --git a/usr/src/uts/common/io/fssnap.c b/usr/src/uts/common/io/fssnap.c index 4e047f03a2ab..54d3470fda2f 100644 --- a/usr/src/uts/common/io/fssnap.c +++ b/usr/src/uts/common/io/fssnap.c @@ -347,7 +347,7 @@ _fini(void) * together the frozen file system. The data may either be on the master * device (no translation exists), in memory (a translation exists but has * not been flushed to the backing store), or in the backing store file. - * The read request may require the snapshot driver to retreive data from + * The read request may require the snapshot driver to retrieve data from * several different places and piece it together to look like a single * contiguous read. * @@ -1032,7 +1032,7 @@ int *rvalp) releasef(fc.rootfiledesc); /* pass ioctl request to file system */ - error = VOP_IOCTL(vp, cmd, arg, 0, credp, rvalp); + error = VOP_IOCTL(vp, cmd, arg, 0, credp, rvalp, NULL); VN_RELE(vp); break; } @@ -1055,7 +1055,7 @@ int *rvalp) releasef(fc.rootfiledesc); /* pass ioctl request to file system */ - error = VOP_IOCTL(vp, cmd, arg, 0, credp, rvalp); + error = VOP_IOCTL(vp, cmd, arg, 0, credp, rvalp, NULL); VN_RELE(vp); break; } @@ -1130,7 +1130,7 @@ int *rvalp) * to use as a locking semaphore across the IOCTL * for mount in progress cases... */ - vfsp = kmem_alloc(sizeof (vfs_t), KM_SLEEP); + vfsp = vfs_alloc(KM_SLEEP); VFS_INIT(vfsp, vfsops, NULL); VFS_HOLD(vfsp); vfs_addmip(dev, vfsp); @@ -1146,7 +1146,7 @@ int *rvalp) * until IOCTL complete to prohibit a mount sneaking * in */ - error = VOP_IOCTL(vp, cmd, arg, 0, credp, rvalp); + error = VOP_IOCTL(vp, cmd, arg, 0, credp, rvalp, NULL); vfs_delmip(vfsp); VFS_RELE(vfsp); VN_RELE(vp); diff --git a/usr/src/uts/common/io/gentty.c b/usr/src/uts/common/io/gentty.c index 431e80245dda..086624fc2689 100644 --- a/usr/src/uts/common/io/gentty.c +++ b/usr/src/uts/common/io/gentty.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. + * Copyright 2007 Sun Microsystems, Inc. * All rights reserved. * Use is subject to license terms. */ @@ -217,7 +217,7 @@ syopen(dev_t *devp, int flag, int otyp, struct cred *cr) * The multi-open, single-clone means that no cloning * can happen via this open, hence the assertion. */ - error = VOP_OPEN(&ttyvp, FNOCTTY | flag, cr); + error = VOP_OPEN(&ttyvp, FNOCTTY | flag, cr, NULL); if (error == 0) { struct snode *csp; @@ -326,7 +326,7 @@ syioctl(dev_t dev, int cmd, intptr_t arg, int mode, struct cred *cr, return (ENXIO); } - error = VOP_IOCTL(sp->s_vp, cmd, arg, mode, cr, rvalp); + error = VOP_IOCTL(sp->s_vp, cmd, arg, mode, cr, rvalp, NULL); tty_rele(sp); return (error); @@ -350,7 +350,7 @@ sypoll(dev_t dev, short events, int anyyet, short *reventsp, return (ENXIO); } - error = VOP_POLL(sp->s_vp, events, anyyet, reventsp, phpp); + error = VOP_POLL(sp->s_vp, events, anyyet, reventsp, phpp, NULL); tty_rele(sp); return (error); diff --git a/usr/src/uts/common/io/ib/mgt/ibcm/ibcm_arp.c b/usr/src/uts/common/io/ib/mgt/ibcm/ibcm_arp.c index a69ecc8fead7..71a20fa91877 100644 --- a/usr/src/uts/common/io/ib/mgt/ibcm/ibcm_arp.c +++ b/usr/src/uts/common/io/ib/mgt/ibcm/ibcm_arp.c @@ -290,7 +290,7 @@ ibcm_arp_unlink_driver(queue_t **q, vnode_t **dev_vp) setq(rq, &strdata, &stwdata, NULL, QMTSAFE, SQ_CI|SQ_CO, B_TRUE); - if ((rc = VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED())) != 0) { + if ((rc = VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL)) != 0) { IBTF_DPRINTF_L2(cmlog, "ibcm_arp_unlink_driver: VOP_CLOSE " "failed %d\n", rc); } diff --git a/usr/src/uts/common/io/iwscons.c b/usr/src/uts/common/io/iwscons.c index e254a4291667..54e4bd77629e 100644 --- a/usr/src/uts/common/io/iwscons.c +++ b/usr/src/uts/common/io/iwscons.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -105,7 +105,7 @@ static iwscn_list_t *iwscn_list; * the redirection streams module (redirmod) pushed on them. * * If both iwscn_redirect_lock and iwscn_list_lock must be held then - * iwscn_redirect_lock must be aquired first. + * iwscn_redirect_lock must be acquired first. */ static kcondvar_t iwscn_list_cv; static kmutex_t iwscn_list_lock; @@ -320,7 +320,7 @@ iwscnpoll(dev_t dev, short events, int anyyet, short *reventsp, ASSERT(getminor(dev) == 0); lp = srhold(); - error = VOP_POLL(lp->wl_vp, events, anyyet, reventsp, phpp); + error = VOP_POLL(lp->wl_vp, events, anyyet, reventsp, phpp, NULL); srrele(lp); return (error); @@ -448,7 +448,7 @@ iwscnioctl(dev_t dev, int cmd, intptr_t arg, int flag, } /* Process the ioctl normally */ - error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp); + error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp, NULL); srrele(lp); mutex_exit(&iwscn_redirect_lock); @@ -457,7 +457,7 @@ iwscnioctl(dev_t dev, int cmd, intptr_t arg, int flag, /* Process the ioctl normally */ lp = srhold(); - error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp); + error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp, NULL); srrele(lp); return (error); } @@ -498,11 +498,11 @@ iwscnopen(dev_t *devp, int flag, int state, cred_t *cred) * There is currently no redirection going on. * pass this open request onto the console driver */ - error = VOP_OPEN(&vp, flag, cred); + error = VOP_OPEN(&vp, flag, cred, NULL); if (error != 0) return (error); - /* Re-aquire the list lock */ + /* Re-acquire the list lock */ mutex_enter(&iwscn_list_lock); if (iwscn_list == NULL) { @@ -513,7 +513,7 @@ iwscnopen(dev_t *devp, int flag, int state, cred_t *cred) * In this case there must already be a copy of * this vnode on the list, so we can free up this one. */ - (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred); + (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL); } } @@ -588,7 +588,8 @@ iwscnclose(dev_t dev, int flag, int state, cred_t *cred) if (lp->wl_is_console == B_TRUE) /* Close the underlying console device. */ - (void) VOP_CLOSE(lp->wl_vp, 0, 1, (offset_t)0, kcred); + (void) VOP_CLOSE(lp->wl_vp, 0, 1, (offset_t)0, kcred, + NULL); kmem_free(lp, sizeof (*lp)); } diff --git a/usr/src/uts/common/io/l_strplumb.c b/usr/src/uts/common/io/l_strplumb.c index 7c6f1b55d3a3..a8ff99b12669 100644 --- a/usr/src/uts/common/io/l_strplumb.c +++ b/usr/src/uts/common/io/l_strplumb.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -70,7 +70,7 @@ kstr_open(major_t maj, minor_t min, vnode_t **vpp, int *fd) if (fd != NULL) error = fassign(&vp, FREAD|FWRITE, fd); else - error = VOP_OPEN(&vp, FREAD|FWRITE, CRED()); + error = VOP_OPEN(&vp, FREAD|FWRITE, CRED(), NULL); /* * Must set vpp after calling fassign()/VOP_OPEN() @@ -135,7 +135,7 @@ kstr_close(vnode_t *vp, int fd) return (EINVAL); } } else { - ret = VOP_CLOSE(vp, FREAD|FWRITE, 1, (offset_t)0, CRED()); + ret = VOP_CLOSE(vp, FREAD|FWRITE, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); return (ret); } diff --git a/usr/src/uts/common/io/lofi.c b/usr/src/uts/common/io/lofi.c index 2f1107e189db..14e06740c8da 100644 --- a/usr/src/uts/common/io/lofi.c +++ b/usr/src/uts/common/io/lofi.c @@ -223,7 +223,8 @@ lofi_free_handle(dev_t dev, minor_t minor, struct lofi_state *lsp, char namebuf[50]; if (lsp->ls_vp) { - (void) VOP_CLOSE(lsp->ls_vp, lsp->ls_openflag, 1, 0, credp); + (void) VOP_CLOSE(lsp->ls_vp, lsp->ls_openflag, + 1, 0, credp, NULL); VN_RELE(lsp->ls_vp); lsp->ls_vp = NULL; } @@ -864,7 +865,7 @@ lofi_map_file(dev_t dev, struct lofi_ioctl *ulip, int pickminor, } } vattr.va_mask = AT_SIZE; - error = VOP_GETATTR(vp, &vattr, 0, credp); + error = VOP_GETATTR(vp, &vattr, 0, credp, NULL); if (error) { goto closeout; } @@ -936,7 +937,7 @@ lofi_map_file(dev_t dev, struct lofi_ioctl *ulip, int pickminor, * Try to handle stacked lofs vnodes. */ if (vp->v_type == VREG) { - if (VOP_REALVP(vp, &lsp->ls_vp) != 0) { + if (VOP_REALVP(vp, &lsp->ls_vp, NULL) != 0) { lsp->ls_vp = vp; } else { /* @@ -971,7 +972,7 @@ lofi_map_file(dev_t dev, struct lofi_ioctl *ulip, int pickminor, (void) ddi_prop_remove(newdev, lofi_dip, SIZE_PROP_NAME); (void) ddi_prop_remove(newdev, lofi_dip, NBLOCKS_PROP_NAME); closeout: - (void) VOP_CLOSE(vp, flag, 1, 0, credp); + (void) VOP_CLOSE(vp, flag, 1, 0, credp, NULL); VN_RELE(vp); out: if (zalloced) @@ -1035,7 +1036,7 @@ lofi_unmap_file(dev_t dev, struct lofi_ioctl *ulip, int byfilename, while (lsp->ls_vp_iocount > 0) cv_wait(&lsp->ls_vp_cv, &lsp->ls_vp_lock); (void) VOP_CLOSE(lsp->ls_vp, lsp->ls_openflag, 1, 0, - credp); + credp, NULL); VN_RELE(lsp->ls_vp); lsp->ls_vp = NULL; cv_broadcast(&lsp->ls_vp_cv); diff --git a/usr/src/uts/common/io/lvm/trans/trans_ioctl.c b/usr/src/uts/common/io/lvm/trans/trans_ioctl.c index 156ed4548aa0..4fd02c4ff235 100644 --- a/usr/src/uts/common/io/lvm/trans/trans_ioctl.c +++ b/usr/src/uts/common/io/lvm/trans/trans_ioctl.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -296,7 +295,7 @@ trans_test_trypage(void *d, int mode, IOLOCK *lock) /* * get rid of the devices pages */ - (void) VOP_PUTPAGE(cvp, (offset_t)0, (uint_t)0, B_INVAL, CRED()); + (void) VOP_PUTPAGE(cvp, (offset_t)0, (uint_t)0, B_INVAL, CRED(), NULL); /* * test 1 -- don't find nonexistant page @@ -335,7 +334,7 @@ trans_test_trypage(void *d, int mode, IOLOCK *lock) /* * get rid of the file's pages */ - (void) VOP_PUTPAGE(cvp, (offset_t)0, (uint_t)0, B_INVAL, CRED()); + (void) VOP_PUTPAGE(cvp, (offset_t)0, (uint_t)0, B_INVAL, CRED(), NULL); VN_RELE(devvp); migp->size = test; @@ -1301,7 +1300,7 @@ trans_detach_ioctl(void *d, int mode, IOLOCK *lock) mdclrerror(&migp->mde); - /* aquire both md_unit_array_rw, and unit_reader lock */ + /* acquire both md_unit_array_rw, and unit_reader lock */ un = trans_getun(migp->id, &migp->mde, READERS, lock); if (un == NULL) @@ -2239,7 +2238,7 @@ trans_renexch_update_kids( } /* - * MDRNM_SELF_UPDATE_FROM (exhange down) [self->child] + * MDRNM_SELF_UPDATE_FROM (exchange down) [self->child] */ void trans_exchange_self_update_from_down( @@ -2366,7 +2365,7 @@ trans_exchange_self_update_from_down( } /* - * MDRNM_PARENT_UPDATE_TO (exhange down) [parent->self] + * MDRNM_PARENT_UPDATE_TO (exchange down) [parent->self] */ void trans_exchange_parent_update_to( diff --git a/usr/src/uts/common/io/physmem.c b/usr/src/uts/common/io/physmem.c index 8a4cab75d1ea..8a9ca67db8bf 100644 --- a/usr/src/uts/common/io/physmem.c +++ b/usr/src/uts/common/io/physmem.c @@ -96,17 +96,17 @@ kmutex_t physmem_mutex; /* protects phsymem_vnodecnt */ static int physmem_getpage(struct vnode *vp, offset_t off, size_t len, uint_t *protp, page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr, - enum seg_rw rw, struct cred *cr); + enum seg_rw rw, struct cred *cr, caller_context_t *ct); static int physmem_addmap(struct vnode *vp, offset_t off, struct as *as, caddr_t addr, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cred); + struct cred *cred, caller_context_t *ct); static int physmem_delmap(struct vnode *vp, offset_t off, struct as *as, caddr_t addr, size_t len, uint_t prot, uint_t maxprot, uint_t flags, - struct cred *cred); + struct cred *cred, caller_context_t *ct); -static void physmem_inactive(vnode_t *vp, cred_t *crp); +static void physmem_inactive(vnode_t *vp, cred_t *crp, caller_context_t *ct); const fs_operation_def_t physmem_vnodeops_template[] = { VOPNAME_GETPAGE, { .vop_getpage = physmem_getpage }, @@ -334,7 +334,7 @@ physmem_setup_vnops() * The guts of the PHYSMEM_SETUP ioctl. * Create a segment in the address space with the specified parameters. * If pspp->user_va is NULL, as_gap will be used to find an appropriate VA. - * We do not do bounds checking on the requested phsycial addresses, if they + * We do not do bounds checking on the requested physical addresses, if they * do not exist in the system, they will not be mappable. * Returns 0 on success with the following error codes on failure: * ENOMEM - The VA range requested was already mapped if pspp->user_va is @@ -643,7 +643,7 @@ physmem_destroy_addrs(uint64_t p_cookie) static int physmem_getpage(struct vnode *vp, offset_t off, size_t len, uint_t *protp, page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr, enum seg_rw rw, - struct cred *cr) + struct cred *cr, caller_context_t *ct) { page_t *pp; @@ -674,7 +674,7 @@ physmem_getpage(struct vnode *vp, offset_t off, size_t len, uint_t *protp, static int physmem_addmap(struct vnode *vp, offset_t off, struct as *as, caddr_t addr, size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, - struct cred *cred) + struct cred *cred, caller_context_t *ct) { if (curproc->p_as != as) { return (EINVAL); @@ -687,7 +687,7 @@ physmem_addmap(struct vnode *vp, offset_t off, struct as *as, static int physmem_delmap(struct vnode *vp, offset_t off, struct as *as, caddr_t addr, size_t len, uint_t prot, uint_t maxprot, uint_t flags, - struct cred *cred) + struct cred *cred, caller_context_t *ct) { /* * Release our hold on the vnode so that the final VN_RELE will @@ -703,7 +703,7 @@ physmem_delmap(struct vnode *vp, offset_t off, struct as *as, */ /*ARGSUSED*/ static void -physmem_inactive(vnode_t *vp, cred_t *crp) +physmem_inactive(vnode_t *vp, cred_t *crp, caller_context_t *ct) { page_t *pp; diff --git a/usr/src/uts/common/io/rsm/rsmops.c b/usr/src/uts/common/io/rsm/rsmops.c index e69af1ad8064..67319d20279d 100644 --- a/usr/src/uts/common/io/rsm/rsmops.c +++ b/usr/src/uts/common/io/rsm/rsmops.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -322,7 +321,7 @@ rsm_get_controller(const char *name, uint_t number, mutex_enter(&rsmops_lock); if (vp != NULL) { (void) VOP_CLOSE(vp, FREAD|FWRITE, 0, 0, - CRED()); + CRED(), NULL); VN_RELE(vp); } p_ctrl = find_rsmpi_controller(name, number); @@ -534,7 +533,7 @@ rsmops_device_open(const char *major_name, const minor_t minor_num) vp = makespecvp(makedevice(maj, minor_num), VCHR); - ret = VOP_OPEN(&vp, FREAD|FWRITE, CRED()); + ret = VOP_OPEN(&vp, FREAD|FWRITE, CRED(), NULL); if (ret == 0) { return (vp); } else { diff --git a/usr/src/uts/common/io/sysmsg.c b/usr/src/uts/common/io/sysmsg.c index e91648b72291..0e7061c42f66 100644 --- a/usr/src/uts/common/io/sysmsg.c +++ b/usr/src/uts/common/io/sysmsg.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -339,7 +338,7 @@ bind_consadm_conf(char *path) if (vn_open(path, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0) != 0) return; vattr.va_mask = AT_SIZE; - if ((err = VOP_GETATTR(vp, &vattr, 0, kcred)) != 0) { + if ((err = VOP_GETATTR(vp, &vattr, 0, kcred, NULL)) != 0) { cmn_err(CE_WARN, "sysmsg: getattr: '%s': error %d", path, err); goto closevp; @@ -358,7 +357,7 @@ bind_consadm_conf(char *path) kmem_free(buf, size); closevp: - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, kcred); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, kcred, NULL); VN_RELE(vp); } @@ -446,7 +445,7 @@ sysmclose(dev_t dev, int flag, int state, cred_t *cred) return (0); } - (void) VOP_CLOSE(dcvp, FWRITE, 1, (offset_t)0, kcred); + (void) VOP_CLOSE(dcvp, FWRITE, 1, (offset_t)0, kcred, NULL); VN_RELE(dcvp); dcvp = NULL; mutex_exit(&dcvp_mutex); @@ -459,7 +458,7 @@ sysmclose(dev_t dev, int flag, int state, cred_t *cred) rw_enter(&sysmcache[i].dca_lock, RW_WRITER); if (sysmcache[i].dca_vp != NULL) { (void) VOP_CLOSE(sysmcache[i].dca_vp, flag, - 1, (offset_t)0, cred); + 1, (offset_t)0, cred, NULL); VN_RELE(sysmcache[i].dca_vp); sysmcache[i].dca_vp = NULL; } @@ -565,7 +564,7 @@ sysmioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred, int *rvalp) } default: /* everything else is sent to the console device */ - return (VOP_IOCTL(dcvp, cmd, arg, flag, cred, rvalp)); + return (VOP_IOCTL(dcvp, cmd, arg, flag, cred, rvalp, NULL)); } if ((rval = secpolicy_console(cred)) != 0) @@ -694,7 +693,7 @@ static int sysmpoll(dev_t dev, short events, int anyyet, short *reventsp, struct pollhead **phpp) { - return (VOP_POLL(dcvp, events, anyyet, reventsp, phpp)); + return (VOP_POLL(dcvp, events, anyyet, reventsp, phpp, NULL)); } /* Sanity check that the device is good */ diff --git a/usr/src/uts/common/ipp/ipgpc/classifier.c b/usr/src/uts/common/ipp/ipgpc/classifier.c index 4f329c9d1b0c..bb09a3ca899a 100644 --- a/usr/src/uts/common/ipp/ipgpc/classifier.c +++ b/usr/src/uts/common/ipp/ipgpc/classifier.c @@ -71,7 +71,7 @@ static void get_port_info(ipgpc_packet_t *, void *, int, mblk_t *); * common_classify(packet, fid_table, slctrs_srchd) * * searches each of the common selectors - * - will return NORMAL_MATCH on sucess. NO_MATCHES on error + * - will return NORMAL_MATCH on success. NO_MATCHES on error */ static int common_classify(ipgpc_packet_t *packet, ht_match_t *fid_table, diff --git a/usr/src/uts/common/ipp/ipgpc/filters.c b/usr/src/uts/common/ipp/ipgpc/filters.c index 2add8eba6aed..7dd4dce48e4a 100644 --- a/usr/src/uts/common/ipp/ipgpc/filters.c +++ b/usr/src/uts/common/ipp/ipgpc/filters.c @@ -1300,7 +1300,7 @@ ipgpc_addclass(ipgpc_class_t *in_class, ipp_flags_t flags) { * - number of bytes that matched this class * - number of packets that matched this class * - time in hrtime of last match for this class - * any failures are returned, zero on sucess + * any failures are returned, zero on success */ static int class_statinit(ipgpc_class_t *in_class, int in_class_id) @@ -1742,7 +1742,7 @@ ipgpc_modifyfilter(nvlist_t **nvlpp, ipp_flags_t flags) if (ret == 0) { /* no error, add filter */ ret = ipgpc_addfilter(filter, name, flags); if (ret != 0) { - /* error occured, free filter fields */ + /* error occurred, free filter fields */ ipgpc0dbg(("ipgpc_modifyfilter: invalid " \ "filter given, unable to modify " \ "existing filter %s", @@ -1756,7 +1756,7 @@ ipgpc_modifyfilter(nvlist_t **nvlpp, ipp_flags_t flags) } ipgpc_filter_destructor(&old_filter); } else { - ipgpc0dbg(("ipgpc_modifyfilter: error %d occured " \ + ipgpc0dbg(("ipgpc_modifyfilter: error %d occurred " \ "when modifying filter", ret)); ipgpc_filter_destructor(&old_filter); ipgpc_filter_destructor(filter); @@ -1863,7 +1863,7 @@ ipgpc_modifyclass(nvlist_t **nvlpp, ipp_flags_t flags) if ((rc = ipp_action_ref(ipgpc_aid, in_class.next_action, 0)) != 0) { ipgpc0dbg(("ipgpc_modifyclass: error " \ - "occured while adding a reference to " \ + "occurred while adding a reference to " \ "the new next_action %d", in_class.next_action)); mutex_exit(&ipgpc_cid_list_lock); diff --git a/usr/src/uts/common/krtld/kobj.c b/usr/src/uts/common/krtld/kobj.c index 7a0427265eb8..deffd012bde7 100644 --- a/usr/src/uts/common/krtld/kobj.c +++ b/usr/src/uts/common/krtld/kobj.c @@ -217,7 +217,7 @@ static kmutex_t kobj_lock; /* protects mach memory list */ * The following functions have been implemented by the kernel. * However, many 3rd party drivers provide their own implementations * of these functions. When such drivers are loaded, messages - * indicateing that these symbols have been mulply defined will be + * indicating that these symbols have been multiply defined will be * emitted to the console. To avoid alarming customers for no good * reason, we simply suppress such warnings for the following set of * functions. @@ -804,7 +804,7 @@ load_exec(val_t *bootaux, char *filename) allocsize += MAXPATHLEN; } bcopy(libname, mp->depends_on + osize, lsize); - *(mp->depends_on + nsize) = ' '; /* seperate */ + *(mp->depends_on + nsize) = ' '; /* separate */ nsize++; osize = nsize; } @@ -1760,7 +1760,7 @@ process_dynamic(struct module *mp, char *dyndata, char *strdata) * finish up the depends string (if any) */ if (depstr != NULL) { - *(depstr + nsize - 1) = '\0'; /* overwrite seperator w/term */ + *(depstr + nsize - 1) = '\0'; /* overwrite separator w/term */ if (path != NULL) kobj_free(path, MAXPATHLEN); @@ -3577,7 +3577,7 @@ kobj_open(char *filename) cred_t *saved_cred = curthread->t_cred; curthread->t_cred = kcred; Errno = vn_openat(filename, UIO_SYSSPACE, FREAD, 0, &vp, - 0, 0, rootdir); + 0, 0, rootdir, -1); curthread->t_cred = saved_cred; } kobjopen_free(ltp); @@ -3704,7 +3704,7 @@ kobj_close(intptr_t descr) if (_modrootloaded) { struct vnode *vp = (struct vnode *)descr; - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); } else (void) kobj_boot_close((int)descr); @@ -3719,7 +3719,7 @@ kobj_fstat(intptr_t descr, struct bootstat *buf) if (_modrootloaded) { vattr_t vattr; struct vnode *vp = (struct vnode *)descr; - if (VOP_GETATTR(vp, &vattr, 0, kcred) != 0) + if (VOP_GETATTR(vp, &vattr, 0, kcred, NULL) != 0) return (-1); /* diff --git a/usr/src/uts/common/ktli/t_kopen.c b/usr/src/uts/common/ktli/t_kopen.c index c49b03b6102b..c373d4a7f9fc 100644 --- a/usr/src/uts/common/ktli/t_kopen.c +++ b/usr/src/uts/common/ktli/t_kopen.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -124,7 +123,8 @@ t_kopen(file_t *fp, dev_t rdev, int flags, TIUSER **tiptr, cred_t *cr) * might fail due to temporarely out of memory. */ do { - if ((error = VOP_OPEN(&vp, flags, cr)) == EAGAIN) { + if ((error = VOP_OPEN(&vp, flags, cr, NULL)) + == EAGAIN) { (void) delay(hz); } } while (error == EAGAIN && ++rtries < 5); diff --git a/usr/src/uts/common/nfs/nfs.h b/usr/src/uts/common/nfs/nfs.h index 03c32254b70d..265b02d3151f 100644 --- a/usr/src/uts/common/nfs/nfs.h +++ b/usr/src/uts/common/nfs/nfs.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -254,7 +254,7 @@ extern bool_t nfs_allow_preepoch_time; * If no negative otw values are allowed, may use the full 32-bits of the * time to represent time later than 2038, by presenting the value as an * unsigned (but this can only be used by 64-bit apps due to cstat32 - * retrictions). If negative values are allowed, cannot represent times + * restrictions). If negative values are allowed, cannot represent times * after 2038. Either way, all 32 bits have a valid representation. */ @@ -496,7 +496,7 @@ struct nfsfattr { uint32_t na_uid; /* owner user id */ uint32_t na_gid; /* owner group id */ uint32_t na_size; /* file size in bytes */ - uint32_t na_blocksize; /* prefered block size */ + uint32_t na_blocksize; /* preferred block size */ uint32_t na_rdev; /* special device # */ uint32_t na_blocks; /* Kb of disk used by file */ uint32_t na_fsid; /* device # */ @@ -907,7 +907,7 @@ extern int nfsinit(int, char *); extern void nfsfini(void); extern int nfs_vfsinit(void); extern void nfs_vfsfini(void); -extern int nfs_dump(vnode_t *, caddr_t, int, int); +extern int nfs_dump(vnode_t *, caddr_t, int, int, caller_context_t *); extern void nfs_perror(int error, char *fmt, ...); extern void nfs_cmn_err(int error, int level, char *fmt, ...); extern int nfs_addcllock(vnode_t *vp, struct flock64 *bfp); diff --git a/usr/src/uts/common/nfs/nfs4.h b/usr/src/uts/common/nfs/nfs4.h index 2000cac44fe7..b9a76a2d438b 100644 --- a/usr/src/uts/common/nfs/nfs4.h +++ b/usr/src/uts/common/nfs/nfs4.h @@ -858,7 +858,8 @@ extern nfsstat4 rfs4_get_deleg_state(stateid4 *, extern nfsstat4 rfs4_get_lo_state(stateid4 *, rfs4_lo_state_t **, bool_t); extern nfsstat4 rfs4_check_stateid(int, vnode_t *, stateid4 *, - bool_t, bool_t *, bool_t); + bool_t, bool_t *, bool_t, + caller_context_t *); extern int rfs4_check_stateid_seqid(rfs4_state_t *, stateid4 *); extern int rfs4_check_lo_stateid_seqid(rfs4_lo_state_t *, stateid4 *); @@ -908,8 +909,8 @@ extern void rfs4_clear_dont_grant(rfs4_file_t *); /* * nfs4 monitored operations. */ -extern int deleg_rdopen(femarg_t *, int, cred_t *); -extern int deleg_wropen(femarg_t *, int, cred_t *); +extern int deleg_rdopen(femarg_t *, int, cred_t *, caller_context_t *); +extern int deleg_wropen(femarg_t *, int, cred_t *, caller_context_t *); extern int deleg_rd_rwlock(femarg_t *, int, caller_context_t *); extern int deleg_wr_rwlock(femarg_t *, int, caller_context_t *); extern int deleg_read(femarg_t *, uio_t *, int, cred_t *, caller_context_t *); @@ -918,8 +919,10 @@ extern int deleg_setattr(femarg_t *, vattr_t *, int, cred_t *, caller_context_t *); extern int deleg_space(femarg_t *, int, flock64_t *, int, offset_t, cred_t *, caller_context_t *); -extern int deleg_setsecattr(femarg_t *, vsecattr_t *, int, cred_t *); -extern int deleg_vnevent(femarg_t *, vnevent_t, vnode_t *, char *); +extern int deleg_setsecattr(femarg_t *, vsecattr_t *, int, cred_t *, + caller_context_t *); +extern int deleg_vnevent(femarg_t *, vnevent_t, vnode_t *, char *, + caller_context_t *); extern void rfs4_mon_hold(void *); extern void rfs4_mon_rele(void *); diff --git a/usr/src/uts/common/nfs/rnode.h b/usr/src/uts/common/nfs/rnode.h index e4eb0cee5c6e..63d38ba6ee13 100644 --- a/usr/src/uts/common/nfs/rnode.h +++ b/usr/src/uts/common/nfs/rnode.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -334,7 +334,7 @@ extern void nfs_async_commit(vnode_t *, page_t *, offset3, count3, cred_t *, void (*)(vnode_t *, page_t *, offset3, count3, cred_t *)); extern void nfs_async_inactive(vnode_t *, cred_t *, void (*)(vnode_t *, - cred_t *)); + cred_t *, caller_context_t *)); extern int writerp(rnode_t *, caddr_t, int, struct uio *, int); extern int nfs_putpages(vnode_t *, u_offset_t, size_t, int, cred_t *); extern void nfs_invalidate_pages(vnode_t *, u_offset_t, cred_t *); diff --git a/usr/src/uts/common/os/acct.c b/usr/src/uts/common/os/acct.c index c596ce45f3d3..a8da2f8fb000 100644 --- a/usr/src/uts/common/os/acct.c +++ b/usr/src/uts/common/os/acct.c @@ -130,7 +130,8 @@ acct_shutdown(zoneid_t zoneid, void *arg) * held vnode may cause filesystems to be busy, and the zone * shutdown operation to fail. */ - (void) VOP_CLOSE(ag->acctvp, FWRITE, 1, (offset_t)0, kcred); + (void) VOP_CLOSE(ag->acctvp, FWRITE, 1, (offset_t)0, kcred, + NULL); VN_RELE(ag->acctvp); } ag->acctvp = NULL; @@ -211,7 +212,7 @@ acct_find(vnode_t *vp, boolean_t compare_vfs) ASSERT(MUTEX_HELD(&acct_list_lock)); ASSERT(vp != NULL); - if (VOP_REALVP(vp, &realvp)) + if (VOP_REALVP(vp, &realvp, NULL)) realvp = vp; for (ag = list_head(&acct_list); ag != NULL; ag = list_next(&acct_list, ag)) { @@ -223,7 +224,7 @@ acct_find(vnode_t *vp, boolean_t compare_vfs) mutex_exit(&ag->aclock); continue; } - if (VOP_REALVP(ag->acctvp, &racctvp)) + if (VOP_REALVP(ag->acctvp, &racctvp, NULL)) racctvp = ag->acctvp; if (compare_vfs) { if (racctvp->v_vfsp == realvp->v_vfsp) @@ -281,7 +282,8 @@ sysacct(char *fname) ag->acctvp = NULL; mutex_exit(&ag->aclock); if (vp) { - error = VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, CRED()); + error = VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, CRED(), + NULL); VN_RELE(vp); } return (error == 0 ? 0 : set_errno(error)); @@ -333,7 +335,7 @@ sysacct(char *fname) } if (vp) { - (void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); } return (error == 0 ? 0 : set_errno(error)); @@ -425,7 +427,7 @@ acct(char st) * currently large file aware. */ va.va_mask = AT_SIZE; - if (VOP_GETATTR(vp, &va, 0, kcred) == 0) { + if (VOP_GETATTR(vp, &va, 0, kcred, NULL) == 0) { error = vn_rdwr(UIO_WRITE, vp, (caddr_t)&ag->acctbuf, sizeof (ag->acctbuf), 0LL, UIO_SYSSPACE, FAPPEND, (rlim64_t)MAXOFF32_T, kcred, &resid); diff --git a/usr/src/uts/common/os/core.c b/usr/src/uts/common/os/core.c index 4adac80e9339..71af0012d475 100644 --- a/usr/src/uts/common/os/core.c +++ b/usr/src/uts/common/os/core.c @@ -159,16 +159,16 @@ remove_core_file(char *fp, enum core_types core_type) else if ((dvfsp = dvp->v_vfsp) != NULL && (dvfsp->vfs_flag & VFS_RDONLY)) error = EROFS; - else if ((error = VOP_ACCESS(vp, VWRITE, 0, CRED())) == 0) { + else if ((error = VOP_ACCESS(vp, VWRITE, 0, CRED(), NULL)) == 0) { if (nbl_need_check(vp)) { nbl_start_crit(vp, RW_READER); in_crit = 1; - if (nbl_share_conflict(vp, NBL_REMOVE)) { + if (nbl_share_conflict(vp, NBL_REMOVE, NULL)) { error = EACCES; } } if (!error) { - error = VOP_REMOVE(dvp, pn.pn_path, CRED()); + error = VOP_REMOVE(dvp, pn.pn_path, CRED(), NULL, 0); } } @@ -254,8 +254,9 @@ create_core_file(char *fp, enum core_types core_type, vnode_t **vpp) pn_setlast(&pn); file = pn.pn_path; } - error = vn_openat(file, UIO_SYSSPACE, FWRITE | FTRUNC | FEXCL | - FCREAT | FOFFMAX, perms, &vp, CRCREAT, PTOU(curproc)->u_cmask, dvp); + error = vn_openat(file, UIO_SYSSPACE, + FWRITE | FTRUNC | FEXCL | FCREAT | FOFFMAX, + perms, &vp, CRCREAT, PTOU(curproc)->u_cmask, dvp, -1); if (core_type != CORE_PROC) { VN_RELE(dvp); pn_free(&pn); @@ -265,10 +266,10 @@ create_core_file(char *fp, enum core_types core_type, vnode_t **vpp) */ vattr.va_mask = AT_UID; if (error == 0 && - (VOP_GETATTR(vp, &vattr, 0, credp) != 0 || + (VOP_GETATTR(vp, &vattr, 0, credp, NULL) != 0 || vattr.va_uid != crgetuid(credp))) { (void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, - credp); + credp, NULL); VN_RELE(vp); (void) remove_core_file(fp, core_type); error = EACCES; @@ -448,7 +449,7 @@ do_core(char *fp, int sig, enum core_types core_type, struct core_globals *cg) rw_exit(eswp->exec_lock); } - closerr = VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, credp); + closerr = VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, credp, NULL); VN_RELE(vp); if (error == 0) error = closerr; diff --git a/usr/src/uts/common/os/cred.c b/usr/src/uts/common/os/cred.c index 349c4f3c127f..c6a1f1fabb46 100644 --- a/usr/src/uts/common/os/cred.c +++ b/usr/src/uts/common/os/cred.c @@ -56,11 +56,13 @@ #include #include #include +#include #include #include #include #include #include +#include typedef struct ephidmap_data { uid_t min_uid, last_uid; @@ -1028,8 +1030,8 @@ eph_gid_alloc(int flags, gid_t *start, int count) } /* - * If the credential contains any ephemeral IDs, map the credential - * to nobody. + * If the credential user SID or group SID is mapped to an ephemeral + * ID, map the credential to nobody. */ cred_t * crgetmapped(const cred_t *cr) @@ -1042,15 +1044,11 @@ crgetmapped(const cred_t *cr) return (NULL); if (cr->cr_ksid != NULL) { - int i; - - for (i = 0; i < KSID_COUNT; i++) - if (cr->cr_ksid->kr_sidx[i].ks_id > MAXUID) - return (ephemeral_data.nobody); - if (cr->cr_ksid->kr_sidlist != NULL && - cr->cr_ksid->kr_sidlist->ksl_neid > 0) { - return (ephemeral_data.nobody); - } + if (cr->cr_ksid->kr_sidx[KSID_USER].ks_id > MAXUID) + return (ephemeral_data.nobody); + + if (cr->cr_ksid->kr_sidx[KSID_GROUP].ks_id > MAXUID) + return (ephemeral_data.nobody); } return ((cred_t *)cr); @@ -1088,7 +1086,38 @@ crgetsid(const cred_t *cr, int i) ksidlist_t * crgetsidlist(const cred_t *cr) { - if (cr->cr_ksid != NULL && cr->cr_ksid->kr_sidlist != NULL) + if (cr->cr_ksid != NULL) return (cr->cr_ksid->kr_sidlist); return (NULL); } + +/* + * Interface to set the effective and permitted privileges for + * a credential; this interface does no security checks and is + * intended for kernel (file)servers creating credentials with + * specific privileges. + */ +int +crsetpriv(cred_t *cr, ...) +{ + va_list ap; + const char *privnm; + + ASSERT(cr->cr_ref <= 2); + + priv_set_PA(cr); + + va_start(ap, cr); + + while ((privnm = va_arg(ap, const char *)) != NULL) { + int priv = priv_getbyname(privnm, 0); + if (priv < 0) + return (-1); + + priv_addset(&CR_PPRIV(cr), priv); + priv_addset(&CR_EPRIV(cr), priv); + } + priv_adjust_PA(cr); + va_end(ap); + return (0); +} diff --git a/usr/src/uts/common/os/devcache.c b/usr/src/uts/common/os/devcache.c index 8e1313d48789..75ed98294034 100644 --- a/usr/src/uts/common/os/devcache.c +++ b/usr/src/uts/common/os/devcache.c @@ -75,7 +75,7 @@ * The data per client is entirely within the control of * the client. When reading, data unpacked from the backing * store should be inserted in the list. The pointer to - * the list can be retreived via nvf_list(). When writing, + * the list can be retrieved via nvf_list(). When writing, * the data on the list is to be packed and returned to the * nvpdaemon as an nvlist. * @@ -618,7 +618,7 @@ kfclose(kfile_t *fp) KFDEBUG((CE_CONT, "close: %s\n", fp->kf_fname)); if ((fp->kf_vnflags & FWRITE) && fp->kf_state == 0) { - rval = VOP_FSYNC(fp->kf_vp, FSYNC, kcred); + rval = VOP_FSYNC(fp->kf_vp, FSYNC, kcred, NULL); if (rval != 0) { nvf_error("%s: sync error %d\n", fp->kf_fname, rval); @@ -626,7 +626,8 @@ kfclose(kfile_t *fp) KFDEBUG((CE_CONT, "%s: sync ok\n", fp->kf_fname)); } - rval = VOP_CLOSE(fp->kf_vp, fp->kf_vnflags, 1, (offset_t)0, kcred); + rval = VOP_CLOSE(fp->kf_vp, fp->kf_vnflags, 1, (offset_t)0, kcred, + NULL); if (rval != 0) { if (fp->kf_state == 0) { nvf_error("%s: close error %d\n", diff --git a/usr/src/uts/common/os/driver.c b/usr/src/uts/common/os/driver.c index 652611382874..3de2712c4c74 100644 --- a/usr/src/uts/common/os/driver.c +++ b/usr/src/uts/common/os/driver.c @@ -297,7 +297,7 @@ dev_lopen(dev_t *devp, int flag, int otype, struct cred *cred) struct vnode *cvp; vp = makespecvp(*devp, (otype == OTYP_BLK) ? VBLK : VCHR); - error = VOP_OPEN(&vp, flag | FKLYR, cred); + error = VOP_OPEN(&vp, flag | FKLYR, cred, NULL); if (error == 0) { /* Pick up the (possibly) new dev_t value. */ *devp = vp->v_rdev; @@ -332,13 +332,13 @@ dev_lclose(dev_t dev, int flag, int otype, struct cred *cred) ulong_t offset; vp = makespecvp(dev, (otype == OTYP_BLK) ? VBLK : VCHR); - error = VOP_CLOSE(vp, flag | FKLYR, 1, (offset_t)0, cred); + error = VOP_CLOSE(vp, flag | FKLYR, 1, (offset_t)0, cred, NULL); /* * Release the extra dev_lopen hold on the common vnode. We inline a * VN_RELE(cvp) call so that we can detect more dev_lclose calls than * dev_lopen calls without panic. See vn_rele. If our inline of - * vn_rele called VOP_INACTIVE(cvp, CRED()) we would panic on the + * vn_rele called VOP_INACTIVE(cvp, CRED(), ...) we would panic on the * "release the makespecvp vnode" VN_RELE(vp) that follows - so * instead we diagnose this situation. Note that the driver has * still seen a double close(9E), but that would have occurred with diff --git a/usr/src/uts/common/os/driver_lyr.c b/usr/src/uts/common/os/driver_lyr.c index 266e3cbb79e6..66e9f58e35c1 100644 --- a/usr/src/uts/common/os/driver_lyr.c +++ b/usr/src/uts/common/os/driver_lyr.c @@ -757,7 +757,7 @@ ldi_open_by_vp(vnode_t **vpp, int flag, cred_t *cr, return (ENXIO); /* open the device */ - if ((err = VOP_OPEN(&vp, flag | FKLYR, cr)) != 0) + if ((err = VOP_OPEN(&vp, flag | FKLYR, cr, NULL)) != 0) return (err); /* possible clone open, make sure that we still have a spec node */ @@ -783,7 +783,7 @@ ldi_open_by_vp(vnode_t **vpp, int flag, cred_t *cr, vnode_t *cvp = common_specvp(nlhp->lh_vp); dev_t dev = cvp->v_rdev; - (void) VOP_PUTPAGE(cvp, 0, 0, B_INVAL, kcred); + (void) VOP_PUTPAGE(cvp, 0, 0, B_INVAL, kcred, NULL); bflush(dev); } @@ -945,7 +945,7 @@ i_check_string(char *str, int prop_len) /* * i_pack_string_array takes a a string array property that is represented - * as a concatination of strings (with the NULL character included for + * as a concatenation of strings (with the NULL character included for * each string) and converts it into a format that can be returned by * ldi_prop_lookup_string_array. */ @@ -1258,7 +1258,7 @@ ldi_mlink_lh(vnode_t *vp, int cmd, intptr_t arg, cred_t *crp, int *rvalp) } /* - * ldi_mlink_fp() is invoked for all successfull streams linkages created + * ldi_mlink_fp() is invoked for all successful streams linkages created * via I_LINK and I_PLINK. ldi_mlink_fp() records the linkage information * in its internal state so that the devinfo snapshot code has some * observability into streams device linkage information. @@ -1693,7 +1693,7 @@ ldi_close(ldi_handle_t lh, int flag, cred_t *cr) vnode_t *cvp = common_specvp(handlep->lh_vp); dev_t dev = cvp->v_rdev; - (void) VOP_PUTPAGE(cvp, 0, 0, B_INVAL, kcred); + (void) VOP_PUTPAGE(cvp, 0, 0, B_INVAL, kcred, NULL); bflush(dev); } @@ -1727,7 +1727,7 @@ ldi_close(ldi_handle_t lh, int flag, cred_t *cr) #endif /* do a layered close on the device */ - err = VOP_CLOSE(handlep->lh_vp, flag | FKLYR, 1, (offset_t)0, cr); + err = VOP_CLOSE(handlep->lh_vp, flag | FKLYR, 1, (offset_t)0, cr, NULL); LDI_OPENCLOSE((CE_WARN, "%s: lh=0x%p", "ldi close", (void *)lh)); @@ -2903,7 +2903,7 @@ ldi_remove_event_handler(ldi_handle_t lh, ldi_callback_id_t id) * * NDI events: These are events which are serviced by the NDI event subsystem. * LDI subsystem just provides a thin wrapper around the NDI event interfaces - * These events are thereefore *not* native events. + * These events are therefore *not* native events. */ static int diff --git a/usr/src/uts/common/os/dumpsubr.c b/usr/src/uts/common/os/dumpsubr.c index 8dd63a298b09..b8dba6189398 100644 --- a/usr/src/uts/common/os/dumpsubr.c +++ b/usr/src/uts/common/os/dumpsubr.c @@ -206,11 +206,11 @@ dumpinit(vnode_t *vp, char *name, int justchecking) * (1) a real device that's not mounted and has a cb_dump routine, or * (2) a swapfile on some filesystem that has a vop_dump routine. */ - if ((error = VOP_OPEN(&cvp, FREAD | FWRITE, kcred)) != 0) + if ((error = VOP_OPEN(&cvp, FREAD | FWRITE, kcred, NULL)) != 0) return (error); vattr.va_mask = AT_SIZE | AT_TYPE | AT_RDEV; - if ((error = VOP_GETATTR(cvp, &vattr, 0, kcred)) == 0) { + if ((error = VOP_GETATTR(cvp, &vattr, 0, kcred, NULL)) == 0) { if (vattr.va_type == VBLK || vattr.va_type == VCHR) { if (devopsp[getmajor(vattr.va_rdev)]-> devo_cb_ops->cb_dump == nodev) @@ -228,7 +228,8 @@ dumpinit(vnode_t *vp, char *name, int justchecking) error = ENOSPC; if (error || justchecking) { - (void) VOP_CLOSE(cvp, FREAD | FWRITE, 1, (offset_t)0, kcred); + (void) VOP_CLOSE(cvp, FREAD | FWRITE, 1, (offset_t)0, + kcred, NULL); return (error); } @@ -251,24 +252,26 @@ dumpinit(vnode_t *vp, char *name, int justchecking) */ if (cvp->v_type == VBLK && (cdev_vp = makespecvp(VTOS(cvp)->s_dev, VCHR)) != NULL) { - if (VOP_OPEN(&cdev_vp, FREAD | FWRITE, kcred) == 0) { + if (VOP_OPEN(&cdev_vp, FREAD | FWRITE, kcred, NULL) == 0) { size_t blk_size; struct dk_cinfo dki; struct vtoc vtoc; if (VOP_IOCTL(cdev_vp, DKIOCGVTOC, (intptr_t)&vtoc, - FKIOCTL, kcred, NULL) == 0 && vtoc.v_sectorsz != 0) + FKIOCTL, kcred, NULL, NULL) == 0 && + vtoc.v_sectorsz != 0) blk_size = vtoc.v_sectorsz; else blk_size = DEV_BSIZE; if (VOP_IOCTL(cdev_vp, DKIOCINFO, (intptr_t)&dki, - FKIOCTL, kcred, NULL) == 0) { + FKIOCTL, kcred, NULL, NULL) == 0) { dump_iosize = dki.dki_maxtransfer * blk_size; dumpbuf_resize(); } - (void) VOP_CLOSE(cdev_vp, FREAD | FWRITE, 1, 0, kcred); + (void) VOP_CLOSE(cdev_vp, FREAD | FWRITE, 1, 0, + kcred, NULL); } VN_RELE(cdev_vp); @@ -286,7 +289,7 @@ dumpfini(void) kmem_free(dumppath, strlen(dumppath) + 1); - (void) VOP_CLOSE(dumpvp, FREAD | FWRITE, 1, (offset_t)0, kcred); + (void) VOP_CLOSE(dumpvp, FREAD | FWRITE, 1, (offset_t)0, kcred, NULL); VN_RELE(dumpvp); @@ -334,7 +337,7 @@ dumpvp_flush(void) } else if (size != 0) { if (panicstr) err = VOP_DUMP(dumpvp, dumpbuf_start, - lbtodb(dumpvp_off), btod(size)); + lbtodb(dumpvp_off), btod(size), NULL); else err = vn_rdwr(UIO_WRITE, dumpvp, dumpbuf_start, size, dumpvp_off, UIO_SYSSPACE, 0, dumpvp_limit, @@ -478,7 +481,7 @@ dump_ereports(void) if (!panicstr) { (void) VOP_PUTPAGE(dumpvp, dumpvp_start, (size_t)(dumpvp_off - dumpvp_start), - B_INVAL | B_FORCE, kcred); + B_INVAL | B_FORCE, kcred, NULL); } } @@ -521,7 +524,7 @@ dump_messages(void) if (!panicstr) { (void) VOP_PUTPAGE(dumpvp, dumpvp_start, (size_t)(dumpvp_off - dumpvp_start), - B_INVAL | B_FORCE, kcred); + B_INVAL | B_FORCE, kcred, NULL); } } @@ -600,8 +603,8 @@ dumpsys(void) if (panicstr) { dumphdr->dump_flags &= ~DF_LIVE; - (void) VOP_DUMPCTL(dumpvp, DUMP_FREE, NULL); - (void) VOP_DUMPCTL(dumpvp, DUMP_ALLOC, NULL); + (void) VOP_DUMPCTL(dumpvp, DUMP_FREE, NULL, NULL); + (void) VOP_DUMPCTL(dumpvp, DUMP_ALLOC, NULL, NULL); (void) vsnprintf(dumphdr->dump_panicstring, DUMP_PANICSIZE, panicstr, panicargs); } diff --git a/usr/src/uts/common/os/exacct.c b/usr/src/uts/common/os/exacct.c index 88e532d691d4..6e2795240273 100644 --- a/usr/src/uts/common/os/exacct.c +++ b/usr/src/uts/common/os/exacct.c @@ -181,7 +181,7 @@ exacct_vn_write(ac_info_t *info, void *buf, ssize_t bufsize) * the present accounting file. */ va.va_mask = AT_SIZE; - error = VOP_GETATTR(info->ac_vnode, &va, 0, kcred); + error = VOP_GETATTR(info->ac_vnode, &va, 0, kcred, NULL); if (error == 0) { error = vn_rdwr(UIO_WRITE, info->ac_vnode, (caddr_t)buf, bufsize, 0LL, UIO_SYSSPACE, FAPPEND, (rlim64_t)MAXOFFSET_T, diff --git a/usr/src/uts/common/os/exec.c b/usr/src/uts/common/os/exec.c index 652a01c34c7c..8823e44e273d 100644 --- a/usr/src/uts/common/os/exec.c +++ b/usr/src/uts/common/os/exec.c @@ -538,7 +538,7 @@ gexec( goto bad; /* need to open vnode for stateful file systems like rfs */ - if ((error = VOP_OPEN(vpp, FREAD, CRED())) != 0) + if ((error = VOP_OPEN(vpp, FREAD, CRED(), NULL)) != 0) goto bad; vp = *vpp; @@ -934,13 +934,13 @@ execpermissions(struct vnode *vp, struct vattr *vattrp, struct uarg *args) proc_t *p = ttoproc(curthread); vattrp->va_mask = AT_MODE | AT_UID | AT_GID | AT_SIZE; - if (error = VOP_GETATTR(vp, vattrp, ATTR_EXEC, p->p_cred)) + if (error = VOP_GETATTR(vp, vattrp, ATTR_EXEC, p->p_cred, NULL)) return (error); /* * Check the access mode. * If VPROC, ask /proc if the file is an object file. */ - if ((error = VOP_ACCESS(vp, VEXEC, 0, p->p_cred)) != 0 || + if ((error = VOP_ACCESS(vp, VEXEC, 0, p->p_cred, NULL)) != 0 || !(vp->v_type == VREG || (vp->v_type == VPROC && pr_isobject(vp))) || (vp->v_vfsp->vfs_flag & VFS_NOEXEC) != 0 || (vattrp->va_mode & (VEXEC|(VEXEC>>3)|(VEXEC>>6))) == 0) { @@ -950,7 +950,7 @@ execpermissions(struct vnode *vp, struct vattr *vattrp, struct uarg *args) } if ((p->p_plist || (p->p_proc_flag & (P_PR_PTRACE|P_PR_TRACE))) && - (error = VOP_ACCESS(vp, VREAD, 0, p->p_cred))) { + (error = VOP_ACCESS(vp, VREAD, 0, p->p_cred, NULL))) { /* * If process is under ptrace(2) compatibility, * fail the exec(2). @@ -1011,7 +1011,7 @@ execmap(struct vnode *vp, caddr_t addr, size_t len, size_t zfodlen, } if (error = VOP_MAP(vp, (offset_t)offset, p->p_as, &addr, len, prot, PROT_ALL, - mflag, CRED())) + mflag, CRED(), NULL)) goto bad; /* @@ -1188,7 +1188,7 @@ execopen(struct vnode **vpp, int *fdp) *fdp = -1; /* just in case falloc changed value */ return (error); } - if (error = VOP_OPEN(&vp, filemode, CRED())) { + if (error = VOP_OPEN(&vp, filemode, CRED(), NULL)) { VN_RELE(vp); setf(*fdp, NULL); unfalloc(fp); diff --git a/usr/src/uts/common/os/fio.c b/usr/src/uts/common/os/fio.c index b31c9c7c2ea5..c5dc56e1505a 100644 --- a/usr/src/uts/common/os/fio.c +++ b/usr/src/uts/common/os/fio.c @@ -23,7 +23,7 @@ /* All Rights Reserved */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -918,7 +918,7 @@ closef(file_t *fp) vp = fp->f_vnode; - error = VOP_CLOSE(vp, flag, count, offset, fp->f_cred); + error = VOP_CLOSE(vp, flag, count, offset, fp->f_cred, NULL); if (count > 1) { mutex_exit(&fp->f_tlock); @@ -1348,7 +1348,7 @@ fassign(vnode_t **vpp, int mode, int *fdp) if (error = falloc((vnode_t *)NULL, mode, &fp, &fd)) return (error); - if (error = VOP_OPEN(vpp, mode, fp->f_cred)) { + if (error = VOP_OPEN(vpp, mode, fp->f_cred, NULL)) { setf(fd, NULL); unfalloc(fp); return (error); @@ -1506,14 +1506,15 @@ vpsetattr(vnode_t *vp, vattr_t *vap, int flags) nbl_start_crit(vp, RW_READER); in_crit = 1; vattr.va_mask = AT_SIZE; - if (!(error = VOP_GETATTR(vp, &vattr, 0, CRED()))) { + if (!(error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL))) { begin = vap->va_size > vattr.va_size ? vattr.va_size : vap->va_size; length = vattr.va_size > vap->va_size ? vattr.va_size - vap->va_size : vap->va_size - vattr.va_size; - if (nbl_conflict(vp, NBL_WRITE, begin, length, 0)) { + if (nbl_conflict(vp, NBL_WRITE, begin, length, 0, + NULL)) { error = EACCES; } } diff --git a/usr/src/uts/common/os/flock.c b/usr/src/uts/common/os/flock.c index a3028b75dc32..da81003d1c53 100644 --- a/usr/src/uts/common/os/flock.c +++ b/usr/src/uts/common/os/flock.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -22,7 +21,7 @@ /* ONC_PLUS EXTRACT START */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -201,7 +200,7 @@ static int level_one_path(lock_descriptor_t *, lock_descriptor_t *); static int level_two_path(lock_descriptor_t *, lock_descriptor_t *, int); #endif -/* proc_graph function definitons */ +/* proc_graph function definitions */ static int flk_check_deadlock(lock_descriptor_t *); static void flk_proc_graph_uncolor(void); static proc_vertex_t *flk_get_proc_vertex(lock_descriptor_t *); @@ -684,7 +683,7 @@ flk_zone_fini(zoneid_t zoneid, void *data) } /* - * Get a lock_descriptor structure with initialisation of edge lists. + * Get a lock_descriptor structure with initialization of edge lists. */ static lock_descriptor_t * @@ -727,14 +726,14 @@ flk_set_state(lock_descriptor_t *lock, int new_state) { /* * Locks in the sleeping list may be woken up in a number of ways, - * and more than once. If a sleeping lock is signalled awake more + * and more than once. If a sleeping lock is signaled awake more * than once, then it may or may not change state depending on its * current state. * Also note that NLM locks that are sleeping could be moved to an * interrupted state more than once if the unlock request is * retransmitted by the NLM client - the second time around, this is * just a nop. - * The ordering of being signalled awake is: + * The ordering of being signaled awake is: * INTERRUPTED_STATE > CANCELLED_STATE > GRANTED_STATE. * The checks below implement this ordering. */ @@ -2671,7 +2670,7 @@ convoff(vp, lckdat, whence, offset) if ((lckdat->l_whence == 2) || (whence == 2)) { vattr.va_mask = AT_SIZE; - if (error = VOP_GETATTR(vp, &vattr, 0, CRED())) + if (error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL)) return (error); } @@ -3271,7 +3270,7 @@ flk_set_lockmgr_status(flk_lockmgr_status_t status) * * A list containing the vnode pointer and an flock structure * describing the lock is returned. Each element in the list is - * dynammically allocated and must be freed by the caller. The + * dynamically allocated and must be freed by the caller. The * last item in the list is denoted by a NULL value in the ll_next * field. * @@ -3721,7 +3720,7 @@ wait_for_lock(lock_descriptor_t *request) * Create an flock structure from the existing lock information * * This routine is used to create flock structures for the lock manager - * to use in a reclaim request. Since the lock was orginated on this + * to use in a reclaim request. Since the lock was originated on this * host, it must be conforming to UNIX semantics, so no checking is * done to make sure it falls within the lower half of the 32-bit range. */ @@ -3768,7 +3767,7 @@ flk_convert_lock_data(vnode_t *vp, flock64_t *flp, break; case 2: /* SEEK_END */ vattr.va_mask = AT_SIZE; - if (error = VOP_GETATTR(vp, &vattr, 0, CRED())) + if (error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL)) return (error); *start = (u_offset_t)(flp->l_start + vattr.va_size); break; @@ -3914,11 +3913,21 @@ cl_flk_change_nlm_state_to_unknown(int nlmid) int nbl_lock_conflict(vnode_t *vp, nbl_op_t op, u_offset_t offset, - ssize_t length, int svmand) + ssize_t length, int svmand, caller_context_t *ct) { int conflict = 0; graph_t *gp; lock_descriptor_t *lock; + pid_t pid; + int sysid; + + if (ct == NULL) { + pid = curproc->p_pid; + sysid = 0; + } else { + pid = ct->cc_pid; + sysid = ct->cc_sysid; + } mutex_enter(&flock_lock); gp = lock_graph[HASH_INDEX(vp)]; @@ -3931,8 +3940,8 @@ nbl_lock_conflict(vnode_t *vp, nbl_op_t op, u_offset_t offset, for (; lock && lock->l_vnode == vp; lock = lock->l_next) { if ((svmand || (lock->l_state & NBMAND_LOCK)) && - lock->l_flock.l_sysid == 0 && - lock->l_flock.l_pid != curproc->p_pid && + (lock->l_flock.l_sysid != sysid || + lock->l_flock.l_pid != pid) && lock_blocks_io(op, offset, length, lock->l_type, lock->l_start, lock->l_end)) { conflict = 1; diff --git a/usr/src/uts/common/os/grow.c b/usr/src/uts/common/os/grow.c index e463b9700486..0033b11ff443 100644 --- a/usr/src/uts/common/os/grow.c +++ b/usr/src/uts/common/os/grow.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -560,6 +561,7 @@ smmap_common(caddr_t *addrp, size_t len, struct as *as = curproc->p_as; uint_t uprot, maxprot, type; int error; + int in_crit = 0; if ((flags & ~(MAP_SHARED | MAP_PRIVATE | MAP_FIXED | _MAP_NEW | _MAP_LOW32 | MAP_NORESERVE | MAP_ANON | MAP_ALIGN | @@ -694,12 +696,36 @@ smmap_common(caddr_t *addrp, size_t len, } } + if ((prot & (PROT_READ | PROT_WRITE | PROT_EXEC)) && + nbl_need_check(vp)) { + int svmand; + nbl_op_t nop; + + nbl_start_crit(vp, RW_READER); + in_crit = 1; + error = nbl_svmand(vp, fp->f_cred, &svmand); + if (error != 0) + goto done; + if ((prot & PROT_WRITE) && (type == MAP_SHARED)) { + if (prot & (PROT_READ | PROT_EXEC)) { + nop = NBL_READWRITE; + } else { + nop = NBL_WRITE; + } + } else { + nop = NBL_READ; + } + if (nbl_conflict(vp, nop, 0, LONG_MAX, svmand, NULL)) { + error = EACCES; + goto done; + } + } /* * Ok, now let the vnode map routine do its thing to set things up. */ error = VOP_MAP(vp, pos, as, - addrp, len, uprot, maxprot, flags, fp->f_cred); + addrp, len, uprot, maxprot, flags, fp->f_cred, NULL); if (error == 0) { if (vp->v_type == VREG && @@ -713,6 +739,9 @@ smmap_common(caddr_t *addrp, size_t len, } } +done: + if (in_crit) + nbl_end_crit(vp); return (error); } diff --git a/usr/src/uts/common/os/inst_sync.c b/usr/src/uts/common/os/inst_sync.c index 1100f1d7292e..a30f83bee688 100644 --- a/usr/src/uts/common/os/inst_sync.c +++ b/usr/src/uts/common/os/inst_sync.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -221,7 +220,7 @@ in_fclose(File *fp) { int error; - error = VOP_CLOSE(fp->vp, FCREAT, 1, (offset_t)0, CRED()); + error = VOP_CLOSE(fp->vp, FCREAT, 1, (offset_t)0, CRED(), NULL); VN_RELE(fp->vp); kmem_free(fp, sizeof (File)); return (error); @@ -235,7 +234,7 @@ in_fflush(File *fp) if (fp->count) error = in_write(fp->vp, &fp->voffset, fp->buf, fp->count); if (error == 0) - error = VOP_FSYNC(fp->vp, FSYNC, CRED()); + error = VOP_FSYNC(fp->vp, FSYNC, CRED(), NULL); return (error); } diff --git a/usr/src/uts/common/os/mem_config.c b/usr/src/uts/common/os/mem_config.c index 64bbc4b5adbc..49f7709c4146 100644 --- a/usr/src/uts/common/os/mem_config.c +++ b/usr/src/uts/common/os/mem_config.c @@ -296,7 +296,7 @@ kphysm_add_memory_dynamic(pfn_t base, pgcnt_t npgs) mem_node_add_slice(base, pnum); /* - * Allocate or resize page counters as necessary to accomodate + * Allocate or resize page counters as necessary to accommodate * the increase in memory pages. */ mnode = PFN_2_MEM_NODE(pnum); @@ -508,7 +508,7 @@ kphysm_addmem_error_undospan(pfn_t pt_base, pgcnt_t tpgs) * Only return an available memseg of exactly the right size. * When the meta data area has it's own virtual address space * we will need to manage this more carefully and do best fit - * allocations, possibly splitting an availble area. + * allocations, possibly splitting an available area. */ static struct memseg * memseg_reuse(pgcnt_t metapgs) @@ -2013,7 +2013,7 @@ delete_memory_thread(caddr_t amhp) VN_HOLD(vp); page_unlock(pp); (void) VOP_PUTPAGE(vp, offset, PAGESIZE, - B_INVAL|B_FORCE, kcred); + B_INVAL|B_FORCE, kcred, NULL); VN_RELE(vp); #ifdef MEM_DEL_STATS ntick_pgrp = (uint64_t)ddi_get_lbolt() - diff --git a/usr/src/uts/common/os/modctl.c b/usr/src/uts/common/os/modctl.c index ac38b40775ae..b4b0751d238a 100644 --- a/usr/src/uts/common/os/modctl.c +++ b/usr/src/uts/common/os/modctl.c @@ -3129,7 +3129,7 @@ static char load_msg[] = "load '%s' id %d loaded @ 0x%p/0x%p size %d/%d\n"; /* * Common code for loading a module (but not installing it). - * Handoff the task of module loading to a seperate thread + * Handoff the task of module loading to a separate thread * with a large stack if possible, since this code may recurse a few times. * Return zero if there are no errors or an errno value. */ @@ -3513,7 +3513,7 @@ moduninstall(struct modctl *mp) /* * Even though we only set mod_installed to zero here, a zero - * return value means we are commited to a code path were + * return value means we are committed to a code path were * mod_loaded will also end up as zero - we have no other * way to get the module data and bss back to the pre _init * state except a reload. To ensure this, after return, diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c index e2d466227d38..bc6ed66429bb 100644 --- a/usr/src/uts/common/os/policy.c +++ b/usr/src/uts/common/os/policy.c @@ -71,7 +71,7 @@ int priv_debug = 0; * by privilege, there is quite a bit of duplication of * functions. * - * The secpolicy functions must not make asssumptions about + * The secpolicy functions must not make assumptions about * locks held or not held as any lock can be held while they're * being called. * @@ -485,16 +485,40 @@ secpolicy_setpriority(const cred_t *cr) int secpolicy_net_privaddr(const cred_t *cr, in_port_t port) { - /* - * NFS ports, these are extra privileged ports, allow bind - * only if the SYS_NFS privilege is present. - */ - if (port == 2049 || port == 4045) - return (PRIV_POLICY(cr, PRIV_SYS_NFS, B_FALSE, EACCES, - "NFS port")); - else - return (PRIV_POLICY(cr, PRIV_NET_PRIVADDR, B_FALSE, EACCES, - NULL)); + char *reason; + int priv; + + switch (port) { + case 137: + case 138: + case 139: + case 445: + /* + * NBT and SMB ports, these are extra privileged ports, + * allow bind only if the SYS_SMB privilege is present. + */ + priv = PRIV_SYS_SMB; + reason = "NBT or SMB port"; + break; + + case 2049: + case 4045: + /* + * NFS ports, these are extra privileged ports, allow bind + * only if the SYS_NFS privilege is present. + */ + priv = PRIV_SYS_NFS; + reason = "NFS port"; + break; + + default: + priv = PRIV_NET_PRIVADDR; + reason = NULL; + break; + + } + + return (PRIV_POLICY(cr, priv, B_FALSE, EACCES, reason)); } /* @@ -583,7 +607,7 @@ secpolicy_fs_common(cred_t *cr, vnode_t *mvp, const vfs_t *vfsp, int err; va.va_mask = AT_UID|AT_MODE; - err = VOP_GETATTR(mvp, &va, 0, cr); + err = VOP_GETATTR(mvp, &va, 0, cr, NULL); if (err != 0) return (err); @@ -965,6 +989,70 @@ secpolicy_setid_setsticky_clear(vnode_t *vp, vattr_t *vap, const vattr_t *ovap, return (0); } +#define ATTR_FLAG_PRIV(attr, value, cr) \ + PRIV_POLICY(cr, value ? PRIV_FILE_FLAG_SET : PRIV_ALL, \ + B_FALSE, EPERM, NULL) + +/* + * Check privileges for setting xvattr attributes + */ +int +secpolicy_xvattr(xvattr_t *xvap, uid_t owner, cred_t *cr, vtype_t vtype) +{ + xoptattr_t *xoap; + int error = 0; + + if ((xoap = xva_getxoptattr(xvap)) == NULL) + return (EINVAL); + + /* + * First process the DOS bits + */ + if (XVA_ISSET_REQ(xvap, XAT_ARCHIVE) || + XVA_ISSET_REQ(xvap, XAT_HIDDEN) || + XVA_ISSET_REQ(xvap, XAT_READONLY) || + XVA_ISSET_REQ(xvap, XAT_SYSTEM) || + XVA_ISSET_REQ(xvap, XAT_CREATETIME)) { + if ((error = secpolicy_vnode_owner(cr, owner)) != 0) + return (error); + } + + /* + * Now handle special attributes + */ + + if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE)) + error = ATTR_FLAG_PRIV(XAT_IMMUTABLE, + xoap->xoa_immutable, cr); + if (error == 0 && XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) + error = ATTR_FLAG_PRIV(XAT_NOUNLINK, + xoap->xoa_nounlink, cr); + if (error == 0 && XVA_ISSET_REQ(xvap, XAT_APPENDONLY)) + error = ATTR_FLAG_PRIV(XAT_APPENDONLY, + xoap->xoa_appendonly, cr); + if (error == 0 && XVA_ISSET_REQ(xvap, XAT_NODUMP)) + error = ATTR_FLAG_PRIV(XAT_NODUMP, + xoap->xoa_nodump, cr); + if (error == 0 && XVA_ISSET_REQ(xvap, XAT_OPAQUE)) + error = EPERM; + if (error == 0 && XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) { + error = ATTR_FLAG_PRIV(XAT_AV_QUARANTINED, + xoap->xoa_av_quarantined, cr); + if (error == 0 && vtype != VREG) + error = EINVAL; + } + if (error == 0 && XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED)) + error = ATTR_FLAG_PRIV(XAT_AV_MODIFIED, + xoap->xoa_av_modified, cr); + if (error == 0 && XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) { + error = ATTR_FLAG_PRIV(XAT_AV_SCANSTAMP, + xoap->xoa_av_scanstamp, cr); + if (error == 0 && vtype != VREG) + error = EINVAL; + } + return (error); +} + /* * This function checks the policy decisions surrounding the * vop setattr call. @@ -1004,15 +1092,24 @@ secpolicy_vnode_setattr(cred_t *cr, struct vnode *vp, struct vattr *vap, { int mask = vap->va_mask; int error = 0; + boolean_t skipaclchk = (flags & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE; if (mask & AT_SIZE) { if (vp->v_type == VDIR) { error = EISDIR; goto out; } - error = unlocked_access(node, VWRITE, cr); - if (error) - goto out; + + /* + * If ATTR_NOACLCHECK is set in the flags, then we don't + * perform the secondary unlocked_access() call since the + * ACL (if any) is being checked there. + */ + if (skipaclchk == B_FALSE) { + error = unlocked_access(node, VWRITE, cr); + if (error) + goto out; + } } if (mask & AT_MODE) { /* @@ -1092,7 +1189,7 @@ secpolicy_vnode_setattr(cred_t *cr, struct vnode *vp, struct vattr *vap, if (cr->cr_uid != ovap->va_uid) { if (flags & ATTR_UTIME) error = secpolicy_vnode_utime_modify(cr); - else { + else if (skipaclchk == B_FALSE) { error = unlocked_access(node, VWRITE, cr); if (error == EACCES && secpolicy_vnode_utime_modify(cr) == 0) @@ -1102,6 +1199,13 @@ secpolicy_vnode_setattr(cred_t *cr, struct vnode *vp, struct vattr *vap, goto out; } } + + /* + * Check for optional attributes here by checking the following: + */ + if (mask & AT_XVATTR) + error = secpolicy_xvattr((xvattr_t *)vap, ovap->va_uid, cr, + vp->v_type); out: return (error); } @@ -1957,3 +2061,20 @@ secpolicy_sadopen(const cred_t *credp) return (secpolicy_require_set(credp, &pset, "devpolicy")); } + +/* + * secpolicy_smb + * + * Determine if the cred_t has PRIV_SYS_SMB privilege, indicating + * that it has permission to access the smbsrv kernel driver. + * PRIV_POLICY checks the privilege and audits the check. + * + * Returns: + * 0 Driver access is allowed. + * EPERM Driver access is NOT permitted. + */ +int +secpolicy_smb(const cred_t *cr) +{ + return (PRIV_POLICY(cr, PRIV_SYS_SMB, B_FALSE, EPERM, NULL)); +} diff --git a/usr/src/uts/common/os/priv_defs b/usr/src/uts/common/os/priv_defs index a39896e73ad3..b6544504281e 100644 --- a/usr/src/uts/common/os/priv_defs +++ b/usr/src/uts/common/os/priv_defs @@ -149,6 +149,11 @@ privilege PRIV_FILE_UPGRADE_SL This privilege is interpreted only if the system is configured with Trusted Extensions. +privilege PRIV_FILE_FLAG_SET + + Allows a process to set immutable, nounlink or appendonly + file attributes. + privilege PRIV_GRAPHICS_ACCESS Allows a process to make privileged ioctls to graphics devices. @@ -402,6 +407,13 @@ unsafe privilege PRIV_SYS_RESOURCE Allows a process to extend or create files on a filesystem that has less than minfree space in reserve. +privilege PRIV_SYS_SMB + + Allows a process to access the Sun private SMB kernel module. + Allows a process to bind to ports reserved by NetBIOS and SMB: + ports 137 (NBNS), 138 (NetBIOS Datagram Service), 139 (NetBIOS + Session Service and SMB-over-NBT) and 445 (SMB-over-TCP). + privilege PRIV_SYS_SUSER_COMPAT Allows a process to successfully call a third party loadable module diff --git a/usr/src/uts/common/os/session.c b/usr/src/uts/common/os/session.c index 7790a090945b..ea93f9f6b3ed 100644 --- a/usr/src/uts/common/os/session.c +++ b/usr/src/uts/common/os/session.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -166,7 +166,7 @@ tty_hold(void) /* * Now we need to drop our hold on the session structure, * but we can't hold any locks when we do this because - * sess_rele() may need to aquire pidlock. + * sess_rele() may need to acquire pidlock. */ mutex_exit(&sp->s_lock); sess_rele(sp, B_FALSE); @@ -306,9 +306,9 @@ strctty(stdata_t *stp) * We are going to try to make stp the default ctty for the session * associated with curproc. Not only does this require holding a * bunch of locks but it also requires waiting for any outstanding - * holds on the session structure (aquired via tty_hold()) to be + * holds on the session structure (acquired via tty_hold()) to be * released. Hence, we have the following for(;;) loop that will - * aquire our locks, do some sanity checks, and wait for the hold + * acquire our locks, do some sanity checks, and wait for the hold * count on the session structure to hit zero. If we get a signal * while waiting for outstanding holds to be released then we abort * the operation and return. @@ -370,12 +370,12 @@ strctty(stdata_t *stp) } /* - * freectty_lock() attempts to aquire the army of locks required to free + * freectty_lock() attempts to acquire the army of locks required to free * the ctty associated with a given session leader process. If it returns * successfully the following locks will be held: * sd_lock, pidlock, p_splock, s_lock * - * as a secondary bit of convience, freectty_lock() will also return + * as a secondary bit of convenience, freectty_lock() will also return * pointers to the session, ctty, and ctty stream associated with the * specified session leader process. */ @@ -543,10 +543,10 @@ freectty(boolean_t at_exit) /* * If the current process is a session leader we are going to * try to release the ctty associated our current session. To - * do this we need to aquire a bunch of locks, signal any + * do this we need to acquire a bunch of locks, signal any * processes in the forground that are associated with the ctty, * and make sure no one has any outstanding holds on the current - * session * structure (aquired via tty_hold()). Hence, we have + * session * structure (acquired via tty_hold()). Hence, we have * the following for(;;) loop that will do all this work for * us and break out when the hold count on the session structure * hits zero. @@ -556,7 +556,7 @@ freectty(boolean_t at_exit) return (EIO); if (freectty_signal(p, sp, stp, at_exit)) { - /* loop around to re-aquire locks */ + /* loop around to re-acquire locks */ continue; } @@ -643,7 +643,7 @@ freectty(boolean_t at_exit) mutex_exit(&stp->sd_lock); /* This is the only remaining thread with access to this vnode */ - (void) VOP_CLOSE(vp, 0, 1, (offset_t)0, cred); + (void) VOP_CLOSE(vp, 0, 1, (offset_t)0, cred, NULL); VN_RELE(vp); crfree(cred); diff --git a/usr/src/uts/common/os/share.c b/usr/src/uts/common/os/share.c index 9714e4b01f67..5d3bbb141852 100644 --- a/usr/src/uts/common/os/share.c +++ b/usr/src/uts/common/os/share.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 1996-1998,2001,2003 Sun Microsystems, Inc. - * All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -74,11 +72,11 @@ add_share(struct vnode *vp, struct shrlock *shr) * Sanity check to make sure we have valid options. * There is known overlap but it doesn't hurt to be careful. */ - if (shr->s_access & ~(F_RDACC|F_WRACC|F_RWACC)) { + if (shr->s_access & ~(F_RDACC|F_WRACC|F_RWACC|F_RMACC|F_MDACC)) { return (EINVAL); } if (shr->s_deny & ~(F_NODNY|F_RDDNY|F_WRDNY|F_RWDNY|F_COMPAT| - F_MANDDNY)) { + F_MANDDNY|F_RMDNY)) { return (EINVAL); } @@ -115,7 +113,7 @@ add_share(struct vnode *vp, struct shrlock *shr) if ((shrl->shr->s_deny & F_COMPAT) && (shr->s_deny & F_COMPAT) && ((shrl->next == NULL) || - (shrl->shr->s_access & F_WRACC))) + (shrl->shr->s_access & F_WRACC))) break; } @@ -280,13 +278,13 @@ is_match_for_del(struct shrlock *shr, struct shrlock *element) * and pids. */ result = (nlmid1 == nlmid2 && - shr->s_pid == element->s_pid); + shr->s_pid == element->s_pid); } } else { /* not in a cluster */ result = ((shr->s_sysid == 0 && - shr->s_pid == element->s_pid) || - (shr->s_sysid != 0 && - shr->s_sysid == element->s_sysid)); + shr->s_pid == element->s_pid) || + (shr->s_sysid != 0 && + shr->s_sysid == element->s_sysid)); } return (result); } @@ -315,11 +313,11 @@ del_share(struct vnode *vp, struct shrlock *shr) shrlp = &vp->v_shrlocks; while (*shrlp) { if ((shr->s_own_len == (*shrlp)->shr->s_own_len && - (bcmp(shr->s_owner, (*shrlp)->shr->s_owner, - shr->s_own_len) == 0)) || + (bcmp(shr->s_owner, (*shrlp)->shr->s_owner, + shr->s_own_len) == 0)) || - (shr->s_own_len == 0 && - is_match_for_del(shr, (*shrlp)->shr))) { + (shr->s_own_len == 0 && + is_match_for_del(shr, (*shrlp)->shr))) { shrl = *shrlp; *shrlp = shrl->next; @@ -427,7 +425,7 @@ static int isreadonly(struct vnode *vp) { return (vp->v_type != VCHR && vp->v_type != VBLK && - vp->v_type != VFIFO && vn_is_readonly(vp)); + vp->v_type != VFIFO && vn_is_readonly(vp)); } #ifdef DEBUG @@ -489,46 +487,117 @@ print_share(struct shrlock *shr) /* * Return non-zero if the given I/O request conflicts with a registered * share reservation. + * + * A process is identified by the tuple (sysid, pid). When the caller + * context is passed to nbl_share_conflict, the sysid and pid in the + * caller context are used. Otherwise the sysid is zero, and the pid is + * taken from the current process. + * + * Conflict Algorithm: + * 1. An op request of NBL_READ will fail if a different + * process has a mandatory share reservation with deny read. + * + * 2. An op request of NBL_WRITE will fail if a different + * process has a mandatory share reservation with deny write. + * + * 3. An op request of NBL_READWRITE will fail if a different + * process has a mandatory share reservation with deny read + * or deny write. + * + * 4. An op request of NBL_REMOVE will fail if there is + * a mandatory share reservation with an access of read, + * write, or remove. (Anything other than meta data access). + * + * 5. An op request of NBL_RENAME will fail if there is + * a mandatory share reservation with: + * a) access write or access remove + * or + * b) access read and deny remove + * + * Otherwise there is no conflict and the op request succeeds. + * + * This behavior is required for interoperability between + * the nfs server, cifs server, and local access. + * This behavior can result in non-posix semantics. + * + * When mandatory share reservations are enabled, a process + * should call nbl_share_conflict to determine if the + * desired operation would conflict with an existing share + * reservation. + * + * The call to nbl_share_conflict may be skipped if the + * process has an existing share reservation and the operation + * is being performed in the context of that existing share + * reservation. */ - int -nbl_share_conflict(vnode_t *vp, nbl_op_t op) +nbl_share_conflict(vnode_t *vp, nbl_op_t op, caller_context_t *ct) { struct shrlocklist *shrl; int conflict = 0; + pid_t pid; + int sysid; ASSERT(nbl_in_crit(vp)); + if (ct == NULL) { + pid = curproc->p_pid; + sysid = 0; + } else { + pid = ct->cc_pid; + sysid = ct->cc_sysid; + } + mutex_enter(&vp->v_lock); for (shrl = vp->v_shrlocks; shrl != NULL; shrl = shrl->next) { - if (shrl->shr->s_sysid == 0 && - (shrl->shr->s_deny & F_MANDDNY) && - shrl->shr->s_pid != curproc->p_pid) { - switch (op) { - case NBL_READ: - if (shrl->shr->s_deny & F_RDDNY) - conflict = 1; - break; - case NBL_WRITE: - if (shrl->shr->s_deny & F_WRDNY) - conflict = 1; - break; - case NBL_READWRITE: - if (shrl->shr->s_deny & F_RWDNY) - conflict = 1; - break; - case NBL_RENAME: - case NBL_REMOVE: + if (!(shrl->shr->s_deny & F_MANDDNY)) + continue; + /* + * NBL_READ, NBL_WRITE, and NBL_READWRITE need to + * check if the share reservation being examined + * belongs to the current process. + * NBL_REMOVE and NBL_RENAME do not. + * This behavior is required by the conflict + * algorithm described above. + */ + switch (op) { + case NBL_READ: + if ((shrl->shr->s_deny & F_RDDNY) && + (shrl->shr->s_sysid != sysid || + shrl->shr->s_pid != pid)) conflict = 1; - break; + break; + case NBL_WRITE: + if ((shrl->shr->s_deny & F_WRDNY) && + (shrl->shr->s_sysid != sysid || + shrl->shr->s_pid != pid)) + conflict = 1; + break; + case NBL_READWRITE: + if ((shrl->shr->s_deny & F_RWDNY) && + (shrl->shr->s_sysid != sysid || + shrl->shr->s_pid != pid)) + conflict = 1; + break; + case NBL_REMOVE: + if (shrl->shr->s_access & (F_RWACC|F_RMACC)) + conflict = 1; + break; + case NBL_RENAME: + if (shrl->shr->s_access & (F_WRACC|F_RMACC)) + conflict = 1; + + else if ((shrl->shr->s_access & F_RDACC) && + (shrl->shr->s_deny & F_RMDNY)) + conflict = 1; + break; #ifdef DEBUG - default: - cmn_err(CE_PANIC, - "nbl_share_conflict: bogus op (%d)", - op); - break; + default: + cmn_err(CE_PANIC, + "nbl_share_conflict: bogus op (%d)", + op); + break; #endif - } } if (conflict) break; @@ -546,10 +615,16 @@ nbl_share_conflict(vnode_t *vp, nbl_op_t op) int share_blocks_lock(vnode_t *vp, flock64_t *flkp) { + caller_context_t ct; + ASSERT(nbl_in_crit(vp)); + ct.cc_pid = flkp->l_pid; + ct.cc_sysid = flkp->l_sysid; + ct.cc_caller_id = 0; + if ((flkp->l_type == F_RDLCK || flkp->l_type == F_WRLCK) && - nbl_share_conflict(vp, nbl_lock_to_op(flkp->l_type))) + nbl_share_conflict(vp, nbl_lock_to_op(flkp->l_type), &ct)) return (1); else return (0); @@ -602,35 +677,34 @@ lock_blocks_share(vnode_t *vp, struct shrlock *shr) { struct flock64 lck; int error; - - /* - * We don't currently have a good way to match lock - * ownership with share ownership for remote requests. - * Fortunately, we know that only local processes (in particular, - * local CIFS servers) care about conflicts between locks and - * share reservations, and we can distinguish local processes from - * each other and from remote processes. - */ - ASSERT(shr->s_sysid == 0); + v_mode_t mode = 0; if ((shr->s_deny & (F_RWDNY|F_COMPAT)) == 0) { /* if no deny mode, then there's no conflict */ return (0); } - lck.l_type = ((shr->s_deny & F_RDDNY) ? F_WRLCK : F_RDLCK); + /* check for conflict with mapped region */ + if ((shr->s_deny & F_RWDNY) == F_WRDNY) { + mode = V_WRITE; + } else if ((shr->s_deny & F_RWDNY) == F_RDDNY) { + mode = V_READ; + } else { + mode = V_RDORWR; + } + if (vn_is_mapped(vp, mode)) + return (1); + lck.l_type = ((shr->s_deny & F_RDDNY) ? F_WRLCK : F_RDLCK); lck.l_whence = 0; lck.l_start = 0; lck.l_len = 0; /* to EOF */ - /* would check here for conflict with mapped region */ - /* XXX should use non-NULL cred? */ - error = VOP_FRLOCK(vp, F_GETLK, &lck, 0, 0, NULL, NULL); + error = VOP_FRLOCK(vp, F_GETLK, &lck, 0, 0, NULL, NULL, NULL); if (error != 0) { cmn_err(CE_WARN, "lock_blocks_share: unexpected error (%d)", - error); + error); return (1); } diff --git a/usr/src/uts/common/os/sid.c b/usr/src/uts/common/os/sid.c index 0da71f3cdf12..2ed5ad2989f1 100644 --- a/usr/src/uts/common/os/sid.c +++ b/usr/src/uts/common/os/sid.c @@ -41,8 +41,6 @@ #include #include -#define KSIDLIST_MEM(n) (sizeof (ksidlist_t) + ((n) - 1) * sizeof (ksid_t)) - static kmutex_t sid_lock; static avl_tree_t sid_tree; static boolean_t sid_inited = B_FALSE; diff --git a/usr/src/uts/common/os/tlabel.c b/usr/src/uts/common/os/tlabel.c index 30569bb963d8..3a467454834d 100644 --- a/usr/src/uts/common/os/tlabel.c +++ b/usr/src/uts/common/os/tlabel.c @@ -287,7 +287,7 @@ getflabel(vnode_t *vp) /* * Traverse lofs mounts and fattach'es to get the real vnode */ - if (VOP_REALVP(rvp, &rvp2) == 0) + if (VOP_REALVP(rvp, &rvp2, NULL) == 0) rvp = rvp2; rvfsp = rvp->v_vfsp; diff --git a/usr/src/uts/common/os/urw.c b/usr/src/uts/common/os/urw.c index a0195a6db3d1..ef70ccac453a 100644 --- a/usr/src/uts/common/os/urw.c +++ b/usr/src/uts/common/os/urw.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -75,7 +74,7 @@ page_valid(struct seg *seg, caddr_t addr) if (seg->s_ops == &segvn_ops && SEGOP_GETVP(seg, addr, &vp) == 0 && vp != NULL && vp->v_type == VREG && - VOP_GETATTR(vp, &vattr, 0, CRED()) == 0) { + VOP_GETATTR(vp, &vattr, 0, CRED(), NULL) == 0) { u_offset_t size = roundup(vattr.va_size, (u_offset_t)PAGESIZE); u_offset_t offset = SEGOP_GETOFFSET(seg, addr); @@ -177,7 +176,7 @@ mapout(struct as *as, caddr_t addr, caddr_t vaddr, int writing) } /* - * Perform I/O to a given process. This will return EIO if we dectect + * Perform I/O to a given process. This will return EIO if we detect * corrupt memory and ENXIO if there is no such mapped address in the * user process's address space. */ diff --git a/usr/src/uts/common/os/vm_pageout.c b/usr/src/uts/common/os/vm_pageout.c index e5c80e9bfdda..afbca6078589 100644 --- a/usr/src/uts/common/os/vm_pageout.c +++ b/usr/src/uts/common/os/vm_pageout.c @@ -634,7 +634,7 @@ int dopageout = 1; /* must be non-zero to turn page stealing on */ * Some filesystems may require resources for the VOP_PUTPAGE * operations (like memory) and hence can block the pageout * thread, but the scanner thread can still operate. There is still - * no gaurentee that memory deadlocks cannot occur. + * no guarantee that memory deadlocks cannot occur. * * For now, this thing is in very rough form. */ @@ -709,7 +709,7 @@ pageout() if (VOP_PUTPAGE(arg->a_vp, (offset_t)arg->a_off, arg->a_len, arg->a_flags, - arg->a_cred) == 0) { + arg->a_cred, NULL) == 0) { pushes++; } diff --git a/usr/src/uts/common/os/zone.c b/usr/src/uts/common/os/zone.c index c89945e7569e..ff2812b78148 100644 --- a/usr/src/uts/common/os/zone.c +++ b/usr/src/uts/common/os/zone.c @@ -2417,7 +2417,8 @@ zone_set_root(zone_t *zone, const char *upath) * filesystem, if 'vp' is an autoFS vnode. * Get the new 'vp' if so. */ - if ((error = VOP_ACCESS(vp, VEXEC, 0, CRED())) == 0 && + if ((error = + VOP_ACCESS(vp, VEXEC, 0, CRED(), NULL)) == 0 && (!vn_ismntpt(vp) || (error = traverse(&vp)) == 0)) { pathlen = pn.pn_pathlen + 2; @@ -4381,7 +4382,7 @@ zone_setattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize) * * Also return zero if the process has any shared mappings which reserve * swap. This is because the counting for zone.max-swap does not allow swap - * revervation to be shared between zones. zone swap reservation is counted + * reservation to be shared between zones. zone swap reservation is counted * on zone->zone_max_swap. */ static int @@ -5645,7 +5646,7 @@ zone_check_datalink(zoneid_t *zoneidp, char *dlname) /* * Get the names of the datalinks assigned to a zone. * Here *nump is the number of datalinks, and the assumption - * is that the caller will gurantee that the the supplied buffer is + * is that the caller will guarantee that the the supplied buffer is * big enough to hold at least #*nump datalink names, that is, * LIFNAMSIZ X *nump * On return, *nump will be the "new" number of datalinks, if it diff --git a/usr/src/uts/common/smbsrv/Makefile b/usr/src/uts/common/smbsrv/Makefile new file mode 100644 index 000000000000..233f5d214106 --- /dev/null +++ b/usr/src/uts/common/smbsrv/Makefile @@ -0,0 +1,131 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" + +include ../../../Makefile.master + +HDRS= alloc.h \ + cifs.h \ + codepage.h \ + cp_cyrillic.h \ + cp_latin1.h \ + cp_latin2.h \ + cp_latin3.h \ + cp_latin4.h \ + cp_latin5.h \ + cp_latin6.h \ + cp_unicode.h \ + cp_usascii.h \ + crypt.h \ + ctype.h \ + doserror.h \ + hash_table.h \ + lm.h \ + lmdfs.h \ + lmerr.h \ + lmshare.h \ + lmshare_door.h \ + lsalib.h \ + mac_cifs.h \ + mailslot.h \ + mbuf.h \ + mlrpc.h \ + mlsvc.h \ + mlsvc_util.h \ + msgbuf.h \ + ndr.h \ + netbios.h \ + netrauth.h \ + nmpipes.h \ + ntaccess.h \ + nterror.h \ + ntifs.h \ + ntlocale.h \ + ntsid.h \ + ntstatus.h \ + oem.h \ + samlib.h \ + smb.h \ + smb_common_door.h \ + smb_door_svc.h \ + smb_fsd.h \ + smb_fsops.h \ + smb_i18n.h \ + smb_idmap.h \ + smb_incl.h \ + smb_ioctl.h \ + smb_kproto.h \ + smb_privilege.h \ + smb_secdesc.h \ + smb_svc_sm.h \ + smb_token.h \ + smb_vops.h \ + smb_winpipe.h \ + smb_xdr.h \ + smbfmt.h \ + smbinfo.h \ + smbtrans.h \ + smbvar.h \ + string.h \ + svrapi.h \ + winioctl.h \ + winsvc.h \ + wintypes.h + +NDLHDRS= dssetup.ndl \ + eventlog.ndl \ + llsrpc.ndl \ + lsarpc.ndl \ + ndrtypes.ndl \ + netdfs.ndl \ + netlogon.ndl \ + rpcpdu.ndl \ + samrpc.ndl \ + spoolss.ndl \ + srvsvc.ndl \ + svcctl.ndl \ + winreg.ndl \ + +ROOTDIR= $(ROOT)/usr/include/smbsrv +NDLDIR= $(ROOTDIR)/ndl +ROOTHDRS= $(HDRS:%=$(ROOTDIR)/%) $(NDLHDRS:%=$(ROOTDIR)/ndl/%) +CHECKHDRS= $(HDRS:%.h=%.check) + +$(ROOTDIR)/%: % + $(INS.file) + +$(NDLDIR)/%: ndl/% + $(INS.file) + +$(ROOTDIR) $(NDLDIR): + $(INS.dir) + +.KEEP_STATE: + +.PARALLEL: $(CHECKHDRS) + +install_h: $(ROOTDIR) $(NDLDIR) $(ROOTHDRS) + +check: $(CHECKHDRS) diff --git a/usr/src/uts/common/smbsrv/alloc.h b/usr/src/uts/common/smbsrv/alloc.h new file mode 100644 index 000000000000..8d4925686386 --- /dev/null +++ b/usr/src/uts/common/smbsrv/alloc.h @@ -0,0 +1,89 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_ALLOC_H +#define _SMBSRV_ALLOC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Memory management macros to aid in developing code that can + * be compiled for both user and kernel. + * + * Set the AREA parameter to a short text string that is a hint + * about the subsystem calling the function. example: "smbrdr" + * + * Do not mix usage of these macros with malloc/free functions. + * It will not work. + * + * All library code shared between user and kernel must use + * these functions instead of malloc/free/kmem_*. + * + * Quick Summary + * MEM_MALLOC - allocate memory + * MEM_ZALLOC - allocate and zero memory + * MEM_STRDUP - string copy + * MEM_REALLOC - reallocate memory + * MEM_FREE - free memory + */ + +#include +#include + +#ifndef _KERNEL +#include +#include + +#define MEM_MALLOC(AREA, SIZE) malloc(SIZE) +#define MEM_ZALLOC(AREA, SIZE) calloc((SIZE), 1) +#define MEM_STRDUP(AREA, PTR) strdup(PTR) +#define MEM_REALLOC(AREA, PTR, SIZE) realloc((PTR), (SIZE)) +#define MEM_FREE(AREA, PTR) free(PTR) + +#else /* _KERNEL */ + +void *mem_malloc(uint32_t size); +void *mem_zalloc(uint32_t size); +char *mem_strdup(const char *ptr); +void *mem_realloc(void *ptr, uint32_t size); +void smb_mem_free(void *ptr); + +#define MEM_MALLOC(AREA, SIZE) mem_malloc(SIZE) +#define MEM_ZALLOC(AREA, SIZE) mem_zalloc(SIZE) +#define MEM_STRDUP(AREA, PTR) mem_strdup(PTR) +#define MEM_REALLOC(AREA, PTR, SIZE) mem_realloc((PTR), (SIZE)) +#define MEM_FREE(AREA, PTR) smb_mem_free(PTR) + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_ALLOC_H */ diff --git a/usr/src/uts/common/smbsrv/cifs.h b/usr/src/uts/common/smbsrv/cifs.h new file mode 100644 index 000000000000..4533a21bb66b --- /dev/null +++ b/usr/src/uts/common/smbsrv/cifs.h @@ -0,0 +1,1161 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_CIFS_H +#define _SMBSRV_CIFS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file provides definitions for the CIFS interface. The Macintosh + * Extensions for CIFS are defined in mac_cifs.h. + */ + +/* + * Macintosh Extensions for CIFS + */ +#include + +/* + * NT Installable File System (IFS) interface. + */ +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + + +/* + * SMB-over-TCP (NETBIOS-less SMB) TCP port + */ +#define SMB_SRVC_TCP_PORT 445 + + +/* Share types */ +#ifndef _SHARE_TYPES_DEFINED_ +#define _SHARE_TYPES_DEFINED_ +#define STYPE_DISKTREE 0x00000000 +#define STYPE_PRINTQ 0x00000001 +#define STYPE_DEVICE 0x00000002 +#define STYPE_IPC 0x00000003 +#define STYPE_MASK 0x0000000F +#define STYPE_DFS 0x00000064 +#define STYPE_HIDDEN 0x80000000 +#define STYPE_SPECIAL 0x80000000 +#endif /* _SHARE_TYPES_DEFINED_ */ + +#define STYPE_ISDSK(S) (((S) & STYPE_MASK) == STYPE_DISKTREE) +#define STYPE_ISPRN(S) (((S) & STYPE_MASK) == STYPE_PRINTQ) +#define STYPE_ISDEV(S) (((S) & STYPE_MASK) == STYPE_DEVICE) +#define STYPE_ISIPC(S) (((S) & STYPE_MASK) == STYPE_IPC) + +/* + * NtCreateAndX and NtTransactCreate creation flags: defined in CIFS + * section 4.2.2 + * + * Creation Flag Name Value Description + * ========================== ====== ================================== + * NT_CREATE_REQUEST_OPLOCK 0x02 Level I oplock requested + * NT_CREATE_REQUEST_OPBATCH 0x04 Batch oplock requested + * NT_CREATE_OPEN_TARGET_DIR 0x08 Target for open is a directory + */ +#define NT_CREATE_FLAG_REQUEST_OPLOCK 0x02 +#define NT_CREATE_FLAG_REQUEST_OPBATCH 0x04 +#define NT_CREATE_FLAG_OPEN_TARGET_DIR 0x08 + + +/* + * Define the filter flags for NtNotifyChangeDirectoryFile + */ +#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 +#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 +#define FILE_NOTIFY_CHANGE_NAME 0x00000003 +#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 +#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 +#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 +#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 +#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 +#define FILE_NOTIFY_CHANGE_EA 0x00000080 +#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 +#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 +#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 +#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 +#define FILE_NOTIFY_VALID_MASK 0x00000fff + + +/* + * Define the file action type codes for NtNotifyChangeDirectoryFile + */ +#define FILE_ACTION_ADDED 0x00000001 +#define FILE_ACTION_REMOVED 0x00000002 +#define FILE_ACTION_MODIFIED 0x00000003 +#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 +#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 +#define FILE_ACTION_ADDED_STREAM 0x00000006 +#define FILE_ACTION_REMOVED_STREAM 0x00000007 +#define FILE_ACTION_MODIFIED_STREAM 0x00000008 + + +/* Lock type flags */ +#define LOCKING_ANDX_NORMAL_LOCK 0x00 +#define LOCKING_ANDX_SHARED_LOCK 0x01 +#define LOCKING_ANDX_OPLOCK_RELEASE 0x02 +#define LOCKING_ANDX_CHANGE_LOCK_TYPE 0x04 +#define LOCKING_ANDX_CANCEL_LOCK 0x08 +#define LOCKING_ANDX_LARGE_FILES 0x10 + +#define SMB_COM_CREATE_DIRECTORY 0x00 +#define SMB_COM_DELETE_DIRECTORY 0x01 +#define SMB_COM_OPEN 0x02 +#define SMB_COM_CREATE 0x03 +#define SMB_COM_CLOSE 0x04 +#define SMB_COM_FLUSH 0x05 +#define SMB_COM_DELETE 0x06 +#define SMB_COM_RENAME 0x07 +#define SMB_COM_QUERY_INFORMATION 0x08 +#define SMB_COM_SET_INFORMATION 0x09 +#define SMB_COM_READ 0x0A +#define SMB_COM_WRITE 0x0B +#define SMB_COM_LOCK_BYTE_RANGE 0x0C +#define SMB_COM_UNLOCK_BYTE_RANGE 0x0D +#define SMB_COM_CREATE_TEMPORARY 0x0E +#define SMB_COM_CREATE_NEW 0x0F +#define SMB_COM_CHECK_DIRECTORY 0x10 +#define SMB_COM_PROCESS_EXIT 0x11 +#define SMB_COM_SEEK 0x12 +#define SMB_COM_LOCK_AND_READ 0x13 +#define SMB_COM_WRITE_AND_UNLOCK 0x14 + +#define SMB_COM_READ_RAW 0x1A +#define SMB_COM_READ_MPX 0x1B +#define SMB_COM_READ_MPX_SECONDARY 0x1C +#define SMB_COM_WRITE_RAW 0x1D +#define SMB_COM_WRITE_MPX 0x1E +#define SMB_COM_WRITE_MPX_SECONDARY 0x1F +#define SMB_COM_WRITE_COMPLETE 0x20 + +#define SMB_COM_SET_INFORMATION2 0x22 +#define SMB_COM_QUERY_INFORMATION2 0x23 +#define SMB_COM_LOCKING_ANDX 0x24 +#define SMB_COM_TRANSACTION 0x25 +#define SMB_COM_TRANSACTION_SECONDARY 0x26 +#define SMB_COM_IOCTL 0x27 +#define SMB_COM_IOCTL_SECONDARY 0x28 +#define SMB_COM_COPY 0x29 +#define SMB_COM_MOVE 0x2A +#define SMB_COM_ECHO 0x2B +#define SMB_COM_WRITE_AND_CLOSE 0x2C +#define SMB_COM_OPEN_ANDX 0x2D +#define SMB_COM_READ_ANDX 0x2E +#define SMB_COM_WRITE_ANDX 0x2F + +#define SMB_COM_CLOSE_AND_TREE_DISC 0x31 +#define SMB_COM_TRANSACTION2 0x32 +#define SMB_COM_TRANSACTION2_SECONDARY 0x33 +#define SMB_COM_FIND_CLOSE2 0x34 +#define SMB_COM_FIND_NOTIFY_CLOSE 0x35 + +#define SMB_COM_TREE_CONNECT 0x70 +#define SMB_COM_TREE_DISCONNECT 0x71 +#define SMB_COM_NEGOTIATE 0x72 +#define SMB_COM_SESSION_SETUP_ANDX 0x73 +#define SMB_COM_LOGOFF_ANDX 0x74 +#define SMB_COM_TREE_CONNECT_ANDX 0x75 + +#define SMB_COM_QUERY_INFORMATION_DISK 0x80 +#define SMB_COM_SEARCH 0x81 +#define SMB_COM_FIND 0x82 +#define SMB_COM_FIND_UNIQUE 0x83 +#define SMB_COM_FIND_CLOSE 0x84 + +#define SMB_COM_NT_TRANSACT 0xA0 +#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 +#define SMB_COM_NT_CREATE_ANDX 0xA2 +#define SMB_COM_NT_CANCEL 0xA4 + +#define SMB_COM_OPEN_PRINT_FILE 0xC0 +#define SMB_COM_WRITE_PRINT_FILE 0xC1 +#define SMB_COM_CLOSE_PRINT_FILE 0xC2 +#define SMB_COM_GET_PRINT_QUEUE 0xC3 + + +/* + * Flags field of the SMB header. The names in parenthesis represent + * alternative names for the flags. + * + * SMB_FLAGS_LOCK_AND_READ_OK If the server supports LockAndRead and + * (SMB_FLAGS_LOCKS_SUBDIALECT) WriteAndUnlock, it sets this bit in the + * Negotiate response. + * + * SMB_FLAGS_SEND_NO_ACK When on, the client guarantees that there + * (SMB_FLAGS_RCV_BUF_POSTED) is a receive buffer posted such that a + * "Send-No-Ack" can be used by the server + * to respond to the client's request. + * + * SMB_FLAGS_CASE_INSENSITIVE This is part of the Flags field of every + * SMB header. If this bit is set, then all + * pathnames in the SMB should be treated as + * case-insensitive. Otherwise pathnames are + * case-sensitive. + * + * SMB_FLAGS_CANONICALIZED_PATHS When on in SessionSetupX, this indicates + * that all paths sent to the server are + * already in OS/2 canonicalized format. + * + * OS/2 canonical format means that file/directory names are in upper case, + * are valid characters, . and .. have been removed and single backslashes + * are used as separators. + * + * SMB_FLAGS_OPLOCK When set in an open file request SMBs + * (Open, Create, OpenX, etc.) this bit + * indicates a request for an oplock on the + * file. When set in the response, this bit + * indicates that the oplock was granted. + * + * SMB_FLAGS_OPLOCK_NOTIFY_ANY When on, this bit indicates that the server + * should notify the client on any request + * that could cause the file to be changed. + * If not set, the server only notifies the + * client on other open requests on the file. + * This bit is only relevant when + * SMB_FLAGS_OPLOCK is set. + * + * SMB_FLAGS_SERVER_TO_REDIR This bit indicates that the SMB is being + * (SMB_FLAGS_REPLY) sent from server to (client) redirector. + */ +#define SMB_FLAGS_LOCK_AND_READ_OK 0x01 +#define SMB_FLAGS_SEND_NO_ACK 0x02 +#define SMB_FLAGS_RESERVED 0x04 +#define SMB_FLAGS_CASE_INSENSITIVE 0x08 +#define SMB_FLAGS_CANONICALIZED_PATHS 0x10 +#define SMB_FLAGS_OPLOCK 0x20 +#define SMB_FLAGS_OPLOCK_NOTIFY_ANY 0x40 +#define SMB_FLAGS_REPLY 0x80 + + +/* + * Flags2 field of the SMB header. + */ +#define SMB_FLAGS2_KNOWS_LONG_NAMES 0x0001 +#define SMB_FLAGS2_KNOWS_EAS 0x0002 +#define SMB_FLAGS2_SMB_SECURITY_SIGNATURE 0x0004 +#define SMB_FLAGS2_IS_LONG_NAME 0x0040 +#define SMB_FLAGS2_EXT_SEC 0x0800 +#define SMB_FLAGS2_DFS 0x1000 +#define SMB_FLAGS2_PAGING_IO 0x2000 +#define SMB_FLAGS2_NT_STATUS 0x4000 +#define SMB_FLAGS2_UNICODE 0x8000 + +#define DIALECT_UNKNOWN 0 +#define PC_NETWORK_PROGRAM_1_0 1 /* The original MSNET SMB protocol */ +#define PCLAN1_0 2 /* Some versions of the original MSNET */ +#define MICROSOFT_NETWORKS_1_03 3 /* This is used for the MS-NET 1.03 */ +#define MICROSOFT_NETWORKS_3_0 4 /* This is the DOS LANMAN 1.0 specific */ +#define LANMAN1_0 5 /* This is the first version of the full */ +#define LM1_2X002 6 /* This is the first version of the full */ +#define DOS_LM1_2X002 7 /* This is the dos equivalent of the */ +#define DOS_LANMAN2_1 8 /* DOS LANMAN2.1 */ +#define LANMAN2_1 9 /* OS/2 LANMAN2.1 */ +#define Windows_for_Workgroups_3_1a 10 /* Windows for Workgroups Version 1.0 */ +#define NT_LM_0_12 11 /* The SMB protocol designed for NT */ + +/* SMB_TREE_CONNECT_ANDX flags */ +#define SMB_TREE_SUPPORT_SEARCH_BITS 0x01 +#define SMB_TREE_SHARE_IS_IN_DFS 0x02 + +#define SMB_FA_READONLY 0x01 /* Read only file */ +#define SMB_FA_HIDDEN 0x02 /* Hidden file */ +#define SMB_FA_SYSTEM 0x04 /* System file */ +#define SMB_FA_VOLUME 0x08 /* Volume */ +#define SMB_FA_DIRECTORY 0x10 /* Directory */ +#define SMB_FA_ARCHIVE 0x20 /* Archive file */ +#define SMB_FA_NORMAL 0x80 /* Normal pipe */ +#define SMB_FA_TEMPORARY 0x100 /* Is a temporary file */ +#define SMB_FA_ATOMIC_WRITE 0x200 /* Do atomic writes */ +#define SMB_FA_XACTION_WRITE 0x400 /* XACTION write */ + +/* + * Mask to match the definitions in section 3.7 File Attribute Encoding. + * The other bits are reserved. + */ +#define SMB_FA_MASK 0x3F + + +/* + * The subcommand codes, placed in SETUP[0], for named pipe operations are: + * SubCommand Code Value Description + * =================== ===== ========================================= + */ + +#define CallNamedPipe 0x54 /* open/write/read/close pipe */ +#define WaitNamedPipe 0x53 /* wait for pipe to be nonbusy */ +#define PeekNmPipe 0x23 /* read but don't remove data */ +#define QNmPHandState 0x21 /* query pipe handle modes */ +#define SetNmPHandState 0x01 /* set pipe handle modes */ +#define QNmPipeInfo 0x22 /* query pipe attributes */ +#define TransactNmPipe 0x26 /* write/read operation on pipe */ +#define RawReadNmPipe 0x11 /* read pipe in "raw" (non message mode) */ +#define RawWriteNmPipe 0x31 /* write pipe "raw" (non message mode) */ + + + +/* + * Setup[0] Transaction2 Value Description + * Subcommand Code + * ========================== ===== ============================= + */ + +#define TRANS2_OPEN2 0x00 /* Create file, extended attributes */ +#define TRANS2_FIND_FIRST2 0x01 /* Begin search for files */ +#define TRANS2_FIND_NEXT2 0x02 /* Resume search for files */ +#define TRANS2_QUERY_FS_INFORMATION 0x03 /* Get file system information */ +#define _TRANS2_RESV_0x04 0x04 /* Reserved */ +#define TRANS2_QUERY_PATH_INFORMATION 0x05 /* Get info, named file or dir */ +#define TRANS2_SET_PATH_INFORMATION 0x06 /* Set info, named file or dir */ +#define TRANS2_QUERY_FILE_INFORMATION 0x07 /* Get info, handle */ +#define TRANS2_SET_FILE_INFORMATION 0x08 /* Set info, handle */ +#define TRANS2_FSCTL 0x09 /* Not implemented by NT server */ +#define TRANS2_IOCTL2 0x0A /* Not implemented by NT server */ +#define TRANS2_FIND_NOTIFY_FIRST 0x0B /* Not implemented by NT server */ +#define TRANS2_FIND_NOTIFY_NEXT 0x0C /* Not implemented by NT server */ +#define TRANS2_CREATE_DIRECTORY 0x0D /* Create dir, extended attributes */ +#define TRANS2_SESSION_SETUP 0x0E /* Session setup, extended security */ +#define TRANS2_GET_DFS_REFERRAL 0x10 /* Get a Dfs referral */ +#define TRANS2_REPORT_DFS_INCONSISTENCY 0x11 /* Report a Dfs inconsistency */ + +/* + * Access Mode Encoding (CIFS/1.0 1996 Section 3.8). + * + * The desired access mode passed in SmbOpen and SmbOpenAndX has the following + * mapping: + * + * 1111 11 + * 5432 1098 7654 3210 + * rWrC rLLL rSSS rAAA + * + * where: + * + * W - Write through mode. No read ahead or write behind allowed on + * this file or device. When protocol is returned, data is expected + * to be on the disk or device. + * + * S - Sharing mode: + * 0 - Compatibility mode (as in core open) + * 1 - Deny read/write/execute (exclusive) + * 2 - Deny write + * 3 - Deny read/execute + * 4 - Deny none + * + * A - Access mode + * 0 - Open for reading + * 1 - Open for writing + * 2 - Open for reading and writing + * 3 - Open for execute + * + * rSSSrAAA = 11111111 (hex FF) indicates FCB open (as in core protocol) + * + * C - Cache mode + * 0 - Normal file + * 1 - Do not cache this file + * + * L - Locality of reference + * 0 - Locality of reference is unknown + * 1 - Mainly sequential access + * 2 - Mainly random access + * 3 - Random access with some locality + * 4 to 7 - Currently undefined + */ + + +#define SMB_DA_SHARE_MASK 0x70 +#define SMB_DA_ACCESS_MASK 0x07 +#define SMB_DA_FCB_MASK (UCHAR)0xFF + +#define SMB_DA_ACCESS_READ 0x00 +#define SMB_DA_ACCESS_WRITE 0x01 +#define SMB_DA_ACCESS_READ_WRITE 0x02 +#define SMB_DA_ACCESS_EXECUTE 0x03 + +#define SMB_DA_SHARE_COMPATIBILITY 0x00 +#define SMB_DA_SHARE_EXCLUSIVE 0x10 +#define SMB_DA_SHARE_DENY_WRITE 0x20 +#define SMB_DA_SHARE_DENY_READ 0x30 +#define SMB_DA_SHARE_DENY_NONE 0x40 + +#define SMB_DA_FCB (UCHAR)0xFF + +#define SMB_CACHE_NORMAL 0x0000 +#define SMB_DO_NOT_CACHE 0x1000 + +#define SMB_LR_UNKNOWN 0x0000 +#define SMB_LR_SEQUENTIAL 0x0100 +#define SMB_LR_RANDOM 0x0200 +#define SMB_LR_RANDOM_WITH_LOCALITY 0x0300 +#define SMB_LR_MASK 0x0F00 + +#define SMB_DA_WRITE_THROUGH 0x4000 + +/* + * The SMB open function determines what action should be taken depending + * on the existence or lack thereof of files used in the operation. It + * has the following mapping: + * + * 1111 1 + * 5432 1098 7654 3210 + * rrrr rrrr rrrC rrOO + * + * where: + * + * O - Open (action to be taken if the target file exists) + * 0 - Fail + * 1 - Open or Append file + * 2 - Truncate file + * + * C - Create (action to be taken if the target file does not exist) + * 0 - Fail + * 1 - Create file + */ + +#define SMB_OFUN_OPEN_MASK 0x3 +#define SMB_OFUN_CREATE_MASK 0x10 + +#define SMB_OFUN_OPEN_FAIL 0 +#define SMB_OFUN_OPEN_APPEND 1 +#define SMB_OFUN_OPEN_OPEN 1 +#define SMB_OFUN_OPEN_TRUNCATE 2 + +#define SMB_OFUN_CREATE_FAIL 0x00 +#define SMB_OFUN_CREATE_CREATE 0x10 + +/* + * The Action field of OpenAndX has the following format: + * + * 1111 11 + * 5432 1098 7654 3210 + * Lrrr rrrr rrrr rrOO + * + * where: + * + * L - Opportunistic lock. 1 if lock granted, else 0. + * + * O - Open action: + * 1 - The file existed and was opened + * 2 - The file did not exist but was created + * 3 - The file existed and was truncated + */ + +#define SMB_OACT_LOCK 0x8000 +#define SMB_OACT_OPENED 0x01 +#define SMB_OACT_CREATED 0x02 +#define SMB_OACT_TRUNCATED 0x03 + +#define SMB_OACT_OPLOCK 0x8000 + +#define SMB_FTYPE_DISK 0 +#define SMB_FTYPE_BYTE_PIPE 1 +#define SMB_FTYPE_MESG_PIPE 2 +#define SMB_FTYPE_PRINTER 3 +#define SMB_FTYPE_UNKNOWN 0xFFFF + +#define SMB_DEVST_BLOCKING 0x8000 +#define SMB_DEVST_ENDPOINT 0x4000 +#define SMB_DEVST_TYPE_MASK 0x0C00 +#define SMB_DEVST_TYPE_BYTE_PIPE 0x0000 +#define SMB_DEVST_TYPE_MESG_PIPE 0x0400 +#define SMB_DEVST_RMODE_MASK 0x0300 +#define SMB_DEVST_RMODE_BYTES 0x0000 +#define SMB_DEVST_RMODE_MESGS 0x0100 +#define SMB_DEVST_ICOUNT_MASK 0x00FF /* not used */ + +#define SMB_FTYPE_IS_DISK(F) ((F) == SMB_FTYPE_DISK) +#define SMB_FTYPE_IS_PIPE(F) \ + (((F) == SMB_FTYPE_BYTE_PIPE) || ((F) == SMB_FTYPE_MESG_PIPE)) +#define SMB_FTYPE_IS_PRINTER(F) ((F) == SMB_FTYPE_PRINTER) + +/* + * TRANS2_FIND + */ +#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 +#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_FILE_NAMES_INFO 0x103 +#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 +#define SMB_MAC_FIND_BOTH_HFS_INFO MAC_FIND_BOTH_HFS_INFO + + +/* + * Flags for TRANS2_FIND_FIRST2 and TRANS2_FIND_NEXT2 + * (NTDDK cifs.h and smbtrans.h). + * + * If SMB_FIND_RETURN_RESUME_KEYS was set in the request parameters, + * each entry is preceded by a four-byte resume key. + */ +#define SMB_FIND_CLOSE_AFTER_REQUEST 0x01 +#define SMB_FIND_CLOSE_AT_EOS 0x02 +#define SMB_FIND_RETURN_RESUME_KEYS 0x04 +#define SMB_FIND_CONTINUE_FROM_LAST 0x08 +#define SMB_FIND_WITH_BACKUP_INTENT 0x10 + + +/* + * TRANS2_QUERY_FS_INFORMATION + */ +#define SMB_INFO_ALLOCATION 1 +#define SMB_INFO_VOLUME 2 +#define SMB_QUERY_FS_LABEL_INFO 0x101 +#define SMB_QUERY_FS_VOLUME_INFO 0x102 +#define SMB_QUERY_FS_SIZE_INFO 0x103 +#define SMB_QUERY_FS_DEVICE_INFO 0x104 +#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 +#define SMB_QUERY_FS_QUOTA_INFO 0x106 /* unused? */ +#define SMB_QUERY_FS_CONTROL_INFO 0x107 +#define SMB_MAC_QUERY_FS_INFO MAC_QUERY_FS_INFO + + +/* TRANS2_QUERY_{PATH,FILE}_INFORMATION */ + +#define SMB_INFO_STANDARD 1 +#define SMB_INFO_QUERY_EA_SIZE 2 +#define SMB_INFO_SET_EAS 2 +#define SMB_INFO_QUERY_EAS_FROM_LIST 3 +#define SMB_INFO_QUERY_ALL_EAS 4 +#define SMB_INFO_QUERY_FULL_NAME 5 +#define SMB_INFO_IS_NAME_VALID 6 +#define SMB_INFO_PASSTHROUGH 1000 + +#define SMB_QUERY_FILE_BASIC_INFO 0x101 +#define SMB_QUERY_FILE_STANDARD_INFO 0x102 +#define SMB_QUERY_FILE_EA_INFO 0x103 +#define SMB_QUERY_FILE_NAME_INFO 0x104 +#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105 +#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106 +#define SMB_QUERY_FILE_ALL_INFO 0x107 +#define SMB_QUERY_FILE_ALT_NAME_INFO 0x108 +#define SMB_QUERY_FILE_STREAM_INFO 0x109 +#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10B +#define SMB_MAC_SET_FINDER_INFO MAC_SET_FINDER_INFO +#define SMB_MAC_DT_ADD_APPL MAC_DT_ADD_APPL +#define SMB_MAC_DT_REMOVE_APPL MAC_DT_REMOVE_APPL +#define SMB_MAC_DT_GET_APPL MAC_DT_GET_APPL +#define SMB_MAC_DT_GET_ICON MAC_DT_GET_ICON +#define SMB_MAC_DT_GET_ICON_INFO MAC_DT_GET_ICON_INFO +#define SMB_MAC_DT_ADD_ICON MAC_DT_ADD_ICON + +#define SMB_SET_FILE_BASIC_INFO 0x101 +#define SMB_SET_FILE_DISPOSITION_INFO 0x102 +#define SMB_SET_FILE_ALLOCATION_INFO 0x103 +#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 + + +/* + * The following bits may be set in the SecurityMode field of the + * SMB_COM_NEGOTIATE response. + * + * Notes: + * NEGOTIATE_SECURITY_SHARE_LEVEL is a montana2 invention. + * + * The NTDDK cifs.h definitions are: + * #define NEGOTIATE_USER_SECURITY 0x01 + * #define NEGOTIATE_ENCRYPT_PASSWORDS 0x02 + * #define NEGOTIATE_SECURITY_SIGNATURES_ENABLED 0x04 + * #define NEGOTIATE_SECURITY_SIGNATURES_REQUIRED 0x08 + */ +#define NEGOTIATE_SECURITY_SHARE_LEVEL 0x00 +#define NEGOTIATE_SECURITY_USER_LEVEL 0x01 +#define NEGOTIATE_SECURITY_CHALLENGE_RESPONSE 0x02 +#define NEGOTIATE_SECURITY_SIGNATURES_ENABLED 0x04 +#define NEGOTIATE_SECURITY_SIGNATURES_REQUIRED 0x08 + + +/* + * Negotiated Capabilities (CIFS/1.0 section 4.1.1) + * + * Capabilities allow the server to tell the client what it supports. + * Undefined bits MUST be set to zero by servers, and MUST be ignored + * by clients. The bit definitions are: + * + * Capability Name Encoding Meaning + * ==================== ======== ================================== + * CAP_RAW_MODE 0x0001 The server supports SMB_COM_READ_RAW and + * SMB_COM_WRITE_RAW (obsolescent) + * CAP_MPX_MODE 0x0002 The server supports SMB_COM_READ_MPX and + * SMB_COM_WRITE_MPX (obsolescent) + * CAP_UNICODE 0x0004 The server supports Unicode strings + * CAP_LARGE_FILES 0x0008 The server supports large files with 64 + * bit offsets + * CAP_NT_SMBS 0x0010 The server supports the SMBs particular + * to the NT LM 0.12 dialect. + * Implies CAP_NT_FIND. + * CAP_RPC_REMOTE_APIS 0x0020 The server supports remote admin API + * requests via DCE RPC + * CAP_STATUS32 0x0040 The server can respond with 32 bit + * status codes in Status.Status + * CAP_LEVEL_II_OPLOCKS 0x0080 The server supports level 2 oplocks + * CAP_LOCK_AND_READ 0x0100 The server supports the + * SMB_COM_LOCK_AND_READ SMB + * CAP_NT_FIND 0x0200 + * CAP_BULK_TRANSFER 0x0400 + * CAP_COMPRESSED_BULK 0x0800 + * CAP_DFS 0x1000 The server is DFS aware + * CAP_LARGE_READX 0x4000 The server supports large + * SMB_COM_READ_ANDX + * CAP_LARGE_WRITEX 0x8000 The server supports large + * SMB_COM_WRITE_ANDX + * CAP_RESERVED 0x02000000 Reserved for future use. + * CAP_EXTENDED_SECURITY 0x80000000 The server supports extended security + * exchanges. + * + * Extended security exchanges provides a means of supporting arbitrary + * authentication protocols within CIFS. Security blobs are opaque to the + * CIFS protocol; they are messages in some authentication protocol that + * has been agreed upon by client and server by some out of band mechanism, + * for which CIFS merely functions as a transport. When + * CAP_EXTENDED_SECURITY is negotiated, the server includes a first + * security blob in its response; subsequent security blobs are exchanged + * in SMB_COM_SESSION_SETUP_ANDX requests and responses until the + * authentication protocol terminates. + */ +#define CAP_RAW_MODE 0x0001 +#define CAP_MPX_MODE 0x0002 +#define CAP_UNICODE 0x0004 +#define CAP_LARGE_FILES 0x0008 +#define CAP_NT_SMBS 0x0010 +#define CAP_RPC_REMOTE_APIS 0x0020 +#define CAP_STATUS32 0x0040 +#define CAP_LEVEL_II_OPLOCKS 0x0080 +#define CAP_LOCK_AND_READ 0x0100 +#define CAP_NT_FIND 0x0200 +#define CAP_BULK_TRANSFER 0x0400 +#define CAP_COMPRESSED_BULK 0x0800 +#define CAP_DFS 0x1000 +#define CAP_LARGE_READX 0x4000 +#define CAP_LARGE_WRITEX 0x8000 +#define CAP_RESERVED 0x02000000 +#define CAP_EXTENDED_SECURITY 0x80000000 + + +/* + * Different device types according to NT + */ +#define FILE_DEVICE_BEEP 0x00000001 +#define FILE_DEVICE_CD_ROM 0x00000002 +#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 +#define FILE_DEVICE_CONTROLLER 0x00000004 +#define FILE_DEVICE_DATALINK 0x00000005 +#define FILE_DEVICE_DFS 0x00000006 +#define FILE_DEVICE_DISK 0x00000007 +#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 +#define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#define FILE_DEVICE_INPORT_PORT 0x0000000a +#define FILE_DEVICE_KEYBOARD 0x0000000b +#define FILE_DEVICE_MAILSLOT 0x0000000c +#define FILE_DEVICE_MIDI_IN 0x0000000d +#define FILE_DEVICE_MIDI_OUT 0x0000000e +#define FILE_DEVICE_MOUSE 0x0000000f +#define FILE_DEVICE_MULTI_UNC_PROVIDER 0x00000010 +#define FILE_DEVICE_NAMED_PIPE 0x00000011 +#define FILE_DEVICE_NETWORK 0x00000012 +#define FILE_DEVICE_NETWORK_BROWSER 0x00000013 +#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 +#define FILE_DEVICE_NULL 0x00000015 +#define FILE_DEVICE_PARALLEL_PORT 0x00000016 +#define FILE_DEVICE_PHYSICAL_NETCARD 0x00000017 +#define FILE_DEVICE_PRINTER 0x00000018 +#define FILE_DEVICE_SCANNER 0x00000019 +#define FILE_DEVICE_SERIAL_MOUSE_PORT 0x0000001a +#define FILE_DEVICE_SERIAL_PORT 0x0000001b +#define FILE_DEVICE_SCREEN 0x0000001c +#define FILE_DEVICE_SOUND 0x0000001d +#define FILE_DEVICE_STREAMS 0x0000001e +#define FILE_DEVICE_TAPE 0x0000001f +#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 +#define FILE_DEVICE_TRANSPORT 0x00000021 +#define FILE_DEVICE_UNKNOWN 0x00000022 +#define FILE_DEVICE_VIDEO 0x00000023 +#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 +#define FILE_DEVICE_WAVE_IN 0x00000025 +#define FILE_DEVICE_WAVE_OUT 0x00000026 +#define FILE_DEVICE_8042_PORT 0x00000027 +#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 +#define FILE_DEVICE_BATTERY 0x00000029 +#define FILE_DEVICE_BUS_EXTENDER 0x0000002a +#define FILE_DEVICE_MODEM 0x0000002b +#define FILE_DEVICE_VDM 0x0000002c + +/* + * Some of these device types are not currently accessible over the network + * and may never be accessible over the network. Some may change to be + * + * accessible over the network. The values for device types that may never + * be accessible over the network may be redefined to be just reserved at + * some date in the future. + * + * Characteristics is the sum of any of the following: + */ + +#define FILE_REMOVABLE_MEDIA 0x00000001 +#define FILE_READ_ONLY_DEVICE 0x00000002 +#define FILE_FLOPPY_DISKETTE 0x00000004 +#define FILE_WRITE_ONE_MEDIA 0x00000008 +#define FILE_REMOTE_DEVICE 0x00000010 +#define FILE_DEVICE_IS_MOUNTED 0x00000020 +#define FILE_VIRTUAL_VOLUME 0x00000040 + +/* + * CREATE_ANDX ShareAccess Flags + */ + +#define FILE_SHARE_NONE 0x00000000 +#define FILE_SHARE_READ 0x00000001 +#define FILE_SHARE_WRITE 0x00000002 +#define FILE_SHARE_DELETE 0x00000004 +#define FILE_SHARE_ALL 0x00000007 +#define FILE_SHARE_VALID_FLAGS 0x00000007 + + +/* + * CREATE_ANDX CreateDisposition flags + * + * FILE_SUPERSEDE If the file already exists it should be superseded + * by the specified file. If the file does not already + * exist then it should be created. + * + * FILE_CREATE If the file already exists the operation should fail. + * If the file does not already exist then it should be + * created. (aka CREATE_NEW) + * + * FILE_OPEN If the file already exists then it should be opened. + * If the file does not already exist then the operation + * should fail. (aka OPEN_EXISTING) + * + * FILE_OPEN_IF If the file already exists then it should be opened. + * If the file does not already exist then it should be + * created. (aka OPEN_ALWAYS) + * + * FILE_OVERWRITE If the file already exists, it should be opened and + * overwritten. If the file does not already exist then + * the operation should fail. (aka TRUNCATE_EXISTING) + * + * FILE_OVERWRITE_IF If the file already exists, it should be opened and + * overwritten. If the file does not already exist then + * it should be created. (aka CREATE_ALWAYS) + */ +#define FILE_SUPERSEDE 0x00000000 +#define FILE_OPEN 0x00000001 +#define FILE_CREATE 0x00000002 +#define FILE_OPEN_IF 0x00000003 +#define FILE_OVERWRITE 0x00000004 +#define FILE_OVERWRITE_IF 0x00000005 +#define FILE_MAXIMUM_DISPOSITION 0x00000005 + +/* + * CREATE_ANDX Impersonation levels + */ + +#define SECURITY_ANONYMOUS 0x00000001 +#define SECURITY_IDENTIFICATION 0x00000002 +#define SECURITY_IMPERSONATION 0x00000003 +#define SECURITY_DELEGATION 0x00000004 + +/* + * CREATE_ANDX SecurityFlags + */ + +#define SECURITY_CONTEXT_TRACKING 0x00000001 +#define SECURITY_EFFECTIVE_ONLY 0x00000002 + +/* + * Server types + */ +#define SV_WORKSTATION 0x00000001 /* All workstations */ +#define SV_SERVER 0x00000002 /* All servers */ +#define SV_SQLSERVER 0x00000004 /* running with SQL server */ +#define SV_DOMAIN_CTRL 0x00000008 /* Primary domain controller */ +#define SV_DOMAIN_BAKCTRL 0x00000010 /* Backup domain controller */ +#define SV_TIME_SOURCE 0x00000020 /* running timesource service */ +#define SV_AFP 0x00000040 /* Apple File Protocol */ +#define SV_NOVELL 0x00000080 /* Novell servers */ +#define SV_DOMAIN_MEMBER 0x00000100 /* Domain Member */ +#define SV_PRINTQ_SERVER 0x00000200 /* Server sharing print queue */ +#define SV_DIALIN_SERVER 0x00000400 /* Server running dialin */ +#define SV_XENIX_SERVER 0x00000800 /* Xenix server */ +#define SV_NT 0x00001000 /* NT server */ +#define SV_WFW 0x00002000 /* Server running Windows for */ +#define SV_SERVER_NT 0x00008000 /* Windows NT non DC server */ +#define SV_POTENTIAL_BROWSER 0x00010000 /* can run browser service */ +#define SV_BACKUP_BROWSER 0x00020000 /* Backup browser server */ +#define SV_MASTER_BROWSER 0x00040000 /* Master browser server */ +#define SV_DOMAIN_MASTER 0x00080000 /* Domain Master Browser */ +#define SV_OSF 0x00100000 /* OSF operating system */ +#define SV_VMS 0x00200000 /* VMS operating system */ +#define SV_WINDOWS_95_PLUS 0x00400000 /* Windows 95 or better */ + +#define SV_LOCAL_LIST_ONLY 0x40000000 /* Enumerate only "local" */ +#define SV_TYPE_DOMAIN_ENUM 0x80000000 /* Enumerate Domains */ + +#define MY_SERVER_TYPE (SV_SERVER | SV_NT | SV_SERVER_NT | SV_DOMAIN_MEMBER) + + +#define PRQ_ACTIVE 0 /* Active */ +#define PRQ_PAUSE 1 /* Paused */ +#define PRQ_ERROR 2 /* Error Occurred */ +#define PRQ_PENDING 3 /* Deletion pending */ + +#define PRJ_QS_QUEUED 0 /* Active */ +#define PRJ_QS_PAUSED 1 /* Paused */ +#define PRJ_QS_SPOOLING 2 /* Paused */ +#define PRJ_QS_PRINTING 3 /* Paused */ + + +#define SHARE_ACCESS_READ 0x01 /* read & execute from resource */ +#define SHARE_ACCESS_WRITE 0x02 /* write data to resource */ +#define SHARE_ACCESS_CREATE 0x04 /* create an instance of */ +#define SHARE_ACCESS_EXEC 0x08 /* execute from resource */ +#define SHARE_ACCESS_DELETE 0x10 /* Permission to delete the resource */ +#define SHARE_ACCESS_ATTRIB 0x20 /* Permission to modify the resource */ +#define SHARE_ACCESS_PERM 0x40 /* Permission to change permissions */ +#define SHARE_ACCESS_ALL 0x7F /* All of the above permissions */ + + +/* + * SMB_COM_NT_TRANSACTION sub-command codes (CIFS/1.0 section 5.3) + * + * SubCommand Code Value Description + * =============================== ===== ================================= + * NT_TRANSACT_CREATE 1 File open/create + * NT_TRANSACT_IOCTL 2 Device IOCTL + * NT_TRANSACT_SET_SECURITY_DESC 3 Set security descriptor + * NT_TRANSACT_NOTIFY_CHANGE 4 Start directory watch + * NT_TRANSACT_RENAME 5 Reserved (handle-based rename) + * NT_TRANSACT_QUERY_SECURITY_DESC 6 Retrieve security descriptor + * NT_TRANSACT_QUERY_QUOTA 7 Retrieve quota information + * NT_TRANSACT_SET_QUOTA 8 Set quota information + */ +#define NT_TRANSACT_MIN_FUNCTION 1 + +#define NT_TRANSACT_CREATE 1 +#define NT_TRANSACT_IOCTL 2 +#define NT_TRANSACT_SET_SECURITY_DESC 3 +#define NT_TRANSACT_NOTIFY_CHANGE 4 +#define NT_TRANSACT_RENAME 5 +#define NT_TRANSACT_QUERY_SECURITY_DESC 6 +#define NT_TRANSACT_QUERY_QUOTA 7 +#define NT_TRANSACT_SET_QUOTA 8 + +#define NT_TRANSACT_MAX_FUNCTION 8 + + +/* + * Pipe states + */ +#define SMB_PIPE_READMODE_BYTE 0x0000 +#define SMB_PIPE_READMODE_MESSAGE 0x0100 +#define SMB_PIPE_TYPE_BYTE 0x0000 +#define SMB_PIPE_TYPE_MESSAGE 0x0400 +#define SMB_PIPE_END_CLIENT 0x0000 +#define SMB_PIPE_END_SERVER 0x4000 +#define SMB_PIPE_WAIT 0x0000 +#define SMB_PIPE_NOWAIT 0x8000 +#define SMB_PIPE_UNLIMITED_INSTANCES 0x00FF + +/* + * smb_com_seek request + */ +#define SMB_SEEK_SET 0 /* set file offset to specified offset */ +#define SMB_SEEK_CUR 1 /* set file offset to current plus specified offset */ +#define SMB_SEEK_END 2 /* set file offset to EOF plus specified offset */ + +/* + * API Numbers for Transact based RAP (Remote Administration Protocol) calls + */ +#define API_WshareEnum 0 +#define API_WshareGetInfo 1 +#define API_WshareSetInfo 2 +#define API_WshareAdd 3 +#define API_WshareDel 4 +#define API_NetShareCheck 5 +#define API_WsessionEnum 6 +#define API_WsessionGetInfo 7 +#define API_WsessionDel 8 +#define API_WconnectionEnum 9 +#define API_WfileEnum 10 +#define API_WfileGetInfo 11 +#define API_WfileClose 12 +#define API_WserverGetInfo 13 +#define API_WserverSetInfo 14 +#define API_WserverDiskEnum 15 +#define API_WserverAdminCommand 16 +#define API_NetAuditOpen 17 +#define API_WauditClear 18 +#define API_NetErrorLogOpen 19 +#define API_WerrorLogClear 20 +#define API_NetCharDevEnum 21 +#define API_NetCharDevGetInfo 22 +#define API_WCharDevControl 23 +#define API_NetCharDevQEnum 24 +#define API_NetCharDevQGetInfo 25 +#define API_WCharDevQSetInfo 26 +#define API_WCharDevQPurge 27 +#define API_WCharDevQPurgeSelf 28 +#define API_WMessageNameEnum 29 +#define API_WMessageNameGetInfo 30 +#define API_WMessageNameAdd 31 +#define API_WMessageNameDel 32 +#define API_WMessageNameFwd 33 +#define API_WMessageNameUnFwd 34 +#define API_WMessageBufferSend 35 +#define API_WMessageFileSend 36 +#define API_WMessageLogFileSet 37 +#define API_WMessageLogFileGet 38 +#define API_WServiceEnum 39 +#define API_WServiceInstall 40 +#define API_WServiceControl 41 +#define API_WAccessEnum 42 +#define API_WAccessGetInfo 43 +#define API_WAccessSetInfo 44 +#define API_WAccessAdd 45 +#define API_WAccessDel 46 +#define API_WGroupEnum 47 +#define API_WGroupAdd 48 +#define API_WGroupDel 49 +#define API_WGroupAddUser 50 +#define API_WGroupDelUser 51 +#define API_WGroupGetUsers 52 +#define API_WUserEnum 53 +#define API_WUserAdd 54 +#define API_WUserDel 55 +#define API_WUserGetInfo 56 +#define API_WUserSetInfo 57 +#define API_WUserPasswordSet 58 +#define API_WUserGetGroups 59 +#define API_DeadTableEntry 60 +#define API_WWkstaSetUID 62 +#define API_WWkstaGetInfo 63 +#define API_WWkstaSetInfo 64 +#define API_WUseEnum 65 +#define API_WUseAdd 66 +#define API_WUseDel 67 +#define API_WUseGetInfo 68 +#define API_WPrintQEnum 69 +#define API_WPrintQGetInfo 70 +#define API_WPrintQSetInfo 71 +#define API_WPrintQAdd 72 +#define API_WPrintQDel 73 +#define API_WPrintQPause 74 +#define API_WPrintQContinue 75 +#define API_WPrintJobEnum 76 +#define API_WPrintJobGetInfo 77 +#define API_WPrintJobSetInfo_OLD 78 +#define API_WPrintJobDel 81 +#define API_WPrintJobPause 82 +#define API_WPrintJobContinue 83 +#define API_WPrintDestEnum 84 +#define API_WPrintDestGetInfo 85 +#define API_WPrintDestControl 86 +#define API_WProfileSave 87 +#define API_WProfileLoad 88 +#define API_WStatisticsGet 89 +#define API_WStatisticsClear 90 +#define API_NetRemoteTOD 91 +#define API_WNetBiosEnum 92 +#define API_WNetBiosGetInfo 93 +#define API_NetServerEnum 94 +#define API_I_NetServerEnum 95 +#define API_WServiceGetInfo 96 +#define API_WPrintQPurge 103 +#define API_NetServerEnum2 104 +#define API_WAccessGetUserPerms 105 +#define API_WGroupGetInfo 106 +#define API_WGroupSetInfo 107 +#define API_WGroupSetUsers 108 +#define API_WUserSetGroups 109 +#define API_WUserModalsGet 110 +#define API_WUserModalsSet 111 +#define API_WFileEnum2 112 +#define API_WUserAdd2 113 +#define API_WUserSetInfo2 114 +#define API_WUserPasswordSet2 115 +#define API_I_NetServerEnum2 116 +#define API_WConfigGet2 117 +#define API_WConfigGetAll2 118 +#define API_WGetDCName 119 +#define API_NetHandleGetInfo 120 +#define API_NetHandleSetInfo 121 +#define API_WStatisticsGet2 122 +#define API_WBuildGetInfo 123 +#define API_WFileGetInfo2 124 +#define API_WFileClose2 125 +#define API_WNetServerReqChallenge 126 +#define API_WNetServerAuthenticate 127 +#define API_WNetServerPasswordSet 128 +#define API_WNetAccountDeltas 129 +#define API_WNetAccountSync 130 +#define API_WUserEnum2 131 +#define API_WWkstaUserLogon 132 +#define API_WWkstaUserLogoff 133 +#define API_WLogonEnum 134 +#define API_WErrorLogRead 135 +#define API_WI_NetPathType 136 +#define API_WI_NetPathCanonicalize 137 +#define API_WI_NetPathCompare 138 +#define API_WI_NetNameValidate 139 +#define API_WI_NetNameCanonicalize 140 +#define API_WI_NetNameCompare 141 +#define API_WAuditRead 142 +#define API_WPrintDestAdd 143 +#define API_WPrintDestSetInfo 144 +#define API_WPrintDestDel 145 +#define API_WUserValidate2 146 +#define API_WPrintJobSetInfo 147 +#define API_TI_NetServerDiskEnum 148 +#define API_TI_NetServerDiskGetInfo 149 +#define API_TI_FTVerifyMirror 150 +#define API_TI_FTAbortVerify 151 +#define API_TI_FTGetInfo 152 +#define API_TI_FTSetInfo 153 +#define API_TI_FTLockDisk 154 +#define API_TI_FTFixError 155 +#define API_TI_FTAbortFix 156 +#define API_TI_FTDiagnoseError 157 +#define API_TI_FTGetDriveStats 158 +#define API_TI_FTErrorGetInfo 160 +#define API_NetAccessCheck 163 +#define API_NetAlertRaise 164 +#define API_NetAlertStart 165 +#define API_NetAlertStop 166 +#define API_NetAuditWrite 167 +#define API_NetIRemoteAPI 168 +#define API_NetServiceStatus 169 +#define API_I_NetServerRegister 170 +#define API_I_NetServerDeregister 171 +#define API_I_NetSessionEntryMake 172 +#define API_I_NetSessionEntryClear 173 +#define API_I_NetSessionEntryGetInfo 174 +#define API_I_NetSessionEntrySetInfo 175 +#define API_I_NetConnectionEntryMake 176 +#define API_I_NetConnectionEntryClear 177 +#define API_I_NetConnectionEntrySetInfo 178 +#define API_I_NetConnectionEntryGetInfo 179 +#define API_I_NetFileEntryMake 180 +#define API_I_NetFileEntryClear 181 +#define API_I_NetFileEntrySetInfo 182 +#define API_I_NetFileEntryGetInfo 183 +#define API_AltSrvMessageBufferSend 184 +#define API_AltSrvMessageFileSend 185 +#define API_wI_NetRplWkstaEnum 186 +#define API_wI_NetRplWkstaGetInfo 187 +#define API_wI_NetRplWkstaSetInfo 188 +#define API_wI_NetRplWkstaAdd 189 +#define API_wI_NetRplWkstaDel 190 +#define API_wI_NetRplProfileEnum 191 +#define API_wI_NetRplProfileGetInfo 192 +#define API_wI_NetRplProfileSetInfo 193 +#define API_wI_NetRplProfileAdd 194 +#define API_wI_NetRplProfileDel 195 +#define API_wI_NetRplProfileClone 196 +#define API_wI_NetRplBaseProfileEnum 197 +#define API_WIServerSetInfo 201 +#define API_WPrintDriverEnum 205 +#define API_WPrintQProcessorEnum 206 +#define API_WPrintPortEnum 207 +#define API_WNetWriteUpdateLog 208 +#define API_WNetAccountUpdate 209 +#define API_WNetAccountConfirmUpdate 210 +#define API_WConfigSet 211 +#define API_WAccountsReplicate 212 +#define API_SamOEMChgPasswordUser2_P 214 +#define API_NetServerEnum3 215 +#define API_WprintDriverGetInfo 250 +#define API_WprintDriverSetInfo 251 +#define API_WaliasAdd 252 +#define API_WaliasDel 253 +#define API_WaliasGetInfo 254 +#define API_WaliasSetInfo 255 +#define API_WaliasEnum 256 +#define API_WuserGetLogonAsn 257 +#define API_WuserSetLogonAsn 258 +#define API_WuserGetAppSel 259 +#define API_WuserSetAppSel 260 +#define API_WappAdd 261 +#define API_WappDel 262 +#define API_WappGetInfo 263 +#define API_WappSetInfo 264 +#define API_WappEnum 265 +#define API_WUserDCDBInit 266 +#define API_WDASDAdd 267 +#define API_WDASDDel 268 +#define API_WDASDGetInfo 269 +#define API_WDASDSetInfo 270 +#define API_WDASDEnum 271 +#define API_WDASDCheck 272 +#define API_WDASDCtl 273 +#define API_WuserRemoteLogonCheck 274 +#define API_WUserPasswordSet3 275 +#define API_WCreateRIPLMachine 276 +#define API_WDeleteRIPLMachine 277 +#define API_WGetRIPLMachineInfo 278 +#define API_WSetRIPLMachineInfo 279 +#define API_WEnumRIPLMachine 280 +#define API_WI_ShareAdd 281 +#define API_WI_AliasEnum 282 +#define API_WaccessApply 283 +#define API_WPrt16Query 284 +#define API_WPrt16Set 285 +#define API_WUserDel100 286 +#define API_WUserRemoteLogonCheck2 287 +#define API_WRemoteTODSet 294 +#define API_WprintJobMoveAll 295 +#define API_W16AppParmAdd 296 +#define API_W16AppParmDel 297 +#define API_W16AppParmGet 298 +#define API_W16AppParmSet 299 +#define API_W16RIPLMachineCreate 300 +#define API_W16RIPLMachineGetInfo 301 +#define API_W16RIPLMachineSetInfo 302 +#define API_W16RIPLMachineEnum 303 +#define API_W16RIPLMachineListParmEnum 304 +#define API_W16RIPLMachClassGetInfo 305 +#define API_W16RIPLMachClassEnum 306 +#define API_W16RIPLMachClassCreate 307 +#define API_W16RIPLMachClassSetInfo 308 +#define API_W16RIPLMachClassDelete 309 +#define API_W16RIPLMachClassLPEnum 310 +#define API_W16RIPLMachineDelete 311 +#define API_W16WSLevelGetInfo 312 +#define API_WserverNameAdd 313 +#define API_WserverNameDel 314 +#define API_WserverNameEnum 315 +#define API_I_WDASDEnum 316 +#define API_I_WDASDEnumTerminate 317 +#define API_I_WDASDSetInfo2 318 +#define MAX_RAP_API 318 + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_CIFS_H */ diff --git a/usr/src/uts/common/smbsrv/codepage.h b/usr/src/uts/common/smbsrv/codepage.h new file mode 100644 index 000000000000..baefb9de22c6 --- /dev/null +++ b/usr/src/uts/common/smbsrv/codepage.h @@ -0,0 +1,85 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_CODEPAGE_H +#define _SMBSRV_CODEPAGE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Labels to define whether a code page table entry is an uppercase + * character, a lowercase character or neither. One of these values + * should appear in the ctype field of the code page tables. + */ +#define CODEPAGE_ISNONE 0x00 +#define CODEPAGE_ISUPPER 0x01 +#define CODEPAGE_ISLOWER 0x02 + +/* + * The structure of a code page entry. Each code page table will + * consist of an array of 256 codepage entries. + * + * ctype indicates case of the value. + * upper indicates the uppercase equivalent value. + * lower indicates the lowercase equivalent value. + */ +typedef struct codepage { + unsigned char ctype; + mts_wchar_t upper; + mts_wchar_t lower; +} codepage_t; + +/* + * Global pointer to the current code page. This is + * defaulted to a standard ASCII table. + */ +extern codepage_t usascii_codepage[]; + +/* + * This buffer is used to store the language string for display. + */ +#define CODEPAGE_BUFSIZ 48 + +extern int oem_language_set(char *language); +extern unsigned int oem_get_smb_cpid(void); +extern unsigned int oem_get_telnet_cpid(void); + +extern int codepage_isupper(int c); +extern int codepage_islower(int c); +extern int codepage_toupper(int c); +extern int codepage_tolower(int c); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_CODEPAGE_H */ diff --git a/usr/src/uts/common/smbsrv/cp_cyrillic.h b/usr/src/uts/common/smbsrv/cp_cyrillic.h new file mode 100644 index 000000000000..d3b2a275964d --- /dev/null +++ b/usr/src/uts/common/smbsrv/cp_cyrillic.h @@ -0,0 +1,312 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_CP_CYRILLIC_H +#define _SMBSRV_CP_CYRILLIC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file specifies a codepage mapping for a given character set as + * specified below: + * + * This is the codepage for Cyrillic Character Set + * This codepage defines values for the special + * characters needed for the written alphabets of the + * following languages: Bulgarian, Byelorussian, + * Macedonian, Russian, Serbian, and pre-1990 Ukrainian + * The cyrillic character set is also known as iso-8859-5 + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +codepage_t cyrillic_codepage[256] = { + { CODEPAGE_ISNONE, 0x0000, 0x0000 }, /* 0x0000 */ + { CODEPAGE_ISNONE, 0x0001, 0x0001 }, /* 0x0001 */ + { CODEPAGE_ISNONE, 0x0002, 0x0002 }, /* 0x0002 */ + { CODEPAGE_ISNONE, 0x0003, 0x0003 }, /* 0x0003 */ + { CODEPAGE_ISNONE, 0x0004, 0x0004 }, /* 0x0004 */ + { CODEPAGE_ISNONE, 0x0005, 0x0005 }, /* 0x0005 */ + { CODEPAGE_ISNONE, 0x0006, 0x0006 }, /* 0x0006 */ + { CODEPAGE_ISNONE, 0x0007, 0x0007 }, /* 0x0007 */ + { CODEPAGE_ISNONE, 0x0008, 0x0008 }, /* 0x0008 */ + { CODEPAGE_ISNONE, 0x0009, 0x0009 }, /* 0x0009 */ + { CODEPAGE_ISNONE, 0x000a, 0x000a }, /* 0x000a */ + { CODEPAGE_ISNONE, 0x000b, 0x000b }, /* 0x000b */ + { CODEPAGE_ISNONE, 0x000c, 0x000c }, /* 0x000c */ + { CODEPAGE_ISNONE, 0x000d, 0x000d }, /* 0x000d */ + { CODEPAGE_ISNONE, 0x000e, 0x000e }, /* 0x000e */ + { CODEPAGE_ISNONE, 0x000f, 0x000f }, /* 0x000f */ + { CODEPAGE_ISNONE, 0x0010, 0x0010 }, /* 0x0010 */ + { CODEPAGE_ISNONE, 0x0011, 0x0011 }, /* 0x0011 */ + { CODEPAGE_ISNONE, 0x0012, 0x0012 }, /* 0x0012 */ + { CODEPAGE_ISNONE, 0x0013, 0x0013 }, /* 0x0013 */ + { CODEPAGE_ISNONE, 0x0014, 0x0014 }, /* 0x0014 */ + { CODEPAGE_ISNONE, 0x0015, 0x0015 }, /* 0x0015 */ + { CODEPAGE_ISNONE, 0x0016, 0x0016 }, /* 0x0016 */ + { CODEPAGE_ISNONE, 0x0017, 0x0017 }, /* 0x0017 */ + { CODEPAGE_ISNONE, 0x0018, 0x0018 }, /* 0x0018 */ + { CODEPAGE_ISNONE, 0x0019, 0x0019 }, /* 0x0019 */ + { CODEPAGE_ISNONE, 0x001a, 0x001a }, /* 0x001a */ + { CODEPAGE_ISNONE, 0x001b, 0x001b }, /* 0x001b */ + { CODEPAGE_ISNONE, 0x001c, 0x001c }, /* 0x001c */ + { CODEPAGE_ISNONE, 0x001d, 0x001d }, /* 0x001d */ + { CODEPAGE_ISNONE, 0x001e, 0x001e }, /* 0x001e */ + { CODEPAGE_ISNONE, 0x001f, 0x001f }, /* 0x001f */ + { CODEPAGE_ISNONE, 0x0020, 0x0020 }, /* 0x0020 */ + { CODEPAGE_ISNONE, 0x0021, 0x0021 }, /* 0x0021 */ + { CODEPAGE_ISNONE, 0x0022, 0x0022 }, /* 0x0022 */ + { CODEPAGE_ISNONE, 0x0023, 0x0023 }, /* 0x0023 */ + { CODEPAGE_ISNONE, 0x0024, 0x0024 }, /* 0x0024 */ + { CODEPAGE_ISNONE, 0x0025, 0x0025 }, /* 0x0025 */ + { CODEPAGE_ISNONE, 0x0026, 0x0026 }, /* 0x0026 */ + { CODEPAGE_ISNONE, 0x0027, 0x0027 }, /* 0x0027 */ + { CODEPAGE_ISNONE, 0x0028, 0x0028 }, /* 0x0028 */ + { CODEPAGE_ISNONE, 0x0029, 0x0029 }, /* 0x0029 */ + { CODEPAGE_ISNONE, 0x002a, 0x002a }, /* 0x002a */ + { CODEPAGE_ISNONE, 0x002b, 0x002b }, /* 0x002b */ + { CODEPAGE_ISNONE, 0x002c, 0x002c }, /* 0x002c */ + { CODEPAGE_ISNONE, 0x002d, 0x002d }, /* 0x002d */ + { CODEPAGE_ISNONE, 0x002e, 0x002e }, /* 0x002e */ + { CODEPAGE_ISNONE, 0x002f, 0x002f }, /* 0x002f */ + { CODEPAGE_ISNONE, 0x0030, 0x0030 }, /* 0x0030 */ + { CODEPAGE_ISNONE, 0x0031, 0x0031 }, /* 0x0031 */ + { CODEPAGE_ISNONE, 0x0032, 0x0032 }, /* 0x0032 */ + { CODEPAGE_ISNONE, 0x0033, 0x0033 }, /* 0x0033 */ + { CODEPAGE_ISNONE, 0x0034, 0x0034 }, /* 0x0034 */ + { CODEPAGE_ISNONE, 0x0035, 0x0035 }, /* 0x0035 */ + { CODEPAGE_ISNONE, 0x0036, 0x0036 }, /* 0x0036 */ + { CODEPAGE_ISNONE, 0x0037, 0x0037 }, /* 0x0037 */ + { CODEPAGE_ISNONE, 0x0038, 0x0038 }, /* 0x0038 */ + { CODEPAGE_ISNONE, 0x0039, 0x0039 }, /* 0x0039 */ + { CODEPAGE_ISNONE, 0x003a, 0x003a }, /* 0x003a */ + { CODEPAGE_ISNONE, 0x003b, 0x003b }, /* 0x003b */ + { CODEPAGE_ISNONE, 0x003c, 0x003c }, /* 0x003c */ + { CODEPAGE_ISNONE, 0x003d, 0x003d }, /* 0x003d */ + { CODEPAGE_ISNONE, 0x003e, 0x003e }, /* 0x003e */ + { CODEPAGE_ISNONE, 0x003f, 0x003f }, /* 0x003f */ + { CODEPAGE_ISNONE, 0x0040, 0x0040 }, /* 0x0040 */ + { CODEPAGE_ISUPPER, 0x0041, 0x0061 }, /* 0x0041 */ + { CODEPAGE_ISUPPER, 0x0042, 0x0062 }, /* 0x0042 */ + { CODEPAGE_ISUPPER, 0x0043, 0x0063 }, /* 0x0043 */ + { CODEPAGE_ISUPPER, 0x0044, 0x0064 }, /* 0x0044 */ + { CODEPAGE_ISUPPER, 0x0045, 0x0065 }, /* 0x0045 */ + { CODEPAGE_ISUPPER, 0x0046, 0x0066 }, /* 0x0046 */ + { CODEPAGE_ISUPPER, 0x0047, 0x0067 }, /* 0x0047 */ + { CODEPAGE_ISUPPER, 0x0048, 0x0068 }, /* 0x0048 */ + { CODEPAGE_ISUPPER, 0x0049, 0x0069 }, /* 0x0049 */ + { CODEPAGE_ISUPPER, 0x004a, 0x006a }, /* 0x004a */ + { CODEPAGE_ISUPPER, 0x004b, 0x006b }, /* 0x004b */ + { CODEPAGE_ISUPPER, 0x004c, 0x006c }, /* 0x004c */ + { CODEPAGE_ISUPPER, 0x004d, 0x006d }, /* 0x004d */ + { CODEPAGE_ISUPPER, 0x004e, 0x006e }, /* 0x004e */ + { CODEPAGE_ISUPPER, 0x004f, 0x006f }, /* 0x004f */ + { CODEPAGE_ISUPPER, 0x0050, 0x0070 }, /* 0x0050 */ + { CODEPAGE_ISUPPER, 0x0051, 0x0071 }, /* 0x0051 */ + { CODEPAGE_ISUPPER, 0x0052, 0x0072 }, /* 0x0052 */ + { CODEPAGE_ISUPPER, 0x0053, 0x0073 }, /* 0x0053 */ + { CODEPAGE_ISUPPER, 0x0054, 0x0074 }, /* 0x0054 */ + { CODEPAGE_ISUPPER, 0x0055, 0x0075 }, /* 0x0055 */ + { CODEPAGE_ISUPPER, 0x0056, 0x0076 }, /* 0x0056 */ + { CODEPAGE_ISUPPER, 0x0057, 0x0077 }, /* 0x0057 */ + { CODEPAGE_ISUPPER, 0x0058, 0x0078 }, /* 0x0058 */ + { CODEPAGE_ISUPPER, 0x0059, 0x0079 }, /* 0x0059 */ + { CODEPAGE_ISUPPER, 0x005a, 0x007a }, /* 0x005a */ + { CODEPAGE_ISNONE, 0x005b, 0x005b }, /* 0x005b */ + { CODEPAGE_ISNONE, 0x005c, 0x005c }, /* 0x005c */ + { CODEPAGE_ISNONE, 0x005d, 0x005d }, /* 0x005d */ + { CODEPAGE_ISNONE, 0x005e, 0x005e }, /* 0x005e */ + { CODEPAGE_ISNONE, 0x005f, 0x005f }, /* 0x005f */ + { CODEPAGE_ISNONE, 0x0060, 0x0060 }, /* 0x0060 */ + { CODEPAGE_ISLOWER, 0x0041, 0x0061 }, /* 0x0061 */ + { CODEPAGE_ISLOWER, 0x0042, 0x0062 }, /* 0x0062 */ + { CODEPAGE_ISLOWER, 0x0043, 0x0063 }, /* 0x0063 */ + { CODEPAGE_ISLOWER, 0x0044, 0x0064 }, /* 0x0064 */ + { CODEPAGE_ISLOWER, 0x0045, 0x0065 }, /* 0x0065 */ + { CODEPAGE_ISLOWER, 0x0046, 0x0066 }, /* 0x0066 */ + { CODEPAGE_ISLOWER, 0x0047, 0x0067 }, /* 0x0067 */ + { CODEPAGE_ISLOWER, 0x0048, 0x0068 }, /* 0x0068 */ + { CODEPAGE_ISLOWER, 0x0049, 0x0069 }, /* 0x0069 */ + { CODEPAGE_ISLOWER, 0x004a, 0x006a }, /* 0x006a */ + { CODEPAGE_ISLOWER, 0x004b, 0x006b }, /* 0x006b */ + { CODEPAGE_ISLOWER, 0x004c, 0x006c }, /* 0x006c */ + { CODEPAGE_ISLOWER, 0x004d, 0x006d }, /* 0x006d */ + { CODEPAGE_ISLOWER, 0x004e, 0x006e }, /* 0x006e */ + { CODEPAGE_ISLOWER, 0x004f, 0x006f }, /* 0x006f */ + { CODEPAGE_ISLOWER, 0x0050, 0x0070 }, /* 0x0070 */ + { CODEPAGE_ISLOWER, 0x0051, 0x0071 }, /* 0x0071 */ + { CODEPAGE_ISLOWER, 0x0052, 0x0072 }, /* 0x0072 */ + { CODEPAGE_ISLOWER, 0x0053, 0x0073 }, /* 0x0073 */ + { CODEPAGE_ISLOWER, 0x0054, 0x0074 }, /* 0x0074 */ + { CODEPAGE_ISLOWER, 0x0055, 0x0075 }, /* 0x0075 */ + { CODEPAGE_ISLOWER, 0x0056, 0x0076 }, /* 0x0076 */ + { CODEPAGE_ISLOWER, 0x0057, 0x0077 }, /* 0x0077 */ + { CODEPAGE_ISLOWER, 0x0058, 0x0078 }, /* 0x0078 */ + { CODEPAGE_ISLOWER, 0x0059, 0x0079 }, /* 0x0079 */ + { CODEPAGE_ISLOWER, 0x005a, 0x007a }, /* 0x007a */ + { CODEPAGE_ISNONE, 0x007b, 0x007b }, /* 0x007b */ + { CODEPAGE_ISNONE, 0x007c, 0x007c }, /* 0x007c */ + { CODEPAGE_ISNONE, 0x007d, 0x007d }, /* 0x007d */ + { CODEPAGE_ISNONE, 0x007e, 0x007e }, /* 0x007e */ + { CODEPAGE_ISNONE, 0x007f, 0x007f }, /* 0x007f */ + { CODEPAGE_ISNONE, 0x0080, 0x0080 }, /* 0x0080 */ + { CODEPAGE_ISNONE, 0x0081, 0x0081 }, /* 0x0081 */ + { CODEPAGE_ISNONE, 0x0082, 0x0082 }, /* 0x0082 */ + { CODEPAGE_ISNONE, 0x0083, 0x0083 }, /* 0x0083 */ + { CODEPAGE_ISNONE, 0x0084, 0x0084 }, /* 0x0084 */ + { CODEPAGE_ISNONE, 0x0085, 0x0085 }, /* 0x0085 */ + { CODEPAGE_ISNONE, 0x0086, 0x0086 }, /* 0x0086 */ + { CODEPAGE_ISNONE, 0x0087, 0x0087 }, /* 0x0087 */ + { CODEPAGE_ISNONE, 0x0088, 0x0088 }, /* 0x0088 */ + { CODEPAGE_ISNONE, 0x0089, 0x0089 }, /* 0x0089 */ + { CODEPAGE_ISNONE, 0x008a, 0x008a }, /* 0x008a */ + { CODEPAGE_ISNONE, 0x008b, 0x008b }, /* 0x008b */ + { CODEPAGE_ISNONE, 0x008c, 0x008c }, /* 0x008c */ + { CODEPAGE_ISNONE, 0x008d, 0x008d }, /* 0x008d */ + { CODEPAGE_ISNONE, 0x008e, 0x008e }, /* 0x008e */ + { CODEPAGE_ISNONE, 0x008f, 0x008f }, /* 0x008f */ + { CODEPAGE_ISNONE, 0x0090, 0x0090 }, /* 0x0090 */ + { CODEPAGE_ISNONE, 0x0091, 0x0091 }, /* 0x0091 */ + { CODEPAGE_ISNONE, 0x0092, 0x0092 }, /* 0x0092 */ + { CODEPAGE_ISNONE, 0x0093, 0x0093 }, /* 0x0093 */ + { CODEPAGE_ISNONE, 0x0094, 0x0094 }, /* 0x0094 */ + { CODEPAGE_ISNONE, 0x0095, 0x0095 }, /* 0x0095 */ + { CODEPAGE_ISNONE, 0x0096, 0x0096 }, /* 0x0096 */ + { CODEPAGE_ISNONE, 0x0097, 0x0097 }, /* 0x0097 */ + { CODEPAGE_ISNONE, 0x0098, 0x0098 }, /* 0x0098 */ + { CODEPAGE_ISNONE, 0x0099, 0x0099 }, /* 0x0099 */ + { CODEPAGE_ISNONE, 0x009a, 0x009a }, /* 0x009a */ + { CODEPAGE_ISNONE, 0x009b, 0x009b }, /* 0x009b */ + { CODEPAGE_ISNONE, 0x009c, 0x009c }, /* 0x009c */ + { CODEPAGE_ISNONE, 0x009d, 0x009d }, /* 0x009d */ + { CODEPAGE_ISNONE, 0x009e, 0x009e }, /* 0x009e */ + { CODEPAGE_ISNONE, 0x009f, 0x009f }, /* 0x009f */ + { CODEPAGE_ISNONE, 0x00a0, 0x00a0 }, /* 0x00a0 */ + { CODEPAGE_ISUPPER, 0x00a1, 0x00f1 }, /* 0x00a1 */ + { CODEPAGE_ISUPPER, 0x00a2, 0x00f2 }, /* 0x00a2 */ + { CODEPAGE_ISUPPER, 0x00a3, 0x00f3 }, /* 0x00a3 */ + { CODEPAGE_ISUPPER, 0x00a4, 0x00f4 }, /* 0x00a4 */ + { CODEPAGE_ISUPPER, 0x00a5, 0x00f5 }, /* 0x00a5 */ + { CODEPAGE_ISUPPER, 0x00a6, 0x00f6 }, /* 0x00a6 */ + { CODEPAGE_ISUPPER, 0x00a7, 0x00f7 }, /* 0x00a7 */ + { CODEPAGE_ISUPPER, 0x00a8, 0x00f8 }, /* 0x00a8 */ + { CODEPAGE_ISUPPER, 0x00a9, 0x00f9 }, /* 0x00a9 */ + { CODEPAGE_ISUPPER, 0x00aa, 0x00fa }, /* 0x00aa */ + { CODEPAGE_ISUPPER, 0x00ab, 0x00fb }, /* 0x00ab */ + { CODEPAGE_ISUPPER, 0x00ac, 0x00fc }, /* 0x00ac */ + { CODEPAGE_ISUPPER, 0x00ad, 0x00fd }, /* 0x00ad */ + { CODEPAGE_ISUPPER, 0x00ae, 0x00fe }, /* 0x00ae */ + { CODEPAGE_ISUPPER, 0x00af, 0x00ff }, /* 0x00af */ + { CODEPAGE_ISNONE, 0x00b0, 0x00b0 }, /* 0x00b0 */ + { CODEPAGE_ISNONE, 0x00b1, 0x00b1 }, /* 0x00b1 */ + { CODEPAGE_ISNONE, 0x00b2, 0x00b2 }, /* 0x00b2 */ + { CODEPAGE_ISNONE, 0x00b3, 0x00b3 }, /* 0x00b3 */ + { CODEPAGE_ISNONE, 0x00b4, 0x00b4 }, /* 0x00b4 */ + { CODEPAGE_ISNONE, 0x00b5, 0x00b5 }, /* 0x00b5 */ + { CODEPAGE_ISNONE, 0x00b6, 0x00b6 }, /* 0x00b6 */ + { CODEPAGE_ISNONE, 0x00b7, 0x00b7 }, /* 0x00b7 */ + { CODEPAGE_ISNONE, 0x00b8, 0x00b8 }, /* 0x00b8 */ + { CODEPAGE_ISNONE, 0x00b9, 0x00b9 }, /* 0x00b9 */ + { CODEPAGE_ISNONE, 0x00ba, 0x00ba }, /* 0x00ba */ + { CODEPAGE_ISNONE, 0x00bb, 0x00bb }, /* 0x00bb */ + { CODEPAGE_ISNONE, 0x00bc, 0x00bc }, /* 0x00bc */ + { CODEPAGE_ISNONE, 0x00bd, 0x00bd }, /* 0x00bd */ + { CODEPAGE_ISNONE, 0x00be, 0x00be }, /* 0x00be */ + { CODEPAGE_ISNONE, 0x00bf, 0x00bf }, /* 0x00bf */ + { CODEPAGE_ISUPPER, 0x00c0, 0x00e0 }, /* 0x00c0 */ + { CODEPAGE_ISUPPER, 0x00c1, 0x00e1 }, /* 0x00c1 */ + { CODEPAGE_ISUPPER, 0x00c2, 0x00e2 }, /* 0x00c2 */ + { CODEPAGE_ISUPPER, 0x00c3, 0x00e3 }, /* 0x00c3 */ + { CODEPAGE_ISUPPER, 0x00c4, 0x00e4 }, /* 0x00c4 */ + { CODEPAGE_ISUPPER, 0x00c5, 0x00e5 }, /* 0x00c5 */ + { CODEPAGE_ISUPPER, 0x00c6, 0x00e6 }, /* 0x00c6 */ + { CODEPAGE_ISUPPER, 0x00c7, 0x00e7 }, /* 0x00c7 */ + { CODEPAGE_ISUPPER, 0x00c8, 0x00e8 }, /* 0x00c8 */ + { CODEPAGE_ISUPPER, 0x00c9, 0x00e9 }, /* 0x00c9 */ + { CODEPAGE_ISUPPER, 0x00ca, 0x00ea }, /* 0x00ca */ + { CODEPAGE_ISUPPER, 0x00cb, 0x00eb }, /* 0x00cb */ + { CODEPAGE_ISUPPER, 0x00cc, 0x00ec }, /* 0x00cc */ + { CODEPAGE_ISUPPER, 0x00cd, 0x00ed }, /* 0x00cd */ + { CODEPAGE_ISUPPER, 0x00ce, 0x00ee }, /* 0x00ce */ + { CODEPAGE_ISUPPER, 0x00cf, 0x00ef }, /* 0x00cf */ + { CODEPAGE_ISNONE, 0x00d0, 0x00d0 }, /* 0x00d0 */ + { CODEPAGE_ISNONE, 0x00d1, 0x00d1 }, /* 0x00d1 */ + { CODEPAGE_ISNONE, 0x00d2, 0x00d2 }, /* 0x00d2 */ + { CODEPAGE_ISNONE, 0x00d3, 0x00d3 }, /* 0x00d3 */ + { CODEPAGE_ISNONE, 0x00d4, 0x00d4 }, /* 0x00d4 */ + { CODEPAGE_ISNONE, 0x00d5, 0x00d5 }, /* 0x00d5 */ + { CODEPAGE_ISNONE, 0x00d6, 0x00d6 }, /* 0x00d6 */ + { CODEPAGE_ISNONE, 0x00d7, 0x00d7 }, /* 0x00d7 */ + { CODEPAGE_ISNONE, 0x00d8, 0x00d8 }, /* 0x00d8 */ + { CODEPAGE_ISNONE, 0x00d9, 0x00d9 }, /* 0x00d9 */ + { CODEPAGE_ISNONE, 0x00da, 0x00da }, /* 0x00da */ + { CODEPAGE_ISNONE, 0x00db, 0x00db }, /* 0x00db */ + { CODEPAGE_ISNONE, 0x00dc, 0x00dc }, /* 0x00dc */ + { CODEPAGE_ISNONE, 0x00dd, 0x00dd }, /* 0x00dd */ + { CODEPAGE_ISNONE, 0x00de, 0x00de }, /* 0x00de */ + { CODEPAGE_ISNONE, 0x00df, 0x00df }, /* 0x00df */ + { CODEPAGE_ISLOWER, 0x00c0, 0x00e0 }, /* 0x00e0 */ + { CODEPAGE_ISLOWER, 0x00c1, 0x00e1 }, /* 0x00e1 */ + { CODEPAGE_ISLOWER, 0x00c2, 0x00e2 }, /* 0x00e2 */ + { CODEPAGE_ISLOWER, 0x00c3, 0x00e3 }, /* 0x00e3 */ + { CODEPAGE_ISLOWER, 0x00c4, 0x00e4 }, /* 0x00e4 */ + { CODEPAGE_ISLOWER, 0x00c5, 0x00e5 }, /* 0x00e5 */ + { CODEPAGE_ISLOWER, 0x00c6, 0x00e6 }, /* 0x00e6 */ + { CODEPAGE_ISLOWER, 0x00c7, 0x00e7 }, /* 0x00e7 */ + { CODEPAGE_ISLOWER, 0x00c8, 0x00e8 }, /* 0x00e8 */ + { CODEPAGE_ISLOWER, 0x00c9, 0x00e9 }, /* 0x00e9 */ + { CODEPAGE_ISLOWER, 0x00ca, 0x00ea }, /* 0x00ea */ + { CODEPAGE_ISLOWER, 0x00cb, 0x00eb }, /* 0x00eb */ + { CODEPAGE_ISLOWER, 0x00cc, 0x00ec }, /* 0x00ec */ + { CODEPAGE_ISLOWER, 0x00cd, 0x00ed }, /* 0x00ed */ + { CODEPAGE_ISLOWER, 0x00ce, 0x00ee }, /* 0x00ee */ + { CODEPAGE_ISLOWER, 0x00cf, 0x00ef }, /* 0x00ef */ + { CODEPAGE_ISNONE, 0x00f0, 0x00f0 }, /* 0x00f0 */ + { CODEPAGE_ISLOWER, 0x00a1, 0x00f1 }, /* 0x00f1 */ + { CODEPAGE_ISLOWER, 0x00a2, 0x00f2 }, /* 0x00f2 */ + { CODEPAGE_ISLOWER, 0x00a3, 0x00f3 }, /* 0x00f3 */ + { CODEPAGE_ISLOWER, 0x00a4, 0x00f4 }, /* 0x00f4 */ + { CODEPAGE_ISLOWER, 0x00a5, 0x00f5 }, /* 0x00f5 */ + { CODEPAGE_ISLOWER, 0x00a6, 0x00f6 }, /* 0x00f6 */ + { CODEPAGE_ISLOWER, 0x00a7, 0x00f7 }, /* 0x00f7 */ + { CODEPAGE_ISLOWER, 0x00a8, 0x00f8 }, /* 0x00f8 */ + { CODEPAGE_ISLOWER, 0x00a9, 0x00f9 }, /* 0x00f9 */ + { CODEPAGE_ISLOWER, 0x00aa, 0x00fa }, /* 0x00fa */ + { CODEPAGE_ISLOWER, 0x00ab, 0x00fb }, /* 0x00fb */ + { CODEPAGE_ISLOWER, 0x00ac, 0x00fc }, /* 0x00fc */ + { CODEPAGE_ISLOWER, 0x00ad, 0x00fd }, /* 0x00fd */ + { CODEPAGE_ISLOWER, 0x00ae, 0x00fe }, /* 0x00fe */ + { CODEPAGE_ISLOWER, 0x00af, 0x00ff } /* 0x00ff */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_CP_CYRILLIC_H */ diff --git a/usr/src/uts/common/smbsrv/cp_latin1.h b/usr/src/uts/common/smbsrv/cp_latin1.h new file mode 100644 index 000000000000..728b28e05bdb --- /dev/null +++ b/usr/src/uts/common/smbsrv/cp_latin1.h @@ -0,0 +1,317 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_CP_LATIN1_H +#define _SMBSRV_CP_LATIN1_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file specifies a codepage mapping for a given character set as + * specified below: + * + * This is the codepage for the Latin-1 Character Set + * (Western Europe). This codepage defines values for + * the special characters needed for the written alphabets + * of the following Languages: French, Spanish, Catalan, + * Basque, Portuguese, Italian, Albanian, Rhaeto-Romanic, + * Dutch, German, Danish, Swedish, Norwegian, Finnish, + * Faroese, Icelandic, Irish, Scottish, English, Afrikaans + * and Swahili. + * This codepage is also used in North & South America, + * Canada, Australia, and much of Africa + * The Latin-1 character set is also Known as iso-8859-1. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +codepage_t Latin1_codepage[256] = { + { CODEPAGE_ISNONE, 0x0000, 0x0000 }, /* 0x0000 */ + { CODEPAGE_ISNONE, 0x0001, 0x0001 }, /* 0x0001 */ + { CODEPAGE_ISNONE, 0x0002, 0x0002 }, /* 0x0002 */ + { CODEPAGE_ISNONE, 0x0003, 0x0003 }, /* 0x0003 */ + { CODEPAGE_ISNONE, 0x0004, 0x0004 }, /* 0x0004 */ + { CODEPAGE_ISNONE, 0x0005, 0x0005 }, /* 0x0005 */ + { CODEPAGE_ISNONE, 0x0006, 0x0006 }, /* 0x0006 */ + { CODEPAGE_ISNONE, 0x0007, 0x0007 }, /* 0x0007 */ + { CODEPAGE_ISNONE, 0x0008, 0x0008 }, /* 0x0008 */ + { CODEPAGE_ISNONE, 0x0009, 0x0009 }, /* 0x0009 */ + { CODEPAGE_ISNONE, 0x000a, 0x000a }, /* 0x000a */ + { CODEPAGE_ISNONE, 0x000b, 0x000b }, /* 0x000b */ + { CODEPAGE_ISNONE, 0x000c, 0x000c }, /* 0x000c */ + { CODEPAGE_ISNONE, 0x000d, 0x000d }, /* 0x000d */ + { CODEPAGE_ISNONE, 0x000e, 0x000e }, /* 0x000e */ + { CODEPAGE_ISNONE, 0x000f, 0x000f }, /* 0x000f */ + { CODEPAGE_ISNONE, 0x0010, 0x0010 }, /* 0x0010 */ + { CODEPAGE_ISNONE, 0x0011, 0x0011 }, /* 0x0011 */ + { CODEPAGE_ISNONE, 0x0012, 0x0012 }, /* 0x0012 */ + { CODEPAGE_ISNONE, 0x0013, 0x0013 }, /* 0x0013 */ + { CODEPAGE_ISNONE, 0x0014, 0x0014 }, /* 0x0014 */ + { CODEPAGE_ISNONE, 0x0015, 0x0015 }, /* 0x0015 */ + { CODEPAGE_ISNONE, 0x0016, 0x0016 }, /* 0x0016 */ + { CODEPAGE_ISNONE, 0x0017, 0x0017 }, /* 0x0017 */ + { CODEPAGE_ISNONE, 0x0018, 0x0018 }, /* 0x0018 */ + { CODEPAGE_ISNONE, 0x0019, 0x0019 }, /* 0x0019 */ + { CODEPAGE_ISNONE, 0x001a, 0x001a }, /* 0x001a */ + { CODEPAGE_ISNONE, 0x001b, 0x001b }, /* 0x001b */ + { CODEPAGE_ISNONE, 0x001c, 0x001c }, /* 0x001c */ + { CODEPAGE_ISNONE, 0x001d, 0x001d }, /* 0x001d */ + { CODEPAGE_ISNONE, 0x001e, 0x001e }, /* 0x001e */ + { CODEPAGE_ISNONE, 0x001f, 0x001f }, /* 0x001f */ + { CODEPAGE_ISNONE, 0x0020, 0x0020 }, /* 0x0020 */ + { CODEPAGE_ISNONE, 0x0021, 0x0021 }, /* 0x0021 */ + { CODEPAGE_ISNONE, 0x0022, 0x0022 }, /* 0x0022 */ + { CODEPAGE_ISNONE, 0x0023, 0x0023 }, /* 0x0023 */ + { CODEPAGE_ISNONE, 0x0024, 0x0024 }, /* 0x0024 */ + { CODEPAGE_ISNONE, 0x0025, 0x0025 }, /* 0x0025 */ + { CODEPAGE_ISNONE, 0x0026, 0x0026 }, /* 0x0026 */ + { CODEPAGE_ISNONE, 0x0027, 0x0027 }, /* 0x0027 */ + { CODEPAGE_ISNONE, 0x0028, 0x0028 }, /* 0x0028 */ + { CODEPAGE_ISNONE, 0x0029, 0x0029 }, /* 0x0029 */ + { CODEPAGE_ISNONE, 0x002a, 0x002a }, /* 0x002a */ + { CODEPAGE_ISNONE, 0x002b, 0x002b }, /* 0x002b */ + { CODEPAGE_ISNONE, 0x002c, 0x002c }, /* 0x002c */ + { CODEPAGE_ISNONE, 0x002d, 0x002d }, /* 0x002d */ + { CODEPAGE_ISNONE, 0x002e, 0x002e }, /* 0x002e */ + { CODEPAGE_ISNONE, 0x002f, 0x002f }, /* 0x002f */ + { CODEPAGE_ISNONE, 0x0030, 0x0030 }, /* 0x0030 */ + { CODEPAGE_ISNONE, 0x0031, 0x0031 }, /* 0x0031 */ + { CODEPAGE_ISNONE, 0x0032, 0x0032 }, /* 0x0032 */ + { CODEPAGE_ISNONE, 0x0033, 0x0033 }, /* 0x0033 */ + { CODEPAGE_ISNONE, 0x0034, 0x0034 }, /* 0x0034 */ + { CODEPAGE_ISNONE, 0x0035, 0x0035 }, /* 0x0035 */ + { CODEPAGE_ISNONE, 0x0036, 0x0036 }, /* 0x0036 */ + { CODEPAGE_ISNONE, 0x0037, 0x0037 }, /* 0x0037 */ + { CODEPAGE_ISNONE, 0x0038, 0x0038 }, /* 0x0038 */ + { CODEPAGE_ISNONE, 0x0039, 0x0039 }, /* 0x0039 */ + { CODEPAGE_ISNONE, 0x003a, 0x003a }, /* 0x003a */ + { CODEPAGE_ISNONE, 0x003b, 0x003b }, /* 0x003b */ + { CODEPAGE_ISNONE, 0x003c, 0x003c }, /* 0x003c */ + { CODEPAGE_ISNONE, 0x003d, 0x003d }, /* 0x003d */ + { CODEPAGE_ISNONE, 0x003e, 0x003e }, /* 0x003e */ + { CODEPAGE_ISNONE, 0x003f, 0x003f }, /* 0x003f */ + { CODEPAGE_ISNONE, 0x0040, 0x0040 }, /* 0x0040 */ + { CODEPAGE_ISUPPER, 0x0041, 0x0061 }, /* 0x0041 */ + { CODEPAGE_ISUPPER, 0x0042, 0x0062 }, /* 0x0042 */ + { CODEPAGE_ISUPPER, 0x0043, 0x0063 }, /* 0x0043 */ + { CODEPAGE_ISUPPER, 0x0044, 0x0064 }, /* 0x0044 */ + { CODEPAGE_ISUPPER, 0x0045, 0x0065 }, /* 0x0045 */ + { CODEPAGE_ISUPPER, 0x0046, 0x0066 }, /* 0x0046 */ + { CODEPAGE_ISUPPER, 0x0047, 0x0067 }, /* 0x0047 */ + { CODEPAGE_ISUPPER, 0x0048, 0x0068 }, /* 0x0048 */ + { CODEPAGE_ISUPPER, 0x0049, 0x0069 }, /* 0x0049 */ + { CODEPAGE_ISUPPER, 0x004a, 0x006a }, /* 0x004a */ + { CODEPAGE_ISUPPER, 0x004b, 0x006b }, /* 0x004b */ + { CODEPAGE_ISUPPER, 0x004c, 0x006c }, /* 0x004c */ + { CODEPAGE_ISUPPER, 0x004d, 0x006d }, /* 0x004d */ + { CODEPAGE_ISUPPER, 0x004e, 0x006e }, /* 0x004e */ + { CODEPAGE_ISUPPER, 0x004f, 0x006f }, /* 0x004f */ + { CODEPAGE_ISUPPER, 0x0050, 0x0070 }, /* 0x0050 */ + { CODEPAGE_ISUPPER, 0x0051, 0x0071 }, /* 0x0051 */ + { CODEPAGE_ISUPPER, 0x0052, 0x0072 }, /* 0x0052 */ + { CODEPAGE_ISUPPER, 0x0053, 0x0073 }, /* 0x0053 */ + { CODEPAGE_ISUPPER, 0x0054, 0x0074 }, /* 0x0054 */ + { CODEPAGE_ISUPPER, 0x0055, 0x0075 }, /* 0x0055 */ + { CODEPAGE_ISUPPER, 0x0056, 0x0076 }, /* 0x0056 */ + { CODEPAGE_ISUPPER, 0x0057, 0x0077 }, /* 0x0057 */ + { CODEPAGE_ISUPPER, 0x0058, 0x0078 }, /* 0x0058 */ + { CODEPAGE_ISUPPER, 0x0059, 0x0079 }, /* 0x0059 */ + { CODEPAGE_ISUPPER, 0x005a, 0x007a }, /* 0x005a */ + { CODEPAGE_ISNONE, 0x005b, 0x005b }, /* 0x005b */ + { CODEPAGE_ISNONE, 0x005c, 0x005c }, /* 0x005c */ + { CODEPAGE_ISNONE, 0x005d, 0x005d }, /* 0x005d */ + { CODEPAGE_ISNONE, 0x005e, 0x005e }, /* 0x005e */ + { CODEPAGE_ISNONE, 0x005f, 0x005f }, /* 0x005f */ + { CODEPAGE_ISNONE, 0x0060, 0x0060 }, /* 0x0060 */ + { CODEPAGE_ISLOWER, 0x0041, 0x0061 }, /* 0x0061 */ + { CODEPAGE_ISLOWER, 0x0042, 0x0062 }, /* 0x0062 */ + { CODEPAGE_ISLOWER, 0x0043, 0x0063 }, /* 0x0063 */ + { CODEPAGE_ISLOWER, 0x0044, 0x0064 }, /* 0x0064 */ + { CODEPAGE_ISLOWER, 0x0045, 0x0065 }, /* 0x0065 */ + { CODEPAGE_ISLOWER, 0x0046, 0x0066 }, /* 0x0066 */ + { CODEPAGE_ISLOWER, 0x0047, 0x0067 }, /* 0x0067 */ + { CODEPAGE_ISLOWER, 0x0048, 0x0068 }, /* 0x0068 */ + { CODEPAGE_ISLOWER, 0x0049, 0x0069 }, /* 0x0069 */ + { CODEPAGE_ISLOWER, 0x004a, 0x006a }, /* 0x006a */ + { CODEPAGE_ISLOWER, 0x004b, 0x006b }, /* 0x006b */ + { CODEPAGE_ISLOWER, 0x004c, 0x006c }, /* 0x006c */ + { CODEPAGE_ISLOWER, 0x004d, 0x006d }, /* 0x006d */ + { CODEPAGE_ISLOWER, 0x004e, 0x006e }, /* 0x006e */ + { CODEPAGE_ISLOWER, 0x004f, 0x006f }, /* 0x006f */ + { CODEPAGE_ISLOWER, 0x0050, 0x0070 }, /* 0x0070 */ + { CODEPAGE_ISLOWER, 0x0051, 0x0071 }, /* 0x0071 */ + { CODEPAGE_ISLOWER, 0x0052, 0x0072 }, /* 0x0072 */ + { CODEPAGE_ISLOWER, 0x0053, 0x0073 }, /* 0x0073 */ + { CODEPAGE_ISLOWER, 0x0054, 0x0074 }, /* 0x0074 */ + { CODEPAGE_ISLOWER, 0x0055, 0x0075 }, /* 0x0075 */ + { CODEPAGE_ISLOWER, 0x0056, 0x0076 }, /* 0x0076 */ + { CODEPAGE_ISLOWER, 0x0057, 0x0077 }, /* 0x0077 */ + { CODEPAGE_ISLOWER, 0x0058, 0x0078 }, /* 0x0078 */ + { CODEPAGE_ISLOWER, 0x0059, 0x0079 }, /* 0x0079 */ + { CODEPAGE_ISLOWER, 0x005a, 0x007a }, /* 0x007a */ + { CODEPAGE_ISNONE, 0x007b, 0x007b }, /* 0x007b */ + { CODEPAGE_ISNONE, 0x007c, 0x007c }, /* 0x007c */ + { CODEPAGE_ISNONE, 0x007d, 0x007d }, /* 0x007d */ + { CODEPAGE_ISNONE, 0x007e, 0x007e }, /* 0x007e */ + { CODEPAGE_ISNONE, 0x007f, 0x007f }, /* 0x007f */ + { CODEPAGE_ISNONE, 0x0080, 0x0080 }, /* 0x0080 */ + { CODEPAGE_ISNONE, 0x0081, 0x0081 }, /* 0x0081 */ + { CODEPAGE_ISNONE, 0x0082, 0x0082 }, /* 0x0082 */ + { CODEPAGE_ISNONE, 0x0083, 0x0083 }, /* 0x0083 */ + { CODEPAGE_ISNONE, 0x0084, 0x0084 }, /* 0x0084 */ + { CODEPAGE_ISNONE, 0x0085, 0x0085 }, /* 0x0085 */ + { CODEPAGE_ISNONE, 0x0086, 0x0086 }, /* 0x0086 */ + { CODEPAGE_ISNONE, 0x0087, 0x0087 }, /* 0x0087 */ + { CODEPAGE_ISNONE, 0x0088, 0x0088 }, /* 0x0088 */ + { CODEPAGE_ISNONE, 0x0089, 0x0089 }, /* 0x0089 */ + { CODEPAGE_ISNONE, 0x008a, 0x008a }, /* 0x008a */ + { CODEPAGE_ISNONE, 0x008b, 0x008b }, /* 0x008b */ + { CODEPAGE_ISNONE, 0x008c, 0x008c }, /* 0x008c */ + { CODEPAGE_ISNONE, 0x008d, 0x008d }, /* 0x008d */ + { CODEPAGE_ISNONE, 0x008e, 0x008e }, /* 0x008e */ + { CODEPAGE_ISNONE, 0x008f, 0x008f }, /* 0x008f */ + { CODEPAGE_ISNONE, 0x0090, 0x0090 }, /* 0x0090 */ + { CODEPAGE_ISNONE, 0x0091, 0x0091 }, /* 0x0091 */ + { CODEPAGE_ISNONE, 0x0092, 0x0092 }, /* 0x0092 */ + { CODEPAGE_ISNONE, 0x0093, 0x0093 }, /* 0x0093 */ + { CODEPAGE_ISNONE, 0x0094, 0x0094 }, /* 0x0094 */ + { CODEPAGE_ISNONE, 0x0095, 0x0095 }, /* 0x0095 */ + { CODEPAGE_ISNONE, 0x0096, 0x0096 }, /* 0x0096 */ + { CODEPAGE_ISNONE, 0x0097, 0x0097 }, /* 0x0097 */ + { CODEPAGE_ISNONE, 0x0098, 0x0098 }, /* 0x0098 */ + { CODEPAGE_ISNONE, 0x0099, 0x0099 }, /* 0x0099 */ + { CODEPAGE_ISNONE, 0x009a, 0x009a }, /* 0x009a */ + { CODEPAGE_ISNONE, 0x009b, 0x009b }, /* 0x009b */ + { CODEPAGE_ISNONE, 0x009c, 0x009c }, /* 0x009c */ + { CODEPAGE_ISNONE, 0x009d, 0x009d }, /* 0x009d */ + { CODEPAGE_ISNONE, 0x009e, 0x009e }, /* 0x009e */ + { CODEPAGE_ISNONE, 0x009f, 0x009f }, /* 0x009f */ + { CODEPAGE_ISNONE, 0x00a0, 0x00a0 }, /* 0x00a0 */ + { CODEPAGE_ISNONE, 0x00a1, 0x00a1 }, /* 0x00a1 */ + { CODEPAGE_ISNONE, 0x00a2, 0x00a2 }, /* 0x00a2 */ + { CODEPAGE_ISNONE, 0x00a3, 0x00a3 }, /* 0x00a3 */ + { CODEPAGE_ISNONE, 0x00a4, 0x00a4 }, /* 0x00a4 */ + { CODEPAGE_ISNONE, 0x00a5, 0x00a5 }, /* 0x00a5 */ + { CODEPAGE_ISNONE, 0x00a6, 0x00a6 }, /* 0x00a6 */ + { CODEPAGE_ISNONE, 0x00a7, 0x00a7 }, /* 0x00a7 */ + { CODEPAGE_ISNONE, 0x00a8, 0x00a8 }, /* 0x00a8 */ + { CODEPAGE_ISNONE, 0x00a9, 0x00a9 }, /* 0x00a9 */ + { CODEPAGE_ISNONE, 0x00aa, 0x00aa }, /* 0x00aa */ + { CODEPAGE_ISNONE, 0x00ab, 0x00ab }, /* 0x00ab */ + { CODEPAGE_ISNONE, 0x00ac, 0x00ac }, /* 0x00ac */ + { CODEPAGE_ISNONE, 0x00ad, 0x00ad }, /* 0x00ad */ + { CODEPAGE_ISNONE, 0x00ae, 0x00ae }, /* 0x00ae */ + { CODEPAGE_ISNONE, 0x00af, 0x00af }, /* 0x00af */ + { CODEPAGE_ISNONE, 0x00b0, 0x00b0 }, /* 0x00b0 */ + { CODEPAGE_ISNONE, 0x00b1, 0x00b1 }, /* 0x00b1 */ + { CODEPAGE_ISNONE, 0x00b2, 0x00b2 }, /* 0x00b2 */ + { CODEPAGE_ISNONE, 0x00b3, 0x00b3 }, /* 0x00b3 */ + { CODEPAGE_ISNONE, 0x00b4, 0x00b4 }, /* 0x00b4 */ + { CODEPAGE_ISNONE, 0x00b5, 0x00b5 }, /* 0x00b5 */ + { CODEPAGE_ISNONE, 0x00b6, 0x00b6 }, /* 0x00b6 */ + { CODEPAGE_ISNONE, 0x00b7, 0x00b7 }, /* 0x00b7 */ + { CODEPAGE_ISNONE, 0x00b8, 0x00b8 }, /* 0x00b8 */ + { CODEPAGE_ISNONE, 0x00b9, 0x00b9 }, /* 0x00b9 */ + { CODEPAGE_ISNONE, 0x00ba, 0x00ba }, /* 0x00ba */ + { CODEPAGE_ISNONE, 0x00bb, 0x00bb }, /* 0x00bb */ + { CODEPAGE_ISNONE, 0x00bc, 0x00bc }, /* 0x00bc */ + { CODEPAGE_ISNONE, 0x00bd, 0x00bd }, /* 0x00bd */ + { CODEPAGE_ISNONE, 0x00be, 0x00be }, /* 0x00be */ + { CODEPAGE_ISNONE, 0x00bf, 0x00bf }, /* 0x00bf */ + { CODEPAGE_ISUPPER, 0x00c0, 0x00e0 }, /* 0x00c0 */ + { CODEPAGE_ISUPPER, 0x00c1, 0x00e1 }, /* 0x00c1 */ + { CODEPAGE_ISUPPER, 0x00c2, 0x00e2 }, /* 0x00c2 */ + { CODEPAGE_ISUPPER, 0x00c3, 0x00e3 }, /* 0x00c3 */ + { CODEPAGE_ISUPPER, 0x00c4, 0x00e4 }, /* 0x00c4 */ + { CODEPAGE_ISUPPER, 0x00c5, 0x00e5 }, /* 0x00c5 */ + { CODEPAGE_ISUPPER, 0x00c6, 0x00e6 }, /* 0x00c6 */ + { CODEPAGE_ISUPPER, 0x00c7, 0x00e7 }, /* 0x00c7 */ + { CODEPAGE_ISUPPER, 0x00c8, 0x00e8 }, /* 0x00c8 */ + { CODEPAGE_ISUPPER, 0x00c9, 0x00e9 }, /* 0x00c9 */ + { CODEPAGE_ISUPPER, 0x00ca, 0x00ea }, /* 0x00ca */ + { CODEPAGE_ISUPPER, 0x00cb, 0x00eb }, /* 0x00cb */ + { CODEPAGE_ISUPPER, 0x00cc, 0x00ec }, /* 0x00cc */ + { CODEPAGE_ISUPPER, 0x00cd, 0x00ed }, /* 0x00cd */ + { CODEPAGE_ISUPPER, 0x00ce, 0x00ee }, /* 0x00ce */ + { CODEPAGE_ISUPPER, 0x00cf, 0x00ef }, /* 0x00cf */ + { CODEPAGE_ISUPPER, 0x00d0, 0x00f0 }, /* 0x00d0 */ + { CODEPAGE_ISUPPER, 0x00d1, 0x00f1 }, /* 0x00d1 */ + { CODEPAGE_ISUPPER, 0x00d2, 0x00f2 }, /* 0x00d2 */ + { CODEPAGE_ISUPPER, 0x00d3, 0x00f3 }, /* 0x00d3 */ + { CODEPAGE_ISUPPER, 0x00d4, 0x00f4 }, /* 0x00d4 */ + { CODEPAGE_ISUPPER, 0x00d5, 0x00f5 }, /* 0x00d5 */ + { CODEPAGE_ISUPPER, 0x00d6, 0x00f6 }, /* 0x00d6 */ + { CODEPAGE_ISNONE, 0x00d7, 0x00d7 }, /* 0x00d7 */ + { CODEPAGE_ISUPPER, 0x00d8, 0x00f8 }, /* 0x00d8 */ + { CODEPAGE_ISUPPER, 0x00d9, 0x00f9 }, /* 0x00d9 */ + { CODEPAGE_ISUPPER, 0x00da, 0x00fa }, /* 0x00da */ + { CODEPAGE_ISUPPER, 0x00db, 0x00fb }, /* 0x00db */ + { CODEPAGE_ISUPPER, 0x00dc, 0x00fc }, /* 0x00dc */ + { CODEPAGE_ISUPPER, 0x00dd, 0x00fd }, /* 0x00dd */ + { CODEPAGE_ISUPPER, 0x00de, 0x00fe }, /* 0x00de */ + { CODEPAGE_ISNONE, 0x00df, 0x00df }, /* 0x00df */ + { CODEPAGE_ISLOWER, 0x00c0, 0x00e0 }, /* 0x00e0 */ + { CODEPAGE_ISLOWER, 0x00c1, 0x00e1 }, /* 0x00e1 */ + { CODEPAGE_ISLOWER, 0x00c2, 0x00e2 }, /* 0x00e2 */ + { CODEPAGE_ISLOWER, 0x00c3, 0x00e3 }, /* 0x00e3 */ + { CODEPAGE_ISLOWER, 0x00c4, 0x00e4 }, /* 0x00e4 */ + { CODEPAGE_ISLOWER, 0x00c5, 0x00e5 }, /* 0x00e5 */ + { CODEPAGE_ISLOWER, 0x00c6, 0x00e6 }, /* 0x00e6 */ + { CODEPAGE_ISLOWER, 0x00c7, 0x00e7 }, /* 0x00e7 */ + { CODEPAGE_ISLOWER, 0x00c8, 0x00e8 }, /* 0x00e8 */ + { CODEPAGE_ISLOWER, 0x00c9, 0x00e9 }, /* 0x00e9 */ + { CODEPAGE_ISLOWER, 0x00ca, 0x00ea }, /* 0x00ea */ + { CODEPAGE_ISLOWER, 0x00cb, 0x00eb }, /* 0x00eb */ + { CODEPAGE_ISLOWER, 0x00cc, 0x00ec }, /* 0x00ec */ + { CODEPAGE_ISLOWER, 0x00cd, 0x00ed }, /* 0x00ed */ + { CODEPAGE_ISLOWER, 0x00ce, 0x00ee }, /* 0x00ee */ + { CODEPAGE_ISLOWER, 0x00cf, 0x00ef }, /* 0x00ef */ + { CODEPAGE_ISLOWER, 0x00d0, 0x00f0 }, /* 0x00f0 */ + { CODEPAGE_ISLOWER, 0x00d1, 0x00f1 }, /* 0x00f1 */ + { CODEPAGE_ISLOWER, 0x00d2, 0x00f2 }, /* 0x00f2 */ + { CODEPAGE_ISLOWER, 0x00d3, 0x00f3 }, /* 0x00f3 */ + { CODEPAGE_ISLOWER, 0x00d4, 0x00f4 }, /* 0x00f4 */ + { CODEPAGE_ISLOWER, 0x00d5, 0x00f5 }, /* 0x00f5 */ + { CODEPAGE_ISLOWER, 0x00d6, 0x00f6 }, /* 0x00f6 */ + { CODEPAGE_ISNONE, 0x00f7, 0x00f7 }, /* 0x00f7 */ + { CODEPAGE_ISLOWER, 0x00d8, 0x00f8 }, /* 0x00f8 */ + { CODEPAGE_ISLOWER, 0x00d9, 0x00f9 }, /* 0x00f9 */ + { CODEPAGE_ISLOWER, 0x00da, 0x00fa }, /* 0x00fa */ + { CODEPAGE_ISLOWER, 0x00db, 0x00fb }, /* 0x00fb */ + { CODEPAGE_ISLOWER, 0x00dc, 0x00fc }, /* 0x00fc */ + { CODEPAGE_ISLOWER, 0x00dd, 0x00fd }, /* 0x00fd */ + { CODEPAGE_ISLOWER, 0x00de, 0x00fe }, /* 0x00fe */ + { CODEPAGE_ISNONE, 0x00ff, 0x00ff } /* 0x00ff */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_CP_LATIN1_H */ diff --git a/usr/src/uts/common/smbsrv/cp_latin2.h b/usr/src/uts/common/smbsrv/cp_latin2.h new file mode 100644 index 000000000000..77f2e5a2a463 --- /dev/null +++ b/usr/src/uts/common/smbsrv/cp_latin2.h @@ -0,0 +1,314 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_CP_LATIN2_H +#define _SMBSRV_CP_LATIN2_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file specifies a codepage mapping for a given character set as + * specified below: + * + * This is the codepage for the Latin-2 Character Set + * (Central & Eastern Europe). This codepage defines + * values for the special characters needed + * for the written alphabets of the following languages: Czech, + * Hungarian, Polish, Romanian, Croatian, Slovak, + * Slovenian, and Sorbian + * The Latin-2 character set is also known as iso-8859-2 + * + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +codepage_t Latin2_codepage[256] = { + { CODEPAGE_ISNONE, 0x0000, 0x0000 }, /* 0x0000 */ + { CODEPAGE_ISNONE, 0x0001, 0x0001 }, /* 0x0001 */ + { CODEPAGE_ISNONE, 0x0002, 0x0002 }, /* 0x0002 */ + { CODEPAGE_ISNONE, 0x0003, 0x0003 }, /* 0x0003 */ + { CODEPAGE_ISNONE, 0x0004, 0x0004 }, /* 0x0004 */ + { CODEPAGE_ISNONE, 0x0005, 0x0005 }, /* 0x0005 */ + { CODEPAGE_ISNONE, 0x0006, 0x0006 }, /* 0x0006 */ + { CODEPAGE_ISNONE, 0x0007, 0x0007 }, /* 0x0007 */ + { CODEPAGE_ISNONE, 0x0008, 0x0008 }, /* 0x0008 */ + { CODEPAGE_ISNONE, 0x0009, 0x0009 }, /* 0x0009 */ + { CODEPAGE_ISNONE, 0x000a, 0x000a }, /* 0x000a */ + { CODEPAGE_ISNONE, 0x000b, 0x000b }, /* 0x000b */ + { CODEPAGE_ISNONE, 0x000c, 0x000c }, /* 0x000c */ + { CODEPAGE_ISNONE, 0x000d, 0x000d }, /* 0x000d */ + { CODEPAGE_ISNONE, 0x000e, 0x000e }, /* 0x000e */ + { CODEPAGE_ISNONE, 0x000f, 0x000f }, /* 0x000f */ + { CODEPAGE_ISNONE, 0x0010, 0x0010 }, /* 0x0010 */ + { CODEPAGE_ISNONE, 0x0011, 0x0011 }, /* 0x0011 */ + { CODEPAGE_ISNONE, 0x0012, 0x0012 }, /* 0x0012 */ + { CODEPAGE_ISNONE, 0x0013, 0x0013 }, /* 0x0013 */ + { CODEPAGE_ISNONE, 0x0014, 0x0014 }, /* 0x0014 */ + { CODEPAGE_ISNONE, 0x0015, 0x0015 }, /* 0x0015 */ + { CODEPAGE_ISNONE, 0x0016, 0x0016 }, /* 0x0016 */ + { CODEPAGE_ISNONE, 0x0017, 0x0017 }, /* 0x0017 */ + { CODEPAGE_ISNONE, 0x0018, 0x0018 }, /* 0x0018 */ + { CODEPAGE_ISNONE, 0x0019, 0x0019 }, /* 0x0019 */ + { CODEPAGE_ISNONE, 0x001a, 0x001a }, /* 0x001a */ + { CODEPAGE_ISNONE, 0x001b, 0x001b }, /* 0x001b */ + { CODEPAGE_ISNONE, 0x001c, 0x001c }, /* 0x001c */ + { CODEPAGE_ISNONE, 0x001d, 0x001d }, /* 0x001d */ + { CODEPAGE_ISNONE, 0x001e, 0x001e }, /* 0x001e */ + { CODEPAGE_ISNONE, 0x001f, 0x001f }, /* 0x001f */ + { CODEPAGE_ISNONE, 0x0020, 0x0020 }, /* 0x0020 */ + { CODEPAGE_ISNONE, 0x0021, 0x0021 }, /* 0x0021 */ + { CODEPAGE_ISNONE, 0x0022, 0x0022 }, /* 0x0022 */ + { CODEPAGE_ISNONE, 0x0023, 0x0023 }, /* 0x0023 */ + { CODEPAGE_ISNONE, 0x0024, 0x0024 }, /* 0x0024 */ + { CODEPAGE_ISNONE, 0x0025, 0x0025 }, /* 0x0025 */ + { CODEPAGE_ISNONE, 0x0026, 0x0026 }, /* 0x0026 */ + { CODEPAGE_ISNONE, 0x0027, 0x0027 }, /* 0x0027 */ + { CODEPAGE_ISNONE, 0x0028, 0x0028 }, /* 0x0028 */ + { CODEPAGE_ISNONE, 0x0029, 0x0029 }, /* 0x0029 */ + { CODEPAGE_ISNONE, 0x002a, 0x002a }, /* 0x002a */ + { CODEPAGE_ISNONE, 0x002b, 0x002b }, /* 0x002b */ + { CODEPAGE_ISNONE, 0x002c, 0x002c }, /* 0x002c */ + { CODEPAGE_ISNONE, 0x002d, 0x002d }, /* 0x002d */ + { CODEPAGE_ISNONE, 0x002e, 0x002e }, /* 0x002e */ + { CODEPAGE_ISNONE, 0x002f, 0x002f }, /* 0x002f */ + { CODEPAGE_ISNONE, 0x0030, 0x0030 }, /* 0x0030 */ + { CODEPAGE_ISNONE, 0x0031, 0x0031 }, /* 0x0031 */ + { CODEPAGE_ISNONE, 0x0032, 0x0032 }, /* 0x0032 */ + { CODEPAGE_ISNONE, 0x0033, 0x0033 }, /* 0x0033 */ + { CODEPAGE_ISNONE, 0x0034, 0x0034 }, /* 0x0034 */ + { CODEPAGE_ISNONE, 0x0035, 0x0035 }, /* 0x0035 */ + { CODEPAGE_ISNONE, 0x0036, 0x0036 }, /* 0x0036 */ + { CODEPAGE_ISNONE, 0x0037, 0x0037 }, /* 0x0037 */ + { CODEPAGE_ISNONE, 0x0038, 0x0038 }, /* 0x0038 */ + { CODEPAGE_ISNONE, 0x0039, 0x0039 }, /* 0x0039 */ + { CODEPAGE_ISNONE, 0x003a, 0x003a }, /* 0x003a */ + { CODEPAGE_ISNONE, 0x003b, 0x003b }, /* 0x003b */ + { CODEPAGE_ISNONE, 0x003c, 0x003c }, /* 0x003c */ + { CODEPAGE_ISNONE, 0x003d, 0x003d }, /* 0x003d */ + { CODEPAGE_ISNONE, 0x003e, 0x003e }, /* 0x003e */ + { CODEPAGE_ISNONE, 0x003f, 0x003f }, /* 0x003f */ + { CODEPAGE_ISNONE, 0x0040, 0x0040 }, /* 0x0040 */ + { CODEPAGE_ISUPPER, 0x0041, 0x0061 }, /* 0x0041 */ + { CODEPAGE_ISUPPER, 0x0042, 0x0062 }, /* 0x0042 */ + { CODEPAGE_ISUPPER, 0x0043, 0x0063 }, /* 0x0043 */ + { CODEPAGE_ISUPPER, 0x0044, 0x0064 }, /* 0x0044 */ + { CODEPAGE_ISUPPER, 0x0045, 0x0065 }, /* 0x0045 */ + { CODEPAGE_ISUPPER, 0x0046, 0x0066 }, /* 0x0046 */ + { CODEPAGE_ISUPPER, 0x0047, 0x0067 }, /* 0x0047 */ + { CODEPAGE_ISUPPER, 0x0048, 0x0068 }, /* 0x0048 */ + { CODEPAGE_ISUPPER, 0x0049, 0x0069 }, /* 0x0049 */ + { CODEPAGE_ISUPPER, 0x004a, 0x006a }, /* 0x004a */ + { CODEPAGE_ISUPPER, 0x004b, 0x006b }, /* 0x004b */ + { CODEPAGE_ISUPPER, 0x004c, 0x006c }, /* 0x004c */ + { CODEPAGE_ISUPPER, 0x004d, 0x006d }, /* 0x004d */ + { CODEPAGE_ISUPPER, 0x004e, 0x006e }, /* 0x004e */ + { CODEPAGE_ISUPPER, 0x004f, 0x006f }, /* 0x004f */ + { CODEPAGE_ISUPPER, 0x0050, 0x0070 }, /* 0x0050 */ + { CODEPAGE_ISUPPER, 0x0051, 0x0071 }, /* 0x0051 */ + { CODEPAGE_ISUPPER, 0x0052, 0x0072 }, /* 0x0052 */ + { CODEPAGE_ISUPPER, 0x0053, 0x0073 }, /* 0x0053 */ + { CODEPAGE_ISUPPER, 0x0054, 0x0074 }, /* 0x0054 */ + { CODEPAGE_ISUPPER, 0x0055, 0x0075 }, /* 0x0055 */ + { CODEPAGE_ISUPPER, 0x0056, 0x0076 }, /* 0x0056 */ + { CODEPAGE_ISUPPER, 0x0057, 0x0077 }, /* 0x0057 */ + { CODEPAGE_ISUPPER, 0x0058, 0x0078 }, /* 0x0058 */ + { CODEPAGE_ISUPPER, 0x0059, 0x0079 }, /* 0x0059 */ + { CODEPAGE_ISUPPER, 0x005a, 0x007a }, /* 0x005a */ + { CODEPAGE_ISNONE, 0x005b, 0x005b }, /* 0x005b */ + { CODEPAGE_ISNONE, 0x005c, 0x005c }, /* 0x005c */ + { CODEPAGE_ISNONE, 0x005d, 0x005d }, /* 0x005d */ + { CODEPAGE_ISNONE, 0x005e, 0x005e }, /* 0x005e */ + { CODEPAGE_ISNONE, 0x005f, 0x005f }, /* 0x005f */ + { CODEPAGE_ISNONE, 0x0060, 0x0060 }, /* 0x0060 */ + { CODEPAGE_ISLOWER, 0x0041, 0x0061 }, /* 0x0061 */ + { CODEPAGE_ISLOWER, 0x0042, 0x0062 }, /* 0x0062 */ + { CODEPAGE_ISLOWER, 0x0043, 0x0063 }, /* 0x0063 */ + { CODEPAGE_ISLOWER, 0x0044, 0x0064 }, /* 0x0064 */ + { CODEPAGE_ISLOWER, 0x0045, 0x0065 }, /* 0x0065 */ + { CODEPAGE_ISLOWER, 0x0046, 0x0066 }, /* 0x0066 */ + { CODEPAGE_ISLOWER, 0x0047, 0x0067 }, /* 0x0067 */ + { CODEPAGE_ISLOWER, 0x0048, 0x0068 }, /* 0x0068 */ + { CODEPAGE_ISLOWER, 0x0049, 0x0069 }, /* 0x0069 */ + { CODEPAGE_ISLOWER, 0x004a, 0x006a }, /* 0x006a */ + { CODEPAGE_ISLOWER, 0x004b, 0x006b }, /* 0x006b */ + { CODEPAGE_ISLOWER, 0x004c, 0x006c }, /* 0x006c */ + { CODEPAGE_ISLOWER, 0x004d, 0x006d }, /* 0x006d */ + { CODEPAGE_ISLOWER, 0x004e, 0x006e }, /* 0x006e */ + { CODEPAGE_ISLOWER, 0x004f, 0x006f }, /* 0x006f */ + { CODEPAGE_ISLOWER, 0x0050, 0x0070 }, /* 0x0070 */ + { CODEPAGE_ISLOWER, 0x0051, 0x0071 }, /* 0x0071 */ + { CODEPAGE_ISLOWER, 0x0052, 0x0072 }, /* 0x0072 */ + { CODEPAGE_ISLOWER, 0x0053, 0x0073 }, /* 0x0073 */ + { CODEPAGE_ISLOWER, 0x0054, 0x0074 }, /* 0x0074 */ + { CODEPAGE_ISLOWER, 0x0055, 0x0075 }, /* 0x0075 */ + { CODEPAGE_ISLOWER, 0x0056, 0x0076 }, /* 0x0076 */ + { CODEPAGE_ISLOWER, 0x0057, 0x0077 }, /* 0x0077 */ + { CODEPAGE_ISLOWER, 0x0058, 0x0078 }, /* 0x0078 */ + { CODEPAGE_ISLOWER, 0x0059, 0x0079 }, /* 0x0079 */ + { CODEPAGE_ISLOWER, 0x005a, 0x007a }, /* 0x007a */ + { CODEPAGE_ISNONE, 0x007b, 0x007b }, /* 0x007b */ + { CODEPAGE_ISNONE, 0x007c, 0x007c }, /* 0x007c */ + { CODEPAGE_ISNONE, 0x007d, 0x007d }, /* 0x007d */ + { CODEPAGE_ISNONE, 0x007e, 0x007e }, /* 0x007e */ + { CODEPAGE_ISNONE, 0x007f, 0x007f }, /* 0x007f */ + { CODEPAGE_ISNONE, 0x0080, 0x0080 }, /* 0x0080 */ + { CODEPAGE_ISNONE, 0x0081, 0x0081 }, /* 0x0081 */ + { CODEPAGE_ISNONE, 0x0082, 0x0082 }, /* 0x0082 */ + { CODEPAGE_ISNONE, 0x0083, 0x0083 }, /* 0x0083 */ + { CODEPAGE_ISNONE, 0x0084, 0x0084 }, /* 0x0084 */ + { CODEPAGE_ISNONE, 0x0085, 0x0085 }, /* 0x0085 */ + { CODEPAGE_ISNONE, 0x0086, 0x0086 }, /* 0x0086 */ + { CODEPAGE_ISNONE, 0x0087, 0x0087 }, /* 0x0087 */ + { CODEPAGE_ISNONE, 0x0088, 0x0088 }, /* 0x0088 */ + { CODEPAGE_ISNONE, 0x0089, 0x0089 }, /* 0x0089 */ + { CODEPAGE_ISNONE, 0x008a, 0x008a }, /* 0x008a */ + { CODEPAGE_ISNONE, 0x008b, 0x008b }, /* 0x008b */ + { CODEPAGE_ISNONE, 0x008c, 0x008c }, /* 0x008c */ + { CODEPAGE_ISNONE, 0x008d, 0x008d }, /* 0x008d */ + { CODEPAGE_ISNONE, 0x008e, 0x008e }, /* 0x008e */ + { CODEPAGE_ISNONE, 0x008f, 0x008f }, /* 0x008f */ + { CODEPAGE_ISNONE, 0x0090, 0x0090 }, /* 0x0090 */ + { CODEPAGE_ISNONE, 0x0091, 0x0091 }, /* 0x0091 */ + { CODEPAGE_ISNONE, 0x0092, 0x0092 }, /* 0x0092 */ + { CODEPAGE_ISNONE, 0x0093, 0x0093 }, /* 0x0093 */ + { CODEPAGE_ISNONE, 0x0094, 0x0094 }, /* 0x0094 */ + { CODEPAGE_ISNONE, 0x0095, 0x0095 }, /* 0x0095 */ + { CODEPAGE_ISNONE, 0x0096, 0x0096 }, /* 0x0096 */ + { CODEPAGE_ISNONE, 0x0097, 0x0097 }, /* 0x0097 */ + { CODEPAGE_ISNONE, 0x0098, 0x0098 }, /* 0x0098 */ + { CODEPAGE_ISNONE, 0x0099, 0x0099 }, /* 0x0099 */ + { CODEPAGE_ISNONE, 0x009a, 0x009a }, /* 0x009a */ + { CODEPAGE_ISNONE, 0x009b, 0x009b }, /* 0x009b */ + { CODEPAGE_ISNONE, 0x009c, 0x009c }, /* 0x009c */ + { CODEPAGE_ISNONE, 0x009d, 0x009d }, /* 0x009d */ + { CODEPAGE_ISNONE, 0x009e, 0x009e }, /* 0x009e */ + { CODEPAGE_ISNONE, 0x009f, 0x009f }, /* 0x009f */ + { CODEPAGE_ISNONE, 0x00a0, 0x00a0 }, /* 0x00a0 */ + { CODEPAGE_ISUPPER, 0x00a1, 0x00b1 }, /* 0x00a1 */ + { CODEPAGE_ISNONE, 0x00a2, 0x00a2 }, /* 0x00a2 */ + { CODEPAGE_ISUPPER, 0x00a3, 0x00b3 }, /* 0x00a3 */ + { CODEPAGE_ISNONE, 0x00a4, 0x00a4 }, /* 0x00a4 */ + { CODEPAGE_ISUPPER, 0x00a5, 0x00b5 }, /* 0x00a5 */ + { CODEPAGE_ISUPPER, 0x00a6, 0x00b6 }, /* 0x00a6 */ + { CODEPAGE_ISNONE, 0x00a7, 0x00a7 }, /* 0x00a7 */ + { CODEPAGE_ISNONE, 0x00a8, 0x00a8 }, /* 0x00a8 */ + { CODEPAGE_ISUPPER, 0x00a9, 0x00b9 }, /* 0x00a9 */ + { CODEPAGE_ISUPPER, 0x00aa, 0x00ba }, /* 0x00aa */ + { CODEPAGE_ISUPPER, 0x00ab, 0x00bb }, /* 0x00ab */ + { CODEPAGE_ISUPPER, 0x00ac, 0x00bc }, /* 0x00ac */ + { CODEPAGE_ISNONE, 0x00ad, 0x00ad }, /* 0x00ad */ + { CODEPAGE_ISUPPER, 0x00ae, 0x00be }, /* 0x00ae */ + { CODEPAGE_ISUPPER, 0x00af, 0x00bf }, /* 0x00af */ + { CODEPAGE_ISNONE, 0x00b0, 0x00b0 }, /* 0x00b0 */ + { CODEPAGE_ISLOWER, 0x00a1, 0x00b1 }, /* 0x00b1 */ + { CODEPAGE_ISNONE, 0x00b2, 0x00b2 }, /* 0x00b2 */ + { CODEPAGE_ISLOWER, 0x00a3, 0x00b3 }, /* 0x00b3 */ + { CODEPAGE_ISNONE, 0x00b4, 0x00b4 }, /* 0x00b4 */ + { CODEPAGE_ISLOWER, 0x00a5, 0x00b5 }, /* 0x00b5 */ + { CODEPAGE_ISLOWER, 0x00a6, 0x00b6 }, /* 0x00b6 */ + { CODEPAGE_ISNONE, 0x00b7, 0x00b7 }, /* 0x00b7 */ + { CODEPAGE_ISNONE, 0x00b8, 0x00b8 }, /* 0x00b8 */ + { CODEPAGE_ISLOWER, 0x00a9, 0x00b9 }, /* 0x00b9 */ + { CODEPAGE_ISLOWER, 0x00aa, 0x00ba }, /* 0x00ba */ + { CODEPAGE_ISLOWER, 0x00ab, 0x00bb }, /* 0x00bb */ + { CODEPAGE_ISLOWER, 0x00ac, 0x00bc }, /* 0x00bc */ + { CODEPAGE_ISNONE, 0x00bd, 0x00bd }, /* 0x00bd */ + { CODEPAGE_ISLOWER, 0x00ae, 0x00be }, /* 0x00be */ + { CODEPAGE_ISLOWER, 0x00af, 0x00bf }, /* 0x00bf */ + { CODEPAGE_ISUPPER, 0x00c0, 0x00e0 }, /* 0x00c0 */ + { CODEPAGE_ISUPPER, 0x00c1, 0x00e1 }, /* 0x00c1 */ + { CODEPAGE_ISUPPER, 0x00c2, 0x00e2 }, /* 0x00c2 */ + { CODEPAGE_ISUPPER, 0x00c3, 0x00e3 }, /* 0x00c3 */ + { CODEPAGE_ISUPPER, 0x00c4, 0x00e4 }, /* 0x00c4 */ + { CODEPAGE_ISUPPER, 0x00c5, 0x00e5 }, /* 0x00c5 */ + { CODEPAGE_ISUPPER, 0x00c6, 0x00e6 }, /* 0x00c6 */ + { CODEPAGE_ISUPPER, 0x00c7, 0x00e7 }, /* 0x00c7 */ + { CODEPAGE_ISUPPER, 0x00c8, 0x00e8 }, /* 0x00c8 */ + { CODEPAGE_ISUPPER, 0x00c9, 0x00e9 }, /* 0x00c9 */ + { CODEPAGE_ISUPPER, 0x00ca, 0x00ea }, /* 0x00ca */ + { CODEPAGE_ISUPPER, 0x00cb, 0x00eb }, /* 0x00cb */ + { CODEPAGE_ISUPPER, 0x00cc, 0x00ec }, /* 0x00cc */ + { CODEPAGE_ISUPPER, 0x00cd, 0x00ed }, /* 0x00cd */ + { CODEPAGE_ISUPPER, 0x00ce, 0x00ee }, /* 0x00ce */ + { CODEPAGE_ISUPPER, 0x00cf, 0x00ef }, /* 0x00cf */ + { CODEPAGE_ISUPPER, 0x00d0, 0x00f0 }, /* 0x00d0 */ + { CODEPAGE_ISUPPER, 0x00d1, 0x00f1 }, /* 0x00d1 */ + { CODEPAGE_ISUPPER, 0x00d2, 0x00f2 }, /* 0x00d2 */ + { CODEPAGE_ISUPPER, 0x00d3, 0x00f3 }, /* 0x00d3 */ + { CODEPAGE_ISUPPER, 0x00d4, 0x00f4 }, /* 0x00d4 */ + { CODEPAGE_ISUPPER, 0x00d5, 0x00f5 }, /* 0x00d5 */ + { CODEPAGE_ISUPPER, 0x00d6, 0x00f6 }, /* 0x00d6 */ + { CODEPAGE_ISNONE, 0x00d7, 0x00d7 }, /* 0x00d7 */ + { CODEPAGE_ISUPPER, 0x00d8, 0x00f8 }, /* 0x00d8 */ + { CODEPAGE_ISUPPER, 0x00d9, 0x00f9 }, /* 0x00d9 */ + { CODEPAGE_ISUPPER, 0x00da, 0x00fa }, /* 0x00da */ + { CODEPAGE_ISUPPER, 0x00db, 0x00fb }, /* 0x00db */ + { CODEPAGE_ISUPPER, 0x00dc, 0x00fc }, /* 0x00dc */ + { CODEPAGE_ISUPPER, 0x00dd, 0x00fd }, /* 0x00dd */ + { CODEPAGE_ISUPPER, 0x00de, 0x00fe }, /* 0x00de */ + { CODEPAGE_ISNONE, 0x00df, 0x00df }, /* 0x00df */ + { CODEPAGE_ISLOWER, 0x00c0, 0x00e0 }, /* 0x00e0 */ + { CODEPAGE_ISLOWER, 0x00c1, 0x00e1 }, /* 0x00e1 */ + { CODEPAGE_ISLOWER, 0x00c2, 0x00e2 }, /* 0x00e2 */ + { CODEPAGE_ISLOWER, 0x00c3, 0x00e3 }, /* 0x00e3 */ + { CODEPAGE_ISLOWER, 0x00c4, 0x00e4 }, /* 0x00e4 */ + { CODEPAGE_ISLOWER, 0x00c5, 0x00e5 }, /* 0x00e5 */ + { CODEPAGE_ISLOWER, 0x00c6, 0x00e6 }, /* 0x00e6 */ + { CODEPAGE_ISLOWER, 0x00c7, 0x00e7 }, /* 0x00e7 */ + { CODEPAGE_ISLOWER, 0x00c8, 0x00e8 }, /* 0x00e8 */ + { CODEPAGE_ISLOWER, 0x00c9, 0x00e9 }, /* 0x00e9 */ + { CODEPAGE_ISLOWER, 0x00ca, 0x00ea }, /* 0x00ea */ + { CODEPAGE_ISLOWER, 0x00cb, 0x00eb }, /* 0x00eb */ + { CODEPAGE_ISLOWER, 0x00cc, 0x00ec }, /* 0x00ec */ + { CODEPAGE_ISLOWER, 0x00cd, 0x00ed }, /* 0x00ed */ + { CODEPAGE_ISLOWER, 0x00ce, 0x00ee }, /* 0x00ee */ + { CODEPAGE_ISLOWER, 0x00cf, 0x00ef }, /* 0x00ef */ + { CODEPAGE_ISLOWER, 0x00d0, 0x00f0 }, /* 0x00f0 */ + { CODEPAGE_ISLOWER, 0x00d1, 0x00f1 }, /* 0x00f1 */ + { CODEPAGE_ISLOWER, 0x00d2, 0x00f2 }, /* 0x00f2 */ + { CODEPAGE_ISLOWER, 0x00d3, 0x00f3 }, /* 0x00f3 */ + { CODEPAGE_ISLOWER, 0x00d4, 0x00f4 }, /* 0x00f4 */ + { CODEPAGE_ISLOWER, 0x00d5, 0x00f5 }, /* 0x00f5 */ + { CODEPAGE_ISLOWER, 0x00d6, 0x00f6 }, /* 0x00f6 */ + { CODEPAGE_ISNONE, 0x00f7, 0x00f7 }, /* 0x00f7 */ + { CODEPAGE_ISLOWER, 0x00d8, 0x00f8 }, /* 0x00f8 */ + { CODEPAGE_ISLOWER, 0x00d9, 0x00f9 }, /* 0x00f9 */ + { CODEPAGE_ISLOWER, 0x00da, 0x00fa }, /* 0x00fa */ + { CODEPAGE_ISLOWER, 0x00db, 0x00fb }, /* 0x00fb */ + { CODEPAGE_ISLOWER, 0x00dc, 0x00fc }, /* 0x00fc */ + { CODEPAGE_ISLOWER, 0x00dd, 0x00fd }, /* 0x00fd */ + { CODEPAGE_ISLOWER, 0x00de, 0x00fe }, /* 0x00fe */ + { CODEPAGE_ISNONE, 0x00ff, 0x00ff } /* 0x00ff */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_CP_LATIN2_H */ diff --git a/usr/src/uts/common/smbsrv/cp_latin3.h b/usr/src/uts/common/smbsrv/cp_latin3.h new file mode 100644 index 000000000000..afc7ae4355ce --- /dev/null +++ b/usr/src/uts/common/smbsrv/cp_latin3.h @@ -0,0 +1,311 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_CP_LATIN3_H +#define _SMBSRV_CP_LATIN3_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file specifies a codepage mapping for a given character set as + * specified below: + * + * This is the codepage for Latin-3 Character Set + * This codepage defines values for the special characters + * needed for the written alphabets of the following + * languages: Esperanto and Maltese. + * The Latin-3 character set is also known as iso-8859-3 + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +codepage_t Latin3_codepage[256] = { + { CODEPAGE_ISNONE, 0x0000, 0x0000 }, /* 0x0000 */ + { CODEPAGE_ISNONE, 0x0001, 0x0001 }, /* 0x0001 */ + { CODEPAGE_ISNONE, 0x0002, 0x0002 }, /* 0x0002 */ + { CODEPAGE_ISNONE, 0x0003, 0x0003 }, /* 0x0003 */ + { CODEPAGE_ISNONE, 0x0004, 0x0004 }, /* 0x0004 */ + { CODEPAGE_ISNONE, 0x0005, 0x0005 }, /* 0x0005 */ + { CODEPAGE_ISNONE, 0x0006, 0x0006 }, /* 0x0006 */ + { CODEPAGE_ISNONE, 0x0007, 0x0007 }, /* 0x0007 */ + { CODEPAGE_ISNONE, 0x0008, 0x0008 }, /* 0x0008 */ + { CODEPAGE_ISNONE, 0x0009, 0x0009 }, /* 0x0009 */ + { CODEPAGE_ISNONE, 0x000a, 0x000a }, /* 0x000a */ + { CODEPAGE_ISNONE, 0x000b, 0x000b }, /* 0x000b */ + { CODEPAGE_ISNONE, 0x000c, 0x000c }, /* 0x000c */ + { CODEPAGE_ISNONE, 0x000d, 0x000d }, /* 0x000d */ + { CODEPAGE_ISNONE, 0x000e, 0x000e }, /* 0x000e */ + { CODEPAGE_ISNONE, 0x000f, 0x000f }, /* 0x000f */ + { CODEPAGE_ISNONE, 0x0010, 0x0010 }, /* 0x0010 */ + { CODEPAGE_ISNONE, 0x0011, 0x0011 }, /* 0x0011 */ + { CODEPAGE_ISNONE, 0x0012, 0x0012 }, /* 0x0012 */ + { CODEPAGE_ISNONE, 0x0013, 0x0013 }, /* 0x0013 */ + { CODEPAGE_ISNONE, 0x0014, 0x0014 }, /* 0x0014 */ + { CODEPAGE_ISNONE, 0x0015, 0x0015 }, /* 0x0015 */ + { CODEPAGE_ISNONE, 0x0016, 0x0016 }, /* 0x0016 */ + { CODEPAGE_ISNONE, 0x0017, 0x0017 }, /* 0x0017 */ + { CODEPAGE_ISNONE, 0x0018, 0x0018 }, /* 0x0018 */ + { CODEPAGE_ISNONE, 0x0019, 0x0019 }, /* 0x0019 */ + { CODEPAGE_ISNONE, 0x001a, 0x001a }, /* 0x001a */ + { CODEPAGE_ISNONE, 0x001b, 0x001b }, /* 0x001b */ + { CODEPAGE_ISNONE, 0x001c, 0x001c }, /* 0x001c */ + { CODEPAGE_ISNONE, 0x001d, 0x001d }, /* 0x001d */ + { CODEPAGE_ISNONE, 0x001e, 0x001e }, /* 0x001e */ + { CODEPAGE_ISNONE, 0x001f, 0x001f }, /* 0x001f */ + { CODEPAGE_ISNONE, 0x0020, 0x0020 }, /* 0x0020 */ + { CODEPAGE_ISNONE, 0x0021, 0x0021 }, /* 0x0021 */ + { CODEPAGE_ISNONE, 0x0022, 0x0022 }, /* 0x0022 */ + { CODEPAGE_ISNONE, 0x0023, 0x0023 }, /* 0x0023 */ + { CODEPAGE_ISNONE, 0x0024, 0x0024 }, /* 0x0024 */ + { CODEPAGE_ISNONE, 0x0025, 0x0025 }, /* 0x0025 */ + { CODEPAGE_ISNONE, 0x0026, 0x0026 }, /* 0x0026 */ + { CODEPAGE_ISNONE, 0x0027, 0x0027 }, /* 0x0027 */ + { CODEPAGE_ISNONE, 0x0028, 0x0028 }, /* 0x0028 */ + { CODEPAGE_ISNONE, 0x0029, 0x0029 }, /* 0x0029 */ + { CODEPAGE_ISNONE, 0x002a, 0x002a }, /* 0x002a */ + { CODEPAGE_ISNONE, 0x002b, 0x002b }, /* 0x002b */ + { CODEPAGE_ISNONE, 0x002c, 0x002c }, /* 0x002c */ + { CODEPAGE_ISNONE, 0x002d, 0x002d }, /* 0x002d */ + { CODEPAGE_ISNONE, 0x002e, 0x002e }, /* 0x002e */ + { CODEPAGE_ISNONE, 0x002f, 0x002f }, /* 0x002f */ + { CODEPAGE_ISNONE, 0x0030, 0x0030 }, /* 0x0030 */ + { CODEPAGE_ISNONE, 0x0031, 0x0031 }, /* 0x0031 */ + { CODEPAGE_ISNONE, 0x0032, 0x0032 }, /* 0x0032 */ + { CODEPAGE_ISNONE, 0x0033, 0x0033 }, /* 0x0033 */ + { CODEPAGE_ISNONE, 0x0034, 0x0034 }, /* 0x0034 */ + { CODEPAGE_ISNONE, 0x0035, 0x0035 }, /* 0x0035 */ + { CODEPAGE_ISNONE, 0x0036, 0x0036 }, /* 0x0036 */ + { CODEPAGE_ISNONE, 0x0037, 0x0037 }, /* 0x0037 */ + { CODEPAGE_ISNONE, 0x0038, 0x0038 }, /* 0x0038 */ + { CODEPAGE_ISNONE, 0x0039, 0x0039 }, /* 0x0039 */ + { CODEPAGE_ISNONE, 0x003a, 0x003a }, /* 0x003a */ + { CODEPAGE_ISNONE, 0x003b, 0x003b }, /* 0x003b */ + { CODEPAGE_ISNONE, 0x003c, 0x003c }, /* 0x003c */ + { CODEPAGE_ISNONE, 0x003d, 0x003d }, /* 0x003d */ + { CODEPAGE_ISNONE, 0x003e, 0x003e }, /* 0x003e */ + { CODEPAGE_ISNONE, 0x003f, 0x003f }, /* 0x003f */ + { CODEPAGE_ISNONE, 0x0040, 0x0040 }, /* 0x0040 */ + { CODEPAGE_ISUPPER, 0x0041, 0x0061 }, /* 0x0041 */ + { CODEPAGE_ISUPPER, 0x0042, 0x0062 }, /* 0x0042 */ + { CODEPAGE_ISUPPER, 0x0043, 0x0063 }, /* 0x0043 */ + { CODEPAGE_ISUPPER, 0x0044, 0x0064 }, /* 0x0044 */ + { CODEPAGE_ISUPPER, 0x0045, 0x0065 }, /* 0x0045 */ + { CODEPAGE_ISUPPER, 0x0046, 0x0066 }, /* 0x0046 */ + { CODEPAGE_ISUPPER, 0x0047, 0x0067 }, /* 0x0047 */ + { CODEPAGE_ISUPPER, 0x0048, 0x0068 }, /* 0x0048 */ + { CODEPAGE_ISUPPER, 0x0049, 0x0069 }, /* 0x0049 */ + { CODEPAGE_ISUPPER, 0x004a, 0x006a }, /* 0x004a */ + { CODEPAGE_ISUPPER, 0x004b, 0x006b }, /* 0x004b */ + { CODEPAGE_ISUPPER, 0x004c, 0x006c }, /* 0x004c */ + { CODEPAGE_ISUPPER, 0x004d, 0x006d }, /* 0x004d */ + { CODEPAGE_ISUPPER, 0x004e, 0x006e }, /* 0x004e */ + { CODEPAGE_ISUPPER, 0x004f, 0x006f }, /* 0x004f */ + { CODEPAGE_ISUPPER, 0x0050, 0x0070 }, /* 0x0050 */ + { CODEPAGE_ISUPPER, 0x0051, 0x0071 }, /* 0x0051 */ + { CODEPAGE_ISUPPER, 0x0052, 0x0072 }, /* 0x0052 */ + { CODEPAGE_ISUPPER, 0x0053, 0x0073 }, /* 0x0053 */ + { CODEPAGE_ISUPPER, 0x0054, 0x0074 }, /* 0x0054 */ + { CODEPAGE_ISUPPER, 0x0055, 0x0075 }, /* 0x0055 */ + { CODEPAGE_ISUPPER, 0x0056, 0x0076 }, /* 0x0056 */ + { CODEPAGE_ISUPPER, 0x0057, 0x0077 }, /* 0x0057 */ + { CODEPAGE_ISUPPER, 0x0058, 0x0078 }, /* 0x0058 */ + { CODEPAGE_ISUPPER, 0x0059, 0x0079 }, /* 0x0059 */ + { CODEPAGE_ISUPPER, 0x005a, 0x007a }, /* 0x005a */ + { CODEPAGE_ISNONE, 0x005b, 0x005b }, /* 0x005b */ + { CODEPAGE_ISNONE, 0x005c, 0x005c }, /* 0x005c */ + { CODEPAGE_ISNONE, 0x005d, 0x005d }, /* 0x005d */ + { CODEPAGE_ISNONE, 0x005e, 0x005e }, /* 0x005e */ + { CODEPAGE_ISNONE, 0x005f, 0x005f }, /* 0x005f */ + { CODEPAGE_ISNONE, 0x0060, 0x0060 }, /* 0x0060 */ + { CODEPAGE_ISLOWER, 0x0041, 0x0061 }, /* 0x0061 */ + { CODEPAGE_ISLOWER, 0x0042, 0x0062 }, /* 0x0062 */ + { CODEPAGE_ISLOWER, 0x0043, 0x0063 }, /* 0x0063 */ + { CODEPAGE_ISLOWER, 0x0044, 0x0064 }, /* 0x0064 */ + { CODEPAGE_ISLOWER, 0x0045, 0x0065 }, /* 0x0065 */ + { CODEPAGE_ISLOWER, 0x0046, 0x0066 }, /* 0x0066 */ + { CODEPAGE_ISLOWER, 0x0047, 0x0067 }, /* 0x0067 */ + { CODEPAGE_ISLOWER, 0x0048, 0x0068 }, /* 0x0068 */ + { CODEPAGE_ISLOWER, 0x0049, 0x0069 }, /* 0x0069 */ + { CODEPAGE_ISLOWER, 0x004a, 0x006a }, /* 0x006a */ + { CODEPAGE_ISLOWER, 0x004b, 0x006b }, /* 0x006b */ + { CODEPAGE_ISLOWER, 0x004c, 0x006c }, /* 0x006c */ + { CODEPAGE_ISLOWER, 0x004d, 0x006d }, /* 0x006d */ + { CODEPAGE_ISLOWER, 0x004e, 0x006e }, /* 0x006e */ + { CODEPAGE_ISLOWER, 0x004f, 0x006f }, /* 0x006f */ + { CODEPAGE_ISLOWER, 0x0050, 0x0070 }, /* 0x0070 */ + { CODEPAGE_ISLOWER, 0x0051, 0x0071 }, /* 0x0071 */ + { CODEPAGE_ISLOWER, 0x0052, 0x0072 }, /* 0x0072 */ + { CODEPAGE_ISLOWER, 0x0053, 0x0073 }, /* 0x0073 */ + { CODEPAGE_ISLOWER, 0x0054, 0x0074 }, /* 0x0074 */ + { CODEPAGE_ISLOWER, 0x0055, 0x0075 }, /* 0x0075 */ + { CODEPAGE_ISLOWER, 0x0056, 0x0076 }, /* 0x0076 */ + { CODEPAGE_ISLOWER, 0x0057, 0x0077 }, /* 0x0077 */ + { CODEPAGE_ISLOWER, 0x0058, 0x0078 }, /* 0x0078 */ + { CODEPAGE_ISLOWER, 0x0059, 0x0079 }, /* 0x0079 */ + { CODEPAGE_ISLOWER, 0x005a, 0x007a }, /* 0x007a */ + { CODEPAGE_ISNONE, 0x007b, 0x007b }, /* 0x007b */ + { CODEPAGE_ISNONE, 0x007c, 0x007c }, /* 0x007c */ + { CODEPAGE_ISNONE, 0x007d, 0x007d }, /* 0x007d */ + { CODEPAGE_ISNONE, 0x007e, 0x007e }, /* 0x007e */ + { CODEPAGE_ISNONE, 0x007f, 0x007f }, /* 0x007f */ + { CODEPAGE_ISNONE, 0x0080, 0x0080 }, /* 0x0080 */ + { CODEPAGE_ISNONE, 0x0081, 0x0081 }, /* 0x0081 */ + { CODEPAGE_ISNONE, 0x0082, 0x0082 }, /* 0x0082 */ + { CODEPAGE_ISNONE, 0x0083, 0x0083 }, /* 0x0083 */ + { CODEPAGE_ISNONE, 0x0084, 0x0084 }, /* 0x0084 */ + { CODEPAGE_ISNONE, 0x0085, 0x0085 }, /* 0x0085 */ + { CODEPAGE_ISNONE, 0x0086, 0x0086 }, /* 0x0086 */ + { CODEPAGE_ISNONE, 0x0087, 0x0087 }, /* 0x0087 */ + { CODEPAGE_ISNONE, 0x0088, 0x0088 }, /* 0x0088 */ + { CODEPAGE_ISNONE, 0x0089, 0x0089 }, /* 0x0089 */ + { CODEPAGE_ISNONE, 0x008a, 0x008a }, /* 0x008a */ + { CODEPAGE_ISNONE, 0x008b, 0x008b }, /* 0x008b */ + { CODEPAGE_ISNONE, 0x008c, 0x008c }, /* 0x008c */ + { CODEPAGE_ISNONE, 0x008d, 0x008d }, /* 0x008d */ + { CODEPAGE_ISNONE, 0x008e, 0x008e }, /* 0x008e */ + { CODEPAGE_ISNONE, 0x008f, 0x008f }, /* 0x008f */ + { CODEPAGE_ISNONE, 0x0090, 0x0090 }, /* 0x0090 */ + { CODEPAGE_ISNONE, 0x0091, 0x0091 }, /* 0x0091 */ + { CODEPAGE_ISNONE, 0x0092, 0x0092 }, /* 0x0092 */ + { CODEPAGE_ISNONE, 0x0093, 0x0093 }, /* 0x0093 */ + { CODEPAGE_ISNONE, 0x0094, 0x0094 }, /* 0x0094 */ + { CODEPAGE_ISNONE, 0x0095, 0x0095 }, /* 0x0095 */ + { CODEPAGE_ISNONE, 0x0096, 0x0096 }, /* 0x0096 */ + { CODEPAGE_ISNONE, 0x0097, 0x0097 }, /* 0x0097 */ + { CODEPAGE_ISNONE, 0x0098, 0x0098 }, /* 0x0098 */ + { CODEPAGE_ISNONE, 0x0099, 0x0099 }, /* 0x0099 */ + { CODEPAGE_ISNONE, 0x009a, 0x009a }, /* 0x009a */ + { CODEPAGE_ISNONE, 0x009b, 0x009b }, /* 0x009b */ + { CODEPAGE_ISNONE, 0x009c, 0x009c }, /* 0x009c */ + { CODEPAGE_ISNONE, 0x009d, 0x009d }, /* 0x009d */ + { CODEPAGE_ISNONE, 0x009e, 0x009e }, /* 0x009e */ + { CODEPAGE_ISNONE, 0x009f, 0x009f }, /* 0x009f */ + { CODEPAGE_ISNONE, 0x00a0, 0x00a0 }, /* 0x00a0 */ + { CODEPAGE_ISUPPER, 0x00a1, 0x00b1 }, /* 0x00a1 */ + { CODEPAGE_ISNONE, 0x00a2, 0x00a2 }, /* 0x00a2 */ + { CODEPAGE_ISNONE, 0x00a3, 0x00a3 }, /* 0x00a3 */ + { CODEPAGE_ISNONE, 0x00a4, 0x00a4 }, /* 0x00a4 */ + { CODEPAGE_ISNONE, 0x00a5, 0x00a5 }, /* 0x00a5 */ + { CODEPAGE_ISUPPER, 0x00a6, 0x00b6 }, /* 0x00a6 */ + { CODEPAGE_ISNONE, 0x00a7, 0x00a7 }, /* 0x00a7 */ + { CODEPAGE_ISNONE, 0x00a8, 0x00a8 }, /* 0x00a8 */ + { CODEPAGE_ISUPPER, 0x00a9, 0x00b9 }, /* 0x00a9 */ + { CODEPAGE_ISUPPER, 0x00aa, 0x00ba }, /* 0x00aa */ + { CODEPAGE_ISUPPER, 0x00ab, 0x00bb }, /* 0x00ab */ + { CODEPAGE_ISUPPER, 0x00ac, 0x00bc }, /* 0x00ac */ + { CODEPAGE_ISNONE, 0x00ad, 0x00ad }, /* 0x00ad */ + { CODEPAGE_ISNONE, 0x00ae, 0x00ae }, /* 0x00ae */ + { CODEPAGE_ISUPPER, 0x00af, 0x00bf }, /* 0x00af */ + { CODEPAGE_ISNONE, 0x00b0, 0x00b0 }, /* 0x00b0 */ + { CODEPAGE_ISLOWER, 0x00a1, 0x00b1 }, /* 0x00b1 */ + { CODEPAGE_ISNONE, 0x00b2, 0x00b2 }, /* 0x00b2 */ + { CODEPAGE_ISNONE, 0x00b3, 0x00b3 }, /* 0x00b3 */ + { CODEPAGE_ISNONE, 0x00b4, 0x00b4 }, /* 0x00b4 */ + { CODEPAGE_ISNONE, 0x00b5, 0x00b5 }, /* 0x00b5 */ + { CODEPAGE_ISLOWER, 0x00a6, 0x00b6 }, /* 0x00b6 */ + { CODEPAGE_ISNONE, 0x00b7, 0x00b7 }, /* 0x00b7 */ + { CODEPAGE_ISNONE, 0x00b8, 0x00b8 }, /* 0x00b8 */ + { CODEPAGE_ISLOWER, 0x00a9, 0x00b9 }, /* 0x00b9 */ + { CODEPAGE_ISLOWER, 0x00aa, 0x00ba }, /* 0x00ba */ + { CODEPAGE_ISLOWER, 0x00ab, 0x00bb }, /* 0x00bb */ + { CODEPAGE_ISLOWER, 0x00ac, 0x00bc }, /* 0x00bc */ + { CODEPAGE_ISNONE, 0x00bd, 0x00bd }, /* 0x00bd */ + { CODEPAGE_ISNONE, 0x00be, 0x00be }, /* 0x00be */ + { CODEPAGE_ISLOWER, 0x00af, 0x00bf }, /* 0x00bf */ + { CODEPAGE_ISUPPER, 0x00c0, 0x00e0 }, /* 0x00c0 */ + { CODEPAGE_ISUPPER, 0x00c1, 0x00e1 }, /* 0x00c1 */ + { CODEPAGE_ISUPPER, 0x00c2, 0x00e2 }, /* 0x00c2 */ + { CODEPAGE_ISNONE, 0x00c3, 0x00c3 }, /* 0x00c3 */ + { CODEPAGE_ISUPPER, 0x00c4, 0x00e4 }, /* 0x00c4 */ + { CODEPAGE_ISUPPER, 0x00c5, 0x00e5 }, /* 0x00c5 */ + { CODEPAGE_ISUPPER, 0x00c6, 0x00e6 }, /* 0x00c6 */ + { CODEPAGE_ISUPPER, 0x00c7, 0x00e7 }, /* 0x00c7 */ + { CODEPAGE_ISUPPER, 0x00c8, 0x00e8 }, /* 0x00c8 */ + { CODEPAGE_ISUPPER, 0x00c9, 0x00e9 }, /* 0x00c9 */ + { CODEPAGE_ISUPPER, 0x00ca, 0x00ea }, /* 0x00ca */ + { CODEPAGE_ISUPPER, 0x00cb, 0x00eb }, /* 0x00cb */ + { CODEPAGE_ISUPPER, 0x00cc, 0x00ec }, /* 0x00cc */ + { CODEPAGE_ISUPPER, 0x00cd, 0x00ed }, /* 0x00cd */ + { CODEPAGE_ISUPPER, 0x00ce, 0x00ee }, /* 0x00ce */ + { CODEPAGE_ISUPPER, 0x00cf, 0x00ef }, /* 0x00cf */ + { CODEPAGE_ISNONE, 0x00d0, 0x00d0 }, /* 0x00d0 */ + { CODEPAGE_ISUPPER, 0x00d1, 0x00f1 }, /* 0x00d1 */ + { CODEPAGE_ISUPPER, 0x00d2, 0x00f2 }, /* 0x00d2 */ + { CODEPAGE_ISUPPER, 0x00d3, 0x00f3 }, /* 0x00d3 */ + { CODEPAGE_ISUPPER, 0x00d4, 0x00f4 }, /* 0x00d4 */ + { CODEPAGE_ISUPPER, 0x00d5, 0x00f5 }, /* 0x00d5 */ + { CODEPAGE_ISUPPER, 0x00d6, 0x00f6 }, /* 0x00d6 */ + { CODEPAGE_ISNONE, 0x00d7, 0x00d7 }, /* 0x00d7 */ + { CODEPAGE_ISUPPER, 0x00d8, 0x00f8 }, /* 0x00d8 */ + { CODEPAGE_ISUPPER, 0x00d9, 0x00f9 }, /* 0x00d9 */ + { CODEPAGE_ISUPPER, 0x00da, 0x00fa }, /* 0x00da */ + { CODEPAGE_ISUPPER, 0x00db, 0x00fb }, /* 0x00db */ + { CODEPAGE_ISUPPER, 0x00dc, 0x00fc }, /* 0x00dc */ + { CODEPAGE_ISUPPER, 0x00dd, 0x00fd }, /* 0x00dd */ + { CODEPAGE_ISUPPER, 0x00de, 0x00fe }, /* 0x00de */ + { CODEPAGE_ISNONE, 0x00df, 0x00df }, /* 0x00df */ + { CODEPAGE_ISLOWER, 0x00c0, 0x00e0 }, /* 0x00e0 */ + { CODEPAGE_ISLOWER, 0x00c1, 0x00e1 }, /* 0x00e1 */ + { CODEPAGE_ISLOWER, 0x00c2, 0x00e2 }, /* 0x00e2 */ + { CODEPAGE_ISNONE, 0x00e3, 0x00e3 }, /* 0x00e3 */ + { CODEPAGE_ISLOWER, 0x00c4, 0x00e4 }, /* 0x00e4 */ + { CODEPAGE_ISLOWER, 0x00c5, 0x00e5 }, /* 0x00e5 */ + { CODEPAGE_ISLOWER, 0x00c6, 0x00e6 }, /* 0x00e6 */ + { CODEPAGE_ISLOWER, 0x00c7, 0x00e7 }, /* 0x00e7 */ + { CODEPAGE_ISLOWER, 0x00c8, 0x00e8 }, /* 0x00e8 */ + { CODEPAGE_ISLOWER, 0x00c9, 0x00e9 }, /* 0x00e9 */ + { CODEPAGE_ISLOWER, 0x00ca, 0x00ea }, /* 0x00ea */ + { CODEPAGE_ISLOWER, 0x00cb, 0x00eb }, /* 0x00eb */ + { CODEPAGE_ISLOWER, 0x00cc, 0x00ec }, /* 0x00ec */ + { CODEPAGE_ISLOWER, 0x00cd, 0x00ed }, /* 0x00ed */ + { CODEPAGE_ISLOWER, 0x00ce, 0x00ee }, /* 0x00ee */ + { CODEPAGE_ISLOWER, 0x00cf, 0x00ef }, /* 0x00ef */ + { CODEPAGE_ISNONE, 0x00f0, 0x00f0 }, /* 0x00f0 */ + { CODEPAGE_ISLOWER, 0x00d1, 0x00f1 }, /* 0x00f1 */ + { CODEPAGE_ISLOWER, 0x00d2, 0x00f2 }, /* 0x00f2 */ + { CODEPAGE_ISLOWER, 0x00d3, 0x00f3 }, /* 0x00f3 */ + { CODEPAGE_ISLOWER, 0x00d4, 0x00f4 }, /* 0x00f4 */ + { CODEPAGE_ISLOWER, 0x00d5, 0x00f5 }, /* 0x00f5 */ + { CODEPAGE_ISLOWER, 0x00d6, 0x00f6 }, /* 0x00f6 */ + { CODEPAGE_ISNONE, 0x00f7, 0x00f7 }, /* 0x00f7 */ + { CODEPAGE_ISLOWER, 0x00d8, 0x00f8 }, /* 0x00f8 */ + { CODEPAGE_ISLOWER, 0x00d9, 0x00f9 }, /* 0x00f9 */ + { CODEPAGE_ISLOWER, 0x00da, 0x00fa }, /* 0x00fa */ + { CODEPAGE_ISLOWER, 0x00db, 0x00fb }, /* 0x00fb */ + { CODEPAGE_ISLOWER, 0x00dc, 0x00fc }, /* 0x00fc */ + { CODEPAGE_ISLOWER, 0x00dd, 0x00fd }, /* 0x00fd */ + { CODEPAGE_ISLOWER, 0x00de, 0x00fe }, /* 0x00fe */ + { CODEPAGE_ISNONE, 0x00ff, 0x00ff } /* 0x00ff */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_CP_LATIN3_H */ diff --git a/usr/src/uts/common/smbsrv/cp_latin4.h b/usr/src/uts/common/smbsrv/cp_latin4.h new file mode 100644 index 000000000000..87e828251013 --- /dev/null +++ b/usr/src/uts/common/smbsrv/cp_latin4.h @@ -0,0 +1,312 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_CP_LATIN4_H +#define _SMBSRV_CP_LATIN4_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file specifies a codepage mapping for a given character set as + * specified below: + * + * This is the codepage for Latin-4 Character Set. + * This codepage defines values for the special + * characters needed for the written alphabets of the + * following languages: Estonian, Baltic [Latvian & + * Lithuanian], Greenlandic, and Lappish + * The Latin-4 character set is also known as iso-8859-4 + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +codepage_t Latin4_codepage[256] = { + { CODEPAGE_ISNONE, 0x0000, 0x0000 }, /* 0x0000 */ + { CODEPAGE_ISNONE, 0x0001, 0x0001 }, /* 0x0001 */ + { CODEPAGE_ISNONE, 0x0002, 0x0002 }, /* 0x0002 */ + { CODEPAGE_ISNONE, 0x0003, 0x0003 }, /* 0x0003 */ + { CODEPAGE_ISNONE, 0x0004, 0x0004 }, /* 0x0004 */ + { CODEPAGE_ISNONE, 0x0005, 0x0005 }, /* 0x0005 */ + { CODEPAGE_ISNONE, 0x0006, 0x0006 }, /* 0x0006 */ + { CODEPAGE_ISNONE, 0x0007, 0x0007 }, /* 0x0007 */ + { CODEPAGE_ISNONE, 0x0008, 0x0008 }, /* 0x0008 */ + { CODEPAGE_ISNONE, 0x0009, 0x0009 }, /* 0x0009 */ + { CODEPAGE_ISNONE, 0x000a, 0x000a }, /* 0x000a */ + { CODEPAGE_ISNONE, 0x000b, 0x000b }, /* 0x000b */ + { CODEPAGE_ISNONE, 0x000c, 0x000c }, /* 0x000c */ + { CODEPAGE_ISNONE, 0x000d, 0x000d }, /* 0x000d */ + { CODEPAGE_ISNONE, 0x000e, 0x000e }, /* 0x000e */ + { CODEPAGE_ISNONE, 0x000f, 0x000f }, /* 0x000f */ + { CODEPAGE_ISNONE, 0x0010, 0x0010 }, /* 0x0010 */ + { CODEPAGE_ISNONE, 0x0011, 0x0011 }, /* 0x0011 */ + { CODEPAGE_ISNONE, 0x0012, 0x0012 }, /* 0x0012 */ + { CODEPAGE_ISNONE, 0x0013, 0x0013 }, /* 0x0013 */ + { CODEPAGE_ISNONE, 0x0014, 0x0014 }, /* 0x0014 */ + { CODEPAGE_ISNONE, 0x0015, 0x0015 }, /* 0x0015 */ + { CODEPAGE_ISNONE, 0x0016, 0x0016 }, /* 0x0016 */ + { CODEPAGE_ISNONE, 0x0017, 0x0017 }, /* 0x0017 */ + { CODEPAGE_ISNONE, 0x0018, 0x0018 }, /* 0x0018 */ + { CODEPAGE_ISNONE, 0x0019, 0x0019 }, /* 0x0019 */ + { CODEPAGE_ISNONE, 0x001a, 0x001a }, /* 0x001a */ + { CODEPAGE_ISNONE, 0x001b, 0x001b }, /* 0x001b */ + { CODEPAGE_ISNONE, 0x001c, 0x001c }, /* 0x001c */ + { CODEPAGE_ISNONE, 0x001d, 0x001d }, /* 0x001d */ + { CODEPAGE_ISNONE, 0x001e, 0x001e }, /* 0x001e */ + { CODEPAGE_ISNONE, 0x001f, 0x001f }, /* 0x001f */ + { CODEPAGE_ISNONE, 0x0020, 0x0020 }, /* 0x0020 */ + { CODEPAGE_ISNONE, 0x0021, 0x0021 }, /* 0x0021 */ + { CODEPAGE_ISNONE, 0x0022, 0x0022 }, /* 0x0022 */ + { CODEPAGE_ISNONE, 0x0023, 0x0023 }, /* 0x0023 */ + { CODEPAGE_ISNONE, 0x0024, 0x0024 }, /* 0x0024 */ + { CODEPAGE_ISNONE, 0x0025, 0x0025 }, /* 0x0025 */ + { CODEPAGE_ISNONE, 0x0026, 0x0026 }, /* 0x0026 */ + { CODEPAGE_ISNONE, 0x0027, 0x0027 }, /* 0x0027 */ + { CODEPAGE_ISNONE, 0x0028, 0x0028 }, /* 0x0028 */ + { CODEPAGE_ISNONE, 0x0029, 0x0029 }, /* 0x0029 */ + { CODEPAGE_ISNONE, 0x002a, 0x002a }, /* 0x002a */ + { CODEPAGE_ISNONE, 0x002b, 0x002b }, /* 0x002b */ + { CODEPAGE_ISNONE, 0x002c, 0x002c }, /* 0x002c */ + { CODEPAGE_ISNONE, 0x002d, 0x002d }, /* 0x002d */ + { CODEPAGE_ISNONE, 0x002e, 0x002e }, /* 0x002e */ + { CODEPAGE_ISNONE, 0x002f, 0x002f }, /* 0x002f */ + { CODEPAGE_ISNONE, 0x0030, 0x0030 }, /* 0x0030 */ + { CODEPAGE_ISNONE, 0x0031, 0x0031 }, /* 0x0031 */ + { CODEPAGE_ISNONE, 0x0032, 0x0032 }, /* 0x0032 */ + { CODEPAGE_ISNONE, 0x0033, 0x0033 }, /* 0x0033 */ + { CODEPAGE_ISNONE, 0x0034, 0x0034 }, /* 0x0034 */ + { CODEPAGE_ISNONE, 0x0035, 0x0035 }, /* 0x0035 */ + { CODEPAGE_ISNONE, 0x0036, 0x0036 }, /* 0x0036 */ + { CODEPAGE_ISNONE, 0x0037, 0x0037 }, /* 0x0037 */ + { CODEPAGE_ISNONE, 0x0038, 0x0038 }, /* 0x0038 */ + { CODEPAGE_ISNONE, 0x0039, 0x0039 }, /* 0x0039 */ + { CODEPAGE_ISNONE, 0x003a, 0x003a }, /* 0x003a */ + { CODEPAGE_ISNONE, 0x003b, 0x003b }, /* 0x003b */ + { CODEPAGE_ISNONE, 0x003c, 0x003c }, /* 0x003c */ + { CODEPAGE_ISNONE, 0x003d, 0x003d }, /* 0x003d */ + { CODEPAGE_ISNONE, 0x003e, 0x003e }, /* 0x003e */ + { CODEPAGE_ISNONE, 0x003f, 0x003f }, /* 0x003f */ + { CODEPAGE_ISNONE, 0x0040, 0x0040 }, /* 0x0040 */ + { CODEPAGE_ISUPPER, 0x0041, 0x0061 }, /* 0x0041 */ + { CODEPAGE_ISUPPER, 0x0042, 0x0062 }, /* 0x0042 */ + { CODEPAGE_ISUPPER, 0x0043, 0x0063 }, /* 0x0043 */ + { CODEPAGE_ISUPPER, 0x0044, 0x0064 }, /* 0x0044 */ + { CODEPAGE_ISUPPER, 0x0045, 0x0065 }, /* 0x0045 */ + { CODEPAGE_ISUPPER, 0x0046, 0x0066 }, /* 0x0046 */ + { CODEPAGE_ISUPPER, 0x0047, 0x0067 }, /* 0x0047 */ + { CODEPAGE_ISUPPER, 0x0048, 0x0068 }, /* 0x0048 */ + { CODEPAGE_ISUPPER, 0x0049, 0x0069 }, /* 0x0049 */ + { CODEPAGE_ISUPPER, 0x004a, 0x006a }, /* 0x004a */ + { CODEPAGE_ISUPPER, 0x004b, 0x006b }, /* 0x004b */ + { CODEPAGE_ISUPPER, 0x004c, 0x006c }, /* 0x004c */ + { CODEPAGE_ISUPPER, 0x004d, 0x006d }, /* 0x004d */ + { CODEPAGE_ISUPPER, 0x004e, 0x006e }, /* 0x004e */ + { CODEPAGE_ISUPPER, 0x004f, 0x006f }, /* 0x004f */ + { CODEPAGE_ISUPPER, 0x0050, 0x0070 }, /* 0x0050 */ + { CODEPAGE_ISUPPER, 0x0051, 0x0071 }, /* 0x0051 */ + { CODEPAGE_ISUPPER, 0x0052, 0x0072 }, /* 0x0052 */ + { CODEPAGE_ISUPPER, 0x0053, 0x0073 }, /* 0x0053 */ + { CODEPAGE_ISUPPER, 0x0054, 0x0074 }, /* 0x0054 */ + { CODEPAGE_ISUPPER, 0x0055, 0x0075 }, /* 0x0055 */ + { CODEPAGE_ISUPPER, 0x0056, 0x0076 }, /* 0x0056 */ + { CODEPAGE_ISUPPER, 0x0057, 0x0077 }, /* 0x0057 */ + { CODEPAGE_ISUPPER, 0x0058, 0x0078 }, /* 0x0058 */ + { CODEPAGE_ISUPPER, 0x0059, 0x0079 }, /* 0x0059 */ + { CODEPAGE_ISUPPER, 0x005a, 0x007a }, /* 0x005a */ + { CODEPAGE_ISNONE, 0x005b, 0x005b }, /* 0x005b */ + { CODEPAGE_ISNONE, 0x005c, 0x005c }, /* 0x005c */ + { CODEPAGE_ISNONE, 0x005d, 0x005d }, /* 0x005d */ + { CODEPAGE_ISNONE, 0x005e, 0x005e }, /* 0x005e */ + { CODEPAGE_ISNONE, 0x005f, 0x005f }, /* 0x005f */ + { CODEPAGE_ISNONE, 0x0060, 0x0060 }, /* 0x0060 */ + { CODEPAGE_ISLOWER, 0x0041, 0x0061 }, /* 0x0061 */ + { CODEPAGE_ISLOWER, 0x0042, 0x0062 }, /* 0x0062 */ + { CODEPAGE_ISLOWER, 0x0043, 0x0063 }, /* 0x0063 */ + { CODEPAGE_ISLOWER, 0x0044, 0x0064 }, /* 0x0064 */ + { CODEPAGE_ISLOWER, 0x0045, 0x0065 }, /* 0x0065 */ + { CODEPAGE_ISLOWER, 0x0046, 0x0066 }, /* 0x0066 */ + { CODEPAGE_ISLOWER, 0x0047, 0x0067 }, /* 0x0067 */ + { CODEPAGE_ISLOWER, 0x0048, 0x0068 }, /* 0x0068 */ + { CODEPAGE_ISLOWER, 0x0049, 0x0069 }, /* 0x0069 */ + { CODEPAGE_ISLOWER, 0x004a, 0x006a }, /* 0x006a */ + { CODEPAGE_ISLOWER, 0x004b, 0x006b }, /* 0x006b */ + { CODEPAGE_ISLOWER, 0x004c, 0x006c }, /* 0x006c */ + { CODEPAGE_ISLOWER, 0x004d, 0x006d }, /* 0x006d */ + { CODEPAGE_ISLOWER, 0x004e, 0x006e }, /* 0x006e */ + { CODEPAGE_ISLOWER, 0x004f, 0x006f }, /* 0x006f */ + { CODEPAGE_ISLOWER, 0x0050, 0x0070 }, /* 0x0070 */ + { CODEPAGE_ISLOWER, 0x0051, 0x0071 }, /* 0x0071 */ + { CODEPAGE_ISLOWER, 0x0052, 0x0072 }, /* 0x0072 */ + { CODEPAGE_ISLOWER, 0x0053, 0x0073 }, /* 0x0073 */ + { CODEPAGE_ISLOWER, 0x0054, 0x0074 }, /* 0x0074 */ + { CODEPAGE_ISLOWER, 0x0055, 0x0075 }, /* 0x0075 */ + { CODEPAGE_ISLOWER, 0x0056, 0x0076 }, /* 0x0076 */ + { CODEPAGE_ISLOWER, 0x0057, 0x0077 }, /* 0x0077 */ + { CODEPAGE_ISLOWER, 0x0058, 0x0078 }, /* 0x0078 */ + { CODEPAGE_ISLOWER, 0x0059, 0x0079 }, /* 0x0079 */ + { CODEPAGE_ISLOWER, 0x005a, 0x007a }, /* 0x007a */ + { CODEPAGE_ISNONE, 0x007b, 0x007b }, /* 0x007b */ + { CODEPAGE_ISNONE, 0x007c, 0x007c }, /* 0x007c */ + { CODEPAGE_ISNONE, 0x007d, 0x007d }, /* 0x007d */ + { CODEPAGE_ISNONE, 0x007e, 0x007e }, /* 0x007e */ + { CODEPAGE_ISNONE, 0x007f, 0x007f }, /* 0x007f */ + { CODEPAGE_ISNONE, 0x0080, 0x0080 }, /* 0x0080 */ + { CODEPAGE_ISNONE, 0x0081, 0x0081 }, /* 0x0081 */ + { CODEPAGE_ISNONE, 0x0082, 0x0082 }, /* 0x0082 */ + { CODEPAGE_ISNONE, 0x0083, 0x0083 }, /* 0x0083 */ + { CODEPAGE_ISNONE, 0x0084, 0x0084 }, /* 0x0084 */ + { CODEPAGE_ISNONE, 0x0085, 0x0085 }, /* 0x0085 */ + { CODEPAGE_ISNONE, 0x0086, 0x0086 }, /* 0x0086 */ + { CODEPAGE_ISNONE, 0x0087, 0x0087 }, /* 0x0087 */ + { CODEPAGE_ISNONE, 0x0088, 0x0088 }, /* 0x0088 */ + { CODEPAGE_ISNONE, 0x0089, 0x0089 }, /* 0x0089 */ + { CODEPAGE_ISNONE, 0x008a, 0x008a }, /* 0x008a */ + { CODEPAGE_ISNONE, 0x008b, 0x008b }, /* 0x008b */ + { CODEPAGE_ISNONE, 0x008c, 0x008c }, /* 0x008c */ + { CODEPAGE_ISNONE, 0x008d, 0x008d }, /* 0x008d */ + { CODEPAGE_ISNONE, 0x008e, 0x008e }, /* 0x008e */ + { CODEPAGE_ISNONE, 0x008f, 0x008f }, /* 0x008f */ + { CODEPAGE_ISNONE, 0x0090, 0x0090 }, /* 0x0090 */ + { CODEPAGE_ISNONE, 0x0091, 0x0091 }, /* 0x0091 */ + { CODEPAGE_ISNONE, 0x0092, 0x0092 }, /* 0x0092 */ + { CODEPAGE_ISNONE, 0x0093, 0x0093 }, /* 0x0093 */ + { CODEPAGE_ISNONE, 0x0094, 0x0094 }, /* 0x0094 */ + { CODEPAGE_ISNONE, 0x0095, 0x0095 }, /* 0x0095 */ + { CODEPAGE_ISNONE, 0x0096, 0x0096 }, /* 0x0096 */ + { CODEPAGE_ISNONE, 0x0097, 0x0097 }, /* 0x0097 */ + { CODEPAGE_ISNONE, 0x0098, 0x0098 }, /* 0x0098 */ + { CODEPAGE_ISNONE, 0x0099, 0x0099 }, /* 0x0099 */ + { CODEPAGE_ISNONE, 0x009a, 0x009a }, /* 0x009a */ + { CODEPAGE_ISNONE, 0x009b, 0x009b }, /* 0x009b */ + { CODEPAGE_ISNONE, 0x009c, 0x009c }, /* 0x009c */ + { CODEPAGE_ISNONE, 0x009d, 0x009d }, /* 0x009d */ + { CODEPAGE_ISNONE, 0x009e, 0x009e }, /* 0x009e */ + { CODEPAGE_ISNONE, 0x009f, 0x009f }, /* 0x009f */ + { CODEPAGE_ISNONE, 0x00a0, 0x00a0 }, /* 0x00a0 */ + { CODEPAGE_ISUPPER, 0x00a1, 0x00b1 }, /* 0x00a1 */ + { CODEPAGE_ISNONE, 0x00a2, 0x00a2 }, /* 0x00a2 */ + { CODEPAGE_ISUPPER, 0x00a3, 0x00b3 }, /* 0x00a3 */ + { CODEPAGE_ISNONE, 0x00a4, 0x00a4 }, /* 0x00a4 */ + { CODEPAGE_ISUPPER, 0x00a5, 0x00b5 }, /* 0x00a5 */ + { CODEPAGE_ISUPPER, 0x00a6, 0x00b6 }, /* 0x00a6 */ + { CODEPAGE_ISNONE, 0x00a7, 0x00a7 }, /* 0x00a7 */ + { CODEPAGE_ISNONE, 0x00a8, 0x00a8 }, /* 0x00a8 */ + { CODEPAGE_ISUPPER, 0x00a9, 0x00b9 }, /* 0x00a9 */ + { CODEPAGE_ISUPPER, 0x00aa, 0x00ba }, /* 0x00aa */ + { CODEPAGE_ISUPPER, 0x00ab, 0x00bb }, /* 0x00ab */ + { CODEPAGE_ISUPPER, 0x00ac, 0x00bc }, /* 0x00ac */ + { CODEPAGE_ISNONE, 0x00ad, 0x00ad }, /* 0x00ad */ + { CODEPAGE_ISUPPER, 0x00ae, 0x00be }, /* 0x00ae */ + { CODEPAGE_ISNONE, 0x00af, 0x00af }, /* 0x00af */ + { CODEPAGE_ISNONE, 0x00b0, 0x00b0 }, /* 0x00b0 */ + { CODEPAGE_ISLOWER, 0x00a1, 0x00b1 }, /* 0x00b1 */ + { CODEPAGE_ISNONE, 0x00b2, 0x00b2 }, /* 0x00b2 */ + { CODEPAGE_ISLOWER, 0x00a3, 0x00b3 }, /* 0x00b3 */ + { CODEPAGE_ISNONE, 0x00b4, 0x00b4 }, /* 0x00b4 */ + { CODEPAGE_ISLOWER, 0x00a5, 0x00b5 }, /* 0x00b5 */ + { CODEPAGE_ISLOWER, 0x00a6, 0x00b6 }, /* 0x00b6 */ + { CODEPAGE_ISNONE, 0x00b7, 0x00b7 }, /* 0x00b7 */ + { CODEPAGE_ISNONE, 0x00b8, 0x00b8 }, /* 0x00b8 */ + { CODEPAGE_ISLOWER, 0x00a9, 0x00b9 }, /* 0x00b9 */ + { CODEPAGE_ISLOWER, 0x00aa, 0x00ba }, /* 0x00ba */ + { CODEPAGE_ISLOWER, 0x00ab, 0x00bb }, /* 0x00bb */ + { CODEPAGE_ISLOWER, 0x00ac, 0x00bc }, /* 0x00bc */ + { CODEPAGE_ISNONE, 0x00bd, 0x00bd }, /* 0x00bd */ + { CODEPAGE_ISLOWER, 0x00ae, 0x00be }, /* 0x00be */ + { CODEPAGE_ISNONE, 0x00bf, 0x00bf }, /* 0x00bf */ + { CODEPAGE_ISUPPER, 0x00c0, 0x00e0 }, /* 0x00c0 */ + { CODEPAGE_ISUPPER, 0x00c1, 0x00e1 }, /* 0x00c1 */ + { CODEPAGE_ISUPPER, 0x00c2, 0x00e2 }, /* 0x00c2 */ + { CODEPAGE_ISUPPER, 0x00c3, 0x00e3 }, /* 0x00c3 */ + { CODEPAGE_ISUPPER, 0x00c4, 0x00e4 }, /* 0x00c4 */ + { CODEPAGE_ISUPPER, 0x00c5, 0x00e5 }, /* 0x00c5 */ + { CODEPAGE_ISUPPER, 0x00c6, 0x00e6 }, /* 0x00c6 */ + { CODEPAGE_ISUPPER, 0x00c7, 0x00e7 }, /* 0x00c7 */ + { CODEPAGE_ISUPPER, 0x00c8, 0x00e8 }, /* 0x00c8 */ + { CODEPAGE_ISUPPER, 0x00c9, 0x00e9 }, /* 0x00c9 */ + { CODEPAGE_ISUPPER, 0x00ca, 0x00ea }, /* 0x00ca */ + { CODEPAGE_ISUPPER, 0x00cb, 0x00eb }, /* 0x00cb */ + { CODEPAGE_ISUPPER, 0x00cc, 0x00ec }, /* 0x00cc */ + { CODEPAGE_ISUPPER, 0x00cd, 0x00ed }, /* 0x00cd */ + { CODEPAGE_ISUPPER, 0x00ce, 0x00ee }, /* 0x00ce */ + { CODEPAGE_ISUPPER, 0x00cf, 0x00ef }, /* 0x00cf */ + { CODEPAGE_ISUPPER, 0x00d0, 0x00f0 }, /* 0x00d0 */ + { CODEPAGE_ISUPPER, 0x00d1, 0x00f1 }, /* 0x00d1 */ + { CODEPAGE_ISUPPER, 0x00d2, 0x00f2 }, /* 0x00d2 */ + { CODEPAGE_ISUPPER, 0x00d3, 0x00f3 }, /* 0x00d3 */ + { CODEPAGE_ISUPPER, 0x00d4, 0x00f4 }, /* 0x00d4 */ + { CODEPAGE_ISUPPER, 0x00d5, 0x00f5 }, /* 0x00d5 */ + { CODEPAGE_ISUPPER, 0x00d6, 0x00f6 }, /* 0x00d6 */ + { CODEPAGE_ISNONE, 0x00d7, 0x00d7 }, /* 0x00d7 */ + { CODEPAGE_ISUPPER, 0x00d8, 0x00f8 }, /* 0x00d8 */ + { CODEPAGE_ISUPPER, 0x00d9, 0x00f9 }, /* 0x00d9 */ + { CODEPAGE_ISUPPER, 0x00da, 0x00fa }, /* 0x00da */ + { CODEPAGE_ISUPPER, 0x00db, 0x00fb }, /* 0x00db */ + { CODEPAGE_ISUPPER, 0x00dc, 0x00fc }, /* 0x00dc */ + { CODEPAGE_ISUPPER, 0x00dd, 0x00fd }, /* 0x00dd */ + { CODEPAGE_ISUPPER, 0x00de, 0x00fe }, /* 0x00de */ + { CODEPAGE_ISNONE, 0x00df, 0x00df }, /* 0x00df */ + { CODEPAGE_ISLOWER, 0x00c0, 0x00e0 }, /* 0x00e0 */ + { CODEPAGE_ISLOWER, 0x00c1, 0x00e1 }, /* 0x00e1 */ + { CODEPAGE_ISLOWER, 0x00c2, 0x00e2 }, /* 0x00e2 */ + { CODEPAGE_ISLOWER, 0x00c3, 0x00e3 }, /* 0x00e3 */ + { CODEPAGE_ISLOWER, 0x00c4, 0x00e4 }, /* 0x00e4 */ + { CODEPAGE_ISLOWER, 0x00c5, 0x00e5 }, /* 0x00e5 */ + { CODEPAGE_ISLOWER, 0x00c6, 0x00e6 }, /* 0x00e6 */ + { CODEPAGE_ISLOWER, 0x00c7, 0x00e7 }, /* 0x00e7 */ + { CODEPAGE_ISLOWER, 0x00c8, 0x00e8 }, /* 0x00e8 */ + { CODEPAGE_ISLOWER, 0x00c9, 0x00e9 }, /* 0x00e9 */ + { CODEPAGE_ISLOWER, 0x00ca, 0x00ea }, /* 0x00ea */ + { CODEPAGE_ISLOWER, 0x00cb, 0x00eb }, /* 0x00eb */ + { CODEPAGE_ISLOWER, 0x00cc, 0x00ec }, /* 0x00ec */ + { CODEPAGE_ISLOWER, 0x00cd, 0x00ed }, /* 0x00ed */ + { CODEPAGE_ISLOWER, 0x00ce, 0x00ee }, /* 0x00ee */ + { CODEPAGE_ISLOWER, 0x00cf, 0x00ef }, /* 0x00ef */ + { CODEPAGE_ISLOWER, 0x00d0, 0x00f0 }, /* 0x00f0 */ + { CODEPAGE_ISLOWER, 0x00d1, 0x00f1 }, /* 0x00f1 */ + { CODEPAGE_ISLOWER, 0x00d2, 0x00f2 }, /* 0x00f2 */ + { CODEPAGE_ISLOWER, 0x00d3, 0x00f3 }, /* 0x00f3 */ + { CODEPAGE_ISLOWER, 0x00d4, 0x00f4 }, /* 0x00f4 */ + { CODEPAGE_ISLOWER, 0x00d5, 0x00f5 }, /* 0x00f5 */ + { CODEPAGE_ISLOWER, 0x00d6, 0x00f6 }, /* 0x00f6 */ + { CODEPAGE_ISNONE, 0x00f7, 0x00f7 }, /* 0x00f7 */ + { CODEPAGE_ISLOWER, 0x00d8, 0x00f8 }, /* 0x00f8 */ + { CODEPAGE_ISLOWER, 0x00d9, 0x00f9 }, /* 0x00f9 */ + { CODEPAGE_ISLOWER, 0x00da, 0x00fa }, /* 0x00fa */ + { CODEPAGE_ISLOWER, 0x00db, 0x00fb }, /* 0x00fb */ + { CODEPAGE_ISLOWER, 0x00dc, 0x00fc }, /* 0x00fc */ + { CODEPAGE_ISLOWER, 0x00dd, 0x00fd }, /* 0x00fd */ + { CODEPAGE_ISLOWER, 0x00de, 0x00fe }, /* 0x00fe */ + { CODEPAGE_ISNONE, 0x00ff, 0x00ff } /* 0x00ff */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_CP_LATIN4_H */ diff --git a/usr/src/uts/common/smbsrv/cp_latin5.h b/usr/src/uts/common/smbsrv/cp_latin5.h new file mode 100644 index 000000000000..87ad426348b7 --- /dev/null +++ b/usr/src/uts/common/smbsrv/cp_latin5.h @@ -0,0 +1,311 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_CP_LATIN5_H +#define _SMBSRV_CP_LATIN5_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file specifies a codepage mapping for a given character set as + * specified below: + * + * This is the codepage for the Latin-5 Character Set + * This codepage defines values for the special characters + * needed for the written alphabet of the following + * language: Turkish * The Latin-5 character set is also known as iso-8859-9. + * + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +codepage_t Latin5_codepage[256] = { + { CODEPAGE_ISNONE, 0x0000, 0x0000 }, /* 0x0000 */ + { CODEPAGE_ISNONE, 0x0001, 0x0001 }, /* 0x0001 */ + { CODEPAGE_ISNONE, 0x0002, 0x0002 }, /* 0x0002 */ + { CODEPAGE_ISNONE, 0x0003, 0x0003 }, /* 0x0003 */ + { CODEPAGE_ISNONE, 0x0004, 0x0004 }, /* 0x0004 */ + { CODEPAGE_ISNONE, 0x0005, 0x0005 }, /* 0x0005 */ + { CODEPAGE_ISNONE, 0x0006, 0x0006 }, /* 0x0006 */ + { CODEPAGE_ISNONE, 0x0007, 0x0007 }, /* 0x0007 */ + { CODEPAGE_ISNONE, 0x0008, 0x0008 }, /* 0x0008 */ + { CODEPAGE_ISNONE, 0x0009, 0x0009 }, /* 0x0009 */ + { CODEPAGE_ISNONE, 0x000a, 0x000a }, /* 0x000a */ + { CODEPAGE_ISNONE, 0x000b, 0x000b }, /* 0x000b */ + { CODEPAGE_ISNONE, 0x000c, 0x000c }, /* 0x000c */ + { CODEPAGE_ISNONE, 0x000d, 0x000d }, /* 0x000d */ + { CODEPAGE_ISNONE, 0x000e, 0x000e }, /* 0x000e */ + { CODEPAGE_ISNONE, 0x000f, 0x000f }, /* 0x000f */ + { CODEPAGE_ISNONE, 0x0010, 0x0010 }, /* 0x0010 */ + { CODEPAGE_ISNONE, 0x0011, 0x0011 }, /* 0x0011 */ + { CODEPAGE_ISNONE, 0x0012, 0x0012 }, /* 0x0012 */ + { CODEPAGE_ISNONE, 0x0013, 0x0013 }, /* 0x0013 */ + { CODEPAGE_ISNONE, 0x0014, 0x0014 }, /* 0x0014 */ + { CODEPAGE_ISNONE, 0x0015, 0x0015 }, /* 0x0015 */ + { CODEPAGE_ISNONE, 0x0016, 0x0016 }, /* 0x0016 */ + { CODEPAGE_ISNONE, 0x0017, 0x0017 }, /* 0x0017 */ + { CODEPAGE_ISNONE, 0x0018, 0x0018 }, /* 0x0018 */ + { CODEPAGE_ISNONE, 0x0019, 0x0019 }, /* 0x0019 */ + { CODEPAGE_ISNONE, 0x001a, 0x001a }, /* 0x001a */ + { CODEPAGE_ISNONE, 0x001b, 0x001b }, /* 0x001b */ + { CODEPAGE_ISNONE, 0x001c, 0x001c }, /* 0x001c */ + { CODEPAGE_ISNONE, 0x001d, 0x001d }, /* 0x001d */ + { CODEPAGE_ISNONE, 0x001e, 0x001e }, /* 0x001e */ + { CODEPAGE_ISNONE, 0x001f, 0x001f }, /* 0x001f */ + { CODEPAGE_ISNONE, 0x0020, 0x0020 }, /* 0x0020 */ + { CODEPAGE_ISNONE, 0x0021, 0x0021 }, /* 0x0021 */ + { CODEPAGE_ISNONE, 0x0022, 0x0022 }, /* 0x0022 */ + { CODEPAGE_ISNONE, 0x0023, 0x0023 }, /* 0x0023 */ + { CODEPAGE_ISNONE, 0x0024, 0x0024 }, /* 0x0024 */ + { CODEPAGE_ISNONE, 0x0025, 0x0025 }, /* 0x0025 */ + { CODEPAGE_ISNONE, 0x0026, 0x0026 }, /* 0x0026 */ + { CODEPAGE_ISNONE, 0x0027, 0x0027 }, /* 0x0027 */ + { CODEPAGE_ISNONE, 0x0028, 0x0028 }, /* 0x0028 */ + { CODEPAGE_ISNONE, 0x0029, 0x0029 }, /* 0x0029 */ + { CODEPAGE_ISNONE, 0x002a, 0x002a }, /* 0x002a */ + { CODEPAGE_ISNONE, 0x002b, 0x002b }, /* 0x002b */ + { CODEPAGE_ISNONE, 0x002c, 0x002c }, /* 0x002c */ + { CODEPAGE_ISNONE, 0x002d, 0x002d }, /* 0x002d */ + { CODEPAGE_ISNONE, 0x002e, 0x002e }, /* 0x002e */ + { CODEPAGE_ISNONE, 0x002f, 0x002f }, /* 0x002f */ + { CODEPAGE_ISNONE, 0x0030, 0x0030 }, /* 0x0030 */ + { CODEPAGE_ISNONE, 0x0031, 0x0031 }, /* 0x0031 */ + { CODEPAGE_ISNONE, 0x0032, 0x0032 }, /* 0x0032 */ + { CODEPAGE_ISNONE, 0x0033, 0x0033 }, /* 0x0033 */ + { CODEPAGE_ISNONE, 0x0034, 0x0034 }, /* 0x0034 */ + { CODEPAGE_ISNONE, 0x0035, 0x0035 }, /* 0x0035 */ + { CODEPAGE_ISNONE, 0x0036, 0x0036 }, /* 0x0036 */ + { CODEPAGE_ISNONE, 0x0037, 0x0037 }, /* 0x0037 */ + { CODEPAGE_ISNONE, 0x0038, 0x0038 }, /* 0x0038 */ + { CODEPAGE_ISNONE, 0x0039, 0x0039 }, /* 0x0039 */ + { CODEPAGE_ISNONE, 0x003a, 0x003a }, /* 0x003a */ + { CODEPAGE_ISNONE, 0x003b, 0x003b }, /* 0x003b */ + { CODEPAGE_ISNONE, 0x003c, 0x003c }, /* 0x003c */ + { CODEPAGE_ISNONE, 0x003d, 0x003d }, /* 0x003d */ + { CODEPAGE_ISNONE, 0x003e, 0x003e }, /* 0x003e */ + { CODEPAGE_ISNONE, 0x003f, 0x003f }, /* 0x003f */ + { CODEPAGE_ISNONE, 0x0040, 0x0040 }, /* 0x0040 */ + { CODEPAGE_ISUPPER, 0x0041, 0x0061 }, /* 0x0041 */ + { CODEPAGE_ISUPPER, 0x0042, 0x0062 }, /* 0x0042 */ + { CODEPAGE_ISUPPER, 0x0043, 0x0063 }, /* 0x0043 */ + { CODEPAGE_ISUPPER, 0x0044, 0x0064 }, /* 0x0044 */ + { CODEPAGE_ISUPPER, 0x0045, 0x0065 }, /* 0x0045 */ + { CODEPAGE_ISUPPER, 0x0046, 0x0066 }, /* 0x0046 */ + { CODEPAGE_ISUPPER, 0x0047, 0x0067 }, /* 0x0047 */ + { CODEPAGE_ISUPPER, 0x0048, 0x0068 }, /* 0x0048 */ + { CODEPAGE_ISUPPER, 0x0049, 0x0069 }, /* 0x0049 */ + { CODEPAGE_ISUPPER, 0x004a, 0x006a }, /* 0x004a */ + { CODEPAGE_ISUPPER, 0x004b, 0x006b }, /* 0x004b */ + { CODEPAGE_ISUPPER, 0x004c, 0x006c }, /* 0x004c */ + { CODEPAGE_ISUPPER, 0x004d, 0x006d }, /* 0x004d */ + { CODEPAGE_ISUPPER, 0x004e, 0x006e }, /* 0x004e */ + { CODEPAGE_ISUPPER, 0x004f, 0x006f }, /* 0x004f */ + { CODEPAGE_ISUPPER, 0x0050, 0x0070 }, /* 0x0050 */ + { CODEPAGE_ISUPPER, 0x0051, 0x0071 }, /* 0x0051 */ + { CODEPAGE_ISUPPER, 0x0052, 0x0072 }, /* 0x0052 */ + { CODEPAGE_ISUPPER, 0x0053, 0x0073 }, /* 0x0053 */ + { CODEPAGE_ISUPPER, 0x0054, 0x0074 }, /* 0x0054 */ + { CODEPAGE_ISUPPER, 0x0055, 0x0075 }, /* 0x0055 */ + { CODEPAGE_ISUPPER, 0x0056, 0x0076 }, /* 0x0056 */ + { CODEPAGE_ISUPPER, 0x0057, 0x0077 }, /* 0x0057 */ + { CODEPAGE_ISUPPER, 0x0058, 0x0078 }, /* 0x0058 */ + { CODEPAGE_ISUPPER, 0x0059, 0x0079 }, /* 0x0059 */ + { CODEPAGE_ISUPPER, 0x005a, 0x007a }, /* 0x005a */ + { CODEPAGE_ISNONE, 0x005b, 0x005b }, /* 0x005b */ + { CODEPAGE_ISNONE, 0x005c, 0x005c }, /* 0x005c */ + { CODEPAGE_ISNONE, 0x005d, 0x005d }, /* 0x005d */ + { CODEPAGE_ISNONE, 0x005e, 0x005e }, /* 0x005e */ + { CODEPAGE_ISNONE, 0x005f, 0x005f }, /* 0x005f */ + { CODEPAGE_ISNONE, 0x0060, 0x0060 }, /* 0x0060 */ + { CODEPAGE_ISLOWER, 0x0041, 0x0061 }, /* 0x0061 */ + { CODEPAGE_ISLOWER, 0x0042, 0x0062 }, /* 0x0062 */ + { CODEPAGE_ISLOWER, 0x0043, 0x0063 }, /* 0x0063 */ + { CODEPAGE_ISLOWER, 0x0044, 0x0064 }, /* 0x0064 */ + { CODEPAGE_ISLOWER, 0x0045, 0x0065 }, /* 0x0065 */ + { CODEPAGE_ISLOWER, 0x0046, 0x0066 }, /* 0x0066 */ + { CODEPAGE_ISLOWER, 0x0047, 0x0067 }, /* 0x0067 */ + { CODEPAGE_ISLOWER, 0x0048, 0x0068 }, /* 0x0068 */ + { CODEPAGE_ISLOWER, 0x0049, 0x0069 }, /* 0x0069 */ + { CODEPAGE_ISLOWER, 0x004a, 0x006a }, /* 0x006a */ + { CODEPAGE_ISLOWER, 0x004b, 0x006b }, /* 0x006b */ + { CODEPAGE_ISLOWER, 0x004c, 0x006c }, /* 0x006c */ + { CODEPAGE_ISLOWER, 0x004d, 0x006d }, /* 0x006d */ + { CODEPAGE_ISLOWER, 0x004e, 0x006e }, /* 0x006e */ + { CODEPAGE_ISLOWER, 0x004f, 0x006f }, /* 0x006f */ + { CODEPAGE_ISLOWER, 0x0050, 0x0070 }, /* 0x0070 */ + { CODEPAGE_ISLOWER, 0x0051, 0x0071 }, /* 0x0071 */ + { CODEPAGE_ISLOWER, 0x0052, 0x0072 }, /* 0x0072 */ + { CODEPAGE_ISLOWER, 0x0053, 0x0073 }, /* 0x0073 */ + { CODEPAGE_ISLOWER, 0x0054, 0x0074 }, /* 0x0074 */ + { CODEPAGE_ISLOWER, 0x0055, 0x0075 }, /* 0x0075 */ + { CODEPAGE_ISLOWER, 0x0056, 0x0076 }, /* 0x0076 */ + { CODEPAGE_ISLOWER, 0x0057, 0x0077 }, /* 0x0077 */ + { CODEPAGE_ISLOWER, 0x0058, 0x0078 }, /* 0x0078 */ + { CODEPAGE_ISLOWER, 0x0059, 0x0079 }, /* 0x0079 */ + { CODEPAGE_ISLOWER, 0x005a, 0x007a }, /* 0x007a */ + { CODEPAGE_ISNONE, 0x007b, 0x007b }, /* 0x007b */ + { CODEPAGE_ISNONE, 0x007c, 0x007c }, /* 0x007c */ + { CODEPAGE_ISNONE, 0x007d, 0x007d }, /* 0x007d */ + { CODEPAGE_ISNONE, 0x007e, 0x007e }, /* 0x007e */ + { CODEPAGE_ISNONE, 0x007f, 0x007f }, /* 0x007f */ + { CODEPAGE_ISNONE, 0x0080, 0x0080 }, /* 0x0080 */ + { CODEPAGE_ISNONE, 0x0081, 0x0081 }, /* 0x0081 */ + { CODEPAGE_ISNONE, 0x0082, 0x0082 }, /* 0x0082 */ + { CODEPAGE_ISNONE, 0x0083, 0x0083 }, /* 0x0083 */ + { CODEPAGE_ISNONE, 0x0084, 0x0084 }, /* 0x0084 */ + { CODEPAGE_ISNONE, 0x0085, 0x0085 }, /* 0x0085 */ + { CODEPAGE_ISNONE, 0x0086, 0x0086 }, /* 0x0086 */ + { CODEPAGE_ISNONE, 0x0087, 0x0087 }, /* 0x0087 */ + { CODEPAGE_ISNONE, 0x0088, 0x0088 }, /* 0x0088 */ + { CODEPAGE_ISNONE, 0x0089, 0x0089 }, /* 0x0089 */ + { CODEPAGE_ISNONE, 0x008a, 0x008a }, /* 0x008a */ + { CODEPAGE_ISNONE, 0x008b, 0x008b }, /* 0x008b */ + { CODEPAGE_ISNONE, 0x008c, 0x008c }, /* 0x008c */ + { CODEPAGE_ISNONE, 0x008d, 0x008d }, /* 0x008d */ + { CODEPAGE_ISNONE, 0x008e, 0x008e }, /* 0x008e */ + { CODEPAGE_ISNONE, 0x008f, 0x008f }, /* 0x008f */ + { CODEPAGE_ISNONE, 0x0090, 0x0090 }, /* 0x0090 */ + { CODEPAGE_ISNONE, 0x0091, 0x0091 }, /* 0x0091 */ + { CODEPAGE_ISNONE, 0x0092, 0x0092 }, /* 0x0092 */ + { CODEPAGE_ISNONE, 0x0093, 0x0093 }, /* 0x0093 */ + { CODEPAGE_ISNONE, 0x0094, 0x0094 }, /* 0x0094 */ + { CODEPAGE_ISNONE, 0x0095, 0x0095 }, /* 0x0095 */ + { CODEPAGE_ISNONE, 0x0096, 0x0096 }, /* 0x0096 */ + { CODEPAGE_ISNONE, 0x0097, 0x0097 }, /* 0x0097 */ + { CODEPAGE_ISNONE, 0x0098, 0x0098 }, /* 0x0098 */ + { CODEPAGE_ISNONE, 0x0099, 0x0099 }, /* 0x0099 */ + { CODEPAGE_ISNONE, 0x009a, 0x009a }, /* 0x009a */ + { CODEPAGE_ISNONE, 0x009b, 0x009b }, /* 0x009b */ + { CODEPAGE_ISNONE, 0x009c, 0x009c }, /* 0x009c */ + { CODEPAGE_ISNONE, 0x009d, 0x009d }, /* 0x009d */ + { CODEPAGE_ISNONE, 0x009e, 0x009e }, /* 0x009e */ + { CODEPAGE_ISNONE, 0x009f, 0x009f }, /* 0x009f */ + { CODEPAGE_ISNONE, 0x00a0, 0x00a0 }, /* 0x00a0 */ + { CODEPAGE_ISNONE, 0x00a1, 0x00a1 }, /* 0x00a1 */ + { CODEPAGE_ISNONE, 0x00a2, 0x00a2 }, /* 0x00a2 */ + { CODEPAGE_ISNONE, 0x00a3, 0x00a3 }, /* 0x00a3 */ + { CODEPAGE_ISNONE, 0x00a4, 0x00a4 }, /* 0x00a4 */ + { CODEPAGE_ISNONE, 0x00a5, 0x00a5 }, /* 0x00a5 */ + { CODEPAGE_ISNONE, 0x00a6, 0x00a6 }, /* 0x00a6 */ + { CODEPAGE_ISNONE, 0x00a7, 0x00a7 }, /* 0x00a7 */ + { CODEPAGE_ISNONE, 0x00a8, 0x00a8 }, /* 0x00a8 */ + { CODEPAGE_ISNONE, 0x00a9, 0x00a9 }, /* 0x00a9 */ + { CODEPAGE_ISNONE, 0x00aa, 0x00aa }, /* 0x00aa */ + { CODEPAGE_ISNONE, 0x00ab, 0x00ab }, /* 0x00ab */ + { CODEPAGE_ISNONE, 0x00ac, 0x00ac }, /* 0x00ac */ + { CODEPAGE_ISNONE, 0x00ad, 0x00ad }, /* 0x00ad */ + { CODEPAGE_ISNONE, 0x00ae, 0x00ae }, /* 0x00ae */ + { CODEPAGE_ISNONE, 0x00af, 0x00af }, /* 0x00af */ + { CODEPAGE_ISNONE, 0x00b0, 0x00b0 }, /* 0x00b0 */ + { CODEPAGE_ISNONE, 0x00b1, 0x00b1 }, /* 0x00b1 */ + { CODEPAGE_ISNONE, 0x00b2, 0x00b2 }, /* 0x00b2 */ + { CODEPAGE_ISNONE, 0x00b3, 0x00b3 }, /* 0x00b3 */ + { CODEPAGE_ISNONE, 0x00b4, 0x00b4 }, /* 0x00b4 */ + { CODEPAGE_ISNONE, 0x00b5, 0x00b5 }, /* 0x00b5 */ + { CODEPAGE_ISNONE, 0x00b6, 0x00b6 }, /* 0x00b6 */ + { CODEPAGE_ISNONE, 0x00b7, 0x00b7 }, /* 0x00b7 */ + { CODEPAGE_ISNONE, 0x00b8, 0x00b8 }, /* 0x00b8 */ + { CODEPAGE_ISNONE, 0x00b9, 0x00b9 }, /* 0x00b9 */ + { CODEPAGE_ISNONE, 0x00ba, 0x00ba }, /* 0x00ba */ + { CODEPAGE_ISNONE, 0x00bb, 0x00bb }, /* 0x00bb */ + { CODEPAGE_ISNONE, 0x00bc, 0x00bc }, /* 0x00bc */ + { CODEPAGE_ISNONE, 0x00bd, 0x00bd }, /* 0x00bd */ + { CODEPAGE_ISNONE, 0x00be, 0x00be }, /* 0x00be */ + { CODEPAGE_ISNONE, 0x00bf, 0x00bf }, /* 0x00bf */ + { CODEPAGE_ISUPPER, 0x00c0, 0x00e0 }, /* 0x00c0 */ + { CODEPAGE_ISUPPER, 0x00c1, 0x00e1 }, /* 0x00c1 */ + { CODEPAGE_ISUPPER, 0x00c2, 0x00e2 }, /* 0x00c2 */ + { CODEPAGE_ISUPPER, 0x00c3, 0x00e3 }, /* 0x00c3 */ + { CODEPAGE_ISUPPER, 0x00c4, 0x00e4 }, /* 0x00c4 */ + { CODEPAGE_ISUPPER, 0x00c5, 0x00e5 }, /* 0x00c5 */ + { CODEPAGE_ISUPPER, 0x00c6, 0x00e6 }, /* 0x00c6 */ + { CODEPAGE_ISUPPER, 0x00c7, 0x00e7 }, /* 0x00c7 */ + { CODEPAGE_ISUPPER, 0x00c8, 0x00e8 }, /* 0x00c8 */ + { CODEPAGE_ISUPPER, 0x00c9, 0x00e9 }, /* 0x00c9 */ + { CODEPAGE_ISUPPER, 0x00ca, 0x00ea }, /* 0x00ca */ + { CODEPAGE_ISUPPER, 0x00cb, 0x00eb }, /* 0x00cb */ + { CODEPAGE_ISUPPER, 0x00cc, 0x00ec }, /* 0x00cc */ + { CODEPAGE_ISUPPER, 0x00cd, 0x00ed }, /* 0x00cd */ + { CODEPAGE_ISUPPER, 0x00ce, 0x00ee }, /* 0x00ce */ + { CODEPAGE_ISUPPER, 0x00cf, 0x00ef }, /* 0x00cf */ + { CODEPAGE_ISUPPER, 0x00d0, 0x00f0 }, /* 0x00d0 */ + { CODEPAGE_ISUPPER, 0x00d1, 0x00f1 }, /* 0x00d1 */ + { CODEPAGE_ISUPPER, 0x00d2, 0x00f2 }, /* 0x00d2 */ + { CODEPAGE_ISUPPER, 0x00d3, 0x00f3 }, /* 0x00d3 */ + { CODEPAGE_ISUPPER, 0x00d4, 0x00f4 }, /* 0x00d4 */ + { CODEPAGE_ISUPPER, 0x00d5, 0x00f5 }, /* 0x00d5 */ + { CODEPAGE_ISUPPER, 0x00d6, 0x00f6 }, /* 0x00d6 */ + { CODEPAGE_ISNONE, 0x00d7, 0x00d7 }, /* 0x00d7 */ + { CODEPAGE_ISUPPER, 0x00d8, 0x00f8 }, /* 0x00d8 */ + { CODEPAGE_ISUPPER, 0x00d9, 0x00f9 }, /* 0x00d9 */ + { CODEPAGE_ISUPPER, 0x00da, 0x00fa }, /* 0x00da */ + { CODEPAGE_ISUPPER, 0x00db, 0x00fb }, /* 0x00db */ + { CODEPAGE_ISUPPER, 0x00dc, 0x00fc }, /* 0x00dc */ + { CODEPAGE_ISUPPER, 0x00dd, 0x00fd }, /* 0x00dd */ + { CODEPAGE_ISUPPER, 0x00de, 0x00fe }, /* 0x00de */ + { CODEPAGE_ISNONE, 0x00df, 0x00df }, /* 0x00df */ + { CODEPAGE_ISLOWER, 0x00c0, 0x00e0 }, /* 0x00e0 */ + { CODEPAGE_ISLOWER, 0x00c1, 0x00e1 }, /* 0x00e1 */ + { CODEPAGE_ISLOWER, 0x00c2, 0x00e2 }, /* 0x00e2 */ + { CODEPAGE_ISLOWER, 0x00c3, 0x00e3 }, /* 0x00e3 */ + { CODEPAGE_ISLOWER, 0x00c4, 0x00e4 }, /* 0x00e4 */ + { CODEPAGE_ISLOWER, 0x00c5, 0x00e5 }, /* 0x00e5 */ + { CODEPAGE_ISLOWER, 0x00c6, 0x00e6 }, /* 0x00e6 */ + { CODEPAGE_ISLOWER, 0x00c7, 0x00e7 }, /* 0x00e7 */ + { CODEPAGE_ISLOWER, 0x00c8, 0x00e8 }, /* 0x00e8 */ + { CODEPAGE_ISLOWER, 0x00c9, 0x00e9 }, /* 0x00e9 */ + { CODEPAGE_ISLOWER, 0x00ca, 0x00ea }, /* 0x00ea */ + { CODEPAGE_ISLOWER, 0x00cb, 0x00eb }, /* 0x00eb */ + { CODEPAGE_ISLOWER, 0x00cc, 0x00ec }, /* 0x00ec */ + { CODEPAGE_ISLOWER, 0x00cd, 0x00ed }, /* 0x00ed */ + { CODEPAGE_ISLOWER, 0x00ce, 0x00ee }, /* 0x00ee */ + { CODEPAGE_ISLOWER, 0x00cf, 0x00ef }, /* 0x00ef */ + { CODEPAGE_ISLOWER, 0x00d0, 0x00f0 }, /* 0x00f0 */ + { CODEPAGE_ISLOWER, 0x00d1, 0x00f1 }, /* 0x00f1 */ + { CODEPAGE_ISLOWER, 0x00d2, 0x00f2 }, /* 0x00f2 */ + { CODEPAGE_ISLOWER, 0x00d3, 0x00f3 }, /* 0x00f3 */ + { CODEPAGE_ISLOWER, 0x00d4, 0x00f4 }, /* 0x00f4 */ + { CODEPAGE_ISLOWER, 0x00d5, 0x00f5 }, /* 0x00f5 */ + { CODEPAGE_ISLOWER, 0x00d6, 0x00f6 }, /* 0x00f6 */ + { CODEPAGE_ISNONE, 0x00f7, 0x00f7 }, /* 0x00f7 */ + { CODEPAGE_ISLOWER, 0x00d8, 0x00f8 }, /* 0x00f8 */ + { CODEPAGE_ISLOWER, 0x00d9, 0x00f9 }, /* 0x00f9 */ + { CODEPAGE_ISLOWER, 0x00da, 0x00fa }, /* 0x00fa */ + { CODEPAGE_ISLOWER, 0x00db, 0x00fb }, /* 0x00fb */ + { CODEPAGE_ISLOWER, 0x00dc, 0x00fc }, /* 0x00fc */ + { CODEPAGE_ISLOWER, 0x00dd, 0x00fd }, /* 0x00fd */ + { CODEPAGE_ISLOWER, 0x00de, 0x00fe }, /* 0x00fe */ + { CODEPAGE_ISNONE, 0x00ff, 0x00ff } /* 0x00ff */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_CP_LATIN5_H */ diff --git a/usr/src/uts/common/smbsrv/cp_latin6.h b/usr/src/uts/common/smbsrv/cp_latin6.h new file mode 100644 index 000000000000..fd86d276c4fa --- /dev/null +++ b/usr/src/uts/common/smbsrv/cp_latin6.h @@ -0,0 +1,309 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_CP_LATIN6_H +#define _SMBSRV_CP_LATIN6_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This is the codepage for the Latin-6 Character Set. + * This codepage defines values for the special characters + * needed for the written alphabets of the following + * Nordic Languages: Greenlandic, Eskimo, Lappish, + * and Icelandic + * The Latin-6 character set is also known as iso-8859-10 + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +codepage_t Latin6_codepage[256] = { + { CODEPAGE_ISNONE, 0x0000, 0x0000 }, /* 0x0000 */ + { CODEPAGE_ISNONE, 0x0001, 0x0001 }, /* 0x0001 */ + { CODEPAGE_ISNONE, 0x0002, 0x0002 }, /* 0x0002 */ + { CODEPAGE_ISNONE, 0x0003, 0x0003 }, /* 0x0003 */ + { CODEPAGE_ISNONE, 0x0004, 0x0004 }, /* 0x0004 */ + { CODEPAGE_ISNONE, 0x0005, 0x0005 }, /* 0x0005 */ + { CODEPAGE_ISNONE, 0x0006, 0x0006 }, /* 0x0006 */ + { CODEPAGE_ISNONE, 0x0007, 0x0007 }, /* 0x0007 */ + { CODEPAGE_ISNONE, 0x0008, 0x0008 }, /* 0x0008 */ + { CODEPAGE_ISNONE, 0x0009, 0x0009 }, /* 0x0009 */ + { CODEPAGE_ISNONE, 0x000a, 0x000a }, /* 0x000a */ + { CODEPAGE_ISNONE, 0x000b, 0x000b }, /* 0x000b */ + { CODEPAGE_ISNONE, 0x000c, 0x000c }, /* 0x000c */ + { CODEPAGE_ISNONE, 0x000d, 0x000d }, /* 0x000d */ + { CODEPAGE_ISNONE, 0x000e, 0x000e }, /* 0x000e */ + { CODEPAGE_ISNONE, 0x000f, 0x000f }, /* 0x000f */ + { CODEPAGE_ISNONE, 0x0010, 0x0010 }, /* 0x0010 */ + { CODEPAGE_ISNONE, 0x0011, 0x0011 }, /* 0x0011 */ + { CODEPAGE_ISNONE, 0x0012, 0x0012 }, /* 0x0012 */ + { CODEPAGE_ISNONE, 0x0013, 0x0013 }, /* 0x0013 */ + { CODEPAGE_ISNONE, 0x0014, 0x0014 }, /* 0x0014 */ + { CODEPAGE_ISNONE, 0x0015, 0x0015 }, /* 0x0015 */ + { CODEPAGE_ISNONE, 0x0016, 0x0016 }, /* 0x0016 */ + { CODEPAGE_ISNONE, 0x0017, 0x0017 }, /* 0x0017 */ + { CODEPAGE_ISNONE, 0x0018, 0x0018 }, /* 0x0018 */ + { CODEPAGE_ISNONE, 0x0019, 0x0019 }, /* 0x0019 */ + { CODEPAGE_ISNONE, 0x001a, 0x001a }, /* 0x001a */ + { CODEPAGE_ISNONE, 0x001b, 0x001b }, /* 0x001b */ + { CODEPAGE_ISNONE, 0x001c, 0x001c }, /* 0x001c */ + { CODEPAGE_ISNONE, 0x001d, 0x001d }, /* 0x001d */ + { CODEPAGE_ISNONE, 0x001e, 0x001e }, /* 0x001e */ + { CODEPAGE_ISNONE, 0x001f, 0x001f }, /* 0x001f */ + { CODEPAGE_ISNONE, 0x0020, 0x0020 }, /* 0x0020 */ + { CODEPAGE_ISNONE, 0x0021, 0x0021 }, /* 0x0021 */ + { CODEPAGE_ISNONE, 0x0022, 0x0022 }, /* 0x0022 */ + { CODEPAGE_ISNONE, 0x0023, 0x0023 }, /* 0x0023 */ + { CODEPAGE_ISNONE, 0x0024, 0x0024 }, /* 0x0024 */ + { CODEPAGE_ISNONE, 0x0025, 0x0025 }, /* 0x0025 */ + { CODEPAGE_ISNONE, 0x0026, 0x0026 }, /* 0x0026 */ + { CODEPAGE_ISNONE, 0x0027, 0x0027 }, /* 0x0027 */ + { CODEPAGE_ISNONE, 0x0028, 0x0028 }, /* 0x0028 */ + { CODEPAGE_ISNONE, 0x0029, 0x0029 }, /* 0x0029 */ + { CODEPAGE_ISNONE, 0x002a, 0x002a }, /* 0x002a */ + { CODEPAGE_ISNONE, 0x002b, 0x002b }, /* 0x002b */ + { CODEPAGE_ISNONE, 0x002c, 0x002c }, /* 0x002c */ + { CODEPAGE_ISNONE, 0x002d, 0x002d }, /* 0x002d */ + { CODEPAGE_ISNONE, 0x002e, 0x002e }, /* 0x002e */ + { CODEPAGE_ISNONE, 0x002f, 0x002f }, /* 0x002f */ + { CODEPAGE_ISNONE, 0x0030, 0x0030 }, /* 0x0030 */ + { CODEPAGE_ISNONE, 0x0031, 0x0031 }, /* 0x0031 */ + { CODEPAGE_ISNONE, 0x0032, 0x0032 }, /* 0x0032 */ + { CODEPAGE_ISNONE, 0x0033, 0x0033 }, /* 0x0033 */ + { CODEPAGE_ISNONE, 0x0034, 0x0034 }, /* 0x0034 */ + { CODEPAGE_ISNONE, 0x0035, 0x0035 }, /* 0x0035 */ + { CODEPAGE_ISNONE, 0x0036, 0x0036 }, /* 0x0036 */ + { CODEPAGE_ISNONE, 0x0037, 0x0037 }, /* 0x0037 */ + { CODEPAGE_ISNONE, 0x0038, 0x0038 }, /* 0x0038 */ + { CODEPAGE_ISNONE, 0x0039, 0x0039 }, /* 0x0039 */ + { CODEPAGE_ISNONE, 0x003a, 0x003a }, /* 0x003a */ + { CODEPAGE_ISNONE, 0x003b, 0x003b }, /* 0x003b */ + { CODEPAGE_ISNONE, 0x003c, 0x003c }, /* 0x003c */ + { CODEPAGE_ISNONE, 0x003d, 0x003d }, /* 0x003d */ + { CODEPAGE_ISNONE, 0x003e, 0x003e }, /* 0x003e */ + { CODEPAGE_ISNONE, 0x003f, 0x003f }, /* 0x003f */ + { CODEPAGE_ISNONE, 0x0040, 0x0040 }, /* 0x0040 */ + { CODEPAGE_ISUPPER, 0x0041, 0x0061 }, /* 0x0041 */ + { CODEPAGE_ISUPPER, 0x0042, 0x0062 }, /* 0x0042 */ + { CODEPAGE_ISUPPER, 0x0043, 0x0063 }, /* 0x0043 */ + { CODEPAGE_ISUPPER, 0x0044, 0x0064 }, /* 0x0044 */ + { CODEPAGE_ISUPPER, 0x0045, 0x0065 }, /* 0x0045 */ + { CODEPAGE_ISUPPER, 0x0046, 0x0066 }, /* 0x0046 */ + { CODEPAGE_ISUPPER, 0x0047, 0x0067 }, /* 0x0047 */ + { CODEPAGE_ISUPPER, 0x0048, 0x0068 }, /* 0x0048 */ + { CODEPAGE_ISUPPER, 0x0049, 0x0069 }, /* 0x0049 */ + { CODEPAGE_ISUPPER, 0x004a, 0x006a }, /* 0x004a */ + { CODEPAGE_ISUPPER, 0x004b, 0x006b }, /* 0x004b */ + { CODEPAGE_ISUPPER, 0x004c, 0x006c }, /* 0x004c */ + { CODEPAGE_ISUPPER, 0x004d, 0x006d }, /* 0x004d */ + { CODEPAGE_ISUPPER, 0x004e, 0x006e }, /* 0x004e */ + { CODEPAGE_ISUPPER, 0x004f, 0x006f }, /* 0x004f */ + { CODEPAGE_ISUPPER, 0x0050, 0x0070 }, /* 0x0050 */ + { CODEPAGE_ISUPPER, 0x0051, 0x0071 }, /* 0x0051 */ + { CODEPAGE_ISUPPER, 0x0052, 0x0072 }, /* 0x0052 */ + { CODEPAGE_ISUPPER, 0x0053, 0x0073 }, /* 0x0053 */ + { CODEPAGE_ISUPPER, 0x0054, 0x0074 }, /* 0x0054 */ + { CODEPAGE_ISUPPER, 0x0055, 0x0075 }, /* 0x0055 */ + { CODEPAGE_ISUPPER, 0x0056, 0x0076 }, /* 0x0056 */ + { CODEPAGE_ISUPPER, 0x0057, 0x0077 }, /* 0x0057 */ + { CODEPAGE_ISUPPER, 0x0058, 0x0078 }, /* 0x0058 */ + { CODEPAGE_ISUPPER, 0x0059, 0x0079 }, /* 0x0059 */ + { CODEPAGE_ISUPPER, 0x005a, 0x007a }, /* 0x005a */ + { CODEPAGE_ISNONE, 0x005b, 0x005b }, /* 0x005b */ + { CODEPAGE_ISNONE, 0x005c, 0x005c }, /* 0x005c */ + { CODEPAGE_ISNONE, 0x005d, 0x005d }, /* 0x005d */ + { CODEPAGE_ISNONE, 0x005e, 0x005e }, /* 0x005e */ + { CODEPAGE_ISNONE, 0x005f, 0x005f }, /* 0x005f */ + { CODEPAGE_ISNONE, 0x0060, 0x0060 }, /* 0x0060 */ + { CODEPAGE_ISLOWER, 0x0041, 0x0061 }, /* 0x0061 */ + { CODEPAGE_ISLOWER, 0x0042, 0x0062 }, /* 0x0062 */ + { CODEPAGE_ISLOWER, 0x0043, 0x0063 }, /* 0x0063 */ + { CODEPAGE_ISLOWER, 0x0044, 0x0064 }, /* 0x0064 */ + { CODEPAGE_ISLOWER, 0x0045, 0x0065 }, /* 0x0065 */ + { CODEPAGE_ISLOWER, 0x0046, 0x0066 }, /* 0x0066 */ + { CODEPAGE_ISLOWER, 0x0047, 0x0067 }, /* 0x0067 */ + { CODEPAGE_ISLOWER, 0x0048, 0x0068 }, /* 0x0068 */ + { CODEPAGE_ISLOWER, 0x0049, 0x0069 }, /* 0x0069 */ + { CODEPAGE_ISLOWER, 0x004a, 0x006a }, /* 0x006a */ + { CODEPAGE_ISLOWER, 0x004b, 0x006b }, /* 0x006b */ + { CODEPAGE_ISLOWER, 0x004c, 0x006c }, /* 0x006c */ + { CODEPAGE_ISLOWER, 0x004d, 0x006d }, /* 0x006d */ + { CODEPAGE_ISLOWER, 0x004e, 0x006e }, /* 0x006e */ + { CODEPAGE_ISLOWER, 0x004f, 0x006f }, /* 0x006f */ + { CODEPAGE_ISLOWER, 0x0050, 0x0070 }, /* 0x0070 */ + { CODEPAGE_ISLOWER, 0x0051, 0x0071 }, /* 0x0071 */ + { CODEPAGE_ISLOWER, 0x0052, 0x0072 }, /* 0x0072 */ + { CODEPAGE_ISLOWER, 0x0053, 0x0073 }, /* 0x0073 */ + { CODEPAGE_ISLOWER, 0x0054, 0x0074 }, /* 0x0074 */ + { CODEPAGE_ISLOWER, 0x0055, 0x0075 }, /* 0x0075 */ + { CODEPAGE_ISLOWER, 0x0056, 0x0076 }, /* 0x0076 */ + { CODEPAGE_ISLOWER, 0x0057, 0x0077 }, /* 0x0077 */ + { CODEPAGE_ISLOWER, 0x0058, 0x0078 }, /* 0x0078 */ + { CODEPAGE_ISLOWER, 0x0059, 0x0079 }, /* 0x0079 */ + { CODEPAGE_ISLOWER, 0x005a, 0x007a }, /* 0x007a */ + { CODEPAGE_ISNONE, 0x007b, 0x007b }, /* 0x007b */ + { CODEPAGE_ISNONE, 0x007c, 0x007c }, /* 0x007c */ + { CODEPAGE_ISNONE, 0x007d, 0x007d }, /* 0x007d */ + { CODEPAGE_ISNONE, 0x007e, 0x007e }, /* 0x007e */ + { CODEPAGE_ISNONE, 0x007f, 0x007f }, /* 0x007f */ + { CODEPAGE_ISNONE, 0x0080, 0x0080 }, /* 0x0080 */ + { CODEPAGE_ISNONE, 0x0081, 0x0081 }, /* 0x0081 */ + { CODEPAGE_ISNONE, 0x0082, 0x0082 }, /* 0x0082 */ + { CODEPAGE_ISNONE, 0x0083, 0x0083 }, /* 0x0083 */ + { CODEPAGE_ISNONE, 0x0084, 0x0084 }, /* 0x0084 */ + { CODEPAGE_ISNONE, 0x0085, 0x0085 }, /* 0x0085 */ + { CODEPAGE_ISNONE, 0x0086, 0x0086 }, /* 0x0086 */ + { CODEPAGE_ISNONE, 0x0087, 0x0087 }, /* 0x0087 */ + { CODEPAGE_ISNONE, 0x0088, 0x0088 }, /* 0x0088 */ + { CODEPAGE_ISNONE, 0x0089, 0x0089 }, /* 0x0089 */ + { CODEPAGE_ISNONE, 0x008a, 0x008a }, /* 0x008a */ + { CODEPAGE_ISNONE, 0x008b, 0x008b }, /* 0x008b */ + { CODEPAGE_ISNONE, 0x008c, 0x008c }, /* 0x008c */ + { CODEPAGE_ISNONE, 0x008d, 0x008d }, /* 0x008d */ + { CODEPAGE_ISNONE, 0x008e, 0x008e }, /* 0x008e */ + { CODEPAGE_ISNONE, 0x008f, 0x008f }, /* 0x008f */ + { CODEPAGE_ISNONE, 0x0090, 0x0090 }, /* 0x0090 */ + { CODEPAGE_ISNONE, 0x0091, 0x0091 }, /* 0x0091 */ + { CODEPAGE_ISNONE, 0x0092, 0x0092 }, /* 0x0092 */ + { CODEPAGE_ISNONE, 0x0093, 0x0093 }, /* 0x0093 */ + { CODEPAGE_ISNONE, 0x0094, 0x0094 }, /* 0x0094 */ + { CODEPAGE_ISNONE, 0x0095, 0x0095 }, /* 0x0095 */ + { CODEPAGE_ISNONE, 0x0096, 0x0096 }, /* 0x0096 */ + { CODEPAGE_ISNONE, 0x0097, 0x0097 }, /* 0x0097 */ + { CODEPAGE_ISNONE, 0x0098, 0x0098 }, /* 0x0098 */ + { CODEPAGE_ISNONE, 0x0099, 0x0099 }, /* 0x0099 */ + { CODEPAGE_ISNONE, 0x009a, 0x009a }, /* 0x009a */ + { CODEPAGE_ISNONE, 0x009b, 0x009b }, /* 0x009b */ + { CODEPAGE_ISNONE, 0x009c, 0x009c }, /* 0x009c */ + { CODEPAGE_ISNONE, 0x009d, 0x009d }, /* 0x009d */ + { CODEPAGE_ISNONE, 0x009e, 0x009e }, /* 0x009e */ + { CODEPAGE_ISNONE, 0x009f, 0x009f }, /* 0x009f */ + { CODEPAGE_ISNONE, 0x00a0, 0x00a0 }, /* 0x00a0 */ + { CODEPAGE_ISUPPER, 0x00a1, 0x00b1 }, /* 0x00a1 */ + { CODEPAGE_ISUPPER, 0x00a2, 0x00b2 }, /* 0x00a2 */ + { CODEPAGE_ISUPPER, 0x00a3, 0x00b3 }, /* 0x00a3 */ + { CODEPAGE_ISUPPER, 0x00a4, 0x00b4 }, /* 0x00a4 */ + { CODEPAGE_ISUPPER, 0x00a5, 0x00b5 }, /* 0x00a5 */ + { CODEPAGE_ISUPPER, 0x00a6, 0x00b6 }, /* 0x00a6 */ + { CODEPAGE_ISNONE, 0x00a7, 0x00a7 }, /* 0x00a7 */ + { CODEPAGE_ISUPPER, 0x00a8, 0x00b8 }, /* 0x00a8 */ + { CODEPAGE_ISUPPER, 0x00a9, 0x00b9 }, /* 0x00a9 */ + { CODEPAGE_ISUPPER, 0x00aa, 0x00ba }, /* 0x00aa */ + { CODEPAGE_ISUPPER, 0x00ab, 0x00bb }, /* 0x00ab */ + { CODEPAGE_ISUPPER, 0x00ac, 0x00bc }, /* 0x00ac */ + { CODEPAGE_ISNONE, 0x00ad, 0x00ad }, /* 0x00ad */ + { CODEPAGE_ISUPPER, 0x00ae, 0x00be }, /* 0x00ae */ + { CODEPAGE_ISUPPER, 0x00af, 0x00bf }, /* 0x00af */ + { CODEPAGE_ISNONE, 0x00b0, 0x00b0 }, /* 0x00b0 */ + { CODEPAGE_ISLOWER, 0x00a1, 0x00b1 }, /* 0x00b1 */ + { CODEPAGE_ISLOWER, 0x00a2, 0x00b2 }, /* 0x00b2 */ + { CODEPAGE_ISLOWER, 0x00a3, 0x00b3 }, /* 0x00b3 */ + { CODEPAGE_ISLOWER, 0x00a4, 0x00b4 }, /* 0x00b4 */ + { CODEPAGE_ISLOWER, 0x00a5, 0x00b5 }, /* 0x00b5 */ + { CODEPAGE_ISLOWER, 0x00a6, 0x00b6 }, /* 0x00b6 */ + { CODEPAGE_ISNONE, 0x00b7, 0x00b7 }, /* 0x00b7 */ + { CODEPAGE_ISLOWER, 0x00a8, 0x00b8 }, /* 0x00b8 */ + { CODEPAGE_ISLOWER, 0x00a9, 0x00b9 }, /* 0x00b9 */ + { CODEPAGE_ISLOWER, 0x00aa, 0x00ba }, /* 0x00ba */ + { CODEPAGE_ISLOWER, 0x00ab, 0x00bb }, /* 0x00bb */ + { CODEPAGE_ISLOWER, 0x00ac, 0x00bc }, /* 0x00bc */ + { CODEPAGE_ISNONE, 0x00bd, 0x00bd }, /* 0x00bd */ + { CODEPAGE_ISLOWER, 0x00ae, 0x00be }, /* 0x00be */ + { CODEPAGE_ISLOWER, 0x00af, 0x00bf }, /* 0x00bf */ + { CODEPAGE_ISUPPER, 0x00c0, 0x00e0 }, /* 0x00c0 */ + { CODEPAGE_ISUPPER, 0x00c1, 0x00e1 }, /* 0x00c1 */ + { CODEPAGE_ISUPPER, 0x00c2, 0x00e2 }, /* 0x00c2 */ + { CODEPAGE_ISUPPER, 0x00c3, 0x00e3 }, /* 0x00c3 */ + { CODEPAGE_ISUPPER, 0x00c4, 0x00e4 }, /* 0x00c4 */ + { CODEPAGE_ISUPPER, 0x00c5, 0x00e5 }, /* 0x00c5 */ + { CODEPAGE_ISUPPER, 0x00c6, 0x00e6 }, /* 0x00c6 */ + { CODEPAGE_ISUPPER, 0x00c7, 0x00e7 }, /* 0x00c7 */ + { CODEPAGE_ISUPPER, 0x00c8, 0x00e8 }, /* 0x00c8 */ + { CODEPAGE_ISUPPER, 0x00c9, 0x00e9 }, /* 0x00c9 */ + { CODEPAGE_ISUPPER, 0x00ca, 0x00ea }, /* 0x00ca */ + { CODEPAGE_ISUPPER, 0x00cb, 0x00eb }, /* 0x00cb */ + { CODEPAGE_ISUPPER, 0x00cc, 0x00ec }, /* 0x00cc */ + { CODEPAGE_ISUPPER, 0x00cd, 0x00ed }, /* 0x00cd */ + { CODEPAGE_ISUPPER, 0x00ce, 0x00ee }, /* 0x00ce */ + { CODEPAGE_ISUPPER, 0x00cf, 0x00ef }, /* 0x00cf */ + { CODEPAGE_ISNONE, 0x00d0, 0x00d0 }, /* 0x00d0 */ + { CODEPAGE_ISUPPER, 0x00d1, 0x00f1 }, /* 0x00d1 */ + { CODEPAGE_ISUPPER, 0x00d2, 0x00f2 }, /* 0x00d2 */ + { CODEPAGE_ISUPPER, 0x00d3, 0x00f3 }, /* 0x00d3 */ + { CODEPAGE_ISUPPER, 0x00d4, 0x00f4 }, /* 0x00d4 */ + { CODEPAGE_ISUPPER, 0x00d5, 0x00f5 }, /* 0x00d5 */ + { CODEPAGE_ISUPPER, 0x00d6, 0x00f6 }, /* 0x00d6 */ + { CODEPAGE_ISUPPER, 0x00d7, 0x00f7 }, /* 0x00d7 */ + { CODEPAGE_ISUPPER, 0x00d8, 0x00f8 }, /* 0x00d8 */ + { CODEPAGE_ISUPPER, 0x00d9, 0x00f9 }, /* 0x00d9 */ + { CODEPAGE_ISUPPER, 0x00da, 0x00fa }, /* 0x00da */ + { CODEPAGE_ISUPPER, 0x00db, 0x00fb }, /* 0x00db */ + { CODEPAGE_ISUPPER, 0x00dc, 0x00fc }, /* 0x00dc */ + { CODEPAGE_ISUPPER, 0x00dd, 0x00fd }, /* 0x00dd */ + { CODEPAGE_ISUPPER, 0x00de, 0x00fe }, /* 0x00de */ + { CODEPAGE_ISNONE, 0x00df, 0x00df }, /* 0x00df */ + { CODEPAGE_ISLOWER, 0x00c0, 0x00e0 }, /* 0x00e0 */ + { CODEPAGE_ISLOWER, 0x00c1, 0x00e1 }, /* 0x00e1 */ + { CODEPAGE_ISLOWER, 0x00c2, 0x00e2 }, /* 0x00e2 */ + { CODEPAGE_ISLOWER, 0x00c3, 0x00e3 }, /* 0x00e3 */ + { CODEPAGE_ISLOWER, 0x00c4, 0x00e4 }, /* 0x00e4 */ + { CODEPAGE_ISLOWER, 0x00c5, 0x00e5 }, /* 0x00e5 */ + { CODEPAGE_ISLOWER, 0x00c6, 0x00e6 }, /* 0x00e6 */ + { CODEPAGE_ISLOWER, 0x00c7, 0x00e7 }, /* 0x00e7 */ + { CODEPAGE_ISLOWER, 0x00c8, 0x00e8 }, /* 0x00e8 */ + { CODEPAGE_ISLOWER, 0x00c9, 0x00e9 }, /* 0x00e9 */ + { CODEPAGE_ISLOWER, 0x00ca, 0x00ea }, /* 0x00ea */ + { CODEPAGE_ISLOWER, 0x00cb, 0x00eb }, /* 0x00eb */ + { CODEPAGE_ISLOWER, 0x00cc, 0x00ec }, /* 0x00ec */ + { CODEPAGE_ISLOWER, 0x00cd, 0x00ed }, /* 0x00ed */ + { CODEPAGE_ISLOWER, 0x00ce, 0x00ee }, /* 0x00ee */ + { CODEPAGE_ISLOWER, 0x00cf, 0x00ef }, /* 0x00ef */ + { CODEPAGE_ISNONE, 0x00f0, 0x00f0 }, /* 0x00f0 */ + { CODEPAGE_ISLOWER, 0x00d1, 0x00f1 }, /* 0x00f1 */ + { CODEPAGE_ISLOWER, 0x00d2, 0x00f2 }, /* 0x00f2 */ + { CODEPAGE_ISLOWER, 0x00d3, 0x00f3 }, /* 0x00f3 */ + { CODEPAGE_ISLOWER, 0x00d4, 0x00f4 }, /* 0x00f4 */ + { CODEPAGE_ISLOWER, 0x00d5, 0x00f5 }, /* 0x00f5 */ + { CODEPAGE_ISLOWER, 0x00d6, 0x00f6 }, /* 0x00f6 */ + { CODEPAGE_ISLOWER, 0x00d7, 0x00f7 }, /* 0x00f7 */ + { CODEPAGE_ISLOWER, 0x00d8, 0x00f8 }, /* 0x00f8 */ + { CODEPAGE_ISLOWER, 0x00d9, 0x00f9 }, /* 0x00f9 */ + { CODEPAGE_ISLOWER, 0x00da, 0x00fa }, /* 0x00fa */ + { CODEPAGE_ISLOWER, 0x00db, 0x00fb }, /* 0x00fb */ + { CODEPAGE_ISLOWER, 0x00dc, 0x00fc }, /* 0x00fc */ + { CODEPAGE_ISLOWER, 0x00dd, 0x00fd }, /* 0x00fd */ + { CODEPAGE_ISLOWER, 0x00de, 0x00fe }, /* 0x00fe */ + { CODEPAGE_ISNONE, 0x00ff, 0x00ff } /* 0x00ff */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_CP_LATIN6_H */ diff --git a/usr/src/uts/common/smbsrv/cp_unicode.h b/usr/src/uts/common/smbsrv/cp_unicode.h new file mode 100644 index 000000000000..e2f1eb83f991 --- /dev/null +++ b/usr/src/uts/common/smbsrv/cp_unicode.h @@ -0,0 +1,6639 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_CP_UNICODE_H +#define _SMBSRV_CP_UNICODE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct unicode_cp { + mts_wchar_t val; + mts_wchar_t ctype; + mts_wchar_t lower; + mts_wchar_t upper; +}; + +struct unicode_cp a_unicode[] = { + { 0x0000, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0001, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0002, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0003, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0004, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0005, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0006, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0007, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0008, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0009, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x000A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x000B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x000C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x000D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x000E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x000F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0010, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0011, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0012, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0013, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0014, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0015, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0016, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0017, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0018, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0019, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x001A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x001B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x001C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x001D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x001E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x001F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0020, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0021, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0022, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0023, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0024, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0025, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0026, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0027, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0028, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0029, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x002A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x002B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x002C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x002D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x002E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x002F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0030, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0031, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0032, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0033, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0034, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0035, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0036, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0037, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0038, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0039, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x003A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x003B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x003C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x003D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x003E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x003F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0040, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0041, CODEPAGE_ISUPPER, 0x0061, 0xFFFF }, + { 0x0042, CODEPAGE_ISUPPER, 0x0062, 0xFFFF }, + { 0x0043, CODEPAGE_ISUPPER, 0x0063, 0xFFFF }, + { 0x0044, CODEPAGE_ISUPPER, 0x0064, 0xFFFF }, + { 0x0045, CODEPAGE_ISUPPER, 0x0065, 0xFFFF }, + { 0x0046, CODEPAGE_ISUPPER, 0x0066, 0xFFFF }, + { 0x0047, CODEPAGE_ISUPPER, 0x0067, 0xFFFF }, + { 0x0048, CODEPAGE_ISUPPER, 0x0068, 0xFFFF }, + { 0x0049, CODEPAGE_ISUPPER, 0x0069, 0xFFFF }, + { 0x004A, CODEPAGE_ISUPPER, 0x006A, 0xFFFF }, + { 0x004B, CODEPAGE_ISUPPER, 0x006B, 0xFFFF }, + { 0x004C, CODEPAGE_ISUPPER, 0x006C, 0xFFFF }, + { 0x004D, CODEPAGE_ISUPPER, 0x006D, 0xFFFF }, + { 0x004E, CODEPAGE_ISUPPER, 0x006E, 0xFFFF }, + { 0x004F, CODEPAGE_ISUPPER, 0x006F, 0xFFFF }, + { 0x0050, CODEPAGE_ISUPPER, 0x0070, 0xFFFF }, + { 0x0051, CODEPAGE_ISUPPER, 0x0071, 0xFFFF }, + { 0x0052, CODEPAGE_ISUPPER, 0x0072, 0xFFFF }, + { 0x0053, CODEPAGE_ISUPPER, 0x0073, 0xFFFF }, + { 0x0054, CODEPAGE_ISUPPER, 0x0074, 0xFFFF }, + { 0x0055, CODEPAGE_ISUPPER, 0x0075, 0xFFFF }, + { 0x0056, CODEPAGE_ISUPPER, 0x0076, 0xFFFF }, + { 0x0057, CODEPAGE_ISUPPER, 0x0077, 0xFFFF }, + { 0x0058, CODEPAGE_ISUPPER, 0x0078, 0xFFFF }, + { 0x0059, CODEPAGE_ISUPPER, 0x0079, 0xFFFF }, + { 0x005A, CODEPAGE_ISUPPER, 0x007A, 0xFFFF }, + { 0x005B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x005C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x005D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x005E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x005F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0060, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0061, CODEPAGE_ISLOWER, 0xFFFF, 0x0041 }, + { 0x0062, CODEPAGE_ISLOWER, 0xFFFF, 0x0042 }, + { 0x0063, CODEPAGE_ISLOWER, 0xFFFF, 0x0043 }, + { 0x0064, CODEPAGE_ISLOWER, 0xFFFF, 0x0044 }, + { 0x0065, CODEPAGE_ISLOWER, 0xFFFF, 0x0045 }, + { 0x0066, CODEPAGE_ISLOWER, 0xFFFF, 0x0046 }, + { 0x0067, CODEPAGE_ISLOWER, 0xFFFF, 0x0047 }, + { 0x0068, CODEPAGE_ISLOWER, 0xFFFF, 0x0048 }, + { 0x0069, CODEPAGE_ISLOWER, 0xFFFF, 0x0049 }, + { 0x006A, CODEPAGE_ISLOWER, 0xFFFF, 0x004A }, + { 0x006B, CODEPAGE_ISLOWER, 0xFFFF, 0x004B }, + { 0x006C, CODEPAGE_ISLOWER, 0xFFFF, 0x004C }, + { 0x006D, CODEPAGE_ISLOWER, 0xFFFF, 0x004D }, + { 0x006E, CODEPAGE_ISLOWER, 0xFFFF, 0x004E }, + { 0x006F, CODEPAGE_ISLOWER, 0xFFFF, 0x004F }, + { 0x0070, CODEPAGE_ISLOWER, 0xFFFF, 0x0050 }, + { 0x0071, CODEPAGE_ISLOWER, 0xFFFF, 0x0051 }, + { 0x0072, CODEPAGE_ISLOWER, 0xFFFF, 0x0052 }, + { 0x0073, CODEPAGE_ISLOWER, 0xFFFF, 0x0053 }, + { 0x0074, CODEPAGE_ISLOWER, 0xFFFF, 0x0054 }, + { 0x0075, CODEPAGE_ISLOWER, 0xFFFF, 0x0055 }, + { 0x0076, CODEPAGE_ISLOWER, 0xFFFF, 0x0056 }, + { 0x0077, CODEPAGE_ISLOWER, 0xFFFF, 0x0057 }, + { 0x0078, CODEPAGE_ISLOWER, 0xFFFF, 0x0058 }, + { 0x0079, CODEPAGE_ISLOWER, 0xFFFF, 0x0059 }, + { 0x007A, CODEPAGE_ISLOWER, 0xFFFF, 0x005A }, + { 0x007B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x007C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x007D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x007E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x007F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0080, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0081, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0082, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0083, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0084, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0085, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0086, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0087, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0088, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0089, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x008A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x008B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x008C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x008D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x008E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x008F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0090, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0091, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0092, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0093, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0094, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0095, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0096, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0097, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0098, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0099, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x009A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x009B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x009C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x009D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x009E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x009F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00A0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00A1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00A2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00A3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00A4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00A5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00A6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00A7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00A8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00A9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00AA, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x00AB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00AC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00AD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00AE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00AF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00B0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00B1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00B2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00B3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00B4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00B5, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x00B6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00B7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00B8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00B9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00BA, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x00BB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00BC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00BD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00BE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00BF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00C0, CODEPAGE_ISUPPER, 0x00E0, 0xFFFF }, + { 0x00C1, CODEPAGE_ISUPPER, 0x00E1, 0xFFFF }, + { 0x00C2, CODEPAGE_ISUPPER, 0x00E2, 0xFFFF }, + { 0x00C3, CODEPAGE_ISUPPER, 0x00E3, 0xFFFF }, + { 0x00C4, CODEPAGE_ISUPPER, 0x00E4, 0xFFFF }, + { 0x00C5, CODEPAGE_ISUPPER, 0x00E5, 0xFFFF }, + { 0x00C6, CODEPAGE_ISUPPER, 0x00E6, 0xFFFF }, + { 0x00C7, CODEPAGE_ISUPPER, 0x00E7, 0xFFFF }, + { 0x00C8, CODEPAGE_ISUPPER, 0x00E8, 0xFFFF }, + { 0x00C9, CODEPAGE_ISUPPER, 0x00E9, 0xFFFF }, + { 0x00CA, CODEPAGE_ISUPPER, 0x00EA, 0xFFFF }, + { 0x00CB, CODEPAGE_ISUPPER, 0x00EB, 0xFFFF }, + { 0x00CC, CODEPAGE_ISUPPER, 0x00EC, 0xFFFF }, + { 0x00CD, CODEPAGE_ISUPPER, 0x00ED, 0xFFFF }, + { 0x00CE, CODEPAGE_ISUPPER, 0x00EE, 0xFFFF }, + { 0x00CF, CODEPAGE_ISUPPER, 0x00EF, 0xFFFF }, + { 0x00D0, CODEPAGE_ISUPPER, 0x00F0, 0xFFFF }, + { 0x00D1, CODEPAGE_ISUPPER, 0x00F1, 0xFFFF }, + { 0x00D2, CODEPAGE_ISUPPER, 0x00F2, 0xFFFF }, + { 0x00D3, CODEPAGE_ISUPPER, 0x00F3, 0xFFFF }, + { 0x00D4, CODEPAGE_ISUPPER, 0x00F4, 0xFFFF }, + { 0x00D5, CODEPAGE_ISUPPER, 0x00F5, 0xFFFF }, + { 0x00D6, CODEPAGE_ISUPPER, 0x00F6, 0xFFFF }, + { 0x00D7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00D8, CODEPAGE_ISUPPER, 0x00F8, 0xFFFF }, + { 0x00D9, CODEPAGE_ISUPPER, 0x00F9, 0xFFFF }, + { 0x00DA, CODEPAGE_ISUPPER, 0x00FA, 0xFFFF }, + { 0x00DB, CODEPAGE_ISUPPER, 0x00FB, 0xFFFF }, + { 0x00DC, CODEPAGE_ISUPPER, 0x00FC, 0xFFFF }, + { 0x00DD, CODEPAGE_ISUPPER, 0x00FD, 0xFFFF }, + { 0x00DE, CODEPAGE_ISUPPER, 0x00FE, 0xFFFF }, + { 0x00DF, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x00E0, CODEPAGE_ISLOWER, 0xFFFF, 0x00C0 }, + { 0x00E1, CODEPAGE_ISLOWER, 0xFFFF, 0x00C1 }, + { 0x00E2, CODEPAGE_ISLOWER, 0xFFFF, 0x00C2 }, + { 0x00E3, CODEPAGE_ISLOWER, 0xFFFF, 0x00C3 }, + { 0x00E4, CODEPAGE_ISLOWER, 0xFFFF, 0x00C4 }, + { 0x00E5, CODEPAGE_ISLOWER, 0xFFFF, 0x00C5 }, + { 0x00E6, CODEPAGE_ISLOWER, 0xFFFF, 0x00C6 }, + { 0x00E7, CODEPAGE_ISLOWER, 0xFFFF, 0x00C7 }, + { 0x00E8, CODEPAGE_ISLOWER, 0xFFFF, 0x00C8 }, + { 0x00E9, CODEPAGE_ISLOWER, 0xFFFF, 0x00C9 }, + { 0x00EA, CODEPAGE_ISLOWER, 0xFFFF, 0x00CA }, + { 0x00EB, CODEPAGE_ISLOWER, 0xFFFF, 0x00CB }, + { 0x00EC, CODEPAGE_ISLOWER, 0xFFFF, 0x00CC }, + { 0x00ED, CODEPAGE_ISLOWER, 0xFFFF, 0x00CD }, + { 0x00EE, CODEPAGE_ISLOWER, 0xFFFF, 0x00CE }, + { 0x00EF, CODEPAGE_ISLOWER, 0xFFFF, 0x00CF }, + { 0x00F0, CODEPAGE_ISLOWER, 0xFFFF, 0x00D0 }, + { 0x00F1, CODEPAGE_ISLOWER, 0xFFFF, 0x00D1 }, + { 0x00F2, CODEPAGE_ISLOWER, 0xFFFF, 0x00D2 }, + { 0x00F3, CODEPAGE_ISLOWER, 0xFFFF, 0x00D3 }, + { 0x00F4, CODEPAGE_ISLOWER, 0xFFFF, 0x00D4 }, + { 0x00F5, CODEPAGE_ISLOWER, 0xFFFF, 0x00D5 }, + { 0x00F6, CODEPAGE_ISLOWER, 0xFFFF, 0x00D6 }, + { 0x00F7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x00F8, CODEPAGE_ISLOWER, 0xFFFF, 0x00D8 }, + { 0x00F9, CODEPAGE_ISLOWER, 0xFFFF, 0x00D9 }, + { 0x00FA, CODEPAGE_ISLOWER, 0xFFFF, 0x00DA }, + { 0x00FB, CODEPAGE_ISLOWER, 0xFFFF, 0x00DB }, + { 0x00FC, CODEPAGE_ISLOWER, 0xFFFF, 0x00DC }, + { 0x00FD, CODEPAGE_ISLOWER, 0xFFFF, 0x00DD }, + { 0x00FE, CODEPAGE_ISLOWER, 0xFFFF, 0x00DE }, + { 0x00FF, CODEPAGE_ISLOWER, 0xFFFF, 0x0178 }, + { 0x0100, CODEPAGE_ISUPPER, 0x0101, 0xFFFF }, + { 0x0101, CODEPAGE_ISLOWER, 0xFFFF, 0x0100 }, + { 0x0102, CODEPAGE_ISUPPER, 0x0103, 0xFFFF }, + { 0x0103, CODEPAGE_ISLOWER, 0xFFFF, 0x0102 }, + { 0x0104, CODEPAGE_ISUPPER, 0x0105, 0xFFFF }, + { 0x0105, CODEPAGE_ISLOWER, 0xFFFF, 0x0104 }, + { 0x0106, CODEPAGE_ISUPPER, 0x0107, 0xFFFF }, + { 0x0107, CODEPAGE_ISLOWER, 0xFFFF, 0x0106 }, + { 0x0108, CODEPAGE_ISUPPER, 0x0109, 0xFFFF }, + { 0x0109, CODEPAGE_ISLOWER, 0xFFFF, 0x0108 }, + { 0x010A, CODEPAGE_ISUPPER, 0x010B, 0xFFFF }, + { 0x010B, CODEPAGE_ISLOWER, 0xFFFF, 0x010A }, + { 0x010C, CODEPAGE_ISUPPER, 0x010D, 0xFFFF }, + { 0x010D, CODEPAGE_ISLOWER, 0xFFFF, 0x010C }, + { 0x010E, CODEPAGE_ISUPPER, 0x010F, 0xFFFF }, + { 0x010F, CODEPAGE_ISLOWER, 0xFFFF, 0x010E }, + { 0x0110, CODEPAGE_ISUPPER, 0x0111, 0xFFFF }, + { 0x0111, CODEPAGE_ISLOWER, 0xFFFF, 0x0110 }, + { 0x0112, CODEPAGE_ISUPPER, 0x0113, 0xFFFF }, + { 0x0113, CODEPAGE_ISLOWER, 0xFFFF, 0x0112 }, + { 0x0114, CODEPAGE_ISUPPER, 0x0115, 0xFFFF }, + { 0x0115, CODEPAGE_ISLOWER, 0xFFFF, 0x0114 }, + { 0x0116, CODEPAGE_ISUPPER, 0x0117, 0xFFFF }, + { 0x0117, CODEPAGE_ISLOWER, 0xFFFF, 0x0116 }, + { 0x0118, CODEPAGE_ISUPPER, 0x0119, 0xFFFF }, + { 0x0119, CODEPAGE_ISLOWER, 0xFFFF, 0x0118 }, + { 0x011A, CODEPAGE_ISUPPER, 0x011B, 0xFFFF }, + { 0x011B, CODEPAGE_ISLOWER, 0xFFFF, 0x011A }, + { 0x011C, CODEPAGE_ISUPPER, 0x011D, 0xFFFF }, + { 0x011D, CODEPAGE_ISLOWER, 0xFFFF, 0x011C }, + { 0x011E, CODEPAGE_ISUPPER, 0x011F, 0xFFFF }, + { 0x011F, CODEPAGE_ISLOWER, 0xFFFF, 0x011E }, + { 0x0120, CODEPAGE_ISUPPER, 0x0121, 0xFFFF }, + { 0x0121, CODEPAGE_ISLOWER, 0xFFFF, 0x0120 }, + { 0x0122, CODEPAGE_ISUPPER, 0x0123, 0xFFFF }, + { 0x0123, CODEPAGE_ISLOWER, 0xFFFF, 0x0122 }, + { 0x0124, CODEPAGE_ISUPPER, 0x0125, 0xFFFF }, + { 0x0125, CODEPAGE_ISLOWER, 0xFFFF, 0x0124 }, + { 0x0126, CODEPAGE_ISUPPER, 0x0127, 0xFFFF }, + { 0x0127, CODEPAGE_ISLOWER, 0xFFFF, 0x0126 }, + { 0x0128, CODEPAGE_ISUPPER, 0x0129, 0xFFFF }, + { 0x0129, CODEPAGE_ISLOWER, 0xFFFF, 0x0128 }, + { 0x012A, CODEPAGE_ISUPPER, 0x012B, 0xFFFF }, + { 0x012B, CODEPAGE_ISLOWER, 0xFFFF, 0x012A }, + { 0x012C, CODEPAGE_ISUPPER, 0x012D, 0xFFFF }, + { 0x012D, CODEPAGE_ISLOWER, 0xFFFF, 0x012C }, + { 0x012E, CODEPAGE_ISUPPER, 0x012F, 0xFFFF }, + { 0x012F, CODEPAGE_ISLOWER, 0xFFFF, 0x012E }, + { 0x0130, CODEPAGE_ISUPPER, 0x0069, 0xFFFF }, + { 0x0131, CODEPAGE_ISLOWER, 0xFFFF, 0x0049 }, + { 0x0132, CODEPAGE_ISUPPER, 0x0133, 0xFFFF }, + { 0x0133, CODEPAGE_ISLOWER, 0xFFFF, 0x0132 }, + { 0x0134, CODEPAGE_ISUPPER, 0x0135, 0xFFFF }, + { 0x0135, CODEPAGE_ISLOWER, 0xFFFF, 0x0134 }, + { 0x0136, CODEPAGE_ISUPPER, 0x0137, 0xFFFF }, + { 0x0137, CODEPAGE_ISLOWER, 0xFFFF, 0x0136 }, + { 0x0138, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0139, CODEPAGE_ISUPPER, 0x013A, 0xFFFF }, + { 0x013A, CODEPAGE_ISLOWER, 0xFFFF, 0x0139 }, + { 0x013B, CODEPAGE_ISUPPER, 0x013C, 0xFFFF }, + { 0x013C, CODEPAGE_ISLOWER, 0xFFFF, 0x013B }, + { 0x013D, CODEPAGE_ISUPPER, 0x013E, 0xFFFF }, + { 0x013E, CODEPAGE_ISLOWER, 0xFFFF, 0x013D }, + { 0x013F, CODEPAGE_ISUPPER, 0x0140, 0xFFFF }, + { 0x0140, CODEPAGE_ISLOWER, 0xFFFF, 0x013F }, + { 0x0141, CODEPAGE_ISUPPER, 0x0142, 0xFFFF }, + { 0x0142, CODEPAGE_ISLOWER, 0xFFFF, 0x0141 }, + { 0x0143, CODEPAGE_ISUPPER, 0x0144, 0xFFFF }, + { 0x0144, CODEPAGE_ISLOWER, 0xFFFF, 0x0143 }, + { 0x0145, CODEPAGE_ISUPPER, 0x0146, 0xFFFF }, + { 0x0146, CODEPAGE_ISLOWER, 0xFFFF, 0x0145 }, + { 0x0147, CODEPAGE_ISUPPER, 0x0148, 0xFFFF }, + { 0x0148, CODEPAGE_ISLOWER, 0xFFFF, 0x0147 }, + { 0x0149, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x014A, CODEPAGE_ISUPPER, 0x014B, 0xFFFF }, + { 0x014B, CODEPAGE_ISLOWER, 0xFFFF, 0x014A }, + { 0x014C, CODEPAGE_ISUPPER, 0x014D, 0xFFFF }, + { 0x014D, CODEPAGE_ISLOWER, 0xFFFF, 0x014C }, + { 0x014E, CODEPAGE_ISUPPER, 0x014F, 0xFFFF }, + { 0x014F, CODEPAGE_ISLOWER, 0xFFFF, 0x014E }, + { 0x0150, CODEPAGE_ISUPPER, 0x0151, 0xFFFF }, + { 0x0151, CODEPAGE_ISLOWER, 0xFFFF, 0x0150 }, + { 0x0152, CODEPAGE_ISUPPER, 0x0153, 0xFFFF }, + { 0x0153, CODEPAGE_ISLOWER, 0xFFFF, 0x0152 }, + { 0x0154, CODEPAGE_ISUPPER, 0x0155, 0xFFFF }, + { 0x0155, CODEPAGE_ISLOWER, 0xFFFF, 0x0154 }, + { 0x0156, CODEPAGE_ISUPPER, 0x0157, 0xFFFF }, + { 0x0157, CODEPAGE_ISLOWER, 0xFFFF, 0x0156 }, + { 0x0158, CODEPAGE_ISUPPER, 0x0159, 0xFFFF }, + { 0x0159, CODEPAGE_ISLOWER, 0xFFFF, 0x0158 }, + { 0x015A, CODEPAGE_ISUPPER, 0x015B, 0xFFFF }, + { 0x015B, CODEPAGE_ISLOWER, 0xFFFF, 0x015A }, + { 0x015C, CODEPAGE_ISUPPER, 0x015D, 0xFFFF }, + { 0x015D, CODEPAGE_ISLOWER, 0xFFFF, 0x015C }, + { 0x015E, CODEPAGE_ISUPPER, 0x015F, 0xFFFF }, + { 0x015F, CODEPAGE_ISLOWER, 0xFFFF, 0x015E }, + { 0x0160, CODEPAGE_ISUPPER, 0x0161, 0xFFFF }, + { 0x0161, CODEPAGE_ISLOWER, 0xFFFF, 0x0160 }, + { 0x0162, CODEPAGE_ISUPPER, 0x0163, 0xFFFF }, + { 0x0163, CODEPAGE_ISLOWER, 0xFFFF, 0x0162 }, + { 0x0164, CODEPAGE_ISUPPER, 0x0165, 0xFFFF }, + { 0x0165, CODEPAGE_ISLOWER, 0xFFFF, 0x0164 }, + { 0x0166, CODEPAGE_ISUPPER, 0x0167, 0xFFFF }, + { 0x0167, CODEPAGE_ISLOWER, 0xFFFF, 0x0166 }, + { 0x0168, CODEPAGE_ISUPPER, 0x0169, 0xFFFF }, + { 0x0169, CODEPAGE_ISLOWER, 0xFFFF, 0x0168 }, + { 0x016A, CODEPAGE_ISUPPER, 0x016B, 0xFFFF }, + { 0x016B, CODEPAGE_ISLOWER, 0xFFFF, 0x016A }, + { 0x016C, CODEPAGE_ISUPPER, 0x016D, 0xFFFF }, + { 0x016D, CODEPAGE_ISLOWER, 0xFFFF, 0x016C }, + { 0x016E, CODEPAGE_ISUPPER, 0x016F, 0xFFFF }, + { 0x016F, CODEPAGE_ISLOWER, 0xFFFF, 0x016E }, + { 0x0170, CODEPAGE_ISUPPER, 0x0171, 0xFFFF }, + { 0x0171, CODEPAGE_ISLOWER, 0xFFFF, 0x0170 }, + { 0x0172, CODEPAGE_ISUPPER, 0x0173, 0xFFFF }, + { 0x0173, CODEPAGE_ISLOWER, 0xFFFF, 0x0172 }, + { 0x0174, CODEPAGE_ISUPPER, 0x0175, 0xFFFF }, + { 0x0175, CODEPAGE_ISLOWER, 0xFFFF, 0x0174 }, + { 0x0176, CODEPAGE_ISUPPER, 0x0177, 0xFFFF }, + { 0x0177, CODEPAGE_ISLOWER, 0xFFFF, 0x0176 }, + { 0x0178, CODEPAGE_ISUPPER, 0x00FF, 0xFFFF }, + { 0x0179, CODEPAGE_ISUPPER, 0x017A, 0xFFFF }, + { 0x017A, CODEPAGE_ISLOWER, 0xFFFF, 0x0179 }, + { 0x017B, CODEPAGE_ISUPPER, 0x017C, 0xFFFF }, + { 0x017C, CODEPAGE_ISLOWER, 0xFFFF, 0x017B }, + { 0x017D, CODEPAGE_ISUPPER, 0x017E, 0xFFFF }, + { 0x017E, CODEPAGE_ISLOWER, 0xFFFF, 0x017D }, + { 0x017F, CODEPAGE_ISLOWER, 0xFFFF, 0x0053 }, + { 0x0180, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0181, CODEPAGE_ISUPPER, 0x0253, 0xFFFF }, + { 0x0182, CODEPAGE_ISUPPER, 0x0183, 0xFFFF }, + { 0x0183, CODEPAGE_ISLOWER, 0xFFFF, 0x0182 }, + { 0x0184, CODEPAGE_ISUPPER, 0x0185, 0xFFFF }, + { 0x0185, CODEPAGE_ISLOWER, 0xFFFF, 0x0184 }, + { 0x0186, CODEPAGE_ISUPPER, 0x0254, 0xFFFF }, + { 0x0187, CODEPAGE_ISUPPER, 0x0188, 0xFFFF }, + { 0x0188, CODEPAGE_ISLOWER, 0xFFFF, 0x0187 }, + { 0x0189, CODEPAGE_ISUPPER, 0x0256, 0xFFFF }, + { 0x018A, CODEPAGE_ISUPPER, 0x0257, 0xFFFF }, + { 0x018B, CODEPAGE_ISUPPER, 0x018C, 0xFFFF }, + { 0x018C, CODEPAGE_ISLOWER, 0xFFFF, 0x018B }, + { 0x018D, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x018E, CODEPAGE_ISUPPER, 0x0258, 0xFFFF }, + { 0x018F, CODEPAGE_ISUPPER, 0x0259, 0xFFFF }, + { 0x0190, CODEPAGE_ISUPPER, 0x025B, 0xFFFF }, + { 0x0191, CODEPAGE_ISUPPER, 0x0192, 0xFFFF }, + { 0x0192, CODEPAGE_ISLOWER, 0xFFFF, 0x0191 }, + { 0x0193, CODEPAGE_ISUPPER, 0x0260, 0xFFFF }, + { 0x0194, CODEPAGE_ISUPPER, 0x0263, 0xFFFF }, + { 0x0195, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0196, CODEPAGE_ISUPPER, 0x0269, 0xFFFF }, + { 0x0197, CODEPAGE_ISUPPER, 0x0268, 0xFFFF }, + { 0x0198, CODEPAGE_ISUPPER, 0x0199, 0xFFFF }, + { 0x0199, CODEPAGE_ISLOWER, 0xFFFF, 0x0198 }, + { 0x019A, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x019B, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x019C, CODEPAGE_ISUPPER, 0x026F, 0xFFFF }, + { 0x019D, CODEPAGE_ISUPPER, 0x0272, 0xFFFF }, + { 0x019E, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x019F, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x01A0, CODEPAGE_ISUPPER, 0x01A1, 0xFFFF }, + { 0x01A1, CODEPAGE_ISLOWER, 0xFFFF, 0x01A0 }, + { 0x01A2, CODEPAGE_ISUPPER, 0x01A3, 0xFFFF }, + { 0x01A3, CODEPAGE_ISLOWER, 0xFFFF, 0x01A2 }, + { 0x01A4, CODEPAGE_ISUPPER, 0x01A5, 0xFFFF }, + { 0x01A5, CODEPAGE_ISLOWER, 0xFFFF, 0x01A4 }, + { 0x01A6, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x01A7, CODEPAGE_ISUPPER, 0x01A8, 0xFFFF }, + { 0x01A8, CODEPAGE_ISLOWER, 0xFFFF, 0x01A7 }, + { 0x01A9, CODEPAGE_ISUPPER, 0x0283, 0xFFFF }, + { 0x01AA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x01AB, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x01AC, CODEPAGE_ISUPPER, 0x01AD, 0xFFFF }, + { 0x01AD, CODEPAGE_ISLOWER, 0xFFFF, 0x01AC }, + { 0x01AE, CODEPAGE_ISUPPER, 0x0288, 0xFFFF }, + { 0x01AF, CODEPAGE_ISUPPER, 0x01B0, 0xFFFF }, + { 0x01B0, CODEPAGE_ISLOWER, 0xFFFF, 0x01AF }, + { 0x01B1, CODEPAGE_ISUPPER, 0x028A, 0xFFFF }, + { 0x01B2, CODEPAGE_ISUPPER, 0x028B, 0xFFFF }, + { 0x01B3, CODEPAGE_ISUPPER, 0x01B4, 0xFFFF }, + { 0x01B4, CODEPAGE_ISLOWER, 0xFFFF, 0x01B3 }, + { 0x01B5, CODEPAGE_ISUPPER, 0x01B6, 0xFFFF }, + { 0x01B6, CODEPAGE_ISLOWER, 0xFFFF, 0x01B5 }, + { 0x01B7, CODEPAGE_ISUPPER, 0x0292, 0xFFFF }, + { 0x01B8, CODEPAGE_ISUPPER, 0x01B9, 0xFFFF }, + { 0x01B9, CODEPAGE_ISLOWER, 0xFFFF, 0x01B8 }, + { 0x01BA, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x01BB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x01BC, CODEPAGE_ISUPPER, 0x01BD, 0xFFFF }, + { 0x01BD, CODEPAGE_ISLOWER, 0xFFFF, 0x01BC }, + { 0x01BE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x01BF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x01C0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x01C1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x01C2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x01C3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x01C4, CODEPAGE_ISUPPER, 0x01C6, 0x01C5 }, + { 0x01C5, CODEPAGE_ISNONE, 0x01C6, 0xFFFF }, + { 0x01C6, CODEPAGE_ISLOWER, 0xFFFF, 0x01C5 }, + { 0x01C7, CODEPAGE_ISUPPER, 0x01C9, 0x01C8 }, + { 0x01C8, CODEPAGE_ISNONE, 0x01C9, 0xFFFF }, + { 0x01C9, CODEPAGE_ISLOWER, 0xFFFF, 0x01C8 }, + { 0x01CA, CODEPAGE_ISUPPER, 0x01CC, 0x01CB }, + { 0x01CB, CODEPAGE_ISNONE, 0x01CC, 0xFFFF }, + { 0x01CC, CODEPAGE_ISLOWER, 0xFFFF, 0x01CB }, + { 0x01CD, CODEPAGE_ISUPPER, 0x01CE, 0xFFFF }, + { 0x01CE, CODEPAGE_ISLOWER, 0xFFFF, 0x01CD }, + { 0x01CF, CODEPAGE_ISUPPER, 0x01D0, 0xFFFF }, + { 0x01D0, CODEPAGE_ISLOWER, 0xFFFF, 0x01CF }, + { 0x01D1, CODEPAGE_ISUPPER, 0x01D2, 0xFFFF }, + { 0x01D2, CODEPAGE_ISLOWER, 0xFFFF, 0x01D1 }, + { 0x01D3, CODEPAGE_ISUPPER, 0x01D4, 0xFFFF }, + { 0x01D4, CODEPAGE_ISLOWER, 0xFFFF, 0x01D3 }, + { 0x01D5, CODEPAGE_ISUPPER, 0x01D6, 0xFFFF }, + { 0x01D6, CODEPAGE_ISLOWER, 0xFFFF, 0x01D5 }, + { 0x01D7, CODEPAGE_ISUPPER, 0x01D8, 0xFFFF }, + { 0x01D8, CODEPAGE_ISLOWER, 0xFFFF, 0x01D7 }, + { 0x01D9, CODEPAGE_ISUPPER, 0x01DA, 0xFFFF }, + { 0x01DA, CODEPAGE_ISLOWER, 0xFFFF, 0x01D9 }, + { 0x01DB, CODEPAGE_ISUPPER, 0x01DC, 0xFFFF }, + { 0x01DC, CODEPAGE_ISLOWER, 0xFFFF, 0x01DB }, + { 0x01DD, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x01DE, CODEPAGE_ISUPPER, 0x01DF, 0xFFFF }, + { 0x01DF, CODEPAGE_ISLOWER, 0xFFFF, 0x01DE }, + { 0x01E0, CODEPAGE_ISUPPER, 0x01E1, 0xFFFF }, + { 0x01E1, CODEPAGE_ISLOWER, 0xFFFF, 0x01E0 }, + { 0x01E2, CODEPAGE_ISUPPER, 0x01E3, 0xFFFF }, + { 0x01E3, CODEPAGE_ISLOWER, 0xFFFF, 0x01E2 }, + { 0x01E4, CODEPAGE_ISUPPER, 0x01E5, 0xFFFF }, + { 0x01E5, CODEPAGE_ISLOWER, 0xFFFF, 0x01E4 }, + { 0x01E6, CODEPAGE_ISUPPER, 0x01E7, 0xFFFF }, + { 0x01E7, CODEPAGE_ISLOWER, 0xFFFF, 0x01E6 }, + { 0x01E8, CODEPAGE_ISUPPER, 0x01E9, 0xFFFF }, + { 0x01E9, CODEPAGE_ISLOWER, 0xFFFF, 0x01E8 }, + { 0x01EA, CODEPAGE_ISUPPER, 0x01EB, 0xFFFF }, + { 0x01EB, CODEPAGE_ISLOWER, 0xFFFF, 0x01EA }, + { 0x01EC, CODEPAGE_ISUPPER, 0x01ED, 0xFFFF }, + { 0x01ED, CODEPAGE_ISLOWER, 0xFFFF, 0x01EC }, + { 0x01EE, CODEPAGE_ISUPPER, 0x01EF, 0xFFFF }, + { 0x01EF, CODEPAGE_ISLOWER, 0xFFFF, 0x01EE }, + { 0x01F0, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x01F1, CODEPAGE_ISUPPER, 0x01F3, 0x01F2 }, + { 0x01F2, CODEPAGE_ISNONE, 0x01F3, 0xFFFF }, + { 0x01F3, CODEPAGE_ISLOWER, 0xFFFF, 0x01F2 }, + { 0x01F4, CODEPAGE_ISUPPER, 0x01F5, 0xFFFF }, + { 0x01F5, CODEPAGE_ISLOWER, 0xFFFF, 0x01F4 }, + { 0x01FA, CODEPAGE_ISUPPER, 0x01FB, 0xFFFF }, + { 0x01FB, CODEPAGE_ISLOWER, 0xFFFF, 0x01FA }, + { 0x01FC, CODEPAGE_ISUPPER, 0x01FD, 0xFFFF }, + { 0x01FD, CODEPAGE_ISLOWER, 0xFFFF, 0x01FC }, + { 0x01FE, CODEPAGE_ISUPPER, 0x01FF, 0xFFFF }, + { 0x01FF, CODEPAGE_ISLOWER, 0xFFFF, 0x01FE }, + { 0x0200, CODEPAGE_ISUPPER, 0x0201, 0xFFFF }, + { 0x0201, CODEPAGE_ISLOWER, 0xFFFF, 0x0200 }, + { 0x0202, CODEPAGE_ISUPPER, 0x0203, 0xFFFF }, + { 0x0203, CODEPAGE_ISLOWER, 0xFFFF, 0x0202 }, + { 0x0204, CODEPAGE_ISUPPER, 0x0205, 0xFFFF }, + { 0x0205, CODEPAGE_ISLOWER, 0xFFFF, 0x0204 }, + { 0x0206, CODEPAGE_ISUPPER, 0x0207, 0xFFFF }, + { 0x0207, CODEPAGE_ISLOWER, 0xFFFF, 0x0206 }, + { 0x0208, CODEPAGE_ISUPPER, 0x0209, 0xFFFF }, + { 0x0209, CODEPAGE_ISLOWER, 0xFFFF, 0x0208 }, + { 0x020A, CODEPAGE_ISUPPER, 0x020B, 0xFFFF }, + { 0x020B, CODEPAGE_ISLOWER, 0xFFFF, 0x020A }, + { 0x020C, CODEPAGE_ISUPPER, 0x020D, 0xFFFF }, + { 0x020D, CODEPAGE_ISLOWER, 0xFFFF, 0x020C }, + { 0x020E, CODEPAGE_ISUPPER, 0x020F, 0xFFFF }, + { 0x020F, CODEPAGE_ISLOWER, 0xFFFF, 0x020E }, + { 0x0210, CODEPAGE_ISUPPER, 0x0211, 0xFFFF }, + { 0x0211, CODEPAGE_ISLOWER, 0xFFFF, 0x0210 }, + { 0x0212, CODEPAGE_ISUPPER, 0x0213, 0xFFFF }, + { 0x0213, CODEPAGE_ISLOWER, 0xFFFF, 0x0212 }, + { 0x0214, CODEPAGE_ISUPPER, 0x0215, 0xFFFF }, + { 0x0215, CODEPAGE_ISLOWER, 0xFFFF, 0x0214 }, + { 0x0216, CODEPAGE_ISUPPER, 0x0217, 0xFFFF }, + { 0x0217, CODEPAGE_ISLOWER, 0xFFFF, 0x0216 }, + { 0x0250, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0251, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0252, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0253, CODEPAGE_ISLOWER, 0xFFFF, 0x0181 }, + { 0x0254, CODEPAGE_ISLOWER, 0xFFFF, 0x0186 }, + { 0x0255, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0256, CODEPAGE_ISLOWER, 0xFFFF, 0x0189 }, + { 0x0257, CODEPAGE_ISLOWER, 0xFFFF, 0x018A }, + { 0x0258, CODEPAGE_ISLOWER, 0xFFFF, 0x018E }, + { 0x0259, CODEPAGE_ISLOWER, 0xFFFF, 0x018F }, + { 0x025A, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x025B, CODEPAGE_ISLOWER, 0xFFFF, 0x0190 }, + { 0x025C, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x025D, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x025E, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x025F, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0260, CODEPAGE_ISLOWER, 0xFFFF, 0x0193 }, + { 0x0261, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0262, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0263, CODEPAGE_ISLOWER, 0xFFFF, 0x0194 }, + { 0x0264, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0265, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0266, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0267, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0268, CODEPAGE_ISLOWER, 0xFFFF, 0x0197 }, + { 0x0269, CODEPAGE_ISLOWER, 0xFFFF, 0x0196 }, + { 0x026A, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x026B, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x026C, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x026D, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x026E, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x026F, CODEPAGE_ISLOWER, 0xFFFF, 0x019C }, + { 0x0270, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0271, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0272, CODEPAGE_ISLOWER, 0xFFFF, 0x019D }, + { 0x0273, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0274, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0275, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0276, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0277, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0278, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0279, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x027A, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x027B, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x027C, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x027D, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x027E, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x027F, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0280, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0281, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0282, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0283, CODEPAGE_ISLOWER, 0xFFFF, 0x01A9 }, + { 0x0284, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0285, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0286, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0287, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0288, CODEPAGE_ISLOWER, 0xFFFF, 0x01AE }, + { 0x0289, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x028A, CODEPAGE_ISLOWER, 0xFFFF, 0x01B1 }, + { 0x028B, CODEPAGE_ISLOWER, 0xFFFF, 0x01B2 }, + { 0x028C, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x028D, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x028E, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x028F, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0290, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0291, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0292, CODEPAGE_ISLOWER, 0xFFFF, 0x01B7 }, + { 0x0293, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0294, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0295, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0296, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0297, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0298, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0299, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x029A, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x029B, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x029C, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x029D, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x029E, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x029F, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x02A0, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x02A1, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x02A2, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x02A3, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x02A4, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x02A5, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x02A6, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x02A7, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x02A8, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x02B0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02B1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02B2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02B3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02B4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02B5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02B6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02B7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02B8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02B9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02BA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02BB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02BC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02BD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02BE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02BF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02C0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02C1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02C2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02C3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02C4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02C5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02C6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02C7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02C8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02C9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02CA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02CB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02CC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02CD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02CE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02CF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02D0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02D1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02D2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02D3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02D4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02D5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02D6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02D7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02D8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02D9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02DA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02DB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02DC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02DD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02DE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02E0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02E1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02E2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02E3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02E4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02E5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02E6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02E7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02E8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x02E9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0300, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0301, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0302, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0303, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0304, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0305, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0306, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0307, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0308, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0309, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x030A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x030B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x030C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x030D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x030E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x030F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0310, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0311, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0312, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0313, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0314, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0315, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0316, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0317, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0318, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0319, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x031A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x031B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x031C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x031D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x031E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x031F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0320, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0321, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0322, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0323, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0324, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0325, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0326, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0327, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0328, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0329, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x032A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x032B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x032C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x032D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x032E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x032F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0330, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0331, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0332, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0333, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0334, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0335, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0336, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0337, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0338, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0339, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x033A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x033B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x033C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x033D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x033E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x033F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0340, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0341, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0342, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0343, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0344, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0345, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0360, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0361, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0374, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0375, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x037A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x037E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0384, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0385, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0386, CODEPAGE_ISUPPER, 0x03AC, 0xFFFF }, + { 0x0387, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0388, CODEPAGE_ISUPPER, 0x03AD, 0xFFFF }, + { 0x0389, CODEPAGE_ISUPPER, 0x03AE, 0xFFFF }, + { 0x038A, CODEPAGE_ISUPPER, 0x03AF, 0xFFFF }, + { 0x038C, CODEPAGE_ISUPPER, 0x03CC, 0xFFFF }, + { 0x038E, CODEPAGE_ISUPPER, 0x03CD, 0xFFFF }, + { 0x038F, CODEPAGE_ISUPPER, 0x03CE, 0xFFFF }, + { 0x0390, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0391, CODEPAGE_ISUPPER, 0x03B1, 0xFFFF }, + { 0x0392, CODEPAGE_ISUPPER, 0x03B2, 0xFFFF }, + { 0x0393, CODEPAGE_ISUPPER, 0x03B3, 0xFFFF }, + { 0x0394, CODEPAGE_ISUPPER, 0x03B4, 0xFFFF }, + { 0x0395, CODEPAGE_ISUPPER, 0x03B5, 0xFFFF }, + { 0x0396, CODEPAGE_ISUPPER, 0x03B6, 0xFFFF }, + { 0x0397, CODEPAGE_ISUPPER, 0x03B7, 0xFFFF }, + { 0x0398, CODEPAGE_ISUPPER, 0x03B8, 0xFFFF }, + { 0x0399, CODEPAGE_ISUPPER, 0x03B9, 0xFFFF }, + { 0x039A, CODEPAGE_ISUPPER, 0x03BA, 0xFFFF }, + { 0x039B, CODEPAGE_ISUPPER, 0x03BB, 0xFFFF }, + { 0x039C, CODEPAGE_ISUPPER, 0x03BC, 0xFFFF }, + { 0x039D, CODEPAGE_ISUPPER, 0x03BD, 0xFFFF }, + { 0x039E, CODEPAGE_ISUPPER, 0x03BE, 0xFFFF }, + { 0x039F, CODEPAGE_ISUPPER, 0x03BF, 0xFFFF }, + { 0x03A0, CODEPAGE_ISUPPER, 0x03C0, 0xFFFF }, + { 0x03A1, CODEPAGE_ISUPPER, 0x03C1, 0xFFFF }, + { 0x03A3, CODEPAGE_ISUPPER, 0x03C3, 0xFFFF }, + { 0x03A4, CODEPAGE_ISUPPER, 0x03C4, 0xFFFF }, + { 0x03A5, CODEPAGE_ISUPPER, 0x03C5, 0xFFFF }, + { 0x03A6, CODEPAGE_ISUPPER, 0x03C6, 0xFFFF }, + { 0x03A7, CODEPAGE_ISUPPER, 0x03C7, 0xFFFF }, + { 0x03A8, CODEPAGE_ISUPPER, 0x03C8, 0xFFFF }, + { 0x03A9, CODEPAGE_ISUPPER, 0x03C9, 0xFFFF }, + { 0x03AA, CODEPAGE_ISUPPER, 0x03CA, 0xFFFF }, + { 0x03AB, CODEPAGE_ISUPPER, 0x03CB, 0xFFFF }, + { 0x03AC, CODEPAGE_ISLOWER, 0xFFFF, 0x0386 }, + { 0x03AD, CODEPAGE_ISLOWER, 0xFFFF, 0x0388 }, + { 0x03AE, CODEPAGE_ISLOWER, 0xFFFF, 0x0389 }, + { 0x03AF, CODEPAGE_ISLOWER, 0xFFFF, 0x038A }, + { 0x03B0, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x03B1, CODEPAGE_ISLOWER, 0xFFFF, 0x0391 }, + { 0x03B2, CODEPAGE_ISLOWER, 0xFFFF, 0x0392 }, + { 0x03B3, CODEPAGE_ISLOWER, 0xFFFF, 0x0393 }, + { 0x03B4, CODEPAGE_ISLOWER, 0xFFFF, 0x0394 }, + { 0x03B5, CODEPAGE_ISLOWER, 0xFFFF, 0x0395 }, + { 0x03B6, CODEPAGE_ISLOWER, 0xFFFF, 0x0396 }, + { 0x03B7, CODEPAGE_ISLOWER, 0xFFFF, 0x0397 }, + { 0x03B8, CODEPAGE_ISLOWER, 0xFFFF, 0x0398 }, + { 0x03B9, CODEPAGE_ISLOWER, 0xFFFF, 0x0399 }, + { 0x03BA, CODEPAGE_ISLOWER, 0xFFFF, 0x039A }, + { 0x03BB, CODEPAGE_ISLOWER, 0xFFFF, 0x039B }, + { 0x03BC, CODEPAGE_ISLOWER, 0xFFFF, 0x039C }, + { 0x03BD, CODEPAGE_ISLOWER, 0xFFFF, 0x039D }, + { 0x03BE, CODEPAGE_ISLOWER, 0xFFFF, 0x039E }, + { 0x03BF, CODEPAGE_ISLOWER, 0xFFFF, 0x039F }, + { 0x03C0, CODEPAGE_ISLOWER, 0xFFFF, 0x03A0 }, + { 0x03C1, CODEPAGE_ISLOWER, 0xFFFF, 0x03A1 }, + { 0x03C2, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x03C3, CODEPAGE_ISLOWER, 0xFFFF, 0x03A3 }, + { 0x03C4, CODEPAGE_ISLOWER, 0xFFFF, 0x03A4 }, + { 0x03C5, CODEPAGE_ISLOWER, 0xFFFF, 0x03A5 }, + { 0x03C6, CODEPAGE_ISLOWER, 0xFFFF, 0x03A6 }, + { 0x03C7, CODEPAGE_ISLOWER, 0xFFFF, 0x03A7 }, + { 0x03C8, CODEPAGE_ISLOWER, 0xFFFF, 0x03A8 }, + { 0x03C9, CODEPAGE_ISLOWER, 0xFFFF, 0x03A9 }, + { 0x03CA, CODEPAGE_ISLOWER, 0xFFFF, 0x03AA }, + { 0x03CB, CODEPAGE_ISLOWER, 0xFFFF, 0x03AB }, + { 0x03CC, CODEPAGE_ISLOWER, 0xFFFF, 0x038C }, + { 0x03CD, CODEPAGE_ISLOWER, 0xFFFF, 0x038E }, + { 0x03CE, CODEPAGE_ISLOWER, 0xFFFF, 0x038F }, + { 0x03D0, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x03D1, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x03D2, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x03D3, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x03D4, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x03D5, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x03D6, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x03DA, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x03DC, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x03DE, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x03E0, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x03E2, CODEPAGE_ISUPPER, 0x03E3, 0xFFFF }, + { 0x03E3, CODEPAGE_ISLOWER, 0xFFFF, 0x03E2 }, + { 0x03E4, CODEPAGE_ISUPPER, 0x03E5, 0xFFFF }, + { 0x03E5, CODEPAGE_ISLOWER, 0xFFFF, 0x03E4 }, + { 0x03E6, CODEPAGE_ISUPPER, 0x03E7, 0xFFFF }, + { 0x03E7, CODEPAGE_ISLOWER, 0xFFFF, 0x03E6 }, + { 0x03E8, CODEPAGE_ISUPPER, 0x03E9, 0xFFFF }, + { 0x03E9, CODEPAGE_ISLOWER, 0xFFFF, 0x03E8 }, + { 0x03EA, CODEPAGE_ISUPPER, 0x03EB, 0xFFFF }, + { 0x03EB, CODEPAGE_ISLOWER, 0xFFFF, 0x03EA }, + { 0x03EC, CODEPAGE_ISUPPER, 0x03ED, 0xFFFF }, + { 0x03ED, CODEPAGE_ISLOWER, 0xFFFF, 0x03EC }, + { 0x03EE, CODEPAGE_ISUPPER, 0x03EF, 0xFFFF }, + { 0x03EF, CODEPAGE_ISLOWER, 0xFFFF, 0x03EE }, + { 0x03F0, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x03F1, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x03F2, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x03F3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0401, CODEPAGE_ISUPPER, 0x0451, 0xFFFF }, + { 0x0402, CODEPAGE_ISUPPER, 0x0452, 0xFFFF }, + { 0x0403, CODEPAGE_ISUPPER, 0x0453, 0xFFFF }, + { 0x0404, CODEPAGE_ISUPPER, 0x0454, 0xFFFF }, + { 0x0405, CODEPAGE_ISUPPER, 0x0455, 0xFFFF }, + { 0x0406, CODEPAGE_ISUPPER, 0x0456, 0xFFFF }, + { 0x0407, CODEPAGE_ISUPPER, 0x0457, 0xFFFF }, + { 0x0408, CODEPAGE_ISUPPER, 0x0458, 0xFFFF }, + { 0x0409, CODEPAGE_ISUPPER, 0x0459, 0xFFFF }, + { 0x040A, CODEPAGE_ISUPPER, 0x045A, 0xFFFF }, + { 0x040B, CODEPAGE_ISUPPER, 0x045B, 0xFFFF }, + { 0x040C, CODEPAGE_ISUPPER, 0x045C, 0xFFFF }, + { 0x040E, CODEPAGE_ISUPPER, 0x045E, 0xFFFF }, + { 0x040F, CODEPAGE_ISUPPER, 0x045F, 0xFFFF }, + { 0x0410, CODEPAGE_ISUPPER, 0x0430, 0xFFFF }, + { 0x0411, CODEPAGE_ISUPPER, 0x0431, 0xFFFF }, + { 0x0412, CODEPAGE_ISUPPER, 0x0432, 0xFFFF }, + { 0x0413, CODEPAGE_ISUPPER, 0x0433, 0xFFFF }, + { 0x0414, CODEPAGE_ISUPPER, 0x0434, 0xFFFF }, + { 0x0415, CODEPAGE_ISUPPER, 0x0435, 0xFFFF }, + { 0x0416, CODEPAGE_ISUPPER, 0x0436, 0xFFFF }, + { 0x0417, CODEPAGE_ISUPPER, 0x0437, 0xFFFF }, + { 0x0418, CODEPAGE_ISUPPER, 0x0438, 0xFFFF }, + { 0x0419, CODEPAGE_ISUPPER, 0x0439, 0xFFFF }, + { 0x041A, CODEPAGE_ISUPPER, 0x043A, 0xFFFF }, + { 0x041B, CODEPAGE_ISUPPER, 0x043B, 0xFFFF }, + { 0x041C, CODEPAGE_ISUPPER, 0x043C, 0xFFFF }, + { 0x041D, CODEPAGE_ISUPPER, 0x043D, 0xFFFF }, + { 0x041E, CODEPAGE_ISUPPER, 0x043E, 0xFFFF }, + { 0x041F, CODEPAGE_ISUPPER, 0x043F, 0xFFFF }, + { 0x0420, CODEPAGE_ISUPPER, 0x0440, 0xFFFF }, + { 0x0421, CODEPAGE_ISUPPER, 0x0441, 0xFFFF }, + { 0x0422, CODEPAGE_ISUPPER, 0x0442, 0xFFFF }, + { 0x0423, CODEPAGE_ISUPPER, 0x0443, 0xFFFF }, + { 0x0424, CODEPAGE_ISUPPER, 0x0444, 0xFFFF }, + { 0x0425, CODEPAGE_ISUPPER, 0x0445, 0xFFFF }, + { 0x0426, CODEPAGE_ISUPPER, 0x0446, 0xFFFF }, + { 0x0427, CODEPAGE_ISUPPER, 0x0447, 0xFFFF }, + { 0x0428, CODEPAGE_ISUPPER, 0x0448, 0xFFFF }, + { 0x0429, CODEPAGE_ISUPPER, 0x0449, 0xFFFF }, + { 0x042A, CODEPAGE_ISUPPER, 0x044A, 0xFFFF }, + { 0x042B, CODEPAGE_ISUPPER, 0x044B, 0xFFFF }, + { 0x042C, CODEPAGE_ISUPPER, 0x044C, 0xFFFF }, + { 0x042D, CODEPAGE_ISUPPER, 0x044D, 0xFFFF }, + { 0x042E, CODEPAGE_ISUPPER, 0x044E, 0xFFFF }, + { 0x042F, CODEPAGE_ISUPPER, 0x044F, 0xFFFF }, + { 0x0430, CODEPAGE_ISLOWER, 0xFFFF, 0x0410 }, + { 0x0431, CODEPAGE_ISLOWER, 0xFFFF, 0x0411 }, + { 0x0432, CODEPAGE_ISLOWER, 0xFFFF, 0x0412 }, + { 0x0433, CODEPAGE_ISLOWER, 0xFFFF, 0x0413 }, + { 0x0434, CODEPAGE_ISLOWER, 0xFFFF, 0x0414 }, + { 0x0435, CODEPAGE_ISLOWER, 0xFFFF, 0x0415 }, + { 0x0436, CODEPAGE_ISLOWER, 0xFFFF, 0x0416 }, + { 0x0437, CODEPAGE_ISLOWER, 0xFFFF, 0x0417 }, + { 0x0438, CODEPAGE_ISLOWER, 0xFFFF, 0x0418 }, + { 0x0439, CODEPAGE_ISLOWER, 0xFFFF, 0x0419 }, + { 0x043A, CODEPAGE_ISLOWER, 0xFFFF, 0x041A }, + { 0x043B, CODEPAGE_ISLOWER, 0xFFFF, 0x041B }, + { 0x043C, CODEPAGE_ISLOWER, 0xFFFF, 0x041C }, + { 0x043D, CODEPAGE_ISLOWER, 0xFFFF, 0x041D }, + { 0x043E, CODEPAGE_ISLOWER, 0xFFFF, 0x041E }, + { 0x043F, CODEPAGE_ISLOWER, 0xFFFF, 0x041F }, + { 0x0440, CODEPAGE_ISLOWER, 0xFFFF, 0x0420 }, + { 0x0441, CODEPAGE_ISLOWER, 0xFFFF, 0x0421 }, + { 0x0442, CODEPAGE_ISLOWER, 0xFFFF, 0x0422 }, + { 0x0443, CODEPAGE_ISLOWER, 0xFFFF, 0x0423 }, + { 0x0444, CODEPAGE_ISLOWER, 0xFFFF, 0x0424 }, + { 0x0445, CODEPAGE_ISLOWER, 0xFFFF, 0x0425 }, + { 0x0446, CODEPAGE_ISLOWER, 0xFFFF, 0x0426 }, + { 0x0447, CODEPAGE_ISLOWER, 0xFFFF, 0x0427 }, + { 0x0448, CODEPAGE_ISLOWER, 0xFFFF, 0x0428 }, + { 0x0449, CODEPAGE_ISLOWER, 0xFFFF, 0x0429 }, + { 0x044A, CODEPAGE_ISLOWER, 0xFFFF, 0x042A }, + { 0x044B, CODEPAGE_ISLOWER, 0xFFFF, 0x042B }, + { 0x044C, CODEPAGE_ISLOWER, 0xFFFF, 0x042C }, + { 0x044D, CODEPAGE_ISLOWER, 0xFFFF, 0x042D }, + { 0x044E, CODEPAGE_ISLOWER, 0xFFFF, 0x042E }, + { 0x044F, CODEPAGE_ISLOWER, 0xFFFF, 0x042F }, + { 0x0451, CODEPAGE_ISLOWER, 0xFFFF, 0x0401 }, + { 0x0452, CODEPAGE_ISLOWER, 0xFFFF, 0x0402 }, + { 0x0453, CODEPAGE_ISLOWER, 0xFFFF, 0x0403 }, + { 0x0454, CODEPAGE_ISLOWER, 0xFFFF, 0x0404 }, + { 0x0455, CODEPAGE_ISLOWER, 0xFFFF, 0x0405 }, + { 0x0456, CODEPAGE_ISLOWER, 0xFFFF, 0x0406 }, + { 0x0457, CODEPAGE_ISLOWER, 0xFFFF, 0x0407 }, + { 0x0458, CODEPAGE_ISLOWER, 0xFFFF, 0x0408 }, + { 0x0459, CODEPAGE_ISLOWER, 0xFFFF, 0x0409 }, + { 0x045A, CODEPAGE_ISLOWER, 0xFFFF, 0x040A }, + { 0x045B, CODEPAGE_ISLOWER, 0xFFFF, 0x040B }, + { 0x045C, CODEPAGE_ISLOWER, 0xFFFF, 0x040C }, + { 0x045E, CODEPAGE_ISLOWER, 0xFFFF, 0x040E }, + { 0x045F, CODEPAGE_ISLOWER, 0xFFFF, 0x040F }, + { 0x0460, CODEPAGE_ISUPPER, 0x0461, 0xFFFF }, + { 0x0461, CODEPAGE_ISLOWER, 0xFFFF, 0x0460 }, + { 0x0462, CODEPAGE_ISUPPER, 0x0463, 0xFFFF }, + { 0x0463, CODEPAGE_ISLOWER, 0xFFFF, 0x0462 }, + { 0x0464, CODEPAGE_ISUPPER, 0x0465, 0xFFFF }, + { 0x0465, CODEPAGE_ISLOWER, 0xFFFF, 0x0464 }, + { 0x0466, CODEPAGE_ISUPPER, 0x0467, 0xFFFF }, + { 0x0467, CODEPAGE_ISLOWER, 0xFFFF, 0x0466 }, + { 0x0468, CODEPAGE_ISUPPER, 0x0469, 0xFFFF }, + { 0x0469, CODEPAGE_ISLOWER, 0xFFFF, 0x0468 }, + { 0x046A, CODEPAGE_ISUPPER, 0x046B, 0xFFFF }, + { 0x046B, CODEPAGE_ISLOWER, 0xFFFF, 0x046A }, + { 0x046C, CODEPAGE_ISUPPER, 0x046D, 0xFFFF }, + { 0x046D, CODEPAGE_ISLOWER, 0xFFFF, 0x046C }, + { 0x046E, CODEPAGE_ISUPPER, 0x046F, 0xFFFF }, + { 0x046F, CODEPAGE_ISLOWER, 0xFFFF, 0x046E }, + { 0x0470, CODEPAGE_ISUPPER, 0x0471, 0xFFFF }, + { 0x0471, CODEPAGE_ISLOWER, 0xFFFF, 0x0470 }, + { 0x0472, CODEPAGE_ISUPPER, 0x0473, 0xFFFF }, + { 0x0473, CODEPAGE_ISLOWER, 0xFFFF, 0x0472 }, + { 0x0474, CODEPAGE_ISUPPER, 0x0475, 0xFFFF }, + { 0x0475, CODEPAGE_ISLOWER, 0xFFFF, 0x0474 }, + { 0x0476, CODEPAGE_ISUPPER, 0x0477, 0xFFFF }, + { 0x0477, CODEPAGE_ISLOWER, 0xFFFF, 0x0476 }, + { 0x0478, CODEPAGE_ISUPPER, 0x0479, 0xFFFF }, + { 0x0479, CODEPAGE_ISLOWER, 0xFFFF, 0x0478 }, + { 0x047A, CODEPAGE_ISUPPER, 0x047B, 0xFFFF }, + { 0x047B, CODEPAGE_ISLOWER, 0xFFFF, 0x047A }, + { 0x047C, CODEPAGE_ISUPPER, 0x047D, 0xFFFF }, + { 0x047D, CODEPAGE_ISLOWER, 0xFFFF, 0x047C }, + { 0x047E, CODEPAGE_ISUPPER, 0x047F, 0xFFFF }, + { 0x047F, CODEPAGE_ISLOWER, 0xFFFF, 0x047E }, + { 0x0480, CODEPAGE_ISUPPER, 0x0481, 0xFFFF }, + { 0x0481, CODEPAGE_ISLOWER, 0xFFFF, 0x0480 }, + { 0x0482, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0483, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0484, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0485, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0486, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0490, CODEPAGE_ISUPPER, 0x0491, 0xFFFF }, + { 0x0491, CODEPAGE_ISLOWER, 0xFFFF, 0x0490 }, + { 0x0492, CODEPAGE_ISUPPER, 0x0493, 0xFFFF }, + { 0x0493, CODEPAGE_ISLOWER, 0xFFFF, 0x0492 }, + { 0x0494, CODEPAGE_ISUPPER, 0x0495, 0xFFFF }, + { 0x0495, CODEPAGE_ISLOWER, 0xFFFF, 0x0494 }, + { 0x0496, CODEPAGE_ISUPPER, 0x0497, 0xFFFF }, + { 0x0497, CODEPAGE_ISLOWER, 0xFFFF, 0x0496 }, + { 0x0498, CODEPAGE_ISUPPER, 0x0499, 0xFFFF }, + { 0x0499, CODEPAGE_ISLOWER, 0xFFFF, 0x0498 }, + { 0x049A, CODEPAGE_ISUPPER, 0x049B, 0xFFFF }, + { 0x049B, CODEPAGE_ISLOWER, 0xFFFF, 0x049A }, + { 0x049C, CODEPAGE_ISUPPER, 0x049D, 0xFFFF }, + { 0x049D, CODEPAGE_ISLOWER, 0xFFFF, 0x049C }, + { 0x049E, CODEPAGE_ISUPPER, 0x049F, 0xFFFF }, + { 0x049F, CODEPAGE_ISLOWER, 0xFFFF, 0x049E }, + { 0x04A0, CODEPAGE_ISUPPER, 0x04A1, 0xFFFF }, + { 0x04A1, CODEPAGE_ISLOWER, 0xFFFF, 0x04A0 }, + { 0x04A2, CODEPAGE_ISUPPER, 0x04A3, 0xFFFF }, + { 0x04A3, CODEPAGE_ISLOWER, 0xFFFF, 0x04A2 }, + { 0x04A4, CODEPAGE_ISUPPER, 0x04A5, 0xFFFF }, + { 0x04A5, CODEPAGE_ISLOWER, 0xFFFF, 0x04A4 }, + { 0x04A6, CODEPAGE_ISUPPER, 0x04A7, 0xFFFF }, + { 0x04A7, CODEPAGE_ISLOWER, 0xFFFF, 0x04A6 }, + { 0x04A8, CODEPAGE_ISUPPER, 0x04A9, 0xFFFF }, + { 0x04A9, CODEPAGE_ISLOWER, 0xFFFF, 0x04A8 }, + { 0x04AA, CODEPAGE_ISUPPER, 0x04AB, 0xFFFF }, + { 0x04AB, CODEPAGE_ISLOWER, 0xFFFF, 0x04AA }, + { 0x04AC, CODEPAGE_ISUPPER, 0x04AD, 0xFFFF }, + { 0x04AD, CODEPAGE_ISLOWER, 0xFFFF, 0x04AC }, + { 0x04AE, CODEPAGE_ISUPPER, 0x04AF, 0xFFFF }, + { 0x04AF, CODEPAGE_ISLOWER, 0xFFFF, 0x04AE }, + { 0x04B0, CODEPAGE_ISUPPER, 0x04B1, 0xFFFF }, + { 0x04B1, CODEPAGE_ISLOWER, 0xFFFF, 0x04B0 }, + { 0x04B2, CODEPAGE_ISUPPER, 0x04B3, 0xFFFF }, + { 0x04B3, CODEPAGE_ISLOWER, 0xFFFF, 0x04B2 }, + { 0x04B4, CODEPAGE_ISUPPER, 0x04B5, 0xFFFF }, + { 0x04B5, CODEPAGE_ISLOWER, 0xFFFF, 0x04B4 }, + { 0x04B6, CODEPAGE_ISUPPER, 0x04B7, 0xFFFF }, + { 0x04B7, CODEPAGE_ISLOWER, 0xFFFF, 0x04B6 }, + { 0x04B8, CODEPAGE_ISUPPER, 0x04B9, 0xFFFF }, + { 0x04B9, CODEPAGE_ISLOWER, 0xFFFF, 0x04B8 }, + { 0x04BA, CODEPAGE_ISUPPER, 0x04BB, 0xFFFF }, + { 0x04BB, CODEPAGE_ISLOWER, 0xFFFF, 0x04BA }, + { 0x04BC, CODEPAGE_ISUPPER, 0x04BD, 0xFFFF }, + { 0x04BD, CODEPAGE_ISLOWER, 0xFFFF, 0x04BC }, + { 0x04BE, CODEPAGE_ISUPPER, 0x04BF, 0xFFFF }, + { 0x04BF, CODEPAGE_ISLOWER, 0xFFFF, 0x04BE }, + { 0x04C0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x04C1, CODEPAGE_ISUPPER, 0x04C2, 0xFFFF }, + { 0x04C2, CODEPAGE_ISLOWER, 0xFFFF, 0x04C1 }, + { 0x04C3, CODEPAGE_ISUPPER, 0x04C4, 0xFFFF }, + { 0x04C4, CODEPAGE_ISLOWER, 0xFFFF, 0x04C3 }, + { 0x04C7, CODEPAGE_ISUPPER, 0x04C8, 0xFFFF }, + { 0x04C8, CODEPAGE_ISLOWER, 0xFFFF, 0x04C7 }, + { 0x04CB, CODEPAGE_ISUPPER, 0x04CC, 0xFFFF }, + { 0x04CC, CODEPAGE_ISLOWER, 0xFFFF, 0x04CB }, + { 0x04D0, CODEPAGE_ISUPPER, 0x04D1, 0xFFFF }, + { 0x04D1, CODEPAGE_ISLOWER, 0xFFFF, 0x04D0 }, + { 0x04D2, CODEPAGE_ISUPPER, 0x04D3, 0xFFFF }, + { 0x04D3, CODEPAGE_ISLOWER, 0xFFFF, 0x04D2 }, + { 0x04D4, CODEPAGE_ISUPPER, 0x04D5, 0xFFFF }, + { 0x04D5, CODEPAGE_ISLOWER, 0xFFFF, 0x04D4 }, + { 0x04D6, CODEPAGE_ISUPPER, 0x04D7, 0xFFFF }, + { 0x04D7, CODEPAGE_ISLOWER, 0xFFFF, 0x04D6 }, + { 0x04D8, CODEPAGE_ISUPPER, 0x04D9, 0xFFFF }, + { 0x04D9, CODEPAGE_ISLOWER, 0xFFFF, 0x04D8 }, + { 0x04DA, CODEPAGE_ISUPPER, 0x04DB, 0xFFFF }, + { 0x04DB, CODEPAGE_ISLOWER, 0xFFFF, 0x04DA }, + { 0x04DC, CODEPAGE_ISUPPER, 0x04DD, 0xFFFF }, + { 0x04DD, CODEPAGE_ISLOWER, 0xFFFF, 0x04DC }, + { 0x04DE, CODEPAGE_ISUPPER, 0x04DF, 0xFFFF }, + { 0x04DF, CODEPAGE_ISLOWER, 0xFFFF, 0x04DE }, + { 0x04E0, CODEPAGE_ISUPPER, 0x04E1, 0xFFFF }, + { 0x04E1, CODEPAGE_ISLOWER, 0xFFFF, 0x04E0 }, + { 0x04E2, CODEPAGE_ISUPPER, 0x04E3, 0xFFFF }, + { 0x04E3, CODEPAGE_ISLOWER, 0xFFFF, 0x04E2 }, + { 0x04E4, CODEPAGE_ISUPPER, 0x04E5, 0xFFFF }, + { 0x04E5, CODEPAGE_ISLOWER, 0xFFFF, 0x04E4 }, + { 0x04E6, CODEPAGE_ISUPPER, 0x04E7, 0xFFFF }, + { 0x04E7, CODEPAGE_ISLOWER, 0xFFFF, 0x04E6 }, + { 0x04E8, CODEPAGE_ISUPPER, 0x04E9, 0xFFFF }, + { 0x04E9, CODEPAGE_ISLOWER, 0xFFFF, 0x04E8 }, + { 0x04EA, CODEPAGE_ISUPPER, 0x04EB, 0xFFFF }, + { 0x04EB, CODEPAGE_ISLOWER, 0xFFFF, 0x04EA }, + { 0x04EE, CODEPAGE_ISUPPER, 0x04EF, 0xFFFF }, + { 0x04EF, CODEPAGE_ISLOWER, 0xFFFF, 0x04EE }, + { 0x04F0, CODEPAGE_ISUPPER, 0x04F1, 0xFFFF }, + { 0x04F1, CODEPAGE_ISLOWER, 0xFFFF, 0x04F0 }, + { 0x04F2, CODEPAGE_ISUPPER, 0x04F3, 0xFFFF }, + { 0x04F3, CODEPAGE_ISLOWER, 0xFFFF, 0x04F2 }, + { 0x04F4, CODEPAGE_ISUPPER, 0x04F5, 0xFFFF }, + { 0x04F5, CODEPAGE_ISLOWER, 0xFFFF, 0x04F4 }, + { 0x04F8, CODEPAGE_ISUPPER, 0x04F9, 0xFFFF }, + { 0x04F9, CODEPAGE_ISLOWER, 0xFFFF, 0x04F8 }, + { 0x0531, CODEPAGE_ISUPPER, 0x0561, 0xFFFF }, + { 0x0532, CODEPAGE_ISUPPER, 0x0562, 0xFFFF }, + { 0x0533, CODEPAGE_ISUPPER, 0x0563, 0xFFFF }, + { 0x0534, CODEPAGE_ISUPPER, 0x0564, 0xFFFF }, + { 0x0535, CODEPAGE_ISUPPER, 0x0565, 0xFFFF }, + { 0x0536, CODEPAGE_ISUPPER, 0x0566, 0xFFFF }, + { 0x0537, CODEPAGE_ISUPPER, 0x0567, 0xFFFF }, + { 0x0538, CODEPAGE_ISUPPER, 0x0568, 0xFFFF }, + { 0x0539, CODEPAGE_ISUPPER, 0x0569, 0xFFFF }, + { 0x053A, CODEPAGE_ISUPPER, 0x056A, 0xFFFF }, + { 0x053B, CODEPAGE_ISUPPER, 0x056B, 0xFFFF }, + { 0x053C, CODEPAGE_ISUPPER, 0x056C, 0xFFFF }, + { 0x053D, CODEPAGE_ISUPPER, 0x056D, 0xFFFF }, + { 0x053E, CODEPAGE_ISUPPER, 0x056E, 0xFFFF }, + { 0x053F, CODEPAGE_ISUPPER, 0x056F, 0xFFFF }, + { 0x0540, CODEPAGE_ISUPPER, 0x0570, 0xFFFF }, + { 0x0541, CODEPAGE_ISUPPER, 0x0571, 0xFFFF }, + { 0x0542, CODEPAGE_ISUPPER, 0x0572, 0xFFFF }, + { 0x0543, CODEPAGE_ISUPPER, 0x0573, 0xFFFF }, + { 0x0544, CODEPAGE_ISUPPER, 0x0574, 0xFFFF }, + { 0x0545, CODEPAGE_ISUPPER, 0x0575, 0xFFFF }, + { 0x0546, CODEPAGE_ISUPPER, 0x0576, 0xFFFF }, + { 0x0547, CODEPAGE_ISUPPER, 0x0577, 0xFFFF }, + { 0x0548, CODEPAGE_ISUPPER, 0x0578, 0xFFFF }, + { 0x0549, CODEPAGE_ISUPPER, 0x0579, 0xFFFF }, + { 0x054A, CODEPAGE_ISUPPER, 0x057A, 0xFFFF }, + { 0x054B, CODEPAGE_ISUPPER, 0x057B, 0xFFFF }, + { 0x054C, CODEPAGE_ISUPPER, 0x057C, 0xFFFF }, + { 0x054D, CODEPAGE_ISUPPER, 0x057D, 0xFFFF }, + { 0x054E, CODEPAGE_ISUPPER, 0x057E, 0xFFFF }, + { 0x054F, CODEPAGE_ISUPPER, 0x057F, 0xFFFF }, + { 0x0550, CODEPAGE_ISUPPER, 0x0580, 0xFFFF }, + { 0x0551, CODEPAGE_ISUPPER, 0x0581, 0xFFFF }, + { 0x0552, CODEPAGE_ISUPPER, 0x0582, 0xFFFF }, + { 0x0553, CODEPAGE_ISUPPER, 0x0583, 0xFFFF }, + { 0x0554, CODEPAGE_ISUPPER, 0x0584, 0xFFFF }, + { 0x0555, CODEPAGE_ISUPPER, 0x0585, 0xFFFF }, + { 0x0556, CODEPAGE_ISUPPER, 0x0586, 0xFFFF }, + { 0x0559, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x055A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x055B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x055C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x055D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x055E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x055F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0561, CODEPAGE_ISLOWER, 0xFFFF, 0x0531 }, + { 0x0562, CODEPAGE_ISLOWER, 0xFFFF, 0x0532 }, + { 0x0563, CODEPAGE_ISLOWER, 0xFFFF, 0x0533 }, + { 0x0564, CODEPAGE_ISLOWER, 0xFFFF, 0x0534 }, + { 0x0565, CODEPAGE_ISLOWER, 0xFFFF, 0x0535 }, + { 0x0566, CODEPAGE_ISLOWER, 0xFFFF, 0x0536 }, + { 0x0567, CODEPAGE_ISLOWER, 0xFFFF, 0x0537 }, + { 0x0568, CODEPAGE_ISLOWER, 0xFFFF, 0x0538 }, + { 0x0569, CODEPAGE_ISLOWER, 0xFFFF, 0x0539 }, + { 0x056A, CODEPAGE_ISLOWER, 0xFFFF, 0x053A }, + { 0x056B, CODEPAGE_ISLOWER, 0xFFFF, 0x053B }, + { 0x056C, CODEPAGE_ISLOWER, 0xFFFF, 0x053C }, + { 0x056D, CODEPAGE_ISLOWER, 0xFFFF, 0x053D }, + { 0x056E, CODEPAGE_ISLOWER, 0xFFFF, 0x053E }, + { 0x056F, CODEPAGE_ISLOWER, 0xFFFF, 0x053F }, + { 0x0570, CODEPAGE_ISLOWER, 0xFFFF, 0x0540 }, + { 0x0571, CODEPAGE_ISLOWER, 0xFFFF, 0x0541 }, + { 0x0572, CODEPAGE_ISLOWER, 0xFFFF, 0x0542 }, + { 0x0573, CODEPAGE_ISLOWER, 0xFFFF, 0x0543 }, + { 0x0574, CODEPAGE_ISLOWER, 0xFFFF, 0x0544 }, + { 0x0575, CODEPAGE_ISLOWER, 0xFFFF, 0x0545 }, + { 0x0576, CODEPAGE_ISLOWER, 0xFFFF, 0x0546 }, + { 0x0577, CODEPAGE_ISLOWER, 0xFFFF, 0x0547 }, + { 0x0578, CODEPAGE_ISLOWER, 0xFFFF, 0x0548 }, + { 0x0579, CODEPAGE_ISLOWER, 0xFFFF, 0x0549 }, + { 0x057A, CODEPAGE_ISLOWER, 0xFFFF, 0x054A }, + { 0x057B, CODEPAGE_ISLOWER, 0xFFFF, 0x054B }, + { 0x057C, CODEPAGE_ISLOWER, 0xFFFF, 0x054C }, + { 0x057D, CODEPAGE_ISLOWER, 0xFFFF, 0x054D }, + { 0x057E, CODEPAGE_ISLOWER, 0xFFFF, 0x054E }, + { 0x057F, CODEPAGE_ISLOWER, 0xFFFF, 0x054F }, + { 0x0580, CODEPAGE_ISLOWER, 0xFFFF, 0x0550 }, + { 0x0581, CODEPAGE_ISLOWER, 0xFFFF, 0x0551 }, + { 0x0582, CODEPAGE_ISLOWER, 0xFFFF, 0x0552 }, + { 0x0583, CODEPAGE_ISLOWER, 0xFFFF, 0x0553 }, + { 0x0584, CODEPAGE_ISLOWER, 0xFFFF, 0x0554 }, + { 0x0585, CODEPAGE_ISLOWER, 0xFFFF, 0x0555 }, + { 0x0586, CODEPAGE_ISLOWER, 0xFFFF, 0x0556 }, + { 0x0587, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x0589, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0591, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0592, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0593, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0594, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0595, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0596, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0597, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0598, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0599, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x059A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x059B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x059C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x059D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x059E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x059F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05A0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05A1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05A3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05A4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05A5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05A6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05A7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05A8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05A9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05AA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05AB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05AC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05AD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05AE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05AF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05B0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05B1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05B2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05B3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05B4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05B5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05B6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05B7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05B8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05B9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05BB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05BC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05BD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05BE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05BF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05C0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05C1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05C2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05C3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05C4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05D0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05D1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05D2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05D3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05D4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05D5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05D6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05D7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05D8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05D9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05DA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05DB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05DC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05DD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05DE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05DF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05E0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05E1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05E2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05E3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05E4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05E5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05E6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05E7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05E8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05E9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05EA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05F0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05F1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05F2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05F3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x05F4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x060C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x061B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x061F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0621, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0622, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0623, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0624, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0625, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0626, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0627, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0628, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0629, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x062A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x062B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x062C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x062D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x062E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x062F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0630, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0631, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0632, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0633, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0634, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0635, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0636, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0637, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0638, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0639, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x063A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0640, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0641, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0642, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0643, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0644, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0645, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0646, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0647, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0648, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0649, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x064A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x064B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x064C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x064D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x064E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x064F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0650, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0651, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0652, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0660, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0661, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0662, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0663, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0664, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0665, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0666, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0667, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0668, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0669, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x066A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x066B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x066C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x066D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0670, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0671, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0672, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0673, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0674, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0675, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0676, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0677, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0678, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0679, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x067A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x067B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x067C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x067D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x067E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x067F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0680, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0681, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0682, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0683, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0684, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0685, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0686, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0687, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0688, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0689, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x068A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x068B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x068C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x068D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x068E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x068F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0690, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0691, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0692, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0693, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0694, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0695, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0696, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0697, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0698, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0699, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x069A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x069B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x069C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x069D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x069E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x069F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06A0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06A1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06A2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06A3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06A4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06A5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06A6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06A7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06A8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06A9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06AA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06AB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06AC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06AD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06AE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06AF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06B0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06B1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06B2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06B3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06B4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06B5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06B6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06B7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06BA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06BB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06BC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06BD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06BE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06C0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06C1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06C2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06C3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06C4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06C5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06C6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06C7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06C8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06C9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06CA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06CB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06CC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06CD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06CE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06D0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06D1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06D2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06D3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06D4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06D5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06D6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06D7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06D8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06D9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06DA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06DB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06DC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06DD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06DE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06DF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06E0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06E1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06E2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06E3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06E4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06E5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06E6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06E7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06E8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06E9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06EA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06EB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06EC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06ED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06F0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06F1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06F2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06F3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06F4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06F5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06F6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06F7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06F8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x06F9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0901, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0902, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0903, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0905, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0906, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0907, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0908, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0909, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x090A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x090B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x090C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x090D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x090E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x090F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0910, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0911, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0912, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0913, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0914, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0915, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0916, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0917, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0918, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0919, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x091A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x091B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x091C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x091D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x091E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x091F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0920, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0921, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0922, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0923, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0924, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0925, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0926, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0927, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0928, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0929, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x092A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x092B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x092C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x092D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x092E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x092F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0930, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0931, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0932, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0933, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0934, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0935, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0936, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0937, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0938, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0939, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x093C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x093D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x093E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x093F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0940, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0941, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0942, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0943, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0944, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0945, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0946, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0947, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0948, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0949, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x094A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x094B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x094C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x094D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0950, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0951, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0952, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0953, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0954, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0958, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0959, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x095A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x095B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x095C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x095D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x095E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x095F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0960, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0961, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0962, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0963, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0964, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0965, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0966, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0967, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0968, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0969, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x096A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x096B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x096C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x096D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x096E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x096F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0970, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0981, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0982, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0983, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0985, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0986, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0987, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0988, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0989, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x098A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x098B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x098C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x098F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0990, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0993, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0994, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0995, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0996, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0997, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0998, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0999, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x099A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x099B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x099C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x099D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x099E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x099F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09A0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09A1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09A2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09A3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09A4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09A5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09A6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09A7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09A8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09AA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09AB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09AC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09AD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09AE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09AF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09B0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09B2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09B6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09B7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09B8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09B9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09BC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09BE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09BF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09C0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09C1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09C2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09C3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09C4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09C7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09C8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09CB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09CC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09CD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09D7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09DC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09DD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09DF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09E0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09E1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09E2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09E3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09E6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09E7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09E8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09E9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09EA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09EB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09EC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09ED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09EE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09EF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09F0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09F1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09F2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09F3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09F4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09F5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09F6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09F7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09F8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09F9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x09FA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A02, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A05, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A06, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A07, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A08, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A09, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A0A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A0F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A10, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A13, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A14, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A15, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A16, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A17, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A18, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A19, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A1A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A1B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A1C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A1D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A1E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A1F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A20, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A21, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A22, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A23, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A24, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A25, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A26, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A27, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A28, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A2A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A2B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A2C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A2D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A2E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A2F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A30, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A32, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A33, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A35, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A36, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A38, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A39, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A3C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A3E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A3F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A40, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A41, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A42, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A47, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A48, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A4B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A4C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A4D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A59, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A5A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A5B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A5C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A5E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A66, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A67, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A68, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A69, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A6A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A6B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A6C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A6D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A6E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A6F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A70, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A71, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A72, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A73, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A74, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A81, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A82, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A83, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A85, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A86, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A87, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A88, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A89, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A8A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A8B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A8D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A8F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A90, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A91, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A93, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A94, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A95, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A96, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A97, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A98, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A99, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A9A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A9B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A9C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A9D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A9E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0A9F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AA0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AA1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AA2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AA3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AA4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AA5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AA6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AA7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AA8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AAA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AAB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AAC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AAD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AAE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AAF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AB0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AB2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AB3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AB5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AB6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AB7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AB8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AB9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ABC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ABD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ABE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ABF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AC0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AC1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AC2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AC3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AC4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AC5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AC7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AC8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AC9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ACB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ACC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ACD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AD0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AE0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AE6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AE7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AE8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AE9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AEA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AEB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AEC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AEE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0AEF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B01, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B02, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B03, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B05, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B06, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B07, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B08, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B09, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B0A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B0B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B0C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B0F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B10, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B13, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B14, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B15, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B16, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B17, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B18, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B19, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B1A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B1B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B1C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B1D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B1E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B1F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B20, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B21, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B22, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B23, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B24, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B25, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B26, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B27, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B28, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B2A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B2B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B2C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B2D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B2E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B2F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B30, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B32, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B33, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B36, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B37, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B38, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B39, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B3C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B3D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B3E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B3F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B40, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B41, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B42, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B43, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B47, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B48, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B4B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B4C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B4D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B56, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B57, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B5C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B5D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B5F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B60, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B61, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B66, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B67, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B68, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B69, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B6A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B6B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B6C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B6D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B6E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B6F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B70, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B82, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B83, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B85, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B86, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B87, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B88, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B89, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B8A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B8E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B8F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B90, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B92, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B93, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B94, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B95, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B99, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B9A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B9C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B9E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0B9F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BA3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BA4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BA8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BA9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BAA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BAE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BAF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BB0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BB1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BB2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BB3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BB4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BB5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BB7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BB8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BB9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BBE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BBF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BC0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BC1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BC2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BC6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BC7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BC8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BCA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BCB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BCC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BCD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BD7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BE7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BE8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BE9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BEA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BEB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BEC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BEE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BEF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BF0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BF1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0BF2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C01, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C02, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C03, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C05, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C06, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C07, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C08, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C09, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C0A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C0B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C0C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C0E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C0F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C10, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C12, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C13, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C14, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C15, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C16, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C17, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C18, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C19, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C1A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C1B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C1C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C1D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C1E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C1F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C20, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C21, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C22, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C23, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C24, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C25, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C26, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C27, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C28, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C2A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C2B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C2C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C2D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C2E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C2F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C30, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C31, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C32, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C33, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C35, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C36, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C37, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C38, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C39, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C3E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C3F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C40, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C41, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C42, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C43, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C44, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C46, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C47, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C48, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C4A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C4B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C4C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C4D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C55, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C56, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C60, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C61, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C66, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C67, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C68, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C69, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C6A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C6B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C6C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C6D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C6E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C6F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C82, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C83, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C85, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C86, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C87, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C88, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C89, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C8A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C8B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C8C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C8E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C8F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C90, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C92, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C93, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C94, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C95, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C96, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C97, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C98, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C99, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C9A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C9B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C9C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C9D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C9E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0C9F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CA0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CA1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CA2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CA3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CA4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CA5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CA6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CA7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CA8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CAA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CAB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CAC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CAD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CAE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CAF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CB0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CB1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CB2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CB3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CB5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CB6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CB7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CB8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CB9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CBE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CBF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CC0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CC1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CC2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CC3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CC4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CC6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CC7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CC8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CCA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CCB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CCC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CCD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CD5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CD6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CDE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CE0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CE1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CE6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CE7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CE8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CE9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CEA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CEB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CEC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CEE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0CEF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D02, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D03, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D05, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D06, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D07, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D08, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D09, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D0A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D0B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D0C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D0E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D0F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D10, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D12, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D13, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D14, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D15, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D16, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D17, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D18, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D19, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D1A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D1B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D1C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D1D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D1E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D1F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D20, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D21, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D22, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D23, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D24, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D25, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D26, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D27, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D28, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D2A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D2B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D2C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D2D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D2E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D2F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D30, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D31, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D32, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D33, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D34, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D35, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D36, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D37, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D38, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D39, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D3E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D3F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D40, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D41, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D42, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D43, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D46, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D47, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D48, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D4A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D4B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D4C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D4D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D57, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D60, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D61, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D66, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D67, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D68, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D69, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D6A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D6B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D6C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D6D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D6E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0D6F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E01, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E02, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E03, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E04, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E05, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E06, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E07, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E08, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E09, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E0A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E0B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E0C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E0D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E0E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E0F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E10, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E11, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E12, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E13, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E14, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E15, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E16, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E17, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E18, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E19, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E1A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E1B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E1C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E1D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E1E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E1F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E20, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E21, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E22, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E23, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E24, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E25, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E26, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E27, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E28, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E29, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E2A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E2B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E2C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E2D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E2E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E2F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E30, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E31, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E32, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E33, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E34, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E35, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E36, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E37, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E38, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E39, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E3A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E3F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E40, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E41, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E42, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E43, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E44, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E45, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E46, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E47, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E48, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E49, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E4A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E4B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E4C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E4D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E4E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E4F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E50, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E51, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E52, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E53, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E54, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E55, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E56, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E57, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E58, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E59, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E5A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E5B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E81, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E82, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E84, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E87, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E88, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E8A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E8D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E94, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E95, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E96, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E97, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E99, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E9A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E9B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E9C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E9D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E9E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0E9F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EA1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EA2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EA3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EA5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EA7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EAA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EAB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EAD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EAE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EAF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EB0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EB1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EB2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EB3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EB4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EB5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EB6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EB7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EB8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EB9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EBB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EBC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EBD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EC0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EC1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EC2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EC3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EC4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EC6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EC8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EC9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ECA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ECB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ECC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ECD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ED0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ED1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ED2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ED3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ED4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ED5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ED6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ED7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ED8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0ED9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EDC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0EDD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F00, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F01, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F02, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F03, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F04, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F05, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F06, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F07, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F08, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F09, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F0A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F0B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F0C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F0D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F0E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F0F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F10, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F11, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F12, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F13, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F14, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F15, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F16, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F17, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F18, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F19, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F1A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F1B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F1C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F1D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F1E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F1F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F20, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F21, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F22, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F23, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F24, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F25, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F26, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F27, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F28, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F29, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F2A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F2B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F2C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F2D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F2E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F2F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F30, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F31, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F32, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F33, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F34, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F35, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F36, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F37, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F38, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F39, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F3A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F3B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F3C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F3D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F3E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F3F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F40, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F41, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F42, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F43, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F44, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F45, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F46, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F47, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F49, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F4A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F4B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F4C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F4D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F4E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F4F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F50, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F51, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F52, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F53, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F54, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F55, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F56, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F57, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F58, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F59, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F5A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F5B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F5C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F5D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F5E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F5F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F60, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F61, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F62, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F63, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F64, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F65, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F66, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F67, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F68, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F69, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F71, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F72, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F73, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F74, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F75, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F76, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F77, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F78, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F79, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F7A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F7B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F7C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F7D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F7E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F7F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F80, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F81, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F82, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F83, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F84, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F85, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F86, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F87, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F88, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F89, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F8A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F8B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F90, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F91, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F92, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F93, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F94, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F95, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F97, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F99, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F9A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F9B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F9C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F9D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F9E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0F9F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FA0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FA1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FA2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FA3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FA4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FA5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FA6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FA7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FA8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FA9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FAA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FAB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FAC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FAD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FB1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FB2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FB3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FB4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FB5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FB6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FB7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x0FB9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x10A0, CODEPAGE_ISUPPER, 0x10D0, 0xFFFF }, + { 0x10A1, CODEPAGE_ISUPPER, 0x10D1, 0xFFFF }, + { 0x10A2, CODEPAGE_ISUPPER, 0x10D2, 0xFFFF }, + { 0x10A3, CODEPAGE_ISUPPER, 0x10D3, 0xFFFF }, + { 0x10A4, CODEPAGE_ISUPPER, 0x10D4, 0xFFFF }, + { 0x10A5, CODEPAGE_ISUPPER, 0x10D5, 0xFFFF }, + { 0x10A6, CODEPAGE_ISUPPER, 0x10D6, 0xFFFF }, + { 0x10A7, CODEPAGE_ISUPPER, 0x10D7, 0xFFFF }, + { 0x10A8, CODEPAGE_ISUPPER, 0x10D8, 0xFFFF }, + { 0x10A9, CODEPAGE_ISUPPER, 0x10D9, 0xFFFF }, + { 0x10AA, CODEPAGE_ISUPPER, 0x10DA, 0xFFFF }, + { 0x10AB, CODEPAGE_ISUPPER, 0x10DB, 0xFFFF }, + { 0x10AC, CODEPAGE_ISUPPER, 0x10DC, 0xFFFF }, + { 0x10AD, CODEPAGE_ISUPPER, 0x10DD, 0xFFFF }, + { 0x10AE, CODEPAGE_ISUPPER, 0x10DE, 0xFFFF }, + { 0x10AF, CODEPAGE_ISUPPER, 0x10DF, 0xFFFF }, + { 0x10B0, CODEPAGE_ISUPPER, 0x10E0, 0xFFFF }, + { 0x10B1, CODEPAGE_ISUPPER, 0x10E1, 0xFFFF }, + { 0x10B2, CODEPAGE_ISUPPER, 0x10E2, 0xFFFF }, + { 0x10B3, CODEPAGE_ISUPPER, 0x10E3, 0xFFFF }, + { 0x10B4, CODEPAGE_ISUPPER, 0x10E4, 0xFFFF }, + { 0x10B5, CODEPAGE_ISUPPER, 0x10E5, 0xFFFF }, + { 0x10B6, CODEPAGE_ISUPPER, 0x10E6, 0xFFFF }, + { 0x10B7, CODEPAGE_ISUPPER, 0x10E7, 0xFFFF }, + { 0x10B8, CODEPAGE_ISUPPER, 0x10E8, 0xFFFF }, + { 0x10B9, CODEPAGE_ISUPPER, 0x10E9, 0xFFFF }, + { 0x10BA, CODEPAGE_ISUPPER, 0x10EA, 0xFFFF }, + { 0x10BB, CODEPAGE_ISUPPER, 0x10EB, 0xFFFF }, + { 0x10BC, CODEPAGE_ISUPPER, 0x10EC, 0xFFFF }, + { 0x10BD, CODEPAGE_ISUPPER, 0x10ED, 0xFFFF }, + { 0x10BE, CODEPAGE_ISUPPER, 0x10EE, 0xFFFF }, + { 0x10BF, CODEPAGE_ISUPPER, 0x10EF, 0xFFFF }, + { 0x10C0, CODEPAGE_ISUPPER, 0x10F0, 0xFFFF }, + { 0x10C1, CODEPAGE_ISUPPER, 0x10F1, 0xFFFF }, + { 0x10C2, CODEPAGE_ISUPPER, 0x10F2, 0xFFFF }, + { 0x10C3, CODEPAGE_ISUPPER, 0x10F3, 0xFFFF }, + { 0x10C4, CODEPAGE_ISUPPER, 0x10F4, 0xFFFF }, + { 0x10C5, CODEPAGE_ISUPPER, 0x10F5, 0xFFFF }, + { 0x10D0, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10D1, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10D2, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10D3, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10D4, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10D5, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10D6, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10D7, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10D8, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10D9, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10DA, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10DB, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10DC, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10DD, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10DE, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10DF, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10E0, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10E1, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10E2, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10E3, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10E4, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10E5, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10E6, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10E7, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10E8, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10E9, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10EA, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10EB, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10EC, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10ED, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10EE, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10EF, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10F0, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10F1, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10F2, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10F3, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10F4, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10F5, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10F6, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x10FB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1100, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1101, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1102, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1103, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1104, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1105, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1106, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1107, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1108, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1109, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x110A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x110B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x110C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x110D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x110E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x110F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1110, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1111, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1112, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1113, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1114, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1115, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1116, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1117, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1118, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1119, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x111A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x111B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x111C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x111D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x111E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x111F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1120, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1121, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1122, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1123, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1124, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1125, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1126, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1127, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1128, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1129, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x112A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x112B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x112C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x112D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x112E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x112F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1130, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1131, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1132, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1133, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1134, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1135, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1136, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1137, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1138, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1139, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x113A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x113B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x113C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x113D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x113E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x113F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1140, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1141, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1142, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1143, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1144, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1145, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1146, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1147, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1148, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1149, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x114A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x114B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x114C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x114D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x114E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x114F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1150, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1151, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1152, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1153, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1154, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1155, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1156, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1157, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1158, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1159, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x115F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1160, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1161, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1162, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1163, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1164, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1165, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1166, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1167, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1168, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1169, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x116A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x116B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x116C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x116D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x116E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x116F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1170, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1171, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1172, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1173, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1174, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1175, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1176, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1177, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1178, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1179, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x117A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x117B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x117C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x117D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x117E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x117F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1180, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1181, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1182, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1183, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1184, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1185, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1186, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1187, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1188, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1189, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x118A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x118B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x118C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x118D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x118E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x118F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1190, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1191, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1192, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1193, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1194, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1195, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1196, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1197, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1198, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1199, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x119A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x119B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x119C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x119D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x119E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x119F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11A0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11A1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11A2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11A8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11A9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11AA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11AB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11AC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11AD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11AE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11AF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11B0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11B1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11B2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11B3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11B4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11B5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11B6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11B7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11B8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11B9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11BA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11BB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11BC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11BD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11BE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11BF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11C0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11C1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11C2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11C3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11C4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11C5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11C6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11C7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11C8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11C9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11CA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11CB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11CC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11CD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11CE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11CF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11D0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11D1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11D2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11D3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11D4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11D5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11D6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11D7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11D8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11D9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11DA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11DB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11DC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11DD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11DE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11DF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11E0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11E1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11E2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11E3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11E4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11E5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11E6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11E7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11E8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11E9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11EA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11EB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11EC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11ED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11EE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11EF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11F0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11F1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11F2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11F3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11F4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11F5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11F6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11F7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11F8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x11F9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1E00, CODEPAGE_ISUPPER, 0x1E01, 0xFFFF }, + { 0x1E01, CODEPAGE_ISLOWER, 0xFFFF, 0x1E00 }, + { 0x1E02, CODEPAGE_ISUPPER, 0x1E03, 0xFFFF }, + { 0x1E03, CODEPAGE_ISLOWER, 0xFFFF, 0x1E02 }, + { 0x1E04, CODEPAGE_ISUPPER, 0x1E05, 0xFFFF }, + { 0x1E05, CODEPAGE_ISLOWER, 0xFFFF, 0x1E04 }, + { 0x1E06, CODEPAGE_ISUPPER, 0x1E07, 0xFFFF }, + { 0x1E07, CODEPAGE_ISLOWER, 0xFFFF, 0x1E06 }, + { 0x1E08, CODEPAGE_ISUPPER, 0x1E09, 0xFFFF }, + { 0x1E09, CODEPAGE_ISLOWER, 0xFFFF, 0x1E08 }, + { 0x1E0A, CODEPAGE_ISUPPER, 0x1E0B, 0xFFFF }, + { 0x1E0B, CODEPAGE_ISLOWER, 0xFFFF, 0x1E0A }, + { 0x1E0C, CODEPAGE_ISUPPER, 0x1E0D, 0xFFFF }, + { 0x1E0D, CODEPAGE_ISLOWER, 0xFFFF, 0x1E0C }, + { 0x1E0E, CODEPAGE_ISUPPER, 0x1E0F, 0xFFFF }, + { 0x1E0F, CODEPAGE_ISLOWER, 0xFFFF, 0x1E0E }, + { 0x1E10, CODEPAGE_ISUPPER, 0x1E11, 0xFFFF }, + { 0x1E11, CODEPAGE_ISLOWER, 0xFFFF, 0x1E10 }, + { 0x1E12, CODEPAGE_ISUPPER, 0x1E13, 0xFFFF }, + { 0x1E13, CODEPAGE_ISLOWER, 0xFFFF, 0x1E12 }, + { 0x1E14, CODEPAGE_ISUPPER, 0x1E15, 0xFFFF }, + { 0x1E15, CODEPAGE_ISLOWER, 0xFFFF, 0x1E14 }, + { 0x1E16, CODEPAGE_ISUPPER, 0x1E17, 0xFFFF }, + { 0x1E17, CODEPAGE_ISLOWER, 0xFFFF, 0x1E16 }, + { 0x1E18, CODEPAGE_ISUPPER, 0x1E19, 0xFFFF }, + { 0x1E19, CODEPAGE_ISLOWER, 0xFFFF, 0x1E18 }, + { 0x1E1A, CODEPAGE_ISUPPER, 0x1E1B, 0xFFFF }, + { 0x1E1B, CODEPAGE_ISLOWER, 0xFFFF, 0x1E1A }, + { 0x1E1C, CODEPAGE_ISUPPER, 0x1E1D, 0xFFFF }, + { 0x1E1D, CODEPAGE_ISLOWER, 0xFFFF, 0x1E1C }, + { 0x1E1E, CODEPAGE_ISUPPER, 0x1E1F, 0xFFFF }, + { 0x1E1F, CODEPAGE_ISLOWER, 0xFFFF, 0x1E1E }, + { 0x1E20, CODEPAGE_ISUPPER, 0x1E21, 0xFFFF }, + { 0x1E21, CODEPAGE_ISLOWER, 0xFFFF, 0x1E20 }, + { 0x1E22, CODEPAGE_ISUPPER, 0x1E23, 0xFFFF }, + { 0x1E23, CODEPAGE_ISLOWER, 0xFFFF, 0x1E22 }, + { 0x1E24, CODEPAGE_ISUPPER, 0x1E25, 0xFFFF }, + { 0x1E25, CODEPAGE_ISLOWER, 0xFFFF, 0x1E24 }, + { 0x1E26, CODEPAGE_ISUPPER, 0x1E27, 0xFFFF }, + { 0x1E27, CODEPAGE_ISLOWER, 0xFFFF, 0x1E26 }, + { 0x1E28, CODEPAGE_ISUPPER, 0x1E29, 0xFFFF }, + { 0x1E29, CODEPAGE_ISLOWER, 0xFFFF, 0x1E28 }, + { 0x1E2A, CODEPAGE_ISUPPER, 0x1E2B, 0xFFFF }, + { 0x1E2B, CODEPAGE_ISLOWER, 0xFFFF, 0x1E2A }, + { 0x1E2C, CODEPAGE_ISUPPER, 0x1E2D, 0xFFFF }, + { 0x1E2D, CODEPAGE_ISLOWER, 0xFFFF, 0x1E2C }, + { 0x1E2E, CODEPAGE_ISUPPER, 0x1E2F, 0xFFFF }, + { 0x1E2F, CODEPAGE_ISLOWER, 0xFFFF, 0x1E2E }, + { 0x1E30, CODEPAGE_ISUPPER, 0x1E31, 0xFFFF }, + { 0x1E31, CODEPAGE_ISLOWER, 0xFFFF, 0x1E30 }, + { 0x1E32, CODEPAGE_ISUPPER, 0x1E33, 0xFFFF }, + { 0x1E33, CODEPAGE_ISLOWER, 0xFFFF, 0x1E32 }, + { 0x1E34, CODEPAGE_ISUPPER, 0x1E35, 0xFFFF }, + { 0x1E35, CODEPAGE_ISLOWER, 0xFFFF, 0x1E34 }, + { 0x1E36, CODEPAGE_ISUPPER, 0x1E37, 0xFFFF }, + { 0x1E37, CODEPAGE_ISLOWER, 0xFFFF, 0x1E36 }, + { 0x1E38, CODEPAGE_ISUPPER, 0x1E39, 0xFFFF }, + { 0x1E39, CODEPAGE_ISLOWER, 0xFFFF, 0x1E38 }, + { 0x1E3A, CODEPAGE_ISUPPER, 0x1E3B, 0xFFFF }, + { 0x1E3B, CODEPAGE_ISLOWER, 0xFFFF, 0x1E3A }, + { 0x1E3C, CODEPAGE_ISUPPER, 0x1E3D, 0xFFFF }, + { 0x1E3D, CODEPAGE_ISLOWER, 0xFFFF, 0x1E3C }, + { 0x1E3E, CODEPAGE_ISUPPER, 0x1E3F, 0xFFFF }, + { 0x1E3F, CODEPAGE_ISLOWER, 0xFFFF, 0x1E3E }, + { 0x1E40, CODEPAGE_ISUPPER, 0x1E41, 0xFFFF }, + { 0x1E41, CODEPAGE_ISLOWER, 0xFFFF, 0x1E40 }, + { 0x1E42, CODEPAGE_ISUPPER, 0x1E43, 0xFFFF }, + { 0x1E43, CODEPAGE_ISLOWER, 0xFFFF, 0x1E42 }, + { 0x1E44, CODEPAGE_ISUPPER, 0x1E45, 0xFFFF }, + { 0x1E45, CODEPAGE_ISLOWER, 0xFFFF, 0x1E44 }, + { 0x1E46, CODEPAGE_ISUPPER, 0x1E47, 0xFFFF }, + { 0x1E47, CODEPAGE_ISLOWER, 0xFFFF, 0x1E46 }, + { 0x1E48, CODEPAGE_ISUPPER, 0x1E49, 0xFFFF }, + { 0x1E49, CODEPAGE_ISLOWER, 0xFFFF, 0x1E48 }, + { 0x1E4A, CODEPAGE_ISUPPER, 0x1E4B, 0xFFFF }, + { 0x1E4B, CODEPAGE_ISLOWER, 0xFFFF, 0x1E4A }, + { 0x1E4C, CODEPAGE_ISUPPER, 0x1E4D, 0xFFFF }, + { 0x1E4D, CODEPAGE_ISLOWER, 0xFFFF, 0x1E4C }, + { 0x1E4E, CODEPAGE_ISUPPER, 0x1E4F, 0xFFFF }, + { 0x1E4F, CODEPAGE_ISLOWER, 0xFFFF, 0x1E4E }, + { 0x1E50, CODEPAGE_ISUPPER, 0x1E51, 0xFFFF }, + { 0x1E51, CODEPAGE_ISLOWER, 0xFFFF, 0x1E50 }, + { 0x1E52, CODEPAGE_ISUPPER, 0x1E53, 0xFFFF }, + { 0x1E53, CODEPAGE_ISLOWER, 0xFFFF, 0x1E52 }, + { 0x1E54, CODEPAGE_ISUPPER, 0x1E55, 0xFFFF }, + { 0x1E55, CODEPAGE_ISLOWER, 0xFFFF, 0x1E54 }, + { 0x1E56, CODEPAGE_ISUPPER, 0x1E57, 0xFFFF }, + { 0x1E57, CODEPAGE_ISLOWER, 0xFFFF, 0x1E56 }, + { 0x1E58, CODEPAGE_ISUPPER, 0x1E59, 0xFFFF }, + { 0x1E59, CODEPAGE_ISLOWER, 0xFFFF, 0x1E58 }, + { 0x1E5A, CODEPAGE_ISUPPER, 0x1E5B, 0xFFFF }, + { 0x1E5B, CODEPAGE_ISLOWER, 0xFFFF, 0x1E5A }, + { 0x1E5C, CODEPAGE_ISUPPER, 0x1E5D, 0xFFFF }, + { 0x1E5D, CODEPAGE_ISLOWER, 0xFFFF, 0x1E5C }, + { 0x1E5E, CODEPAGE_ISUPPER, 0x1E5F, 0xFFFF }, + { 0x1E5F, CODEPAGE_ISLOWER, 0xFFFF, 0x1E5E }, + { 0x1E60, CODEPAGE_ISUPPER, 0x1E61, 0xFFFF }, + { 0x1E61, CODEPAGE_ISLOWER, 0xFFFF, 0x1E60 }, + { 0x1E62, CODEPAGE_ISUPPER, 0x1E63, 0xFFFF }, + { 0x1E63, CODEPAGE_ISLOWER, 0xFFFF, 0x1E62 }, + { 0x1E64, CODEPAGE_ISUPPER, 0x1E65, 0xFFFF }, + { 0x1E65, CODEPAGE_ISLOWER, 0xFFFF, 0x1E64 }, + { 0x1E66, CODEPAGE_ISUPPER, 0x1E67, 0xFFFF }, + { 0x1E67, CODEPAGE_ISLOWER, 0xFFFF, 0x1E66 }, + { 0x1E68, CODEPAGE_ISUPPER, 0x1E69, 0xFFFF }, + { 0x1E69, CODEPAGE_ISLOWER, 0xFFFF, 0x1E68 }, + { 0x1E6A, CODEPAGE_ISUPPER, 0x1E6B, 0xFFFF }, + { 0x1E6B, CODEPAGE_ISLOWER, 0xFFFF, 0x1E6A }, + { 0x1E6C, CODEPAGE_ISUPPER, 0x1E6D, 0xFFFF }, + { 0x1E6D, CODEPAGE_ISLOWER, 0xFFFF, 0x1E6C }, + { 0x1E6E, CODEPAGE_ISUPPER, 0x1E6F, 0xFFFF }, + { 0x1E6F, CODEPAGE_ISLOWER, 0xFFFF, 0x1E6E }, + { 0x1E70, CODEPAGE_ISUPPER, 0x1E71, 0xFFFF }, + { 0x1E71, CODEPAGE_ISLOWER, 0xFFFF, 0x1E70 }, + { 0x1E72, CODEPAGE_ISUPPER, 0x1E73, 0xFFFF }, + { 0x1E73, CODEPAGE_ISLOWER, 0xFFFF, 0x1E72 }, + { 0x1E74, CODEPAGE_ISUPPER, 0x1E75, 0xFFFF }, + { 0x1E75, CODEPAGE_ISLOWER, 0xFFFF, 0x1E74 }, + { 0x1E76, CODEPAGE_ISUPPER, 0x1E77, 0xFFFF }, + { 0x1E77, CODEPAGE_ISLOWER, 0xFFFF, 0x1E76 }, + { 0x1E78, CODEPAGE_ISUPPER, 0x1E79, 0xFFFF }, + { 0x1E79, CODEPAGE_ISLOWER, 0xFFFF, 0x1E78 }, + { 0x1E7A, CODEPAGE_ISUPPER, 0x1E7B, 0xFFFF }, + { 0x1E7B, CODEPAGE_ISLOWER, 0xFFFF, 0x1E7A }, + { 0x1E7C, CODEPAGE_ISUPPER, 0x1E7D, 0xFFFF }, + { 0x1E7D, CODEPAGE_ISLOWER, 0xFFFF, 0x1E7C }, + { 0x1E7E, CODEPAGE_ISUPPER, 0x1E7F, 0xFFFF }, + { 0x1E7F, CODEPAGE_ISLOWER, 0xFFFF, 0x1E7E }, + { 0x1E80, CODEPAGE_ISUPPER, 0x1E81, 0xFFFF }, + { 0x1E81, CODEPAGE_ISLOWER, 0xFFFF, 0x1E80 }, + { 0x1E82, CODEPAGE_ISUPPER, 0x1E83, 0xFFFF }, + { 0x1E83, CODEPAGE_ISLOWER, 0xFFFF, 0x1E82 }, + { 0x1E84, CODEPAGE_ISUPPER, 0x1E85, 0xFFFF }, + { 0x1E85, CODEPAGE_ISLOWER, 0xFFFF, 0x1E84 }, + { 0x1E86, CODEPAGE_ISUPPER, 0x1E87, 0xFFFF }, + { 0x1E87, CODEPAGE_ISLOWER, 0xFFFF, 0x1E86 }, + { 0x1E88, CODEPAGE_ISUPPER, 0x1E89, 0xFFFF }, + { 0x1E89, CODEPAGE_ISLOWER, 0xFFFF, 0x1E88 }, + { 0x1E8A, CODEPAGE_ISUPPER, 0x1E8B, 0xFFFF }, + { 0x1E8B, CODEPAGE_ISLOWER, 0xFFFF, 0x1E8A }, + { 0x1E8C, CODEPAGE_ISUPPER, 0x1E8D, 0xFFFF }, + { 0x1E8D, CODEPAGE_ISLOWER, 0xFFFF, 0x1E8C }, + { 0x1E8E, CODEPAGE_ISUPPER, 0x1E8F, 0xFFFF }, + { 0x1E8F, CODEPAGE_ISLOWER, 0xFFFF, 0x1E8E }, + { 0x1E90, CODEPAGE_ISUPPER, 0x1E91, 0xFFFF }, + { 0x1E91, CODEPAGE_ISLOWER, 0xFFFF, 0x1E90 }, + { 0x1E92, CODEPAGE_ISUPPER, 0x1E93, 0xFFFF }, + { 0x1E93, CODEPAGE_ISLOWER, 0xFFFF, 0x1E92 }, + { 0x1E94, CODEPAGE_ISUPPER, 0x1E95, 0xFFFF }, + { 0x1E95, CODEPAGE_ISLOWER, 0xFFFF, 0x1E94 }, + { 0x1E96, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1E97, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1E98, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1E99, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1E9A, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1E9B, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1EA0, CODEPAGE_ISUPPER, 0x1EA1, 0xFFFF }, + { 0x1EA1, CODEPAGE_ISLOWER, 0xFFFF, 0x1EA0 }, + { 0x1EA2, CODEPAGE_ISUPPER, 0x1EA3, 0xFFFF }, + { 0x1EA3, CODEPAGE_ISLOWER, 0xFFFF, 0x1EA2 }, + { 0x1EA4, CODEPAGE_ISUPPER, 0x1EA5, 0xFFFF }, + { 0x1EA5, CODEPAGE_ISLOWER, 0xFFFF, 0x1EA4 }, + { 0x1EA6, CODEPAGE_ISUPPER, 0x1EA7, 0xFFFF }, + { 0x1EA7, CODEPAGE_ISLOWER, 0xFFFF, 0x1EA6 }, + { 0x1EA8, CODEPAGE_ISUPPER, 0x1EA9, 0xFFFF }, + { 0x1EA9, CODEPAGE_ISLOWER, 0xFFFF, 0x1EA8 }, + { 0x1EAA, CODEPAGE_ISUPPER, 0x1EAB, 0xFFFF }, + { 0x1EAB, CODEPAGE_ISLOWER, 0xFFFF, 0x1EAA }, + { 0x1EAC, CODEPAGE_ISUPPER, 0x1EAD, 0xFFFF }, + { 0x1EAD, CODEPAGE_ISLOWER, 0xFFFF, 0x1EAC }, + { 0x1EAE, CODEPAGE_ISUPPER, 0x1EAF, 0xFFFF }, + { 0x1EAF, CODEPAGE_ISLOWER, 0xFFFF, 0x1EAE }, + { 0x1EB0, CODEPAGE_ISUPPER, 0x1EB1, 0xFFFF }, + { 0x1EB1, CODEPAGE_ISLOWER, 0xFFFF, 0x1EB0 }, + { 0x1EB2, CODEPAGE_ISUPPER, 0x1EB3, 0xFFFF }, + { 0x1EB3, CODEPAGE_ISLOWER, 0xFFFF, 0x1EB2 }, + { 0x1EB4, CODEPAGE_ISUPPER, 0x1EB5, 0xFFFF }, + { 0x1EB5, CODEPAGE_ISLOWER, 0xFFFF, 0x1EB4 }, + { 0x1EB6, CODEPAGE_ISUPPER, 0x1EB7, 0xFFFF }, + { 0x1EB7, CODEPAGE_ISLOWER, 0xFFFF, 0x1EB6 }, + { 0x1EB8, CODEPAGE_ISUPPER, 0x1EB9, 0xFFFF }, + { 0x1EB9, CODEPAGE_ISLOWER, 0xFFFF, 0x1EB8 }, + { 0x1EBA, CODEPAGE_ISUPPER, 0x1EBB, 0xFFFF }, + { 0x1EBB, CODEPAGE_ISLOWER, 0xFFFF, 0x1EBA }, + { 0x1EBC, CODEPAGE_ISUPPER, 0x1EBD, 0xFFFF }, + { 0x1EBD, CODEPAGE_ISLOWER, 0xFFFF, 0x1EBC }, + { 0x1EBE, CODEPAGE_ISUPPER, 0x1EBF, 0xFFFF }, + { 0x1EBF, CODEPAGE_ISLOWER, 0xFFFF, 0x1EBE }, + { 0x1EC0, CODEPAGE_ISUPPER, 0x1EC1, 0xFFFF }, + { 0x1EC1, CODEPAGE_ISLOWER, 0xFFFF, 0x1EC0 }, + { 0x1EC2, CODEPAGE_ISUPPER, 0x1EC3, 0xFFFF }, + { 0x1EC3, CODEPAGE_ISLOWER, 0xFFFF, 0x1EC2 }, + { 0x1EC4, CODEPAGE_ISUPPER, 0x1EC5, 0xFFFF }, + { 0x1EC5, CODEPAGE_ISLOWER, 0xFFFF, 0x1EC4 }, + { 0x1EC6, CODEPAGE_ISUPPER, 0x1EC7, 0xFFFF }, + { 0x1EC7, CODEPAGE_ISLOWER, 0xFFFF, 0x1EC6 }, + { 0x1EC8, CODEPAGE_ISUPPER, 0x1EC9, 0xFFFF }, + { 0x1EC9, CODEPAGE_ISLOWER, 0xFFFF, 0x1EC8 }, + { 0x1ECA, CODEPAGE_ISUPPER, 0x1ECB, 0xFFFF }, + { 0x1ECB, CODEPAGE_ISLOWER, 0xFFFF, 0x1ECA }, + { 0x1ECC, CODEPAGE_ISUPPER, 0x1ECD, 0xFFFF }, + { 0x1ECD, CODEPAGE_ISLOWER, 0xFFFF, 0x1ECC }, + { 0x1ECE, CODEPAGE_ISUPPER, 0x1ECF, 0xFFFF }, + { 0x1ECF, CODEPAGE_ISLOWER, 0xFFFF, 0x1ECE }, + { 0x1ED0, CODEPAGE_ISUPPER, 0x1ED1, 0xFFFF }, + { 0x1ED1, CODEPAGE_ISLOWER, 0xFFFF, 0x1ED0 }, + { 0x1ED2, CODEPAGE_ISUPPER, 0x1ED3, 0xFFFF }, + { 0x1ED3, CODEPAGE_ISLOWER, 0xFFFF, 0x1ED2 }, + { 0x1ED4, CODEPAGE_ISUPPER, 0x1ED5, 0xFFFF }, + { 0x1ED5, CODEPAGE_ISLOWER, 0xFFFF, 0x1ED4 }, + { 0x1ED6, CODEPAGE_ISUPPER, 0x1ED7, 0xFFFF }, + { 0x1ED7, CODEPAGE_ISLOWER, 0xFFFF, 0x1ED6 }, + { 0x1ED8, CODEPAGE_ISUPPER, 0x1ED9, 0xFFFF }, + { 0x1ED9, CODEPAGE_ISLOWER, 0xFFFF, 0x1ED8 }, + { 0x1EDA, CODEPAGE_ISUPPER, 0x1EDB, 0xFFFF }, + { 0x1EDB, CODEPAGE_ISLOWER, 0xFFFF, 0x1EDA }, + { 0x1EDC, CODEPAGE_ISUPPER, 0x1EDD, 0xFFFF }, + { 0x1EDD, CODEPAGE_ISLOWER, 0xFFFF, 0x1EDC }, + { 0x1EDE, CODEPAGE_ISUPPER, 0x1EDF, 0xFFFF }, + { 0x1EDF, CODEPAGE_ISLOWER, 0xFFFF, 0x1EDE }, + { 0x1EE0, CODEPAGE_ISUPPER, 0x1EE1, 0xFFFF }, + { 0x1EE1, CODEPAGE_ISLOWER, 0xFFFF, 0x1EE0 }, + { 0x1EE2, CODEPAGE_ISUPPER, 0x1EE3, 0xFFFF }, + { 0x1EE3, CODEPAGE_ISLOWER, 0xFFFF, 0x1EE2 }, + { 0x1EE4, CODEPAGE_ISUPPER, 0x1EE5, 0xFFFF }, + { 0x1EE5, CODEPAGE_ISLOWER, 0xFFFF, 0x1EE4 }, + { 0x1EE6, CODEPAGE_ISUPPER, 0x1EE7, 0xFFFF }, + { 0x1EE7, CODEPAGE_ISLOWER, 0xFFFF, 0x1EE6 }, + { 0x1EE8, CODEPAGE_ISUPPER, 0x1EE9, 0xFFFF }, + { 0x1EE9, CODEPAGE_ISLOWER, 0xFFFF, 0x1EE8 }, + { 0x1EEA, CODEPAGE_ISUPPER, 0x1EEB, 0xFFFF }, + { 0x1EEB, CODEPAGE_ISLOWER, 0xFFFF, 0x1EEA }, + { 0x1EEC, CODEPAGE_ISUPPER, 0x1EED, 0xFFFF }, + { 0x1EED, CODEPAGE_ISLOWER, 0xFFFF, 0x1EEC }, + { 0x1EEE, CODEPAGE_ISUPPER, 0x1EEF, 0xFFFF }, + { 0x1EEF, CODEPAGE_ISLOWER, 0xFFFF, 0x1EEE }, + { 0x1EF0, CODEPAGE_ISUPPER, 0x1EF1, 0xFFFF }, + { 0x1EF1, CODEPAGE_ISLOWER, 0xFFFF, 0x1EF0 }, + { 0x1EF2, CODEPAGE_ISUPPER, 0x1EF3, 0xFFFF }, + { 0x1EF3, CODEPAGE_ISLOWER, 0xFFFF, 0x1EF2 }, + { 0x1EF4, CODEPAGE_ISUPPER, 0x1EF5, 0xFFFF }, + { 0x1EF5, CODEPAGE_ISLOWER, 0xFFFF, 0x1EF4 }, + { 0x1EF6, CODEPAGE_ISUPPER, 0x1EF7, 0xFFFF }, + { 0x1EF7, CODEPAGE_ISLOWER, 0xFFFF, 0x1EF6 }, + { 0x1EF8, CODEPAGE_ISUPPER, 0x1EF9, 0xFFFF }, + { 0x1EF9, CODEPAGE_ISLOWER, 0xFFFF, 0x1EF8 }, + { 0x1F00, CODEPAGE_ISLOWER, 0xFFFF, 0x1F08 }, + { 0x1F01, CODEPAGE_ISLOWER, 0xFFFF, 0x1F09 }, + { 0x1F02, CODEPAGE_ISLOWER, 0xFFFF, 0x1F0A }, + { 0x1F03, CODEPAGE_ISLOWER, 0xFFFF, 0x1F0B }, + { 0x1F04, CODEPAGE_ISLOWER, 0xFFFF, 0x1F0C }, + { 0x1F05, CODEPAGE_ISLOWER, 0xFFFF, 0x1F0D }, + { 0x1F06, CODEPAGE_ISLOWER, 0xFFFF, 0x1F0E }, + { 0x1F07, CODEPAGE_ISLOWER, 0xFFFF, 0x1F0F }, + { 0x1F08, CODEPAGE_ISUPPER, 0x1F00, 0xFFFF }, + { 0x1F09, CODEPAGE_ISUPPER, 0x1F01, 0xFFFF }, + { 0x1F0A, CODEPAGE_ISUPPER, 0x1F02, 0xFFFF }, + { 0x1F0B, CODEPAGE_ISUPPER, 0x1F03, 0xFFFF }, + { 0x1F0C, CODEPAGE_ISUPPER, 0x1F04, 0xFFFF }, + { 0x1F0D, CODEPAGE_ISUPPER, 0x1F05, 0xFFFF }, + { 0x1F0E, CODEPAGE_ISUPPER, 0x1F06, 0xFFFF }, + { 0x1F0F, CODEPAGE_ISUPPER, 0x1F07, 0xFFFF }, + { 0x1F10, CODEPAGE_ISLOWER, 0xFFFF, 0x1F18 }, + { 0x1F11, CODEPAGE_ISLOWER, 0xFFFF, 0x1F19 }, + { 0x1F12, CODEPAGE_ISLOWER, 0xFFFF, 0x1F1A }, + { 0x1F13, CODEPAGE_ISLOWER, 0xFFFF, 0x1F1B }, + { 0x1F14, CODEPAGE_ISLOWER, 0xFFFF, 0x1F1C }, + { 0x1F15, CODEPAGE_ISLOWER, 0xFFFF, 0x1F1D }, + { 0x1F18, CODEPAGE_ISUPPER, 0x1F10, 0xFFFF }, + { 0x1F19, CODEPAGE_ISUPPER, 0x1F11, 0xFFFF }, + { 0x1F1A, CODEPAGE_ISUPPER, 0x1F12, 0xFFFF }, + { 0x1F1B, CODEPAGE_ISUPPER, 0x1F13, 0xFFFF }, + { 0x1F1C, CODEPAGE_ISUPPER, 0x1F14, 0xFFFF }, + { 0x1F1D, CODEPAGE_ISUPPER, 0x1F15, 0xFFFF }, + { 0x1F20, CODEPAGE_ISLOWER, 0xFFFF, 0x1F28 }, + { 0x1F21, CODEPAGE_ISLOWER, 0xFFFF, 0x1F29 }, + { 0x1F22, CODEPAGE_ISLOWER, 0xFFFF, 0x1F2A }, + { 0x1F23, CODEPAGE_ISLOWER, 0xFFFF, 0x1F2B }, + { 0x1F24, CODEPAGE_ISLOWER, 0xFFFF, 0x1F2C }, + { 0x1F25, CODEPAGE_ISLOWER, 0xFFFF, 0x1F2D }, + { 0x1F26, CODEPAGE_ISLOWER, 0xFFFF, 0x1F2E }, + { 0x1F27, CODEPAGE_ISLOWER, 0xFFFF, 0x1F2F }, + { 0x1F28, CODEPAGE_ISUPPER, 0x1F20, 0xFFFF }, + { 0x1F29, CODEPAGE_ISUPPER, 0x1F21, 0xFFFF }, + { 0x1F2A, CODEPAGE_ISUPPER, 0x1F22, 0xFFFF }, + { 0x1F2B, CODEPAGE_ISUPPER, 0x1F23, 0xFFFF }, + { 0x1F2C, CODEPAGE_ISUPPER, 0x1F24, 0xFFFF }, + { 0x1F2D, CODEPAGE_ISUPPER, 0x1F25, 0xFFFF }, + { 0x1F2E, CODEPAGE_ISUPPER, 0x1F26, 0xFFFF }, + { 0x1F2F, CODEPAGE_ISUPPER, 0x1F27, 0xFFFF }, + { 0x1F30, CODEPAGE_ISLOWER, 0xFFFF, 0x1F38 }, + { 0x1F31, CODEPAGE_ISLOWER, 0xFFFF, 0x1F39 }, + { 0x1F32, CODEPAGE_ISLOWER, 0xFFFF, 0x1F3A }, + { 0x1F33, CODEPAGE_ISLOWER, 0xFFFF, 0x1F3B }, + { 0x1F34, CODEPAGE_ISLOWER, 0xFFFF, 0x1F3C }, + { 0x1F35, CODEPAGE_ISLOWER, 0xFFFF, 0x1F3D }, + { 0x1F36, CODEPAGE_ISLOWER, 0xFFFF, 0x1F3E }, + { 0x1F37, CODEPAGE_ISLOWER, 0xFFFF, 0x1F3F }, + { 0x1F38, CODEPAGE_ISUPPER, 0x1F30, 0xFFFF }, + { 0x1F39, CODEPAGE_ISUPPER, 0x1F31, 0xFFFF }, + { 0x1F3A, CODEPAGE_ISUPPER, 0x1F32, 0xFFFF }, + { 0x1F3B, CODEPAGE_ISUPPER, 0x1F33, 0xFFFF }, + { 0x1F3C, CODEPAGE_ISUPPER, 0x1F34, 0xFFFF }, + { 0x1F3D, CODEPAGE_ISUPPER, 0x1F35, 0xFFFF }, + { 0x1F3E, CODEPAGE_ISUPPER, 0x1F36, 0xFFFF }, + { 0x1F3F, CODEPAGE_ISUPPER, 0x1F37, 0xFFFF }, + { 0x1F40, CODEPAGE_ISLOWER, 0xFFFF, 0x1F48 }, + { 0x1F41, CODEPAGE_ISLOWER, 0xFFFF, 0x1F49 }, + { 0x1F42, CODEPAGE_ISLOWER, 0xFFFF, 0x1F4A }, + { 0x1F43, CODEPAGE_ISLOWER, 0xFFFF, 0x1F4B }, + { 0x1F44, CODEPAGE_ISLOWER, 0xFFFF, 0x1F4C }, + { 0x1F45, CODEPAGE_ISLOWER, 0xFFFF, 0x1F4D }, + { 0x1F48, CODEPAGE_ISUPPER, 0x1F40, 0xFFFF }, + { 0x1F49, CODEPAGE_ISUPPER, 0x1F41, 0xFFFF }, + { 0x1F4A, CODEPAGE_ISUPPER, 0x1F42, 0xFFFF }, + { 0x1F4B, CODEPAGE_ISUPPER, 0x1F43, 0xFFFF }, + { 0x1F4C, CODEPAGE_ISUPPER, 0x1F44, 0xFFFF }, + { 0x1F4D, CODEPAGE_ISUPPER, 0x1F45, 0xFFFF }, + { 0x1F50, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1F51, CODEPAGE_ISLOWER, 0xFFFF, 0x1F59 }, + { 0x1F52, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1F53, CODEPAGE_ISLOWER, 0xFFFF, 0x1F5B }, + { 0x1F54, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1F55, CODEPAGE_ISLOWER, 0xFFFF, 0x1F5D }, + { 0x1F56, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1F57, CODEPAGE_ISLOWER, 0xFFFF, 0x1F5F }, + { 0x1F59, CODEPAGE_ISUPPER, 0x1F51, 0xFFFF }, + { 0x1F5B, CODEPAGE_ISUPPER, 0x1F53, 0xFFFF }, + { 0x1F5D, CODEPAGE_ISUPPER, 0x1F55, 0xFFFF }, + { 0x1F5F, CODEPAGE_ISUPPER, 0x1F57, 0xFFFF }, + { 0x1F60, CODEPAGE_ISLOWER, 0xFFFF, 0x1F68 }, + { 0x1F61, CODEPAGE_ISLOWER, 0xFFFF, 0x1F69 }, + { 0x1F62, CODEPAGE_ISLOWER, 0xFFFF, 0x1F6A }, + { 0x1F63, CODEPAGE_ISLOWER, 0xFFFF, 0x1F6B }, + { 0x1F64, CODEPAGE_ISLOWER, 0xFFFF, 0x1F6C }, + { 0x1F65, CODEPAGE_ISLOWER, 0xFFFF, 0x1F6D }, + { 0x1F66, CODEPAGE_ISLOWER, 0xFFFF, 0x1F6E }, + { 0x1F67, CODEPAGE_ISLOWER, 0xFFFF, 0x1F6F }, + { 0x1F68, CODEPAGE_ISUPPER, 0x1F60, 0xFFFF }, + { 0x1F69, CODEPAGE_ISUPPER, 0x1F61, 0xFFFF }, + { 0x1F6A, CODEPAGE_ISUPPER, 0x1F62, 0xFFFF }, + { 0x1F6B, CODEPAGE_ISUPPER, 0x1F63, 0xFFFF }, + { 0x1F6C, CODEPAGE_ISUPPER, 0x1F64, 0xFFFF }, + { 0x1F6D, CODEPAGE_ISUPPER, 0x1F65, 0xFFFF }, + { 0x1F6E, CODEPAGE_ISUPPER, 0x1F66, 0xFFFF }, + { 0x1F6F, CODEPAGE_ISUPPER, 0x1F67, 0xFFFF }, + { 0x1F70, CODEPAGE_ISLOWER, 0xFFFF, 0x1FBA }, + { 0x1F71, CODEPAGE_ISLOWER, 0xFFFF, 0x1FBB }, + { 0x1F72, CODEPAGE_ISLOWER, 0xFFFF, 0x1FC8 }, + { 0x1F73, CODEPAGE_ISLOWER, 0xFFFF, 0x1FC9 }, + { 0x1F74, CODEPAGE_ISLOWER, 0xFFFF, 0x1FCA }, + { 0x1F75, CODEPAGE_ISLOWER, 0xFFFF, 0x1FCB }, + { 0x1F76, CODEPAGE_ISLOWER, 0xFFFF, 0x1FDA }, + { 0x1F77, CODEPAGE_ISLOWER, 0xFFFF, 0x1FDB }, + { 0x1F78, CODEPAGE_ISLOWER, 0xFFFF, 0x1FF8 }, + { 0x1F79, CODEPAGE_ISLOWER, 0xFFFF, 0x1FF9 }, + { 0x1F7A, CODEPAGE_ISLOWER, 0xFFFF, 0x1FEA }, + { 0x1F7B, CODEPAGE_ISLOWER, 0xFFFF, 0x1FEB }, + { 0x1F7C, CODEPAGE_ISLOWER, 0xFFFF, 0x1FFA }, + { 0x1F7D, CODEPAGE_ISLOWER, 0xFFFF, 0x1FFB }, + { 0x1F80, CODEPAGE_ISLOWER, 0xFFFF, 0x1F88 }, + { 0x1F81, CODEPAGE_ISLOWER, 0xFFFF, 0x1F89 }, + { 0x1F82, CODEPAGE_ISLOWER, 0xFFFF, 0x1F8A }, + { 0x1F83, CODEPAGE_ISLOWER, 0xFFFF, 0x1F8B }, + { 0x1F84, CODEPAGE_ISLOWER, 0xFFFF, 0x1F8C }, + { 0x1F85, CODEPAGE_ISLOWER, 0xFFFF, 0x1F8D }, + { 0x1F86, CODEPAGE_ISLOWER, 0xFFFF, 0x1F8E }, + { 0x1F87, CODEPAGE_ISLOWER, 0xFFFF, 0x1F8F }, + { 0x1F88, CODEPAGE_ISUPPER, 0x1F80, 0xFFFF }, + { 0x1F89, CODEPAGE_ISUPPER, 0x1F81, 0xFFFF }, + { 0x1F8A, CODEPAGE_ISUPPER, 0x1F82, 0xFFFF }, + { 0x1F8B, CODEPAGE_ISUPPER, 0x1F83, 0xFFFF }, + { 0x1F8C, CODEPAGE_ISUPPER, 0x1F84, 0xFFFF }, + { 0x1F8D, CODEPAGE_ISUPPER, 0x1F85, 0xFFFF }, + { 0x1F8E, CODEPAGE_ISUPPER, 0x1F86, 0xFFFF }, + { 0x1F8F, CODEPAGE_ISUPPER, 0x1F87, 0xFFFF }, + { 0x1F90, CODEPAGE_ISLOWER, 0xFFFF, 0x1F98 }, + { 0x1F91, CODEPAGE_ISLOWER, 0xFFFF, 0x1F99 }, + { 0x1F92, CODEPAGE_ISLOWER, 0xFFFF, 0x1F9A }, + { 0x1F93, CODEPAGE_ISLOWER, 0xFFFF, 0x1F9B }, + { 0x1F94, CODEPAGE_ISLOWER, 0xFFFF, 0x1F9C }, + { 0x1F95, CODEPAGE_ISLOWER, 0xFFFF, 0x1F9D }, + { 0x1F96, CODEPAGE_ISLOWER, 0xFFFF, 0x1F9E }, + { 0x1F97, CODEPAGE_ISLOWER, 0xFFFF, 0x1F9F }, + { 0x1F98, CODEPAGE_ISUPPER, 0x1F90, 0xFFFF }, + { 0x1F99, CODEPAGE_ISUPPER, 0x1F91, 0xFFFF }, + { 0x1F9A, CODEPAGE_ISUPPER, 0x1F92, 0xFFFF }, + { 0x1F9B, CODEPAGE_ISUPPER, 0x1F93, 0xFFFF }, + { 0x1F9C, CODEPAGE_ISUPPER, 0x1F94, 0xFFFF }, + { 0x1F9D, CODEPAGE_ISUPPER, 0x1F95, 0xFFFF }, + { 0x1F9E, CODEPAGE_ISUPPER, 0x1F96, 0xFFFF }, + { 0x1F9F, CODEPAGE_ISUPPER, 0x1F97, 0xFFFF }, + { 0x1FA0, CODEPAGE_ISLOWER, 0xFFFF, 0x1FA8 }, + { 0x1FA1, CODEPAGE_ISLOWER, 0xFFFF, 0x1FA9 }, + { 0x1FA2, CODEPAGE_ISLOWER, 0xFFFF, 0x1FAA }, + { 0x1FA3, CODEPAGE_ISLOWER, 0xFFFF, 0x1FAB }, + { 0x1FA4, CODEPAGE_ISLOWER, 0xFFFF, 0x1FAC }, + { 0x1FA5, CODEPAGE_ISLOWER, 0xFFFF, 0x1FAD }, + { 0x1FA6, CODEPAGE_ISLOWER, 0xFFFF, 0x1FAE }, + { 0x1FA7, CODEPAGE_ISLOWER, 0xFFFF, 0x1FAF }, + { 0x1FA8, CODEPAGE_ISUPPER, 0x1FA0, 0xFFFF }, + { 0x1FA9, CODEPAGE_ISUPPER, 0x1FA1, 0xFFFF }, + { 0x1FAA, CODEPAGE_ISUPPER, 0x1FA2, 0xFFFF }, + { 0x1FAB, CODEPAGE_ISUPPER, 0x1FA3, 0xFFFF }, + { 0x1FAC, CODEPAGE_ISUPPER, 0x1FA4, 0xFFFF }, + { 0x1FAD, CODEPAGE_ISUPPER, 0x1FA5, 0xFFFF }, + { 0x1FAE, CODEPAGE_ISUPPER, 0x1FA6, 0xFFFF }, + { 0x1FAF, CODEPAGE_ISUPPER, 0x1FA7, 0xFFFF }, + { 0x1FB0, CODEPAGE_ISLOWER, 0xFFFF, 0x1FB8 }, + { 0x1FB1, CODEPAGE_ISLOWER, 0xFFFF, 0x1FB9 }, + { 0x1FB2, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FB3, CODEPAGE_ISLOWER, 0xFFFF, 0x1FBC }, + { 0x1FB4, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FB6, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FB7, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FB8, CODEPAGE_ISUPPER, 0x1FB0, 0xFFFF }, + { 0x1FB9, CODEPAGE_ISUPPER, 0x1FB1, 0xFFFF }, + { 0x1FBA, CODEPAGE_ISUPPER, 0x1F70, 0xFFFF }, + { 0x1FBB, CODEPAGE_ISUPPER, 0x1F71, 0xFFFF }, + { 0x1FBC, CODEPAGE_ISUPPER, 0x1FB3, 0xFFFF }, + { 0x1FBD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1FBE, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x1FBF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1FC0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1FC1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1FC2, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FC3, CODEPAGE_ISLOWER, 0xFFFF, 0x1FCC }, + { 0x1FC4, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FC6, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FC7, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FC8, CODEPAGE_ISUPPER, 0x1F72, 0xFFFF }, + { 0x1FC9, CODEPAGE_ISUPPER, 0x1F73, 0xFFFF }, + { 0x1FCA, CODEPAGE_ISUPPER, 0x1F74, 0xFFFF }, + { 0x1FCB, CODEPAGE_ISUPPER, 0x1F75, 0xFFFF }, + { 0x1FCC, CODEPAGE_ISUPPER, 0x1FC3, 0xFFFF }, + { 0x1FCD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1FCE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1FCF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1FD0, CODEPAGE_ISLOWER, 0xFFFF, 0x1FD8 }, + { 0x1FD1, CODEPAGE_ISLOWER, 0xFFFF, 0x1FD9 }, + { 0x1FD2, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FD3, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FD6, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FD7, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FD8, CODEPAGE_ISUPPER, 0x1FD0, 0xFFFF }, + { 0x1FD9, CODEPAGE_ISUPPER, 0x1FD1, 0xFFFF }, + { 0x1FDA, CODEPAGE_ISUPPER, 0x1F76, 0xFFFF }, + { 0x1FDB, CODEPAGE_ISUPPER, 0x1F77, 0xFFFF }, + { 0x1FDD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1FDE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1FDF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1FE0, CODEPAGE_ISLOWER, 0xFFFF, 0x1FE8 }, + { 0x1FE1, CODEPAGE_ISLOWER, 0xFFFF, 0x1FE9 }, + { 0x1FE2, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FE3, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FE4, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FE5, CODEPAGE_ISLOWER, 0xFFFF, 0x1FEC }, + { 0x1FE6, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FE7, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FE8, CODEPAGE_ISUPPER, 0x1FE0, 0xFFFF }, + { 0x1FE9, CODEPAGE_ISUPPER, 0x1FE1, 0xFFFF }, + { 0x1FEA, CODEPAGE_ISUPPER, 0x1F7A, 0xFFFF }, + { 0x1FEB, CODEPAGE_ISUPPER, 0x1F7B, 0xFFFF }, + { 0x1FEC, CODEPAGE_ISUPPER, 0x1FE5, 0xFFFF }, + { 0x1FED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1FEE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1FEF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1FF2, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FF3, CODEPAGE_ISLOWER, 0xFFFF, 0x1FFC }, + { 0x1FF4, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FF6, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FF7, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x1FF8, CODEPAGE_ISUPPER, 0x1F78, 0xFFFF }, + { 0x1FF9, CODEPAGE_ISUPPER, 0x1F79, 0xFFFF }, + { 0x1FFA, CODEPAGE_ISUPPER, 0x1F7C, 0xFFFF }, + { 0x1FFB, CODEPAGE_ISUPPER, 0x1F7D, 0xFFFF }, + { 0x1FFC, CODEPAGE_ISUPPER, 0x1FF3, 0xFFFF }, + { 0x1FFD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x1FFE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2000, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2001, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2002, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2003, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2004, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2005, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2006, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2007, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2008, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2009, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x200A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x200B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x200C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x200D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x200E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x200F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2010, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2011, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2012, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2013, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2014, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2015, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2016, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2017, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2018, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2019, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x201A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x201B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x201C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x201D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x201E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x201F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2020, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2021, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2022, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2023, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2024, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2025, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2026, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2027, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2028, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2029, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x202A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x202B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x202C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x202D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x202E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2030, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2031, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2032, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2033, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2034, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2035, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2036, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2037, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2038, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2039, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x203A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x203B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x203C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x203D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x203E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x203F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2040, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2041, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2042, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2043, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2044, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2045, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2046, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x206A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x206B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x206C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x206D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x206E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x206F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2070, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2074, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2075, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2076, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2077, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2078, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2079, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x207A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x207B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x207C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x207D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x207E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x207F, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x2080, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2081, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2082, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2083, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2084, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2085, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2086, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2087, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2088, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2089, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x208A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x208B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x208C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x208D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x208E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20A0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20A1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20A2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20A3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20A4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20A5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20A6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20A7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20A8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20A9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20AA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20AB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20D0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20D1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20D2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20D3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20D4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20D5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20D6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20D7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20D8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20D9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20DA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20DB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20DC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20DD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20DE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20DF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20E0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x20E1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2100, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2101, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2102, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2103, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2104, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2105, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2106, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2107, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2108, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2109, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x210A, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x210B, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x210C, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x210D, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x210E, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x210F, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x2110, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2111, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2112, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2113, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x2114, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2115, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2116, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2117, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2118, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2119, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x211A, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x211B, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x211C, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x211D, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x211E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x211F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2120, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2121, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2122, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2123, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2124, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2125, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2126, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2127, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2128, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2129, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x212A, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x212B, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x212C, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x212D, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x212E, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x212F, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x2130, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2131, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2132, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2133, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x2134, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x2135, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2136, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2137, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2138, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2153, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2154, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2155, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2156, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2157, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2158, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2159, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x215A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x215B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x215C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x215D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x215E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x215F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2160, CODEPAGE_ISNONE, 0x2170, 0xFFFF }, + { 0x2161, CODEPAGE_ISNONE, 0x2171, 0xFFFF }, + { 0x2162, CODEPAGE_ISNONE, 0x2172, 0xFFFF }, + { 0x2163, CODEPAGE_ISNONE, 0x2173, 0xFFFF }, + { 0x2164, CODEPAGE_ISNONE, 0x2174, 0xFFFF }, + { 0x2165, CODEPAGE_ISNONE, 0x2175, 0xFFFF }, + { 0x2166, CODEPAGE_ISNONE, 0x2176, 0xFFFF }, + { 0x2167, CODEPAGE_ISNONE, 0x2177, 0xFFFF }, + { 0x2168, CODEPAGE_ISNONE, 0x2178, 0xFFFF }, + { 0x2169, CODEPAGE_ISNONE, 0x2179, 0xFFFF }, + { 0x216A, CODEPAGE_ISNONE, 0x217A, 0xFFFF }, + { 0x216B, CODEPAGE_ISNONE, 0x217B, 0xFFFF }, + { 0x216C, CODEPAGE_ISNONE, 0x217C, 0xFFFF }, + { 0x216D, CODEPAGE_ISNONE, 0x217D, 0xFFFF }, + { 0x216E, CODEPAGE_ISNONE, 0x217E, 0xFFFF }, + { 0x216F, CODEPAGE_ISNONE, 0x217F, 0xFFFF }, + { 0x2170, CODEPAGE_ISNONE, 0xFFFF, 0x2160 }, + { 0x2171, CODEPAGE_ISNONE, 0xFFFF, 0x2161 }, + { 0x2172, CODEPAGE_ISNONE, 0xFFFF, 0x2162 }, + { 0x2173, CODEPAGE_ISNONE, 0xFFFF, 0x2163 }, + { 0x2174, CODEPAGE_ISNONE, 0xFFFF, 0x2164 }, + { 0x2175, CODEPAGE_ISNONE, 0xFFFF, 0x2165 }, + { 0x2176, CODEPAGE_ISNONE, 0xFFFF, 0x2166 }, + { 0x2177, CODEPAGE_ISNONE, 0xFFFF, 0x2167 }, + { 0x2178, CODEPAGE_ISNONE, 0xFFFF, 0x2168 }, + { 0x2179, CODEPAGE_ISNONE, 0xFFFF, 0x2169 }, + { 0x217A, CODEPAGE_ISNONE, 0xFFFF, 0x216A }, + { 0x217B, CODEPAGE_ISNONE, 0xFFFF, 0x216B }, + { 0x217C, CODEPAGE_ISNONE, 0xFFFF, 0x216C }, + { 0x217D, CODEPAGE_ISNONE, 0xFFFF, 0x216D }, + { 0x217E, CODEPAGE_ISNONE, 0xFFFF, 0x216E }, + { 0x217F, CODEPAGE_ISNONE, 0xFFFF, 0x216F }, + { 0x2180, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2181, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2182, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2190, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2191, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2192, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2193, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2194, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2195, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2196, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2197, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2198, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2199, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x219A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x219B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x219C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x219D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x219E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x219F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21A0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21A1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21A2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21A3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21A4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21A5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21A6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21A7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21A8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21A9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21AA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21AB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21AC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21AD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21AE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21AF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21B0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21B1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21B2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21B3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21B4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21B5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21B6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21B7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21B8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21B9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21BA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21BB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21BC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21BD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21BE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21BF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21C0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21C1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21C2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21C3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21C4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21C5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21C6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21C7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21C8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21C9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21CA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21CB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21CC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21CD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21CE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21CF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21D0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21D1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21D2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21D3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21D4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21D5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21D6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21D7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21D8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21D9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21DA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21DB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21DC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21DD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21DE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21DF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21E0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21E1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21E2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21E3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21E4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21E5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21E6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21E7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21E8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21E9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x21EA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2200, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2201, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2202, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2203, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2204, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2205, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2206, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2207, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2208, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2209, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x220A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x220B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x220C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x220D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x220E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x220F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2210, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2211, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2212, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2213, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2214, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2215, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2216, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2217, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2218, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2219, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x221A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x221B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x221C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x221D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x221E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x221F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2220, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2221, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2222, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2223, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2224, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2225, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2226, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2227, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2228, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2229, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x222A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x222B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x222C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x222D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x222E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x222F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2230, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2231, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2232, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2233, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2234, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2235, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2236, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2237, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2238, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2239, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x223A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x223B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x223C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x223D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x223E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x223F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2240, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2241, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2242, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2243, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2244, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2245, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2246, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2247, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2248, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2249, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x224A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x224B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x224C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x224D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x224E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x224F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2250, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2251, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2252, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2253, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2254, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2255, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2256, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2257, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2258, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2259, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x225A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x225B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x225C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x225D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x225E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x225F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2260, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2261, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2262, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2263, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2264, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2265, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2266, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2267, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2268, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2269, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x226A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x226B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x226C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x226D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x226E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x226F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2270, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2271, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2272, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2273, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2274, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2275, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2276, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2277, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2278, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2279, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x227A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x227B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x227C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x227D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x227E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x227F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2280, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2281, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2282, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2283, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2284, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2285, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2286, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2287, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2288, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2289, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x228A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x228B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x228C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x228D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x228E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x228F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2290, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2291, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2292, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2293, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2294, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2295, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2296, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2297, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2298, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2299, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x229A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x229B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x229C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x229D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x229E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x229F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22A0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22A1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22A2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22A3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22A4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22A5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22A6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22A7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22A8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22A9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22AA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22AB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22AC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22AD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22AE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22AF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22B0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22B1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22B2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22B3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22B4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22B5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22B6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22B7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22B8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22B9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22BA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22BB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22BC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22BD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22BE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22BF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22C0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22C1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22C2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22C3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22C4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22C5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22C6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22C7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22C8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22C9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22CA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22CB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22CC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22CD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22CE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22CF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22D0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22D1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22D2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22D3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22D4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22D5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22D6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22D7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22D8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22D9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22DA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22DB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22DC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22DD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22DE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22DF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22E0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22E1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22E2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22E3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22E4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22E5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22E6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22E7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22E8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22E9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22EA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22EB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22EC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22ED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22EE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22EF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22F0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x22F1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2300, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2302, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2303, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2304, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2305, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2306, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2307, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2308, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2309, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x230A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x230B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x230C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x230D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x230E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x230F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2310, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2311, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2312, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2313, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2314, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2315, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2316, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2317, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2318, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2319, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x231A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x231B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x231C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x231D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x231E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x231F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2320, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2321, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2322, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2323, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2324, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2325, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2326, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2327, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2328, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2329, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x232A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x232B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x232C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x232D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x232E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x232F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2330, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2331, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2332, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2333, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2334, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2335, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2336, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2337, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2338, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2339, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x233A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x233B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x233C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x233D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x233E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x233F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2340, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2341, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2342, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2343, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2344, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2345, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2346, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2347, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2348, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2349, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x234A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x234B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x234C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x234D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x234E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x234F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2350, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2351, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2352, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2353, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2354, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2355, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2356, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2357, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2358, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2359, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x235A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x235B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x235C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x235D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x235E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x235F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2360, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2361, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2362, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2363, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2364, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2365, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2366, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2367, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2368, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2369, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x236A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x236B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x236C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x236D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x236E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x236F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2370, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2371, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2372, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2373, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2374, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2375, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2376, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2377, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2378, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2379, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x237A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2400, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2401, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2402, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2403, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2404, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2405, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2406, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2407, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2408, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2409, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x240A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x240B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x240C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x240D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x240E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x240F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2410, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2411, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2412, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2413, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2414, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2415, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2416, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2417, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2418, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2419, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x241A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x241B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x241C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x241D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x241E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x241F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2420, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2421, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2422, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2423, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2424, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2440, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2441, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2442, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2443, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2444, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2445, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2446, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2447, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2448, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2449, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x244A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2460, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2461, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2462, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2463, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2464, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2465, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2466, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2467, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2468, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2469, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x246A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x246B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x246C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x246D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x246E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x246F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2470, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2471, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2472, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2473, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2474, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2475, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2476, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2477, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2478, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2479, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x247A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x247B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x247C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x247D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x247E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x247F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2480, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2481, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2482, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2483, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2484, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2485, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2486, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2487, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2488, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2489, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x248A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x248B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x248C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x248D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x248E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x248F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2490, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2491, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2492, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2493, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2494, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2495, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2496, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2497, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2498, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2499, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x249A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x249B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x249C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x249D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x249E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x249F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24A0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24A1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24A2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24A3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24A4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24A5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24A6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24A7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24A8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24A9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24AA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24AB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24AC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24AD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24AE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24AF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24B0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24B1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24B2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24B3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24B4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24B5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x24B6, CODEPAGE_ISUPPER, 0x24D0, 0xFFFF }, + { 0x24B7, CODEPAGE_ISUPPER, 0x24D1, 0xFFFF }, + { 0x24B8, CODEPAGE_ISUPPER, 0x24D2, 0xFFFF }, + { 0x24B9, CODEPAGE_ISUPPER, 0x24D3, 0xFFFF }, + { 0x24BA, CODEPAGE_ISUPPER, 0x24D4, 0xFFFF }, + { 0x24BB, CODEPAGE_ISUPPER, 0x24D5, 0xFFFF }, + { 0x24BC, CODEPAGE_ISUPPER, 0x24D6, 0xFFFF }, + { 0x24BD, CODEPAGE_ISUPPER, 0x24D7, 0xFFFF }, + { 0x24BE, CODEPAGE_ISUPPER, 0x24D8, 0xFFFF }, + { 0x24BF, CODEPAGE_ISUPPER, 0x24D9, 0xFFFF }, + { 0x24C0, CODEPAGE_ISUPPER, 0x24DA, 0xFFFF }, + { 0x24C1, CODEPAGE_ISUPPER, 0x24DB, 0xFFFF }, + { 0x24C2, CODEPAGE_ISUPPER, 0x24DC, 0xFFFF }, + { 0x24C3, CODEPAGE_ISUPPER, 0x24DD, 0xFFFF }, + { 0x24C4, CODEPAGE_ISUPPER, 0x24DE, 0xFFFF }, + { 0x24C5, CODEPAGE_ISUPPER, 0x24DF, 0xFFFF }, + { 0x24C6, CODEPAGE_ISUPPER, 0x24E0, 0xFFFF }, + { 0x24C7, CODEPAGE_ISUPPER, 0x24E1, 0xFFFF }, + { 0x24C8, CODEPAGE_ISUPPER, 0x24E2, 0xFFFF }, + { 0x24C9, CODEPAGE_ISUPPER, 0x24E3, 0xFFFF }, + { 0x24CA, CODEPAGE_ISUPPER, 0x24E4, 0xFFFF }, + { 0x24CB, CODEPAGE_ISUPPER, 0x24E5, 0xFFFF }, + { 0x24CC, CODEPAGE_ISUPPER, 0x24E6, 0xFFFF }, + { 0x24CD, CODEPAGE_ISUPPER, 0x24E7, 0xFFFF }, + { 0x24CE, CODEPAGE_ISUPPER, 0x24E8, 0xFFFF }, + { 0x24CF, CODEPAGE_ISUPPER, 0x24E9, 0xFFFF }, + { 0x24D0, CODEPAGE_ISLOWER, 0xFFFF, 0x24B6 }, + { 0x24D1, CODEPAGE_ISLOWER, 0xFFFF, 0x24B7 }, + { 0x24D2, CODEPAGE_ISLOWER, 0xFFFF, 0x24B8 }, + { 0x24D3, CODEPAGE_ISLOWER, 0xFFFF, 0x24B9 }, + { 0x24D4, CODEPAGE_ISLOWER, 0xFFFF, 0x24BA }, + { 0x24D5, CODEPAGE_ISLOWER, 0xFFFF, 0x24BB }, + { 0x24D6, CODEPAGE_ISLOWER, 0xFFFF, 0x24BC }, + { 0x24D7, CODEPAGE_ISLOWER, 0xFFFF, 0x24BD }, + { 0x24D8, CODEPAGE_ISLOWER, 0xFFFF, 0x24BE }, + { 0x24D9, CODEPAGE_ISLOWER, 0xFFFF, 0x24BF }, + { 0x24DA, CODEPAGE_ISLOWER, 0xFFFF, 0x24C0 }, + { 0x24DB, CODEPAGE_ISLOWER, 0xFFFF, 0x24C1 }, + { 0x24DC, CODEPAGE_ISLOWER, 0xFFFF, 0x24C2 }, + { 0x24DD, CODEPAGE_ISLOWER, 0xFFFF, 0x24C3 }, + { 0x24DE, CODEPAGE_ISLOWER, 0xFFFF, 0x24C4 }, + { 0x24DF, CODEPAGE_ISLOWER, 0xFFFF, 0x24C5 }, + { 0x24E0, CODEPAGE_ISLOWER, 0xFFFF, 0x24C6 }, + { 0x24E1, CODEPAGE_ISLOWER, 0xFFFF, 0x24C7 }, + { 0x24E2, CODEPAGE_ISLOWER, 0xFFFF, 0x24C8 }, + { 0x24E3, CODEPAGE_ISLOWER, 0xFFFF, 0x24C9 }, + { 0x24E4, CODEPAGE_ISLOWER, 0xFFFF, 0x24CA }, + { 0x24E5, CODEPAGE_ISLOWER, 0xFFFF, 0x24CB }, + { 0x24E6, CODEPAGE_ISLOWER, 0xFFFF, 0x24CC }, + { 0x24E7, CODEPAGE_ISLOWER, 0xFFFF, 0x24CD }, + { 0x24E8, CODEPAGE_ISLOWER, 0xFFFF, 0x24CE }, + { 0x24E9, CODEPAGE_ISLOWER, 0xFFFF, 0x24CF }, + { 0x24EA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2500, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2501, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2502, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2503, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2504, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2505, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2506, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2507, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2508, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2509, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x250A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x250B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x250C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x250D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x250E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x250F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2510, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2511, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2512, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2513, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2514, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2515, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2516, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2517, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2518, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2519, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x251A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x251B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x251C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x251D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x251E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x251F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2520, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2521, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2522, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2523, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2524, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2525, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2526, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2527, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2528, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2529, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x252A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x252B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x252C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x252D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x252E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x252F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2530, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2531, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2532, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2533, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2534, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2535, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2536, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2537, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2538, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2539, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x253A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x253B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x253C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x253D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x253E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x253F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2540, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2541, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2542, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2543, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2544, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2545, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2546, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2547, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2548, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2549, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x254A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x254B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x254C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x254D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x254E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x254F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2550, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2551, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2552, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2553, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2554, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2555, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2556, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2557, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2558, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2559, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x255A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x255B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x255C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x255D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x255E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x255F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2560, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2561, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2562, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2563, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2564, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2565, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2566, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2567, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2568, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2569, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x256A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x256B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x256C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x256D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x256E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x256F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2570, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2571, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2572, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2573, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2574, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2575, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2576, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2577, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2578, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2579, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x257A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x257B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x257C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x257D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x257E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x257F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2580, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2581, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2582, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2583, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2584, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2585, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2586, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2587, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2588, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2589, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x258A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x258B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x258C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x258D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x258E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x258F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2590, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2591, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2592, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2593, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2594, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2595, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25A0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25A1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25A2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25A3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25A4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25A5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25A6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25A7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25A8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25A9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25AA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25AB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25AC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25AD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25AE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25AF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25B0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25B1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25B2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25B3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25B4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25B5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25B6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25B7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25B8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25B9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25BA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25BB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25BC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25BD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25BE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25BF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25C0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25C1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25C2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25C3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25C4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25C5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25C6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25C7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25C8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25C9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25CA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25CB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25CC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25CD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25CE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25CF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25D0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25D1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25D2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25D3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25D4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25D5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25D6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25D7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25D8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25D9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25DA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25DB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25DC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25DD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25DE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25DF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25E0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25E1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25E2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25E3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25E4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25E5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25E6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25E7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25E8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25E9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25EA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25EB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25EC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25ED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25EE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x25EF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2600, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2601, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2602, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2603, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2604, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2605, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2606, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2607, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2608, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2609, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x260A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x260B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x260C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x260D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x260E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x260F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2610, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2611, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2612, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2613, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x261A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x261B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x261C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x261D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x261E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x261F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2620, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2621, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2622, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2623, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2624, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2625, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2626, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2627, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2628, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2629, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x262A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x262B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x262C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x262D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x262E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x262F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2630, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2631, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2632, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2633, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2634, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2635, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2636, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2637, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2638, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2639, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x263A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x263B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x263C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x263D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x263E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x263F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2640, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2641, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2642, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2643, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2644, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2645, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2646, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2647, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2648, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2649, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x264A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x264B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x264C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x264D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x264E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x264F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2650, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2651, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2652, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2653, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2654, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2655, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2656, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2657, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2658, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2659, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x265A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x265B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x265C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x265D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x265E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x265F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2660, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2661, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2662, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2663, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2664, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2665, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2666, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2667, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2668, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2669, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x266A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x266B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x266C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x266D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x266E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x266F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2701, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2702, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2703, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2704, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2706, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2707, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2708, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2709, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x270C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x270D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x270E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x270F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2710, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2711, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2712, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2713, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2714, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2715, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2716, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2717, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2718, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2719, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x271A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x271B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x271C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x271D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x271E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x271F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2720, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2721, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2722, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2723, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2724, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2725, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2726, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2727, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2729, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x272A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x272B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x272C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x272D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x272E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x272F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2730, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2731, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2732, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2733, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2734, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2735, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2736, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2737, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2738, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2739, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x273A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x273B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x273C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x273D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x273E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x273F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2740, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2741, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2742, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2743, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2744, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2745, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2746, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2747, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2748, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2749, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x274A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x274B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x274D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x274F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2750, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2751, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2752, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2756, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2758, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2759, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x275A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x275B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x275C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x275D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x275E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2761, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2762, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2763, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2764, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2765, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2766, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2767, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2776, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2777, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2778, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2779, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x277A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x277B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x277C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x277D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x277E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x277F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2780, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2781, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2782, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2783, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2784, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2785, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2786, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2787, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2788, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2789, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x278A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x278B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x278C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x278D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x278E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x278F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2790, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2791, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2792, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2793, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2794, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2798, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x2799, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x279A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x279B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x279C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x279D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x279E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x279F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27A0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27A1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27A2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27A3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27A4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27A5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27A6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27A7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27A8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27A9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27AA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27AB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27AC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27AD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27AE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27AF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27B1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27B2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27B3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27B4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27B5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27B6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27B7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27B8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27B9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27BA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27BB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27BC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27BD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x27BE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3000, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3001, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3002, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3003, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3004, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3005, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3006, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3007, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3008, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3009, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x300A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x300B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x300C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x300D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x300E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x300F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3010, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3011, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3012, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3013, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3014, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3015, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3016, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3017, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3018, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3019, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x301A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x301B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x301C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x301D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x301E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x301F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3020, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3021, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3022, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3023, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3024, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3025, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3026, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3027, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3028, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3029, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x302A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x302B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x302C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x302D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x302E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x302F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3030, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3031, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3032, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3033, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3034, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3035, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3036, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3037, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x303F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3041, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3042, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3043, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3044, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3045, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3046, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3047, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3048, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3049, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x304A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x304B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x304C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x304D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x304E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x304F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3050, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3051, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3052, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3053, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3054, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3055, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3056, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3057, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3058, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3059, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x305A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x305B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x305C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x305D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x305E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x305F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3060, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3061, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3062, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3063, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3064, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3065, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3066, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3067, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3068, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3069, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x306A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x306B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x306C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x306D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x306E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x306F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3070, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3071, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3072, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3073, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3074, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3075, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3076, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3077, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3078, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3079, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x307A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x307B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x307C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x307D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x307E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x307F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3080, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3081, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3082, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3083, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3084, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3085, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3086, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3087, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3088, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3089, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x308A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x308B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x308C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x308D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x308E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x308F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3090, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3091, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3092, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3093, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3094, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3099, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x309A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x309B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x309C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x309D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x309E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30A1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30A2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30A3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30A4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30A5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30A6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30A7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30A8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30A9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30AA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30AB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30AC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30AD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30AE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30AF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30B0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30B1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30B2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30B3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30B4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30B5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30B6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30B7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30B8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30B9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30BA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30BB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30BC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30BD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30BE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30BF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30C0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30C1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30C2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30C3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30C4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30C5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30C6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30C7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30C8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30C9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30CA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30CB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30CC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30CD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30CE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30CF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30D0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30D1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30D2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30D3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30D4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30D5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30D6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30D7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30D8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30D9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30DA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30DB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30DC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30DD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30DE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30DF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30E0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30E1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30E2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30E3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30E4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30E5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30E6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30E7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30E8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30E9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30EA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30EB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30EC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30ED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30EE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30EF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30F0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30F1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30F2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30F3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30F4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30F5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30F6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30F7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30F8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30F9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30FA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30FB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30FC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30FD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x30FE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3105, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3106, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3107, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3108, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3109, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x310A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x310B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x310C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x310D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x310E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x310F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3110, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3111, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3112, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3113, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3114, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3115, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3116, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3117, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3118, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3119, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x311A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x311B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x311C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x311D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x311E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x311F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3120, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3121, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3122, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3123, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3124, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3125, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3126, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3127, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3128, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3129, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x312A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x312B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x312C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3131, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3132, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3133, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3134, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3135, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3136, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3137, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3138, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3139, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x313A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x313B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x313C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x313D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x313E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x313F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3140, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3141, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3142, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3143, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3144, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3145, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3146, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3147, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3148, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3149, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x314A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x314B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x314C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x314D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x314E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x314F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3150, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3151, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3152, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3153, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3154, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3155, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3156, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3157, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3158, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3159, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x315A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x315B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x315C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x315D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x315E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x315F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3160, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3161, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3162, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3163, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3164, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3165, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3166, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3167, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3168, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3169, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x316A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x316B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x316C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x316D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x316E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x316F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3170, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3171, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3172, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3173, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3174, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3175, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3176, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3177, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3178, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3179, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x317A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x317B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x317C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x317D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x317E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x317F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3180, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3181, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3182, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3183, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3184, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3185, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3186, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3187, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3188, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3189, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x318A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x318B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x318C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x318D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x318E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3190, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3191, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3192, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3193, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3194, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3195, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3196, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3197, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3198, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3199, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x319A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x319B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x319C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x319D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x319E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x319F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3200, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3201, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3202, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3203, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3204, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3205, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3206, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3207, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3208, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3209, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x320A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x320B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x320C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x320D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x320E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x320F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3210, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3211, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3212, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3213, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3214, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3215, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3216, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3217, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3218, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3219, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x321A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x321B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x321C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3220, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3221, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3222, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3223, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3224, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3225, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3226, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3227, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3228, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3229, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x322A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x322B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x322C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x322D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x322E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x322F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3230, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3231, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3232, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3233, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3234, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3235, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3236, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3237, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3238, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3239, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x323A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x323B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x323C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x323D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x323E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x323F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3240, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3241, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3242, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3243, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3260, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3261, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3262, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3263, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3264, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3265, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3266, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3267, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3268, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3269, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x326A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x326B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x326C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x326D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x326E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x326F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3270, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3271, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3272, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3273, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3274, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3275, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3276, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3277, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3278, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3279, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x327A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x327B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x327F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3280, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3281, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3282, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3283, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3284, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3285, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3286, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3287, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3288, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3289, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x328A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x328B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x328C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x328D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x328E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x328F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3290, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3291, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3292, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3293, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3294, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3295, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3296, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3297, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3298, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3299, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x329A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x329B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x329C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x329D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x329E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x329F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32A0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32A1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32A2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32A3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32A4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32A5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32A6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32A7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32A8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32A9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32AA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32AB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32AC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32AD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32AE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32AF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32B0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32C0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32C1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32C2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32C3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32C4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32C5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32C6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32C7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32C8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32C9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32CA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32CB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32D0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32D1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32D2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32D3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32D4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32D5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32D6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32D7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32D8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32D9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32DA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32DB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32DC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32DD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32DE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32DF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32E0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32E1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32E2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32E3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32E4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32E5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32E6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32E7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32E8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32E9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32EA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32EB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32EC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32ED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32EE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32EF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32F0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32F1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32F2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32F3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32F4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32F5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32F6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32F7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32F8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32F9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32FA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32FB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32FC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32FD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x32FE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3300, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3301, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3302, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3303, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3304, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3305, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3306, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3307, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3308, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3309, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x330A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x330B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x330C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x330D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x330E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x330F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3310, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3311, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3312, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3313, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3314, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3315, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3316, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3317, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3318, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3319, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x331A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x331B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x331C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x331D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x331E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x331F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3320, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3321, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3322, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3323, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3324, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3325, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3326, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3327, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3328, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3329, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x332A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x332B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x332C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x332D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x332E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x332F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3330, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3331, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3332, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3333, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3334, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3335, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3336, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3337, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3338, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3339, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x333A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x333B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x333C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x333D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x333E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x333F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3340, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3341, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3342, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3343, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3344, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3345, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3346, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3347, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3348, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3349, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x334A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x334B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x334C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x334D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x334E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x334F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3350, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3351, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3352, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3353, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3354, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3355, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3356, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3357, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3358, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3359, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x335A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x335B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x335C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x335D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x335E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x335F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3360, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3361, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3362, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3363, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3364, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3365, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3366, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3367, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3368, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3369, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x336A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x336B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x336C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x336D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x336E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x336F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3370, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3371, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3372, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x3373, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x3374, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x3375, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3376, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x337B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x337C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x337D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x337E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x337F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3380, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3381, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3382, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3383, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3384, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3385, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x3386, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x3387, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x3388, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x3389, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x338A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x338B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x338C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x338D, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x338E, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x338F, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x3390, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3391, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3392, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3393, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3394, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3395, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3396, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3397, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3398, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x3399, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x339A, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x339B, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x339C, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x339D, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x339E, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x339F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33A0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33A1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33A2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33A3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33A4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33A5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33A6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33A7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33A8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33A9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33AA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33AB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33AC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33AD, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33AE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33AF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33B0, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33B1, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33B2, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33B3, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33B4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33B5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33B6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33B7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33B8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33B9, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x33BA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33BB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33BC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33BD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33BE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33BF, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x33C0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33C1, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x33C2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33C3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33C4, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33C5, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33C6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33C7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33C8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33C9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33CA, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33CB, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x33CC, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33CD, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x33CE, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x33CF, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33D0, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33D1, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33D2, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33D3, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33D4, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33D5, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33D6, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33D7, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x33D8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33D9, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x33DA, CODEPAGE_ISUPPER, 0xFFFF, 0xFFFF }, + { 0x33DB, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0x33DC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33DD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33E0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33E1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33E2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33E3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33E4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33E5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33E6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33E7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33E8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33E9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33EA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33EB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33EC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33ED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33EE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33EF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33F0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33F1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33F2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33F3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33F4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33F5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33F6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33F7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33F8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33F9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33FA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33FB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33FC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33FD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x33FE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x4E00, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0x9FA5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xAC00, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xD7A3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xD800, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xDB7F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xDB80, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xDBFF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xDC00, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xDFFF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xE000, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xF8FF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xF900, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFA2D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB00, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0xFB01, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0xFB02, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0xFB03, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0xFB04, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0xFB05, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0xFB06, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0xFB13, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0xFB14, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0xFB15, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0xFB16, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0xFB17, CODEPAGE_ISLOWER, 0xFFFF, 0xFFFF }, + { 0xFB1E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB1F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB20, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB21, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB22, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB23, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB24, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB25, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB26, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB27, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB28, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB29, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB2A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB2B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB2C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB2D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB2E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB2F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB30, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB31, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB32, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB33, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB34, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB35, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB36, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB38, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB39, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB3A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB3B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB3C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB3E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB40, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB41, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB43, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB44, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB46, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB47, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB48, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB49, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB4A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB4B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB4C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB4D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB4E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB4F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB50, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB51, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB52, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB53, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB54, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB55, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB56, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB57, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB58, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB59, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB5A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB5B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB5C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB5D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB5E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB5F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB60, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB61, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB62, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB63, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB64, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB65, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB66, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB67, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB68, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB69, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB6A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB6B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB6C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB6D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB6E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB6F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB70, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB71, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB72, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB73, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB74, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB75, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB76, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB77, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB78, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB79, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB7A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB7B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB7C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB7D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB7E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB7F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB80, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB81, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB82, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB83, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB84, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB85, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB86, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB87, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB88, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB89, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB8A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB8B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB8C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB8D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB8E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB8F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB90, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB91, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB92, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB93, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB94, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB95, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB96, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB97, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB98, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB99, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB9A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB9B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB9C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB9D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB9E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFB9F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBA0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBA1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBA2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBA3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBA4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBA5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBA6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBA7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBA8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBA9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBAA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBAB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBAC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBAD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBAE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBAF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBB0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBB1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBD3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBD4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBD5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBD6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBD7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBD8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBD9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBDA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBDB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBDC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBDD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBDE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBDF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBE0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBE1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBE2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBE3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBE4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBE5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBE6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBE7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBE8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBE9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBEA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBEB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBEC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBEE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBEF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBF0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBF1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBF2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBF3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBF4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBF5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBF6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBF7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBF8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBF9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBFA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBFB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBFC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBFD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBFE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFBFF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC00, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC01, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC02, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC03, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC04, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC05, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC06, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC07, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC08, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC09, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC0A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC0B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC0C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC0D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC0E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC0F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC10, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC11, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC12, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC13, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC14, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC15, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC16, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC17, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC18, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC19, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC1A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC1B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC1C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC1D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC1E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC1F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC20, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC21, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC22, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC23, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC24, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC25, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC26, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC27, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC28, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC29, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC2A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC2B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC2C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC2D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC2E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC2F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC30, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC31, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC32, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC33, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC34, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC35, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC36, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC37, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC38, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC39, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC3A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC3B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC3C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC3D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC3E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC3F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC40, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC41, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC42, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC43, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC44, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC45, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC46, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC47, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC48, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC49, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC4A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC4B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC4C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC4D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC4E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC4F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC50, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC51, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC52, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC53, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC54, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC55, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC56, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC57, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC58, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC59, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC5A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC5B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC5C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC5D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC5E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC5F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC60, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC61, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC62, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC63, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC64, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC65, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC66, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC67, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC68, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC69, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC6A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC6B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC6C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC6D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC6E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC6F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC70, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC71, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC72, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC73, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC74, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC75, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC76, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC77, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC78, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC79, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC7A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC7B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC7C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC7D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC7E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC7F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC80, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC81, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC82, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC83, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC84, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC85, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC86, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC87, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC88, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC89, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC8A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC8B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC8C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC8D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC8E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC8F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC90, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC91, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC92, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC93, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC94, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC95, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC96, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC97, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC98, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC99, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC9A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC9B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC9C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC9D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC9E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFC9F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCA0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCA1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCA2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCA3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCA4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCA5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCA6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCA7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCA8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCA9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCAA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCAB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCAC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCAD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCAE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCAF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCB0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCB1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCB2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCB3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCB4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCB5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCB6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCB7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCB8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCB9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCBA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCBB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCBC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCBD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCBE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCBF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCC0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCC1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCC2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCC3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCC4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCC5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCC6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCC7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCC8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCC9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCCA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCCB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCCC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCCD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCCE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCCF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCD0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCD1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCD2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCD3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCD4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCD5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCD6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCD7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCD8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCD9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCDA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCDB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCDC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCDD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCDE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCDF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCE0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCE1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCE2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCE3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCE4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCE5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCE6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCE7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCE8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCE9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCEA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCEB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCEC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCEE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCEF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCF0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCF1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCF2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCF3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCF4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCF5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCF6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCF7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCF8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCF9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCFA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCFB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCFC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCFD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCFE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFCFF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD00, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD01, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD02, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD03, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD04, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD05, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD06, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD07, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD08, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD09, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD0A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD0B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD0C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD0D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD0E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD0F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD10, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD11, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD12, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD13, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD14, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD15, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD16, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD17, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD18, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD19, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD1A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD1B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD1C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD1D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD1E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD1F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD20, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD21, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD22, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD23, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD24, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD25, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD26, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD27, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD28, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD29, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD2A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD2B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD2C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD2D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD2E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD2F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD30, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD31, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD32, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD33, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD34, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD35, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD36, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD37, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD38, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD39, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD3A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD3B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD3C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD3D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD3E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD3F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD50, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD51, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD52, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD53, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD54, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD55, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD56, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD57, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD58, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD59, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD5A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD5B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD5C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD5D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD5E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD5F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD60, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD61, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD62, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD63, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD64, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD65, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD66, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD67, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD68, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD69, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD6A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD6B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD6C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD6D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD6E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD6F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD70, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD71, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD72, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD73, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD74, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD75, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD76, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD77, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD78, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD79, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD7A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD7B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD7C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD7D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD7E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD7F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD80, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD81, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD82, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD83, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD84, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD85, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD86, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD87, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD88, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD89, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD8A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD8B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD8C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD8D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD8E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD8F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD92, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD93, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD94, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD95, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD96, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD97, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD98, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD99, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD9A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD9B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD9C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD9D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD9E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFD9F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDA0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDA1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDA2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDA3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDA4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDA5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDA6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDA7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDA8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDA9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDAA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDAB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDAC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDAD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDAE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDAF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDB0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDB1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDB2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDB3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDB4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDB5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDB6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDB7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDB8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDB9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDBA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDBB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDBC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDBD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDBE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDBF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDC0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDC1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDC2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDC3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDC4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDC5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDC6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDC7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDF0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDF1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDF2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDF3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDF4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDF5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDF6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDF7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDF8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDF9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDFA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFDFB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE20, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE21, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE22, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE23, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE30, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE31, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE32, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE33, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE34, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE35, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE36, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE37, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE38, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE39, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE3A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE3B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE3C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE3D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE3E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE3F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE40, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE41, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE42, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE43, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE44, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE49, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE4A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE4B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE4C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE4D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE4E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE4F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE50, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE51, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE52, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE54, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE55, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE56, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE57, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE58, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE59, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE5A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE5B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE5C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE5D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE5E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE5F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE60, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE61, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE62, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE63, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE64, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE65, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE66, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE68, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE69, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE6A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE6B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE70, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE71, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE72, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE74, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE76, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE77, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE78, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE79, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE7A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE7B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE7C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE7D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE7E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE7F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE80, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE81, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE82, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE83, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE84, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE85, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE86, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE87, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE88, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE89, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE8A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE8B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE8C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE8D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE8E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE8F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE90, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE91, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE92, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE93, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE94, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE95, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE96, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE97, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE98, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE99, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE9A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE9B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE9C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE9D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE9E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFE9F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEA0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEA1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEA2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEA3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEA4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEA5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEA6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEA7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEA8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEA9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEAA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEAB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEAC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEAD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEAE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEAF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEB0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEB1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEB2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEB3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEB4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEB5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEB6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEB7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEB8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEB9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEBA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEBB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEBC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEBD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEBE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEBF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEC0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEC1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEC2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEC3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEC4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEC5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEC6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEC7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEC8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEC9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFECA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFECB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFECC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFECD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFECE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFECF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFED0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFED1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFED2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFED3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFED4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFED5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFED6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFED7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFED8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFED9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEDA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEDB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEDC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEDD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEDE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEDF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEE0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEE1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEE2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEE3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEE4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEE5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEE6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEE7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEE8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEE9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEEA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEEB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEEC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEEE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEEF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEF0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEF1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEF2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEF3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEF4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEF5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEF6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEF7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEF8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEF9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEFA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEFB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEFC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFEFF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF01, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF02, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF03, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF04, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF05, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF06, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF07, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF08, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF09, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF0A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF0B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF0C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF0D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF0E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF0F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF10, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF11, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF12, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF13, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF14, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF15, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF16, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF17, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF18, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF19, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF1A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF1B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF1C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF1D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF1E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF1F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF20, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF21, CODEPAGE_ISUPPER, 0xFF41, 0xFFFF }, + { 0xFF22, CODEPAGE_ISUPPER, 0xFF42, 0xFFFF }, + { 0xFF23, CODEPAGE_ISUPPER, 0xFF43, 0xFFFF }, + { 0xFF24, CODEPAGE_ISUPPER, 0xFF44, 0xFFFF }, + { 0xFF25, CODEPAGE_ISUPPER, 0xFF45, 0xFFFF }, + { 0xFF26, CODEPAGE_ISUPPER, 0xFF46, 0xFFFF }, + { 0xFF27, CODEPAGE_ISUPPER, 0xFF47, 0xFFFF }, + { 0xFF28, CODEPAGE_ISUPPER, 0xFF48, 0xFFFF }, + { 0xFF29, CODEPAGE_ISUPPER, 0xFF49, 0xFFFF }, + { 0xFF2A, CODEPAGE_ISUPPER, 0xFF4A, 0xFFFF }, + { 0xFF2B, CODEPAGE_ISUPPER, 0xFF4B, 0xFFFF }, + { 0xFF2C, CODEPAGE_ISUPPER, 0xFF4C, 0xFFFF }, + { 0xFF2D, CODEPAGE_ISUPPER, 0xFF4D, 0xFFFF }, + { 0xFF2E, CODEPAGE_ISUPPER, 0xFF4E, 0xFFFF }, + { 0xFF2F, CODEPAGE_ISUPPER, 0xFF4F, 0xFFFF }, + { 0xFF30, CODEPAGE_ISUPPER, 0xFF50, 0xFFFF }, + { 0xFF31, CODEPAGE_ISUPPER, 0xFF51, 0xFFFF }, + { 0xFF32, CODEPAGE_ISUPPER, 0xFF52, 0xFFFF }, + { 0xFF33, CODEPAGE_ISUPPER, 0xFF53, 0xFFFF }, + { 0xFF34, CODEPAGE_ISUPPER, 0xFF54, 0xFFFF }, + { 0xFF35, CODEPAGE_ISUPPER, 0xFF55, 0xFFFF }, + { 0xFF36, CODEPAGE_ISUPPER, 0xFF56, 0xFFFF }, + { 0xFF37, CODEPAGE_ISUPPER, 0xFF57, 0xFFFF }, + { 0xFF38, CODEPAGE_ISUPPER, 0xFF58, 0xFFFF }, + { 0xFF39, CODEPAGE_ISUPPER, 0xFF59, 0xFFFF }, + { 0xFF3A, CODEPAGE_ISUPPER, 0xFF5A, 0xFFFF }, + { 0xFF3B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF3C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF3D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF3E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF3F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF40, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF41, CODEPAGE_ISLOWER, 0xFFFF, 0xFF21 }, + { 0xFF42, CODEPAGE_ISLOWER, 0xFFFF, 0xFF22 }, + { 0xFF43, CODEPAGE_ISLOWER, 0xFFFF, 0xFF23 }, + { 0xFF44, CODEPAGE_ISLOWER, 0xFFFF, 0xFF24 }, + { 0xFF45, CODEPAGE_ISLOWER, 0xFFFF, 0xFF25 }, + { 0xFF46, CODEPAGE_ISLOWER, 0xFFFF, 0xFF26 }, + { 0xFF47, CODEPAGE_ISLOWER, 0xFFFF, 0xFF27 }, + { 0xFF48, CODEPAGE_ISLOWER, 0xFFFF, 0xFF28 }, + { 0xFF49, CODEPAGE_ISLOWER, 0xFFFF, 0xFF29 }, + { 0xFF4A, CODEPAGE_ISLOWER, 0xFFFF, 0xFF2A }, + { 0xFF4B, CODEPAGE_ISLOWER, 0xFFFF, 0xFF2B }, + { 0xFF4C, CODEPAGE_ISLOWER, 0xFFFF, 0xFF2C }, + { 0xFF4D, CODEPAGE_ISLOWER, 0xFFFF, 0xFF2D }, + { 0xFF4E, CODEPAGE_ISLOWER, 0xFFFF, 0xFF2E }, + { 0xFF4F, CODEPAGE_ISLOWER, 0xFFFF, 0xFF2F }, + { 0xFF50, CODEPAGE_ISLOWER, 0xFFFF, 0xFF30 }, + { 0xFF51, CODEPAGE_ISLOWER, 0xFFFF, 0xFF31 }, + { 0xFF52, CODEPAGE_ISLOWER, 0xFFFF, 0xFF32 }, + { 0xFF53, CODEPAGE_ISLOWER, 0xFFFF, 0xFF33 }, + { 0xFF54, CODEPAGE_ISLOWER, 0xFFFF, 0xFF34 }, + { 0xFF55, CODEPAGE_ISLOWER, 0xFFFF, 0xFF35 }, + { 0xFF56, CODEPAGE_ISLOWER, 0xFFFF, 0xFF36 }, + { 0xFF57, CODEPAGE_ISLOWER, 0xFFFF, 0xFF37 }, + { 0xFF58, CODEPAGE_ISLOWER, 0xFFFF, 0xFF38 }, + { 0xFF59, CODEPAGE_ISLOWER, 0xFFFF, 0xFF39 }, + { 0xFF5A, CODEPAGE_ISLOWER, 0xFFFF, 0xFF3A }, + { 0xFF5B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF5C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF5D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF5E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF61, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF62, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF63, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF64, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF65, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF66, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF67, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF68, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF69, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF6A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF6B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF6C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF6D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF6E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF6F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF70, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF71, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF72, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF73, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF74, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF75, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF76, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF77, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF78, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF79, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF7A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF7B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF7C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF7D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF7E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF7F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF80, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF81, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF82, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF83, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF84, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF85, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF86, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF87, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF88, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF89, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF8A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF8B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF8C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF8D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF8E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF8F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF90, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF91, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF92, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF93, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF94, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF95, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF96, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF97, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF98, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF99, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF9A, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF9B, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF9C, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF9D, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF9E, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFF9F, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFA0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFA1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFA2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFA3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFA4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFA5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFA6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFA7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFA8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFA9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFAA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFAB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFAC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFAD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFAE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFAF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFB0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFB1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFB2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFB3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFB4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFB5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFB6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFB7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFB8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFB9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFBA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFBB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFBC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFBD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFBE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFC2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFC3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFC4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFC5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFC6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFC7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFCA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFCB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFCC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFCD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFCE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFCF, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFD2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFD3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFD4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFD5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFD6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFD7, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFDA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFDB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFDC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFE0, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFE1, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFE2, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFE3, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFE4, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFE5, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFE6, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFE8, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFE9, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFEA, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFEB, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFEC, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFED, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFEE, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF }, + { 0xFFFD, CODEPAGE_ISNONE, 0xFFFF, 0xFFFF } +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_CP_UNICODE_H */ diff --git a/usr/src/uts/common/smbsrv/cp_usascii.h b/usr/src/uts/common/smbsrv/cp_usascii.h new file mode 100644 index 000000000000..d72c28bb88f1 --- /dev/null +++ b/usr/src/uts/common/smbsrv/cp_usascii.h @@ -0,0 +1,310 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_CP_USASCII_H +#define _SMBSRV_CP_USASCII_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file specifies a codepage mapping for a given character set as + * specified below: + * + * This is the codepage for the US-ASCII Character Set + * This codepage defines values for the characters of the + * written alphabet of the English language. The US-ASCII + * character set is used in the USA. It is a proper + * subset of the Latin-1 character set. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +codepage_t usascii_codepage[256] = { + { CODEPAGE_ISNONE, 0x0000, 0x0000 }, /* 0x0000 */ + { CODEPAGE_ISNONE, 0x0001, 0x0001 }, /* 0x0001 */ + { CODEPAGE_ISNONE, 0x0002, 0x0002 }, /* 0x0002 */ + { CODEPAGE_ISNONE, 0x0003, 0x0003 }, /* 0x0003 */ + { CODEPAGE_ISNONE, 0x0004, 0x0004 }, /* 0x0004 */ + { CODEPAGE_ISNONE, 0x0005, 0x0005 }, /* 0x0005 */ + { CODEPAGE_ISNONE, 0x0006, 0x0006 }, /* 0x0006 */ + { CODEPAGE_ISNONE, 0x0007, 0x0007 }, /* 0x0007 */ + { CODEPAGE_ISNONE, 0x0008, 0x0008 }, /* 0x0008 */ + { CODEPAGE_ISNONE, 0x0009, 0x0009 }, /* 0x0009 */ + { CODEPAGE_ISNONE, 0x000a, 0x000a }, /* 0x000a */ + { CODEPAGE_ISNONE, 0x000b, 0x000b }, /* 0x000b */ + { CODEPAGE_ISNONE, 0x000c, 0x000c }, /* 0x000c */ + { CODEPAGE_ISNONE, 0x000d, 0x000d }, /* 0x000d */ + { CODEPAGE_ISNONE, 0x000e, 0x000e }, /* 0x000e */ + { CODEPAGE_ISNONE, 0x000f, 0x000f }, /* 0x000f */ + { CODEPAGE_ISNONE, 0x0010, 0x0010 }, /* 0x0010 */ + { CODEPAGE_ISNONE, 0x0011, 0x0011 }, /* 0x0011 */ + { CODEPAGE_ISNONE, 0x0012, 0x0012 }, /* 0x0012 */ + { CODEPAGE_ISNONE, 0x0013, 0x0013 }, /* 0x0013 */ + { CODEPAGE_ISNONE, 0x0014, 0x0014 }, /* 0x0014 */ + { CODEPAGE_ISNONE, 0x0015, 0x0015 }, /* 0x0015 */ + { CODEPAGE_ISNONE, 0x0016, 0x0016 }, /* 0x0016 */ + { CODEPAGE_ISNONE, 0x0017, 0x0017 }, /* 0x0017 */ + { CODEPAGE_ISNONE, 0x0018, 0x0018 }, /* 0x0018 */ + { CODEPAGE_ISNONE, 0x0019, 0x0019 }, /* 0x0019 */ + { CODEPAGE_ISNONE, 0x001a, 0x001a }, /* 0x001a */ + { CODEPAGE_ISNONE, 0x001b, 0x001b }, /* 0x001b */ + { CODEPAGE_ISNONE, 0x001c, 0x001c }, /* 0x001c */ + { CODEPAGE_ISNONE, 0x001d, 0x001d }, /* 0x001d */ + { CODEPAGE_ISNONE, 0x001e, 0x001e }, /* 0x001e */ + { CODEPAGE_ISNONE, 0x001f, 0x001f }, /* 0x001f */ + { CODEPAGE_ISNONE, 0x0020, 0x0020 }, /* 0x0020 */ + { CODEPAGE_ISNONE, 0x0021, 0x0021 }, /* 0x0021 */ + { CODEPAGE_ISNONE, 0x0022, 0x0022 }, /* 0x0022 */ + { CODEPAGE_ISNONE, 0x0023, 0x0023 }, /* 0x0023 */ + { CODEPAGE_ISNONE, 0x0024, 0x0024 }, /* 0x0024 */ + { CODEPAGE_ISNONE, 0x0025, 0x0025 }, /* 0x0025 */ + { CODEPAGE_ISNONE, 0x0026, 0x0026 }, /* 0x0026 */ + { CODEPAGE_ISNONE, 0x0027, 0x0027 }, /* 0x0027 */ + { CODEPAGE_ISNONE, 0x0028, 0x0028 }, /* 0x0028 */ + { CODEPAGE_ISNONE, 0x0029, 0x0029 }, /* 0x0029 */ + { CODEPAGE_ISNONE, 0x002a, 0x002a }, /* 0x002a */ + { CODEPAGE_ISNONE, 0x002b, 0x002b }, /* 0x002b */ + { CODEPAGE_ISNONE, 0x002c, 0x002c }, /* 0x002c */ + { CODEPAGE_ISNONE, 0x002d, 0x002d }, /* 0x002d */ + { CODEPAGE_ISNONE, 0x002e, 0x002e }, /* 0x002e */ + { CODEPAGE_ISNONE, 0x002f, 0x002f }, /* 0x002f */ + { CODEPAGE_ISNONE, 0x0030, 0x0030 }, /* 0x0030 */ + { CODEPAGE_ISNONE, 0x0031, 0x0031 }, /* 0x0031 */ + { CODEPAGE_ISNONE, 0x0032, 0x0032 }, /* 0x0032 */ + { CODEPAGE_ISNONE, 0x0033, 0x0033 }, /* 0x0033 */ + { CODEPAGE_ISNONE, 0x0034, 0x0034 }, /* 0x0034 */ + { CODEPAGE_ISNONE, 0x0035, 0x0035 }, /* 0x0035 */ + { CODEPAGE_ISNONE, 0x0036, 0x0036 }, /* 0x0036 */ + { CODEPAGE_ISNONE, 0x0037, 0x0037 }, /* 0x0037 */ + { CODEPAGE_ISNONE, 0x0038, 0x0038 }, /* 0x0038 */ + { CODEPAGE_ISNONE, 0x0039, 0x0039 }, /* 0x0039 */ + { CODEPAGE_ISNONE, 0x003a, 0x003a }, /* 0x003a */ + { CODEPAGE_ISNONE, 0x003b, 0x003b }, /* 0x003b */ + { CODEPAGE_ISNONE, 0x003c, 0x003c }, /* 0x003c */ + { CODEPAGE_ISNONE, 0x003d, 0x003d }, /* 0x003d */ + { CODEPAGE_ISNONE, 0x003e, 0x003e }, /* 0x003e */ + { CODEPAGE_ISNONE, 0x003f, 0x003f }, /* 0x003f */ + { CODEPAGE_ISNONE, 0x0040, 0x0040 }, /* 0x0040 */ + { CODEPAGE_ISUPPER, 0x0041, 0x0061 }, /* 0x0041 */ + { CODEPAGE_ISUPPER, 0x0042, 0x0062 }, /* 0x0042 */ + { CODEPAGE_ISUPPER, 0x0043, 0x0063 }, /* 0x0043 */ + { CODEPAGE_ISUPPER, 0x0044, 0x0064 }, /* 0x0044 */ + { CODEPAGE_ISUPPER, 0x0045, 0x0065 }, /* 0x0045 */ + { CODEPAGE_ISUPPER, 0x0046, 0x0066 }, /* 0x0046 */ + { CODEPAGE_ISUPPER, 0x0047, 0x0067 }, /* 0x0047 */ + { CODEPAGE_ISUPPER, 0x0048, 0x0068 }, /* 0x0048 */ + { CODEPAGE_ISUPPER, 0x0049, 0x0069 }, /* 0x0049 */ + { CODEPAGE_ISUPPER, 0x004a, 0x006a }, /* 0x004a */ + { CODEPAGE_ISUPPER, 0x004b, 0x006b }, /* 0x004b */ + { CODEPAGE_ISUPPER, 0x004c, 0x006c }, /* 0x004c */ + { CODEPAGE_ISUPPER, 0x004d, 0x006d }, /* 0x004d */ + { CODEPAGE_ISUPPER, 0x004e, 0x006e }, /* 0x004e */ + { CODEPAGE_ISUPPER, 0x004f, 0x006f }, /* 0x004f */ + { CODEPAGE_ISUPPER, 0x0050, 0x0070 }, /* 0x0050 */ + { CODEPAGE_ISUPPER, 0x0051, 0x0071 }, /* 0x0051 */ + { CODEPAGE_ISUPPER, 0x0052, 0x0072 }, /* 0x0052 */ + { CODEPAGE_ISUPPER, 0x0053, 0x0073 }, /* 0x0053 */ + { CODEPAGE_ISUPPER, 0x0054, 0x0074 }, /* 0x0054 */ + { CODEPAGE_ISUPPER, 0x0055, 0x0075 }, /* 0x0055 */ + { CODEPAGE_ISUPPER, 0x0056, 0x0076 }, /* 0x0056 */ + { CODEPAGE_ISUPPER, 0x0057, 0x0077 }, /* 0x0057 */ + { CODEPAGE_ISUPPER, 0x0058, 0x0078 }, /* 0x0058 */ + { CODEPAGE_ISUPPER, 0x0059, 0x0079 }, /* 0x0059 */ + { CODEPAGE_ISUPPER, 0x005a, 0x007a }, /* 0x005a */ + { CODEPAGE_ISNONE, 0x005b, 0x005b }, /* 0x005b */ + { CODEPAGE_ISNONE, 0x005c, 0x005c }, /* 0x005c */ + { CODEPAGE_ISNONE, 0x005d, 0x005d }, /* 0x005d */ + { CODEPAGE_ISNONE, 0x005e, 0x005e }, /* 0x005e */ + { CODEPAGE_ISNONE, 0x005f, 0x005f }, /* 0x005f */ + { CODEPAGE_ISNONE, 0x0060, 0x0060 }, /* 0x0060 */ + { CODEPAGE_ISLOWER, 0x0041, 0x0061 }, /* 0x0061 */ + { CODEPAGE_ISLOWER, 0x0042, 0x0062 }, /* 0x0062 */ + { CODEPAGE_ISLOWER, 0x0043, 0x0063 }, /* 0x0063 */ + { CODEPAGE_ISLOWER, 0x0044, 0x0064 }, /* 0x0064 */ + { CODEPAGE_ISLOWER, 0x0045, 0x0065 }, /* 0x0065 */ + { CODEPAGE_ISLOWER, 0x0046, 0x0066 }, /* 0x0066 */ + { CODEPAGE_ISLOWER, 0x0047, 0x0067 }, /* 0x0067 */ + { CODEPAGE_ISLOWER, 0x0048, 0x0068 }, /* 0x0068 */ + { CODEPAGE_ISLOWER, 0x0049, 0x0069 }, /* 0x0069 */ + { CODEPAGE_ISLOWER, 0x004a, 0x006a }, /* 0x006a */ + { CODEPAGE_ISLOWER, 0x004b, 0x006b }, /* 0x006b */ + { CODEPAGE_ISLOWER, 0x004c, 0x006c }, /* 0x006c */ + { CODEPAGE_ISLOWER, 0x004d, 0x006d }, /* 0x006d */ + { CODEPAGE_ISLOWER, 0x004e, 0x006e }, /* 0x006e */ + { CODEPAGE_ISLOWER, 0x004f, 0x006f }, /* 0x006f */ + { CODEPAGE_ISLOWER, 0x0050, 0x0070 }, /* 0x0070 */ + { CODEPAGE_ISLOWER, 0x0051, 0x0071 }, /* 0x0071 */ + { CODEPAGE_ISLOWER, 0x0052, 0x0072 }, /* 0x0072 */ + { CODEPAGE_ISLOWER, 0x0053, 0x0073 }, /* 0x0073 */ + { CODEPAGE_ISLOWER, 0x0054, 0x0074 }, /* 0x0074 */ + { CODEPAGE_ISLOWER, 0x0055, 0x0075 }, /* 0x0075 */ + { CODEPAGE_ISLOWER, 0x0056, 0x0076 }, /* 0x0076 */ + { CODEPAGE_ISLOWER, 0x0057, 0x0077 }, /* 0x0077 */ + { CODEPAGE_ISLOWER, 0x0058, 0x0078 }, /* 0x0078 */ + { CODEPAGE_ISLOWER, 0x0059, 0x0079 }, /* 0x0079 */ + { CODEPAGE_ISLOWER, 0x005a, 0x007a }, /* 0x007a */ + { CODEPAGE_ISNONE, 0x007b, 0x007b }, /* 0x007b */ + { CODEPAGE_ISNONE, 0x007c, 0x007c }, /* 0x007c */ + { CODEPAGE_ISNONE, 0x007d, 0x007d }, /* 0x007d */ + { CODEPAGE_ISNONE, 0x007e, 0x007e }, /* 0x007e */ + { CODEPAGE_ISNONE, 0x007f, 0x007f }, /* 0x007f */ + { CODEPAGE_ISNONE, 0x0080, 0x0080 }, /* 0x0080 */ + { CODEPAGE_ISNONE, 0x0081, 0x0081 }, /* 0x0081 */ + { CODEPAGE_ISNONE, 0x0082, 0x0082 }, /* 0x0082 */ + { CODEPAGE_ISNONE, 0x0083, 0x0083 }, /* 0x0083 */ + { CODEPAGE_ISNONE, 0x0084, 0x0084 }, /* 0x0084 */ + { CODEPAGE_ISNONE, 0x0085, 0x0085 }, /* 0x0085 */ + { CODEPAGE_ISNONE, 0x0086, 0x0086 }, /* 0x0086 */ + { CODEPAGE_ISNONE, 0x0087, 0x0087 }, /* 0x0087 */ + { CODEPAGE_ISNONE, 0x0088, 0x0088 }, /* 0x0088 */ + { CODEPAGE_ISNONE, 0x0089, 0x0089 }, /* 0x0089 */ + { CODEPAGE_ISNONE, 0x008a, 0x008a }, /* 0x008a */ + { CODEPAGE_ISNONE, 0x008b, 0x008b }, /* 0x008b */ + { CODEPAGE_ISNONE, 0x008c, 0x008c }, /* 0x008c */ + { CODEPAGE_ISNONE, 0x008d, 0x008d }, /* 0x008d */ + { CODEPAGE_ISNONE, 0x008e, 0x008e }, /* 0x008e */ + { CODEPAGE_ISNONE, 0x008f, 0x008f }, /* 0x008f */ + { CODEPAGE_ISNONE, 0x0090, 0x0090 }, /* 0x0090 */ + { CODEPAGE_ISNONE, 0x0091, 0x0091 }, /* 0x0091 */ + { CODEPAGE_ISNONE, 0x0092, 0x0092 }, /* 0x0092 */ + { CODEPAGE_ISNONE, 0x0093, 0x0093 }, /* 0x0093 */ + { CODEPAGE_ISNONE, 0x0094, 0x0094 }, /* 0x0094 */ + { CODEPAGE_ISNONE, 0x0095, 0x0095 }, /* 0x0095 */ + { CODEPAGE_ISNONE, 0x0096, 0x0096 }, /* 0x0096 */ + { CODEPAGE_ISNONE, 0x0097, 0x0097 }, /* 0x0097 */ + { CODEPAGE_ISNONE, 0x0098, 0x0098 }, /* 0x0098 */ + { CODEPAGE_ISNONE, 0x0099, 0x0099 }, /* 0x0099 */ + { CODEPAGE_ISNONE, 0x009a, 0x009a }, /* 0x009a */ + { CODEPAGE_ISNONE, 0x009b, 0x009b }, /* 0x009b */ + { CODEPAGE_ISNONE, 0x009c, 0x009c }, /* 0x009c */ + { CODEPAGE_ISNONE, 0x009d, 0x009d }, /* 0x009d */ + { CODEPAGE_ISNONE, 0x009e, 0x009e }, /* 0x009e */ + { CODEPAGE_ISNONE, 0x009f, 0x009f }, /* 0x009f */ + { CODEPAGE_ISNONE, 0x00a0, 0x00a0 }, /* 0x00a0 */ + { CODEPAGE_ISNONE, 0x00a1, 0x00a1 }, /* 0x00a1 */ + { CODEPAGE_ISNONE, 0x00a2, 0x00a2 }, /* 0x00a2 */ + { CODEPAGE_ISNONE, 0x00a3, 0x00a3 }, /* 0x00a3 */ + { CODEPAGE_ISNONE, 0x00a4, 0x00a4 }, /* 0x00a4 */ + { CODEPAGE_ISNONE, 0x00a5, 0x00a5 }, /* 0x00a5 */ + { CODEPAGE_ISNONE, 0x00a6, 0x00a6 }, /* 0x00a6 */ + { CODEPAGE_ISNONE, 0x00a7, 0x00a7 }, /* 0x00a7 */ + { CODEPAGE_ISNONE, 0x00a8, 0x00a8 }, /* 0x00a8 */ + { CODEPAGE_ISNONE, 0x00a9, 0x00a9 }, /* 0x00a9 */ + { CODEPAGE_ISNONE, 0x00aa, 0x00aa }, /* 0x00aa */ + { CODEPAGE_ISNONE, 0x00ab, 0x00ab }, /* 0x00ab */ + { CODEPAGE_ISNONE, 0x00ac, 0x00ac }, /* 0x00ac */ + { CODEPAGE_ISNONE, 0x00ad, 0x00ad }, /* 0x00ad */ + { CODEPAGE_ISNONE, 0x00ae, 0x00ae }, /* 0x00ae */ + { CODEPAGE_ISNONE, 0x00af, 0x00af }, /* 0x00af */ + { CODEPAGE_ISNONE, 0x00b0, 0x00b0 }, /* 0x00b0 */ + { CODEPAGE_ISNONE, 0x00b1, 0x00b1 }, /* 0x00b1 */ + { CODEPAGE_ISNONE, 0x00b2, 0x00b2 }, /* 0x00b2 */ + { CODEPAGE_ISNONE, 0x00b3, 0x00b3 }, /* 0x00b3 */ + { CODEPAGE_ISNONE, 0x00b4, 0x00b4 }, /* 0x00b4 */ + { CODEPAGE_ISNONE, 0x00b5, 0x00b5 }, /* 0x00b5 */ + { CODEPAGE_ISNONE, 0x00b6, 0x00b6 }, /* 0x00b6 */ + { CODEPAGE_ISNONE, 0x00b7, 0x00b7 }, /* 0x00b7 */ + { CODEPAGE_ISNONE, 0x00b8, 0x00b8 }, /* 0x00b8 */ + { CODEPAGE_ISNONE, 0x00b9, 0x00b9 }, /* 0x00b9 */ + { CODEPAGE_ISNONE, 0x00ba, 0x00ba }, /* 0x00ba */ + { CODEPAGE_ISNONE, 0x00bb, 0x00bb }, /* 0x00bb */ + { CODEPAGE_ISNONE, 0x00bc, 0x00bc }, /* 0x00bc */ + { CODEPAGE_ISNONE, 0x00bd, 0x00bd }, /* 0x00bd */ + { CODEPAGE_ISNONE, 0x00be, 0x00be }, /* 0x00be */ + { CODEPAGE_ISNONE, 0x00bf, 0x00bf }, /* 0x00bf */ + { CODEPAGE_ISNONE, 0x00c0, 0x00c0 }, /* 0x00c0 */ + { CODEPAGE_ISNONE, 0x00c1, 0x00c1 }, /* 0x00c1 */ + { CODEPAGE_ISNONE, 0x00c2, 0x00c2 }, /* 0x00c2 */ + { CODEPAGE_ISNONE, 0x00c3, 0x00c3 }, /* 0x00c3 */ + { CODEPAGE_ISNONE, 0x00c4, 0x00c4 }, /* 0x00c4 */ + { CODEPAGE_ISNONE, 0x00c5, 0x00c5 }, /* 0x00c5 */ + { CODEPAGE_ISNONE, 0x00c6, 0x00c6 }, /* 0x00c6 */ + { CODEPAGE_ISNONE, 0x00c7, 0x00c7 }, /* 0x00c7 */ + { CODEPAGE_ISNONE, 0x00c8, 0x00c8 }, /* 0x00c8 */ + { CODEPAGE_ISNONE, 0x00c9, 0x00c9 }, /* 0x00c9 */ + { CODEPAGE_ISNONE, 0x00ca, 0x00ca }, /* 0x00ca */ + { CODEPAGE_ISNONE, 0x00cb, 0x00cb }, /* 0x00cb */ + { CODEPAGE_ISNONE, 0x00cc, 0x00cc }, /* 0x00cc */ + { CODEPAGE_ISNONE, 0x00cd, 0x00cd }, /* 0x00cd */ + { CODEPAGE_ISNONE, 0x00ce, 0x00ce }, /* 0x00ce */ + { CODEPAGE_ISNONE, 0x00cf, 0x00cf }, /* 0x00cf */ + { CODEPAGE_ISNONE, 0x00d0, 0x00d0 }, /* 0x00d0 */ + { CODEPAGE_ISNONE, 0x00d1, 0x00d1 }, /* 0x00d1 */ + { CODEPAGE_ISNONE, 0x00d2, 0x00d2 }, /* 0x00d2 */ + { CODEPAGE_ISNONE, 0x00d3, 0x00d3 }, /* 0x00d3 */ + { CODEPAGE_ISNONE, 0x00d4, 0x00d4 }, /* 0x00d4 */ + { CODEPAGE_ISNONE, 0x00d5, 0x00d5 }, /* 0x00d5 */ + { CODEPAGE_ISNONE, 0x00d6, 0x00d6 }, /* 0x00d6 */ + { CODEPAGE_ISNONE, 0x00d7, 0x00d7 }, /* 0x00d7 */ + { CODEPAGE_ISNONE, 0x00d8, 0x00d8 }, /* 0x00d8 */ + { CODEPAGE_ISNONE, 0x00d9, 0x00d9 }, /* 0x00d9 */ + { CODEPAGE_ISNONE, 0x00da, 0x00da }, /* 0x00da */ + { CODEPAGE_ISNONE, 0x00db, 0x00db }, /* 0x00db */ + { CODEPAGE_ISNONE, 0x00dc, 0x00dc }, /* 0x00dc */ + { CODEPAGE_ISNONE, 0x00dd, 0x00dd }, /* 0x00dd */ + { CODEPAGE_ISNONE, 0x00de, 0x00de }, /* 0x00de */ + { CODEPAGE_ISNONE, 0x00df, 0x00df }, /* 0x00df */ + { CODEPAGE_ISNONE, 0x00e0, 0x00e0 }, /* 0x00e0 */ + { CODEPAGE_ISNONE, 0x00e1, 0x00e1 }, /* 0x00e1 */ + { CODEPAGE_ISNONE, 0x00e2, 0x00e2 }, /* 0x00e2 */ + { CODEPAGE_ISNONE, 0x00e3, 0x00e3 }, /* 0x00e3 */ + { CODEPAGE_ISNONE, 0x00e4, 0x00e4 }, /* 0x00e4 */ + { CODEPAGE_ISNONE, 0x00e5, 0x00e5 }, /* 0x00e5 */ + { CODEPAGE_ISNONE, 0x00e6, 0x00e6 }, /* 0x00e6 */ + { CODEPAGE_ISNONE, 0x00e7, 0x00e7 }, /* 0x00e7 */ + { CODEPAGE_ISNONE, 0x00e8, 0x00e8 }, /* 0x00e8 */ + { CODEPAGE_ISNONE, 0x00e9, 0x00e9 }, /* 0x00e9 */ + { CODEPAGE_ISNONE, 0x00ea, 0x00ea }, /* 0x00ea */ + { CODEPAGE_ISNONE, 0x00eb, 0x00eb }, /* 0x00eb */ + { CODEPAGE_ISNONE, 0x00ec, 0x00ec }, /* 0x00ec */ + { CODEPAGE_ISNONE, 0x00ed, 0x00ed }, /* 0x00ed */ + { CODEPAGE_ISNONE, 0x00ee, 0x00ee }, /* 0x00ee */ + { CODEPAGE_ISNONE, 0x00ef, 0x00ef }, /* 0x00ef */ + { CODEPAGE_ISNONE, 0x00f0, 0x00f0 }, /* 0x00f0 */ + { CODEPAGE_ISNONE, 0x00f1, 0x00f1 }, /* 0x00f1 */ + { CODEPAGE_ISNONE, 0x00f2, 0x00f2 }, /* 0x00f2 */ + { CODEPAGE_ISNONE, 0x00f3, 0x00f3 }, /* 0x00f3 */ + { CODEPAGE_ISNONE, 0x00f4, 0x00f4 }, /* 0x00f4 */ + { CODEPAGE_ISNONE, 0x00f5, 0x00f5 }, /* 0x00f5 */ + { CODEPAGE_ISNONE, 0x00f6, 0x00f6 }, /* 0x00f6 */ + { CODEPAGE_ISNONE, 0x00f7, 0x00f7 }, /* 0x00f7 */ + { CODEPAGE_ISNONE, 0x00f8, 0x00f8 }, /* 0x00f8 */ + { CODEPAGE_ISNONE, 0x00f9, 0x00f9 }, /* 0x00f9 */ + { CODEPAGE_ISNONE, 0x00fa, 0x00fa }, /* 0x00fa */ + { CODEPAGE_ISNONE, 0x00fb, 0x00fb }, /* 0x00fb */ + { CODEPAGE_ISNONE, 0x00fc, 0x00fc }, /* 0x00fc */ + { CODEPAGE_ISNONE, 0x00fd, 0x00fd }, /* 0x00fd */ + { CODEPAGE_ISNONE, 0x00fe, 0x00fe }, /* 0x00fe */ + { CODEPAGE_ISNONE, 0x00ff, 0x00ff } }; /* 0x00ff */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_CP_USASCII_H */ diff --git a/usr/src/uts/common/smbsrv/crypt.h b/usr/src/uts/common/smbsrv/crypt.h new file mode 100644 index 000000000000..b2765a2f8e44 --- /dev/null +++ b/usr/src/uts/common/smbsrv/crypt.h @@ -0,0 +1,45 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_CRYPT_H +#define _SMBSRV_CRYPT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PASS_LEN 20 +#define BUF_LEN (2 * PASS_LEN) + +extern int smb_des_setkey(const char *); +extern int smb_des_cipher(const char *, char *, long, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_CRYPT_H */ diff --git a/usr/src/uts/common/smbsrv/ctype.h b/usr/src/uts/common/smbsrv/ctype.h new file mode 100644 index 000000000000..284db64419cb --- /dev/null +++ b/usr/src/uts/common/smbsrv/ctype.h @@ -0,0 +1,76 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_CTYPE_H +#define _SMBSRV_CTYPE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define _mts_between(l, c, u) ((l) <= (c) && (c) <= (u)) + +/* + * These macros take non-ascii characters into account. + * Their behavior depends on the codepage that is used. + */ +#define mts_islower(c) codepage_islower((c)) +#define mts_isupper(c) codepage_isupper((c)) +#define mts_tolower(c) codepage_tolower((c)) +#define mts_toupper(c) codepage_toupper((c)) + +#define mts_isalpha(c) (mts_islower(c) || mts_isupper(c)) +#define mts_isdigit(c) _mts_between('0', (c), '9') +#define mts_isalnum(c) (mts_isalpha(c) || mts_isdigit(c)) +#define mts_isxdigit(c) (mts_isdigit(c) || \ + _mts_between('a', (c), 'f') || \ + _mts_between('A', (c), 'F')) +#define mts_isblank(c) ((c) == ' ' || (c) == '\t') +#define mts_isspace(c) ((c) == ' ' || \ + (c) == '\t' || \ + (c) == '\n' || \ + (c) == '\r' || \ + (c) == '\f') +#define mts_isascii(c) (!((c) &~ 0x7F)) + +/* These macros only apply to ASCII */ +#define mts_isalpha_ascii(c) \ + (_mts_between('a', (c), 'z') || _mts_between('A', (c), 'Z')) +#define mts_isalnum_ascii(c) (mts_isalpha_ascii(c) || mts_isdigit(c)) + +/* should it include non-ascii characters ? */ +#define mts_isprint(c) _mts_between('!', (c), '~') +#define mts_iscntrl(c) (((c) >= 0) && ((c) <= 0x1f)) || ((c) == 0x7f)) +#define mts_ispunct(c) (mts_isprint(c) && !mts_isxdigit(c) && !mts_isspace(c)) + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_CTYPE_H */ diff --git a/usr/src/uts/common/smbsrv/doserror.h b/usr/src/uts/common/smbsrv/doserror.h new file mode 100644 index 000000000000..a5703e1b492f --- /dev/null +++ b/usr/src/uts/common/smbsrv/doserror.h @@ -0,0 +1,145 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifndef _SMBSRV_DOSERROR_H +#define _SMBSRV_DOSERROR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file defines the list of DOS error codes. I think the error + * codes are divided into different classes, which is why there are + * duplicate values. + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Error source or class + */ +#define SUCCESS 0x00 /* The request was successful. */ +#define ERRDOS 0x01 /* Core DOS operating system error. */ +#define ERRSRV 0x02 /* Server network file error */ +#define ERRHRD 0x03 /* Hardware error */ +#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ + + +/* + * ERRDOS error codes + */ +#define ERRbadfunc 1 /* Invalid function. The server did not */ +#define ERRbadfile 2 /* File not found. The last component of a */ +#define ERRbadpath 3 /* Directory invalid. A directory component in */ +#define ERRnofids 4 /* Too many open files. The server has no file */ +#define ERRnoaccess 5 /* Access denied, the client's context does not */ +#define ERRbadfid 6 /* Invalid file handle. The file handle */ +#define ERRbadmcb 7 /* Memory control blocks destroyed. */ +#define ERRnomem 8 /* Insufficient server memory to perform the */ +#define ERRbadmem 9 /* Invalid memory block address. */ +#define ERRbadenv 10 /* Invalid environment. */ +#define ERRbadformat 11 /* Invalid format. */ +#define ERRbadaccess 12 /* Invalid open mode. */ +#define ERRbaddata 13 /* Invalid data (generated only by IOCTL calls */ +#define ERRbaddrive 15 /* Invalid drive specified. */ +#define ERRremcd 16 /* A Delete Directory request attempted to */ +#define ERRdiffdevice 17 /* Not same device (e.g., a cross volume rename */ +#define ERRnofiles 18 /* A File Search command can find no more files */ +#define ERRbadshare 32 /* The sharing mode specified for an Open */ +#define ERRlock 33 /* A Lock request conflicted with an existing */ +#define ERRfilexists 80 /* The file named in a Create Directory, Make */ +#define ERRnotlocked 158 /* No lock matched the unlock range */ +#define ERRnoatomiclocks 174 /* Change lock type not supported */ +#define ERRbadpipe 230 /* Pipe invalid. */ +#define ERRpipebusy 231 /* All instances of the requested pipe are busy. */ +#define ERRpipeclosing 232 /* Pipe close in progress. */ +#define ERRnotconnected 233 /* No process on other end of pipe. */ +#define ERRmoredata 234 /* There is more data to be returned. */ +#define ERRunknownlevel 124 + + +/* + * ERRSRV error codes + */ +#define ERRerror 1 /* Non-specific error code. It is returned */ +#define ERRbadpw 2 /* Bad password - name/password pair in a Tree */ +#define ERRaccess 4 /* The client does not have the necessary access */ +#define ERRinvnid 5 /* The Tid specified in a command was invalid. */ +#define ERRinvnetname 6 /* Invalid network name in tree connect. */ +#define ERRinvdevice 7 /* Invalid device - printer request made to non- */ +#define ERRqfull 49 /* Print queue full (files) -- returned by open */ +#define ERRqtoobig 50 /* Print queue full -- no space. */ +#define ERRqeof 51 /* EOF on print queue dump. */ +#define ERRinvpfid 52 /* Invalid print file FID. */ +#define ERRsmbcmd 64 /* The server did not recognize the command */ +#define ERRsrverror 65 /* The server encountered an internal error, */ +#define ERRfilespecs 67 /* The Fid and pathname parameters contained an */ +#define ERRbadpermits 69 /* The access permissions specified for a file */ +#define ERRsetattrmode 71 /* The attribute mode in the Set File Attribute */ +#define ERRpaused 81 /* Server is paused. (reserved for messaging) */ +#define ERRmsgoff 82 /* Not receiving messages. (reserved for */ +#define ERRnoroom 83 /* No room to buffer message. (reserved for */ +#define ERRrmuns 87 /* Too many remote user names. (reserved for */ +#define ERRtimeout 88 /* Operation timed out. */ +#define ERRnoresource 89 /* No resources currently available for request. */ +#define ERRtoomanyuids 90 /* Too many Uids active on this session. */ +#define ERRbaduid 91 /* The Uid is not known as a valid user */ +#define ERRusempx 250 /* Temporarily unable to support Raw, use MPX */ +#define ERRusestd 251 /* Temporarily unable to support Raw, use */ +#define ERRcontmpx 252 /* Continue in MPX mode. */ +#define ERRnosupport 65535 /* Function not supported. */ + + +/* + * ERRHRD error codes + */ +#define ERRnowrite 19 /* Attempt to write on write-protected media */ +#define ERRbadunit 20 /* Unknown unit. */ +#define ERRnotready 21 /* Drive not ready. */ +#define ERRbadcmd 22 /* Unknown command. */ +#define ERRdata 23 /* Data error (CRC). */ +#define ERRbadreq 24 /* Bad request structure length. */ +#define ERRseek 25 /* Seek error. */ +#define ERRbadmedia 26 /* Unknown media type. */ +#define ERRbadsector 27 /* Sector not found. */ +#define ERRnopaper 28 /* Printer out of paper. */ +#define ERRwrite 29 /* Write fault. */ +#define ERRread 30 /* Read fault. */ +#define ERRgeneral 31 /* General failure. */ +#define ERRbadshare 32 /* A open conflicts with an existing open. */ +#define ERRlock 33 /* A Lock request conflicted with an existing */ +#define ERRwrongdisk 34 /* The wrong disk was found in a drive. */ +#define ERRFCBUnavail 35 /* No FCBs are available to process request. */ +#define ERRsharebufexc 36 /* A sharing buffer has been exceeded. */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_DOSERROR_H */ diff --git a/usr/src/uts/common/smbsrv/hash_table.h b/usr/src/uts/common/smbsrv/hash_table.h new file mode 100755 index 000000000000..0e4586097c59 --- /dev/null +++ b/usr/src/uts/common/smbsrv/hash_table.h @@ -0,0 +1,192 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_HASH_TABLE_H +#define _SMBSRV_HASH_TABLE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * + * Interface definition for the hash table library. The hash table is a + * user-specified array of pointers to items. Hash collisions are handled + * using linked lists from the table entries. A handle is associated with + * each table, which is used to maintain the hash table. + * + * +------+ +-------+ +----+ +----+ + * |handle|---> |index 0|--->|item|--->|item|---> + * | ... | +-------+ +----+ +----+ + * | ... | |index 1|---> + * +------+ +-------+ +----+ +----+ +----+ + * |index 2|--->|item|--->|item|--->|item|---> + * +-------+ +----+ +----+ +----+ + * | ... |---> + * +-------+ + * | ... |---> + * +-------+ + * |index n|---> + * +-------+ + * + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This is the hash multiplier value. + */ +#define HASH_MESH_VALUE 77 + +/* + * Each entry (item) in the hash table has a linked-list pointer, a key, + * a pointer to some user defined data (which may be null) and some flags. + * The key is a user provided key and is used to position the item within + * the table. The linked-list is used to store items whose hash values + * collide. The data pointer is never dereferenced in the hash code so + * it may be a null pointer. + * + * The item bit flags are: + * + * HTIF_DELETE: Specifies that an item is marked for deletion (see + * ht_mark_delete and ht_clean_table). + */ +#define HTIF_MARKED_DELETED 0x01 +#define HT_DELETE HTIF_MARKED_DELETED + +typedef struct ht_item { + struct ht_item *hi_next; + char *hi_key; + void *hi_data; + size_t hi_flags; +} HT_ITEM; + +/* + * HT_TABLE_ENTRY is an opaque structure (to the public) used to maintain + * a pointer to the hash table and the number of items in the table entry. + * This number shows number of both available items and those are marked + * as deleted. + */ +typedef struct ht_table_entry { + HT_ITEM *he_head; + size_t he_count; +} HT_TABLE_ENTRY; + +/* + * The HT_HANDLE is an opaque handle that associates each request with + * a hash table. A handle is generated when a hash table is created and + * it is used to maintain all global data associated with the table. + * + * The handle bit flags are: + * + * HTHF_FIXED_KEY: Specifies that keys are fixed length and should + * not be assumed to be null terminated. + */ +#define HTHF_FIXED_KEY 0x01 + +typedef struct ht_handle { + HT_TABLE_ENTRY *ht_table; + size_t ht_sequence; + size_t ht_table_size; + size_t ht_table_mask; + size_t ht_key_size; + size_t ht_total_items; /* show total number of available items */ + size_t ht_flags; + size_t (*ht_hash)(struct ht_handle *handle, const char *key); + void (*ht_callback)(HT_ITEM *item); + int (*ht_cmp)(const char *key1, const char *key2, size_t n); +} HT_HANDLE; + +/* + * Typedefs for the optional user-installable functions. + */ +typedef void (*HT_CALLBACK)(HT_ITEM *item); + +/* + * Compare function cast to make all compare + * functions look like strncmp. + */ +typedef int (*HT_CMP)(const char *, const char *, size_t); + +/* + * Iterator used with ht_findfirst and ht_findnext to walk through + * all the items in a hash table. The iterator should be treated as + * an opaque handle. The sequence number in the iterator is used + * to maintain consistency with the table on which the iteration + * is being performed. If the table sequence number changes, the + * iterator becomes invalid. + */ +typedef struct ht_iterator { + HT_HANDLE *hti_handle; + HT_ITEM *hti_item; + size_t hti_index; + size_t hti_sequence; +} HT_ITERATOR; + +/* + * Public API to create and destroy hash tables, to change the hash + * function and to find out how many items are in a hash table. + */ +extern HT_HANDLE *ht_create_table(size_t table_size, size_t key_size, + size_t flags); +extern void ht_destroy_table(HT_HANDLE *handle); +extern void ht_set_cmpfn(HT_HANDLE *handle, HT_CMP cmpfn); +extern size_t ht_get_total_items(HT_HANDLE *handle); + +/* + * Public API to add, remove, replace or find specific items + * in a hash table. + */ +extern HT_ITEM *ht_add_item(HT_HANDLE *handle, const char *key, + const void *data); +extern HT_ITEM *ht_replace_item(HT_HANDLE *handle, const char *key, + const void *data); +extern void *ht_remove_item(HT_HANDLE *handle, const char *key); +extern HT_ITEM *ht_find_item(HT_HANDLE *handle, const char *key); + +/* + * Public API to iterate over a hash table. A mechanism is provided to + * mark items for deletion while searching the table so that the table + * is not modified during the search. When the search is complete, all + * of the marked items can be deleted by calling ht_clean_table. If + * the item data has been dynamically allocated, a callback can be + * registered to free the memory. The callback will be invoked with a + * pointer to each item as it is removed from the hash table. + */ +extern HT_ITEM *ht_findfirst(HT_HANDLE *handle, HT_ITERATOR *iterator); +extern HT_ITEM *ht_findnext(HT_ITERATOR *iterator); +extern void ht_mark_delete(HT_HANDLE *handle, HT_ITEM *item); +extern void ht_clear_delete(HT_HANDLE *handle, HT_ITEM *item); +extern size_t ht_clean_table(HT_HANDLE *handle); +extern HT_CALLBACK ht_register_callback(HT_HANDLE *handle, + HT_CALLBACK callback); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_HASH_TABLE_H */ diff --git a/usr/src/uts/common/smbsrv/lm.h b/usr/src/uts/common/smbsrv/lm.h new file mode 100644 index 000000000000..e1cc28c22094 --- /dev/null +++ b/usr/src/uts/common/smbsrv/lm.h @@ -0,0 +1,47 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifndef _SMBSRV_LM_H +#define _SMBSRV_LM_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file provides global Lan Manager definitions. + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_LM_H */ diff --git a/usr/src/uts/common/smbsrv/lmdfs.h b/usr/src/uts/common/smbsrv/lmdfs.h new file mode 100644 index 000000000000..418c4a9ad263 --- /dev/null +++ b/usr/src/uts/common/smbsrv/lmdfs.h @@ -0,0 +1,67 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_LMDFS_H +#define _SMBSRV_LMDFS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * LAN Manager DFS interface definition. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * DFS Volume state + */ +#define DFS_VOLUME_STATE_OK 1 +#define DFS_VOLUME_STATE_INCONSISTENT 2 +#define DFS_VOLUME_STATE_OFFLINE 3 +#define DFS_VOLUME_STATE_ONLINE 4 + +/* + * DFS Storage state + */ +#define DFS_STORAGE_STATE_OFFLINE 1 +#define DFS_STORAGE_STATE_ONLINE 2 + +/* + * Flags: + * DFS_ADD_VOLUME: Add a new volume to the DFS if not already there. + * DFS_RESTORE_VOLUME: Volume/Replica is being restored - do not verify + * share etc. + */ +#define DFS_ADD_VOLUME 1 +#define DFS_RESTORE_VOLUME 2 + + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_LMDFS_H */ diff --git a/usr/src/uts/common/smbsrv/lmerr.h b/usr/src/uts/common/smbsrv/lmerr.h new file mode 100644 index 000000000000..13e8843bbc83 --- /dev/null +++ b/usr/src/uts/common/smbsrv/lmerr.h @@ -0,0 +1,536 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_LMERR_H +#define _SMBSRV_LMERR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file contains the LAN Manager network error definitions. All + * network error codes are relative to NERR_BASE (2100), assigned by + * Microsoft, to avoid conflicts with system and redirector error + * codes. It should be safe to mix NERR error codes with the Win32 + * error codes defined in nterror.h. + * + * This file defines error codes in the range 2100 - 2999. NERR values + * must not exceed MAX_NERR (2999); values above this are used by other + * services. + * + * The range 2750-2799 has been allocated to the IBM LAN Server. + * The range 2900-2999 has been reserved for Microsoft OEMs. + * + * See lmcons.h for information on the full LANMAN error code range. + * + * See msdn.microsoft.com for additional information on the meaning + * of each error code. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define NERR_Success 0 + +#define NERR_BASE 2100 + +/* UNUSED BASE+0 */ +/* UNUSED BASE+1 */ +#define NERR_NetNotStarted (NERR_BASE+2) +#define NERR_UnknownServer (NERR_BASE+3) +#define NERR_ShareMem (NERR_BASE+4) + +#define NERR_NoNetworkResource (NERR_BASE+5) +#define NERR_RemoteOnly (NERR_BASE+6) +#define NERR_DevNotRedirected (NERR_BASE+7) +/* NERR_BASE+8 is used for ERROR_CONNECTED_OTHER_PASSWORD */ +/* UNUSED BASE+9 */ +/* UNUSED BASE+10 */ +/* UNUSED BASE+11 */ +/* UNUSED BASE+12 */ +/* UNUSED BASE+13 */ +#define NERR_ServerNotStarted (NERR_BASE+14) +#define NERR_ItemNotFound (NERR_BASE+15) +#define NERR_UnknownDevDir (NERR_BASE+16) +#define NERR_RedirectedPath (NERR_BASE+17) +#define NERR_DuplicateShare (NERR_BASE+18) +#define NERR_NoRoom (NERR_BASE+19) +/* UNUSED BASE+20 */ +#define NERR_TooManyItems (NERR_BASE+21) +#define NERR_InvalidMaxUsers (NERR_BASE+22) +#define NERR_BufTooSmall (NERR_BASE+23) +/* UNUSED BASE+24 */ +/* UNUSED BASE+25 */ +/* UNUSED BASE+26 */ +#define NERR_RemoteErr (NERR_BASE+27) +/* UNUSED BASE+28 */ +/* UNUSED BASE+29 */ +/* UNUSED BASE+30 */ +#define NERR_LanmanIniError (NERR_BASE+31) +/* UNUSED BASE+32 */ +/* UNUSED BASE+33 */ +/* UNUSED BASE+34 */ +/* UNUSED BASE+35 */ +#define NERR_NetworkError (NERR_BASE+36) +#define NERR_WkstaInconsistentState (NERR_BASE+37) +#define NERR_WkstaNotStarted (NERR_BASE+38) +#define NERR_BrowserNotStarted (NERR_BASE+39) +#define NERR_InternalError (NERR_BASE+40) +#define NERR_BadTransactConfig (NERR_BASE+41) +#define NERR_InvalidAPI (NERR_BASE+42) +#define NERR_BadEventName (NERR_BASE+43) +#define NERR_DupNameReboot (NERR_BASE+44) + +/* + * Config API related + * Error codes from BASE+45 to BASE+49 + */ +/* UNUSED BASE+45 */ +#define NERR_CfgCompNotFound (NERR_BASE+46) +#define NERR_CfgParamNotFound (NERR_BASE+47) +#define NERR_LineTooLong (NERR_BASE+49) + +/* + * Spooler API related + * Error codes from BASE+50 to BASE+79 + */ +#define NERR_QNotFound (NERR_BASE+50) +#define NERR_JobNotFound (NERR_BASE+51) +#define NERR_DestNotFound (NERR_BASE+52) +#define NERR_DestExists (NERR_BASE+53) +#define NERR_QExists (NERR_BASE+54) +#define NERR_QNoRoom (NERR_BASE+55) +#define NERR_JobNoRoom (NERR_BASE+56) +#define NERR_DestNoRoom (NERR_BASE+57) +#define NERR_DestIdle (NERR_BASE+58) +#define NERR_DestInvalidOp (NERR_BASE+59) +#define NERR_ProcNoRespond (NERR_BASE+60) +#define NERR_SpoolerNotLoaded (NERR_BASE+61) +#define NERR_DestInvalidState (NERR_BASE+62) +#define NERR_QInvalidState (NERR_BASE+63) +#define NERR_JobInvalidState (NERR_BASE+64) +#define NERR_SpoolNoMemory (NERR_BASE+65) +#define NERR_DriverNotFound (NERR_BASE+66) +#define NERR_DataTypeInvalid (NERR_BASE+67) +#define NERR_ProcNotFound (NERR_BASE+68) + +/* + * Service API related + * Error codes from BASE+80 to BASE+99 + */ +#define NERR_ServiceTableLocked (NERR_BASE+80) +#define NERR_ServiceTableFull (NERR_BASE+81) +#define NERR_ServiceInstalled (NERR_BASE+82) +#define NERR_ServiceEntryLocked (NERR_BASE+83) +#define NERR_ServiceNotInstalled (NERR_BASE+84) +#define NERR_BadServiceName (NERR_BASE+85) +#define NERR_ServiceCtlTimeout (NERR_BASE+86) +#define NERR_ServiceCtlBusy (NERR_BASE+87) +#define NERR_BadServiceProgName (NERR_BASE+88) +#define NERR_ServiceNotCtrl (NERR_BASE+89) +#define NERR_ServiceKillProc (NERR_BASE+90) +#define NERR_ServiceCtlNotValid (NERR_BASE+91) +#define NERR_NotInDispatchTbl (NERR_BASE+92) +#define NERR_BadControlRecv (NERR_BASE+93) +#define NERR_ServiceNotStarting (NERR_BASE+94) + +/* + * Wksta and Logon API related + * Error codes from BASE+100 to BASE+118 + */ +#define NERR_AlreadyLoggedOn (NERR_BASE+100) +#define NERR_NotLoggedOn (NERR_BASE+101) +#define NERR_BadUsername (NERR_BASE+102) +#define NERR_BadPassword (NERR_BASE+103) +#define NERR_UnableToAddName_W (NERR_BASE+104) +#define NERR_UnableToAddName_F (NERR_BASE+105) +#define NERR_UnableToDelName_W (NERR_BASE+106) +#define NERR_UnableToDelName_F (NERR_BASE+107) +/* UNUSED BASE+108 */ +#define NERR_LogonsPaused (NERR_BASE+109) +#define NERR_LogonServerConflict (NERR_BASE+110) +#define NERR_LogonNoUserPath (NERR_BASE+111) +#define NERR_LogonScriptError (NERR_BASE+112) +/* UNUSED BASE+113 */ +#define NERR_StandaloneLogon (NERR_BASE+114) +#define NERR_LogonServerNotFound (NERR_BASE+115) +#define NERR_LogonDomainExists (NERR_BASE+116) +#define NERR_NonValidatedLogon (NERR_BASE+117) + +/* + * ACF API related (access, user, group) + * Error codes from BASE+119 to BASE+149 + */ +#define NERR_ACFNotFound (NERR_BASE+119) +#define NERR_GroupNotFound (NERR_BASE+120) +#define NERR_UserNotFound (NERR_BASE+121) +#define NERR_ResourceNotFound (NERR_BASE+122) +#define NERR_GroupExists (NERR_BASE+123) +#define NERR_UserExists (NERR_BASE+124) +#define NERR_ResourceExists (NERR_BASE+125) +#define NERR_NotPrimary (NERR_BASE+126) +#define NERR_ACFNotLoaded (NERR_BASE+127) +#define NERR_ACFNoRoom (NERR_BASE+128) +#define NERR_ACFFileIOFail (NERR_BASE+129) +#define NERR_ACFTooManyLists (NERR_BASE+130) +#define NERR_UserLogon (NERR_BASE+131) +#define NERR_ACFNoParent (NERR_BASE+132) +#define NERR_CanNotGrowSegment (NERR_BASE+133) +#define NERR_SpeGroupOp (NERR_BASE+134) +#define NERR_NotInCache (NERR_BASE+135) +#define NERR_UserInGroup (NERR_BASE+136) +#define NERR_UserNotInGroup (NERR_BASE+137) +#define NERR_AccountUndefined (NERR_BASE+138) +#define NERR_AccountExpired (NERR_BASE+139) +#define NERR_InvalidWorkstation (NERR_BASE+140) +#define NERR_InvalidLogonHours (NERR_BASE+141) +#define NERR_PasswordExpired (NERR_BASE+142) +#define NERR_PasswordCantChange (NERR_BASE+143) +#define NERR_PasswordHistConflict (NERR_BASE+144) +#define NERR_PasswordTooShort (NERR_BASE+145) +#define NERR_PasswordTooRecent (NERR_BASE+146) +#define NERR_InvalidDatabase (NERR_BASE+147) +#define NERR_DatabaseUpToDate (NERR_BASE+148) +#define NERR_SyncRequired (NERR_BASE+149) + +/* + * Use API related + * Error codes from BASE+150 to BASE+169 + */ +#define NERR_UseNotFound (NERR_BASE+150) +#define NERR_BadAsgType (NERR_BASE+151) +#define NERR_DeviceIsShared (NERR_BASE+152) + +/* + * Message Server related + * Error codes BASE+170 to BASE+209 + */ +#define NERR_NoComputerName (NERR_BASE+170) +#define NERR_MsgAlreadyStarted (NERR_BASE+171) +#define NERR_MsgInitFailed (NERR_BASE+172) +#define NERR_NameNotFound (NERR_BASE+173) +#define NERR_AlreadyForwarded (NERR_BASE+174) +#define NERR_AddForwarded (NERR_BASE+175) +#define NERR_AlreadyExists (NERR_BASE+176) +#define NERR_TooManyNames (NERR_BASE+177) +#define NERR_DelComputerName (NERR_BASE+178) +#define NERR_LocalForward (NERR_BASE+179) +#define NERR_GrpMsgProcessor (NERR_BASE+180) +#define NERR_PausedRemote (NERR_BASE+181) +#define NERR_BadReceive (NERR_BASE+182) +#define NERR_NameInUse (NERR_BASE+183) +#define NERR_MsgNotStarted (NERR_BASE+184) +#define NERR_NotLocalName (NERR_BASE+185) +#define NERR_NoForwardName (NERR_BASE+186) +#define NERR_RemoteFull (NERR_BASE+187) +#define NERR_NameNotForwarded (NERR_BASE+188) +#define NERR_TruncatedBroadcast (NERR_BASE+189) +#define NERR_InvalidDevice (NERR_BASE+194) +#define NERR_WriteFault (NERR_BASE+195) +/* UNUSED BASE+196 */ +#define NERR_DuplicateName (NERR_BASE+197) +#define NERR_DeleteLater (NERR_BASE+198) +#define NERR_IncompleteDel (NERR_BASE+199) +#define NERR_MultipleNets (NERR_BASE+200) + +/* + * Server API related + * Error codes BASE+210 to BASE+229 + */ +#define NERR_NetNameNotFound (NERR_BASE+210) +#define NERR_DeviceNotShared (NERR_BASE+211) +#define NERR_ClientNameNotFound (NERR_BASE+212) +#define NERR_FileIdNotFound (NERR_BASE+214) +#define NERR_ExecFailure (NERR_BASE+215) +#define NERR_TmpFile (NERR_BASE+216) +#define NERR_TooMuchData (NERR_BASE+217) +#define NERR_DeviceShareConflict (NERR_BASE+218) +#define NERR_BrowserTableIncomplete (NERR_BASE+219) +#define NERR_NotLocalDomain (NERR_BASE+220) +#define NERR_IsDfsShare (NERR_BASE+221) + +/* + * CharDev API related + * Error codes BASE+230 to BASE+249 + */ +/* UNUSED BASE+230 */ +#define NERR_DevInvalidOpCode (NERR_BASE+231) +#define NERR_DevNotFound (NERR_BASE+232) +#define NERR_DevNotOpen (NERR_BASE+233) +#define NERR_BadQueueDevString (NERR_BASE+234) +#define NERR_BadQueuePriority (NERR_BASE+235) +#define NERR_NoCommDevs (NERR_BASE+237) +#define NERR_QueueNotFound (NERR_BASE+238) +#define NERR_BadDevString (NERR_BASE+240) +#define NERR_BadDev (NERR_BASE+241) +#define NERR_InUseBySpooler (NERR_BASE+242) +#define NERR_CommDevInUse (NERR_BASE+243) + +/* + * NetICanonicalize and NetIType and NetIMakeLMFileName + * NetIListCanon and NetINameCheck + * Error codes BASE+250 to BASE+269 + */ +#define NERR_InvalidComputer (NERR_BASE+251) +/* UNUSED BASE+252 */ +/* UNUSED BASE+253 */ +#define NERR_MaxLenExceeded (NERR_BASE+254) +/* UNUSED BASE+255 */ +#define NERR_BadComponent (NERR_BASE+256) +#define NERR_CantType (NERR_BASE+257) +/* UNUSED BASE+258 */ +/* UNUSED BASE+259 */ +#define NERR_TooManyEntries (NERR_BASE+262) + +/* + * NetProfile + * Error codes BASE+270 to BASE+276 + */ +#define NERR_ProfileFileTooBig (NERR_BASE+270) +#define NERR_ProfileOffset (NERR_BASE+271) +#define NERR_ProfileCleanup (NERR_BASE+272) +#define NERR_ProfileUnknownCmd (NERR_BASE+273) +#define NERR_ProfileLoadErr (NERR_BASE+274) +#define NERR_ProfileSaveErr (NERR_BASE+275) + +/* + * NetAudit and NetErrorLog + * Error codes BASE+277 to BASE+279 + */ +#define NERR_LogOverflow (NERR_BASE+277) +#define NERR_LogFileChanged (NERR_BASE+278) +#define NERR_LogFileCorrupt (NERR_BASE+279) + +/* + * NetRemote + * Error codes BASE+280 to BASE+299 + */ +#define NERR_SourceIsDir (NERR_BASE+280) +#define NERR_BadSource (NERR_BASE+281) +#define NERR_BadDest (NERR_BASE+282) +#define NERR_DifferentServers (NERR_BASE+283) +/* UNUSED BASE+284 */ +#define NERR_RunSrvPaused (NERR_BASE+285) +/* UNUSED BASE+286 */ +/* UNUSED BASE+287 */ +/* UNUSED BASE+288 */ +#define NERR_ErrCommRunSrv (NERR_BASE+289) +/* UNUSED BASE+290 */ +#define NERR_ErrorExecingGhost (NERR_BASE+291) +#define NERR_ShareNotFound (NERR_BASE+292) +/* UNUSED BASE+293 */ +/* UNUSED BASE+294 */ + + +/* + * NetWksta.sys (redir) returned error codes. + * NERR_BASE + (300-329) + */ +#define NERR_InvalidLana (NERR_BASE+300) +#define NERR_OpenFiles (NERR_BASE+301) +#define NERR_ActiveConns (NERR_BASE+302) +#define NERR_BadPasswordCore (NERR_BASE+303) +#define NERR_DevInUse (NERR_BASE+304) +#define NERR_LocalDrive (NERR_BASE+305) + +/* + * Alert error codes. + * NERR_BASE + (330-339) + */ +#define NERR_AlertExists (NERR_BASE+330) +#define NERR_TooManyAlerts (NERR_BASE+331) +#define NERR_NoSuchAlert (NERR_BASE+332) +#define NERR_BadRecipient (NERR_BASE+333) +#define NERR_AcctLimitExceeded (NERR_BASE+334) + +/* + * Additional Error and Audit log codes. + * NERR_BASE +(340-343) + */ +#define NERR_InvalidLogSeek (NERR_BASE+340) +/* UNUSED BASE+341 */ +/* UNUSED BASE+342 */ +/* UNUSED BASE+343 */ + +/* + * Additional UAS and NETLOGON codes + * NERR_BASE +(350-359) + */ +#define NERR_BadUasConfig (NERR_BASE+350) +#define NERR_InvalidUASOp (NERR_BASE+351) +#define NERR_LastAdmin (NERR_BASE+352) +#define NERR_DCNotFound (NERR_BASE+353) +#define NERR_LogonTrackingError (NERR_BASE+354) +#define NERR_NetlogonNotStarted (NERR_BASE+355) +#define NERR_CanNotGrowUASFile (NERR_BASE+356) +#define NERR_TimeDiffAtDC (NERR_BASE+357) +#define NERR_PasswordMismatch (NERR_BASE+358) + +/* + * Server Integration error codes. + * NERR_BASE +(360-369) + */ +#define NERR_NoSuchServer (NERR_BASE+360) +#define NERR_NoSuchSession (NERR_BASE+361) +#define NERR_NoSuchConnection (NERR_BASE+362) +#define NERR_TooManyServers (NERR_BASE+363) +#define NERR_TooManySessions (NERR_BASE+364) +#define NERR_TooManyConnections (NERR_BASE+365) +#define NERR_TooManyFiles (NERR_BASE+366) +#define NERR_NoAlternateServers (NERR_BASE+367) +/* UNUSED BASE+368 */ +/* UNUSED BASE+369 */ +#define NERR_TryDownLevel (NERR_BASE+370) + +/* + * UPS error codes. + * NERR_BASE + (380-384) + */ +#define NERR_UPSDriverNotStarted (NERR_BASE+380) +#define NERR_UPSInvalidConfig (NERR_BASE+381) +#define NERR_UPSInvalidCommPort (NERR_BASE+382) +#define NERR_UPSSignalAsserted (NERR_BASE+383) +#define NERR_UPSShutdownFailed (NERR_BASE+384) + +/* + * Remoteboot error codes. + * NERR_BASE + (400-419) + * Error codes 400 - 405 are used by RPLBOOT.SYS. + * Error codes 403, 407 - 416 are used by RPLLOADR.COM, + * Error code 417 is the alerter message of REMOTEBOOT (RPLSERVR.EXE). + * Error code 418 is for when REMOTEBOOT can't start + * Error code 419 is for a disallowed 2nd rpl connection + */ +#define NERR_BadDosRetCode (NERR_BASE+400) +#define NERR_ProgNeedsExtraMem (NERR_BASE+401) +#define NERR_BadDosFunction (NERR_BASE+402) +#define NERR_RemoteBootFailed (NERR_BASE+403) +#define NERR_BadFileCheckSum (NERR_BASE+404) +#define NERR_NoRplBootSystem (NERR_BASE+405) +#define NERR_RplLoadrNetBiosErr (NERR_BASE+406) +#define NERR_RplLoadrDiskErr (NERR_BASE+407) +#define NERR_ImageParamErr (NERR_BASE+408) +#define NERR_TooManyImageParams (NERR_BASE+409) +#define NERR_NonDosFloppyUsed (NERR_BASE+410) +#define NERR_RplBootRestart (NERR_BASE+411) +#define NERR_RplSrvrCallFailed (NERR_BASE+412) +#define NERR_CantConnectRplSrvr (NERR_BASE+413) +#define NERR_CantOpenImageFile (NERR_BASE+414) +#define NERR_CallingRplSrvr (NERR_BASE+415) +#define NERR_StartingRplBoot (NERR_BASE+416) +#define NERR_RplBootServiceTerm (NERR_BASE+417) +#define NERR_RplBootStartFailed (NERR_BASE+418) +#define NERR_RPL_CONNECTED (NERR_BASE+419) + +/* + * FTADMIN API error codes + * NERR_BASE + (425-434) + * (Currently not used in NT) + */ + +/* + * Browser service API error codes + * NERR_BASE + (450-475) + */ +#define NERR_BrowserConfiguredToNotRun (NERR_BASE+450) + +/* + * Additional Remoteboot error codes. + * NERR_BASE + (510-550) + */ +#define NERR_RplNoAdaptersStarted (NERR_BASE+510) +#define NERR_RplBadRegistry (NERR_BASE+511) +#define NERR_RplBadDatabase (NERR_BASE+512) +#define NERR_RplRplfilesShare (NERR_BASE+513) +#define NERR_RplNotRplServer (NERR_BASE+514) +#define NERR_RplCannotEnum (NERR_BASE+515) +#define NERR_RplWkstaInfoCorrupted (NERR_BASE+516) +#define NERR_RplWkstaNotFound (NERR_BASE+517) +#define NERR_RplWkstaNameUnavailable (NERR_BASE+518) +#define NERR_RplProfileInfoCorrupted (NERR_BASE+519) +#define NERR_RplProfileNotFound (NERR_BASE+520) +#define NERR_RplProfileNameUnavailable (NERR_BASE+521) +#define NERR_RplProfileNotEmpty (NERR_BASE+522) +#define NERR_RplConfigInfoCorrupted (NERR_BASE+523) +#define NERR_RplConfigNotFound (NERR_BASE+524) +#define NERR_RplAdapterInfoCorrupted (NERR_BASE+525) +#define NERR_RplInternal (NERR_BASE+526) +#define NERR_RplVendorInfoCorrupted (NERR_BASE+527) +#define NERR_RplBootInfoCorrupted (NERR_BASE+528) +#define NERR_RplWkstaNeedsUserAcct (NERR_BASE+529) +#define NERR_RplNeedsRPLUSERAcct (NERR_BASE+530) +#define NERR_RplBootNotFound (NERR_BASE+531) +#define NERR_RplIncompatibleProfile (NERR_BASE+532) +#define NERR_RplAdapterNameUnavailable (NERR_BASE+533) +#define NERR_RplConfigNotEmpty (NERR_BASE+534) +#define NERR_RplBootInUse (NERR_BASE+535) +#define NERR_RplBackupDatabase (NERR_BASE+536) +#define NERR_RplAdapterNotFound (NERR_BASE+537) +#define NERR_RplVendorNotFound (NERR_BASE+538) +#define NERR_RplVendorNameUnavailable (NERR_BASE+539) +#define NERR_RplBootNameUnavailable (NERR_BASE+540) +#define NERR_RplConfigNameUnavailable (NERR_BASE+541) + +/* + * Dfs API error codes. + * NERR_BASE + (560-590) + */ +#define NERR_DfsInternalCorruption (NERR_BASE+560) +#define NERR_DfsVolumeDataCorrupt (NERR_BASE+561) +#define NERR_DfsNoSuchVolume (NERR_BASE+562) +#define NERR_DfsVolumeAlreadyExists (NERR_BASE+563) +#define NERR_DfsAlreadyShared (NERR_BASE+564) +#define NERR_DfsNoSuchShare (NERR_BASE+565) +#define NERR_DfsNotALeafVolume (NERR_BASE+566) +#define NERR_DfsLeafVolume (NERR_BASE+567) +#define NERR_DfsVolumeHasMultipleServers (NERR_BASE+568) +#define NERR_DfsCantCreateJunctionPoint (NERR_BASE+569) +#define NERR_DfsServerNotDfsAware (NERR_BASE+570) +#define NERR_DfsBadRenamePath (NERR_BASE+571) +#define NERR_DfsVolumeIsOffline (NERR_BASE+572) +#define NERR_DfsNoSuchServer (NERR_BASE+573) +#define NERR_DfsCyclicalName (NERR_BASE+574) +#define NERR_DfsNotSupportedInServerDfs (NERR_BASE+575) +#define NERR_DfsInternalError (NERR_BASE+590) + +/* + * Net setup error codes. + * NERR_BASE + (591-595) + */ +#define NERR_SetupAlreadyJoined (NERR_BASE+591) +#define NERR_SetupNotJoined (NERR_BASE+592) +#define NERR_SetupDomainController (NERR_BASE+593) + +/* + * MAX_NERR is the last value in the NERR range. + * Do not exceed this value here. + */ +#define MAX_NERR (NERR_BASE+899) + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_LMERR_H */ diff --git a/usr/src/uts/common/smbsrv/lmshare.h b/usr/src/uts/common/smbsrv/lmshare.h new file mode 100644 index 000000000000..db41a5bdee27 --- /dev/null +++ b/usr/src/uts/common/smbsrv/lmshare.h @@ -0,0 +1,195 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_LMSHARE_H +#define _SMBSRV_LMSHARE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file defines the LanMan (CIFS/SMB) resource share interface. + */ + +#include +#include +#include +#include +#include +#include + +#ifndef _KERNEL +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define SHOPT_AD_CONTAINER "ad-container" +#define SHOPT_NAME "name" /* name is a pseudo property */ + +#define SMB_DEFAULT_SHARE_GROUP "smb" +#define SMB_PROTOCOL_NAME "smb" + +/* + * Despite the fact that the MAXNAMELEN is 256, we only + * support a maximum share name length of 15 characters. + */ +#define LMSHR_VALID_NAME_MAX 15 +#define LMSHR_VALID_NAME_BUFSIZ 16 +#define LMSHR_COMMENT_MAX (64 * MTS_MB_CHAR_MAX) + +/* + * Mode should be renamed to flags. + * + * LMSHRM_TRANS Transient share + * LMSHRM_PERM Permanent share + */ +#define LMSHRM_TRANS 0x0001 +#define LMSHRM_PERM 0x0002 +#define LMSHRM_ALL (LMSHRM_TRANS | LMSHRM_PERM) + +#define LMSHR_PUBLISH 0 +#define LMSHR_UNPUBLISH 1 + +#define LMSHR_ADD 0 +#define LMSHR_DELETE 1 + + +/* + * refcnt is currently only used for autohome. autohome needs a refcnt + * because a user can map his autohome share from more than one client + * at the same time and the share should only be removed when the last + * one is disconnected + */ +typedef struct lmshare_info { + char share_name[MAXNAMELEN]; + char directory[MAXPATHLEN]; + char comment[LMSHR_COMMENT_MAX]; + char container[MAXPATHLEN]; + int mode; + int stype; + int refcnt; +} lmshare_info_t; + +typedef struct lmshare_iterator { + lmshare_info_t si; + HT_ITERATOR *iterator; + unsigned int iteration; + int mode; +} lmshare_iterator_t; + +#define LMSHARES_PER_REQUEST 10 +typedef struct lmshare_list { + int no; + lmshare_info_t smbshr[LMSHARES_PER_REQUEST]; +} lmshare_list_t; + + +#ifndef _KERNEL +/* + * CIFS share management functions (private to the smb daemon). + */ +extern int lmshare_start(void); +extern void lmshare_stop(void); +extern lmshare_iterator_t *lmshare_open_iterator(int mode); +extern void lmshare_close_iterator(lmshare_iterator_t *iterator); +extern lmshare_info_t *lmshare_iterate(lmshare_iterator_t *iterator); + +extern DWORD lmshare_list(int offset, lmshare_list_t *list); +extern DWORD lmshare_list_transient(int offset, lmshare_list_t *list); +extern int lmshare_num_transient(void); + +extern int lmshare_num_shares(void); +extern DWORD lmshare_add(lmshare_info_t *si, int); +extern DWORD lmshare_delete(char *share_name, int); +extern DWORD lmshare_rename(char *from, char *to, int); +extern DWORD lmshare_getinfo(char *share_name, lmshare_info_t *si); +extern DWORD lmshare_setinfo(lmshare_info_t *si, int); +extern DWORD lmshare_get_realpath(const char *srcbuf, char *dstbuf, int maxlen); +extern void lmshare_do_publish(lmshare_info_t *, char, int); + +extern int lmshare_exists(char *share_name); +extern int lmshare_is_special(char *share_name); +extern int lmshare_is_restricted(char *share_name); +extern int lmshare_is_admin(char *share_name); +extern int lmshare_is_valid(char *share_name); +extern int lmshare_is_dir(char *path); +/* XXX Move these 2 functions in mlsvc_util.h, after the libmlsvc cleanup */ +extern sa_group_t smb_get_smb_share_group(sa_handle_t handle); +extern void smb_build_lmshare_info(char *share_name, char *path, + sa_optionset_t opts, lmshare_info_t *si); + +/* The following 3 functions are called by FSD user-space library */ +extern DWORD lmshare_add_adminshare(char *volname, unsigned char drive); + +#endif /* _KERNEL */ + +/* + * LanMan share API (for both SMB kernel module and GUI/CLI sub-system) + * + * NOTE: If any error is encounted by either the door server or client, + * NERR_InternalError will be returned by most functions. + * lmshrd_num_shares will return -1 while the lmshrd_open_iterator/ + * lmshrd_close_iterator will return NULL. + */ + +extern uint64_t lmshrd_open_iterator(int mode); +extern DWORD lmshrd_close_iterator(uint64_t iterator); +extern DWORD lmshrd_iterate(uint64_t iterator, lmshare_info_t *si); +#ifndef _KERNEL +extern DWORD lmshrd_list(int offset, lmshare_list_t *list); +extern DWORD lmshrd_list_transient(int offset, lmshare_list_t *list); +extern DWORD lmshrd_num_transient(void); +extern int lmshrd_dump_hash(char *logfname); +#endif +extern int lmshrd_num_shares(void); +extern DWORD lmshrd_delete(char *share_name); +extern DWORD lmshrd_rename(char *from, char *to); +extern DWORD lmshrd_getinfo(char *share_name, lmshare_info_t *si); +extern DWORD lmshrd_add(lmshare_info_t *si); +extern DWORD lmshrd_setinfo(lmshare_info_t *si); + +extern int lmshrd_exists(char *share_name); +extern int lmshrd_is_special(char *share_name); +extern int lmshrd_is_restricted(char *share_name); +extern int lmshrd_is_admin(char *share_name); +extern int lmshrd_is_valid(char *share_name); +extern int lmshrd_is_dir(char *path); + +/* + * The SMB kernel module must invoke the following functions to start/stop + * the LanMan share door client. + */ +#ifdef _KERNEL +extern int lmshrd_kclient_start(void); +extern void lmshrd_kclient_stop(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_LMSHARE_H */ diff --git a/usr/src/uts/common/smbsrv/lmshare_door.h b/usr/src/uts/common/smbsrv/lmshare_door.h new file mode 100644 index 000000000000..c1c6f2ba0c1a --- /dev/null +++ b/usr/src/uts/common/smbsrv/lmshare_door.h @@ -0,0 +1,116 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_LMSHARE_DOOR_H +#define _SMBSRV_LMSHARE_DOOR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include + +/* + * Door interface for CIFS share management. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define LMSHR_DOOR_NAME "/var/run/smb_lmshare_door" +#define LMSHR_DOOR_VERSION 1 + +#define LMSHR_DOOR_COOKIE ((void*)(0xdeadbeef^LMSHR_DOOR_VERSION)) +#define LMSHR_DOOR_SIZE (sizeof (lmshare_list_t) + 32) + +/* + * Door interface + * + * Define door operations + */ +#define LMSHR_DOOR_OPEN_ITERATOR 1 +#define LMSHR_DOOR_CLOSE_ITERATOR 2 +#define LMSHR_DOOR_ITERATE 3 +#define LMSHR_DOOR_NUM_SHARES 4 +#define LMSHR_DOOR_DELETE 5 +#define LMSHR_DOOR_RENAME 6 +#define LMSHR_DOOR_GETINFO 7 +#define LMSHR_DOOR_ADD 8 +#define LMSHR_DOOR_SETINFO 9 +#define LMSHR_DOOR_EXISTS 10 +#define LMSHR_DOOR_IS_SPECIAL 11 +#define LMSHR_DOOR_IS_RESTRICTED 12 +#define LMSHR_DOOR_IS_ADMIN 13 +#define LMSHR_DOOR_IS_VALID 14 +#define LMSHR_DOOR_IS_DIR 15 +#define LMSHR_DOOR_LIST 16 + +#define SMB_GET_KCONFIG 17 + +void smb_load_kconfig(smb_kmod_cfg_t *cfg); +void smb_dr_get_kconfig(smb_dr_ctx_t *ctx, smb_kmod_cfg_t *cfg); +void smb_dr_put_kconfig(smb_dr_ctx_t *ctx, smb_kmod_cfg_t *cfg); + +/* + * Door server status + * + * LMSHR_DOOR_ERROR is returned by the door server if there is problem + * with marshalling/unmarshalling. Otherwise, LMSHR_DOOR_SUCCESS is + * returned. + * + */ +#define LMSHR_DOOR_SRV_SUCCESS 0 +#define LMSHR_DOOR_SRV_ERROR -1 + +/* + * struct door_request { + * int req_type; + * + * }; + * + * struct door_response { + * int door_srv_status; + * + * }; + */ + +void smb_dr_get_lmshare(smb_dr_ctx_t *ctx, lmshare_info_t *si); +void smb_dr_put_lmshare(smb_dr_ctx_t *ctx, lmshare_info_t *si); + +uint64_t smb_dr_get_lmshr_iterator(smb_dr_ctx_t *ctx); +void smb_dr_put_lmshr_iterator(smb_dr_ctx_t *ctx, + uint64_t lmshr_iter); +void smb_dr_free_lmshr_iterator(smb_dr_ctx_t *ctx); +void smb_dr_get_lmshr_list(smb_dr_ctx_t *ctx, + lmshare_list_t *shrlist); +void smb_dr_put_lmshr_list(smb_dr_ctx_t *ctx, + lmshare_list_t *shrlist); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_LMSHARE_DOOR_H */ diff --git a/usr/src/uts/common/smbsrv/lsalib.h b/usr/src/uts/common/smbsrv/lsalib.h new file mode 100644 index 000000000000..b8566bf342d6 --- /dev/null +++ b/usr/src/uts/common/smbsrv/lsalib.h @@ -0,0 +1,164 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_LSALIB_H +#define _SMBSRV_LSALIB_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Prototypes for the LSA library and RPC client side library interface. + * There are two levels of interface defined here: lsa_xxx and lsar_xxx. + * The lsa_xxx functions provide a high level interface which make + * multiple RPC calls and do all the work necessary to obtain and return + * the requested information. The lsar_xxx functions provide a low level + * interface in which each function maps to a single underlying RPC. + */ + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * lsalib.c + */ +int lsa_lookup_builtin_name(char *account_name, + smb_userinfo_t *user_info); + +int lsa_lookup_local_sam(char *domain, + char *account_name, + smb_userinfo_t *user_info); + +int lsa_lookup_local(char *name, + smb_userinfo_t *user_info); + +int lsa_lookup_name(char *server, + char *domain, + char *account_name, + smb_userinfo_t *user_info); + +DWORD lsa_lookup_name2(char *server, + char *domain, + char *account_name, + smb_userinfo_t *user_info); + +int lsa_lookup_sid(nt_sid_t *sid, + smb_userinfo_t *user_info); + +DWORD lsa_lookup_sid2(nt_sid_t *sid, + smb_userinfo_t *user_info); + +int lsa_lookup_privs(char *server, + char *account_name, + char *target_name, + smb_userinfo_t *user_info); + +int lsa_test(char *server, char *account_name); + + +/* + * lsar_open.c + */ +int lsar_open(int ipc_mode, + char *server, + char *domain, + char *username, + char *password, + mlsvc_handle_t *domain_handle); + +int lsar_open_policy2(char *server, + char *domain, + char *username, + mlsvc_handle_t *lsa_handle); + +int lsar_open_account(mlsvc_handle_t *lsa_handle, + struct mslsa_sid *sid, + mlsvc_handle_t *lsa_account_handle); + +int lsar_close(mlsvc_handle_t *lsa_handle); + + +/* + * lsar_lookup.c + */ +int lsar_query_security_desc(mlsvc_handle_t *lsa_handle); + +DWORD lsar_query_info_policy(mlsvc_handle_t *lsa_handle, WORD infoClass); + +int lsar_lookup_names(mlsvc_handle_t *lsa_handle, + char *name, + smb_userinfo_t *user_info); + +int lsar_lookup_sids(mlsvc_handle_t *lsa_handle, + struct mslsa_sid *sid, + smb_userinfo_t *user_info); + +DWORD lsar_get_userid(char *server, char *name); + +int lsar_enum_accounts(mlsvc_handle_t *lsa_handle, + DWORD *enum_context, + struct mslsa_EnumAccountBuf *accounts); + +DWORD lsar_enum_trusted_domains(mlsvc_handle_t *lsa_handle, + DWORD *enum_context); + +int lsar_enum_privs_account(mlsvc_handle_t *account_handle, + smb_userinfo_t *user_info); + +int lsar_lookup_priv_value(mlsvc_handle_t *lsa_handle, + char *name, + struct ms_luid *luid); + +int lsar_lookup_priv_name(mlsvc_handle_t *lsa_handle, + struct ms_luid *luid, + char *name, + int namelen); + +DWORD lsar_lookup_priv_display_name(mlsvc_handle_t *lsa_handle, + char *name, + char *display_name, + int display_len); + +DWORD lsar_lookup_sids2(mlsvc_handle_t *lsa_handle, + struct mslsa_sid *sid, + smb_userinfo_t *user_info); + +DWORD lsar_lookup_names2(mlsvc_handle_t *lsa_handle, + char *name, + smb_userinfo_t *user_info); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _SMBSRV_LSALIB_H */ diff --git a/usr/src/uts/common/smbsrv/mac_cifs.h b/usr/src/uts/common/smbsrv/mac_cifs.h new file mode 100644 index 000000000000..26f04519582f --- /dev/null +++ b/usr/src/uts/common/smbsrv/mac_cifs.h @@ -0,0 +1,112 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_MAC_CIFS_H +#define _SMBSRV_MAC_CIFS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file provides definitions for the Macintosh Extensions for CIFS + * interface (see http://www.thursby.com/cifs). + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Macintosh information level extensions. The entire list is presented + * here for convenience but for consistency with the existing CIFS + * information levels don't use these values directly. Use the SMB_MAC_ + * definitions in cifs.h. + * + * SmbTrans2QueryFsInformation: MAC_QUERY_FS_INFO + * SmbTrans2Find{First|Next}2: MAC_FIND_BOTH_HFS_INFO + * SmbTrans2SetPathInformation: MAC_SET_FINDER_INFO + * SmbTrans2QueryPathInformation: MAC_DT_{ADD|REMOVE|GET}_{APPL|ICON} + */ +#define MAC_QUERY_FS_INFO 0x301 +#define MAC_FIND_BOTH_HFS_INFO 0x302 +#define MAC_SET_FINDER_INFO 0x303 +#define MAC_DT_ADD_APPL 0x304 +#define MAC_DT_REMOVE_APPL 0x305 +#define MAC_DT_GET_APPL 0x306 +#define MAC_DT_GET_ICON 0x307 +#define MAC_DT_GET_ICON_INFO 0x308 +#define MAC_DT_ADD_ICON 0x309 + + +/* + * Macintosh extensions support bits. Returned by the server in response + * to a TRANS2_QUERY_FS_INFORMATION request when the information level + * is MAC_QUERY_FS_INFO. + */ +#define MAC_SUPPORT_ACCESS_CONTROL 0x0010 +#define MAC_SUPPORT_GETSETCOMMENTS 0x0020 +#define MAC_SUPPORT_DESKTOPDB_CALLS 0x0040 +#define MAC_SUPPORT_UNIQUE_IDS 0x0080 +#define MAC_SUPPORT_NO_STREAMS 0x0100 + + +/* + * The MAC_ACCESS values are returned from the MAC_FIND_BOTH_HFS_INFO + * info level of TRANS2_FIND. Set SUPPORT_MAC_ACCESS_CNTRL to enable + * support. + * + * The MAC_OWNER bit indicates that the user is the owner of the file + * or directory. + */ +#define MAC_ACCESS_OWNER 0x0800 +#define MAC_ACCESS_OWNER_READ 0x0400 +#define MAC_ACCESS_OWNER_WRITE 0x0200 +#define MAC_ACCESS_OWNER_SEARCH 0x0100 +#define MAC_ACCESS_GROUP_READ 0x0040 +#define MAC_ACCESS_GROUP_WRITE 0x0020 +#define MAC_ACCESS_GROUP_SEARCH 0x0010 +#define MAC_ACCESS_OTHER_READ 0x0004 +#define MAC_ACCESS_OTHER_WRITE 0x0002 +#define MAC_ACCESS_OTHER_SEARCH 0x0001 + + +/* + * The MAC_FINDER values support the SMB_MAC_SET_FINDER_INFO info level + * of TRANS2_SET_PATH_INFORMATION. + */ +#define MAC_FINDER_SET_CREATE_DATE 0x0001 +#define MAC_FINDER_SET_MODE_DATE 0x0002 +#define MAC_FINDER_SET_FL_ATTRIB 0x0004 +#define MAC_FINDER_SET_INFO1 0x0008 +#define MAC_FINDER_SET_INFO2 0x0010 +#define MAC_FINDER_SET_HIDDEN 0x0020 + + +#ifdef __cplusplus +} +#endif + + +#endif /* _SMBSRV_MAC_CIFS_H */ diff --git a/usr/src/uts/common/smbsrv/mailslot.h b/usr/src/uts/common/smbsrv/mailslot.h new file mode 100644 index 000000000000..35f2e74552a4 --- /dev/null +++ b/usr/src/uts/common/smbsrv/mailslot.h @@ -0,0 +1,76 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_MAILSLOT_H +#define _SMBSRV_MAILSLOT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Mailslots are a one-way, unreliable IPC mechanism that allows a + * client to send or broadcast messages to a server. The names follow + * the same universal naming convention (UNC) used with named pipes: + * \\server\mailslot\name, \\.\mailslot\name etc. There is a good + * overview of mailslots, including limitations of NT and Windows 2000, + * in Network Programming for Microsoft Windows Chapter 3. + * + * Network Programming for Microsoft Windows + * Anthony Jones and Jim Ohlund + * Microsoft Press, ISBN 0-7356-0560-2 + * + * This file defines pre-defined and system common mailslots. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Well-known or pre-defined mailslots. + */ +#define MAILSLOT_LANMAN "\\MAILSLOT\\LANMAN" +#define MAILSLOT_MSBROWSE "\\MAILSLOT\\MSBROWSE" +#define MAILSLOT_BROWSE "\\MAILSLOT\\BROWSE" +#define MAILSLOT_NETLOGON "\\MAILSLOT\\NET\\NETLOGON" +#define MAILSLOT_NTLOGON "\\MAILSLOT\\NET\\NTLOGON" + + +/* + * System common mailslots. These should be dynamically assigned + * at runtime but we don't support a full mailslot implementation + * so we use a set of predefined values that appear to work. + */ +#define MAILSLOT_NETLOGON_RDC "\\MAILSLOT\\NET\\GETDC354" +#define MAILSLOT_NETLOGON_MDC "\\MAILSLOT\\NET\\GETDC576" +#define MAILSLOT_NETLOGON_SAMLOGON_RDC "\\MAILSLOT\\NET\\GETDC873" +#define MAILSLOT_NETLOGON_SAMLOGON_MDC "\\MAILSLOT\\NET\\GETDC875" + + +#ifdef __cplusplus +} +#endif + + +#endif /* _SMBSRV_MAILSLOT_H */ diff --git a/usr/src/uts/common/smbsrv/mbuf.h b/usr/src/uts/common/smbsrv/mbuf.h new file mode 100644 index 000000000000..51a6bea6e82b --- /dev/null +++ b/usr/src/uts/common/smbsrv/mbuf.h @@ -0,0 +1,269 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 1982, 1986, 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef _SMBSRV_MBUF_H +#define _SMBSRV_MBUF_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * PBSHORTCUT This file should be removed from the PB port but is required + * for now to get it to compile. This file has also been modified. + */ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MSIZE 256 +#define MCLBYTES 2048 +#define MCLSHIFT 11 +#define MCLOFSET (MCLBYTES - 1) + +#define NBPG 4096 +#define CLBYTES NBPG +#define PB_PAGESIZE 4096 + +/* + * Mbufs are of a single size, MSIZE (machine/machparam.h), which + * includes overhead. An mbuf may add a single "mbuf cluster" of size + * MCLBYTES (also in machine/machparam.h), which has no additional overhead + * and is used instead of the internal data area; this is done when + * at least MINCLSIZE of data must be stored. + */ + +#define MLEN (MSIZE - sizeof (struct m_hdr)) /* normal data len */ +#define MHLEN (MLEN - sizeof (struct pkthdr)) /* data len w/pkthdr */ + +#define MINCLSIZE (MHLEN + MLEN) /* smallest amount to put in cluster */ + +/* + * Macros for type conversion + * mtod(m,t) - convert mbuf pointer to data pointer of correct type + */ +#define mtod(m, t) ((t)((m)->m_data)) + + +/* header at beginning of each mbuf: */ +struct m_hdr { + struct mbuf *mh_next; /* next buffer in chain */ + struct mbuf *mh_nextpkt; /* next chain in queue/record */ + int mh_len; /* amount of data in this mbuf */ + caddr_t mh_data; /* location of data */ + short mh_type; /* type of data in this mbuf */ + short mh_flags; /* flags; see below */ +}; + +/* record/packet header in first mbuf of chain; valid if M_PKTHDR set */ +struct pkthdr { + int len; /* total packet length */ +}; + + +/* XXX probably do not need m_ext */ + +/* description of external storage mapped into mbuf, valid if M_EXT set */ +struct m_ext { + caddr_t ext_buf; /* start of buffer */ + int (*ext_ref)(); /* refcount adjust function */ + uint_t ext_size; /* size of buffer, for ext_free */ +}; + +struct mbuf { + struct m_hdr m_hdr; + union { + struct { + struct pkthdr MH_pkthdr; /* M_PKTHDR set */ + union { + struct m_ext MH_ext; /* M_EXT set */ + char MH_databuf[MHLEN]; + } MH_dat; + } MH; + char M_databuf[MLEN]; /* !M_PKTHDR, !M_EXT */ + } M_dat; +}; +#define m_next m_hdr.mh_next +#define m_len m_hdr.mh_len +#define m_data m_hdr.mh_data +#define m_type m_hdr.mh_type +#define m_flags m_hdr.mh_flags +#define m_nextpkt m_hdr.mh_nextpkt +#define m_act m_nextpkt +#define m_pkthdr M_dat.MH.MH_pkthdr +#define m_ext M_dat.MH.MH_dat.MH_ext +#define m_pktdat M_dat.MH.MH_dat.MH_databuf +#define m_dat M_dat.M_databuf + +/* mbuf flags */ +#define M_EXT 0x0001 /* has associated external storage */ +#define M_PKTHDR 0x0002 /* start of record */ +#define M_EOR 0x0004 /* end of record */ + +/* mbuf pkthdr flags, also in m_flags */ +#define M_BCAST 0x0100 /* send/received as link-level broadcast */ +#define M_MCAST 0x0200 /* send/received as link-level multicast */ + +/* flags copied when copying m_pkthdr */ +#define M_COPYFLAGS (M_PKTHDR|M_EOR|M_BCAST|M_MCAST) + +/* XXX probably only need MT_DATA */ + +/* mbuf types */ +#define MT_FREE 0 /* should be on free list */ +#define MT_DATA 1 /* dynamic (data) allocation */ +#define MT_HEADER 2 /* packet header */ +#define MT_SOCKET 3 /* socket structure */ +#define MT_PCB 4 /* protocol control block */ +#define MT_RTABLE 5 /* routing tables */ +#define MT_HTABLE 6 /* IMP host tables */ +#define MT_ATABLE 7 /* address resolution tables */ +#define MT_SONAME 8 /* socket name */ +#define MT_SOOPTS 10 /* socket options */ +#define MT_FTABLE 11 /* fragment reassembly header */ +#define MT_RIGHTS 12 /* access rights */ +#define MT_IFADDR 13 /* interface address */ +#define MT_CONTROL 14 /* extra-data protocol message */ +#define MT_OOBDATA 15 /* expedited data */ + +/* + * flags to malloc: PBSHORTCUT + */ +#define M_WAITOK 0x0000 +#define M_NOWAIT 0x0001 + +/* flags to m_get/MGET */ +#define M_DONTWAIT M_NOWAIT +#define M_WAIT M_WAITOK + + +/* + * mbuf allocation/deallocation macros: + * + * MGET(struct mbuf *m, int how, int type) + * allocates an mbuf and initializes it to contain internal data. + * + * MGETHDR(struct mbuf *m, int how, int type) + * allocates an mbuf and initializes it to contain a packet header + * and internal data. + */ + +#define MGET(m, how, type) { \ + m = MEM_ZALLOC("mbuf", sizeof (struct mbuf)); \ + (m)->m_next = (struct mbuf *)NULL; \ + (m)->m_nextpkt = (struct mbuf *)NULL; \ + (m)->m_data = (m)->m_dat; \ + (m)->m_flags = 0; \ + (m)->m_type = (short)(type); \ +} + +#define MGETHDR(m, how, type) { \ + m = MEM_ZALLOC("mbuf", sizeof (struct mbuf)); \ + (m)->m_type = (MT_HEADER); \ + (m)->m_next = (struct mbuf *)NULL; \ + (m)->m_nextpkt = (struct mbuf *)NULL; \ + (m)->m_data = (m)->m_pktdat; \ + (m)->m_flags = M_PKTHDR; \ +} + +extern int mclref(); +extern int mclrefnoop(); +#define MCLGET(m, how) \ + { \ + (m)->m_ext.ext_buf = MEM_ZALLOC("mbuf", MCLBYTES); \ + (m)->m_data = (m)->m_ext.ext_buf; \ + (m)->m_flags |= M_EXT; \ + (m)->m_ext.ext_size = MCLBYTES; \ + (m)->m_ext.ext_ref = mclref; \ + } + +/* + * MFREE(struct mbuf *m, struct mbuf *nn) + * Free a single mbuf and associated external storage. + * Place the successor, if any, in nn. + */ +#define MFREE(m, nn) \ + { \ + if ((m)->m_flags & M_EXT) { \ + (*((m)->m_ext.ext_ref))((m)->m_ext.ext_buf, \ + (m)->m_ext.ext_size, -1); \ + (m)->m_ext.ext_buf = 0; \ + } \ + (nn) = (m)->m_next; \ + (m)->m_next = 0; \ + MEM_FREE("mbuf", m); \ + } + + + +/* + * As above, for mbufs allocated with m_gethdr/MGETHDR + * or initialized by M_COPY_PKTHDR. + */ +#define MH_ALIGN(m, len) \ + { (m)->m_data += (MHLEN - (len)) &~ (sizeof (int32_t) - 1); } + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_MBUF_H */ diff --git a/usr/src/uts/common/smbsrv/mlrpc.h b/usr/src/uts/common/smbsrv/mlrpc.h new file mode 100644 index 000000000000..fe82cf01dfd9 --- /dev/null +++ b/usr/src/uts/common/smbsrv/mlrpc.h @@ -0,0 +1,414 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_MLRPC_H +#define _SMBSRV_MLRPC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * MSRPC Like RPC (MLRPC) is an MSRPC compatible implementation of OSF + * DCE RPC. DCE RPC is derived from the Apollo Network Computing + * Architecture (NCA) RPC implementation. This implementation is based + * on the X/Open DCE: Remote Procedure Call specification. The main + * MSRPC compatibility issue is the use of Unicode strings. This work + * was originally based on the X/Open DCE Remote Procedure Call CAE + * 1994 Specification. The current DCE RPC specification is detailed + * below. + * + * CAE Specification (1997) + * DCE 1.1: Remote Procedure Call + * Document Number: C706 + * The Open Group + * ogspecs@opengroup.org + */ + +/* + * Layering + * + * This shows the software layers of the DCE RPC system compared against + * ONC SUN RPC. + * + * MLRPC Layers Sun RPC Layers Remark + * +---------------+ +---------------+ +---------------+ + * +---------------+ +---------------+ + * | Application | | Application | The application + * +---------------+ +---------------+ + * | Hand coded | | RPCGEN gen'd | Where the real + * | client/server | | client/server | work happens + * | srvsvc.ndl | | *_svc.c *_clnt| + * | srvsvc.c | | | + * +---------------+ +---------------+ + * | RPC Library | | RPC Library | Calls/Return + * | mlrpc_*.c | | | Binding/PMAP + * +---------------+ +---------------+ + * | RPC Protocol | | RPC Protocol | Headers, Auth, + * | mlrpcpdu.ndl | | | + * +---------------+ +---------------+ + * | IDL gen'd | | RPCGEN gen'd | Aggregate + * | NDR stubs | | XDR stubs | Composition + * | *__ndr.c | | *_xdr.c | + * +---------------+ +---------------+ + * | NDR Represen | | XDR Represen | Byte order, padding + * +---------------+ +---------------+ + * | Packet Heaps | | Network Conn | BIG DIFF: DCERPC does + * | mlndo_*.c | | clnt_{tcp,udp}| not talk directly to + * +---------------+ +---------------+ network. + * + * There are two major differences between the DCE RPC and ONC RPC: + * + * 1. MLRPC only generates or processes packets from buffers. Other + * layers must take care of packet transmission and reception. + * The packet heaps are managed through a simple interface provided + * by the Network Data Representation (NDR) module, called struct + * mlndr_stream. mlndo_*.c modules implement the different flavors + * (operations) of packet heaps. + * + * ONC RPC communicates directly with the network. You have to do + * something special for the RPC packet to be placed in a buffer + * rather than sent to the wire. + * + * 2. MLRPC uses application provided heaps to support operations. + * A heap is a single, monolithic chunk of memory that MLRPC manages + * as it allocates. When the operation and its result are done, the + * heap is disposed of as a single item. The mlrpc_xaction, which + * is the anchor of most operations, contains the necessary book- + * keeping for the heap. + * + * ONC RPC uses malloc() liberally throughout its run-time system. + * To free results, ONC RPC supports an XDR_FREE operation that + * traverses data structures freeing memory as it goes, whether + * it was malloc'd or not. + */ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Dispatch Return Code (DRC) + * + * 0x8000 15:01 Set to indicate a fault, clear indicates status + * 0x7F00 08:07 Status/Fault specific + * 0x00FF 00:08 MLRPC_PTYPE_... of PDU, 0xFF for header + */ +#define MLRPC_DRC_MASK_FAULT 0x8000 +#define MLRPC_DRC_MASK_SPECIFIER 0xFF00 +#define MLRPC_DRC_MASK_PTYPE 0x00FF + +/* Usual stuff */ +#define MLRPC_DRC_OK 0x0000 + +/* Fake PTYPEs for MLRPC_DRC */ +#define MLRPC_DRC_PTYPE_RPCHDR 0x00FF +#define MLRPC_DRC_PTYPE_API 0x00AA + +/* DRC Recognizers */ +#define MLRPC_DRC_IS_OK(DRC) (((DRC)&MLRPC_DRC_MASK_SPECIFIER) == 0) +#define MLRPC_DRC_IS_FAULT(DRC) (((DRC)&MLRPC_DRC_MASK_FAULT) != 0) + +/* + * (Un)Marshalling category specifiers + */ +#define MLRPC_DRC_FAULT_MODE_MISMATCH 0x8100 +#define MLRPC_DRC_RECEIVED 0x0200 +#define MLRPC_DRC_FAULT_RECEIVED_RUNT 0x8300 +#define MLRPC_DRC_FAULT_RECEIVED_MALFORMED 0x8400 +#define MLRPC_DRC_DECODED 0x0500 +#define MLRPC_DRC_FAULT_DECODE_FAILED 0x8600 +#define MLRPC_DRC_ENCODED 0x0700 +#define MLRPC_DRC_FAULT_ENCODE_FAILED 0x8800 +#define MLRPC_DRC_FAULT_ENCODE_TOO_BIG 0x8900 +#define MLRPC_DRC_SENT 0x0A00 +#define MLRPC_DRC_FAULT_SEND_FAILED 0x8B00 + +/* + * Resource category specifier + */ +#define MLRPC_DRC_FAULT_RESOURCE_1 0x9100 +#define MLRPC_DRC_FAULT_RESOURCE_2 0x9200 + +/* + * Parameters. Usually #define'd with useful alias + */ +#define MLRPC_DRC_FAULT_PARAM_0_INVALID 0xC000 +#define MLRPC_DRC_FAULT_PARAM_0_UNIMPLEMENTED 0xD000 +#define MLRPC_DRC_FAULT_PARAM_1_INVALID 0xC100 +#define MLRPC_DRC_FAULT_PARAM_1_UNIMPLEMENTED 0xD100 +#define MLRPC_DRC_FAULT_PARAM_2_INVALID 0xC200 +#define MLRPC_DRC_FAULT_PARAM_2_UNIMPLEMENTED 0xD200 +#define MLRPC_DRC_FAULT_PARAM_3_INVALID 0xC300 +#define MLRPC_DRC_FAULT_PARAM_3_UNIMPLEMENTED 0xD300 + +#define MLRPC_DRC_FAULT_OUT_OF_MEMORY 0xF000 + +/* RPCHDR */ +#define MLRPC_DRC_FAULT_RPCHDR_PTYPE_INVALID 0xC0FF /* PARAM_0_INVALID */ +#define MLRPC_DRC_FAULT_RPCHDR_PTYPE_UNIMPLEMENTED 0xD0FF /* PARAM_0_UNIMP */ + +/* Request */ +#define MLRPC_DRC_FAULT_REQUEST_PCONT_INVALID 0xC000 /* PARAM_0_INVALID */ +#define MLRPC_DRC_FAULT_REQUEST_OPNUM_INVALID 0xC100 /* PARAM_1_INVALID */ + +/* Bind */ +#define MLRPC_DRC_FAULT_BIND_PCONT_BUSY 0xC00B /* PARAM_0_INVALID */ +#define MLRPC_DRC_FAULT_BIND_UNKNOWN_SERVICE 0xC10B /* PARAM_1_INVALID */ +#define MLRPC_DRC_FAULT_BIND_NO_SLOTS 0x910B /* RESOURCE_1 */ +#define MLRPC_DRC_BINDING_MADE 0x000B /* OK */ + +/* API */ +#define MLRPC_DRC_FAULT_API_SERVICE_INVALID 0xC0AA /* PARAM_0_INVALID */ +#define MLRPC_DRC_FAULT_API_BIND_NO_SLOTS 0x91AA /* RESOURCE_1 */ +#define MLRPC_DRC_FAULT_API_OPNUM_INVALID 0xC1AA /* PARAM_1_INVALID */ + +struct mlrpc_xaction; + +typedef struct mlrpc_stub_table { + int (*func)(void *param, struct mlrpc_xaction *mreq); + unsigned short opnum; +} mlrpc_stub_table_t; + +typedef struct mlrpc_service { + char *name; + char *desc; + char *endpoint; + char *sec_addr_port; + char *abstract_syntax_uuid; + int abstract_syntax_version; + char *transfer_syntax_uuid; + int transfer_syntax_version; + unsigned bind_instance_size; + int (*bind_req)(); + int (*unbind_and_close)(); + int (*call_stub)(struct mlrpc_xaction *mreq); + struct ndr_typeinfo *interface_ti; + struct mlrpc_stub_table *stub_table; +} mlrpc_service_t; + +/* + * The list of bindings is anchored at a connection. Nothing in the + * RPC mechanism allocates them. Binding elements which have service==0 + * indicate free elements. When a connection is instantiated, at least + * one free binding entry should also be established. Something like + * this should suffice for most (all) situations: + * + * struct connection { + * .... + * struct mlrpc_binding *binding_list_head; + * struct mlrpc_binding binding_pool[N_BINDING_POOL]; + * .... + * }; + * + * init_connection(struct connection *conn) { + * .... + * mlrpc_binding_pool_initialize(&conn->binding_list_head, + * conn->binding_pool, N_BINDING_POOL); + */ +struct mlrpc_binding { + struct mlrpc_binding *next; + mlrpc_p_context_id_t p_cont_id; + unsigned char which_side; + void * context; + struct mlrpc_service *service; + void *instance_specific; +}; + +#define MLRPC_BIND_SIDE_CLIENT 1 +#define MLRPC_BIND_SIDE_SERVER 2 + +#define MLRPC_BINDING_TO_SPECIFIC(BINDING, TYPE) \ + ((TYPE *) (BINDING)->instance_specific) + +/* + * mlrpc_heap.c + * + * A number of heap areas are used during marshalling and unmarshalling. + * Under some circumstances these areas can be discarded by the library + * code, i.e. on the server side before returning to the client and on + * completion of a client side bind. In the case of a client side RPC + * call, these areas must be preserved after an RPC returns to give the + * caller time to take a copy of the data. In this case the client must + * call mlrpc_c_free_heap to free the memory. + * + * The heap management data definition looks a bit like this: + * + * heap -> +---------------+ +------------+ + * | iovec[0].base | --> | data block | + * | iovec[0].len | +------------+ + * +---------------+ + * :: + * :: + * iov -> +---------------+ +------------+ + * | iovec[n].base | --> | data block | + * | iovec[n].len | +------------+ + * +---------------+ ^ ^ + * | | + * next ----------------------+ | + * top -----------------------------------+ + * + */ + +/* + * Setting MAXIOV to 384 will use ((8 * 384) + 16) = 3088 bytes + * of the first heap block. + */ +#define MLRPC_HEAP_MAXIOV 384 +#define MLRPC_HEAP_BLKSZ 4096 + +typedef struct mlrpc_heap { + struct iovec iovec[MLRPC_HEAP_MAXIOV]; + struct iovec *iov; + int iovcnt; + char *top; + char *next; +} mlrpc_heap_t; + +/* + * To support the client-side heap preserve functionality. + */ +#define MLRPC_HRST_PRESERVED 1 + +typedef struct mlrpc_heapref { + mlrpc_heap_t *heap; + char *recv_pdu_buf; + char *send_pdu_buf; + unsigned int state; +} mlrpc_heapref_t; + +/* + * Alternate varying/conformant string definition + * - for non-null-terminated strings. + */ +struct mlrpc_vcb { + /* + * size_is (actually a copy of length_is) will + * be inserted here by the marshalling library. + */ + DWORD vc_first_is; + DWORD vc_length_is; + WORD buffer[ANY_SIZE_ARRAY]; +}; + +typedef struct mlrpc_vcbuf { + WORD wclen; + WORD wcsize; + struct mlrpc_vcb *vcb; +} mlrpc_vcbuf_t; + +mlrpc_heap_t *mlrpc_heap_create(void); +void mlrpc_heap_destroy(mlrpc_heap_t *); +void *mlrpc_heap_malloc(mlrpc_heap_t *, unsigned); +void *mlrpc_heap_strsave(mlrpc_heap_t *, char *); +void mlrpc_heap_mkvcs(mlrpc_heap_t *, char *, mlrpc_vcbuf_t *); +int mlrpc_heap_used(mlrpc_heap_t *); +int mlrpc_heap_avail(mlrpc_heap_t *); + +#define MLRPC_HEAP_MALLOC(MXA, SIZE) \ + mlrpc_heap_malloc((MXA)->heap, SIZE) + +#define MLRPC_HEAP_NEW(MXA, TYPE) \ + mlrpc_heap_malloc((MXA)->heap, sizeof (TYPE)) + +#define MLRPC_HEAP_NEWN(MXA, TYPE, N) \ + mlrpc_heap_malloc((MXA)->heap, sizeof (TYPE)*(N)) + +#define MLRPC_HEAP_STRSAVE(MXA, STR) \ + mlrpc_heap_strsave((MXA)->heap, (STR)) + +struct mlrpc_xaction { + unsigned short ptype; /* just handy, hi bits spcl */ + unsigned short opnum; /* for requests */ + struct mlndr_stream recv_mlnds; + mlrpcconn_hdr_t recv_hdr; + struct mlndr_stream send_mlnds; + mlrpcconn_hdr_t send_hdr; + struct mlrpc_binding *binding; /* what we're using */ + struct mlrpc_binding *binding_list; /* from connection */ + mlrpc_heap_t *heap; + struct mlsvc_rpc_context *context; +}; + +struct mlrpc_client { + int (*xa_init)(struct mlrpc_client *, struct mlrpc_xaction *, + mlrpc_heap_t *); + int (*xa_exchange)(struct mlrpc_client *, struct mlrpc_xaction *); + int (*xa_read)(struct mlrpc_client *, struct mlrpc_xaction *); + int (*xa_preserve)(struct mlrpc_client *, struct mlrpc_xaction *, + mlrpc_heapref_t *); + int (*xa_destruct)(struct mlrpc_client *, struct mlrpc_xaction *); + void (*xa_release)(struct mlrpc_client *, mlrpc_heapref_t *); + + void *context; + struct mlrpc_binding *binding_list; + uint32_t next_call_id; + unsigned next_p_cont_id; +}; + +/* mlndo.c */ +int mlnds_initialize(struct mlndr_stream *, unsigned, int, mlrpc_heap_t *); +void mlnds_destruct(struct mlndr_stream *); + +/* mlrpc_client.c */ +int mlrpc_c_bind(struct mlrpc_client *, char *, struct mlrpc_binding **); +int mlrpc_c_call(struct mlrpc_binding *, int, void *, mlrpc_heapref_t *); +int mlrpc_c_free_heap(struct mlrpc_binding *, mlrpc_heapref_t *); + +/* mlrpc_encdec.c */ +int mlrpc_encode_decode_common(struct mlrpc_xaction *, int, unsigned, + struct ndr_typeinfo *, void *); +int mlrpc_decode_call(struct mlrpc_xaction *, void *); +int mlrpc_encode_return(struct mlrpc_xaction *, void *); +int mlrpc_encode_call(struct mlrpc_xaction *, void *); +int mlrpc_decode_return(struct mlrpc_xaction *, void *); +int mlrpc_decode_pdu_hdr(struct mlrpc_xaction *); +int mlrpc_encode_pdu_hdr(struct mlrpc_xaction *); +void mlrpc_decode_frag_hdr(struct mlndr_stream *, mlrpcconn_common_header_t *); +unsigned mlrpc_bind_ack_hdr_size(struct mlrpcconn_bind_ack_hdr *); + +/* mlrpc_svc.c */ +struct mlrpc_stub_table *mlrpc_find_stub_in_svc(struct mlrpc_service *, int); +struct mlrpc_service *mlrpc_find_service_by_name(const char *); +struct mlrpc_service *mlrpc_find_service_by_uuids(mlrpc_uuid_t *, int, + mlrpc_uuid_t *, int); +int mlrpc_register_service(struct mlrpc_service *); +void mlrpc_unregister_service(struct mlrpc_service *); +void mlrpc_uuid_to_str(mlrpc_uuid_t *, char *); +int mlrpc_str_to_uuid(char *, mlrpc_uuid_t *); +void mlrpc_binding_pool_initialize(struct mlrpc_binding **, + struct mlrpc_binding pool[], unsigned); +struct mlrpc_binding *mlrpc_find_binding(struct mlrpc_xaction *, + mlrpc_p_context_id_t); +struct mlrpc_binding *mlrpc_new_binding(struct mlrpc_xaction *); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_MLRPC_H */ diff --git a/usr/src/uts/common/smbsrv/mlsvc.h b/usr/src/uts/common/smbsrv/mlsvc.h new file mode 100644 index 000000000000..cb13cf57332d --- /dev/null +++ b/usr/src/uts/common/smbsrv/mlsvc.h @@ -0,0 +1,233 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_MLSVC_H +#define _SMBSRV_MLSVC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * MLSVC RPC layer public interface definitions. + */ + +#include +#include +#include + +#include +#include + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * RPC strings + * + * DCE RPC strings (CAE section 14.3.4) are represented as varying or + * varying and conformant one-dimensional arrays. Characters can be + * single-byte or multi-byte as long as all characters conform to a + * fixed element size, i.e. UCS-2 is okay but UTF-8 is not a valid + * DCE RPC string format. The string is terminated by a null character + * of the appropriate element size. + * + * MSRPC strings are always varying and conformant format and not null + * terminated. This format uses the size_is, first_is and length_is + * attributes (CAE section 4.2.18). + * + * typedef struct mlrpc_string { + * DWORD size_is; + * DWORD first_is; + * DWORD length_is; + * wchar_t string[ANY_SIZE_ARRAY]; + * } mlrpc_string_t; + * + * The size_is attribute is used to specify the number of data elements + * in each dimension of an array. + * + * The first_is attribute is used to define the lower bound for + * significant elements in each dimension of an array. For strings + * this is always 0. + * + * The length_is attribute is used to define the number of significant + * elements in each dimension of an array. For strings this is typically + * the same as size_is. Although it might be (size_is - 1) if the string + * is null terminated. + * + * In MSRPC, Unicode strings are not null terminated. This means + * that the recipient has to manually null-terminate the string after + * it has been unmarshalled. Note that there is often a wide-char pad + * following a string. Although the padding sometimes contains zero, + * it's not guaranteed. + * + * 4 bytes 4 bytes 4 bytes 2bytes 2bytes 2bytes 2bytes + * +---------+---------+---------+------+------+------+------+ + * |size_is |first_is |length_is| char | char | char | char | + * +---------+---------+---------+------+------+------+------+ + * + * The problem is that some strings are null terminated. This seems + * to conflict with the statement above that Unicode strings are not + * null terminated, which may be a historical thing from earlier + * implementations or it may be that different services do different + * things. So there is an additional string wrapper with two more + * fields used in some RPC structures as shown below (LPTSTR is + * automatically converted to mlrpc_string by the NDR marshalling). + * + * typedef struct ms_string { + * WORD length; + * WORD maxlen; + * LPTSTR str; + * } ms_string_t; + * + * Here, length is the array length in bytes excluding any terminating + * null bytes and maxlen is the array length in bytes including null + * terminator bytes. + */ +typedef struct mlsvc_string { + WORD length; + WORD maxlen; + LPTSTR str; +} mlsvc_string_t; + +/* + * The maximum number of domains (NT limit). + */ +#define MLSVC_DOMAIN_MAX 32 + +/* + * Some buffer size limits. I don't know if these are definitive + * limits for NT but these numbers appear in various places. + */ +#define MLSVC_DOMAIN_NAME_MAX 32 +#define MLSVC_ACCOUNT_NAME_MAX 32 +#define MLSVC_CLIENT_NAME_MAX 48 + +/* 32-byte machine account password (null-terminated) */ +#define MLSVC_MACHINE_ACCT_PASSWD_MAX 32 + 1 + +/* + * Status code returned from enumeration RPCs to indicate + * that the server has no more data. Normally returned at + * severity level ERROR_SEVERITY_WARNING. + */ +#define MLSVC_NO_MORE_DATA 0x1A + +/* + * IPC connection types, used to indicate the type of session + * required for a subsequent series of requests. + */ +#define MLSVC_IPC_ANON 0x00 +#define MLSVC_IPC_USER 0x01 +#define MLSVC_IPC_ADMIN 0x02 + +#define MLSVC_ANON_USER "IPC$" + +char *mlsvc_ipc_name(int ipc_type, char *username); + +/* + * Passthrough negotiation and authentication interface. + * + * NT supports two forms of password: a Lanman (case-insensitive) + * password and an NT (case-sensitive) password. If either of the + * passwords is not available its pointer and length should be set + * to zero. The session key and vc number are required to validate + * the encrypted passwords. + */ + +int mlsvc_anonymous_logon(char *domain_controller, char *domain_name, + char **username); +int mlsvc_user_logon(char *domain_controller, char *domain_name, + char *username, char *password); +int mlsvc_admin_logon(char *domain_controller, char *domain_name); +int mlsvc_echo(char *server); +int mlsvc_open_pipe(char *hostname, char *domain, char *username, + char *pipename); +int mlsvc_close_pipe(int fid); +void mlsvc_nt_password_hash(char *result, char *password); +int mlsvc_encrypt_nt_password(char *password, char *key, int keylen, char *out, + int outmax); +DWORD mlsvc_validate_user(char *server, char *domain, char *username, + char *password); +int mlsvc_locate_domain_controller(char *domain); + +/* + * RPC request processing interface (mlsvc_server.c). + */ +#define MLSVC_MAX_IOVEC 512 + +typedef struct mlrpc_frag { + struct mlrpc_frag *next; + struct mbuf *mhead; + uint32_t length; +} mlrpc_frag_t; + +typedef struct mlsvc_stream { + mlrpc_frag_t *head; + mlrpc_frag_t *tail; + mlrpc_frag_t *pending; + unsigned int nfrag; + struct uio uio; + struct iovec iovec[MLSVC_MAX_IOVEC]; +} mlsvc_stream_t; + +typedef struct mlsvc_pipe { + kmutex_t mutex; + kcondvar_t cv; + uint32_t busy; + uint32_t fid; + char *pipe_name; + mlsvc_stream_t input; + uchar_t *output; + int32_t outlen; +} mlsvc_pipe_t; + +int mlsvc_rpc_process( + smb_pipe_t *inpipe, + smb_pipe_t **outpipe, + smb_dr_user_ctx_t *user_ctx); + +struct mlsvc_rpc_context *mlsvc_lookup_context(int fid); + +void mlsvc_rpc_release(int fid); +int mlsvc_session_native_values(int fid, int *remote_os, int *remote_lm, + int *pdc_type); +void mlsvc_rpc_report_status(int opnum, DWORD status); + +/* + * This is a temporary location for this NETLOGON stuff. + */ +typedef int (*mlsvc_locate_pdc_t)(char *domain); +void mlsvc_install_pdc_cb(mlsvc_locate_pdc_t locate_pdc_cb); + +#ifdef __cplusplus +} +#endif + + +#endif /* _SMBSRV_MLSVC_H */ diff --git a/usr/src/uts/common/smbsrv/mlsvc_util.h b/usr/src/uts/common/smbsrv/mlsvc_util.h new file mode 100644 index 000000000000..e9ababec1567 --- /dev/null +++ b/usr/src/uts/common/smbsrv/mlsvc_util.h @@ -0,0 +1,244 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_MLSVC_UTIL_H +#define _SMBSRV_MLSVC_UTIL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * MLSVC RPC interface and utility function definitions. + */ + +#include +#include +#include +#include +#include +#include + +#ifndef _KERNEL +#include +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Predefined global RIDs. + */ +#define MLSVC_DOMAIN_GROUP_RID_ADMINS 0x00000200L +#define MLSVC_DOMAIN_GROUP_RID_USERS 0x00000201L +#define MLSVC_DOMAIN_GROUP_RID_GUESTS 0x00000202L +#define MLSVC_DOMAIN_GROUP_RID_COMPUTERS 0x00000203L +#define MLSVC_DOMAIN_GROUP_RID_CONTROLLERS 0x00000204L +#define MLSVC_DOMAIN_GROUP_RID_CERT_ADMINS 0x00000205L +#define MLSVC_DOMAIN_GROUP_RID_SCHEMA_ADMINS 0x00000206L + +/* + * Predefined local alias RIDs. + */ +#define MLSVC_LOCAL_GROUP_RID_ADMINS 0x00000220L +#define MLSVC_LOCAL_GROUP_RID_USERS 0x00000221L +#define MLSVC_LOCAL_GROUP_RID_GUESTS 0x00000222L +#define MLSVC_LOCAL_GROUP_RID_POWER_USERS 0x00000223L +#define MLSVC_LOCAL_GROUP_RID_ACCOUNT_OPS 0x00000224L +#define MLSVC_LOCAL_GROUP_RID_SERVER_OPS 0x00000225L +#define MLSVC_LOCAL_GROUP_RID_PRINT_OPS 0x00000226L +#define MLSVC_LOCAL_GROUP_RID_BACKUP_OPS 0x00000227L +#define MLSVC_LOCAL_GROUP_RID_REPLICATOR 0x00000228L + +/* + * All predefined local group RIDs belong + * to a special domain called BUILTIN. + */ +#define MLSVC_BUILTIN_DOMAIN_NAME "BUILTIN" +#define MLSVC_BUILTIN_DOMAIN_SIDSTRLEN 8 + +/* + * Universal and NT well-known SIDs + */ +#define MLSVC_NULL_SIDSTR "S-1-0-0" +#define MSLVC_WORLD_SIDSTR "S-1-1-0" +#define MSLVC_LOCAL_SIDSTR "S-1-2-0" +#define MSLVC_CREATOR_OWNER_ID_SIDSTR "S-1-3-0" +#define MSLVC_CREATOR_GROUP_ID_SIDSTR "S-1-3-1" +#define MSLVC_CREATOR_OWNER_SERVER_ID_SIDSTR "S-1-3-2" +#define MSLVC_CREATOR_GROUP_SERVER_ID_SIDSTR "S-1-3-3" +#define MSLVC_NON_UNIQUE_IDS_SIDSTR "S-1-4" +#define MLSVC_NT_AUTHORITY_SIDSTR "S-1-5" +#define MLSVC_DIALUP_SIDSTR "S-1-5-1" +#define MLSVC_NETWORK_SIDSTR "S-1-5-2" +#define MLSVC_BATCH_SIDSTR "S-1-5-3" +#define MLSVC_INTERACTIVE_SIDSTR "S-1-5-4" +#define MLSVC_SERVICE_SIDSTR "S-1-5-6" +#define MLSVC_ANONYMOUS_LOGON_SIDSTR "S-1-5-7" +#define MLSVC_PROXY_SIDSTR "S-1-5-8" +#define MLSVC_SERVER_LOGON_SIDSTR "S-1-5-9" +#define MLSVC_SELF_SIDSTR "S-1-5-10" +#define MLSVC_AUTHENTICATED_USER_SIDSTR "S-1-5-11" +#define MLSVC_RESTRICTED_CODE_SIDSTR "S-1-5-12" +#define MLSVC_NT_LOCAL_SYSTEM_SIDSTR "S-1-5-18" +#define MLSVC_NT_NON_UNIQUE_SIDSTR "S-1-5-21" +#define MLSVC_BUILTIN_DOMAIN_SIDSTR "S-1-5-32" + +int mlsvc_lookup_name(char *domain, char *name, nt_sid_t **sid); +int mlsvc_lookup_sid(nt_sid_t *sid, char *buf, int bufsize); + +smb_userinfo_t *mlsvc_alloc_user_info(void); +void mlsvc_free_user_info(smb_userinfo_t *user_info); +void mlsvc_release_user_info(smb_userinfo_t *user_info); +void mlsvc_setadmin_user_info(smb_userinfo_t *user_info); +char *mlsvc_sid_name_use(unsigned int snu_id); + +/* + * The definition of a local unique id (LUID). This is an opaque id + * used by servers to identify local resources, such as privileges. + * A client will use lookup functions to translate the LUID to a + * more general, machine independent form; like a string. + */ +struct ms_luid { + DWORD low_part; + DWORD high_part; +}; + +/* + * As with SIDs, this is the generic, interface independent string + * definition. + */ +struct ms_string_desc { + WORD length; + WORD allosize; + LPTSTR str; +}; +typedef struct ms_string_desc ms_string_t; + +int mlsvc_string_save(ms_string_t *ms, char *str, struct mlrpc_xaction *mxa); +nt_sid_t *mlsvc_sid_save(nt_sid_t *sid, struct mlrpc_xaction *mxa); + +/* + * This is the generic, interface independent handle definition. + */ +typedef struct ms_handle { + DWORD handle[5]; +} ms_handle_t; + +/* + * List of interface specifications: can be used to identify the + * sub-system to which a handle is assigned. The handle management + * library doesn't check or care about the ifspec value. + */ +typedef enum ms_ifspec { + MLSVC_IFSPEC_NULL, + MLSVC_IFSPEC_LSAR, + MLSVC_IFSPEC_SAMR, + MLSVC_IFSPEC_WINREG, + MLSVC_IFSPEC_SVCCTL, + MLSVC_IFSPEC_SPOOLSS, + MLSVC_IFSPEC_LOGR, + MLSVC_IFSPEC_LLSR, + MLSVC_NUM_IFSPECS +} ms_ifspec_t; + +#define MLSVC_HANDLE_KEY_MAX 32 + +typedef struct ms_handle_desc { + struct ms_handle_desc *next; + ms_handle_t handle; + ms_ifspec_t ifspec; + char key[MLSVC_HANDLE_KEY_MAX]; + DWORD discrim; +} ms_handle_desc_t; + +ms_handle_t *mlsvc_get_handle(ms_ifspec_t ifspec, char *key, DWORD discrim); +int mlsvc_put_handle(ms_handle_t *handle); +int mlsvc_validate_handle(ms_handle_t *handle, char *key); +ms_handle_desc_t *mlsvc_lookup_handle(ms_handle_t *handle); + +/* + * The mlsvc_rpc_context structure provides the connection binding context + * for client RPC calls. This space must be provided by the client library + * for use by the underlying RPC library. Note that we need two binding + * pools per connection. + */ +#define CTXT_N_BINDING_POOL 2 + +struct mlsvc_rpc_context { + struct mlrpc_client cli; + int fid; + ms_handle_t *handle; + smb_dr_user_ctx_t *user_ctx; + smb_pipe_t *inpipe; /* used for winpipe */ + uint32_t inlen; /* inpipes */ + smb_pipe_t *outpipe; /* used for winpipe */ + uint32_t outcookie; /* for rpc_read and transact */ + uint32_t outlen; /* outpipes */ + int server_os; + int server_pdc; + WORD max_xmit_frag; + WORD max_recv_frag; + struct mlrpc_binding *binding; + struct mlrpc_binding binding_pool[CTXT_N_BINDING_POOL]; +}; + +/* + * Each RPC interface requires a context and each RPC call within that + * interface requires a handle. Handles are call specific, however, so + * a number of different handles may be used during a sequence of calls + * to a specific RPC interface. Contexts are interface specific so + * there is one per interface per thread of execution. This structure + * provides a handle to context relationship so that we know which + * context to use with any particular handle. + * + * The context contains a pointer to the top level handle for the + * interface, which is assigned during the bind. It's used when closing + * to detect when to free the context. + * + * I know this is really tacky but the elements in the descriptor are + * arranged so that a handle can be overlaid directly onto a descriptor. + * I probably won't do this but now you know - just in case you see it + * in the code. + */ +typedef struct mlsvc_rpc_desc { + ms_handle_t handle; + struct mlsvc_rpc_context *context; +} mlsvc_handle_t; + + +int mlsvc_rpc_bind(mlsvc_handle_t *handle, int fid, char *service); +int mlsvc_rpc_init(mlrpc_heapref_t *heapref); +int mlsvc_rpc_call(struct mlsvc_rpc_context *context, int opnum, void *params, + mlrpc_heapref_t *heapref); +void mlsvc_rpc_free(struct mlsvc_rpc_context *context, + mlrpc_heapref_t *heapref); +int mlsvc_is_null_handle(mlsvc_handle_t *handle); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_MLSVC_UTIL_H */ diff --git a/usr/src/uts/common/smbsrv/msgbuf.h b/usr/src/uts/common/smbsrv/msgbuf.h new file mode 100644 index 000000000000..465ba476def7 --- /dev/null +++ b/usr/src/uts/common/smbsrv/msgbuf.h @@ -0,0 +1,124 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_MSGBUF_H +#define _SMBSRV_MSGBUF_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Definition and interface for smb_msgbuf buffer management. The + * smb_msgbuf interface is typically used to encode or decode SMB + * data using sprintf/scanf style operations. It can also be used + * for general purpose encoding and decoding. + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * When unicode strings are decoded, the resultant UTF-8 strings are + * stored in dynamically allocated areas, which are held on a linked + * list anchored at smb_msgbuf.mlist. The list is deallocated by + * smb_msgbuf_term. + */ +typedef struct smb_msgbuf_mlist { + struct smb_msgbuf_mlist *next; + size_t size; +} smb_msgbuf_mlist_t; + +/* + * smb_smgbuf flags + * + * SMB_MSGBUF_UNICODE When there is a choice between unicode or ascii + * formatting, select unicode processing. + * SMB_MSGBUF_NOTERM Do not null terminate strings. + */ +#define SMB_MSGBUF_UNICODE 0x00000001 +#define SMB_MSGBUF_NOTERM 0x00000002 + +/* + * base: points to the beginning of the buffer + * end: points to the limit of the buffer. + * scan: points to the current offset. + * max: holds the number of bytes in the buffer. + * count: unused. + * mlist: anchors the dynamically allocated memory list. + * flags: see SMB_SMGBUF flags. + */ +typedef struct smb_msgbuf { + uint8_t *base; + uint8_t *end; + uint8_t *scan; + size_t count; + size_t max; + smb_msgbuf_mlist_t mlist; + uint32_t flags; +} smb_msgbuf_t; + +/* + * List of smb_msgbuf_decode and smb_msgbuf_encode return values. + */ +#define SMB_MSGBUF_SUCCESS 0 +#define SMB_MSGBUF_UNDERFLOW -1 +#define SMB_MSGBUF_OVERFLOW SMB_MSGBUF_UNDERFLOW +#define SMB_MSGBUF_INVALID_FORMAT -2 +#define SMB_MSGBUF_INVALID_HEADER -3 +#define SMB_MSGBUF_DATA_ERROR -4 + +/* + * smb_msgbuf_init must be called to associate the smb_msgbuf_t with + * a buffer before any encode or decode operations may be performed. + * + * smb_msgbuf_term must be called to free any dynamically allocated memory + * that was acquired during encode or decode operations. At this time + * the only operation that allocates memory is a unicode string decode. + * + * If there are no errors, smb_msgbuf_decode and smb_msgbuf_encode return + * the number of bytes decoded or encoded. If there is a problem they + * return -ve error codes. + */ +extern void smb_msgbuf_init(smb_msgbuf_t *, uint8_t *, size_t, uint32_t); +extern void smb_msgbuf_term(smb_msgbuf_t *); +extern int smb_msgbuf_decode(smb_msgbuf_t *, char *, ...); +extern int smb_msgbuf_encode(smb_msgbuf_t *, char *, ...); +extern size_t smb_msgbuf_used(smb_msgbuf_t *); +extern size_t smb_msgbuf_size(smb_msgbuf_t *); +extern uint8_t *smb_msgbuf_base(smb_msgbuf_t *); +extern void smb_msgbuf_word_align(smb_msgbuf_t *); +extern void smb_msgbuf_dword_align(smb_msgbuf_t *); +extern int smb_msgbuf_has_space(smb_msgbuf_t *, size_t); +extern void smb_msgbuf_fset(smb_msgbuf_t *, uint32_t); +extern void smb_msgbuf_fclear(smb_msgbuf_t *, uint32_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_MSGBUF_H */ diff --git a/usr/src/uts/common/smbsrv/ndl/dssetup.ndl b/usr/src/uts/common/smbsrv/ndl/dssetup.ndl new file mode 100644 index 000000000000..143d243e5a18 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ndl/dssetup.ndl @@ -0,0 +1,168 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DSSETUP_NDL_ +#define _DSSETUP_NDL_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Active Directory Service Setup + */ + +#include "ndrtypes.ndl" + + +#define DSSETUP_OPNUM_DsRoleGetPrimaryDomainInfo 0x00 +#define DSSETUP_OPNUM_DsRoleDnsNameToFlatName 0x01 +#define DSSETUP_OPNUM_DsRoleDcAsDc 0x02 +#define DSSETUP_OPNUM_DsRoleDcAsReplica 0x03 +#define DSSETUP_OPNUM_DsRoleDemoteDc 0x04 +#define DSSETUP_OPNUM_DsRoleGetDcOperationProgress 0x05 +#define DSSETUP_OPNUM_DsRoleGetDcOperationResults 0x06 +#define DSSETUP_OPNUM_DsRoleCancel 0x07 +#define DSSETUP_OPNUM_DsRoleServerSaveStateForUpgrade 0x08 +#define DSSETUP_OPNUM_DsRoleUpgradeDownlevelServer 0x09 +#define DSSETUP_OPNUM_DsRoleAbortDownlevelServerUpgrade 0x0a + +/* + * DS roles + */ +#define DS_ROLE_STANDALONE_WORKSTATION 0 +#define DS_ROLE_MEMBER_WORKSTATION 1 +#define DS_ROLE_STANDALONE_SERVER 2 +#define DS_ROLE_MEMBER_SERVER 3 +#define DS_ROLE_BACKUP_DC 4 +#define DS_ROLE_PRIMARY_DC 5 + +/* + * DS role flags + */ +#define DS_ROLE_PRIMARY_DS_RUNNING 0x00000001 +#define DS_ROLE_PRIMARY_DS_MIXED_MODE 0x00000002 +#define DS_ROLE_UPGRADE_IN_PROGRESS 0x00000004 +#define DS_ROLE_PRIMARY_DOMAIN_GUID_PRESENT 0x01000000 + +/* + * DS role upgrade + */ +#define DS_ROLE_NOT_UPGRADING 0 +#define DS_ROLE_UPGRADING 1 + +/* + * DS role previous + */ +#define DS_ROLE_PREVIOUS_UNKNOWN 0 +#define DS_ROLE_PREVIOUS_PRIMARY 1 +#define DS_ROLE_PREVIOUS_BACKUP 2 + +/* + * DS role state + */ +#define DS_ROLE_OP_IDLE 0 +#define DS_ROLE_OP_ACTIVE 1 +#define DS_ROLE_OP_NEEDS_REBOOT 2 + +/* + * DS role information levels + */ +#define DS_ROLE_BASIC_INFORMATION 1 +#define DS_ROLE_UPGRADE_STATUS 2 +#define DS_ROLE_OP_STATUS 3 + +struct dssetup_uuid { + DWORD data1; + WORD data2; + WORD data3; + BYTE data4[8]; +}; +typedef struct dssetup_uuid dssetup_uuid_t; + +/* + * DS_ROLE_BASIC_INFORMATION + */ +struct dssetup_DsRolePrimaryDomInfo1 { + DWORD role; + DWORD flags; + LPTSTR nt_domain; + LPTSTR dns_domain; + LPTSTR forest; + dssetup_uuid_t domain_guid; +}; + +/* + * DS_ROLE_UPGRADE_STATUS + */ +struct dssetup_DsRolePrimaryDomInfo2 { + DWORD upgrade_state; + DWORD previous_role; +}; + +/* + * DS_ROLE_OP_STATUS + */ +struct dssetup_DsRolePrimaryDomInfo3 { + DWORD status; +}; + +union dssetup_GetPrimaryDomainInfo_ru { + UNION_INFO_ENT(1,dssetup_DsRolePrimaryDomInfo); + UNION_INFO_ENT(2,dssetup_DsRolePrimaryDomInfo); + UNION_INFO_ENT(3,dssetup_DsRolePrimaryDomInfo); + DEFAULT char *nullptr; +}; + +struct dssetup_GetPrimaryDomainInfoRes { + DWORD address; + WORD switch_value; + SWITCH(switch_value) + union dssetup_GetPrimaryDomainInfo_ru ru; +}; + +OPERATION(DSSETUP_OPNUM_DsRoleGetPrimaryDomainInfo) +struct dssetup_DsRoleGetPrimaryDomainInfo { + IN WORD level; + OUT DWORD address; + OUT WORD switch_value; + SWITCH(level) + OUT union dssetup_GetPrimaryDomainInfo_ru ru; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * DSSETUP interface definiton. + *********************************************************************** + */ +INTERFACE(0) +union dssetup_interface { + CASE(DSSETUP_OPNUM_DsRoleGetPrimaryDomainInfo) + struct dssetup_DsRoleGetPrimaryDomainInfo GetPrimaryDomainInfo; +}; +typedef union dssetup_interface dssetup_interface_t; +EXTERNTYPEINFO(dssetup_interface) + +#endif /* _DSSETUP_NDL_ */ diff --git a/usr/src/uts/common/smbsrv/ndl/eventlog.ndl b/usr/src/uts/common/smbsrv/ndl/eventlog.ndl new file mode 100644 index 000000000000..3e412ceb0021 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ndl/eventlog.ndl @@ -0,0 +1,204 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MLSVC_LOGR_NDL_ +#define _MLSVC_LOGR_NDL_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + *********************************************************************** + * + * Event log RPC (EVENTLOG) interface definition. + * + *********************************************************************** + */ + +#include "ndrtypes.ndl" + +#define LOGR_OPNUM_EventLogClose 0x02 +#define LOGR_OPNUM_EventLogQueryCount 0x04 +#define LOGR_OPNUM_EventLogGetOldestRec 0x05 +#define LOGR_OPNUM_EventLogOpen 0x07 +#define LOGR_OPNUM_EventLogRead 0x0A + +#define LOGR_INFOLEN 200 +#define LOGR_RECBUFLEN 0x4000 + +struct logr_handle { + DWORD hand1; + DWORD hand2; + WORD hand3[2]; + BYTE hand4[8]; +}; + +typedef struct logr_handle logr_handle_t; + + +struct logr_string { + WORD length; + WORD allosize; + LPTSTR str; +}; +typedef struct logr_string logr_string_t; + + +struct logr_record { + DWORD Length1; // Length of full record + DWORD Reserved; // Used by the service + DWORD RecordNumber; // Absolute record number + DWORD TimeGenerated; // Seconds since 1-1-1970 + DWORD TimeWritten; // Seconds since 1-1-1970 + DWORD EventID; + WORD EventType; + WORD NumStrings; + WORD EventCategory; + WORD ReservedFlags; // For use with paired events (auditing) + DWORD ClosingRecordNumber; // For use with paired events (auditing) + DWORD StringOffset; // Offset from beginning of record + DWORD UserSidLength; + DWORD UserSidOffset; + DWORD DataLength; + DWORD DataOffset; + // + // Then follow: + // + // WCHAR SourceName[] null terminated + // WCHAR Computername[] null terminated + // SID UserSid + // WCHAR Strings[] + // BYTE Data[] + // CHAR Pad[] to DWORD + // DWORD Length; must be appear + BYTE info[LOGR_INFOLEN]; + DWORD Length2; +}; +typedef struct logr_record logr_record_t; + +/* + *********************************************************************** + * LOGR_OPNUM_EventLogClose + *********************************************************************** + */ +OPERATION(LOGR_OPNUM_EventLogClose) +struct logr_EventLogClose { + IN logr_handle_t handle; + OUT logr_handle_t result_handle; + OUT DWORD status; +}; + +/* + *********************************************************************** + * LOGR_OPNUM_EventLogQueryCount + *********************************************************************** + */ +OPERATION(LOGR_OPNUM_EventLogQueryCount) +struct logr_EventLogQueryCount { + IN logr_handle_t handle; + OUT DWORD rec_num; + OUT DWORD status; +}; + +/* + *********************************************************************** + * LOGR_OPNUM_EventLogGetOldestRec + *********************************************************************** + */ +OPERATION(LOGR_OPNUM_EventLogGetOldestRec) +struct logr_EventLogGetOldestRec { + IN logr_handle_t handle; + OUT DWORD oldest_rec; + OUT DWORD status; +}; + +/* + *********************************************************************** + * LOGR_OPNUM_EventLogOpen + *********************************************************************** + */ +OPERATION(LOGR_OPNUM_EventLogOpen) +struct logr_EventLogOpen { + IN DWORD *whatever; + IN logr_string_t log_name; + IN DWORD unknown1; + IN DWORD unknown2; + IN DWORD unknown3; + OUT logr_handle_t handle; + OUT DWORD status; +}; + +/* + *********************************************************************** + * LOGR_OPNUM_EventLogRead + *********************************************************************** + */ +union logr_read_u { + CASE(1024) BYTE rec[1024]; + DEFAULT BYTE recs[LOGR_RECBUFLEN]; +}; + + +struct logr_read_info { + DWORD nbytes_to_read; + SWITCH(nbytes_to_read) + union logr_read_u ru; +}; + +OPERATION(LOGR_OPNUM_EventLogRead) +struct logr_EventLogRead { + IN logr_handle_t handle; + IN DWORD read_flags; + IN DWORD rec_offset; + INOUT DWORD nbytes_to_read; +SWITCH (nbytes_to_read) + OUT union logr_read_u ru; + OUT DWORD sent_size; + OUT DWORD unknown; + OUT DWORD status; +}; + +/* + *********************************************************************** + * The EVENTLOG interface definition. + *********************************************************************** + */ +INTERFACE(0) +union logr_interface { + CASE(LOGR_OPNUM_EventLogClose) + struct logr_EventLogClose EventLogClose; + CASE(LOGR_OPNUM_EventLogQueryCount) + struct logr_EventLogQueryCount EventLogQueryCount; + CASE(LOGR_OPNUM_EventLogGetOldestRec) + struct logr_EventLogGetOldestRec EventLogGetOldestRec; + CASE(LOGR_OPNUM_EventLogOpen) + struct logr_EventLogOpen EventLogOpen; + CASE(LOGR_OPNUM_EventLogRead) + struct logr_EventLogRead EventLogRead; +}; +typedef union logr_interface logr_interface_t; +EXTERNTYPEINFO(logr_interface) + + +#endif /* _MLSVC_LOGR_NDL_ */ diff --git a/usr/src/uts/common/smbsrv/ndl/llsrpc.ndl b/usr/src/uts/common/smbsrv/ndl/llsrpc.ndl new file mode 100644 index 000000000000..33b3cccd7783 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ndl/llsrpc.ndl @@ -0,0 +1,110 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MLSVC_LLSR_NDL_ +#define _MLSVC_LLSR_NDL_ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * LLSRPC interface. + * + * 0x50 takes the 3a handle + DWORD, returns 2 DWORDs + * 0x3c + * 0x3f list of services? + * 0x3d unknown + * 0x3e unknown + * 0x4f + * 0x4d + * 0x4e + * 0x01 closes the handle obtained via 0x00 + * 0x3b closes the handle obtained via 0x3a + */ + +#include "ndrtypes.ndl" + +#define LLSR_OPNUM_Open 0x00 +#define LLSR_OPNUM_Close 0x01 +#define LLSR_OPNUM_Connect 0x3a +#define LLSR_OPNUM_Disconnect 0x3b +#define LLSR_OPNUM_Unknown3c 0x3c +#define LLSR_OPNUM_Unknown3d 0x3d +#define LLSR_OPNUM_Unknown3e 0x3e +#define LLSR_OPNUM_Unknown3f 0x3f +#define LLSR_OPNUM_Unknown4d 0x4d +#define LLSR_OPNUM_Unknown4e 0x4e +#define LLSR_OPNUM_Unknown4f 0x4f +#define LLSR_OPNUM_Unknown50 0x50 + + +struct llsr_handle { + DWORD opaque[5]; +}; +typedef struct llsr_handle llsr_handle_t; + + +OPERATION(LLSR_OPNUM_Open) +struct llsr_Open { + IN LPTSTR hostname; + OUT llsr_handle_t open_handle; + OUT DWORD status; +}; + + +OPERATION(LLSR_OPNUM_Close) +struct llsr_Close { + IN llsr_handle_t open_handle; + OUT DWORD status; +}; + + +OPERATION(LLSR_OPNUM_Connect) +struct llsr_Connect { + IN LPTSTR hostname; + OUT llsr_handle_t connect_handle; + OUT DWORD status; +}; + + +OPERATION(LLSR_OPNUM_Disconnect) +struct llsr_Disconnect { + IN llsr_handle_t connect_handle; + OUT llsr_handle_t echoed_handle; + OUT DWORD status; +}; + + +OPERATION(LLSR_OPNUM_Unknown50) +struct llsr_Unknown50 { + IN llsr_handle_t open_handle; + IN DWORD unknown1; /* 0x00000004 */ + OUT DWORD unknown2; /* 0x00000004 */ + OUT DWORD unknown3; /* 0x0000003F */ + OUT DWORD status; +}; + + +#endif /* _MLSVC_LLSR_NDL_ */ diff --git a/usr/src/uts/common/smbsrv/ndl/lsarpc.ndl b/usr/src/uts/common/smbsrv/ndl/lsarpc.ndl new file mode 100644 index 000000000000..102a3cf63d8d --- /dev/null +++ b/usr/src/uts/common/smbsrv/ndl/lsarpc.ndl @@ -0,0 +1,801 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MLSVC_LSA_NDL_ +#define _MLSVC_LSA_NDL_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + *********************************************************************** + * Local Security Authority RPC (LSARPC) interface definition. + *********************************************************************** + */ + +#include "ndrtypes.ndl" + + +#define LSARPC_OPNUM_CloseHandle 0x00 +#define LSARPC_OPNUM_EnumPrivileges 0x02 +#define LSARPC_OPNUM_QuerySecurityObject 0x03 +#define LSARPC_OPNUM_SetSecurityObject 0x04 +#define LSARPC_OPNUM_ChangePassword 0x05 +#define LSARPC_OPNUM_OpenPolicy 0x06 +#define LSARPC_OPNUM_QueryInfoPolicy 0x07 +#define LSARPC_OPNUM_SetInfoPolicy 0x08 +#define LSARPC_OPNUM_Unknown09 0x09 /* Crashed the DC */ +#define LSARPC_OPNUM_CreateAccount 0x0a +#define LSARPC_OPNUM_EnumerateAccounts 0x0b +#define LSARPC_OPNUM_CreateTrustedDomain 0x0c +#define LSARPC_OPNUM_EnumTrustedDomain 0x0d +#define LSARPC_OPNUM_LookupNames 0x0e +#define LSARPC_OPNUM_LookupSids 0x0f +#define LSARPC_OPNUM_CreateSecret 0x10 +#define LSARPC_OPNUM_OpenAccount 0x11 +#define LSARPC_OPNUM_EnumPrivsAccount 0x12 +#define LSARPC_OPNUM_GetSystemAccessAccount 0x17 +#define LSARPC_OPNUM_OpenSecret 0x1c +#define LSARPC_OPNUM_LookupPrivValue 0x1f +#define LSARPC_OPNUM_LookupPrivName 0x20 +#define LSARPC_OPNUM_LookupPrivDisplayName 0x21 +#define LSARPC_OPNUM_AddAccountRights 0x25 +#define LSARPC_OPNUM_OpenPolicy2 0x2c +#define LSARPC_OPNUM_GetConnectedUser 0x2d +#define LSARPC_OPNUM_Discovery 0x2e +#define LSARPC_OPNUM_LookupSids2 0x39 +#define LSARPC_OPNUM_LookupNames2 0x3a + + +/* + * There are at least two lookup level settings. Level 1 appears to mean + * only look on the local host and level 2 means forward the request to + * the PDC. On the PDC it probably doesn't matter which level you use but + * on a BDC a level 1 lookup will fail if the BDC doesn't have the info + * whereas a level 2 lookup will also check with the PDC. + */ +#define MSLSA_LOOKUP_LEVEL_1 1 +#define MSLSA_LOOKUP_LEVEL_2 2 + + +/* + * Definition for a SID. The ndl compiler won't allow a typedef of + * a structure containing variable size members. + */ +struct mslsa_sid { + BYTE Revision; + BYTE SubAuthCount; + BYTE Authority[6]; + SIZE_IS(SubAuthCount) + DWORD SubAuthority[ANY_SIZE_ARRAY]; +}; + +struct mslsa_string_desc { + WORD length; + WORD allosize; + LPTSTR str; +}; +typedef struct mslsa_string_desc mslsa_string_t; + + +struct mslsa_handle { + DWORD hand1; + DWORD hand2; + WORD hand3[2]; + BYTE hand4[8]; +}; +typedef struct mslsa_handle mslsa_handle_t; + + +struct mslsa_luid { + DWORD low_part; + DWORD high_part; +}; +typedef struct mslsa_luid mslsa_luid_t; + + +/* + *********************************************************************** + * OpenPolicy2 obtains a handle for a remote LSA. This handle is + * required for all subsequent LSA requests. + * + * The server name should be the name of the target PDC or BDC, with + * the double backslash prefix. + * + * As far as I can tell, the mslsa_object_attributes structure can be + * all zero except for the length, which should be set to sizeof(struct + * mslsa_object_attributes). + * + * For read access, the desired access mask should contain the + * READ_CONTROL standard right and whatever policy rights are required. + * I haven't tried any update operations but if you get the access mask + * wrong you can crash the domain controller. + *********************************************************************** + */ + + +/* + * From netmon: + * length = 12 + * impersonation_level = 2 + * context_tracking_mode = 1 + * effective_only = 0 + */ +struct mslsa_quality_of_service { + DWORD length; + WORD impersonation_level; + BYTE context_tracking_mode; + BYTE effective_only; +}; + + +struct mslsa_object_attributes { + DWORD length; + DWORD rootDirectory; + DWORD objectName; + DWORD attributes; + DWORD securityDescriptor; + struct mslsa_quality_of_service *qualityOfService; +}; + + +OPERATION(LSARPC_OPNUM_OpenPolicy) +struct mslsa_OpenPolicy { + IN DWORD *servername; + IN struct mslsa_object_attributes attributes; + IN DWORD desiredAccess; + OUT mslsa_handle_t domain_handle; + OUT DWORD status; +}; + +OPERATION(LSARPC_OPNUM_OpenPolicy2) +struct mslsa_OpenPolicy2 { + IN LPTSTR servername; + IN struct mslsa_object_attributes attributes; + IN DWORD desiredAccess; + OUT mslsa_handle_t domain_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * CloseHandle closes an association with the LSA. The returned handle + * will be all zero. + *********************************************************************** + */ +OPERATION(LSARPC_OPNUM_CloseHandle) +struct mslsa_CloseHandle { + IN mslsa_handle_t handle; + OUT mslsa_handle_t result_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * EnumPrivileges + * + * Obtain a list of privilege names. This interface is not implemented + * yet The definition below has not been tested. This is a guess based + * on data available from netmon. + *********************************************************************** + */ +struct mslsa_PrivDef { + mslsa_string_t name; + mslsa_luid_t luid; +}; + + +struct mslsa_PrivEnumBuf { + DWORD entries_read; + SIZE_IS(entries_read) + struct mslsa_PrivDef *def; +}; + + +OPERATION(LSARPC_OPNUM_EnumPrivileges) +struct mslsa_EnumPrivileges { + IN mslsa_handle_t handle; + INOUT DWORD enum_context; + IN DWORD max_length; + OUT REFERENCE struct mslsa_PrivEnumBuf *enum_buf; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * QuerySecurityObject. I'm not entirely sure how to set this up yet. + * I used the discovery RPC to scope it out. The structures are set up + * according to netmon and the assumption that a security descriptor + * on the wire looks like the regular user level security descriptor. + *********************************************************************** + */ +struct mslsa_SecurityDescriptor { + BYTE revision; + BYTE sbz1; + WORD control; + DWORD owner; + DWORD group; + DWORD sacl; + DWORD dacl; +}; + + +struct mslsa_SecurityDescInfo { + DWORD length; + SIZE_IS(length) + BYTE *desc; /* temporary */ + /* struct mslsa_SecurityDescriptor *desc; */ +}; + + +OPERATION(LSARPC_OPNUM_QuerySecurityObject) +struct mslsa_QuerySecurityObject { + IN mslsa_handle_t handle; + IN DWORD security_info; + OUT struct mslsa_SecurityDescInfo *desc_info; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * EnumerateAccounts and EnumerateTrustedDomain. + *********************************************************************** + */ +struct mslsa_AccountInfo { + struct mslsa_sid *sid; +}; + + +struct mslsa_EnumAccountBuf { + DWORD entries_read; + SIZE_IS(entries_read) + struct mslsa_AccountInfo *info; +}; + + +OPERATION(LSARPC_OPNUM_EnumerateAccounts) +struct mslsa_EnumerateAccounts { + IN mslsa_handle_t handle; + INOUT DWORD enum_context; + IN DWORD max_length; + OUT REFERENCE struct mslsa_EnumAccountBuf *enum_buf; + OUT DWORD status; +}; + + +struct mslsa_TrustedDomainInfo { + mslsa_string_t name; + struct mslsa_sid *sid; +}; + + +struct mslsa_EnumTrustedDomainBuf { + DWORD entries_read; + SIZE_IS(entries_read) + struct mslsa_TrustedDomainInfo *info; +}; + + +OPERATION(LSARPC_OPNUM_EnumTrustedDomain) +struct mslsa_EnumTrustedDomain { + IN mslsa_handle_t handle; + INOUT DWORD enum_context; + IN DWORD max_length; + OUT REFERENCE struct mslsa_EnumTrustedDomainBuf *enum_buf; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * Definitions common to both LookupSids and LookupNames. Both return + * an mslsa_domain_table[]. Each interface also returns a specific + * table with entries which index the mslsa_domain_table[]. + *********************************************************************** + */ +struct mslsa_domain_entry { + mslsa_string_t domain_name; + struct mslsa_sid *domain_sid; +}; +typedef struct mslsa_domain_entry mslsa_domain_entry_t; + + +struct mslsa_domain_table { + DWORD n_entry; + SIZE_IS(n_entry) + mslsa_domain_entry_t *entries; + DWORD max_n_entry; +}; + + +/* + *********************************************************************** + * Definitions for LookupSids. + * + * The input parameters are: + * + * A valid LSA handle obtained from an LsarOpenPolicy. + * The table of SIDs to be looked up. + * A table of names (probably empty). + * The lookup level (local=1 or PDC=2). + * An enumeration counter (used for continuation operations). + * + * The output results are: + * + * A table of referenced domains. + * A table of usernames. + * The updated value of the enumeration counter. + * The result status. + *********************************************************************** + */ + +struct mslsa_lup_sid_entry { + struct mslsa_sid *psid; +}; + +struct mslsa_lup_sid_table { + DWORD n_entry; + SIZE_IS(n_entry) + struct mslsa_lup_sid_entry *entries; +}; + +struct mslsa_name_entry { + WORD sid_name_use; + WORD unknown_flags; + mslsa_string_t name; + DWORD domain_ix; /* -1 means none */ +}; + +struct mslsa_name_table { + DWORD n_entry; + SIZE_IS(n_entry) + struct mslsa_name_entry *entries; +}; + +OPERATION(LSARPC_OPNUM_LookupSids) +struct mslsa_LookupSids { + IN mslsa_handle_t handle; + IN struct mslsa_lup_sid_table lup_sid_table; + + OUT struct mslsa_domain_table *domain_table; + INOUT struct mslsa_name_table name_table; + + IN DWORD lookup_level; + INOUT DWORD mapped_count; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * Definitions for LookupNames. + * + * LookupNames requires the following input parameters. + * + * A valid LSA handle obtained from an LsarOpenPolicy. + * The table of names to be looked up. + * A table of translated sids (probably empty). + * The lookup level (local=1 or PDC=2). + * An enumeration counter (used for continuation operations). + * + * The outputs are as follows. + * + * A table of referenced domains. + * A table of translated sids (actually rids). + * The updated value of the enumeration counter. + * The result status. + *********************************************************************** + */ +struct mslsa_lup_name_table { + DWORD n_entry; + SIZE_IS(n_entry) + mslsa_string_t names[ANY_SIZE_ARRAY]; +}; + + +struct mslsa_rid_entry { + WORD sid_name_use; + WORD pad; /* alignment - probably not required */ + DWORD rid; + DWORD domain_index; +}; + + +struct mslsa_rid_table { + DWORD n_entry; + SIZE_IS(n_entry) + struct mslsa_rid_entry *rids; +}; + + +OPERATION(LSARPC_OPNUM_LookupNames) +struct mslsa_LookupNames { + IN mslsa_handle_t handle; + IN REFERENCE struct mslsa_lup_name_table *name_table; + + OUT struct mslsa_domain_table *domain_table; + INOUT struct mslsa_rid_table translated_sids; + + IN DWORD lookup_level; + INOUT DWORD mapped_count; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * QueryInfoPolicy returns various pieces of policy information. The + * desired information is specified using a class value, as defined + * below. + *********************************************************************** + */ +#define MSLSA_POLICY_UNKNOWN_1_INFO 1 +#define MSLSA_POLICY_UNKNOWN_2_INFO 2 +#define MSLSA_POLICY_PRIMARY_DOMAIN_INFO 3 +#define MSLSA_POLICY_UNKNOWN_4_INFO 4 +#define MSLSA_POLICY_ACCOUNT_DOMAIN_INFO 5 +#define MSLSA_POLICY_SERVER_ROLE_INFO 6 +#define MSLSA_POLICY_REPLICA_SOURCE_INFO 7 +#define MSLSA_POLICY_DEFAULT_QUOTA_INFO 8 + + +struct mslsa_PrimaryDomainInfo { + struct mslsa_string_desc name; + struct mslsa_sid *sid; +}; + + +struct mslsa_AccountDomainInfo { + struct mslsa_string_desc name; + struct mslsa_sid *sid; +}; + +/* +struct mslsa_ServerRoleInfo { + WORD unknown_0x0003; + WORD unknown_0x000e; +}; +*/ + +union mslsa_PolicyInfoResUnion { + CASE(3) struct mslsa_PrimaryDomainInfo pd_info; + CASE(5) struct mslsa_AccountDomainInfo ad_info; + DEFAULT char *nullptr; +}; + + +struct mslsa_PolicyInfo { + WORD switch_value; + SWITCH(switch_value) + union mslsa_PolicyInfoResUnion ru; +}; + + +OPERATION(LSARPC_OPNUM_QueryInfoPolicy) +struct mslsa_QueryInfoPolicy { + IN mslsa_handle_t handle; + IN WORD info_class; + OUT struct mslsa_PolicyInfo *info; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * OpenAccount. + * + * Returns a handle that can be used to access the account specified + * by a SID. This handle can be used to enumerate account privileges. + *********************************************************************** + */ +OPERATION(LSARPC_OPNUM_OpenAccount) +struct mslsa_OpenAccount { + IN mslsa_handle_t handle; + IN REFERENCE struct mslsa_sid *sid; + IN DWORD access_mask; + OUT mslsa_handle_t account_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * EnumPrivilegesAccount. + * + * Enumerate the list of privileges held by the specified account. The + * handle must be a valid account handle obtained via OpenAccount. The + * luid values returned will be probably only be relevant on the domain + * controller so we'll need to find a way to convert them to the + * actual privilege names. + *********************************************************************** + */ +struct mslsa_LuidAndAttributes { + struct mslsa_luid luid; + DWORD attributes; +}; + + +struct mslsa_PrivilegeSet { + DWORD privilege_count; + DWORD control; + SIZE_IS(privilege_count) + struct mslsa_LuidAndAttributes privilege[ANY_SIZE_ARRAY]; +}; + + +OPERATION(LSARPC_OPNUM_EnumPrivsAccount) + struct mslsa_EnumPrivsAccount { + IN mslsa_handle_t account_handle; + OUT struct mslsa_PrivilegeSet *privileges; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * LookupPrivValue + * + * Map a privilege name to a local unique id (LUID). Privilege names + * are consistent across the network. LUIDs are machine specific. + * The privilege list is provided as a set of LUIDs so the privilege + * lookup functions must be used to identify which the privilege to + * which each LUID refers. The handle here is a policy handle. + *********************************************************************** + */ +OPERATION(LSARPC_OPNUM_LookupPrivValue) +struct mslsa_LookupPrivValue { + IN mslsa_handle_t handle; + IN mslsa_string_t name; + OUT struct mslsa_luid luid; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * LookupPrivName + * + * Map a privilege value (LUID) to a privilege name. Privilege names + * are consistent across the network. LUIDs are machine specific. + * The privilege list is provided as a set of LUIDs so the privilege + * lookup functions must be used to identify which the privilege to + * which each LUID refers. The handle here is a policy handle. + *********************************************************************** + */ +OPERATION(LSARPC_OPNUM_LookupPrivName) +struct mslsa_LookupPrivName { + IN mslsa_handle_t handle; + IN struct mslsa_luid luid; + OUT mslsa_string_t *name; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * LookupPrivDisplayName + * + * Map a privilege name to a local unique id (LUID). Privilege names + * are consistent across the network. LUIDs are machine specific. + * The privilege list is provided as a set of LUIDs so the privilege + * lookup functions must be used to identify which the privilege to + * which each LUID refers. The handle here is a policy handle. + *********************************************************************** + */ +OPERATION(LSARPC_OPNUM_LookupPrivDisplayName) +struct mslsa_LookupPrivDisplayName { + IN mslsa_handle_t handle; + IN mslsa_string_t name; + IN WORD client_language; + IN WORD default_language; + OUT mslsa_string_t *display_name; + OUT WORD language_ret; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * GetConnectedUser + * + * This is still guesswork. Netmon doesn't know about this + * call and I'm not really sure what it is intended to achieve. + * Another packet capture application, Ethereal, calls this RPC as + * GetConnectedUser. + * We will receive our own hostname in the request and it appears + * we should respond with an account name and the domain name of connected + * user from the client that makes this call. + *********************************************************************** + */ + +struct mslsa_DomainName { + struct mslsa_string_desc *name; +}; + + +OPERATION(LSARPC_OPNUM_GetConnectedUser) +struct mslsa_GetConnectedUser { + IN LPTSTR hostname; + IN BYTE *unknown1; + IN BYTE *unknown2; + OUT struct mslsa_string_desc *owner; + OUT struct mslsa_DomainName *domain; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * LSARPC_OPNUM_LookupSids2 + * + * SID lookup function that appeared in Windows 2000. It appears to be + * very similar to the original SID lookup RPC. There are two extra IN + * parameters, which we don't care about. The OUT name structure has + * an extra field, in which zero seems to be okay. + *********************************************************************** + */ +struct lsar_name_entry2 { + WORD sid_name_use; + WORD unknown_flags; /* maybe alignment */ + mslsa_string_t name; + DWORD domain_ix; /* -1 means none */ + DWORD unknown; /* added */ +}; + + +struct lsar_name_table2 { + DWORD n_entry; + SIZE_IS(n_entry) + struct lsar_name_entry2 *entries; +}; + + +OPERATION(LSARPC_OPNUM_LookupSids2) +struct lsar_lookup_sids2 { + IN mslsa_handle_t policy_handle; + IN struct mslsa_lup_sid_table lup_sid_table; + OUT struct mslsa_domain_table *domain_table; + INOUT struct lsar_name_table2 name_table; + IN DWORD lookup_level; + INOUT DWORD mapped_count; + IN DWORD zero; + IN DWORD requested_count; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * LSARPC_OPNUM_LookupNames2 + * + * Name lookup function that appeared in Windows 2000. It appears to be + * very similar to the original name lookup RPC. There are two extra IN + * parameters, which we don't care about. The lsar_rid_entry2 structure + * has an extra field, in which zero seems to be okay. + *********************************************************************** + */ +struct lsar_rid_entry2 { + WORD sid_name_use; + WORD pad; /* alignment - probably not required */ + DWORD rid; + DWORD domain_index; /* -1 means none */ + DWORD unknown; /* new */ +}; + + +struct lsar_rid_table2 { + DWORD n_entry; + SIZE_IS(n_entry) + struct lsar_rid_entry2 *rids; +}; + + +OPERATION(LSARPC_OPNUM_LookupNames2) +struct lsar_LookupNames2 { + IN mslsa_handle_t policy_handle; + IN REFERENCE struct mslsa_lup_name_table *name_table; + OUT struct mslsa_domain_table *domain_table; + INOUT struct lsar_rid_table2 translated_sids; + IN DWORD lookup_level; + INOUT DWORD mapped_count; + IN DWORD unknown_sbz; + IN DWORD unknown_sb2; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * This is a generic discovery entry. As long as the handle is valid + * this is useful for scoping the network to discover new worlds. To + * seek out new life, new civilizations. To boldly spilt infinitives + * where no man has gone before. So basically we send and receive a + * big buffer and let netmon tell us to which RPC the opnum refers. + *********************************************************************** + */ + +#define LSA_DISCOVERY_SIZE 16 + + +OPERATION(LSARPC_OPNUM_Discovery) +struct mslsa_Discovery { + IN mslsa_handle_t handle; + IN DWORD in_stuff[LSA_DISCOVERY_SIZE]; + OUT DWORD out_stuff[LSA_DISCOVERY_SIZE]; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * The LSARPC interface definition. + *********************************************************************** + */ +INTERFACE(0) +union lsarpc_interface { + CASE(LSARPC_OPNUM_CloseHandle) + struct mslsa_CloseHandle CloseHandle; + CASE(LSARPC_OPNUM_QuerySecurityObject) + struct mslsa_QuerySecurityObject QuerySecurityObj; + CASE(LSARPC_OPNUM_EnumerateAccounts) + struct mslsa_EnumerateAccounts EnumAccounts; + CASE(LSARPC_OPNUM_EnumTrustedDomain) + struct mslsa_EnumTrustedDomain EnumTrustedDomain; + CASE(LSARPC_OPNUM_OpenAccount) + struct mslsa_OpenAccount OpenAccount; + CASE(LSARPC_OPNUM_EnumPrivsAccount) + struct mslsa_EnumPrivsAccount EnumPrivsAccount; + CASE(LSARPC_OPNUM_LookupPrivValue) + struct mslsa_LookupPrivValue LookupPrivValue; + CASE(LSARPC_OPNUM_LookupPrivName) + struct mslsa_LookupPrivName LookupPrivName; + CASE(LSARPC_OPNUM_LookupPrivDisplayName) + struct mslsa_LookupPrivDisplayName LookupPrivDisplayName; + CASE(LSARPC_OPNUM_Discovery) + struct mslsa_Discovery Discovery; + CASE(LSARPC_OPNUM_QueryInfoPolicy) + struct mslsa_QueryInfoPolicy QueryInfoPolicy; + CASE(LSARPC_OPNUM_OpenPolicy) + struct mslsa_OpenPolicy OpenPolicy; + CASE(LSARPC_OPNUM_OpenPolicy2) + struct mslsa_OpenPolicy2 OpenPolicy2; + CASE(LSARPC_OPNUM_LookupSids) + struct mslsa_LookupSids LookupSids; + CASE(LSARPC_OPNUM_LookupNames) + struct mslsa_LookupNames LookupNames; + CASE(LSARPC_OPNUM_GetConnectedUser) + struct mslsa_GetConnectedUser GetConnectedUser; + CASE(LSARPC_OPNUM_LookupSids2) + struct lsar_lookup_sids2 LookupSids2; + CASE(LSARPC_OPNUM_LookupNames2) + struct lsar_LookupNames2 LookupNames2; +}; +typedef union lsarpc_interface lsarpc_interface_t; +EXTERNTYPEINFO(lsarpc_interface) + +#endif /* _MLSVC_LSA_NDL_ */ diff --git a/usr/src/uts/common/smbsrv/ndl/ndrtypes.ndl b/usr/src/uts/common/smbsrv/ndl/ndrtypes.ndl new file mode 100644 index 000000000000..ec3b76c97874 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ndl/ndrtypes.ndl @@ -0,0 +1,166 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _NDR_TYPES_NDL_ +#define _NDR_TYPES_NDL_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#define TYPEINFO(TYPE) ndt__##TYPE + +#ifdef NDRGEN + +#define ALIGN(X) [align(X)] +#define OPERATION(X) [operation(X)] +#define IN [in] +#define OUT [out] +#define INOUT [in out] + +#define STRING [string] +#define SIZE_IS(X) [size_is(X)] + +#define SWITCH(X) [switch_is(X)] +#define CASE(X) [case(X)] +#define DEFAULT [default] + +#define INTERFACE(X) [interface(X)] +#define UUID(X) [uuid(X)] + +#define ARG_IS(X) [arg_is(X)] + +#define REFERENCE [reference] + +#define ANY_SIZE_ARRAY * + +#define IMPORT_EXTERN [extern] + +#define BYTE uchar +#define WORD ushort +#define DWORD ulong + +#define LPTSTR STRING wchar * + +#define LPBYTE uchar * +#define LPWORD ushort * +#define LPDWORD ulong * + +#define EXTERNTYPEINFO(TYPE) + +#else /* NDRGEN */ + +#define ALIGN(X) +#define OPERATION(X) +#define IN +#define OUT +#define INOUT + +#define STRING +#define SIZE_IS(X) + +#define SWITCH(X) +#define CASE(X) +#define DEFAULT + +#define INTERFACE(X) +#define UUID(X) + +#define ARG_IS(X) + +#define REFERENCE + + +#ifndef ANY_SIZE_ARRAY +#define ANY_SIZE_ARRAY 1 +#endif /* ANY_SIZE_ARRAY */ + + +#define IMPORT_EXTERN + + +#ifndef UNSIGNED_TYPES_DEFINED +#define UNSIGNED_TYPES_DEFINED + +#define BYTE unsigned char +#define WORD unsigned short +#define DWORD unsigned long +#define LPTSTR unsigned char * +#define LPBYTE unsigned char * +#define LPWORD unsigned short * +#define LPDWORD unsigned long * + +#endif /* UNSIGNED_TYPES_DEFINED */ + + +#define EXTERNTYPEINFO(TYPE) extern struct ndr_typeinfo TYPEINFO(TYPE); + + +/* + *********************************************************************** + * There is a bug in the way that midl and the marshalling code handles + * unions so we need to fix some of the data offsets at runtime. The + * following macros and the fixup function handle the correction. + *********************************************************************** + */ + +/* + * DECL_FIXUP_STRUCT allows us to declare external references to data + * structures generated by ndrgen in the _ndr.c file. + */ +#define DECL_FIXUP_STRUCT(NAME) \ + extern struct ndr_typeinfo ndt__##NAME + +/* + * CASE_INFO_ENT is intended to simplify the declaration of the case + * statement in the fixup function. Assuming you have followed the + * convention for naming the individual structures all you have to do + * is add a single line to the fixup function for each new case. + */ +#define CASE_INFO_ENT(NAME,N) \ + case N: size1 = sizeof (struct NAME##N); \ + break + +/* + * FIXUP_PDU_SIZE is used to patch the appropriate structures (identified + * by DECL_FIXUP_STRUCT) at runtime. The values are based on the + * switch_index. + */ +#define FIXUP_PDU_SIZE(NAME,SIZE) { \ + ndt__##NAME.pdu_size_fixed_part = SIZE; \ + ndt__##NAME.c_size_fixed_part = SIZE; \ +} + + +#endif /* NDRGEN */ + +/* + * UNION_INFO_ENT is intended to simplify adding new entries to a union. + * If the entry structures are named using the form FunctionNameX, + * where X is the sitch_value, you can just add a single line. Note + * that you must also update the fixup function in mlsvc_xxx.c. + */ +#define UNION_INFO_ENT(N,NAME) CASE(N) struct NAME##N info##N +#define UNION_INFO_PTR(N,NAME) CASE(N) struct NAME##N *info##N + +#endif /* _NDR_TYPES_NDL_ */ diff --git a/usr/src/uts/common/smbsrv/ndl/netdfs.ndl b/usr/src/uts/common/smbsrv/ndl/netdfs.ndl new file mode 100644 index 000000000000..a2f86b452d13 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ndl/netdfs.ndl @@ -0,0 +1,493 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _NETDFS_NDL_ +#define _NETDFS_NDL_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * NT Distributed File Service (NETDFS) RPC interface definition. + */ + +#include "ndrtypes.ndl" + + +#define NETDFS_ABSTRACT_UUID "4fc742e0-4a10-11cf-827300aa004ae673" +#define NETDFS_ABSTRACT_VERS 3 + +#define NETDFS_TRANSFER_UUID "8a885d04-1ceb-11c9-9fe808002b104860" +#define NETDFS_TRANSFER_VERS 2 + +#define NETDFS_OPNUM_GETVER 0x00 +#define NETDFS_OPNUM_ADD 0x01 +#define NETDFS_OPNUM_REMOVE 0x02 +#define NETDFS_OPNUM_SETINFO 0x03 +#define NETDFS_OPNUM_GETINFO 0x04 +#define NETDFS_OPNUM_ENUM 0x05 +#define NETDFS_OPNUM_RENAME 0x06 +#define NETDFS_OPNUM_MOVE 0x07 +#define NETDFS_OPNUM_ADDSTDROOT 0x0c +#define NETDFS_OPNUM_REMSTDROOT 0x0d +#define NETDFS_OPNUM_ENUMEX 0x15 + +#define DFS_MANAGER_VERSION_NT4 0x01 +#define DFS_MANAGER_VERSION_W2K 0x02 +#define DFS_MANAGER_VERSION_W2K3 0x04 + + +#define DFS_PROP_FLAG_INSITE_REFERRALS 0x01 +#define DFS_PROP_FLAG_ROOT_SCALABILITY 0x02 +#define DFS_PROP_FLAG_SITE_COSTING 0x04 +#define DFS_PROP_FLAG_TARGET_FAILBACK 0x08 +#define DFS_PROP_FLAG_CLUSTER_ENABLED 0x10 + + +#define DFS_STORAGE_PRI_INVALID -1 +#define DFS_STORAGE_PRI_SITE_COST_NORM 0 +#define DFS_STORAGE_PRI_GLOBAL_HIGH 1 +#define DFS_STORAGE_PRI_SITE_COST_HIGH 2 +#define DFS_STORAGE_PRI_SITE_COST_LOW 3 +#define DFS_STORAGE_PRI_GLOBAL_LOW 4 + + +struct netdfs_storage_info { + DWORD state; + LPTSTR server; + LPTSTR share; +}; + + +struct netdfs_storage_info2 { + DWORD state; + LPTSTR server; + LPTSTR share; + DWORD priority; + DWORD rank; +}; + +struct netdfs_info1 { + LPTSTR entry_path; +}; + + +struct netdfs_info2 { + LPTSTR entry_path; + LPTSTR comment; + DWORD state; + DWORD n_store; +}; + + +struct netdfs_info3 { + LPTSTR entry_path; + LPTSTR comment; + DWORD state; + DWORD n_store; + SIZE_IS(n_store) + struct netdfs_storage_info *si; +}; + + +struct netdfs_info4 { + LPTSTR entry_path; + LPTSTR comment; + DWORD state; + DWORD timeout; + DWORD guuid[4]; + DWORD n_store; + SIZE_IS(n_store) + struct netdfs_storage_info *si; +}; + + +struct netdfs_info6 { + LPTSTR entry_path; + LPTSTR comment; + DWORD state; + DWORD timeout; + DWORD guuid[4]; + DWORD flags; + DWORD pktsize; + DWORD n_store; + SIZE_IS(n_store) + struct netdfs_storage_info2 *si; +}; + + +struct netdfs_info100 { + LPTSTR comment; +}; + + +struct netdfs_info101 { + DWORD state; +}; + + +struct netdfs_info102 { + DWORD timeout; +}; + + +struct netdfs_info103 { + DWORD property_flags; +}; + + +struct netdfs_info104 { + DWORD priority_class; + DWORD priority_rank; +}; + + +struct netdfs_info105 { + LPTSTR comment; + DWORD volume_state; + DWORD timeout; + DWORD property_flag_mask; + DWORD property_flags; +}; + + +struct netdfs_info106 { + DWORD storage_state; + DWORD priority_class; + DWORD priority_rank; +}; + + +struct netdfs_info200 { + LPTSTR entry_path; +}; + + +struct netdfs_info300 { + DWORD flavor; + LPTSTR entry_path; +}; + + +union netdfs_info_u { + CASE(1) struct netdfs_info1 *info1; + CASE(2) struct netdfs_info2 *info2; + CASE(3) struct netdfs_info3 *info3; + CASE(4) struct netdfs_info4 *info4; + CASE(6) struct netdfs_info6 *info6; + CASE(100) struct netdfs_info100 *info100; + CASE(101) struct netdfs_info101 *info101; + CASE(102) struct netdfs_info102 *info102; + CASE(103) struct netdfs_info103 *info103; + CASE(104) struct netdfs_info104 *info104; + CASE(105) struct netdfs_info105 *info105; + CASE(106) struct netdfs_info106 *info106; + DEFAULT char *nullptr; +}; + + +struct netdfs_info { + DWORD level; + SWITCH(level) + union netdfs_info_u iu; +}; + + +struct netdfs_array1 { + DWORD count; + SIZE_IS(count) + struct netdfs_info1 *info1; +}; + +struct netdfs_array2 { + DWORD count; + SIZE_IS(count) + struct netdfs_info2 *info2; +}; + +struct netdfs_array3 { + DWORD count; + SIZE_IS(count) + struct netdfs_info3 *info3; +}; + +struct netdfs_array4 { + DWORD count; + SIZE_IS(count) + struct netdfs_info4 *info4; +}; + +struct netdfs_array6 { + DWORD count; + SIZE_IS(count) + struct netdfs_info6 *info6; +}; + +struct netdfs_array200 { + DWORD count; + SIZE_IS(count) + struct netdfs_info200 *info200; +}; + +struct netdfs_array300 { + DWORD count; + SIZE_IS(count) + struct netdfs_info300 *info300; +}; + +union netdfs_enum_info_u { + CASE(1) struct netdfs_array1 *info1; + CASE(2) struct netdfs_array2 *info2; + CASE(3) struct netdfs_array3 *info3; + CASE(4) struct netdfs_array4 *info4; + CASE(6) struct netdfs_array6 *info6; + CASE(200) struct netdfs_array200 *info200; + CASE(300) struct netdfs_array300 *info300; + DEFAULT char *nullptr; +}; + + +struct netdfs_enum_info { + DWORD address; + DWORD level; + SWITCH(level) + union netdfs_enum_info_u iu; +}; + + +/* + *********************************************************************** + * Return server version id + *********************************************************************** + */ +OPERATION(NETDFS_OPNUM_GETVER) +struct netdfs_getver { + OUT DWORD version; +}; + + +/* + *********************************************************************** + * Add a new volume or additional storage for an existing volume at + * dfs_path. + *********************************************************************** + */ +OPERATION(NETDFS_OPNUM_ADD) +struct netdfs_add { + IN REFERENCE LPTSTR dfs_path; + IN REFERENCE LPTSTR server; + IN LPTSTR share; + IN LPTSTR comment; + IN DWORD flags; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * Remove a volume or additional storage for volume from the DFS at + * dfs_path. When applied to the last storage in a volume, removes + * the volume from the DFS. + *********************************************************************** + */ +OPERATION(NETDFS_OPNUM_REMOVE) +struct netdfs_remove { + IN REFERENCE LPTSTR dfs_path; + IN LPTSTR server; + IN LPTSTR share; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * Set information about the volume or storage. If the server and share + * are specified, the information set is specific to that server and + * share. Otherwise the information is specific to the volume as a whole. + * + * Valid levels are 100-102. + *********************************************************************** + */ +OPERATION(NETDFS_OPNUM_SETINFO) +struct netdfs_setinfo { + IN REFERENCE LPTSTR dfs_path; + IN LPTSTR server; + IN LPTSTR share; + IN DWORD level; + IN struct netdfs_info info; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * Get information about the volume or storage. If the server and share + * are specified, the information returned is specific to that server + * and share. Otherwise the information is specific to the volume as a + * whole. + * + * Valid levels are 1-4, 100-102. + *********************************************************************** + */ +OPERATION(NETDFS_OPNUM_GETINFO) +struct netdfs_getinfo { + IN REFERENCE LPTSTR dfs_path; + IN LPTSTR server; + IN LPTSTR share; + IN DWORD level; + OUT struct netdfs_info info; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * Get information about all of the volumes in the DFS. dfs_path is + * the "server" part of the UNC name used to refer to this particular + * DFS. + * + * Valid levels are 1-3. + *********************************************************************** + */ +OPERATION(NETDFS_OPNUM_ENUM) +struct netdfs_enum { + IN DWORD level; + IN DWORD pref_max_len; + INOUT struct netdfs_enum_info *info; + INOUT DWORD *resume_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * Rename the current Win32 path in a DFS to a new Win32 path in the + * same DFS. + *********************************************************************** + */ +OPERATION(NETDFS_OPNUM_RENAME) +struct netdfs_rename { + IN REFERENCE LPTSTR dfs_path; + IN REFERENCE LPTSTR new_path; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * Move a DFS volume and all subordinate volumes from one place in the + * DFS to another place in the DFS. + *********************************************************************** + */ +OPERATION(NETDFS_OPNUM_MOVE) +struct netdfs_move { + IN REFERENCE LPTSTR dfs_path; + IN REFERENCE LPTSTR new_path; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * Add a DFS root share. + *********************************************************************** + */ +OPERATION(NETDFS_OPNUM_ADDSTDROOT) +struct netdfs_addstdroot { + IN REFERENCE LPTSTR server; + IN REFERENCE LPTSTR share; + IN REFERENCE LPTSTR comment; + IN DWORD flags; + OUT DWORD status; +}; + +/* + *********************************************************************** + * Remove a DFS root share. + *********************************************************************** + */ +OPERATION(NETDFS_OPNUM_REMSTDROOT) +struct netdfs_remstdroot { + IN REFERENCE LPTSTR server; + IN REFERENCE LPTSTR share; + IN DWORD flags; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * Get information about all of the volumes in the DFS. dfs_path is + * the "server" part of the UNC name used to refer to this particular + * DFS. + * + * Valid levels are 1-3. + *********************************************************************** + */ +OPERATION(NETDFS_OPNUM_ENUMEX) +struct netdfs_enumex { + IN REFERENCE LPTSTR dfs_path; + IN DWORD level; + IN DWORD pref_max_len; + INOUT struct netdfs_enum_info *info; + INOUT DWORD *resume_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * The NETDFS interface definiton. + *********************************************************************** + */ +INTERFACE(0) +union netdfs_interface { + CASE(NETDFS_OPNUM_GETVER) + struct netdfs_getver netdfs_getver; + CASE(NETDFS_OPNUM_ADD) + struct netdfs_add netdfs_add; + CASE(NETDFS_OPNUM_REMOVE) + struct netdfs_remove netdfs_remove; + CASE(NETDFS_OPNUM_SETINFO) + struct netdfs_setinfo netdfs_setinfo; + CASE(NETDFS_OPNUM_GETINFO) + struct netdfs_getinfo netdfs_getinfo; + CASE(NETDFS_OPNUM_ENUM) + struct netdfs_enum netdfs_enum; + CASE(NETDFS_OPNUM_MOVE) + struct netdfs_move netdfs_move; + CASE(NETDFS_OPNUM_RENAME) + struct netdfs_rename netdfs_rename; + CASE(NETDFS_OPNUM_ADDSTDROOT) + struct netdfs_addstdroot netdfs_addstdroot; + CASE(NETDFS_OPNUM_REMSTDROOT) + struct netdfs_remstdroot netdfs_remstdroot; + CASE(NETDFS_OPNUM_ENUMEX) + struct netdfs_enumex netdfs_enumex; +}; +typedef union netdfs_interface netdfs_interface_t; +EXTERNTYPEINFO(netdfs_interface) + + +#endif /* _NETDFS_NDL_ */ diff --git a/usr/src/uts/common/smbsrv/ndl/netlogon.ndl b/usr/src/uts/common/smbsrv/ndl/netlogon.ndl new file mode 100644 index 000000000000..e37ee4ca8ce2 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ndl/netlogon.ndl @@ -0,0 +1,395 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MLSVC_NETR_NDL_ +#define _MLSVC_NETR_NDL_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + *********************************************************************** + * + * NetLogon RPC (NETR) interface definition. + * + *********************************************************************** + */ + +#include "ndrtypes.ndl" + + +#define NETR_OPNUM_SamLogon 0x02 +#define NETR_OPNUM_SamLogoff 0x03 +#define NETR_OPNUM_ServerReqChallenge 0x04 +#define NETR_OPNUM_ServerPasswordSet 0x06 +#define NETR_OPNUM_LogonControl2 0x0E +#define NETR_OPNUM_ServerAuthenticate2 0x0F +#define NETR_OPNUM_TrustDomainList 0x13 + + +struct netr_sid { + BYTE Revision; + BYTE SubAuthCount; + BYTE Authority[6]; + SIZE_IS(SubAuthCount) + DWORD SubAuthority[ANY_SIZE_ARRAY]; +}; + + +struct netr_string { + WORD length; + WORD allosize; + LPTSTR str; +}; +typedef struct netr_string netr_string_t; + + +/* + * Alternative varying/conformant string definition - for + * non-null terminated strings. This definition must match + * mlrpc_vcbuf_t. + */ +struct netr_vcb { + /* + * size_is (actually a copy of length_is) will + * be inserted here by the marshalling library. + */ + DWORD vc_first_is; + DWORD vc_length_is; + SIZE_IS(vc_length_is) + WORD buffer[ANY_SIZE_ARRAY]; +}; + +struct netr_vcbuf { + WORD wclen; + WORD wcsize; + struct netr_vcb *vcb; +}; +typedef struct netr_vcbuf netr_vcbuf_t; + + +struct netr_credential { + BYTE data[8]; +}; + + +struct netr_authenticator { + struct netr_credential credential; + DWORD timestamp; +}; +typedef struct netr_authenticator netr_auth_t; + + +struct OLD_LARGE_INTEGER { + DWORD LowPart; + DWORD HighPart; +}; +typedef struct OLD_LARGE_INTEGER netr_int64_t; + + +struct OWF_PASSWORD { + BYTE data[16]; +}; +typedef struct OWF_PASSWORD netr_owf_password_t; + + +struct CYPHER_BLOCK { + BYTE data[8]; +}; + + +struct USER_SESSION_KEY { + struct CYPHER_BLOCK data[2]; +}; + + + + +/* + *********************************************************************** + * ServerReqChallenge + *********************************************************************** + */ +ALIGN(2) +OPERATION(NETR_OPNUM_ServerReqChallenge) +struct netr_ServerReqChallenge { + IN LPTSTR servername; + IN REFERENCE LPTSTR hostname; + IN struct netr_credential client_challenge; + OUT struct netr_credential server_challenge; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * ServerAuthenticate2 + *********************************************************************** + */ +ALIGN(2) +OPERATION(NETR_OPNUM_ServerAuthenticate2) +struct netr_ServerAuthenticate2 { + IN LPTSTR servername; + IN REFERENCE LPTSTR account_name; + IN WORD account_type; + IN REFERENCE LPTSTR hostname; + IN struct netr_credential client_credential; + OUT struct netr_credential server_credential; + INOUT DWORD negotiate_flags; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * ServerPasswordSet + *********************************************************************** + */ +ALIGN(2) +OPERATION(NETR_OPNUM_ServerPasswordSet) +struct netr_PasswordSet { + IN LPTSTR servername; + IN REFERENCE LPTSTR account_name; + IN WORD account_type; + IN REFERENCE LPTSTR hostname; + INOUT struct netr_authenticator auth; + IN netr_owf_password_t uas_new_password; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * SamLogon + *********************************************************************** + */ + +/* + * The challenge-response data should always be 24 bytes. + */ +#define NETR_CR_PASSWORD_SIZE 24 + + +struct lm_challenge { + BYTE data[8]; +}; +typedef struct lm_challenge lm_challenge_t; + + +struct netr_response { + DWORD length; + DWORD start; + DWORD max_length; + BYTE data[NETR_CR_PASSWORD_SIZE]; +}; +typedef struct netr_response netr_response_t; + + +struct netr_response_desc { + WORD length; + WORD max_length; + netr_response_t *data; +}; +typedef struct netr_response_desc netr_response_desc_t; + +/* + * Input data + */ +struct netr_logon_identity_info { + netr_vcbuf_t domain_name; + DWORD parameter_control; + struct OLD_LARGE_INTEGER logon_id; + netr_vcbuf_t username; + netr_vcbuf_t workstation; +}; +typedef struct netr_logon_identity_info netr_logon_id_t; + + +/* + * Level 1: interactive logon + */ +struct netr_logon_info1 { + netr_logon_id_t identity; + netr_owf_password_t lm_owf_password; + netr_owf_password_t nt_owf_password; +}; + + +/* + * Level 2: network logon. + */ +struct netr_logon_info2 { + netr_logon_id_t identity; + lm_challenge_t lm_challenge; + netr_response_desc_t nt_response; + netr_response_desc_t lm_response; +}; + + +union netr_logon_info_u { + UNION_INFO_PTR(1,netr_logon_info); + UNION_INFO_PTR(2,netr_logon_info); + DEFAULT DWORD nothing; +}; + + +struct netr_login_info { + WORD logon_level; + WORD switch_value; + SWITCH(switch_value) + union netr_logon_info_u ru; +}; + + +/* + * Output data + */ +struct netr_group_membership { + DWORD rid; + DWORD attributes; +}; + + +struct netr_sid_and_attributes { + struct netr_sid *sid; + DWORD attributes; +}; + + +struct netr_validation_info3 { + struct OLD_LARGE_INTEGER LogonTime; + struct OLD_LARGE_INTEGER LogoffTime; + struct OLD_LARGE_INTEGER KickOffTime; + struct OLD_LARGE_INTEGER PasswordLastSet; + struct OLD_LARGE_INTEGER PasswordCanChange; + struct OLD_LARGE_INTEGER PasswordMustChange; + netr_string_t EffectiveName; + netr_string_t FullName; + netr_string_t LogonScript; + netr_string_t ProfilePath; + netr_string_t HomeDirectory; + netr_string_t HomeDirectoryDrive; + WORD LogonCount; + WORD BadPasswordCount; + DWORD UserId; + DWORD PrimaryGroupId; + DWORD GroupCount; + SIZE_IS(GroupCount) + struct netr_group_membership *GroupIds; + DWORD UserFlags; + struct USER_SESSION_KEY UserSessionKey; + netr_string_t LogonServer; + netr_string_t LogonDomainName; + struct netr_sid *LogonDomainId; + DWORD ExpansionRoom[10]; + DWORD SidCount; + SIZE_IS(SidCount) + struct netr_sid_and_attributes *ExtraSids; +}; + + +union netr_validation_u { + CASE(3) struct netr_validation_info3 *info3; + DEFAULT DWORD nothing; +}; + + +/* + * This structure needs to be declared, even though it can't be used + * in netr_SamLogon, in order to get the appropriate size to calculate + * the correct fixup offsets. If ndrgen did the right thing, + * netr_validation_info would be one of the out parameters. However, + * if we do it that way, the switch_value isn't known early enough to + * do the fixup calculation. So it all has to go in netr_SamLogon. + */ +struct netr_validation_info { + WORD validation_level; + SWITCH(validation_level) + union netr_validation_u ru; +}; + + +/* + * WARNING + * + * Validation_level is really a WORD and authoritative is really a + * BYTE. They are declared as DWORD here due to the way things are + * unmarshalled. NT does not clear out the unused bytes in the + * DWORD so they must be cast to get the correct value. + */ +OPERATION(NETR_OPNUM_SamLogon) +struct netr_SamLogon { + IN LPTSTR servername; + IN LPTSTR hostname; + IN struct netr_authenticator *auth; + INOUT struct netr_authenticator *ret_auth; + IN struct netr_login_info logon_info; + INOUT WORD validation_level; + SWITCH(validation_level) + OUT union netr_validation_u ru; + OUT DWORD authoritative; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * SamLogoff + *********************************************************************** + */ +OPERATION(NETR_OPNUM_SamLogoff) +struct netr_SamLogoff { + IN LPTSTR servername; + IN REFERENCE LPTSTR hostname; + IN struct netr_authenticator auth; + INOUT struct netr_authenticator ret_auth; + IN DWORD logon_level; + SWITCH(logon_level) + IN union netr_logon_info_u ru; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * The NETR interface definition. + *********************************************************************** + */ +INTERFACE(0) +union netr_interface { + CASE(NETR_OPNUM_ServerReqChallenge) + struct netr_ServerReqChallenge ServerReqChallenge; + CASE(NETR_OPNUM_ServerAuthenticate2) + struct netr_ServerAuthenticate2 ServerAuthenticate2; + CASE(NETR_OPNUM_SamLogon) + struct netr_SamLogon SamLogon; + CASE(NETR_OPNUM_SamLogoff) + struct netr_SamLogoff SamLogoff; + CASE(NETR_OPNUM_ServerPasswordSet) + struct netr_PasswordSet PasswordSet; +}; +typedef union netr_interface netr_interface_t; +EXTERNTYPEINFO(netr_interface) + +#endif /* _MLSVC_NETR_NDL_ */ diff --git a/usr/src/uts/common/smbsrv/ndl/rpcpdu.ndl b/usr/src/uts/common/smbsrv/ndl/rpcpdu.ndl new file mode 100644 index 000000000000..fd42fcac1c12 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ndl/rpcpdu.ndl @@ -0,0 +1,552 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MLRPCPDU_NDL_ +#define _MLRPCPDU_NDL_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "ndrtypes.ndl" + +/* + * Normally, constructs are (un)marshalled atoms first, then + * constructs, then pointers. This can be confusing sometimes + * when debugging. We know that everything in here can be + * safely (un)marshalled in member order, so we say so. + */ +#ifdef NDRGEN +#define _NO_REORDER_ [_no_reorder] +#else +#define _NO_REORDER_ +#endif + +/* + * UUID (Universal Unique IDentifier) + */ +/* (X/Open CAE Spec Appendix A) */ +struct mlrpc_uuid_dce { + DWORD time_low; + WORD time_mid; + WORD time_hi_and_version; + BYTE clock_seq_hi_and_reserved; + BYTE clock_seq_low; + BYTE node[6]; +}; + +struct mlrpc_uuid { + DWORD data1; + WORD data2; + WORD data3; + BYTE data4[8]; +}; +typedef struct mlrpc_uuid mlrpc_uuid_t; + +/* + * Representation label -- needed for RPC header + * (X/Open CAE Spec Chapter 14.1) + * + * Bits Data Type Description + * ---- --------- ----------- + * 0-3 charset 0=ASCII + * 1=EBCDIC + * 4-7 byte-order 0=big-endian + * 1=little-endian + * 8-15 float 0=IEEE + * 1=VAX + * 2=Cray + * 3=IBM + * 16-31 reserved + */ +#define MLRPC_REPLAB_CHAR_MASK 0x0F /* low nibble of intg_char */ +#define MLRPC_REPLAB_CHAR_ASCII 0x00 /* ASCII */ +#define MLRPC_REPLAB_CHAR_EBCDIC 0x01 /* EBCDIC (never happen) */ +#define MLRPC_REPLAB_INTG_MASK 0xF0 /* hi nibble of intg_char */ +#define MLRPC_REPLAB_INTG_BIG_ENDIAN 0x00 /* big endian */ +#define MLRPC_REPLAB_INTG_LITTLE_ENDIAN 0x10 /* little endian (x86) */ +#define MLRPC_REPLAB_FLOAT_IEEE 0x00 +#define MLRPC_REPLAB_FLOAT_VAX 0x01 +#define MLRPC_REPLAB_FLOAT_CRAY 0x02 +#define MLRPC_REPLAB_FLOAT_IBM 0x03 + +struct mlrpc_representation_label { + BYTE intg_char_rep; /* integer and charset */ + BYTE float_rep; + BYTE _spare[2]; +}; + + + +/* + * RPC PDU (Protocol Data Unit) types + **************************************************************** + * (X/Open CAE Spec 12.1) + */ + +#define MLRPC_PTYPE_REQUEST 0x00 /* CO/CL */ +#define MLRPC_PTYPE_PING 0x01 /* CL */ +#define MLRPC_PTYPE_RESPONSE 0x02 /* CO/CL */ +#define MLRPC_PTYPE_FAULT 0x03 /* CL/CL */ +#define MLRPC_PTYPE_WORKING 0x04 /* CL */ +#define MLRPC_PTYPE_NOCALL 0x05 /* CL */ +#define MLRPC_PTYPE_REJECT 0x06 /* CL */ +#define MLRPC_PTYPE_ACK 0x07 /* CL */ +#define MLRPC_PTYPE_CL_CANCEL 0x08 /* CL */ +#define MLRPC_PTYPE_FACK 0x09 /* CL */ +#define MLRPC_PTYPE_CANCEL_ACK 0x0A /* CL */ +#define MLRPC_PTYPE_BIND 0x0B /* CO */ +#define MLRPC_PTYPE_BIND_ACK 0x0C /* CO */ +#define MLRPC_PTYPE_BIND_NAK 0x0D /* CO */ +#define MLRPC_PTYPE_ALTER_CONTEXT 0x0E /* CO */ +#define MLRPC_PTYPE_ALTER_CONTEXT_RESP 0x0F /* CO */ + /* 0x10 missing from DCE/RPC */ +#define MLRPC_PTYPE_SHUTDOWN 0x11 /* CO */ +#define MLRPC_PTYPE_CO_CANCEL 0x12 /* CO */ +#define MLRPC_PTYPE_ORPHANED 0x13 /* CO */ + +/* + * Flags in the RPC header for Connection-oriented PDU data types + * (X/Open CAE Spec 12.6.3.1) + */ +#define MLRPC_PFC_FIRST_FRAG 0x01 /* First fragment */ +#define MLRPC_PFC_LAST_FRAG 0x02 /* Last framgent */ +#define MLRPC_PFC_PENDING_CANCEL 0x04 /* Cancel was pending@sender*/ +#define MLRPC_PFC_RESERVED_1 0x08 /* */ +#define MLRPC_PFC_CONC_MPX 0x10 /* supports concurrent muxing + * of single connection */ +#define MLRPC_PFC_DID_NOT_EXECUTE 0x20 /* for PTYPE_FAULT, guarantee + * call did not execute */ +#define MLRPC_PFC_MAYBE 0x40 /* "maybe" semantics req'ed*/ +#define MLRPC_PFC_OBJECT_UUID 0x80 /* */ + + +/* + * Header common to all Connection-oriented RPC PDUs + **************************************************************** + * (X/Open CAE Spec 12.6.3.1) + */ +_NO_REORDER_ +struct mlrpcconn_common_header { + BYTE rpc_vers; /* 00:01 5 */ + BYTE rpc_vers_minor; /* 01:01 0 */ + BYTE ptype; /* 02:01 MLRPC_PTYPE_... */ + BYTE pfc_flags; /* 03:01 MLRPC_PFC_... */ + struct mlrpc_representation_label + packed_drep; /* 04:04 NDR representation label */ + WORD frag_length; /* 08:02 total length of frag */ + WORD auth_length; /* 10:02 length of auth_value */ + DWORD call_id; /* 12:04 call identifier */ + /* 16: */ +}; +typedef struct mlrpcconn_common_header mlrpcconn_common_header_t; +EXTERNTYPEINFO(mlrpcconn_common_header) + + +/* + * A plethora of supporting types, only defined the ones we need + * (X/Open CAE Spec 12.6.3.1) + */ +typedef WORD mlrpc_p_context_id_t; + +_NO_REORDER_ +struct mlrpc_p_syntax_id { + mlrpc_uuid_t if_uuid; + DWORD if_version; +}; +typedef struct mlrpc_p_syntax_id mlrpc_p_syntax_id_t; + +_NO_REORDER_ +struct mlrpc_p_cont_elem { + mlrpc_p_context_id_t p_cont_id; + BYTE n_transfer_syn; + BYTE _reserved; + mlrpc_p_syntax_id_t abstract_syntax; + /*SIZE_IS(n_transfer_syn)*/ + mlrpc_p_syntax_id_t transfer_syntaxes[1]; +}; +typedef struct mlrpc_p_cont_elem mlrpc_p_cont_elem_t; +EXTERNTYPEINFO(mlrpc_p_cont_elem) + +_NO_REORDER_ +struct mlrpc_p_cont_list { + BYTE n_context_elem; + BYTE _reserved; + WORD _reserved2; + /*SIZE_IS(n_context_elem)*/ + mlrpc_p_cont_elem_t p_cont_elem[1]; +}; +typedef struct mlrpc_p_cont_list mlrpc_p_cont_list_t; +EXTERNTYPEINFO(mlrpc_p_cont_list) + +typedef WORD mlrpc_p_cont_def_result_t; +#define MLRPC_PCDR_ACCEPTANCE 0 +#define MLRPC_PCDR_USER_REJECTION 1 +#define MLRPC_PCDR_PROVIDER_REJECTION 2 + +typedef WORD mlrpc_p_provider_reason_t; +#define MLRPC_PPR_REASON_NOT_SPECIFIED 0 +#define MLRPC_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED 1 +#define MLRPC_PPR_PROPOSED_TRANSFER_SYNTAXES_NOT_SUPPORTED 2 +#define MLRPC_PPR_LOCAL_LIMIT_EXCEEDED 3 + + +_NO_REORDER_ +struct mlrpc_p_result { + mlrpc_p_cont_def_result_t result; /* MLRPC_PCDR_... */ + mlrpc_p_provider_reason_t reason; /* MLRPC_PPR_... */ + mlrpc_p_syntax_id_t transfer_syntax; /* 0-fill if + * result!=ACCEPT*/ +}; +typedef struct mlrpc_p_result mlrpc_p_result_t; +EXTERNTYPEINFO(mlrpc_p_result) + +_NO_REORDER_ +struct mlrpc_p_result_list { + BYTE n_results; + BYTE reserved; + WORD reserved2; + /*SIZE_IS(n_results)*/ + mlrpc_p_result_t p_results[1]; +}; +typedef struct mlrpc_p_result_list mlrpc_p_result_list_t; +EXTERNTYPEINFO(mlrpc_p_result_list) + +#define MLRPC_PORT_ANY_MAX_PORT_SPEC 30 +_NO_REORDER_ +struct mlrpc_port_any { + WORD length; /* always 18 */ + /*SIZE_IS(length)*/ + BYTE port_spec[MLRPC_PORT_ANY_MAX_PORT_SPEC]; + /* \PIPE\ntsvcs */ + /* We cheat by using 18, and pad on the right with zeroes */ +}; +typedef struct mlrpc_port_any mlrpc_port_any_t; +EXTERNTYPEINFO(mlrpc_port_any) + + +/* + * Alter Context PDU (0x0E) + * (X/Open CAE Spec 12.6.4.1) + */ +_NO_REORDER_ +struct mlrpcconn_alter_context_hdr { + mlrpcconn_common_header_t common_hdr; /* 00:16 (see above) */ + + WORD max_xmit_frag; /* 16:02 ignored */ + WORD max_recv_frag; /* 18:02 ignored */ + DWORD assoc_group_id; /* 20:04 ignored */ + + /* + * Presentation context list (see bind hdr comments). + */ + mlrpc_p_cont_list_t p_context_elem; /* 24: */ + + /* optional authentication verifier if auth_length != 0 */ + /* auth_verifier_co_t auth_verifier; */ +}; +typedef struct mlrpcconn_alter_context_hdr mlrpcconn_alter_context_hdr_t; + + +/* + * Alter Context Response PDU (0x0F) + * (X/Open CAE Spec 12.6.4.2) + */ +_NO_REORDER_ +struct mlrpcconn_alter_context_rsp_hdr { + mlrpcconn_common_header_t common_hdr; /* 00:16 (see above) */ + + WORD max_xmit_frag; /* 16:02 ignored */ + WORD max_recv_frag; /* 18:02 ignored */ + DWORD assoc_group_id; /* 20:04 ignored */ + mlrpc_port_any_t sec_addr; /* 24:20 ignored */ + + /* + * Presentation context list (see bind hdr comments). + */ + mlrpc_p_result_list_t p_result_list; /* 44:nn */ + + /* optional authentication verifier if auth_length != 0 */ + /* auth_verifier_co_t auth_verifier; */ +}; +typedef struct mlrpcconn_alter_context_rsp_hdr mlrpcconn_alter_context_rsp_hdr_t; + + +/* + * Bind PDU (0x0B) + **************************************************************** + * (X/Open CAE Spec 12.6.4.3) + */ + +_NO_REORDER_ +struct mlrpcconn_bind_hdr { + mlrpcconn_common_header_t common_hdr; /* 00:16 (see above) */ + + WORD max_xmit_frag; /* 16:02 max xmit frag size, bytes */ + WORD max_recv_frag; /* 18:02 max recv frag size, bytes */ + DWORD assoc_group_id; /* 20:04 incarnation of client-server + * association group (???) */ + /* 24: */ + + /* presentation, a variable**2 list, of presentation contexts */ + mlrpc_p_cont_list_t p_context_elem; + /* + * This could be follow by more transfer_syntaxes[] for + * the p_cont_elem[0], and subsequently followed by + * more p_cont_elem[] each with one ore more + * transfer_syntaxes[]. The single p_cont_elem[] + * with a single transfer_syntaxes[] is so common, + * though, we embed such right in the bind_hdr. + * The mlrpc_s_bind() processor must walk through + * this tail if there is one. + */ + + /* optional authentication verifier iff auth_length != 0 */ + /* auth_verifier_co_t auth_verifier; */ +}; +typedef struct mlrpcconn_bind_hdr mlrpcconn_bind_hdr_t; + + + + +/* + * Bind_Ack PDU (0x0C) + **************************************************************** + * (X/Open CAE Spec 12.6.4.4) + */ + +/* + * hand coded in mlrpc_encdec.c because sec_addr is an + * interior conformant (variable length) array. + */ + +IMPORT_EXTERN /* don't generate function */ +_NO_REORDER_ +struct mlrpcconn_bind_ack_hdr { + mlrpcconn_common_header_t common_hdr; /* 00:16 (see above) */ + + WORD max_xmit_frag; /* 16:02 max xmit frag size, bytes */ + WORD max_recv_frag; /* 18:02 max recv frag size, bytes */ + DWORD assoc_group_id; /* 20:04 incarnation of client-server + * association group (???) */ + /* 24: */ + + mlrpc_port_any_t sec_addr; /* 24:20 */ + + mlrpc_p_result_list_t p_result_list; /* 44:nn */ + /* This could be followed by more. See bind_hdr above */ + + /* optional authentication verifier iff auth_length != 0 */ + /* auth_verifier_co_t auth_verifier; */ +}; +typedef struct mlrpcconn_bind_ack_hdr mlrpcconn_bind_ack_hdr_t; + + + + +/* + * Request PDU (0x00) + **************************************************************** + * Two flavors, selected based on MLRPC_PFC_OBJECT_UUID in hdr.pfc_flags + * one without the "object" (flag clear) + * one with the "object" (flag set) + * (X/Open CAE Spec 12.6.4.9) + */ + +_NO_REORDER_ +struct mlrpcconn_request_hdr { + mlrpcconn_common_header_t common_hdr; /* 00:16 (see above) */ + + /* needed for request, response, or fault */ + DWORD alloc_hint; /* 16:04 allocation hint */ + mlrpc_p_context_id_t + p_cont_id; /* 20:02 pres context, i.e. data rep */ + + WORD opnum; /* 22:02 op number w/i interface */ + + /* optional field if PFC_OBJECT_UUID, not present */ + /* mlrpc_uuid_t object; */ + + /* stub-data, 8-octet aligned */ /* 24:nn */ + /* nn = frag_len - sizeof(common_header) - auth_len */ + + /* optional authentication verifier iff auth_length != 0 */ + /* auth_verifier_co_t auth_verifier; */ +}; +typedef struct mlrpcconn_request_hdr mlrpcconn_request_hdr_t; + +_NO_REORDER_ +struct mlrpcconn_request_hdr_with_object { + mlrpcconn_common_header_t common_hdr; /* 00:16 (see above) */ + + /* needed for request, response, or fault */ + DWORD alloc_hint; /* 16:04 allocation hint */ + mlrpc_p_context_id_t + p_cont_id; /* 20:02 pres context, i.e. data rep */ + + WORD opnum; /* 22:02 op number w/i interface */ + + /* optional field if PFC_OBJECT_UUID, is present */ + mlrpc_uuid_t object; /* 24:16 object UUID, unknown purpose*/ + + /* stub-data, 8-octet aligned */ /* 28:nn */ + /* nn = frag_len - sizeof(common_header) - auth_len */ + /* nn -= sizeof(mlrpc_uuid_t); */ + + /* optional authentication verifier iff auth_length != 0 */ + /* auth_verifier_co_t auth_verifier; */ +}; + + + +/* + * Hack for response header sizing and multi-fragment responses. + * We know the header is going to be 24 bytes. + */ +#define MLRPC_RSP_HDR_SIZE 24 + + +/* + * Response PDU (0x02) + * (X/Open CAE Spec 12.6.4.10) + */ + +_NO_REORDER_ +struct mlrpcconn_response_hdr { + mlrpcconn_common_header_t common_hdr; /* 00:16 (see above) */ + + /* needed for request, response, or fault */ + DWORD alloc_hint; /* 16:04 allocation hint */ + mlrpc_p_context_id_t + p_cont_id; /* 20:02 pres context, i.e. data rep */ + + /* needed for response or fault */ + BYTE cancel_count; /* 22:01 cancel count */ + BYTE reserved; /* 23:01 mbz */ + + /* stub-data, 8-octet aligned */ /* 24:nn */ + /* nn = frag_len - sizeof(common_header) - auth_len */ + + /* optional authentication verifier iff auth_length != 0 */ + /* auth_verifier_co_t auth_verifier; */ +}; +typedef struct mlrpcconn_response_hdr mlrpcconn_response_hdr_t; + + + +/* + * Fault PDU (0x03) + * (X/Open CAE Spec 12.6.4.7) + */ + +_NO_REORDER_ +struct mlrpcconn_fault_hdr { + mlrpcconn_common_header_t common_hdr; /* 00:16 (see above) */ + + DWORD alloc_hint; /* 16:04 allocation hint */ + mlrpc_p_context_id_t + p_cont_id; /* 20:02 pres context, i.e. data rep */ + + /* needed for response or fault */ + BYTE cancel_count; /* 22:01 cancel count */ + BYTE reserved; /* 23:01 mbz */ + + /* fault code */ + DWORD status; /* 24:04 run-time fault code or 0 */ + + /* pad to 8-byte alignment */ + BYTE reserved2[4]; /* 28:04 must-be-zero */ + + /* stub-data here if status==0. We do not use this mode. */ + + /* optional authentication verifier iff auth_length != 0 */ + /* auth_verifier_co_t auth_verifier; */ +}; +typedef struct mlrpcconn_fault_hdr mlrpcconn_fault_hdr_t; + + +/* Fault status code (X/Open CAE Spec Appendix E) */ +#define MLRPC_FAULT_NCA_RPC_VERSION_MISMATCH 0x1c000008 /* CO/CL */ +#define MLRPC_FAULT_NCA_UNSPEC_REJECT 0x1c000009 /* CO/CL */ +#define MLRPC_FAULT_NCA_S_BAD_ACTID 0x1c00000A /* CL */ +#define MLRPC_FAULT_NCA_WHO_ARE_YOU_FAILED 0x1c00000B /* CL */ +#define MLRPC_FAULT_NCA_MANAGER_NOT_ENTERED 0x1c00000C /* CO/CL */ +#define MLRPC_FAULT_NCA_OP_RNG_ERROR 0x1c010002 /* CO/CL */ +#define MLRPC_FAULT_NCA_UNK_IF 0x1c010003 /* CO/CL */ +#define MLRPC_FAULT_NCA_WRONG_BOOT_TIME 0x1c010006 /* CL */ +#define MLRPC_FAULT_NCA_S_YOU_CRASHED 0x1c010009 /* CL */ +#define MLRPC_FAULT_NCA_PROTO_ERROR 0x1c01000B /* CO/CL */ +#define MLRPC_FAULT_NCA_OUT_ARGS_TOO_BIG 0x1c010013 /* CO/CL */ +#define MLRPC_FAULT_NCA_SERVER_TOO_BUSY 0x1c010014 /* CO/CL */ +#define MLRPC_FAULT_NCA_UNSUPPORTED_TYPE 0x1c010017 /* CO/CL */ +#define MLRPC_FAULT_NCA_INVALID_PRES_CONTEXT_ID 0x1c00001c /* CO */ +#define MLRPC_FAULT_NCA_UNSUPPORTED_AUTHN_LEVEL 0x1c00001d /* CO/CL */ +#define MLRPC_FAULT_NCA_INVALID_CHECKSUM 0x1c00001f /* CO/CL */ +#define MLRPC_FAULT_NCA_INVALID_CRC 0x1c000020 /* CO/CL */ + + + + +/* + * The Header Union/Switch + **************************************************************** + */ + +#define MLRPC_PTYPE_COMMON 999 +#define MLRPC_PTYPE_REQUEST_WITH 998 + + +INTERFACE(0) +union mlrpcconn_hdr { + CASE(MLRPC_PTYPE_COMMON) /* exceeds BYTE range, obtains common hdr */ + struct mlrpcconn_common_header common_hdr; + + CASE(MLRPC_PTYPE_BIND) + struct mlrpcconn_bind_hdr bind_hdr; + + CASE(MLRPC_PTYPE_BIND_ACK) + struct mlrpcconn_bind_ack_hdr bind_ack_hdr; + + CASE(MLRPC_PTYPE_REQUEST) + struct mlrpcconn_request_hdr request_hdr; + + CASE(MLRPC_PTYPE_REQUEST_WITH) /* exceeds BYTE range, ... */ + struct mlrpcconn_request_hdr_with_object request_hdr_with; + + CASE(MLRPC_PTYPE_RESPONSE) + struct mlrpcconn_response_hdr response_hdr; + + CASE(MLRPC_PTYPE_ALTER_CONTEXT) + struct mlrpcconn_alter_context_hdr alter_context_hdr; + + CASE(MLRPC_PTYPE_ALTER_CONTEXT_RESP) + struct mlrpcconn_alter_context_rsp_hdr alter_context_rsp_hdr; + + CASE(MLRPC_PTYPE_FAULT) + struct mlrpcconn_fault_hdr fault_hdr; +}; +typedef union mlrpcconn_hdr mlrpcconn_hdr_t; +EXTERNTYPEINFO(mlrpcconn_hdr) + +#endif /* _MLRPCPDU_NDL_ */ + diff --git a/usr/src/uts/common/smbsrv/ndl/samrpc.ndl b/usr/src/uts/common/smbsrv/ndl/samrpc.ndl new file mode 100644 index 000000000000..0dfda51186b1 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ndl/samrpc.ndl @@ -0,0 +1,1314 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MLSVC_SAM_NDL_ +#define _MLSVC_SAM_NDL_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Security Accounts Manager RPC (SAMR) interface definition. + */ + +#include "ndrtypes.ndl" + + +#define SAMR_OPNUM_ConnectAnon 0x00 +#define SAMR_OPNUM_CloseHandle 0x01 +#define SAMR_OPNUM_QuerySecObject 0x03 +#define SAMR_OPNUM_LookupDomain 0x05 +#define SAMR_OPNUM_EnumLocalDomains 0x06 +#define SAMR_OPNUM_OpenDomain 0x07 +#define SAMR_OPNUM_QueryDomainInfo 0x08 +#define SAMR_OPNUM_CreateDomainGroup 0x0a +#define SAMR_OPNUM_QueryDomainGroups 0x0b +#define SAMR_OPNUM_EnumDomainUsers 0x0d +#define SAMR_OPNUM_CreateDomainAlias 0x0e +#define SAMR_OPNUM_EnumDomainAliases 0x0f +#define SAMR_OPNUM_LookupIds 0x10 +#define SAMR_OPNUM_LookupNames 0x11 +#define SAMR_OPNUM_LookupDomainIds 0x12 +#define SAMR_OPNUM_OpenGroup 0x13 +#define SAMR_OPNUM_QueryGroupInfo 0x14 +#define SAMR_OPNUM_StoreGroupInfo 0x15 +#define SAMR_OPNUM_AddGroupMember 0x16 +#define SAMR_OPNUM_DeleteDomainGroup 0x17 +#define SAMR_OPNUM_DeleteGroupMember 0x18 +#define SAMR_OPNUM_ListGroupMembers 0x19 +#define SAMR_OPNUM_OpenAlias 0x1b +#define SAMR_OPNUM_QueryAliasInfo 0x1c +#define SAMR_OPNUM_SetAliasInfo 0x1d +#define SAMR_OPNUM_DeleteDomainAlias 0x1e +#define SAMR_OPNUM_AddAliasMember 0x1f +#define SAMR_OPNUM_DeleteAliasMember 0x20 +#define SAMR_OPNUM_QueryAliasMember 0x21 +#define SAMR_OPNUM_OpenUser 0x22 +#define SAMR_OPNUM_DeleteUser 0x23 +#define SAMR_OPNUM_QueryUserInfo 0x24 +#define SAMR_OPNUM_QueryUserGroups 0x27 +#define SAMR_OPNUM_QueryDispInfo 0x28 /* QueryDispInfo1 */ +#define SAMR_OPNUM_GetUserPwInfo 0x2c +#define SAMR_OPNUM_EnumDomainGroups 0x30 /* QueryDispInfo3 */ +#define SAMR_OPNUM_CreateUser 0x32 +#define SAMR_OPNUM_QueryDispInfo4 0x33 +#define SAMR_OPNUM_AddMultiAliasMember 0x34 +#define SAMR_OPNUM_ChangeUserPasswd 0x37 +#define SAMR_OPNUM_GetDomainPwInfo 0x38 +#define SAMR_OPNUM_Connect 0x39 +#define SAMR_OPNUM_SetUserInfo 0x3a +#define SAMR_OPNUM_Connect3 0x3e +#define SAMR_OPNUM_Connect4 0x40 + + +/* + * UNION_INFO_ENT is intended to simplify adding new entries to a union. + * If the entry structures are named using the form samr_QueryUserInfoX, + * where X is the sitch_value, you can just add a single line. Note + * that you must also update the fixup function in mlsvc_sam.c. + */ +#define UNION_INFO_ENT(N,NAME) CASE(N) struct NAME##N info##N + + +/* + * Sam account flags used when creating an account. These flags seem + * to be very similar to the USER_INFO_X flags (UF_XXX) in lmaccess.h + * but the values are different. + */ +#define SAMR_AF_ACCOUNTDISABLE 0x0001 +#define SAMR_AF_HOMEDIR_REQUIRED 0x0002 +#define SAMR_AF_PASSWD_NOTREQD 0x0004 +#define SAMR_AF_TEMP_DUPLICATE_ACCOUNT 0x0008 +#define SAMR_AF_NORMAL_ACCOUNT 0x0010 +#define SAMR_AF_MNS_LOGON_ACCOUNT 0x0020 +#define SAMR_AF_INTERDOMAIN_TRUST_ACCOUNT 0x0040 +#define SAMR_AF_WORKSTATION_TRUST_ACCOUNT 0x0080 +#define SAMR_AF_SERVER_TRUST_ACCOUNT 0x0100 +#define SAMR_AF_DONT_EXPIRE_PASSWD 0x0200 +#define SAMR_AF_ACCOUNT_AUTOLOCK 0x0400 + + +#define SAMR_AF_MACHINE_ACCOUNT_MASK ( \ + SAMR_AF_INTERDOMAIN_TRUST_ACCOUNT \ + | SAMR_AF_WORKSTATION_TRUST_ACCOUNT \ + | SAMR_AF_SERVER_TRUST_ACCOUNT) + +#define SAMR_AF_ACCOUNT_TYPE_MASK ( \ + SAMR_AF_TEMP_DUPLICATE_ACCOUNT \ + | SAMR_AF_NORMAL_ACCOUNT \ + | SAMR_AF_INTERDOMAIN_TRUST_ACCOUNT \ + | SAMR_AF_WORKSTATION_TRUST_ACCOUNT \ + | SAMR_AF_SERVER_TRUST_ACCOUNT) + + +/* + * specific access rights which can be used in OpenAlias. + * extracted from Ethereal network analyzer + */ +#define SAMR_ALIAS_ACCESS_SET_INFO 0x00000010 +#define SAMR_ALIAS_ACCESS_GET_INFO 0x00000008 +#define SAMR_ALIAS_ACCESS_GET_MEMBERS 0x00000004 +#define SAMR_ALIAS_ACCESS_DEL_MEMBER 0x00000002 +#define SAMR_ALIAS_ACCESS_ADD_MEMBER 0x00000001 + +/* + * Definition for a SID. The ndl compiler does not allow a typedef of + * a structure containing variable size members. + */ +struct samr_sid { + BYTE Revision; + BYTE SubAuthCount; + BYTE Authority[6]; + SIZE_IS(SubAuthCount) + DWORD SubAuthority[ANY_SIZE_ARRAY]; +}; + + +/* + * SAMR definition of a security_descriptor. + */ +struct samr_sec_desc { + BYTE Revision; + BYTE Sbz1; + WORD Control; + struct samr_sid *owner; + struct samr_sid *group; + struct samr_sid *sacl; + struct samr_sid *dacl; +}; + + +/* + * Definition for a string. The length and allosize should be set to + * twice the string length (i.e. strlen(str) * 2). The runtime code + * will perform the appropriate string to a wide-char conversions, + * so str should point to a regular char * string. + */ +struct samr_string { + WORD length; + WORD allosize; + LPTSTR str; +}; +typedef struct samr_string samr_string_t; + + +/* + * Alternative varying/conformant string definition - for + * non-null terminated strings. This definition must match + * mlrpc_vcbuf_t. + */ +struct samr_vcb { + /* + * size_is (actually a copy of length_is) will + * be inserted here by the marshalling library. + */ + DWORD vc_first_is; + DWORD vc_length_is; + SIZE_IS(vc_length_is) + WORD buffer[ANY_SIZE_ARRAY]; +}; + +struct samr_vcbuf { + WORD wclen; + WORD wcsize; + struct samr_vcb *vcb; +}; +typedef struct samr_vcbuf samr_vcbuf_t; + + +/* + * Handles appear to be a 20 byte object with the top 4 bytes all zero. + * Handles may have some internal structure but this should work since + * we always treat it as an opaque handle. They do appear to contain a + * sequence number which is incremented when new handle is issued. +*/ + +struct samr_handle { + DWORD hand1; + DWORD hand2; + WORD hand3[2]; + BYTE hand4[8]; +}; +typedef struct samr_handle samr_handle_t; + +/* + * A long long, i.e. 64-bit, value. + */ +struct samr_quad { + DWORD low; + DWORD high; +}; +typedef struct samr_quad samr_quad_t; + + +/* + *********************************************************************** + * ConnectAnon. It looks like the SAM handle is identical to an LSA + * handle. See Connect. + *********************************************************************** + */ +OPERATION(SAMR_OPNUM_ConnectAnon) +struct samr_ConnectAnon { + IN DWORD *servername; + IN DWORD access_mask; + OUT samr_handle_t handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * Connect. I'm not sure what the difference is between Connect and + * ConnectAnon but this call seems to work better than ConnectAnon. + *********************************************************************** + */ +OPERATION(SAMR_OPNUM_Connect) +struct samr_Connect { + IN LPTSTR servername; + IN DWORD access_mask; + OUT samr_handle_t handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * SamrConnect3. A new form of connect first seen with Windows 2000. + * A new field has been added to the input request. Value: 0x00000002. + * I haven't looked at the Win2K response yet to see if it differs + * from SAMR_OPNUM_Connect. + *********************************************************************** + */ +OPERATION(SAMR_OPNUM_Connect3) +struct samr_Connect3 { + IN LPTSTR servername; + IN DWORD unknown_02; + IN DWORD access_mask; + OUT samr_handle_t handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * SamrConnect4. A new form of connect first seen with Windows XP. + * The server name is the fully qualified domain name, i.e. + * \\server.procom.com. + *********************************************************************** + */ +OPERATION(SAMR_OPNUM_Connect4) +struct samr_Connect4 { + IN LPTSTR servername; + IN DWORD access_mask; + INOUT DWORD unknown2_00000001; + INOUT DWORD unknown3_00000001; + INOUT DWORD unknown4_00000003; + INOUT DWORD unknown5_00000000; + OUT samr_handle_t handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * CloseHandle closes an association with the SAM. Using the same + * structure as the LSA seems to work. + *********************************************************************** + */ +OPERATION(SAMR_OPNUM_CloseHandle) +struct samr_CloseHandle { + IN samr_handle_t handle; + OUT samr_handle_t result_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * LookupDomain: lookup up the domain SID. + *********************************************************************** + */ +OPERATION(SAMR_OPNUM_LookupDomain) +struct samr_LookupDomain { + IN samr_handle_t handle; + IN samr_string_t domain_name; + OUT struct samr_sid *sid; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * EnumLocalDomain + * + * This looks like a request to get the local domains supported by a + * remote server. NT always seems to return 2 domains: the local + * domain (hostname) and the Builtin domain. + * + * The max_length field is set to 0x2000. + * Enum_context is set to 0 in the request and set to entries_read in + * the reply. Like most of these enums, total_entries is the same as + * entries_read. + *********************************************************************** + */ +struct samr_LocalDomainEntry { + DWORD unknown; + samr_string_t name; +}; + +struct samr_LocalDomainInfo { + DWORD entries_read; + SIZE_IS(entries_read) + struct samr_LocalDomainEntry *entry; +}; + + +OPERATION(SAMR_OPNUM_EnumLocalDomains) +struct samr_EnumLocalDomain { + IN samr_handle_t handle; + INOUT DWORD enum_context; + IN DWORD max_length; + OUT struct samr_LocalDomainInfo *info; + OUT DWORD total_entries; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * OpenDomain + * + * Open a specific domain within the SAM. From this I assume that each + * SAM can handle multiple domains so you need to identify the one with + * which you want to work. Working with a domain handle does appear to + * offer the benefit that you can then use RIDs instead of full SIDs, + * which simplifies things a bit. The domain handle can be used to get + * user and group handles. + *********************************************************************** + */ +OPERATION(SAMR_OPNUM_OpenDomain) +struct samr_OpenDomain { + IN samr_handle_t handle; + IN DWORD access_mask; + IN REFERENCE struct samr_sid *sid; + OUT samr_handle_t domain_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * QueryDomainInfo + * + * Windows 95 Server Manager sends requests for levels 6 and 7 when + * the services menu item is selected. + *********************************************************************** + */ +#define SAMR_QUERY_DOMAIN_INFO_2 2 +#define SAMR_QUERY_DOMAIN_INFO_6 6 +#define SAMR_QUERY_DOMAIN_INFO_7 7 + + +struct samr_QueryDomainInfo2 { + DWORD unknown1; /* 00 00 00 00 */ + DWORD unknown2; /* 00 00 00 80 */ + samr_string_t s1; + samr_string_t domain; + samr_string_t s2; + DWORD sequence_num; /* 2B 00 00 00 */ + DWORD unknown3; /* 00 00 00 00 */ + DWORD unknown4; /* 01 00 00 00 */ + DWORD unknown5; /* 03 00 00 00 */ + DWORD unknown6; /* 01 */ + DWORD num_users; + DWORD num_groups; + DWORD num_aliases; +}; + + +struct samr_QueryDomainInfo6 { + DWORD unknown1; /* 00 00 00 00 */ + DWORD unknown2; /* B0 7F 14 00 */ + DWORD unknown3; /* 00 00 00 00 */ + DWORD unknown4; /* 00 00 00 00 */ + DWORD unknown5; /* 00 00 00 00 */ +}; + + +struct samr_QueryDomainInfo7 { + DWORD unknown1; /* 03 00 00 00 */ +}; + + +union samr_QueryDomainInfo_ru { + UNION_INFO_ENT(2,samr_QueryDomainInfo); + UNION_INFO_ENT(6,samr_QueryDomainInfo); + UNION_INFO_ENT(7,samr_QueryDomainInfo); + DEFAULT char *nullptr; +}; + + +/* + * This structure needs to be declared, even though it can't be used in + * samr_QueryDomainInfo, in order to calculate the correct fixup offsets. + * If ndrgen did the right thing, samr_QueryDomainInfoRes would be one of + * the out parameters. However, if we do it that way, the switch_value + * isn't known early enough to do the fixup calculation. So it all has + * to go in samr_QueryDomainInfo. + */ +struct samr_QueryDomainInfoRes { + DWORD address; + WORD switch_value; + SWITCH(switch_value) + union samr_QueryDomainInfo_ru ru; +}; + + +OPERATION(SAMR_OPNUM_QueryDomainInfo) +struct samr_QueryDomainInfo { + IN samr_handle_t domain_handle; + IN WORD info_level; + /* + * Can't use the standard "OUT result" form because + * we need to include members explicitly. + * OUT struct samr_QueryDomainInfoRes result; + */ + OUT DWORD address; + OUT WORD switch_value; + SWITCH(info_level) + OUT union samr_QueryDomainInfo_ru ru; + OUT DWORD status; +}; + +#define SAMR_QUERY_ALIAS_INFO_1 1 +#define SAMR_QUERY_ALIAS_INFO_3 3 + + +struct samr_QueryAliasInfo1 { + WORD level; + samr_string_t name; + DWORD unknown; + samr_string_t desc; +}; + +struct samr_QueryAliasInfo3 { + WORD level; + samr_string_t desc; +}; + +union samr_QueryAliasInfo_ru { + UNION_INFO_ENT(1,samr_QueryAliasInfo); + UNION_INFO_ENT(3,samr_QueryAliasInfo); + DEFAULT char *nullptr; +}; + +struct samr_QueryAliasInfoRes { + DWORD address; + WORD switch_value; + SWITCH(switch_value) + union samr_QueryAliasInfo_ru ru; +}; + +OPERATION(SAMR_OPNUM_QueryAliasInfo) +struct samr_QueryAliasInfo { + IN samr_handle_t alias_handle; + IN WORD level; + OUT DWORD address; + SWITCH (level) + OUT union samr_QueryAliasInfo_ru ru; + OUT DWORD status; +}; + +OPERATION(SAMR_OPNUM_CreateDomainAlias) +struct samr_CreateDomainAlias { + IN samr_handle_t domain_handle; + IN samr_string_t alias_name; + IN DWORD access_mask; + OUT samr_handle_t alias_handle; + OUT DWORD rid; + OUT DWORD status; +}; + +OPERATION(SAMR_OPNUM_SetAliasInfo) +struct samr_SetAliasInfo { + IN samr_handle_t alias_handle; + IN WORD level; + /* TBD */ + OUT DWORD status; +}; + +OPERATION(SAMR_OPNUM_DeleteDomainAlias) +struct samr_DeleteDomainAlias { + IN samr_handle_t alias_handle; + OUT DWORD status; +}; + +OPERATION(SAMR_OPNUM_OpenAlias) +struct samr_OpenAlias { + IN samr_handle_t domain_handle; + IN DWORD access_mask; + IN DWORD rid; + OUT samr_handle_t alias_handle; + OUT DWORD status; +}; + +struct name_rid { + DWORD rid; + samr_string_t name; +}; + +struct aliases_info { + DWORD count; + DWORD address; + SIZE_IS(count) + struct name_rid info[ANY_SIZE_ARRAY]; +}; + +OPERATION(SAMR_OPNUM_EnumDomainAliases) +struct samr_EnumDomainAliases { + IN samr_handle_t domain_handle; + IN DWORD resume_handle; + IN DWORD mask; + OUT DWORD out_resume; + OUT struct aliases_info *aliases; + OUT DWORD entries; + OUT DWORD status; +}; + +struct user_acct_info { + DWORD index; + DWORD rid; + DWORD ctrl; + samr_string_t name; + samr_string_t fullname; + samr_string_t desc; +}; + +struct user_disp_info { + DWORD count; + /* right now we just need two entries */ + struct user_acct_info acct[2]; +}; + +OPERATION(SAMR_OPNUM_QueryDispInfo) +struct samr_QueryDispInfo { + IN samr_handle_t domain_handle; + IN WORD level; + IN DWORD start_idx; + IN DWORD max_entries; + IN DWORD pref_maxsize; + OUT DWORD total_size; + OUT DWORD returned_size; + OUT WORD switch_value; + OUT DWORD count; + OUT struct user_disp_info *users; + OUT DWORD status; +}; + +struct group_acct_info { + DWORD index; + DWORD rid; + DWORD ctrl; + samr_string_t name; + samr_string_t desc; +}; + +struct group_disp_info { + DWORD count; + /* right now we just need one entry */ + struct group_acct_info acct[1]; +}; + +OPERATION(SAMR_OPNUM_EnumDomainGroups) +struct samr_EnumDomainGroups { + IN samr_handle_t domain_handle; + IN WORD level; + IN DWORD start_idx; + IN DWORD max_entries; + IN DWORD pref_maxsize; + OUT DWORD total_size; + OUT DWORD returned_size; + OUT WORD switch_value; + OUT DWORD count; + OUT struct group_disp_info *groups; + OUT DWORD status; +}; + +/* + *********************************************************************** + * OpenUser + * + * Input must be a domain handle obtained via SAMR_OPNUM_OpenDomain, + * an access mask and the appropriate user rid. The output will be a + * handle for use with the specified user. + *********************************************************************** + */ +OPERATION(SAMR_OPNUM_OpenUser) +struct samr_OpenUser { + IN samr_handle_t handle; + IN DWORD access_mask; + IN DWORD rid; + OUT samr_handle_t user_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * DeleteUser + *********************************************************************** + */ +OPERATION(SAMR_OPNUM_DeleteUser) +struct samr_DeleteUser { + INOUT samr_handle_t user_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * QueryUserInfo + * + * Provides various pieces of information on a specific user (see + * SAM_Q_QUERY_USERINFO and SAM_R_QUERY_USERINFO). The handle must + * be a valid SAM user handle. + * + * QueryUserInfo ( + * IN samr_handle_t user_handle, + * IN WORD switch_value, + * OUT union switch(switch_value) { + * case 1: struct QueryUserInfo1 *info1; + * } bufptr, + * OUT DWORD status + * ) + * + * The cases identified so far are: + * + * 1 = username, fullname, description and some other stuff. + * 2 = unknown + * 3 = large structure containing user rid, group rid, username + * and fullname. + * 4 = unknown + * 5 = large structure (like 3) containing user rid, group rid, + * username, fullname and description. + * 6 = username and fullname + * 7 = username + * 8 = fullname + * 9 = group rid + * 16 = used after creating a new account + * + * Due to an ndrgen bug, a function must be provided to to patch the + * offsets used by the unmarshalling code at runtime. In order to + * simplify things it is useful to use a naming convention that + * indicates the switch value for each structure. + * + *********************************************************************** + */ + + +#define SAMR_QUERY_USER_INFO_1 1 +#define SAMR_QUERY_USER_UNAME_AND_FNAME 6 +#define SAMR_QUERY_USER_USERNAME 7 +#define SAMR_QUERY_USER_FULLNAME 8 +#define SAMR_QUERY_USER_GROUPRID 9 +#define SAMR_QUERY_USER_UNKNOWN16 16 + + +struct samr_QueryUserInfo1 { + samr_string_t username; + samr_string_t fullname; + DWORD group_rid; + samr_string_t description; + samr_string_t unknown; +}; + + +struct samr_QueryUserInfo6 { + samr_string_t username; + samr_string_t fullname; +}; + +struct samr_QueryUserInfo7 { + samr_string_t username; +}; + + +struct samr_QueryUserInfo8 { + samr_string_t fullname; +}; + + +struct samr_QueryUserInfo9 { + DWORD group_rid; +}; + + +struct samr_QueryUserInfo16 { + DWORD unknown; +}; + + +union QueryUserInfo_result_u { + UNION_INFO_ENT(1,samr_QueryUserInfo); + UNION_INFO_ENT(6,samr_QueryUserInfo); + UNION_INFO_ENT(7,samr_QueryUserInfo); + UNION_INFO_ENT(8,samr_QueryUserInfo); + UNION_INFO_ENT(9,samr_QueryUserInfo); + UNION_INFO_ENT(16,samr_QueryUserInfo); + DEFAULT char *nullptr; +}; + + +/* + * This structure needs to be declared, even though it can't be used in + * samr_QueryUserInfo, in order to get the appropriate size to calculate + * the correct fixup offsets. If ndrgen did the right thing, + * QueryUserInfo_result would be one of the out parameters. However, if + * we do it that way, the switch_value isn't known early enough to do + * the fixup calculation. So it all has to go in samr_QueryUserInfo. + */ +struct QueryUserInfo_result { + DWORD address; + WORD switch_value; + SWITCH(switch_value) + union QueryUserInfo_result_u ru; +}; + + +OPERATION(SAMR_OPNUM_QueryUserInfo) +struct samr_QueryUserInfo { + IN samr_handle_t user_handle; + IN WORD switch_value; + /* + * Can't use this form because we need to include members explicitly. + * OUT struct QueryUserInfo_result result; + */ + OUT DWORD address; + OUT WORD switch_index; + SWITCH(switch_value) + OUT union QueryUserInfo_result_u ru; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * QueryUserGroups + *********************************************************************** + */ +struct samr_UserGroups { + DWORD rid; + DWORD attr; +}; + + +struct samr_UserGroupInfo { + DWORD n_entry; + SIZE_IS(n_entry) + struct samr_UserGroups *groups; +}; + + +OPERATION(SAMR_OPNUM_QueryUserGroups) +struct samr_QueryUserGroups { + IN samr_handle_t user_handle; + OUT struct samr_UserGroupInfo *info; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * LookupName + *********************************************************************** + */ +struct samr_LookupNameTable { + DWORD n_entry; + SIZE_IS(n_entry) + samr_string_t names[ANY_SIZE_ARRAY]; +}; + + +struct samr_LookupRidTable { + DWORD n_entry; + SIZE_IS(n_entry) + DWORD *rid; +}; + +struct samr_RidType { + DWORD n_entry; + SIZE_IS(n_entry) + DWORD *rid_type; +}; + + +OPERATION(SAMR_OPNUM_LookupNames) +struct samr_LookupNames { + IN samr_handle_t handle; + IN DWORD n_entry; + IN DWORD max_n_entry; + IN DWORD index; + IN DWORD total; + IN samr_string_t name; + OUT struct samr_LookupRidTable rids; + OUT struct samr_RidType rid_types; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * OpenGroup + * + * Input must be a domain handle obtained via SAMR_OPNUM_OpenDomain, + * an access mask and the appropriate group rid. The output will be a + * handle for use with the specified group. + *********************************************************************** + */ +OPERATION(SAMR_OPNUM_OpenGroup) +struct samr_OpenGroup { + IN samr_handle_t handle; + IN DWORD access_mask; + IN DWORD rid; + OUT samr_handle_t group_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * QueryGroupInfo + * + * Input must be a group handle obtained via SAMR_OPNUM_OpenGroup, + * an access mask and the appropriate group rid. The output will + * be a handle for use with the specified group. + *********************************************************************** + */ +struct samr_QueryGroupInfo1 { + samr_string_t groupname; +}; + + +union samr_QueryGroupInfo_result_u { + UNION_INFO_ENT(1,samr_QueryGroupInfo); + DEFAULT char *nullptr; +}; + + +struct samr_QueryGroupInfo_result { + DWORD address; + WORD switch_index; + SWITCH(switch_index) + union samr_QueryGroupInfo_result_u ru; +}; + + +OPERATION(SAMR_OPNUM_QueryGroupInfo) +struct samr_QueryGroupInfo { + IN samr_handle_t group_handle; + IN DWORD switch_value; + OUT DWORD address; + OUT WORD switch_index; + SWITCH(switch_index) + OUT union samr_QueryGroupInfo_result_u ru; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * StoreGroupInfo + * + * This definition is mostly just a place holder in case this is useful + * in the future. Note that it may not be correct. The information is + * from a netmon trace captured when I added a group description. I + * haven't implemented it because we don't have to update anything on + * the PDC. The description should almost certainly be in a separate + * structure. + *********************************************************************** + */ +OPERATION(SAMR_OPNUM_StoreGroupInfo) +struct samr_StoreGroupInfo { + IN samr_handle_t group_handle; + IN DWORD switch_value; + IN samr_string_t group_description; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * Request 0x2c is a user request. The only parameter is a user handle. + * The response is 12 bytes of the form: + * unknown: 00 00 BB 01 (443) + * unknown: 00 00 00 00 + * status: 00 00 00 00 + * RPC book lists this as GetUsrDomPwInfo. + *********************************************************************** + */ +struct samr_UserPwInfo { + WORD unknown1; + WORD unknown2; + DWORD unknown3; +}; + + +OPERATION(SAMR_OPNUM_GetUserPwInfo) +struct samr_GetUserPwInfo { + IN samr_handle_t user_handle; + OUT struct samr_UserPwInfo pw_info; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * CreateUser + * + * Create a user in the domain specified by the domain handle. The + * domain handle is obtained obtained via SAMR_OPNUM_OpenDomain. There + * is an unknown value at the end of the request: 0xe00500b0. + * The output will be a handle for use with the specified user and the + * user's RID. I think the RID may be a pointer but the value came back + * as zero once so I've padded it out so that the marshalling doesn't + * get confused. + *********************************************************************** + */ +OPERATION(SAMR_OPNUM_CreateUser) +struct samr_CreateUser { + IN samr_handle_t handle; + IN samr_vcbuf_t username; + IN DWORD account_flags; + IN DWORD unknown_e00500b0; + OUT samr_handle_t user_handle; + OUT DWORD maybe_ptr; + OUT DWORD rid; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * ChangeUserPasswd + *********************************************************************** + */ +struct samr_newpasswd { + BYTE data[516]; +}; + + +struct samr_oldpasswd { + BYTE data[16]; +}; + + +OPERATION(SAMR_OPNUM_ChangeUserPasswd) +struct samr_ChangeUserPasswd { + IN LPTSTR servername; + IN LPTSTR username; + IN struct samr_newpasswd *nt_newpasswd; + IN struct samr_oldpasswd *nt_oldpasswd; + IN struct samr_newpasswd *lm_newpasswd; + IN struct samr_oldpasswd *lm_oldpasswd; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * GetDomainPwInfo + *********************************************************************** + */ +OPERATION(SAMR_OPNUM_GetDomainPwInfo) +struct samr_GetDomainPwInfo { + IN LPTSTR servername; + OUT WORD unknown0; + OUT WORD unknown1; + OUT WORD unknown2; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * SetUserInfo + * + * +++ 20 byte user handle and the union switch_value +++ + * 00 00 00 00 77 F2 DD D5 66 48 D4 11 AD 5F D1 CD + * 18 43 7A DF 17 00 17 00 + * + * +++ 14 dwords (56 bytes) of zeros +++ + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * + * +++ 9 sets of something - 72 bytes +++ + * 00 00 02 00 D0 04 8A 77 + * 00 00 02 00 D0 04 8A 77 + * 00 00 02 00 D0 04 8A 77 + * 00 00 02 00 D0 04 8A 77 + * 00 00 02 00 D0 04 8A 77 + * 00 00 02 00 D0 04 8A 77 + * 00 00 02 00 D0 04 8A 77 + * 00 00 02 00 D0 04 8A 77 + * 00 00 02 00 D0 04 8A 77 + * + * +++ 9 DWORD zeros +++ + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 + * + * +++ miscellaneous +++ + * 01 02 00 00 + * 80 00 00 00 + * FA 27 F8 09 + * A8 00 00 00 70 F1 14 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 + * + * +++ encrypted password buffer - 512 bytes +++ + * 76 68 E8 AA 23 4F 62 C4 81 4E 30 B8 92 29 66 B9 + * 12 FF 3A 84 82 3A 55 0F C7 18 EA 56 86 50 D7 C5 + * 43 BA 9C F8 32 D4 E0 15 74 A1 6F E1 59 C2 F2 95 + * 53 A9 F2 68 9F 7F 29 B9 88 4C 65 A5 C1 DC 0B 44 + * B8 3C ED 74 D1 6A F7 09 66 97 94 6B 2C 3A A5 88 + * 39 34 C6 FE 24 59 30 2D CF 6D 7F D5 EC B1 9A 84 + * E6 57 96 29 40 32 FB 62 9D 93 E2 BE D8 A3 74 88 + * 8B 85 BC A0 76 D6 C9 DB 8C AF 81 BD 8A F0 08 8D + * 23 B0 52 FD 69 DE EF A1 36 E5 30 19 BD DA 67 A3 + * 81 BD 3F D0 2A A2 8F 60 62 B0 8D 34 9E A4 4F 20 + * 4E 79 93 82 58 A8 E5 6F 7A DC 12 13 33 E6 74 02 + * 4C 32 F9 FC 1A E1 C5 0D E2 CC 36 8D FC 72 87 DD + * 6C 44 E3 6F 4B FD 46 10 08 89 E5 64 B8 27 14 83 + * E7 08 DE CF 69 C7 E1 40 63 DF CB 67 95 73 03 1B + * CA 99 E1 1B 53 2A 89 6B 30 39 CD 5C DF A0 8A 1C + * 4E 50 74 7C 6D 3D E7 EA E9 B2 97 DD 38 7B DA EC + * 1A AD DA CE C4 58 9B 29 F3 6D 30 70 4E 63 6D 84 + * DB DC 5B CD 9A 4E 57 9C E4 65 5D 4F 76 E3 C7 52 + * 8B 3B 20 0A 3B 4C 4B B1 2E 5B 4D AB BA 2F 45 6A + * CA 17 AD 9F C0 B2 07 FB 56 7F E4 3F 9F D4 C6 8C + * A1 05 BF 53 42 1E 67 F4 57 54 E3 2C 38 CF E1 94 + * 75 69 F7 4E 5C 74 CC B3 FD EF 73 3F D5 28 22 EC + * 9B 40 E1 1D 65 44 7C BB 69 88 57 10 05 3A C5 48 + * 8E 4F 77 DB 1A 5C 49 9C D5 06 00 AC 79 BC 7E 89 + * B0 01 66 70 88 A2 E5 DF 96 DC 75 98 10 12 45 02 + * 33 35 6C DF 74 8B 14 2F 26 C6 FD 7A B4 D0 A6 7D + * DE 2B 13 44 EF 34 46 4D 9D 3E C3 75 BC 11 B4 41 + * 27 58 25 1E AF AA F0 BB DA 27 7A 1E AE 81 1A 78 + * 44 19 DE FC C4 7C 4E 32 44 F7 57 2A 41 A2 85 DC + * C0 AD 5D 6B 58 FD 2E 75 25 B9 F2 B6 19 82 E5 0E + * B6 69 0D C1 27 A9 B6 40 A6 50 49 E5 CB 17 98 65 + * 88 18 CA E4 1D 2E 20 F7 DE 8E 7D F2 9D A5 6B CD + * + * D6 79 45 71 + * + * +++ table of 9 things +++ + * 01 00 00 00 00 00 00 00 00 00 00 00 + * 01 00 00 00 00 00 00 00 00 00 00 00 + * 01 00 00 00 00 00 00 00 00 00 00 00 + * 01 00 00 00 00 00 00 00 00 00 00 00 + * 01 00 00 00 00 00 00 00 00 00 00 00 + * 01 00 00 00 00 00 00 00 00 00 00 00 + * 01 00 00 00 00 00 00 00 00 00 00 00 + * 01 00 00 00 00 00 00 00 00 00 00 00 + * 01 00 00 00 00 00 00 00 00 00 00 00 + * + * +++ miscellaneous +++ + * EC 04 00 00 00 00 00 00 15 00 00 00 + * FF FF FF FF FF FF FF FF FF FF FF FF + * FF FF FF FF FF FF FF FF FF + * + *********************************************************************** + */ + +#define SAMR_SET_USER_INFO_23 23 +#define SAMR_SET_USER_DATA_SZ 516 + +#define SAMR_MINS_PER_WEEK 10080 +#define SAMR_HOURS_PER_WEEK 168 + +#define SAMR_HOURS_MAX_SIZE (SAMR_MINS_PER_WEEK / 8) +#define SAMR_HOURS_SET_LEN(LEN) ((LEN) / 8) +#define SAMR_SET_USER_HOURS_SZ 21 + + +struct samr_sd { + DWORD length; + SIZE_IS(length) + BYTE *data; +}; + + +/* + * There is some sort of logon bitmap structure in here, which I + * think is a varying and conformant array, i.e. + * + * struct samr_logon_hours { + * DWORD size_is; (0x04ec) + * DWORD first_is; (zero) + * DWORD length_is; (0xa8) + * BYTE bitmap[21]; + * }; + * + * struct samr_logon_info { + * DWORD length; + * SIZE_IS(length / 8) + * struct samr_logon_hours *hours; + * }; + * + * There are 10080 minutes/week => 10080/8 = 1260 (0x04EC). + * So size_is is set as some sort of maximum. + * + * There are 168 hours/week => 168/8 = 21 (0xA8). Since there are 21 + * bytes (all set to 0xFF), this is is probably the default setting. + * + * ndrgen has a problem with complex [size_is] statements. For now, + * we can try to fake it using two separate components. + */ +struct samr_logon_hours { + DWORD size; + DWORD first; + DWORD length; + BYTE bitmap[SAMR_SET_USER_HOURS_SZ]; +}; + + +struct samr_logon_info { + DWORD units; + DWORD hours; +}; + + +struct samr_oem_password { + BYTE password[512]; + DWORD length; +}; + + +struct samr_SetUserInfo23 { + samr_quad_t logon_time; /* 00 00 00 00 00 00 00 00 */ + samr_quad_t logoff_time; /* 00 00 00 00 00 00 00 00 */ + samr_quad_t kickoff_time; /* 00 00 00 00 00 00 00 00 */ + samr_quad_t passwd_last_set_time; /* 00 00 00 00 00 00 00 00 */ + samr_quad_t passwd_can_change_time; /* 00 00 00 00 00 00 00 00 */ + samr_quad_t passwd_must_change_time; /* 00 00 00 00 00 00 00 00 */ + + samr_vcbuf_t user_name; /* 00 00 00 00 00 00 00 00 */ + samr_vcbuf_t full_name; /* 00 00 02 00 D0 04 8A 77 */ + samr_vcbuf_t home_dir; /* 00 00 02 00 D0 04 8A 77 */ + samr_vcbuf_t home_drive; /* 00 00 02 00 D0 04 8A 77 */ + samr_vcbuf_t logon_script; /* 00 00 02 00 D0 04 8A 77 */ + samr_vcbuf_t profile_path; /* 00 00 02 00 D0 04 8A 77 */ + samr_vcbuf_t acct_desc; /* 00 00 02 00 D0 04 8A 77 */ + samr_vcbuf_t workstations; /* 00 00 02 00 D0 04 8A 77 */ + samr_vcbuf_t unknown1; /* 00 00 02 00 D0 04 8A 77 */ + samr_vcbuf_t unknown2; /* 00 00 02 00 D0 04 8A 77 */ + samr_vcbuf_t lm_password; /* 00 00 00 00 00 00 00 00 */ + samr_vcbuf_t nt_password; /* 00 00 00 00 00 00 00 00 */ + samr_vcbuf_t unknown3; /* 00 00 00 00 00 00 00 00 */ + + struct samr_sd sd; /* 00 00 00 00 00 00 00 00 */ + DWORD user_rid; /* 00 00 00 00 */ + DWORD group_rid; /* 01 02 00 00 */ + DWORD acct_info; /* 80 00 00 00 */ + DWORD flags; /* FA 27 F8 09 */ + struct samr_logon_info logon_info; /* A8 00 00 00 70 F1 14 00->0xFF */ + /* + * The following 12 bytes are encoded in Ethereal as: + * + * WORD bad_pwd_count; + * WORD logon_count; + * + * WORD country; (default 0) + * WORD codepage; + * + * BYTE nt_pwd_set; + * BYTE lm_pwd_set; + * BYTE expired_flag; + * BYTE unknown_char; + */ + DWORD unknown4_zero; /* 00 00 00 00 */ + DWORD unknown5_zero; /* 00 00 00 00 */ + DWORD unknown6_zero; /* 00 00 00 00 */ + BYTE password[SAMR_SET_USER_DATA_SZ]; +}; + + +union samr_SetUserInfo_u { + UNION_INFO_ENT(23,samr_SetUserInfo); + DEFAULT char *nullptr; +}; + + +struct samr_SetUserInfo_s { + WORD index; + WORD switch_value; + SWITCH(switch_value) + union samr_SetUserInfo_u ru; +}; + + +/* + IN DWORD unknown_04EC; + IN DWORD unknown_zero; + IN DWORD logon_bitmap_size; + IN BYTE logon_bitmap[SAMR_SET_USER_HOURS_SZ]; +*/ +OPERATION(SAMR_OPNUM_SetUserInfo) +struct samr_SetUserInfo { + IN samr_handle_t user_handle; + IN struct samr_SetUserInfo_s info; + IN struct samr_logon_hours logon_hours; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * The SAMR interface definition. + *********************************************************************** + */ +INTERFACE(0) +union samr_interface { + CASE(SAMR_OPNUM_ConnectAnon) + struct samr_ConnectAnon ConnectAnon; + CASE(SAMR_OPNUM_CloseHandle) + struct samr_CloseHandle CloseHandle; + CASE(SAMR_OPNUM_LookupDomain) + struct samr_LookupDomain LookupDomain; + CASE(SAMR_OPNUM_EnumLocalDomains) + struct samr_EnumLocalDomain EnumLocalDomain; + CASE(SAMR_OPNUM_OpenDomain) + struct samr_OpenDomain OpenDomain; + CASE(SAMR_OPNUM_QueryDomainInfo) + struct samr_QueryDomainInfo QueryDomainInfo; + CASE(SAMR_OPNUM_LookupNames) + struct samr_LookupNames LookupNames; + CASE(SAMR_OPNUM_OpenUser) + struct samr_OpenUser OpenUser; + CASE(SAMR_OPNUM_DeleteUser) + struct samr_DeleteUser DeleteUser; + CASE(SAMR_OPNUM_QueryUserInfo) + struct samr_QueryUserInfo QueryUserInfo; + CASE(SAMR_OPNUM_QueryUserGroups) + struct samr_QueryUserGroups QueryUserGroups; + CASE(SAMR_OPNUM_OpenGroup) + struct samr_OpenGroup OpenGroup; + CASE(SAMR_OPNUM_GetUserPwInfo) + struct samr_GetUserPwInfo GetUserPwInfo; + CASE(SAMR_OPNUM_CreateUser) + struct samr_CreateUser CreateUser; + CASE(SAMR_OPNUM_ChangeUserPasswd) + struct samr_ChangeUserPasswd ChangeUserPasswd; + CASE(SAMR_OPNUM_GetDomainPwInfo) + struct samr_GetDomainPwInfo GetDomainPwInfo; + CASE(SAMR_OPNUM_Connect) + struct samr_Connect Connect; + CASE(SAMR_OPNUM_SetUserInfo) + struct samr_SetUserInfo SetUserInfo; + CASE(SAMR_OPNUM_Connect3) + struct samr_Connect3 Connect3; + CASE(SAMR_OPNUM_Connect4) + struct samr_Connect4 Connect4; + CASE(SAMR_OPNUM_QueryDispInfo) + struct samr_QueryDispInfo QueryDispInfo; + CASE(SAMR_OPNUM_OpenAlias) + struct samr_OpenAlias OpenAlias; + CASE(SAMR_OPNUM_CreateDomainAlias) + struct samr_CreateDomainAlias CreateDomainAlias; + CASE(SAMR_OPNUM_SetAliasInfo) + struct samr_SetAliasInfo SetAliasInfo; + CASE(SAMR_OPNUM_QueryAliasInfo) + struct samr_QueryAliasInfo QueryAliasInfo; + CASE(SAMR_OPNUM_DeleteDomainAlias) + struct samr_DeleteDomainAlias DeleteDomainAlias; + CASE(SAMR_OPNUM_EnumDomainAliases) + struct samr_EnumDomainAliases EnumDomainAliases; + CASE(SAMR_OPNUM_EnumDomainGroups) + struct samr_EnumDomainGroups EnumDomainGroups; +}; +typedef union samr_interface samr_interface_t; +EXTERNTYPEINFO(samr_interface) + +#endif /* _MLSVC_SAM_NDL_ */ diff --git a/usr/src/uts/common/smbsrv/ndl/spoolss.ndl b/usr/src/uts/common/smbsrv/ndl/spoolss.ndl new file mode 100644 index 000000000000..1598e09d50b0 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ndl/spoolss.ndl @@ -0,0 +1,489 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MLSVC_SPOOLSS_NDL_ +#define _MLSVC_SPOOLSS_NDL_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Printing and Spooling RPC interface definition. + */ + +#include "ndrtypes.ndl" + + +/* + * The spoolss opcodes. + */ +#define SPOOLSS_OPNUM_OpenPrinter 0x01 +#define SPOOLSS_OPNUM_GetJob 0x03 +#define SPOOLSS_OPNUM_DeletePrinter 0x06 +#define SPOOLSS_OPNUM_GetPrinterDriver 0x0b +#define SPOOLSS_OPNUM_DeletePrinterDriver 0x0d +#define SPOOLSS_OPNUM_AddPrintProcessor 0x0e +#define SPOOLSS_OPNUM_GetPrintProcessorDirectory 0x10 +#define SPOOLSS_OPNUM_AbortPrinter 0x15 +#define SPOOLSS_OPNUM_ReadPrinter 0x16 +#define SPOOLSS_OPNUM_WaitForPrinterChange 0x1c +#define SPOOLSS_OPNUM_AddForm 0x1e +#define SPOOLSS_OPNUM_DeleteForm 0x1f +#define SPOOLSS_OPNUM_GetForm 0x20 +#define SPOOLSS_OPNUM_SetForm 0x21 +#define SPOOLSS_OPNUM_EnumMonitors 0x24 +#define SPOOLSS_OPNUM_AddPort 0x25 +#define SPOOLSS_OPNUM_ConfigurePort 0x26 +#define SPOOLSS_OPNUM_DeletePort 0x27 +#define SPOOLSS_OPNUM_CreatePrinterIc 0x28 +#define SPOOLSS_OPNUM_PlayDescriptionPrinterIc 0x29 +#define SPOOLSS_OPNUM_DeletePrinterIc 0x2a +#define SPOOLSS_OPNUM_AddPrinterConnection 0x2b +#define SPOOLSS_OPNUM_DeletePrinterConnection 0x2c +#define SPOOLSS_OPNUM_PrinterMessageBox 0x2d +#define SPOOLSS_OPNUM_AddMonitor 0x2e +#define SPOOLSS_OPNUM_DeleteMonitor 0x2f +#define SPOOLSS_OPNUM_DeletePrintProcessor 0x30 +#define SPOOLSS_OPNUM_AddPrintProvider 0x31 +#define SPOOLSS_OPNUM_DeletePrintProvider 0x32 +#define SPOOLSS_OPNUM_ResetPrinter 0x34 +#define SPOOLSS_OPNUM_FindFirstChangeNotify 0x36 +#define SPOOLSS_OPNUM_FindNextChangeNotify 0x37 +#define SPOOLSS_OPNUM_RouterFindFirstNotify 0x39 +#define SPOOLSS_OPNUM_ReplyOpenPrinter 0x3a +#define SPOOLSS_OPNUM_RouterReplyPrinter 0x3b +#define SPOOLSS_OPNUM_ReplyClosePrinter 0x3c +#define SPOOLSS_OPNUM_AddPortEx 0x3d +#define SPOOLSS_OPNUM_RemoteFindFirstChangeNotify 0x3e +#define SPOOLSS_OPNUM_SpoolerInitialize 0x3f +#define SPOOLSS_OPNUM_ResetPrinterEx 0x40 +#define SPOOLSS_OPNUM_RouterRefreshChangeNotify 0x42 +#define SPOOLSS_OPNUM_OpenPrinter2 0x45 + + +/* + * The private handle definition for this interface. + */ +struct spoolss_handle { + DWORD data[5]; +}; +typedef struct spoolss_handle spoolss_handle_t; + + +OPERATION(SPOOLSS_OPNUM_OpenPrinter) +struct spoolss_OpenPrinter { + IN DWORD dontcare; + OUT spoolss_handle_t handle; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_GetJob) +struct spoolss_GetJob { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_DeletePrinter) +struct spoolss_DeletePrinter { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_GetPrinterDriver) +struct spoolss_GetPrinterDriver { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_DeletePrinterDriver) +struct spoolss_DeletePrinterDriver { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_AddPrintProcessor) +struct spoolss_AddPrintProcessor { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_GetPrintProcessorDirectory) +struct spoolss_GetPrintProcessorDirectory { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_AbortPrinter) +struct spoolss_AbortPrinter { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_ReadPrinter) +struct spoolss_ReadPrinter { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_WaitForPrinterChange) +struct spoolss_WaitForPrinterChange { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_AddForm) +struct spoolss_AddForm { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_DeleteForm) +struct spoolss_DeleteForm { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_GetForm) +struct spoolss_GetForm { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_SetForm) +struct spoolss_SetForm { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_EnumMonitors) +struct spoolss_EnumMonitors { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_AddPort) +struct spoolss_AddPort { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_ConfigurePort) +struct spoolss_ConfigurePort { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_DeletePort) +struct spoolss_DeletePort { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_CreatePrinterIc) +struct spoolss_CreatePrinterIc { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_PlayDescriptionPrinterIc) +struct spoolss_PlayDescriptionPrinterIc { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_DeletePrinterIc) +struct spoolss_DeletePrinterIc { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_AddPrinterConnection) +struct spoolss_AddPrinterConnection { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_DeletePrinterConnection) +struct spoolss_DeletePrinterConnection { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_PrinterMessageBox) +struct spoolss_PrinterMessageBox { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_AddMonitor) +struct spoolss_AddMonitor { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_DeleteMonitor) +struct spoolss_DeleteMonitor { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_DeletePrintProcessor) +struct spoolss_DeletePrintProcessor { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_AddPrintProvider) +struct spoolss_AddPrintProvider { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_DeletePrintProvider) +struct spoolss_DeletePrintProvider { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_ResetPrinter) +struct spoolss_ResetPrinter { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_FindFirstChangeNotify) +struct spoolss_FindFirstChangeNotify { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_FindNextChangeNotify) +struct spoolss_FindNextChangeNotify { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_RouterFindFirstNotify) +struct spoolss_RouterFindFirstNotify { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_ReplyOpenPrinter) +struct spoolss_ReplyOpenPrinter { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_RouterReplyPrinter) +struct spoolss_RouterReplyPrinter { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_ReplyClosePrinter) +struct spoolss_ReplyClosePrinter { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_AddPortEx) +struct spoolss_AddPortEx { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_RemoteFindFirstChangeNotify) +struct spoolss_RemoteFindFirstChangeNotify { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_SpoolerInitialize) +struct spoolss_SpoolerInitialize { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_ResetPrinterEx) +struct spoolss_ResetPrinterEx { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_RouterRefreshChangeNotify) +struct spoolss_RouterRefreshChangeNotify { + IN DWORD dontcare; + OUT DWORD status; +}; + + +OPERATION(SPOOLSS_OPNUM_OpenPrinter2) +struct spoolss_OpenPrinter2 { + IN DWORD dontcare; + OUT spoolss_handle_t handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * The spoolss interface definition. + *********************************************************************** + */ +INTERFACE(0) +union spoolss_interface { + CASE(SPOOLSS_OPNUM_OpenPrinter) + struct spoolss_OpenPrinter OpenPrinter; + CASE(SPOOLSS_OPNUM_GetJob) + struct spoolss_GetJob GetJob; + CASE(SPOOLSS_OPNUM_DeletePrinter) + struct spoolss_DeletePrinter DeletePrinter; + CASE(SPOOLSS_OPNUM_GetPrinterDriver) + struct spoolss_GetPrinterDriver GetPrinterDriver; + CASE(SPOOLSS_OPNUM_DeletePrinterDriver) + struct spoolss_DeletePrinterDriver DeletePrinterDriver; + CASE(SPOOLSS_OPNUM_AddPrintProcessor) + struct spoolss_AddPrintProcessor AddPrintProcessor; + CASE(SPOOLSS_OPNUM_GetPrintProcessorDirectory) + struct spoolss_GetPrintProcessorDirectory + GetPrintProcessorDirectory; + CASE(SPOOLSS_OPNUM_AbortPrinter) + struct spoolss_AbortPrinter AbortPrinter; + CASE(SPOOLSS_OPNUM_ReadPrinter) + struct spoolss_ReadPrinter ReadPrinter; + CASE(SPOOLSS_OPNUM_WaitForPrinterChange) + struct spoolss_WaitForPrinterChange WaitForPrinterChange; + CASE(SPOOLSS_OPNUM_AddForm) + struct spoolss_AddForm AddForm; + CASE(SPOOLSS_OPNUM_DeleteForm) + struct spoolss_DeleteForm DeleteForm; + CASE(SPOOLSS_OPNUM_GetForm) + struct spoolss_GetForm GetForm; + CASE(SPOOLSS_OPNUM_SetForm) + struct spoolss_SetForm SetForm; + CASE(SPOOLSS_OPNUM_EnumMonitors) + struct spoolss_EnumMonitors EnumMonitors; + CASE(SPOOLSS_OPNUM_AddPort) + struct spoolss_AddPort AddPort; + CASE(SPOOLSS_OPNUM_ConfigurePort) + struct spoolss_ConfigurePort ConfigurePort; + CASE(SPOOLSS_OPNUM_DeletePort) + struct spoolss_DeletePort DeletePort; + CASE(SPOOLSS_OPNUM_CreatePrinterIc) + struct spoolss_CreatePrinterIc CreatePrinterIc; + CASE(SPOOLSS_OPNUM_PlayDescriptionPrinterIc) + struct spoolss_PlayDescriptionPrinterIc + PlayDescriptionPrinterIc; + CASE(SPOOLSS_OPNUM_DeletePrinterIc) + struct spoolss_DeletePrinterIc DeletePrinterIc; + CASE(SPOOLSS_OPNUM_AddPrinterConnection) + struct spoolss_AddPrinterConnection AddPrinterConnection; + CASE(SPOOLSS_OPNUM_DeletePrinterConnection) + struct spoolss_DeletePrinterConnection DeletePrinterConnection; + CASE(SPOOLSS_OPNUM_PrinterMessageBox) + struct spoolss_PrinterMessageBox PrinterMessageBox; + CASE(SPOOLSS_OPNUM_AddMonitor) + struct spoolss_AddMonitor AddMonitor; + CASE(SPOOLSS_OPNUM_DeleteMonitor) + struct spoolss_DeleteMonitor DeleteMonitor; + CASE(SPOOLSS_OPNUM_DeletePrintProcessor) + struct spoolss_DeletePrintProcessor DeletePrintProcessor; + CASE(SPOOLSS_OPNUM_AddPrintProvider) + struct spoolss_AddPrintProvider AddPrintProvider; + CASE(SPOOLSS_OPNUM_DeletePrintProvider) + struct spoolss_DeletePrintProvider DeletePrintProvider; + CASE(SPOOLSS_OPNUM_ResetPrinter) + struct spoolss_ResetPrinter ResetPrinter; + CASE(SPOOLSS_OPNUM_FindFirstChangeNotify) + struct spoolss_FindFirstChangeNotify FindFirstChangeNotify; + CASE(SPOOLSS_OPNUM_FindNextChangeNotify) + struct spoolss_FindNextChangeNotify FindNextChangeNotify; + CASE(SPOOLSS_OPNUM_RouterFindFirstNotify) + struct spoolss_RouterFindFirstNotify RouterFindFirstNotify; + CASE(SPOOLSS_OPNUM_ReplyOpenPrinter) + struct spoolss_ReplyOpenPrinter ReplyOpenPrinter; + CASE(SPOOLSS_OPNUM_RouterReplyPrinter) + struct spoolss_RouterReplyPrinter RouterReplyPrinter; + CASE(SPOOLSS_OPNUM_ReplyClosePrinter) + struct spoolss_ReplyClosePrinter ReplyClosePrinter; + CASE(SPOOLSS_OPNUM_AddPortEx) + struct spoolss_AddPortEx AddPortEx; + CASE(SPOOLSS_OPNUM_RemoteFindFirstChangeNotify) + struct spoolss_RemoteFindFirstChangeNotify + RemoteFindFirstChangeNotify; + CASE(SPOOLSS_OPNUM_SpoolerInitialize) + struct spoolss_SpoolerInitialize SpoolerInitialize; + CASE(SPOOLSS_OPNUM_ResetPrinterEx) + struct spoolss_ResetPrinterEx ResetPrinterEx; + CASE(SPOOLSS_OPNUM_RouterRefreshChangeNotify) + struct spoolss_RouterRefreshChangeNotify + RouterRefreshChangeNotify; + CASE(SPOOLSS_OPNUM_OpenPrinter2) + struct spoolss_OpenPrinter2 OpenPrinter2; +}; +typedef union spoolss_interface spoolss_interface_t; +EXTERNTYPEINFO(spoolss_interface) + +#endif /* _MLSVC_SPOOLSS_NDL_ */ diff --git a/usr/src/uts/common/smbsrv/ndl/srvsvc.ndl b/usr/src/uts/common/smbsrv/ndl/srvsvc.ndl new file mode 100644 index 000000000000..b952feacb726 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ndl/srvsvc.ndl @@ -0,0 +1,1331 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MLSVC_LANMAN_NDL_ +#define _MLSVC_LANMAN_NDL_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * LanMan RPC (WKSSVC and SRVSVC) interface definitions. + */ + +#include "ndrtypes.ndl" + +/* + * WARNING: The cpp(1) macros in this file are not understood by + * /usr/bin/cpp. Use /usr/libexec/cpp instead. + */ + +/* + * TYPE CONSTRUCTOR MACROS FOR INFORMATION RESULTS + **************************************************************** + * + * This is an explanation of the macros that follow this comment. + * + * The LANMAN API's look something like this: + * + * NetXXXGetInfo ( + * IN char * servername, + * IN char * XXX_name, + * IN int level, + * OUT char ** bufptr); + * + * The bufptr is a pointer-to-pointer (**). The NetXXXGetInfo() function + * malloc()s memory, and sets *bufptr to the memory. The API's + * are undiscriminated about what bufptr really points to. + * + * However, for RPI (Remote Procedure Interface), this just won't fly. + * We have to know what the result data looks like in order to + * properly (un)marshall it. + * + * As best we can determine, the MSC developers use an RPI that looks + * like this (approximately in IDL): + * + * RemoteNetXXXGetInfo ( + * IN char * servername, + * IN char * XXX_name, + * IN int level, + * OUT union switch(level) { + * case(1): XXX_INFO_1 * info1; + * case(2): XXX_INFO_2 * info2; + * } bufptr); + * + * The level guides the (un)marshalling as it follows the pointer. + * DCE(MS) IDL will automatically form a structure for the union + * which looks about like this (much as Sun/RPC does): + * + * struct { + * int _keyvalue_; + * union { + * XXX_INFO_1 *info1; + * XXX_INFO_2 *info2; + * } _u_; + * } bufptr; + * + * This struct is not made visible to the application. It is purely + * an internal (automagic) thing. However, ndrgen does not do this. + * The ndrgen input MUST remain a valid C header file, and all + * struct and union declarations must be exact, and we (would) have + * to tediously code sequences like this (approximately NDL)): + * + * union XXXGetInfo_result_u { + * [case(1)] + * XXX_INFO_1 * info1; + * [case(2)] + * XXX_INFO_2 * info2; + * }; + * + * struct XXXGetInfo_result { + * int level; + * + * union XXXGetInfo_result_u bufptr; + * }; + * + * struct XXXGetInfo_param { // still have to code this one + * [in] char * servername; + * [in] ushort level; + * [out] struct XXXGetInfo_result result; + * }; + * + * This is error prone and difficult to write, and more difficult + * and distracting to read. It is hard to pick through the + * necessary evils and see what's really going on. To mitigate + * the situation, we have a series of macros which generate + * the tedious code, and are easily recognized as supporting + * fluff rather than important structures: + * + * INFO1RES_DEFINITION(XXXGetInfo, + * INFO1RES_UNION_ENTRY(XXXGetInfo, 1) + * INFO1RES_UNION_ENTRY(XXXGetInfo, 2)) + * + * structt XXXGetInfo_param { // still have to code this one + * [in] char * servername; + * [in] ushort level; + * [out] struct XXXGetInfo_result result; + * }; + * + * The INFO1RES_DEFINITION macro defines two types: + * + * union ...__ru {...} + * struct ..._result { DWORD level; union ..._ru bufptr; } + * + * There is a similar macro, INFO1RESBUF_DEFINITION, which defines + * actual space rather than just pointers. It defines: + * + * union ...._rb {...} + * typedef union ..._rb ..._rb; + * + * Which is handy in functions because the initial coding sequence + * looks something like: + * + * XXXGetInfoParam (struct XXXGetInfo_param *param) { + * XXXGetInfo_rb rb; + * + * param->result.level = param->level; // for marshalling + * param->result.bufptr.nullptr = &rb; // anything fits + * + * There are two flavors of Info results. The first is the + * single XXX_INFO_x result, which the foregoing example + * uses. The second flavor is when there are multiple entries + * possible. Again, for the sake of guiding the marshalling, + * the RPIs use something accommodating: + * + * struct XXX_INFO_1_result { + * unsigned entriesread; + * [size_is(entriesread)] + * XXX_INFO_1 * table; + * }; + * + * union { XXX_INFO_1_result *info1; ...} + * + * Notice this is using XXX_INFO_1_result rather than just XXX_INFO_1. + * The requirements from this point are much like before. Because of + * the variable-length value, there is no realistic way to do something + * like INFO1RESBUF_DEFINITION. + * + * There are two sets of macros here. INFO1RES_xxx are for the + * single result case, and INFONRES_xxx for the multiple entry case. + */ + +/* + * INFO1RES_... + * Type constructors for single-result case + */ + +#define INFO1RES_DEFINITION(INFOPREF, ENTRIES) \ + INFO1RES_UNION(INFOPREF, ENTRIES) \ + INFO1RES_STRUCT(INFOPREF) + +#define INFO1RES_UNION(INFOPREF, ENTRIES) \ + union INFOPREF##__ru { \ + INFO1RES_UNION_NULLPTR \ + ENTRIES \ + }; + +#define INFO1RES_UNION_NULLPTR \ + DEFAULT char * nullptr; + +#define INFO1RES_UNION_ENTRY(INFOPREF,NUM) \ + CASE(NUM) struct INFOPREF##_##NUM * bufptr##NUM; + +#define INFO1RES_STRUCT(INFOPREF) \ + struct INFOPREF##_result { \ + DWORD level; \ + SWITCH(level) \ + union INFOPREF##__ru bufptr; \ + }; + +/* + * INFO1RESBUF_... + * Type constructors for single-result buffering. + */ + + +#ifndef NDRGEN +#define INFO1RESBUF_DEFINITION(INFOPREF, ENTRIES) \ + typedef union INFOPREF##_rb { \ + ENTRIES \ + } INFOPREF##_rb; +#define INFO1RESBUF_UNION_ENTRY(INFOPREF,NUM) \ + CASE(NUM) struct INFOPREF##_##NUM buf##NUM; +#else +#define INFO1RESBUF_DEFINITION(INFOPREF, ENTRIES) +#define INFO1RESBUF_UNION_ENTRY(INFOPREF,NUM) +#endif + + + + +/* + * INFONRES_... + * Type constructors for multiple-result case + */ + +#define INFONRES_RESULT(INFOPREF,NUM) \ + struct INFOPREF##_##NUM##_result { \ + DWORD entriesread; \ + SIZE_IS(entriesread) \ + struct INFOPREF##_##NUM *entries; \ + }; + +#define INFONRES_DEFINITION(INFOPREF, ENTRIES) \ + INFONRES_UNION(INFOPREF, ENTRIES) \ + INFONRES_STRUCT(INFOPREF) + +#define INFONRES_UNION(INFOPREF, ENTRIES) \ + union INFOPREF##__ru { \ + INFONRES_UNION_NULLPTR \ + INFONRES_UNION_INFONRES \ + ENTRIES \ + }; + +#define INFONRES_UNION_NULLPTR \ + DEFAULT char * nullptr; + +#ifndef NDRGEN +#define INFONRES_UNION_INFONRES \ + struct mslm_infonres * p; +#else +#define INFONRES_UNION_INFONRES +#endif + +#define INFONRES_UNION_ENTRY(INFOPREF,NUM) \ + CASE(NUM) struct INFOPREF##_##NUM##_result * bufptr##NUM; + +#define INFONRES_STRUCT(INFOPREF) \ + struct INFOPREF##_result { \ + DWORD level; \ + SWITCH(level) \ + union INFOPREF##__ru bufptr; \ + }; + +#ifndef NDRGEN +/* + * This just makes things a little easier on the stub modules: + * + * XXXGetInfoParam (struct XXXGetInfo_param *param) { + * struct mslm_infonres infonres; + * + * infonres.entriesread = 0; + * infonres.entries = 0; + * param->result.level = param->level; // for marshalling + * param->result.bufptr.p = &infonres; + */ +struct mslm_infonres { + DWORD entriesread; + void * entries; +}; +#endif + + +/* + * SERVER SERVICE (SRVSVC) + **************************************************************** + */ + + +#define SRVSVC_OPNUM_NetConnectEnum 0x08 +#define SRVSVC_OPNUM_NetFileEnum 0x09 +#define SRVSVC_OPNUM_NetFileClose 0x0b +#define SRVSVC_OPNUM_NetSessionEnum 0x0c +#define SRVSVC_OPNUM_NetSessionDel 0x0d +#define SRVSVC_OPNUM_NetShareAdd 0x0e +#define SRVSVC_OPNUM_NetShareEnum 0x0f +#define SRVSVC_OPNUM_NetShareGetInfo 0x10 +#define SRVSVC_OPNUM_NetShareSetInfo 0x11 +#define SRVSVC_OPNUM_NetShareDel 0x12 +#define SRVSVC_OPNUM_NetServerGetInfo 0x15 +#define SRVSVC_OPNUM_NetServerSetInfo 0x16 +#define SRVSVC_OPNUM_NetRemoteTOD 0x1c +#define SRVSVC_OPNUM_NetNameValidate 0x21 +#define SRVSVC_OPNUM_NetShareEnumSticky 0x24 +#define SRVSVC_OPNUM_NetGetFileSecurity 0x27 +#define SRVSVC_OPNUM_NetSetFileSecurity 0x28 + +/* + *********************************************************************** + * NetConnectEnum: + * + * Description: + * + * Request for mslm_NetConnectEnum returns + * info of resources in MLRPC server connected + * to network. Correctly level 0 and level 1 information + * are supported. + * + *********************************************************************** + */ + +/* + * Level 0 connect information. + */ +struct mslm_NetConnectInfoBuf0 { + DWORD coni0_id; +}; + +struct mslm_NetConnectInfo0 { + DWORD entries_read; + SIZE_IS(entries_read) + struct mslm_NetConnectInfoBuf0 *ci0; +}; + +/* + * Level 1 connect information. + */ +struct mslm_NetConnectInfoBuf1 { + DWORD coni1_id; + DWORD coni1_type; + DWORD coni1_num_opens; + DWORD coni1_num_users; + DWORD coni1_time; + LPTSTR coni1_username; + LPTSTR coni1_netname; /* share name */ +}; + +struct mslm_NetConnectInfo1 { + DWORD entries_read; + SIZE_IS(entries_read) + struct mslm_NetConnectInfoBuf1 *ci1; +}; + +union mslm_NetConnectInfoResUnion { + CASE(0) struct mslm_NetConnectInfo0 *info0; + CASE(1) struct mslm_NetConnectInfo1 *info1; + DEFAULT char *nullptr; +}; + +struct mslm_NetConnectInfo { + DWORD level; + DWORD switch_value; + SWITCH(switch_value) + union mslm_NetConnectInfoResUnion ru; +}; + +OPERATION(SRVSVC_OPNUM_NetConnectEnum) +struct mslm_NetConnectEnum { + IN LPTSTR servername; + IN LPTSTR qualifier; /* share name */ + INOUT struct mslm_NetConnectInfo info; + IN DWORD pref_max_len; + OUT DWORD total_entries; + INOUT DWORD *resume_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * NetFileEnum: under construction. + *********************************************************************** + */ +struct mslm_NetFileInfoBuf3 { + DWORD fi3_id; + DWORD fi3_permissions; + DWORD fi3_num_locks; + LPTSTR fi3_pathname; + LPTSTR fi3_username; +}; + + +struct mslm_NetFileInfo3 { + DWORD entries_read; + SIZE_IS(entries_read) + struct mslm_NetFileInfoBuf3 *fi3; +}; + + +union mslm_NetFileInfoResUnion { + CASE(3) struct mslm_NetFileInfo3 *info3; + DEFAULT char *nullptr; +}; + + +struct mslm_NetFileInfo { + DWORD level; + DWORD switch_value; + SWITCH(switch_value) + union mslm_NetFileInfoResUnion ru; +}; + + +OPERATION(SRVSVC_OPNUM_NetFileEnum) +struct mslm_NetFileEnum { + IN LPTSTR servername; + IN DWORD unknown1; + IN DWORD unknown2; + INOUT struct mslm_NetFileInfo info; + IN DWORD pref_max_len; + OUT DWORD total_entries; + INOUT DWORD *resume_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * NetFileClose + * + * I think this definition is complete but as it doesn't do anything + * it probably isn't a big issue. It is used to close files reported + * via NetFileEnum (i.e. the file id). + *********************************************************************** + */ +OPERATION(SRVSVC_OPNUM_NetFileClose) +struct mslm_NetFileClose { + IN LPTSTR servername; + IN DWORD file_id; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * NetShareGetInfo: netname is the name of a share. + *********************************************************************** + */ +struct mslm_NetShareGetInfo0 { + LPTSTR shi0_netname; +}; + + +struct mslm_NetShareGetInfo1 { + LPTSTR shi1_netname; + DWORD shi1_type; /* type of resource such as IPC$ */ + LPTSTR shi1_comment; +}; + + +struct mslm_NetShareGetInfo2 { + LPTSTR shi2_netname; + DWORD shi2_type; + LPTSTR shi2_comment; + DWORD shi2_permissions; + DWORD shi2_max_uses; + DWORD shi2_current_uses; + LPTSTR shi2_path; + LPTSTR shi2_passwd; +}; + +struct mslm_NetShareGetInfo502 { + LPTSTR shi502_netname; + DWORD shi502_type; + LPTSTR shi502_comment; + DWORD shi502_permissions; + DWORD shi502_max_uses; + DWORD shi502_current_uses; + LPTSTR shi502_path; + LPTSTR shi502_passwd; + DWORD shi502_reserved; + DWORD shi502_security_descriptor; +}; + +struct mslm_NetShareGetInfo1005 { + DWORD shi1005_flags; +}; + +union mlsm_NetShareGetInfoResUnion { + CASE(0) struct mslm_NetShareGetInfo0 *info0; + CASE(1) struct mslm_NetShareGetInfo1 *info1; + CASE(2) struct mslm_NetShareGetInfo2 *info2; + CASE(502) struct mslm_NetShareGetInfo502 *info502; + CASE(1005) struct mslm_NetShareGetInfo1005 *info1005; + DEFAULT char *nullptr; +}; + + +struct mlsm_NetShareGetInfoRes { + DWORD switch_value; + SWITCH(switch_value) + union mlsm_NetShareGetInfoResUnion ru; +}; + + +OPERATION(SRVSVC_OPNUM_NetShareGetInfo) +struct mlsm_NetShareGetInfo { + IN LPTSTR servername; + IN REFERENCE LPTSTR netname; + IN DWORD level; + OUT struct mlsm_NetShareGetInfoRes result; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * NetShareSetInfo: netname is the name of a share. + *********************************************************************** + */ +OPERATION(SRVSVC_OPNUM_NetShareSetInfo) +struct mlsm_NetShareSetInfo { + IN LPTSTR servername; + IN REFERENCE LPTSTR netname; + IN DWORD level; +/* + * This should accept all the same levels as NetShareGetInfo + * but we always return ACCESS_DENIED for now. So there's no + * point in unmarshalling the share information. + * + * IN struct mlsm_NetShareGetInfoRes result; + */ + OUT DWORD parm_err_ptr; + OUT DWORD parm_err; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * NetSessionEnum + * + * The NetSessionEnum function provides information about sessions + * established on a server. + * + * Only members of the Administrators or Account Operators local groups + * can successfully execute the NetSessionEnum function at level 1 or + * level 2. No special group membership is required for level 0 or level + * 10 calls. + * + * Windows NT/2000/XP: The parameter order is as follows. + * + * NET_API_STATUS NetSessionEnum(LPWSTR servername, + * LPWSTR UncClientName, + * LPWSTR username, + * DWORD level, + * LPBYTE *bufptr, + * DWORD prefmaxlen, + * LPDWORD entriesread, + * LPDWORD totalentries, + * LPDWORD resume_handle); + * + * Windows 95/98/Me: The calling application must use the cbBuffer parameter + * to specify the size, in bytes, of the information buffer pointed to by the + * pbBuffer parameter. (The cbBuffer parameter replaces the prefmaxlen + * parameter.) Neither a user name parameter nor a resume handle parameter is + * available on this platform. Therefore, the parameter list is as follows. + * + * API_FUNCTION NetSessionEnum(const char FAR *pszServer, + * short sLevel, + * char FAR *pbBuffer, + * unsigned short cbBuffer, + * unsigned short FAR *pcEntriesRead, + * unsigned short FAR *pcTotalAvail); + * + * Parameters + * + * servername + * [in] Pointer to a string that specifies the DNS or NetBIOS name of the + * remote server on which the function is to execute. If this parameter is + * NULL, the local computer is used. + * Windows NT 4.0 and earlier: This string must begin with \\. + * + * UncClientName + * [in] Pointer to a string that specifies the name of the computer session + * for which information is to be returned. If this parameter is NULL, + * NetSessionEnum returns information for all computer sessions on the server. + * + * username + * [in] Pointer to a string that specifies the name of the user for which + * information is to be returned. If this parameter is NULL, NetSessionEnum + * returns information for all users. + * + * level + * [in] Specifies the information level of the data. This parameter can be + * one of the following values. + * Windows NT/2000/XP: The following levels are valid. + * Value Meaning + * 0 Return the name of the computer that established the session. + * The bufptr parameter points to an array of SESSION_INFO_0 + * structures. + * 1 Return the name of the computer, name of the user, and open files, + * pipes, and devices on the computer. The bufptr parameter points to + * an array of SESSION_INFO_1 structures. + * 2 In addition to the information indicated for level 1, return the + * type of client and how the user established the session. The bufptr + * parameter points to an array of SESSION_INFO_2 structures. + * 10 Return the name of the computer, name of the user, and active and + * idle times for the session. The bufptr parameter points to an array + * of SESSION_INFO_10 structures. + * 502 Return the name of the computer; name of the user; open files, + * pipes, and devices on the computer; and the name of the transport + * the client is using. The bufptr parameter points to an array of + * SESSION_INFO_502 structures. + * + * Windows 95/98/Me: The following level is valid. + * Value Meaning + * 50 Return the name of the computer, name of the user, open files on + * the computer, and the name of the transport protocol the client is + * using. The pbBuffer parameter points to an array of session_info_50 + * structures. + * + * bufptr + * [out] Pointer to the buffer that receives the data. The format of this + * data depends on the value of the level parameter. + * Windows NT/2000/XP: This buffer is allocated by the system and must be + * freed using the NetApiBufferFree function. Note that you must free the + * buffer even if the function fails with ERROR_MORE_DATA. + * Windows 95/98/Me: The caller must allocate and deallocate this buffer. + * + * prefmaxlen + * [in] Specifies the preferred maximum length of returned data, in bytes. + * If you specify MAX_PREFERRED_LENGTH, the function allocates the amount + * of memory required for the data. If you specify another value in this + * parameter, it can restrict the number of bytes that the function returns. + * If the buffer size is insufficient to hold all entries, the function + * returns ERROR_MORE_DATA. For more information, see Network Management + * Function Buffers and Network Management Function Buffer Lengths. + * + * entriesread + * [out] Pointer to a value that receives the count of elements actually + * enumerated. + * + * totalentries + * [out] Pointer to a value that receives the total number of entries that + * could have been enumerated from the current resume position. + * + * resume_handle + * [in/out] Pointer to a value that contains a resume handle which is used + * to continue an existing session search. The handle should be zero on the + * first call and left unchanged for subsequent calls. If resume_handle is + * NULL, no resume handle is stored. + * + * + * SESSION_INFO_1 + * ============== + * The SESSION_INFO_1 structure contains information about the session, + * including name of the computer; name of the user; and open files, pipes, + * and devices on the computer. + * + * Members + * + * sesi1_cname + * Pointer to a Unicode string specifying the name of the computer that + * established the session. + * + * sesi1_username + * Pointer to a Unicode string specifying the name of the user who established + * the session. + * + * sesi1_num_opens + * Specifies a DWORD value that contains the number of files, devices, + * and pipes opened during the session. + * + * sesi1_time + * Specifies a DWORD value that contains the number of seconds the session + * has been active. + * + * sesi1_idle_time + * Specifies a DWORD value that contains the number of seconds the session + * has been idle. + * + * sesi1_user_flags + * Specifies a DWORD value that describes how the user established the + * session. This member can be one of the following values: + * SESS_GUEST The user specified by the sesi1_username member + * established the session using a guest account. + * SESS_NOENCRYPTION The user specified by the sesi1_username member + * established the session without using password + * encryption. + *********************************************************************** + */ + +#define SESS_GUEST 0x00000001 +#define SESS_NOENCRYPTION 0x00000002 + +struct mslm_SESSION_INFO_0 { + LPTSTR sesi0_cname; +}; +INFONRES_RESULT(mslm_SESSION_INFO, 0) + +struct mslm_SESSION_INFO_1 { + LPTSTR sesi1_cname; + LPTSTR sesi1_uname; + DWORD sesi1_nopens; + DWORD sesi1_time; + DWORD sesi1_itime; + DWORD sesi1_uflags; +}; +INFONRES_RESULT(mslm_SESSION_INFO, 1) + +INFONRES_DEFINITION(mslm_NetSessionEnum, + INFONRES_UNION_ENTRY(mslm_SESSION_INFO, 0) + INFONRES_UNION_ENTRY(mslm_SESSION_INFO, 1)) + +OPERATION(SRVSVC_OPNUM_NetSessionEnum) +struct mslm_NetSessionEnum { + IN LPTSTR servername; + IN DWORD unc_clientname; + IN DWORD username; + INOUT DWORD level; + INOUT struct mslm_NetSessionEnum_result result; + IN DWORD pref_max_len; + OUT DWORD total_entries; + INOUT DWORD *resume_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * NetSessionDel (Platform SDK: Network Management) + * + * The NetSessionDel function ends a network session between a server + * and a workstation. + * + * Security Requirements + * Only members of the Administrators or Account Operators local group + * can successfully execute the NetSessionDel function. + * + * Windows NT/2000/XP: The parameter order is as follows. + * + * NET_API_STATUS NetSessionDel(LPWSTR servername, + * LPWSTR UncClientName, + * LPWSTR username); + * + * Windows 95/98/Me: The sReserved parameter replaces the username + * parameter. For more information, see the following Remarks section. + * The parameter list is as follows. + * + * API_FUNCTION NetSessionDel(const char FAR *pszServer, + * const char FAR *pszClientName, + * short sReserved); + * + * Parameters + * + * servername + * [in] Pointer to a string that specifies the DNS or NetBIOS name + * of the remote server on which the function is to execute. If this + * parameter is NULL, the local computer is used. + * Windows NT 4.0 and earlier: This string must begin with \\. + * + * UncClientName + * [in] Pointer to a string that specifies the computer name of the + * client to disconnect. If UncClientName is NULL, then all the sessions + * of the user identified by the username parameter will be deleted on + * the server specified by servername. For more information, see + * NetSessionEnum. + * + * username + * [in] Pointer to a string that specifies the name of the user whose + * session is to be terminated. If this parameter is NULL, all users' + * sessions from the client specified by the UncClientName parameter + * are to be terminated. + * + * Remarks + * Windows 95/98/Me: You must specify the session key in the sReserved + * parameter when you call NetSessionDel. The session key is returned by + * the NetSessionEnum function or the NetSessionGetInfo function in the + * sesi50_key member of the session_info_50 structure. + *********************************************************************** + */ + +OPERATION(SRVSVC_OPNUM_NetSessionDel) +struct mslm_NetSessionDel { + IN LPTSTR servername; + IN LPTSTR unc_clientname; + IN LPTSTR username; + OUT DWORD status; +}; + + +/* + * SRVSVC NetServerGetInfo ( + * IN LPTSTR servername, + * IN DWORD level, + * OUT union switch(level) { + * case 100: _SERVER_INFO_100 * p100; + * case 101: _SERVER_INFO_101 * p101; + * case 102: _SERVER_INFO_102 * p102; + * } bufptr, + * OUT DWORD status + * ) + */ + +/* for svX_platform */ +#define SV_PLATFORM_ID_OS2 400 +#define SV_PLATFORM_ID_NT 500 + +/* Bit-mapped values for svX_type fields */ +#define SV_TYPE_WORKSTATION 0x00000001 +#define SV_TYPE_SERVER 0x00000002 +#define SV_TYPE_SQLSERVER 0x00000004 +#define SV_TYPE_DOMAIN_CTRL 0x00000008 +#define SV_TYPE_DOMAIN_BAKCTRL 0x00000010 +#define SV_TYPE_TIME_SOURCE 0x00000020 +#define SV_TYPE_AFP 0x00000040 +#define SV_TYPE_NOVELL 0x00000080 +#define SV_TYPE_DOMAIN_MEMBER 0x00000100 +#define SV_TYPE_PRINTQ_SERVER 0x00000200 +#define SV_TYPE_DIALIN_SERVER 0x00000400 +#define SV_TYPE_XENIX_SERVER 0x00000800 +#define SV_TYPE_SERVER_UNIX SV_TYPE_XENIX_SERVER +#define SV_TYPE_NT 0x00001000 +#define SV_TYPE_WFW 0x00002000 + +#define SV_TYPE_SERVER_MFPN 0x00004000 +#define SV_TYPE_SERVER_NT 0x00008000 +#define SV_TYPE_POTENTIAL_BROWSER 0x00010000 +#define SV_TYPE_BACKUP_BROWSER 0x00020000 +#define SV_TYPE_MASTER_BROWSER 0x00040000 +#define SV_TYPE_DOMAIN_MASTER 0x00080000 +#define SV_TYPE_SERVER_OSF 0x00100000 +#define SV_TYPE_SERVER_VMS 0x00200000 +#define SV_TYPE_WINDOWS 0x00400000 /* Windows95 and above */ +#define SV_TYPE_ALTERNATE_XPORT 0x20000000 /* return list for + * alternate transport */ +#define SV_TYPE_LOCAL_LIST_ONLY 0x40000000 /* Return local list only */ +#define SV_TYPE_DOMAIN_ENUM 0x80000000 +#define SV_TYPE_ALL 0xFFFFFFFF /* handy for NetServerEnum2 */ + +/* NT-Server 4.0 sends 0x0006_120B */ +#define SV_TYPE_SENT_BY_NT_4_0_SERVER \ + ( SV_TYPE_WORKSTATION \ + | SV_TYPE_SERVER \ + | SV_TYPE_DOMAIN_CTRL \ + | SV_TYPE_NT \ + | SV_TYPE_BACKUP_BROWSER \ + | SV_TYPE_MASTER_BROWSER) +/* NT-workstation 4.0 send 0x0004_1013 */ +#define SV_TYPE_SENT_BY_NT_4_0_WORKSTATION \ + ( SV_TYPE_WORKSTATION \ + | SV_TYPE_SERVER \ + | SV_TYPE_DOMAIN_BAKCTRL \ + | SV_TYPE_NT \ + | SV_TYPE_MASTER_BROWSER) + +/* Special value for sv102_disc that specifies infinite disconnect time */ +#define SV_NODISC (-1L) /* No autodisconnect timeout enforced */ + +/* Values of svX_security field */ +#define SV_USERSECURITY 1 +#define SV_SHARESECURITY 0 + +/* Values of svX_hidden field */ +#define SV_HIDDEN 1 +#define SV_VISIBLE 0 + + +/* Let's get some info already */ +struct mslm_SERVER_INFO_100 { + DWORD sv100_platform_id; + LPTSTR sv100_name; +}; + +struct mslm_SERVER_INFO_101 { + DWORD sv101_platform_id; + LPTSTR sv101_name; + DWORD sv101_version_major; + DWORD sv101_version_minor; + DWORD sv101_type; + LPTSTR sv101_comment; +}; + +struct mslm_SERVER_INFO_102 { + DWORD sv102_platform_id; + LPTSTR sv102_name; + DWORD sv102_version_major; + DWORD sv102_version_minor; + DWORD sv102_type; + LPTSTR sv102_comment; + DWORD sv102_users; + DWORD sv102_disc; + DWORD sv102_hidden; /* BOOL */ + DWORD sv102_announce; + DWORD sv102_anndelta; + DWORD sv102_licenses; + LPTSTR sv102_userpath; +}; + +union mslm_NetServerGetInfo_ru { + CASE(100) struct mslm_SERVER_INFO_100 *bufptr100; + CASE(101) struct mslm_SERVER_INFO_101 *bufptr101; + CASE(102) struct mslm_SERVER_INFO_102 *bufptr102; + DEFAULT char *nullptr; +}; + +struct mslm_NetServerGetInfo_result { + DWORD level; + SWITCH(level) + union mslm_NetServerGetInfo_ru bufptr; +}; + + +OPERATION(SRVSVC_OPNUM_NetServerGetInfo) +struct mslm_NetServerGetInfo { + IN LPTSTR servername; + IN DWORD level; + OUT struct mslm_NetServerGetInfo_result result; + OUT DWORD status; +}; + +/* + * SRVSVC NetRemoteTOD ( + * IN LPTSTR servername, + * OUT _TIME_OF_DAY_INFO *bufptr, + * OUT long status + * ) + */ + +struct mslm_TIME_OF_DAY_INFO { + DWORD tod_elapsedt; + DWORD tod_msecs; + DWORD tod_hours; + DWORD tod_mins; + DWORD tod_secs; + DWORD tod_hunds; + DWORD tod_timezone; + DWORD tod_tinterval; + DWORD tod_day; + DWORD tod_month; + DWORD tod_year; + DWORD tod_weekday; +}; + +OPERATION(SRVSVC_OPNUM_NetRemoteTOD) +struct mslm_NetRemoteTOD { + IN LPTSTR servername; + OUT struct mslm_TIME_OF_DAY_INFO *bufptr; + OUT DWORD status; +}; + +/* + * SRVSVC_NetNameValidate ( + * IN LPTSTR servername; + * IN REFERENCE LPTSTR pathname; + * IN DWORD type; + * IN DWORD flags; + * OUT DWORD status; + * ) + */ +OPERATION(SRVSVC_OPNUM_NetNameValidate) +struct mslm_NetNameValidate { + IN LPTSTR servername; + IN REFERENCE LPTSTR pathname; + IN DWORD type; + IN DWORD flags; + OUT DWORD status; +}; + +/* + * SRVSVC NetShareEnum ( + * IN LPTSTR servername, + * IN DWORD level; + * OUT union switch(level) { + * case 0: struct { + * DWORD entriesread; + * [size_is(entriesread)] + * _SHARE_INFO_0 *entries; + * } *bufptr0; + * case 1: struct { + * DWORD entriesread; + * [size_is(entriesread)] + * _SHARE_INFO_1 *entries; + * } *bufptr1; + * ... + * } bufptr, + * IN DWORD prefmaxlen, + * OUT DWORD totalentries, + * IN OUT DWORD ?* resume_handle, + * OUT DWORD status + * ) + */ + +/* + * Share types for shiX_type fields - duplicated from cifs.h + */ +#ifndef _SHARE_TYPES_DEFINED_ +#define _SHARE_TYPES_DEFINED_ +#define STYPE_DISKTREE 0x00000000 +#define STYPE_PRINTQ 0x00000001 +#define STYPE_DEVICE 0x00000002 +#define STYPE_IPC 0x00000003 +#define STYPE_MASK 0x0000000F +#define STYPE_DFS 0x00000064 +#define STYPE_HIDDEN 0x80000000 +#define STYPE_SPECIAL 0x80000000 +#endif /* _SHARE_TYPES_DEFINED_ */ + +/* Maximum uses for shiX_max_uses fields */ +#define SHI_USES_UNLIMITED (DWORD)-1 + + + +struct mslm_SHARE_INFO_0 { + LPTSTR shi0_netname; +}; +INFONRES_RESULT(mslm_SHARE_INFO,0) + +struct mslm_SHARE_INFO_1 { + LPTSTR shi1_netname; + DWORD shi1_type; + LPTSTR shi1_remark; +}; +INFONRES_RESULT(mslm_SHARE_INFO,1) + +struct mslm_SHARE_INFO_2 { + LPTSTR shi2_netname; + DWORD shi2_type; + LPTSTR shi2_remark; + DWORD shi2_permissions; + DWORD shi2_max_uses; + DWORD shi2_current_uses; + LPTSTR shi2_path; + LPTSTR shi2_passwd; +}; +INFONRES_RESULT(mslm_SHARE_INFO,2) + +/* + * Note: shi502_security_descriptor should be a pointer to a + * security descriptor (W32SEC_SECURITY_DESCRIPTOR): + * PSECURITY_DESCRIPTOR shi502_security_descriptor; + * + * For now we can just use a DWORD and set it to zero. + */ +struct mslm_SHARE_INFO_502 { + LPTSTR shi502_netname; + DWORD shi502_type; + LPTSTR shi502_remark; + DWORD shi502_permissions; + DWORD shi502_max_uses; + DWORD shi502_current_uses; + LPTSTR shi502_path; + LPTSTR shi502_passwd; + DWORD shi502_reserved; + DWORD shi502_security_descriptor; +}; +INFONRES_RESULT(mslm_SHARE_INFO,502) + +union mslm_NetShareAddInfo_u { + CASE(2) struct mslm_SHARE_INFO_2 *info2; + CASE(502) struct mslm_SHARE_INFO_502 *info502; +}; + +struct mslm_NetShareAddInfo { + DWORD switch_value; + SWITCH(switch_value) + union mslm_NetShareAddInfo_u un; +}; + + +OPERATION(SRVSVC_OPNUM_NetShareAdd) +struct mslm_NetShareAdd { + IN LPTSTR servername; + IN DWORD level; + IN struct mslm_NetShareAddInfo info; + INOUT DWORD *parm_err; + OUT DWORD status; +}; + + +INFONRES_DEFINITION(mslm_NetShareEnum, + INFONRES_UNION_ENTRY(mslm_SHARE_INFO,0) + INFONRES_UNION_ENTRY(mslm_SHARE_INFO,1) + INFONRES_UNION_ENTRY(mslm_SHARE_INFO,2) + INFONRES_UNION_ENTRY(mslm_SHARE_INFO,502)) + + +OPERATION(SRVSVC_OPNUM_NetShareEnum) +struct mslm_NetShareEnum { + IN LPTSTR servername; + INOUT DWORD level; + OUT struct mslm_NetShareEnum_result result; + IN DWORD prefmaxlen; + OUT DWORD totalentries; + INOUT DWORD * resume_handle; /* not sure about ptr */ + OUT DWORD status; +}; + + +/* + * Delete a share. The reserved field appears in netmon + * but I've left it out in case it's not always present. + * This won't affect RPC processing. + */ +OPERATION(SRVSVC_OPNUM_NetShareDel) +struct mslm_NetShareDel { + IN LPTSTR servername; + IN REFERENCE LPTSTR netname; + /* IN DWORD reserved; */ + OUT DWORD status; +}; + + +/* + * NetShareEnumSticky is the same as NetShareEnum except that hidden + * shares are not returned. This call was apparently added due to a + * bug in the NT implementation of NetShareEnum - it didn't process + * the resume handle correctly so that attempts to enumerate large + * share lists resulted in an infinite loop. + */ +OPERATION(SRVSVC_OPNUM_NetShareEnumSticky) +struct mslm_NetShareEnumSticky { + IN LPTSTR servername; + INOUT DWORD level; + OUT struct mslm_NetShareEnum_result result; + IN DWORD prefmaxlen; + OUT DWORD totalentries; + INOUT DWORD * resume_handle; /* not sure about ptr */ + OUT DWORD status; +}; + +/* + * When you install Windows NT Server Tools on a Win95 client, + * a security tab will be added to properties dialog box of files/folders. + * Within this security tab, when you try to get/set permissions on a + * file/folder the next two RPC calls are used. + */ +OPERATION(SRVSVC_OPNUM_NetGetFileSecurity) +struct mslm_NetGetFileSecurity { + IN LPTSTR servername; + IN LPTSTR sharename; + IN REFERENCE LPTSTR filename; + IN DWORD securityinfo; + + /* + * Right now, we can't send back SD of the requested object + * in MLRPC code, so we just reply with access denied error + * code. Thus, this output declaration is only valid in this + * case i.e., it's not complete. + * It looks like: + * + * A Pointer + * A Length + * + * A Pointer + * A Length (equal to the prev length) + * A buffer + * + * return value + */ + OUT DWORD length; + OUT DWORD status; +}; + +/* + * This is the request: + * + * R_SRVSVC: RPC Client call srvsvc:NetrpSetFileSecurity(..) + * R_SRVSVC: SRVSVC_HANDLE ServerName = \\WK76-177 + * R_SRVSVC: LPWSTR ShareName = AFSHIN + * R_SRVSVC: LPWSTR lpFileName = \salek.txt + * R_SRVSVC: SECURITY_INFORMATION SecurityInformation = 4 (0x4) + * -R_SRVSVC: PADT_SECURITY_DESCRIPTOR SecurityDescriptor {..} + * R_SRVSVC: DWORD Length = 64 (0x40) + * R_SRVSVC: LPBYTE Buffer = 4496048 (0x449AB0) + * R_SRVSVC: LPBYTE Buffer [..] = 01 00 04 80 00 00 00 00 00 00 00 00 00 00 00 + * ... + * + * 000000A0 00 83 46 00 0B 00 00 00 00 00 00 00 0B 00 ..F........... + * 000000B0 00 00 5C 00 5C 00 57 00 4B 00 37 00 36 00 2D 00 ..\.\.W.K.7.6.-. + * 000000C0 31 00 37 00 37 00 00 00 08 00 16 83 46 00 07 00 1.7.7.......F... + * 000000D0 00 00 00 00 00 00 07 00 00 00 41 00 46 00 53 00 ..........A.F.S. + * 000000E0 48 00 49 00 4E 00 00 00 00 00 0B 00 00 00 00 00 H.I.N........... + * 000000F0 00 00 0B 00 00 00 5C 00 73 00 61 00 6C 00 65 00 ......\.s.a.l.e. + * 00000100 6B 00 2E 00 74 00 78 00 74 00 00 00 00 00 04 00 k...t.x.t....... + * 00000110 00 00 40 00 00 00 B0 9A 44 00 40 00 00 00 01 00 ..@.....D.@..... + * 00000120 04 80 00 00 00 00 00 00 00 00 00 00 00 00 14 00 ................ + * 00000130 00 00 02 00 2C 00 01 00 00 00 00 00 24 00 00 00 ....,.......$... + * 00000140 00 A0 01 05 00 00 00 00 00 05 15 00 00 00 1A 24 ...............$ + * 00000150 44 38 90 00 0F 02 65 3A BE 4C FF 03 00 00 00 00 D8....e:.L...... + * 00000160 00 00 00 00 00 00 00 00 00 00 .......... + */ +OPERATION(SRVSVC_OPNUM_NetSetFileSecurity) +struct mslm_NetSetFileSecurity { + IN LPTSTR servername; + IN LPTSTR sharename; + IN REFERENCE LPTSTR filename; + IN DWORD securityinfo; + /* + * IN Security Descriptor (looks like): + * Length1 + * Pointer + * Length2 (== Length1) + * buffer itself + */ + + OUT DWORD status; +}; + +/* + * The SRVSVC already + */ +INTERFACE(0) +union srvsvc_interface { + CASE(SRVSVC_OPNUM_NetConnectEnum) + struct mslm_NetConnectEnum NetConnectEnum; + CASE(SRVSVC_OPNUM_NetFileEnum) + struct mslm_NetFileEnum NetFileEnum; + CASE(SRVSVC_OPNUM_NetFileClose) + struct mslm_NetFileClose NetFileClose; + CASE(SRVSVC_OPNUM_NetShareGetInfo) + struct mlsm_NetShareGetInfo NetShareGetInfo; + CASE(SRVSVC_OPNUM_NetShareSetInfo) + struct mlsm_NetShareGetInfo NetShareSetInfo; + CASE(SRVSVC_OPNUM_NetSessionDel) + struct mslm_NetSessionDel NetSessionDel; + CASE(SRVSVC_OPNUM_NetSessionEnum) + struct mslm_NetSessionEnum NetSessionEnum; + CASE(SRVSVC_OPNUM_NetServerGetInfo) + struct mslm_NetServerGetInfo NetServerGetInfo; + CASE(SRVSVC_OPNUM_NetRemoteTOD) + struct mslm_NetRemoteTOD NetRemoteTOD; + CASE(SRVSVC_OPNUM_NetNameValidate) + struct mslm_NetNameValidate NetNameValidate; + CASE(SRVSVC_OPNUM_NetShareAdd) + struct mslm_NetShareAdd NetShareAdd; + CASE(SRVSVC_OPNUM_NetShareDel) + struct mslm_NetShareDel NetShareDel; + CASE(SRVSVC_OPNUM_NetShareEnum) + struct mslm_NetShareEnum NetShareEnum; + CASE(SRVSVC_OPNUM_NetShareEnumSticky) + struct mslm_NetShareEnumSticky NetShareEnumSticky; + CASE(SRVSVC_OPNUM_NetGetFileSecurity) + struct mslm_NetGetFileSecurity NetGetFileSecurity; + CASE(SRVSVC_OPNUM_NetSetFileSecurity) + struct mslm_NetSetFileSecurity NetSetFileSecurity; +}; +typedef union srvsvc_interface srvsvc_interface_t; +EXTERNTYPEINFO(srvsvc_interface) + + + +/* + * WKSSVC -- Workstation Service + **************************************************************** + */ + +#define WKSSVC_OPNUM_NetWkstaGetInfo 0x00 + + +/* + * NET_API_STATUS NET_API_FUNCTION + * NetWkstaGetInfo ( + * IN LPTSTR servername OPTIONAL, + * IN DWORD level, + * OUT LPBYTE *bufptr + * ); + */ + +struct mslm_WKSTA_INFO_100 { + DWORD wki100_platform_id; + LPTSTR wki100_computername; + LPTSTR wki100_langroup; + DWORD wki100_ver_major; + DWORD wki100_ver_minor; +}; + +/* NetWkstaGetInfo only. System information - user access */ +struct mslm_WKSTA_INFO_101 { + DWORD wki101_platform_id; + LPTSTR wki101_computername; + LPTSTR wki101_langroup; + DWORD wki101_ver_major; + DWORD wki101_ver_minor; + LPTSTR wki101_lanroot; +}; + +/* NetWkstaGetInfo only. System information - admin or operator access */ +struct mslm_WKSTA_INFO_102 { + DWORD wki102_platform_id; + LPTSTR wki102_computername; + LPTSTR wki102_langroup; + DWORD wki102_ver_major; + DWORD wki102_ver_minor; + LPTSTR wki102_lanroot; + DWORD wki102_logged_on_users; +}; + +INFO1RES_DEFINITION(mslm_NetWkstaGetInfo, + INFO1RES_UNION_ENTRY(mslm_WKSTA_INFO,100) + INFO1RES_UNION_ENTRY(mslm_WKSTA_INFO,101) + INFO1RES_UNION_ENTRY(mslm_WKSTA_INFO,102)) + +INFO1RESBUF_DEFINITION(mslm_NetWkstaGetInfo, + INFO1RESBUF_UNION_ENTRY(mslm_WKSTA_INFO,100) + INFO1RESBUF_UNION_ENTRY(mslm_WKSTA_INFO,101) + INFO1RESBUF_UNION_ENTRY(mslm_WKSTA_INFO,102)) + + +OPERATION(WKSSVC_OPNUM_NetWkstaGetInfo) +struct mslm_NetWkstaGetInfo { + IN LPTSTR servername; + IN DWORD level; + OUT struct mslm_NetWkstaGetInfo_result result; + OUT DWORD status; +}; + +/* + * The WKSSVC already + */ +INTERFACE(0) +union wkssvc_interface { + CASE(WKSSVC_OPNUM_NetWkstaGetInfo) + struct mslm_NetWkstaGetInfo NetWkstaGetInfo; +}; +typedef union wkssvc_interface wkssvc_interface_t; +EXTERNTYPEINFO(wkssvc_interface) + + +#endif /* _MLSVC_LANMAN_NDL_ */ diff --git a/usr/src/uts/common/smbsrv/ndl/svcctl.ndl b/usr/src/uts/common/smbsrv/ndl/svcctl.ndl new file mode 100644 index 000000000000..a2fb7f670514 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ndl/svcctl.ndl @@ -0,0 +1,287 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MLSVC_SVCCTL_NDL_ +#define _MLSVC_SVCCTL_NDL_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * NT Service Control Services (SVCCTL) RPC interface definition. + * This interface provides remote access to add, remove, start and + * stop services. + */ + +#include "ndrtypes.ndl" + +#define SVCCTL_OPNUM_Close 0x00 +#define SVCCTL_OPNUM_QueryServiceStatus 0x06 +#define SVCCTL_OPNUM_EnumServicesStatus 0x0E +#define SVCCTL_OPNUM_OpenManager 0x0F +#define SVCCTL_OPNUM_OpenService 0x10 +#define SVCCTL_OPNUM_QueryServiceConfig 0x11 + +/* + * Standard opaque 20 byte RPC handle. + */ + + +struct svcctl_handle { + DWORD hand1; + DWORD hand2; + WORD hand3[2]; + BYTE hand4[8]; +}; + +typedef struct svcctl_handle svcctl_handle_t; + +/* + * The svc_status (SERVICE_STATUS) structure contains information about a + * service. The ControlService, EnumDependentServices, EnumServicesStatus, + * and QueryServiceStatus functions use this structure to return information + * about a service. A service uses this structure in the SetServiceStatus + * function to report its current status to the service control manager. + * + * service_type + * The type of service. This member can be one of the following values. + * + * SERVICE_FILE_SYSTEM_DRIVER + * SERVICE_KERNEL_DRIVER + * SERVICE_WIN32_OWN_PROCESS + * SERVICE_WIN32_SHARE_PROCESS + * + * If the service type is either SERVICE_WIN32_OWN_PROCESS or + * SERVICE_WIN32_SHARE_PROCESS, and the service is running in + * the context of the LocalSystem account, the following type + * may also be specified to indicate that the service can + * interact with the desktop. + * + * SERVICE_INTERACTIVE_PROCESS + * + * cur_state + * The current state of the service. This member can be one of the + * following values. + * + * SERVICE_CONTINUE_PENDING + * SERVICE_PAUSE_PENDING + * SERVICE_PAUSED + * SERVICE_RUNNING + * SERVICE_START_PENDING + * SERVICE_STOP_PENDING + * SERVICE_STOPPED + * + * ctrl_accepted + * The control codes that the service will accept and process in its + * handler function (see Handler and HandlerEx). A user interface + * process can control a service by specifying a control command in + * the ControlService function. By default, all services accept the + * SERVICE_CONTROL_INTERROGATE value. The following are the control + * codes. + * + * SERVICE_ACCEPT_STOP + * SERVICE_ACCEPT_PAUSE_CONTINUE + * SERVICE_ACCEPT_SHUTDOWN + * SERVICE_ACCEPT_PARAMCHANGE + * SERVICE_ACCEPT_NETBINDCHANGE + * + * w32_exitcode + * An error code that the service uses to report an error that occurs when + * it is starting or stopping. To return an error code specific to the + * service, the service must set this value to ERROR_SERVICE_SPECIFIC_ERROR + * to indicate that the dwServiceSpecificExitCode member contains the error + * code. The service should set this value to NO_ERROR when it is running + * and on normal termination. + * + * svc_specified_exitcode + * A service-specific error code that the service returns when an error + * occurs while the service is starting or stopping. This value is ignored + * unless the w32_exitcode member is set to ERROR_SERVICE_SPECIFIC_ERROR. + * + * check_point + * A value that the service increments periodically to report its progress + * during a lengthy start, stop, pause, or continue operation. For example, + * the service should increment this value as it completes each step of its + * initialization when it is starting up. The user interface program that + * invoked the operation on the service uses this value to track the progress + * of the service during a lengthy operation. This value is not valid and + * should be zero when the service does not have a start, stop, pause, or + * continue operation pending. + * + * wait_hint + * An estimate of the amount of time, in milliseconds, that the service + * expects a pending start, stop, pause, or continue operation to take + * before the service makes its next call to the SetServiceStatus + * function with either an incremented check_point value or a change in + * dwCurrentState. If the amount of time specified by wait_hint passes, + * and check_point has not been incremented, or cur_state has not changed, + * the service control manager or service control program can assume that + * an error has occurred and the service should be stopped. + */ +struct svc_status { + DWORD service_type; + DWORD cur_state; + DWORD ctrl_accepted; + DWORD w32_exitcode; + DWORD svc_specified_exitcode; + DWORD check_point; + DWORD wait_hint; +}; +typedef struct svc_status svc_status_t; + +struct svc_enum_status { + DWORD svc_name; /* offset within response buffer */ + DWORD display_name; /* offset within response buffer */ + svc_status_t svc_status; +}; +typedef struct svc_enum_status svc_enum_status_t; + +struct svc_config { + DWORD service_type; + DWORD start_type; + DWORD error_control; + LPTSTR binary_pathname; + LPTSTR loadorder_group; + DWORD tag_id; + LPTSTR dependencies; + LPTSTR service_startname; + LPTSTR display_name; +}; +typedef struct svc_config svc_config_t; + + +/* + *********************************************************************** + * Close + *********************************************************************** + */ +OPERATION(SVCCTL_OPNUM_Close) +struct svcctl_Close { + IN svcctl_handle_t handle; + OUT svcctl_handle_t result_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * OpenManager + *********************************************************************** + */ +OPERATION(SVCCTL_OPNUM_OpenManager) +struct svcctl_OpenManager { + IN LPTSTR machine_name; + IN LPTSTR database_name; + IN DWORD desired_access; + OUT svcctl_handle_t handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * OpenService + *********************************************************************** + */ +OPERATION(SVCCTL_OPNUM_OpenService) +struct svcctl_OpenService { + IN svcctl_handle_t manager_handle; + IN REFERENCE LPTSTR service_name; + IN DWORD desired_access; + OUT svcctl_handle_t service_handle; + OUT DWORD status; +}; + + +/* + *********************************************************************** + * QueryServiceStatus + *********************************************************************** + */ +OPERATION(SVCCTL_OPNUM_QueryServiceStatus) +struct svcctl_QueryServiceStatus { + IN svcctl_handle_t service_handle; + OUT svc_status_t service_status; + OUT DWORD status; +}; + +/* + *********************************************************************** + * EnumServicesStatus + *********************************************************************** + */ +OPERATION(SVCCTL_OPNUM_EnumServicesStatus) +struct svcctl_EnumServicesStatus { + IN svcctl_handle_t manager_handle; + IN DWORD svc_type; + IN DWORD svc_state; + INOUT DWORD buf_size; + IN DWORD unknown; + OUT BYTE services[1024]; + OUT DWORD bytes_needed; + OUT DWORD svc_num; + OUT DWORD resume_handle; + OUT DWORD status; +}; + +/* + *********************************************************************** + * QueryServiceConfig + *********************************************************************** + */ +OPERATION(SVCCTL_OPNUM_QueryServiceConfig) +struct svcctl_QueryServiceConfig { + IN svcctl_handle_t service_handle; + IN DWORD buf_size; + OUT svc_config_t service_cfg; + OUT DWORD cfg_bytes; + OUT DWORD status; +}; + +/* + *********************************************************************** + * The SVCCTL interface definition. + *********************************************************************** + */ +INTERFACE(0) +union svcctl_interface { + CASE(SVCCTL_OPNUM_Close) + struct svcctl_Close SvcClose; + CASE(SVCCTL_OPNUM_OpenManager) + struct svcctl_OpenManager SvcOpenManager; + CASE(SVCCTL_OPNUM_OpenService) + struct svcctl_OpenService SvcOpenService; + CASE(SVCCTL_OPNUM_QueryServiceStatus) + struct svcctl_QueryServiceStatus SvcQueryServiceStatus; + CASE(SVCCTL_OPNUM_EnumServicesStatus) + struct svcctl_EnumServicesStatus SvcEnumServicesStatus; + CASE(SVCCTL_OPNUM_QueryServiceConfig) + struct svcctl_QueryServiceConfig SvcQueryServiceConfig; +}; + +typedef union svcctl_interface svcctl_interface_t; +EXTERNTYPEINFO(svcctl_interface) + + +#endif /* _MLSVC_SVCCTL_NDL_ */ diff --git a/usr/src/uts/common/smbsrv/ndl/winreg.ndl b/usr/src/uts/common/smbsrv/ndl/winreg.ndl new file mode 100644 index 000000000000..2d3d1de28dab --- /dev/null +++ b/usr/src/uts/common/smbsrv/ndl/winreg.ndl @@ -0,0 +1,288 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MLSVC_WINREG_NDL_ +#define _MLSVC_WINREG_NDL_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Windows Registry (WINREG) RPC interface definition. + */ + +#include "ndrtypes.ndl" + +#define WINREG_OPNUM_OpenHKLM 0x02 +#define WINREG_OPNUM_OpenHKUsers 0x04 +#define WINREG_OPNUM_Close 0x05 +#define WINREG_OPNUM_CreateKey 0x06 +#define WINREG_OPNUM_DeleteKey 0x07 +#define WINREG_OPNUM_DeleteValue 0x08 +#define WINREG_OPNUM_EnumKey 0x09 +#define WINREG_OPNUM_EnumValue 0x0a +#define WINREG_OPNUM_FlushKey 0x0b +#define WINREG_OPNUM_GetKeySec 0x0c +#define WINREG_OPNUM_OpenKey 0x0f +#define WINREG_OPNUM_QueryKey 0x10 +#define WINREG_OPNUM_QueryValue 0x11 +#define WINREG_OPNUM_SetKeySec 0x15 +#define WINREG_OPNUM_CreateValue 0x16 +#define WINREG_OPNUM_Shutdown 0x18 +#define WINREG_OPNUM_GetVersion 0x1a + + +struct msreg_handle { + DWORD hand1; + DWORD hand2; + WORD hand3[2]; + BYTE hand4[8]; +}; +typedef struct msreg_handle msreg_handle_t; + +struct msreg_string_desc { + WORD length; + WORD allosize; + LPTSTR str; +}; +typedef struct msreg_string_desc msreg_string_t; + +/* + * Fake Varying/Conformant with a funny conformant. + */ +struct msreg_value { + DWORD vc_first_is; /* 0 */ + DWORD vc_length_is; + SIZE_IS(vc_length_is) + BYTE value[ANY_SIZE_ARRAY]; +}; + +struct file_time { + DWORD low; + DWORD high; +}; +typedef struct file_time file_time_t; + + +OPERATION(WINREG_OPNUM_OpenHKLM) +struct msreg_OpenHKLM { + IN BYTE whatever[8]; + IN DWORD access_mask; + OUT msreg_handle_t handle; + OUT DWORD status; +}; + + +OPERATION(WINREG_OPNUM_OpenHKUsers) +struct msreg_OpenHKUsers { + IN BYTE whatever[8]; + IN DWORD access_mask; + OUT msreg_handle_t handle; + OUT DWORD status; +}; + + +OPERATION(WINREG_OPNUM_Close) +struct msreg_Close { + IN msreg_handle_t handle; + OUT msreg_handle_t result_handle; + OUT DWORD status; +}; + + +OPERATION(WINREG_OPNUM_CreateKey) +struct msreg_CreateKey { + IN msreg_handle_t handle; + IN msreg_string_t subkey; + /* IN ignore the remaining input data */ + + OUT DWORD status; +}; + + +OPERATION(WINREG_OPNUM_DeleteKey) +struct msreg_DeleteKey { + IN msreg_handle_t handle; + IN msreg_string_t subkey; + /* IN ignore the remaining input data */ + + OUT DWORD status; +}; + + +OPERATION(WINREG_OPNUM_DeleteValue) +struct msreg_DeleteValue { + IN msreg_handle_t handle; + IN msreg_string_t name; + /* IN ignore the remaining input data */ + + OUT DWORD status; +}; + + +/* + * Some of the OUT parameters are also supplied + * as IN parameters but we can ignore them. + */ +OPERATION(WINREG_OPNUM_EnumValue) +struct msreg_EnumValue { + IN msreg_handle_t handle; + IN DWORD index; + /* IN ignore the remaining input data */ + + OUT msreg_string_t name; + OUT DWORD *type; + OUT struct msreg_value *value; + OUT DWORD *value_size; + OUT DWORD *value_size_total; + OUT DWORD status; +}; + + +OPERATION(WINREG_OPNUM_OpenKey) +struct msreg_OpenKey { + IN msreg_handle_t handle; + IN msreg_string_t name; + IN DWORD unknown; + IN DWORD access_mask; + OUT msreg_handle_t result_handle; + OUT DWORD status; +}; + + +/* + * 000000A0 00 00 00 00 C1 F9 C0 86 18 B1 .......... + * 000000B0 D5 11 99 C8 00 C0 F0 1F 42 26 00 00 10 04 CC ED ........B&...... + * 000000C0 12 00 08 02 00 00 00 00 00 00 00 00 00 00 .............. + DWORD unknown_0x04100000; + DWORD unkown_ptr; + DWORD unknown_0x00000208; + DWORD unknown2; + DWORD unknown3; + */ + +OPERATION(WINREG_OPNUM_QueryKey) +struct msreg_QueryKey { + IN msreg_handle_t handle; + /* + * Ignore the remaining input data + * (2 * DWORD, possibly msreg_string_t). + */ + + OUT msreg_string_t name; + OUT DWORD unknown; + OUT DWORD sub_keys; + OUT DWORD max_subkey_len; + OUT DWORD max_class_len; + OUT DWORD values; + OUT DWORD max_value_namelen; + OUT DWORD max_value_len; + OUT DWORD security_desc; + OUT file_time_t last_write_time; + OUT DWORD status; +}; + + +/* + * Some of the OUT parameters are also supplied + * as IN parameters but we can ignore them. + */ +OPERATION(WINREG_OPNUM_QueryValue) +struct msreg_QueryValue { + IN msreg_handle_t handle; + IN msreg_string_t value_name; + /* IN ignore the remaining input data */ + + OUT DWORD *type; + OUT struct msreg_value *value; + OUT DWORD *value_size; + OUT DWORD *value_size_total; + OUT DWORD status; +}; + + +OPERATION(WINREG_OPNUM_CreateValue) +struct msreg_CreateValue { + IN msreg_handle_t handle; + IN msreg_string_t name; + /* IN ignore the remaining input data */ + + OUT DWORD status; +}; + + +/* + * The real structure of shutdown passes some strings, a timeout + * and reboot/shutdown flags but this allows us to accept the call, + * without anything appearing in the log, and return access denied. + */ +OPERATION(WINREG_OPNUM_Shutdown) +struct msreg_Shutdown { + IN DWORD ignored; + OUT DWORD status; +}; + + +OPERATION(WINREG_OPNUM_GetVersion) +struct msreg_GetVersion { + IN msreg_handle_t handle; + OUT DWORD version; + OUT DWORD status; +}; + + +/* + * The WINREG interface. + */ +INTERFACE(0) +union winreg_interface { + CASE(WINREG_OPNUM_OpenHKLM) + struct msreg_OpenHKLM OpenHKLM; + CASE(WINREG_OPNUM_OpenHKUsers) + struct msreg_OpenHKUsers OpenHKUsers; + CASE(WINREG_OPNUM_Close) + struct msreg_Close Close; + CASE(WINREG_OPNUM_CreateKey) + struct msreg_CreateKey CreateKey; + CASE(WINREG_OPNUM_DeleteKey) + struct msreg_DeleteKey DeleteKey; + CASE(WINREG_OPNUM_DeleteValue) + struct msreg_DeleteValue DeleteValue; + CASE(WINREG_OPNUM_OpenKey) + struct msreg_OpenKey OpenKey; + CASE(WINREG_OPNUM_QueryKey) + struct msreg_QueryKey QueryKey; + CASE(WINREG_OPNUM_QueryValue) + struct msreg_QueryValue QueryValue; + CASE(WINREG_OPNUM_CreateValue) + struct msreg_CreateValue CreateValue; + CASE(WINREG_OPNUM_Shutdown) + struct msreg_Shutdown Shutdown; + CASE(WINREG_OPNUM_GetVersion) + struct msreg_GetVersion GetVersion; +}; +typedef union winreg_interface winreg_interface_t; +EXTERNTYPEINFO(winreg_interface) + +#endif /* _MLSVC_WINREG_NDL_ */ diff --git a/usr/src/uts/common/smbsrv/ndr.h b/usr/src/uts/common/smbsrv/ndr.h new file mode 100644 index 000000000000..36c502d1b9e9 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ndr.h @@ -0,0 +1,468 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_NDR_H +#define _SMBSRV_NDR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Network Data Representation (NDR) is a compatible subset of DCE RPC + * and MSRPC NDR. NDR is used to move parameters consisting of + * complicated trees of data constructs between an RPC client and server. + * + * CAE Specification (1997) + * DCE 1.1: Remote Procedure Call + * Document Number: C706 + * The Open Group + * ogspecs@opengroup.org + */ + +#ifndef _KERNEL +#include +#include +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Normal sequence: + * - Application calls client-side stub w/ TOP-MOST arg structure + * - client stub performs NDR_M_OP_MARSHALL+NDR_DIR_IN + * - PDU conveyed (request, aka call, aka query) + * - server stub performs NDR_M_OP_UNMARSHALL+NDR_DIR_IN + * - server function called w/ TOP-MOST arg structure + * - server function returns w/ TOP-MOST arg structure modified + * - server stub performs NDR_M_OP_MARSHALL+NDR_DIR_OUT + * - PDU conveyed (reply, aka result, aka response) + * - client stub performs NDR_M_OP_UNMARSHALL+NDR_DIR_OUT + * - return to Application w/ TOP-MOST arg structure modified + * + * An interface is a sequence of top-most constructs. Each top-most + * construct corresponds to one parameter, either argument or return + * value. + * + * A top-most construct is a sequence of outer constructs. The first + * outer construct is the referent of the argument, and the subsequent + * outer constructs are descendents referenced by pointers from prior + * constructs. + * + * An outer construct is a sequence of variable-sized info, fixed-sized + * data, and variable-sized data. + */ + +/* + * Terminology + * + * The ALL UPPER CASE terms recur in the DCE/RPC documentation. + * The mixed-case names have been introduced as a reading aid. + * + * Size The size of an array in elements. Think of this + * as the amount to malloc(). + * + * Length The number of elements of an array which are significant + * Think of this as the amount to bcopy(). + * + * Known Size/length is known at build time. + * + * Determined Size/length is determined at run time. + * + * FIXED The Size and Length are Known. + * Think of this as a string constant or a DOS 8.3 file name. + * char array[] = "A Constant Size/Length"; + * + * CONFORMANT The Size is Determined. Length is the same as Size. + * Think of this as strdup(). + * char *array = strdup("Something"); + * + * VARYING The Size is Known. The Length is determined. + * Think of this as a strcpy() of a variable length string + * into a fixed length buffer: + * char array[100]; + * strcpy(array, "very short string"); + * + * VARYING/CONFORMANT + * The Size is Determined. The Length is separately Determined. + * Think of this like: + * char *array = malloc(size); + * strcpy(array, "short string"); + * + * STRING Strings can be CONFORMANT, VARYING, or CONFORMANT/VARYING. + * A string is fundamentally an array with the last + * significant element some sort of NULL. + */ + +#define NDR_F_NONE 0x0000 /* no flags */ +#define NDR_F_PARAMS_MASK 0x00FF +#define NDR_F_SIZE_IS 0x0001 /* [size_is(X)] required/given */ +#define NDR_F_LENGTH_IS 0x0002 /* not implemented */ +#define NDR_F_SWITCH_IS 0x0004 /* [switch_is(X)] req./given */ +#define NDR_F_IS_STRING 0x0008 /* [string] req./given */ +#define NDR_F_IS_POINTER 0x0010 /* TYPE * ... req./given */ +#define NDR_F_IS_REFERENCE 0x0020 /* TYPE & ... req./given */ +#define NDR_F_DIMENSION_IS 0x0040 /* TYPE [N] req./given */ + +#define NDR_F_WHENCE_MASK 0x00F0 +#define NDR_F_BACKPTR 0x0010 /* ref cause by pointer */ +#define NDR_F_OUTER 0x0020 /* ref caused by outer */ +#define NDR_F_TOPMOST 0x0040 /* ref caused by topmost */ + +#define NDR_F_TYPEOP_MASK 0x0F00 +#define NDR_F_ARRAY 0x0100 /* type is array of somethings */ +#define NDR_F_POINTER 0x0200 /* type is pointer to something(s) */ +#define NDR_F_STRING 0x0300 /* type is string of somethings */ +#define NDR_F_UNION 0x0400 /* type is a union */ +#define NDR_F_STRUCT 0x0500 /* type is a structure */ +#define NDR_F_OPERATION 0x0600 /* type is a structure, special */ +#define NDR_F_INTERFACE 0x0700 /* type is a union, special */ +#define NDR_F_CONFORMANT 0x1000 /* struct conforming (var-size tail) */ +#define NDR_F_VARYING 0x2000 /* not implemented */ + +struct mlrpc_heap; +struct mlndr_stream; +struct ndr_reference; +struct ndr_typeinfo; + +struct ndr_typeinfo { + unsigned char version; /* sanity check */ + unsigned char alignment; /* mask */ + unsigned short type_flags; /* NDR_F_... */ + int (*ndr_func)(struct ndr_reference *encl_ref); + unsigned short pdu_size_fixed_part; + unsigned short pdu_size_variable_part; + unsigned short c_size_fixed_part; + unsigned short c_size_variable_part; +}; + +struct ndr_reference { + struct ndr_reference *next; /* queue list (outer only) */ + struct ndr_reference *enclosing; /* e.g. struct for this memb */ + struct mlndr_stream *stream; /* root of NDR */ + struct ndr_typeinfo *ti; /* type of data referenced */ + char *name; /* name of this member */ + unsigned long pdu_offset; /* referent in stub data */ + char *datum; /* referent in local memory */ + char **backptr; /* referer to set */ + unsigned short outer_flags; /* XXX_is() from top level */ + unsigned short inner_flags; /* XXX_is() in encapsulated */ + unsigned short type_flags; /* "requires" */ + unsigned short packed_alignment; + unsigned long size_is; /* conforming constructs */ + unsigned long strlen_is; /* strings */ + unsigned long switch_is; /* union arg selector */ + unsigned long dimension_is; /* fixed-len array size */ + unsigned long pdu_end_offset; /* offset for limit of PDU */ +}; + +/* + * For all operations, the mlndr_stream, which is the root of NDR processing, + * is the primary object. When available, the appropriate ndr_reference + * is passed, NULL otherwise. Functions that return 'int' should return + * TRUE (!0) or FALSE (0). When functions return FALSE, including + * mlndo_malloc() returning NULL, they should set the stream->error to an + * appropriate indicator of what went wrong. + * + * Functions mlndo_get_pdu(), mlndo_put_pdu(), and mlndo_pad_pdu() must + * never grow the PDU data. A request for out-of-bounds data is an error. + * The swap_bytes flag is 1 if NDR knows that the byte-order in the PDU + * is different from the local system. mlndo_pad_pdu() advised that the + * affected bytes should be zero filled. + */ +struct mlndr_stream_ops { + char *(*mlndo_malloc)(struct mlndr_stream *, unsigned, + struct ndr_reference *); + + int (*mlndo_free)(struct mlndr_stream *, char *, + struct ndr_reference *); + + int (*mlndo_grow_pdu)(struct mlndr_stream *, unsigned long, + struct ndr_reference *); + + int (*mlndo_pad_pdu)(struct mlndr_stream *, unsigned long, + unsigned long, struct ndr_reference *); + + int (*mlndo_get_pdu)(struct mlndr_stream *, unsigned long, + unsigned long, char *, int, struct ndr_reference *); + + int (*mlndo_put_pdu)(struct mlndr_stream *, unsigned long, + unsigned long, char *, int, struct ndr_reference *); + + void (*mlndo_tattle)(struct mlndr_stream *, char *, + struct ndr_reference *); + + void (*mlndo_tattle_error)(struct mlndr_stream *, + struct ndr_reference *); + + int (*mlndo_reset)(struct mlndr_stream *); + void (*mlndo_destruct)(struct mlndr_stream *); +}; + +#define MLNDS_MALLOC(MLNDS, LEN, REF) \ + (*(MLNDS)->mlndo->mlndo_malloc)(MLNDS, LEN, REF) + +#define MLNDS_GROW_PDU(MLNDS, WANT_END_OFF, REF) \ + (*(MLNDS)->mlndo->mlndo_grow_pdu)(MLNDS, WANT_END_OFF, REF) +#define MLNDS_PAD_PDU(MLNDS, PDU_OFFSET, N_BYTES, REF) \ + (*(MLNDS)->mlndo->mlndo_pad_pdu)(MLNDS, PDU_OFFSET, N_BYTES, REF) +#define MLNDS_GET_PDU(MLNDS, PDU_OFFSET, N_BYTES, BUF, SWAP, REF) \ + (*(MLNDS)->mlndo->mlndo_get_pdu)(MLNDS, PDU_OFFSET, N_BYTES, BUF, \ + SWAP, REF) +#define MLNDS_PUT_PDU(MLNDS, PDU_OFFSET, N_BYTES, BUF, SWAP, REF) \ + (*(MLNDS)->mlndo->mlndo_put_pdu)(MLNDS, PDU_OFFSET, N_BYTES, BUF, \ + SWAP, REF) + +#define MLNDS_TATTLE(MLNDS, WHAT, REF) \ + (*(MLNDS)->mlndo->mlndo_tattle)(MLNDS, WHAT, REF) +#define MLNDS_TATTLE_ERROR(MLNDS, WHAT, REF) \ + (*(MLNDS)->mlndo->mlndo_tattle_error)(MLNDS, REF) +#define MLNDS_RESET(MLNDS) \ + (*(MLNDS)->mlndo->mlndo_reset)(MLNDS) +#define MLNDS_DESTRUCT(MLNDS) \ + (*(MLNDS)->mlndo->mlndo_destruct)(MLNDS) + +struct mlndr_stream { + unsigned long pdu_size; + unsigned long pdu_size_with_rpc_hdrs; + unsigned long pdu_max_size; + unsigned long pdu_base_offset; + unsigned long pdu_scan_offset; + unsigned char *pdu_base_addr; + unsigned char *pdu_base_addr_with_rpc_hdrs; + + struct mlndr_stream_ops *mlndo; + + unsigned char m_op; + unsigned char dir; + unsigned char swap; /* native/net endian swap */ + short error; + short error_ref; + + struct ndr_reference *outer_queue_head; + struct ndr_reference **outer_queue_tailp; + struct ndr_reference *outer_current; + struct mlrpc_heap *heap; +}; + + +#define NDR_M_OP_NONE 0x00 +#define NDR_M_OP_MARSHALL 0x01 /* data moving from datum to PDU */ +#define NDR_M_OP_UNMARSHALL 0x02 /* data moving from PDU to datum */ + +#define NDR_DIR_NONE 0x00 +#define NDR_DIR_IN 0x10 /* data moving from caller to callee */ +#define NDR_DIR_OUT 0x20 /* data moving from callee to caller */ + +#define NDR_MODE_CALL_SEND (NDR_M_OP_MARSHALL + NDR_DIR_IN) +#define NDR_MODE_CALL_RECV (NDR_M_OP_UNMARSHALL + NDR_DIR_IN) +#define NDR_MODE_RETURN_SEND (NDR_M_OP_MARSHALL + NDR_DIR_OUT) +#define NDR_MODE_RETURN_RECV (NDR_M_OP_UNMARSHALL + NDR_DIR_OUT) + +#define NDR_MODE_TO_M_OP(MODE) ((MODE)&0x0F) +#define NDR_MODE_TO_DIR(MODE) ((MODE)&0xF0) +#define NDR_M_OP_AND_DIR_TO_MODE(M_OP, DIR) ((M_OP)|(DIR)) + +#define NDR_MODE_MATCH(MLNDS, MODE) \ + (NDR_M_OP_AND_DIR_TO_MODE((MLNDS)->m_op, (MLNDS)->dir) == (MODE)) + + +#define NDR_ERR_MALLOC_FAILED -1 +#define NDR_ERR_M_OP_INVALID -2 +#define NDR_ERR_UNDERFLOW -3 +#define NDR_ERR_GROW_FAILED -4 /* overflow */ +#define NDR_ERR_PAD_FAILED -5 /* couldn't possibly happen */ +#define NDR_ERR_OUTER_HEADER_BAD -6 +#define NDR_ERR_SWITCH_VALUE_ILLEGAL -7 +#define NDR_ERR_SWITCH_VALUE_INVALID -8 +#define NDR_ERR_SWITCH_VALUE_MISSING -9 +#define NDR_ERR_SIZE_IS_MISMATCH_PDU -10 +#define NDR_ERR_SIZE_IS_MISMATCH_AFTER -11 +#define NDR_ERR_SIZE_IS_UNEXPECTED -12 +#define NDR_ERR_SIZE_IS_DUPLICATED -13 +#define NDR_ERR_OUTER_PARAMS_MISMATCH -14 +#define NDR_ERR_ARRAY_VARLEN_ILLEGAL -15 +#define NDR_ERR_ARRAY_UNION_ILLEGAL -16 +#define NDR_ERR_OUTER_PARAMS_BAD -17 +#define NDR_ERR_OUTER_UNION_ILLEGAL -18 +#define NDR_ERR_TOPMOST_UNION_ILLEGAL -19 +#define NDR_ERR_TOPMOST_VARLEN_ILLEGAL -20 +#define NDR_ERR_INNER_PARAMS_BAD -21 +#define NDR_ERR_UNIMPLEMENTED -22 +#define NDR_ERR_NOT_AN_INTERFACE -23 +#define NDR_ERR_STRLEN -24 +#define NDR_ERR_STRING_SIZING -25 +#define NDR_ERR_BOUNDS_CHECK -26 + +#define NDR_SET_ERROR(REF, ERROR) \ + ((REF)->stream->error = (ERROR), \ + (REF)->stream->error_ref = __LINE__, \ + MLNDS_TATTLE_ERROR((REF)->stream, 0, REF)) + +#define NDR_TATTLE(REF, WHAT) \ + (*(REF)->stream->mlndo->mlndo_tattle)((REF)->stream, WHAT, REF) + +#define MEMBER_STR(MEMBER) #MEMBER + +#define NDR_DIR_IS_IN (encl_ref->stream->dir == NDR_DIR_IN) +#define NDR_DIR_IS_OUT (encl_ref->stream->dir == NDR_DIR_OUT) + +#define NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ + ARGFLAGS, ARGMEM, ARGVAL) { \ + myref.pdu_offset = encl_ref->pdu_offset + (OFFSET); \ + myref.name = MEMBER_STR(MEMBER); \ + myref.datum = (char *)&val->MEMBER; \ + myref.inner_flags = ARGFLAGS; \ + myref.ti = &ndt_##TYPE; \ + myref.ARGMEM = ARGVAL; \ + if (!mlndr_inner(&myref)) \ + return (0); \ + } + +#define NDR_MEMBER(TYPE, MEMBER, OFFSET) \ + NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ + NDR_F_NONE, size_is, 0) + +#define NDR_MEMBER_ARR_WITH_SIZE_IS(TYPE, MEMBER, OFFSET, SIZE_IS) \ + NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ + NDR_F_SIZE_IS, size_is, SIZE_IS) + +#define NDR_MEMBER_ARR_WITH_DIMENSION(TYPE, MEMBER, OFFSET, SIZE_IS) \ + NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ + NDR_F_DIMENSION_IS, dimension_is, SIZE_IS) + +#define NDR_MEMBER_PTR_WITH_SIZE_IS(TYPE, MEMBER, OFFSET, SIZE_IS) \ + NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ + NDR_F_SIZE_IS+NDR_F_IS_POINTER, size_is, SIZE_IS) + +#define NDR_MEMBER_PTR(TYPE, MEMBER, OFFSET) \ + NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ + NDR_F_IS_POINTER, size_is, 0) + +#define NDR_MEMBER_WITH_SWITCH_IS(TYPE, MEMBER, OFFSET, SWITCH_IS) \ + NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ + NDR_F_SWITCH_IS, switch_is, SWITCH_IS) + + +#define NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ + ARGFLAGS, ARGMEM, ARGVAL) { \ + myref.pdu_offset = -1; \ + myref.name = MEMBER_STR(MEMBER); \ + myref.datum = (char *)&val->MEMBER; \ + myref.inner_flags = ARGFLAGS; \ + myref.ti = &ndt_##TYPE; \ + myref.ARGMEM = ARGVAL; \ + if (!mlndr_topmost(&myref)) \ + return (0); \ + } + +#define NDR_TOPMOST_MEMBER(TYPE, MEMBER) \ + NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ + NDR_F_NONE, size_is, 0) + +#define NDR_TOPMOST_MEMBER_ARR_WITH_SIZE_IS(TYPE, MEMBER, SIZE_IS) \ + NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ + NDR_F_SIZE_IS, size_is, SIZE_IS) + +#define NDR_TOPMOST_MEMBER_ARR_WITH_DIMENSION(TYPE, MEMBER, SIZE_IS) \ + NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ + NDR_F_DIMENSION_IS, dimension_is, SIZE_IS) + +#define NDR_TOPMOST_MEMBER_PTR_WITH_SIZE_IS(TYPE, MEMBER, SIZE_IS) \ + NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ + NDR_F_SIZE_IS+NDR_F_IS_POINTER, size_is, SIZE_IS) + +#define NDR_TOPMOST_MEMBER_PTR(TYPE, MEMBER) \ + NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ + NDR_F_IS_POINTER, size_is, 0) + +#define NDR_TOPMOST_MEMBER_REF(TYPE, MEMBER) \ + NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ + NDR_F_IS_REFERENCE, size_is, 0) + +#define NDR_TOPMOST_MEMBER_REF_WITH_SIZE_IS(TYPE, MEMBER, SIZE_IS) \ + NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ + NDR_F_SIZE_IS+NDR_F_IS_REFERENCE, size_is, SIZE_IS) + +#define NDR_TOPMOST_MEMBER_WITH_SWITCH_IS(TYPE, MEMBER, SWITCH_IS) \ + NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ + NDR_F_SWITCH_IS, switch_is, SWITCH_IS) + +/* this is assuming offset+0 */ +#define NDR_PARAMS_MEMBER_WITH_ARG(TYPE, MEMBER, ARGFLAGS, \ + ARGMEM, ARGVAL) { \ + myref.pdu_offset = encl_ref->pdu_offset; \ + myref.name = MEMBER_STR(MEMBER); \ + myref.datum = (char *)&val->MEMBER; \ + myref.inner_flags = ARGFLAGS; \ + myref.ti = &ndt_##TYPE; \ + myref.ARGMEM = ARGVAL; \ + if (!mlndr_params(&myref)) \ + return (0); \ + } + +#define NDR_PARAMS_MEMBER(TYPE, MEMBER) \ + NDR_PARAMS_MEMBER_WITH_ARG(TYPE, MEMBER, \ + NDR_F_NONE, size_is, 0) + +#define NDR_STRING_DIM 1 +#define NDR_ANYSIZE_DIM 1 + +int mlndo_process(struct mlndr_stream *, struct ndr_typeinfo *, char *); +int mlndo_operation(struct mlndr_stream *, struct ndr_typeinfo *, + int opnum, char *); +void mlndo_printf(struct mlndr_stream *, struct ndr_reference *, + const char *, ...); +void mlndo_trace(const char *); +void mlndo_fmt(struct mlndr_stream *, struct ndr_reference *, char *); + +int mlndr_params(struct ndr_reference *); +int mlndr_topmost(struct ndr_reference *); +int mlndr_run_outer_queue(struct mlndr_stream *); +int mlndr_outer(struct ndr_reference *); +int mlndr_outer_fixed(struct ndr_reference *); +int mlndr_outer_fixed_array(struct ndr_reference *); +int mlndr_outer_conformant_array(struct ndr_reference *); +int mlndr_outer_conformant_construct(struct ndr_reference *); +int mlndr_size_is(struct ndr_reference *); +int mlndr_outer_string(struct ndr_reference *); +int mlndr_outer_peek_sizing(struct ndr_reference *, unsigned, + unsigned long *); +int mlndr_outer_poke_sizing(struct ndr_reference *, unsigned, + unsigned long *); +int mlndr_outer_align(struct ndr_reference *); +int mlndr_outer_grow(struct ndr_reference *, unsigned); +int mlndr_inner(struct ndr_reference *); +int mlndr_inner_pointer(struct ndr_reference *); +int mlndr_inner_reference(struct ndr_reference *); +int mlndr_inner_array(struct ndr_reference *); +void mlnds_bswap(void *src, void *dst, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_NDR_H */ diff --git a/usr/src/uts/common/smbsrv/netbios.h b/usr/src/uts/common/smbsrv/netbios.h new file mode 100644 index 000000000000..3c3d616f4b96 --- /dev/null +++ b/usr/src/uts/common/smbsrv/netbios.h @@ -0,0 +1,147 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_NETBIOS_H +#define _SMBSRV_NETBIOS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * NetBIOS over TCP/IP interface definitions. NetBIOS over TCP/IP is + * documented in the following RFC documents: + * + * RFC 1001: Protocol Standard for a NetBIOS Service on a TCP/UDP + * Transport: Concepts and Methods + * + * RFC 1002: Protocol Standard for a NetBIOS Service on a TCP/UDP + * Transport: Detailed Specifications + * + * These documents reference RCF883. + * RFC 883: Domain Names - Implementation and Specification + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * NetBIOS names in NetBIOS packets are valid domain names as defined in + * RFC 883. Each label is limited to 63 bytes with an overall length of + * 255 bytes as described in RFC 1002 section 4.1. This is known as + * second-level encoding. In first-level encoding the label lengths are + * represented as dots (.). + * + * RFC 1001 section 14.1 describes first-level encoding of the NetBIOS + * name (hostname) and scope. The ASCII name is padded to 15 bytes using + * spaces and a one byte type or suffix is written to the 16th byte. + * This is then encoded as a 32 byte string. + * + * NetBIOS Name: NetBIOS + * NetBIOS Scope: PROCOM.COM + * First Level: EOGFHEECEJEPFDCACACACACACACACACA.PROCOM.COM + * Second Level: <32>EOGFHEECEJEPFDCACACACACACACACACA<6>PROCOM<3>COM<0> + */ +#define NETBIOS_NAME_SZ 16 +#define NETBIOS_ENCODED_NAME_SZ 32 +#define NETBIOS_LABEL_MAX 63 +#define NETBIOS_DOMAIN_NAME_MAX 255 +#define NETBIOS_DOMAIN_NAME_BUFLEN (NETBIOS_DOMAIN_NAME_MAX + 1) +#define NETBIOS_SESSION_REQUEST_DATA_LENGTH \ + ((NETBIOS_ENCODED_NAME_SZ + 2) * 2) + +#define NETBIOS_HDR_SZ 4 /* bytes */ +/* + * Session Packet Types (RFC 1002 4.3.1). + */ +#define SESSION_MESSAGE 0x00 +#define SESSION_REQUEST 0x81 +#define POSITIVE_SESSION_RESPONSE 0x82 +#define NEGATIVE_SESSION_RESPONSE 0x83 +#define RETARGET_SESSION_RESPONSE 0x84 +#define SESSION_KEEP_ALIVE 0x85 + +/* + * NEGATIVE SESSION RESPONSE packet error code values (RFC 1002 4.3.4). + */ +#define SESSION_NOT_LISTENING_ON_CALLED_NAME 0x80 +#define SESSION_NOT_LISTENING_FOR_CALLING_NAME 0x81 +#define SESSION_CALLED_NAME_NOT_PRESENT 0x82 +#define SESSION_INSUFFICIENT_RESOURCES 0x83 +#define SESSION_UNSPECIFIED_ERROR 0x8F + +/* + * Time conversions + */ +#define MILLISECONDS 1 +#define SECONDS (1000 * MILLISECONDS) +#define MINUTES (60 * SECONDS) +#define HOURS (60 * MINUTES) +#define TO_SECONDS(x) ((x) / 1000) +#define TO_MILLISECONDS(x) ((x) * 1000) + +/* + * DATAGRAM service definitions + */ +#define DATAGRAM_DESTINATION_NAME_NOT_PRESENT 0x82 +#define DATAGRAM_INVALID_SOURCE_NAME_FORMAT 0x83 +#define DATAGRAM_INVALID_DESTINATION_NAME_FORMAT 0x84 + +#define NAME_SERVICE_TCP_PORT 137 +#define NAME_SERVICE_UDP_PORT 137 +#define DGM_SRVC_UDP_PORT 138 +#define SSN_SRVC_TCP_PORT 139 +#define MAX_DATAGRAM_LENGTH 576 +#define DATAGRAM_HEADER_LENGTH 14 +#define MAX_NAME_LENGTH 256 +#define BCAST_REQ_RETRY_COUNT 2 +#define UCAST_REQ_RETRY_COUNT 2 +#define BCAST_REQ_RETRY_TIMEOUT (500 * MILLISECONDS) +#define UCAST_REQ_RETRY_TIMEOUT (500 * MILLISECONDS) +#define CONFLICT_TIMER (1 * SECONDS) +#define INFINITE_TTL 0 +#define DEFAULT_TTL (600 * SECONDS) +#define SSN_RETRY_COUNT 4 +#define SSN_CLOSE_TIMEOUT (30 * SECONDS) +/* + * K.L. The keep alive time out use to be default to + * 900 seconds. It is not long enough for some applications + * i.e. MS Access. Therefore, the timeout is increased to + * 5400 seconds. + */ +#define SSN_KEEP_ALIVE_TIMEOUT (90 * 60) /* seconds */ +#define FRAGMENT_TIMEOUT (2 * SECONDS) + +/* smb_netbios_util.c */ +extern int netbios_first_level_name_decode(char *in, char *name, char *scope); +extern int netbios_first_level_name_encode(unsigned char *name, + unsigned char *scope, unsigned char *out, int max_out); +extern int netbios_name_isvalid(char *in, char *out); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_NETBIOS_H */ diff --git a/usr/src/uts/common/smbsrv/netrauth.h b/usr/src/uts/common/smbsrv/netrauth.h new file mode 100644 index 000000000000..075bbba2d236 --- /dev/null +++ b/usr/src/uts/common/smbsrv/netrauth.h @@ -0,0 +1,162 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_NETRAUTH_H +#define _SMBSRV_NETRAUTH_H + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * Interface definitions for the NETR remote authentication and logon + * services. + */ + +#include +#include +#include + +#ifndef _KERNEL +#include +#endif /* _KERNEL */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * See also netlogon.ndl. + */ +#define NETR_WKSTA_TRUST_ACCOUNT_TYPE 0x02 +#define NETR_DOMAIN_TRUST_ACCOUNT_TYPE 0x04 + +/* + * Negotiation flags for challenge/response authentication. + * The extra flag (0x40000000) was added in SP4. + */ +#define NETR_NEGOTIATE_FLAGS 0x000001FF +#define NETR_NEGOTIATE_SP4_FLAG 0x40000000 + +#define NETR_SESSION_KEY_SZ 8 +#define NETR_CRED_DATA_SZ 8 +#define NETR_OWF_PASSWORD_SZ 16 + + +/* + * SAM logon levels: interactive and network. + */ +#define NETR_INTERACTIVE_LOGON 0x01 +#define NETR_NETWORK_LOGON 0x02 + + +/* + * SAM logon validation levels. + */ +#define NETR_VALIDATION_LEVEL3 0x03 + + +/* + * This is a duplicate of the netr_credential + * from netlogon.ndl. + */ +typedef struct netr_cred { + BYTE data[NETR_CRED_DATA_SZ]; +} netr_cred_t; + + + +#define NETR_FLG_NULL 0x00000001 +#define NETR_FLG_VALID 0x00000001 +#define NETR_FLG_INIT 0x00000002 + + +typedef struct netr_info { + DWORD flags; + char server[MLSVC_DOMAIN_NAME_MAX * 2]; + char hostname[MLSVC_DOMAIN_NAME_MAX * 2]; + netr_cred_t client_challenge; + netr_cred_t server_challenge; + netr_cred_t client_credential; + netr_cred_t server_credential; + BYTE session_key[NETR_SESSION_KEY_SZ]; + BYTE password[MLSVC_MACHINE_ACCT_PASSWD_MAX]; + time_t timestamp; +} netr_info_t; + +/* + * netr_client_t flags + * + * NETR_CFLG_ANON Anonymous connection + * NETR_CFLG_LOCAL Local user + * NETR_CFLG_DOMAIN Domain user + */ +#define NETR_CFLG_ANON 0x01 +#define NETR_CFLG_LOCAL 0x02 +#define NETR_CFLG_DOMAIN 0x04 + + +typedef struct netr_client { + uint16_t logon_level; + char *username; + char *domain; + char *workstation; + uint32_t ipaddr; + struct { + uint32_t challenge_key_len; + uint8_t *challenge_key_val; + } challenge_key; + struct { + uint32_t nt_password_len; + uint8_t *nt_password_val; + } nt_password; + struct { + uint32_t lm_password_len; + uint8_t *lm_password_val; + } lm_password; + uint32_t logon_id; + int native_os; + int native_lm; + uint32_t local_ipaddr; + uint16_t local_port; + uint32_t flags; +} netr_client_t; + + +/* + * NETLOGON private interface. + */ +int netr_gen_session_key(netr_info_t *netr_info); + +int netr_gen_credentials(BYTE *session_key, netr_cred_t *challenge, + DWORD timestamp, netr_cred_t *out_cred); + + +#define NETR_A2H(c) (isdigit(c)) ? ((c) - '0') : ((c) - 'A' + 10) + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_NETRAUTH_H */ diff --git a/usr/src/uts/common/smbsrv/nmpipes.h b/usr/src/uts/common/smbsrv/nmpipes.h new file mode 100644 index 000000000000..f0cd81cc3a6a --- /dev/null +++ b/usr/src/uts/common/smbsrv/nmpipes.h @@ -0,0 +1,165 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_NMPIPES_H +#define _SMBSRV_NMPIPES_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file defines pre-defined and system common named pipes. + * + * Named pipes are a simple IPC mechanism supported by Windows 9x, NT + * and 2000. The Windows named pipe implementation supports reliable + * one-way and two-way transport independent network messaging. The + * names follow the universal naming convention (UNC) defined for the + * Windows redirector: \\[server]\[share]\[path]name. There is a good + * overview of named pipes in Network Programming for Microsoft Windows + * Chapter 4. The redirector is described in Chapter 2. UNC names are + * case-insensitive. + * + * Network Programming for Microsoft Windows + * Anthony Jones and Jim Ohlund + * Microsoft Press, ISBN 0-7356-0560-2 + * + * Microsoft RPC, which is derived from DCE RPC, uses SMB named pipes + * as its transport mechanism. In addition to the pipe used to open + * each connection, a named pipe also appears in the bind response as + * a secondary address port. Sometimes the secondary address port is + * the same and sometimes it is different. The following associations + * have been observed. + * + * LSARPC lsass + * NETLOGON lsass + * SAMR lsass + * SPOOLSS spoolss + * SRVSVC ntsvcs + * SVCCTL ntsvcs + * WINREG winreg + * WKSSVC ntsvcs + * EVENTLOG ntsvcs + * LLSRPC llsrpc + * + * Further information on RPC named pipes is available in the following + * references. + * + * RPC for NT + * Guy R. Eddon + * R&D PUblications, ISBN 0-87930-450-2 + * + * Network Programming in Windows NT + * Alok K. Sinha + * Addison-Wesley, ISBN 0-201-59056-5 + * + * DCE/RPC over SMB Samba and Windows NT Domain Internals + * Luke Kenneth Casson Leighton + * Macmillan Technical Publishing, ISBN 1-57870-150-3 + */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Well-known or pre-defined Windows named pipes. Typically used + * with SmbNtCreateAndX and/or SmbTransactNmPipe. When passed to + * SmbNtCreateAndX the \PIPE prefix is often missing. These names + * are presented as observed on the wire but should be treated in + * a case-insensitive manner. + */ +#define PIPE_LANMAN "\\PIPE\\LANMAN" +#define PIPE_NETLOGON "\\PIPE\\NETLOGON" +#define PIPE_LSARPC "\\PIPE\\lsarpc" +#define PIPE_SAMR "\\PIPE\\samr" +#define PIPE_SPOOLSS "\\PIPE\\spoolss" +#define PIPE_SRVSVC "\\PIPE\\srvsvc" +#define PIPE_SVCCTL "\\PIPE\\svcctl" +#define PIPE_WINREG "\\PIPE\\winreg" +#define PIPE_WKSSVC "\\PIPE\\wkssvc" +#define PIPE_EVENTLOG "\\PIPE\\EVENTLOG" +#define PIPE_LSASS "\\PIPE\\lsass" +#define PIPE_NTSVCS "\\PIPE\\ntsvcs" +#define PIPE_ATSVC "\\PIPE\\atsvc" +#define PIPE_BROWSESS "\\PIPE\\browsess" +#define PIPE_WINSSVC "\\PIPE\\winssvc" +#define PIPE_WINSMGR "\\PIPE\\winsmgr" +#define PIPE_LLSRPC "\\PIPE\\llsrpc" +#define PIPE_REPL "\\PIPE\\repl" + +/* + * Named pipe function codes (NTDDK cifs.h). + */ +#define TRANS_SET_NMPIPE_STATE 0x01 +#define TRANS_RAW_READ_NMPIPE 0x11 +#define TRANS_QUERY_NMPIPE_STATE 0x21 +#define TRANS_QUERY_NMPIPE_INFO 0x22 +#define TRANS_PEEK_NMPIPE 0x23 +#define TRANS_TRANSACT_NMPIPE 0x26 +#define TRANS_RAW_WRITE_NMPIPE 0x31 +#define TRANS_READ_NMPIPE 0x36 +#define TRANS_WRITE_NMPIPE 0x37 +#define TRANS_WAIT_NMPIPE 0x53 +#define TRANS_CALL_NMPIPE 0x54 + +/* + * SMB pipe handle state bits used by Query/SetNamedPipeHandleState. + * These numbers are the bit locations of the fields in the handle state. + */ +#define PIPE_COMPLETION_MODE_BITS 15 +#define PIPE_PIPE_END_BITS 14 +#define PIPE_PIPE_TYPE_BITS 10 +#define PIPE_READ_MODE_BITS 8 +#define PIPE_MAXIMUM_INSTANCES_BITS 0 + +/* + * DosPeekNmPipe pipe states. + */ +#define PIPE_STATE_DISCONNECTED 0x0001 +#define PIPE_STATE_LISTENING 0x0002 +#define PIPE_STATE_CONNECTED 0x0003 +#define PIPE_STATE_CLOSING 0x0004 + +/* + * DosCreateNPipe and DosQueryNPHState state. + */ +#define SMB_PIPE_READMODE_BYTE 0x0000 +#define SMB_PIPE_READMODE_MESSAGE 0x0100 +#define SMB_PIPE_TYPE_BYTE 0x0000 +#define SMB_PIPE_TYPE_MESSAGE 0x0400 +#define SMB_PIPE_END_CLIENT 0x0000 +#define SMB_PIPE_END_SERVER 0x4000 +#define SMB_PIPE_WAIT 0x0000 +#define SMB_PIPE_NOWAIT 0x8000 +#define SMB_PIPE_UNLIMITED_INSTANCES 0x00FF + + +#ifdef __cplusplus +} +#endif + + +#endif /* _SMBSRV_NMPIPES_H */ diff --git a/usr/src/uts/common/smbsrv/ntaccess.h b/usr/src/uts/common/smbsrv/ntaccess.h new file mode 100644 index 000000000000..114150baa9ba --- /dev/null +++ b/usr/src/uts/common/smbsrv/ntaccess.h @@ -0,0 +1,237 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_NTACCESS_H +#define _SMBSRV_NTACCESS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file defines the NT compatible access control masks and values. + * An access mask as a 32-bit value arranged as shown below. + * + * 31-28 Generic bits, interpreted per object type + * 27-26 Reserved, must-be-zero + * 25 Maximum allowed + * 24 System Security rights (SACL is SD) + * 23-16 Standard access rights, generic to all object types + * 15-0 Specific access rights, object specific + * + * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +---------------+---------------+-------------------------------+ + * |G|G|G|G|Res'd|A| StandardRights| SpecificRights | + * |R|W|E|A| |S| | | + * +-+-------------+---------------+-------------------------------+ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Specific rights for files, pipes and directories. + */ +#define FILE_READ_DATA (0x0001) /* file & pipe */ +#define FILE_LIST_DIRECTORY (0x0001) /* directory */ +#define FILE_WRITE_DATA (0x0002) /* file & pipe */ +#define FILE_ADD_FILE (0x0002) /* directory */ +#define FILE_APPEND_DATA (0x0004) /* file */ +#define FILE_ADD_SUBDIRECTORY (0x0004) /* directory */ +#define FILE_CREATE_PIPE_INSTANCE (0x0004) /* named pipe */ +#define FILE_READ_EA (0x0008) /* file & directory */ +#define FILE_READ_PROPERTIES (0x0008) /* pipe */ +#define FILE_WRITE_EA (0x0010) /* file & directory */ +#define FILE_WRITE_PROPERTIES (0x0010) /* pipe */ +#define FILE_EXECUTE (0x0020) /* file */ +#define FILE_TRAVERSE (0x0020) /* directory */ +#define FILE_DELETE_CHILD (0x0040) /* directory */ +#define FILE_READ_ATTRIBUTES (0x0080) /* all */ +#define FILE_WRITE_ATTRIBUTES (0x0100) /* all */ +#define FILE_SPECIFIC_ALL (0x000001FFL) +#define SPECIFIC_RIGHTS_ALL (0x0000FFFFL) + + +/* + * Standard rights: + * + * DELETE The right to delete the object. + * + * READ_CONTROL The right to read the information in the object's security + * descriptor, not including the information in the SACL. + * + * WRITE_DAC The right to modify the DACL in the object's security + * descriptor. + * + * WRITE_OWNER The right to change the owner in the object's security + * descriptor. + * + * SYNCHRONIZE The right to use the object for synchronization. This enables + * a thread to wait until the object is in the signaled state. + */ +#define DELETE (0x00010000L) +#define READ_CONTROL (0x00020000L) +#define WRITE_DAC (0x00040000L) +#define WRITE_OWNER (0x00080000L) /* take ownership */ +#define SYNCHRONIZE (0x00100000L) +#define STANDARD_RIGHTS_REQUIRED (0x000F0000L) +#define STANDARD_RIGHTS_ALL (0x001F0000L) + + +#define STANDARD_RIGHTS_READ (READ_CONTROL) +#define STANDARD_RIGHTS_WRITE (READ_CONTROL) +#define STANDARD_RIGHTS_EXECUTE (READ_CONTROL) + +#define FILE_METADATA_ALL (FILE_READ_EA |\ + FILE_READ_ATTRIBUTES |\ + READ_CONTROL |\ + FILE_WRITE_EA |\ + FILE_WRITE_ATTRIBUTES |\ + WRITE_DAC |\ + WRITE_OWNER |\ + SYNCHRONIZE) + +#define FILE_DATA_ALL (FILE_READ_DATA |\ + FILE_WRITE_DATA |\ + FILE_APPEND_DATA |\ + FILE_EXECUTE |\ + DELETE) + +#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF) + + +/* + * Miscellaneous bits: SACL access and maximum allowed access. + */ +#define ACCESS_SYSTEM_SECURITY (0x01000000L) +#define MAXIMUM_ALLOWED (0x02000000L) + + +/* + * Generic rights. These are shorthands that are interpreted as + * appropriate for the type of secured object being accessed. + */ +#define GENERIC_ALL (0x10000000UL) +#define GENERIC_EXECUTE (0x20000000UL) +#define GENERIC_WRITE (0x40000000UL) +#define GENERIC_READ (0x80000000UL) + +#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ | \ + FILE_READ_DATA | \ + FILE_READ_ATTRIBUTES | \ + FILE_READ_EA | \ + SYNCHRONIZE) + +#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE | \ + FILE_WRITE_DATA | \ + FILE_WRITE_ATTRIBUTES | \ + FILE_WRITE_EA | \ + FILE_APPEND_DATA | \ + SYNCHRONIZE) + +#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE | \ + FILE_READ_ATTRIBUTES | \ + FILE_EXECUTE | \ + SYNCHRONIZE) + +#define FILE_GENERIC_ALL (FILE_GENERIC_READ | \ + FILE_GENERIC_WRITE | \ + FILE_GENERIC_EXECUTE) + + +/* + * LSA policy desired access masks. + */ +#define POLICY_VIEW_LOCAL_INFORMATION 0x00000001L +#define POLICY_VIEW_AUDIT_INFORMATION 0x00000002L +#define POLICY_GET_PRIVATE_INFORMATION 0x00000004L +#define POLICY_TRUST_ADMIN 0x00000008L +#define POLICY_CREATE_ACCOUNT 0x00000010L +#define POLICY_CREATE_SECRET 0x00000020L +#define POLICY_CREATE_PRIVILEGE 0x00000040L +#define POLICY_SET_DEFAULT_QUOTA_LIMITS 0x00000080L +#define POLICY_SET_AUDIT_REQUIREMENTS 0x00000100L +#define POLICY_AUDIT_LOG_ADMIN 0x00000200L +#define POLICY_SERVER_ADMIN 0x00000400L +#define POLICY_LOOKUP_NAMES 0x00000800L + + +/* + * SAM specific rights desired access masks. These definitions are listed + * mostly as a convenience; they don't seem to be documented. Setting the + * desired access mask to GENERIC_EXECUTE and STANDARD_RIGHTS_EXECUTE + * seems to work when just looking up information. + */ +#define SAM_LOOKUP_INFORMATION (GENERIC_EXECUTE \ + | STANDARD_RIGHTS_EXECUTE) + +#define SAM_ACCESS_USER_READ 0x0000031BL +#define SAM_ACCESS_USER_UPDATE 0x0000031FL +#define SAM_ACCESS_USER_SETPWD 0x0000037FL +#define SAM_CONNECT_CREATE_ACCOUNT 0x00000020L +#define SAM_ENUM_LOCAL_DOMAIN 0x00000030L +#define SAM_DOMAIN_CREATE_ACCOUNT 0x00000211L + + +/* + * File attributes + * + * Note: 0x00000008 is reserved for use for the old DOS VOLID (volume ID) + * and is therefore not considered valid in NT. + * + * Note: 0x00000010 is reserved for use for the old DOS SUBDIRECTORY flag + * and is therefore not considered valid in NT. This flag has + * been disassociated with file attributes since the other flags are + * protected with READ_ and WRITE_ATTRIBUTES access to the file. + * + * Note: Note also that the order of these flags is set to allow both the + * FAT and the Pinball File Systems to directly set the attributes + * flags in attributes words without having to pick each flag out + * individually. The order of these flags should not be changed! + * + * The file attributes are defined in smbsrv/smb_vops.h + */ + +/* Filesystem Attributes */ +#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 +#define FILE_CASE_PRESERVED_NAMES 0x00000002 +#define FILE_UNICODE_ON_DISK 0x00000004 +#define FILE_PERSISTENT_ACLS 0x00000008 +#define FILE_FILE_COMPRESSION 0x00000010 +#define FILE_VOLUME_QUOTAS 0x00000020 +#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 +#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 +#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 +#define FILE_VOLUME_IS_COMPRESSED 0x00008000 +#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 +#define FILE_SUPPORTS_ENCRYPTION 0x00020000 +#define FILE_NAMED_STREAMS 0x00040000 +#define FILE_READ_ONLY_VOLUME 0x00080000 + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_NTACCESS_H */ diff --git a/usr/src/uts/common/smbsrv/nterror.h b/usr/src/uts/common/smbsrv/nterror.h new file mode 100644 index 000000000000..4695d4f15da3 --- /dev/null +++ b/usr/src/uts/common/smbsrv/nterror.h @@ -0,0 +1,941 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifndef _SMBSRV_NTERROR_H +#define _SMBSRV_NTERROR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file defines the list of Win32 error codes. If you need an + * error code that is defined in the Win32 Error Codes document but + * is not listed here, please add it to the file. There is a list + * of Win32 error codes on: + * + * http://msdn.microsoft.com/library/psdk/psdkref/errlist_9usz.htm + * + * Be careful not to confuse status codes with error codes. The status + * codes are listed in ntstatus.h. Some mappings between NT status + * codes and Win32 error codes is provided in the Microsoft knowledge + * base article Q113996. + * + * Win32 error codes are 32-bit values with the following format + * (winerror.h): + * + * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +---+-+-+-----------------------+-------------------------------+ + * |Sev|C|R| Facility | Code | + * +---+-+-+-----------------------+-------------------------------+ + * + * Sev severity code + * 00 - Success + * 01 - Informational + * 10 - Warning + * 11 - Error + * + * C customer/client flag (set to 1 for user defined codes). + * R reserved (set to zero) + * Facility facility code + * Code facility's status code + */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Facility codes + */ +#define FACILITY_NULL 0 +#define FACILITY_RPC 1 +#define FACILITY_DISPATCH 2 +#define FACILITY_STORAGE 3 +#define FACILITY_ITF 4 +/* 5 */ +/* 6 */ +#define FACILITY_WIN32 7 +#define FACILITY_WINDOWS 8 +#define FACILITY_SSPI 9 +#define FACILITY_CONTROL 10 +#define FACILITY_CERT 11 +#define FACILITY_INTERNET 12 +#define FACILITY_MEDIASERVER 13 +#define FACILITY_MSMQ 14 +#define FACILITY_SETUPAPI 15 + + +/* + * Complete list of Win32 error codes. For error description + * you can look at MS-KB articles 155011 and 155012 + */ + +#define ERROR_SUCCESS 0 +#define NO_ERROR 0 +#define ERROR_INVALID_FUNCTION 1 +#define ERROR_FILE_NOT_FOUND 2 +#define ERROR_PATH_NOT_FOUND 3 +#define ERROR_TOO_MANY_OPEN_FILES 4 +#define ERROR_ACCESS_DENIED 5 +#define ERROR_INVALID_HANDLE 6 +#define ERROR_ARENA_TRASHED 7 +#define ERROR_NOT_ENOUGH_MEMORY 8 +#define ERROR_INVALID_BLOCK 9 +#define ERROR_BAD_ENVIRONMENT 10 +#define ERROR_BAD_FORMAT 11 +#define ERROR_INVALID_ACCESS 12 +#define ERROR_INVALID_DATA 13 +#define ERROR_OUTOFMEMORY 14 +#define ERROR_INVALID_DRIVE 15 +#define ERROR_CURRENT_DIRECTORY 16 +#define ERROR_NOT_SAME_DEVICE 17 +#define ERROR_NO_MORE_FILES 18 +#define ERROR_WRITE_PROTECT 19 +#define ERROR_BAD_UNIT 20 +#define ERROR_NOT_READY 21 +#define ERROR_BAD_COMMAND 22 +#define ERROR_CRC 23 +#define ERROR_BAD_LENGTH 24 +#define ERROR_SEEK 25 +#define ERROR_NOT_DOS_DISK 26 +#define ERROR_SECTOR_NOT_FOUND 27 +#define ERROR_OUT_OF_PAPER 28 +#define ERROR_WRITE_FAULT 29 +#define ERROR_READ_FAULT 30 +#define ERROR_GEN_FAILURE 31 +#define ERROR_SHARING_VIOLATION 32 +#define ERROR_LOCK_VIOLATION 33 +#define ERROR_WRONG_DISK 34 +#define ERROR_SHARING_BUFFER_EXCEEDED 36 +#define ERROR_HANDLE_EOF 38 +#define ERROR_HANDLE_DISK_FULL 39 +#define ERROR_NOT_SUPPORTED 50 +#define ERROR_REM_NOT_LIST 51 +#define ERROR_DUP_NAME 52 +#define ERROR_BAD_NETPATH 53 +#define ERROR_NETWORK_BUSY 54 +#define ERROR_DEV_NOT_EXIST 55 +#define ERROR_TOO_MANY_CMDS 56 +#define ERROR_ADAP_HDW_ERR 57 +#define ERROR_BAD_NET_RESP 58 +#define ERROR_UNEXP_NET_ERR 59 +#define ERROR_BAD_REM_ADAP 60 +#define ERROR_PRINTQ_FULL 61 +#define ERROR_NO_SPOOL_SPACE 62 +#define ERROR_PRINT_CANCELLED 63 +#define ERROR_NETNAME_DELETED 64 +#define ERROR_NETWORK_ACCESS_DENIED 65 +#define ERROR_BAD_DEV_TYPE 66 +#define ERROR_BAD_NET_NAME 67 +#define ERROR_TOO_MANY_NAMES 68 +#define ERROR_TOO_MANY_SESS 69 +#define ERROR_SHARING_PAUSED 70 +#define ERROR_REQ_NOT_ACCEP 71 +#define ERROR_REDIR_PAUSED 72 +#define ERROR_FILE_EXISTS 80 +#define ERROR_CANNOT_MAKE 82 +#define ERROR_FAIL_I24 83 +#define ERROR_OUT_OF_STRUCTURES 84 +#define ERROR_ALREADY_ASSIGNED 85 +#define ERROR_INVALID_PASSWORD 86 +#define ERROR_INVALID_PARAMETER 87 +#define ERROR_NET_WRITE_FAULT 88 +#define ERROR_NO_PROC_SLOTS 89 +#define ERROR_TOO_MANY_SEMAPHORES 100 +#define ERROR_EXCL_SEM_ALREADY_OWNED 101 +#define ERROR_SEM_IS_SET 102 +#define ERROR_TOO_MANY_SEM_REQUESTS 103 +#define ERROR_INVALID_AT_INTERRUPT_TIME 104 +#define ERROR_SEM_OWNER_DIED 105 +#define ERROR_SEM_USER_LIMIT 106 +#define ERROR_DISK_CHANGE 107 +#define ERROR_DRIVE_LOCKED 108 +#define ERROR_BROKEN_PIPE 109 +#define ERROR_OPEN_FAILED 110 +#define ERROR_BUFFER_OVERFLOW 111 +#define ERROR_DISK_FULL 112 +#define ERROR_NO_MORE_SEARCH_HANDLES 113 +#define ERROR_INVALID_TARGET_HANDLE 114 +#define ERROR_INVALID_CATEGORY 117 +#define ERROR_INVALID_VERIFY_SWITCH 118 +#define ERROR_BAD_DRIVER_LEVEL 119 +#define ERROR_CALL_NOT_IMPLEMENTED 120 +#define ERROR_SEM_TIMEOUT 121 +#define ERROR_INSUFFICIENT_BUFFER 122 +#define ERROR_INVALID_NAME 123 +#define ERROR_INVALID_LEVEL 124 +#define ERROR_NO_VOLUME_LABEL 125 +#define ERROR_MOD_NOT_FOUND 126 +#define ERROR_PROC_NOT_FOUND 127 +#define ERROR_WAIT_NO_CHILDREN 128 +#define ERROR_CHILD_NOT_COMPLETE 129 +#define ERROR_DIRECT_ACCESS_HANDLE 130 +#define ERROR_NEGATIVE_SEEK 131 +#define ERROR_SEEK_ON_DEVICE 132 +#define ERROR_IS_JOIN_TARGET 133 +#define ERROR_IS_JOINED 134 +#define ERROR_IS_SUBSTED 135 +#define ERROR_NOT_JOINED 136 +#define ERROR_NOT_SUBSTED 137 +#define ERROR_JOIN_TO_JOIN 138 +#define ERROR_SUBST_TO_SUBST 139 +#define ERROR_JOIN_TO_SUBST 140 +#define ERROR_SUBST_TO_JOIN 141 +#define ERROR_BUSY_DRIVE 142 +#define ERROR_SAME_DRIVE 143 +#define ERROR_DIR_NOT_ROOT 144 +#define ERROR_DIR_NOT_EMPTY 145 +#define ERROR_IS_SUBST_PATH 146 +#define ERROR_IS_JOIN_PATH 147 +#define ERROR_PATH_BUSY 148 +#define ERROR_IS_SUBST_TARGET 149 +#define ERROR_SYSTEM_TRACE 150 +#define ERROR_INVALID_EVENT_COUNT 151 +#define ERROR_TOO_MANY_MUXWAITERS 152 +#define ERROR_INVALID_LIST_FORMAT 153 +#define ERROR_LABEL_TOO_LONG 154 +#define ERROR_TOO_MANY_TCBS 155 +#define ERROR_SIGNAL_REFUSED 156 +#define ERROR_DISCARDED 157 +#define ERROR_NOT_LOCKED 158 +#define ERROR_BAD_THREADID_ADDR 159 +#define ERROR_BAD_ARGUMENTS 160 +#define ERROR_BAD_PATHNAME 161 +#define ERROR_SIGNAL_PENDING 162 +#define ERROR_MAX_THRDS_REACHED 164 +#define ERROR_LOCK_FAILED 167 +#define ERROR_BUSY 170 +#define ERROR_CANCEL_VIOLATION 173 +#define ERROR_ATOMIC_LOCKS_NOT_SUPPORTED 174 +#define ERROR_INVALID_SEGMENT_NUMBER 180 +#define ERROR_INVALID_ORDINAL 182 +#define ERROR_ALREADY_EXISTS 183 +#define ERROR_INVALID_FLAG_NUMBER 186 +#define ERROR_SEM_NOT_FOUND 187 +#define ERROR_INVALID_STARTING_CODESEG 188 +#define ERROR_INVALID_STACKSEG 189 +#define ERROR_INVALID_MODULETYPE 190 +#define ERROR_INVALID_EXE_SIGNATURE 191 +#define ERROR_EXE_MARKED_INVALID 192 +#define ERROR_BAD_EXE_FORMAT 193 +#define ERROR_ITERATED_DATA_EXCEEDS_64k 194 +#define ERROR_INVALID_MINALLOCSIZE 195 +#define ERROR_DYNLINK_FROM_INVALID_RING 196 +#define ERROR_IOPL_NOT_ENABLED 197 +#define ERROR_INVALID_SEGDPL 198 +#define ERROR_AUTODATASEG_EXCEEDS_64k 199 +#define ERROR_RING2SEG_MUST_BE_MOVABLE 200 +#define ERROR_RELOC_CHAIN_XEEDS_SEGLIM 201 +#define ERROR_INFLOOP_IN_RELOC_CHAIN 202 +#define ERROR_ENVVAR_NOT_FOUND 203 +#define ERROR_NO_SIGNAL_SENT 205 +#define ERROR_FILENAME_EXCED_RANGE 206 +#define ERROR_RING2_STACK_IN_USE 207 +#define ERROR_META_EXPANSION_TOO_LONG 208 +#define ERROR_INVALID_SIGNAL_NUMBER 209 +#define ERROR_THREAD_1_INACTIVE 210 +#define ERROR_LOCKED 212 +#define ERROR_TOO_MANY_MODULES 214 +#define ERROR_NESTING_NOT_ALLOWED 215 +#define ERROR_EXE_MACHINE_TYPE_MISMATCH 216 +#define ERROR_BAD_PIPE 230 +#define ERROR_PIPE_BUSY 231 +#define ERROR_NO_DATA 232 +#define ERROR_PIPE_NOT_CONNECTED 233 +#define ERROR_MORE_DATA 234 +#define ERROR_VC_DISCONNECTED 240 +#define ERROR_INVALID_EA_NAME 254 +#define ERROR_EA_LIST_INCONSISTENT 255 +#define ERROR_NO_MORE_ITEMS 259 +#define ERROR_CANNOT_COPY 266 +#define ERROR_DIRECTORY 267 +#define ERROR_EAS_DIDNT_FIT 275 +#define ERROR_EA_FILE_CORRUPT 276 +#define ERROR_EA_TABLE_FULL 277 +#define ERROR_INVALID_EA_HANDLE 278 +#define ERROR_EAS_NOT_SUPPORTED 282 +#define ERROR_NOT_OWNER 288 +#define ERROR_TOO_MANY_POSTS 298 +#define ERROR_PARTIAL_COPY 299 +#define ERROR_OPLOCK_NOT_GRANTED 300 +#define ERROR_INVALID_OPLOCK_PROTOCOL 301 +#define ERROR_MR_MID_NOT_FOUND 317 +#define ERROR_INVALID_ADDRESS 487 +#define ERROR_ARITHMETIC_OVERFLOW 534 +#define ERROR_PIPE_CONNECTED 535 +#define ERROR_PIPE_LISTENING 536 +#define ERROR_EA_ACCESS_DENIED 994 +#define ERROR_OPERATION_ABORTED 995 +#define ERROR_IO_INCOMPLETE 996 +#define ERROR_IO_PENDING 997 +#define ERROR_NOACCESS 998 +#define ERROR_SWAPERROR 999 +#define ERROR_STACK_OVERFLOW 1001 +#define ERROR_INVALID_MESSAGE 1002 +#define ERROR_CAN_NOT_COMPLETE 1003 +#define ERROR_INVALID_FLAGS 1004 +#define ERROR_UNRECOGNIZED_VOLUME 1005 +#define ERROR_FILE_INVALID 1006 +#define ERROR_FULLSCREEN_MODE 1007 +#define ERROR_NO_TOKEN 1008 +#define ERROR_BADDB 1009 +#define ERROR_BADKEY 1010 +#define ERROR_CANTOPEN 1011 +#define ERROR_CANTREAD 1012 +#define ERROR_CANTWRITE 1013 +#define ERROR_REGISTRY_RECOVERED 1014 +#define ERROR_REGISTRY_CORRUPT 1015 +#define ERROR_REGISTRY_IO_FAILED 1016 +#define ERROR_NOT_REGISTRY_FILE 1017 +#define ERROR_KEY_DELETED 1018 +#define ERROR_NO_LOG_SPACE 1019 +#define ERROR_KEY_HAS_CHILDREN 1020 +#define ERROR_CHILD_MUST_BE_VOLATILE 1021 +#define ERROR_NOTIFY_ENUM_DIR 1022 +#define ERROR_DEPENDENT_SERVICES_RUNNING 1051 +#define ERROR_INVALID_SERVICE_CONTROL 1052 +#define ERROR_SERVICE_REQUEST_TIMEOUT 1053 +#define ERROR_SERVICE_NO_THREAD 1054 +#define ERROR_SERVICE_DATABASE_LOCKED 1055 +#define ERROR_SERVICE_ALREADY_RUNNING 1056 +#define ERROR_INVALID_SERVICE_ACCOUNT 1057 +#define ERROR_SERVICE_DISABLED 1058 +#define ERROR_CIRCULAR_DEPENDENCY 1059 +#define ERROR_SERVICE_DOES_NOT_EXIST 1060 +#define ERROR_SERVICE_CANNOT_ACCEPT_CTRL 1061 +#define ERROR_SERVICE_NOT_ACTIVE 1062 +#define ERROR_FAILED_SERVICE_CONTROLLER_CONNECT 1063 +#define ERROR_EXCEPTION_IN_SERVICE 1064 +#define ERROR_DATABASE_DOES_NOT_EXIST 1065 +#define ERROR_SERVICE_SPECIFIC_ERROR 1066 +#define ERROR_PROCESS_ABORTED 1067 +#define ERROR_SERVICE_DEPENDENCY_FAIL 1068 +#define ERROR_SERVICE_LOGON_FAILED 1069 +#define ERROR_SERVICE_START_HANG 1070 +#define ERROR_INVALID_SERVICE_LOCK 1071 +#define ERROR_SERVICE_MARKED_FOR_DELETE 1072 +#define ERROR_SERVICE_EXISTS 1073 +#define ERROR_ALREADY_RUNNING_LKG 1074 +#define ERROR_SERVICE_DEPENDENCY_DELETED 1075 +#define ERROR_BOOT_ALREADY_ACCEPTED 1076 +#define ERROR_SERVICE_NEVER_STARTED 1077 +#define ERROR_DUPLICATE_SERVICE_NAME 1078 +#define ERROR_DIFFERENT_SERVICE_ACCOUNT 1079 +#define ERROR_CANNOT_DETECT_DRIVER_FAILURE 1080 +#define ERROR_CANNOT_DETECT_PROCESS_ABORT 1081 +#define ERROR_NO_RECOVERY_PROGRAM 1082 +#define ERROR_END_OF_MEDIA 1100 +#define ERROR_FILEMARK_DETECTED 1101 +#define ERROR_BEGINNING_OF_MEDIA 1102 +#define ERROR_SETMARK_DETECTED 1103 +#define ERROR_NO_DATA_DETECTED 1104 +#define ERROR_PARTITION_FAILURE 1105 +#define ERROR_INVALID_BLOCK_LENGTH 1106 +#define ERROR_DEVICE_NOT_PARTITIONED 1107 +#define ERROR_UNABLE_TO_LOCK_MEDIA 1108 +#define ERROR_UNABLE_TO_UNLOAD_MEDIA 1109 +#define ERROR_MEDIA_CHANGED 1110 +#define ERROR_BUS_RESET 1111 +#define ERROR_NO_MEDIA_IN_DRIVE 1112 +#define ERROR_NO_UNICODE_TRANSLATION 1113 +#define ERROR_DLL_INIT_FAILED 1114 +#define ERROR_SHUTDOWN_IN_PROGRESS 1115 +#define ERROR_NO_SHUTDOWN_IN_PROGRESS 1116 +#define ERROR_IO_DEVICE 1117 +#define ERROR_SERIAL_NO_DEVICE 1118 +#define ERROR_IRQ_BUSY 1119 +#define ERROR_MORE_WRITES 1120 +#define ERROR_COUNTER_TIMEOUT 1121 +#define ERROR_FLOPPY_ID_MARK_NOT_FOUND 1122 +#define ERROR_FLOPPY_WRONG_CYLINDER 1123 +#define ERROR_FLOPPY_UNKNOWN_ERROR 1124 +#define ERROR_FLOPPY_BAD_REGISTERS 1125 +#define ERROR_DISK_RECALIBRATE_FAILED 1126 +#define ERROR_DISK_OPERATION_FAILED 1127 +#define ERROR_DISK_RESET_FAILED 1128 +#define ERROR_EOM_OVERFLOW 1129 +#define ERROR_NOT_ENOUGH_SERVER_MEMORY 1130 +#define ERROR_POSSIBLE_DEADLOCK 1131 +#define ERROR_MAPPED_ALIGNMENT 1132 +#define ERROR_SET_POWER_STATE_VETOED 1140 +#define ERROR_SET_POWER_STATE_FAILED 1141 +#define ERROR_TOO_MANY_LINKS 1142 +#define ERROR_OLD_WIN_VERSION 1150 +#define ERROR_APP_WRONG_OS 1151 +#define ERROR_SINGLE_INSTANCE_APP 1152 +#define ERROR_RMODE_APP 1153 +#define ERROR_INVALID_DLL 1154 +#define ERROR_NO_ASSOCIATION 1155 +#define ERROR_DDE_FAIL 1156 +#define ERROR_DLL_NOT_FOUND 1157 +#define ERROR_NO_MORE_USER_HANDLES 1158 +#define ERROR_MESSAGE_SYNC_ONLY 1159 +#define ERROR_SOURCE_ELEMENT_EMPTY 1160 +#define ERROR_DESTINATION_ELEMENT_FULL 1161 +#define ERROR_ILLEGAL_ELEMENT_ADDRESS 1162 +#define ERROR_MAGAZINE_NOT_PRESENT 1163 +#define ERROR_DEVICE_REINITIALIZATION_NEEDED 1164 +#define ERROR_DEVICE_REQUIRES_CLEANING 1165 +#define ERROR_DEVICE_DOOR_OPEN 1166 +#define ERROR_DEVICE_NOT_CONNECTED 1167 +#define ERROR_NOT_FOUND 1168 +#define ERROR_NO_MATCH 1169 +#define ERROR_SET_NOT_FOUND 1170 +#define ERROR_POINT_NOT_FOUND 1171 +#define ERROR_NO_TRACKING_SERVICE 1172 +#define ERROR_NO_VOLUME_ID 1173 +#define ERROR_CONNECTED_OTHER_PASSWORD 2108 +#define ERROR_BAD_USERNAME 2202 +#define ERROR_NOT_CONNECTED 2250 +#define ERROR_OPEN_FILES 2401 +#define ERROR_ACTIVE_CONNECTIONS 2402 +#define ERROR_DEVICE_IN_USE 2404 +#define ERROR_BAD_DEVICE 1200 +#define ERROR_CONNECTION_UNAVAIL 1201 +#define ERROR_DEVICE_ALREADY_REMEMBERED 1202 +#define ERROR_NO_NET_OR_BAD_PATH 1203 +#define ERROR_BAD_PROVIDER 1204 +#define ERROR_CANNOT_OPEN_PROFILE 1205 +#define ERROR_BAD_PROFILE 1206 +#define ERROR_NOT_CONTAINER 1207 +#define ERROR_EXTENDED_ERROR 1208 +#define ERROR_INVALID_GROUPNAME 1209 +#define ERROR_INVALID_COMPUTERNAME 1210 +#define ERROR_INVALID_EVENTNAME 1211 +#define ERROR_INVALID_DOMAINNAME 1212 +#define ERROR_INVALID_SERVICENAME 1213 +#define ERROR_INVALID_NETNAME 1214 +#define ERROR_INVALID_SHARENAME 1215 +#define ERROR_INVALID_PASSWORDNAME 1216 +#define ERROR_INVALID_MESSAGENAME 1217 +#define ERROR_INVALID_MESSAGEDEST 1218 +#define ERROR_SESSION_CREDENTIAL_CONFLICT 1219 +#define ERROR_REMOTE_SESSION_LIMIT_EXCEEDED 1220 +#define ERROR_DUP_DOMAINNAME 1221 +#define ERROR_NO_NETWORK 1222 +#define ERROR_CANCELLED 1223 +#define ERROR_USER_MAPPED_FILE 1224 +#define ERROR_CONNECTION_REFUSED 1225 +#define ERROR_GRACEFUL_DISCONNECT 1226 +#define ERROR_ADDRESS_ALREADY_ASSOCIATED 1227 +#define ERROR_ADDRESS_NOT_ASSOCIATED 1228 +#define ERROR_CONNECTION_INVALID 1229 +#define ERROR_CONNECTION_ACTIVE 1230 +#define ERROR_NETWORK_UNREACHABLE 1231 +#define ERROR_HOST_UNREACHABLE 1232 +#define ERROR_PROTOCOL_UNREACHABLE 1233 +#define ERROR_PORT_UNREACHABLE 1234 +#define ERROR_REQUEST_ABORTED 1235 +#define ERROR_CONNECTION_ABORTED 1236 +#define ERROR_RETRY 1237 +#define ERROR_CONNECTION_COUNT_LIMIT 1238 +#define ERROR_LOGIN_TIME_RESTRICTION 1239 +#define ERROR_LOGIN_WKSTA_RESTRICTION 1240 +#define ERROR_INCORRECT_ADDRESS 1241 +#define ERROR_ALREADY_REGISTERED 1242 +#define ERROR_SERVICE_NOT_FOUND 1243 +#define ERROR_NOT_AUTHENTICATED 1244 +#define ERROR_NOT_LOGGED_ON 1245 +#define ERROR_CONTINUE 1246 +#define ERROR_ALREADY_INITIALIZED 1247 +#define ERROR_NO_MORE_DEVICES 1248 +#define ERROR_NO_SUCH_SITE 1249 +#define ERROR_DOMAIN_CONTROLLER_EXISTS 1250 +#define ERROR_DS_NOT_INSTALLED 1251 +#define ERROR_NOT_ALL_ASSIGNED 1300 +#define ERROR_SOME_NOT_MAPPED 1301 +#define ERROR_NO_QUOTAS_FOR_ACCOUNT 1302 +#define ERROR_LOCAL_USER_SESSION_KEY 1303 +#define ERROR_NULL_LM_PASSWORD 1304 +#define ERROR_UNKNOWN_REVISION 1305 +#define ERROR_REVISION_MISMATCH 1306 +#define ERROR_INVALID_OWNER 1307 +#define ERROR_INVALID_PRIMARY_GROUP 1308 +#define ERROR_NO_IMPERSONATION_TOKEN 1309 +#define ERROR_CANT_DISABLE_MANDATORY 1310 +#define ERROR_NO_LOGON_SERVERS 1311 +#define ERROR_NO_SUCH_LOGON_SESSION 1312 +#define ERROR_NO_SUCH_PRIVILEGE 1313 +#define ERROR_PRIVILEGE_NOT_HELD 1314 +#define ERROR_INVALID_ACCOUNT_NAME 1315 +#define ERROR_USER_EXISTS 1316 +#define ERROR_NO_SUCH_USER 1317 +#define ERROR_GROUP_EXISTS 1318 +#define ERROR_NO_SUCH_GROUP 1319 +#define ERROR_MEMBER_IN_GROUP 1320 +#define ERROR_MEMBER_NOT_IN_GROUP 1321 +#define ERROR_LAST_ADMIN 1322 +#define ERROR_WRONG_PASSWORD 1323 +#define ERROR_ILL_FORMED_PASSWORD 1324 +#define ERROR_PASSWORD_RESTRICTION 1325 +#define ERROR_LOGON_FAILURE 1326 +#define ERROR_ACCOUNT_RESTRICTION 1327 +#define ERROR_INVALID_LOGON_HOURS 1328 +#define ERROR_INVALID_WORKSTATION 1329 +#define ERROR_PASSWORD_EXPIRED 1330 +#define ERROR_ACCOUNT_DISABLED 1331 +#define ERROR_NONE_MAPPED 1332 +#define ERROR_TOO_MANY_LUIDS_REQUESTED 1333 +#define ERROR_LUIDS_EXHAUSTED 1334 +#define ERROR_INVALID_SUB_AUTHORITY 1335 +#define ERROR_INVALID_ACL 1336 +#define ERROR_INVALID_SID 1337 +#define ERROR_INVALID_SECURITY_DESCR 1338 +#define ERROR_BAD_INHERITANCE_ACL 1340 +#define ERROR_SERVER_DISABLED 1341 +#define ERROR_SERVER_NOT_DISABLED 1342 +#define ERROR_INVALID_ID_AUTHORITY 1343 +#define ERROR_ALLOTTED_SPACE_EXCEEDED 1344 +#define ERROR_INVALID_GROUP_ATTRIBUTES 1345 +#define ERROR_BAD_IMPERSONATION_LEVEL 1346 +#define ERROR_CANT_OPEN_ANONYMOUS 1347 +#define ERROR_BAD_VALIDATION_CLASS 1348 +#define ERROR_BAD_TOKEN_TYPE 1349 +#define ERROR_NO_SECURITY_ON_OBJECT 1350 +#define ERROR_CANT_ACCESS_DOMAIN_INFO 1351 +#define ERROR_INVALID_SERVER_STATE 1352 +#define ERROR_INVALID_DOMAIN_STATE 1353 +#define ERROR_INVALID_DOMAIN_ROLE 1354 +#define ERROR_NO_SUCH_DOMAIN 1355 +#define ERROR_DOMAIN_EXISTS 1356 +#define ERROR_DOMAIN_LIMIT_EXCEEDED 1357 +#define ERROR_INTERNAL_DB_CORRUPTION 1358 +#define ERROR_INTERNAL_ERROR 1359 +#define ERROR_GENERIC_NOT_MAPPED 1360 +#define ERROR_BAD_DESCRIPTOR_FORMAT 1361 +#define ERROR_NOT_LOGON_PROCESS 1362 +#define ERROR_LOGON_SESSION_EXISTS 1363 +#define ERROR_NO_SUCH_PACKAGE 1364 +#define ERROR_BAD_LOGON_SESSION_STATE 1365 +#define ERROR_LOGON_SESSION_COLLISION 1366 +#define ERROR_INVALID_LOGON_TYPE 1367 +#define ERROR_CANNOT_IMPERSONATE 1368 +#define ERROR_RXACT_INVALID_STATE 1369 +#define ERROR_RXACT_COMMIT_FAILURE 1370 +#define ERROR_SPECIAL_ACCOUNT 1371 +#define ERROR_SPECIAL_GROUP 1372 +#define ERROR_SPECIAL_USER 1373 +#define ERROR_MEMBERS_PRIMARY_GROUP 1374 +#define ERROR_TOKEN_ALREADY_IN_USE 1375 +#define ERROR_NO_SUCH_ALIAS 1376 +#define ERROR_MEMBER_NOT_IN_ALIAS 1377 +#define ERROR_MEMBER_IN_ALIAS 1378 +#define ERROR_ALIAS_EXISTS 1379 +#define ERROR_LOGON_NOT_GRANTED 1380 +#define ERROR_TOO_MANY_SECRETS 1381 +#define ERROR_SECRET_TOO_LONG 1382 +#define ERROR_INTERNAL_DB_ERROR 1383 +#define ERROR_TOO_MANY_CONTEXT_IDS 1384 +#define ERROR_LOGON_TYPE_NOT_GRANTED 1385 +#define ERROR_NT_CROSS_ENCRYPTION_REQUIRED 1386 +#define ERROR_NO_SUCH_MEMBER 1387 +#define ERROR_INVALID_MEMBER 1388 +#define ERROR_TOO_MANY_SIDS 1389 +#define ERROR_LM_CROSS_ENCRYPTION_REQUIRED 1390 +#define ERROR_NO_INHERITANCE 1391 +#define ERROR_FILE_CORRUPT 1392 +#define ERROR_DISK_CORRUPT 1393 +#define ERROR_NO_USER_SESSION_KEY 1394 +#define ERROR_LICENSE_QUOTA_EXCEEDED 1395 +#define ERROR_INVALID_WINDOW_HANDLE 1400 +#define ERROR_INVALID_MENU_HANDLE 1401 +#define ERROR_INVALID_CURSOR_HANDLE 1402 +#define ERROR_INVALID_ACCEL_HANDLE 1403 +#define ERROR_INVALID_HOOK_HANDLE 1404 +#define ERROR_INVALID_DWP_HANDLE 1405 +#define ERROR_TLW_WITH_WSCHILD 1406 +#define ERROR_CANNOT_FIND_WND_CLASS 1407 +#define ERROR_WINDOW_OF_OTHER_THREAD 1408 +#define ERROR_HOTKEY_ALREADY_REGISTERED 1409 +#define ERROR_CLASS_ALREADY_EXISTS 1410 +#define ERROR_CLASS_DOES_NOT_EXIST 1411 +#define ERROR_CLASS_HAS_WINDOWS 1412 +#define ERROR_INVALID_INDEX 1413 +#define ERROR_INVALID_ICON_HANDLE 1414 +#define ERROR_PRIVATE_DIALOG_INDEX 1415 +#define ERROR_LISTBOX_ID_NOT_FOUND 1416 +#define ERROR_NO_WILDCARD_CHARACTERS 1417 +#define ERROR_CLIPBOARD_NOT_OPEN 1418 +#define ERROR_HOTKEY_NOT_REGISTERED 1419 +#define ERROR_WINDOW_NOT_DIALOG 1420 +#define ERROR_CONTROL_ID_NOT_FOUND 1421 +#define ERROR_INVALID_COMBOBOX_MESSAGE 1422 +#define ERROR_WINDOW_NOT_COMBOBOX 1423 +#define ERROR_INVALID_EDIT_HEIGHT 1424 +#define ERROR_DC_NOT_FOUND 1425 +#define ERROR_INVALID_HOOK_FILTER 1426 +#define ERROR_INVALID_FILTER_PROC 1427 +#define ERROR_HOOK_NEEDS_HMOD 1428 +#define ERROR_GLOBAL_ONLY_HOOK 1429 +#define ERROR_JOURNAL_HOOK_SET 1430 +#define ERROR_HOOK_NOT_INSTALLED 1431 +#define ERROR_INVALID_LB_MESSAGE 1432 +#define ERROR_SETCOUNT_ON_BAD_LB 1433 +#define ERROR_LB_WITHOUT_TABSTOPS 1434 +#define ERROR_DESTROY_OBJECT_OF_OTHER_THREAD 1435 +#define ERROR_CHILD_WINDOW_MENU 1436 +#define ERROR_NO_SYSTEM_MENU 1437 +#define ERROR_INVALID_MSGBOX_STYLE 1438 +#define ERROR_INVALID_SPI_VALUE 1439 +#define ERROR_SCREEN_ALREADY_LOCKED 1440 +#define ERROR_HWNDS_HAVE_DIFF_PARENT 1441 +#define ERROR_NOT_CHILD_WINDOW 1442 +#define ERROR_INVALID_GW_COMMAND 1443 +#define ERROR_INVALID_THREAD_ID 1444 +#define ERROR_NON_MDICHILD_WINDOW 1445 +#define ERROR_POPUP_ALREADY_ACTIVE 1446 +#define ERROR_NO_SCROLLBARS 1447 +#define ERROR_INVALID_SCROLLBAR_RANGE 1448 +#define ERROR_INVALID_SHOWWIN_COMMAND 1449 +#define ERROR_NO_SYSTEM_RESOURCES 1450 +#define ERROR_NONPAGED_SYSTEM_RESOURCES 1451 +#define ERROR_PAGED_SYSTEM_RESOURCES 1452 +#define ERROR_WORKING_SET_QUOTA 1453 +#define ERROR_PAGEFILE_QUOTA 1454 +#define ERROR_COMMITMENT_LIMIT 1455 +#define ERROR_MENU_ITEM_NOT_FOUND 1456 +#define ERROR_INVALID_KEYBOARD_HANDLE 1457 +#define ERROR_HOOK_TYPE_NOT_ALLOWED 1458 +#define ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION 1459 +#define ERROR_TIMEOUT 1460 +#define ERROR_INVALID_MONITOR_HANDLE 1461 +#define ERROR_EVENTLOG_FILE_CORRUPT 1500 +#define ERROR_EVENTLOG_CANT_START 1501 +#define ERROR_LOG_FILE_FULL 1502 +#define ERROR_EVENTLOG_FILE_CHANGED 1503 +#define ERROR_INSTALL_SERVICE 1601 +#define ERROR_INSTALL_USEREXIT 1602 +#define ERROR_INSTALL_FAILURE 1603 +#define ERROR_INSTALL_SUSPEND 1604 +#define ERROR_UNKNOWN_PRODUCT 1605 +#define ERROR_UNKNOWN_FEATURE 1606 +#define ERROR_UNKNOWN_COMPONENT 1607 +#define ERROR_UNKNOWN_PROPERTY 1608 +#define ERROR_INVALID_HANDLE_STATE 1609 +#define ERROR_BAD_CONFIGURATION 1610 +#define ERROR_INDEX_ABSENT 1611 +#define ERROR_INSTALL_SOURCE_ABSENT 1612 +#define ERROR_BAD_DATABASE_VERSION 1613 +#define ERROR_PRODUCT_UNINSTALLED 1614 +#define ERROR_BAD_QUERY_SYNTAX 1615 +#define ERROR_INVALID_FIELD 1616 +#define RPC_S_INVALID_STRING_BINDING 1700 +#define RPC_S_WRONG_KIND_OF_BINDING 1701 +#define RPC_S_INVALID_BINDING 1702 +#define RPC_S_PROTSEQ_NOT_SUPPORTED 1703 +#define RPC_S_INVALID_RPC_PROTSEQ 1704 +#define RPC_S_INVALID_STRING_UUID 1705 +#define RPC_S_INVALID_ENDPOINT_FORMAT 1706 +#define RPC_S_INVALID_NET_ADDR 1707 +#define RPC_S_NO_ENDPOINT_FOUND 1708 +#define RPC_S_INVALID_TIMEOUT 1709 +#define RPC_S_OBJECT_NOT_FOUND 1710 +#define RPC_S_ALREADY_REGISTERED 1711 +#define RPC_S_TYPE_ALREADY_REGISTERED 1712 +#define RPC_S_ALREADY_LISTENING 1713 +#define RPC_S_NO_PROTSEQS_REGISTERED 1714 +#define RPC_S_NOT_LISTENING 1715 +#define RPC_S_UNKNOWN_MGR_TYPE 1716 +#define RPC_S_UNKNOWN_IF 1717 +#define RPC_S_NO_BINDINGS 1718 +#define RPC_S_NO_PROTSEQS 1719 +#define RPC_S_CANT_CREATE_ENDPOINT 1720 +#define RPC_S_OUT_OF_RESOURCES 1721 +#define RPC_S_SERVER_UNAVAILABLE 1722 +#define RPC_S_SERVER_TOO_BUSY 1723 +#define RPC_S_INVALID_NETWORK_OPTIONS 1724 +#define RPC_S_NO_CALL_ACTIVE 1725 +#define RPC_S_CALL_FAILED 1726 +#define RPC_S_CALL_FAILED_DNE 1727 +#define RPC_S_PROTOCOL_ERROR 1728 +#define RPC_S_UNSUPPORTED_TRANS_SYN 1730 +#define RPC_S_UNSUPPORTED_TYPE 1732 +#define RPC_S_INVALID_TAG 1733 +#define RPC_S_INVALID_BOUND 1734 +#define RPC_S_NO_ENTRY_NAME 1735 +#define RPC_S_INVALID_NAME_SYNTAX 1736 +#define RPC_S_UNSUPPORTED_NAME_SYNTAX 1737 +#define RPC_S_UUID_NO_ADDRESS 1739 +#define RPC_S_DUPLICATE_ENDPOINT 1740 +#define RPC_S_UNKNOWN_AUTHN_TYPE 1741 +#define RPC_S_MAX_CALLS_TOO_SMALL 1742 +#define RPC_S_STRING_TOO_LONG 1743 +#define RPC_S_PROTSEQ_NOT_FOUND 1744 +#define RPC_S_PROCNUM_OUT_OF_RANGE 1745 +#define RPC_S_BINDING_HAS_NO_AUTH 1746 +#define RPC_S_UNKNOWN_AUTHN_SERVICE 1747 +#define RPC_S_UNKNOWN_AUTHN_LEVEL 1748 +#define RPC_S_INVALID_AUTH_IDENTITY 1749 +#define RPC_S_UNKNOWN_AUTHZ_SERVICE 1750 +#define EPT_S_INVALID_ENTRY 1751 +#define EPT_S_CANT_PERFORM_OP 1752 +#define EPT_S_NOT_REGISTERED 1753 +#define RPC_S_NOTHING_TO_EXPORT 1754 +#define RPC_S_INCOMPLETE_NAME 1755 +#define RPC_S_INVALID_VERS_OPTION 1756 +#define RPC_S_NO_MORE_MEMBERS 1757 +#define RPC_S_NOT_ALL_OBJS_UNEXPORTED 1758 +#define RPC_S_INTERFACE_NOT_FOUND 1759 +#define RPC_S_ENTRY_ALREADY_EXISTS 1760 +#define RPC_S_ENTRY_NOT_FOUND 1761 +#define RPC_S_NAME_SERVICE_UNAVAILABLE 1762 +#define RPC_S_INVALID_NAF_ID 1763 +#define RPC_S_CANNOT_SUPPORT 1764 +#define RPC_S_NO_CONTEXT_AVAILABLE 1765 +#define RPC_S_INTERNAL_ERROR 1766 +#define RPC_S_ZERO_DIVIDE 1767 +#define RPC_S_ADDRESS_ERROR 1768 +#define RPC_S_FP_DIV_ZERO 1769 +#define RPC_S_FP_UNDERFLOW 1770 +#define RPC_S_FP_OVERFLOW 1771 +#define RPC_X_NO_MORE_ENTRIES 1772 +#define RPC_X_SS_CHAR_TRANS_OPEN_FAIL 1773 +#define RPC_X_SS_CHAR_TRANS_SHORT_FILE 1774 +#define RPC_X_SS_IN_NULL_CONTEXT 1775 +#define RPC_X_SS_CONTEXT_DAMAGED 1777 +#define RPC_X_SS_HANDLES_MISMATCH 1778 +#define RPC_X_SS_CANNOT_GET_CALL_HANDLE 1779 +#define RPC_X_NULL_REF_POINTER 1780 +#define RPC_X_ENUM_VALUE_OUT_OF_RANGE 1781 +#define RPC_X_BYTE_COUNT_TOO_SMALL 1782 +#define RPC_X_BAD_STUB_DATA 1783 +#define ERROR_INVALID_USER_BUFFER 1784 +#define ERROR_UNRECOGNIZED_MEDIA 1785 +#define ERROR_NO_TRUST_LSA_SECRET 1786 +#define ERROR_NO_TRUST_SAM_ACCOUNT 1787 +#define ERROR_TRUSTED_DOMAIN_FAILURE 1788 +#define ERROR_TRUSTED_RELATIONSHIP_FAILURE 1789 +#define ERROR_TRUST_FAILURE 1790 +#define RPC_S_CALL_IN_PROGRESS 1791 +#define ERROR_NETLOGON_NOT_STARTED 1792 +#define ERROR_ACCOUNT_EXPIRED 1793 +#define ERROR_REDIRECTOR_HAS_OPEN_HANDLES 1794 +#define ERROR_PRINTER_DRIVER_ALREADY_INSTALLED 1795 +#define ERROR_UNKNOWN_PORT 1796 +#define ERROR_UNKNOWN_PRINTER_DRIVER 1797 +#define ERROR_UNKNOWN_PRINTPROCESSOR 1798 +#define ERROR_INVALID_SEPARATOR_FILE 1799 +#define ERROR_INVALID_PRIORITY 1800 +#define ERROR_INVALID_PRINTER_NAME 1801 +#define ERROR_PRINTER_ALREADY_EXISTS 1802 +#define ERROR_INVALID_PRINTER_COMMAND 1803 +#define ERROR_INVALID_DATATYPE 1804 +#define ERROR_INVALID_ENVIRONMENT 1805 +#define RPC_S_NO_MORE_BINDINGS 1806 +#define ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT 1807 +#define ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT 1808 +#define ERROR_NOLOGON_SERVER_TRUST_ACCOUNT 1809 +#define ERROR_DOMAIN_TRUST_INCONSISTENT 1810 +#define ERROR_SERVER_HAS_OPEN_HANDLES 1811 +#define ERROR_RESOURCE_DATA_NOT_FOUND 1812 +#define ERROR_RESOURCE_TYPE_NOT_FOUND 1813 +#define ERROR_RESOURCE_NAME_NOT_FOUND 1814 +#define ERROR_RESOURCE_LANG_NOT_FOUND 1815 +#define ERROR_NOT_ENOUGH_QUOTA 1816 +#define RPC_S_NO_INTERFACES 1817 +#define RPC_S_CALL_CANCELLED 1818 +#define RPC_S_BINDING_INCOMPLETE 1819 +#define RPC_S_COMM_FAILURE 1820 +#define RPC_S_UNSUPPORTED_AUTHN_LEVEL 1821 +#define RPC_S_NO_PRINC_NAME 1822 +#define RPC_S_NOT_RPC_ERROR 1823 +#define RPC_S_UUID_LOCAL_ONLY 1824 +#define RPC_S_SEC_PKG_ERROR 1825 +#define RPC_S_NOT_CANCELLED 1826 +#define RPC_X_INVALID_ES_ACTION 1827 +#define RPC_X_WRONG_ES_VERSION 1828 +#define RPC_X_WRONG_STUB_VERSION 1829 +#define RPC_X_INVALID_PIPE_OBJECT 1830 +#define RPC_X_WRONG_PIPE_ORDER 1831 +#define RPC_X_WRONG_PIPE_VERSION 1832 +#define RPC_S_GROUP_MEMBER_NOT_FOUND 1898 +#define EPT_S_CANT_CREATE 1899 +#define RPC_S_INVALID_OBJECT 1900 +#define ERROR_INVALID_TIME 1901 +#define ERROR_INVALID_FORM_NAME 1902 +#define ERROR_INVALID_FORM_SIZE 1903 +#define ERROR_ALREADY_WAITING 1904 +#define ERROR_PRINTER_DELETED 1905 +#define ERROR_INVALID_PRINTER_STATE 1906 +#define ERROR_PASSWORD_MUST_CHANGE 1907 +#define ERROR_DOMAIN_CONTROLLER_NOT_FOUND 1908 +#define ERROR_ACCOUNT_LOCKED_OUT 1909 +#define OR_INVALID_OXID 1910 +#define OR_INVALID_OID 1911 +#define OR_INVALID_SET 1912 +#define RPC_S_SEND_INCOMPLETE 1913 +#define RPC_S_INVALID_ASYNC_HANDLE 1914 +#define RPC_S_INVALID_ASYNC_CALL 1915 +#define RPC_X_PIPE_CLOSED 1916 +#define RPC_X_PIPE_DISCIPLINE_ERROR 1917 +#define RPC_X_PIPE_EMPTY 1918 +#define ERROR_NO_SITENAME 1919 +#define ERROR_CANT_ACCESS_FILE 1920 +#define ERROR_CANT_RESOLVE_FILENAME 1921 +#define ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY 1922 +#define ERROR_DS_NO_ATTRIBUTE_OR_VALUE 1923 +#define ERROR_DS_INVALID_ATTRIBUTE_SYNTAX 1924 +#define ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED 1925 +#define ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS 1926 +#define ERROR_DS_BUSY 1927 +#define ERROR_DS_UNAVAILABLE 1928 +#define ERROR_DS_NO_RIDS_ALLOCATED 1929 +#define ERROR_DS_NO_MORE_RIDS 1930 +#define ERROR_DS_INCORRECT_ROLE_OWNER 1931 +#define ERROR_DS_RIDMGR_INIT_ERROR 1932 +#define ERROR_DS_OBJ_CLASS_VIOLATION 1933 +#define ERROR_DS_CANT_ON_NON_LEAF 1934 +#define ERROR_DS_CANT_ON_RDN 1935 +#define ERROR_DS_CANT_MOD_OBJ_CLASS 1936 +#define ERROR_DS_CROSS_DOM_MOVE_ERROR 1937 +#define ERROR_DS_GC_NOT_AVAILABLE 1938 +#define ERROR_NO_BROWSER_SERVERS_FOUND 6118 +#define ERROR_INVALID_PIXEL_FORMAT 2000 +#define ERROR_BAD_DRIVER 2001 +#define ERROR_INVALID_WINDOW_STYLE 2002 +#define ERROR_METAFILE_NOT_SUPPORTED 2003 +#define ERROR_TRANSFORM_NOT_SUPPORTED 2004 +#define ERROR_CLIPPING_NOT_SUPPORTED 2005 +#define ERROR_INVALID_CMM 2300 +#define ERROR_INVALID_PROFILE 2301 +#define ERROR_TAG_NOT_FOUND 2302 +#define ERROR_TAG_NOT_PRESENT 2303 +#define ERROR_DUPLICATE_TAG 2304 +#define ERROR_PROFILE_NOT_ASSOCIATED_WITH_DEVICE 2305 +#define ERROR_PROFILE_NOT_FOUND 2306 +#define ERROR_INVALID_COLORSPACE 2307 +#define ERROR_ICM_NOT_ENABLED 2308 +#define ERROR_DELETING_ICM_XFORM 2309 +#define ERROR_INVALID_TRANSFORM 2310 +#define ERROR_UNKNOWN_PRINT_MONITOR 3000 +#define ERROR_PRINTER_DRIVER_IN_USE 3001 +#define ERROR_SPOOL_FILE_NOT_FOUND 3002 +#define ERROR_SPL_NO_STARTDOC 3003 +#define ERROR_SPL_NO_ADDJOB 3004 +#define ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED 3005 +#define ERROR_PRINT_MONITOR_ALREADY_INSTALLED 3006 +#define ERROR_INVALID_PRINT_MONITOR 3007 +#define ERROR_PRINT_MONITOR_IN_USE 3008 +#define ERROR_PRINTER_HAS_JOBS_QUEUED 3009 +#define ERROR_SUCCESS_REBOOT_REQUIRED 3010 +#define ERROR_SUCCESS_RESTART_REQUIRED 3011 +#define ERROR_WINS_INTERNAL 4000 +#define ERROR_CAN_NOT_DEL_LOCAL_WINS 4001 +#define ERROR_STATIC_INIT 4002 +#define ERROR_INC_BACKUP 4003 +#define ERROR_FULL_BACKUP 4004 +#define ERROR_REC_NON_EXISTENT 4005 +#define ERROR_RPL_NOT_ALLOWED 4006 +#define ERROR_DHCP_ADDRESS_CONFLICT 4100 +#define ERROR_WMI_GUID_NOT_FOUND 4200 +#define ERROR_WMI_INSTANCE_NOT_FOUND 4201 +#define ERROR_WMI_ITEMID_NOT_FOUND 4202 +#define ERROR_WMI_TRY_AGAIN 4203 +#define ERROR_WMI_DP_NOT_FOUND 4204 +#define ERROR_WMI_UNRESOLVED_INSTANCE_REF 4205 +#define ERROR_WMI_ALREADY_ENABLED 4206 +#define ERROR_WMI_GUID_DISCONNECTED 4207 +#define ERROR_WMI_SERVER_UNAVAILABLE 4208 +#define ERROR_WMI_DP_FAILED 4209 +#define ERROR_WMI_INVALID_MOF 4210 +#define ERROR_WMI_INVALID_REGINFO 4211 +#define ERROR_INVALID_MEDIA 4300 +#define ERROR_INVALID_LIBRARY 4301 +#define ERROR_INVALID_MEDIA_POOL 4302 +#define ERROR_DRIVE_MEDIA_MISMATCH 4303 +#define ERROR_MEDIA_OFFLINE 4304 +#define ERROR_LIBRARY_OFFLINE 4305 +#define ERROR_EMPTY 4306 +#define ERROR_NOT_EMPTY 4307 +#define ERROR_MEDIA_UNAVAILABLE 4308 +#define ERROR_RESOURCE_DISABLED 4309 +#define ERROR_INVALID_CLEANER 4310 +#define ERROR_UNABLE_TO_CLEAN 4311 +#define ERROR_OBJECT_NOT_FOUND 4312 +#define ERROR_DATABASE_FAILURE 4313 +#define ERROR_DATABASE_FULL 4314 +#define ERROR_MEDIA_INCOMPATIBLE 4315 +#define ERROR_RESOURCE_NOT_PRESENT 4316 +#define ERROR_INVALID_OPERATION 4317 +#define ERROR_MEDIA_NOT_AVAILABLE 4318 +#define ERROR_DEVICE_NOT_AVAILABLE 4319 +#define ERROR_REQUEST_REFUSED 4320 +#define ERROR_FILE_OFFLINE 4350 +#define ERROR_REMOTE_STORAGE_NOT_ACTIVE 4351 +#define ERROR_REMOTE_STORAGE_MEDIA_ERROR 4352 +#define ERROR_NOT_A_REPARSE_POINT 4390 +#define ERROR_REPARSE_ATTRIBUTE_CONFLICT 4391 +#define ERROR_DEPENDENT_RESOURCE_EXISTS 5001 +#define ERROR_DEPENDENCY_NOT_FOUND 5002 +#define ERROR_DEPENDENCY_ALREADY_EXISTS 5003 +#define ERROR_RESOURCE_NOT_ONLINE 5004 +#define ERROR_HOST_NODE_NOT_AVAILABLE 5005 +#define ERROR_RESOURCE_NOT_AVAILABLE 5006 +#define ERROR_RESOURCE_NOT_FOUND 5007 +#define ERROR_SHUTDOWN_CLUSTER 5008 +#define ERROR_CANT_EVICT_ACTIVE_NODE 5009 +#define ERROR_OBJECT_ALREADY_EXISTS 5010 +#define ERROR_OBJECT_IN_LIST 5011 +#define ERROR_GROUP_NOT_AVAILABLE 5012 +#define ERROR_GROUP_NOT_FOUND 5013 +#define ERROR_GROUP_NOT_ONLINE 5014 +#define ERROR_HOST_NODE_NOT_RESOURCE_OWNER 5015 +#define ERROR_HOST_NODE_NOT_GROUP_OWNER 5016 +#define ERROR_RESMON_CREATE_FAILED 5017 +#define ERROR_RESMON_ONLINE_FAILED 5018 +#define ERROR_RESOURCE_ONLINE 5019 +#define ERROR_QUORUM_RESOURCE 5020 +#define ERROR_NOT_QUORUM_CAPABLE 5021 +#define ERROR_CLUSTER_SHUTTING_DOWN 5022 +#define ERROR_INVALID_STATE 5023 +#define ERROR_RESOURCE_PROPERTIES_STORED 5024 +#define ERROR_NOT_QUORUM_CLASS 5025 +#define ERROR_CORE_RESOURCE 5026 +#define ERROR_QUORUM_RESOURCE_ONLINE_FAILED 5027 +#define ERROR_QUORUMLOG_OPEN_FAILED 5028 +#define ERROR_CLUSTERLOG_CORRUPT 5029 +#define ERROR_CLUSTERLOG_RECORD_EXCEEDS_MAXSIZE 5030 +#define ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE 5031 +#define ERROR_CLUSTERLOG_CHKPOINT_NOT_FOUND 5032 +#define ERROR_CLUSTERLOG_NOT_ENOUGH_SPACE 5033 +#define ERROR_ENCRYPTION_FAILED 6000 +#define ERROR_DECRYPTION_FAILED 6001 +#define ERROR_FILE_ENCRYPTED 6002 +#define ERROR_NO_RECOVERY_POLICY 6003 +#define ERROR_NO_EFS 6004 +#define ERROR_WRONG_EFS 6005 +#define ERROR_NO_USER_KEYS 6006 +#define ERROR_FILE_NOT_ENCRYPTED 6007 +#define ERROR_NOT_EXPORT_FORMAT 6008 + +#ifdef __cplusplus +} +#endif + + +#endif /* _SMBSRV_NTERROR_H */ diff --git a/usr/src/uts/common/smbsrv/ntifs.h b/usr/src/uts/common/smbsrv/ntifs.h new file mode 100644 index 000000000000..c538e4139e8e --- /dev/null +++ b/usr/src/uts/common/smbsrv/ntifs.h @@ -0,0 +1,216 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_NTIFS_H +#define _SMBSRV_NTIFS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file provides definitions compatible with the NT Installable + * File System (IFS) interface. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * File creation flags must start at the high end since they + * are combined with the attributes + */ + +#define FILE_FLAG_WRITE_THROUGH 0x80000000 +#define FILE_FLAG_OVERLAPPED 0x40000000 +#define FILE_FLAG_NO_BUFFERING 0x20000000 +#define FILE_FLAG_RANDOM_ACCESS 0x10000000 +#define FILE_FLAG_SEQUENTIAL_SCAN 0x08000000 +#define FILE_FLAG_DELETE_ON_CLOSE 0x04000000 +#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000 +#define FILE_FLAG_POSIX_SEMANTICS 0x01000000 +#define FILE_FLAG_OPEN_REPARSE_POINT 0x00200000 +#define FILE_FLAG_OPEN_NO_RECALL 0x00100000 + +/* + * The create/open option flags: used in NtCreateAndx and NtTransactCreate + * SMB requests. + * + * The CreateOptions specify the options to be applied when creating or + * opening the file, as a compatible combination of the following flags: + * + * FILE_DIRECTORY_FILE + * The file being created or opened is a directory file. With this + * flag, the Disposition parameter must be set to one of FILE_CREATE, + * FILE_OPEN, or FILE_OPEN_IF. With this flag, other compatible + * CreateOptions flags include only the following: + * FILE_SYNCHRONOUS_IO_ALERT + * FILE_SYNCHRONOUS_IO_NONALERT + * FILE_WRITE_THROUGH + * FILE_OPEN_FOR_BACKUP_INTENT + * FILE_OPEN_BY_FILE_ID + * + * FILE_NON_DIRECTORY_FILE + * The file being opened must not be a directory file or this call + * will fail. The file object being opened can represent a data file, + * a logical, virtual, or physical device, or a volume. + * + * FILE_WRITE_THROUGH + * System services, FSDs, and drivers that write data to the file must + * actually transfer the data into the file before any requested write + * operation is considered complete. This flag is automatically set if + * the CreateOptions flag FILE_NO_INTERMEDIATE _BUFFERING is set. + * + * FILE_SEQUENTIAL_ONLY + * All accesses to the file will be sequential. + * + * FILE_RANDOM_ACCESS + * Accesses to the file can be random, so no sequential read-ahead + * operations should be performed on the file by FSDs or the system. + * FILE_NO_INTERMEDIATE _BUFFERING The file cannot be cached or + * buffered in a driver's internal buffers. This flag is incompatible + * with the DesiredAccess FILE_APPEND_DATA flag. + * + * FILE_SYNCHRONOUS_IO_ALERT + * All operations on the file are performed synchronously. Any wait + * on behalf of the caller is subject to premature termination from + * alerts. This flag also causes the I/O system to maintain the file + * position context. If this flag is set, the DesiredAccess + * SYNCHRONIZE flag also must be set. + * + * FILE_SYNCHRONOUS_IO _NONALERT + * All operations on the file are performed synchronously. Waits in + * the system to synchronize I/O queuing and completion are not subject + * to alerts. This flag also causes the I/O system to maintain the file + * position context. If this flag is set, the DesiredAccess SYNCHRONIZE + * flag also must be set. + * + * FILE_CREATE_TREE _CONNECTION + * Create a tree connection for this file in order to open it over the + * network. This flag is irrelevant to device and intermediate drivers. + * + * FILE_COMPLETE_IF_OPLOCKED + * Complete this operation immediately with an alternate success code + * if the target file is oplocked, rather than blocking the caller's + * thread. If the file is oplocked, another caller already has access + * to the file over the network. This flag is irrelevant to device and + * intermediate drivers. + * + * FILE_NO_EA_KNOWLEDGE + * If the extended attributes on an existing file being opened indicate + * that the caller must understand EAs to properly interpret the file, + * fail this request because the caller does not understand how to deal + * with EAs. Device and intermediate drivers can ignore this flag. + * + * FILE_DELETE_ON_CLOSE + * Delete the file when the last reference to it is passed to close. + * + * FILE_OPEN_BY_FILE_ID + * The file name contains the name of a device and a 64-bit ID to + * be used to open the file. This flag is irrelevant to device and + * intermediate drivers. + * + * FILE_OPEN_FOR_BACKUP _INTENT + * The file is being opened for backup intent, hence, the system should + * check for certain access rights and grant the caller the appropriate + * accesses to the file before checking the input DesiredAccess against + * the file's security descriptor. This flag is irrelevant to device + * and intermediate drivers. + */ +#define FILE_DIRECTORY_FILE 0x00000001 +#define FILE_WRITE_THROUGH 0x00000002 +#define FILE_SEQUENTIAL_ONLY 0x00000004 +#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008 + +#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010 +#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 +#define FILE_NON_DIRECTORY_FILE 0x00000040 +#define FILE_CREATE_TREE_CONNECTION 0x00000080 + +#define FILE_COMPLETE_IF_OPLOCKED 0x00000100 +#define FILE_NO_EA_KNOWLEDGE 0x00000200 +/* UNUSED 0x00000400 */ +#define FILE_RANDOM_ACCESS 0x00000800 + +#define FILE_DELETE_ON_CLOSE 0x00001000 +#define FILE_OPEN_BY_FILE_ID 0x00002000 +#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000 +#define FILE_NO_COMPRESSION 0x00008000 + +#define FILE_RESERVE_OPFILTER 0x00100000 +#define FILE_RESERVED0 0x00200000 +#define FILE_RESERVED1 0x00400000 +#define FILE_RESERVED2 0x00800000 + +#define FILE_VALID_OPTION_FLAGS 0x007fffff +#define FILE_VALID_PIPE_OPTION_FLAGS 0x00000032 +#define FILE_VALID_MAILSLOT_OPTION_FLAGS 0x00000032 +#define FILE_VALID_SET_FLAGS 0x00000036 + +/* + * Define the file information class values used by the NT DDK and HAL. + */ +typedef enum _FILE_INFORMATION_CLASS { + FileDirectoryInformation = 1, + FileFullDirectoryInformation, /* 2 */ + FileBothDirectoryInformation, /* 3 */ + FileBasicInformation, /* 4 */ + FileStandardInformation, /* 5 */ + FileInternalInformation, /* 6 */ + FileEaInformation, /* 7 */ + FileAccessInformation, /* 8 */ + FileNameInformation, /* 9 */ + FileRenameInformation, /* 10 */ + FileLinkInformation, /* 11 */ + FileNamesInformation, /* 12 */ + FileDispositionInformation, /* 13 */ + FilePositionInformation, /* 14 */ + FileFullEaInformation, /* 15 */ + FileModeInformation, /* 16 */ + FileAlignmentInformation, /* 17 */ + FileAllInformation, /* 18 */ + FileAllocationInformation, /* 19 */ + FileEndOfFileInformation, /* 20 */ + FileAlternateNameInformation, /* 21 */ + FileStreamInformation, /* 22 */ + FilePipeInformation, /* 23 */ + FilePipeLocalInformation, /* 24 */ + FilePipeRemoteInformation, /* 25 */ + FileMailslotQueryInformation, /* 26 */ + FileMailslotSetInformation, /* 27 */ + FileCompressionInformation, /* 28 */ + FileObjectIdInformation, /* 29 */ + FileCompletionInformation, /* 30 */ + FileMoveClusterInformation, /* 31 */ + FileInformationReserved32, /* 32 */ + FileInformationReserved33, /* 33 */ + FileNetworkOpenInformation, /* 34 */ + FileMaximumInformation +} FILE_INFORMATION_CLASS; + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_NTIFS_H */ diff --git a/usr/src/uts/common/smbsrv/ntlocale.h b/usr/src/uts/common/smbsrv/ntlocale.h new file mode 100644 index 000000000000..778d6fe68ea8 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ntlocale.h @@ -0,0 +1,330 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_NTLOCALE_H +#define _SMBSRV_NTLOCALE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * NT language and locale identifiers. + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Language IDs. + * + * A language ID is a 16 bit value which is the combination of a + * primary language ID and a secondary language ID. The bits are + * allocated as follows: + * + * +-----------------------+-------------------------+ + * | Sublanguage ID | Primary Language ID | + * +-----------------------+-------------------------+ + * 15 10 9 0 bit + * + * The following two combinations of primary language ID and sub- + * language ID have special semantics: + * + * Primary Language ID Sublanguage ID Result + * ------------------- --------------- ------------------------ + * LANG_NEUTRAL SUBLANG_NEUTRAL Language neutral + * LANG_NEUTRAL SUBLANG_DEFAULT User default language + * LANG_NEUTRAL SUBLANG_SYS_DEFAULT System default language + * + * Language ID creation/extraction macros: + * MAKELANGID - construct language id from a primary language + * id and a sublanguage id. + * PRIMARYLANGID - extract primary language id from a language id. + * SUBLANGID - extract sublanguage id from a language id. + */ +#define MAKELANGID(p, s) ((((WORD)(s)) << 10) | (WORD)(p)) +#define PRIMARYLANGID(lgid) ((WORD)(lgid) & 0x3ff) +#define SUBLANGID(lgid) ((WORD)(lgid) >> 10) + + +/* + * Primary language IDs. + */ +#define LANG_NEUTRAL 0x00 + +#define LANG_AFRIKAANS 0x36 +#define LANG_ALBANIAN 0x1c +#define LANG_ARABIC 0x01 +#define LANG_ARMENIAN 0x2b +#define LANG_ASSAMESE 0x4d +#define LANG_AZERI 0x2c +#define LANG_BASQUE 0x2d +#define LANG_BELARUSIAN 0x23 +#define LANG_BENGALI 0x45 +#define LANG_BULGARIAN 0x02 +#define LANG_CATALAN 0x03 +#define LANG_CHINESE 0x04 +#define LANG_CROATIAN 0x1a +#define LANG_CZECH 0x05 +#define LANG_DANISH 0x06 +#define LANG_DUTCH 0x13 +#define LANG_ENGLISH 0x09 +#define LANG_ESTONIAN 0x25 +#define LANG_FAEROESE 0x38 +#define LANG_FARSI 0x29 +#define LANG_FINNISH 0x0b +#define LANG_FRENCH 0x0c +#define LANG_GEORGIAN 0x37 +#define LANG_GERMAN 0x07 +#define LANG_GREEK 0x08 +#define LANG_GUJARATI 0x47 +#define LANG_HEBREW 0x0d +#define LANG_HINDI 0x39 +#define LANG_HUNGARIAN 0x0e +#define LANG_ICELANDIC 0x0f +#define LANG_INDONESIAN 0x21 +#define LANG_ITALIAN 0x10 +#define LANG_JAPANESE 0x11 +#define LANG_KANNADA 0x4b +#define LANG_KASHMIRI 0x60 +#define LANG_KAZAK 0x3f +#define LANG_KONKANI 0x57 +#define LANG_KOREAN 0x12 +#define LANG_LATVIAN 0x26 +#define LANG_LITHUANIAN 0x27 +#define LANG_MACEDONIAN 0x2f +#define LANG_MALAY 0x3e +#define LANG_MALAYALAM 0x4c +#define LANG_MANIPURI 0x58 +#define LANG_MARATHI 0x4e +#define LANG_NEPALI 0x61 +#define LANG_NORWEGIAN 0x14 +#define LANG_ORIYA 0x48 +#define LANG_POLISH 0x15 +#define LANG_PORTUGUESE 0x16 +#define LANG_PUNJABI 0x46 +#define LANG_ROMANIAN 0x18 +#define LANG_RUSSIAN 0x19 +#define LANG_SANSKRIT 0x4f +#define LANG_SERBIAN 0x1a +#define LANG_SINDHI 0x59 +#define LANG_SLOVAK 0x1b +#define LANG_SLOVENIAN 0x24 +#define LANG_SPANISH 0x0a +#define LANG_SWAHILI 0x41 +#define LANG_SWEDISH 0x1d +#define LANG_TAMIL 0x49 +#define LANG_TATAR 0x44 +#define LANG_TELUGU 0x4a +#define LANG_THAI 0x1e +#define LANG_TURKISH 0x1f +#define LANG_UKRAINIAN 0x22 +#define LANG_URDU 0x20 +#define LANG_UZBEK 0x43 +#define LANG_VIETNAMESE 0x2a + + +/* + * Sublanguage IDs. + * + * The name immediately following SUBLANG_ dictates which primary + * language ID can be combined with the sub-language ID to form a + * valid language ID. + */ +#define SUBLANG_NEUTRAL 0x00 /* language neutral */ +#define SUBLANG_DEFAULT 0x01 /* user default */ +#define SUBLANG_SYS_DEFAULT 0x02 /* system default */ + +#define SUBLANG_ARABIC_SAUDI_ARABIA 0x01 /* Arabic (Saudi Arabia) */ +#define SUBLANG_ARABIC_IRAQ 0x02 /* Arabic (Iraq) */ +#define SUBLANG_ARABIC_EGYPT 0x03 /* Arabic (Egypt) */ +#define SUBLANG_ARABIC_LIBYA 0x04 /* Arabic (Libya) */ +#define SUBLANG_ARABIC_ALGERIA 0x05 /* Arabic (Algeria) */ +#define SUBLANG_ARABIC_MOROCCO 0x06 /* Arabic (Morocco) */ +#define SUBLANG_ARABIC_TUNISIA 0x07 /* Arabic (Tunisia) */ +#define SUBLANG_ARABIC_OMAN 0x08 /* Arabic (Oman) */ +#define SUBLANG_ARABIC_YEMEN 0x09 /* Arabic (Yemen) */ +#define SUBLANG_ARABIC_SYRIA 0x0a /* Arabic (Syria) */ +#define SUBLANG_ARABIC_JORDAN 0x0b /* Arabic (Jordan) */ +#define SUBLANG_ARABIC_LEBANON 0x0c /* Arabic (Lebanon) */ +#define SUBLANG_ARABIC_KUWAIT 0x0d /* Arabic (Kuwait) */ +#define SUBLANG_ARABIC_UAE 0x0e /* Arabic (U.A.E) */ +#define SUBLANG_ARABIC_BAHRAIN 0x0f /* Arabic (Bahrain) */ +#define SUBLANG_ARABIC_QATAR 0x10 /* Arabic (Qatar) */ +#define SUBLANG_AZERI_LATIN 0x01 /* Azeri (Latin) */ +#define SUBLANG_AZERI_CYRILLIC 0x02 /* Azeri (Cyrillic) */ +#define SUBLANG_CHINESE_TRADITIONAL 0x01 /* Chinese (Taiwan Region) */ +#define SUBLANG_CHINESE_SIMPLIFIED 0x02 /* Chinese (PR China) */ +#define SUBLANG_CHINESE_HONGKONG 0x03 /* Chinese (Hong Kong) */ +#define SUBLANG_CHINESE_SINGAPORE 0x04 /* Chinese (Singapore) */ +#define SUBLANG_CHINESE_MACAU 0x05 /* Chinese (Macau) */ +#define SUBLANG_DUTCH 0x01 /* Dutch */ +#define SUBLANG_DUTCH_BELGIAN 0x02 /* Dutch (Belgian) */ +#define SUBLANG_ENGLISH_US 0x01 /* English (USA) */ +#define SUBLANG_ENGLISH_UK 0x02 /* English (UK) */ +#define SUBLANG_ENGLISH_AUS 0x03 /* English (Australian) */ +#define SUBLANG_ENGLISH_CAN 0x04 /* English (Canadian) */ +#define SUBLANG_ENGLISH_NZ 0x05 /* English (New Zealand) */ +#define SUBLANG_ENGLISH_EIRE 0x06 /* English (Irish) */ +#define SUBLANG_ENGLISH_SOUTH_AFRICA 0x07 /* English (South Africa) */ +#define SUBLANG_ENGLISH_JAMAICA 0x08 /* English (Jamaica) */ +#define SUBLANG_ENGLISH_CARIBBEAN 0x09 /* English (Caribbean) */ +#define SUBLANG_ENGLISH_BELIZE 0x0a /* English (Belize) */ +#define SUBLANG_ENGLISH_TRINIDAD 0x0b /* English (Trinidad) */ +#define SUBLANG_ENGLISH_ZIMBABWE 0x0c /* English (Zimbabwe) */ +#define SUBLANG_ENGLISH_PHILIPPINES 0x0d /* English (Philippines) */ +#define SUBLANG_FRENCH 0x01 /* French */ +#define SUBLANG_FRENCH_BELGIAN 0x02 /* French (Belgian) */ +#define SUBLANG_FRENCH_CANADIAN 0x03 /* French (Canadian) */ +#define SUBLANG_FRENCH_SWISS 0x04 /* French (Swiss) */ +#define SUBLANG_FRENCH_LUXEMBOURG 0x05 /* French (Luxembourg) */ +#define SUBLANG_FRENCH_MONACO 0x06 /* French (Monaco) */ +#define SUBLANG_GERMAN 0x01 /* German */ +#define SUBLANG_GERMAN_SWISS 0x02 /* German (Swiss) */ +#define SUBLANG_GERMAN_AUSTRIAN 0x03 /* German (Austrian) */ +#define SUBLANG_GERMAN_LUXEMBOURG 0x04 /* German (Luxembourg) */ +#define SUBLANG_GERMAN_LIECHTENSTEIN 0x05 /* German (Liechtenstein) */ +#define SUBLANG_ITALIAN 0x01 /* Italian */ +#define SUBLANG_ITALIAN_SWISS 0x02 /* Italian (Swiss) */ +#define SUBLANG_KASHMIRI_INDIA 0x02 /* Kashmiri (India) */ +#define SUBLANG_KOREAN 0x01 /* Korean (Extended Wansung) */ +#define SUBLANG_LITHUANIAN 0x01 /* Lithuanian */ +#define SUBLANG_LITHUANIAN_CLASSIC 0x02 /* Lithuanian (Classic) */ +#define SUBLANG_MALAY_MALAYSIA 0x01 /* Malay (Malaysia) */ +#define SUBLANG_MALAY_BRUNEI_DARUSSALAM 0x02 /* Malay (Brunei Darussalam) */ +#define SUBLANG_NEPALI_INDIA 0x02 /* Nepali (India) */ +#define SUBLANG_NORWEGIAN_BOKMAL 0x01 /* Norwegian (Bokmal) */ +#define SUBLANG_NORWEGIAN_NYNORSK 0x02 /* Norwegian (Nynorsk) */ +#define SUBLANG_PORTUGUESE 0x02 /* Portuguese */ +#define SUBLANG_PORTUGUESE_BRAZILIAN 0x01 /* Portuguese (Brazilian) */ +#define SUBLANG_SERBIAN_LATIN 0x02 /* Serbian (Latin) */ +#define SUBLANG_SERBIAN_CYRILLIC 0x03 /* Serbian (Cyrillic) */ +#define SUBLANG_SPANISH 0x01 /* Spanish (Castilian) */ +#define SUBLANG_SPANISH_MEXICAN 0x02 /* Spanish (Mexican) */ +#define SUBLANG_SPANISH_MODERN 0x03 /* Spanish (Modern) */ +#define SUBLANG_SPANISH_GUATEMALA 0x04 /* Spanish (Guatemala) */ +#define SUBLANG_SPANISH_COSTA_RICA 0x05 /* Spanish (Costa Rica) */ +#define SUBLANG_SPANISH_PANAMA 0x06 /* Spanish (Panama) */ +#define SUBLANG_SPANISH_DOMINICAN_REPUBLIC 0x07 /* Spanish (Dom. Republic) */ +#define SUBLANG_SPANISH_VENEZUELA 0x08 /* Spanish (Venezuela) */ +#define SUBLANG_SPANISH_COLOMBIA 0x09 /* Spanish (Colombia) */ +#define SUBLANG_SPANISH_PERU 0x0a /* Spanish (Peru) */ +#define SUBLANG_SPANISH_ARGENTINA 0x0b /* Spanish (Argentina) */ +#define SUBLANG_SPANISH_ECUADOR 0x0c /* Spanish (Ecuador) */ +#define SUBLANG_SPANISH_CHILE 0x0d /* Spanish (Chile) */ +#define SUBLANG_SPANISH_URUGUAY 0x0e /* Spanish (Uruguay) */ +#define SUBLANG_SPANISH_PARAGUAY 0x0f /* Spanish (Paraguay) */ +#define SUBLANG_SPANISH_BOLIVIA 0x10 /* Spanish (Bolivia) */ +#define SUBLANG_SPANISH_EL_SALVADOR 0x11 /* Spanish (El Salvador) */ +#define SUBLANG_SPANISH_HONDURAS 0x12 /* Spanish (Honduras) */ +#define SUBLANG_SPANISH_NICARAGUA 0x13 /* Spanish (Nicaragua) */ +#define SUBLANG_SPANISH_PUERTO_RICO 0x14 /* Spanish (Puerto Rico) */ +#define SUBLANG_SWEDISH 0x01 /* Swedish */ +#define SUBLANG_SWEDISH_FINLAND 0x02 /* Swedish (Finland) */ +#define SUBLANG_URDU_PAKISTAN 0x01 /* Urdu (Pakistan) */ +#define SUBLANG_URDU_INDIA 0x02 /* Urdu (India) */ +#define SUBLANG_UZBEK_LATIN 0x01 /* Uzbek (Latin) */ +#define SUBLANG_UZBEK_CYRILLIC 0x02 /* Uzbek (Cyrillic) */ + +/* + * Sorting IDs. + */ +#define SORT_DEFAULT 0x0 /* sorting default */ + +#define SORT_JAPANESE_XJIS 0x0 /* Japanese XJIS order */ +#define SORT_JAPANESE_UNICODE 0x1 /* Japanese Unicode order */ + +#define SORT_CHINESE_BIG5 0x0 /* Chinese BIG5 order */ +#define SORT_CHINESE_PRCP 0x0 /* PRC Chinese Phonetic order */ +#define SORT_CHINESE_UNICODE 0x1 /* Chinese Unicode order */ +#define SORT_CHINESE_PRC 0x2 /* PRC Chinese Stroke Count */ + /* order */ +#define SORT_CHINESE_BOPOMOFO 0x3 /* Traditional Chinese */ + /* Bopomofo order */ + +#define SORT_KOREAN_KSC 0x0 /* Korean KSC order */ +#define SORT_KOREAN_UNICODE 0x1 /* Korean Unicode order */ + +#define SORT_GERMAN_PHONE_BOOK 0x1 /* German Phone Book order */ + +#define SORT_HUNGARIAN_DEFAULT 0x0 /* Hungarian Default order */ +#define SORT_HUNGARIAN_TECHNICAL 0x1 /* Hungarian Technical order */ + +#define SORT_GEORGIAN_TRADITIONAL 0x0 /* Georgian Traditional order */ +#define SORT_GEORGIAN_MODERN 0x1 /* Georgian Modern order */ + + +/* + * A locale ID is a 32 bit value which is the combination of a + * language ID, a sort ID, and a reserved area. The bits are + * allocated as follows: + * + * +-------------+---------+-------------------------+ + * | Reserved | Sort ID | Language ID | + * +-------------+---------+-------------------------+ + * 31 20 19 16 15 0 bit + * + * Locale ID creation/extraction macros: + * + * MAKELCID - construct the locale id from a language id + * and a sort id. + * MAKESORTLCID - construct the locale id from a language id, + * sort id, and sort version. + * LANGIDFROMLCID - extract the language id from a locale id. + * SORTIDFROMLCID - extract the sort id from a locale id. + * SORTVERSIONFROMLCID - extract the sort version from a locale id. + */ + +#define NLS_VALID_LOCALE_MASK 0x000fffff + +#define MAKELCID(lgid, srtid) \ + ((DWORD)((((DWORD)((WORD)(srtid))) << 16) | ((DWORD)((WORD)(lgid))))) + +#define MAKESORTLCID(lgid, srtid, ver) \ + ((DWORD)((MAKELCID(lgid, srtid)) | (((DWORD)((WORD)(ver))) << 20))) + +#define LANGIDFROMLCID(lcid) ((WORD)(lcid)) +#define SORTIDFROMLCID(lcid) ((WORD)((((DWORD)(lcid)) >> 16) & 0xf)) +#define SORTVERSIONFROMLCID(lcid) ((WORD)((((DWORD)(lcid)) >> 20) & 0xf)) + + +/* + * Default System and User IDs for language and locale. + */ +#define LANG_SYSTEM_DEFAULT MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT)) +#define LANG_USER_DEFAULT (MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)) + +#define LOCALE_SYSTEM_DEFAULT (MAKELCID(LANG_SYSTEM_DEFAULT, SORT_DEFAULT)) +#define LOCALE_USER_DEFAULT (MAKELCID(LANG_USER_DEFAULT, SORT_DEFAULT)) + +#define LOCALE_NEUTRAL \ + (MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), SORT_DEFAULT)) + + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_NTLOCALE_H */ diff --git a/usr/src/uts/common/smbsrv/ntsid.h b/usr/src/uts/common/smbsrv/ntsid.h new file mode 100644 index 000000000000..36974ba8fd54 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ntsid.h @@ -0,0 +1,304 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_NTSID_H +#define _SMBSRV_NTSID_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * NT Security Identifier (SID) interface definition. + */ + +/* + * some kernel include file /usr/include/... is + * overriding DWORD and causing conflicts + * will investigate further - to be removed + */ + +#ifdef DWORD +#undef DWORD +#define DWORD uint32_t +#endif + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Predefined global user RIDs. + */ +#define DOMAIN_USER_RID_ADMIN (0x000001F4L) /* 500 */ +#define DOMAIN_USER_RID_GUEST (0x000001F5L) /* 501 */ +#define DOMAIN_USER_RID_KRBTGT (0x000001F6L) /* 502 */ + +/* + * Predefined global group RIDs. + */ +#define DOMAIN_GROUP_RID_ADMINS (0x00000200L) /* 512 */ +#define DOMAIN_GROUP_RID_USERS (0x00000201L) +#define DOMAIN_GROUP_RID_GUESTS (0x00000202L) +#define DOMAIN_GROUP_RID_COMPUTERS (0x00000203L) +#define DOMAIN_GROUP_RID_CONTROLLERS (0x00000204L) +#define DOMAIN_GROUP_RID_CERT_ADMINS (0x00000205L) +#define DOMAIN_GROUP_RID_SCHEMA_ADMINS (0x00000206L) + + +/* + * Predefined local alias RIDs. + */ +#define DOMAIN_ALIAS_RID_ADMINS (0x00000220L) /* 544 */ +#define DOMAIN_ALIAS_RID_USERS (0x00000221L) +#define DOMAIN_ALIAS_RID_GUESTS (0x00000222L) +#define DOMAIN_ALIAS_RID_POWER_USERS (0x00000223L) +#define DOMAIN_ALIAS_RID_ACCOUNT_OPS (0x00000224L) +#define DOMAIN_ALIAS_RID_SYSTEM_OPS (0x00000225L) +#define DOMAIN_ALIAS_RID_PRINT_OPS (0x00000226L) +#define DOMAIN_ALIAS_RID_BACKUP_OPS (0x00000227L) +#define DOMAIN_ALIAS_RID_REPLICATOR (0x00000228L) + + +/* + * Universal and NT well-known SIDs + */ +#define NT_NULL_SIDSTR "S-1-0-0" +#define NT_WORLD_SIDSTR "S-1-1-0" +#define NT_LOCAL_SIDSTR "S-1-2-0" +#define NT_CREATOR_OWNER_ID_SIDSTR "S-1-3-0" +#define NT_CREATOR_GROUP_ID_SIDSTR "S-1-3-1" +#define NT_CREATOR_OWNER_SERVER_ID_SIDSTR "S-1-3-2" +#define NT_CREATOR_GROUP_SERVER_ID_SIDSTR "S-1-3-3" +#define NT_NON_UNIQUE_IDS_SIDSTR "S-1-4" +#define NT_AUTHORITY_SIDSTR "S-1-5" +#define NT_DIALUP_SIDSTR "S-1-5-1" +#define NT_NETWORK_SIDSTR "S-1-5-2" +#define NT_BATCH_SIDSTR "S-1-5-3" +#define NT_INTERACTIVE_SIDSTR "S-1-5-4" +#define NT_SERVICE_SIDSTR "S-1-5-6" +#define NT_ANONYMOUS_LOGON_SIDSTR "S-1-5-7" +#define NT_PROXY_SIDSTR "S-1-5-8" +#define NT_SERVER_LOGON_SIDSTR "S-1-5-9" +#define NT_SELF_SIDSTR "S-1-5-10" +#define NT_AUTHENTICATED_USER_SIDSTR "S-1-5-11" +#define NT_RESTRICTED_CODE_SIDSTR "S-1-5-12" +#define NT_LOCAL_SYSTEM_SIDSTR "S-1-5-18" +#define NT_NON_UNIQUE_SIDSTR "S-1-5-21" +#define NT_BUILTIN_DOMAIN_SIDSTR "S-1-5-32" + + +/* + * SID type indicators (SID_NAME_USE). + */ +#define SidTypeNull 0 +#define SidTypeUser 1 +#define SidTypeGroup 2 +#define SidTypeDomain 3 +#define SidTypeAlias 4 +#define SidTypeWellKnownGroup 5 +#define SidTypeDeletedAccount 6 +#define SidTypeInvalid 7 +#define SidTypeUnknown 8 +#define SidTypeComputer 9 + + +/* + * Identifier authorities for various domains. + */ +#define NT_SID_NULL_AUTH 0 +#define NT_SID_WORLD_AUTH 1 +#define NT_SID_LOCAL_AUTH 2 +#define NT_SID_CREATOR_AUTH 3 +#define NT_SID_NON_UNIQUE_AUTH 4 +#define NT_SID_NT_AUTH 5 + + +#define NT_SECURITY_NULL_AUTH {0, 0, 0, 0, 0, 0} +#define NT_SECURITY_WORLD_AUTH {0, 0, 0, 0, 0, 1} +#define NT_SECURITY_LOCAL_AUTH {0, 0, 0, 0, 0, 2} +#define NT_SECURITY_CREATOR_AUTH {0, 0, 0, 0, 0, 3} +#define NT_SECURITY_NON_UNIQUE_AUTH {0, 0, 0, 0, 0, 4} +#define NT_SECURITY_NT_AUTH {0, 0, 0, 0, 0, 5} +#define NT_SECURITY_UNIX_AUTH {0, 0, 0, 0, 0, 99} + + +#define SECURITY_NULL_RID (0x00000000L) +#define SECURITY_WORLD_RID (0x00000000L) +#define SECURITY_LOCAL_RID (0X00000000L) + +#define SECURITY_CREATOR_OWNER_RID (0x00000000L) +#define SECURITY_CREATOR_GROUP_RID (0x00000001L) +#define SECURITY_CREATOR_OWNER_SERVER_RID (0x00000002L) +#define SECURITY_CREATOR_GROUP_SERVER_RID (0x00000003L) + +#define SECURITY_DIALUP_RID (0x00000001L) +#define SECURITY_NETWORK_RID (0x00000002L) +#define SECURITY_BATCH_RID (0x00000003L) +#define SECURITY_INTERACTIVE_RID (0x00000004L) +#define SECURITY_LOGON_IDS_RID (0x00000005L) +#define SECURITY_LOGON_IDS_RID_COUNT (3L) +#define SECURITY_SERVICE_RID (0x00000006L) +#define SECURITY_ANONYMOUS_LOGON_RID (0x00000007L) +#define SECURITY_PROXY_RID (0x00000008L) +#define SECURITY_ENTERPRISE_CONTROLLERS_RID (0x00000009L) +#define SECURITY_SERVER_LOGON_RID SECURITY_ENTERPRISE_CONTROLLERS_RID +#define SECURITY_PRINCIPAL_SELF_RID (0x0000000AL) +#define SECURITY_AUTHENTICATED_USER_RID (0x0000000BL) +#define SECURITY_RESTRICTED_CODE_RID (0x0000000CL) + +#define SECURITY_LOCAL_SYSTEM_RID (0x00000012L) +#define SECURITY_NT_NON_UNIQUE (0x00000015L) +#define SECURITY_BUILTIN_DOMAIN_RID (0x00000020L) + + +#define NT_SID_NON_UNIQUE_SUBAUTH 21 + + +/* + * Common definition for a SID. + */ +#define NT_SID_REVISION 1 +#define NT_SID_AUTH_MAX 6 +#define NT_SID_SUBAUTH_MAX 15 + + +/* + * Security Identifier (SID) + * + * The security identifier (SID) uniquely identifies a user, group or + * a domain. It consists of a revision number, the identifier authority, + * and a list of sub-authorities. The revision number is currently 1. + * The identifier authority identifies which system issued the SID. The + * sub-authorities of a domain SID uniquely identify a domain. A user + * or group SID consists of a domain SID with the user or group id + * appended. The user or group id (also known as a relative id (RID) + * uniquely identifies a user within a domain. A user or group SID + * uniquely identifies a user or group across all domains. The SidType + * values identify the various types of SID. + * + * 1 1 1 1 1 1 + * 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +---------------------------------------------------------------+ + * | SubAuthorityCount |Reserved1 (SBZ)| Revision | + * +---------------------------------------------------------------+ + * | IdentifierAuthority[0] | + * +---------------------------------------------------------------+ + * | IdentifierAuthority[1] | + * +---------------------------------------------------------------+ + * | IdentifierAuthority[2] | + * +---------------------------------------------------------------+ + * | | + * +- - - - - - - - SubAuthority[] - - - - - - - - -+ + * | | + * +---------------------------------------------------------------+ + * + */ +/* + * Note: NT defines the Identifier Authority as a separate + * structure (SID_IDENTIFIER_AUTHORITY) containing a literal + * definition of a 6 byte vector but the effect is the same + * as defining it as a member value. + */ +typedef struct nt_sid { + BYTE Revision; + BYTE SubAuthCount; + BYTE Authority[NT_SID_AUTH_MAX]; + DWORD SubAuthority[ANY_SIZE_ARRAY]; +} nt_sid_t; + +/* + * The structure for entries in a static table of well known + * SIDs. The table definition is in os/libnt/ntbuitin.c + * The domain_ix field is an index into a predefined domain + * list in os/libnt/ntbuitin.c + */ +typedef struct well_known_account { + WORD sid_name_use; + WORD domain_ix; /* index to a predefine domain list */ + char *sid; + char *name; + WORD flags; + char *desc; + nt_sid_t *binsid; +} well_known_account_t; + +/* + * flags for local group table entry + * + * LGF_HIDDEN this entry won't be represented to users + * via builtin group management interface + */ +#define LGF_HIDDEN 0x1 + + +/* + * The maximum size of the SID format buffer. + */ +#define NT_SID_FMTBUF_SIZE 256 + + +int nt_sid_is_valid(nt_sid_t *sid); +int nt_sid_length(nt_sid_t *sid); +nt_sid_t *nt_sid_dup(nt_sid_t *sid); +nt_sid_t *nt_sid_splice(nt_sid_t *domain_sid, DWORD rid); +int nt_sid_get_rid(nt_sid_t *sid, DWORD *rid); +int nt_sid_split(nt_sid_t *sid, DWORD *rid); +nt_sid_t *nt_sid_gen_null_sid(void); +int nt_sid_domain_equal(nt_sid_t *domain_sid, nt_sid_t *sid); +int nt_sid_is_equal(nt_sid_t *sid1, nt_sid_t *sid2); +int nt_sid_is_local(nt_sid_t *sid); +int nt_sid_is_builtin(nt_sid_t *sid); +int nt_sid_is_domain_equal(nt_sid_t *sid1, nt_sid_t *sid2); +int nt_sid_is_indomain(nt_sid_t *domain_sid, nt_sid_t *sid); +void nt_sid_logf(nt_sid_t *sid); +char *nt_sid_format(nt_sid_t *sid); +void nt_sid_format2(nt_sid_t *sid, char *fmtbuf); +nt_sid_t *nt_sid_strtosid(char *sidstr); +char *nt_sid_name_use(unsigned int snu_id); +int nt_sid_copy(nt_sid_t *dessid, nt_sid_t *srcsid, unsigned buflen); + + +/* + * SID/name translation service for NT BUILTIN SIDs. + */ +int nt_builtin_init(void); +void nt_builtin_fini(void); +well_known_account_t *nt_builtin_lookup(char *name); +char *nt_builtin_lookup_sid(nt_sid_t *sid, WORD *sid_name_use); +nt_sid_t *nt_builtin_lookup_name(char *name, WORD *sid_name_use); +char *nt_builtin_lookup_domain(char *name); +int nt_builtin_is_wellknown(char *name); +well_known_account_t *nt_builtin_findfirst(DWORD *iterator); +well_known_account_t *nt_builtin_findnext(DWORD *iterator); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _SMBSRV_NTSID_H */ diff --git a/usr/src/uts/common/smbsrv/ntstatus.h b/usr/src/uts/common/smbsrv/ntstatus.h new file mode 100644 index 000000000000..1388dcfcc236 --- /dev/null +++ b/usr/src/uts/common/smbsrv/ntstatus.h @@ -0,0 +1,595 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_NTSTATUS_H +#define _SMBSRV_NTSTATUS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file defines the list of NT status codes. + * + * Be careful not to confuse status codes with error + * codes. The error codes are listed in nterror.h. + */ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Status codes are 32-bit values in which the top 2 bits represent + * the severity and the lower 16-bits contain a Win32 status code. + * The severity levels are: + * + * 00 success + * 01 information + * 10 warning + * 11 error + * + * NT also defines an application mask, which is included here + * for completeness. + */ +#define APPLICATION_ERROR_MASK 0x20000000 +#define ERROR_SEVERITY_SUCCESS 0x00000000 +#define ERROR_SEVERITY_INFORMATIONAL 0x40000000 +#define ERROR_SEVERITY_WARNING 0x80000000 +#define ERROR_SEVERITY_ERROR 0xC0000000 + +/* + * Severity code helper macros. + */ +#define NT_SC_SUCCESS(STATUS) (ERROR_SEVERITY_SUCCESS+(STATUS)) +#define NT_SC_INFO(STATUS) (ERROR_SEVERITY_INFORMATIONAL+(STATUS)) +#define NT_SC_WARNING(STATUS) (ERROR_SEVERITY_WARNING+(STATUS)) +#define NT_SC_ERROR(STATUS) (ERROR_SEVERITY_ERROR+(STATUS)) + +#define NT_SC_IS_SUCCESS(S) (((S) & 0xC0000000) == ERROR_SEVERITY_SUCCESS) +#define NT_SC_IS_INFO(S) (((S) & 0xC0000000) == ERROR_SEVERITY_INFORMATIONAL) +#define NT_SC_IS_WARNING(S) (((S) & 0xC0000000) == ERROR_SEVERITY_WARNING) +#define NT_SC_IS_ERROR(S) (((S) & 0xC0000000) == ERROR_SEVERITY_ERROR) + +#define NT_SC_VALUE(S) ((S) & 0x0FFFFFFF) + +/* + * Win32 status codes + */ +#define NT_STATUS_SUCCESS 0 +#define NT_STATUS_UNSUCCESSFUL 1 +#define NT_STATUS_NOT_IMPLEMENTED 2 +#define NT_STATUS_INVALID_INFO_CLASS 3 +#define NT_STATUS_INFO_LENGTH_MISMATCH 4 +#define NT_STATUS_BUFFER_OVERFLOW 5 +/* NT_STATUS_IN_PAGE_ERROR */ +#define NT_STATUS_NO_MORE_FILES 6 +#define NT_STATUS_PAGEFILE_QUOTA 7 +#define NT_STATUS_INVALID_HANDLE 8 +#define NT_STATUS_BAD_INITIAL_STACK 9 +#define NT_STATUS_BAD_INITIAL_PC 10 +#define NT_STATUS_INVALID_CID 11 +#define NT_STATUS_TIMER_NOT_CANCELED 12 +#define NT_STATUS_INVALID_PARAMETER 13 +#define NT_STATUS_NO_SUCH_DEVICE 14 +#define NT_STATUS_NO_SUCH_FILE 15 +#define NT_STATUS_INVALID_DEVICE_REQUEST 16 +#define NT_STATUS_END_OF_FILE 17 +#define NT_STATUS_WRONG_VOLUME 18 +#define NT_STATUS_NO_MEDIA_IN_DEVICE 19 +#define NT_STATUS_UNRECOGNIZED_MEDIA 20 +#define NT_STATUS_NONEXISTENT_SECTOR 21 +#define NT_STATUS_MORE_PROCESSING_REQUIRED 22 +#define NT_STATUS_NO_MEMORY 23 +#define NT_STATUS_CONFLICTING_ADDRESSES 24 +#define NT_STATUS_NOT_MAPPED_VIEW 25 +#define NT_STATUS_UNABLE_TO_FREE_VM 26 +#define NT_STATUS_UNABLE_TO_DELETE_SECTION 27 +#define NT_STATUS_INVALID_SYSTEM_SERVICE 28 +#define NT_STATUS_ILLEGAL_INSTRUCTION 29 +#define NT_STATUS_INVALID_LOCK_SEQUENCE 30 +#define NT_STATUS_INVALID_VIEW_SIZE 31 +#define NT_STATUS_INVALID_FILE_FOR_SECTION 32 +#define NT_STATUS_ALREADY_COMMITTED 33 +#define NT_STATUS_ACCESS_DENIED 34 +#define NT_STATUS_BUFFER_TOO_SMALL 35 +#define NT_STATUS_OBJECT_TYPE_MISMATCH 36 +#define NT_STATUS_NONCONTINUABLE_EXCEPTION 37 +#define NT_STATUS_INVALID_DISPOSITION 38 +#define NT_STATUS_UNWIND 39 +#define NT_STATUS_BAD_STACK 40 +#define NT_STATUS_INVALID_UNWIND_TARGET 41 +#define NT_STATUS_NOT_LOCKED 42 +#define NT_STATUS_PARITY_ERROR 43 +#define NT_STATUS_UNABLE_TO_DECOMMIT_VM 44 +#define NT_STATUS_NOT_COMMITTED 45 +#define NT_STATUS_INVALID_PORT_ATTRIBUTES 46 +#define NT_STATUS_PORT_MESSAGE_TOO_LONG 47 +#define NT_STATUS_INVALID_PARAMETER_MIX 48 +#define NT_STATUS_INVALID_QUOTA_LOWER 49 +#define NT_STATUS_DISK_CORRUPT_ERROR 50 +#define NT_STATUS_OBJECT_NAME_INVALID 51 +#define NT_STATUS_OBJECT_NAME_NOT_FOUND 52 +#define NT_STATUS_OBJECT_NAME_COLLISION 53 +#define NT_STATUS_HANDLE_NOT_WAITABLE 54 +#define NT_STATUS_PORT_DISCONNECTED 55 +#define NT_STATUS_DEVICE_ALREADY_ATTACHED 56 +#define NT_STATUS_OBJECT_PATH_INVALID 57 +#define NT_STATUS_OBJECT_PATH_NOT_FOUND 58 +#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD 59 +#define NT_STATUS_DATA_OVERRUN 60 +#define NT_STATUS_DATA_LATE_ERROR 61 +#define NT_STATUS_DATA_ERROR 62 +#define NT_STATUS_CRC_ERROR 63 +#define NT_STATUS_SECTION_TOO_BIG 64 +#define NT_STATUS_PORT_CONNECTION_REFUSED 65 +#define NT_STATUS_INVALID_PORT_HANDLE 66 +#define NT_STATUS_SHARING_VIOLATION 67 +#define NT_STATUS_QUOTA_EXCEEDED 68 +#define NT_STATUS_INVALID_PAGE_PROTECTION 69 +#define NT_STATUS_MUTANT_NOT_OWNED 70 +#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED 71 +#define NT_STATUS_PORT_ALREADY_SET 72 +#define NT_STATUS_SECTION_NOT_IMAGE 73 +#define NT_STATUS_SUSPEND_COUNT_EXCEEDED 74 +#define NT_STATUS_THREAD_IS_TERMINATING 75 +#define NT_STATUS_BAD_WORKING_SET_LIMIT 76 +#define NT_STATUS_INCOMPATIBLE_FILE_MAP 77 +#define NT_STATUS_SECTION_PROTECTION 78 +#define NT_STATUS_EAS_NOT_SUPPORTED 79 +#define NT_STATUS_EA_TOO_LARGE 80 +#define NT_STATUS_NONEXISTENT_EA_ENTRY 81 +#define NT_STATUS_NO_EAS_ON_FILE 82 +#define NT_STATUS_EA_CORRUPT_ERROR 83 +#define NT_STATUS_FILE_LOCK_CONFLICT 84 +#define NT_STATUS_LOCK_NOT_GRANTED 85 +#define NT_STATUS_DELETE_PENDING 86 +#define NT_STATUS_CTL_FILE_NOT_SUPPORTED 87 +#define NT_STATUS_UNKNOWN_REVISION 88 +#define NT_STATUS_REVISION_MISMATCH 89 +#define NT_STATUS_INVALID_OWNER 90 +#define NT_STATUS_INVALID_PRIMARY_GROUP 91 +#define NT_STATUS_NO_IMPERSONATION_TOKEN 92 +#define NT_STATUS_CANT_DISABLE_MANDATORY 93 +#define NT_STATUS_NO_LOGON_SERVERS 94 +#define NT_STATUS_NO_SUCH_LOGON_SESSION 95 +#define NT_STATUS_NO_SUCH_PRIVILEGE 96 +#define NT_STATUS_PRIVILEGE_NOT_HELD 97 +#define NT_STATUS_INVALID_ACCOUNT_NAME 98 +#define NT_STATUS_USER_EXISTS 99 +#define NT_STATUS_NO_SUCH_USER 100 +#define NT_STATUS_GROUP_EXISTS 101 +#define NT_STATUS_NO_SUCH_GROUP 102 +#define NT_STATUS_MEMBER_IN_GROUP 103 +#define NT_STATUS_MEMBER_NOT_IN_GROUP 104 +#define NT_STATUS_LAST_ADMIN 105 +#define NT_STATUS_WRONG_PASSWORD 106 +#define NT_STATUS_ILL_FORMED_PASSWORD 107 +#define NT_STATUS_PASSWORD_RESTRICTION 108 +#define NT_STATUS_LOGON_FAILURE 109 +#define NT_STATUS_ACCOUNT_RESTRICTION 110 +#define NT_STATUS_INVALID_LOGON_HOURS 111 +#define NT_STATUS_INVALID_WORKSTATION 112 +#define NT_STATUS_PASSWORD_EXPIRED 113 +#define NT_STATUS_ACCOUNT_DISABLED 114 +#define NT_STATUS_NONE_MAPPED 115 +#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED 116 +#define NT_STATUS_LUIDS_EXHAUSTED 117 +#define NT_STATUS_INVALID_SUB_AUTHORITY 118 +#define NT_STATUS_INVALID_ACL 119 +#define NT_STATUS_INVALID_SID 120 +#define NT_STATUS_INVALID_SECURITY_DESCR 121 +#define NT_STATUS_PROCEDURE_NOT_FOUND 122 +#define NT_STATUS_INVALID_IMAGE_FORMAT 123 +#define NT_STATUS_NO_TOKEN 124 +#define NT_STATUS_BAD_INHERITANCE_ACL 125 +#define NT_STATUS_RANGE_NOT_LOCKED 126 +#define NT_STATUS_DISK_FULL 127 +#define NT_STATUS_SERVER_DISABLED 128 +#define NT_STATUS_SERVER_NOT_DISABLED 129 +#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED 130 +#define NT_STATUS_GUIDS_EXHAUSTED 131 +#define NT_STATUS_INVALID_ID_AUTHORITY 132 +#define NT_STATUS_AGENTS_EXHAUSTED 133 +#define NT_STATUS_INVALID_VOLUME_LABEL 134 +#define NT_STATUS_SECTION_NOT_EXTENDED 135 +#define NT_STATUS_NOT_MAPPED_DATA 136 +#define NT_STATUS_RESOURCE_DATA_NOT_FOUND 137 +#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND 138 +#define NT_STATUS_RESOURCE_NAME_NOT_FOUND 139 +#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED 140 +#define NT_STATUS_FLOAT_DENORMAL_OPERAND 141 +#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO 142 +#define NT_STATUS_FLOAT_INEXACT_RESULT 143 +#define NT_STATUS_FLOAT_INVALID_OPERATION 144 +#define NT_STATUS_FLOAT_OVERFLOW 145 +#define NT_STATUS_FLOAT_STACK_CHECK 146 +#define NT_STATUS_FLOAT_UNDERFLOW 147 +#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO 148 +#define NT_STATUS_INTEGER_OVERFLOW 149 +#define NT_STATUS_PRIVILEGED_INSTRUCTION 150 +#define NT_STATUS_TOO_MANY_PAGING_FILES 151 +#define NT_STATUS_FILE_INVALID 152 +#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED 153 +#define NT_STATUS_INSUFFICIENT_RESOURCES 154 +#define NT_STATUS_DFS_EXIT_PATH_FOUND 155 +#define NT_STATUS_DEVICE_DATA_ERROR 156 +#define NT_STATUS_DEVICE_NOT_CONNECTED 157 +#define NT_STATUS_DEVICE_POWER_FAILURE 158 +#define NT_STATUS_FREE_VM_NOT_AT_BASE 159 +#define NT_STATUS_MEMORY_NOT_ALLOCATED 160 +#define NT_STATUS_WORKING_SET_QUOTA 161 +#define NT_STATUS_MEDIA_WRITE_PROTECTED 162 +#define NT_STATUS_DEVICE_NOT_READY 163 +#define NT_STATUS_INVALID_GROUP_ATTRIBUTES 164 +#define NT_STATUS_BAD_IMPERSONATION_LEVEL 165 +#define NT_STATUS_CANT_OPEN_ANONYMOUS 166 +#define NT_STATUS_BAD_VALIDATION_CLASS 167 +#define NT_STATUS_BAD_TOKEN_TYPE 168 +#define NT_STATUS_BAD_MASTER_BOOT_RECORD 169 +#define NT_STATUS_INSTRUCTION_MISALIGNMENT 170 +#define NT_STATUS_INSTANCE_NOT_AVAILABLE 171 +#define NT_STATUS_PIPE_NOT_AVAILABLE 172 +#define NT_STATUS_INVALID_PIPE_STATE 173 +#define NT_STATUS_PIPE_BUSY 174 +#define NT_STATUS_ILLEGAL_FUNCTION 175 +#define NT_STATUS_PIPE_DISCONNECTED 176 +#define NT_STATUS_PIPE_CLOSING 177 +#define NT_STATUS_PIPE_CONNECTED 178 +#define NT_STATUS_PIPE_LISTENING 179 +#define NT_STATUS_INVALID_READ_MODE 180 +#define NT_STATUS_IO_TIMEOUT 181 +#define NT_STATUS_FILE_FORCED_CLOSED 182 +#define NT_STATUS_PROFILING_NOT_STARTED 183 +#define NT_STATUS_PROFILING_NOT_STOPPED 184 +#define NT_STATUS_COULD_NOT_INTERPRET 185 +#define NT_STATUS_FILE_IS_A_DIRECTORY 186 +#define NT_STATUS_NOT_SUPPORTED 187 +#define NT_STATUS_REMOTE_NOT_LISTENING 188 +#define NT_STATUS_DUPLICATE_NAME 189 +#define NT_STATUS_BAD_NETWORK_PATH 190 +#define NT_STATUS_NETWORK_BUSY 191 +#define NT_STATUS_DEVICE_DOES_NOT_EXIST 192 +#define NT_STATUS_TOO_MANY_COMMANDS 193 +#define NT_STATUS_ADAPTER_HARDWARE_ERROR 194 +#define NT_STATUS_INVALID_NETWORK_RESPONSE 195 +#define NT_STATUS_UNEXPECTED_NETWORK_ERROR 196 +#define NT_STATUS_BAD_REMOTE_ADAPTER 197 +#define NT_STATUS_PRINT_QUEUE_FULL 198 +#define NT_STATUS_NO_SPOOL_SPACE 199 +#define NT_STATUS_PRINT_CANCELLED 200 +#define NT_STATUS_NETWORK_NAME_DELETED 201 +#define NT_STATUS_NETWORK_ACCESS_DENIED 202 +#define NT_STATUS_BAD_DEVICE_TYPE 203 +#define NT_STATUS_BAD_NETWORK_NAME 204 +#define NT_STATUS_TOO_MANY_NAMES 205 +#define NT_STATUS_TOO_MANY_SESSIONS 206 +#define NT_STATUS_SHARING_PAUSED 207 +#define NT_STATUS_REQUEST_NOT_ACCEPTED 208 +#define NT_STATUS_REDIRECTOR_PAUSED 209 +#define NT_STATUS_NET_WRITE_FAULT 210 +#define NT_STATUS_PROFILING_AT_LIMIT 211 +#define NT_STATUS_NOT_SAME_DEVICE 212 +#define NT_STATUS_FILE_RENAMED 213 +#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED 214 +#define NT_STATUS_NO_SECURITY_ON_OBJECT 215 +#define NT_STATUS_CANT_WAIT 216 +#define NT_STATUS_PIPE_EMPTY 217 +#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO 218 +#define NT_STATUS_CANT_TERMINATE_SELF 219 +#define NT_STATUS_INVALID_SERVER_STATE 220 +#define NT_STATUS_INVALID_DOMAIN_STATE 221 +#define NT_STATUS_INVALID_DOMAIN_ROLE 222 +#define NT_STATUS_NO_SUCH_DOMAIN 223 +#define NT_STATUS_DOMAIN_EXISTS 224 +#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED 225 +#define NT_STATUS_OPLOCK_NOT_GRANTED 226 +#define NT_STATUS_INVALID_OPLOCK_PROTOCOL 227 +#define NT_STATUS_INTERNAL_DB_CORRUPTION 228 +#define NT_STATUS_INTERNAL_ERROR 229 +#define NT_STATUS_GENERIC_NOT_MAPPED 230 +#define NT_STATUS_BAD_DESCRIPTOR_FORMAT 231 +#define NT_STATUS_INVALID_USER_BUFFER 232 +#define NT_STATUS_UNEXPECTED_IO_ERROR 233 +#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR 234 +#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR 235 +#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR 236 +#define NT_STATUS_NOT_LOGON_PROCESS 237 +#define NT_STATUS_LOGON_SESSION_EXISTS 238 +#define NT_STATUS_INVALID_PARAMETER_1 239 +#define NT_STATUS_INVALID_PARAMETER_2 240 +#define NT_STATUS_INVALID_PARAMETER_3 241 +#define NT_STATUS_INVALID_PARAMETER_4 242 +#define NT_STATUS_INVALID_PARAMETER_5 243 +#define NT_STATUS_INVALID_PARAMETER_6 244 +#define NT_STATUS_INVALID_PARAMETER_7 245 +#define NT_STATUS_INVALID_PARAMETER_8 246 +#define NT_STATUS_INVALID_PARAMETER_9 247 +#define NT_STATUS_INVALID_PARAMETER_10 248 +#define NT_STATUS_INVALID_PARAMETER_11 249 +#define NT_STATUS_INVALID_PARAMETER_12 250 +#define NT_STATUS_REDIRECTOR_NOT_STARTED 251 +#define NT_STATUS_REDIRECTOR_STARTED 252 +#define NT_STATUS_STACK_OVERFLOW 253 +#define NT_STATUS_NO_SUCH_PACKAGE 254 +#define NT_STATUS_BAD_FUNCTION_TABLE 255 +#define NT_STATUS_DIRECTORY_NOT_EMPTY 257 +#define NT_STATUS_FILE_CORRUPT_ERROR 258 +#define NT_STATUS_NOT_A_DIRECTORY 259 +#define NT_STATUS_BAD_LOGON_SESSION_STATE 260 +#define NT_STATUS_LOGON_SESSION_COLLISION 261 +#define NT_STATUS_NAME_TOO_LONG 262 +#define NT_STATUS_FILES_OPEN 263 +#define NT_STATUS_CONNECTION_IN_USE 264 +#define NT_STATUS_MESSAGE_NOT_FOUND 265 +#define NT_STATUS_PROCESS_IS_TERMINATING 266 +#define NT_STATUS_INVALID_LOGON_TYPE 267 +#define NT_STATUS_NO_GUID_TRANSLATION 268 +#define NT_STATUS_CANNOT_IMPERSONATE 269 +#define NT_STATUS_IMAGE_ALREADY_LOADED 270 +#define NT_STATUS_ABIOS_NOT_PRESENT 271 +#define NT_STATUS_ABIOS_LID_NOT_EXIST 272 +#define NT_STATUS_ABIOS_LID_ALREADY_OWNED 273 +#define NT_STATUS_ABIOS_NOT_LID_OWNER 274 +#define NT_STATUS_ABIOS_INVALID_COMMAND 275 +#define NT_STATUS_ABIOS_INVALID_LID 276 +#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE 277 +#define NT_STATUS_ABIOS_INVALID_SELECTOR 278 +#define NT_STATUS_NO_LDT 279 +#define NT_STATUS_INVALID_LDT_SIZE 280 +#define NT_STATUS_INVALID_LDT_OFFSET 281 +#define NT_STATUS_INVALID_LDT_DESCRIPTOR 282 +#define NT_STATUS_INVALID_IMAGE_NE_FORMAT 283 +#define NT_STATUS_RXACT_INVALID_STATE 284 +#define NT_STATUS_RXACT_COMMIT_FAILURE 285 +#define NT_STATUS_MAPPED_FILE_SIZE_ZERO 286 +#define NT_STATUS_TOO_MANY_OPENED_FILES 287 +#define NT_STATUS_CANCELLED 288 +#define NT_STATUS_CANNOT_DELETE 289 +#define NT_STATUS_INVALID_COMPUTER_NAME 290 +#define NT_STATUS_FILE_DELETED 291 +#define NT_STATUS_SPECIAL_ACCOUNT 292 +#define NT_STATUS_SPECIAL_GROUP 293 +#define NT_STATUS_SPECIAL_USER 294 +#define NT_STATUS_MEMBERS_PRIMARY_GROUP 295 +#define NT_STATUS_FILE_CLOSED 296 +#define NT_STATUS_TOO_MANY_THREADS 297 +#define NT_STATUS_THREAD_NOT_IN_PROCESS 298 +#define NT_STATUS_TOKEN_ALREADY_IN_USE 299 +#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED 300 +#define NT_STATUS_COMMITMENT_LIMIT 301 +#define NT_STATUS_INVALID_IMAGE_LE_FORMAT 302 +#define NT_STATUS_INVALID_IMAGE_NOT_MZ 303 +#define NT_STATUS_INVALID_IMAGE_PROTECT 304 +#define NT_STATUS_INVALID_IMAGE_WIN_16 305 +#define NT_STATUS_LOGON_SERVER_CONFLICT 306 +#define NT_STATUS_TIME_DIFFERENCE_AT_DC 307 +#define NT_STATUS_SYNCHRONIZATION_REQUIRED 308 +#define NT_STATUS_DLL_NOT_FOUND 309 +#define NT_STATUS_OPEN_FAILED 310 +#define NT_STATUS_IO_PRIVILEGE_FAILED 311 +#define NT_STATUS_ORDINAL_NOT_FOUND 312 +#define NT_STATUS_ENTRYPOINT_NOT_FOUND 313 +#define NT_STATUS_CONTROL_C_EXIT 314 +#define NT_STATUS_LOCAL_DISCONNECT 315 +#define NT_STATUS_REMOTE_DISCONNECT 316 +#define NT_STATUS_REMOTE_RESOURCES 317 +#define NT_STATUS_LINK_FAILED 318 +#define NT_STATUS_LINK_TIMEOUT 319 +#define NT_STATUS_INVALID_CONNECTION 320 +#define NT_STATUS_INVALID_ADDRESS 321 +#define NT_STATUS_DLL_INIT_FAILED 322 +#define NT_STATUS_MISSING_SYSTEMFILE 323 +#define NT_STATUS_UNHANDLED_EXCEPTION 324 +#define NT_STATUS_APP_INIT_FAILURE 325 +#define NT_STATUS_PAGEFILE_CREATE_FAILED 326 +#define NT_STATUS_NO_PAGEFILE 327 +#define NT_STATUS_INVALID_LEVEL 328 +#define NT_STATUS_WRONG_PASSWORD_CORE 329 +#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT 330 +#define NT_STATUS_PIPE_BROKEN 331 +#define NT_STATUS_REGISTRY_CORRUPT 332 +#define NT_STATUS_REGISTRY_IO_FAILED 333 +#define NT_STATUS_NO_EVENT_PAIR 334 +#define NT_STATUS_UNRECOGNIZED_VOLUME 335 +#define NT_STATUS_SERIAL_NO_DEVICE_INITED 336 +#define NT_STATUS_NO_SUCH_ALIAS 337 +#define NT_STATUS_MEMBER_NOT_IN_ALIAS 338 +#define NT_STATUS_MEMBER_IN_ALIAS 339 +#define NT_STATUS_ALIAS_EXISTS 340 +#define NT_STATUS_LOGON_NOT_GRANTED 341 +#define NT_STATUS_TOO_MANY_SECRETS 342 +#define NT_STATUS_SECRET_TOO_LONG 343 +#define NT_STATUS_INTERNAL_DB_ERROR 344 +#define NT_STATUS_FULLSCREEN_MODE 345 +#define NT_STATUS_TOO_MANY_CONTEXT_IDS 346 +#define NT_STATUS_LOGON_TYPE_NOT_GRANTED 347 +#define NT_STATUS_NOT_REGISTRY_FILE 348 +#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED 349 +#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR 350 +#define NT_STATUS_FT_MISSING_MEMBER 351 +#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY 352 +#define NT_STATUS_ILLEGAL_CHARACTER 353 +#define NT_STATUS_UNMAPPABLE_CHARACTER 354 +#define NT_STATUS_UNDEFINED_CHARACTER 355 +#define NT_STATUS_FLOPPY_VOLUME 356 +#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND 357 +#define NT_STATUS_FLOPPY_WRONG_CYLINDER 358 +#define NT_STATUS_FLOPPY_UNKNOWN_ERROR 359 +#define NT_STATUS_FLOPPY_BAD_REGISTERS 360 +#define NT_STATUS_DISK_RECALIBRATE_FAILED 361 +#define NT_STATUS_DISK_OPERATION_FAILED 362 +#define NT_STATUS_DISK_RESET_FAILED 363 +#define NT_STATUS_SHARED_IRQ_BUSY 364 +#define NT_STATUS_FT_ORPHANING 365 +#define NT_STATUS_PARTITION_FAILURE 370 +#define NT_STATUS_INVALID_BLOCK_LENGTH 371 +#define NT_STATUS_DEVICE_NOT_PARTITIONED 372 +#define NT_STATUS_UNABLE_TO_LOCK_MEDIA 373 +#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA 374 +#define NT_STATUS_EOM_OVERFLOW 375 +#define NT_STATUS_NO_MEDIA 376 +#define NT_STATUS_NO_SUCH_MEMBER 378 +#define NT_STATUS_INVALID_MEMBER 379 +#define NT_STATUS_KEY_DELETED 380 +#define NT_STATUS_NO_LOG_SPACE 381 +#define NT_STATUS_TOO_MANY_SIDS 382 +#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED 383 +#define NT_STATUS_KEY_HAS_CHILDREN 384 +#define NT_STATUS_CHILD_MUST_BE_VOLATILE 385 +#define NT_STATUS_DEVICE_CONFIGURATION_ERROR 386 +#define NT_STATUS_DRIVER_INTERNAL_ERROR 387 +#define NT_STATUS_INVALID_DEVICE_STATE 388 +#define NT_STATUS_IO_DEVICE_ERROR 389 +#define NT_STATUS_DEVICE_PROTOCOL_ERROR 390 +#define NT_STATUS_BACKUP_CONTROLLER 391 +#define NT_STATUS_LOG_FILE_FULL 392 +#define NT_STATUS_TOO_LATE 393 +#define NT_STATUS_NO_TRUST_LSA_SECRET 394 +#define NT_STATUS_NO_TRUST_SAM_ACCOUNT 395 +#define NT_STATUS_TRUSTED_DOMAIN_FAILURE 396 +#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE 397 +#define NT_STATUS_EVENTLOG_FILE_CORRUPT 398 +#define NT_STATUS_EVENTLOG_CANT_START 399 +#define NT_STATUS_TRUST_FAILURE 400 +#define NT_STATUS_MUTANT_LIMIT_EXCEEDED 401 +#define NT_STATUS_NETLOGON_NOT_STARTED 402 +#define NT_STATUS_ACCOUNT_EXPIRED 403 +#define NT_STATUS_POSSIBLE_DEADLOCK 404 +#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT 405 +#define NT_STATUS_REMOTE_SESSION_LIMIT 406 +#define NT_STATUS_EVENTLOG_FILE_CHANGED 407 +#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT 408 +#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT 409 +#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT 410 +#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT 411 +#define NT_STATUS_FS_DRIVER_REQUIRED 412 +#define NT_STATUS_NO_USER_SESSION_KEY 514 +#define NT_STATUS_USER_SESSION_DELETED 515 +#define NT_STATUS_RESOURCE_LANG_NOT_FOUND 516 +#define NT_STATUS_INSUFF_SERVER_RESOURCES 517 +#define NT_STATUS_INVALID_BUFFER_SIZE 518 +#define NT_STATUS_INVALID_ADDRESS_COMPONENT 519 +#define NT_STATUS_INVALID_ADDRESS_WILDCARD 520 +#define NT_STATUS_TOO_MANY_ADDRESSES 521 +#define NT_STATUS_ADDRESS_ALREADY_EXISTS 522 +#define NT_STATUS_ADDRESS_CLOSED 523 +#define NT_STATUS_CONNECTION_DISCONNECTED 524 +#define NT_STATUS_CONNECTION_RESET 525 +#define NT_STATUS_TOO_MANY_NODES 526 +#define NT_STATUS_TRANSACTION_ABORTED 527 +#define NT_STATUS_TRANSACTION_TIMED_OUT 528 +#define NT_STATUS_TRANSACTION_NO_RELEASE 529 +#define NT_STATUS_TRANSACTION_NO_MATCH 530 +#define NT_STATUS_TRANSACTION_RESPONDED 531 +#define NT_STATUS_TRANSACTION_INVALID_ID 532 +#define NT_STATUS_TRANSACTION_INVALID_TYPE 533 +#define NT_STATUS_NOT_SERVER_SESSION 534 +#define NT_STATUS_NOT_CLIENT_SESSION 535 +#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE 536 +#define NT_STATUS_DEBUG_ATTACH_FAILED 537 +#define NT_STATUS_SYSTEM_PROCESS_TERMINATED 538 +#define NT_STATUS_DATA_NOT_ACCEPTED 539 +#define NT_STATUS_NO_BROWSER_SERVERS_FOUND 540 +#define NT_STATUS_VDM_HARD_ERROR 541 +#define NT_STATUS_DRIVER_CANCEL_TIMEOUT 542 +#define NT_STATUS_REPLY_MESSAGE_MISMATCH 543 +#define NT_STATUS_MAPPED_ALIGNMENT 544 +#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH 545 +#define NT_STATUS_LOST_WRITEBEHIND_DATA 546 +#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID 547 +#define NT_STATUS_PASSWORD_MUST_CHANGE 548 +#define NT_STATUS_NOT_FOUND 549 +#define NT_STATUS_NOT_TINY_STREAM 550 +#define NT_STATUS_RECOVERY_FAILURE 551 +#define NT_STATUS_STACK_OVERFLOW_READ 552 +#define NT_STATUS_FAIL_CHECK 553 +#define NT_STATUS_DUPLICATE_OBJECTID 554 +#define NT_STATUS_OBJECTID_EXISTS 555 +#define NT_STATUS_CONVERT_TO_LARGE 556 +#define NT_STATUS_RETRY 557 +#define NT_STATUS_FOUND_OUT_OF_SCOPE 558 +#define NT_STATUS_ALLOCATE_BUCKET 559 +#define NT_STATUS_PROPSET_NOT_FOUND 560 +#define NT_STATUS_MARSHALL_OVERFLOW 561 +#define NT_STATUS_INVALID_VARIANT 562 +#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND 563 +#define NT_STATUS_ACCOUNT_LOCKED_OUT 564 +#define NT_STATUS_HANDLE_NOT_CLOSABLE 565 +#define NT_STATUS_CONNECTION_REFUSED 566 +#define NT_STATUS_GRACEFUL_DISCONNECT 567 +#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED 568 +#define NT_STATUS_ADDRESS_NOT_ASSOCIATED 569 +#define NT_STATUS_CONNECTION_INVALID 570 +#define NT_STATUS_CONNECTION_ACTIVE 571 +#define NT_STATUS_NETWORK_UNREACHABLE 572 +#define NT_STATUS_HOST_UNREACHABLE 573 +#define NT_STATUS_PROTOCOL_UNREACHABLE 574 +#define NT_STATUS_PORT_UNREACHABLE 575 +#define NT_STATUS_REQUEST_ABORTED 576 +#define NT_STATUS_CONNECTION_ABORTED 577 +#define NT_STATUS_BAD_COMPRESSION_BUFFER 578 +#define NT_STATUS_USER_MAPPED_FILE 579 +#define NT_STATUS_AUDIT_FAILED 580 +#define NT_STATUS_TIMER_RESOLUTION_NOT_SET 581 +#define NT_STATUS_CONNECTION_COUNT_LIMIT 582 +#define NT_STATUS_LOGIN_TIME_RESTRICTION 583 +#define NT_STATUS_LOGIN_WKSTA_RESTRICTION 584 +#define NT_STATUS_IMAGE_MP_UP_MISMATCH 585 +#define NT_STATUS_INSUFFICIENT_LOGON_INFO 592 +#define NT_STATUS_BAD_DLL_ENTRYPOINT 593 +#define NT_STATUS_BAD_SERVICE_ENTRYPOINT 594 +#define NT_STATUS_LPC_REPLY_LOST 595 +#define NT_STATUS_IP_ADDRESS_CONFLICT1 596 +#define NT_STATUS_IP_ADDRESS_CONFLICT2 597 +#define NT_STATUS_REGISTRY_QUOTA_LIMIT 598 +#define NT_STATUS_PATH_NOT_COVERED 599 +#define NT_STATUS_NO_CALLBACK_ACTIVE 600 +#define NT_STATUS_LICENSE_QUOTA_EXCEEDED 601 +#define NT_STATUS_PWD_TOO_SHORT 602 +#define NT_STATUS_PWD_TOO_RECENT 603 +#define NT_STATUS_PWD_HISTORY_CONFLICT 604 +#define NT_STATUS_PLUGPLAY_NO_DEVICE 606 +#define NT_STATUS_UNSUPPORTED_COMPRESSION 607 +#define NT_STATUS_INVALID_HW_PROFILE 608 +#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH 609 +#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND 610 +#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND 611 +#define NT_STATUS_RESOURCE_NOT_OWNED 612 +#define NT_STATUS_TOO_MANY_LINKS 613 +#define NT_STATUS_QUOTA_LIST_INCONSISTENT 614 +#define NT_STATUS_FILE_IS_OFFLINE 615 + +char *xlate_nt_status(DWORD ntstatus); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _SMBSRV_NTSTATUS_H */ diff --git a/usr/src/uts/common/smbsrv/oem.h b/usr/src/uts/common/smbsrv/oem.h new file mode 100644 index 000000000000..af30711dac02 --- /dev/null +++ b/usr/src/uts/common/smbsrv/oem.h @@ -0,0 +1,106 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Support for oem <-> unicode translations. + */ + +#ifndef _SMBSRV_OEM_H +#define _SMBSRV_OEM_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define oem_default_smb_cpid OEM_CP_IND_850 +#define oem_default_telnet_cpid OEM_CP_IND_1252 +#define oem_default_language "english" + +/* + * The id should corresponds to oemcp_table in os/library/oem.c. + */ +typedef enum codepage_id { + OEM_CP_IND_850 = 0, + OEM_CP_IND_950, + OEM_CP_IND_1252, + OEM_CP_IND_949, + OEM_CP_IND_936, + OEM_CP_IND_932, + OEM_CP_IND_852, + OEM_CP_IND_1250, + OEM_CP_IND_1253, + OEM_CP_IND_737, + OEM_CP_IND_1254, + OEM_CP_IND_857, + OEM_CP_IND_1251, + OEM_CP_IND_866, + OEM_CP_IND_1255, + OEM_CP_IND_862, + OEM_CP_IND_1256, + OEM_CP_IND_720, + NO_OF_OEM_CP_INDS +} codepage_id_t; + + +typedef struct language { + char *language; + unsigned int smbIndex; + unsigned int telnetIndex; +} language; + + +/* + * cpid = the cpid of the oemcp_table that oempage_t belong to. + * value = the conversion values + */ +typedef struct oempage_t { + unsigned int cpid; + mts_wchar_t *value; +} oempage_t; + +/* + * Private functions for opmlang.c + */ +extern int oem_codepage_init(unsigned int); +extern void oem_codepage_free(unsigned int); +extern language *oem_get_lang_table(void); +extern int oem_no_of_languages(void); +#define NO_OF_LANGUAGES oem_no_of_languages() + +/* + * Public functions + */ +extern size_t unicodestooems(char *, const mts_wchar_t *, size_t, unsigned int); +extern size_t oemstounicodes(mts_wchar_t *, const char *, size_t, unsigned int); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_OEM_H */ diff --git a/usr/src/uts/common/smbsrv/samlib.h b/usr/src/uts/common/smbsrv/samlib.h new file mode 100644 index 000000000000..19186ad8ba6e --- /dev/null +++ b/usr/src/uts/common/smbsrv/samlib.h @@ -0,0 +1,159 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_SAMLIB_H +#define _SMBSRV_SAMLIB_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Prototypes for the SAM library and RPC client side library interface. + * There are two levels of interface defined here: sam_xxx and samr_xxx. + * The sam_xxx functions provide a high level interface which make + * multiple RPC calls and do all the work necessary to obtain and return + * the requested information. The samr_xxx functions provide a low level + * interface in which each function maps to a single underlying RPC. + */ + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * samlib.c + */ +int sam_lookup_user_info(char *server, char *domain_name, char *username, + char *password, smb_userinfo_t *user_info); + +DWORD sam_create_trust_account(char *server, char *domain, + smb_auth_info_t *auth); + +DWORD sam_create_account(char *server, char *domain_name, char *account_name, + smb_auth_info_t *auth, DWORD account_flags, smb_userinfo_t *user_info); + +DWORD sam_remove_trust_account(char *server, char *domain); + +DWORD sam_delete_account(char *server, char *domain_name, char *account_name); + +DWORD sam_lookup_name(char *server, char *domain_name, char *account_name, + DWORD *rid_ret); + +DWORD sam_get_local_domains(char *server, char *domain_name); + +/* + * samr_open.c + */ +int samr_open(int ipc_mode, char *server, char *domain, char *username, + char *password, DWORD access_mask, mlsvc_handle_t *samr_handle); + +int samr_connect(char *server, char *domain, char *username, + DWORD access_mask, mlsvc_handle_t *samr_handle); + +int samr_close_handle(mlsvc_handle_t *handle); + +DWORD samr_open_domain(mlsvc_handle_t *samr_handle, DWORD access_mask, + struct samr_sid *sid, mlsvc_handle_t *domain_handle); + +int samr_open_user(mlsvc_handle_t *domain_handle, DWORD access_mask, + DWORD rid, mlsvc_handle_t *user_handle); + +DWORD samr_delete_user(mlsvc_handle_t *user_handle); + +int samr_open_group(mlsvc_handle_t *domain_handle, DWORD rid, + mlsvc_handle_t *group_handle); + +DWORD samr_create_user(mlsvc_handle_t *domain_handle, char *username, + DWORD account_flags, DWORD *rid, mlsvc_handle_t *user_handle); + +/* + * samr_lookup.c + */ +union samr_user_info { + struct info1 { + char *username; + char *fullname; + DWORD group_rid; + char *description; + char *unknown; + } info1; + + struct info6 { + char *username; + char *fullname; + } info6; + + struct info7 { + char *username; + } info7; + + struct info8 { + char *fullname; + } info8; + + struct info9 { + DWORD group_rid; + } info9; + + struct info16 { + DWORD unknown; + } info16; +}; + + +int samr_lookup_domain(mlsvc_handle_t *samr_handle, char *domain_name, + smb_userinfo_t *user_info); + +DWORD samr_enum_local_domains(mlsvc_handle_t *samr_handle); + +DWORD samr_lookup_domain_names(mlsvc_handle_t *domain_handle, char *name, + smb_userinfo_t *user_info); + +int samr_query_user_info(mlsvc_handle_t *user_handle, WORD switch_value, + union samr_user_info *user_info); + +int samr_query_user_groups(mlsvc_handle_t *user_handle, + smb_userinfo_t *user_info); + +DWORD samr_get_user_pwinfo(mlsvc_handle_t *user_handle); + +typedef struct oem_password { + BYTE data[512]; + DWORD length; +} oem_password_t; + + +int sam_oem_password(oem_password_t *oem_password, unsigned char *new_password, + unsigned char *old_password); + +#ifdef __cplusplus +} +#endif + + +#endif /* _SMBSRV_SAMLIB_H */ diff --git a/usr/src/uts/common/smbsrv/smb.h b/usr/src/uts/common/smbsrv/smb.h new file mode 100644 index 000000000000..1214f041d790 --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb.h @@ -0,0 +1,272 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_SMB_H +#define _SMBSRV_SMB_H + +/* + * SMB definitions and interfaces, mostly defined in the CIFS spec. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB definitions and interfaces, mostly defined in the CIFS spec. + */ + +#ifdef _KERNEL +#include +#endif +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Typedefs from CIFS section 3.2 + */ +typedef unsigned char UCHAR; +typedef unsigned short USHORT; +typedef uint32_t ULONG; +typedef int32_t LONG; + + +/* + * The msgbuf format and length of an SMB header. + */ +#define SMB_HEADER_DOS_FMT "Mbbbwbww10.wwww" +#define SMB_HEADER_NT_FMT "Mblbww#c2.wwww" +#define SMB_HEADER_LEN 32 +#define SMB_SIG_SIZE 8 /* SMB signature size */ + +/* + * CIFS definition for the SMB header (CIFS Section 3.2). Note that the + * pid_high field is not documented in the 1997 CIFS specificaction. This + * is a decoded or memory-based definition, which may be padded to align + * its elements on word boundaries. See smb_hdrbuf_t for the network + * ready structure. + */ +typedef struct smb_hdr { + UCHAR protocol[4]; + UCHAR command; + + union { + struct { + UCHAR error_class; + UCHAR reserved; + USHORT error; + } dos_error; + ULONG ntstatus; + }status; + + UCHAR flags; + USHORT flags2; + USHORT pid_high; + + union { + USHORT pad[5]; + struct { + USHORT reserved; + UCHAR security_sig[SMB_SIG_SIZE]; + } extra; + } extra; + + USHORT tid; + USHORT pid; + USHORT uid; + USHORT mid; +} smb_hdr_t; + + +/* + * Encoded or packed SMB header in network ready format. + */ +typedef struct smb_hdrbuf { + unsigned char hdr[SMB_HEADER_LEN]; +} smb_hdrbuf_t; + +typedef struct smb_nethdr { + uint8_t sh_protocol[4]; + uint8_t sh_command; + + union { + struct { + uint8_t sh_error_class; + uint8_t sh_reserved; + uint8_t sh_error[2]; + } dos_error; + uint8_t sh_ntstatus[4]; + } status; + + uint8_t sh_flags; + uint8_t sh_flags2[2]; + uint8_t sh_pid_high[2]; + + union { + uint8_t sh_pad[10]; + struct { + uint8_t sh_reserved[2]; + uint8_t sh_security_sig[SMB_SIG_SIZE]; + } extra; + } extra; + + uint8_t sh_tid[2]; + uint8_t sh_pid[2]; + uint8_t sh_uid[2]; + uint8_t sh_mid[2]; +} smb_nethdr_t; + +/* + * Protocol magic value as a 32-bit. This will be 0xff 0x53 0x4d 0x42 on + * the wire. + */ + +#define SMB_PROTOCOL_MAGIC 0x424d53ff + +/* + * Time and date encoding (CIFS Section 3.6). The date is encoded such + * that the year has a range of 0-119, which represents 1980-2099. The + * month range is 1-12, and the day range is 1-31. + */ +typedef struct smb_date { + USHORT day : 5; + USHORT month : 4; + USHORT year : 7; +} smb_date_t; + + +/* + * The hours range is 0-23, the minutes range is 0-59 and the two_sec + * range is 0-29. + */ +typedef struct smb_time { + USHORT two_sec : 5; + USHORT minutes : 6; + USHORT hours : 5; +} smb_time_t; + + +/* + * This is a 64-bit signed absolute time representing 100ns increments. + * A positive value represents the absolute time since 1601AD. A + * negative value represents a context specific relative time. + */ +typedef struct smb_time2 { + ULONG low_time; + LONG high_time; +} smb_time2_t; + + +/* + * The number of seconds since Jan 1, 1970, 00:00:00.0. + */ +typedef uint32_t smb_utime_t; + + +#define SMB_LM_NEGOTIATE_WORDCNT 13 +#define SMB_NT_NEGOTIATE_WORDCNT 17 + + +typedef struct smb_nt_negotiate_rsp { + UCHAR word_count; + USHORT dialect_index; + UCHAR security_mode; + USHORT max_mpx; + USHORT max_vc; + ULONG max_buffer_size; + ULONG max_raw_size; + ULONG session_key; + ULONG capabilities; + ULONG time_low; + ULONG time_high; + USHORT server_tz; + UCHAR security_len; + USHORT byte_count; + UCHAR *guid; + UCHAR *challenge; + UCHAR *oem_domain; +} smb_nt_negotiate_rsp_t; + +/* + * SMB_COM_TRANSACTION + */ +typedef struct smb_transact_rsp { + UCHAR WordCount; /* Count of data bytes */ + /* value = 10 + SetupCount */ + USHORT TotalParamCount; /* Total parameter bytes being sent */ + USHORT TotalDataCount; /* Total data bytes being sent */ + USHORT Reserved; + USHORT ParamCount; /* Parameter bytes sent this buffer */ + USHORT ParamOffset; /* Offset (from hdr start) to params */ + USHORT ParamDisplacement; /* Displacement of these param bytes */ + USHORT DataCount; /* Data bytes sent this buffer */ + USHORT DataOffset; /* Offset (from hdr start) to data */ + USHORT DataDisplacement; /* Displacement of these data bytes */ + UCHAR SetupCount; /* Count of setup words */ + USHORT BCC; +#if 0 + UCHAR Reserved2; /* Reserved (pad above to word) */ + UCHAR Buffer[1]; /* Buffer containing: */ + USHORT Setup[]; /* Setup words (# = SetupWordCount) */ + USHORT ByteCount; /* Count of data bytes */ + UCHAR Pad[]; /* Pad to SHORT or LONG */ + UCHAR Params[]; /* Param. bytes (# = ParamCount) */ + UCHAR Pad1[]; /* Pad to SHORT or LONG */ + UCHAR Data[]; /* Data bytes (# = DataCount) */ +#endif +} smb_transact_rsp_t; + +/* + * SMBreadX + */ +typedef struct smb_read_andx_rsp { + UCHAR WordCount; + UCHAR AndXCmd; + UCHAR AndXReserved; + USHORT AndXOffset; + USHORT Remaining; + USHORT DataCompactionMode; + USHORT Reserved; + USHORT DataLength; + USHORT DataOffset; + ULONG DataLengthHigh; + USHORT Reserved2[3]; + USHORT ByteCount; +#if 0 + UCHAR Pad[]; + UCHAR Data[]; +#endif +} smb_read_andx_rsp_t; + +#ifdef __cplusplus +} +#endif + + +#endif /* _SMBSRV_SMB_H */ diff --git a/usr/src/uts/common/smbsrv/smb_common_door.h b/usr/src/uts/common/smbsrv/smb_common_door.h new file mode 100644 index 000000000000..07ff2f1dd44f --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_common_door.h @@ -0,0 +1,130 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_SMB_COMMON_DOOR_H +#define _SMBSRV_SMB_COMMON_DOOR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int smb_dr_get_opcode(char *argp, size_t arg_size); +extern int smb_dr_get_res_stat(char *rbufp, size_t rbuf_size); +extern char *smb_dr_set_opcode(uint32_t opcode, size_t *len); +extern char *smb_dr_set_res_stat(uint32_t stat, size_t *len); +extern char *smb_dr_encode_string(uint32_t reserved, char *buf, size_t *len); + +#ifdef _KERNEL +extern int smb_kdr_decode_common(char *buf, size_t len, xdrproc_t proc, + void *data); +extern char *smb_kdr_encode_common(uint_t reserved, void *data, + xdrproc_t proc, size_t *len); + +/* kernel encode functions */ +extern char *smb_dr_encode_arg_get_token(netr_client_t *clnt_info, + size_t *len); +/* kernel decode functions */ +extern smb_token_t *smb_dr_decode_res_token(char *buf, size_t len); +extern smb_dr_kshare_t *smb_dr_decode_kshare(char *buf, size_t len); + +/* kernel free functions */ +void smb_dr_kshare_free(smb_dr_kshare_t *kshare); +#else /* _KERNEL */ +extern int smb_dr_decode_common(char *buf, size_t len, xdrproc_t proc, + void *data); +extern char *smb_dr_encode_common(uint_t reserved, void *data, xdrproc_t proc, + size_t *len); + +/* user-space encode functions */ +extern char *smb_dr_encode_res_token(smb_token_t *token, size_t *len); +extern char *smb_dr_encode_kshare(smb_dr_kshare_t *, size_t *); + +/* user-space decode functions */ +extern netr_client_t *smb_dr_decode_arg_get_token(char *buf, size_t len); +extern char *smb_dr_decode_string(char *buf, size_t len); + +/* user-space free functions */ +extern void smb_dr_ulist_free(smb_dr_ulist_t *ulist); +#endif /* _KERNEL */ + +/* + * PBSHORTCUT - should be removed once XDR is used for + * serializing/deserializing data across door. + */ + +/* + * Common encode/decode functions used by door clients/servers. + */ + +typedef struct smb_dr_ctx { + char *ptr; + char *start_ptr; + char *end_ptr; + int status; +} smb_dr_ctx_t; + + +extern smb_dr_ctx_t *smb_dr_decode_start(char *ptr, int size); +extern int smb_dr_decode_finish(smb_dr_ctx_t *ctx); + +extern smb_dr_ctx_t *smb_dr_encode_start(char *ptr, int size); +extern int smb_dr_encode_finish(smb_dr_ctx_t *ctx, unsigned int *used); + +extern int32_t smb_dr_get_int32(smb_dr_ctx_t *ctx); +extern DWORD smb_dr_get_dword(smb_dr_ctx_t *ctx); +extern uint32_t smb_dr_get_uint32(smb_dr_ctx_t *ctx); +extern int64_t smb_dr_get_int64(smb_dr_ctx_t *ctx); +extern uint64_t smb_dr_get_uint64(smb_dr_ctx_t *ctx); + +extern void smb_dr_put_int32(smb_dr_ctx_t *ctx, int32_t num); +extern void smb_dr_put_dword(smb_dr_ctx_t *ctx, DWORD num); +extern void smb_dr_put_uint32(smb_dr_ctx_t *ctx, uint32_t num); +extern void smb_dr_put_int64(smb_dr_ctx_t *ctx, int64_t num); +extern void smb_dr_put_uint64(smb_dr_ctx_t *ctx, uint64_t num); + +extern char *smb_dr_get_string(smb_dr_ctx_t *ctx); +extern void smb_dr_put_string(smb_dr_ctx_t *ctx, char *buf); +extern void smb_dr_free_string(char *buf); + +extern void smb_dr_put_word(smb_dr_ctx_t *ctx, WORD num); +extern WORD smb_dr_get_word(smb_dr_ctx_t *ctx); + +extern void smb_dr_put_BYTE(smb_dr_ctx_t *ctx, BYTE byte); +extern BYTE smb_dr_get_BYTE(smb_dr_ctx_t *ctx); + +extern void smb_dr_put_buf(smb_dr_ctx_t *ctx, unsigned char *start, int len); +extern int smb_dr_get_buf(smb_dr_ctx_t *ctx, unsigned char *buf, int bufsize); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_SMB_COMMON_DOOR_H */ diff --git a/usr/src/uts/common/smbsrv/smb_door_svc.h b/usr/src/uts/common/smbsrv/smb_door_svc.h new file mode 100644 index 000000000000..be8898f42a96 --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_door_svc.h @@ -0,0 +1,202 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_SMB_DOOR_SVC_H +#define _SMBSRV_SMB_DOOR_SVC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * SMB door service (user-space and kernel-space) + */ +#define SMB_DR_SVC_NAME "/var/run/smbd_door" +#define SMB_DR_SVC_VERSION 1 +#define SMB_DR_SVC_COOKIE ((void*)(0xdeadbeef^SMB_DR_SVC_VERSION)) + +/* + * Door argument buffer starts off by the four-byte opcode. + * Door result buffer starts off by the four-byte status. + * The real data starts at offset 4 of the door buffer. + */ +#define SMB_DR_DATA_OFFSET 4 + +/* + * A smb_dr_op_t exists for each user-space door operation. + * A smb_kdr_op_t exists for each kernel-space door operation. + * + * The first argument to smb_dr_op_t/smb_kdr_op_t is a pointer to the + * door argument buffer. The second argument indicates the size of + * the door argument buffer. + * + * The user-space door server accepts file descriptors from clients. + * Thus, door_desc_t and n_desc can be passed to any smb_dr_op_t operation. + * + * Returns door result buffer and its size 'rbufsize' upon success. + * Otherwise, NULL pointer will be returned and appropriate error code + * will be set. + */ +typedef char *(*smb_dr_op_t)(char *argp, size_t arg_size, door_desc_t *dp, + uint_t n_desc, size_t *rbufsize, int *err); +typedef char *(*smb_kdr_op_t)(char *argp, size_t arg_size, size_t *rbufsize, + int *errno); + +extern smb_dr_op_t smb_doorsrv_optab[]; + +/* + * Door Opcode + * ------------- + * smb_dr_opcode_t - opcodes for user-space door operations. + * smb_kdr_opcode_t - opcodes for kernel-space door operations. + */ +enum smb_dr_opcode_t { + SMB_DR_USER_AUTH_LOGON, + SMB_DR_SET_DWNCALL_DESC, + SMB_DR_USER_NONAUTH_LOGON, + SMB_DR_USER_AUTH_LOGOFF, + SMB_DR_USER_LIST, + SMB_DR_GROUP_ADD, + SMB_DR_GROUP_DELETE, + SMB_DR_GROUP_MEMBER_ADD, + SMB_DR_GROUP_MEMBER_REMOVE, + SMB_DR_GROUP_COUNT, + SMB_DR_GROUP_CACHE_SIZE, + SMB_DR_GROUP_MODIFY, + SMB_DR_GROUP_PRIV_NUM, + SMB_DR_GROUP_PRIV_LIST, + SMB_DR_GROUP_PRIV_GET, + SMB_DR_GROUP_PRIV_SET, + SMB_DR_GROUP_LIST, + SMB_DR_GROUP_MEMBER_LIST, + SMB_DR_GROUP_MEMBER_COUNT +}; + +enum smb_kdr_opcode_t { + SMB_KDR_USER_NUM, + SMB_KDR_USER_LIST, + SMB_KDR_SHARE +}; + +/* + * Door result status + * SMB door servers will pass the following result status along with the + * requested data back to the clients. + */ +#define SMB_DR_OP_SUCCESS 0 +#define SMB_DR_OP_ERR 1 +#define SMB_DR_OP_ERR_DECODE 2 +#define SMB_DR_OP_ERR_ENCODE 3 +#define SMB_DR_OP_ERR_EMPTYBUF 4 +#define SMB_DR_OP_ERR_INVALID_OPCODE 5 + +#ifdef _KERNEL +/* + * The 2nd argument of the smb_kdoor_srv_callback will be of the + * following data structure type. + * + * rbuf - The pointer to a dynamically allocated door result buffer that + * is required to be freed after the kernel completes the copyout + * operation. + */ +typedef struct smb_kdoor_cb_arg { + char *rbuf; + size_t rbuf_size; +} smb_kdoor_cb_arg_t; + +/* + * SMB kernel door server + * ------------------------ + * NOTE: smb_kdoor_srv_init()/smb_kdoor_srv_fini() are noops. + */ +extern int smb_kdoor_srv_start(); +extern void smb_kdoor_srv_stop(); +extern int smb_kdr_is_valid_opcode(int opcode); + +extern char *smb_kdr_op_user_num(char *argp, size_t arg_size, + size_t *rbufsize, int *errno); +extern char *smb_kdr_op_users(char *argp, size_t arg_size, + size_t *rbufsize, int *errno); +extern char *smb_kdr_op_share(char *argp, size_t arg_size, + size_t *rbufsize, int *errno); + +/* + * SMB kernel door client + * ------------------------ + * NOTE: smb_kdoor_clnt_init()/smb_kdoor_clnt_fini() are noops. + */ +extern int smb_kdoor_clnt_start(); +extern void smb_kdoor_clnt_stop(); +extern void smb_kdoor_clnt_free(); +extern char *smb_kdoor_clnt_upcall(char *argp, size_t arg_size, door_desc_t *dp, + uint_t desc_num, size_t *rbufsize); + +/* + * SMB upcalls + */ +extern smb_token_t *smb_upcall_get_token(netr_client_t *clnt_info); +extern int smb_upcall_set_dwncall_desc(uint32_t opcode, door_desc_t *dp, + uint_t n_desc); +extern void smb_user_nonauth_logon(uint32_t); +extern void smb_user_auth_logoff(uint32_t); +#else /* _KERNEL */ + +/* + * SMB user-space door server + */ +extern int smb_door_srv_start(); +extern void smb_door_srv_stop(void); + +/* downcall descriptor */ +typedef int (*smb_dwncall_get_desc_t)(); +extern int smb_dwncall_install_callback(smb_dwncall_get_desc_t get_desc_cb); + +extern int smb_dr_is_valid_opcode(int opcode); + +/* + * SMB user-space door client + */ +extern int smb_dr_clnt_open(int *fd, char *path, char *op_desc); +extern char *smb_dr_clnt_call(int fd, char *argp, size_t arg_size, + size_t *rbufsize, char *op_desc); +extern void smb_dr_clnt_free(char *argp, size_t arg_size, char *rbufp, + size_t rbuf_size); +/* + * SMB downcalls + */ +extern int smb_dwncall_get_users(int offset, smb_dr_ulist_t *users); +extern int smb_dwncall_share(int op, char *path, char *sharename); + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_SMB_DOOR_SVC_H */ diff --git a/usr/src/uts/common/smbsrv/smb_fsd.h b/usr/src/uts/common/smbsrv/smb_fsd.h new file mode 100644 index 000000000000..e2fe7787b808 --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_fsd.h @@ -0,0 +1,90 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_SMB_FSD_H +#define _SMBSRV_SMB_FSD_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include + +#ifndef _KERNEL +#include +#include +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * VOL_NAME_MAX is derived from Montana's FSOL_NAME_MAX (32). + * This is consistent with MAX_FS_NAME from PB fsadm (QFS). + */ + +#define VOL_NAME_MAX 32 + +typedef struct fsvol_attr { + char name[VOL_NAME_MAX]; + char fs_typename[_ST_FSTYPSZ]; + unsigned flags; + uint32_t fs_sequence; +} fsvol_attr_t; + +/* + * Note: fsid_t consists of two 32-bit values. + * The first corresponds to the dev and the second to the file system type. + * The fsid_t uniquely (and persistently) denotes a file system in a running + * system. + * + * For the CIFS volume serial number, fsid.val[0] is used (a 32-bit value + * is expected by TRANS2_QUERY_FS_INFORMATION). + */ + +#define fs_desc_t fsid_t + +extern fs_desc_t null_fsd; + +#ifdef _KERNEL + +void *fsd_lookup(char *, unsigned, fs_desc_t *); +int fsd_cmp(fs_desc_t *, fs_desc_t *); +int fsd_getattr(fs_desc_t *, fsvol_attr_t *); +int fsd_chkcap(fs_desc_t *, unsigned); + +void *fsd_hold(fs_desc_t *fsd); +void fsd_rele(void *vfsp); + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_SMB_FSD_H */ diff --git a/usr/src/uts/common/smbsrv/smb_fsops.h b/usr/src/uts/common/smbsrv/smb_fsops.h new file mode 100644 index 000000000000..56a15aa2d0c6 --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_fsops.h @@ -0,0 +1,153 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_SMB_FSOPS_H +#define _SMBSRV_SMB_FSOPS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This header file contains all the functions for the interface between + * the smb layer and the fs layer. + */ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int smb_fsop_open(smb_ofile_t *of); + +int smb_fsop_close(smb_ofile_t *of); + +int smb_fsop_create(struct smb_request *sr, cred_t *cr, smb_node_t *snode, + char *name, smb_attr_t *attr, smb_node_t **ret_snode, smb_attr_t *ret_attr); + +int smb_fsop_mkdir(struct smb_request *sr, cred_t *cr, smb_node_t *snode, + char *name, smb_attr_t *attr, smb_node_t **ret_snode, smb_attr_t *ret_attr); + +int smb_fsop_remove(struct smb_request *sr, cred_t *cr, smb_node_t *dir_snode, + char *name, int od); + +int smb_fsop_rmdir(struct smb_request *sr, cred_t *cr, smb_node_t *dir_snode, + char *name, int od); + +int smb_fsop_getattr(struct smb_request *sr, cred_t *cr, smb_node_t *snode, + smb_attr_t *attr); + +int smb_fsop_readdir(struct smb_request *sr, cred_t *cr, smb_node_t *snode, + uint32_t *cookie, char *name, int *namelen, ino64_t *fileid, + struct fs_stream_info *stream_info, smb_node_t **ret_snode, + smb_attr_t *ret_attr); + +int smb_fsop_getdents(struct smb_request *sr, cred_t *cr, + struct smb_node *dir_snode, uint32_t *cookie, uint64_t *verifierp, + int32_t *maxcnt, char *args, char *pattern); + +int smb_maybe_mangled_name(char *name); + +int smb_fsop_rename(struct smb_request *sr, cred_t *cr, + smb_node_t *from_snode, char *from_name, smb_node_t *to_snode, + char *to_name); + +int smb_fsop_setattr(struct smb_request *sr, cred_t *cr, smb_node_t *snode, + smb_attr_t *set_attr, smb_attr_t *ret_attr); + +int smb_fsop_read(struct smb_request *sr, cred_t *cr, + smb_node_t *snode, uio_t *uio, smb_attr_t *ret_attr); + +int smb_fsop_write(struct smb_request *sr, cred_t *cr, smb_node_t *snode, + uio_t *uio, uint32_t *lcount, smb_attr_t *ret_attr, + uint32_t *stability); + +int smb_fsop_statfs(cred_t *cr, struct smb_node *snode, + struct statvfs64 *statp); + +int smb_fsop_remove_streams(struct smb_request *sr, cred_t *cr, + smb_node_t *fnode); + +int smb_fsop_access(smb_request_t *sr, cred_t *cr, smb_node_t *snode, + uint32_t faccess); + +void smb_fsop_eaccess(smb_request_t *sr, cred_t *cr, smb_node_t *snode, + uint32_t *faccess); + +int smb_fsop_lookup_name(struct smb_request *sr, cred_t *cr, int flags, + smb_node_t *root_node, smb_node_t *dir_snode, char *name, + smb_node_t **ret_snode, smb_attr_t *ret_attr); + +int smb_fsop_lookup(struct smb_request *sr, cred_t *cr, int flags, + smb_node_t *root_node, smb_node_t *dir_snode, char *name, + smb_node_t **ret_snode, smb_attr_t *ret_attr, char *ret_shortname, + char *ret_name83); + +int smb_fsop_commit(smb_request_t *sr, cred_t *cr, struct smb_node *snode); + +int smb_fsop_stream_readdir(struct smb_request *sr, cred_t *cr, + smb_node_t *fnode, uint32_t *cookiep, struct fs_stream_info *stream_info, + smb_node_t **ret_snode, smb_attr_t *ret_attr); + +void smb_fsop_aclfree(acl_t *acl); +acl_t *smb_fsop_aclalloc(int acenum, int flags); +int smb_fsop_aclread(smb_request_t *sr, cred_t *cr, smb_node_t *snode, + smb_fssd_t *fssd); +int smb_fsop_aclwrite(smb_request_t *sr, cred_t *cr, smb_node_t *snode, + smb_fssd_t *fs_sd); +acl_type_t smb_fsop_acltype(smb_node_t *snode); + +void smb_fsop_sdinit(smb_fssd_t *fs_sd, uint32_t secinfo, uint32_t sd_flags); +void smb_fsop_sdterm(smb_fssd_t *fssd); +int smb_fsop_sdread(smb_request_t *sr, cred_t *cr, smb_node_t *snode, + smb_fssd_t *fssd); +int smb_fsop_sdwrite(smb_request_t *sr, cred_t *cr, smb_node_t *snode, + smb_fssd_t *fs_sd, int overwrite); + + +void smb_get_caller_context(smb_request_t *sr, caller_context_t *ct); + +/* + * Lookup-related flags + * + * SMB_FOLLOW_LINKS Follow symbolic links. + * SMB_IGNORE_CASE Perform case-insensitive lookup. + * + * Misc flags + * + * SMB_STREAM_RDDIR use eflags=0 for streams readdirs this + * is currently a workaround because the + * vfs isn't filling in this flag + */ + +#define SMB_FOLLOW_LINKS 0x00000001 +#define SMB_IGNORE_CASE 0x00000002 +#define SMB_STREAM_RDDIR 0x00000004 + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_SMB_FSOPS_H */ diff --git a/usr/src/uts/common/smbsrv/smb_i18n.h b/usr/src/uts/common/smbsrv/smb_i18n.h new file mode 100644 index 000000000000..74804d0056ac --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_i18n.h @@ -0,0 +1,41 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMB_I18N_H +#define _SMB_I18N_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned short mts_wchar_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _SMB_I18N_H */ diff --git a/usr/src/uts/common/smbsrv/smb_idmap.h b/usr/src/uts/common/smbsrv/smb_idmap.h new file mode 100644 index 000000000000..0f6380f1bf68 --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_idmap.h @@ -0,0 +1,100 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMB_IDMAP_H +#define _SMB_IDMAP_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef _KERNEL +#include +#else +#include +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * SMB ID mapping + * + * Solaris ID mapping service (aka Winchester) works with domain SIDs + * and RIDs where domain SIDs are in string format. CIFS service works + * with binary SIDs understanable by CIFS clients. A layer of SMB ID + * mapping functions are implemeted to hide the SID conversion details + * and also hide the handling of array of batch mapping requests. + */ + +#define SMB_IDMAP_UNKNOWN -1 +#define SMB_IDMAP_GROUP 0 +#define SMB_IDMAP_USER 1 +#define SMB_IDMAP_EVERYONE 2 + +#define SMB_IDMAP_SID2ID 0x0001 +#define SMB_IDMAP_ID2SID 0x0002 + +/* + * smb_idmap_t + * + * sim_idtype: ID type (output in sid->uid mapping) + * sim_id: UID/GID (output in sid->uid mapping) + */ +typedef struct smb_idmap { + int sim_idtype; + uid_t *sim_id; + char *sim_domsid; + uint32_t sim_rid; + nt_sid_t *sim_sid; + idmap_stat sim_stat; +} smb_idmap_t; + +typedef struct smb_idmap_batch { + uint16_t sib_nmap; + uint32_t sib_flags; + uint32_t sib_size; + smb_idmap_t *sib_maps; + idmap_get_handle_t *sib_idmaph; +} smb_idmap_batch_t; + +idmap_stat smb_idmap_getsid(uid_t, int, nt_sid_t **); +idmap_stat smb_idmap_getid(nt_sid_t *, uid_t *, int *); + +void smb_idmap_batch_destroy(smb_idmap_batch_t *); +idmap_stat smb_idmap_batch_create(smb_idmap_batch_t *, uint16_t, int); +idmap_stat smb_idmap_batch_getmappings(smb_idmap_batch_t *); +idmap_stat smb_idmap_batch_getid(idmap_get_handle_t *, smb_idmap_t *, + nt_sid_t *, int); +idmap_stat smb_idmap_batch_getsid(idmap_get_handle_t *, smb_idmap_t *, + uid_t, int); + +#ifdef __cplusplus +} +#endif + + +#endif /* _SMB_IDMAP_H */ diff --git a/usr/src/uts/common/smbsrv/smb_incl.h b/usr/src/uts/common/smbsrv/smb_incl.h new file mode 100644 index 000000000000..b9cdaa3f1387 --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_incl.h @@ -0,0 +1,121 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_SMB_INCL_H +#define _SMBSRV_SMB_INCL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define QUEUE_INSERT_HEAD(q, e) \ + { \ + ((e)->forw) = (void *)((q)->forw); \ + ((e)->back) = (void *)(q); \ + ((q)->forw->back) = (void *)(e); \ + ((q)->forw) = (void *)(e); \ + } + + +#define QUEUE_INSERT_TAIL(q, e) \ + { \ + ((e)->back) = (void *)((q)->back); \ + ((e)->forw) = (void *)(q); \ + ((q)->back->forw) = (void *)(e); \ + ((q)->back) = (void *)(e); \ + } + +#define QUEUE_INSERT_SORT(q, e, k, t) \ + { \ + (void *)(t) = (void *)((q)->forw); \ + while (((t)->k) < ((e)->k)) { \ + (void *)(t) = (void *)((t)->forw); \ + } \ + QUEUE_INSERT_TAIL(t, e); \ + } + +#define QUEUE_CLIP(e) \ + { \ + (e)->forw->back = (e)->back; \ + (e)->back->forw = (e)->forw; \ + (e)->forw = 0; \ + (e)->back = 0; \ + } + +/* These should be defined in system header files */ + +extern int atoi(const char *); +extern int getchar(void); + +/* + * PBSHORTCUT - remove this when we replace BYTE/WORD/DWORD to + * uint8_t/uint16_t/uint32_t and gets included by + * files that invoke the following functions. + */ +extern char *inet_ntop(int, const void *, char *, int); +extern int inet_pton(int, char *, void *); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_SMB_INCL_H */ diff --git a/usr/src/uts/common/smbsrv/smb_ioctl.h b/usr/src/uts/common/smbsrv/smb_ioctl.h new file mode 100755 index 000000000000..6b6ad051fb2a --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_ioctl.h @@ -0,0 +1,45 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMB_IOCTL_H_ +#define _SMB_IOCTL_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define SMB_IOC_BASE (('S' << 16) | ('B' << 8)) +#define SMB_IOC_GMTOFF _IOW(SMB_IOC_BASE, 1, int) +#define SMB_IOC_CONFIG_REFRESH _IOW(SMB_IOC_BASE, 2, int) + +#ifdef __cplusplus +} +#endif + +#endif /* _SMB_IOCTL_H_ */ diff --git a/usr/src/uts/common/smbsrv/smb_kproto.h b/usr/src/uts/common/smbsrv/smb_kproto.h new file mode 100644 index 000000000000..26ac73055e88 --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_kproto.h @@ -0,0 +1,602 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Function prototypes for the SMB module. + */ + +#ifndef _SMB_KPROTO_H_ +#define _SMB_KPROTO_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Definitions that should be elsewhere... + */ +struct mbuf *m_free(struct mbuf *); +void m_freem(struct mbuf *); + +int fd_dealloc(int); + +off_t lseek(int fildes, off_t offset, int whence); + +int arpioctl(int cmd, void *data); +/* Why? uint32_t inet_addr(char *str); */ +int microtime(timestruc_t *tvp); +int clock_get_uptime(void); + +/* + * SMB Service init/shutdown functions + */ +int smb_service_init(void); +void smb_service_fini(void); +int smb_service_open(struct smb_info *si); +void smb_service_close(struct smb_info *si); +int smb_service_connect(struct smb_info *si); +void smb_service_disconnect(struct smb_info *si); +int smb_service_start_threads(struct smb_info *si); +void smb_service_stop_threads(struct smb_info *si); + +/* + * Logging functions + */ +void smb_log_flush(void); +void smb_correct_keep_alive_values(uint32_t new_keep_alive); +void smb_close_all_connections(void); +int smb_set_file_size(struct smb_request *sr); +int smb_session_send(smb_session_t *, uint8_t type, struct mbuf_chain *); +int smb_session_xprt_gethdr(smb_session_t *, smb_xprt_t *); + +int smb_net_id(uint32_t); +uint32_t smb_gmt_to_local_time(uint32_t); +uint32_t smb_local_time_to_gmt(uint32_t); + +void smb_process_file_notify_change_queue(struct smb_ofile *of); + +void smb_oplock_init(void); + +DWORD smb_acquire_oplock(struct smb_request *sr, + struct smb_ofile *file, + unsigned int level_requested, + unsigned int *level_granted); + +DWORD smb_break_oplock(struct smb_request *sr, struct smb_node *node); +void smb_release_oplock(struct smb_ofile *file, int reason); + +uint32_t smb_unlock_range(struct smb_request *, struct smb_node *, + uint64_t, uint64_t); +void smb_unlock_range_raise_error(smb_request_t *sr, uint32_t ntstatus); +uint32_t smb_lock_range(struct smb_request *, struct smb_ofile *, + uint64_t, uint64_t, uint32_t, uint32_t); +void smb_lock_range_raise_error(smb_request_t *sr, uint32_t ntstatus); + +int smb_mangle_name(ino64_t fileid, char *name, char *shortname, + char *name83, int force); +int smb_unmangle_name(struct smb_request *sr, cred_t *cred, + smb_node_t *dir_node, char *name, char *real_name, int realname_size, + char *shortname, char *name83, int od); +int smb_maybe_mangled_name(char *name); +int smb_maybe_mangled_path(const char *path, size_t pathlen); +int smb_needs_mangle(char *name, char **dot_pos); + +void smb_set_stability(int mode); +void smb_commit_required(int state); + + +void smbsr_cleanup(struct smb_request *sr); + +int smbsr_connect_tree(struct smb_request *); + +int smb_convert_unicode_wildcards(char *); +int smb_ascii_or_unicode_strlen(struct smb_request *, char *); +int smb_ascii_or_unicode_strlen_null(struct smb_request *, char *); +int smb_ascii_or_unicode_null_len(struct smb_request *); + +int smb_search(struct smb_request *); +void smb_rdir_close(struct smb_request *); +int smb_rdir_open(struct smb_request *, char *, unsigned short); +int smb_rdir_next(smb_request_t *sr, smb_node_t **rnode, + smb_odir_context_t *pc); + +DWORD smb_open_subr(struct smb_request *); +DWORD smb_validate_object_name(char *path, unsigned int ftype); + +uint32_t smb_omode_to_amask(uint32_t desired_access); + +void sshow_distribution_info(char *); + +int smb_dispatch_request(struct smb_request *); +void smbsr_disconnect_file(smb_request_t *sr); +void smbsr_disconnect_dir(smb_request_t *sr); +void smbsr_check_result(struct smb_request *, int, int); +void smbsr_decode_error(struct smb_request *); +void smbsr_encode_error(struct smb_request *); +void smbsr_encode_empty_result(struct smb_request *sr); + +#pragma does_not_return(smbsr_decode_error) +#pragma does_not_return(smbsr_encode_error) + +int smbsr_decode_vwv(struct smb_request *sr, char *fmt, ...); +int smbsr_decode_data(struct smb_request *sr, char *fmt, ...); +void smbsr_encode_result(struct smb_request *, int, int, char *, ...); +smb_xa_t *smbsr_lookup_xa(smb_request_t *sr); +void smbsr_send_reply(struct smb_request *); + +void smbsr_raise_cifs_error(struct smb_request *sr, DWORD status, + int error_class, int error_code); + +int smbsr_set_errno(struct smb_request *, int); +void smbsr_raise_errno(struct smb_request *, int); +void smbsr_raise_error(struct smb_request *, int, int); +void smbsr_raise_nt_error(struct smb_request *sr, uint32_t); + +#pragma does_not_return(smbsr_raise_cifs_error) +#pragma does_not_return(smbsr_raise_error) +#pragma does_not_return(smbsr_raise_nt_error) +#pragma does_not_return(smbsr_raise_errno) + +void smbsr_setup_nt_status(struct smb_request *sr, + uint32_t severity, + uint32_t nt_status); + +int smb_mbc_encode(struct mbuf_chain *mbc, char *fmt, va_list ap); +int smb_mbc_decode(struct mbuf_chain *mbc, char *fmt, va_list ap); + +int clock_get_milli_uptime(void); +int dosfs_dos_to_ux_time(int, int); +int dosfs_ux_to_dos_time(int, short int *, short int *); + +int smb_decode_mbc(struct mbuf_chain *mbc, char *fmt, ...); +int smb_decode_buf(unsigned char *buf, int n_buf, char *fmt, ...); +int smb_encode_mbc(struct mbuf_chain *mbc, char *fmt, ...); +int smb_encode_buf(unsigned char *buf, int n_buf, char *fmt, ...); +int smb_peek_mbc(struct mbuf_chain *buf, int offset, char *fmt, ...); +int smb_poke_mbc(struct mbuf_chain *buf, int offset, char *fmt, ...); + +void smbsr_encode_header(struct smb_request *sr, int wct, + int bcc, char *fmt, ...); + +int smb_xlate_dialect_str_to_cd(char *); +char *smb_xlate_com_cd_to_str(int); +char *smb_xlate_dialect_cd_to_str(int); + +void smb_od_destruct(struct smb_session *, struct smb_odir *); +int smbd_fs_query(struct smb_request *, struct smb_fqi *, int); +int smb_component_match(struct smb_request *sr, ino64_t fileid, + struct smb_odir *od, smb_odir_context_t *pc); + +int smb_lock_range_access(struct smb_request *, struct smb_node *, + uint64_t, uint64_t, uint32_t desired_access); + +/* + * Socket functions + */ +struct sonode *smb_socreate(int domain, int type, int protocol); +void smb_soshutdown(struct sonode *so); +void smb_sodestroy(struct sonode *so); +int smb_sosend(struct sonode *so, void *msg, size_t len); +int smb_sorecv(struct sonode *so, void *msg, size_t len); +int smb_iov_sosend(struct sonode *so, iovec_t *iop, int iovlen, + size_t total_len); +int smb_iov_sorecv(struct sonode *so, iovec_t *iop, int iovlen, + size_t total_len); + +/* + * SMB RPC interface + */ +int smb_rpc_open(struct smb_request *sr); +void smb_rpc_close(struct smb_ofile *of); +int smb_rpc_transact(struct smb_request *sr, struct uio *uio); +int smb_rpc_read(struct smb_request *sr, struct uio *uio); +int smb_rpc_write(struct smb_request *sr, struct uio *uio); + +/* + * SMB node functions (file smb_node.c) + */ +struct smb_node *smb_node_lookup(struct smb_request *sr, struct open_param *op, + cred_t *cr, vnode_t *vp, char *od_name, smb_node_t *dir_snode, + smb_node_t *unnamed_node, smb_attr_t *attr); +struct smb_node *smb_stream_node_lookup(struct smb_request *sr, cred_t *cr, + smb_node_t *fnode, vnode_t *xattrdirvp, vnode_t *vp, char *stream_name, + smb_attr_t *ret_attr); +void smb_node_ref(smb_node_t *node); +void smb_node_release(smb_node_t *node); +int smb_node_assert(smb_node_t *node, const char *file, int line); +int smb_node_rename(smb_node_t *from_dir_snode, smb_node_t *ret_snode, + smb_node_t *to_dir_snode, char *to_name); +int smb_node_root_init(); +void smb_node_root_fini(); +void smb_node_add_lock(smb_node_t *node, smb_lock_t *lock); +void smb_node_destroy_lock(smb_node_t *node, smb_lock_t *lock); +void smb_node_destroy_lock_by_ofile(smb_node_t *node, smb_ofile_t *file); +uint64_t smb_node_get_size(smb_node_t *node, smb_attr_t *attr); +void smb_node_set_time(struct smb_node *node, timestruc_t *crtime, + timestruc_t *mtime, timestruc_t *atime, + timestruc_t *ctime, unsigned int what); +timestruc_t *smb_node_get_crtime(struct smb_node *node); +timestruc_t *smb_node_get_atime(struct smb_node *node); +timestruc_t *smb_node_get_ctime(struct smb_node *node); +timestruc_t *smb_node_get_mtime(struct smb_node *node); +void smb_node_set_dosattr(struct smb_node *, uint32_t); +uint32_t smb_node_get_dosattr(struct smb_node *node); +int smb_node_set_delete_on_close(smb_node_t *, cred_t *); +void smb_node_reset_delete_on_close(smb_node_t *); + + + +/* + * Pathname functions + */ + +int smb_pathname_reduce(struct smb_request *, cred_t *, + const char *, smb_node_t *, smb_node_t *, smb_node_t **, char *); + +int smb_pathname(struct smb_request *, char *, int, smb_node_t *, + smb_node_t *, smb_node_t **, smb_node_t **, cred_t *); + +/* + * Share functions + */ + +int smb_share_export(char *); +int smb_share_unexport(char *, char *); + +/* + * smb_vfs functions + */ + +boolean_t smb_vfs_hold(vfs_t *); +void smb_vfs_rele(vfs_t *); +void smb_vfs_rele_all(void); + +/* + * String manipulation function + */ +char *smb_kstrdup(const char *s, size_t n); + +int smb_sync_fsattr(struct smb_request *sr, cred_t *cr, + struct smb_node *node); + +int smb_com_create_directory(struct smb_request *sr); +DWORD smb_validate_dirname(char *path); + +int smb_com_trans2_create_directory(struct smb_request *sr, struct smb_xa *xa); +int smb_com_trans2_find_first2(struct smb_request *sr, struct smb_xa *xa); +int smb_com_trans2_find_next2(struct smb_request *sr, struct smb_xa *xa); +int smb_com_trans2_query_fs_information(struct smb_request *sr, + struct smb_xa *xa); +int smb_com_trans2_query_path_information( + struct smb_request *sr, struct smb_xa *xa); +int smb_com_trans2_query_file_information( + struct smb_request *sr, struct smb_xa *xa); +int smb_com_trans2_set_path_information( + struct smb_request *sr, struct smb_xa *xa); +int smb_com_trans2_set_file_information( + struct smb_request *sr, struct smb_xa *xa); + +void smb_encode_stream_info(struct smb_request *sr, struct smb_xa *xa, + smb_node_t *snode, smb_attr_t *attr); + +int smb_nt_transact_create(struct smb_request *sr, struct smb_xa *xa); +int smb_nt_transact_notify_change(struct smb_request *sr, struct smb_xa *xa); +int smb_nt_transact_query_security_info(struct smb_request *sr, + struct smb_xa *xa); +int smb_nt_transact_set_security_info(struct smb_request *sr, + struct smb_xa *xa); +int smb_nt_transact_ioctl(struct smb_request *sr, struct smb_xa *xa); + +/* NOTIFY CHANGE */ +int smb_reply_notify_change_request(smb_request_t *sr); +void smb_process_session_notify_change_queue(struct smb_session *session); +void smb_process_node_notify_change_queue(struct smb_node *node); +void smb_reply_specific_cancel_request(struct smb_request *sr); + +void smb_fem_fcn_install(smb_node_t *node); +void smb_fem_fcn_uninstall(smb_node_t *node); + +/* FEM */ + +int smb_fem_init(); +void smb_fem_shutdown(); + +int smb_try_grow(struct smb_request *sr, int64_t new_size); + +/* functions from smb_memory_manager.c */ + +void *smbsr_malloc(smb_malloc_list *, size_t); +void *smbsr_realloc(void *, size_t); +void smbsr_free_malloc_list(smb_malloc_list *); + +void smbsr_rq_notify(smb_request_t *sr, + smb_session_t *session, smb_tree_t *tree); + +unsigned short smb_worker_getnum(); +int smb_common_close(struct smb_request *sr, uint32_t last_wtime); +void smb_preset_delete_on_close(struct smb_ofile *file); +void smb_commit_delete_on_close(struct smb_ofile *file); + +int smb_stream_parse_name(char *name, char *u_stream_name, + char *stream_name); + +uint32_t smb_get_gmtoff(void); +void smb_set_gmtoff(uint32_t); + +void smb_errmap_unix2smb(int en, smb_error_t *smberr); +DWORD smb_trans2_set_information(struct smb_request *sr, + smb_trans2_setinfo_t *info, + smb_error_t *smberr); + +/* SMB signing routines smb_signing.c */ +void smb_sign_init(struct smb_request *req, + smb_session_key_t *session_key, char *resp, int resp_len); + +int smb_sign_check_request(struct smb_request *req); + +int smb_sign_check_secondary(struct smb_request *req, unsigned int seqnum); + +void smb_sign_reply(struct smb_request *req, struct mbuf_chain *reply); + +uint32_t smb_mode_to_dos_attributes(smb_attr_t *ap); +int smb_sattr_check(smb_attr_t *ap, char *name, unsigned short sattr); + +smb_request_t *smb_request_alloc(struct smb_session *session, + int request_length); +void smb_request_cancel(smb_request_t *sr); +void smb_request_free(smb_request_t *sr); + +smb_session_t *smb_session_create(struct sonode *new_so, uint16_t port); +void smb_session_delete(smb_session_t *session); +void smb_session_cancel(smb_session_t *session); +void smb_session_cancel_requests(smb_session_t *session); +void smb_session_config(smb_session_t *session); +void smb_session_reject(smb_session_t *session, char *reason); +void smb_session_disconnect_share(char *); +void smb_session_disconnect_volume(fs_desc_t *); + + +/* + * ofile functions (file smb_ofile.c) + */ +smb_ofile_t *smb_ofile_lookup_by_fid(smb_tree_t *tree, uint16_t fid); +smb_ofile_t *smb_ofile_open(smb_tree_t *tree, smb_node_t *node, uint16_t pid, + uint32_t access_granted, uint32_t create_options, uint32_t share_access, + uint16_t ftype, char *pipe_name, uint32_t rpc_fid, smb_error_t *err); +int smb_ofile_close(smb_ofile_t *ofile, uint32_t last_wtime); +uint32_t smb_ofile_access(smb_ofile_t *ofile, cred_t *cr, uint32_t access); +int smb_ofile_seek(smb_ofile_t *of, ushort_t mode, int32_t off, + uint32_t *retoff); +void smb_ofile_release(smb_ofile_t *ofile); +void smb_ofile_close_all(smb_tree_t *tree); +void smb_ofile_close_all_by_pid(smb_tree_t *tree, uint16_t pid); +void smb_ofile_set_flags(smb_ofile_t *of, uint32_t flags); +void smb_ofile_close_timestamp_update(smb_ofile_t *of, uint32_t last_wtime); +boolean_t smb_ofile_is_open(smb_ofile_t *of); +#define smb_ofile_granted_access(_of_) ((_of_)->f_granted_access) + +/* + * odir functions (file smb_odir.c) + */ +smb_odir_t *smb_odir_open(smb_tree_t *tree, smb_node_t *node, char *pattern, + uint16_t pid, unsigned short sattr); +void smb_odir_close(smb_odir_t *od); +void smb_odir_close_all(smb_tree_t *tree); +void smb_odir_close_all_by_pid(smb_tree_t *tree, uint16_t pid); +void smb_odir_release(smb_odir_t *od); +smb_odir_t *smb_odir_lookup_by_sid(smb_tree_t *tree, uint16_t sid); + +/* + * SMB user functions (file smb_user.c) + */ +smb_user_t *smb_user_login(smb_session_t *, cred_t *, + char *, char *, uint32_t, uint32_t, uint32_t); +smb_user_t *smb_user_dup(smb_user_t *); +void smb_user_logoff(smb_user_t *user); +void smb_user_logoff_all(smb_session_t *session); +smb_user_t *smb_user_lookup_by_uid(smb_session_t *, cred_t **, uint16_t); +smb_user_t *smb_user_lookup_by_name(smb_session_t *, char *, char *); +smb_user_t *smb_user_lookup_by_state(smb_session_t *, smb_user_t *user); +void smb_user_disconnect_share(smb_user_t *user, char *sharename); +void smb_user_disconnect_volume(smb_user_t *user, fs_desc_t *fsd); +void smb_user_release(smb_user_t *user); + +/* + * SMB tree functions (file smb_tree.c) + */ +smb_tree_t *smb_tree_connect(smb_user_t *user, uint16_t access_flags, + char *sharename, char *resource, int32_t rt_share, + smb_node_t *snode, fsvol_attr_t *vol_attr); +void smb_tree_disconnect(smb_tree_t *tree); +void smb_tree_disconnect_all(smb_user_t *user); +void smb_tree_close_all_by_pid(smb_user_t *user, uint16_t pid); +smb_tree_t *smb_tree_lookup_by_tid(smb_user_t *user, uint16_t tid); +smb_tree_t *smb_tree_lookup_by_name(smb_user_t *, char *, smb_tree_t *); +smb_tree_t *smb_tree_lookup_by_fsd(smb_user_t *, fs_desc_t *, smb_tree_t *); +void smb_tree_release(smb_tree_t *tree); + +uint32_t smb_user_get_num(void); +void smb_dr_user_free(smb_dr_user_ctx_t *uinfo); +void smb_dr_ulist_free(smb_dr_ulist_t *ulist); +int smb_dr_ulist_get(int offset, smb_dr_ulist_t *dr_ulist); + +/* + * SMB user's credential functions + */ +cred_t *smb_cred_create(smb_token_t *, uint32_t *); +void smb_cred_rele(cred_t *cr); +int smb_cred_is_member(cred_t *cr, nt_sid_t *sid); + +smb_xa_t *smb_xa_create(smb_session_t *session, smb_request_t *sr, + uint32_t total_parameter_count, uint32_t total_data_count, + uint32_t max_parameter_count, uint32_t max_data_count, + uint32_t max_setup_count, uint32_t setup_word_count); +void smb_xa_delete(smb_xa_t *xa); +smb_xa_t *smb_xa_hold(smb_xa_t *xa); +void smb_xa_rele(smb_session_t *session, smb_xa_t *xa); +int smb_xa_open(smb_xa_t *xa); +void smb_xa_close(smb_xa_t *xa); +int smb_xa_complete(smb_xa_t *xa); +smb_xa_t *smb_xa_find(smb_session_t *session, uint16_t pid, uint16_t mid); + +struct mbuf *smb_mbuf_get(uchar_t *buf, int nbytes); +struct mbuf *smb_mbuf_allocate(struct uio *uio); +void smb_mbuf_trim(struct mbuf *mhead, int nbytes); + +void smb_check_status(void); +int smb_handle_write_raw(smb_session_t *session, smb_request_t *sr); + +void smb_winpipe_init(void); +void smb_winpipe_fini(void); +int smb_winpipe_open(void); +void smb_winpipe_close(void); +int smb_winpipe_call(smb_request_t *, mlsvc_pipe_t *, mlsvc_stream_t *, + uint16_t, uint32_t *); + +void smb_reconnection_check(struct smb_session *session); + +uint32_t nt_to_unix_time(uint64_t nt_time, timestruc_t *unix_time); +uint64_t unix_to_nt_time(timestruc_t *); + +int netbios_name_isvalid(char *in, char *out); + +size_t +unicodestooems(char *oemstring, const mts_wchar_t *unicodestring, + size_t nbytes, unsigned int cpid); + +size_t oemstounicodes(mts_wchar_t *unicodestring, const char *oemstring, + size_t nwchars, unsigned int cpid); + +int uioxfer(struct uio *src_uio, struct uio *dst_uio, int n); + +int smb_match_name(ino64_t fileid, char *name, char *shortname, + char *name83, char *pattern, int ignore_case); +int is_dot_or_dotdot(char *name); +int token2buf(smb_token_t *token, char *buf); + +/* + * Pool ID function prototypes + */ +int smb_idpool_constructor(smb_idpool_t *pool); +void smb_idpool_destructor(smb_idpool_t *pool); +int smb_idpool_alloc(smb_idpool_t *pool, uint16_t *id); +void smb_idpool_free(smb_idpool_t *pool, uint16_t id); + +/* + * SMB thread function prototypes + */ +void smb_session_worker(void *arg); + +/* + * SMB locked list function prototypes + */ +void smb_llist_constructor(smb_llist_t *, size_t, size_t); +void smb_llist_destructor(smb_llist_t *); +void smb_llist_insert_head(smb_llist_t *ll, void *obj); +void smb_llist_insert_tail(smb_llist_t *ll, void *obj); +void smb_llist_remove(smb_llist_t *ll, void *obj); +int smb_llist_upgrade(smb_llist_t *ll); +uint32_t smb_llist_get_count(smb_llist_t *ll); +#define smb_llist_enter(ll, mode) rw_enter(&(ll)->ll_lock, mode) +#define smb_llist_exit(ll) rw_exit(&(ll)->ll_lock) +#define smb_llist_head(ll) list_head(&(ll)->ll_list) +#define smb_llist_next(ll, obj) list_next(&(ll)->ll_list, obj) +int smb_account_connected(smb_user_t *user); + +/* + * SMB Synchronized list function prototypes + */ +void smb_slist_constructor(smb_slist_t *, size_t, size_t); +void smb_slist_destructor(smb_slist_t *); +void smb_slist_insert_head(smb_slist_t *sl, void *obj); +void smb_slist_insert_tail(smb_slist_t *sl, void *obj); +void smb_slist_remove(smb_slist_t *sl, void *obj); +void smb_slist_wait_for_empty(smb_slist_t *sl); +void smb_slist_exit(smb_slist_t *sl); +uint32_t smb_slist_move_tail(list_t *lst, smb_slist_t *sl); +void smb_slist_obj_move(smb_slist_t *dst, smb_slist_t *src, void *obj); +#define smb_slist_enter(sl) mutex_enter(&(sl)->sl_mutex) +#define smb_slist_head(sl) list_head(&(sl)->sl_list) +#define smb_slist_next(sl, obj) list_next(&(sl)->sl_list, obj) + +void smb_rwx_init(smb_rwx_t *rwx); +void smb_rwx_destroy(smb_rwx_t *rwx); +#define smb_rwx_rwenter(rwx, mode) rw_enter(&(rwx)->rwx_lock, mode) +void smb_rwx_rwexit(smb_rwx_t *rwx); +int smb_rwx_rwwait(smb_rwx_t *rwx, clock_t timeout); +#define smb_rwx_xenter(rwx) mutex_enter(&(rwx)->rwx_mutex) +#define smb_rwx_xexit(rwx) mutex_exit(&(rwx)->rwx_mutex) +krw_t smb_rwx_rwupgrade(smb_rwx_t *rwx); +void smb_rwx_rwdowngrade(smb_rwx_t *rwx, krw_t mode); + +void smb_thread_init(smb_thread_t *, char *, smb_thread_ep_t, void *, + smb_thread_aw_t, void *); +void smb_thread_destroy(smb_thread_t *); +int smb_thread_start(smb_thread_t *); +void smb_thread_stop(smb_thread_t *); +void smb_thread_signal(smb_thread_t *); +boolean_t smb_thread_continue(smb_thread_t *); +boolean_t smb_thread_continue_nowait(smb_thread_t *); +boolean_t smb_thread_continue_timedwait(smb_thread_t *, int /* seconds */); +void smb_thread_set_awaken(smb_thread_t *, smb_thread_aw_t, void *); + +uint32_t smb_denymode_to_sharemode(uint32_t desired_access, char *fname); +uint32_t smb_ofun_to_crdisposition(uint16_t ofun); + +void smb_audit_buf_node_create(smb_node_t *node); +void smb_audit_buf_node_destroy(smb_node_t *node); +#define smb_audit_node(_n_) \ + if ((_n_)->n_audit_buf) { \ + smb_audit_record_node_t *anr; \ + \ + anr = (_n_)->n_audit_buf->anb_records; \ + anr += (_n_)->n_audit_buf->anb_index; \ + (_n_)->n_audit_buf->anb_index++; \ + (_n_)->n_audit_buf->anb_index &= \ + (_n_)->n_audit_buf->anb_max_index; \ + anr->anr_refcnt = node->n_refcnt; \ + anr->anr_depth = getpcstack(anr->anr_stack, \ + SMB_AUDIT_STACK_DEPTH); \ + } + +/* 100's of ns between 1/1/1970 and 1/1/1601 */ +#define NT_TIME_BIAS (134774LL * 24LL * 60LL * 60LL * 10000000LL) + +#ifdef __cplusplus +} +#endif + +#endif /* _SMB_KPROTO_H_ */ diff --git a/usr/src/uts/common/smbsrv/smb_privilege.h b/usr/src/uts/common/smbsrv/smb_privilege.h new file mode 100644 index 000000000000..c03455d4690d --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_privilege.h @@ -0,0 +1,197 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMB_PRIVILEGE_H +#define _SMB_PRIVILEGE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Privileges + * + * Privileges apply to all objects and over-ride the access controls + * in an object's security descriptor in a manner specific to each + * privilege. Privileges are still not full defined. Privileges are + * defined in a set structure (LUID = Locally Unique Identifier). + * + * The default LUID, name and display names defined on NT 4.0 are: + * LUID Privilege Name Display Name + * ---- -------------- ------------ + * 0:2 SeCreateTokenPrivilege Create a token object + * 0:3 SeAssignPrimaryTokenPrivilege Replace a process level token + * 0:4 SeLockMemoryPrivilege Lock pages in memory + * 0:5 SeIncreaseQuotaPrivilege Increase quotas + * 0:6 SeMachineAccountPrivilege Add workstations to domain + * 0:7 SeTcbPrivilege Act as part of the operating system + * 0:8 SeSecurityPrivilege Manage auditing and security log + * 0:9 SeTakeOwnershipPrivilege Take ownership of files or other objects + * 0:10 SeLoadDriverPrivilege Load and unload device drivers + * 0:11 SeSystemProfilePrivilege Profile system performance + * 0:12 SeSystemtimePrivilege Change the system time + * 0:13 SeProfileSingleProcessPrivilege Profile single process + * 0:14 SeIncreaseBasePriorityPrivilege Increase scheduling priority + * 0:15 SeCreatePagefilePrivilege Create a pagefile + * 0:16 SeCreatePermanentPrivilege Create permanent shared objects + * 0:17 SeBackupPrivilege Back up files and directories + * 0:18 SeRestorePrivilege Restore files and directories + * 0:19 SeShutdownPrivilege Shut down the system + * 0:20 SeDebugPrivilege Debug programs + * 0:21 SeAuditPrivilege Generate security audits + * 0:22 SeSystemEnvironmentPrivilege Modify firmware environment values + * 0:23 SeChangeNotifyPrivilege Bypass traverse checking + * 0:24 SeRemoteShutdownPrivilege Force shutdown from a remote system + */ + +/* + * Privilege names + */ +#define SE_CREATE_TOKEN_NAME "SeCreateTokenPrivilege" +#define SE_ASSIGNPRIMARYTOKEN_NAME "SeAssignPrimaryTokenPrivilege" +#define SE_LOCK_MEMORY_NAME "SeLockMemoryPrivilege" +#define SE_INCREASE_QUOTA_NAME "SeIncreaseQuotaPrivilege" +#define SE_UNSOLICITED_INPUT_NAME "SeUnsolicitedInputPrivilege" +#define SE_MACHINE_ACCOUNT_NAME "SeMachineAccountPrivilege" +#define SE_TCB_NAME "SeTcbPrivilege" +#define SE_SECURITY_NAME "SeSecurityPrivilege" +#define SE_TAKE_OWNERSHIP_NAME "SeTakeOwnershipPrivilege" +#define SE_LOAD_DRIVER_NAME "SeLoadDriverPrivilege" +#define SE_SYSTEM_PROFILE_NAME "SeSystemProfilePrivilege" +#define SE_SYSTEMTIME_NAME "SeSystemtimePrivilege" +#define SE_PROF_SINGLE_PROCESS_NAME "SeProfileSingleProcessPrivilege" +#define SE_INC_BASE_PRIORITY_NAME "SeIncreaseBasePriorityPrivilege" +#define SE_CREATE_PAGEFILE_NAME "SeCreatePagefilePrivilege" +#define SE_CREATE_PERMANENT_NAME "SeCreatePermanentPrivilege" +#define SE_BACKUP_NAME "SeBackupPrivilege" +#define SE_RESTORE_NAME "SeRestorePrivilege" +#define SE_SHUTDOWN_NAME "SeShutdownPrivilege" +#define SE_DEBUG_NAME "SeDebugPrivilege" +#define SE_AUDIT_NAME "SeAuditPrivilege" +#define SE_SYSTEM_ENVIRONMENT_NAME "SeSystemEnvironmentPrivilege" +#define SE_CHANGE_NOTIFY_NAME "SeChangeNotifyPrivilege" +#define SE_REMOTE_SHUTDOWN_NAME "SeRemoteShutdownPrivilege" + +#define SE_CREATE_TOKEN_LUID 2 +#define SE_ASSIGNPRIMARYTOKEN_LUID 3 +#define SE_LOCK_MEMORY_LUID 4 +#define SE_INCREASE_QUOTA_LUID 5 +#define SE_MACHINE_ACCOUNT_LUID 6 +#define SE_TCB_LUID 7 +#define SE_SECURITY_LUID 8 +#define SE_TAKE_OWNERSHIP_LUID 9 +#define SE_LOAD_DRIVER_LUID 10 +#define SE_SYSTEM_PROFILE_LUID 11 +#define SE_SYSTEMTIME_LUID 12 +#define SE_PROF_SINGLE_PROCESS_LUID 13 +#define SE_INC_BASE_PRIORITY_LUID 14 +#define SE_CREATE_PAGEFILE_LUID 15 +#define SE_CREATE_PERMANENT_LUID 16 +#define SE_BACKUP_LUID 17 +#define SE_RESTORE_LUID 18 +#define SE_SHUTDOWN_LUID 19 +#define SE_DEBUG_LUID 20 +#define SE_AUDIT_LUID 21 +#define SE_SYSTEM_ENVIRONMENT_LUID 22 +#define SE_CHANGE_NOTIFY_LUID 23 +#define SE_REMOTE_SHUTDOWN_LUID 24 + +/* + * Privilege attributes + */ +#define SE_PRIVILEGE_DISABLED 0x00000000 +#define SE_PRIVILEGE_ENABLED_BY_DEFAULT 0x00000001 +#define SE_PRIVILEGE_ENABLED 0x00000002 +#define SE_PRIVILEGE_USED_FOR_ACCESS 0x80000000 + +/* + * Privilege Set Control flags + */ +#define PRIVILEGE_SET_ALL_NECESSARY 1 + +typedef struct smb_luid { + uint32_t lo_part; + uint32_t hi_part; +} smb_luid_t; + + +typedef struct smb_luid_attrs { + smb_luid_t luid; + uint32_t attrs; +} smb_luid_attrs_t; + + +typedef struct smb_privset { + uint32_t priv_cnt; + uint32_t control; + smb_luid_attrs_t priv[ANY_SIZE_ARRAY]; +} smb_privset_t; + +/* + * These are possible value for smb_privinfo_t.flags + * + * PF_PRESENTABLE Privilege is user visible + */ +#define PF_PRESENTABLE 0x1 + +/* + * Structure for passing privilege name and id information around within + * the system. Note that we are only storing the low uint32_t of the LUID; + * the high part is always zero here. + */ +typedef struct smb_privinfo { + uint32_t id; + char *name; + char *display_name; + uint16_t flags; +} smb_privinfo_t; + +smb_privinfo_t *smb_priv_getbyvalue(uint32_t id); +smb_privinfo_t *smb_priv_getbyname(char *name); +int smb_priv_presentable_num(void); +int smb_priv_presentable_ids(uint32_t *ids, int num); +smb_privset_t *smb_privset_new(); +int smb_privset_size(); +void smb_privset_init(smb_privset_t *privset); +void smb_privset_free(smb_privset_t *privset); +void smb_privset_copy(smb_privset_t *dst, smb_privset_t *src); +void smb_privset_enable(smb_privset_t *privset, uint32_t id); +int smb_privset_query(smb_privset_t *privset, uint32_t id); +void smb_privset_log(smb_privset_t *privset); + +/* XDR routines */ +extern bool_t xdr_smb_luid_t(); +extern bool_t xdr_smb_luid_attrs_t(); +extern bool_t xdr_smb_privset_t(); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMB_PRIVILEGE_H */ diff --git a/usr/src/uts/common/smbsrv/smb_secdesc.h b/usr/src/uts/common/smbsrv/smb_secdesc.h new file mode 100644 index 000000000000..68b890a8e41a --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_secdesc.h @@ -0,0 +1,397 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMB_SECDESC_H +#define _SMB_SECDESC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Discretionary Access Control List (DACL) + * + * A Discretionary Access Control List (DACL), often abbreviated to + * ACL, is a list of access controls which either allow or deny access + * for users or groups to a resource. There is a list header followed + * by a list of access control entries (ACE). Each ACE specifies the + * access allowed or denied to a single user or group (identified by + * a SID). + * + * There is another access control list object called a System Access + * Control List (SACL), which is used to control auditing, but no + * support is provideed for SACLs at this time. + * + * ACL header format: + * + * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-------------------------------+---------------+---------------+ + * | AclSize | Sbz1 | AclRevision | + * +-------------------------------+---------------+---------------+ + * | Sbz2 | AceCount | + * +-------------------------------+-------------------------------+ + * + * AclRevision specifies the revision level of the ACL. This value should + * be ACL_REVISION, unless the ACL contains an object-specific ACE, in which + * case this value must be ACL_REVISION_DS. All ACEs in an ACL must be at the + * same revision level. + * + * ACE header format: + * + * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +---------------+-------+-------+---------------+---------------+ + * | AceSize | AceFlags | AceType | + * +---------------+-------+-------+---------------+---------------+ + * + * Access mask format: + * + * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +---------------+---------------+-------------------------------+ + * |G|G|G|G|Res'd|A| StandardRights| SpecificRights | + * |R|W|E|A| |S| | | + * +-+-------------+---------------+-------------------------------+ + * + * typedef struct ACCESS_MASK { + * WORD SpecificRights; + * BYTE StandardRights; + * BYTE AccessSystemAcl : 1; + * BYTE Reserved : 3; + * BYTE GenericAll : 1; + * BYTE GenericExecute : 1; + * BYTE GenericWrite : 1; + * BYTE GenericRead : 1; + * } ACCESS_MASK; + * + */ + +#define ACL_REVISION1 1 +#define ACL_REVISION2 2 +#define MIN_ACL_REVISION2 ACL_REVISION2 +#define ACL_REVISION3 3 +#define ACL_REVISION4 4 +#define MAX_ACL_REVISION ACL_REVISION4 + +/* + * Current ACE and ACL revision Levels + */ +#define ACE_REVISION 1 +#define ACL_REVISION ACL_REVISION2 +#define ACL_REVISION_DS ACL_REVISION4 + + +#define ACCESS_ALLOWED_ACE_TYPE 0 +#define ACCESS_DENIED_ACE_TYPE 1 +#define SYSTEM_AUDIT_ACE_TYPE 2 +#define SYSTEM_ALARM_ACE_TYPE 3 + +/* + * se_flags + * ---------- + * Specifies a set of ACE type-specific control flags. This member can be a + * combination of the following values. + * + * CONTAINER_INHERIT_ACE: Child objects that are containers, such as + * directories, inherit the ACE as an effective ACE. The inherited + * ACE is inheritable unless the NO_PROPAGATE_INHERIT_ACE bit flag + * is also set. + * + * INHERIT_ONLY_ACE: Indicates an inherit-only ACE which does not control + * access to the object to which it is attached. + * If this flag is not set, + * the ACE is an effective ACE which controls access to the object + * to which it is attached. + * Both effective and inherit-only ACEs can be inherited + * depending on the state of the other inheritance flags. + * + * INHERITED_ACE: Windows 2000/XP: Indicates that the ACE was inherited. + * The system sets this bit when it propagates an + * inherited ACE to a child object. + * + * NO_PROPAGATE_INHERIT_ACE: If the ACE is inherited by a child object, the + * system clears the OBJECT_INHERIT_ACE and CONTAINER_INHERIT_ACE + * flags in the inherited ACE. + * This prevents the ACE from being inherited by + * subsequent generations of objects. + * + * OBJECT_INHERIT_ACE: Noncontainer child objects inherit the ACE as an + * effective ACE. For child objects that are containers, + * the ACE is inherited as an inherit-only ACE unless the + * NO_PROPAGATE_INHERIT_ACE bit flag is also set. + */ +#define OBJECT_INHERIT_ACE 0x01 +#define CONTAINER_INHERIT_ACE 0x02 +#define NO_PROPOGATE_INHERIT_ACE 0x04 +#define INHERIT_ONLY_ACE 0x08 +#define INHERITED_ACE 0x10 +#define INHERIT_MASK_ACE 0x1F + + +/* + * These flags are only used in system audit or alarm ACEs to + * indicate when an audit message should be generated, i.e. + * on successful access or on unsuccessful access. + */ +#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 +#define FAILED_ACCESS_ACE_FLAG 0x80 + + +/* + * AclSize is the size, in bytes, allocated for the ACL. This + * includes the ACL header, ACEs and remaining free space in + * the buffer. sl_acecnt is the number of ACES in the ACL. + */ +typedef struct smb_acl { + uint8_t sl_revision; + uint8_t sl_sbz1; + uint16_t sl_size; + uint16_t sl_acecnt; + uint16_t sl_sbz2; + /* immediately followed by ACE[]s */ +} smb_acl_t; + + +/* + * se_type denotes the type of the ace, there are some predefined + * ACE types. se_size is the size, in bytes, of ACE. se_flags are + * the ACE flags for auditing and inheritance. + */ +typedef struct smb_ace_hdr { + uint8_t se_type; + uint8_t se_flags; + uint16_t se_size; +} smb_ace_hdr_t; + + +typedef struct smb_ace { + smb_ace_hdr_t se_header; + uint32_t se_mask; + nt_sid_t se_sid; /* variable length */ +} smb_ace_t; + + +/* + * Security Descriptor (SD) + * + * Security descriptors provide protection for objects, for example + * files and directories. It identifies the owner and primary group + * (SIDs) and contains an access control list. When a user tries to + * access an object his SID is compared to the permissions in the + * DACL to determine if access should be allowed or denied. Note that + * this is a simplification because there are other factors, such as + * default behavior and privileges to be taken into account (see also + * access tokens). + * + * The boolean flags have the following meanings when set: + * + * SE_OWNER_DEFAULTED indicates that the SID pointed to by the Owner + * field was provided by a defaulting mechanism rather than explicitly + * provided by the original provider of the security descriptor. This + * may affect the treatment of the SID with respect to inheritance of + * an owner. + * + * SE_GROUP_DEFAULTED indicates that the SID in the Group field was + * provided by a defaulting mechanism rather than explicitly provided + * by the original provider of the security descriptor. This may + * affect the treatment of the SID with respect to inheritance of a + * primary group. + * + * SE_DACL_PRESENT indicates that the security descriptor contains a + * discretionary ACL. If this flag is set and the Dacl field of the + * SECURITY_DESCRIPTOR is null, then a null ACL is explicitly being + * specified. + * + * SE_DACL_DEFAULTED indicates that the ACL pointed to by the Dacl + * field was provided by a defaulting mechanism rather than explicitly + * provided by the original provider of the security descriptor. This + * may affect the treatment of the ACL with respect to inheritance of + * an ACL. This flag is ignored if the DaclPresent flag is not set. + * + * SE_SACL_PRESENT indicates that the security descriptor contains a + * system ACL pointed to by the Sacl field. If this flag is set and + * the Sacl field of the SECURITY_DESCRIPTOR is null, then an empty + * (but present) ACL is being specified. + * + * SE_SACL_DEFAULTED indicates that the ACL pointed to by the Sacl + * field was provided by a defaulting mechanism rather than explicitly + * provided by the original provider of the security descriptor. This + * may affect the treatment of the ACL with respect to inheritance of + * an ACL. This flag is ignored if the SaclPresent flag is not set. + * + * SE_DACL_PROTECTED Prevents ACEs set on the DACL of the parent container + * (and any objects above the parent container in the directory hierarchy) + * from being applied to the object's DACL. + * + * SE_SACL_PROTECTED Prevents ACEs set on the SACL of the parent container + * (and any objects above the parent container in the directory hierarchy) + * from being applied to the object's SACL. + * + * Note that the SE_DACL_PRESENT flag needs to be present to set + * SE_DACL_PROTECTED and SE_SACL_PRESENT needs to be present to set + * SE_SACL_PROTECTED. + * + * SE_SELF_RELATIVE indicates that the security descriptor is in self- + * relative form. In this form, all fields of the security descriptor + * are contiguous in memory and all pointer fields are expressed as + * offsets from the beginning of the security descriptor. + * + * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +---------------------------------------------------------------+ + * | Control |Reserved1 (SBZ)| Revision | + * +---------------------------------------------------------------+ + * | Owner | + * +---------------------------------------------------------------+ + * | Group | + * +---------------------------------------------------------------+ + * | Sacl | + * +---------------------------------------------------------------+ + * | Dacl | + * +---------------------------------------------------------------+ + * + */ + +#define SMB_OWNER_SECINFO 0x0001 +#define SMB_GROUP_SECINFO 0x0002 +#define SMB_DACL_SECINFO 0x0004 +#define SMB_SACL_SECINFO 0x0008 +#define SMB_ALL_SECINFO 0x000F +#define SMB_ACL_SECINFO (SMB_DACL_SECINFO | SMB_SACL_SECINFO) + +#define SECURITY_DESCRIPTOR_REVISION 1 + + +#define SE_OWNER_DEFAULTED 0x0001 +#define SE_GROUP_DEFAULTED 0x0002 +#define SE_DACL_PRESENT 0x0004 +#define SE_DACL_DEFAULTED 0x0008 +#define SE_SACL_PRESENT 0x0010 +#define SE_SACL_DEFAULTED 0x0020 +#define SE_DACL_AUTO_INHERIT_REQ 0x0100 +#define SE_SACL_AUTO_INHERIT_REQ 0x0200 +#define SE_DACL_AUTO_INHERITED 0x0400 +#define SE_SACL_AUTO_INHERITED 0x0800 +#define SE_DACL_PROTECTED 0x1000 +#define SE_SACL_PROTECTED 0x2000 +#define SE_SELF_RELATIVE 0x8000 + +#define SE_DACL_INHERITANCE_MASK 0x1500 +#define SE_SACL_INHERITANCE_MASK 0x2A00 + +/* + * Security descriptor structures: + * + * smb_sd_t SD in SMB pointer form + * smb_sdbuf_t SD in SMB self-relative form + * smb_fssd_t SD in filesystem form + * + * We have to use two different structures to represent + * pointer form and self-relative form of the security + * descriptor because in SR form the offsets are 4-byte + * but in pointer form, pointers will be 8-byte in 64-bit + * kernel binary. + * + * Filesystems (e.g. ZFS/UFS) don't have something equivalent + * to SD. The items comprising a SMB SD are kept separately in + * filesystem. smb_fssd_t is introduced as a helper to provide + * the required abstraction for CIFS code. + */ +typedef struct smb_sd_hdr { + uint8_t sd_revision; + uint8_t sd_sbz1; + uint16_t sd_control; +} smb_sd_hdr_t; + +typedef struct smb_sd { + smb_sd_hdr_t sd_hdr; + nt_sid_t *sd_owner; /* SID file owner */ + nt_sid_t *sd_group; /* SID group (for POSIX) */ + smb_acl_t *sd_sacl; /* ACL System (audits) */ + smb_acl_t *sd_dacl; /* ACL Discretionary (perm) */ +} smb_sd_t; + +typedef struct smb_sdbuf { + smb_sd_hdr_t sd_hdr; + uint32_t sd_owner_offs; /* SID file owner */ + uint32_t sd_group_offs; /* SID group (for POSIX) */ + uint32_t sd_sacl_offs; /* ACL System (audits) */ + uint32_t sd_dacl_offs; /* ACL Discretionary (perm) */ +} smb_sdbuf_t; + +/* + * values for smb_fssd.sd_flags + */ +#define SMB_FSSD_FLAGS_DIR 0x01 + +typedef struct smb_fssd { + uint32_t sd_secinfo; + uint32_t sd_flags; + uid_t sd_uid; + gid_t sd_gid; + acl_t *sd_zdacl; + acl_t *sd_zsacl; +} smb_fssd_t; + +void smb_sd_init(smb_sd_t *sd, uint8_t revision); +void smb_sd_set_owner(smb_sd_t *sd, nt_sid_t *owner, int defaulted); +void smb_sd_set_group(smb_sd_t *sd, nt_sid_t *group, int defaulted); +void smb_sd_set_dacl(smb_sd_t *sd, int present, smb_acl_t *acl, int defaulted); +void smb_sd_set_sacl(smb_sd_t *sd, int present, smb_acl_t *acl, int defaulted); + +nt_sid_t *smb_sd_get_owner(void *sd, int *defaulted); +nt_sid_t *smb_sd_get_group(void *sd, int *defaulted); +smb_acl_t *smb_sd_get_dacl(void *sd, int *present, int *defaulted); +smb_acl_t *smb_sd_get_sacl(void *sd, int *present, int *defaulted); +uint32_t smb_sd_get_secinfo(void *sd); +uint32_t smb_sd_len(void *sd, uint32_t secinfo); +void smb_sd_log(void *sd); +void smb_sd_term(smb_sd_t *sd); + +smb_acl_t *smb_acl_from_zfs(acl_t *, uid_t, gid_t); +uint32_t smb_acl_to_zfs(smb_acl_t *, uint32_t, int, acl_t **); +int smb_acl_isvalid(smb_acl_t *, int); +uint16_t smb_acl_len(smb_acl_t *); +smb_acl_t *smb_acl_sort(smb_acl_t *); +int smb_acl_copy(uint16_t, smb_acl_t *, smb_acl_t *); +acl_t *smb_acl_inherit(acl_t *, int, int, uid_t); + +smb_ace_t *smb_ace_get(smb_acl_t *acl, uint16_t idx); +int smb_ace_is_generic(int type); +int smb_ace_is_access(int type); +int smb_ace_is_audit(int type); + + +#ifdef __cplusplus +} +#endif + +#endif /* _SMB_SECDESC_H */ diff --git a/usr/src/uts/common/smbsrv/smb_svc_sm.h b/usr/src/uts/common/smbsrv/smb_svc_sm.h new file mode 100644 index 000000000000..d843585edcd7 --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_svc_sm.h @@ -0,0 +1,162 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Structures and type definitions for the SMB module. + */ + +#ifndef _SMBSRV_SMB_SVC_SM_H +#define _SMBSRV_SMB_SVC_SM_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * CIFS Service State Machine Definitions + */ + +/* + * Events + * + * SMB_SVCEVT_UNDEFINED Invalid Event + * SMB_SVCEVT_OPEN Pseudo-device opened + * SMB_SVCEVT_CLOSE Pseudo-device closed + * SMB_SVCEVT_OPEN_SUCCESS Open actions completed successfully + * SMB_SVCEVT_OPEN_FAILED Open actions failed + * SMB_SVCEVT_CLOSE_SUCCESS Close actions completed successfully + * SMB_SVCEVT_CONNECT Connected and listening on SMB session port + * SMB_SVCEVT_DISCONNECT SMB connection dropped or failed to connect + * SMB_SVCEVT_CONFIG New config from smbd + * SMB_SVCEVT_CONFIG_SUCCESS Configuration updated successfully + * SMB_SVCEVT_CONFIG_FAILED Configuration update failed + * SMB_SVCEVT_SESSION_CREATE SMB port listener accepted a connection + * SMB_SVCEVT_SESSION_DELETE Session ended + * SMB_SVCEVT_MAX_EVENT Invalid Event + */ + +typedef enum { + SMB_SVCEVT_UNDEFINED = 0, + SMB_SVCEVT_OPEN, + SMB_SVCEVT_CLOSE, + SMB_SVCEVT_OPEN_SUCCESS, + SMB_SVCEVT_OPEN_FAILED, + SMB_SVCEVT_CLOSE_SUCCESS, + SMB_SVCEVT_CONNECT, + SMB_SVCEVT_DISCONNECT, + SMB_SVCEVT_CONFIG, + SMB_SVCEVT_CONFIG_SUCCESS, + SMB_SVCEVT_CONFIG_FAILED, + SMB_SVCEVT_SESSION_CREATE, + SMB_SVCEVT_SESSION_DELETE, + SMB_SVCEVT_MAX_EVENT +} smb_svcevt_t; + +/* + * States + * + * SMB_SVCSTATE_UNDEFINED Invalid state + * SMB_SVCSTATE_INIT Pseudo-driver loaded/idle + * SMB_SVCSTATE_OPENING Pseudo-driver opened, starting + * SMB_SVCSTATE_CONFIG_WAIT Waiting for configuration + * SMB_SVCSTATE_CONNECTING Waiting for socket bind to SMB + * SMB_SVCSTATE_ONLINE Online, accepting connections + * SMB_SVCSTATE_RECONFIGURING Updating config, no new connections + * SMB_SVCSTATE_DISCONNECTING Smbd requested shutdown, closing socket + * SMB_SVCSTATE_SESSION_CLOSE Shutting down, closing active sessions + * SMB_SVCSTATE_CLOSING Shutting down, releasing resources + * SMB_SVCSTATE_ERROR_SESSION_CLOSE Unexpected SMB socket error, + * closing active sessions + * SMB_SVCSTATE_ERROR_CLOSING Error, releasing resources + * SMB_SVCSTATE_MAX_STATE Invalid state + */ +typedef enum { + SMB_SVCSTATE_UNDEFINED = 0, + SMB_SVCSTATE_INIT, + SMB_SVCSTATE_OPENING, + SMB_SVCSTATE_CONFIG_WAIT, + SMB_SVCSTATE_CONNECTING, + SMB_SVCSTATE_ONLINE, + SMB_SVCSTATE_RECONFIGURING, + SMB_SVCSTATE_DISCONNECTING, + SMB_SVCSTATE_SESSION_CLOSE, + SMB_SVCSTATE_ERROR_SESSION_CLOSE, + SMB_SVCSTATE_CLOSING, + SMB_SVCSTATE_ERROR_CLOSING, + SMB_SVCSTATE_ERROR, + SMB_SVCSTATE_MAX_STATE +} smb_svcstate_t; + +#ifdef _KERNEL +/* Event context */ +typedef struct { + smb_svcevt_t sec_event; + uintptr_t sec_info; +} smb_event_ctx_t; + +/* Service state machine context */ +typedef struct { + taskq_t *ssc_taskq; + krwlock_t ssc_state_rwlock; + kmutex_t ssc_state_cv_mutex; + kcondvar_t ssc_state_cv; + int ssc_started; + int ssc_start_error; + int ssc_disconnect_error; + smb_svcstate_t ssc_state; + smb_svcstate_t ssc_last_state; /* Debug only */ + int ssc_session_creates_waiting; + int ssc_deferred_session_count; + list_t ssc_deferred_sessions; + int ssc_active_session_count; + list_t ssc_active_sessions; + uint32_t ssc_error_no_resources; +} smb_svc_sm_ctx_t; + +/* + * SMB service state machine API + */ + +extern int smb_svcstate_sm_init(smb_svc_sm_ctx_t *svc_sm); +extern void smb_svcstate_sm_fini(smb_svc_sm_ctx_t *svc_sm); +extern int smb_svcstate_sm_start(smb_svc_sm_ctx_t *svc_sm); +extern void smb_svcstate_sm_stop(smb_svc_sm_ctx_t *svc_sm); +extern boolean_t smb_svcstate_sm_busy(void); +extern void smb_svcstate_event(smb_svcevt_t event, uintptr_t event_info); +extern void smb_svcstate_lock_read(smb_svc_sm_ctx_t *svc_sm); +extern void smb_svcstate_unlock(smb_svc_sm_ctx_t *svc_sm); +extern smb_session_t *smb_svcstate_session_getnext(smb_svc_sm_ctx_t *svc_sm, + smb_session_t *prev); +extern int smb_svcstate_session_count(smb_svc_sm_ctx_t *svc_sm); + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_SMB_SVC_SM_H */ diff --git a/usr/src/uts/common/smbsrv/smb_token.h b/usr/src/uts/common/smbsrv/smb_token.h new file mode 100644 index 000000000000..c2bea1e9a9bd --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_token.h @@ -0,0 +1,224 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMB_TOKEN_H +#define _SMB_TOKEN_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * User Session Key + * + * This is part of the MAC key which is required for signing SMB messages. + */ +typedef struct smb_session_key { + uint8_t data[16]; +} smb_session_key_t; + +/* + * Access Token + * + * An access token identifies a user, the user's privileges and the + * list of groups of which the user is a member. This information is + * used when access is requested to an object by comparing this + * information with the DACL in the object's security descriptor. + * + * Only group attributes are defined. No user attributes defined. + */ + +#define SE_GROUP_MANDATORY 0x00000001 +#define SE_GROUP_ENABLED_BY_DEFAULT 0x00000002 +#define SE_GROUP_ENABLED 0x00000004 +#define SE_GROUP_OWNER 0x00000008 +#define SE_GROUP_USE_FOR_DENY_ONLY 0x00000010 +#define SE_GROUP_LOGON_ID 0xC0000000 + +typedef struct smb_sid_attrs { + uint32_t attrs; + nt_sid_t *sid; +} smb_sid_attrs_t; + +/* + * smb_id_t consists of both the Windows security identifier + * and its corresponding POSIX/ephemeral ID. + */ +typedef struct smb_id { + smb_sid_attrs_t i_sidattr; + uid_t i_id; +} smb_id_t; + +/* + * Windows groups (each group SID is associated with a POSIX/ephemeral + * gid. + */ +typedef struct smb_win_grps { + uint16_t wg_count; + smb_id_t wg_groups[ANY_SIZE_ARRAY]; +} smb_win_grps_t; + +/* + * Access Token Flags + * + * SMB_ATF_GUEST Token belongs to guest user + * SMB_ATF_ANON Token belongs to anonymous user + * and it's only good for IPC Connection. + * SMB_ATF_POWERUSER Token belongs to a Power User member + * SMB_ATF_BACKUPOP Token belongs to a Power User member + * SMB_ATF_ADMIN Token belongs to a Domain Admins member + */ +#define SMB_ATF_GUEST 0x00000001 +#define SMB_ATF_ANON 0x00000002 +#define SMB_ATF_POWERUSER 0x00000004 +#define SMB_ATF_BACKUPOP 0x00000008 +#define SMB_ATF_ADMIN 0x00000010 + +#define SMB_POSIX_GRPS_SIZE(n) \ + (sizeof (smb_posix_grps_t) + (n - 1) * sizeof (gid_t)) +/* + * It consists of the primary and supplementary POSIX groups. + */ +typedef struct smb_posix_grps { + uint32_t pg_ngrps; + gid_t pg_grps[ANY_SIZE_ARRAY]; +} smb_posix_grps_t; + +/* + * Token Structure. + * + * This structure contains information of a user. There should be one + * unique token per user per session per client. The information + * provided will either give or deny access to shares, files or folders. + */ +typedef struct smb_token { + smb_id_t *tkn_user; + smb_id_t *tkn_owner; + smb_id_t *tkn_primary_grp; + smb_win_grps_t *tkn_win_grps; + smb_privset_t *tkn_privileges; + char *tkn_account_name; + char *tkn_domain_name; + uint32_t tkn_flags; + uint32_t tkn_audit_sid; + smb_session_key_t *tkn_session_key; + smb_posix_grps_t *tkn_posix_grps; +} smb_token_t; + +/* + * This is the max buffer length for holding certain fields of + * any access token: domain, account, workstation, and IP with the + * format as show below: + * [domain name]\[user account] [workstation] (IP) + * + * This is not meant to be the maximum buffer length for holding + * the entire context of a token. + */ +#define NTTOKEN_BASIC_INFO_MAXLEN (SMB_PI_MAX_DOMAIN + SMB_PI_MAX_USERNAME \ + + SMB_PI_MAX_HOST + INET_ADDRSTRLEN + 8) + +/* + * Information returned by an RPC call is allocated on an internal heap + * which is deallocated before returning from the interface call. The + * smb_userinfo structure provides a useful common mechanism to get the + * information back to the caller. It's like a compact access token but + * only parts of it are filled in by each RPC so the content is call + * specific. + */ +typedef struct smb_rid_attrs { + uint32_t rid; + uint32_t attributes; +} smb_rid_attrs_t; + +#define SMB_UINFO_FLAG_ANON 0x01 +#define SMB_UINFO_FLAG_LADMIN 0x02 /* Local admin */ +#define SMB_UINFO_FLAG_DADMIN 0x04 /* Domain admin */ +#define SMB_UINFO_FLAG_ADMIN (SMB_UINFO_FLAG_LADMIN | SMB_UINFO_FLAG_DADMIN) + +/* + * This structure is mainly used where there's some + * kind of user related interaction with a domain + * controller via different RPC calls. + */ +typedef struct smb_userinfo { + uint16_t sid_name_use; + uint32_t rid; + uint32_t primary_group_rid; + char *name; + char *domain_name; + nt_sid_t *domain_sid; + uint32_t n_groups; + smb_rid_attrs_t *groups; + uint32_t n_other_grps; + smb_sid_attrs_t *other_grps; + smb_session_key_t *session_key; + + nt_sid_t *user_sid; + nt_sid_t *pgrp_sid; + uint32_t flags; +} smb_userinfo_t; + +/* XDR routines */ +extern bool_t xdr_smb_session_key_t(); +extern bool_t xdr_netr_client_t(); +extern bool_t xdr_nt_sid_t(); +extern bool_t xdr_smb_sid_attrs_t(); +extern bool_t xdr_smb_id_t(); +extern bool_t xdr_smb_win_grps_t(); +extern bool_t xdr_smb_posix_grps_t(); +extern bool_t xdr_smb_token_t(); + + +#ifndef _KERNEL +smb_token_t *smb_logon(netr_client_t *clnt); +void smb_token_destroy(smb_token_t *token); +uint8_t *smb_token_mkselfrel(smb_token_t *obj, uint32_t *len); +netr_client_t *netr_client_mkabsolute(uint8_t *buf, uint32_t len); +#else /* _KERNEL */ +smb_token_t *smb_token_mkabsolute(uint8_t *buf, uint32_t len); +void smb_token_free(smb_token_t *token); +uint8_t *netr_client_mkselfrel(netr_client_t *obj, uint32_t *len); +#endif /* _KERNEL */ + +int smb_token_query_privilege(smb_token_t *token, int priv_id); +/* + * Diagnostic routines: + * smb_token_print: write the contents of a token to the log. + * smb_token_log: log message is prefixed with token basic info. + */ +void smb_token_print(smb_token_t *token); +void smb_token_log(int level, smb_dr_user_ctx_t *user_ctx, char *fmt, ...); + +#ifdef __cplusplus +} +#endif + + +#endif /* _SMB_TOKEN_H */ diff --git a/usr/src/uts/common/smbsrv/smb_vops.h b/usr/src/uts/common/smbsrv/smb_vops.h new file mode 100644 index 000000000000..8bc18b73caa3 --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_vops.h @@ -0,0 +1,386 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_SMB_VOPS_H +#define _SMBSRV_SMB_VOPS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Common file system interfaces and definitions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ROOTVOL "" +#define CHKPNT ".chkpnt" +#define XATTR_DIR "xattr_dir" + +#define SMB_STREAM_PREFIX "SUNWsmb" +#define SMB_STREAM_PREFIX_LEN (sizeof (SMB_STREAM_PREFIX) - 1) + +#define MANGLE_NAMELEN 14 +#define SMB_EOF 0x7FFFFFFF + +/* + * SMB_MINLEN_RDDIR_BUF: minimum length of buffer server will provide to + * VOP_READDIR. Its value is the size of the maximum possible edirent_t + * for solaris. The EDIRENT_RECLEN macro returns the size of edirent_t + * required for a given name length. MAXNAMELEN is the maximum + * filename length allowed in Solaris. The first two EDIRENT_RECLEN() + * macros are to allow for . and .. entries -- just a minor tweak to try + * and guarantee that buffer we give to VOP_READDIR will be large enough + * to hold ., .., and the largest possible solaris edirent_t. + * + * This bufsize will also be used when reading dirent64_t entries. + */ + +#define SMB_MINLEN_RDDIR_BUF \ + (EDIRENT_RECLEN(1) + EDIRENT_RECLEN(2) + EDIRENT_RECLEN(MAXNAMELEN)) + +/* + * DP_TO_EDP + * + * Fill in an edirent_t structure with information from a dirent64_t. + * This allows the use of an edirent_t in code where both edirent_t's + * and dirent64_t's are manipulated. + */ + +#define DP_TO_EDP(dp, edp) \ +{ \ + ASSERT((dp)); \ + ASSERT((edp)); \ + (edp)->ed_ino = (dp)->d_ino; \ + (edp)->ed_off = (dp)->d_off; \ + (edp)->ed_eflags = 0; \ + (edp)->ed_reclen = (dp)->d_reclen; \ + (void) strlcpy((edp)->ed_name, (dp)->d_name, MAXNAMELEN); \ +} + +/* + * DP_ADVANCE + * + * In readdir operations, advance to read the next entry in a buffer + * returned from VOP_READDIR. The entries are of type dirent64_t. + */ + +#define DP_ADVANCE(dp, dirbuf, numbytes) \ +{ \ + ASSERT((dp)); \ + if ((dp)->d_reclen == 0) { \ + (dp) = NULL; \ + } else { \ + (dp) = (dirent64_t *)((char *)(dp) + (dp)->d_reclen); \ + if ((dp) >= (dirent64_t *)((dirbuf) + (numbytes))) \ + (dp) = NULL; \ + } \ +} + +/* + * EDP_ADVANCE + * + * In readdir operations, advance to read the next entry in a buffer + * returned from VOP_READDIR. The entries are of type edirent_t. + */ + +#define EDP_ADVANCE(edp, dirbuf, numbytes) \ +{ \ + ASSERT((edp)); \ + if ((edp)->ed_reclen == 0) { \ + (edp) = NULL; \ + } else { \ + (edp) = (edirent_t *)((char *)(edp) + (edp)->ed_reclen);\ + if ((edp) >= (edirent_t *)((dirbuf) + (numbytes))) \ + (edp) = NULL; \ + } \ +} + +struct smb_node; +struct smb_request; + +/* + * Note: When specifying the mask for an smb_attr_t, + * the sa_mask, and not the sa_vattr.va_mask, should be + * filled in. The #define's that should be used are those + * prefixed with SMB_AT_*. Only FSIL routines should + * manipulate the sa_vattr.va_mask field. + */ +typedef struct smb_attr { + uint_t sa_mask; /* For both vattr and CIFS attr's */ + vattr_t sa_vattr; /* Legacy vattr */ + uint32_t sa_dosattr; /* DOS attributes */ + timestruc_t sa_crtime; /* Creation time */ +} smb_attr_t; + +#define SMB_AT_TYPE 0x00001 +#define SMB_AT_MODE 0x00002 +#define SMB_AT_UID 0x00004 +#define SMB_AT_GID 0x00008 +#define SMB_AT_FSID 0x00010 +#define SMB_AT_NODEID 0x00020 +#define SMB_AT_NLINK 0x00040 +#define SMB_AT_SIZE 0x00080 +#define SMB_AT_ATIME 0x00100 +#define SMB_AT_MTIME 0x00200 +#define SMB_AT_CTIME 0x00400 +#define SMB_AT_RDEV 0x00800 +#define SMB_AT_BLKSIZE 0x01000 +#define SMB_AT_NBLOCKS 0x02000 +#define SMB_AT_SEQ 0x08000 + +#define SMB_AT_DOSATTR 0x00100000 +#define SMB_AT_CRTIME 0x00200000 +#define SMB_AT_SMB 0x00300000 + +#define SMB_AT_ALL (SMB_AT_TYPE|SMB_AT_MODE|SMB_AT_UID|SMB_AT_GID|\ + SMB_AT_FSID|SMB_AT_NODEID|SMB_AT_NLINK|SMB_AT_SIZE|\ + SMB_AT_ATIME|SMB_AT_MTIME|SMB_AT_CTIME|SMB_AT_RDEV|\ + SMB_AT_BLKSIZE|SMB_AT_NBLOCKS|SMB_AT_SEQ|SMB_AT_SMB) + +/* + * DOS Attributes + * Previously defined in smbsrv/ntaccess.h + */ + +#define FILE_ATTRIBUTE_READONLY 0x00000001 +#define FILE_ATTRIBUTE_HIDDEN 0x00000002 +#define FILE_ATTRIBUTE_SYSTEM 0x00000004 +#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 +#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 +#define FILE_ATTRIBUTE_ENCRYPTED 0x00000040 +#define FILE_ATTRIBUTE_NORMAL 0x00000080 +#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 +#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 +#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 +#define FILE_ATTRIBUTE_COMPRESSED 0x00000800 +#define FILE_ATTRIBUTE_OFFLINE 0x00001000 +#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 +#define FILE_ATTRIBUTE_MODIFIED 0x00004000 +#define FILE_ATTRIBUTE_QUARANTINED 0x00008000 +#define FILE_ATTRIBUTE_VALID_FLAGS 0x0000dfb7 +#define FILE_ATTRIBUTE_VALID_SET_FLAGS 0x0000dfa7 +#define FILE_ATTRIBUTE_MASK 0x00003FFF + + +#ifndef PBSHORTCUT +/* remove from libsmbbase */ +#define FHF_SMB 0x02 +#endif + +/* DOS specific attribute bits */ +#define FSA_DOSATTR (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_SYSTEM | \ + FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN) + +/* + * File types (FSA_FMT) and permissions (FSA_MODMASK). + * Restricted to lower 16-bits due to FS inode definitions. + */ +#define FSA_MTIME_SEQ 0x10000000 +/* #define FSA_USTREAM_SKIPSEQ 0x10000000 */ +#define FSA_UNDEF 0007000 +#define FSA_SUID 0004000 +#define FSA_SGID 0002000 +#define FSA_STICKY 0001000 +#define FSA_UPERM 0000700 +#define FSA_UREAD 0000400 +#define FSA_UWRITE 0000200 +#define FSA_UEXEC 0000100 +#define FSA_GPERM 0000070 +#define FSA_GREAD 0000040 +#define FSA_GWRITE 0000020 +#define FSA_GEXEC 0000010 +#define FSA_OPERM 0000007 +#define FSA_OREAD 0000004 +#define FSA_OWRITE 0000002 +#define FSA_OEXEC 0000001 + + +#define FSA_PERM_MASK (FSA_UPERM | FSA_GPERM | FSA_OPERM) +#define FSA_MODMASK 0007777 /* mutable by fs_setaddr() */ +#define FSA_DIR_PERM 0777 /* default permission for new */ + /* directories */ +#define FSA_FILE_PERM 0666 /* default permission for new files */ + +#define FCM_CREATEVERFSIZE 8 + +/* stability for write */ +#define FSSTAB_UNSTABLE 0 +#define FSSTAB_DATA_SYNC 1 +#define FSSTAB_FILE_SYNC 2 + +/* + * fs_online flags (meaning when set): + * + * FSOLF_NOMON Do not monitor this FS. + * FSOLF_UTF8_NAME All names in this FS should be in UTF-8 format. + * FSOLF_SYNCNOW Flush all dirty blocks for this FS. + * FSOLF_NODRIVE Do not assign a drive letter to this FS. + * FSOLF_STREAMS This FS supports streams. + * FSOLF_DISABLE_OPLOCKS Oplocks are disabled on this FS. + * FSOLF_RM_PENDING The volume is being removed (unmounted, deleted, + * zapped etc.). + * FSOLF_MDCACHE Enable VFS meta-data caching for this FS. + * FSOLF_ERROR Inconsistencies detected in the volume. + * FSOLF_SYSTEM This is a system volume, no del, ren, dtq, quotas etc + * allowed + * FSOLF_COMPLIANT This volume is compliant; supports retention on + * immutable and unlinkable (no delete, no rename). + * FSOLF_LITE_COMPLIANT This volume has a less-stringent compliant capability + * FSOLF_SYSAUDIT This volume supports the storing of system audit logs + */ +#define FSOLF_NOEXPORT 0x00000001 +#define FSOLF_READONLY 0x00000002 +#define FSOLF_LOCKED 0x00000004 +#define FSOLF_NOMON 0x00000008 +#define FSOLF_NOSHOWMNT 0x00000010 +#define FSOLF_CASE_INSENSITIVE 0x00000020 +#define FSOLF_SUPPORTS_ACLS 0x00000040 +#define FSOLF_UTF8_NAME 0x00000080 +#define FSOLF_MIRRORING 0x00000100 +#define FSOLF_SYNCNOW 0x00000200 +#define FSOLF_NODRIVE 0x00000400 +#define FSOLF_OFFLINE 0x00000800 +#define FSOLF_STREAMS 0x00001000 +#define FSOLF_DISABLE_OPLOCKS 0x00002000 +#define FSOLF_RM_PENDING 0x00004000 +#define FSOLF_MDCACHE 0x00008000 +#define FSOLF_MNT_IN_PROGRESS 0x00010000 +#define FSOLF_NO_ATIME 0x00020000 +#define FSOLF_ERROR 0x00040000 +#define FSOLF_SYSTEM 0x00080000 +#define FSOLF_COMPLIANT 0x00100000 +#define FSOLF_LITE_COMPLIANT 0x00200000 +#define FSOLF_SYSAUDIT 0x00400000 +#define FSOLF_NO_CASE_SENSITIVE 0x00800000 +#define FSOLF_XVATTR 0x02000000 +#define FSOLF_DIRENTFLAGS 0x04000000 + +/* + * The following flags are shared between live and checkpoint volumes. + */ +#define FSOLF_SHARED_FLAGS (FSOLF_CASE_INSENSITIVE | FSOLF_UTF8_NAME | \ + FSOLF_STREAMS) + +/* + * the following flags are dynamically set and reset so should not be stored + * in volume. + */ +#define FSOLF_MASK ~(FSOLF_NOEXPORT | FSOLF_READONLY | \ + FSOLF_LOCKED | FSOLF_NOMON | \ + FSOLF_SYNCNOW | FSOLF_NOSHOWMNT | \ + FSOLF_NODRIVE | FSOLF_RM_PENDING) + +/* + * case_flag: set FHF_IGNORECASE for case-insensitive compare. + */ + +struct fs_stream_info { + char name[MAXPATHLEN]; + uint64_t size; +}; + +int fhopen(const struct smb_node *, int); + +extern int smb_vop_open(vnode_t **vpp, int mode, cred_t *cred, + caller_context_t *ct); +extern int smb_vop_close(vnode_t *vp, int flag, cred_t *cred, + caller_context_t *ct); +extern int smb_vop_read(vnode_t *vp, uio_t *uiop, cred_t *cr, + caller_context_t *ct); +extern int smb_vop_write(vnode_t *vp, uio_t *uiop, unsigned int *flag, + uint32_t *lcount, cred_t *cr, caller_context_t *ct); +extern int smb_vop_getattr(vnode_t *vp, vnode_t *unnamed_vp, + smb_attr_t *ret_attr, int flags, cred_t *cr, caller_context_t *ct); +extern int smb_vop_setattr(vnode_t *vp, vnode_t *unnamed_vp, + smb_attr_t *set_attr, int flags, cred_t *cr, caller_context_t *ct); +extern int smb_vop_access(vnode_t *vp, int mode, int flags, vnode_t *dir_vp, + cred_t *cr); +extern void smb_vop_eaccess(vnode_t *vp, int *mode, int flags, vnode_t *dir_vp, + cred_t *cr); +extern int smb_vop_lookup(vnode_t *dvp, char *name, vnode_t **vpp, + char *od_name, int flags, vnode_t *rootvp, cred_t *cr, + caller_context_t *ct); +extern int smb_vop_create(vnode_t *dvp, char *name, smb_attr_t *attr, + vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct, + vsecattr_t *vsap); +extern int smb_vop_remove(vnode_t *dvp, char *name, int flags, cred_t *cr, + caller_context_t *ct); +extern int smb_vop_rename(vnode_t *from_dvp, char *from_name, vnode_t *to_dvp, + char *to_name, int flags, cred_t *cr, caller_context_t *ct); +extern int smb_vop_mkdir(vnode_t *dvp, char *name, smb_attr_t *attr, + vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct, + vsecattr_t *vsap); +extern int smb_vop_rmdir(vnode_t *dvp, char *name, int flags, cred_t *cr, + caller_context_t *ct); +extern int smb_vop_readdir(vnode_t *dvp, uint32_t *cookiep, char *name, + int *namelen, ino64_t *inop, vnode_t **vpp, char *od_name, int flags, + cred_t *cr, caller_context_t *ct); +extern int smb_vop_commit(vnode_t *vp, cred_t *cr, caller_context_t *ct); +extern int smb_vop_getdents(struct smb_node *dir_snode, uint32_t *cookiep, + uint64_t *verifierp, int32_t *dircountp, char *arg, char *pattern, + uint32_t flags, struct smb_request *sr, cred_t *cr, + caller_context_t *ct); +extern int smb_vop_statfs(vnode_t *vp, struct statvfs64 *statp, cred_t *cr); +extern int smb_vop_stream_lookup(vnode_t *fvp, char *stream_name, + vnode_t **vpp, char *name, vnode_t **xattrdirvpp, int flags, + vnode_t *rootvp, cred_t *cr, caller_context_t *ct); +extern int smb_vop_stream_create(vnode_t *fvp, char *stream_name, + smb_attr_t *attr, vnode_t **vpp, vnode_t **xattrdirvpp, int flags, + cred_t *cr, caller_context_t *ct); +extern int smb_vop_stream_remove(vnode_t *vp, char *stream_name, int flags, + cred_t *cr, caller_context_t *ct); +extern int smb_vop_stream_readdir(vnode_t *fvp, uint32_t *cookiep, + struct fs_stream_info *stream_info, vnode_t **vpp, vnode_t **xattrdirvp, + int flags, cred_t *cr, caller_context_t *ct); +extern int smb_vop_lookup_xattrdir(vnode_t *fvp, vnode_t **xattrdirvpp, + int flags, cred_t *cr, caller_context_t *ct); +extern int smb_vop_traverse_check(vnode_t **vpp); + +int smb_vop_acl_read(vnode_t *vp, acl_t **aclp, int flags, acl_type_t acl_type, + cred_t *cr, caller_context_t *ct); +int smb_vop_acl_write(vnode_t *vp, acl_t *aclp, int flags, cred_t *cr, + caller_context_t *ct); +acl_type_t smb_vop_acl_type(vnode_t *vp); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_SMB_VOPS_H */ diff --git a/usr/src/uts/common/smbsrv/smb_winpipe.h b/usr/src/uts/common/smbsrv/smb_winpipe.h new file mode 100755 index 000000000000..93a176c10170 --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_winpipe.h @@ -0,0 +1,84 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMB_WINPIPE_H_ +#define _SMB_WINPIPE_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef _KERNEL +#include +#endif /* _KERNEL */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SMB_IO_MAX_SIZE 32 +#define SMB_MAX_PIPENAMELEN 32 + +#define SMB_WINPIPE_DOOR_DOWN_PATH "/var/run/winpipe_doordown" +#define SMB_WINPIPE_DOOR_UP_PATH "/var/run/winpipe_doorup" + +#define SMB_DOWNCALLINFO_MAGIC 0x19121969 +#define SMB_MLSVC_DOOR_VERSION 1 + +#define SMB_RPC_FLUSH_MAGIC 0x123456CC +#define SMB_RPC_TRANSACT 1 +#define SMB_RPC_READ 2 +#define SMB_RPC_WRITE 3 +#define SMB_RPC_FLUSH 4 + +typedef struct { + uint64_t md_tid; /* caller's thread id */ + uint16_t md_version; /* version number, start with 1 */ + uint16_t md_call_type; /* transact, read, write, flush */ + uint32_t md_length; /* max bytes to return */ + uint64_t md_reserved; +} mlsvc_door_hdr_t; + +typedef struct { + uint32_t sp_pipeid; + char sp_pipename[SMB_MAX_PIPENAMELEN]; + int32_t sp_datalen; + char sp_data[1]; /* any size buffer */ +} smb_pipe_t; + +void smb_downcall_service(void *, door_arg_t *, void (**)(void *, void *), + void **, int *); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMB_WINPIPE_H_ */ diff --git a/usr/src/uts/common/smbsrv/smb_xdr.h b/usr/src/uts/common/smbsrv/smb_xdr.h new file mode 100644 index 000000000000..31a7b3e72e8c --- /dev/null +++ b/usr/src/uts/common/smbsrv/smb_xdr.h @@ -0,0 +1,111 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_SMB_XDR_H +#define _SMBSRV_SMB_XDR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct smb_dr_kshare { + int32_t k_op; + char *k_path; + char *k_sharename; +} smb_dr_kshare_t; + +#ifdef _KERNEL +#define xdr_int8_t xdr_char +#define xdr_uint8_t xdr_u_char +#define xdr_int16_t xdr_short +#define xdr_uint16_t xdr_u_short + +extern bool_t xdr_u_char(XDR *xdrs, uchar_t *cp); +extern bool_t xdr_vector(XDR *xdrs, char *basep, uint_t nelem, + uint_t elemsize, xdrproc_t xdr_elem); + +smb_dr_kshare_t *smb_share_mkabsolute(uint8_t *buf, uint32_t len); +#else +uint8_t *smb_kshare_mkselfrel(smb_dr_kshare_t *kshare, uint32_t *len); +#endif /* _KERNEL */ + +/* null-terminated string buffer */ +typedef struct smb_dr_string { + char *buf; +} smb_dr_string_t; + +/* byte buffer (non-null terminated) */ +typedef struct smb_dr_bytes { + uint32_t bytes_len; + uint8_t *bytes_val; +} smb_dr_bytes_t; + +/* + * smb_dr_user_ctx/smb_dr_ulist data structures are defined to transfer + * the necessary information for all connected users via door to + * mlsvc. The smb_dr_user_ctx provides user context that will be part + * of the MLSVC rpc context. + * + * Both SMB session ID and SMB UID of smb_dr_user_ctx_t are used to + * uniquely identified the corresponding in-kernel SMB user object. + */ +#define SMB_DR_MAX_USERS 50 +typedef struct smb_dr_user_ctx { + uint64_t du_session_id; + uint16_t du_uid; + uint16_t du_domain_len; + char *du_domain; + uint16_t du_account_len; + char *du_account; + uint16_t du_workstation_len; + char *du_workstation; + uint32_t du_ipaddr; + int32_t du_native_os; + int64_t du_logon_time; + uint32_t du_flags; +} smb_dr_user_ctx_t; + +typedef struct smb_dr_ulist { + uint32_t dul_cnt; + smb_dr_user_ctx_t dul_users[SMB_DR_MAX_USERS]; +} smb_dr_ulist_t; + +/* xdr routines for common door arguments/results */ +extern bool_t xdr_smb_dr_string_t(XDR *, smb_dr_string_t *); +extern bool_t xdr_smb_dr_bytes_t(XDR *, smb_dr_bytes_t *); +extern bool_t xdr_smb_dr_user_ctx_t(XDR *, smb_dr_user_ctx_t *); +extern bool_t xdr_smb_dr_ulist_t(XDR *, smb_dr_ulist_t *); +extern bool_t xdr_smb_dr_kshare_t(XDR *, smb_dr_kshare_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_SMB_XDR_H */ diff --git a/usr/src/uts/common/smbsrv/smbfmt.h b/usr/src/uts/common/smbsrv/smbfmt.h new file mode 100644 index 000000000000..4f7e852e14dc --- /dev/null +++ b/usr/src/uts/common/smbsrv/smbfmt.h @@ -0,0 +1,56 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_SMBFMT_H +#define _SMBSRV_SMBFMT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * SMB message header formats. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define SMB_RESUME_KEY_FMT ".11ccl4." +#define SMB_HEADER_ED_FMT "Mbbbwbww8c2.wwww" +#define SMB_HEADER_ED_LEN (4+1+1+1+2+1+2+12+2+2+2+2) +#define SMB_TRANSHDR_ED_FMT "wwwwb.wl2.wwwwb." +#define SMB_TRANSHDR_ED_LEN (2+2+2+2+1+1+2+4+2+2+2+2+2+1+1) +#define SMB_TRANSSHDR_ED_FMT "wwwwwwwww" +#define SMB_TRANSSHDR_ED_LEN (2+2+2+2+2+2+2+2) +#define SMB_TRANS2SHDR_ED_FMT "wwwwwwwww" +#define SMB_TRANS2SHDR_ED_LEN (2+2+2+2+2+2+2+2+2) +/* There is something wrong with this. Should be 38 bytes. It is 37 bytes */ +#define SMB_NT_TRANSHDR_ED_FMT "b2.llllllllbw" +#define SMB_NT_TRANSHDR_ED_LEN (1+2+4+4+4+4+4+4+4+4+1+2) + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_SMBFMT_H */ diff --git a/usr/src/uts/common/smbsrv/smbinfo.h b/usr/src/uts/common/smbsrv/smbinfo.h new file mode 100644 index 000000000000..d6a7a03a9afb --- /dev/null +++ b/usr/src/uts/common/smbsrv/smbinfo.h @@ -0,0 +1,181 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_SMBINFO_H +#define _SMBSRV_SMBINFO_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Native OS types used in SmbSessionSetupX. + */ +#ifndef NATIVE_OS_DEFINED +#define NATIVE_OS_DEFINED + +#define NATIVE_OS_UNKNOWN 0x00000000 +#define NATIVE_OS_NT4_0 0x00000001 +#define NATIVE_OS_WIN95 0x00000002 +#define NATIVE_OS_OTHER 0x00000003 +#define NATIVE_OS_NT5_0 0x00000004 +#define NATIVE_OS_NT5_1 0x00000005 +#define NATIVE_OS_MACOS 0x00000006 + +/* + * Backward compatibility aliases. + */ +#define NATIVE_OS_WINNT NATIVE_OS_NT4_0 +#define NATIVE_OS_WIN2000 NATIVE_OS_NT5_0 +#define NATIVE_OS_WINXP NATIVE_OS_NT5_1 + +#endif /* NATIVE_OS_DEFINED */ + + +/* + * Native lanman types in SmbSessionSetupX. Note that these values + * are not directly related to the negotiated protocol dialect. + */ +#ifndef NATIVE_LANMAN_DEFINED +#define NATIVE_LANMAN_DEFINED + +#define NATIVE_LM_NONE 0x00000000 +#define NATIVE_LM_NT 0x00000001 +#define NATIVE_LM_WIN2000 0x00000002 + +#endif /* NATIVE_LANMAN_DEFINED */ + + +/* PDC types to be used in user authentication process */ + +#define PDC_UNKNOWN 0 +#define PDC_WINNT 1 +#define PDC_WIN2000 2 +#define PDC_WINXP 3 +#define PDC_SAMBA 4 + +/* + * Please replace the use of MAX_NETWORKS with SMB_PI_MAX_NETWORKS if + * you find it used in conjunction with smbparm_info and maybe one day + * there will be just a single definition (here) throughout the code. + */ +#ifndef MAX_NETWORKS +#define MAX_NETWORKS 36 +#endif /* MAX_NETWORKS */ + +#define SMB_PI_MAX_NETWORKS 36 +#define SMB_PI_MAX_WINS 2 + +#define SMB_SECMODE_WORKGRP 1 +#define SMB_SECMODE_DOMAIN 2 + +#define SMB_PI_MAX_HOST 48 +#define SMB_PI_MAX_DOMAIN 48 +#define SMB_PI_MAX_SCOPE 16 +#define SMB_PI_MAX_COMMENT 58 +#define SMB_PI_MAX_USERNAME 40 +#define SMB_PI_MAX_PASSWD 40 +#define SMB_PI_MAX_NATIVE_OS 32 +#define SMB_PI_MAX_LANMAN 32 + + +#define SMB_PI_UNKNOWN_DOMAIN 0 +#define SMB_PI_RESOURCE_DOMAIN 1 + +/* + * K.L. The keep alive time out use to default to 900 + * seconds. It is not long enough for some applications + * i.e. MS Access. We currently use 5400 seconds. + */ +#define SMB_PI_KEEP_ALIVE_MIN (90 * 60) +#define SMB_LM_COMPATIBILITY_DEFAULT_LEV 3 + +/* + * This is max networks multiply by canonical address for IPv4 + * This needs a fix for IPv4 + */ +#define MAX_EXCLUDE_LIST_LEN (SMB_PI_MAX_NETWORKS * INET_ADDRSTRLEN) + +typedef struct smb_kmod_cfg { + uint32_t skc_maxbufsize; + uint32_t skc_maxworkers; + uint32_t skc_maxconnections; + uint32_t skc_keepalive; + uint32_t skc_restrict_anon; + uint32_t skc_signing_enable; + uint32_t skc_signing_required; + uint32_t skc_signing_check; + uint32_t skc_oplock_enable; + uint32_t skc_oplock_timeout; + uint32_t skc_flush_required; + uint32_t skc_sync_enable; + uint32_t skc_dirsymlink_enable; + uint32_t skc_announce_quota; + uint32_t skc_secmode; + uint32_t skc_lmlevel; + + char skc_resource_domain[SMB_PI_MAX_DOMAIN]; + char skc_hostname[SMB_PI_MAX_HOST]; + char skc_system_comment[SMB_PI_MAX_COMMENT]; +} smb_kmod_cfg_t; + +#define SMB_VERSION_MAJOR 4 +#define SMB_VERSION_MINOR 0 + +int smbnative_os_value(char *native_os); +int smbnative_lm_value(char *native_lm); +int smbnative_pdc_value(char *native_lm); + +/* + * Support for passthrough authentication. + */ +#define AUTH_USER_GRANT 0x00000000 +#define AUTH_GUEST_GRANT 0x00000001 +#define AUTH_IPC_ONLY_GRANT 0x00000002 +#define AUTH_CONEXUS_GRANT 0x00000004 + +#define SMBD_DOOR_NAME "/var/run/smbd_door_old" +#define SMBD_DOOR_VERSION 1 + +#define SMBD_DOOR_COOKIE ((void*)(0xdeadbeef^SMBD_DOOR_VERSION)) +#define SMBD_DOOR_SIZE 256 + +#define SMBD_DOOR_SRV_SUCCESS 0 +#define SMBD_DOOR_SRV_ERROR -1 + +#define SMBD_DOOR_JOIN 1 +#define SMBD_DOOR_PARAM_GET 2 +#define SMBD_DOOR_PARAM_SET 3 +#define SMBD_DOOR_NETBIOS_RECONFIG 4 + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_SMBINFO_H */ diff --git a/usr/src/uts/common/smbsrv/smbtrans.h b/usr/src/uts/common/smbsrv/smbtrans.h new file mode 100644 index 000000000000..9ffd7e366783 --- /dev/null +++ b/usr/src/uts/common/smbsrv/smbtrans.h @@ -0,0 +1,70 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_SMBTRANS_H +#define _SMBSRV_SMBTRANS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Note that name can be variable length; therefore, it has to + * stay last. + */ +typedef struct smb_dent_info { + uint32_t cookie; + smb_attr_t attr; + struct smb_node *snode; + char name83[14]; + char shortname[14]; + char name[1]; +} smb_dent_info_t; + +#define SMB_MAX_DENT_INFO_SIZE (sizeof (smb_dent_info_t) + MAXNAMELEN - 1) +#define SMB_MAX_DENTS_BUF_SIZE (64 * 1024) /* 64k */ +#define SMB_MAX_DENTS_IOVEC (SMB_MAX_DENTS_BUF_SIZE / SMB_MAX_DENT_INFO_SIZE) + +typedef struct smb_dent_info_hdr { + struct smb_request *sr; + char *pattern; + unsigned short sattr; + struct uio uio; + struct iovec iov[SMB_MAX_DENTS_IOVEC]; +} smb_dent_info_hdr_t; + +int smb_get_dents(struct smb_request *sr, uint32_t *cookie, + struct smb_node *dir_snode, unsigned int wildcards, + smb_dent_info_hdr_t *ihdr, int *more); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_SMBTRANS_H */ diff --git a/usr/src/uts/common/smbsrv/smbvar.h b/usr/src/uts/common/smbsrv/smbvar.h new file mode 100644 index 000000000000..30e41c714a1d --- /dev/null +++ b/usr/src/uts/common/smbsrv/smbvar.h @@ -0,0 +1,1402 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Structures and type definitions for the SMB module. + */ + +#ifndef _SMBSRV_SMBVAR_H +#define _SMBSRV_SMBVAR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct smb_session smb_session_t; +typedef struct smb_request smb_request_t; + +#include + +int smb_noop(void *, size_t, int); + +#define SMB_AUDIT_STACK_DEPTH 16 +#define SMB_AUDIT_BUF_MAX_REC 16 +#define SMB_AUDIT_NODE 0x00000001 + +extern uint32_t smb_audit_flags; + +typedef struct { + uint32_t anr_refcnt; + int anr_depth; + pc_t anr_stack[SMB_AUDIT_STACK_DEPTH]; +} smb_audit_record_node_t; + +typedef struct { + int anb_index; + int anb_max_index; + smb_audit_record_node_t anb_records[SMB_AUDIT_BUF_MAX_REC]; +} smb_audit_buf_node_t; + +#define SMB_WORKER_PRIORITY 99 +/* + * Thread State Machine + * -------------------- + * + * T5 T0 + * smb_thread_destroy() <-------+ +------- smb_thread_init() + * | | + * | v + * +-----------------------------+ + * | SMB_THREAD_STATE_EXITED |<---+ + * +-----------------------------+ | + * | T1 | + * v | + * +-----------------------------+ | + * | SMB_THREAD_STATE_STARTING | | + * +-----------------------------+ | + * | T2 | T4 + * v | + * +-----------------------------+ | + * | SMB_THREAD_STATE_RUNNING | | + * +-----------------------------+ | + * | T3 | + * v | + * +-----------------------------+ | + * | SMB_THREAD_STATE_EXITING |----+ + * +-----------------------------+ + * + * Transition T0 + * + * This transition is executed in smb_thread_init(). + * + * Transition T1 + * + * This transition is executed in smb_thread_start(). + * + * Transition T2 + * + * This transition is executed by the thread itself when it starts running. + * + * Transition T3 + * + * This transition is executed by the thread itself in + * smb_thread_entry_point() just before calling thread_exit(). + * + * + * Transition T4 + * + * This transition is executed in smb_thread_stop(). + * + * Transition T5 + * + * This transition is executed in smb_thread_destroy(). + * + * Comments + * -------- + * + * The field smb_thread_aw_t contains a function pointer that knows how to + * awake the thread. It is a temporary solution to work around the fact that + * kernel threads (not part of a userspace process) cannot be signaled. + */ +typedef enum smb_thread_state { + SMB_THREAD_STATE_STARTING = 0, + SMB_THREAD_STATE_RUNNING, + SMB_THREAD_STATE_EXITING, + SMB_THREAD_STATE_EXITED +} smb_thread_state_t; + +struct _smb_thread; + +typedef void (*smb_thread_ep_t)(struct _smb_thread *, void *ep_arg); +typedef void (*smb_thread_aw_t)(struct _smb_thread *, void *aw_arg); + +#define SMB_THREAD_MAGIC 0x534D4254 /* SMBT */ + +typedef struct _smb_thread { + uint32_t sth_magic; + char sth_name[16]; + smb_thread_state_t sth_state; + kthread_t *sth_th; + kt_did_t sth_did; + smb_thread_ep_t sth_ep; + void *sth_ep_arg; + smb_thread_aw_t sth_aw; + void *sth_aw_arg; + boolean_t sth_kill; + kmutex_t sth_mtx; + kcondvar_t sth_cv; +} smb_thread_t; + +/* + * Pool of IDs + * ----------- + * + * A pool of IDs is a pool of 16 bit numbers. It is implemented as a bitmap. + * A bit set to '1' indicates that that particular value has been allocated. + * The allocation process is done shifting a bit through the whole bitmap. + * The current position of that index bit is kept in the smb_idpool_t + * structure and represented by a byte index (0 to buffer size minus 1) and + * a bit index (0 to 7). + * + * The pools start with a size of 8 bytes or 64 IDs. Each time the pool runs + * out of IDs its current size is doubled until it reaches its maximum size + * (8192 bytes or 65536 IDs). The IDs 0 and 65535 are never given out which + * means that a pool can have a maximum number of 65534 IDs available. + */ +#define SMB_IDPOOL_MAGIC 0x4944504C /* IDPL */ +#define SMB_IDPOOL_MIN_SIZE 64 /* Number of IDs to begin with */ +#define SMB_IDPOOL_MAX_SIZE 64 * 1024 + +typedef struct smb_idpool { + uint32_t id_magic; + kmutex_t id_mutex; + uint8_t *id_pool; + uint32_t id_size; + uint8_t id_bit; + uint8_t id_bit_idx; + uint32_t id_idx; + uint32_t id_idx_msk; + uint32_t id_free_counter; + uint32_t id_max_free_counter; +} smb_idpool_t; + +/* + * Maximum size of a Netbios Request. + * 0x1FFFF -> Maximum size of the data + * 4 -> Size of the Netbios header + */ +#define NETBIOS_REQ_MAX_SIZE (0x1FFFF + 0x4) + +/* + * IR104720 Experiments with Windows 2000 indicate that we achieve better + * SmbWriteX performance with a buffer size of 64KB instead of the 37KB + * used with Windows NT4.0. Previous experiments with NT4.0 resulted in + * directory listing problems so this buffer size is configurable based + * on the end-user environment. When in doubt use 37KB. + * + * smb_maxbufsize (smb_negotiate.c) is setup from SMB_NT_MAXBUF during + * initialization. + */ +#define NBMAXBUF 8 +#define SMB_NT_MAXBUF(S) (((S) * 1024) - NBMAXBUF) +extern int smb_maxbufsize; + +#define OUTBUFSIZE (65 * 1024) +#define SMBHEADERSIZE 32 +#define SMBND_HASH_MASK (0xFF) +#define MAX_IOVEC 512 +#define MAX_READREF (8 * 1024) + +#define SMB_WORKER_MIN 4 +#define SMB_WORKER_DEFAULT 64 +#define SMB_WORKER_MAX 1024 + +/* + * Fix align a pointer or offset appropriately so that fields will not + * cross word boundaries. + */ +#define PTRALIGN(x) \ + (((uintptr_t)(x) + (uintptr_t)(_POINTER_ALIGNMENT) - 1l) & \ + ~((uintptr_t)(_POINTER_ALIGNMENT) - 1l)) + +/* + * native os types are defined in win32/smbinfo.h + */ + +/* + * All 4 different time / date formats that will bee seen in SMB + */ +typedef struct { + uint16_t Day : 5; + uint16_t Month : 4; + uint16_t Year : 7; +} SMB_DATE; + +typedef struct { + uint16_t TwoSeconds : 5; + uint16_t Minutes : 6; + uint16_t Hours : 5; +} SMB_TIME; + + +typedef uint32_t UTIME; /* seconds since Jan 1 1970 */ + +typedef struct smb_malloc_list { + struct smb_malloc_list *forw; + struct smb_malloc_list *back; +} smb_malloc_list; + +typedef struct smb_llist { + krwlock_t ll_lock; + list_t ll_list; + uint32_t ll_count; + uint64_t ll_wrop; +} smb_llist_t; + +typedef struct smb_slist { + kmutex_t sl_mutex; + kcondvar_t sl_cv; + list_t sl_list; + uint32_t sl_count; + boolean_t sl_waiting; +} smb_slist_t; + +typedef struct { + kcondvar_t rwx_cv; + kmutex_t rwx_mutex; + krwlock_t rwx_lock; + boolean_t rwx_waiting; +} smb_rwx_t; + +/* NOTIFY CHANGE */ + +typedef struct smb_notify_change_req { + list_node_t nc_lnd; + struct smb_node *nc_node; + uint32_t nc_reply_type; + uint32_t nc_flags; +} smb_notify_change_req_t; + +/* + * SMB operates over a NetBIOS-over-TCP transport (NBT) or directly + * over TCP, which is also known as direct hosted NetBIOS-less SMB + * or SMB-over-TCP. + * + * NBT messages have a 4-byte header that defines the message type + * (8-bits), a 7-bit flags field and a 17-bit length. + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TYPE | FLAGS |E| LENGTH | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 8-bit type Defined in RFC 1002 + * 7-bit flags Bits 0-6 reserved (must be 0) + * Bit 7: Length extension bit (E) + * 17-bit length Includes bit 7 of the flags byte + * + * + * SMB-over-TCP is defined to use a modified version of the NBT header + * containing an 8-bit message type and 24-bit message length. + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | TYPE | LENGTH | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 8-bit type Must be 0 + * 24-bit length + * + * The following structure is used to represent a generic, in-memory + * SMB transport header; it is not intended to map directly to either + * of the over-the-wire formats. + */ +typedef struct { + uint8_t xh_type; + uint32_t xh_length; +} smb_xprt_t; + +struct mbuf_chain { + volatile uint32_t flags; /* Various flags */ + struct mbuf_chain *shadow_of; /* I'm shadowing someone */ + struct mbuf *chain; /* Start of chain */ + int32_t max_bytes; /* max # of bytes for chain */ + int32_t chain_offset; /* Current offset into chain */ +}; + +int MBC_LENGTH(struct mbuf_chain *MBC); +void MBC_SETUP(struct mbuf_chain *MBC, uint32_t max_bytes); +void MBC_INIT(struct mbuf_chain *MBC, uint32_t max_bytes); +void MBC_FLUSH(struct mbuf_chain *MBC); +void MBC_ATTACH_MBUF(struct mbuf_chain *MBC, struct mbuf *MBUF); +void MBC_APPEND_MBUF(struct mbuf_chain *MBC, struct mbuf *MBUF); +void MBC_ATTACH_BUF(struct mbuf_chain *MBC, unsigned char *BUF, int LEN); +int MBC_SHADOW_CHAIN(struct mbuf_chain *SUBMBC, struct mbuf_chain *MBC, + int OFF, int LEN); + +#define MBC_ROOM_FOR(b, n) (((b)->chain_offset + (n)) <= (b)->max_bytes) + +typedef struct smb_oplock { + struct smb_ofile *op_ofile; + uint32_t op_flags; +} smb_oplock_t; + +#define OPLOCK_FLAG_BREAKING 1 + +#define OPLOCK_RELEASE_LOCK_RELEASED 0 +#define OPLOCK_RELEASE_FILE_CLOSED 1 + +#define DOS_ATTR_VALID 0x80000000 + +#define SMB_VFS_MAGIC 0x534D4256 /* 'SMBV' */ + +typedef struct smb_vfs { + uint32_t sv_magic; + list_node_t sv_lnd; + uint32_t sv_refcnt; + vfs_t *sv_vfsp; + vnode_t *sv_rootvp; +} smb_vfs_t; + +#define SMB_NODE_MAGIC 0x4E4F4445 /* 'NODE' */ + +typedef enum { + SMB_NODE_STATE_AVAILABLE = 0, + SMB_NODE_STATE_DESTROYING +} smb_node_state_t; + +typedef struct smb_node { + uint32_t n_magic; + smb_rwx_t n_lock; + list_node_t n_lnd; + smb_node_state_t n_state; + uint32_t n_refcnt; + uint32_t n_hashkey; + smb_llist_t *n_hash_bucket; + uint64_t n_orig_session_id; + uint32_t n_orig_uid; + smb_llist_t n_ofile_list; + smb_llist_t n_lock_list; + volatile int flags; /* FILE_NOTIFY_CHANGE_* */ + volatile int waiting_event; /* # of clients requesting FCN */ + smb_attr_t attr; + unsigned int what; + off_t n_size; + smb_oplock_t n_oplock; + struct smb_node *dir_snode; /* Directory of node */ + struct smb_node *unnamed_stream_node; /* set in stream nodes */ + /* Credentials for delayed delete */ + cred_t *delete_on_close_cred; + char od_name[MAXNAMELEN]; + timestruc_t set_mtime; + fs_desc_t tree_fsd; + vnode_t *vp; + smb_audit_buf_node_t *n_audit_buf; + +} smb_node_t; + +#define NODE_FLAGS_NOTIFY_CHANGE 0x10000fff +#define NODE_OPLOCKS_IN_FORCE 0x0000f000 +#define NODE_OPLOCK_NONE 0x00000000 +#define NODE_EXCLUSIVE_OPLOCK 0x00001000 +#define NODE_BATCH_OPLOCK 0x00002000 +#define NODE_LEVEL_II_OPLOCK 0x00003000 +#define NODE_CAP_LEVEL_II 0x00010000 +#define NODE_PROTOCOL_LOCK 0x00020000 +#define NODE_READ_ONLY 0x00040000 +#define NODE_CREATED_READONLY 0x00080000 +#define NODE_FLAGS_WRITE_THROUGH 0x00100000 +#define NODE_FLAGS_SYNCATIME 0x00200000 +#define NODE_FLAGS_LOCKED 0x00400000 +#define NODE_FLAGS_ATTR_VALID 0x00800000 +#define NODE_FLAGS_CREATED 0x04000000 +#define NODE_FLAGS_CHANGED 0x08000000 +#define NODE_FLAGS_WATCH_TREE 0x10000000 +#define NODE_FLAGS_SET_SIZE 0x20000000 +#define NODE_FLAGS_DELETE_ON_CLOSE 0x40000000 +#define NODE_FLAGS_EXECUTABLE 0x80000000 + +#define NODE_IS_READONLY(node) \ + ((node->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) || \ + (node->flags & NODE_READ_ONLY) || \ + (node->flags & NODE_CREATED_READONLY)) + +#define OPLOCK_TYPE(n) ((n)->flags & NODE_OPLOCKS_IN_FORCE) +#define OPLOCKS_IN_FORCE(n) (OPLOCK_TYPE(n) != NODE_OPLOCK_NONE) +#define EXCLUSIVE_OPLOCK_IN_FORCE(n) \ + (OPLOCK_TYPE(n) == NODE_EXCLUSIVE_OPLOCK) +#define BATCH_OPLOCK_IN_FORCE(n) (OPLOCK_TYPE(n) == NODE_BATCH_OPLOCK) +#define LEVEL_II_OPLOCK_IN_FORCE(n) (OPLOCK_TYPE(n) == NODE_LEVEL_II_OPLOCK) + +/* + * Based on section 2.6.1.2 (Connection Management) of the June 13, + * 1996 CIFS spec, a server may terminate the transport connection + * due to inactivity. The client software is expected to be able to + * automatically reconnect to the server if this happens. Like much + * of the useful background information, this section appears to + * have been dropped from later revisions of the document. + * + * Each session has an activity timestamp that's updated whenever a + * request is dispatched. If the session is idle, i.e. receives no + * requests, for SMB_SESSION_INACTIVITY_TIMEOUT minutes it will be + * closed. + * + * Each session has an I/O semaphore to serialize communication with + * the client. For example, after receiving a raw-read request, the + * server is not allowed to send an oplock break to the client until + * after it has sent the raw-read data. + */ +#define SMB_SESSION_INACTIVITY_TIMEOUT (15 * 60) + +#define SMB_SESSION_OFILE_MAX (16 * 1024) + +/* + * When a connection is set up we need to remember both the client + * (peer) IP address and the local IP address used to establish the + * connection. When a client connects with a vc number of zero, we + * are supposed to abort any existing connections with that client + * (see notes in smb_negotiate.c and smb_session_setup_andx.c). For + * servers with multiple network interfaces or IP aliases, however, + * each interface has to be managed independently since the client + * is not aware of the server configuration. We have to allow the + * client to establish a connection on each interface with a vc + * number of zero without aborting the other connections. + * + * ipaddr: the client (peer) IP address for the session. + * local_ipaddr: the local IP address used to connect to the server. + */ + +#define SMB_MAC_KEYSZ 512 + +struct smb_sign { + unsigned int seqnum; + unsigned int mackey_len; + unsigned int flags; + unsigned char mackey[SMB_MAC_KEYSZ]; +}; + +#define SMB_SIGNING_ENABLED 1 +#define SMB_SIGNING_CHECK 2 + +/* + * Session State Machine + * --------------------- + * + * +-----------------------------+ +------------------------------+ + * | SMB_SESSION_STATE_CONNECTED | | SMB_SESSION_STATE_TERMINATED | + * +-----------------------------+ +------------------------------+ + * T0| ^ + * +--------------------+ |T13 + * v |T14 | + * +-------------------------------+ | +--------------------------------+ + * | SMB_SESSION_STATE_ESTABLISHED |---+--->| SMB_SESSION_STATE_DISCONNECTED | + * +-------------------------------+ +--------------------------------+ + * T1| ^ ^ ^ ^ + * +----------+ |T9 | | | + * v | | | | + * +------------------------------+ | | | + * | SMB_SESSION_STATE_NEGOTIATED | | | | + * +------------------------------+ | | | + * ^| ^| | ^ | | | + * +----------------+| || | | | | | + * |+----------------+ || T7| |T8 | | | + * || || | | | | | + * || +----------------+| | | | | | + * || |+----------------+ | | | | | + * || || v | | | | + * || || +-----------------------------------+ T10| | | + * || || | SMB_SESSION_STATE_OPLOCK_BREAKING |----+ | | + * || || +-----------------------------------+ | | + * || ||T5 | | + * || |+-->+-----------------------------------+ T11| | + * || |T6 | SMB_SESSION_STATE_READ_RAW_ACTIVE |------+ | + * || +----+-----------------------------------+ | + * ||T3 | + * |+------->+------------------------------------+ T12| + * |T4 | SMB_SESSION_STATE_WRITE_RAW_ACTIVE |-------+ + * +---------+------------------------------------+ + * + * Transition T0 + * + * + * + * Transition T1 + * + * + * + * Transition T2 + * + * + * + * Transition T3 + * + * + * + * Transition T4 + * + * + * + * Transition T5 + * + * + * + * Transition T6 + * + * + * + * Transition T7 + * + * + * + * Transition T8 + * + * + * + * Transition T9 + * + * + * + * Transition T10 + * + * + * + * Transition T11 + * + * + * + * Transition T12 + * + * + * + * Transition T13 + * + * + * + * Transition T14 + * + * + * + */ +#define SMB_SESSION_MAGIC 0x53455353 /* 'SESS' */ + +typedef enum { + SMB_SESSION_STATE_DISCONNECTED = 0, + SMB_SESSION_STATE_CONNECTED, + SMB_SESSION_STATE_ESTABLISHED, + SMB_SESSION_STATE_NEGOTIATED, + SMB_SESSION_STATE_OPLOCK_BREAKING, + SMB_SESSION_STATE_WRITE_RAW_ACTIVE, + SMB_SESSION_STATE_TERMINATED +} smb_session_state_t; + +struct smb_session { + uint32_t s_magic; + smb_rwx_t s_lock; + list_node_t s_lnd; + uint64_t s_kid; + smb_session_state_t s_state; + uint32_t s_flags; + int s_write_raw_status; + smb_thread_t s_thread; + uint32_t keep_alive; + uint64_t opentime; + uint16_t vcnumber; + uint16_t s_local_port; + uint32_t ipaddr; + uint32_t local_ipaddr; + char workstation[SMB_PI_MAX_HOST]; + int dialect; + int native_os; + uint32_t capabilities; + struct smb_sign signing; + + struct sonode *sock; + + smb_slist_t s_req_list; + smb_llist_t s_xa_list; + smb_llist_t s_user_list; + smb_idpool_t s_uid_pool; + + volatile uint32_t s_tree_cnt; + volatile uint32_t s_file_cnt; + volatile uint32_t s_dir_cnt; + + uint16_t secmode; + uint32_t sesskey; + uint32_t challenge_len; + unsigned char challenge_key[8]; + unsigned char MAC_key[44]; + int64_t activity_timestamp; + /* + * Maximum negotiated buffer size between SMB client and server + * in SMB_SESSION_SETUP_ANDX + */ + uint16_t smb_msg_size; + uchar_t *outpipe_data; + int outpipe_datalen; + int outpipe_cookie; +}; + +#define SMB_USER_MAGIC 0x55534552 /* 'USER' */ + +#define SMB_USER_FLAG_GUEST SMB_ATF_GUEST +#define SMB_USER_FLAG_IPC SMB_ATF_ANON +#define SMB_USER_FLAG_ADMIN SMB_ATF_ADMIN +#define SMB_USER_FLAG_POWER_USER SMB_ATF_POWERUSER +#define SMB_USER_FLAG_BACKUP_OPERATOR SMB_ATF_BACKUPOP + +#define SMB_USER_PRIV_TAKE_OWNERSHIP 0x00000001 +#define SMB_USER_PRIV_BACKUP 0x00000002 +#define SMB_USER_PRIV_RESTORE 0x00000004 +#define SMB_USER_PRIV_SECURITY 0x00000008 + + +typedef enum { + SMB_USER_STATE_LOGGED_IN = 0, + SMB_USER_STATE_LOGGING_OFF, + SMB_USER_STATE_LOGGED_OFF +} smb_user_state_t; + +typedef struct smb_user { + uint32_t u_magic; + list_node_t u_lnd; + kmutex_t u_mutex; + smb_user_state_t u_state; + + smb_session_t *u_session; + uint16_t u_name_len; + char *u_name; + uint16_t u_domain_len; + char *u_domain; + time_t u_logon_time; + cred_t *u_cred; + + smb_llist_t u_tree_list; + smb_idpool_t u_tid_pool; + + uint32_t u_refcnt; + uint32_t u_flags; + uint32_t u_privileges; + uint16_t u_uid; + uint32_t u_audit_sid; +} smb_user_t; + +#define SMB_TREE_MAGIC 0x54524545 /* 'TREE' */ +#define SMB_TREE_TYPENAME_SZ 8 + +typedef enum { + SMB_TREE_STATE_CONNECTED = 0, + SMB_TREE_STATE_DISCONNECTING, + SMB_TREE_STATE_DISCONNECTED +} smb_tree_state_t; + +typedef struct smb_tree { + uint32_t t_magic; + kmutex_t t_mutex; + list_node_t t_lnd; + smb_tree_state_t t_state; + + smb_session_t *t_session; + smb_user_t *t_user; + smb_node_t *t_snode; + + smb_llist_t t_ofile_list; + smb_idpool_t t_fid_pool; + + smb_llist_t t_odir_list; + smb_idpool_t t_sid_pool; + + uint32_t t_refcnt; + uint32_t t_flags; + int32_t t_res_type; + uint16_t t_tid; + uint16_t t_access; + uint16_t t_umask; + char t_sharename[MAXNAMELEN]; + char t_resource[MAXPATHLEN]; + char t_typename[SMB_TREE_TYPENAME_SZ]; + fs_desc_t t_fsd; + acl_type_t t_acltype; +} smb_tree_t; + +/* Tree access bits */ +#define SMB_TREE_NO_ACCESS 0x0000 +#define SMB_TREE_READ_ONLY 0x0001 +#define SMB_TREE_READ_WRITE 0x0002 + +/* + * Tree flags + * + * SMB_TREE_FLAG_ACLONCREATE Underlying FS supports ACL on create. + * + * SMB_TREE_FLAG_ACEMASKONACCESS Underlying FS understands 32-bit access mask + */ +#define SMB_TREE_FLAG_OPEN 0x0001 +#define SMB_TREE_FLAG_CLOSE 0x0002 +#define SMB_TREE_FLAG_ACLONCREATE 0x0004 +#define SMB_TREE_FLAG_ACEMASKONACCESS 0x0008 +#define SMB_TREE_FLAG_IGNORE_CASE 0x0010 +#define SMB_TREE_CLOSED(tree) ((tree)->t_flags & SMB_TREE_FLAG_CLOSE) + +/* + * SMB_TREE_CASE_INSENSITIVE returns whether operations on a given tree + * will be case-insensitive or not. SMB_TREE_FLAG_IGNORE_CASE is set at + * share set up time based on file system capability and client preference. + */ + +#define SMB_TREE_CASE_INSENSITIVE(sr) \ + (((sr) && (sr)->tid_tree) ? \ + ((sr)->tid_tree->t_flags & SMB_TREE_FLAG_IGNORE_CASE) : 0) + +/* + * SMB_TREE_ROOT_FS is called by certain smb_fsop_* functions to make sure + * that a given vnode is in the same file system as the share root. + */ + +#define SMB_TREE_ROOT_FS(sr, node) \ + (((sr) && (sr)->tid_tree) ? \ + ((sr)->tid_tree->t_snode->vp->v_vfsp == (node)->vp->v_vfsp) : 1) + +#define SMB_TREE_IS_READ_ONLY(sr) \ + ((sr) && ((sr)->tid_tree->t_access == SMB_TREE_READ_ONLY)) + + +#define PIPE_STATE_AUTH_VERIFY 0x00000001 + +/* + * The of_ftype of an open file should contain the SMB_FTYPE value + * (cifs.h) returned when the file/pipe was opened. The following + * assumptions are currently made: + * + * File Type Node PipeInfo + * --------- -------- -------- + * SMB_FTYPE_DISK Valid Null + * SMB_FTYPE_BYTE_PIPE Undefined Undefined + * SMB_FTYPE_MESG_PIPE Null Valid + * SMB_FTYPE_PRINTER Undefined Undefined + * SMB_FTYPE_UNKNOWN Undefined Undefined + */ + +/* + * Some flags for ofile structure + * + * SMB_OFLAGS_SET_DELETE_ON_CLOSE + * Set this flag when the corresponding open operation whose + * DELETE_ON_CLOSE bit of the CreateOptions is set. If any + * open file instance has this bit set, the NODE_FLAGS_DELETE_ON_CLOSE + * will be set for the file node upon close. + */ + +#define SMB_OFLAGS_SET_DELETE_ON_CLOSE 0x0004 +#define SMB_OFLAGS_LLF_POS_VALID 0x0008 + +#define SMB_OFILE_MAGIC 0x4F464C45 /* 'OFLE' */ + +typedef enum { + SMB_OFILE_STATE_OPEN = 0, + SMB_OFILE_STATE_CLOSING, + SMB_OFILE_STATE_CLOSED +} smb_ofile_state_t; + +typedef struct smb_ofile { + uint32_t f_magic; + kmutex_t f_mutex; + list_node_t f_lnd; + list_node_t f_nnd; + smb_ofile_state_t f_state; + + smb_session_t *f_session; + smb_user_t *f_user; + smb_tree_t *f_tree; + smb_node_t *f_node; + + mlsvc_pipe_t *f_pipe_info; + + uint32_t f_refcnt; + uint64_t f_seek_pos; + uint32_t f_flags; + uint32_t f_granted_access; + uint32_t f_share_access; + uint32_t f_create_options; + uint16_t f_fid; + uint16_t f_opened_by_pid; + uint16_t f_ftype; + uint64_t f_llf_pos; + cred_t *f_cr; +} smb_ofile_t; + +/* odir flags bits */ +#define SMB_DIR_FLAG_OPEN 0x0001 +#define SMB_DIR_FLAG_CLOSE 0x0002 +#define SMB_DIR_CLOSED(dir) ((dir)->d_flags & SMB_DIR_FLAG_CLOSE) + +#define SMB_ODIR_MAGIC 0x4F444952 /* 'ODIR' */ + +typedef enum { + SMB_ODIR_STATE_OPEN = 0, + SMB_ODIR_STATE_CLOSING, + SMB_ODIR_STATE_CLOSED +} smb_odir_state_t; + +typedef struct smb_odir { + uint32_t d_magic; + kmutex_t d_mutex; + list_node_t d_lnd; + smb_odir_state_t d_state; + + smb_session_t *d_session; + smb_user_t *d_user; + smb_tree_t *d_tree; + + uint32_t d_refcnt; + uint32_t d_cookie; + uint16_t d_sid; + uint16_t d_opened_by_pid; + uint16_t d_sattr; + char d_pattern[MAXNAMELEN]; + struct smb_node *d_dir_snode; + unsigned int d_wildcards; +} smb_odir_t; + +typedef struct smb_odir_context { + uint32_t dc_cookie; + uint16_t dc_dattr; + char dc_name[MAXNAMELEN]; /* Real 'Xxxx.yyy.xx' */ + char dc_name83[14]; /* w/ dot 'XXXX .XX ' */ + char dc_shortname[14]; /* w/ dot 'XXXX.XX' */ + smb_attr_t dc_attr; +} smb_odir_context_t; + +#define SMB_LOCK_MAGIC 0x4C4F434B /* 'LOCK' */ + +typedef struct smb_lock { + uint32_t l_magic; + kmutex_t l_mutex; + list_node_t l_lnd; + kcondvar_t l_cv; + + list_node_t l_conflict_lnd; + smb_slist_t l_conflict_list; + + smb_session_t *l_session; + smb_ofile_t *l_file; + smb_request_t *l_sr; + + uint32_t l_flags; + uint64_t l_session_kid; + struct smb_lock *l_blocked_by; /* Debug info only */ + + uint16_t l_pid; + uint16_t l_uid; + uint32_t l_type; + uint64_t l_start; + uint64_t l_length; + clock_t l_end_time; +} smb_lock_t; + +#define SMB_LOCK_FLAG_INDEFINITE 0x0004 +#define SMB_LOCK_INDEFINITE_WAIT(lock) \ + ((lock)->l_flags & SMB_LOCK_FLAG_INDEFINITE) + +#define SMB_LOCK_TYPE_READWRITE 101 +#define SMB_LOCK_TYPE_READONLY 102 + + +struct smb_fqi { /* fs_query_info */ + char *path; + uint16_t srch_attr; + struct smb_node *dir_snode; + smb_attr_t dir_attr; + char last_comp[MAXNAMELEN]; + int last_comp_was_found; + char last_comp_od[MAXNAMELEN]; + struct smb_node *last_snode; + smb_attr_t last_attr; +}; + +#define SMB_NULL_FQI_NODES(fqi) \ + (fqi).last_snode = NULL; \ + (fqi).dir_snode = NULL; + +#define FQM_DIR_MUST_EXIST 1 +#define FQM_PATH_MUST_EXIST 2 +#define FQM_PATH_MUST_NOT_EXIST 3 + +#define MYF_OPLOCK_MASK 0x000000F0 +#define MYF_OPLOCK_NONE 0x00000000 +#define MYF_EXCLUSIVE_OPLOCK 0x00000010 +#define MYF_BATCH_OPLOCK 0x00000020 +#define MYF_LEVEL_II_OPLOCK 0x00000030 +#define MYF_MUST_BE_DIRECTORY 0x00000100 + +#define MYF_OPLOCK_TYPE(o) ((o) & MYF_OPLOCK_MASK) +#define MYF_OPLOCKS_REQUEST(o) (MYF_OPLOCK_TYPE(o) != MYF_OPLOCK_NONE) +#define MYF_IS_EXCLUSIVE_OPLOCK(o) (MYF_OPLOCK_TYPE(o) == MYF_EXCLUSIVE_OPLOCK) +#define MYF_IS_BATCH_OPLOCK(o) (MYF_OPLOCK_TYPE(o) == MYF_BATCH_OPLOCK) +#define MYF_IS_LEVEL_II_OPLOCK(o) (MYF_OPLOCK_TYPE(o) == MYF_LEVEL_II_OPLOCK) + +#define OPLOCK_MIN_TIMEOUT (5 * 1000) +#define OPLOCK_STD_TIMEOUT (15 * 1000) + + +/* + * SMB Request State Machine + * ------------------------- + * + * T4 +------+ T0 + * +--------------------------->| FREE |---------------------------+ + * | +------+ | + * +-----------+ | + * | COMPLETED | | + * +-----------+ + * ^ | + * | T15 +----------+ v + * +------------+ T6 | | +--------------+ + * | CLEANED_UP |<-----------------| CANCELED | | INITIALIZING | + * +------------+ | | +--------------+ + * | ^ +----------+ | + * | | ^ ^ ^ ^ | + * | | +-------------+ | | | | + * | | T3 | | | | T13 | T1 + * | +-------------------------+ | | +----------------------+ | + * +----------------------------+ | | | | | + * T16 | | | | +-----------+ | | + * | \/ | | T5 | | v + * +-----------------+ | T12 +--------+ | T2 +-----------+ + * | EVENT_OCCURRED |------------->| ACTIVE |<--------------------| SUBMITTED | + * +-----------------+ | +--------+ | +-----------+ + * ^ | | ^ | | + * | | T8 | | | T7 | + * | T10 T9 | +----------+ | +-------+ | T11 + * | | | +-------+ | | + * | | | T14 | | | + * | | v | v | + * +----------------------+ +--------------+ + * | WAITING_EVENT | | WAITING_LOCK | + * +----------------------+ +--------------+ + * + * + * + * + * + * Transition T0 + * + * This transition occurs when the request is allocated and is still under the + * control of the session thread. + * + * Transition T1 + * + * This transition occurs when the session thread dispatches a task to treat the + * request. + * + * Transition T2 + * + * + * + * Transition T3 + * + * A request completes and smbsr_cleanup is called to release resources + * associated with the request (but not the smb_request_t itself). This + * includes references on smb_ofile_t, smb_node_t, and other structures. + * CLEANED_UP state exists to detect if we attempt to cleanup a request + * multiple times and to allow us to detect that we are accessing a + * request that has already been cleaned up. + * + * Transition T4 + * + * + * + * Transition T5 + * + * + * + * Transition T6 + * + * + * + * Transition T7 + * + * + * + * Transition T8 + * + * + * + * Transition T9 + * + * + * + * Transition T10 + * + * + * + * Transition T11 + * + * + * + * Transition T12 + * + * + * + * Transition T13 + * + * + * + * Transition T14 + * + * + * + * Transition T15 + * + * Request processing is completed (control returns from smb_dispatch) + * + * Transition T16 + * + * Multipart (andx) request was cleaned up with smbsr_cleanup but more "andx" + * sections remain to be processed. + * + */ + +#define SMB_REQ_MAGIC 0x534D4252 /* 'SMBR' */ + +typedef enum smb_req_state { + SMB_REQ_STATE_FREE = 0, + SMB_REQ_STATE_INITIALIZING, + SMB_REQ_STATE_SUBMITTED, + SMB_REQ_STATE_ACTIVE, + SMB_REQ_STATE_WAITING_EVENT, + SMB_REQ_STATE_EVENT_OCCURRED, + SMB_REQ_STATE_WAITING_LOCK, + SMB_REQ_STATE_COMPLETED, + SMB_REQ_STATE_CANCELED, + SMB_REQ_STATE_CLEANED_UP +} smb_req_state_t; + +struct smb_request { + uint32_t sr_magic; + kmutex_t sr_mutex; + list_node_t sr_session_lnd; + smb_req_state_t sr_state; + boolean_t sr_keep; + + struct smb_session *session; + + smb_notify_change_req_t sr_ncr; + + /* Info from session service header */ + uint32_t sr_req_length; /* Excluding NBT header */ + + /* Request buffer excluding NBT header */ + void *sr_request_buf; + + /* Fields for raw writes */ + uint32_t sr_raw_data_length; + void *sr_raw_data_buf; + + smb_lock_t *sr_awaiting; + struct mbuf_chain command; + struct mbuf_chain reply; + struct mbuf_chain raw_data; + smb_malloc_list request_storage; + struct smb_xa *r_xa; + int andx_prev_wct; + int cur_reply_offset; + int orig_request_hdr; + unsigned int reply_seqnum; /* reply sequence number */ + unsigned char first_smb_com; /* command code */ + unsigned char smb_com; /* command code */ + unsigned char smb_rcls; /* error code class */ + unsigned char smb_reh; /* rsvd (AH DOS INT-24 ERR) */ + uint16_t smb_err; /* error code */ + uint8_t smb_flg; /* flags */ + uint16_t smb_flg2; /* flags */ + uint16_t smb_pid_high; /* high part of pid */ + unsigned char smb_sig[8]; /* signiture */ + uint16_t smb_tid; /* tree id # */ + uint16_t smb_pid; /* caller's process id # */ + uint16_t smb_uid; /* user id # */ + uint16_t smb_mid; /* mutiplex id # */ + unsigned char smb_wct; /* count of parameter words */ + uint16_t smb_bcc; /* data byte count */ + + /* Parameters */ + struct mbuf_chain smb_vwv; /* variable width value */ + + /* Data */ + struct mbuf_chain smb_data; + + uint16_t smb_fid; /* not in hdr, but common */ + uint16_t smb_sid; /* not in hdr, but common */ + + unsigned char andx_com; + uint16_t andx_off; + + struct smb_tree *tid_tree; + struct smb_ofile *fid_ofile; + struct smb_odir *sid_odir; + smb_user_t *uid_user; + + union { + struct { + char *path; + char *service; + int pwdlen; + char *password; + uint16_t flags; + } tcon; + + struct open_param { + struct smb_fqi fqi; + uint16_t omode; + uint16_t oflags; + uint16_t ofun; + uint32_t my_flags; + uint32_t timeo; + uint32_t dattr; + timestruc_t utime; + uint64_t dsize; + uint32_t desired_access; + uint32_t share_access; + uint32_t create_options; + uint32_t create_disposition; + uint32_t ftype, devstate; + uint32_t action_taken; + uint64_t fileid; + /* This is only set by NTTransactCreate */ + smb_sdbuf_t *sd_buf; + } open; + + struct { + struct smb_fqi fqi; + struct smb_fqi dst_fqi; + } dirop; + + } arg; + + label_t exjb; + cred_t *user_cr; + caller_context_t ct; +}; + +caller_context_t local_ct; + +#define SMB_READ_PROTOCOL(smb_nh_ptr) \ + LE_IN32(((smb_nethdr_t *)(smb_nh_ptr))->sh_protocol) + +#define SMB_PROTOCOL_MAGIC_INVALID(rd_sr) \ + (SMB_READ_PROTOCOL((rd_sr)->sr_request_buf) != SMB_PROTOCOL_MAGIC) + +#define SMB_READ_COMMAND(smb_nh_ptr) \ + (((smb_nethdr_t *)(smb_nh_ptr))->sh_command) + +#define SMB_IS_WRITERAW(rd_sr) \ + (SMB_READ_COMMAND((rd_sr)->sr_request_buf) == SMB_COM_WRITE_RAW) + + +#define SR_FLG_OFFSET 9 + +#define MAX_TRANS_NAME 64 + +#define SMB_XA_FLAG_OPEN 0x0001 +#define SMB_XA_FLAG_CLOSE 0x0002 +#define SMB_XA_FLAG_COMPLETE 0x0004 +#define SMB_XA_CLOSED(xa) (!((xa)->xa_flags & SMB_XA_FLAG_OPEN)) + +#define SMB_XA_MAGIC 0x534D4258 /* 'SMBX' */ + +typedef struct smb_xa { + uint32_t xa_magic; + kmutex_t xa_mutex; + list_node_t xa_lnd; + + uint32_t xa_refcnt; + uint32_t xa_flags; + + struct smb_session *xa_session; + + unsigned char smb_com; /* which TRANS type */ + unsigned char smb_flg; /* flags */ + uint16_t smb_flg2; /* flags */ + uint16_t smb_tid; /* tree id number */ + uint16_t smb_pid; /* caller's process id number */ + uint16_t smb_uid; /* user id number */ + uint32_t smb_func; /* NT_TRANS function */ + + uint16_t xa_smb_mid; /* mutiplex id number */ + uint16_t xa_smb_fid; /* TRANS2 secondary */ + + unsigned int reply_seqnum; /* reply sequence number */ + + uint32_t smb_tpscnt; /* total parameter bytes being sent */ + uint32_t smb_tdscnt; /* total data bytes being sent */ + uint32_t smb_mprcnt; /* max parameter bytes to return */ + uint32_t smb_mdrcnt; /* max data bytes to return */ + uint32_t smb_msrcnt; /* max setup words to return */ + uint32_t smb_flags; /* additional information: */ + /* bit 0 - if set, disconnect TID in smb_tid */ + /* bit 1 - if set, transaction is one way */ + /* (no final response) */ + int32_t smb_timeout; /* number of milliseconds to await completion */ + uint32_t smb_suwcnt; /* set up word count */ + + + char *xa_smb_trans_name; + + int req_disp_param; + int req_disp_data; + + struct mbuf_chain req_setup_mb; + struct mbuf_chain req_param_mb; + struct mbuf_chain req_data_mb; + + struct mbuf_chain rep_setup_mb; + struct mbuf_chain rep_param_mb; + struct mbuf_chain rep_data_mb; +} smb_xa_t; + + +#define SDDF_NO_FLAGS 0 +#define SDDF_SUPPRESS_TID 0x0001 +#define SDDF_SUPPRESS_UID 0x0002 +#define SDDF_SUPPRESS_UNLEASH 0x0004 +#define SDDF_SUPPRESS_SHOW 0x0080 + +#define SDRC_NORMAL_REPLY 0 +#define SDRC_DROP_VC 1 +#define SDRC_NO_REPLY 2 +#define SDRC_ERROR_REPLY 3 +#define SDRC_UNIMPLEMENTED 4 +#define SDRC_UNSUPPORTED 5 + + +struct vardata_block { + unsigned char tag; + uint16_t len; + struct uio uio; + struct iovec iovec[MAX_IOVEC]; +}; + +#define VAR_BCC ((short)-1) + + + + +extern struct smb_info smb_info; + +#define SMB_SI_NBT_CONNECTED 0x01 +#define SMB_SI_TCP_CONNECTED 0x02 + +typedef struct smb_info { + smb_svc_sm_ctx_t si_svc_sm_ctx; + + uint32_t si_open_progress; + uint32_t si_connect_progress; + + volatile uint64_t si_global_kid; + volatile uint32_t si_gmtoff; + + smb_thread_t si_nbt_daemon; + smb_thread_t si_tcp_daemon; + smb_thread_t si_thread_notify_change; + smb_thread_t si_thread_timers; + + taskq_t *thread_pool; + + kmem_cache_t *si_cache_vfs; + kmem_cache_t *si_cache_request; + kmem_cache_t *si_cache_session; + kmem_cache_t *si_cache_user; + kmem_cache_t *si_cache_tree; + kmem_cache_t *si_cache_ofile; + kmem_cache_t *si_cache_odir; + kmem_cache_t *si_cache_node; + + smb_slist_t si_ncr_list; + smb_slist_t si_nce_list; + + smb_kmod_cfg_t si; + + volatile uint32_t open_trees; + volatile uint32_t open_files; + volatile uint32_t open_users; + + smb_node_t *si_root_smb_node; + smb_llist_t node_hash_table[SMBND_HASH_MASK+1]; + smb_llist_t si_vfs_list; +} smb_info_t; + +#define SMB_INFO_NETBIOS_SESSION_SVC_RUNNING 0x0001 +#define SMB_INFO_NETBIOS_SESSION_SVC_FAILED 0x0002 +#define SMB_INFO_USER_LEVEL_SECURITY 0x40000000 +#define SMB_INFO_ENCRYPT_PASSWORDS 0x80000000 + +#define SMB_NEW_KID() atomic_inc_64_nv(&smb_info.si_global_kid) + +typedef struct { + uint16_t errcls; + uint16_t errcode; + DWORD status; +} smb_error_t; + +/* + * This is to be used by Trans2SetFileInfo + * and Trans2SetPathInfo + */ +typedef struct smb_trans2_setinfo { + uint16_t level; + struct smb_xa *ts_xa; + struct smb_node *node; + char *path; + char name[MAXNAMELEN]; +} smb_trans2_setinfo_t; + + +#define SMB_IS_STREAM(node) ((node)->unnamed_stream_node) + +#ifdef DEBUG +extern uint_t smb_tsd_key; +#endif + +typedef struct smb_tsd { + void (*proc)(); + void *arg; + char name[100]; +} smb_tsd_t; + +#define SMB_INVALID_AMASK -1 +#define SMB_INVALID_SHAREMODE -1 +#define SMB_INVALID_CRDISPOSITION -1 + +typedef struct smb_dispatch_table { + int (*sdt_function)(struct smb_request *); + char sdt_dialect; + unsigned char sdt_flags; + krw_t sdt_slock_mode; + kstat_named_t sdt_dispatch_stats; /* invocations */ +} smb_dispatch_table_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_SMBVAR_H */ diff --git a/usr/src/uts/common/smbsrv/string.h b/usr/src/uts/common/smbsrv/string.h new file mode 100644 index 000000000000..6696cbc33dcd --- /dev/null +++ b/usr/src/uts/common/smbsrv/string.h @@ -0,0 +1,75 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_STRING_H +#define _SMBSRV_STRING_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern char *strsubst(char *, char, char); +extern char *strsep(char **, const char *); +extern char *strcanon(char *, const char *); + +extern char *utf8_strupr(char *); +extern char *utf8_strlwr(char *); +extern int utf8_isstrupr(const char *); +extern int utf8_isstrlwr(const char *); +extern int utf8_strcasecmp(const char *, const char *); +extern int utf8_strncasecmp(const char *, const char *, int); +extern int utf8_isstrascii(const char *); + +extern int smb_match(char *patn, char *str); +extern int smb_match_ci(char *patn, char *str); +extern int smb_match83(char *patn, char *str83); + +/* + * Maximum number of bytes per multi-byte character. + */ +#define MTS_MB_CUR_MAX 3 +#define MTS_MB_CHAR_MAX MTS_MB_CUR_MAX + +size_t mts_mbstowcs(mts_wchar_t *, const char *, size_t); +size_t mts_wcstombs(char *, const mts_wchar_t *, size_t); +int mts_mbtowc(mts_wchar_t *, const char *, size_t); +int mts_wctomb(char *, mts_wchar_t); + +size_t mts_wcequiv_strlen(const char *); +size_t mts_sbequiv_strlen(const char *); + +int mts_stombs(char *, char *, int); +int mts_mbstos(char *, const char *); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_STRING_H */ diff --git a/usr/src/uts/common/smbsrv/svrapi.h b/usr/src/uts/common/smbsrv/svrapi.h new file mode 100644 index 000000000000..4199feb8d7d8 --- /dev/null +++ b/usr/src/uts/common/smbsrv/svrapi.h @@ -0,0 +1,263 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_SVRAPI_H +#define _SMBSRV_SVRAPI_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file provides definitions for the SMB Net interface. On Windows + * this would be NetAccess, NetConnection, NetFile, NetServer, + * NetSession, NetShare and NetSecurity but here things are a limited. + * This stuff should be described in Windows 9x LanMan documentation. + * + * Notes: + * Lengths of ASCIIZ strings are given as the maximum strlen() value. + * This does not include space for the terminating 0-byte. When + * allocating space for such an item, use the form: + * + * char username[LM20_UNLEN+1]; + * + * An exception to this is PATHLEN, which does include space for the + * terminating 0-byte. + * + * User names, computer names and share names should be upper-cased + * by the caller and drawn from the ANSI character set. + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Server Class (NetServerGetInfo, NetServerEnum2) + */ + +struct server_info_0 { + char sv0_name[CNLEN + 1]; /* Server name */ +}; /* server_info_0 */ + + +struct server_info_1 { + char sv1_name[CNLEN + 1]; /* Server name */ + unsigned char sv1_version_major; /* Major version # of net */ + unsigned char sv1_version_minor; /* Minor version # of net */ + uint32_t sv1_type; /* Server type */ + char *sv1_comment; /* Exported server comment */ +}; /* server_info_1 */ + + +/* NOTE struct prefix must equal server_info_1 format */ + +struct server_info_50 { + char sv50_name[CNLEN + 1]; + unsigned char sv50_version_major; /* Major version # of net */ + unsigned char sv50_version_minor; /* Minor version # of net */ + uint32_t sv50_type; /* Server type */ + char *sv50_comment; /* Exported server comment */ + unsigned short sv50_security; /* SV_SECURITY_* (see below) */ + unsigned short sv50_auditing; /* 0 = no auditing; !0 = auditing */ + char *sv50_container; /* Security server/domain */ + char *sv50_ab_server; /* Address book server */ + char *sv50_ab_dll; /* Address book provider DLL */ +}; /* server_info_50 */ + + +struct server_info_2 { + char sv2_name[CNLEN + 1]; + unsigned char sv2_version_major; + unsigned char sv2_version_minor; + uint32_t sv2_type; + char *sv2_comment; + uint32_t sv2_ulist_mtime; /* User list, last modification time */ + uint32_t sv2_glist_mtime; /* Group list, last modification time */ + uint32_t sv2_alist_mtime; /* Access list, last modification time */ + uint16_t sv2_users; /* max number of users allowed */ + uint16_t sv2_disc; /* auto-disconnect timeout(in minutes) */ + char *sv2_alerts; /* alert names (semicolon separated) */ + uint16_t sv2_security; /* SV_USERSECURITY or SV_SHARESECURITY */ + uint16_t sv2_auditing; /* 0 = no auditing; nonzero = auditing */ + + uint16_t sv2_numadmin; /* max number of administrators allowed */ + uint16_t sv2_lanmask; /* bit mask representing the srv'd nets */ + uint16_t sv2_hidden; /* 0 = visible; nonzero = hidden */ + uint16_t sv2_announce; /* visible server announce rate (sec) */ + uint16_t sv2_anndelta; /* announce randomize interval (sec) */ + /* name of guest account */ + char sv2_guestacct[LM20_UNLEN + 1]; + unsigned char sv2_pad1; /* Word alignment pad byte */ + char *sv2_userpath; /* ASCIIZ path to user directories */ + uint16_t sv2_chdevs; /* max # shared character devices */ + uint16_t sv2_chdevq; /* max # character device queues */ + uint16_t sv2_chdevjobs; /* max # character device jobs */ + uint16_t sv2_connections; /* max # of connections */ + uint16_t sv2_shares; /* max # of shares */ + uint16_t sv2_openfiles; /* max # of open files */ + uint16_t sv2_sessopens; /* max # of open files per session */ + uint16_t sv2_sessvcs; /* max # of virtual circuits per client */ + uint16_t sv2_sessreqs; /* max # of simul. reqs. from a client */ + uint16_t sv2_opensearch; /* max # of open searches */ + uint16_t sv2_activelocks; /* max # of active file locks */ + uint16_t sv2_numreqbuf; /* number of server (standard) buffers */ + uint16_t sv2_sizreqbuf; /* size of svr (standard) bufs (bytes) */ + uint16_t sv2_numbigbuf; /* number of big (64K) buffers */ + uint16_t sv2_numfiletasks; /* number of file worker processes */ + uint16_t sv2_alertsched; /* alert counting interval (minutes) */ + uint16_t sv2_erroralert; /* error log alerting threshold */ + uint16_t sv2_logonalert; /* logon violation alerting threshold */ + uint16_t sv2_accessalert; /* access violation alerting threshold */ + uint16_t sv2_diskalert; /* low disk space alert threshold (KB) */ + uint16_t sv2_netioalert; /* net I/O error ratio alert threshold */ + /* (tenths of a percent) */ + uint16_t sv2_maxauditsz; /* Maximum audit file size (KB) */ + char *sv2_srvheuristics; /* performance related server switches */ +}; /* server_info_2 */ + + +struct server_info_3 { + char sv3_name[CNLEN + 1]; + unsigned char sv3_version_major; + unsigned char sv3_version_minor; + uint32_t sv3_type; + char *sv3_comment; + uint32_t sv3_ulist_mtime; /* User list, last modification time */ + uint32_t sv3_glist_mtime; /* Group list, last modification time */ + uint32_t sv3_alist_mtime; /* Access list, last modification time */ + uint16_t sv3_users; /* max number of users allowed */ + uint16_t sv3_disc; /* auto-disconnect timeout(in minutes) */ + char *sv3_alerts; /* alert names (semicolon separated) */ + uint16_t sv3_security; /* SV_USERSECURITY or SV_SHARESECURITY */ + uint16_t sv3_auditing; /* 0 = no auditing; nonzero = auditing */ + + uint16_t sv3_numadmin; /* max number of administrators allowed */ + uint16_t sv3_lanmask; /* bit mask representing the srv'd nets */ + uint16_t sv3_hidden; /* 0 = visible; nonzero = hidden */ + uint16_t sv3_announce; /* visible server announce rate (sec) */ + uint16_t sv3_anndelta; /* announce randomize interval (sec) */ + /* name of guest account */ + char sv3_guestacct[LM20_UNLEN + 1]; + unsigned char sv3_pad1; /* Word alignment pad byte */ + char *sv3_userpath; /* ASCIIZ path to user directories */ + uint16_t sv3_chdevs; /* max # shared character devices */ + uint16_t sv3_chdevq; /* max # character device queues */ + uint16_t sv3_chdevjobs; /* max # character device jobs */ + uint16_t sv3_connections; /* max # of connections */ + uint16_t sv3_shares; /* max # of shares */ + uint16_t sv3_openfiles; /* max # of open files */ + uint16_t sv3_sessopens; /* max # of open files per session */ + uint16_t sv3_sessvcs; /* max # of virtual circuits per client */ + uint16_t sv3_sessreqs; /* max # of simul. reqs. from a client */ + uint16_t sv3_opensearch; /* max # of open searches */ + uint16_t sv3_activelocks; /* max # of active file locks */ + uint16_t sv3_numreqbuf; /* number of server (standard) buffers */ + uint16_t sv3_sizreqbuf; /* size of svr (standard) bufs (bytes) */ + uint16_t sv3_numbigbuf; /* number of big (64K) buffers */ + uint16_t sv3_numfiletasks; /* number of file worker processes */ + uint16_t sv3_alertsched; /* alert counting interval (minutes) */ + uint16_t sv3_erroralert; /* error log alerting threshold */ + uint16_t sv3_logonalert; /* logon violation alerting threshold */ + uint16_t sv3_accessalert; /* access violation alerting threshold */ + uint16_t sv3_diskalert; /* low disk space alert threshold (KB) */ + uint16_t sv3_netioalert; /* net I/O error ratio alert threshold */ + /* (tenths of a percent) */ + uint16_t sv3_maxauditsz; /* Maximum audit file size (KB) */ + char *sv3_srvheuristics; /* performance related server switches */ + uint32_t sv3_auditedevents; /* Audit event control mask */ + uint16_t sv3_autoprofile; /* (0,1,2,3) = (NONE,LOAD,SAVE,or BOTH) */ + char *sv3_autopath; /* file pathname (where to load & save) */ +}; /* server_info_3 */ + + +/* + * Mask to be applied to svX_version_major in order to obtain + * the major version number. + */ +#define MAJOR_VERSION_MASK 0x0F + + +/* + * Bit-mapped values for svX_type fields. X = 1, 2, 3 etc. + * + * SV_TYPE_WORKSTATION 0x00000001 All workstations + * SV_TYPE_SERVER 0x00000002 All servers + * SV_TYPE_SQLSERVER 0x00000004 Any server running with SQL + * server + * SV_TYPE_DOMAIN_CTRL 0x00000008 Primary domain controller + * SV_TYPE_DOMAIN_BAKCTRL 0x00000010 Backup domain controller + * SV_TYPE_TIME_SOURCE 0x00000020 Server running the timesource + * service + * SV_TYPE_AFP 0x00000040 Apple File Protocol servers + * SV_TYPE_NOVELL 0x00000080 Novell servers + * SV_TYPE_DOMAIN_MEMBER 0x00000100 Domain Member + * SV_TYPE_PRINTQ_SERVER 0x00000200 Server sharing print queue + * SV_TYPE_DIALIN_SERVER 0x00000400 Server running dialin service. + * SV_TYPE_XENIX_SERVER 0x00000800 Xenix server + * SV_TYPE_NT 0x00001000 NT server + * SV_TYPE_WFW 0x00002000 Server running Windows for + * Workgroups + * SV_TYPE_SERVER_NT 0x00008000 Windows NT non DC server + * SV_TYPE_POTENTIAL_BROWSER 0x00010000 Server that can run the browser + * service + * SV_TYPE_BACKUP_BROWSER 0x00020000 Backup browser server + * SV_TYPE_MASTER_BROWSER 0x00040000 Master browser server + * SV_TYPE_DOMAIN_MASTER 0x00080000 Domain Master Browser server + * SV_TYPE_LOCAL_LIST_ONLY 0x40000000 Enumerate only entries marked + * "local" + * SV_TYPE_DOMAIN_ENUM 0x80000000 Enumerate Domains. The pszDomain + * parameter must be NULL. + */ +#define SV_TYPE_WORKSTATION 0x00000001 +#define SV_TYPE_SERVER 0x00000002 +#define SV_TYPE_SQLSERVER 0x00000004 +#define SV_TYPE_DOMAIN_CTRL 0x00000008 +#define SV_TYPE_DOMAIN_BAKCTRL 0x00000010 +#define SV_TYPE_TIME_SOURCE 0x00000020 +#define SV_TYPE_AFP 0x00000040 +/* Also set by Win95 NWSERVER */ +#define SV_TYPE_NOVELL 0x00000080 +#define SV_TYPE_DOMAIN_MEMBER 0x00000100 +#define SV_TYPE_PRINTQ_SERVER 0x00000200 +#define SV_TYPE_DIALIN_SERVER 0x00000400 +#define SV_TYPE_XENIX_SERVER 0x00000800 +#define SV_TYPE_NT 0x00001000 +#define SV_TYPE_WFW 0x00002000 +#define SV_TYPE_SERVER_NT 0x00008000 +#define SV_TYPE_POTENTIAL_BROWSER 0x00010000 +#define SV_TYPE_BACKUP_BROWSER 0x00020000 +#define SV_TYPE_MASTER_BROWSER 0x00040000 +#define SV_TYPE_DOMAIN_MASTER 0x00080000 +#define SV_TYPE_LOCAL_LIST_ONLY 0x40000000 +#define SV_TYPE_DOMAIN_ENUM 0x80000000 +/* Handy for NetServerEnum2 */ +#define SV_TYPE_ALL 0xFFFFFFFF + + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_SVRAPI_H */ diff --git a/usr/src/uts/common/smbsrv/winioctl.h b/usr/src/uts/common/smbsrv/winioctl.h new file mode 100644 index 000000000000..8a9badc5443b --- /dev/null +++ b/usr/src/uts/common/smbsrv/winioctl.h @@ -0,0 +1,475 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +#ifndef _SMBSRV_WINIOCTL_H +#define _SMBSRV_WINIOCTL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Standard Windows NT IOCTL/FSCTL definitions (derived from the VC++ + * include file of the same name). + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _DEVIOCTL_ +#define _DEVIOCTL_ + +/* + * begin_ntddk begin_wdm begin_nthal begin_ntifs + * + * Define the various device type values. Note that values used by Microsoft + * Corporation are in the range 0-32767, and 32768-65535 are reserved for use + * by customers. + */ + +#define DEVICE_TYPE DWORD + +#define FILE_DEVICE_BEEP 0x00000001 +#define FILE_DEVICE_CD_ROM 0x00000002 +#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 +#define FILE_DEVICE_CONTROLLER 0x00000004 +#define FILE_DEVICE_DATALINK 0x00000005 +#define FILE_DEVICE_DFS 0x00000006 +#define FILE_DEVICE_DISK 0x00000007 +#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 +#define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#define FILE_DEVICE_INPORT_PORT 0x0000000a +#define FILE_DEVICE_KEYBOARD 0x0000000b +#define FILE_DEVICE_MAILSLOT 0x0000000c +#define FILE_DEVICE_MIDI_IN 0x0000000d +#define FILE_DEVICE_MIDI_OUT 0x0000000e +#define FILE_DEVICE_MOUSE 0x0000000f +#define FILE_DEVICE_MULTI_UNC_PROVIDER 0x00000010 +#define FILE_DEVICE_NAMED_PIPE 0x00000011 +#define FILE_DEVICE_NETWORK 0x00000012 +#define FILE_DEVICE_NETWORK_BROWSER 0x00000013 +#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 +#define FILE_DEVICE_NULL 0x00000015 +#define FILE_DEVICE_PARALLEL_PORT 0x00000016 +#define FILE_DEVICE_PHYSICAL_NETCARD 0x00000017 +#define FILE_DEVICE_PRINTER 0x00000018 +#define FILE_DEVICE_SCANNER 0x00000019 +#define FILE_DEVICE_SERIAL_MOUSE_PORT 0x0000001a +#define FILE_DEVICE_SERIAL_PORT 0x0000001b +#define FILE_DEVICE_SCREEN 0x0000001c +#define FILE_DEVICE_SOUND 0x0000001d +#define FILE_DEVICE_STREAMS 0x0000001e +#define FILE_DEVICE_TAPE 0x0000001f +#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 +#define FILE_DEVICE_TRANSPORT 0x00000021 +#define FILE_DEVICE_UNKNOWN 0x00000022 +#define FILE_DEVICE_VIDEO 0x00000023 +#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 +#define FILE_DEVICE_WAVE_IN 0x00000025 +#define FILE_DEVICE_WAVE_OUT 0x00000026 +#define FILE_DEVICE_8042_PORT 0x00000027 +#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 +#define FILE_DEVICE_BATTERY 0x00000029 +#define FILE_DEVICE_BUS_EXTENDER 0x0000002a +#define FILE_DEVICE_MODEM 0x0000002b +#define FILE_DEVICE_VDM 0x0000002c +#define FILE_DEVICE_MASS_STORAGE 0x0000002d +#define FILE_DEVICE_SMB 0x0000002e +#define FILE_DEVICE_KS 0x0000002f +#define FILE_DEVICE_CHANGER 0x00000030 +#define FILE_DEVICE_SMARTCARD 0x00000031 +#define FILE_DEVICE_ACPI 0x00000032 +#define FILE_DEVICE_DVD 0x00000033 +#define FILE_DEVICE_FULLSCREEN_VIDEO 0x00000034 +#define FILE_DEVICE_DFS_FILE_SYSTEM 0x00000035 +#define FILE_DEVICE_DFS_VOLUME 0x00000036 + +/* + * Macro definition for defining IOCTL and FSCTL function control codes. Note + * that function codes 0-2047 are reserved for Microsoft Corporation, and + * 2048-4095 are reserved for customers. + */ + +#define CTL_CODE(DeviceType, Function, Method, Access) \ + (((DeviceType) << 16) | ((Access) << 14) | \ + ((Function) << 2) | (Method)) + +/* + * Define the method codes for how buffers are passed for I/O and FS controls + */ + +#define METHOD_BUFFERED 0 +#define METHOD_IN_DIRECT 1 +#define METHOD_OUT_DIRECT 2 +#define METHOD_NEITHER 3 + +/* + * Define the access check value for any access + */ + +#define FILE_ANY_ACCESS 0 +#define FILE_READ_ACCESS 0x0001 /* file & pipe */ +#define FILE_WRITE_ACCESS 0x0002 /* file & pipe */ + +/* end_ntddk end_wdm end_nthal end_ntifs */ + +#endif /* _DEVIOCTL_ */ + + +#ifndef _NTDDSTOR_H_ +#define _NTDDSTOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * IoControlCode values for storage devices + */ + +#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE + +/* + * The following device control codes are common for all class drivers. They + * should be used in place of the older IOCTL_DISK, IOCTL_CDROM and IOCTL_TAPE + * common codes + */ + +#define IOCTL_STORAGE_CHECK_VERIFY \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0200, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_MEDIA_REMOVAL \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_EJECT_MEDIA \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_LOAD_MEDIA \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0203, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_RESERVE \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0204, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_RELEASE \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0205, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_FIND_NEW_DEVICES \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0206, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_STORAGE_GET_MEDIA_TYPES \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0300, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_STORAGE_GET_MEDIA_TYPES_EX \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0301, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_STORAGE_RESET_BUS \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0400, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_RESET_DEVICE \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_STORAGE_GET_DEVICE_NUMBER \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0420, METHOD_BUFFERED, FILE_ANY_ACCESS) + + +#ifdef __cplusplus +} +#endif +#endif /* _NTDDSTOR_H_ */ + +/* + * IoControlCode values for disk devices. + */ + +#define IOCTL_DISK_BASE FILE_DEVICE_DISK +#define IOCTL_DISK_GET_DRIVE_GEOMETRY \ + CTL_CODE(IOCTL_DISK_BASE, 0x0000, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_DISK_GET_PARTITION_INFO \ + CTL_CODE(IOCTL_DISK_BASE, 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_DISK_SET_PARTITION_INFO \ + CTL_CODE(IOCTL_DISK_BASE, 0x0002, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define IOCTL_DISK_GET_DRIVE_LAYOUT \ + CTL_CODE(IOCTL_DISK_BASE, 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_DISK_SET_DRIVE_LAYOUT \ + CTL_CODE(IOCTL_DISK_BASE, 0x0004, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define IOCTL_DISK_VERIFY \ + CTL_CODE(IOCTL_DISK_BASE, 0x0005, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_DISK_FORMAT_TRACKS \ + CTL_CODE(IOCTL_DISK_BASE, 0x0006, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define IOCTL_DISK_REASSIGN_BLOCKS \ + CTL_CODE(IOCTL_DISK_BASE, 0x0007, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define IOCTL_DISK_PERFORMANCE \ + CTL_CODE(IOCTL_DISK_BASE, 0x0008, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_DISK_IS_WRITABLE \ + CTL_CODE(IOCTL_DISK_BASE, 0x0009, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_DISK_LOGGING \ + CTL_CODE(IOCTL_DISK_BASE, 0x000a, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_DISK_FORMAT_TRACKS_EX \ + CTL_CODE(IOCTL_DISK_BASE, 0x000b, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define IOCTL_DISK_HISTOGRAM_STRUCTURE \ + CTL_CODE(IOCTL_DISK_BASE, 0x000c, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_DISK_HISTOGRAM_DATA \ + CTL_CODE(IOCTL_DISK_BASE, 0x000d, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_DISK_HISTOGRAM_RESET \ + CTL_CODE(IOCTL_DISK_BASE, 0x000e, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_DISK_REQUEST_STRUCTURE \ + CTL_CODE(IOCTL_DISK_BASE, 0x000f, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_DISK_REQUEST_DATA \ + CTL_CODE(IOCTL_DISK_BASE, 0x0010, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_DISK_CONTROLLER_NUMBER \ + CTL_CODE(IOCTL_DISK_BASE, 0x0011, METHOD_BUFFERED, FILE_ANY_ACCESS) + +/* + * IOCTL support for SMART drive fault prediction. + */ + +#define SMART_GET_VERSION \ + CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS) +#define SMART_SEND_DRIVE_COMMAND \ + CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define SMART_RCV_DRIVE_DATA \ + CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + + +/* + * The following device control codes are common for all class drivers. The + * functions codes defined here must match all of the other class drivers. + * + * Warning: these codes will be replaced in the future by equivalent + * IOCTL_STORAGE codes + */ + +#define IOCTL_DISK_CHECK_VERIFY \ + CTL_CODE(IOCTL_DISK_BASE, 0x0200, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_DISK_MEDIA_REMOVAL \ + CTL_CODE(IOCTL_DISK_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_DISK_EJECT_MEDIA \ + CTL_CODE(IOCTL_DISK_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_DISK_LOAD_MEDIA \ + CTL_CODE(IOCTL_DISK_BASE, 0x0203, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_DISK_RESERVE \ + CTL_CODE(IOCTL_DISK_BASE, 0x0204, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_DISK_RELEASE \ + CTL_CODE(IOCTL_DISK_BASE, 0x0205, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_DISK_FIND_NEW_DEVICES \ + CTL_CODE(IOCTL_DISK_BASE, 0x0206, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_DISK_GET_MEDIA_TYPES \ + CTL_CODE(IOCTL_DISK_BASE, 0x0300, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_CHANGER_BASE FILE_DEVICE_CHANGER + +#define IOCTL_CHANGER_GET_PARAMETERS \ + CTL_CODE(IOCTL_CHANGER_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_CHANGER_GET_STATUS \ + CTL_CODE(IOCTL_CHANGER_BASE, 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_CHANGER_GET_PRODUCT_DATA \ + CTL_CODE(IOCTL_CHANGER_BASE, 0x0002, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_CHANGER_SET_ACCESS \ + CTL_CODE(IOCTL_CHANGER_BASE, 0x0004, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define IOCTL_CHANGER_GET_ELEMENT_STATUS \ + CTL_CODE(IOCTL_CHANGER_BASE, 0x0005, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define IOCTL_CHANGER_INITIALIZE_ELEMENT_STATUS \ + CTL_CODE(IOCTL_CHANGER_BASE, 0x0006, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_CHANGER_SET_POSITION \ + CTL_CODE(IOCTL_CHANGER_BASE, 0x0007, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_CHANGER_EXCHANGE_MEDIUM \ + CTL_CODE(IOCTL_CHANGER_BASE, 0x0008, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_CHANGER_MOVE_MEDIUM \ + CTL_CODE(IOCTL_CHANGER_BASE, 0x0009, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_CHANGER_REINITIALIZE_TRANSPORT \ + CTL_CODE(IOCTL_CHANGER_BASE, 0x000A, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_CHANGER_QUERY_VOLUME_TAGS \ + CTL_CODE(IOCTL_CHANGER_BASE, 0x000B, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#ifndef _FILESYSTEMFSCTL_ +#define _FILESYSTEMFSCTL_ +/* + * The following is a list of the native file system fsctls followed by + * additional network file system fsctls. Some values have been + * decommissioned. + */ + +#define FSCTL_REQUEST_OPLOCK_LEVEL_1 \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_REQUEST_OPLOCK_LEVEL_2 \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 1, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_REQUEST_BATCH_OPLOCK \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 2, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_OPLOCK_BREAK_ACKNOWLEDGE \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 3, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_OPBATCH_ACK_CLOSE_PENDING \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 4, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_OPLOCK_BREAK_NOTIFY \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 5, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_LOCK_VOLUME \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 6, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_UNLOCK_VOLUME \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 7, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_DISMOUNT_VOLUME \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 8, METHOD_BUFFERED, FILE_ANY_ACCESS) +/* decommissioned fsctl value 9 */ +#define FSCTL_IS_VOLUME_MOUNTED \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 10, METHOD_BUFFERED, FILE_ANY_ACCESS) +/* PATHNAME_BUFFER, */ +#define FSCTL_IS_PATHNAME_VALID \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 11, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_MARK_VOLUME_DIRTY \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 12, METHOD_BUFFERED, FILE_ANY_ACCESS) +/* decommissioned fsctl value 13 */ +#define FSCTL_QUERY_RETRIEVAL_POINTERS \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 14, METHOD_NEITHER, FILE_ANY_ACCESS) +#define FSCTL_GET_COMPRESSION \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 15, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_SET_COMPRESSION \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 16, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) +/* decommissioned fsctl value 17 */ +/* decommissioned fsctl value 18 */ +#define FSCTL_MARK_AS_SYSTEM_HIVE \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 19, METHOD_NEITHER, FILE_ANY_ACCESS) +#define FSCTL_OPLOCK_BREAK_ACK_NO_2 \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 20, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_INVALIDATE_VOLUMES \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 21, METHOD_BUFFERED, FILE_ANY_ACCESS) + +/* FSCTL_QUERY_FAT_BPB_BUFFER */ +#define FSCTL_QUERY_FAT_BPB \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 22, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_REQUEST_FILTER_OPLOCK \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 23, METHOD_BUFFERED, FILE_ANY_ACCESS) + +/* FILESYSTEM_STATISTICS */ +#define FSCTL_FILESYSTEM_GET_STATISTICS \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 24, METHOD_BUFFERED, FILE_ANY_ACCESS) + +/* NTFS_VOLUME_DATA_BUFFER */ +#define FSCTL_GET_NTFS_VOLUME_DATA \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 25, METHOD_BUFFERED, FILE_ANY_ACCESS) +/* NTFS_FILE_RECORD_INPUT_BUFFER, NTFS_FILE_RECORD_OUTPUT_BUFFER */ +#define FSCTL_GET_NTFS_FILE_RECORD \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 26, METHOD_BUFFERED, FILE_ANY_ACCESS) +/* STARTING_LCN_INPUT_BUFFER, VOLUME_BITMAP_BUFFER */ +#define FSCTL_GET_VOLUME_BITMAP \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 27, METHOD_NEITHER, FILE_ANY_ACCESS) +/* STARTING_VCN_INPUT_BUFFER, RETRIEVAL_POINTERS_BUFFER */ +#define FSCTL_GET_RETRIEVAL_POINTERS \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 28, METHOD_NEITHER, FILE_ANY_ACCESS) +/* MOVE_FILE_DATA, */ +#define FSCTL_MOVE_FILE \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 29, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_IS_VOLUME_DIRTY \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 30, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_GET_HFS_INFORMATION \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 31, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_ALLOW_EXTENDED_DASD_IO \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 32, METHOD_NEITHER, FILE_ANY_ACCESS) +#define FSCTL_READ_PROPERTY_DATA \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 33, METHOD_NEITHER, FILE_ANY_ACCESS) +#define FSCTL_WRITE_PROPERTY_DATA \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 34, METHOD_NEITHER, FILE_ANY_ACCESS) +#define FSCTL_FIND_FILES_BY_SID \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 35, METHOD_NEITHER, FILE_ANY_ACCESS) +/* decommissioned fsctl value 36 */ +#define FSCTL_DUMP_PROPERTY_DATA \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 37, METHOD_NEITHER, FILE_ANY_ACCESS) +/* FILE_OBJECTID_BUFFER */ +#define FSCTL_SET_OBJECT_ID \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 38, METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) +/* FILE_OBJECTID_BUFFER */ +#define FSCTL_GET_OBJECT_ID \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 39, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_DELETE_OBJECT_ID \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 40, METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) +/* REPARSE_DATA_BUFFER, */ +#define FSCTL_SET_REPARSE_POINT \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) +/* REPARSE_DATA_BUFFER */ +#define FSCTL_GET_REPARSE_POINT \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) +/* REPARSE_DATA_BUFFER, */ +#define FSCTL_DELETE_REPARSE_POINT \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 43, METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) +/* MFT_ENUM_DATA, */ +#define FSCTL_ENUM_USN_DATA \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 44, METHOD_NEITHER, FILE_READ_ACCESS) +/* BULK_SECURITY_TEST_DATA, */ +#define FSCTL_SECURITY_ID_CHECK \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 45, METHOD_NEITHER, FILE_READ_ACCESS) +/* READ_USN_JOURNAL_DATA, USN */ +#define FSCTL_READ_USN_JOURNAL \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 46, METHOD_NEITHER, FILE_READ_ACCESS) +#define FSCTL_SET_OBJECT_ID_EXTENDED \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 47, METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) +/* FILE_OBJECTID_BUFFER */ +#define FSCTL_CREATE_OR_GET_OBJECT_ID \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 48, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_SET_SPARSE \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) +/* FILE_ZERO_DATA_INFORMATION, */ +#define FSCTL_SET_ZERO_DATA \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 50, METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) +/* FILE_ALLOCATED_RANGE_BUFFER, FILE_ALLOCATED_RANGE_BUFFER */ +#define FSCTL_QUERY_ALLOCATED_RANGES \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 51, METHOD_NEITHER, FILE_READ_ACCESS) +#define FSCTL_ENABLE_UPGRADE \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 52, METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) +/* ENCRYPTION_BUFFER, */ +#define FSCTL_SET_ENCRYPTION \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 53, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_ENCRYPTION_FSCTL_IO \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 54, METHOD_NEITHER, FILE_ANY_ACCESS) +/* ENCRYPTED_DATA_INFO, */ +#define FSCTL_WRITE_RAW_ENCRYPTED \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 55, METHOD_NEITHER, FILE_ANY_ACCESS) +/* REQUEST_RAW_ENCRYPTED_DATA, ENCRYPTED_DATA_INFO */ +#define FSCTL_READ_RAW_ENCRYPTED \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 56, METHOD_NEITHER, FILE_ANY_ACCESS) +/* CREATE_USN_JOUNRAL_DATA, */ +#define FSCTL_CREATE_USN_JOURNAL \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 57, METHOD_NEITHER, FILE_READ_ACCESS) +/* Read the Usn Record for a file */ +#define FSCTL_READ_FILE_USN_DATA \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 58, METHOD_NEITHER, FILE_READ_ACCESS) +/* Generate Close Usn Record */ +#define FSCTL_WRITE_USN_CLOSE_RECORD \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 59, METHOD_NEITHER, FILE_READ_ACCESS) +#define FSCTL_EXTEND_VOLUME \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 60, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#endif /* _FILESYSTEMFSCTL_ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_WINIOCTL_H */ diff --git a/usr/src/uts/common/smbsrv/winsvc.h b/usr/src/uts/common/smbsrv/winsvc.h new file mode 100644 index 000000000000..9da682c1bbc1 --- /dev/null +++ b/usr/src/uts/common/smbsrv/winsvc.h @@ -0,0 +1,217 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_WINSVC_H +#define _SMBSRV_WINSVC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * NT Service Control interface definition for the Service Control + * Manager (SCM). + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Service types (Bit Mask). + * + * SERVICE_WIN32_OWN_PROCESS The service runs in its own process. + * SERVICE_WIN32_SHARE_PROCESS The service shares a process with other + * services. + */ +#define SERVICE_KERNEL_DRIVER 0x00000001 +#define SERVICE_FILE_SYSTEM_DRIVER 0x00000002 +#define SERVICE_ADAPTER 0x00000004 +#define SERVICE_RECOGNIZER_DRIVER 0x00000008 +#define SERVICE_WIN32_OWN_PROCESS 0x00000010 +#define SERVICE_WIN32_SHARE_PROCESS 0x00000020 +#define SERVICE_INTERACTIVE_PROCESS 0x00000100 + +#define SERVICE_DRIVER (SERVICE_KERNEL_DRIVER \ + | SERVICE_FILE_SYSTEM_DRIVER \ + | SERVICE_RECOGNIZER_DRIVER) + +#define SERVICE_WIN32 (SERVICE_WIN32_OWN_PROCESS \ + | SERVICE_WIN32_SHARE_PROCESS) + +#define SERVICE_TYPE_ALL (SERVICE_WIN32 \ + | SERVICE_ADAPTER \ + | SERVICE_DRIVER \ + | SERVICE_INTERACTIVE_PROCESS) + +/* + * Start type. + */ +#define SERVICE_BOOT_START 0x00000000 +#define SERVICE_SYSTEM_START 0x00000001 +#define SERVICE_AUTO_START 0x00000002 +#define SERVICE_DEMAND_START 0x00000003 +#define SERVICE_DISABLED 0x00000004 + +/* + * Error control type. + */ +#define SERVICE_ERROR_IGNORE 0x00000000 +#define SERVICE_ERROR_NORMAL 0x00000001 +#define SERVICE_ERROR_SEVERE 0x00000002 +#define SERVICE_ERROR_CRITICAL 0x00000003 + +/* + * Value to indicate no change to an optional parameter. + */ +#define SERVICE_NO_CHANGE 0xffffffff + +/* + * Service State - for Enum Requests (Bit Mask). + */ +#define SERVICE_ACTIVE 0x00000001 +#define SERVICE_INACTIVE 0x00000002 +#define SERVICE_STATE_ALL (SERVICE_ACTIVE | SERVICE_INACTIVE) + +/* + * Controls + */ +#define SERVICE_CONTROL_STOP 0x00000001 +#define SERVICE_CONTROL_PAUSE 0x00000002 +#define SERVICE_CONTROL_CONTINUE 0x00000003 +#define SERVICE_CONTROL_INTERROGATE 0x00000004 +#define SERVICE_CONTROL_SHUTDOWN 0x00000005 +#define SERVICE_CONTROL_PARAMCHANGE 0x00000006 +#define SERVICE_CONTROL_NETBINDADD 0x00000007 +#define SERVICE_CONTROL_NETBINDREMOVE 0x00000008 +#define SERVICE_CONTROL_NETBINDENABLE 0x00000009 +#define SERVICE_CONTROL_NETBINDDISABLE 0x0000000A + +/* + * Service State -- for CurrentState + */ +#define SERVICE_STOPPED 0x00000001 +#define SERVICE_START_PENDING 0x00000002 +#define SERVICE_STOP_PENDING 0x00000003 +#define SERVICE_RUNNING 0x00000004 +#define SERVICE_CONTINUE_PENDING 0x00000005 +#define SERVICE_PAUSE_PENDING 0x00000006 +#define SERVICE_PAUSED 0x00000007 + +/* + * Controls Accepted (Bit Mask) + * + * SERVICE_ACCEPT_NETBINDCHANGE + * Windows 2000/XP: The service is a network component that + * can accept changes in its binding without being stopped and restarted. + * This control code allows the service to receive SERVICE_CONTROL_NETBINDADD, + * SERVICE_CONTROL_NETBINDREMOVE, SERVICE_CONTROL_NETBINDENABLE, and + * SERVICE_CONTROL_NETBINDDISABLE notifications. + * + * SERVICE_ACCEPT_PARAMCHANGE + * Windows 2000/XP: The service can reread its startup parameters without + * being stopped and restarted. This control code allows the service to + * receive SERVICE_CONTROL_PARAMCHANGE notifications. + * + * SERVICE_ACCEPT_PAUSE_CONTINUE + * The service can be paused and continued. This control code allows the + * service to receive SERVICE_CONTROL_PAUSE and SERVICE_CONTROL_CONTINUE + * notifications. + * + * SERVICE_ACCEPT_SHUTDOWN + * The service is notified when system shutdown occurs. This control code + * allows the service to receive SERVICE_CONTROL_SHUTDOWN notifications. + * Note that ControlService cannot send this notification; only the system + * can send it. + * + * SERVICE_ACCEPT_STOP + * The service can be stopped. This control code allows the service to + * receive SERVICE_CONTROL_STOP notifications. + */ +#define SERVICE_ACCEPT_STOP 0x00000001 +#define SERVICE_ACCEPT_PAUSE_CONTINUE 0x00000002 +#define SERVICE_ACCEPT_SHUTDOWN 0x00000004 +#define SERVICE_ACCEPT_PARAMCHANGE 0x00000008 +#define SERVICE_ACCEPT_NETBINDCHANGE 0x00000010 + +/* + * Service Control Manager object specific access types. + */ +#define SC_MANAGER_CONNECT 0x0001 +#define SC_MANAGER_CREATE_SERVICE 0x0002 +#define SC_MANAGER_ENUMERATE_SERVICE 0x0004 +#define SC_MANAGER_LOCK 0x0008 +#define SC_MANAGER_QUERY_LOCK_STATUS 0x0010 +#define SC_MANAGER_MODIFY_BOOT_CONFIG 0x0020 + +#define SC_MANAGER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED \ + | SC_MANAGER_CONNECT \ + | SC_MANAGER_CREATE_SERVICE \ + | SC_MANAGER_ENUMERATE_SERVICE \ + | SC_MANAGER_LOCK \ + | SC_MANAGER_QUERY_LOCK_STATUS \ + | SC_MANAGER_MODIFY_BOOT_CONFIG) + +/* + * Service object specific access type. + */ +#define SERVICE_QUERY_CONFIG 0x0001 +#define SERVICE_CHANGE_CONFIG 0x0002 +#define SERVICE_QUERY_STATUS 0x0004 +#define SERVICE_ENUMERATE_DEPENDENTS 0x0008 +#define SERVICE_START 0x0010 +#define SERVICE_STOP 0x0020 +#define SERVICE_PAUSE_CONTINUE 0x0040 +#define SERVICE_INTERROGATE 0x0080 +#define SERVICE_USER_DEFINED_CONTROL 0x0100 + +#define SERVICE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED \ + | SERVICE_QUERY_CONFIG \ + | SERVICE_CHANGE_CONFIG \ + | SERVICE_QUERY_STATUS \ + | SERVICE_ENUMERATE_DEPENDENTS \ + | SERVICE_START \ + | SERVICE_STOP \ + | SERVICE_PAUSE_CONTINUE \ + | SERVICE_INTERROGATE \ + | SERVICE_USER_DEFINED_CONTROL) + +/* + * Info levels for ChangeServiceConfig2 and QueryServiceConfig2. + */ +#define SERVICE_CONFIG_DESCRIPTION 1 +#define SERVICE_CONFIG_FAILURE_ACTIONS 2 + +/* + * Actions to take on service failure (SC_ACTION_TYPE). + */ +#define SC_ACTION_NONE 0 +#define SC_ACTION_RESTART 1 +#define SC_ACTION_REBOOT 2 +#define SC_ACTION_RUN_COMMAND 3 + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_WINSVC_H */ diff --git a/usr/src/uts/common/smbsrv/wintypes.h b/usr/src/uts/common/smbsrv/wintypes.h new file mode 100644 index 000000000000..698e5dcdde49 --- /dev/null +++ b/usr/src/uts/common/smbsrv/wintypes.h @@ -0,0 +1,62 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBSRV_WINTYPES_H +#define _SMBSRV_WINTYPES_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Standard win32 types and definitions. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef UNSIGNED_TYPES_DEFINED +#define UNSIGNED_TYPES_DEFINED + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned int DWORD; +/* XXX PGD Shouldn't this be "char *"? */ +typedef unsigned char *LPTSTR; +typedef unsigned char *LPBYTE; +typedef unsigned short *LPWORD; +typedef unsigned int *LPDWORD; + +#endif /* UNSIGNED_TYPES_DEFINED */ + + +#ifndef ANY_SIZE_ARRAY +#define ANY_SIZE_ARRAY 1 +#endif /* ANY_SIZE_ARRAY */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SMBSRV_WINTYPES_H */ diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile index 4cd97b5c3a3a..55b5b18e42b6 100644 --- a/usr/src/uts/common/sys/Makefile +++ b/usr/src/uts/common/sys/Makefile @@ -76,6 +76,7 @@ CHKHDRS= \ acct.h \ acctctl.h \ acl.h \ + acl_impl.h \ aggr.h \ aggr_impl.h \ aio.h \ @@ -85,6 +86,7 @@ CHKHDRS= \ ascii.h \ asynch.h \ atomic.h \ + attr.h \ audio.h \ audiodebug.h \ audioio.h \ @@ -223,6 +225,7 @@ CHKHDRS= \ exacct_impl.h \ exec.h \ exechdr.h \ + extdirent.h \ fault.h \ fasttrap.h \ fasttrap_impl.h \ @@ -540,6 +543,7 @@ CHKHDRS= \ turnstile.h \ types.h \ types32.h \ + tzfile.h \ u8_textprep.h \ u8_textprep_data.h \ uadmin.h \ diff --git a/usr/src/uts/common/sys/acl.h b/usr/src/uts/common/sys/acl.h index b0dc94240d13..98f962debaa5 100644 --- a/usr/src/uts/common/sys/acl.h +++ b/usr/src/uts/common/sys/acl.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -29,6 +29,7 @@ #pragma ident "%Z%%M% %I% %E% SMI" #include +#include #ifdef __cplusplus extern "C" { @@ -62,7 +63,7 @@ typedef struct acl_info acl_t; #define ACL_DEFAULT (0x1000) /* default flag */ /* default object owner */ #define DEF_USER_OBJ (ACL_DEFAULT | USER_OBJ) -/* defalut additional users */ +/* default additional users */ #define DEF_USER (ACL_DEFAULT | USER) /* default owning group */ #define DEF_GROUP_OBJ (ACL_DEFAULT | GROUP_OBJ) @@ -101,6 +102,7 @@ typedef struct acl_info acl_t; #define ACE_SUCCESSFUL_ACCESS_ACE_FLAG 0x0010 #define ACE_FAILED_ACCESS_ACE_FLAG 0x0020 #define ACE_IDENTIFIER_GROUP 0x0040 +#define ACE_INHERITED_ACE 0x0080 #define ACE_OWNER 0x1000 #define ACE_GROUP 0x2000 #define ACE_EVERYONE 0x4000 @@ -110,6 +112,44 @@ typedef struct acl_info acl_t; #define ACE_SYSTEM_AUDIT_ACE_TYPE 0x0002 #define ACE_SYSTEM_ALARM_ACE_TYPE 0x0003 +#define ACL_AUTO_INHERIT 0x0001 +#define ACL_PROTECTED 0x0002 +#define ACL_DEFAULTED 0x0004 +#define ACL_FLAGS_ALL (ACL_AUTO_INHERIT|ACL_PROTECTED| \ + ACL_DEFAULTED) + +#ifdef _KERNEL + +/* + * These are only applicable in a CIFS context. + */ +#define ACE_ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 +#define ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 +#define ACE_ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 +#define ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 +#define ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 +#define ACE_ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 +#define ACE_ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A +#define ACE_ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B +#define ACE_ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C +#define ACE_SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D +#define ACE_SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E +#define ACE_SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F +#define ACE_SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 + +#define ACE_ALL_TYPES 0x001F + +typedef struct ace_object { + uid_t a_who; /* uid or gid */ + uint32_t a_access_mask; /* read,write,... */ + uint16_t a_flags; /* see below */ + uint16_t a_type; /* allow or deny */ + uint8_t a_obj_type[16]; /* obj type */ + uint8_t a_inherit_obj_type[16]; /* inherit obj */ +} ace_object_t; + +#endif + #define ACE_ALL_PERMS (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \ ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_READ_NAMED_ATTRS| \ ACE_WRITE_NAMED_ATTRS|ACE_EXECUTE|ACE_DELETE_CHILD|ACE_READ_ATTRIBUTES| \ @@ -125,7 +165,10 @@ typedef struct acl_info acl_t; ACE_INHERIT_ONLY_ACE | \ ACE_IDENTIFIER_GROUP) -#define ACE_TYPE_FLAGS (ACE_OWNER|ACE_GROUP|ACE_EVERYONE|ACE_IDENTIFIER_GROUP) +#define ACE_TYPE_FLAGS (ACE_OWNER|ACE_GROUP|ACE_EVERYONE| \ + ACE_IDENTIFIER_GROUP) +#define ACE_INHERIT_FLAGS (ACE_FILE_INHERIT_ACE| \ + ACE_DIRECTORY_INHERIT_ACE|ACE_NO_PROPAGATE_INHERIT_ACE|ACE_INHERIT_ONLY_ACE) /* cmd args to acl(2) for aclent_t */ #define GETACL 1 diff --git a/usr/src/uts/common/sys/acl_impl.h b/usr/src/uts/common/sys/acl_impl.h new file mode 100644 index 000000000000..b82f259143ac --- /dev/null +++ b/usr/src/uts/common/sys/acl_impl.h @@ -0,0 +1,61 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_ACL_IMPL_H +#define _SYS_ACL_IMPL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * acl flags + * + * ACL_AUTO_INHERIT, ACL_PROTECTED and ACL_DEFAULTED + * flags can also be stored in this field. + */ +#define ACL_IS_TRIVIAL 0x10000 +#define ACL_IS_DIR 0x20000 + +typedef enum acl_type { + ACLENT_T = 0, + ACE_T = 1 +} acl_type_t; + +struct acl_info { + acl_type_t acl_type; /* style of acl */ + int acl_cnt; /* number of acl entries */ + int acl_entry_size; /* sizeof acl entry */ + int acl_flags; /* special flags about acl */ + void *acl_aclp; /* the acl */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_ACL_IMPL_H */ diff --git a/usr/src/uts/common/sys/attr.h b/usr/src/uts/common/sys/attr.h new file mode 100644 index 000000000000..34e802eed123 --- /dev/null +++ b/usr/src/uts/common/sys/attr.h @@ -0,0 +1,148 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_ATTR_H +#define _SYS_ATTR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _KERNEL +#include +#include +#include +#endif +#include + +/* Attribute names for nvlist's */ +#define A_CRTIME "crtime" +#define A_HIDDEN "hidden" +#define A_SYSTEM "system" +#define A_READONLY "readonly" +#define A_ARCHIVE "archive" +#define A_NOUNLINK "nounlink" +#define A_IMMUTABLE "immutable" +#define A_APPENDONLY "appendonly" +#define A_NODUMP "nodump" +#define A_OPAQUE "opaque" +#define A_AV_QUARANTINED "av_quarantined" +#define A_AV_MODIFIED "av_modified" +#define A_FSID "fsid" +#define A_AV_SCANSTAMP "av_scanstamp" +#define A_MDEV "mdev" +#define A_OWNERSID "ownersid" +#define A_GROUPSID "groupsid" + +/* Attribute option for utilities */ +#define O_HIDDEN "H" +#define O_SYSTEM "S" +#define O_READONLY "R" +#define O_ARCHIVE "A" +#define O_NOUNLINK "u" +#define O_IMMUTABLE "i" +#define O_APPENDONLY "a" +#define O_NODUMP "d" +#define O_AV_QUARANTINED "q" +#define O_AV_MODIFIED "m" +#define O_NONE "" + +/* ownersid and groupsid are composed of two nvpairs */ +#define SID_DOMAIN "domain" +#define SID_RID "rid" + +typedef enum { + F_ATTR_INVAL = -1, + F_ARCHIVE, + F_HIDDEN, + F_READONLY, + F_SYSTEM, + F_APPENDONLY, + F_NODUMP, + F_IMMUTABLE, + F_AV_MODIFIED, + F_OPAQUE, + F_AV_SCANSTAMP, + F_AV_QUARANTINED, + F_NOUNLINK, + F_CRTIME, + F_OWNERSID, + F_GROUPSID, + F_FSID, + F_MDEV, + F_ATTR_ALL +} f_attr_t; + +#define VIEW_READONLY "SUNWattr_ro" +#define VIEW_READWRITE "SUNWattr_rw" + +/* + * These are the supported views into the virtual sysattr directory. + * Additional views should be added before XATTR_VIEW_LAST. + */ +typedef enum { + XATTR_VIEW_INVALID = -1, + XATTR_VIEW_READONLY, + XATTR_VIEW_READWRITE, + XATTR_VIEW_LAST +} xattr_view_t; + +typedef struct { + char *x_name; + char *x_option; + xattr_view_t x_xattr_view; + data_type_t x_data_type; +} xattr_entry_t; + +#ifdef _KERNEL +#define XATTR_MAXFIDSZ NFS_FHMAXDATA + +typedef struct { + uint16_t len; + char parent_fid[XATTR_MAXFIDSZ]; + uint16_t parent_len; + uint16_t dir_offset; +} xattr_fid_t; + +#define XATTR_FIDSZ (sizeof (xattr_fid_t) - sizeof (uint16_t)) + +int xattr_dir_vget(vfs_t *, vnode_t **, fid_t *); +#endif + +int attr_count(void); +const char *attr_to_name(f_attr_t); +const char *attr_to_option(f_attr_t); +f_attr_t name_to_attr(const char *name); +f_attr_t option_to_attr(const char *option); +xattr_view_t attr_to_xattr_view(f_attr_t attr); +data_type_t attr_to_data_type(f_attr_t attr); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_ATTR_H */ diff --git a/usr/src/uts/common/sys/byteorder.h b/usr/src/uts/common/sys/byteorder.h index b80a0f02bd57..36a82e2bbf3b 100644 --- a/usr/src/uts/common/sys/byteorder.h +++ b/usr/src/uts/common/sys/byteorder.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -128,6 +128,68 @@ extern in_port_t ntohs(in_port_t); #define BE_64(x) BSWAP_64(x) #endif +/* + * Macros to read unaligned values from a specific byte order to + * native byte order + */ + +#define BE_IN8(xa) \ + *((uint8_t *)(xa)) + +#define BE_IN16(xa) \ + (((uint16_t)BE_IN8(xa) << 8) | BE_IN8((uint8_t *)(xa)+1)) + +#define BE_IN32(xa) \ + (((uint32_t)BE_IN16(xa) << 16) | BE_IN16((uint8_t *)(xa)+2)) + +#define BE_IN64(xa) \ + (((uint64_t)BE_IN32(xa) << 32) | BE_IN32((uint8_t *)(xa)+4)) + +#define LE_IN8(xa) \ + *((uint8_t *)(xa)) + +#define LE_IN16(xa) \ + (((uint16_t)LE_IN8((uint8_t *)(xa) + 1) << 8) | LE_IN8(xa)) + +#define LE_IN32(xa) \ + (((uint32_t)LE_IN16((uint8_t *)(xa) + 2) << 16) | LE_IN16(xa)) + +#define LE_IN64(xa) \ + (((uint64_t)LE_IN32((uint8_t *)(xa) + 4) << 32) | LE_IN32(xa)) + +/* + * Macros to write unaligned values from native byte order to a specific byte + * order. + */ + +#define BE_OUT8(xa, yv) *((uint8_t *)(xa)) = (uint8_t)(yv); + +#define BE_OUT16(xa, yv) \ + BE_OUT8((uint8_t *)(xa) + 1, yv); \ + BE_OUT8((uint8_t *)(xa), (yv) >> 8); + +#define BE_OUT32(xa, yv) \ + BE_OUT16((uint8_t *)(xa) + 2, yv); \ + BE_OUT16((uint8_t *)(xa), (yv) >> 16); + +#define BE_OUT64(xa, yv) \ + BE_OUT32((uint8_t *)(xa) + 4, yv); \ + BE_OUT32((uint8_t *)(xa), (yv) >> 32); + +#define LE_OUT8(xa, yv) *((uint8_t *)(xa)) = (uint8_t)(yv); + +#define LE_OUT16(xa, yv) \ + LE_OUT8((uint8_t *)(xa), yv); \ + LE_OUT8((uint8_t *)(xa) + 1, (yv) >> 8); + +#define LE_OUT32(xa, yv) \ + LE_OUT16((uint8_t *)(xa), yv); \ + LE_OUT16((uint8_t *)(xa) + 2, (yv) >> 16); + +#define LE_OUT64(xa, yv) \ + LE_OUT32((uint8_t *)(xa), yv); \ + LE_OUT32((uint8_t *)(xa) + 4, (yv) >> 32); + #endif /* !defined(_XPG4_2) || defined(__EXTENSIONS__) */ #ifdef __cplusplus diff --git a/usr/src/uts/common/sys/cred.h b/usr/src/uts/common/sys/cred.h index 29e9a6ddeb7c..41f2827f7845 100644 --- a/usr/src/uts/common/sys/cred.h +++ b/usr/src/uts/common/sys/cred.h @@ -170,6 +170,8 @@ extern void crsetsidlist(cred_t *, struct ksidlist *); extern struct ksid *crgetsid(const cred_t *, int); extern struct ksidlist *crgetsidlist(const cred_t *); +extern int crsetpriv(cred_t *, ...); + #endif /* _KERNEL */ #ifdef __cplusplus diff --git a/usr/src/uts/common/sys/ctfs_impl.h b/usr/src/uts/common/sys/ctfs_impl.h index 1c835a60f6fe..77e491a55059 100644 --- a/usr/src/uts/common/sys/ctfs_impl.h +++ b/usr/src/uts/common/sys/ctfs_impl.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -207,11 +206,16 @@ extern vnode_t *ctfs_create_symnode(vnode_t *, contract_t *); * common ctfs routines */ extern void ctfs_common_getattr(vnode_t *, vattr_t *); -extern int ctfs_close(vnode_t *, int, int, offset_t, cred_t *); -extern int ctfs_access_dir(vnode_t *, int, int, cred_t *); -extern int ctfs_access_readonly(vnode_t *, int, int, cred_t *); -extern int ctfs_access_readwrite(vnode_t *, int, int, cred_t *); -extern int ctfs_open(vnode_t **, int, cred_t *); +extern int ctfs_close(vnode_t *, int, int, offset_t, cred_t *, + caller_context_t *); +extern int ctfs_access_dir(vnode_t *, int, int, cred_t *, + caller_context_t *); +extern int ctfs_access_readonly(vnode_t *, int, int, cred_t *, + caller_context_t *); +extern int ctfs_access_readwrite(vnode_t *, int, int, cred_t *, + caller_context_t *); +extern int ctfs_open(vnode_t **, int, cred_t *, + caller_context_t *); /* * vnode ops vector templates diff --git a/usr/src/uts/common/sys/epm.h b/usr/src/uts/common/sys/epm.h index 35b656409b10..d1c67f383fd2 100644 --- a/usr/src/uts/common/sys/epm.h +++ b/usr/src/uts/common/sys/epm.h @@ -33,6 +33,7 @@ #include #include #include +#include /* * XXXX @@ -166,7 +167,7 @@ typedef struct pm_component { * kidsupcnt counts (the number of components of new-style children at non-zero * level (unknown counts as non-zero)) + (the number of old-style children with * component 0 at non-zero level) for parents that have not asked for - * notifcation. When kidsupcnt is 0 for a nexus node, then pm scans it, + * notification. When kidsupcnt is 0 for a nexus node, then pm scans it, * otherwise it leaves it alone. * Parents that ask for notification always get get scanned, * so we keep their kidsupcnt at zero. @@ -911,7 +912,7 @@ typedef struct pscc { /* pm_state_change_control */ #define PSCCOUNT 128 /* number of state change entries kept per process */ /* - * Struct used to track the existance of devices exporting the + * Struct used to track the existence of devices exporting the * no-involuntary-power-cycles property, and remember things from their * devinfo node for later attach. */ @@ -985,14 +986,6 @@ typedef struct pm_desc_pwrchk { * These defines are used by pm_trans_check() to calculate time. * Mostly copied from "tzfile.h". */ -#define EPOCH_YEAR 1970 -#define SECSPERMIN 60 -#define MINSPERHOUR 60 -#define HOURSPERDAY 24 -#define DAYSPERWEEK 7 -#define DAYSPERNYEAR 365 -#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) -#define SECSPERDAY (SECSPERHOUR * HOURSPERDAY) #define DC_SPY (SECSPERDAY * DAYSPERNYEAR) #define DC_SPW (SECSPERDAY * DAYSPERWEEK) #define DC_SPD SECSPERDAY diff --git a/usr/src/uts/common/sys/extdirent.h b/usr/src/uts/common/sys/extdirent.h new file mode 100644 index 000000000000..3f9a665f0076 --- /dev/null +++ b/usr/src/uts/common/sys/extdirent.h @@ -0,0 +1,77 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_EXTDIRENT_H +#define _SYS_EXTDIRENT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if defined(_KERNEL) + +/* + * Extended file-system independent directory entry. This style of + * dirent provides additional informational flag bits for each + * directory entry. This dirent will be returned instead of the + * standard dirent if a VOP_READDIR() requests dirent flags via + * V_RDDIR_ENTFLAGS, and if the file system supports the flags. + */ +typedef struct edirent { + ino64_t ed_ino; /* "inode number" of entry */ + off64_t ed_off; /* offset of disk directory entry */ + uint32_t ed_eflags; /* per-entry flags */ + unsigned short ed_reclen; /* length of this record */ + char ed_name[1]; /* name of file */ +} edirent_t; + +#define EDIRENT_RECLEN(namelen) \ + ((offsetof(edirent_t, ed_name[0]) + 1 + (namelen) + 7) & ~ 7) +#define EDIRENT_NAMELEN(reclen) \ + ((reclen) - (offsetof(edirent_t, ed_name[0]))) + +/* + * Extended entry flags + * Extended entries include a bitfield of extra information + * regarding that entry. + */ +#define ED_CASE_CONFLICT 0x10 /* Disconsidering case, entry is not unique */ + +/* + * Extended flags accessor function + */ +#define ED_CASE_CONFLICTS(x) ((x)->ed_eflags & ED_CASE_CONFLICT) + +#endif /* defined(_KERNEL) */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_EXTDIRENT_H */ diff --git a/usr/src/uts/common/sys/fcntl.h b/usr/src/uts/common/sys/fcntl.h index 70df5244f632..cb551610605c 100644 --- a/usr/src/uts/common/sys/fcntl.h +++ b/usr/src/uts/common/sys/fcntl.h @@ -329,6 +329,8 @@ typedef struct fshare { #define F_RDACC 0x1 /* Read-only share access */ #define F_WRACC 0x2 /* Write-only share access */ #define F_RWACC 0x3 /* Read-Write share access */ +#define F_RMACC 0x4 /* private flag: Delete share access */ +#define F_MDACC 0x20 /* private flag: Metadata share access */ /* * f_deny values @@ -337,6 +339,7 @@ typedef struct fshare { #define F_RDDNY 0x1 /* Deny others read share access */ #define F_WRDNY 0x2 /* Deny others write share access */ #define F_RWDNY 0x3 /* Deny others read or write share access */ +#define F_RMDNY 0x4 /* private flag: Deny delete share access */ #define F_COMPAT 0x8 /* Set share to old DOS compatibility mode */ #define F_MANDDNY 0x10 /* private flag: mandatory enforcement */ #endif /* defined(__EXTENSIONS__) || !defined(__XOPEN_OR_POSIX) */ diff --git a/usr/src/uts/common/sys/fem.h b/usr/src/uts/common/sys/fem.h index 28167342917c..e1f051b4ad60 100644 --- a/usr/src/uts/common/sys/fem.h +++ b/usr/src/uts/common/sys/fem.h @@ -92,6 +92,8 @@ typedef struct fsem fsem_t; typedef int femop_t(); +typedef void (*fem_func_t)(void *); + /* * The following enumerations specify the conditions * under which a monitor (operation/argument combination) @@ -146,95 +148,119 @@ struct fem_head { * the fem structure (below) and the fs_func_p union (vfs_opreg.h). */ #define FEM_OPS \ - int (*femop_open)(femarg_t *vf, int mode, cred_t *cr); \ + int (*femop_open)(femarg_t *vf, int mode, cred_t *cr, \ + caller_context_t *ct); \ int (*femop_close)(femarg_t *vf, int flag, int count, \ - offset_t offset, cred_t *cr); \ + offset_t offset, cred_t *cr, \ + caller_context_t *ct); \ int (*femop_read)(femarg_t *vf, uio_t *uiop, int ioflag, \ - cred_t *cr, struct caller_context *ct); \ + cred_t *cr, caller_context_t *ct); \ int (*femop_write)(femarg_t *vf, uio_t *uiop, int ioflag, \ - cred_t *cr, struct caller_context *ct); \ + cred_t *cr, caller_context_t *ct); \ int (*femop_ioctl)(femarg_t *vf, int cmd, intptr_t arg, \ - int flag, cred_t *cr, int *rvalp); \ + int flag, cred_t *cr, int *rvalp, \ + caller_context_t *ct); \ int (*femop_setfl)(femarg_t *vf, int oflags, int nflags, \ - cred_t *cr); \ + cred_t *cr, caller_context_t *ct); \ int (*femop_getattr)(femarg_t *vf, vattr_t *vap, int flags, \ - cred_t *cr); \ + cred_t *cr, caller_context_t *ct); \ int (*femop_setattr)(femarg_t *vf, vattr_t *vap, int flags, \ cred_t *cr, caller_context_t *ct); \ int (*femop_access)(femarg_t *vf, int mode, int flags, \ - cred_t *cr); \ + cred_t *cr, caller_context_t *ct); \ int (*femop_lookup)(femarg_t *vf, char *nm, vnode_t **vpp, \ pathname_t *pnp, int flags, vnode_t *rdir, \ - cred_t *cr); \ + cred_t *cr, caller_context_t *ct, \ + int *direntflags, pathname_t *realpnp); \ int (*femop_create)(femarg_t *vf, char *name, vattr_t *vap, \ vcexcl_t excl, int mode, vnode_t **vpp, \ - cred_t *cr, int flag); \ - int (*femop_remove)(femarg_t *vf, char *nm, cred_t *cr); \ + cred_t *cr, int flag, caller_context_t *ct, \ + vsecattr_t *vsecp); \ + int (*femop_remove)(femarg_t *vf, char *nm, cred_t *cr, \ + caller_context_t *ct, int flags); \ int (*femop_link)(femarg_t *vf, vnode_t *svp, char *tnm, \ - cred_t *cr); \ + cred_t *cr, caller_context_t *ct, int flags); \ int (*femop_rename)(femarg_t *vf, char *snm, vnode_t *tdvp, \ - char *tnm, cred_t *cr); \ + char *tnm, cred_t *cr, caller_context_t *ct, \ + int flags); \ int (*femop_mkdir)(femarg_t *vf, char *dirname, vattr_t *vap, \ - vnode_t **vpp, cred_t *cr); \ + vnode_t **vpp, cred_t *cr, \ + caller_context_t *ct, int flags, \ + vsecattr_t *vsecp); \ int (*femop_rmdir)(femarg_t *vf, char *nm, vnode_t *cdir, \ - cred_t *cr); \ + cred_t *cr, caller_context_t *ct, int flags); \ int (*femop_readdir)(femarg_t *vf, uio_t *uiop, cred_t *cr, \ - int *eofp); \ + int *eofp, caller_context_t *ct, int flags); \ int (*femop_symlink)(femarg_t *vf, char *linkname, \ - vattr_t *vap, char *target, cred_t *cr); \ - int (*femop_readlink)(femarg_t *vf, uio_t *uiop, cred_t *cr); \ - int (*femop_fsync)(femarg_t *vf, int syncflag, cred_t *cr); \ - void (*femop_inactive)(femarg_t *vf, cred_t *cr); \ - int (*femop_fid)(femarg_t *vf, fid_t *fidp); \ + vattr_t *vap, char *target, cred_t *cr, \ + caller_context_t *ct, int flags); \ + int (*femop_readlink)(femarg_t *vf, uio_t *uiop, cred_t *cr, \ + caller_context_t *ct); \ + int (*femop_fsync)(femarg_t *vf, int syncflag, cred_t *cr, \ + caller_context_t *ct); \ + void (*femop_inactive)(femarg_t *vf, cred_t *cr, \ + caller_context_t *ct); \ + int (*femop_fid)(femarg_t *vf, fid_t *fidp, \ + caller_context_t *ct); \ int (*femop_rwlock)(femarg_t *vf, int write_lock, \ caller_context_t *ct); \ void (*femop_rwunlock)(femarg_t *vf, int write_lock, \ caller_context_t *ct); \ int (*femop_seek)(femarg_t *vf, offset_t ooff, \ - offset_t *noffp); \ - int (*femop_cmp)(femarg_t *vf, vnode_t *vp2); \ + offset_t *noffp, caller_context_t *ct); \ + int (*femop_cmp)(femarg_t *vf, vnode_t *vp2, \ + caller_context_t *ct); \ int (*femop_frlock)(femarg_t *vf, int cmd, struct flock64 *bfp, \ int flag, offset_t offset, \ - struct flk_callback *flk_cbp, cred_t *cr); \ + struct flk_callback *flk_cbp, cred_t *cr, \ + caller_context_t *ct); \ int (*femop_space)(femarg_t *vf, int cmd, struct flock64 *bfp, \ int flag, offset_t offset, cred_t *cr, \ caller_context_t *ct); \ - int (*femop_realvp)(femarg_t *vf, vnode_t **vpp); \ + int (*femop_realvp)(femarg_t *vf, vnode_t **vpp, \ + caller_context_t *ct); \ int (*femop_getpage)(femarg_t *vf, offset_t off, size_t len, \ uint_t *protp, struct page **plarr, \ size_t plsz, struct seg *seg, caddr_t addr, \ - enum seg_rw rw, cred_t *cr); \ + enum seg_rw rw, cred_t *cr, \ + caller_context_t *ct); \ int (*femop_putpage)(femarg_t *vf, offset_t off, size_t len, \ - int flags, cred_t *cr); \ + int flags, cred_t *cr, caller_context_t *ct); \ int (*femop_map)(femarg_t *vf, offset_t off, struct as *as, \ caddr_t *addrp, size_t len, uchar_t prot, \ - uchar_t maxprot, uint_t flags, cred_t *cr); \ + uchar_t maxprot, uint_t flags, cred_t *cr, \ + caller_context_t *ct); \ int (*femop_addmap)(femarg_t *vf, offset_t off, struct as *as, \ caddr_t addr, size_t len, uchar_t prot, \ - uchar_t maxprot, uint_t flags, cred_t *cr); \ + uchar_t maxprot, uint_t flags, cred_t *cr, \ + caller_context_t *ct); \ int (*femop_delmap)(femarg_t *vf, offset_t off, struct as *as, \ caddr_t addr, size_t len, uint_t prot, \ - uint_t maxprot, uint_t flags, cred_t *cr); \ + uint_t maxprot, uint_t flags, cred_t *cr, \ + caller_context_t *ct); \ int (*femop_poll)(femarg_t *vf, short events, int anyyet, \ - short *reventsp, struct pollhead **phpp); \ + short *reventsp, struct pollhead **phpp, \ + caller_context_t *ct); \ int (*femop_dump)(femarg_t *vf, caddr_t addr, int lbdn, \ - int dblks); \ + int dblks, caller_context_t *ct); \ int (*femop_pathconf)(femarg_t *vf, int cmd, ulong_t *valp, \ - cred_t *cr); \ + cred_t *cr, caller_context_t *ct); \ int (*femop_pageio)(femarg_t *vf, struct page *pp, \ u_offset_t io_off, size_t io_len, int flags, \ - cred_t *cr); \ - int (*femop_dumpctl)(femarg_t *vf, int action, int *blkp); \ + cred_t *cr, caller_context_t *ct); \ + int (*femop_dumpctl)(femarg_t *vf, int action, int *blkp, \ + caller_context_t *ct); \ void (*femop_dispose)(femarg_t *vf, struct page *pp, int flag, \ - int dn, cred_t *cr); \ + int dn, cred_t *cr, caller_context_t *ct); \ int (*femop_setsecattr)(femarg_t *vf, vsecattr_t *vsap, \ - int flag, cred_t *cr); \ + int flag, cred_t *cr, caller_context_t *ct); \ int (*femop_getsecattr)(femarg_t *vf, vsecattr_t *vsap, \ - int flag, cred_t *cr); \ + int flag, cred_t *cr, caller_context_t *ct); \ int (*femop_shrlock)(femarg_t *vf, int cmd, \ - struct shrlock *shr, int flag, cred_t *cr); \ + struct shrlock *shr, int flag, cred_t *cr, \ + caller_context_t *ct); \ int (*femop_vnevent)(femarg_t *vf, vnevent_t vnevent, \ - vnode_t *dvp, char *cname) + vnode_t *dvp, char *cname, caller_context_t *ct) /* NB: No ";" */ struct fem { @@ -267,83 +293,105 @@ struct fsem { FSEM_OPS; /* Signatures of all FSEM operations (fsemops) */ }; -extern int vnext_open(femarg_t *vf, int mode, cred_t *cr); +extern int vnext_open(femarg_t *vf, int mode, cred_t *cr, + caller_context_t *ct); extern int vnext_close(femarg_t *vf, int flag, int count, offset_t offset, - cred_t *cr); + cred_t *cr, caller_context_t *ct); extern int vnext_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr, - struct caller_context *ct); + caller_context_t *ct); extern int vnext_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr, - struct caller_context *ct); + caller_context_t *ct); extern int vnext_ioctl(femarg_t *vf, int cmd, intptr_t arg, int flag, - cred_t *cr, int *rvalp); -extern int vnext_setfl(femarg_t *vf, int oflags, int nflags, cred_t *cr); -extern int vnext_getattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr); + cred_t *cr, int *rvalp, caller_context_t *ct); +extern int vnext_setfl(femarg_t *vf, int oflags, int nflags, cred_t *cr, + caller_context_t *ct); +extern int vnext_getattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr, + caller_context_t *ct); extern int vnext_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr, caller_context_t *ct); -extern int vnext_access(femarg_t *vf, int mode, int flags, cred_t *cr); +extern int vnext_access(femarg_t *vf, int mode, int flags, cred_t *cr, + caller_context_t *ct); extern int vnext_lookup(femarg_t *vf, char *nm, vnode_t **vpp, pathname_t *pnp, int flags, vnode_t *rdir, - cred_t *cr); + cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp); extern int vnext_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl, int mode, vnode_t **vpp, cred_t *cr, - int flag); -extern int vnext_remove(femarg_t *vf, char *nm, cred_t *cr); -extern int vnext_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr); + int flag, caller_context_t *ct, vsecattr_t *vsecp); +extern int vnext_remove(femarg_t *vf, char *nm, cred_t *cr, + caller_context_t *ct, int flags); +extern int vnext_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr, + caller_context_t *ct, int flags); extern int vnext_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, - cred_t *cr); + cred_t *cr, caller_context_t *ct, int flags); extern int vnext_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, - vnode_t **vpp, cred_t *cr); -extern int vnext_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr); -extern int vnext_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp); + vnode_t **vpp, cred_t *cr, caller_context_t *ct, + int flags, vsecattr_t *vsecp); +extern int vnext_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr, + caller_context_t *ct, int flags); +extern int vnext_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags); extern int vnext_symlink(femarg_t *vf, char *linkname, vattr_t *vap, - char *target, cred_t *cr); -extern int vnext_readlink(femarg_t *vf, uio_t *uiop, cred_t *cr); -extern int vnext_fsync(femarg_t *vf, int syncflag, cred_t *cr); -extern void vnext_inactive(femarg_t *vf, cred_t *cr); -extern int vnext_fid(femarg_t *vf, fid_t *fidp); + char *target, cred_t *cr, caller_context_t *ct, + int flags); +extern int vnext_readlink(femarg_t *vf, uio_t *uiop, cred_t *cr, + caller_context_t *ct); +extern int vnext_fsync(femarg_t *vf, int syncflag, cred_t *cr, + caller_context_t *ct); +extern void vnext_inactive(femarg_t *vf, cred_t *cr, caller_context_t *ct); +extern int vnext_fid(femarg_t *vf, fid_t *fidp, caller_context_t *ct); extern int vnext_rwlock(femarg_t *vf, int write_lock, caller_context_t *ct); extern void vnext_rwunlock(femarg_t *vf, int write_lock, caller_context_t *ct); -extern int vnext_seek(femarg_t *vf, offset_t ooff, offset_t *noffp); -extern int vnext_cmp(femarg_t *vf, vnode_t *vp2); +extern int vnext_seek(femarg_t *vf, offset_t ooff, offset_t *noffp, + caller_context_t *ct); +extern int vnext_cmp(femarg_t *vf, vnode_t *vp2, caller_context_t *ct); extern int vnext_frlock(femarg_t *vf, int cmd, struct flock64 *bfp, int flag, offset_t offset, - struct flk_callback *flk_cbp, cred_t *cr); + struct flk_callback *flk_cbp, cred_t *cr, + caller_context_t *ct); extern int vnext_space(femarg_t *vf, int cmd, struct flock64 *bfp, int flag, offset_t offset, cred_t *cr, caller_context_t *ct); -extern int vnext_realvp(femarg_t *vf, vnode_t **vpp); +extern int vnext_realvp(femarg_t *vf, vnode_t **vpp, caller_context_t *ct); extern int vnext_getpage(femarg_t *vf, offset_t off, size_t len, uint_t *protp, struct page **plarr, size_t plsz, struct seg *seg, caddr_t addr, enum seg_rw rw, - cred_t *cr); + cred_t *cr, caller_context_t *ct); extern int vnext_putpage(femarg_t *vf, offset_t off, size_t len, int flags, - cred_t *cr); + cred_t *cr, caller_context_t *ct); extern int vnext_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxprot, - uint_t flags, cred_t *cr); + uint_t flags, cred_t *cr, caller_context_t *ct); extern int vnext_addmap(femarg_t *vf, offset_t off, struct as *as, caddr_t addr, size_t len, uchar_t prot, - uchar_t maxprot, uint_t flags, cred_t *cr); + uchar_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct); extern int vnext_delmap(femarg_t *vf, offset_t off, struct as *as, caddr_t addr, size_t len, uint_t prot, - uint_t maxprot, uint_t flags, cred_t *cr); + uint_t maxprot, uint_t flags, cred_t *cr, + caller_context_t *ct); extern int vnext_poll(femarg_t *vf, short events, int anyyet, - short *reventsp, struct pollhead **phpp); -extern int vnext_dump(femarg_t *vf, caddr_t addr, int lbdn, int dblks); -extern int vnext_pathconf(femarg_t *vf, int cmd, ulong_t *valp, cred_t *cr); + short *reventsp, struct pollhead **phpp, + caller_context_t *ct); +extern int vnext_dump(femarg_t *vf, caddr_t addr, int lbdn, int dblks, + caller_context_t *ct); +extern int vnext_pathconf(femarg_t *vf, int cmd, ulong_t *valp, cred_t *cr, + caller_context_t *ct); extern int vnext_pageio(femarg_t *vf, struct page *pp, u_offset_t io_off, - size_t io_len, int flags, cred_t *cr); -extern int vnext_dumpctl(femarg_t *vf, int action, int *blkp); + size_t io_len, int flags, cred_t *cr, + caller_context_t *ct); +extern int vnext_dumpctl(femarg_t *vf, int action, int *blkp, + caller_context_t *ct); extern void vnext_dispose(femarg_t *vf, struct page *pp, int flag, int dn, - cred_t *cr); + cred_t *cr, caller_context_t *ct); extern int vnext_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, - cred_t *cr); + cred_t *cr, caller_context_t *ct); extern int vnext_getsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, - cred_t *cr); + cred_t *cr, caller_context_t *ct); extern int vnext_shrlock(femarg_t *vf, int cmd, struct shrlock *shr, - int flag, cred_t *cr); + int flag, cred_t *cr, caller_context_t *ct); extern int vnext_vnevent(femarg_t *vf, vnevent_t vevent, vnode_t *dvp, - char *cname); + char *cname, caller_context_t *ct); extern int vfsnext_mount(fsemarg_t *vf, vnode_t *mvp, struct mounta *uap, cred_t *cr); diff --git a/usr/src/uts/common/sys/file.h b/usr/src/uts/common/sys/file.h index e8936965641e..01af00ddb212 100644 --- a/usr/src/uts/common/sys/file.h +++ b/usr/src/uts/common/sys/file.h @@ -89,10 +89,6 @@ typedef struct fpollinfo { #define FREVOKED 0x20 /* C2 Security - Revoke Subsystem */ #endif #define FDSYNC 0x40 /* file data only integrity while writing */ -#define FRSYNC 0x8000 /* sync read operations at same level of */ - /* integrity as specified for writes by */ - /* FSYNC and FDSYNC flags */ -#define FOFFMAX 0x2000 /* large file */ #define FNONBLOCK 0x80 #define FMASK 0xa0ff /* all flags that can be changed by F_SETFL */ @@ -102,14 +98,20 @@ typedef struct fpollinfo { #define FCREAT 0x0100 #define FTRUNC 0x0200 #define FEXCL 0x0400 -#define FNOCTTY 0x0800 +#define FASYNC 0x1000 /* asyncio in progress pseudo flag */ +#define FOFFMAX 0x2000 /* large file */ #define FXATTR 0x4000 /* open as extended attribute */ +#define FNOCTTY 0x0800 +#define FRSYNC 0x8000 /* sync read operations at same level of */ + /* integrity as specified for writes by */ + /* FSYNC and FDSYNC flags */ -#define FASYNC 0x1000 /* asyncio in progress pseudo flag */ #define FNODSYNC 0x10000 /* fsync pseudo flag */ #define FNOFOLLOW 0x20000 /* don't follow symlinks */ #define FNOLINKS 0x40000 /* don't allow multiple hard links */ +#define FIGNORECASE 0x80000 /* request case-insensitive lookups */ +#define FXATTRDIROPEN 0x100000 /* only opening hidden attribute directory */ #ifdef _KERNEL diff --git a/usr/src/uts/common/sys/fs/fifonode.h b/usr/src/uts/common/sys/fs/fifonode.h index ddd64ab0a33f..5ef83b477882 100644 --- a/usr/src/uts/common/sys/fs/fifonode.h +++ b/usr/src/uts/common/sys/fs/fifonode.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -123,8 +123,8 @@ typedef struct fifodata { #define FIFOPOLLR 0x1000 /* process waiting on poll read */ #define FIFOISOPEN 0x2000 /* pipe is open */ #define FIFOSYNC 0x4000 /* FIFO is waiting for open sync */ -#define FIFOWOCR 0x8000 /* Write open occured */ -#define FIFOROCR 0x10000 /* Read open occured */ +#define FIFOWOCR 0x8000 /* Write open occurred */ +#define FIFOROCR 0x10000 /* Read open occurred */ /* * process waiting on poll read on band data * this can only occur if we go to streams @@ -165,8 +165,9 @@ struct queue; extern int fifoinit(int, char *); extern int fifo_stropen(vnode_t **, int, cred_t *, int, int); -extern int fifo_open(vnode_t **, int, cred_t *); -extern int fifo_close(vnode_t *, int, int, offset_t, cred_t *); +extern int fifo_open(vnode_t **, int, cred_t *, caller_context_t *); +extern int fifo_close(vnode_t *, int, int, offset_t, cred_t *, + caller_context_t *); extern void fifo_cleanup(vnode_t *, int); extern void fiforemove(fifonode_t *); extern ino_t fifogetid(void); diff --git a/usr/src/uts/common/sys/fs/pc_node.h b/usr/src/uts/common/sys/fs/pc_node.h index 72e96d579b43..1e8921c6c14e 100644 --- a/usr/src/uts/common/sys/fs/pc_node.h +++ b/usr/src/uts/common/sys/fs/pc_node.h @@ -129,8 +129,10 @@ extern void pc_mark_irrecov(struct pcfs *); extern int pc_dirlook(struct pcnode *, char *, struct pcnode **); extern int pc_direnter(struct pcnode *, char *, struct vattr *, struct pcnode **); -extern int pc_dirremove(struct pcnode *, char *, struct vnode *, enum vtype); -extern int pc_rename(struct pcnode *, struct pcnode *, char *, char *); +extern int pc_dirremove(struct pcnode *, char *, struct vnode *, enum vtype, + caller_context_t *); +extern int pc_rename(struct pcnode *, struct pcnode *, char *, char *, + caller_context_t *); extern int pc_blkatoff(struct pcnode *, offset_t, struct buf **, struct pcdir **); extern int pc_truncate(struct pcnode *, uint_t); diff --git a/usr/src/uts/common/sys/fs/snode.h b/usr/src/uts/common/sys/fs/snode.h index cd572d545c45..d0176af2932e 100644 --- a/usr/src/uts/common/sys/fs/snode.h +++ b/usr/src/uts/common/sys/fs/snode.h @@ -162,7 +162,8 @@ void sdelete(struct snode *); void smark(struct snode *, int); int specinit(int, char *); int device_close(struct vnode *, int, struct cred *); -int spec_putpage(struct vnode *, offset_t, size_t, int, struct cred *); +int spec_putpage(struct vnode *, offset_t, size_t, int, struct cred *, + caller_context_t *); int spec_segmap(dev_t, off_t, struct as *, caddr_t *, off_t, uint_t, uint_t, uint_t, cred_t *); struct vnode *specvp_devfs(struct vnode *, dev_t, vtype_t, diff --git a/usr/src/uts/common/sys/fs/tmp.h b/usr/src/uts/common/sys/fs/tmp.h index 583eaab18cec..68dd67c61e9b 100644 --- a/usr/src/uts/common/sys/fs/tmp.h +++ b/usr/src/uts/common/sys/fs/tmp.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 1989-1991, 1996-1999, 2001-2003 Sun Microsystems, Inc. + * Copyright 2007 Sun Microsystems, Inc. * All rights reserved. Use is subject to license terms. */ @@ -111,7 +110,7 @@ extern int tmp_sticky_remove_access(struct tmpnode *, struct tmpnode *, extern int tmp_convnum(char *, pgcnt_t *); extern int tdirenter(struct tmount *, struct tmpnode *, char *, enum de_op, struct tmpnode *, struct tmpnode *, struct vattr *, - struct tmpnode **, struct cred *); + struct tmpnode **, struct cred *, caller_context_t *); #define TMP_MUSTHAVE 0x01 diff --git a/usr/src/uts/common/sys/fs/udf_inode.h b/usr/src/uts/common/sys/fs/udf_inode.h index 6a2b3e83e1a8..dd68124bcaea 100644 --- a/usr/src/uts/common/sys/fs/udf_inode.h +++ b/usr/src/uts/common/sys/fs/udf_inode.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -709,10 +708,10 @@ int32_t ud_dirlook(struct ud_inode *, char *, struct ud_inode **, struct cred *, int32_t); int32_t ud_direnter(struct ud_inode *, char *, enum de_op, struct ud_inode *, struct ud_inode *, struct vattr *, - struct ud_inode **, struct cred *); + struct ud_inode **, struct cred *, caller_context_t *); int32_t ud_dirremove(struct ud_inode *, char *, struct ud_inode *, struct vnode *, - enum dr_op, struct cred *); + enum dr_op, struct cred *, caller_context_t *); /* diff --git a/usr/src/uts/common/sys/fs/zfs.h b/usr/src/uts/common/sys/fs/zfs.h index 8babbef0d1fe..1250b18c4e94 100644 --- a/usr/src/uts/common/sys/fs/zfs.h +++ b/usr/src/uts/common/sys/fs/zfs.h @@ -92,6 +92,12 @@ typedef enum { ZFS_PROP_NUMCLONES, /* not exposed to the user */ ZFS_PROP_COPIES, ZFS_PROP_VERSION, + ZFS_PROP_UTF8ONLY, + ZFS_PROP_NORMALIZE, + ZFS_PROP_CASE, + ZFS_PROP_VSCAN, + ZFS_PROP_NBMAND, + ZFS_PROP_SHARESMB, ZFS_NUM_PROPS } zfs_prop_t; @@ -144,11 +150,13 @@ const char *zfs_prop_default_string(zfs_prop_t); uint64_t zfs_prop_default_numeric(zfs_prop_t); boolean_t zfs_prop_readonly(zfs_prop_t); boolean_t zfs_prop_inheritable(zfs_prop_t); +boolean_t zfs_prop_setonce(zfs_prop_t); const char *zfs_prop_to_name(zfs_prop_t); zfs_prop_t zfs_name_to_prop(const char *); boolean_t zfs_prop_user(const char *); int zfs_prop_index_to_string(zfs_prop_t, uint64_t, const char **); int zfs_prop_string_to_index(zfs_prop_t, const char *, uint64_t *); +int zfs_prop_valid_for_type(int, zfs_type_t); /* * Pool property functions shared between libzfs and kernel. @@ -190,6 +198,13 @@ typedef enum { #define ZFS_DELEG_PERM_GID "gid" #define ZFS_DELEG_PERM_GROUPS "groups" +typedef enum zfs_share_op { + ZFS_SHARE_NFS = 0, + ZFS_UNSHARE_NFS = 1, + ZFS_SHARE_SMB = 2, + ZFS_UNSHARE_SMB = 3 +} zfs_share_op_t; + /* * On-disk version number. */ @@ -201,13 +216,15 @@ typedef enum { #define SPA_VERSION_6 6ULL #define SPA_VERSION_7 7ULL #define SPA_VERSION_8 8ULL +#define SPA_VERSION_9 9ULL + /* * When bumping up SPA_VERSION, make sure GRUB ZFS understand the on-disk * format change. Go to usr/src/grub/grub-0.95/stage2/{zfs-include/, fsys_zfs*}, * and do the appropriate changes. */ -#define SPA_VERSION SPA_VERSION_8 -#define SPA_VERSION_STRING "8" +#define SPA_VERSION SPA_VERSION_9 +#define SPA_VERSION_STRING "9" /* * Symbolic names for the changes that caused a SPA_VERSION switch. @@ -232,6 +249,8 @@ typedef enum { #define SPA_VERSION_BOOTFS SPA_VERSION_6 #define SPA_VERSION_SLOGS SPA_VERSION_7 #define SPA_VERSION_DELEGATED_PERMS SPA_VERSION_8 +#define SPA_VERSION_FUID SPA_VERSION_9 +#define SPA_VERSION_NORMALIZATION SPA_VERSION_9 /* * ZPL version - rev'd whenever an incompatible on-disk format change @@ -243,11 +262,14 @@ typedef enum { */ #define ZPL_VERSION_1 1ULL #define ZPL_VERSION_2 2ULL -#define ZPL_VERSION ZPL_VERSION_2 -#define ZPL_VERSION_STRING "2" +#define ZPL_VERSION_3 3ULL +#define ZPL_VERSION ZPL_VERSION_3 +#define ZPL_VERSION_STRING "3" #define ZPL_VERSION_INITIAL ZPL_VERSION_1 #define ZPL_VERSION_DIRENT_TYPE ZPL_VERSION_2 +#define ZPL_VERSION_FUID ZPL_VERSION_3 +#define ZPL_VERSION_SYSATTR ZPL_VERSION_3 /* * The following are configuration names used in the nvlist describing a pool's diff --git a/usr/src/uts/common/sys/gfs.h b/usr/src/uts/common/sys/gfs.h index ce21cbe52526..b53031086bab 100644 --- a/usr/src/uts/common/sys/gfs.h +++ b/usr/src/uts/common/sys/gfs.h @@ -76,7 +76,8 @@ typedef struct gfs_file { typedef int (*gfs_readdir_cb)(vnode_t *, struct dirent64 *, int *, offset_t *, offset_t *, void *); -typedef int (*gfs_lookup_cb)(vnode_t *, const char *, vnode_t **, ino64_t *); +typedef int (*gfs_lookup_cb)(vnode_t *, const char *, vnode_t **, ino64_t *, + cred_t *); typedef ino64_t (*gfs_inode_cb)(vnode_t *, int); typedef struct gfs_dir { @@ -103,8 +104,9 @@ extern vnode_t *gfs_root_create_file(size_t, struct vfs *, vnodeops_t *, extern void *gfs_file_inactive(vnode_t *); extern void *gfs_dir_inactive(vnode_t *); -extern int gfs_dir_lookup(vnode_t *, const char *, vnode_t **); -extern int gfs_dir_readdir(vnode_t *, uio_t *, int *, void *); +extern int gfs_dir_lookup(vnode_t *, const char *, vnode_t **, cred_t *); +extern int gfs_dir_readdir(vnode_t *, uio_t *, int *, void *, cred_t *, + caller_context_t *); #define gfs_dir_lock(gd) mutex_enter(&(gd)->gfsd_lock) #define gfs_dir_unlock(gd) mutex_exit(&(gd)->gfsd_lock) @@ -137,14 +139,22 @@ extern int gfs_readdir_emitn(gfs_readdir_state_t *, uio_t *, offset_t, ino64_t, extern int gfs_readdir_pred(gfs_readdir_state_t *, uio_t *, offset_t *); extern int gfs_readdir_fini(gfs_readdir_state_t *, int, int *, int); +/* + * Objects with real extended attributes will get their . and .. + * readdir entries from the real xattr directory. GFS_STATIC_ENTRY_OFFSET + * lets us skip right to the static entries in the GFS directory. + */ +#define GFS_STATIC_ENTRY_OFFSET ((offset_t)2) + extern int gfs_lookup_dot(vnode_t **, vnode_t *, vnode_t *, const char *); extern int gfs_vop_lookup(vnode_t *, char *, vnode_t **, pathname_t *, - int, vnode_t *, cred_t *); -extern int gfs_vop_readdir(vnode_t *, uio_t *, cred_t *, int *); + int, vnode_t *, cred_t *, caller_context_t *, int *, pathname_t *); +extern int gfs_vop_readdir(vnode_t *, uio_t *, cred_t *, int *, + caller_context_t *, int); extern int gfs_vop_map(vnode_t *, offset_t, struct as *, caddr_t *, - size_t, uchar_t, uchar_t, uint_t, cred_t *); -extern void gfs_vop_inactive(vnode_t *, cred_t *); + size_t, uchar_t, uchar_t, uint_t, cred_t *, caller_context_t *); +extern void gfs_vop_inactive(vnode_t *, cred_t *, caller_context_t *); #ifdef __cplusplus diff --git a/usr/src/uts/common/sys/modctl.h b/usr/src/uts/common/sys/modctl.h index 3477a11cb6ea..818572a94dc6 100644 --- a/usr/src/uts/common/sys/modctl.h +++ b/usr/src/uts/common/sys/modctl.h @@ -109,7 +109,7 @@ struct modlsys { struct modlfs { struct mod_ops *fs_modops; char *fs_linkinfo; - struct vfsdef_v3 *fs_vfsdef; /* version may actually vary */ + struct vfsdef_v4 *fs_vfsdef; /* version may actually vary */ }; #if defined(__i386) || defined(__amd64) diff --git a/usr/src/uts/common/sys/nbmlock.h b/usr/src/uts/common/sys/nbmlock.h index 98211d7b70b2..e5215cc9eea8 100644 --- a/usr/src/uts/common/sys/nbmlock.h +++ b/usr/src/uts/common/sys/nbmlock.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,8 +19,8 @@ * CDDL HEADER END */ /* - * Copyright (c) 2001 by Sun Microsystems, Inc. - * All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. */ #ifndef _NBMLOCK_H @@ -57,9 +56,11 @@ extern int nbl_in_crit(vnode_t *); /* conflict checking */ extern int nbl_need_check(vnode_t *); -extern int nbl_conflict(vnode_t *, nbl_op_t, u_offset_t, ssize_t, int); -extern int nbl_share_conflict(vnode_t *, nbl_op_t); -extern int nbl_lock_conflict(vnode_t *, nbl_op_t, u_offset_t, ssize_t, int); +extern int nbl_conflict(vnode_t *, nbl_op_t, u_offset_t, ssize_t, int, + caller_context_t *); +extern int nbl_share_conflict(vnode_t *, nbl_op_t, caller_context_t *); +extern int nbl_lock_conflict(vnode_t *, nbl_op_t, u_offset_t, ssize_t, int, + caller_context_t *); extern int nbl_svmand(vnode_t *, cred_t *, int *); extern nbl_op_t nbl_lock_to_op(int); diff --git a/usr/src/uts/common/sys/objfs_impl.h b/usr/src/uts/common/sys/objfs_impl.h index df60a8af95cd..687db9edec01 100644 --- a/usr/src/uts/common/sys/objfs_impl.h +++ b/usr/src/uts/common/sys/objfs_impl.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -49,9 +48,11 @@ typedef struct objfs_vfs { /* * Common vop_ entry points */ -extern int objfs_dir_open(vnode_t **, int, cred_t *); -extern int objfs_dir_access(vnode_t *, int, int, cred_t *); -extern int objfs_common_close(vnode_t *, int, int, offset_t, cred_t *); +extern int objfs_dir_open(vnode_t **, int, cred_t *, caller_context_t *); +extern int objfs_dir_access(vnode_t *, int, int, cred_t *, + caller_context_t *); +extern int objfs_common_close(vnode_t *, int, int, offset_t, cred_t *, + caller_context_t *); /* * Common vop_ support functions diff --git a/usr/src/uts/common/sys/policy.h b/usr/src/uts/common/sys/policy.h index c132402d3b75..2949605b9e51 100644 --- a/usr/src/uts/common/sys/policy.h +++ b/usr/src/uts/common/sys/policy.h @@ -129,6 +129,7 @@ int secpolicy_rpcmod_open(const cred_t *); int secpolicy_rsm_access(const cred_t *, uid_t, mode_t); int secpolicy_setpriority(const cred_t *); int secpolicy_settime(const cred_t *); +int secpolicy_smb(const cred_t *); int secpolicy_spec_open(const cred_t *, struct vnode *, int); int secpolicy_sti(const cred_t *); int secpolicy_swapctl(const cred_t *); @@ -154,6 +155,7 @@ void secpolicy_setid_clear(vattr_t *, cred_t *); void secpolicy_fs_mount_clearopts(cred_t *, struct vfs *); int secpolicy_setid_setsticky_clear(vnode_t *, vattr_t *, const vattr_t *, cred_t *); +int secpolicy_xvattr(xvattr_t *, uid_t, cred_t *, vtype_t); int secpolicy_basic_exec(const cred_t *); int secpolicy_basic_fork(const cred_t *); diff --git a/usr/src/uts/common/sys/sid.h b/usr/src/uts/common/sys/sid.h index 9d68191eb684..96b9f5a0b87f 100644 --- a/usr/src/uts/common/sys/sid.h +++ b/usr/src/uts/common/sys/sid.h @@ -52,6 +52,8 @@ extern "C" { #define SIDSYS_ID2SID 1 #ifdef _KERNEL +#define KSIDLIST_MEM(n) (sizeof (ksidlist_t) + ((n) - 1) * sizeof (ksid_t)) + /* Domains are stored in AVL trees so we can share them among SIDs */ typedef struct ksiddomain { uint_t kd_ref; diff --git a/usr/src/uts/common/sys/socketvar.h b/usr/src/uts/common/sys/socketvar.h index 1863d2ea01f3..0680546adede 100644 --- a/usr/src/uts/common/sys/socketvar.h +++ b/usr/src/uts/common/sys/socketvar.h @@ -262,7 +262,7 @@ struct sonode { * but one of the key reasons for their existence and careful * tracking in sockfs is to support getsockname and getpeername * when the transport does not handle the TI_GET*NAME ioctls - * and caching when it does (signalled by valid bits in so_state). + * and caching when it does (signaled by valid bits in so_state). * When all transports support the new TPI (with T_ADDR_REQ) * we can revisit this code. * The other usage of so_faddr is to keep the "connected to" @@ -704,7 +704,8 @@ extern int sock_putmsg(vnode_t *, struct strbuf *, struct strbuf *, uchar_t, int, int); struct sonode *sotpi_create(vnode_t *, int, int, int, int, struct sonode *, int *); -extern int socktpi_open(struct vnode **, int, struct cred *); +extern int socktpi_open(struct vnode **, int, struct cred *, + caller_context_t *); extern int so_sock2stream(struct sonode *); extern void so_stream2sock(struct sonode *); extern int sockinit(int, char *); @@ -783,7 +784,7 @@ extern int sotpi_getsockopt(struct sonode *, int, int, void *, extern int sotpi_setsockopt(struct sonode *, int, int, const void *, socklen_t); extern int socktpi_ioctl(struct vnode *, int, intptr_t, int, - struct cred *, int *); + struct cred *, int *, caller_context_t *); extern int sodisconnect(struct sonode *, t_scalar_t, int); extern ssize_t soreadfile(file_t *, uchar_t *, u_offset_t, int *, size_t); extern int so_set_asyncsigs(vnode_t *, pid_t, int, int, cred_t *); @@ -795,7 +796,7 @@ extern void sock_kstat_fini(zoneid_t, void *); extern struct sonode *getsonode(int, int *, file_t **); /* - * Function wrappers (mostly arround the sonode switch) for + * Function wrappers (mostly around the sonode switch) for * backward compatibility. */ extern int soaccept(struct sonode *, int, struct sonode **); @@ -820,15 +821,19 @@ extern struct sonode *socreate(vnode_t *, int, int, int, int, extern int so_copyin(const void *, void *, size_t, int); extern int so_copyout(const void *, void *, size_t, int); -extern int socktpi_access(struct vnode *, int, int, struct cred *); -extern int socktpi_fid(struct vnode *, struct fid *); -extern int socktpi_fsync(struct vnode *, int, struct cred *); +extern int socktpi_access(struct vnode *, int, int, struct cred *, + caller_context_t *); +extern int socktpi_fid(struct vnode *, struct fid *, caller_context_t *); +extern int socktpi_fsync(struct vnode *, int, struct cred *, + caller_context_t *); extern int socktpi_getattr(struct vnode *, struct vattr *, int, - struct cred *); -extern int socktpi_seek(struct vnode *, offset_t, offset_t *); + struct cred *, caller_context_t *); +extern int socktpi_seek(struct vnode *, offset_t, offset_t *, + caller_context_t *); extern int socktpi_setattr(struct vnode *, struct vattr *, int, struct cred *, caller_context_t *); -extern int socktpi_setfl(vnode_t *, int, int, cred_t *); +extern int socktpi_setfl(vnode_t *, int, int, cred_t *, + caller_context_t *); /* SCTP sockfs */ extern struct sonode *sosctp_create(vnode_t *, int, int, int, int, diff --git a/usr/src/uts/common/sys/tzfile.h b/usr/src/uts/common/sys/tzfile.h new file mode 100644 index 000000000000..8f451f27d918 --- /dev/null +++ b/usr/src/uts/common/sys/tzfile.h @@ -0,0 +1,166 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * from Arthur Olson's 6.1 + */ + +#ifndef _SYS_TZFILE_H +#define _SYS_TZFILE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Information about time zone files. + */ + +#define TZDIR "/usr/share/lib/zoneinfo" /* Time zone object file directory */ + +#define TZDEFAULT (getenv("TZ")) + +#define TZDEFRULES "posixrules" + +/* + * Each file begins with. . . + */ + +struct tzhead { + char tzh_reserved[24]; /* reserved for future use */ + char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ + char tzh_leapcnt[4]; /* coded number of leap seconds */ + char tzh_timecnt[4]; /* coded number of transition times */ + char tzh_typecnt[4]; /* coded number of local time types */ + char tzh_charcnt[4]; /* coded number of abbr. chars */ +}; + +/* + * . . .followed by. . . + * + * tzh_timecnt (char [4])s coded transition times a la time(2) + * tzh_timecnt (unsigned char)s types of local time starting at above + * tzh_typecnt repetitions of + * one (char [4]) coded GMT offset in seconds + * one (unsigned char) used to set tm_isdst + * one (unsigned char) that's an abbreviation list index + * tzh_charcnt (char)s '\0'-terminated zone abbreviations + * tzh_leapcnt repetitions of + * one (char [4]) coded leap second transition times + * one (char [4]) total correction after above + * tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition + * time is standard time, if FALSE, + * transition time is wall clock time + * if absent, transition times are + * assumed to be wall clock time + */ + +/* + * In the current implementation, "tzset()" refuses to deal with files that + * exceed any of the limits below. + */ + +/* + * The TZ_MAX_TIMES value below is enough to handle a bit more than a + * year's worth of solar time (corrected daily to the nearest second) or + * 138 years of Pacific Presidential Election time + * (where there are three time zone transitions every fourth year). + */ +#define TZ_MAX_TIMES 370 + +#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ + +#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ + +#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ + +#define SECSPERMIN 60 +#define MINSPERHOUR 60 +#define HOURSPERDAY 24 +#define DAYSPERWEEK 7 +#define DAYSPERNYEAR 365 +#define DAYSPERLYEAR 366 +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY ((long)SECSPERHOUR * HOURSPERDAY) +#define MONSPERYEAR 12 + +#define TM_SUNDAY 0 +#define TM_MONDAY 1 +#define TM_TUESDAY 2 +#define TM_WEDNESDAY 3 +#define TM_THURSDAY 4 +#define TM_FRIDAY 5 +#define TM_SATURDAY 6 + +#define TM_JANUARY 0 +#define TM_FEBRUARY 1 +#define TM_MARCH 2 +#define TM_APRIL 3 +#define TM_MAY 4 +#define TM_JUNE 5 +#define TM_JULY 6 +#define TM_AUGUST 7 +#define TM_SEPTEMBER 8 +#define TM_OCTOBER 9 +#define TM_NOVEMBER 10 +#define TM_DECEMBER 11 + +#define TM_YEAR_BASE 1900 + +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY TM_THURSDAY + +/* + * Accurate only for the past couple of centuries; + * that will probably do. + */ + +#define isleap(y) (((y) % 4) == 0 && ((y) % 100) != 0 || ((y) % 400) == 0) + +/* + * Use of the underscored variants may cause problems if you move your code to + * certain System-V-based systems; for maximum portability, use the + * underscore-free variants. The underscored variants are provided for + * backward compatibility only; they may disappear from future versions of + * this file. + */ + +#define SECS_PER_MIN SECSPERMIN +#define MINS_PER_HOUR MINSPERHOUR +#define HOURS_PER_DAY HOURSPERDAY +#define DAYS_PER_WEEK DAYSPERWEEK +#define DAYS_PER_NYEAR DAYSPERNYEAR +#define DAYS_PER_LYEAR DAYSPERLYEAR +#define SECS_PER_HOUR SECSPERHOUR +#define SECS_PER_DAY SECSPERDAY +#define MONS_PER_YEAR MONSPERYEAR + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_TZFILE_H */ diff --git a/usr/src/uts/common/sys/unistd.h b/usr/src/uts/common/sys/unistd.h index 7cfbb9744f04..29e0476abd08 100644 --- a/usr/src/uts/common/sys/unistd.h +++ b/usr/src/uts/common/sys/unistd.h @@ -310,6 +310,10 @@ extern "C" { #define _PC_2_SYMLINKS 19 #define _PC_ACL_ENABLED 20 #define _PC_MIN_HOLE_SIZE 21 +#define _PC_CASE_BEHAVIOR 22 +#define _PC_SATTR_ENABLED 23 +#define _PC_SATTR_EXISTS 24 + /* * Large File Summit names * @@ -325,6 +329,12 @@ extern "C" { #define _PC_XATTR_ENABLED 100 #define _PC_XATTR_EXISTS 101 +/* + * Case sensitivity values (related to _PC_CASE_BEHAVIOR) + */ +#define _CASE_SENSITIVE 0x1 +#define _CASE_INSENSITIVE 0x2 + /* * The value of 0 is returned when * ACL's are not supported diff --git a/usr/src/uts/common/sys/vfs.h b/usr/src/uts/common/sys/vfs.h index 114ce9781113..2b627cd344d6 100644 --- a/usr/src/uts/common/sys/vfs.h +++ b/usr/src/uts/common/sys/vfs.h @@ -174,6 +174,24 @@ typedef struct vskstat_anchor { extern avl_tree_t vskstat_tree; extern kmutex_t vskstat_tree_lock; +/* + * Private vfs data, NOT to be used by a file system implementation. + */ + +#define VFS_FEATURE_MAXSZ 4 + +typedef struct vfs_impl { + /* Counted array - Bitmap of vfs features */ + uint32_t vi_featureset[VFS_FEATURE_MAXSZ]; + /* + * Support for statistics on the vnode operations + */ + vsk_anchor_t *vi_vskap; /* anchor for vopstats' kstat */ + vopstats_t *vi_fstypevsp; /* ptr to per-fstype vopstats */ + vopstats_t vi_vopstats; /* per-mount vnode op stats */ +} vfs_impl_t; + + /* * Structure per mounted file system. Each mounted file system has * an array of operations and an instance record. @@ -200,19 +218,6 @@ extern kmutex_t vskstat_tree_lock; struct zone; /* from zone.h */ struct fem_head; /* from fem.h */ -/* - * Private vfs data, NOT to be used by a file system implementation. - */ -typedef struct vfs_impl { - struct fem_head *vi_femhead; /* fs monitoring */ - /* - * Support for statistics on the vnode operations - */ - vsk_anchor_t *vi_vskap; /* anchor for vopstats' kstat */ - vopstats_t *vi_fstypevsp; /* ptr to per-fstype vopstats */ - vopstats_t vi_vopstats; /* per-mount vnode op stats */ -} vfs_impl_t; - typedef struct vfs { struct vfs *vfs_next; /* next VFS in VFS list */ struct vfs *vfs_prev; /* prev VFS in VFS list */ @@ -246,9 +251,10 @@ typedef struct vfs { struct zone *vfs_zone; /* zone that owns the mount */ struct vfs *vfs_zone_next; /* next VFS visible in zone */ struct vfs *vfs_zone_prev; /* prev VFS visible in zone */ + struct fem_head *vfs_femhead; /* fs monitoring */ } vfs_t; -#define vfs_femhead vfs_implp->vi_femhead +#define vfs_featureset vfs_implp->vi_featureset #define vfs_vskap vfs_implp->vi_vskap #define vfs_fstypevsp vfs_implp->vi_fstypevsp #define vfs_vopstats vfs_implp->vi_vopstats @@ -274,6 +280,22 @@ typedef struct vfs { #define VFS_NORESOURCE "unspecified_resource" #define VFS_NOMNTPT "unspecified_mountpoint" +/* + * VFS features are implemented as bits set in the vfs_t. + * The vfs_feature_t typedef is a 64-bit number that will translate + * into an element in an array of bitmaps and a bit in that element. + * Developers must not depend on the implementation of this and + * need to use vfs_has_feature()/vfs_set_feature() routines. + */ +typedef uint64_t vfs_feature_t; + +#define VFSFT_XVATTR 0x100000001 /* Supports xvattr for attrs */ +#define VFSFT_CASEINSENSITIVE 0x100000002 /* Supports case-insensitive */ +#define VFSFT_NOCASESENSITIVE 0x100000004 /* NOT case-sensitive */ +#define VFSFT_DIRENTFLAGS 0x100000008 /* Supports dirent flags */ +#define VFSFT_ACLONCREATE 0x100000010 /* Supports ACL on create */ +#define VFSFT_ACEMASKONACCESS 0x100000020 /* Can use ACEMASK for access */ + /* * Argument structure for mount(2). * @@ -380,21 +402,22 @@ typedef struct vfssw { /* * Filesystem type definition record. All file systems must export a record - * of this type through their modlfs structure. + * of this type through their modlfs structure. N.B., changing the version + * number requires a change in sys/modctl.h. */ -typedef struct vfsdef_v3 { +typedef struct vfsdef_v4 { int def_version; /* structure version, must be first */ char *name; /* filesystem type name */ int (*init) (int, char *); /* init routine */ int flags; /* filesystem flags */ mntopts_t *optproto; /* mount options table prototype */ -} vfsdef_v3; +} vfsdef_v4; -typedef struct vfsdef_v3 vfsdef_t; +typedef struct vfsdef_v4 vfsdef_t; enum { - VFSDEF_VERSION = 3 + VFSDEF_VERSION = 4 }; /* @@ -424,6 +447,8 @@ void vfs_setops(vfs_t *, vfsops_t *); vfsops_t *vfs_getops(vfs_t *vfsp); int vfs_matchops(vfs_t *, vfsops_t *); int vfs_can_sync(vfs_t *vfsp); +vfs_t *vfs_alloc(int); +void vfs_free(vfs_t *); void vfs_init(vfs_t *vfsp, vfsops_t *, void *); void vfsimpl_setup(vfs_t *vfsp); void vfsimpl_teardown(vfs_t *vfsp); @@ -450,6 +475,10 @@ void vfs_mountroot(void); void vfs_add(vnode_t *, struct vfs *, int); void vfs_remove(struct vfs *); +/* VFS feature routines */ +void vfs_set_feature(vfs_t *, vfs_feature_t); +int vfs_has_feature(vfs_t *, vfs_feature_t); + /* The following functions are not for general use by filesystems */ void vfs_createopttbl(mntopts_t *, const char *); @@ -545,7 +574,6 @@ extern const mntopts_t vfs_mntopts; /* globally recognized options */ #define VFS_INIT(vfsp, op, data) { \ vfs_init((vfsp), (op), (data)); \ - vfsimpl_setup((vfsp)); \ } diff --git a/usr/src/uts/common/sys/vnode.h b/usr/src/uts/common/sys/vnode.h index 56f4ac42d8bc..3195fb4ebd6b 100644 --- a/usr/src/uts/common/sys/vnode.h +++ b/usr/src/uts/common/sys/vnode.h @@ -194,6 +194,7 @@ struct vsd_node { * v_shrlocks * v_path * v_vsd + * v_xattrdir * * A special lock (implemented by vn_vfswlock in vnode.c) protects: * v_vfsmountedhere @@ -262,6 +263,7 @@ typedef struct vnode { krwlock_t v_mslock; /* protects v_mset */ void *v_fopdata; /* list of file ops event watches */ struct vsd_node *v_vsd; /* vnode specific data */ + struct vnode *v_xattrdir; /* unnamed extended attr dir (GFS) */ } vnode_t; #define IS_DEVVP(vp) \ @@ -334,6 +336,8 @@ typedef struct vn_vfslocks_entry { #define VISSWAPFS 0x20000 /* vnode is being used for swapfs */ #define IS_SWAPFSVP(vp) (((vp)->v_flag & VISSWAPFS) != 0) +#define V_SYSATTR 0x40000 /* vnode is a GFS system attribute */ + /* * Vnode attributes. A bit-mask is supplied as part of the * structure to indicate the attributes the caller wants to @@ -366,6 +370,101 @@ typedef struct vattr { uint_t va_seq; /* sequence number */ } vattr_t; +#define AV_SCANSTAMP_SZ 32 /* length of anti-virus scanstamp */ + +/* + * Structure of all optional attributes. + */ +typedef struct xoptattr { + timestruc_t xoa_createtime; /* Create time of file */ + uint8_t xoa_archive; + uint8_t xoa_system; + uint8_t xoa_readonly; + uint8_t xoa_hidden; + uint8_t xoa_nounlink; + uint8_t xoa_immutable; + uint8_t xoa_appendonly; + uint8_t xoa_nodump; + uint8_t xoa_opaque; + uint8_t xoa_av_quarantined; + uint8_t xoa_av_modified; + uint8_t xoa_av_scanstamp[AV_SCANSTAMP_SZ]; +} xoptattr_t; + +/* + * The xvattr structure is really a variable length structure that + * is made up of: + * - The classic vattr_t (xva_vattr) + * - a 32 bit quantity (xva_mapsize) that specifies the size of the + * attribute bitmaps in 32 bit words. + * - A pointer to the returned attribute bitmap (needed because the + * previous element, the requested attribute bitmap) is variable lenth. + * - The requested attribute bitmap, which is an array of 32 bit words. + * Callers use the XVA_SET_REQ() macro to set the bits corresponding to + * the attributes that are being requested. + * - The returned attribute bitmap, which is an array of 32 bit words. + * File systems that support optional attributes use the XVA_SET_RTN() + * macro to set the bits corresponding to the attributes that are being + * returned. + * - The xoptattr_t structure which contains the attribute values + * + * xva_mapsize determines how many words in the attribute bitmaps. + * Immediately following the attribute bitmaps is the xoptattr_t. + * xva_getxoptattr() is used to get the pointer to the xoptattr_t + * section. + */ + +#define XVA_MAPSIZE 3 /* Size of attr bitmaps */ +#define XVA_MAGIC 0x78766174 /* Magic # for verification */ + +/* + * The xvattr structure is an extensible structure which permits optional + * attributes to be requested/returned. File systems may or may not support + * optional attributes. They do so at their own discretion but if they do + * support optional attributes, they must register the VFSFT_XVATTR feature + * so that the optional attributes can be set/retrived. + * + * The fields of the xvattr structure are: + * + * xva_vattr - The first element of an xvattr is a legacy vattr structure + * which includes the common attributes. If AT_XVATTR is set in the va_mask + * then the entire structure is treated as an xvattr. If AT_XVATTR is not + * set, then only the xva_vattr structure can be used. + * + * xva_magic - 0x78766174 (hex for "xvat"). Magic number for verification. + * + * xva_mapsize - Size of requested and returned attribute bitmaps. + * + * xva_rtnattrmapp - Pointer to xva_rtnattrmap[]. We need this since the + * size of the array before it, xva_reqattrmap[], could change which means + * the location of xva_rtnattrmap[] could change. This will allow unbundled + * file systems to find the location of xva_rtnattrmap[] when the sizes change. + * + * xva_reqattrmap[] - Array of requested attributes. Attributes are + * represented by a specific bit in a specific element of the attribute + * map array. Callers set the bits corresponding to the attributes + * that the caller wants to get/set. + * + * xva_rtnattrmap[] - Array of attributes that the file system was able to + * process. Not all file systems support all optional attributes. This map + * informs the caller which attributes the underlying file system was able + * to set/get. (Same structure as the requested attributes array in terms + * of each attribute corresponding to specific bits and array elements.) + * + * xva_xoptattrs - Structure containing values of optional attributes. + * These values are only valid if the corresponding bits in xva_reqattrmap + * are set and the underlying file system supports those attributes. + */ +typedef struct xvattr { + vattr_t xva_vattr; /* Embedded vattr structure */ + uint32_t xva_magic; /* Magic Number */ + uint32_t xva_mapsize; /* Size of attr bitmap (32-bit words) */ + uint32_t *xva_rtnattrmapp; /* Ptr to xva_rtnattrmap[] */ + uint32_t xva_reqattrmap[XVA_MAPSIZE]; /* Requested attrs */ + uint32_t xva_rtnattrmap[XVA_MAPSIZE]; /* Returned attrs */ + xoptattr_t xva_xoptattrs; /* Optional attributes */ +} xvattr_t; + #ifdef _SYSCALL32 /* * For bigtypes time_t changed to 64 bit on the 64-bit kernel. @@ -407,22 +506,29 @@ typedef vattr_t vattr32_t; /* * Attributes of interest to the caller of setattr or getattr. */ -#define AT_TYPE 0x0001 -#define AT_MODE 0x0002 -#define AT_UID 0x0004 -#define AT_GID 0x0008 -#define AT_FSID 0x0010 -#define AT_NODEID 0x0020 -#define AT_NLINK 0x0040 -#define AT_SIZE 0x0080 -#define AT_ATIME 0x0100 -#define AT_MTIME 0x0200 -#define AT_CTIME 0x0400 -#define AT_RDEV 0x0800 -#define AT_BLKSIZE 0x1000 -#define AT_NBLOCKS 0x2000 -/* 0x4000 */ /* unused */ -#define AT_SEQ 0x8000 +#define AT_TYPE 0x00001 +#define AT_MODE 0x00002 +#define AT_UID 0x00004 +#define AT_GID 0x00008 +#define AT_FSID 0x00010 +#define AT_NODEID 0x00020 +#define AT_NLINK 0x00040 +#define AT_SIZE 0x00080 +#define AT_ATIME 0x00100 +#define AT_MTIME 0x00200 +#define AT_CTIME 0x00400 +#define AT_RDEV 0x00800 +#define AT_BLKSIZE 0x01000 +#define AT_NBLOCKS 0x02000 +/* 0x04000 */ /* unused */ +#define AT_SEQ 0x08000 +/* + * If AT_XVATTR is set then there are additional bits to process in + * the xvattr_t's attribute bitmap. If this is not set then the bitmap + * MUST be ignored. Note that this bit must be set/cleared explicitly. + * That is, setting AT_ALL will NOT set AT_XVATTR. + */ +#define AT_XVATTR 0x10000 #define AT_ALL (AT_TYPE|AT_MODE|AT_UID|AT_GID|AT_FSID|AT_NODEID|\ AT_NLINK|AT_SIZE|AT_ATIME|AT_MTIME|AT_CTIME|\ @@ -436,6 +542,116 @@ typedef vattr_t vattr32_t; #define AT_NOSET (AT_NLINK|AT_RDEV|AT_FSID|AT_NODEID|AT_TYPE|\ AT_BLKSIZE|AT_NBLOCKS|AT_SEQ) +/* + * Attribute bits used in the extensible attribute's (xva's) attribute + * bitmaps. Note that the bitmaps are made up of a variable length number + * of 32-bit words. The convention is to use XAT{n}_{attrname} where "n" + * is the element in the bitmap (starting at 1). This convention is for + * the convenience of the maintainer to keep track of which element each + * attribute belongs to. + * + * NOTE THAT CONSUMERS MUST *NOT* USE THE XATn_* DEFINES DIRECTLY. CONSUMERS + * MUST USE THE XAT_* DEFINES. + */ +#define XAT0_INDEX 0LL /* Index into bitmap for XAT0 attrs */ +#define XAT0_CREATETIME 0x00000001 /* Create time of file */ +#define XAT0_ARCHIVE 0x00000002 /* Archive */ +#define XAT0_SYSTEM 0x00000004 /* System */ +#define XAT0_READONLY 0x00000008 /* Readonly */ +#define XAT0_HIDDEN 0x00000010 /* Hidden */ +#define XAT0_NOUNLINK 0x00000020 /* Nounlink */ +#define XAT0_IMMUTABLE 0x00000040 /* immutable */ +#define XAT0_APPENDONLY 0x00000080 /* appendonly */ +#define XAT0_NODUMP 0x00000100 /* nodump */ +#define XAT0_OPAQUE 0x00000200 /* opaque */ +#define XAT0_AV_QUARANTINED 0x00000400 /* anti-virus quarantine */ +#define XAT0_AV_MODIFIED 0x00000800 /* anti-virus modified */ +#define XAT0_AV_SCANSTAMP 0x00001000 /* anti-virus scanstamp */ + +#define XAT0_ALL_ATTRS (XAT0_CREATETIME|XAT0_ARCHIVE|XAT0_SYSTEM| \ + XAT0_READONLY|XAT0_HIDDEN|XAT0_NOUNLINK|XAT0_IMMUTABLE|XAT0_APPENDONLY| \ + XAT0_NODUMP|XAT0_OPAQUE|XAT0_AV_QUARANTINED| \ + XAT0_AV_MODIFIED|XAT0_AV_SCANSTAMP) + +/* Support for XAT_* optional attributes */ +#define XVA_MASK 0xffffffff /* Used to mask off 32 bits */ +#define XVA_SHFT 32 /* Used to shift index */ + +/* + * Used to pry out the index and attribute bits from the XAT_* attributes + * defined below. Note that we're masking things down to 32 bits then + * casting to uint32_t. + */ +#define XVA_INDEX(attr) ((uint32_t)(((attr) >> XVA_SHFT) & XVA_MASK)) +#define XVA_ATTRBIT(attr) ((uint32_t)((attr) & XVA_MASK)) + +/* + * The following defines present a "flat namespace" so that consumers don't + * need to keep track of which element belongs to which bitmap entry. + * + * NOTE THAT THESE MUST NEVER BE OR-ed TOGETHER + */ +#define XAT_CREATETIME ((XAT0_INDEX << XVA_SHFT) | XAT0_CREATETIME) +#define XAT_ARCHIVE ((XAT0_INDEX << XVA_SHFT) | XAT0_ARCHIVE) +#define XAT_SYSTEM ((XAT0_INDEX << XVA_SHFT) | XAT0_SYSTEM) +#define XAT_READONLY ((XAT0_INDEX << XVA_SHFT) | XAT0_READONLY) +#define XAT_HIDDEN ((XAT0_INDEX << XVA_SHFT) | XAT0_HIDDEN) +#define XAT_NOUNLINK ((XAT0_INDEX << XVA_SHFT) | XAT0_NOUNLINK) +#define XAT_IMMUTABLE ((XAT0_INDEX << XVA_SHFT) | XAT0_IMMUTABLE) +#define XAT_APPENDONLY ((XAT0_INDEX << XVA_SHFT) | XAT0_APPENDONLY) +#define XAT_NODUMP ((XAT0_INDEX << XVA_SHFT) | XAT0_NODUMP) +#define XAT_OPAQUE ((XAT0_INDEX << XVA_SHFT) | XAT0_OPAQUE) +#define XAT_AV_QUARANTINED ((XAT0_INDEX << XVA_SHFT) | XAT0_AV_QUARANTINED) +#define XAT_AV_MODIFIED ((XAT0_INDEX << XVA_SHFT) | XAT0_AV_MODIFIED) +#define XAT_AV_SCANSTAMP ((XAT0_INDEX << XVA_SHFT) | XAT0_AV_SCANSTAMP) + +/* + * The returned attribute map array (xva_rtnattrmap[]) is located past the + * requested attribute map array (xva_reqattrmap[]). Its location changes + * when the array sizes change. We use a separate pointer in a known location + * (xva_rtnattrmapp) to hold the location of xva_rtnattrmap[]. This is + * set in xva_init() + */ +#define XVA_RTNATTRMAP(xvap) ((xvap)->xva_rtnattrmapp) + +/* + * XVA_SET_REQ() sets an attribute bit in the proper element in the bitmap + * of requested attributes (xva_reqattrmap[]). + */ +#define XVA_SET_REQ(xvap, attr) \ + ASSERT((xvap)->xva_vattr.va_mask | AT_XVATTR); \ + ASSERT((xvap)->xva_magic == XVA_MAGIC); \ + (xvap)->xva_reqattrmap[XVA_INDEX(attr)] |= XVA_ATTRBIT(attr) + +/* + * XVA_SET_RTN() sets an attribute bit in the proper element in the bitmap + * of returned attributes (xva_rtnattrmap[]). + */ +#define XVA_SET_RTN(xvap, attr) \ + ASSERT((xvap)->xva_vattr.va_mask | AT_XVATTR); \ + ASSERT((xvap)->xva_magic == XVA_MAGIC); \ + (XVA_RTNATTRMAP(xvap))[XVA_INDEX(attr)] |= XVA_ATTRBIT(attr) + +/* + * XVA_ISSET_REQ() checks the requested attribute bitmap (xva_reqattrmap[]) + * to see of the corresponding attribute bit is set. If so, returns non-zero. + */ +#define XVA_ISSET_REQ(xvap, attr) \ + ((((xvap)->xva_vattr.va_mask | AT_XVATTR) && \ + ((xvap)->xva_magic == XVA_MAGIC) && \ + ((xvap)->xva_mapsize > XVA_INDEX(attr))) ? \ + ((xvap)->xva_reqattrmap[XVA_INDEX(attr)] & XVA_ATTRBIT(attr)) : 0) + +/* + * XVA_ISSET_RTN() checks the returned attribute bitmap (xva_rtnattrmap[]) + * to see of the corresponding attribute bit is set. If so, returns non-zero. + */ +#define XVA_ISSET_RTN(xvap, attr) \ + ((((xvap)->xva_vattr.va_mask | AT_XVATTR) && \ + ((xvap)->xva_magic == XVA_MAGIC) && \ + ((xvap)->xva_mapsize > XVA_INDEX(attr))) ? \ + ((XVA_RTNATTRMAP(xvap))[XVA_INDEX(attr)] & XVA_ATTRBIT(attr)) : 0) + /* * Modes. Some values same as S_xxx entries from stat.h for convenience. */ @@ -453,6 +669,12 @@ typedef vattr_t vattr32_t; #define MODEMASK 07777 /* mode bits plus permission bits */ #define PERMMASK 00777 /* permission bits */ +/* + * VOP_ACCESS flags + */ +#define V_ACE_MASK 0x1 /* mask represents NFSv4 ACE permissions */ +#define V_APPEND 0x2 /* want to do append only check */ + /* * Check whether mandatory file locking is enabled. */ @@ -506,15 +728,19 @@ typedef struct vsecattr { void *vsa_aclentp; /* pointer to ACL entries */ int vsa_dfaclcnt; /* default ACL entry count */ void *vsa_dfaclentp; /* pointer to default ACL entries */ + size_t vsa_aclentsz; /* ACE size in bytes of vsa_aclentp */ + uint_t vsa_aclflags; /* ACE ACL flags */ } vsecattr_t; /* vsa_mask values */ -#define VSA_ACL 0x0001 -#define VSA_ACLCNT 0x0002 -#define VSA_DFACL 0x0004 -#define VSA_DFACLCNT 0x0008 -#define VSA_ACE 0x0010 -#define VSA_ACECNT 0x0020 +#define VSA_ACL 0x0001 +#define VSA_ACLCNT 0x0002 +#define VSA_DFACL 0x0004 +#define VSA_DFACLCNT 0x0008 +#define VSA_ACE 0x0010 +#define VSA_ACECNT 0x0020 +#define VSA_ACE_ALLTYPES 0x0040 +#define VSA_ACE_ACLFLAGS 0x0080 /* get/set ACE ACL flags */ /* * Structure used by various vnode operations to determine @@ -552,79 +778,110 @@ struct pollhead; * the vnodeops structure (below) and the fs_func_p union (vfs_opreg.h). */ #define VNODE_OPS \ - int (*vop_open)(vnode_t **, int, cred_t *); \ - int (*vop_close)(vnode_t *, int, int, offset_t, cred_t *); \ + int (*vop_open)(vnode_t **, int, cred_t *, \ + caller_context_t *); \ + int (*vop_close)(vnode_t *, int, int, offset_t, cred_t *, \ + caller_context_t *); \ int (*vop_read)(vnode_t *, uio_t *, int, cred_t *, \ caller_context_t *); \ int (*vop_write)(vnode_t *, uio_t *, int, cred_t *, \ caller_context_t *); \ int (*vop_ioctl)(vnode_t *, int, intptr_t, int, cred_t *, \ - int *); \ - int (*vop_setfl)(vnode_t *, int, int, cred_t *); \ - int (*vop_getattr)(vnode_t *, vattr_t *, int, cred_t *); \ + int *, caller_context_t *); \ + int (*vop_setfl)(vnode_t *, int, int, cred_t *, \ + caller_context_t *); \ + int (*vop_getattr)(vnode_t *, vattr_t *, int, cred_t *, \ + caller_context_t *); \ int (*vop_setattr)(vnode_t *, vattr_t *, int, cred_t *, \ caller_context_t *); \ - int (*vop_access)(vnode_t *, int, int, cred_t *); \ + int (*vop_access)(vnode_t *, int, int, cred_t *, \ + caller_context_t *); \ int (*vop_lookup)(vnode_t *, char *, vnode_t **, \ struct pathname *, \ - int, vnode_t *, cred_t *); \ + int, vnode_t *, cred_t *, \ + caller_context_t *, int *, \ + struct pathname *); \ int (*vop_create)(vnode_t *, char *, vattr_t *, vcexcl_t, \ - int, vnode_t **, cred_t *, int); \ - int (*vop_remove)(vnode_t *, char *, cred_t *); \ - int (*vop_link)(vnode_t *, vnode_t *, char *, cred_t *); \ + int, vnode_t **, cred_t *, int, \ + caller_context_t *, vsecattr_t *); \ + int (*vop_remove)(vnode_t *, char *, cred_t *, \ + caller_context_t *, int); \ + int (*vop_link)(vnode_t *, vnode_t *, char *, cred_t *, \ + caller_context_t *, int); \ int (*vop_rename)(vnode_t *, char *, vnode_t *, char *, \ - cred_t *); \ + cred_t *, caller_context_t *, int); \ int (*vop_mkdir)(vnode_t *, char *, vattr_t *, vnode_t **, \ - cred_t *); \ - int (*vop_rmdir)(vnode_t *, char *, vnode_t *, cred_t *); \ - int (*vop_readdir)(vnode_t *, uio_t *, cred_t *, int *); \ + cred_t *, caller_context_t *, int, \ + vsecattr_t *); \ + int (*vop_rmdir)(vnode_t *, char *, vnode_t *, cred_t *, \ + caller_context_t *, int); \ + int (*vop_readdir)(vnode_t *, uio_t *, cred_t *, int *, \ + caller_context_t *, int); \ int (*vop_symlink)(vnode_t *, char *, vattr_t *, char *, \ - cred_t *); \ - int (*vop_readlink)(vnode_t *, uio_t *, cred_t *); \ - int (*vop_fsync)(vnode_t *, int, cred_t *); \ - void (*vop_inactive)(vnode_t *, cred_t *); \ - int (*vop_fid)(vnode_t *, struct fid *); \ + cred_t *, caller_context_t *, int); \ + int (*vop_readlink)(vnode_t *, uio_t *, cred_t *, \ + caller_context_t *); \ + int (*vop_fsync)(vnode_t *, int, cred_t *, \ + caller_context_t *); \ + void (*vop_inactive)(vnode_t *, cred_t *, \ + caller_context_t *); \ + int (*vop_fid)(vnode_t *, struct fid *, \ + caller_context_t *); \ int (*vop_rwlock)(vnode_t *, int, caller_context_t *); \ void (*vop_rwunlock)(vnode_t *, int, caller_context_t *); \ - int (*vop_seek)(vnode_t *, offset_t, offset_t *); \ - int (*vop_cmp)(vnode_t *, vnode_t *); \ + int (*vop_seek)(vnode_t *, offset_t, offset_t *, \ + caller_context_t *); \ + int (*vop_cmp)(vnode_t *, vnode_t *, caller_context_t *); \ int (*vop_frlock)(vnode_t *, int, struct flock64 *, \ int, offset_t, \ - struct flk_callback *, cred_t *); \ + struct flk_callback *, cred_t *, \ + caller_context_t *); \ int (*vop_space)(vnode_t *, int, struct flock64 *, \ int, offset_t, \ cred_t *, caller_context_t *); \ - int (*vop_realvp)(vnode_t *, vnode_t **); \ + int (*vop_realvp)(vnode_t *, vnode_t **, \ + caller_context_t *); \ int (*vop_getpage)(vnode_t *, offset_t, size_t, uint_t *, \ struct page **, size_t, struct seg *, \ - caddr_t, enum seg_rw, cred_t *); \ + caddr_t, enum seg_rw, cred_t *, \ + caller_context_t *); \ int (*vop_putpage)(vnode_t *, offset_t, size_t, \ - int, cred_t *); \ + int, cred_t *, caller_context_t *); \ int (*vop_map)(vnode_t *, offset_t, struct as *, \ caddr_t *, size_t, \ - uchar_t, uchar_t, uint_t, cred_t *); \ + uchar_t, uchar_t, uint_t, cred_t *, \ + caller_context_t *); \ int (*vop_addmap)(vnode_t *, offset_t, struct as *, \ caddr_t, size_t, \ - uchar_t, uchar_t, uint_t, cred_t *); \ + uchar_t, uchar_t, uint_t, cred_t *, \ + caller_context_t *); \ int (*vop_delmap)(vnode_t *, offset_t, struct as *, \ caddr_t, size_t, \ - uint_t, uint_t, uint_t, cred_t *); \ + uint_t, uint_t, uint_t, cred_t *, \ + caller_context_t *); \ int (*vop_poll)(vnode_t *, short, int, short *, \ - struct pollhead **); \ - int (*vop_dump)(vnode_t *, caddr_t, int, int); \ - int (*vop_pathconf)(vnode_t *, int, ulong_t *, cred_t *); \ + struct pollhead **, \ + caller_context_t *); \ + int (*vop_dump)(vnode_t *, caddr_t, int, int, \ + caller_context_t *); \ + int (*vop_pathconf)(vnode_t *, int, ulong_t *, cred_t *, \ + caller_context_t *); \ int (*vop_pageio)(vnode_t *, struct page *, \ - u_offset_t, size_t, int, cred_t *); \ - int (*vop_dumpctl)(vnode_t *, int, int *); \ + u_offset_t, size_t, int, cred_t *, \ + caller_context_t *); \ + int (*vop_dumpctl)(vnode_t *, int, int *, \ + caller_context_t *); \ void (*vop_dispose)(vnode_t *, struct page *, \ - int, int, cred_t *); \ + int, int, cred_t *, \ + caller_context_t *); \ int (*vop_setsecattr)(vnode_t *, vsecattr_t *, \ - int, cred_t *); \ + int, cred_t *, caller_context_t *); \ int (*vop_getsecattr)(vnode_t *, vsecattr_t *, \ - int, cred_t *); \ + int, cred_t *, caller_context_t *); \ int (*vop_shrlock)(vnode_t *, int, struct shrlock *, \ - int, cred_t *); \ - int (*vop_vnevent)(vnode_t *, vnevent_t, vnode_t *, char *) \ + int, cred_t *, caller_context_t *); \ + int (*vop_vnevent)(vnode_t *, vnevent_t, vnode_t *, \ + char *, caller_context_t *) /* NB: No ";" */ /* @@ -639,153 +896,178 @@ typedef struct vnodeops { typedef int (*fs_generic_func_p) (); /* Generic vop/vfsop/femop/fsemop ptr */ -extern int fop_open(vnode_t **, int, cred_t *); -extern int fop_close(vnode_t *, int, int, offset_t, cred_t *); +extern int fop_open(vnode_t **, int, cred_t *, caller_context_t *); +extern int fop_close(vnode_t *, int, int, offset_t, cred_t *, + caller_context_t *); extern int fop_read(vnode_t *, uio_t *, int, cred_t *, caller_context_t *); extern int fop_write(vnode_t *, uio_t *, int, cred_t *, caller_context_t *); -extern int fop_ioctl(vnode_t *, int, intptr_t, int, cred_t *, int *); -extern int fop_setfl(vnode_t *, int, int, cred_t *); -extern int fop_getattr(vnode_t *, vattr_t *, int, cred_t *); +extern int fop_ioctl(vnode_t *, int, intptr_t, int, cred_t *, int *, + caller_context_t *); +extern int fop_setfl(vnode_t *, int, int, cred_t *, caller_context_t *); +extern int fop_getattr(vnode_t *, vattr_t *, int, cred_t *, + caller_context_t *); extern int fop_setattr(vnode_t *, vattr_t *, int, cred_t *, caller_context_t *); -extern int fop_access(vnode_t *, int, int, cred_t *); +extern int fop_access(vnode_t *, int, int, cred_t *, caller_context_t *); extern int fop_lookup(vnode_t *, char *, vnode_t **, struct pathname *, - int, vnode_t *, cred_t *); + int, vnode_t *, cred_t *, caller_context_t *, + int *, struct pathname *); extern int fop_create(vnode_t *, char *, vattr_t *, vcexcl_t, int, - vnode_t **, cred_t *, int); -extern int fop_remove(vnode_t *vp, char *, cred_t *); -extern int fop_link(vnode_t *, vnode_t *, char *, cred_t *); -extern int fop_rename(vnode_t *, char *, vnode_t *, char *, cred_t *); -extern int fop_mkdir(vnode_t *, char *, vattr_t *, vnode_t **, cred_t *); -extern int fop_rmdir(vnode_t *, char *, vnode_t *, cred_t *); -extern int fop_readdir(vnode_t *, uio_t *, cred_t *, int *); -extern int fop_symlink(vnode_t *, char *, vattr_t *, char *, cred_t *); -extern int fop_readlink(vnode_t *, uio_t *, cred_t *); -extern int fop_fsync(vnode_t *, int, cred_t *); -extern void fop_inactive(vnode_t *, cred_t *); -extern int fop_fid(vnode_t *, struct fid *); + vnode_t **, cred_t *, int, caller_context_t *, + vsecattr_t *); +extern int fop_remove(vnode_t *vp, char *, cred_t *, caller_context_t *, + int); +extern int fop_link(vnode_t *, vnode_t *, char *, cred_t *, + caller_context_t *, int); +extern int fop_rename(vnode_t *, char *, vnode_t *, char *, cred_t *, + caller_context_t *, int); +extern int fop_mkdir(vnode_t *, char *, vattr_t *, vnode_t **, cred_t *, + caller_context_t *, int, vsecattr_t *); +extern int fop_rmdir(vnode_t *, char *, vnode_t *, cred_t *, + caller_context_t *, int); +extern int fop_readdir(vnode_t *, uio_t *, cred_t *, int *, + caller_context_t *, int); +extern int fop_symlink(vnode_t *, char *, vattr_t *, char *, cred_t *, + caller_context_t *, int); +extern int fop_readlink(vnode_t *, uio_t *, cred_t *, caller_context_t *); +extern int fop_fsync(vnode_t *, int, cred_t *, caller_context_t *); +extern void fop_inactive(vnode_t *, cred_t *, caller_context_t *); +extern int fop_fid(vnode_t *, struct fid *, caller_context_t *); extern int fop_rwlock(vnode_t *, int, caller_context_t *); extern void fop_rwunlock(vnode_t *, int, caller_context_t *); -extern int fop_seek(vnode_t *, offset_t, offset_t *); -extern int fop_cmp(vnode_t *, vnode_t *); +extern int fop_seek(vnode_t *, offset_t, offset_t *, caller_context_t *); +extern int fop_cmp(vnode_t *, vnode_t *, caller_context_t *); extern int fop_frlock(vnode_t *, int, struct flock64 *, int, offset_t, - struct flk_callback *, cred_t *); + struct flk_callback *, cred_t *, + caller_context_t *); extern int fop_space(vnode_t *, int, struct flock64 *, int, offset_t, cred_t *, caller_context_t *); -extern int fop_realvp(vnode_t *, vnode_t **); +extern int fop_realvp(vnode_t *, vnode_t **, caller_context_t *); extern int fop_getpage(vnode_t *, offset_t, size_t, uint_t *, struct page **, size_t, struct seg *, - caddr_t, enum seg_rw, cred_t *); -extern int fop_putpage(vnode_t *, offset_t, size_t, int, cred_t *); + caddr_t, enum seg_rw, cred_t *, + caller_context_t *); +extern int fop_putpage(vnode_t *, offset_t, size_t, int, cred_t *, + caller_context_t *); extern int fop_map(vnode_t *, offset_t, struct as *, caddr_t *, size_t, - uchar_t, uchar_t, uint_t, cred_t *cr); + uchar_t, uchar_t, uint_t, cred_t *cr, + caller_context_t *); extern int fop_addmap(vnode_t *, offset_t, struct as *, caddr_t, size_t, - uchar_t, uchar_t, uint_t, cred_t *); + uchar_t, uchar_t, uint_t, cred_t *, + caller_context_t *); extern int fop_delmap(vnode_t *, offset_t, struct as *, caddr_t, size_t, - uint_t, uint_t, uint_t, cred_t *); -extern int fop_poll(vnode_t *, short, int, short *, struct pollhead **); -extern int fop_dump(vnode_t *, caddr_t, int, int); -extern int fop_pathconf(vnode_t *, int, ulong_t *, cred_t *); + uint_t, uint_t, uint_t, cred_t *, + caller_context_t *); +extern int fop_poll(vnode_t *, short, int, short *, struct pollhead **, + caller_context_t *); +extern int fop_dump(vnode_t *, caddr_t, int, int, caller_context_t *); +extern int fop_pathconf(vnode_t *, int, ulong_t *, cred_t *, + caller_context_t *); extern int fop_pageio(vnode_t *, struct page *, u_offset_t, size_t, int, - cred_t *); -extern int fop_dumpctl(vnode_t *, int, int *); -extern void fop_dispose(vnode_t *, struct page *, int, int, cred_t *); -extern int fop_setsecattr(vnode_t *, vsecattr_t *, int, cred_t *); -extern int fop_getsecattr(vnode_t *, vsecattr_t *, int, cred_t *); -extern int fop_shrlock(vnode_t *, int, struct shrlock *, int, cred_t *); -extern int fop_vnevent(vnode_t *, vnevent_t, vnode_t *, char *); + cred_t *, caller_context_t *); +extern int fop_dumpctl(vnode_t *, int, int *, caller_context_t *); +extern void fop_dispose(vnode_t *, struct page *, int, int, cred_t *, + caller_context_t *); +extern int fop_setsecattr(vnode_t *, vsecattr_t *, int, cred_t *, + caller_context_t *); +extern int fop_getsecattr(vnode_t *, vsecattr_t *, int, cred_t *, + caller_context_t *); +extern int fop_shrlock(vnode_t *, int, struct shrlock *, int, cred_t *, + caller_context_t *); +extern int fop_vnevent(vnode_t *, vnevent_t, vnode_t *, char *, + caller_context_t *); #endif /* _KERNEL */ -#define VOP_OPEN(vpp, mode, cr) \ - fop_open(vpp, mode, cr) -#define VOP_CLOSE(vp, f, c, o, cr) \ - fop_close(vp, f, c, o, cr) +#define VOP_OPEN(vpp, mode, cr, ct) \ + fop_open(vpp, mode, cr, ct) +#define VOP_CLOSE(vp, f, c, o, cr, ct) \ + fop_close(vp, f, c, o, cr, ct) #define VOP_READ(vp, uiop, iof, cr, ct) \ fop_read(vp, uiop, iof, cr, ct) #define VOP_WRITE(vp, uiop, iof, cr, ct) \ fop_write(vp, uiop, iof, cr, ct) -#define VOP_IOCTL(vp, cmd, a, f, cr, rvp) \ - fop_ioctl(vp, cmd, a, f, cr, rvp) -#define VOP_SETFL(vp, f, a, cr) \ - fop_setfl(vp, f, a, cr) -#define VOP_GETATTR(vp, vap, f, cr) \ - fop_getattr(vp, vap, f, cr) +#define VOP_IOCTL(vp, cmd, a, f, cr, rvp, ct) \ + fop_ioctl(vp, cmd, a, f, cr, rvp, ct) +#define VOP_SETFL(vp, f, a, cr, ct) \ + fop_setfl(vp, f, a, cr, ct) +#define VOP_GETATTR(vp, vap, f, cr, ct) \ + fop_getattr(vp, vap, f, cr, ct) #define VOP_SETATTR(vp, vap, f, cr, ct) \ fop_setattr(vp, vap, f, cr, ct) -#define VOP_ACCESS(vp, mode, f, cr) \ - fop_access(vp, mode, f, cr) -#define VOP_LOOKUP(vp, cp, vpp, pnp, f, rdir, cr) \ - fop_lookup(vp, cp, vpp, pnp, f, rdir, cr) -#define VOP_CREATE(dvp, p, vap, ex, mode, vpp, cr, flag) \ - fop_create(dvp, p, vap, ex, mode, vpp, cr, flag) -#define VOP_REMOVE(dvp, p, cr) \ - fop_remove(dvp, p, cr) -#define VOP_LINK(tdvp, fvp, p, cr) \ - fop_link(tdvp, fvp, p, cr) -#define VOP_RENAME(fvp, fnm, tdvp, tnm, cr) \ - fop_rename(fvp, fnm, tdvp, tnm, cr) -#define VOP_MKDIR(dp, p, vap, vpp, cr) \ - fop_mkdir(dp, p, vap, vpp, cr) -#define VOP_RMDIR(dp, p, cdir, cr) \ - fop_rmdir(dp, p, cdir, cr) -#define VOP_READDIR(vp, uiop, cr, eofp) \ - fop_readdir(vp, uiop, cr, eofp) -#define VOP_SYMLINK(dvp, lnm, vap, tnm, cr) \ - fop_symlink(dvp, lnm, vap, tnm, cr) -#define VOP_READLINK(vp, uiop, cr) \ - fop_readlink(vp, uiop, cr) -#define VOP_FSYNC(vp, syncflag, cr) \ - fop_fsync(vp, syncflag, cr) -#define VOP_INACTIVE(vp, cr) \ - fop_inactive(vp, cr) -#define VOP_FID(vp, fidp) \ - fop_fid(vp, fidp) +#define VOP_ACCESS(vp, mode, f, cr, ct) \ + fop_access(vp, mode, f, cr, ct) +#define VOP_LOOKUP(vp, cp, vpp, pnp, f, rdir, cr, ct, defp, rpnp) \ + fop_lookup(vp, cp, vpp, pnp, f, rdir, cr, ct, defp, rpnp) +#define VOP_CREATE(dvp, p, vap, ex, mode, vpp, cr, flag, ct, vsap) \ + fop_create(dvp, p, vap, ex, mode, vpp, cr, flag, ct, vsap) +#define VOP_REMOVE(dvp, p, cr, ct, f) \ + fop_remove(dvp, p, cr, ct, f) +#define VOP_LINK(tdvp, fvp, p, cr, ct, f) \ + fop_link(tdvp, fvp, p, cr, ct, f) +#define VOP_RENAME(fvp, fnm, tdvp, tnm, cr, ct, f) \ + fop_rename(fvp, fnm, tdvp, tnm, cr, ct, f) +#define VOP_MKDIR(dp, p, vap, vpp, cr, ct, f, vsap) \ + fop_mkdir(dp, p, vap, vpp, cr, ct, f, vsap) +#define VOP_RMDIR(dp, p, cdir, cr, ct, f) \ + fop_rmdir(dp, p, cdir, cr, ct, f) +#define VOP_READDIR(vp, uiop, cr, eofp, ct, f) \ + fop_readdir(vp, uiop, cr, eofp, ct, f) +#define VOP_SYMLINK(dvp, lnm, vap, tnm, cr, ct, f) \ + fop_symlink(dvp, lnm, vap, tnm, cr, ct, f) +#define VOP_READLINK(vp, uiop, cr, ct) \ + fop_readlink(vp, uiop, cr, ct) +#define VOP_FSYNC(vp, syncflag, cr, ct) \ + fop_fsync(vp, syncflag, cr, ct) +#define VOP_INACTIVE(vp, cr, ct) \ + fop_inactive(vp, cr, ct) +#define VOP_FID(vp, fidp, ct) \ + fop_fid(vp, fidp, ct) #define VOP_RWLOCK(vp, w, ct) \ fop_rwlock(vp, w, ct) #define VOP_RWUNLOCK(vp, w, ct) \ fop_rwunlock(vp, w, ct) -#define VOP_SEEK(vp, ooff, noffp) \ - fop_seek(vp, ooff, noffp) -#define VOP_CMP(vp1, vp2) \ - fop_cmp(vp1, vp2) -#define VOP_FRLOCK(vp, cmd, a, f, o, cb, cr) \ - fop_frlock(vp, cmd, a, f, o, cb, cr) +#define VOP_SEEK(vp, ooff, noffp, ct) \ + fop_seek(vp, ooff, noffp, ct) +#define VOP_CMP(vp1, vp2, ct) \ + fop_cmp(vp1, vp2, ct) +#define VOP_FRLOCK(vp, cmd, a, f, o, cb, cr, ct) \ + fop_frlock(vp, cmd, a, f, o, cb, cr, ct) #define VOP_SPACE(vp, cmd, a, f, o, cr, ct) \ fop_space(vp, cmd, a, f, o, cr, ct) -#define VOP_REALVP(vp1, vp2) \ - fop_realvp(vp1, vp2) -#define VOP_GETPAGE(vp, of, sz, pr, pl, ps, sg, a, rw, cr) \ - fop_getpage(vp, of, sz, pr, pl, ps, sg, a, rw, cr) -#define VOP_PUTPAGE(vp, of, sz, fl, cr) \ - fop_putpage(vp, of, sz, fl, cr) -#define VOP_MAP(vp, of, as, a, sz, p, mp, fl, cr) \ - fop_map(vp, of, as, a, sz, p, mp, fl, cr) -#define VOP_ADDMAP(vp, of, as, a, sz, p, mp, fl, cr) \ - fop_addmap(vp, of, as, a, sz, p, mp, fl, cr) -#define VOP_DELMAP(vp, of, as, a, sz, p, mp, fl, cr) \ - fop_delmap(vp, of, as, a, sz, p, mp, fl, cr) -#define VOP_POLL(vp, events, anyyet, reventsp, phpp) \ - fop_poll(vp, events, anyyet, reventsp, phpp) -#define VOP_DUMP(vp, addr, bn, count) \ - fop_dump(vp, addr, bn, count) -#define VOP_PATHCONF(vp, cmd, valp, cr) \ - fop_pathconf(vp, cmd, valp, cr) -#define VOP_PAGEIO(vp, pp, io_off, io_len, flags, cr) \ - fop_pageio(vp, pp, io_off, io_len, flags, cr) -#define VOP_DUMPCTL(vp, action, blkp) \ - fop_dumpctl(vp, action, blkp) -#define VOP_DISPOSE(vp, pp, flag, dn, cr) \ - fop_dispose(vp, pp, flag, dn, cr) -#define VOP_GETSECATTR(vp, vsap, f, cr) \ - fop_getsecattr(vp, vsap, f, cr) -#define VOP_SETSECATTR(vp, vsap, f, cr) \ - fop_setsecattr(vp, vsap, f, cr) -#define VOP_SHRLOCK(vp, cmd, shr, f, cr) \ - fop_shrlock(vp, cmd, shr, f, cr) -#define VOP_VNEVENT(vp, vnevent, dvp, fnm) \ - fop_vnevent(vp, vnevent, dvp, fnm) +#define VOP_REALVP(vp1, vp2, ct) \ + fop_realvp(vp1, vp2, ct) +#define VOP_GETPAGE(vp, of, sz, pr, pl, ps, sg, a, rw, cr, ct) \ + fop_getpage(vp, of, sz, pr, pl, ps, sg, a, rw, cr, ct) +#define VOP_PUTPAGE(vp, of, sz, fl, cr, ct) \ + fop_putpage(vp, of, sz, fl, cr, ct) +#define VOP_MAP(vp, of, as, a, sz, p, mp, fl, cr, ct) \ + fop_map(vp, of, as, a, sz, p, mp, fl, cr, ct) +#define VOP_ADDMAP(vp, of, as, a, sz, p, mp, fl, cr, ct) \ + fop_addmap(vp, of, as, a, sz, p, mp, fl, cr, ct) +#define VOP_DELMAP(vp, of, as, a, sz, p, mp, fl, cr, ct) \ + fop_delmap(vp, of, as, a, sz, p, mp, fl, cr, ct) +#define VOP_POLL(vp, events, anyyet, reventsp, phpp, ct) \ + fop_poll(vp, events, anyyet, reventsp, phpp, ct) +#define VOP_DUMP(vp, addr, bn, count, ct) \ + fop_dump(vp, addr, bn, count, ct) +#define VOP_PATHCONF(vp, cmd, valp, cr, ct) \ + fop_pathconf(vp, cmd, valp, cr, ct) +#define VOP_PAGEIO(vp, pp, io_off, io_len, flags, cr, ct) \ + fop_pageio(vp, pp, io_off, io_len, flags, cr, ct) +#define VOP_DUMPCTL(vp, action, blkp, ct) \ + fop_dumpctl(vp, action, blkp, ct) +#define VOP_DISPOSE(vp, pp, flag, dn, cr, ct) \ + fop_dispose(vp, pp, flag, dn, cr, ct) +#define VOP_GETSECATTR(vp, vsap, f, cr, ct) \ + fop_getsecattr(vp, vsap, f, cr, ct) +#define VOP_SETSECATTR(vp, vsap, f, cr, ct) \ + fop_setsecattr(vp, vsap, f, cr, ct) +#define VOP_SHRLOCK(vp, cmd, shr, f, cr, ct) \ + fop_shrlock(vp, cmd, shr, f, cr, ct) +#define VOP_VNEVENT(vp, vnevent, dvp, fnm, ct) \ + fop_vnevent(vp, vnevent, dvp, fnm, ct) #define VOPNAME_OPEN "open" #define VOPNAME_CLOSE "close" @@ -834,10 +1116,19 @@ extern int fop_vnevent(vnode_t *, vnevent_t, vnode_t *, char *); /* * Flags for VOP_LOOKUP + * + * Defined in file.h, but also possible, FIGNORECASE + * */ #define LOOKUP_DIR 0x01 /* want parent dir vp */ #define LOOKUP_XATTR 0x02 /* lookup up extended attr dir */ #define CREATE_XATTR_DIR 0x04 /* Create extended attr dir */ +#define LOOKUP_HAVE_SYSATTR_DIR 0x08 /* Already created virtual GFS dir */ + +/* + * Flags for VOP_READDIR + */ +#define V_RDDIR_ENTFLAGS 0x01 /* request dirent flags */ /* * Flags for VOP_RWLOCK/VOP_RWUNLOCK @@ -866,6 +1157,9 @@ void vn_free(vnode_t *); int vn_is_readonly(vnode_t *); int vn_is_opened(vnode_t *, v_mode_t); int vn_is_mapped(vnode_t *, v_mode_t); +int vn_has_other_opens(vnode_t *, v_mode_t); +void vn_open_upgrade(vnode_t *, int); +void vn_open_downgrade(vnode_t *, int); int vn_can_change_zones(vnode_t *vp); @@ -890,7 +1184,7 @@ int vn_open(char *pnamep, enum uio_seg seg, int filemode, int createmode, struct vnode **vpp, enum create crwhy, mode_t umask); int vn_openat(char *pnamep, enum uio_seg seg, int filemode, int createmode, struct vnode **vpp, enum create crwhy, - mode_t umask, struct vnode *startvp); + mode_t umask, struct vnode *startvp, int fd); int vn_create(char *pnamep, enum uio_seg seg, struct vattr *vap, enum vcexcl excl, int mode, struct vnode **vpp, enum create why, int flag, mode_t umask); @@ -927,15 +1221,15 @@ void vn_setpath(vnode_t *rootvp, struct vnode *startvp, struct vnode *vp, const char *path, size_t plen); /* Vnode event notification */ -void vnevent_rename_src(vnode_t *, vnode_t *, char *); -void vnevent_rename_dest(vnode_t *, vnode_t *, char *); -void vnevent_remove(vnode_t *, vnode_t *, char *); -void vnevent_rmdir(vnode_t *, vnode_t *, char *); -void vnevent_create(vnode_t *); -void vnevent_link(vnode_t *); -void vnevent_rename_dest_dir(vnode_t *); -void vnevent_mountedover(vnode_t *); -int vnevent_support(vnode_t *); +void vnevent_rename_src(vnode_t *, vnode_t *, char *, caller_context_t *); +void vnevent_rename_dest(vnode_t *, vnode_t *, char *, caller_context_t *); +void vnevent_remove(vnode_t *, vnode_t *, char *, caller_context_t *); +void vnevent_rmdir(vnode_t *, vnode_t *, char *, caller_context_t *); +void vnevent_create(vnode_t *, caller_context_t *); +void vnevent_link(vnode_t *, caller_context_t *); +void vnevent_rename_dest_dir(vnode_t *, caller_context_t *ct); +void vnevent_mountedover(vnode_t *, caller_context_t *); +int vnevent_support(vnode_t *, caller_context_t *); /* Vnode specific data */ void vsd_create(uint_t *, void (*)(void *)); @@ -944,6 +1238,19 @@ void *vsd_get(vnode_t *, uint_t); int vsd_set(vnode_t *, uint_t, void *); void vsd_free(vnode_t *); +/* + * Extensible vnode attribute (xva) routines: + * xva_init() initializes an xvattr_t (zero struct, init mapsize, set AT_XATTR) + * xva_getxoptattr() returns a ponter to the xoptattr_t section of xvattr_t + */ +void xva_init(xvattr_t *); +xoptattr_t *xva_getxoptattr(xvattr_t *); /* Get ptr to xoptattr_t */ + +void xattr_init(void); /* Initialize vnodeops for xattrs */ + +/* GFS tunnel for xattrs */ +int xattr_dir_lookup(vnode_t *, vnode_t **, int, cred_t *); + /* Context identification */ u_longlong_t fs_new_caller_id(); @@ -976,7 +1283,7 @@ extern uint_t pvn_vmodsort_supported; */ #define VN_CMP(VP1, VP2) ((VP1) == (VP2) ? 1 : \ ((VP1) && (VP2) && (vn_getops(VP1) == vn_getops(VP2)) ? \ - VOP_CMP(VP1, VP2) : 0)) + VOP_CMP(VP1, VP2, NULL) : 0)) extern struct vnode kvp; extern struct vnode zvp; @@ -993,8 +1300,8 @@ extern struct vnode zvp; #define ATTR_COMM 0x04 /* yield common vp attributes */ #define ATTR_HINT 0x08 /* information returned will be `hint' */ #define ATTR_REAL 0x10 /* yield attributes of the real vp */ +#define ATTR_NOACLCHECK 0x20 /* Don't check ACL when checking permissions */ #define ATTR_TRIGGER 0x40 /* Mount first if vnode is a trigger mount */ - /* * Generally useful macros. */ @@ -1026,7 +1333,7 @@ struct async_reqs { #define VN_DISPOSE(pp, flag, dn, cr) { \ extern struct vnode kvp; \ if ((pp)->p_vnode != NULL && !VN_ISKAS((pp)->p_vnode)) \ - VOP_DISPOSE((pp)->p_vnode, (pp), (flag), (dn), (cr)); \ + VOP_DISPOSE((pp)->p_vnode, (pp), (flag), (dn), (cr), NULL); \ else if ((flag) == B_FREE) \ page_free((pp), (dn)); \ else \ diff --git a/usr/src/uts/common/syscall/access.c b/usr/src/uts/common/syscall/access.c index 0c19d8898ae1..467cb4ecb1d0 100644 --- a/usr/src/uts/common/syscall/access.c +++ b/usr/src/uts/common/syscall/access.c @@ -101,7 +101,7 @@ caccess(char *fname, int fmode, vnode_t *startvp) } if (mode) { - error = VOP_ACCESS(vp, mode, 0, tmpcr); + error = VOP_ACCESS(vp, mode, 0, tmpcr, NULL); if (error) { if ((error == ESTALE) && fs_need_estale_retry(estale_retry++)) { diff --git a/usr/src/uts/common/syscall/acctctl.c b/usr/src/uts/common/syscall/acctctl.c index 8c134b0a62de..1f614f23fb40 100644 --- a/usr/src/uts/common/syscall/acctctl.c +++ b/usr/src/uts/common/syscall/acctctl.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -161,7 +160,8 @@ ac_file_set(ac_info_t *info, void *ubuf, size_t bufsz) * Closing accounting file */ if (info->ac_vnode != NULL) { - error = VOP_CLOSE(info->ac_vnode, FWRITE, 1, 0, CRED()); + error = VOP_CLOSE(info->ac_vnode, FWRITE, 1, 0, + CRED(), NULL); if (error) { mutex_exit(&info->ac_lock); return (error); @@ -265,7 +265,7 @@ ac_file_set(ac_info_t *info, void *ubuf, size_t bufsz) /* * We still need to close the old file. */ - if ((error = VOP_CLOSE(vp, FWRITE, 1, 0, CRED())) != 0) { + if ((error = VOP_CLOSE(vp, FWRITE, 1, 0, CRED(), NULL)) != 0) { VN_RELE(vp); mutex_exit(&info->ac_lock); kmem_free(namebuf, namelen); @@ -545,7 +545,7 @@ exacct_free_info(ac_info_t *info) { mutex_enter(&info->ac_lock); if (info->ac_vnode) { - (void) VOP_CLOSE(info->ac_vnode, FWRITE, 1, 0, kcred); + (void) VOP_CLOSE(info->ac_vnode, FWRITE, 1, 0, kcred, NULL); VN_RELE(info->ac_vnode); kmem_free(info->ac_file, strlen(info->ac_file) + 1); } diff --git a/usr/src/uts/common/syscall/acl.c b/usr/src/uts/common/syscall/acl.c index 1a7321928dee..5c2fe3a9bc95 100644 --- a/usr/src/uts/common/syscall/acl.c +++ b/usr/src/uts/common/syscall/acl.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -133,6 +133,7 @@ cacl(int cmd, int nentries, void *aclbufp, vnode_t *vp, int *rv) caddr_t uaddrp; aclent_t *aclp, *aaclp; vsecattr_t vsecattr; + size_t entry_size; ASSERT(vp); @@ -142,20 +143,23 @@ cacl(int cmd, int nentries, void *aclbufp, vnode_t *vp, int *rv) case ACE_GETACLCNT: case GETACLCNT: - if (cmd == GETACLCNT) + if (cmd == GETACLCNT) { + entry_size = sizeof (aclent_t); vsecattr.vsa_mask = VSA_ACLCNT | VSA_DFACLCNT; - else + } else { + entry_size = sizeof (ace_t); vsecattr.vsa_mask = VSA_ACECNT; - if (error = VOP_GETSECATTR(vp, &vsecattr, 0, CRED())) + } + if (error = VOP_GETSECATTR(vp, &vsecattr, 0, CRED(), NULL)) return (error); *rv = vsecattr.vsa_aclcnt + vsecattr.vsa_dfaclcnt; if (vsecattr.vsa_aclcnt && vsecattr.vsa_aclentp) { kmem_free(vsecattr.vsa_aclentp, - vsecattr.vsa_aclcnt * sizeof (aclent_t)); + vsecattr.vsa_aclcnt * entry_size); } if (vsecattr.vsa_dfaclcnt && vsecattr.vsa_dfaclentp) { kmem_free(vsecattr.vsa_dfaclentp, - vsecattr.vsa_dfaclcnt * sizeof (aclent_t)); + vsecattr.vsa_dfaclcnt * entry_size); } break; case GETACL: @@ -172,7 +176,7 @@ cacl(int cmd, int nentries, void *aclbufp, vnode_t *vp, int *rv) return (EFAULT); vsecattr.vsa_mask = VSA_ACL | VSA_ACLCNT | VSA_DFACL | VSA_DFACLCNT; - if (error = VOP_GETSECATTR(vp, &vsecattr, 0, CRED())) + if (error = VOP_GETSECATTR(vp, &vsecattr, 0, CRED(), NULL)) return (error); /* Check user's buffer is big enough */ numacls = vsecattr.vsa_aclcnt + vsecattr.vsa_dfaclcnt; @@ -222,7 +226,7 @@ cacl(int cmd, int nentries, void *aclbufp, vnode_t *vp, int *rv) return (EFAULT); vsecattr.vsa_mask = VSA_ACE | VSA_ACECNT; - if (error = VOP_GETSECATTR(vp, &vsecattr, 0, CRED())) + if (error = VOP_GETSECATTR(vp, &vsecattr, 0, CRED(), NULL)) return (error); aclbsize = vsecattr.vsa_aclcnt * sizeof (ace_t); @@ -240,8 +244,7 @@ cacl(int cmd, int nentries, void *aclbufp, vnode_t *vp, int *rv) *rv = vsecattr.vsa_aclcnt; if (vsecattr.vsa_aclcnt) { - kmem_free(vsecattr.vsa_aclentp, - vsecattr.vsa_aclcnt * sizeof (ace_t)); + kmem_free(vsecattr.vsa_aclentp, vsecattr.vsa_aclentsz); } break; @@ -300,7 +303,7 @@ cacl(int cmd, int nentries, void *aclbufp, vnode_t *vp, int *rv) return (ENOTDIR); } (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); - if (error = VOP_SETSECATTR(vp, &vsecattr, 0, CRED())) { + if (error = VOP_SETSECATTR(vp, &vsecattr, 0, CRED(), NULL)) { kmem_free(aaclp, aclbsize); VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); return (error); @@ -327,13 +330,14 @@ cacl(int cmd, int nentries, void *aclbufp, vnode_t *vp, int *rv) vsecattr.vsa_aclentp = kmem_alloc(aclbsize, KM_SLEEP); aaclp = vsecattr.vsa_aclentp; vsecattr.vsa_aclcnt = nentries; + vsecattr.vsa_aclentsz = aclbsize; uaddrp = (caddr_t)aclbufp; if (copyin(uaddrp, vsecattr.vsa_aclentp, aclbsize)) { kmem_free(aaclp, aclbsize); return (EFAULT); } (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); - if (error = VOP_SETSECATTR(vp, &vsecattr, 0, CRED())) { + if (error = VOP_SETSECATTR(vp, &vsecattr, 0, CRED(), NULL)) { kmem_free(aaclp, aclbsize); VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); return (error); diff --git a/usr/src/uts/common/syscall/chdir.c b/usr/src/uts/common/syscall/chdir.c index 28abd43076ce..02bc93da744b 100644 --- a/usr/src/uts/common/syscall/chdir.c +++ b/usr/src/uts/common/syscall/chdir.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -174,7 +174,7 @@ chdirec(vnode_t *vp, int ischroot, int do_traverse) error = ENOTDIR; goto bad; } - if (error = VOP_ACCESS(vp, VEXEC, 0, CRED())) + if (error = VOP_ACCESS(vp, VEXEC, 0, CRED(), NULL)) goto bad; /* @@ -199,11 +199,11 @@ chdirec(vnode_t *vp, int ischroot, int do_traverse) vnode_t *zonevp = curproc->p_zone->zone_rootvp; tattr.va_mask = AT_FSID|AT_NODEID; - if (error = VOP_GETATTR(vp, &tattr, 0, CRED())) + if (error = VOP_GETATTR(vp, &tattr, 0, CRED(), NULL)) goto bad; rattr.va_mask = AT_FSID|AT_NODEID; - if (error = VOP_GETATTR(zonevp, &rattr, 0, CRED())) + if (error = VOP_GETATTR(zonevp, &rattr, 0, CRED(), NULL)) goto bad; if ((tattr.va_fsid != rattr.va_fsid || diff --git a/usr/src/uts/common/syscall/fcntl.c b/usr/src/uts/common/syscall/fcntl.c index 15245e4c8c5b..2e0807e697bf 100644 --- a/usr/src/uts/common/syscall/fcntl.c +++ b/usr/src/uts/common/syscall/fcntl.c @@ -20,7 +20,7 @@ */ /* ONC_PLUS EXTRACT START */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -205,7 +205,8 @@ fcntl(int fdes, int cmd, intptr_t arg) flag = fp->f_flag; if ((iarg & (FNONBLOCK|FNDELAY)) == (FNONBLOCK|FNDELAY)) iarg &= ~FNDELAY; - if ((error = VOP_SETFL(vp, flag, iarg, fp->f_cred)) == 0) { + if ((error = VOP_SETFL(vp, flag, iarg, fp->f_cred, NULL)) == + 0) { iarg &= FMASK; mutex_enter(&fp->f_tlock); fp->f_flag &= ~FMASK | (FREAD|FWRITE); @@ -317,7 +318,7 @@ fcntl(int fdes, int cmd, intptr_t arg) * there's no need for them to know. Map it to F_GETLK. */ if ((error = VOP_FRLOCK(vp, (cmd == F_O_GETLK) ? F_GETLK : cmd, - &bf, flag, offset, NULL, fp->f_cred)) != 0) + &bf, flag, offset, NULL, fp->f_cred, NULL)) != 0) break; /* @@ -527,12 +528,14 @@ fcntl(int fdes, int cmd, intptr_t arg) nbl_start_crit(vp, RW_READER); in_crit = 1; vattr.va_mask = AT_SIZE; - if ((error = VOP_GETATTR(vp, &vattr, 0, CRED())) != 0) + if ((error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL)) + != 0) break; begin = start > vattr.va_size ? vattr.va_size : start; length = vattr.va_size > start ? vattr.va_size - start : start - vattr.va_size; - if (nbl_conflict(vp, NBL_WRITE, begin, length, 0)) { + if (nbl_conflict(vp, NBL_WRITE, begin, length, 0, + NULL)) { error = EACCES; break; } @@ -599,7 +602,7 @@ fcntl(int fdes, int cmd, intptr_t arg) break; if ((error = VOP_FRLOCK(vp, cmd, &bf, flag, offset, - NULL, fp->f_cred)) != 0) + NULL, fp->f_cred, NULL)) != 0) break; if ((cmd == F_GETLK) && bf.l_type == F_UNLCK) { @@ -658,7 +661,7 @@ fcntl(int fdes, int cmd, intptr_t arg) shr_own.sl_id = fsh.f_id; shr.s_own_len = sizeof (shr_own); shr.s_owner = (caddr_t)&shr_own; - error = VOP_SHRLOCK(vp, cmd, &shr, flag, fp->f_cred); + error = VOP_SHRLOCK(vp, cmd, &shr, flag, fp->f_cred, NULL); /* ONC_PLUS EXTRACT END */ break; @@ -710,7 +713,7 @@ flock_check(vnode_t *vp, flock64_t *flp, offset_t offset, offset_t max) break; case 2: /* SEEK_END */ vattr.va_mask = AT_SIZE; - if (error = VOP_GETATTR(vp, &vattr, 0, CRED())) + if (error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL)) return (error); if (flp->l_start > (max - (offset_t)vattr.va_size)) return (EOVERFLOW); @@ -774,7 +777,7 @@ flock_get_start(vnode_t *vp, flock64_t *flp, offset_t offset, u_offset_t *start) break; case 2: /* SEEK_END */ vattr.va_mask = AT_SIZE; - if (error = VOP_GETATTR(vp, &vattr, 0, CRED())) + if (error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL)) return (error); *start = (u_offset_t)(flp->l_start + (offset_t)vattr.va_size); break; diff --git a/usr/src/uts/common/syscall/fdsync.c b/usr/src/uts/common/syscall/fdsync.c index 9951eb87272a..8d8eaeab14e8 100644 --- a/usr/src/uts/common/syscall/fdsync.c +++ b/usr/src/uts/common/syscall/fdsync.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 1998 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -32,7 +31,7 @@ * under license from the Regents of the University of California. */ -#ident "%Z%%M% %I% %E% SMI" +#pragma ident "%Z%%M% %I% %E% SMI" #include #include @@ -65,7 +64,7 @@ fdsync(int fd, int flag) */ syncflag = flag & (FSYNC|FDSYNC); - if (error = VOP_FSYNC(fp->f_vnode, syncflag, fp->f_cred)) + if (error = VOP_FSYNC(fp->f_vnode, syncflag, fp->f_cred, NULL)) (void) set_errno(error); releasef(fd); } else diff --git a/usr/src/uts/common/syscall/fsat.c b/usr/src/uts/common/syscall/fsat.c index a558518d85b0..0ad6a6446e8c 100644 --- a/usr/src/uts/common/syscall/fsat.c +++ b/usr/src/uts/common/syscall/fsat.c @@ -42,6 +42,7 @@ extern int fchownat(int, char *, uid_t, gid_t, int); extern int fstatat(int, char *, struct stat *, int); extern int futimesat(int, char *, struct timeval *); extern int accessat(int, char *, int); +extern int openattrdirat(int, char *); #if defined(_SYSCALL32_IMPL) || defined(_ILP32) extern int fstatat64_32(int, char *, struct stat64_32 *, int); extern int fstatat32(int, char *, struct stat32 *, int); @@ -65,6 +66,7 @@ extern int fstatat64_32(int, char *, struct stat64_32 *, int); * 6 - futimesat * 7 - renameat * 8 - accessat + * 9 - openattrdirat * * The code for handling the at functionality exists in the file where the * base syscall is defined. For example openat is in open.c @@ -81,43 +83,45 @@ fsat32(int code, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, case 0: /* openat */ #if defined(_LP64) return (openat32((int)arg1, (char *)arg2, - (int)arg3, (int)arg4)); + (int)arg3, (int)arg4)); #else return (openat((int)arg1, (char *)arg2, - (int)arg3, (int)arg4)); + (int)arg3, (int)arg4)); #endif case 1: /* openat64 */ return (openat64((int)arg1, (char *)arg2, - (int)arg3, (int)arg4)); + (int)arg3, (int)arg4)); case 2: /* fstatat64 */ #if defined(_LP64) return (fstatat64_32((int)arg1, (char *)arg2, - (struct stat64_32 *)arg3, (int)arg4)); + (struct stat64_32 *)arg3, (int)arg4)); #else return (fstatat64((int)arg1, (char *)arg2, - (struct stat64 *)arg3, (int)arg4)); + (struct stat64 *)arg3, (int)arg4)); #endif case 3: /* fstatat */ #if defined(_LP64) return (fstatat32((int)arg1, (char *)arg2, - (struct stat32 *)arg3, (int)arg4)); + (struct stat32 *)arg3, (int)arg4)); #else return (fstatat((int)arg1, (char *)arg2, - (struct stat *)arg3, (int)arg4)); + (struct stat *)arg3, (int)arg4)); #endif case 4: /* fchownat */ return (fchownat((int)arg1, (char *)arg2, - (uid_t)arg3, (gid_t)arg4, (int)arg5)); + (uid_t)arg3, (gid_t)arg4, (int)arg5)); case 5: /* unlinkat */ return (unlinkat((int)arg1, (char *)arg2, (int)arg3)); case 6: /* futimesat */ return (futimesat((int)arg1, - (char *)arg2, (struct timeval *)arg3)); + (char *)arg2, (struct timeval *)arg3)); case 7: /* renameat */ return (renameat((int)arg1, (char *)arg2, (int)arg3, - (char *)arg4)); + (char *)arg4)); case 8: /* accessat */ return (accessat((int)arg1, (char *)arg2, (int)arg3)); + case 9: /* openattrdirat */ + return (openattrdirat((int)arg1, (char *)arg2)); default: return (set_errno(EINVAL)); } @@ -139,27 +143,29 @@ fsat64(int code, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, case 0: /* openat */ return (openat((int)arg1, (char *)arg2, - (int)arg3, (int)arg4)); + (int)arg3, (int)arg4)); case 1: /* openat64 */ return (set_errno(ENOSYS)); case 2: /* fstatat64 */ return (set_errno(ENOSYS)); case 3: /* fstatat */ return (fstatat((int)arg1, (char *)arg2, - (struct stat *)arg3, (int)arg4)); + (struct stat *)arg3, (int)arg4)); case 4: /* fchownat */ return (fchownat((int)arg1, (char *)arg2, - (uid_t)arg3, (gid_t)arg4, (int)arg5)); + (uid_t)arg3, (gid_t)arg4, (int)arg5)); case 5: /* unlinkat */ return (unlinkat((int)arg1, (char *)arg2, (int)arg3)); case 6: /* futimesat */ return (futimesat((int)arg1, - (char *)arg2, (struct timeval *)arg3)); + (char *)arg2, (struct timeval *)arg3)); case 7: /* renameat */ return (renameat((int)arg1, (char *)arg2, (int)arg3, - (char *)arg4)); + (char *)arg4)); case 8: /* accessat */ return (accessat((int)arg1, (char *)arg2, (int)arg3)); + case 9: /* openattrdirat */ + return (openattrdirat((int)arg1, (char *)arg2)); default: return (set_errno(EINVAL)); } diff --git a/usr/src/uts/common/syscall/getdents.c b/usr/src/uts/common/syscall/getdents.c index a7d410d2a6bb..5daa9c39b422 100644 --- a/usr/src/uts/common/syscall/getdents.c +++ b/usr/src/uts/common/syscall/getdents.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -119,7 +118,7 @@ getdents32(int fd, void *buf, size_t count) auio.uio_fmode = 0; auio.uio_extflg = UIO_COPY_CACHED; (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); - error = VOP_READDIR(vp, &auio, fp->f_cred, &sink); + error = VOP_READDIR(vp, &auio, fp->f_cred, &sink, NULL, 0); VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); if (error) goto out; @@ -222,7 +221,7 @@ getdents64(int fd, void *buf, size_t count) auio.uio_fmode = 0; auio.uio_extflg = UIO_COPY_CACHED; (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); - error = VOP_READDIR(vp, &auio, fp->f_cred, &sink); + error = VOP_READDIR(vp, &auio, fp->f_cred, &sink, NULL, 0); VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); if (error) { releasef(fd); diff --git a/usr/src/uts/common/syscall/ioctl.c b/usr/src/uts/common/syscall/ioctl.c index c4b514d4deff..f3c068dc6b3d 100644 --- a/usr/src/uts/common/syscall/ioctl.c +++ b/usr/src/uts/common/syscall/ioctl.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2001 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -90,7 +89,7 @@ ioctl(int fdes, int cmd, intptr_t arg) int32_t offset; vattr.va_mask = AT_SIZE; - error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred); + error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL); if (error) { releasef(fdes); return (set_errno(error)); @@ -127,7 +126,7 @@ ioctl(int fdes, int cmd, intptr_t arg) * ioctl() now passes in the model information in some high bits. */ flag = fp->f_flag | get_udatamodel(); - error = VOP_IOCTL(fp->f_vnode, cmd, arg, flag, CRED(), &rv); + error = VOP_IOCTL(fp->f_vnode, cmd, arg, flag, CRED(), &rv, NULL); if (error != 0) { releasef(fdes); return (set_errno(error)); diff --git a/usr/src/uts/common/syscall/lseek.c b/usr/src/uts/common/syscall/lseek.c index d03687eb68d0..b5cc2e5d4e29 100644 --- a/usr/src/uts/common/syscall/lseek.c +++ b/usr/src/uts/common/syscall/lseek.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -113,7 +112,7 @@ lseek32_common(file_t *fp, int stype, offset_t off, offset_t max, case SEEK_END: vattr.va_mask = AT_SIZE; - if (error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred)) { + if (error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL)) { goto out; } if (reg && (off > (max - (offset_t)vattr.va_size))) { @@ -134,7 +133,7 @@ lseek32_common(file_t *fp, int stype, offset_t off, offset_t max, */ noff = (u_offset_t)off; error = VOP_IOCTL(vp, _FIO_SEEK_DATA, (intptr_t)(&noff), - FKIOCTL, kcred, NULL); + FKIOCTL, kcred, NULL, NULL); if (error) { if (error != ENOTTY) return (error); @@ -143,7 +142,7 @@ lseek32_common(file_t *fp, int stype, offset_t off, offset_t max, * "off" is not past the end of file */ vattr.va_mask = AT_SIZE; - error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred); + error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL); if (error) return (error); if (noff >= (u_offset_t)vattr.va_size) @@ -163,7 +162,7 @@ lseek32_common(file_t *fp, int stype, offset_t off, offset_t max, */ noff = (u_offset_t)off; error = VOP_IOCTL(vp, _FIO_SEEK_HOLE, (intptr_t)(&noff), - FKIOCTL, kcred, NULL); + FKIOCTL, kcred, NULL, NULL); if (error) { if (error != ENOTTY) return (error); @@ -172,7 +171,7 @@ lseek32_common(file_t *fp, int stype, offset_t off, offset_t max, * the "virtual hole" at the end of the file. */ vattr.va_mask = AT_SIZE; - error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred); + error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL); if (error) return (error); if (off < (offset_t)vattr.va_size) @@ -194,7 +193,7 @@ lseek32_common(file_t *fp, int stype, offset_t off, offset_t max, ASSERT((reg && noff <= max) || !reg); newoff = (offset_t)noff; - if ((error = VOP_SEEK(vp, curoff, &newoff)) == 0) { + if ((error = VOP_SEEK(vp, curoff, &newoff, NULL)) == 0) { fp->f_offset = newoff; (*retoff) = newoff; return (0); @@ -294,7 +293,7 @@ lseek64(int fdes, off_t off, int stype) case SEEK_END: vattr.va_mask = AT_SIZE; - if ((error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred)) != 0) + if ((error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL)) != 0) goto lseek64error; new_off += vattr.va_size; break; @@ -309,7 +308,7 @@ lseek64(int fdes, off_t off, int stype) */ new_off = (offset_t)off; error = VOP_IOCTL(vp, _FIO_SEEK_DATA, (intptr_t)(&new_off), - FKIOCTL, kcred, NULL); + FKIOCTL, kcred, NULL, NULL); if (error) { if (error != ENOTTY) { goto lseek64error; @@ -319,7 +318,7 @@ lseek64(int fdes, off_t off, int stype) * is not past end of file */ vattr.va_mask = AT_SIZE; - error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred); + error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL); if (error) goto lseek64error; if (new_off >= (offset_t)vattr.va_size) { @@ -338,7 +337,7 @@ lseek64(int fdes, off_t off, int stype) */ new_off = off; error = VOP_IOCTL(vp, _FIO_SEEK_HOLE, (intptr_t)(&new_off), - FKIOCTL, kcred, NULL); + FKIOCTL, kcred, NULL, NULL); if (error) { if (error != ENOTTY) goto lseek64error; @@ -347,7 +346,7 @@ lseek64(int fdes, off_t off, int stype) * the "virtual hole" at the end of the file. */ vattr.va_mask = AT_SIZE; - error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred); + error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL); if (error) goto lseek64error; if (off < (offset_t)vattr.va_size) { @@ -367,7 +366,7 @@ lseek64(int fdes, off_t off, int stype) } old_off = fp->f_offset; - if ((error = VOP_SEEK(vp, old_off, &new_off)) == 0) { + if ((error = VOP_SEEK(vp, old_off, &new_off, NULL)) == 0) { fp->f_offset = new_off; releasef(fdes); return (new_off); diff --git a/usr/src/uts/common/syscall/open.c b/usr/src/uts/common/syscall/open.c index f5b873bdb702..b45f7bf35ba3 100644 --- a/usr/src/uts/common/syscall/open.c +++ b/usr/src/uts/common/syscall/open.c @@ -49,6 +49,7 @@ #include #include #include +#include /* * Common code for open()/openat() and creat(). Check permissions, allocate @@ -66,6 +67,8 @@ copen(int startfd, char *fname, int filemode, int createmode) int fd, dupfd; vnode_t *startvp; proc_t *p = curproc; + uio_seg_t seg = UIO_USERSPACE; + char *open_filename = fname; if (startfd == AT_FDCWD) { /* @@ -95,7 +98,31 @@ copen(int startfd, char *fname, int filemode, int createmode) } } - if (filemode & FXATTR) { + /* + * Handle openattrdirat request + */ + if (filemode & FXATTRDIROPEN) { +#ifdef C2_AUDIT + if (audit_active) + audit_setfsat_path(1); +#endif /* C2_AUDIT */ + + if (error = lookupnameat(fname, seg, FOLLOW, + NULLVPP, &vp, startvp)) + return (set_errno(error)); + if (startvp) { + VN_RELE(startvp); + startvp = NULL; + } + + startvp = vp; + } + + /* + * Do we need to go into extended attribute space? + */ + if (filemode & (FXATTR|FXATTRDIROPEN)) { + vattr_t vattr; /* * Make sure we have a valid request. @@ -111,7 +138,7 @@ copen(int startfd, char *fname, int filemode, int createmode) goto out; } - if (startfd == AT_FDCWD) { + if (startfd == AT_FDCWD && !(filemode & FXATTRDIROPEN)) { mutex_enter(&p->p_lock); startvp = PTOU(p)->u_cdir; VN_HOLD(startvp); @@ -119,23 +146,34 @@ copen(int startfd, char *fname, int filemode, int createmode) } /* - * Verify permission to put attributes on file + * In order to access hidden attribute directory the + * user must be able to stat() the file */ - if ((VOP_ACCESS(startvp, VREAD, 0, CRED()) != 0) && - (VOP_ACCESS(startvp, VWRITE, 0, CRED()) != 0) && - (VOP_ACCESS(startvp, VEXEC, 0, CRED()) != 0)) { - error = EACCES; + vattr.va_mask = AT_ALL; + if (error = VOP_GETATTR(startvp, &vattr, 0, CRED(), NULL)) { pn_free(&pn); goto out; } - if ((startvp->v_vfsp->vfs_flag & VFS_XATTR) != 0) { + if ((startvp->v_vfsp->vfs_flag & VFS_XATTR) != 0 || + vfs_has_feature(startvp->v_vfsp, VFSFT_XVATTR)) { error = VOP_LOOKUP(startvp, "", &sdvp, &pn, - LOOKUP_XATTR|CREATE_XATTR_DIR, rootvp, CRED()); + LOOKUP_XATTR|CREATE_XATTR_DIR, rootvp, CRED(), + NULL, NULL, NULL); } else { error = EINVAL; } + + /* + * For openattrdirat use "." as filename to open + * as part of vn_openat() + */ + if (error == 0 && (filemode & FXATTRDIROPEN)) { + open_filename = "."; + seg = UIO_SYSSPACE; + } + pn_free(&pn); if (error != 0) goto out; @@ -144,7 +182,7 @@ copen(int startfd, char *fname, int filemode, int createmode) startvp = sdvp; } - if ((filemode & (FREAD|FWRITE)) != 0) { + if ((filemode & (FREAD|FWRITE|FXATTRDIROPEN)) != 0) { if ((filemode & (FNONBLOCK|FNDELAY)) == (FNONBLOCK|FNDELAY)) filemode &= ~FNDELAY; error = falloc((vnode_t *)NULL, filemode, &fp, &fd); @@ -157,9 +195,11 @@ copen(int startfd, char *fname, int filemode, int createmode) * Last arg is a don't-care term if * !(filemode & FCREAT). */ - error = vn_openat(fname, UIO_USERSPACE, filemode, - (int)(createmode & MODEMASK), &vp, CRCREAT, - PTOU(curproc)->u_cmask, startvp); + + error = vn_openat(open_filename, seg, filemode, + (int)(createmode & MODEMASK), + &vp, CRCREAT, PTOU(curproc)->u_cmask, + startvp, fd); if (startvp != NULL) VN_RELE(startvp); @@ -223,6 +263,7 @@ copen(int startfd, char *fname, int filemode, int createmode) #define OPENMODE32(fmode) ((int)((fmode)-FOPEN)) #define CREATMODE32 (FWRITE|FCREAT|FTRUNC) #define OPENMODE64(fmode) (OPENMODE32(fmode) | FOFFMAX) +#define OPENMODEATTRDIR FXATTRDIROPEN #define CREATMODE64 (CREATMODE32 | FOFFMAX) #ifdef _LP64 #define OPENMODE(fmode) OPENMODE64(fmode) @@ -301,4 +342,14 @@ openat32(int fd, char *path, int fmode, int cmode) { return (copen(fd, path, OPENMODE32(fmode), cmode)); } + #endif /* _SYSCALL32_IMPL */ + +/* + * Special interface to open hidden attribute directory. + */ +int +openattrdirat(int fd, char *fname) +{ + return (copen(fd, fname, OPENMODEATTRDIR, 0)); +} diff --git a/usr/src/uts/common/syscall/pathconf.c b/usr/src/uts/common/syscall/pathconf.c index f6b38d4d84fe..5ef8ce167541 100644 --- a/usr/src/uts/common/syscall/pathconf.c +++ b/usr/src/uts/common/syscall/pathconf.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -59,7 +59,7 @@ cpathconf(register vnode_t *vp, int cmd, struct cred *cr) switch (cmd) { case _PC_2_SYMLINKS: - if (error = VOP_PATHCONF(vp, _PC_SYMLINK_MAX, &val, cr)) + if (error = VOP_PATHCONF(vp, _PC_SYMLINK_MAX, &val, cr, NULL)) return ((long)set_errno(error)); return ((long)(val > 0)); @@ -77,7 +77,7 @@ cpathconf(register vnode_t *vp, int cmd, struct cred *cr) return ((long)set_errno(EINVAL)); case _PC_SYNC_IO: - if (!(error = VOP_FSYNC(vp, FSYNC, cr))) + if (!(error = VOP_FSYNC(vp, FSYNC, cr, NULL))) return (1l); return ((long)set_errno(error)); @@ -85,7 +85,7 @@ cpathconf(register vnode_t *vp, int cmd, struct cred *cr) return ((vp->v_vfsp->vfs_flag & VFS_XATTR) ? 1 : 0); default: - if (error = VOP_PATHCONF(vp, cmd, &val, cr)) + if (error = VOP_PATHCONF(vp, cmd, &val, cr, NULL)) return ((long)set_errno(error)); return (val); } diff --git a/usr/src/uts/common/syscall/pipe.c b/usr/src/uts/common/syscall/pipe.c index c980270a55b7..7721cd27641a 100644 --- a/usr/src/uts/common/syscall/pipe.c +++ b/usr/src/uts/common/syscall/pipe.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -142,7 +141,7 @@ pipe() if (error = fifo_stropen(&vp2, FWRITE|FREAD, fp2->f_cred, 0, 0)) { (void) VOP_CLOSE(vp1, FWRITE|FREAD, 1, (offset_t)0, - fp1->f_cred); + fp1->f_cred, NULL); goto out; } diff --git a/usr/src/uts/common/syscall/poll.c b/usr/src/uts/common/syscall/poll.c index c5ab2b83e4cc..2fdac673b3c4 100644 --- a/usr/src/uts/common/syscall/poll.c +++ b/usr/src/uts/common/syscall/poll.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -1264,7 +1264,7 @@ pcache_insert(pollstate_t *ps, file_t *fp, pollfd_t *pollfdp, int *fdcntp, */ ASSERT(curthread->t_pollcache == NULL); error = VOP_POLL(fp->f_vnode, pollfdp->events, 0, &pollfdp->revents, - &memphp); + &memphp, NULL); if (error) { return (error); } @@ -1979,7 +1979,7 @@ pcache_poll(pollfd_t *pollfdp, pollstate_t *ps, nfds_t nfds, int *fdcntp, */ ASSERT(curthread->t_pollcache == NULL); error = VOP_POLL(fp->f_vnode, pollfdp[entry].events, 0, - &pollfdp[entry].revents, &php); + &pollfdp[entry].revents, &php, NULL); /* * releasef after completely done with this cached * poll entry. To prevent close() coming in to clear @@ -2804,7 +2804,7 @@ plist_chkdupfd(file_t *fp, polldat_t *pdp, pollstate_t *psp, pollfd_t *pollfdp, ASSERT(curthread->t_pollcache == NULL); error = VOP_POLL(fp->f_vnode, pollfdp[i].events, 0, - &pollfdp[i].revents, &php); + &pollfdp[i].revents, &php, NULL); if (error) { return (error); } diff --git a/usr/src/uts/common/syscall/readlink.c b/usr/src/uts/common/syscall/readlink.c index aaeff14c0dcd..4c31e16d3f2d 100644 --- a/usr/src/uts/common/syscall/readlink.c +++ b/usr/src/uts/common/syscall/readlink.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -77,7 +77,7 @@ readlink(char *name, char *buf, size_t count) * object to look like a symlink at user-level. */ vattr.va_mask = AT_TYPE; - error = VOP_GETATTR(vp, &vattr, 0, CRED()); + error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL); if (error || vattr.va_type != VLNK) { VN_RELE(vp); if ((error == ESTALE) && @@ -94,7 +94,7 @@ readlink(char *name, char *buf, size_t count) auio.uio_segflg = UIO_USERSPACE; auio.uio_extflg = UIO_COPY_CACHED; auio.uio_resid = cnt; - error = VOP_READLINK(vp, &auio, CRED()); + error = VOP_READLINK(vp, &auio, CRED(), NULL); VN_RELE(vp); if (error) { if ((error == ESTALE) && fs_need_estale_retry(estale_retry++)) diff --git a/usr/src/uts/common/syscall/rw.c b/usr/src/uts/common/syscall/rw.c index 3eb98b50ac42..f614761e335d 100644 --- a/usr/src/uts/common/syscall/rw.c +++ b/usr/src/uts/common/syscall/rw.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -115,7 +115,8 @@ read(int fdes, void *cbuf, size_t count) error = nbl_svmand(vp, fp->f_cred, &svmand); if (error != 0) goto out; - if (nbl_conflict(vp, NBL_READ, fp->f_offset, cnt, svmand)) { + if (nbl_conflict(vp, NBL_READ, fp->f_offset, cnt, svmand, + NULL)) { error = EACCES; goto out; } @@ -138,7 +139,7 @@ read(int fdes, void *cbuf, size_t count) if (fileoff >= OFFSET_MAX(fp) && (vp->v_type == VREG)) { struct vattr va; va.va_mask = AT_SIZE; - if ((error = VOP_GETATTR(vp, &va, 0, fp->f_cred))) { + if ((error = VOP_GETATTR(vp, &va, 0, fp->f_cred, NULL))) { VOP_RWUNLOCK(vp, rwflag, NULL); goto out; } @@ -250,7 +251,8 @@ write(int fdes, void *cbuf, size_t count) error = nbl_svmand(vp, fp->f_cred, &svmand); if (error != 0) goto out; - if (nbl_conflict(vp, NBL_WRITE, fp->f_offset, cnt, svmand)) { + if (nbl_conflict(vp, NBL_WRITE, fp->f_offset, cnt, svmand, + NULL)) { error = EACCES; goto out; } @@ -402,7 +404,8 @@ pread(int fdes, void *cbuf, size_t count, off_t offset) error = nbl_svmand(vp, fp->f_cred, &svmand); if (error != 0) goto out; - if (nbl_conflict(vp, NBL_READ, fileoff, bcount, svmand)) { + if (nbl_conflict(vp, NBL_READ, fileoff, bcount, svmand, + NULL)) { error = EACCES; goto out; } @@ -414,7 +417,7 @@ pread(int fdes, void *cbuf, size_t count, off_t offset) if (vp->v_type == VREG && fileoff == (u_offset_t)maxoff) { struct vattr va; va.va_mask = AT_SIZE; - if ((error = VOP_GETATTR(vp, &va, 0, fp->f_cred))) { + if ((error = VOP_GETATTR(vp, &va, 0, fp->f_cred, NULL))) { VOP_RWUNLOCK(vp, rwflag, NULL); goto out; } @@ -555,7 +558,8 @@ pwrite(int fdes, void *cbuf, size_t count, off_t offset) error = nbl_svmand(vp, fp->f_cred, &svmand); if (error != 0) goto out; - if (nbl_conflict(vp, NBL_WRITE, fileoff, bcount, svmand)) { + if (nbl_conflict(vp, NBL_WRITE, fileoff, bcount, svmand, + NULL)) { error = EACCES; goto out; } @@ -682,7 +686,8 @@ readv(int fdes, struct iovec *iovp, int iovcnt) error = nbl_svmand(vp, fp->f_cred, &svmand); if (error != 0) goto out; - if (nbl_conflict(vp, NBL_READ, fp->f_offset, count, svmand)) { + if (nbl_conflict(vp, NBL_READ, fp->f_offset, count, svmand, + NULL)) { error = EACCES; goto out; } @@ -698,7 +703,7 @@ readv(int fdes, struct iovec *iovp, int iovcnt) if ((vp->v_type == VREG) && (fileoff >= OFFSET_MAX(fp))) { struct vattr va; va.va_mask = AT_SIZE; - if ((error = VOP_GETATTR(vp, &va, 0, fp->f_cred))) { + if ((error = VOP_GETATTR(vp, &va, 0, fp->f_cred, NULL))) { VOP_RWUNLOCK(vp, rwflag, NULL); goto out; } @@ -839,7 +844,8 @@ writev(int fdes, struct iovec *iovp, int iovcnt) error = nbl_svmand(vp, fp->f_cred, &svmand); if (error != 0) goto out; - if (nbl_conflict(vp, NBL_WRITE, fp->f_offset, count, svmand)) { + if (nbl_conflict(vp, NBL_WRITE, fp->f_offset, count, svmand, + NULL)) { error = EACCES; goto out; } @@ -980,7 +986,8 @@ pread64(int fdes, void *cbuf, size32_t count, uint32_t offset_1, error = nbl_svmand(vp, fp->f_cred, &svmand); if (error != 0) goto out; - if (nbl_conflict(vp, NBL_READ, fileoff, bcount, svmand)) { + if (nbl_conflict(vp, NBL_READ, fileoff, bcount, svmand, + NULL)) { error = EACCES; goto out; } @@ -1112,7 +1119,8 @@ pwrite64(int fdes, void *cbuf, size32_t count, uint32_t offset_1, error = nbl_svmand(vp, fp->f_cred, &svmand); if (error != 0) goto out; - if (nbl_conflict(vp, NBL_WRITE, fileoff, bcount, svmand)) { + if (nbl_conflict(vp, NBL_WRITE, fileoff, bcount, svmand, + NULL)) { error = EACCES; goto out; } diff --git a/usr/src/uts/common/syscall/sendfile.c b/usr/src/uts/common/syscall/sendfile.c index c9c14df59a62..72c1e8446572 100644 --- a/usr/src/uts/common/syscall/sendfile.c +++ b/usr/src/uts/common/syscall/sendfile.c @@ -916,7 +916,7 @@ sendvec_chunk(file_t *fp, u_offset_t *fileoff, struct sendfilevec *sfv, } readvp = ffp->f_vnode; - if (VOP_REALVP(readvp, &realvp) == 0) + if (VOP_REALVP(readvp, &realvp, NULL) == 0) readvp = realvp; if (readvp->v_type != VREG) { releasef(sfv->sfv_fd); diff --git a/usr/src/uts/common/syscall/stat.c b/usr/src/uts/common/syscall/stat.c index 33403cf89eaf..c559747e2f92 100644 --- a/usr/src/uts/common/syscall/stat.c +++ b/usr/src/uts/common/syscall/stat.c @@ -252,7 +252,7 @@ cstat(vnode_t *vp, struct stat *ubp, int flag, cred_t *cr) int error; vattr.va_mask = AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE; - if ((error = VOP_GETATTR(vp, &vattr, flag, cr)) != 0) + if ((error = VOP_GETATTR(vp, &vattr, flag, cr, NULL)) != 0) return (error); #ifdef _ILP32 /* @@ -339,7 +339,6 @@ cstatat(int fd, char *name, int nmflag, struct stat *sb, int follow, int flags) */ static int cstatat32(int, char *, int, struct stat32 *, int, int); static int cstat32(vnode_t *, struct stat32 *, int, cred_t *); - int stat32(char *fname, struct stat32 *sb) { @@ -401,7 +400,7 @@ cstat32(vnode_t *vp, struct stat32 *ubp, int flag, struct cred *cr) dev32_t st_dev, st_rdev; vattr.va_mask = AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE; - if (error = VOP_GETATTR(vp, &vattr, flag, cr)) + if (error = VOP_GETATTR(vp, &vattr, flag, cr, NULL)) return (error); /* devices are a special case, see comments in cstat */ @@ -521,7 +520,7 @@ cstat64(vnode_t *vp, struct stat64 *ubp, int flag, cred_t *cr) int error; vattr.va_mask = AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE; - if (error = VOP_GETATTR(vp, &vattr, flag, cr)) + if (error = VOP_GETATTR(vp, &vattr, flag, cr, NULL)) return (error); bzero(&lsb, sizeof (lsb)); @@ -627,7 +626,7 @@ cstat64_32(vnode_t *vp, struct stat64_32 *ubp, int flag, cred_t *cr) dev32_t st_dev, st_rdev; vattr.va_mask = AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE; - if (error = VOP_GETATTR(vp, &vattr, flag, cr)) + if (error = VOP_GETATTR(vp, &vattr, flag, cr, NULL)) return (error); if (!cmpldev(&st_dev, vattr.va_fsid) || diff --git a/usr/src/uts/common/syscall/symlink.c b/usr/src/uts/common/syscall/symlink.c index 915fc9d050c7..1db42e474e26 100644 --- a/usr/src/uts/common/syscall/symlink.c +++ b/usr/src/uts/common/syscall/symlink.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -83,7 +83,7 @@ symlink(char *target, char *linkname) vattr.va_mode = 0777; vattr.va_mask = AT_TYPE|AT_MODE; error = VOP_SYMLINK(dvp, lpn.pn_path, &vattr, - tbuf, CRED()); + tbuf, CRED(), NULL, 0); #ifdef C2_AUDIT if (audit_active) audit_symlink_create(dvp, lpn.pn_path, diff --git a/usr/src/uts/common/syscall/ucredsys.c b/usr/src/uts/common/syscall/ucredsys.c index 38d13884c735..0f5c0e6303ba 100644 --- a/usr/src/uts/common/syscall/ucredsys.c +++ b/usr/src/uts/common/syscall/ucredsys.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -66,7 +66,7 @@ getpeerucred(int fd, void *buf) case VFIFO: case VSOCK: err = VOP_IOCTL(vp, _I_GETPEERCRED, (intptr_t)&kpc, - FKIOCTL, CRED(), &rval); + FKIOCTL, CRED(), &rval, NULL); break; case VCHR: { struct strioctl strioc; diff --git a/usr/src/uts/common/vm/seg_dev.c b/usr/src/uts/common/vm/seg_dev.c index f24a1d46edac..02e2306703a7 100644 --- a/usr/src/uts/common/vm/seg_dev.c +++ b/usr/src/uts/common/vm/seg_dev.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -422,7 +422,7 @@ segdev_create(struct seg *seg, void *argsp) */ error = VOP_ADDMAP(VTOCVP(sdp->vp), sdp->offset, seg->s_as, seg->s_base, seg->s_size, - sdp->prot, sdp->maxprot, sdp->type, CRED()); + sdp->prot, sdp->maxprot, sdp->type, CRED(), NULL); if (error != 0) { sdp->devmap_data = NULL; @@ -522,7 +522,7 @@ segdev_dup(struct seg *seg, struct seg *newseg) return (VOP_ADDMAP(VTOCVP(newsdp->vp), newsdp->offset, newseg->s_as, newseg->s_base, newseg->s_size, newsdp->prot, - newsdp->maxprot, sdp->type, CRED())); + newsdp->maxprot, sdp->type, CRED(), NULL)); } /* @@ -707,7 +707,7 @@ segdev_unmap(struct seg *seg, caddr_t addr, size_t len) */ ASSERT(sdp->vp != NULL); (void) VOP_DELMAP(VTOCVP(sdp->vp), off, seg->s_as, addr, len, - sdp->prot, sdp->maxprot, sdp->type, CRED()); + sdp->prot, sdp->maxprot, sdp->type, CRED(), NULL); /* * Check for entire segment @@ -2889,7 +2889,7 @@ devmap_get_large_pgsize(devmap_handle_t *dhp, size_t len, caddr_t addr, *llen += pgsize; off = ptob(pfn - dhp->dh_pfn) + pgsize; } - /* Large page mapping len/addr cover more range than orginal fault */ + /* Large page mapping len/addr cover more range than original fault */ ASSERT(*llen >= len && *laddr <= addr); ASSERT((*laddr + *llen) >= (addr + len)); } @@ -3799,7 +3799,7 @@ devmap_alloc_pages(vmem_t *vmp, size_t size, int vmflag) } /* - * This is where things are a bit incestrous with seg_kmem: unlike + * This is where things are a bit incestuous with seg_kmem: unlike * seg_kp, seg_kmem does not keep its pages long-term sharelocked, so * we need to do a bit of a dance around that to prevent duplication of * code until we decide to bite the bullet and implement a new kernel @@ -3851,7 +3851,7 @@ devmap_free_pages(vmem_t *vmp, void *inaddr, size_t size) * default request. For now we allocate our own pages and we keep * them long-term sharelocked, since: A) the fault routines expect the * memory to already be locked; B) pageable umem is already long-term - * locked; C) it's a lot of work to make it otherwise, particuarly + * locked; C) it's a lot of work to make it otherwise, particularly * since the nexus layer expects the pages to never fault. An RFE is to * not keep the pages long-term locked, but instead to be able to * take faults on them and simply look them up in kvp in case we diff --git a/usr/src/uts/common/vm/seg_kp.c b/usr/src/uts/common/vm/seg_kp.c index d58e873a1969..d65b3062bc06 100644 --- a/usr/src/uts/common/vm/seg_kp.c +++ b/usr/src/uts/common/vm/seg_kp.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -536,7 +536,7 @@ segkp_get_internal( */ err = VOP_GETPAGE(vp, (offset_t)off, PAGESIZE, NULL, pl, PAGESIZE, seg, va, S_CREATE, - kcred); + kcred, NULL); if (err) { /* * XXX - This should not fail. @@ -1129,7 +1129,7 @@ segkp_load( * which is returned to us already kept. */ err = VOP_GETPAGE(vp, (offset_t)off, PAGESIZE, NULL, - pl, PAGESIZE, seg, va, S_READ, kcred); + pl, PAGESIZE, seg, va, S_READ, kcred, NULL); if (err) { /* @@ -1242,7 +1242,7 @@ segkp_unlock( * use kcred. */ (void) VOP_PUTPAGE(vp, (offset_t)off, PAGESIZE, - B_ASYNC | B_FREE, kcred); + B_ASYNC | B_FREE, kcred, NULL); VN_RELE(vp); } else { page_unlock(pp); diff --git a/usr/src/uts/common/vm/seg_map.c b/usr/src/uts/common/vm/seg_map.c index de27f6e2fff7..ad18a2cb317e 100644 --- a/usr/src/uts/common/vm/seg_map.c +++ b/usr/src/uts/common/vm/seg_map.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -707,7 +707,7 @@ segmap_fault( TRACE_3(TR_FAC_VM, TR_SEGMAP_GETPAGE, "segmap_getpage:seg %p addr %p vp %p", seg, addr, vp); err = VOP_GETPAGE(vp, (offset_t)off, len, &prot, pl, MAXBSIZE, - seg, addr, rw, CRED()); + seg, addr, rw, CRED(), NULL); if (err) return (FC_MAKE_ERR(err)); @@ -829,7 +829,7 @@ segmap_faulta(struct seg *seg, caddr_t addr) err = VOP_GETPAGE(vp, (offset_t)(off + ((offset_t)((uintptr_t)addr & MAXBOFFSET))), PAGESIZE, (uint_t *)NULL, (page_t **)NULL, 0, - seg, addr, S_READ, CRED()); + seg, addr, S_READ, CRED(), NULL); if (err) return (FC_MAKE_ERR(err)); @@ -1369,7 +1369,7 @@ get_free_smp(int free_ndx) */ /* - * Create pages (without using VOP_GETPAGE) and load up tranlations to them. + * Create pages (without using VOP_GETPAGE) and load up translations to them. * If softlock is TRUE, then set things up so that it looks like a call * to segmap_fault with F_SOFTLOCK. * @@ -1455,7 +1455,7 @@ segmap_pagecreate(struct seg *seg, caddr_t addr, size_t len, int softlock) * "exclusive" lock will not be dropped to prevent * other users from accessing the page. We also * have to lock the translation to prevent a fault - * from occuring when the virtual address mapped by + * from occurring when the virtual address mapped by * this page is written into. This is necessary to * avoid a deadlock since we haven't dropped the * "exclusive" lock. @@ -1898,7 +1898,7 @@ segmap_getmapflt( base = segkpm_create_va(baseoff); error = VOP_GETPAGE(vp, (offset_t)baseoff, len, &prot, pl, MAXBSIZE, - seg, base, rw, CRED()); + seg, base, rw, CRED(), NULL); pp = pl[0]; if (error || pp == NULL) { @@ -2010,7 +2010,7 @@ segmap_release(struct seg *seg, caddr_t addr, uint_t flags) smtx = SMAPMTX(smp); /* - * For compatibilty reasons segmap_pagecreate_kpm sets this + * For compatibility reasons segmap_pagecreate_kpm sets this * flag to allow a following segmap_pagecreate to return * this as "newpage" flag. When segmap_pagecreate is not * called at all we clear it now. @@ -2117,7 +2117,7 @@ segmap_release(struct seg *seg, caddr_t addr, uint_t flags) */ if ((flags & ~SM_DONTNEED) != 0) { error = VOP_PUTPAGE(vp, offset, MAXBSIZE, - bflags, CRED()); + bflags, CRED(), NULL); } else { error = 0; } diff --git a/usr/src/uts/common/vm/seg_vn.c b/usr/src/uts/common/vm/seg_vn.c index b7fe79806106..e3f67cf336cd 100644 --- a/usr/src/uts/common/vm/seg_vn.c +++ b/usr/src/uts/common/vm/seg_vn.c @@ -466,7 +466,7 @@ segvn_setvnode_mpss(vnode_t *vp) if (vp->v_mpssdata == NULL) { if (vn_vmpss_usepageio(vp)) { err = VOP_PAGEIO(vp, (page_t *)NULL, - (u_offset_t)0, 0, 0, CRED()); + (u_offset_t)0, 0, 0, CRED(), NULL); } else { err = ENOSYS; } @@ -594,7 +594,7 @@ segvn_create(struct seg *seg, void *argsp) if (a->vp != NULL) { error = VOP_ADDMAP(a->vp, a->offset & PAGEMASK, seg->s_as, seg->s_base, seg->s_size, a->prot, - a->maxprot, a->type, cred); + a->maxprot, a->type, cred, NULL); if (error) { if (swresv != 0) { anon_unresv(swresv); @@ -1614,7 +1614,7 @@ segvn_dup(struct seg *seg, struct seg *newseg) if (newsvd->vp != NULL) { error = VOP_ADDMAP(newsvd->vp, (offset_t)newsvd->offset, newseg->s_as, newseg->s_base, newseg->s_size, newsvd->prot, - newsvd->maxprot, newsvd->type, newsvd->cred); + newsvd->maxprot, newsvd->type, newsvd->cred, NULL); } out: if (error == 0 && HAT_IS_REGION_COOKIE_VALID(svd->rcookie)) { @@ -1771,7 +1771,7 @@ segvn_unmap(struct seg *seg, caddr_t addr, size_t len) error = VOP_DELMAP(svd->vp, (offset_t)svd->offset + (uintptr_t)(addr - seg->s_base), seg->s_as, addr, len, svd->prot, svd->maxprot, - svd->type, svd->cred); + svd->type, svd->cred, NULL); if (error == EAGAIN) return (error); @@ -2265,7 +2265,7 @@ ulong_t segvn_lpglck_limit = 0; * Support routines used by segvn_pagelock() and softlock faults for anonymous * pages to implement availrmem accounting in a way that makes sure the * same memory is accounted just once for all softlock/pagelock purposes. - * This prevents a bug when availrmem is quickly incorrectly exausted from + * This prevents a bug when availrmem is quickly incorrectly exhausted from * several pagelocks to different parts of the same large page since each * pagelock has to decrement availrmem by the size of the entire large * page. Note those pages are not COW shared until softunlock/pageunlock so @@ -2274,7 +2274,7 @@ ulong_t segvn_lpglck_limit = 0; * entire large page because large anon pages can't be demoted when any of * constituent pages is locked. The caller calls this routine for every page_t * it locks. The very first page in the range may not be the root page of a - * large page. For all other pages it's guranteed we are going to visit the + * large page. For all other pages it's guaranteed we are going to visit the * root of a particular large page before any other constituent page as we are * locking sequential pages belonging to the same anon map. So we do all the * locking when the root is encountered except for the very first page. Since @@ -3255,9 +3255,9 @@ segvn_full_szcpages(page_t **ppa, uint_t szc, int *upgrdfail, uint_t *pszc) * page_size(szc)) range and for private segment return them in ppa array. * Pages are created either via IO or relocations. * - * Return 1 on sucess and 0 on failure. + * Return 1 on success and 0 on failure. * - * If physically contiguos pages already exist for this range return 1 without + * If physically contiguous pages already exist for this range return 1 without * filling ppa array. Caller initializes ppa[0] as NULL to detect that ppa * array wasn't filled. In this case caller fills ppa array via VOP_GETPAGE(). */ @@ -3394,7 +3394,7 @@ segvn_fill_vp_pages(struct segvn_data *svd, vnode_t *vp, u_offset_t off, * XXX fix NFS to remove this check. */ va.va_mask = AT_SIZE; - if (VOP_GETATTR(vp, &va, ATTR_HINT, svd->cred) != 0) { + if (VOP_GETATTR(vp, &va, ATTR_HINT, svd->cred, NULL)) { VM_STAT_ADD(segvnvmstats.fill_vp_pages[6]); page_unlock(targpp); goto out; @@ -3407,7 +3407,7 @@ segvn_fill_vp_pages(struct segvn_data *svd, vnode_t *vp, u_offset_t off, goto out; } io_err = VOP_PAGEIO(vp, io_pplist, io_off, io_len, - B_READ, svd->cred); + B_READ, svd->cred, NULL); if (io_err) { VM_STAT_ADD(segvnvmstats.fill_vp_pages[8]); page_unlock(targpp); @@ -3464,7 +3464,7 @@ segvn_fill_vp_pages(struct segvn_data *svd, vnode_t *vp, u_offset_t off, VM_STAT_ADD(segvnvmstats.fill_vp_pages[12]); io_len = eoff - io_off; va.va_mask = AT_SIZE; - if (VOP_GETATTR(vp, &va, ATTR_HINT, svd->cred) != 0) { + if (VOP_GETATTR(vp, &va, ATTR_HINT, svd->cred, NULL) != 0) { VM_STAT_ADD(segvnvmstats.fill_vp_pages[13]); goto out; } @@ -3475,7 +3475,7 @@ segvn_fill_vp_pages(struct segvn_data *svd, vnode_t *vp, u_offset_t off, goto out; } io_err = VOP_PAGEIO(vp, io_pplist, io_off, io_len, - B_READ, svd->cred); + B_READ, svd->cred, NULL); if (io_err) { VM_STAT_ADD(segvnvmstats.fill_vp_pages[15]); if (io_err == EDEADLK) { @@ -3966,7 +3966,7 @@ segvn_fault_vnodepages(struct hat *hat, struct seg *seg, caddr_t lpgaddr, ppa[0] = NULL; ierr = VOP_GETPAGE(vp, (offset_t)off, pgsz, &vpprot, ppa, pgsz, seg, a, arw, - svd->cred); + svd->cred, NULL); #ifdef DEBUG if (ierr == 0) { for (i = 0; i < pages; i++) { @@ -4003,7 +4003,7 @@ segvn_fault_vnodepages(struct hat *hat, struct seg *seg, caddr_t lpgaddr, goto out; } va.va_mask = AT_SIZE; - if (VOP_GETATTR(vp, &va, 0, svd->cred) != 0) { + if (VOP_GETATTR(vp, &va, 0, svd->cred, NULL)) { SEGVN_VMSTAT_FLTVNPAGES(20); err = FC_MAKE_ERR(EIO); goto out; @@ -5313,7 +5313,7 @@ segvn_fault(struct hat *hat, struct seg *seg, caddr_t addr, size_t len, (void) VOP_PUTPAGE(fvp, (offset_t)fpgoff, PAGESIZE, (B_DONTNEED|B_FREE|B_ASYNC), - svd->cred); + svd->cred, NULL); VN_RELE(fvp); } else { /* @@ -5429,7 +5429,7 @@ segvn_fault(struct hat *hat, struct seg *seg, caddr_t addr, size_t len, seg, addr, vp); err = VOP_GETPAGE(vp, (offset_t)vp_off, vp_len, &vpprot, plp, plsz, seg, addr + (vp_off - off), arw, - svd->cred); + svd->cred, NULL); if (err) { SEGVN_LOCK_EXIT(seg->s_as, &svd->lock); segvn_pagelist_rele(plp); @@ -5661,7 +5661,7 @@ segvn_faulta(struct seg *seg, caddr_t addr) err = VOP_GETPAGE(vp, (offset_t)(svd->offset + (uintptr_t)(addr - seg->s_base)), PAGESIZE, NULL, NULL, 0, seg, addr, - S_OTHER, svd->cred); + S_OTHER, svd->cred, NULL); SEGVN_LOCK_EXIT(seg->s_as, &svd->lock); if (err) @@ -5745,7 +5745,7 @@ segvn_setprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot) /* * If we are holding the as lock as a reader then * we need to return IE_RETRY and let the as - * layer drop and re-aquire the lock as a writer. + * layer drop and re-acquire the lock as a writer. */ if (AS_READ_HELD(seg->s_as, &seg->s_as->a_lock)) return (IE_RETRY); @@ -6207,7 +6207,7 @@ segvn_setpagesize(struct seg *seg, caddr_t addr, size_t len, uint_t szc) va.va_mask = AT_SIZE; eoffpage += seg->s_size; eoffpage = btopr(eoffpage); - if (VOP_GETATTR(svd->vp, &va, 0, svd->cred) != 0) { + if (VOP_GETATTR(svd->vp, &va, 0, svd->cred, NULL) != 0) { segvn_setpgsz_getattr_err++; return (EINVAL); } @@ -6451,7 +6451,7 @@ segvn_claim_pages( } /* - * Returns right (upper address) segment if split occured. + * Returns right (upper address) segment if split occurred. * If the address is equal to the beginning or end of its segment it returns * the current segment. */ @@ -6920,7 +6920,7 @@ segvn_kluster(struct seg *seg, caddr_t addr, ssize_t delta) swap_xlate(oap, &vp2, &off2); - if (!VOP_CMP(vp1, vp2) || off1 - off2 != delta) + if (!VOP_CMP(vp1, vp2, NULL) || off1 - off2 != delta) return (-1); return (0); } @@ -7194,7 +7194,7 @@ segvn_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags) * is not on, just use one big request. */ err = VOP_PUTPAGE(svd->vp, (offset_t)offset, len, - bflags, svd->cred); + bflags, svd->cred, NULL); SEGVN_LOCK_EXIT(seg->s_as, &svd->lock); return (err); } @@ -7270,7 +7270,7 @@ segvn_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags) } } else if (svd->type == MAP_SHARED && amp != NULL) { /* - * Avoid writting out to disk ISM's large pages + * Avoid writing out to disk ISM's large pages * because segspt_free_pages() relies on NULL an_pvp * of anon slots of such pages. */ @@ -7303,7 +7303,7 @@ segvn_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags) */ VN_HOLD(vp); err = VOP_PUTPAGE(vp, (offset_t)off, PAGESIZE, - bflags, svd->cred); + bflags, svd->cred, NULL); VN_RELE(vp); if (err) break; @@ -7734,7 +7734,7 @@ segvn_lockop(struct seg *seg, caddr_t addr, size_t len, error = VOP_GETPAGE(vp, (offset_t)off, PAGESIZE, (uint_t *)NULL, pl, PAGESIZE, seg, addr, - S_OTHER, svd->cred); + S_OTHER, svd->cred, NULL); /* * If the error is EDEADLK then we must bounce @@ -7759,7 +7759,7 @@ segvn_lockop(struct seg *seg, caddr_t addr, size_t len, if (error && svd->vp) { va.va_mask = AT_SIZE; if (VOP_GETATTR(svd->vp, &va, 0, - svd->cred) != 0) { + svd->cred, NULL) != 0) { err = EIO; goto out; } @@ -9007,7 +9007,7 @@ segvn_textrepl(struct seg *seg) * If VOP_GETATTR() call fails bail out. */ va.va_mask = AT_SIZE | AT_MTIME | AT_CTIME; - if (VOP_GETATTR(vp, &va, 0, svd->cred) != 0) { + if (VOP_GETATTR(vp, &va, 0, svd->cred, NULL) != 0) { svd->tr_state = SEGVN_TR_OFF; SEGVN_TR_ADDSTAT(gaerr); return; @@ -9337,7 +9337,7 @@ segvn_textunrepl(struct seg *seg, int unload_unmap) } /* - * This is called when a MAP_SHARED writabble mapping is created to a vnode + * This is called when a MAP_SHARED writable mapping is created to a vnode * that is currently used for execution (VVMEXEC flag is set). In this case we * need to prevent further use of existing replicas. */ diff --git a/usr/src/uts/common/vm/vm_anon.c b/usr/src/uts/common/vm/vm_anon.c index 58b505f0a674..10f6ccafa122 100644 --- a/usr/src/uts/common/vm/vm_anon.c +++ b/usr/src/uts/common/vm/vm_anon.c @@ -1418,7 +1418,8 @@ anon_fill_cow_holes( page_t *pp; err = VOP_GETPAGE(vp, vp_off, PAGESIZE, NULL, - pl, PAGESIZE, seg, addr, S_READ, cred); + pl, PAGESIZE, seg, addr, S_READ, cred, + NULL); if (err) { break; } @@ -1770,7 +1771,7 @@ anon_getpage( seg, addr, vp); err = VOP_GETPAGE(vp, (u_offset_t)off, PAGESIZE, protp, pl, plsz, - seg, addr, rw, cred); + seg, addr, rw, cred, NULL); if (err == 0 && pl != NULL) { ahm = &anonhash_lock[AH_LOCK(ap->an_vp, ap->an_off)]; @@ -1787,8 +1788,8 @@ anon_getpage( * page cannot be allocated. returns -2 if some other process has allocated a * larger page. * - * For cowfault it will alocate any size pages to fill the requested area to - * avoid partially overwritting anon slots (i.e. sharing only some of the anon + * For cowfault it will allocate any size pages to fill the requested area to + * avoid partially overwriting anon slots (i.e. sharing only some of the anon * slots within a large page with other processes). This policy greatly * simplifies large page freeing (which is only freed when all anon slot * refcnts are 0). @@ -2271,7 +2272,7 @@ anon_private( * vnode at the same time. */ err = VOP_GETPAGE(vp, (u_offset_t)off, PAGESIZE, NULL, - anon_pl, PAGESIZE, seg, addr, S_CREATE, cred); + anon_pl, PAGESIZE, seg, addr, S_CREATE, cred, NULL); if (err) goto out; @@ -2640,7 +2641,7 @@ anon_zero(struct seg *seg, caddr_t addr, struct anon **app, struct cred *cred) * the vnode at the same time since it is locked. */ err = VOP_GETPAGE(vp, off, PAGESIZE, NULL, - anon_pl, PAGESIZE, seg, addr, S_CREATE, cred); + anon_pl, PAGESIZE, seg, addr, S_CREATE, cred, NULL); if (err) { *app = NULL; anon_decref(ap); @@ -3109,7 +3110,7 @@ anon_map_demotepages( * have at most one reference at this point. This means underlying pages can * be exclusively locked and demoted or freed. If not freeing the entire * large pages demote the ends of the region we free to be able to free - * subpages. Page roots correspend to aligned index positions in anon map. + * subpages. Page roots correspond to aligned index positions in anon map. */ void anon_shmap_free_pages(struct anon_map *amp, ulong_t sidx, size_t len) @@ -3216,7 +3217,7 @@ anonmap_free(struct anon_map *amp) /* * Returns true if the app array has some empty slots. - * The offp and lenp paramters are in/out paramters. On entry + * The offp and lenp parameters are in/out parameters. On entry * these values represent the starting offset and length of the * mapping. When true is returned, these values may be modified * to be the largest range which includes empty slots. @@ -3272,7 +3273,7 @@ anon_pages(struct anon_hdr *ahp, ulong_t anon_index, pgcnt_t nslots) /* * Move reserved phys swap into memory swap (unreserve phys swap * and reserve mem swap by the same amount). - * Used by segspt when it needs to lock resrved swap npages in memory + * Used by segspt when it needs to lock reserved swap npages in memory */ int anon_swap_adjust(pgcnt_t npages) diff --git a/usr/src/uts/common/vm/vm_as.c b/usr/src/uts/common/vm/vm_as.c index d7ed7da8ec45..86d7bc982f68 100644 --- a/usr/src/uts/common/vm/vm_as.c +++ b/usr/src/uts/common/vm/vm_as.c @@ -1160,7 +1160,7 @@ as_setprot(struct as *as, caddr_t addr, size_t size, uint_t prot) /* * Normally we only lock the as as a reader. But * if due to setprot the segment driver needs to split - * a segment it will return IE_RETRY. Therefore we re-aquire + * a segment it will return IE_RETRY. Therefore we re-acquire * the as lock as a writer so the segment driver can change * the seg list. Also the segment driver will return IE_RETRY * after it has changed the segment list so we therefore keep @@ -1602,7 +1602,7 @@ as_map_vnsegs(struct as *as, caddr_t addr, size_t size, } va.va_mask = AT_SIZE; - if (VOP_GETATTR(vn_a->vp, &va, ATTR_HINT, vn_a->cred) != 0) { + if (VOP_GETATTR(vn_a->vp, &va, ATTR_HINT, vn_a->cred, NULL) != 0) { szcvec = 0; goto again; } diff --git a/usr/src/uts/common/vm/vm_page.c b/usr/src/uts/common/vm/vm_page.c index c725b13f172f..bb1adbe42af1 100644 --- a/usr/src/uts/common/vm/vm_page.c +++ b/usr/src/uts/common/vm/vm_page.c @@ -109,11 +109,11 @@ pgcnt_t availrmem_initial; * segvn_pages_locked : This keeps track on a global basis how many pages * are currently locked because of I/O. * - * pages_locked : How many pages are locked becuase of user specified + * pages_locked : How many pages are locked because of user specified * locking through mlock or plock. * * pages_useclaim,pages_claimed : These two variables track the - * cliam adjustments because of the protection changes on a segvn segment. + * claim adjustments because of the protection changes on a segvn segment. * * All these globals are protected by the same lock which protects availrmem. */ @@ -503,7 +503,7 @@ page_free_large_ctr(pgcnt_t npages) } /* - * Add a physical chunk of memory to the system freee lists during startup. + * Add a physical chunk of memory to the system free lists during startup. * Platform specific startup() allocates the memory for the page structs. * * num - number of page structures @@ -670,7 +670,7 @@ page_lookup(vnode_t *vp, u_offset_t off, se_t se) * Find a page representing the specified [vp, offset]. * We either return the one we found or, if passed in, * create one with identity of [vp, offset] of the - * pre-allocated page. If we find exsisting page but it is + * pre-allocated page. If we find existing page but it is * intransit coming in, it will have an "exclusive" lock * and we wait for the i/o to complete. A page found on * the free list is always reclaimed and then locked. @@ -1466,7 +1466,7 @@ page_create_throttle(pgcnt_t npages, int flags) } /* - * page_create_wait() is called to either coalecse pages from the + * page_create_wait() is called to either coalesce pages from the * different pcf buckets or to wait because there simply are not * enough pages to satisfy the caller's request. * @@ -2410,7 +2410,7 @@ page_create_va(vnode_t *vp, u_offset_t off, size_t bytes, uint_t flags, if (!enough) { /* * Have to look harder. If npages is greater than - * one, then we might have to coalecse the counters. + * one, then we might have to coalesce the counters. * * Go wait. We come back having accounted * for the memory. @@ -4394,7 +4394,7 @@ page_busy(int cleanit) VN_HOLD(vp); page_unlock(pp); (void) VOP_PUTPAGE(vp, off, PAGESIZE, - B_ASYNC | B_FREE, kcred); + B_ASYNC | B_FREE, kcred, NULL); VN_RELE(vp); } } while ((pp = page_next(pp)) != page0); @@ -4527,7 +4527,7 @@ page_invalidate_pages() * if this putpage fails. */ (void) VOP_PUTPAGE(vp, offset, PAGESIZE, B_INVAL, - kcred); + kcred, NULL); VN_RELE(vp); } else { page_destroy(pp, 0); @@ -4542,7 +4542,7 @@ page_invalidate_pages() /* * Replace the page "old" with the page "new" on the page hash and vnode lists * - * the replacemnt must be done in place, ie the equivalent sequence: + * the replacement must be done in place, ie the equivalent sequence: * * vp = old->p_vnode; * off = old->p_offset; @@ -4690,7 +4690,7 @@ page_relocate_hash(page_t *pp_new, page_t *pp_old) * * Returns 1 on success, 0 on failure. * - * If success is returned this routine gurantees p_szc for all constituent + * If success is returned this routine guarantees p_szc for all constituent * pages of a large page pp belongs to can't change. To achieve this we * recheck szc of pp after locking all constituent pages and retry if szc * changed (it could only decrease). Since hat_page_demote() needs an EXCL @@ -4698,7 +4698,7 @@ page_relocate_hash(page_t *pp_new, page_t *pp_old) * pages are locked. hat_page_demote() with a lock on a constituent page * outside of this large page (i.e. pp belonged to a larger large page) is * already done with all constituent pages of pp since the root's p_szc is - * changed last. Thefore no need to synchronize with hat_page_demote() that + * changed last. Therefore no need to synchronize with hat_page_demote() that * locked a constituent page outside of pp's current large page. */ #ifdef DEBUG @@ -5335,7 +5335,7 @@ page_try_demote_pages(page_t *pp) * cache). However file system pages can be truncated or invalidated at a * PAGESIZE level from the file system side and end up in page_free() or * page_destroy() (we also allow only part of the large page to be SOFTLOCKed - * and therfore pageout should be able to demote a large page by EXCL locking + * and therefore pageout should be able to demote a large page by EXCL locking * any constituent page that is not under SOFTLOCK). In those cases we cannot * rely on being able to lock EXCL all constituent pages. * @@ -5354,9 +5354,9 @@ page_try_demote_pages(page_t *pp) * * This routine calls page_szc_lock() before calling hat_page_demote() to * allow segvn in one special case not to lock all constituent pages SHARED - * before calling hat_memload_array() that relies on p_szc not changeing even + * before calling hat_memload_array() that relies on p_szc not changing even * before hat level mlist lock is taken. In that case segvn uses - * page_szc_lock() to prevent hat_page_demote() changeing p_szc values. + * page_szc_lock() to prevent hat_page_demote() changing p_szc values. * * Anonymous or kernel page demotion still has to lock all pages exclusively * and do hat_pageunload() on all constituent pages before demoting the page @@ -5371,7 +5371,7 @@ page_try_demote_pages(page_t *pp) * pages within szc 1 area to prevent szc changes because hat_page_demote() * that started on this page when it had szc > 1 is done for this szc 1 area. * - * We are guranteed that all constituent pages of pp's large page belong to + * We are guaranteed that all constituent pages of pp's large page belong to * the same vnode with the consecutive offsets increasing in the direction of * the pfn i.e. the identity of constituent pages can't change until their * p_szc is decreased. Therefore it's safe for hat_page_demote() to remove @@ -5443,7 +5443,7 @@ page_mark_migrate(struct seg *seg, caddr_t addr, size_t len, len = P2ROUNDUP(len, segpgsz); /* - * Allocate page array to accomodate largest page size + * Allocate page array to accommodate largest page size */ pgsz = page_get_pagesize(page_num_pagesizes() - 1); ppa_nentries = btop(pgsz); diff --git a/usr/src/uts/common/vm/vm_rm.c b/usr/src/uts/common/vm/vm_rm.c index 36cd5f0375c8..eb920214f7ff 100644 --- a/usr/src/uts/common/vm/vm_rm.c +++ b/usr/src/uts/common/vm/vm_rm.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -118,7 +117,8 @@ rm_assize(struct as *as) if (seg->s_ops == &segvn_ops && SEGOP_GETVP(seg, addr, &vp) == 0 && vp != NULL && vp->v_type == VREG && - VOP_GETATTR(vp, &vattr, ATTR_HINT, CRED()) == 0) { + VOP_GETATTR(vp, &vattr, ATTR_HINT, + CRED(), NULL) == 0) { u_offset_t filesize = vattr.va_size; u_offset_t offset = SEGOP_GETOFFSET(seg, addr); diff --git a/usr/src/uts/common/vm/vm_swap.c b/usr/src/uts/common/vm/vm_swap.c index d7028b6f296c..d39c85f06935 100644 --- a/usr/src/uts/common/vm/vm_swap.c +++ b/usr/src/uts/common/vm/vm_swap.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -653,7 +652,7 @@ swapctl(int sc_cmd, void *sc_arg, int *rv) if (vp->v_vfsp && vn_is_readonly(vp)) error = EROFS; else - error = VOP_ACCESS(vp, VREAD|VWRITE, 0, CRED()); + error = VOP_ACCESS(vp, VREAD|VWRITE, 0, CRED(), NULL); break; case VDIR: @@ -858,7 +857,7 @@ swapctl32(int sc_cmd, void *sc_arg, int *rv) if (vp->v_vfsp && vn_is_readonly(vp)) error = EROFS; else - error = VOP_ACCESS(vp, VREAD|VWRITE, 0, CRED()); + error = VOP_ACCESS(vp, VREAD|VWRITE, 0, CRED(), NULL); break; case VDIR: @@ -918,7 +917,7 @@ swapadd(struct vnode *vp, ulong_t lowblk, ulong_t nblks, char *swapname) mutex_exit(&cvp->v_lock); mutex_enter(&swap_lock); - if (error = VOP_OPEN(&cvp, FREAD|FWRITE, CRED())) { + if (error = VOP_OPEN(&cvp, FREAD|FWRITE, CRED(), NULL)) { mutex_exit(&swap_lock); /* restore state of v_flag */ if (!wasswap) { @@ -939,7 +938,7 @@ swapadd(struct vnode *vp, ulong_t lowblk, ulong_t nblks, char *swapname) * on a machine with a different size swap partition. */ vattr.va_mask = AT_SIZE; - if (error = VOP_GETATTR(cvp, &vattr, ATTR_COMM, CRED())) + if (error = VOP_GETATTR(cvp, &vattr, ATTR_COMM, CRED(), NULL)) goto out; /* @@ -973,7 +972,8 @@ swapadd(struct vnode *vp, ulong_t lowblk, ulong_t nblks, char *swapname) goto out; /* Fail if fs does not support VOP_PAGEIO */ - error = VOP_PAGEIO(cvp, (page_t *)NULL, (u_offset_t)0, 0, 0, CRED()); + error = VOP_PAGEIO(cvp, (page_t *)NULL, (u_offset_t)0, 0, 0, CRED(), + NULL); if (error == ENOSYS) goto out; @@ -1165,7 +1165,8 @@ swapadd(struct vnode *vp, ulong_t lowblk, ulong_t nblks, char *swapname) kmem_free(nsip, sizeof (*nsip)); } mutex_enter(&swap_lock); - (void) VOP_CLOSE(cvp, FREAD|FWRITE, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(cvp, FREAD|FWRITE, 1, (offset_t)0, CRED(), + NULL); mutex_exit(&swap_lock); } return (error); @@ -1361,7 +1362,7 @@ swapdel( /* Release the vnode */ mutex_enter(&swap_lock); - (void) VOP_CLOSE(cvp, FREAD|FWRITE, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(cvp, FREAD|FWRITE, 1, (offset_t)0, CRED(), NULL); mutex_enter(&cvp->v_lock); cvp->v_flag &= ~VISSWAP; mutex_exit(&cvp->v_lock); @@ -1391,7 +1392,7 @@ swapslot_free( * Users of the physical slot will synchronize on the i/o lock. */ if (error = VOP_GETPAGE(vp, (offset_t)off, ptob(1), NULL, - pl, ptob(1), segkmap, NULL, S_READ, CRED())) { + pl, ptob(1), segkmap, NULL, S_READ, CRED(), NULL)) { /* * Anon slot went away (EIDRM) or vp was truncated (EFAULT) * while we got the page. Thus the physical slot must be diff --git a/usr/src/uts/common/vm/vpm.c b/usr/src/uts/common/vm/vpm.c index 935680d6008e..5cb72b628f77 100644 --- a/usr/src/uts/common/vm/vpm.c +++ b/usr/src/uts/common/vm/vpm.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -237,7 +237,7 @@ vpm_init() * Use prefetch as we have to walk thru a large number of * these data structures. We just use the smap's prefetch * routine as it does the same. This should work fine - * for x64(this needs to be modifed when enabled on sparc). + * for x64(this needs to be modified when enabled on sparc). */ prefetch_smap_w((void *)vpm); @@ -866,7 +866,7 @@ vpm_map_pages( base = segkpm_create_va(baseoff); error = VOP_GETPAGE(vp, baseoff, len, &prot, &pplist[i], - len, segkmap, base, rw, CRED()); + len, segkmap, base, rw, CRED(), NULL); if (error) { VPM_DEBUG(vpmd_getpagefailed); pplist[i] = NULL; @@ -1070,7 +1070,7 @@ vpm_sync_pages(struct vnode *vp, if (flags & SM_DONTNEED) bflags |= B_DONTNEED; - error = VOP_PUTPAGE(vp, off, psize, bflags, CRED()); + error = VOP_PUTPAGE(vp, off, psize, bflags, CRED(), NULL); } return (error); diff --git a/usr/src/uts/i86xpv/vm/seg_mf.c b/usr/src/uts/i86xpv/vm/seg_mf.c index 7c042ad13792..836434991196 100644 --- a/usr/src/uts/i86xpv/vm/seg_mf.c +++ b/usr/src/uts/i86xpv/vm/seg_mf.c @@ -105,7 +105,7 @@ segmf_create(struct seg *seg, void *args) data->mfns[i] = MFN_INVALID; error = VOP_ADDMAP(VTOCVP(data->vp), 0, as, seg->s_base, seg->s_size, - data->prot, data->maxprot, MAP_SHARED, CRED()); + data->prot, data->maxprot, MAP_SHARED, CRED(), NULL); if (error != 0) hat_unload(as->a_hat, @@ -136,7 +136,7 @@ segmf_dup(struct seg *seg, struct seg *newseg) return (VOP_ADDMAP(VTOCVP(ndata->vp), 0, newseg->s_as, newseg->s_base, newseg->s_size, ndata->prot, ndata->maxprot, - MAP_SHARED, CRED())); + MAP_SHARED, CRED(), NULL)); } /* @@ -164,7 +164,7 @@ segmf_unmap(struct seg *seg, caddr_t addr, size_t len) ASSERT(data->vp != NULL); (void) VOP_DELMAP(VTOCVP(data->vp), off, seg->s_as, addr, len, - data->prot, data->maxprot, MAP_SHARED, CRED()); + data->prot, data->maxprot, MAP_SHARED, CRED(), NULL); seg_free(seg); return (0); diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared index 4602092e1501..b6568504fe8a 100644 --- a/usr/src/uts/intel/Makefile.intel.shared +++ b/usr/src/uts/intel/Makefile.intel.shared @@ -302,6 +302,7 @@ DRV_KMODS += sgen DRV_KMODS += si3124 DRV_KMODS += smbios DRV_KMODS += spdsock +DRV_KMODS += smbsrv DRV_KMODS += sppp DRV_KMODS += sppptun DRV_KMODS += st diff --git a/usr/src/uts/intel/os/minor_perm b/usr/src/uts/intel/os/minor_perm index 11822630054e..435e1d67568c 100644 --- a/usr/src/uts/intel/os/minor_perm +++ b/usr/src/uts/intel/os/minor_perm @@ -114,6 +114,7 @@ bmc:bmc 0666 root sys dld:* 0666 root sys aggr:* 0666 root sys smbios:smbios 0444 root sys +smbsrv:* 0640 root sys zfs:* 0600 root sys zfs:zfs 0666 root sys scsi_vhci:* 0666 root sys diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major index 28810d57b042..aa5a760625f1 100644 --- a/usr/src/uts/intel/os/name_to_major +++ b/usr/src/uts/intel/os/name_to_major @@ -129,6 +129,7 @@ domcaps 200 balloon 201 acpippm 202 srn 203 +smbsrv 210 did 239 lx_ptm 240 lx_systrace 241 diff --git a/usr/src/uts/intel/smbsrv/Makefile b/usr/src/uts/intel/smbsrv/Makefile new file mode 100644 index 000000000000..f8482ba8ce45 --- /dev/null +++ b/usr/src/uts/intel/smbsrv/Makefile @@ -0,0 +1,100 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the cifs server file system +# kernel module. +# +# intel implementation architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = smbsrv +OBJECTS = $(SMBSRV_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(SMBSRV_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/common/fs/smbsrv + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +# +# Module dependencies +# +# +LDFLAGS += -dy -Nfs/sockfs -Ndrv/ip -Nstrmod/rpcmod -Nsys/doorfs -Nmisc/kcf + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE) + +# +# Overrides. +# +MODSTUBS_DIR = $(OBJS_DIR) +CLEANFILES += $(MODSTUBS_O) + +INC_PATH += -I$(SRC)/common + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +$(ROOTLINK): $(ROOT_SYS_DIR) $(ROOTMODULE) + -$(RM) $@; ln $(ROOTMODULE) $@ + +# +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/intel/zfs/Makefile b/usr/src/uts/intel/zfs/Makefile index 5239b2cbf7fd..c9596a4eefc8 100644 --- a/usr/src/uts/intel/zfs/Makefile +++ b/usr/src/uts/intel/zfs/Makefile @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -60,7 +60,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE) # Overrides and depends_on # MODSTUBS_DIR = $(OBJS_DIR) -LDFLAGS += -dy -Nfs/specfs -Ndrv/random +LDFLAGS += -dy -Nfs/specfs -Ndrv/random -Nmisc/idmap INC_PATH += -I$(UTSBASE)/common/fs/zfs INC_PATH += -I$(SRC)/common diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared index 694538725e64..35ced8cc2533 100644 --- a/usr/src/uts/sparc/Makefile.sparc.shared +++ b/usr/src/uts/sparc/Makefile.sparc.shared @@ -241,6 +241,7 @@ DRV_KMODS += vni vnic DRV_KMODS += xge DRV_KMODS += rds DRV_KMODS += chxge +DRV_KMODS += smbsrv # # Don't build some of these for OpenSolaris, since they will be diff --git a/usr/src/uts/sparc/os/minor_perm b/usr/src/uts/sparc/os/minor_perm index 21b3b4d1ceac..1106dea0aa5b 100644 --- a/usr/src/uts/sparc/os/minor_perm +++ b/usr/src/uts/sparc/os/minor_perm @@ -74,6 +74,7 @@ sd:* 0640 root sys dad:* 0640 root sys sdt:sdt 0644 root sys sgen:* 0600 root sys +smbsrv:* 0640 root sys ssd:* 0640 root sys st:* 0666 root sys su:[a-z] 0666 root sys diff --git a/usr/src/uts/sparc/os/name_to_major b/usr/src/uts/sparc/os/name_to_major index 82d40a46e815..eced54e622e2 100644 --- a/usr/src/uts/sparc/os/name_to_major +++ b/usr/src/uts/sparc/os/name_to_major @@ -149,6 +149,7 @@ bscbus 198 bscv 199 i2bsc 200 mc-us3i 201 +smbsrv 202 rmc_comm 203 rmclomv 204 rmcadm 205 diff --git a/usr/src/uts/sparc/smbsrv/Makefile b/usr/src/uts/sparc/smbsrv/Makefile new file mode 100644 index 000000000000..71c4cc539822 --- /dev/null +++ b/usr/src/uts/sparc/smbsrv/Makefile @@ -0,0 +1,99 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +# +# This makefile drives the production of the cifs server file system +# kernel module. +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = smbsrv +OBJECTS = $(SMBSRV_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(SMBSRV_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/common/fs/smbsrv + +# +# Include common rules. +# +include $(UTSBASE)/sparc/Makefile.sparc + +# +# Module dependencies +# +LDFLAGS += -dy -Nfs/sockfs -Ndrv/ip -Nstrmod/rpcmod -Nsys/doorfs -Nmisc/kcf + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE) + +# +# Overrides. +# +MODSTUBS_DIR = $(OBJS_DIR) +CLEANFILES += $(MODSTUBS_O) + +INC_PATH += -I$(SRC)/common + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +$(ROOTLINK): $(ROOT_SYS_DIR) $(ROOTMODULE) + -$(RM) $@; ln $(ROOTMODULE) $@ + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/zfs/Makefile b/usr/src/uts/sparc/zfs/Makefile index 5239b2cbf7fd..c9596a4eefc8 100644 --- a/usr/src/uts/sparc/zfs/Makefile +++ b/usr/src/uts/sparc/zfs/Makefile @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -60,7 +60,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE) # Overrides and depends_on # MODSTUBS_DIR = $(OBJS_DIR) -LDFLAGS += -dy -Nfs/specfs -Ndrv/random +LDFLAGS += -dy -Nfs/specfs -Ndrv/random -Nmisc/idmap INC_PATH += -I$(UTSBASE)/common/fs/zfs INC_PATH += -I$(SRC)/common diff --git a/usr/src/uts/sun4u/montecarlo/io/ttymux_dacf/ttymux_dacf.c b/usr/src/uts/sun4u/montecarlo/io/ttymux_dacf/ttymux_dacf.c index c02a9984e74f..4e08be6d8d7b 100644 --- a/usr/src/uts/sun4u/montecarlo/io/ttymux_dacf/ttymux_dacf.c +++ b/usr/src/uts/sun4u/montecarlo/io/ttymux_dacf/ttymux_dacf.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -536,7 +535,7 @@ fs_devtype(char *fspath) return (NODEV); } else { dev = vp->v_rdev; - VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); return (dev); } @@ -568,7 +567,7 @@ open_stream(vnode_t **vp, int *fd, dev_t dev) /* create a vnode for the device and open it */ *vp = makespecvp(dev, VCHR); - if ((rv = VOP_OPEN(vp, FREAD+FWRITE+FNOCTTY, CRED())) != 0) { + if ((rv = VOP_OPEN(vp, FREAD+FWRITE+FNOCTTY, CRED(), NULL)) != 0) { goto out2; } /* Associate a file pointer with the vnode */ @@ -586,7 +585,7 @@ open_stream(vnode_t **vp, int *fd, dev_t dev) return (0); out1: - VOP_CLOSE(*vp, FREAD+FWRITE+FNOCTTY, 1, (offset_t)0, CRED()); + VOP_CLOSE(*vp, FREAD+FWRITE+FNOCTTY, 1, (offset_t)0, CRED(), NULL); out2: VN_RELE(*vp); return (rv); @@ -669,7 +668,7 @@ link_aconsole(vnode_t *mux_avp, sm_console_t *cn) return (rv); out: - VOP_CLOSE(lvp, FREAD+FWRITE+FNOCTTY, 1, (offset_t)0, CRED()); + VOP_CLOSE(lvp, FREAD+FWRITE+FNOCTTY, 1, (offset_t)0, CRED(), NULL); VN_RELE(lvp); return (rv); } @@ -905,12 +904,12 @@ ttymux_config(dacf_infohdl_t info_hdl, dacf_arghdl_t arg_hdl, int flags) muxvp = dacf_makevp(info_hdl); - if ((rv = VOP_OPEN(&muxvp, OFLAGS, CRED())) == 0) { + if ((rv = VOP_OPEN(&muxvp, OFLAGS, CRED(), NULL)) == 0) { (void) enable_all_consoles(ms, muxvp); (void) usable_consoles(ms, &icnt, &ocnt); - VOP_CLOSE(muxvp, OFLAGS, 1, (offset_t)0, CRED()); + VOP_CLOSE(muxvp, OFLAGS, 1, (offset_t)0, CRED(), NULL); VN_RELE(muxvp); } else { ttymux_dprintf(DPRINT_L3, diff --git a/usr/src/uts/sun4u/os/cpr_impl.c b/usr/src/uts/sun4u/os/cpr_impl.c index e0c6a4b2d82e..b08f6660e397 100644 --- a/usr/src/uts/sun4u/os/cpr_impl.c +++ b/usr/src/uts/sun4u/os/cpr_impl.c @@ -229,7 +229,7 @@ i_cpr_mp_setup(void) /* * Do not allow setting page size codes in MMU primary context * register while using cif wrapper. This is needed to work - * arround OBP incorrect handling of this MMU register. + * around OBP incorrect handling of this MMU register. */ kcontextreg = 0; @@ -1421,7 +1421,7 @@ i_cpr_reusefini(void) } } - (void) VOP_CLOSE(vp, FREAD|FWRITE, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD|FWRITE, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); kmem_free(cdef, sizeof (*cdef)); @@ -1473,7 +1473,7 @@ i_cpr_check_cprinfo(void) } rc = cpr_rdwr(UIO_READ, vp, &mini, sizeof (mini)); - (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED()); + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); if (rc) { diff --git a/usr/src/uts/sun4v/io/vds.c b/usr/src/uts/sun4v/io/vds.c index 14afdb7a6452..525d8c85296b 100644 --- a/usr/src/uts/sun4v/io/vds.c +++ b/usr/src/uts/sun4v/io/vds.c @@ -1270,7 +1270,7 @@ vd_reset_if_needed(vd_t *vd) ddi_taskq_wait(vd->completionq); if (vd->file) { - status = VOP_FSYNC(vd->file_vnode, FSYNC, kcred); + status = VOP_FSYNC(vd->file_vnode, FSYNC, kcred, NULL); if (status) { PR0("VOP_FSYNC returned errno %d", status); } @@ -1982,7 +1982,7 @@ vd_do_file_ioctl(vd_t *vd, int cmd, void *ioctl_arg) return (0); case DKIOCFLUSHWRITECACHE: - return (VOP_FSYNC(vd->file_vnode, FSYNC, kcred)); + return (VOP_FSYNC(vd->file_vnode, FSYNC, kcred, NULL)); default: return (ENOTSUP); @@ -2550,7 +2550,7 @@ vd_process_ver_msg(vd_t *vd, vio_msg_t *msg, size_t msglen) * the negotiated major and minor version values in the "vd" data * structure to govern further communication; in particular, note that * the client might have specified a lower minor version for the - * agreed major version than specifed in the vds_version[] array. The + * agreed major version than specified in the vds_version[] array. The * following assertions should help remind future maintainers to make * the appropriate changes to support multiple versions. */ @@ -3758,7 +3758,8 @@ vd_setup_backend_vnode(vd_t *vd) vd->file = B_TRUE; vattr.va_mask = AT_SIZE; - if ((status = VOP_GETATTR(vd->file_vnode, &vattr, 0, kcred)) != 0) { + if ((status = VOP_GETATTR(vd->file_vnode, &vattr, 0, kcred, NULL)) + != 0) { PRN("VOP_GETATTR(%s) = errno %d", file_path, status); return (EIO); } @@ -4376,10 +4377,11 @@ vds_destroy_vd(void *arg) kmem_free(vd->inband_task.msg, vd->max_msglen); vd->inband_task.msg = NULL; } + if (vd->file) { /* Close file */ (void) VOP_CLOSE(vd->file_vnode, vd->open_flags, 1, - 0, kcred); + 0, kcred, NULL); VN_RELE(vd->file_vnode); if (vd->file_devid != NULL) ddi_devid_free(vd->file_devid);