dir.c 16.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4
/*
 *   fs/cifs/dir.c
 *
 *   vfs operations that deal with dentries
S
Steve French 已提交
5
 *
S
Steve French 已提交
6
 *   Copyright (C) International Business Machines  Corp., 2002,2008
L
Linus Torvalds 已提交
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 *   Author(s): Steve French (sfrench@us.ibm.com)
 *
 *   This library is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published
 *   by the Free Software Foundation; either version 2.1 of the License, or
 *   (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/namei.h>
#include "cifsfs.h"
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_debug.h"
#include "cifs_fs_sb.h"

34
static void
L
Linus Torvalds 已提交
35 36
renew_parental_timestamps(struct dentry *direntry)
{
S
Steve French 已提交
37 38
	/* BB check if there is a way to get the kernel to do this or if we
	   really need this */
L
Linus Torvalds 已提交
39 40 41
	do {
		direntry->d_time = jiffies;
		direntry = direntry->d_parent;
S
Steve French 已提交
42
	} while (!IS_ROOT(direntry));
L
Linus Torvalds 已提交
43 44 45 46 47 48 49
}

/* Note: caller must free return buffer */
char *
build_path_from_dentry(struct dentry *direntry)
{
	struct dentry *temp;
50 51
	int namelen;
	int pplen;
L
Linus Torvalds 已提交
52
	char *full_path;
53
	char dirsep;
L
Linus Torvalds 已提交
54

S
Steve French 已提交
55
	if (direntry == NULL)
L
Linus Torvalds 已提交
56 57 58 59
		return NULL;  /* not much we can do if dentry is freed and
		we need to reopen the file after it was closed implicitly
		when the server crashed */

60
	dirsep = CIFS_DIR_SEP(CIFS_SB(direntry->d_sb));
61
	pplen = CIFS_SB(direntry->d_sb)->prepathlen;
L
Linus Torvalds 已提交
62
cifs_bp_rename_retry:
S
Steve French 已提交
63
	namelen = pplen;
L
Linus Torvalds 已提交
64 65 66
	for (temp = direntry; !IS_ROOT(temp);) {
		namelen += (1 + temp->d_name.len);
		temp = temp->d_parent;
S
Steve French 已提交
67 68
		if (temp == NULL) {
			cERROR(1, ("corrupt dentry"));
L
Linus Torvalds 已提交
69 70 71 72 73
			return NULL;
		}
	}

	full_path = kmalloc(namelen+1, GFP_KERNEL);
S
Steve French 已提交
74
	if (full_path == NULL)
L
Linus Torvalds 已提交
75 76 77 78 79 80 81
		return full_path;
	full_path[namelen] = 0;	/* trailing null */
	for (temp = direntry; !IS_ROOT(temp);) {
		namelen -= 1 + temp->d_name.len;
		if (namelen < 0) {
			break;
		} else {
82
			full_path[namelen] = dirsep;
L
Linus Torvalds 已提交
83 84
			strncpy(full_path + namelen + 1, temp->d_name.name,
				temp->d_name.len);
85
			cFYI(0, ("name: %s", full_path + namelen));
L
Linus Torvalds 已提交
86 87
		}
		temp = temp->d_parent;
S
Steve French 已提交
88 89
		if (temp == NULL) {
			cERROR(1, ("corrupt dentry"));
L
Linus Torvalds 已提交
90 91 92 93
			kfree(full_path);
			return NULL;
		}
	}
94
	if (namelen != pplen) {
L
Linus Torvalds 已提交
95
		cERROR(1,
96
		       ("did not end path lookup where expected namelen is %d",
L
Linus Torvalds 已提交
97
			namelen));
S
Steve French 已提交
98
		/* presumably this is only possible if racing with a rename
L
Linus Torvalds 已提交
99 100 101 102 103
		of one of the parent directories  (we can not lock the dentries
		above us to prevent this, but retrying should be harmless) */
		kfree(full_path);
		goto cifs_bp_rename_retry;
	}
104 105 106 107 108 109
	/* DIR_SEP already set for byte  0 / vs \ but not for
	   subsequent slashes in prepath which currently must
	   be entered the right way - not sure if there is an alternative
	   since the '\' is a valid posix character so we can not switch
	   those safely to '/' if any are found in the middle of the prepath */
	/* BB test paths to Windows with '/' in the midst of prepath */
S
Steve French 已提交
110
	strncpy(full_path, CIFS_SB(direntry->d_sb)->prepath, pplen);
L
Linus Torvalds 已提交
111 112 113
	return full_path;
}

114
/* Inode operations in similar order to how they appear in Linux file fs.h */
L
Linus Torvalds 已提交
115 116 117 118 119 120 121 122 123 124 125 126 127

int
cifs_create(struct inode *inode, struct dentry *direntry, int mode,
		struct nameidata *nd)
{
	int rc = -ENOENT;
	int xid;
	int oplock = 0;
	int desiredAccess = GENERIC_READ | GENERIC_WRITE;
	__u16 fileHandle;
	struct cifs_sb_info *cifs_sb;
	struct cifsTconInfo *pTcon;
	char *full_path = NULL;
S
Steve French 已提交
128
	FILE_ALL_INFO *buf = NULL;
L
Linus Torvalds 已提交
129
	struct inode *newinode = NULL;
S
Steve French 已提交
130 131
	struct cifsFileInfo *pCifsFile = NULL;
	struct cifsInodeInfo *pCifsInode;
L
Linus Torvalds 已提交
132 133 134 135 136 137 138 139 140
	int disposition = FILE_OVERWRITE_IF;
	int write_only = FALSE;

	xid = GetXid();

	cifs_sb = CIFS_SB(inode->i_sb);
	pTcon = cifs_sb->tcon;

	full_path = build_path_from_dentry(direntry);
S
Steve French 已提交
141
	if (full_path == NULL) {
L
Linus Torvalds 已提交
142 143 144 145
		FreeXid(xid);
		return -ENOMEM;
	}

S
Steve French 已提交
146
	if (nd && (nd->flags & LOOKUP_OPEN)) {
M
Miklos Szeredi 已提交
147 148 149 150 151 152 153 154 155
		int oflags = nd->intent.open.flags;

		desiredAccess = 0;
		if (oflags & FMODE_READ)
			desiredAccess |= GENERIC_READ;
		if (oflags & FMODE_WRITE) {
			desiredAccess |= GENERIC_WRITE;
			if (!(oflags & FMODE_READ))
				write_only = TRUE;
L
Linus Torvalds 已提交
156 157
		}

S
Steve French 已提交
158
		if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
L
Linus Torvalds 已提交
159
			disposition = FILE_CREATE;
S
Steve French 已提交
160
		else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
L
Linus Torvalds 已提交
161
			disposition = FILE_OVERWRITE_IF;
S
Steve French 已提交
162
		else if ((oflags & O_CREAT) == O_CREAT)
L
Linus Torvalds 已提交
163
			disposition = FILE_OPEN_IF;
S
Steve French 已提交
164
		else
S
Steve French 已提交
165
			cFYI(1, ("Create flag not set in create function"));
L
Linus Torvalds 已提交
166 167
	}

S
Steve French 已提交
168 169
	/* BB add processing to set equivalent of mode - e.g. via CreateX with
	   ACLs */
L
Linus Torvalds 已提交
170 171 172
	if (oplockEnabled)
		oplock = REQ_OPLOCK;

S
Steve French 已提交
173 174
	buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
	if (buf == NULL) {
L
Linus Torvalds 已提交
175 176 177 178
		kfree(full_path);
		FreeXid(xid);
		return -ENOMEM;
	}
S
Steve French 已提交
179
	if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS)
180
		rc = CIFSSMBOpen(xid, pTcon, full_path, disposition,
L
Linus Torvalds 已提交
181
			 desiredAccess, CREATE_NOT_DIR,
182 183
			 &fileHandle, &oplock, buf, cifs_sb->local_nls,
			 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
184 185 186
	else
		rc = -EIO; /* no NT SMB support fall into legacy open below */

S
Steve French 已提交
187
	if (rc == -EIO) {
188 189 190 191 192
		/* old server, retry the open legacy style */
		rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
			desiredAccess, CREATE_NOT_DIR,
			&fileHandle, &oplock, buf, cifs_sb->local_nls,
			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
S
Steve French 已提交
193
	}
L
Linus Torvalds 已提交
194
	if (rc) {
195
		cFYI(1, ("cifs_create returned 0x%x", rc));
L
Linus Torvalds 已提交
196 197 198
	} else {
		/* If Open reported that we actually created a file
		then we now have to set the mode if possible */
199
		if ((pTcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) {
S
Steve French 已提交
200
			mode &= ~current->fs->umask;
S
Steve French 已提交
201
			if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
L
Linus Torvalds 已提交
202
				CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode,
203 204
					(__u64)current->fsuid,
					(__u64)current->fsgid,
L
Linus Torvalds 已提交
205
					0 /* dev */,
S
Steve French 已提交
206 207
					cifs_sb->local_nls,
					cifs_sb->mnt_cifs_flags &
208
						CIFS_MOUNT_MAP_SPECIAL_CHR);
L
Linus Torvalds 已提交
209 210 211 212 213
			} else {
				CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode,
					(__u64)-1,
					(__u64)-1,
					0 /* dev */,
214
					cifs_sb->local_nls,
S
Steve French 已提交
215
					cifs_sb->mnt_cifs_flags &
216
						CIFS_MOUNT_MAP_SPECIAL_CHR);
L
Linus Torvalds 已提交
217
			}
S
Steve French 已提交
218
		} else {
S
Steve French 已提交
219 220 221 222 223
			/* BB implement mode setting via Windows security
			   descriptors e.g. */
			/* CIFSSMBWinSetPerms(xid,pTcon,path,mode,-1,-1,nls);*/

			/* Could set r/o dos attribute if mode & 0222 == 0 */
L
Linus Torvalds 已提交
224 225
		}

226 227
		/* server might mask mode so we have to query for it */
		if (pTcon->unix_ext)
L
Linus Torvalds 已提交
228
			rc = cifs_get_inode_info_unix(&newinode, full_path,
S
Steve French 已提交
229
						 inode->i_sb, xid);
L
Linus Torvalds 已提交
230 231
		else {
			rc = cifs_get_inode_info(&newinode, full_path,
232 233
						 buf, inode->i_sb, xid,
						 &fileHandle);
S
Steve French 已提交
234
			if (newinode) {
L
Linus Torvalds 已提交
235
				newinode->i_mode = mode;
S
Steve French 已提交
236 237
				if ((oplock & CIFS_CREATE_ACTION) &&
				    (cifs_sb->mnt_cifs_flags &
238 239 240 241 242
				     CIFS_MOUNT_SET_UID)) {
					newinode->i_uid = current->fsuid;
					newinode->i_gid = current->fsgid;
				}
			}
L
Linus Torvalds 已提交
243 244 245
		}

		if (rc != 0) {
246 247
			cFYI(1,
			     ("Create worked but get_inode_info failed rc = %d",
L
Linus Torvalds 已提交
248 249
			      rc));
		} else {
250 251 252 253
			if (pTcon->nocase)
				direntry->d_op = &cifs_ci_dentry_ops;
			else
				direntry->d_op = &cifs_dentry_ops;
L
Linus Torvalds 已提交
254 255
			d_instantiate(direntry, newinode);
		}
256 257
		if ((nd == NULL /* nfsd case - nfs srv does not set nd */) ||
			((nd->flags & LOOKUP_OPEN) == FALSE)) {
L
Linus Torvalds 已提交
258 259
			/* mknod case - do not leave file open */
			CIFSSMBClose(xid, pTcon, fileHandle);
S
Steve French 已提交
260
		} else if (newinode) {
261
			pCifsFile =
262
			   kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
S
Steve French 已提交
263

S
Steve French 已提交
264
			if (pCifsFile == NULL)
265 266 267 268 269 270 271
				goto cifs_create_out;
			pCifsFile->netfid = fileHandle;
			pCifsFile->pid = current->tgid;
			pCifsFile->pInode = newinode;
			pCifsFile->invalidHandle = FALSE;
			pCifsFile->closePend     = FALSE;
			init_MUTEX(&pCifsFile->fh_sem);
272
			mutex_init(&pCifsFile->lock_mutex);
273
			INIT_LIST_HEAD(&pCifsFile->llist);
S
Steve French 已提交
274
			atomic_set(&pCifsFile->wrtPending, 0);
275

S
Steve French 已提交
276
			/* set the following in open now
277 278
				pCifsFile->pfile = file; */
			write_lock(&GlobalSMBSeslock);
S
Steve French 已提交
279
			list_add(&pCifsFile->tlist, &pTcon->openFileList);
280
			pCifsInode = CIFS_I(newinode);
S
Steve French 已提交
281
			if (pCifsInode) {
L
Linus Torvalds 已提交
282
				/* if readable file instance put first in list*/
283
				if (write_only == TRUE) {
S
Steve French 已提交
284
					list_add_tail(&pCifsFile->flist,
285 286 287 288
						&pCifsInode->openFileList);
				} else {
					list_add(&pCifsFile->flist,
						&pCifsInode->openFileList);
L
Linus Torvalds 已提交
289
				}
S
Steve French 已提交
290
				if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
291 292
					pCifsInode->clientCanCacheAll = TRUE;
					pCifsInode->clientCanCacheRead = TRUE;
S
Steve French 已提交
293
					cFYI(1, ("Exclusive Oplock inode %p",
294
						newinode));
S
Steve French 已提交
295
				} else if ((oplock & 0xF) == OPLOCK_READ)
296
					pCifsInode->clientCanCacheRead = TRUE;
L
Linus Torvalds 已提交
297
			}
298
			write_unlock(&GlobalSMBSeslock);
L
Linus Torvalds 已提交
299
		}
S
Steve French 已提交
300
	}
301 302 303
cifs_create_out:
	kfree(buf);
	kfree(full_path);
L
Linus Torvalds 已提交
304 305 306 307
	FreeXid(xid);
	return rc;
}

S
Steve French 已提交
308 309
int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
		dev_t device_number)
L
Linus Torvalds 已提交
310 311 312 313 314 315
{
	int rc = -EPERM;
	int xid;
	struct cifs_sb_info *cifs_sb;
	struct cifsTconInfo *pTcon;
	char *full_path = NULL;
S
Steve French 已提交
316
	struct inode *newinode = NULL;
L
Linus Torvalds 已提交
317 318 319 320 321 322 323 324 325 326

	if (!old_valid_dev(device_number))
		return -EINVAL;

	xid = GetXid();

	cifs_sb = CIFS_SB(inode->i_sb);
	pTcon = cifs_sb->tcon;

	full_path = build_path_from_dentry(direntry);
S
Steve French 已提交
327
	if (full_path == NULL)
L
Linus Torvalds 已提交
328
		rc = -ENOMEM;
329
	else if (pTcon->unix_ext) {
S
Steve French 已提交
330
		mode &= ~current->fs->umask;
S
Steve French 已提交
331
		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
L
Linus Torvalds 已提交
332
			rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path,
S
Steve French 已提交
333 334
				mode, (__u64)current->fsuid,
				(__u64)current->fsgid,
335
				device_number, cifs_sb->local_nls,
S
Steve French 已提交
336
				cifs_sb->mnt_cifs_flags &
337
					CIFS_MOUNT_MAP_SPECIAL_CHR);
L
Linus Torvalds 已提交
338 339 340
		} else {
			rc = CIFSSMBUnixSetPerms(xid, pTcon,
				full_path, mode, (__u64)-1, (__u64)-1,
341
				device_number, cifs_sb->local_nls,
S
Steve French 已提交
342
				cifs_sb->mnt_cifs_flags &
343
					CIFS_MOUNT_MAP_SPECIAL_CHR);
L
Linus Torvalds 已提交
344 345
		}

S
Steve French 已提交
346
		if (!rc) {
L
Linus Torvalds 已提交
347
			rc = cifs_get_inode_info_unix(&newinode, full_path,
S
Steve French 已提交
348
						inode->i_sb, xid);
349 350 351 352
			if (pTcon->nocase)
				direntry->d_op = &cifs_ci_dentry_ops;
			else
				direntry->d_op = &cifs_dentry_ops;
S
Steve French 已提交
353
			if (rc == 0)
L
Linus Torvalds 已提交
354 355
				d_instantiate(direntry, newinode);
		}
356
	} else {
S
Steve French 已提交
357
		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
358 359
			int oplock = 0;
			u16 fileHandle;
S
Steve French 已提交
360
			FILE_ALL_INFO *buf;
361

S
Steve French 已提交
362
			cFYI(1, ("sfu compat create special file"));
363

S
Steve French 已提交
364 365
			buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
			if (buf == NULL) {
366 367 368 369 370 371 372
				kfree(full_path);
				FreeXid(xid);
				return -ENOMEM;
			}

			rc = CIFSSMBOpen(xid, pTcon, full_path,
					 FILE_CREATE, /* fail if exists */
S
Steve French 已提交
373
					 GENERIC_WRITE /* BB would
374 375 376 377 378 379
					  WRITE_OWNER | WRITE_DAC be better? */,
					 /* Create a file and set the
					    file attribute to SYSTEM */
					 CREATE_NOT_DIR | CREATE_OPTION_SPECIAL,
					 &fileHandle, &oplock, buf,
					 cifs_sb->local_nls,
S
Steve French 已提交
380
					 cifs_sb->mnt_cifs_flags &
381 382
					    CIFS_MOUNT_MAP_SPECIAL_CHR);

383 384
			/* BB FIXME - add handling for backlevel servers
			   which need legacy open and check for all
S
Steve French 已提交
385 386
			   calls to SMBOpen for fallback to SMBLeagcyOpen */
			if (!rc) {
387
				/* BB Do not bother to decode buf since no
388 389
				   local inode yet to put timestamps in,
				   but we can reuse it safely */
390
				unsigned int bytes_written;
391 392
				struct win_dev *pdev;
				pdev = (struct win_dev *)buf;
S
Steve French 已提交
393
				if (S_ISCHR(mode)) {
394 395 396
					memcpy(pdev->type, "IntxCHR", 8);
					pdev->major =
					      cpu_to_le64(MAJOR(device_number));
S
Steve French 已提交
397
					pdev->minor =
398 399 400 401 402 403
					      cpu_to_le64(MINOR(device_number));
					rc = CIFSSMBWrite(xid, pTcon,
						fileHandle,
						sizeof(struct win_dev),
						0, &bytes_written, (char *)pdev,
						NULL, 0);
S
Steve French 已提交
404
				} else if (S_ISBLK(mode)) {
405 406 407 408 409 410 411 412 413 414 415
					memcpy(pdev->type, "IntxBLK", 8);
					pdev->major =
					      cpu_to_le64(MAJOR(device_number));
					pdev->minor =
					      cpu_to_le64(MINOR(device_number));
					rc = CIFSSMBWrite(xid, pTcon,
						fileHandle,
						sizeof(struct win_dev),
						0, &bytes_written, (char *)pdev,
						NULL, 0);
				} /* else if(S_ISFIFO */
416 417 418 419
				CIFSSMBClose(xid, pTcon, fileHandle);
				d_drop(direntry);
			}
			kfree(buf);
420 421
			/* add code here to set EAs */
		}
L
Linus Torvalds 已提交
422 423
	}

424
	kfree(full_path);
L
Linus Torvalds 已提交
425 426 427 428 429 430
	FreeXid(xid);
	return rc;
}


struct dentry *
S
Steve French 已提交
431 432
cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
	    struct nameidata *nd)
L
Linus Torvalds 已提交
433 434 435 436 437 438 439 440 441 442
{
	int xid;
	int rc = 0; /* to get around spurious gcc warning, set to zero here */
	struct cifs_sb_info *cifs_sb;
	struct cifsTconInfo *pTcon;
	struct inode *newInode = NULL;
	char *full_path = NULL;

	xid = GetXid();

443
	cFYI(1, (" parent inode = 0x%p name is: %s and dentry = 0x%p",
L
Linus Torvalds 已提交
444 445 446 447 448 449 450
	      parent_dir_inode, direntry->d_name.name, direntry));

	/* check whether path exists */

	cifs_sb = CIFS_SB(parent_dir_inode->i_sb);
	pTcon = cifs_sb->tcon;

451 452 453 454 455 456 457 458 459 460 461 462 463 464
	/*
	 * Don't allow the separator character in a path component.
	 * The VFS will not allow "/", but "\" is allowed by posix.
	 */
	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
		int i;
		for (i = 0; i < direntry->d_name.len; i++)
			if (direntry->d_name.name[i] == '\\') {
				cFYI(1, ("Invalid file name"));
				FreeXid(xid);
				return ERR_PTR(-EINVAL);
			}
	}

L
Linus Torvalds 已提交
465 466 467 468
	/* can not grab the rename sem here since it would
	deadlock in the cases (beginning of sys_rename itself)
	in which we already have the sb rename sem */
	full_path = build_path_from_dentry(direntry);
S
Steve French 已提交
469
	if (full_path == NULL) {
L
Linus Torvalds 已提交
470 471 472 473 474 475 476 477 478 479 480 481
		FreeXid(xid);
		return ERR_PTR(-ENOMEM);
	}

	if (direntry->d_inode != NULL) {
		cFYI(1, (" non-NULL inode in lookup"));
	} else {
		cFYI(1, (" NULL inode in lookup"));
	}
	cFYI(1,
	     (" Full path: %s inode = 0x%p", full_path, direntry->d_inode));

482
	if (pTcon->unix_ext)
L
Linus Torvalds 已提交
483
		rc = cifs_get_inode_info_unix(&newInode, full_path,
S
Steve French 已提交
484
					      parent_dir_inode->i_sb, xid);
L
Linus Torvalds 已提交
485 486
	else
		rc = cifs_get_inode_info(&newInode, full_path, NULL,
487
					 parent_dir_inode->i_sb, xid, NULL);
L
Linus Torvalds 已提交
488 489

	if ((rc == 0) && (newInode != NULL)) {
490 491 492 493
		if (pTcon->nocase)
			direntry->d_op = &cifs_ci_dentry_ops;
		else
			direntry->d_op = &cifs_dentry_ops;
L
Linus Torvalds 已提交
494 495
		d_add(direntry, newInode);

S
Steve French 已提交
496
		/* since paths are not looked up by component - the parent
497
		   directories are presumed to be good here */
L
Linus Torvalds 已提交
498 499 500 501
		renew_parental_timestamps(direntry);

	} else if (rc == -ENOENT) {
		rc = 0;
502 503 504 505 506
		direntry->d_time = jiffies;
		if (pTcon->nocase)
			direntry->d_op = &cifs_ci_dentry_ops;
		else
			direntry->d_op = &cifs_dentry_ops;
L
Linus Torvalds 已提交
507
		d_add(direntry, NULL);
S
Steve French 已提交
508 509
	/*	if it was once a directory (but how can we tell?) we could do
		shrink_dcache_parent(direntry); */
510 511 512 513
	} else if (rc != -EACCES) {
		cERROR(1, ("Unexpected lookup error %d", rc));
		/* We special case check for Access Denied - since that
		is a common return code */
L
Linus Torvalds 已提交
514 515
	}

516
	kfree(full_path);
L
Linus Torvalds 已提交
517 518 519 520 521 522 523 524 525 526
	FreeXid(xid);
	return ERR_PTR(rc);
}

static int
cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
{
	int isValid = 1;

	if (direntry->d_inode) {
S
Steve French 已提交
527
		if (cifs_revalidate(direntry))
L
Linus Torvalds 已提交
528 529
			return 0;
	} else {
530 531
		cFYI(1, ("neg dentry 0x%p name = %s",
			 direntry, direntry->d_name.name));
S
Steve French 已提交
532
		if (time_after(jiffies, direntry->d_time + HZ) ||
533 534 535
			!lookupCacheEnabled) {
			d_drop(direntry);
			isValid = 0;
S
Steve French 已提交
536
		}
L
Linus Torvalds 已提交
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
	}

	return isValid;
}

/* static int cifs_d_delete(struct dentry *direntry)
{
	int rc = 0;

	cFYI(1, ("In cifs d_delete, name = %s", direntry->d_name.name));

	return rc;
}     */

struct dentry_operations cifs_dentry_ops = {
	.d_revalidate = cifs_d_revalidate,
S
Steve French 已提交
553
/* d_delete:       cifs_d_delete,      */ /* not needed except for debugging */
L
Linus Torvalds 已提交
554
};
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582

static int cifs_ci_hash(struct dentry *dentry, struct qstr *q)
{
	struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
	unsigned long hash;
	int i;

	hash = init_name_hash();
	for (i = 0; i < q->len; i++)
		hash = partial_name_hash(nls_tolower(codepage, q->name[i]),
					 hash);
	q->hash = end_name_hash(hash);

	return 0;
}

static int cifs_ci_compare(struct dentry *dentry, struct qstr *a,
			   struct qstr *b)
{
	struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;

	if ((a->len == b->len) &&
	    (nls_strnicmp(codepage, a->name, b->name, a->len) == 0)) {
		/*
		 * To preserve case, don't let an existing negative dentry's
		 * case take precedence.  If a is not a negative dentry, this
		 * should have no side effects
		 */
S
Steve French 已提交
583
		memcpy(a->name, b->name, a->len);
584 585 586 587 588 589 590 591 592 593
		return 0;
	}
	return 1;
}

struct dentry_operations cifs_ci_dentry_ops = {
	.d_revalidate = cifs_d_revalidate,
	.d_hash = cifs_ci_hash,
	.d_compare = cifs_ci_compare,
};