blob: e7ae78b66fa15b89162b18a12d76c90f48b8b508 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * fs/cifs/dir.c
3 *
4 * vfs operations that deal with dentries
Steve French5fdae1f2007-06-05 18:30:44 +00005 *
Steve Frenchc3b2a0c2009-02-20 04:32:45 +00006 * Copyright (C) International Business Machines Corp., 2002,2009
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * Author(s): Steve French (sfrench@us.ibm.com)
8 *
9 * This library is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published
11 * by the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
17 * the GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23#include <linux/fs.h>
24#include <linux/stat.h>
25#include <linux/slab.h>
26#include <linux/namei.h>
Jeff Layton3bc303c2009-09-21 06:47:50 -040027#include <linux/mount.h>
Jeff Layton6ca9f3b2010-06-16 13:40:16 -040028#include <linux/file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include "cifsfs.h"
30#include "cifspdu.h"
31#include "cifsglob.h"
32#include "cifsproto.h"
33#include "cifs_debug.h"
34#include "cifs_fs_sb.h"
35
Steve French99ee4db2007-02-27 05:35:17 +000036static void
Linus Torvalds1da177e2005-04-16 15:20:36 -070037renew_parental_timestamps(struct dentry *direntry)
38{
Steve French5fdae1f2007-06-05 18:30:44 +000039 /* BB check if there is a way to get the kernel to do this or if we
40 really need this */
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 do {
42 direntry->d_time = jiffies;
43 direntry = direntry->d_parent;
Steve French5fdae1f2007-06-05 18:30:44 +000044 } while (!IS_ROOT(direntry));
Linus Torvalds1da177e2005-04-16 15:20:36 -070045}
46
47/* Note: caller must free return buffer */
48char *
49build_path_from_dentry(struct dentry *direntry)
50{
51 struct dentry *temp;
Steve French2fe87f02006-09-21 07:02:52 +000052 int namelen;
53 int pplen;
Steve French646dd532008-05-15 01:50:56 +000054 int dfsplen;
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 char *full_path;
Steve French88274812006-03-09 22:21:45 +000056 char dirsep;
Steve French646dd532008-05-15 01:50:56 +000057 struct cifs_sb_info *cifs_sb;
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
Steve French5fdae1f2007-06-05 18:30:44 +000059 if (direntry == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 return NULL; /* not much we can do if dentry is freed and
61 we need to reopen the file after it was closed implicitly
62 when the server crashed */
63
Steve French646dd532008-05-15 01:50:56 +000064 cifs_sb = CIFS_SB(direntry->d_sb);
65 dirsep = CIFS_DIR_SEP(cifs_sb);
66 pplen = cifs_sb->prepathlen;
67 if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS))
68 dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1);
69 else
70 dfsplen = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070071cifs_bp_rename_retry:
Steve French646dd532008-05-15 01:50:56 +000072 namelen = pplen + dfsplen;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 for (temp = direntry; !IS_ROOT(temp);) {
74 namelen += (1 + temp->d_name.len);
75 temp = temp->d_parent;
Steve French5fdae1f2007-06-05 18:30:44 +000076 if (temp == NULL) {
Joe Perchesb6b38f72010-04-21 03:50:45 +000077 cERROR(1, "corrupt dentry");
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 return NULL;
79 }
80 }
81
82 full_path = kmalloc(namelen+1, GFP_KERNEL);
Steve French5fdae1f2007-06-05 18:30:44 +000083 if (full_path == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 return full_path;
85 full_path[namelen] = 0; /* trailing null */
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 for (temp = direntry; !IS_ROOT(temp);) {
87 namelen -= 1 + temp->d_name.len;
88 if (namelen < 0) {
89 break;
90 } else {
Steve French7f573562005-08-30 11:32:14 -070091 full_path[namelen] = dirsep;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 strncpy(full_path + namelen + 1, temp->d_name.name,
93 temp->d_name.len);
Joe Perchesb6b38f72010-04-21 03:50:45 +000094 cFYI(0, "name: %s", full_path + namelen);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 }
96 temp = temp->d_parent;
Steve French5fdae1f2007-06-05 18:30:44 +000097 if (temp == NULL) {
Joe Perchesb6b38f72010-04-21 03:50:45 +000098 cERROR(1, "corrupt dentry");
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 kfree(full_path);
100 return NULL;
101 }
102 }
Steve French646dd532008-05-15 01:50:56 +0000103 if (namelen != pplen + dfsplen) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000104 cERROR(1, "did not end path lookup where expected namelen is %d",
105 namelen);
Steve French5fdae1f2007-06-05 18:30:44 +0000106 /* presumably this is only possible if racing with a rename
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 of one of the parent directories (we can not lock the dentries
108 above us to prevent this, but retrying should be harmless) */
109 kfree(full_path);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 goto cifs_bp_rename_retry;
111 }
Steve French2fe87f02006-09-21 07:02:52 +0000112 /* DIR_SEP already set for byte 0 / vs \ but not for
113 subsequent slashes in prepath which currently must
114 be entered the right way - not sure if there is an alternative
115 since the '\' is a valid posix character so we can not switch
116 those safely to '/' if any are found in the middle of the prepath */
117 /* BB test paths to Windows with '/' in the midst of prepath */
Steve French646dd532008-05-15 01:50:56 +0000118
119 if (dfsplen) {
120 strncpy(full_path, cifs_sb->tcon->treeName, dfsplen);
121 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
122 int i;
123 for (i = 0; i < dfsplen; i++) {
124 if (full_path[i] == '\\')
125 full_path[i] = '/';
126 }
127 }
128 }
129 strncpy(full_path + dfsplen, CIFS_SB(direntry->d_sb)->prepath, pplen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 return full_path;
131}
132
Suresh Jayaramanfae683f2010-05-10 20:00:05 +0530133/*
134 * When called with struct file pointer set to NULL, there is no way we could
135 * update file->private_data, but getting it stuck on openFileList provides a
136 * way to access it from cifs_fill_filedata and thereby set file->private_data
137 * from cifs_open.
138 */
Jeff Layton086f68b2009-09-21 14:08:18 -0400139struct cifsFileInfo *
140cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle,
141 struct file *file, struct vfsmount *mnt, unsigned int oflags)
Steve Frencha6ce4932009-04-09 01:14:32 +0000142{
143 int oplock = 0;
144 struct cifsFileInfo *pCifsFile;
145 struct cifsInodeInfo *pCifsInode;
Jeff Layton3bc303c2009-09-21 06:47:50 -0400146 struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb);
Steve Frencha6ce4932009-04-09 01:14:32 +0000147
148 pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
Steve Frencha6ce4932009-04-09 01:14:32 +0000149 if (pCifsFile == NULL)
Jeff Layton086f68b2009-09-21 14:08:18 -0400150 return pCifsFile;
Steve Frencha6ce4932009-04-09 01:14:32 +0000151
152 if (oplockEnabled)
153 oplock = REQ_OPLOCK;
154
155 pCifsFile->netfid = fileHandle;
156 pCifsFile->pid = current->tgid;
Jeff Layton48541bd2009-09-12 11:54:29 -0400157 pCifsFile->pInode = igrab(newinode);
Jeff Layton3bc303c2009-09-21 06:47:50 -0400158 pCifsFile->mnt = mnt;
Jeff Layton086f68b2009-09-21 14:08:18 -0400159 pCifsFile->pfile = file;
Steve Frencha6ce4932009-04-09 01:14:32 +0000160 pCifsFile->invalidHandle = false;
Steve Frenchbc8cd432009-04-12 18:18:40 +0000161 pCifsFile->closePend = false;
Steve Frencha6ce4932009-04-09 01:14:32 +0000162 mutex_init(&pCifsFile->fh_mutex);
163 mutex_init(&pCifsFile->lock_mutex);
164 INIT_LIST_HEAD(&pCifsFile->llist);
Dave Kleikamp6ab409b2009-08-31 11:07:12 -0400165 atomic_set(&pCifsFile->count, 1);
Jeff Layton3bc303c2009-09-21 06:47:50 -0400166 slow_work_init(&pCifsFile->oplock_break, &cifs_oplock_break_ops);
Steve Frencha6ce4932009-04-09 01:14:32 +0000167
Steve Frencha6ce4932009-04-09 01:14:32 +0000168 write_lock(&GlobalSMBSeslock);
Jeff Layton3bc303c2009-09-21 06:47:50 -0400169 list_add(&pCifsFile->tlist, &cifs_sb->tcon->openFileList);
Steve Frencha6ce4932009-04-09 01:14:32 +0000170 pCifsInode = CIFS_I(newinode);
171 if (pCifsInode) {
172 /* if readable file instance put first in list*/
Jeff Layton086f68b2009-09-21 14:08:18 -0400173 if (oflags & FMODE_READ)
174 list_add(&pCifsFile->flist, &pCifsInode->openFileList);
175 else
Steve Frencha6ce4932009-04-09 01:14:32 +0000176 list_add_tail(&pCifsFile->flist,
177 &pCifsInode->openFileList);
Steve Frenchbc8cd432009-04-12 18:18:40 +0000178
Steve Frencha6ce4932009-04-09 01:14:32 +0000179 if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
180 pCifsInode->clientCanCacheAll = true;
181 pCifsInode->clientCanCacheRead = true;
Joe Perchesb6b38f72010-04-21 03:50:45 +0000182 cFYI(1, "Exclusive Oplock inode %p", newinode);
Steve Frencha6ce4932009-04-09 01:14:32 +0000183 } else if ((oplock & 0xF) == OPLOCK_READ)
Steve Frenchbc8cd432009-04-12 18:18:40 +0000184 pCifsInode->clientCanCacheRead = true;
Steve Frencha6ce4932009-04-09 01:14:32 +0000185 }
186 write_unlock(&GlobalSMBSeslock);
Jeff Layton086f68b2009-09-21 14:08:18 -0400187
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400188 file->private_data = pCifsFile;
189
Jeff Layton086f68b2009-09-21 14:08:18 -0400190 return pCifsFile;
Steve Frencha6ce4932009-04-09 01:14:32 +0000191}
192
Steve French7fc8f4e2009-02-23 20:43:11 +0000193int cifs_posix_open(char *full_path, struct inode **pinode,
Jeff Layton2422f672010-06-16 13:40:16 -0400194 struct super_block *sb, int mode, int oflags,
Steve Frenchfa588e02010-04-22 19:21:55 +0000195 __u32 *poplock, __u16 *pnetfid, int xid)
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000196{
197 int rc;
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000198 FILE_UNIX_BASIC_INFO *presp_data;
199 __u32 posix_flags = 0;
Steve Frenchfa588e02010-04-22 19:21:55 +0000200 struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
Jeff Laytoncc0bad72009-06-25 00:56:52 -0400201 struct cifs_fattr fattr;
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000202
Joe Perchesb6b38f72010-04-21 03:50:45 +0000203 cFYI(1, "posix open %s", full_path);
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000204
205 presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
206 if (presp_data == NULL)
207 return -ENOMEM;
208
209/* So far cifs posix extensions can only map the following flags.
210 There are other valid fmode oflags such as FMODE_LSEEK, FMODE_PREAD, but
211 so far we do not seem to need them, and we can treat them as local only */
212 if ((oflags & (FMODE_READ | FMODE_WRITE)) ==
213 (FMODE_READ | FMODE_WRITE))
214 posix_flags = SMB_O_RDWR;
215 else if (oflags & FMODE_READ)
216 posix_flags = SMB_O_RDONLY;
217 else if (oflags & FMODE_WRITE)
218 posix_flags = SMB_O_WRONLY;
219 if (oflags & O_CREAT)
220 posix_flags |= SMB_O_CREAT;
221 if (oflags & O_EXCL)
222 posix_flags |= SMB_O_EXCL;
223 if (oflags & O_TRUNC)
224 posix_flags |= SMB_O_TRUNC;
Christoph Hellwig6b2f3d12009-10-27 11:05:28 +0100225 /* be safe and imply O_SYNC for O_DSYNC */
226 if (oflags & O_DSYNC)
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000227 posix_flags |= SMB_O_SYNC;
228 if (oflags & O_DIRECTORY)
229 posix_flags |= SMB_O_DIRECTORY;
230 if (oflags & O_NOFOLLOW)
231 posix_flags |= SMB_O_NOFOLLOW;
232 if (oflags & O_DIRECT)
233 posix_flags |= SMB_O_DIRECT;
234
Steve French703a3b82009-05-21 22:21:53 +0000235 mode &= ~current_umask();
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000236 rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode,
Jeff Layton590a3fe2009-09-12 11:54:28 -0400237 pnetfid, presp_data, poplock, full_path,
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000238 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
239 CIFS_MOUNT_MAP_SPECIAL_CHR);
240 if (rc)
241 goto posix_open_ret;
242
243 if (presp_data->Type == cpu_to_le32(-1))
244 goto posix_open_ret; /* open ok, caller does qpathinfo */
245
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000246 if (!pinode)
247 goto posix_open_ret; /* caller does not need info */
248
Jeff Laytoncc0bad72009-06-25 00:56:52 -0400249 cifs_unix_basic_to_fattr(&fattr, presp_data, cifs_sb);
250
251 /* get new inode and set it up */
Steve French85a6dac2009-04-01 05:22:00 +0000252 if (*pinode == NULL) {
Jeff Layton4065c802010-05-17 07:18:58 -0400253 cifs_fill_uniqueid(sb, &fattr);
Steve Frenchfa588e02010-04-22 19:21:55 +0000254 *pinode = cifs_iget(sb, &fattr);
Jeff Laytoncc0bad72009-06-25 00:56:52 -0400255 if (!*pinode) {
256 rc = -ENOMEM;
257 goto posix_open_ret;
258 }
259 } else {
260 cifs_fattr_to_inode(*pinode, &fattr);
Steve French85a6dac2009-04-01 05:22:00 +0000261 }
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000262
263posix_open_ret:
264 kfree(presp_data);
265 return rc;
266}
267
Steve Frenchf818dd52009-01-19 02:38:35 +0000268static void setup_cifs_dentry(struct cifsTconInfo *tcon,
269 struct dentry *direntry,
270 struct inode *newinode)
271{
272 if (tcon->nocase)
273 direntry->d_op = &cifs_ci_dentry_ops;
274 else
275 direntry->d_op = &cifs_dentry_ops;
276 d_instantiate(direntry, newinode);
277}
278
Steve French39798772006-05-31 22:40:51 +0000279/* Inode operations in similar order to how they appear in Linux file fs.h */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280
281int
282cifs_create(struct inode *inode, struct dentry *direntry, int mode,
283 struct nameidata *nd)
284{
285 int rc = -ENOENT;
286 int xid;
Jeff Layton67750fb2008-05-09 22:28:02 +0000287 int create_options = CREATE_NOT_DIR;
Jeff Layton590a3fe2009-09-12 11:54:28 -0400288 __u32 oplock = 0;
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000289 int oflags;
290 /*
291 * BB below access is probably too much for mknod to request
292 * but we have to do query and setpathinfo so requesting
293 * less could fail (unless we want to request getatr and setatr
294 * permissions (only). At least for POSIX we do not have to
295 * request so much.
296 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 int desiredAccess = GENERIC_READ | GENERIC_WRITE;
298 __u16 fileHandle;
299 struct cifs_sb_info *cifs_sb;
Steve Frenchf818dd52009-01-19 02:38:35 +0000300 struct cifsTconInfo *tcon;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 char *full_path = NULL;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000302 FILE_ALL_INFO *buf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 struct inode *newinode = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 int disposition = FILE_OVERWRITE_IF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305
306 xid = GetXid();
307
308 cifs_sb = CIFS_SB(inode->i_sb);
Steve Frenchf818dd52009-01-19 02:38:35 +0000309 tcon = cifs_sb->tcon;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 full_path = build_path_from_dentry(direntry);
Steve French5fdae1f2007-06-05 18:30:44 +0000312 if (full_path == NULL) {
Suresh Jayaraman0f3bc092009-06-25 18:12:34 +0530313 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 FreeXid(xid);
Suresh Jayaraman0f3bc092009-06-25 18:12:34 +0530315 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 }
317
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000318 if (oplockEnabled)
319 oplock = REQ_OPLOCK;
320
321 if (nd && (nd->flags & LOOKUP_OPEN))
322 oflags = nd->intent.open.flags;
323 else
Steve Frenchfa588e02010-04-22 19:21:55 +0000324 oflags = FMODE_READ | SMB_O_CREAT;
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000325
326 if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
327 (CIFS_UNIX_POSIX_PATH_OPS_CAP &
328 le64_to_cpu(tcon->fsUnixInfo.Capability))) {
Steve Frenchfa588e02010-04-22 19:21:55 +0000329 rc = cifs_posix_open(full_path, &newinode,
Steve Frenchfa588e02010-04-22 19:21:55 +0000330 inode->i_sb, mode, oflags, &oplock, &fileHandle, xid);
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000331 /* EIO could indicate that (posix open) operation is not
332 supported, despite what server claimed in capability
333 negotation. EREMOTE indicates DFS junction, which is not
334 handled in posix open */
335
Steve French90e4ee52009-05-08 03:04:30 +0000336 if (rc == 0) {
Steve French90e4ee52009-05-08 03:04:30 +0000337 if (newinode == NULL) /* query inode info */
338 goto cifs_create_get_file_info;
339 else /* success, no need to query */
340 goto cifs_create_set_dentry;
341 } else if ((rc != -EIO) && (rc != -EREMOTE) &&
Steve French703a3b82009-05-21 22:21:53 +0000342 (rc != -EOPNOTSUPP) && (rc != -EINVAL))
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000343 goto cifs_create_out;
344 /* else fallthrough to retry, using older open call, this is
345 case where server does not support this SMB level, and
346 falsely claims capability (also get here for DFS case
347 which should be rare for path not covered on files) */
348 }
Steve Frenchf818dd52009-01-19 02:38:35 +0000349
Steve French5fdae1f2007-06-05 18:30:44 +0000350 if (nd && (nd->flags & LOOKUP_OPEN)) {
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000351 /* if the file is going to stay open, then we
352 need to set the desired access properly */
Miklos Szeredie08fc042005-09-06 15:18:26 -0700353 desiredAccess = 0;
354 if (oflags & FMODE_READ)
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000355 desiredAccess |= GENERIC_READ; /* is this too little? */
Jeff Layton086f68b2009-09-21 14:08:18 -0400356 if (oflags & FMODE_WRITE)
Miklos Szeredie08fc042005-09-06 15:18:26 -0700357 desiredAccess |= GENERIC_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358
Steve French5fdae1f2007-06-05 18:30:44 +0000359 if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 disposition = FILE_CREATE;
Steve French5fdae1f2007-06-05 18:30:44 +0000361 else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 disposition = FILE_OVERWRITE_IF;
Steve French5fdae1f2007-06-05 18:30:44 +0000363 else if ((oflags & O_CREAT) == O_CREAT)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 disposition = FILE_OPEN_IF;
Steve Frenchad7a2922008-02-07 23:25:02 +0000365 else
Joe Perchesb6b38f72010-04-21 03:50:45 +0000366 cFYI(1, "Create flag not set in create function");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 }
368
Steve French5fdae1f2007-06-05 18:30:44 +0000369 /* BB add processing to set equivalent of mode - e.g. via CreateX with
370 ACLs */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
Steve French5fdae1f2007-06-05 18:30:44 +0000372 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
373 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 kfree(full_path);
375 FreeXid(xid);
376 return -ENOMEM;
377 }
Jeff Layton67750fb2008-05-09 22:28:02 +0000378
Jeff Layton67750fb2008-05-09 22:28:02 +0000379 /*
380 * if we're not using unix extensions, see if we need to set
381 * ATTR_READONLY on the create call
382 */
Steve Frenchf818dd52009-01-19 02:38:35 +0000383 if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
Jeff Layton67750fb2008-05-09 22:28:02 +0000384 create_options |= CREATE_OPTION_READONLY;
385
Steve French5fdae1f2007-06-05 18:30:44 +0000386 if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS)
Steve Frenchf818dd52009-01-19 02:38:35 +0000387 rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
Jeff Layton67750fb2008-05-09 22:28:02 +0000388 desiredAccess, create_options,
Steve French737b7582005-04-28 22:41:06 -0700389 &fileHandle, &oplock, buf, cifs_sb->local_nls,
390 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
Steve French5bafd762006-06-07 00:18:43 +0000391 else
392 rc = -EIO; /* no NT SMB support fall into legacy open below */
393
Steve French5fdae1f2007-06-05 18:30:44 +0000394 if (rc == -EIO) {
Steve Frencha9d02ad2005-08-24 23:06:05 -0700395 /* old server, retry the open legacy style */
Steve Frenchf818dd52009-01-19 02:38:35 +0000396 rc = SMBLegacyOpen(xid, tcon, full_path, disposition,
Jeff Layton67750fb2008-05-09 22:28:02 +0000397 desiredAccess, create_options,
Steve Frencha9d02ad2005-08-24 23:06:05 -0700398 &fileHandle, &oplock, buf, cifs_sb->local_nls,
399 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
Steve French5fdae1f2007-06-05 18:30:44 +0000400 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000402 cFYI(1, "cifs_create returned 0x%x", rc);
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000403 goto cifs_create_out;
404 }
405
406 /* If Open reported that we actually created a file
407 then we now have to set the mode if possible */
408 if ((tcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) {
409 struct cifs_unix_set_info_args args = {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400410 .mode = mode,
411 .ctime = NO_CHANGE_64,
412 .atime = NO_CHANGE_64,
413 .mtime = NO_CHANGE_64,
414 .device = 0,
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000415 };
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400416
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000417 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
418 args.uid = (__u64) current_fsuid();
419 if (inode->i_mode & S_ISGID)
420 args.gid = (__u64) inode->i_gid;
421 else
422 args.gid = (__u64) current_fsgid();
Steve French3ce53fc2007-06-08 14:55:14 +0000423 } else {
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000424 args.uid = NO_CHANGE_64;
425 args.gid = NO_CHANGE_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 }
Jeff Layton01ea95e2009-07-09 20:02:49 -0400427 CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
428 cifs_sb->local_nls,
429 cifs_sb->mnt_cifs_flags &
430 CIFS_MOUNT_MAP_SPECIAL_CHR);
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000431 } else {
432 /* BB implement mode setting via Windows security
433 descriptors e.g. */
434 /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000436 /* Could set r/o dos attribute if mode & 0222 == 0 */
437 }
438
439cifs_create_get_file_info:
440 /* server might mask mode so we have to query for it */
441 if (tcon->unix_ext)
442 rc = cifs_get_inode_info_unix(&newinode, full_path,
443 inode->i_sb, xid);
444 else {
445 rc = cifs_get_inode_info(&newinode, full_path, buf,
446 inode->i_sb, xid, &fileHandle);
447 if (newinode) {
448 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
449 newinode->i_mode = mode;
450 if ((oplock & CIFS_CREATE_ACTION) &&
451 (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) {
452 newinode->i_uid = current_fsuid();
453 if (inode->i_mode & S_ISGID)
454 newinode->i_gid = inode->i_gid;
455 else
456 newinode->i_gid = current_fsgid();
Steve French6473a552005-11-29 20:20:10 -0800457 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 }
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000459 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000461cifs_create_set_dentry:
462 if (rc == 0)
463 setup_cifs_dentry(tcon, direntry, newinode);
464 else
Joe Perchesb6b38f72010-04-21 03:50:45 +0000465 cFYI(1, "Create worked, get_inode_info failed rc = %d", rc);
Steve Frenchf818dd52009-01-19 02:38:35 +0000466
Jeff Layton2422f672010-06-16 13:40:16 -0400467 if (newinode && nd && (nd->flags & LOOKUP_OPEN)) {
Suresh Jayaramanfdb36032010-05-11 09:46:46 +0530468 struct cifsFileInfo *pfile_info;
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400469 struct file *filp;
470
471 filp = lookup_instantiate_filp(nd, direntry, generic_file_open);
472 if (IS_ERR(filp)) {
473 rc = PTR_ERR(filp);
474 CIFSSMBClose(xid, tcon, fileHandle);
475 goto cifs_create_out;
476 }
477
478 pfile_info = cifs_new_fileinfo(newinode, fileHandle, filp,
Suresh Jayaramanfdb36032010-05-11 09:46:46 +0530479 nd->path.mnt, oflags);
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400480 if (pfile_info == NULL) {
481 fput(filp);
482 CIFSSMBClose(xid, tcon, fileHandle);
Suresh Jayaramanfdb36032010-05-11 09:46:46 +0530483 rc = -ENOMEM;
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400484 }
Jeff Layton2422f672010-06-16 13:40:16 -0400485 } else {
486 CIFSSMBClose(xid, tcon, fileHandle);
Steve French5fdae1f2007-06-05 18:30:44 +0000487 }
Jeff Layton2422f672010-06-16 13:40:16 -0400488
Steve Frenchd14537f12005-04-28 22:41:05 -0700489cifs_create_out:
490 kfree(buf);
491 kfree(full_path);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 FreeXid(xid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 return rc;
494}
495
Steve French5fdae1f2007-06-05 18:30:44 +0000496int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
497 dev_t device_number)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498{
499 int rc = -EPERM;
500 int xid;
501 struct cifs_sb_info *cifs_sb;
502 struct cifsTconInfo *pTcon;
503 char *full_path = NULL;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000504 struct inode *newinode = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505
506 if (!old_valid_dev(device_number))
507 return -EINVAL;
508
509 xid = GetXid();
510
511 cifs_sb = CIFS_SB(inode->i_sb);
512 pTcon = cifs_sb->tcon;
513
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 full_path = build_path_from_dentry(direntry);
Steve French5fdae1f2007-06-05 18:30:44 +0000515 if (full_path == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 rc = -ENOMEM;
Steve Frenchc18c8422007-07-18 23:21:09 +0000517 else if (pTcon->unix_ext) {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400518 struct cifs_unix_set_info_args args = {
Al Viroce3b0f82009-03-29 19:08:22 -0400519 .mode = mode & ~current_umask(),
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400520 .ctime = NO_CHANGE_64,
521 .atime = NO_CHANGE_64,
522 .mtime = NO_CHANGE_64,
523 .device = device_number,
524 };
Steve French5fdae1f2007-06-05 18:30:44 +0000525 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
David Howellsa001e5b2008-11-14 10:38:47 +1100526 args.uid = (__u64) current_fsuid();
527 args.gid = (__u64) current_fsgid();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 } else {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400529 args.uid = NO_CHANGE_64;
530 args.gid = NO_CHANGE_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 }
Jeff Layton01ea95e2009-07-09 20:02:49 -0400532 rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, &args,
533 cifs_sb->local_nls,
534 cifs_sb->mnt_cifs_flags &
535 CIFS_MOUNT_MAP_SPECIAL_CHR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
Steve French5fdae1f2007-06-05 18:30:44 +0000537 if (!rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 rc = cifs_get_inode_info_unix(&newinode, full_path,
Steve French5fdae1f2007-06-05 18:30:44 +0000539 inode->i_sb, xid);
Steve Frenchb92327f2005-08-22 20:09:43 -0700540 if (pTcon->nocase)
541 direntry->d_op = &cifs_ci_dentry_ops;
542 else
543 direntry->d_op = &cifs_dentry_ops;
Steve French5fdae1f2007-06-05 18:30:44 +0000544 if (rc == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 d_instantiate(direntry, newinode);
546 }
Steve Frenchd7245c22005-07-14 18:25:12 -0500547 } else {
Steve French5fdae1f2007-06-05 18:30:44 +0000548 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
Steve Frencheda3c022005-07-21 15:20:28 -0700549 int oplock = 0;
550 u16 fileHandle;
Steve Frenchad7a2922008-02-07 23:25:02 +0000551 FILE_ALL_INFO *buf;
Steve Frenchd7245c22005-07-14 18:25:12 -0500552
Joe Perchesb6b38f72010-04-21 03:50:45 +0000553 cFYI(1, "sfu compat create special file");
Steve Frenchd7245c22005-07-14 18:25:12 -0500554
Steve French5fdae1f2007-06-05 18:30:44 +0000555 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
556 if (buf == NULL) {
Steve Frencheda3c022005-07-21 15:20:28 -0700557 kfree(full_path);
Suresh Jayaraman0f3bc092009-06-25 18:12:34 +0530558 rc = -ENOMEM;
Steve Frencheda3c022005-07-21 15:20:28 -0700559 FreeXid(xid);
Suresh Jayaraman0f3bc092009-06-25 18:12:34 +0530560 return rc;
Steve Frencheda3c022005-07-21 15:20:28 -0700561 }
562
563 rc = CIFSSMBOpen(xid, pTcon, full_path,
564 FILE_CREATE, /* fail if exists */
Steve French5fdae1f2007-06-05 18:30:44 +0000565 GENERIC_WRITE /* BB would
Steve Frencheda3c022005-07-21 15:20:28 -0700566 WRITE_OWNER | WRITE_DAC be better? */,
567 /* Create a file and set the
568 file attribute to SYSTEM */
569 CREATE_NOT_DIR | CREATE_OPTION_SPECIAL,
570 &fileHandle, &oplock, buf,
571 cifs_sb->local_nls,
Steve French5fdae1f2007-06-05 18:30:44 +0000572 cifs_sb->mnt_cifs_flags &
Steve Frencheda3c022005-07-21 15:20:28 -0700573 CIFS_MOUNT_MAP_SPECIAL_CHR);
574
Steve French5bafd762006-06-07 00:18:43 +0000575 /* BB FIXME - add handling for backlevel servers
576 which need legacy open and check for all
Steve French5fdae1f2007-06-05 18:30:44 +0000577 calls to SMBOpen for fallback to SMBLeagcyOpen */
578 if (!rc) {
Steve Frencheda3c022005-07-21 15:20:28 -0700579 /* BB Do not bother to decode buf since no
Steve French86c96b42005-11-18 20:25:31 -0800580 local inode yet to put timestamps in,
581 but we can reuse it safely */
Steve French77159b42007-08-31 01:10:17 +0000582 unsigned int bytes_written;
Steve French86c96b42005-11-18 20:25:31 -0800583 struct win_dev *pdev;
584 pdev = (struct win_dev *)buf;
Steve French5fdae1f2007-06-05 18:30:44 +0000585 if (S_ISCHR(mode)) {
Steve French86c96b42005-11-18 20:25:31 -0800586 memcpy(pdev->type, "IntxCHR", 8);
587 pdev->major =
588 cpu_to_le64(MAJOR(device_number));
Steve French5fdae1f2007-06-05 18:30:44 +0000589 pdev->minor =
Steve French86c96b42005-11-18 20:25:31 -0800590 cpu_to_le64(MINOR(device_number));
591 rc = CIFSSMBWrite(xid, pTcon,
592 fileHandle,
593 sizeof(struct win_dev),
594 0, &bytes_written, (char *)pdev,
595 NULL, 0);
Steve French5fdae1f2007-06-05 18:30:44 +0000596 } else if (S_ISBLK(mode)) {
Steve French86c96b42005-11-18 20:25:31 -0800597 memcpy(pdev->type, "IntxBLK", 8);
598 pdev->major =
599 cpu_to_le64(MAJOR(device_number));
600 pdev->minor =
601 cpu_to_le64(MINOR(device_number));
602 rc = CIFSSMBWrite(xid, pTcon,
603 fileHandle,
604 sizeof(struct win_dev),
605 0, &bytes_written, (char *)pdev,
606 NULL, 0);
607 } /* else if(S_ISFIFO */
Steve Frencheda3c022005-07-21 15:20:28 -0700608 CIFSSMBClose(xid, pTcon, fileHandle);
609 d_drop(direntry);
610 }
611 kfree(buf);
Steve Frenchd7245c22005-07-14 18:25:12 -0500612 /* add code here to set EAs */
613 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 }
615
Steve Frenchd14537f12005-04-28 22:41:05 -0700616 kfree(full_path);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 FreeXid(xid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 return rc;
619}
620
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621struct dentry *
Steve French5fdae1f2007-06-05 18:30:44 +0000622cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
623 struct nameidata *nd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624{
625 int xid;
626 int rc = 0; /* to get around spurious gcc warning, set to zero here */
Jeff Layton590a3fe2009-09-12 11:54:28 -0400627 __u32 oplock = 0;
Steve Frencha6ce4932009-04-09 01:14:32 +0000628 __u16 fileHandle = 0;
629 bool posix_open = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 struct cifs_sb_info *cifs_sb;
631 struct cifsTconInfo *pTcon;
Jeff Layton2422f672010-06-16 13:40:16 -0400632 struct cifsFileInfo *cfile;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 struct inode *newInode = NULL;
634 char *full_path = NULL;
Steve Frencha6ce4932009-04-09 01:14:32 +0000635 struct file *filp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
637 xid = GetXid();
638
Joe Perchesb6b38f72010-04-21 03:50:45 +0000639 cFYI(1, "parent inode = 0x%p name is: %s and dentry = 0x%p",
640 parent_dir_inode, direntry->d_name.name, direntry);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 /* check whether path exists */
643
644 cifs_sb = CIFS_SB(parent_dir_inode->i_sb);
645 pTcon = cifs_sb->tcon;
646
Steve French296034f2006-04-21 18:18:37 +0000647 /*
648 * Don't allow the separator character in a path component.
649 * The VFS will not allow "/", but "\" is allowed by posix.
650 */
651 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
652 int i;
653 for (i = 0; i < direntry->d_name.len; i++)
654 if (direntry->d_name.name[i] == '\\') {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000655 cFYI(1, "Invalid file name");
Steve French296034f2006-04-21 18:18:37 +0000656 FreeXid(xid);
657 return ERR_PTR(-EINVAL);
658 }
659 }
660
Jeff Layton5ddf1e02009-07-05 11:01:02 -0400661 /*
662 * O_EXCL: optimize away the lookup, but don't hash the dentry. Let
663 * the VFS handle the create.
664 */
Steve French8e6c0332009-11-24 22:17:59 +0000665 if (nd && (nd->flags & LOOKUP_EXCL)) {
Jeff Layton5ddf1e02009-07-05 11:01:02 -0400666 d_instantiate(direntry, NULL);
Steve French2f81e752009-11-25 00:11:31 +0000667 return NULL;
Jeff Layton5ddf1e02009-07-05 11:01:02 -0400668 }
669
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 /* can not grab the rename sem here since it would
671 deadlock in the cases (beginning of sys_rename itself)
672 in which we already have the sb rename sem */
673 full_path = build_path_from_dentry(direntry);
Steve French5fdae1f2007-06-05 18:30:44 +0000674 if (full_path == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 FreeXid(xid);
676 return ERR_PTR(-ENOMEM);
677 }
678
679 if (direntry->d_inode != NULL) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000680 cFYI(1, "non-NULL inode in lookup");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 } else {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000682 cFYI(1, "NULL inode in lookup");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 }
Joe Perchesb6b38f72010-04-21 03:50:45 +0000684 cFYI(1, "Full path: %s inode = 0x%p", full_path, direntry->d_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685
Steve French8db14ca2009-05-23 18:57:25 +0000686 /* Posix open is only called (at lookup time) for file create now.
687 * For opens (rather than creates), because we do not know if it
688 * is a file or directory yet, and current Samba no longer allows
689 * us to do posix open on dirs, we could end up wasting an open call
690 * on what turns out to be a dir. For file opens, we wait to call posix
691 * open till cifs_open. It could be added here (lookup) in the future
692 * but the performance tradeoff of the extra network request when EISDIR
693 * or EACCES is returned would have to be weighed against the 50%
694 * reduction in network traffic in the other paths.
695 */
Steve Frencha6ce4932009-04-09 01:14:32 +0000696 if (pTcon->unix_ext) {
Steve French8e6c0332009-11-24 22:17:59 +0000697 if (nd && !(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) &&
Steve French8db14ca2009-05-23 18:57:25 +0000698 (nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open &&
699 (nd->intent.open.flags & O_CREAT)) {
Jeff Layton2422f672010-06-16 13:40:16 -0400700 rc = cifs_posix_open(full_path, &newInode,
Steve Frenchfa588e02010-04-22 19:21:55 +0000701 parent_dir_inode->i_sb,
Steve French703a3b82009-05-21 22:21:53 +0000702 nd->intent.open.create_mode,
Steve Frencha6ce4932009-04-09 01:14:32 +0000703 nd->intent.open.flags, &oplock,
704 &fileHandle, xid);
Steve French8db14ca2009-05-23 18:57:25 +0000705 /*
706 * The check below works around a bug in POSIX
707 * open in samba versions 3.3.1 and earlier where
708 * open could incorrectly fail with invalid parameter.
709 * If either that or op not supported returned, follow
710 * the normal lookup.
711 */
712 if ((rc == 0) || (rc == -ENOENT))
713 posix_open = true;
714 else if ((rc == -EINVAL) || (rc != -EOPNOTSUPP))
715 pTcon->broken_posix_open = true;
Steve Frencha6ce4932009-04-09 01:14:32 +0000716 }
717 if (!posix_open)
718 rc = cifs_get_inode_info_unix(&newInode, full_path,
719 parent_dir_inode->i_sb, xid);
720 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 rc = cifs_get_inode_info(&newInode, full_path, NULL,
Steve Frencha6ce4932009-04-09 01:14:32 +0000722 parent_dir_inode->i_sb, xid, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
724 if ((rc == 0) && (newInode != NULL)) {
Steve Frenchb92327f2005-08-22 20:09:43 -0700725 if (pTcon->nocase)
726 direntry->d_op = &cifs_ci_dentry_ops;
727 else
728 direntry->d_op = &cifs_dentry_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 d_add(direntry, newInode);
Jeff Layton2422f672010-06-16 13:40:16 -0400730 if (posix_open) {
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400731 filp = lookup_instantiate_filp(nd, direntry,
732 generic_file_open);
733 if (IS_ERR(filp)) {
734 rc = PTR_ERR(filp);
735 CIFSSMBClose(xid, pTcon, fileHandle);
736 goto lookup_out;
737 }
738
739 cfile = cifs_new_fileinfo(newInode, fileHandle, filp,
Jeff Layton2422f672010-06-16 13:40:16 -0400740 nd->path.mnt,
741 nd->intent.open.flags);
742 if (cfile == NULL) {
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400743 fput(filp);
Jeff Layton2422f672010-06-16 13:40:16 -0400744 CIFSSMBClose(xid, pTcon, fileHandle);
745 rc = -ENOMEM;
746 goto lookup_out;
747 }
Jeff Layton2422f672010-06-16 13:40:16 -0400748 }
Steve French5fdae1f2007-06-05 18:30:44 +0000749 /* since paths are not looked up by component - the parent
Steve French3abb9272005-11-28 08:16:13 -0800750 directories are presumed to be good here */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 renew_parental_timestamps(direntry);
752
753 } else if (rc == -ENOENT) {
754 rc = 0;
Steve French3abb9272005-11-28 08:16:13 -0800755 direntry->d_time = jiffies;
756 if (pTcon->nocase)
757 direntry->d_op = &cifs_ci_dentry_ops;
758 else
759 direntry->d_op = &cifs_dentry_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 d_add(direntry, NULL);
Steve French5fdae1f2007-06-05 18:30:44 +0000761 /* if it was once a directory (but how can we tell?) we could do
762 shrink_dcache_parent(direntry); */
Steve Frenched2b9172008-01-20 00:30:29 +0000763 } else if (rc != -EACCES) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000764 cERROR(1, "Unexpected lookup error %d", rc);
Steve Frenched2b9172008-01-20 00:30:29 +0000765 /* We special case check for Access Denied - since that
766 is a common return code */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 }
768
Jeff Layton2422f672010-06-16 13:40:16 -0400769lookup_out:
Steve Frenchd14537f12005-04-28 22:41:05 -0700770 kfree(full_path);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 FreeXid(xid);
772 return ERR_PTR(rc);
773}
774
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775static int
776cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
777{
778 int isValid = 1;
779
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 if (direntry->d_inode) {
Jeff Laytondf2cf172010-02-12 07:44:16 -0500781 if (cifs_revalidate_dentry(direntry))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 } else {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000784 cFYI(1, "neg dentry 0x%p name = %s",
785 direntry, direntry->d_name.name);
Steve French5fdae1f2007-06-05 18:30:44 +0000786 if (time_after(jiffies, direntry->d_time + HZ) ||
Steve French3abb9272005-11-28 08:16:13 -0800787 !lookupCacheEnabled) {
788 d_drop(direntry);
789 isValid = 0;
Steve French5fdae1f2007-06-05 18:30:44 +0000790 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 }
792
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 return isValid;
794}
795
796/* static int cifs_d_delete(struct dentry *direntry)
797{
798 int rc = 0;
799
Joe Perchesb6b38f72010-04-21 03:50:45 +0000800 cFYI(1, "In cifs d_delete, name = %s", direntry->d_name.name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
802 return rc;
803} */
804
Al Viro4fd03e82009-02-20 05:57:07 +0000805const struct dentry_operations cifs_dentry_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 .d_revalidate = cifs_d_revalidate,
Steve French5fdae1f2007-06-05 18:30:44 +0000807/* d_delete: cifs_d_delete, */ /* not needed except for debugging */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808};
Steve Frenchb92327f2005-08-22 20:09:43 -0700809
810static int cifs_ci_hash(struct dentry *dentry, struct qstr *q)
811{
812 struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
813 unsigned long hash;
814 int i;
815
816 hash = init_name_hash();
817 for (i = 0; i < q->len; i++)
818 hash = partial_name_hash(nls_tolower(codepage, q->name[i]),
819 hash);
820 q->hash = end_name_hash(hash);
821
822 return 0;
823}
824
825static int cifs_ci_compare(struct dentry *dentry, struct qstr *a,
826 struct qstr *b)
827{
828 struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
829
830 if ((a->len == b->len) &&
831 (nls_strnicmp(codepage, a->name, b->name, a->len) == 0)) {
832 /*
833 * To preserve case, don't let an existing negative dentry's
834 * case take precedence. If a is not a negative dentry, this
835 * should have no side effects
836 */
Steve Frenchc3291632008-05-15 05:41:54 +0000837 memcpy((void *)a->name, b->name, a->len);
Steve Frenchb92327f2005-08-22 20:09:43 -0700838 return 0;
839 }
840 return 1;
841}
842
Al Viro4fd03e82009-02-20 05:57:07 +0000843const struct dentry_operations cifs_ci_dentry_ops = {
Steve Frenchb92327f2005-08-22 20:09:43 -0700844 .d_revalidate = cifs_d_revalidate,
845 .d_hash = cifs_ci_hash,
846 .d_compare = cifs_ci_compare,
847};