blob: 8c1af7128384492bd6f74e1efd83c948e482cb79 [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;
Jeff Layton0d424ad2010-09-20 16:01:35 -070057 struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
58 struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
Steve French5fdae1f2007-06-05 18:30:44 +000060 if (direntry == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 return NULL; /* not much we can do if dentry is freed and
62 we need to reopen the file after it was closed implicitly
63 when the server crashed */
64
Steve French646dd532008-05-15 01:50:56 +000065 dirsep = CIFS_DIR_SEP(cifs_sb);
66 pplen = cifs_sb->prepathlen;
Jeff Layton0d424ad2010-09-20 16:01:35 -070067 if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
68 dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
Steve French646dd532008-05-15 01:50:56 +000069 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) {
Jeff Layton0d424ad2010-09-20 16:01:35 -0700120 strncpy(full_path, tcon->treeName, dfsplen);
Steve French646dd532008-05-15 01:50:56 +0000121 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
Jeff Layton086f68b2009-09-21 14:08:18 -0400133struct cifsFileInfo *
Jeff Laytonf7a40682010-09-20 16:01:36 -0700134cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file,
Jeff Laytond7c86ff2010-10-11 15:07:19 -0400135 struct tcon_link *tlink, unsigned int oflags, __u32 oplock)
Steve Frencha6ce4932009-04-09 01:14:32 +0000136{
Jeff Laytona5e18bc2010-10-11 15:07:18 -0400137 struct dentry *dentry = file->f_path.dentry;
Steve Frencha6ce4932009-04-09 01:14:32 +0000138 struct cifsFileInfo *pCifsFile;
139 struct cifsInodeInfo *pCifsInode;
140
141 pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
Steve Frencha6ce4932009-04-09 01:14:32 +0000142 if (pCifsFile == NULL)
Jeff Layton086f68b2009-09-21 14:08:18 -0400143 return pCifsFile;
Steve Frencha6ce4932009-04-09 01:14:32 +0000144
Steve Frencha6ce4932009-04-09 01:14:32 +0000145 pCifsFile->netfid = fileHandle;
146 pCifsFile->pid = current->tgid;
Jeff Layton6508d902010-09-29 19:51:11 -0400147 pCifsFile->uid = current_fsuid();
Jeff Laytona5e18bc2010-10-11 15:07:18 -0400148 pCifsFile->dentry = dget(dentry);
Jeff Layton086f68b2009-09-21 14:08:18 -0400149 pCifsFile->pfile = file;
Steve Frencha6ce4932009-04-09 01:14:32 +0000150 pCifsFile->invalidHandle = false;
Steve Frenchbc8cd432009-04-12 18:18:40 +0000151 pCifsFile->closePend = false;
Jeff Layton13cfb732010-09-29 19:51:11 -0400152 pCifsFile->tlink = cifs_get_tlink(tlink);
Steve Frencha6ce4932009-04-09 01:14:32 +0000153 mutex_init(&pCifsFile->fh_mutex);
154 mutex_init(&pCifsFile->lock_mutex);
155 INIT_LIST_HEAD(&pCifsFile->llist);
Dave Kleikamp6ab409b2009-08-31 11:07:12 -0400156 atomic_set(&pCifsFile->count, 1);
Tejun Heo9b646972010-07-20 22:09:02 +0200157 INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break);
Steve Frencha6ce4932009-04-09 01:14:32 +0000158
Steve Frencha6ce4932009-04-09 01:14:32 +0000159 write_lock(&GlobalSMBSeslock);
Jeff Layton13cfb732010-09-29 19:51:11 -0400160 list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList));
Steve Frencha6ce4932009-04-09 01:14:32 +0000161 pCifsInode = CIFS_I(newinode);
162 if (pCifsInode) {
163 /* if readable file instance put first in list*/
Jeff Layton086f68b2009-09-21 14:08:18 -0400164 if (oflags & FMODE_READ)
165 list_add(&pCifsFile->flist, &pCifsInode->openFileList);
166 else
Steve Frencha6ce4932009-04-09 01:14:32 +0000167 list_add_tail(&pCifsFile->flist,
168 &pCifsInode->openFileList);
Steve Frenchbc8cd432009-04-12 18:18:40 +0000169
Steve Frencha6ce4932009-04-09 01:14:32 +0000170 if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
171 pCifsInode->clientCanCacheAll = true;
172 pCifsInode->clientCanCacheRead = true;
Joe Perchesb6b38f72010-04-21 03:50:45 +0000173 cFYI(1, "Exclusive Oplock inode %p", newinode);
Steve Frencha6ce4932009-04-09 01:14:32 +0000174 } else if ((oplock & 0xF) == OPLOCK_READ)
Steve Frenchbc8cd432009-04-12 18:18:40 +0000175 pCifsInode->clientCanCacheRead = true;
Steve Frencha6ce4932009-04-09 01:14:32 +0000176 }
177 write_unlock(&GlobalSMBSeslock);
Jeff Layton086f68b2009-09-21 14:08:18 -0400178
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400179 file->private_data = pCifsFile;
180
Jeff Layton086f68b2009-09-21 14:08:18 -0400181 return pCifsFile;
Steve Frencha6ce4932009-04-09 01:14:32 +0000182}
183
Steve Frenchf818dd52009-01-19 02:38:35 +0000184static void setup_cifs_dentry(struct cifsTconInfo *tcon,
185 struct dentry *direntry,
186 struct inode *newinode)
187{
188 if (tcon->nocase)
189 direntry->d_op = &cifs_ci_dentry_ops;
190 else
191 direntry->d_op = &cifs_dentry_ops;
192 d_instantiate(direntry, newinode);
193}
194
Steve French39798772006-05-31 22:40:51 +0000195/* Inode operations in similar order to how they appear in Linux file fs.h */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196
197int
198cifs_create(struct inode *inode, struct dentry *direntry, int mode,
199 struct nameidata *nd)
200{
201 int rc = -ENOENT;
202 int xid;
Jeff Layton67750fb2008-05-09 22:28:02 +0000203 int create_options = CREATE_NOT_DIR;
Jeff Layton590a3fe2009-09-12 11:54:28 -0400204 __u32 oplock = 0;
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000205 int oflags;
206 /*
207 * BB below access is probably too much for mknod to request
208 * but we have to do query and setpathinfo so requesting
209 * less could fail (unless we want to request getatr and setatr
210 * permissions (only). At least for POSIX we do not have to
211 * request so much.
212 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 int desiredAccess = GENERIC_READ | GENERIC_WRITE;
214 __u16 fileHandle;
215 struct cifs_sb_info *cifs_sb;
Jeff Layton7ffec372010-09-29 19:51:11 -0400216 struct tcon_link *tlink;
Steve Frenchf818dd52009-01-19 02:38:35 +0000217 struct cifsTconInfo *tcon;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 char *full_path = NULL;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000219 FILE_ALL_INFO *buf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 struct inode *newinode = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 int disposition = FILE_OVERWRITE_IF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222
223 xid = GetXid();
224
225 cifs_sb = CIFS_SB(inode->i_sb);
Jeff Layton7ffec372010-09-29 19:51:11 -0400226 tlink = cifs_sb_tlink(cifs_sb);
227 if (IS_ERR(tlink)) {
228 FreeXid(xid);
229 return PTR_ERR(tlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 }
Jeff Layton7ffec372010-09-29 19:51:11 -0400231 tcon = tlink_tcon(tlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000233 if (oplockEnabled)
234 oplock = REQ_OPLOCK;
235
236 if (nd && (nd->flags & LOOKUP_OPEN))
Jeff Layton608712f2010-10-15 15:33:56 -0400237 oflags = nd->intent.open.file->f_flags;
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000238 else
Jeff Layton608712f2010-10-15 15:33:56 -0400239 oflags = O_RDONLY | O_CREAT;
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000240
Jeff Layton7ffec372010-09-29 19:51:11 -0400241 full_path = build_path_from_dentry(direntry);
242 if (full_path == NULL) {
243 rc = -ENOMEM;
244 goto cifs_create_out;
245 }
246
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000247 if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
248 (CIFS_UNIX_POSIX_PATH_OPS_CAP &
249 le64_to_cpu(tcon->fsUnixInfo.Capability))) {
Steve Frenchfa588e02010-04-22 19:21:55 +0000250 rc = cifs_posix_open(full_path, &newinode,
Steve Frenchfa588e02010-04-22 19:21:55 +0000251 inode->i_sb, mode, oflags, &oplock, &fileHandle, xid);
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000252 /* EIO could indicate that (posix open) operation is not
253 supported, despite what server claimed in capability
254 negotation. EREMOTE indicates DFS junction, which is not
255 handled in posix open */
256
Steve French90e4ee52009-05-08 03:04:30 +0000257 if (rc == 0) {
Steve French90e4ee52009-05-08 03:04:30 +0000258 if (newinode == NULL) /* query inode info */
259 goto cifs_create_get_file_info;
260 else /* success, no need to query */
261 goto cifs_create_set_dentry;
262 } else if ((rc != -EIO) && (rc != -EREMOTE) &&
Steve French703a3b82009-05-21 22:21:53 +0000263 (rc != -EOPNOTSUPP) && (rc != -EINVAL))
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000264 goto cifs_create_out;
265 /* else fallthrough to retry, using older open call, this is
266 case where server does not support this SMB level, and
267 falsely claims capability (also get here for DFS case
268 which should be rare for path not covered on files) */
269 }
Steve Frenchf818dd52009-01-19 02:38:35 +0000270
Steve French5fdae1f2007-06-05 18:30:44 +0000271 if (nd && (nd->flags & LOOKUP_OPEN)) {
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000272 /* if the file is going to stay open, then we
273 need to set the desired access properly */
Miklos Szeredie08fc042005-09-06 15:18:26 -0700274 desiredAccess = 0;
Jeff Layton608712f2010-10-15 15:33:56 -0400275 if (OPEN_FMODE(oflags) & FMODE_READ)
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000276 desiredAccess |= GENERIC_READ; /* is this too little? */
Jeff Layton608712f2010-10-15 15:33:56 -0400277 if (OPEN_FMODE(oflags) & FMODE_WRITE)
Miklos Szeredie08fc042005-09-06 15:18:26 -0700278 desiredAccess |= GENERIC_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279
Steve French5fdae1f2007-06-05 18:30:44 +0000280 if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 disposition = FILE_CREATE;
Steve French5fdae1f2007-06-05 18:30:44 +0000282 else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 disposition = FILE_OVERWRITE_IF;
Steve French5fdae1f2007-06-05 18:30:44 +0000284 else if ((oflags & O_CREAT) == O_CREAT)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 disposition = FILE_OPEN_IF;
Steve Frenchad7a2922008-02-07 23:25:02 +0000286 else
Joe Perchesb6b38f72010-04-21 03:50:45 +0000287 cFYI(1, "Create flag not set in create function");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 }
289
Steve French5fdae1f2007-06-05 18:30:44 +0000290 /* BB add processing to set equivalent of mode - e.g. via CreateX with
291 ACLs */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292
Steve French5fdae1f2007-06-05 18:30:44 +0000293 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
294 if (buf == NULL) {
Jeff Layton232341b2010-08-05 13:58:38 -0400295 rc = -ENOMEM;
296 goto cifs_create_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 }
Jeff Layton67750fb2008-05-09 22:28:02 +0000298
Jeff Layton67750fb2008-05-09 22:28:02 +0000299 /*
300 * if we're not using unix extensions, see if we need to set
301 * ATTR_READONLY on the create call
302 */
Steve Frenchf818dd52009-01-19 02:38:35 +0000303 if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
Jeff Layton67750fb2008-05-09 22:28:02 +0000304 create_options |= CREATE_OPTION_READONLY;
305
Jeff Laytona6e8a842010-09-20 16:01:33 -0700306 if (tcon->ses->capabilities & CAP_NT_SMBS)
Steve Frenchf818dd52009-01-19 02:38:35 +0000307 rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
Jeff Layton67750fb2008-05-09 22:28:02 +0000308 desiredAccess, create_options,
Steve French737b7582005-04-28 22:41:06 -0700309 &fileHandle, &oplock, buf, cifs_sb->local_nls,
310 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
Steve French5bafd762006-06-07 00:18:43 +0000311 else
312 rc = -EIO; /* no NT SMB support fall into legacy open below */
313
Steve French5fdae1f2007-06-05 18:30:44 +0000314 if (rc == -EIO) {
Steve Frencha9d02ad2005-08-24 23:06:05 -0700315 /* old server, retry the open legacy style */
Steve Frenchf818dd52009-01-19 02:38:35 +0000316 rc = SMBLegacyOpen(xid, tcon, full_path, disposition,
Jeff Layton67750fb2008-05-09 22:28:02 +0000317 desiredAccess, create_options,
Steve Frencha9d02ad2005-08-24 23:06:05 -0700318 &fileHandle, &oplock, buf, cifs_sb->local_nls,
319 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
Steve French5fdae1f2007-06-05 18:30:44 +0000320 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000322 cFYI(1, "cifs_create returned 0x%x", rc);
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000323 goto cifs_create_out;
324 }
325
326 /* If Open reported that we actually created a file
327 then we now have to set the mode if possible */
328 if ((tcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) {
329 struct cifs_unix_set_info_args args = {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400330 .mode = mode,
331 .ctime = NO_CHANGE_64,
332 .atime = NO_CHANGE_64,
333 .mtime = NO_CHANGE_64,
334 .device = 0,
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000335 };
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400336
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000337 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
338 args.uid = (__u64) current_fsuid();
339 if (inode->i_mode & S_ISGID)
340 args.gid = (__u64) inode->i_gid;
341 else
342 args.gid = (__u64) current_fsgid();
Steve French3ce53fc2007-06-08 14:55:14 +0000343 } else {
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000344 args.uid = NO_CHANGE_64;
345 args.gid = NO_CHANGE_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 }
Jeff Layton01ea95e2009-07-09 20:02:49 -0400347 CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
348 cifs_sb->local_nls,
349 cifs_sb->mnt_cifs_flags &
350 CIFS_MOUNT_MAP_SPECIAL_CHR);
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000351 } else {
352 /* BB implement mode setting via Windows security
353 descriptors e.g. */
354 /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000356 /* Could set r/o dos attribute if mode & 0222 == 0 */
357 }
358
359cifs_create_get_file_info:
360 /* server might mask mode so we have to query for it */
361 if (tcon->unix_ext)
362 rc = cifs_get_inode_info_unix(&newinode, full_path,
363 inode->i_sb, xid);
364 else {
365 rc = cifs_get_inode_info(&newinode, full_path, buf,
366 inode->i_sb, xid, &fileHandle);
367 if (newinode) {
368 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
369 newinode->i_mode = mode;
370 if ((oplock & CIFS_CREATE_ACTION) &&
371 (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) {
372 newinode->i_uid = current_fsuid();
373 if (inode->i_mode & S_ISGID)
374 newinode->i_gid = inode->i_gid;
375 else
376 newinode->i_gid = current_fsgid();
Steve French6473a552005-11-29 20:20:10 -0800377 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 }
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000379 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000381cifs_create_set_dentry:
382 if (rc == 0)
383 setup_cifs_dentry(tcon, direntry, newinode);
384 else
Joe Perchesb6b38f72010-04-21 03:50:45 +0000385 cFYI(1, "Create worked, get_inode_info failed rc = %d", rc);
Steve Frenchf818dd52009-01-19 02:38:35 +0000386
Jeff Layton2422f6762010-06-16 13:40:16 -0400387 if (newinode && nd && (nd->flags & LOOKUP_OPEN)) {
Suresh Jayaramanfdb36032010-05-11 09:46:46 +0530388 struct cifsFileInfo *pfile_info;
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400389 struct file *filp;
390
391 filp = lookup_instantiate_filp(nd, direntry, generic_file_open);
392 if (IS_ERR(filp)) {
393 rc = PTR_ERR(filp);
394 CIFSSMBClose(xid, tcon, fileHandle);
395 goto cifs_create_out;
396 }
397
398 pfile_info = cifs_new_fileinfo(newinode, fileHandle, filp,
Jeff Laytond7c86ff2010-10-11 15:07:19 -0400399 tlink, oflags, oplock);
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400400 if (pfile_info == NULL) {
401 fput(filp);
402 CIFSSMBClose(xid, tcon, fileHandle);
Suresh Jayaramanfdb36032010-05-11 09:46:46 +0530403 rc = -ENOMEM;
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400404 }
Jeff Layton2422f6762010-06-16 13:40:16 -0400405 } else {
406 CIFSSMBClose(xid, tcon, fileHandle);
Steve French5fdae1f2007-06-05 18:30:44 +0000407 }
Jeff Layton2422f6762010-06-16 13:40:16 -0400408
Steve Frenchd14537f12005-04-28 22:41:05 -0700409cifs_create_out:
410 kfree(buf);
411 kfree(full_path);
Jeff Layton7ffec372010-09-29 19:51:11 -0400412 cifs_put_tlink(tlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 FreeXid(xid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 return rc;
415}
416
Steve French5fdae1f2007-06-05 18:30:44 +0000417int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
418 dev_t device_number)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419{
420 int rc = -EPERM;
421 int xid;
422 struct cifs_sb_info *cifs_sb;
Jeff Layton7ffec372010-09-29 19:51:11 -0400423 struct tcon_link *tlink;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 struct cifsTconInfo *pTcon;
425 char *full_path = NULL;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000426 struct inode *newinode = NULL;
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400427 int oplock = 0;
428 u16 fileHandle;
429 FILE_ALL_INFO *buf = NULL;
430 unsigned int bytes_written;
431 struct win_dev *pdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432
433 if (!old_valid_dev(device_number))
434 return -EINVAL;
435
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 cifs_sb = CIFS_SB(inode->i_sb);
Jeff Layton7ffec372010-09-29 19:51:11 -0400437 tlink = cifs_sb_tlink(cifs_sb);
438 if (IS_ERR(tlink))
439 return PTR_ERR(tlink);
440
441 pTcon = tlink_tcon(tlink);
442
443 xid = GetXid();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 full_path = build_path_from_dentry(direntry);
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400446 if (full_path == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 rc = -ENOMEM;
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400448 goto mknod_out;
449 }
450
451 if (pTcon->unix_ext) {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400452 struct cifs_unix_set_info_args args = {
Al Viroce3b0f82009-03-29 19:08:22 -0400453 .mode = mode & ~current_umask(),
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400454 .ctime = NO_CHANGE_64,
455 .atime = NO_CHANGE_64,
456 .mtime = NO_CHANGE_64,
457 .device = device_number,
458 };
Steve French5fdae1f2007-06-05 18:30:44 +0000459 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
David Howellsa001e5b2008-11-14 10:38:47 +1100460 args.uid = (__u64) current_fsuid();
461 args.gid = (__u64) current_fsgid();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 } else {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400463 args.uid = NO_CHANGE_64;
464 args.gid = NO_CHANGE_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 }
Jeff Layton01ea95e2009-07-09 20:02:49 -0400466 rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, &args,
467 cifs_sb->local_nls,
468 cifs_sb->mnt_cifs_flags &
469 CIFS_MOUNT_MAP_SPECIAL_CHR);
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400470 if (rc)
471 goto mknod_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400473 rc = cifs_get_inode_info_unix(&newinode, full_path,
Steve French5fdae1f2007-06-05 18:30:44 +0000474 inode->i_sb, xid);
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400475 if (pTcon->nocase)
476 direntry->d_op = &cifs_ci_dentry_ops;
477 else
478 direntry->d_op = &cifs_dentry_ops;
Steve Frenchd7245c22005-07-14 18:25:12 -0500479
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400480 if (rc == 0)
481 d_instantiate(direntry, newinode);
482 goto mknod_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 }
484
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400485 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
486 goto mknod_out;
487
488
489 cFYI(1, "sfu compat create special file");
490
491 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
492 if (buf == NULL) {
493 kfree(full_path);
494 rc = -ENOMEM;
495 FreeXid(xid);
496 return rc;
497 }
498
499 /* FIXME: would WRITE_OWNER | WRITE_DAC be better? */
500 rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_CREATE,
501 GENERIC_WRITE, CREATE_NOT_DIR | CREATE_OPTION_SPECIAL,
502 &fileHandle, &oplock, buf, cifs_sb->local_nls,
503 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
504 if (rc)
505 goto mknod_out;
506
507 /* BB Do not bother to decode buf since no local inode yet to put
508 * timestamps in, but we can reuse it safely */
509
510 pdev = (struct win_dev *)buf;
511 if (S_ISCHR(mode)) {
512 memcpy(pdev->type, "IntxCHR", 8);
513 pdev->major =
514 cpu_to_le64(MAJOR(device_number));
515 pdev->minor =
516 cpu_to_le64(MINOR(device_number));
517 rc = CIFSSMBWrite(xid, pTcon,
518 fileHandle,
519 sizeof(struct win_dev),
520 0, &bytes_written, (char *)pdev,
521 NULL, 0);
522 } else if (S_ISBLK(mode)) {
523 memcpy(pdev->type, "IntxBLK", 8);
524 pdev->major =
525 cpu_to_le64(MAJOR(device_number));
526 pdev->minor =
527 cpu_to_le64(MINOR(device_number));
528 rc = CIFSSMBWrite(xid, pTcon,
529 fileHandle,
530 sizeof(struct win_dev),
531 0, &bytes_written, (char *)pdev,
532 NULL, 0);
533 } /* else if (S_ISFIFO) */
534 CIFSSMBClose(xid, pTcon, fileHandle);
535 d_drop(direntry);
536
537 /* FIXME: add code here to set EAs */
538
539mknod_out:
Steve Frenchd14537f12005-04-28 22:41:05 -0700540 kfree(full_path);
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400541 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 FreeXid(xid);
Jeff Layton7ffec372010-09-29 19:51:11 -0400543 cifs_put_tlink(tlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 return rc;
545}
546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547struct dentry *
Steve French5fdae1f2007-06-05 18:30:44 +0000548cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
549 struct nameidata *nd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550{
551 int xid;
552 int rc = 0; /* to get around spurious gcc warning, set to zero here */
Jeff Layton590a3fe2009-09-12 11:54:28 -0400553 __u32 oplock = 0;
Steve Frencha6ce4932009-04-09 01:14:32 +0000554 __u16 fileHandle = 0;
555 bool posix_open = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 struct cifs_sb_info *cifs_sb;
Jeff Layton7ffec372010-09-29 19:51:11 -0400557 struct tcon_link *tlink;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 struct cifsTconInfo *pTcon;
Jeff Layton2422f6762010-06-16 13:40:16 -0400559 struct cifsFileInfo *cfile;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 struct inode *newInode = NULL;
561 char *full_path = NULL;
Steve Frencha6ce4932009-04-09 01:14:32 +0000562 struct file *filp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
564 xid = GetXid();
565
Joe Perchesb6b38f72010-04-21 03:50:45 +0000566 cFYI(1, "parent inode = 0x%p name is: %s and dentry = 0x%p",
567 parent_dir_inode, direntry->d_name.name, direntry);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 /* check whether path exists */
570
571 cifs_sb = CIFS_SB(parent_dir_inode->i_sb);
Jeff Layton7ffec372010-09-29 19:51:11 -0400572 tlink = cifs_sb_tlink(cifs_sb);
573 if (IS_ERR(tlink)) {
574 FreeXid(xid);
575 return (struct dentry *)tlink;
576 }
577 pTcon = tlink_tcon(tlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578
Steve French296034f2006-04-21 18:18:37 +0000579 /*
580 * Don't allow the separator character in a path component.
581 * The VFS will not allow "/", but "\" is allowed by posix.
582 */
583 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
584 int i;
585 for (i = 0; i < direntry->d_name.len; i++)
586 if (direntry->d_name.name[i] == '\\') {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000587 cFYI(1, "Invalid file name");
Jeff Layton7ffec372010-09-29 19:51:11 -0400588 rc = -EINVAL;
589 goto lookup_out;
Steve French296034f2006-04-21 18:18:37 +0000590 }
591 }
592
Jeff Layton5ddf1e02009-07-05 11:01:02 -0400593 /*
594 * O_EXCL: optimize away the lookup, but don't hash the dentry. Let
595 * the VFS handle the create.
596 */
Steve French8e6c0332009-11-24 22:17:59 +0000597 if (nd && (nd->flags & LOOKUP_EXCL)) {
Jeff Layton5ddf1e02009-07-05 11:01:02 -0400598 d_instantiate(direntry, NULL);
Jeff Layton7ffec372010-09-29 19:51:11 -0400599 rc = 0;
600 goto lookup_out;
Jeff Layton5ddf1e02009-07-05 11:01:02 -0400601 }
602
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 /* can not grab the rename sem here since it would
604 deadlock in the cases (beginning of sys_rename itself)
605 in which we already have the sb rename sem */
606 full_path = build_path_from_dentry(direntry);
Steve French5fdae1f2007-06-05 18:30:44 +0000607 if (full_path == NULL) {
Jeff Layton7ffec372010-09-29 19:51:11 -0400608 rc = -ENOMEM;
609 goto lookup_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 }
611
612 if (direntry->d_inode != NULL) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000613 cFYI(1, "non-NULL inode in lookup");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 } else {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000615 cFYI(1, "NULL inode in lookup");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 }
Joe Perchesb6b38f72010-04-21 03:50:45 +0000617 cFYI(1, "Full path: %s inode = 0x%p", full_path, direntry->d_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618
Steve French8db14ca2009-05-23 18:57:25 +0000619 /* Posix open is only called (at lookup time) for file create now.
620 * For opens (rather than creates), because we do not know if it
621 * is a file or directory yet, and current Samba no longer allows
622 * us to do posix open on dirs, we could end up wasting an open call
623 * on what turns out to be a dir. For file opens, we wait to call posix
624 * open till cifs_open. It could be added here (lookup) in the future
625 * but the performance tradeoff of the extra network request when EISDIR
626 * or EACCES is returned would have to be weighed against the 50%
627 * reduction in network traffic in the other paths.
628 */
Steve Frencha6ce4932009-04-09 01:14:32 +0000629 if (pTcon->unix_ext) {
Steve French8e6c0332009-11-24 22:17:59 +0000630 if (nd && !(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) &&
Steve French8db14ca2009-05-23 18:57:25 +0000631 (nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open &&
Jeff Layton608712f2010-10-15 15:33:56 -0400632 (nd->intent.open.file->f_flags & O_CREAT)) {
Jeff Layton2422f6762010-06-16 13:40:16 -0400633 rc = cifs_posix_open(full_path, &newInode,
Steve Frenchfa588e02010-04-22 19:21:55 +0000634 parent_dir_inode->i_sb,
Steve French703a3b82009-05-21 22:21:53 +0000635 nd->intent.open.create_mode,
Jeff Layton608712f2010-10-15 15:33:56 -0400636 nd->intent.open.file->f_flags, &oplock,
Steve Frencha6ce4932009-04-09 01:14:32 +0000637 &fileHandle, xid);
Steve French8db14ca2009-05-23 18:57:25 +0000638 /*
639 * The check below works around a bug in POSIX
640 * open in samba versions 3.3.1 and earlier where
641 * open could incorrectly fail with invalid parameter.
642 * If either that or op not supported returned, follow
643 * the normal lookup.
644 */
645 if ((rc == 0) || (rc == -ENOENT))
646 posix_open = true;
647 else if ((rc == -EINVAL) || (rc != -EOPNOTSUPP))
648 pTcon->broken_posix_open = true;
Steve Frencha6ce4932009-04-09 01:14:32 +0000649 }
650 if (!posix_open)
651 rc = cifs_get_inode_info_unix(&newInode, full_path,
652 parent_dir_inode->i_sb, xid);
653 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 rc = cifs_get_inode_info(&newInode, full_path, NULL,
Steve Frencha6ce4932009-04-09 01:14:32 +0000655 parent_dir_inode->i_sb, xid, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656
657 if ((rc == 0) && (newInode != NULL)) {
Steve Frenchb92327f2005-08-22 20:09:43 -0700658 if (pTcon->nocase)
659 direntry->d_op = &cifs_ci_dentry_ops;
660 else
661 direntry->d_op = &cifs_dentry_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 d_add(direntry, newInode);
Jeff Layton2422f6762010-06-16 13:40:16 -0400663 if (posix_open) {
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400664 filp = lookup_instantiate_filp(nd, direntry,
665 generic_file_open);
666 if (IS_ERR(filp)) {
667 rc = PTR_ERR(filp);
668 CIFSSMBClose(xid, pTcon, fileHandle);
669 goto lookup_out;
670 }
671
672 cfile = cifs_new_fileinfo(newInode, fileHandle, filp,
Jeff Laytond7c86ff2010-10-11 15:07:19 -0400673 tlink, nd->intent.open.flags,
Suresh Jayaramanaa91c7e2010-09-17 18:56:39 +0530674 oplock);
Jeff Layton2422f6762010-06-16 13:40:16 -0400675 if (cfile == NULL) {
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400676 fput(filp);
Jeff Layton2422f6762010-06-16 13:40:16 -0400677 CIFSSMBClose(xid, pTcon, fileHandle);
678 rc = -ENOMEM;
679 goto lookup_out;
680 }
Jeff Layton2422f6762010-06-16 13:40:16 -0400681 }
Steve French5fdae1f2007-06-05 18:30:44 +0000682 /* since paths are not looked up by component - the parent
Steve French3abb9272005-11-28 08:16:13 -0800683 directories are presumed to be good here */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 renew_parental_timestamps(direntry);
685
686 } else if (rc == -ENOENT) {
687 rc = 0;
Steve French3abb9272005-11-28 08:16:13 -0800688 direntry->d_time = jiffies;
689 if (pTcon->nocase)
690 direntry->d_op = &cifs_ci_dentry_ops;
691 else
692 direntry->d_op = &cifs_dentry_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 d_add(direntry, NULL);
Steve French5fdae1f2007-06-05 18:30:44 +0000694 /* if it was once a directory (but how can we tell?) we could do
695 shrink_dcache_parent(direntry); */
Steve Frenched2b9172008-01-20 00:30:29 +0000696 } else if (rc != -EACCES) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000697 cERROR(1, "Unexpected lookup error %d", rc);
Steve Frenched2b9172008-01-20 00:30:29 +0000698 /* We special case check for Access Denied - since that
699 is a common return code */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 }
701
Jeff Layton2422f6762010-06-16 13:40:16 -0400702lookup_out:
Steve Frenchd14537f12005-04-28 22:41:05 -0700703 kfree(full_path);
Jeff Layton7ffec372010-09-29 19:51:11 -0400704 cifs_put_tlink(tlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 FreeXid(xid);
706 return ERR_PTR(rc);
707}
708
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709static int
710cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
711{
712 int isValid = 1;
713
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 if (direntry->d_inode) {
Jeff Laytondf2cf172010-02-12 07:44:16 -0500715 if (cifs_revalidate_dentry(direntry))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 } else {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000718 cFYI(1, "neg dentry 0x%p name = %s",
719 direntry, direntry->d_name.name);
Steve French5fdae1f2007-06-05 18:30:44 +0000720 if (time_after(jiffies, direntry->d_time + HZ) ||
Steve French3abb9272005-11-28 08:16:13 -0800721 !lookupCacheEnabled) {
722 d_drop(direntry);
723 isValid = 0;
Steve French5fdae1f2007-06-05 18:30:44 +0000724 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 }
726
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 return isValid;
728}
729
730/* static int cifs_d_delete(struct dentry *direntry)
731{
732 int rc = 0;
733
Joe Perchesb6b38f72010-04-21 03:50:45 +0000734 cFYI(1, "In cifs d_delete, name = %s", direntry->d_name.name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735
736 return rc;
737} */
738
Al Viro4fd03e82009-02-20 05:57:07 +0000739const struct dentry_operations cifs_dentry_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 .d_revalidate = cifs_d_revalidate,
Steve French5fdae1f2007-06-05 18:30:44 +0000741/* d_delete: cifs_d_delete, */ /* not needed except for debugging */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742};
Steve Frenchb92327f2005-08-22 20:09:43 -0700743
744static int cifs_ci_hash(struct dentry *dentry, struct qstr *q)
745{
746 struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
747 unsigned long hash;
748 int i;
749
750 hash = init_name_hash();
751 for (i = 0; i < q->len; i++)
752 hash = partial_name_hash(nls_tolower(codepage, q->name[i]),
753 hash);
754 q->hash = end_name_hash(hash);
755
756 return 0;
757}
758
759static int cifs_ci_compare(struct dentry *dentry, struct qstr *a,
760 struct qstr *b)
761{
762 struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
763
764 if ((a->len == b->len) &&
765 (nls_strnicmp(codepage, a->name, b->name, a->len) == 0)) {
766 /*
767 * To preserve case, don't let an existing negative dentry's
768 * case take precedence. If a is not a negative dentry, this
769 * should have no side effects
770 */
Steve Frenchc3291632008-05-15 05:41:54 +0000771 memcpy((void *)a->name, b->name, a->len);
Steve Frenchb92327f2005-08-22 20:09:43 -0700772 return 0;
773 }
774 return 1;
775}
776
Al Viro4fd03e82009-02-20 05:57:07 +0000777const struct dentry_operations cifs_ci_dentry_ops = {
Steve Frenchb92327f2005-08-22 20:09:43 -0700778 .d_revalidate = cifs_d_revalidate,
779 .d_hash = cifs_ci_hash,
780 .d_compare = cifs_ci_compare,
781};