Home | History | Annotate | Download | only in smbsrv
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <smbsrv/smb_kproto.h>
     27 #include <smbsrv/smb_fsops.h>
     28 #include <smbsrv/smbinfo.h>
     29 #include <sys/nbmlock.h>
     30 
     31 static int smb_delete_check_path(smb_request_t *, boolean_t *);
     32 static int smb_delete_single_file(smb_request_t *, smb_error_t *);
     33 static int smb_delete_multiple_files(smb_request_t *, smb_error_t *);
     34 static int smb_delete_find_fname(smb_request_t *, smb_odir_t *, char *, int);
     35 static int smb_delete_check_dosattr(smb_request_t *, smb_error_t *);
     36 static int smb_delete_remove_file(smb_request_t *, smb_error_t *);
     37 
     38 static void smb_delete_error(smb_error_t *, uint32_t, uint16_t, uint16_t);
     39 
     40 /*
     41  * smb_com_delete
     42  *
     43  * The delete file message is sent to delete a data file. The appropriate
     44  * Tid and additional pathname are passed. Read only files may not be
     45  * deleted, the read-only attribute must be reset prior to file deletion.
     46  *
     47  * NT supports a hidden permission known as File Delete Child (FDC). If
     48  * the user has FullControl access to a directory, the user is permitted
     49  * to delete any object in the directory regardless of the permissions
     50  * on the object.
     51  *
     52  * Client Request                     Description
     53  * ================================== =================================
     54  * UCHAR WordCount;                   Count of parameter words = 1
     55  * USHORT SearchAttributes;
     56  * USHORT ByteCount;                  Count of data bytes; min = 2
     57  * UCHAR BufferFormat;                0x04
     58  * STRING FileName[];                 File name
     59  *
     60  * Multiple files may be deleted in response to a single request as
     61  * SMB_COM_DELETE supports wildcards
     62  *
     63  * SearchAttributes indicates the attributes that the target file(s) must
     64  * have. If the attribute is zero then only normal files are deleted. If
     65  * the system file or hidden attributes are specified then the delete is
     66  * inclusive -both the specified type(s) of files and normal files are
     67  * deleted. Attributes are described in the "Attribute Encoding" section
     68  * of this document.
     69  *
     70  * If bit0 of the Flags2 field of the SMB header is set, a pattern is
     71  * passed in, and the file has a long name, then the passed pattern  much
     72  * match the long file name for the delete to succeed. If bit0 is clear, a
     73  * pattern is passed in, and the file has a long name, then the passed
     74  * pattern must match the file's short name for the deletion to succeed.
     75  *
     76  * Server Response                    Description
     77  * ================================== =================================
     78  * UCHAR WordCount;                   Count of parameter words = 0
     79  * USHORT ByteCount;                  Count of data bytes = 0
     80  *
     81  * 4.2.10.1  Errors
     82  *
     83  * ERRDOS/ERRbadpath
     84  * ERRDOS/ERRbadfile
     85  * ERRDOS/ERRnoaccess
     86  * ERRDOS/ERRbadshare	# returned by NT for files that are already open
     87  * ERRHRD/ERRnowrite
     88  * ERRSRV/ERRaccess
     89  * ERRSRV/ERRinvdevice
     90  * ERRSRV/ERRinvid
     91  * ERRSRV/ERRbaduid
     92  */
     93 smb_sdrc_t
     94 smb_pre_delete(smb_request_t *sr)
     95 {
     96 	int rc;
     97 	smb_fqi_t *fqi;
     98 
     99 	fqi = &sr->arg.dirop.fqi;
    100 
    101 	if ((rc = smbsr_decode_vwv(sr, "w", &fqi->fq_sattr)) == 0)
    102 		rc = smbsr_decode_data(sr, "%S", sr, &fqi->fq_path.pn_path);
    103 
    104 	DTRACE_SMB_2(op__Delete__start, smb_request_t *, sr, smb_fqi_t *, fqi);
    105 
    106 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
    107 }
    108 
    109 void
    110 smb_post_delete(smb_request_t *sr)
    111 {
    112 	DTRACE_SMB_1(op__Delete__done, smb_request_t *, sr);
    113 }
    114 
    115 /*
    116  * smb_com_delete
    117  *
    118  * 1. pre-process pathname -  smb_delete_check_path()
    119  *    checks dot, bad path syntax, wildcards in path
    120  *
    121  * 2. process the path to get directory node & last_comp,
    122  *    store these in fqi
    123  *    - If smb_pathname_reduce cannot find the specified path,
    124  *      the error (ENOTDIR) is translated to NT_STATUS_OBJECT_PATH_NOT_FOUND
    125  *      if the target is a single file (no wildcards).  If there are
    126  *      wildcards in the last_comp, NT_STATUS_OBJECT_NAME_NOT_FOUND is
    127  *      used instead.
    128  *    - If the directory node is the mount point and the last component
    129  *      is ".." NT_STATUS_OBJECT_PATH_SYNTAX_BAD is returned.
    130  *
    131  * 3. check access permissions
    132  *
    133  * 4. invoke the appropriate deletion routine to find and remove
    134  *    the specified file(s).
    135  *    - if target is a single file (no wildcards) - smb_delete_single_file
    136  *    - if the target contains wildcards - smb_delete_multiple_files
    137  *
    138  * Returns: SDRC_SUCCESS or SDRC_ERROR
    139  */
    140 smb_sdrc_t
    141 smb_com_delete(smb_request_t *sr)
    142 {
    143 	int rc;
    144 	smb_error_t err;
    145 	uint32_t status;
    146 	boolean_t wildcards;
    147 	smb_fqi_t *fqi;
    148 
    149 	fqi = &sr->arg.dirop.fqi;
    150 
    151 	if (smb_delete_check_path(sr, &wildcards) != 0)
    152 		return (SDRC_ERROR);
    153 
    154 	rc = smb_pathname_reduce(sr, sr->user_cr, fqi->fq_path.pn_path,
    155 	    sr->tid_tree->t_snode, sr->tid_tree->t_snode,
    156 	    &fqi->fq_dnode, fqi->fq_last_comp);
    157 	if (rc == 0) {
    158 		if (fqi->fq_dnode->vp->v_type != VDIR) {
    159 			smb_node_release(fqi->fq_dnode);
    160 			rc = ENOTDIR;
    161 		}
    162 	}
    163 	if (rc != 0) {
    164 		if (rc == ENOTDIR) {
    165 			if (wildcards)
    166 				status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
    167 			else
    168 				status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
    169 			smbsr_error(sr, status, ERRDOS, ERROR_FILE_NOT_FOUND);
    170 		} else {
    171 			smbsr_errno(sr, rc);
    172 		}
    173 
    174 		return (SDRC_ERROR);
    175 	}
    176 
    177 	if ((fqi->fq_dnode == sr->tid_tree->t_snode) &&
    178 	    (strcmp(fqi->fq_last_comp, "..") == 0)) {
    179 		smb_node_release(fqi->fq_dnode);
    180 		smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
    181 		    ERRDOS, ERROR_BAD_PATHNAME);
    182 		return (SDRC_ERROR);
    183 	}
    184 
    185 	rc = smb_fsop_access(sr, sr->user_cr, fqi->fq_dnode,
    186 	    FILE_LIST_DIRECTORY);
    187 	if (rc != 0) {
    188 		smb_node_release(fqi->fq_dnode);
    189 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
    190 		    ERRDOS, ERROR_ACCESS_DENIED);
    191 		return (SDRC_ERROR);
    192 	}
    193 
    194 	if (wildcards)
    195 		rc = smb_delete_multiple_files(sr, &err);
    196 	else
    197 		rc = smb_delete_single_file(sr, &err);
    198 
    199 	smb_node_release(fqi->fq_dnode);
    200 
    201 	if (rc != 0)
    202 		smbsr_set_error(sr, &err);
    203 	else
    204 		rc = smbsr_encode_empty_result(sr);
    205 
    206 	return (rc == 0 ? SDRC_SUCCESS : SDRC_ERROR);
    207 }
    208 
    209 /*
    210  * smb_delete_single_file
    211  *
    212  * Find the specified file and, if its attributes match the search
    213  * criteria, delete it.
    214  *
    215  * Returns 0 - success (file deleted)
    216  *        -1 - error, err is populated with error details
    217  */
    218 static int
    219 smb_delete_single_file(smb_request_t *sr, smb_error_t *err)
    220 {
    221 	smb_fqi_t *fqi;
    222 	uint32_t status;
    223 
    224 	fqi = &sr->arg.dirop.fqi;
    225 
    226 	smb_pathname_setup(sr, &fqi->fq_path);
    227 	status = smb_validate_object_name(&fqi->fq_path);
    228 	if (status != NT_STATUS_SUCCESS) {
    229 		smb_delete_error(err, status, ERRDOS, ERROR_INVALID_NAME);
    230 		return (-1);
    231 	}
    232 
    233 	if (smb_fsop_lookup_name(sr, sr->user_cr, 0, sr->tid_tree->t_snode,
    234 	    fqi->fq_dnode, fqi->fq_last_comp, &fqi->fq_fnode) != 0) {
    235 		smb_delete_error(err, NT_STATUS_OBJECT_NAME_NOT_FOUND,
    236 		    ERRDOS, ERROR_FILE_NOT_FOUND);
    237 		return (-1);
    238 	}
    239 
    240 	if (smb_delete_check_dosattr(sr, err) != 0) {
    241 		smb_node_release(fqi->fq_fnode);
    242 		return (-1);
    243 	}
    244 
    245 	if (smb_delete_remove_file(sr, err) != 0) {
    246 		smb_node_release(fqi->fq_fnode);
    247 		return (-1);
    248 	}
    249 
    250 	smb_node_release(fqi->fq_fnode);
    251 	return (0);
    252 }
    253 
    254 /*
    255  * smb_delete_multiple_files
    256  *
    257  * For each matching file found by smb_delete_find_fname:
    258  * 1. lookup file
    259  * 2. check the file's attributes
    260  *    - The search ends with an error if a readonly file
    261  *      (NT_STATUS_CANNOT_DELETE) is matched.
    262  *    - The search ends (but not an error) if a directory is
    263  *      matched and the request's search did not include
    264  *      directories.
    265  *    - Otherwise, if smb_delete_check_dosattr fails the file
    266  *      is skipped and the search continues (at step 1)
    267  * 3. delete the file
    268  *
    269  * Returns 0 - success
    270  *        -1 - error, err is populated with error details
    271  */
    272 static int
    273 smb_delete_multiple_files(smb_request_t *sr, smb_error_t *err)
    274 {
    275 	int rc, deleted = 0;
    276 	smb_fqi_t *fqi;
    277 	uint16_t odid;
    278 	smb_odir_t *od;
    279 	char namebuf[MAXNAMELEN];
    280 
    281 	fqi = &sr->arg.dirop.fqi;
    282 
    283 	/*
    284 	 * Specify all search attributes (SMB_SEARCH_ATTRIBUTES) so that
    285 	 * delete-specific checking can be done (smb_delete_check_dosattr).
    286 	 */
    287 	odid = smb_odir_open(sr, fqi->fq_path.pn_path,
    288 	    SMB_SEARCH_ATTRIBUTES, 0);
    289 	if (odid == 0)
    290 		return (-1);
    291 
    292 	if ((od = smb_tree_lookup_odir(sr->tid_tree, odid)) == NULL)
    293 		return (-1);
    294 
    295 	for (;;) {
    296 		rc = smb_delete_find_fname(sr, od, namebuf, MAXNAMELEN);
    297 		if (rc != 0)
    298 			break;
    299 
    300 		rc = smb_fsop_lookup_name(sr, sr->user_cr, 0,
    301 		    sr->tid_tree->t_snode, fqi->fq_dnode,
    302 		    namebuf, &fqi->fq_fnode);
    303 		if (rc != 0)
    304 			break;
    305 
    306 		if (smb_delete_check_dosattr(sr, err) != 0) {
    307 			smb_node_release(fqi->fq_fnode);
    308 			if (err->status == NT_STATUS_CANNOT_DELETE) {
    309 				smb_odir_close(od);
    310 				smb_odir_release(od);
    311 				return (-1);
    312 			}
    313 			if ((err->status == NT_STATUS_FILE_IS_A_DIRECTORY) &&
    314 			    (SMB_SEARCH_DIRECTORY(fqi->fq_sattr) != 0))
    315 				break;
    316 			continue;
    317 		}
    318 
    319 		if (smb_delete_remove_file(sr, err) == 0) {
    320 			++deleted;
    321 			smb_node_release(fqi->fq_fnode);
    322 			continue;
    323 		}
    324 		if (err->status == NT_STATUS_OBJECT_NAME_NOT_FOUND) {
    325 			smb_node_release(fqi->fq_fnode);
    326 			continue;
    327 		}
    328 
    329 		smb_odir_close(od);
    330 		smb_odir_release(od);
    331 		smb_node_release(fqi->fq_fnode);
    332 		return (-1);
    333 	}
    334 
    335 	smb_odir_close(od);
    336 	smb_odir_release(od);
    337 
    338 	if ((rc != 0) && (rc != ENOENT)) {
    339 		smbsr_map_errno(rc, err);
    340 		return (-1);
    341 	}
    342 
    343 	if (deleted == 0) {
    344 		smb_delete_error(err, NT_STATUS_NO_SUCH_FILE,
    345 		    ERRDOS, ERROR_FILE_NOT_FOUND);
    346 		return (-1);
    347 	}
    348 
    349 	return (0);
    350 }
    351 
    352 /*
    353  * smb_delete_find_fname
    354  *
    355  * Find next filename that matches search pattern and return it
    356  * in namebuf.
    357  *
    358  * Case insensitivity note:
    359  * If the tree is case insensitive and there's a case conflict
    360  * with the name returned from smb_odir_read, smb_delete_find_fname
    361  * performs case conflict name mangling to produce a unique filename.
    362  * This ensures that any subsequent smb_fsop_lookup, (which will
    363  * find the first case insensitive match) will find the correct file.
    364  *
    365  * Returns: 0 - success
    366  *          errno
    367  */
    368 static int
    369 smb_delete_find_fname(smb_request_t *sr, smb_odir_t *od, char *namebuf, int len)
    370 {
    371 	int		rc;
    372 	smb_odirent_t	*odirent;
    373 	boolean_t	eos;
    374 	char		*name;
    375 	char		shortname[SMB_SHORTNAMELEN];
    376 	char		name83[SMB_SHORTNAMELEN];
    377 
    378 	odirent = kmem_alloc(sizeof (smb_odirent_t), KM_SLEEP);
    379 
    380 	rc = smb_odir_read(sr, od, odirent, &eos);
    381 	if (rc != 0) {
    382 		kmem_free(odirent, sizeof (smb_odirent_t));
    383 		return (rc);
    384 	}
    385 	if (eos) {
    386 		kmem_free(odirent, sizeof (smb_odirent_t));
    387 		return (ENOENT);
    388 	}
    389 
    390 	/* if case conflict, force mangle and use shortname */
    391 	if ((od->d_flags & SMB_ODIR_FLAG_IGNORE_CASE) &&
    392 	    (odirent->od_eflags & ED_CASE_CONFLICT)) {
    393 		(void) smb_mangle_name(odirent->od_ino, odirent->od_name,
    394 		    shortname, name83, 1);
    395 		name = shortname;
    396 	} else {
    397 		name = odirent->od_name;
    398 	}
    399 	(void) strlcpy(namebuf, name, len);
    400 
    401 	kmem_free(odirent, sizeof (smb_odirent_t));
    402 	return (0);
    403 }
    404 
    405 /*
    406  * smb_delete_check_dosattr
    407  *
    408  * Check file's dos atributes to ensure that
    409  * 1. the file is not a directory - NT_STATUS_FILE_IS_A_DIRECTORY
    410  * 2. the file is not readonly - NT_STATUS_CANNOT_DELETE
    411  * 3. the file's dos attributes comply with the specified search attributes
    412  *     If the file is either hidden or system and those attributes
    413  *     are not specified in the search attributes - NT_STATUS_NO_SUCH_FILE
    414  *
    415  * Returns: 0 - file's attributes pass all checks
    416  *         -1 - err populated with error details
    417  */
    418 static int
    419 smb_delete_check_dosattr(smb_request_t *sr, smb_error_t *err)
    420 {
    421 	smb_fqi_t *fqi;
    422 	smb_node_t *node;
    423 	smb_attr_t attr;
    424 	uint16_t sattr;
    425 
    426 	fqi = &sr->arg.dirop.fqi;
    427 	sattr = fqi->fq_sattr;
    428 	node = fqi->fq_fnode;
    429 
    430 	if (smb_node_getattr(sr, node, &attr) != 0) {
    431 		smb_delete_error(err, NT_STATUS_INTERNAL_ERROR,
    432 		    ERRDOS, ERROR_INTERNAL_ERROR);
    433 		return (-1);
    434 	}
    435 
    436 	if (attr.sa_dosattr & FILE_ATTRIBUTE_DIRECTORY) {
    437 		smb_delete_error(err, NT_STATUS_FILE_IS_A_DIRECTORY,
    438 		    ERRDOS, ERROR_ACCESS_DENIED);
    439 		return (-1);
    440 	}
    441 
    442 	if (SMB_PATHFILE_IS_READONLY(sr, node)) {
    443 		smb_delete_error(err, NT_STATUS_CANNOT_DELETE,
    444 		    ERRDOS, ERROR_ACCESS_DENIED);
    445 		return (-1);
    446 	}
    447 
    448 	if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) &&
    449 	    !(SMB_SEARCH_HIDDEN(sattr))) {
    450 		smb_delete_error(err, NT_STATUS_NO_SUCH_FILE,
    451 		    ERRDOS, ERROR_FILE_NOT_FOUND);
    452 		return (-1);
    453 	}
    454 
    455 	if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) &&
    456 	    !(SMB_SEARCH_SYSTEM(sattr))) {
    457 		smb_delete_error(err, NT_STATUS_NO_SUCH_FILE,
    458 		    ERRDOS, ERROR_FILE_NOT_FOUND);
    459 		return (-1);
    460 	}
    461 
    462 	return (0);
    463 }
    464 
    465 /*
    466  * smb_delete_remove_file
    467  *
    468  * For consistency with Windows 2000, the range check should be done
    469  * after checking for sharing violations.  Attempting to delete a
    470  * locked file will result in sharing violation, which is the same
    471  * thing that will happen if you try to delete a non-locked open file.
    472  *
    473  * Note that windows 2000 rejects lock requests on open files that
    474  * have been opened with metadata open modes.  The error is
    475  * STATUS_ACCESS_DENIED.
    476  *
    477  * NT does not always close a file immediately, which can cause the
    478  * share and access checking to fail (the node refcnt is greater
    479  * than one), and the file doesn't get deleted. Breaking the oplock
    480  * before share and access checking gives the client a chance to
    481  * close the file.
    482  *
    483  * Returns: 0 - success
    484  *         -1 - error, err populated with error details
    485  */
    486 static int
    487 smb_delete_remove_file(smb_request_t *sr, smb_error_t *err)
    488 {
    489 	int rc;
    490 	uint32_t status;
    491 	smb_fqi_t *fqi;
    492 	smb_node_t *node;
    493 	uint32_t flags = 0;
    494 
    495 	fqi = &sr->arg.dirop.fqi;
    496 	node = fqi->fq_fnode;
    497 
    498 	(void) smb_oplock_break(node, sr->session, B_FALSE);
    499 
    500 	smb_node_start_crit(node, RW_READER);
    501 
    502 	status = smb_node_delete_check(node);
    503 	if (status != NT_STATUS_SUCCESS) {
    504 		smb_delete_error(err, NT_STATUS_SHARING_VIOLATION,
    505 		    ERRDOS, ERROR_SHARING_VIOLATION);
    506 		smb_node_end_crit(node);
    507 		return (-1);
    508 	}
    509 
    510 	status = smb_range_check(sr, node, 0, UINT64_MAX, B_TRUE);
    511 	if (status != NT_STATUS_SUCCESS) {
    512 		smb_delete_error(err, NT_STATUS_ACCESS_DENIED,
    513 		    ERRDOS, ERROR_ACCESS_DENIED);
    514 		smb_node_end_crit(node);
    515 		return (-1);
    516 	}
    517 
    518 	if (SMB_TREE_SUPPORTS_CATIA(sr))
    519 		flags |= SMB_CATIA;
    520 
    521 	rc = smb_fsop_remove(sr, sr->user_cr, node->n_dnode,
    522 	    node->od_name, flags);
    523 	if (rc != 0) {
    524 		if (rc == ENOENT)
    525 			smb_delete_error(err, NT_STATUS_OBJECT_NAME_NOT_FOUND,
    526 			    ERRDOS, ERROR_FILE_NOT_FOUND);
    527 		else
    528 			smbsr_map_errno(rc, err);
    529 
    530 		smb_node_end_crit(node);
    531 		return (-1);
    532 	}
    533 
    534 	smb_node_end_crit(node);
    535 	return (0);
    536 }
    537 
    538 
    539 /*
    540  * smb_delete_check_path
    541  *
    542  * Perform initial validation on the pathname and last_comp.
    543  *
    544  * wildcards in path:
    545  * Wildcards in the path (excluding the last_comp) should result
    546  * in NT_STATUS_OBJECT_NAME_INVALID.
    547  *
    548  * bad path syntax:
    549  * On unix .. at the root of a file system links to the root. Thus
    550  * an attempt to lookup "/../../.." will be the same as looking up "/"
    551  * CIFs clients expect the above to result in
    552  * NT_STATUS_OBJECT_PATH_SYNTAX_BAD. It is currently not possible
    553  * (and questionable if it's desirable) to deal with all cases
    554  * but paths beginning with \\.. are handled. See bad_paths[].
    555  * Cases like "\\dir\\..\\.." will be caught and handled after the
    556  * pnreduce.  Cases like "\\dir\\..\\..\\filename" will still result
    557  * in "\\filename" which is contrary to windows behavior.
    558  *
    559  * dot:
    560  * A filename of '.' should result in NT_STATUS_OBJECT_NAME_INVALID
    561  * Any wildcard filename that resolves to '.' should result in
    562  * NT_STATUS_OBJECT_NAME_INVALID if the search attributes include
    563  * FILE_ATTRIBUTE_DIRECTORY
    564  *
    565  * Returns:
    566  *   0:  path is valid. Sets *wildcard to TRUE if wildcard delete
    567  *	         i.e. if wildcards in last component
    568  *  -1: path is invalid. Sets error information in sr.
    569  */
    570 static int
    571 smb_delete_check_path(smb_request_t *sr, boolean_t *wildcard)
    572 {
    573 	smb_fqi_t *fqi = &sr->arg.dirop.fqi;
    574 	char *p, *last_comp;
    575 	int i, wildcards;
    576 	smb_pathname_t *pn = &fqi->fq_path;
    577 
    578 	struct {
    579 		char *name;
    580 		int len;
    581 	} *bad, bad_paths[] = {
    582 		{"\\..\0", 4},
    583 		{"\\..\\", 4},
    584 		{"..\0", 3},
    585 		{"..\\", 3}
    586 	};
    587 
    588 	/* find last component, strip trailing '\\' */
    589 	p = pn->pn_path + strlen(pn->pn_path) - 1;
    590 	while (*p == '\\') {
    591 		*p = '\0';
    592 		--p;
    593 	}
    594 
    595 	if ((p = strrchr(pn->pn_path, '\\')) == NULL)
    596 		last_comp = pn->pn_path;
    597 	else
    598 		last_comp = ++p;
    599 
    600 	wildcards = smb_convert_wildcards(last_comp);
    601 
    602 	if (last_comp != pn->pn_path) {
    603 		/*
    604 		 * Wildcards are only allowed in the last component.
    605 		 * Check for additional wildcards in the path.
    606 		 */
    607 		if (smb_convert_wildcards(pn->pn_path) != wildcards) {
    608 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
    609 			    ERRDOS, ERROR_INVALID_NAME);
    610 			return (-1);
    611 		}
    612 	}
    613 
    614 	/* path above the mount point */
    615 	for (i = 0; i < sizeof (bad_paths) / sizeof (bad_paths[0]); ++i) {
    616 		bad = &bad_paths[i];
    617 		if (strncmp(pn->pn_path, bad->name, bad->len) == 0) {
    618 			smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
    619 			    ERRDOS, ERROR_BAD_PATHNAME);
    620 			return (-1);
    621 		}
    622 	}
    623 
    624 	/* last component is, or resolves to, '.' (dot) */
    625 	if ((strcmp(last_comp, ".") == 0) ||
    626 	    (SMB_SEARCH_DIRECTORY(fqi->fq_sattr) &&
    627 	    (smb_match(last_comp, ".")))) {
    628 		smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
    629 		    ERRDOS, ERROR_INVALID_NAME);
    630 		return (-1);
    631 	}
    632 
    633 	*wildcard = (wildcards != 0);
    634 	return (0);
    635 }
    636 
    637 /*
    638  * smb_delete_error
    639  */
    640 static void
    641 smb_delete_error(smb_error_t *err,
    642     uint32_t status, uint16_t errcls, uint16_t errcode)
    643 {
    644 	err->severity = ERROR_SEVERITY_ERROR;
    645 	err->status = status;
    646 	err->errcls = errcls;
    647 	err->errcode = errcode;
    648 }
    649