blob: 81914df47ef1612c1ab742d228503bbf5e598046 [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;
Steve French646dd532008-05-15 01:50:56 +000053 int dfsplen;
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 char *full_path;
Steve French88274812006-03-09 22:21:45 +000055 char dirsep;
Jeff Layton0d424ad2010-09-20 16:01:35 -070056 struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
Steve French96daf2b2011-05-27 04:34:02 +000057 struct cifs_tcon *tcon = cifs_sb_master_tcon(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 dirsep = CIFS_DIR_SEP(cifs_sb);
Jeff Layton0d424ad2010-09-20 16:01:35 -070065 if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
66 dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
Steve French646dd532008-05-15 01:50:56 +000067 else
68 dfsplen = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070069cifs_bp_rename_retry:
Steve Frenchf87d39d2011-05-27 03:50:55 +000070 namelen = dfsplen;
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 for (temp = direntry; !IS_ROOT(temp);) {
72 namelen += (1 + temp->d_name.len);
73 temp = temp->d_parent;
Steve French5fdae1f2007-06-05 18:30:44 +000074 if (temp == NULL) {
Joe Perchesb6b38f72010-04-21 03:50:45 +000075 cERROR(1, "corrupt dentry");
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 return NULL;
77 }
78 }
79
80 full_path = kmalloc(namelen+1, GFP_KERNEL);
Steve French5fdae1f2007-06-05 18:30:44 +000081 if (full_path == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 return full_path;
83 full_path[namelen] = 0; /* trailing null */
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 for (temp = direntry; !IS_ROOT(temp);) {
85 namelen -= 1 + temp->d_name.len;
86 if (namelen < 0) {
87 break;
88 } else {
Steve French7f573562005-08-30 11:32:14 -070089 full_path[namelen] = dirsep;
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 strncpy(full_path + namelen + 1, temp->d_name.name,
91 temp->d_name.len);
Joe Perchesb6b38f72010-04-21 03:50:45 +000092 cFYI(0, "name: %s", full_path + namelen);
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 }
94 temp = temp->d_parent;
Steve French5fdae1f2007-06-05 18:30:44 +000095 if (temp == NULL) {
Joe Perchesb6b38f72010-04-21 03:50:45 +000096 cERROR(1, "corrupt dentry");
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 kfree(full_path);
98 return NULL;
99 }
100 }
Steve Frenchf87d39d2011-05-27 03:50:55 +0000101 if (namelen != dfsplen) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000102 cERROR(1, "did not end path lookup where expected namelen is %d",
103 namelen);
Steve French5fdae1f2007-06-05 18:30:44 +0000104 /* presumably this is only possible if racing with a rename
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 of one of the parent directories (we can not lock the dentries
106 above us to prevent this, but retrying should be harmless) */
107 kfree(full_path);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 goto cifs_bp_rename_retry;
109 }
Steve French2fe87f02006-09-21 07:02:52 +0000110 /* DIR_SEP already set for byte 0 / vs \ but not for
111 subsequent slashes in prepath which currently must
112 be entered the right way - not sure if there is an alternative
113 since the '\' is a valid posix character so we can not switch
114 those safely to '/' if any are found in the middle of the prepath */
115 /* BB test paths to Windows with '/' in the midst of prepath */
Steve French646dd532008-05-15 01:50:56 +0000116
117 if (dfsplen) {
Jeff Layton0d424ad2010-09-20 16:01:35 -0700118 strncpy(full_path, tcon->treeName, dfsplen);
Steve French646dd532008-05-15 01:50:56 +0000119 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
120 int i;
121 for (i = 0; i < dfsplen; i++) {
122 if (full_path[i] == '\\')
123 full_path[i] = '/';
124 }
125 }
126 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 return full_path;
128}
129
Steve French39798772006-05-31 22:40:51 +0000130/* Inode operations in similar order to how they appear in Linux file fs.h */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131
132int
133cifs_create(struct inode *inode, struct dentry *direntry, int mode,
134 struct nameidata *nd)
135{
136 int rc = -ENOENT;
137 int xid;
Jeff Layton67750fb2008-05-09 22:28:02 +0000138 int create_options = CREATE_NOT_DIR;
Jeff Layton590a3fe2009-09-12 11:54:28 -0400139 __u32 oplock = 0;
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000140 int oflags;
141 /*
142 * BB below access is probably too much for mknod to request
143 * but we have to do query and setpathinfo so requesting
144 * less could fail (unless we want to request getatr and setatr
145 * permissions (only). At least for POSIX we do not have to
146 * request so much.
147 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 int desiredAccess = GENERIC_READ | GENERIC_WRITE;
149 __u16 fileHandle;
150 struct cifs_sb_info *cifs_sb;
Jeff Layton7ffec372010-09-29 19:51:11 -0400151 struct tcon_link *tlink;
Steve French96daf2b2011-05-27 04:34:02 +0000152 struct cifs_tcon *tcon;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 char *full_path = NULL;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000154 FILE_ALL_INFO *buf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 struct inode *newinode = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 int disposition = FILE_OVERWRITE_IF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
158 xid = GetXid();
159
160 cifs_sb = CIFS_SB(inode->i_sb);
Jeff Layton7ffec372010-09-29 19:51:11 -0400161 tlink = cifs_sb_tlink(cifs_sb);
162 if (IS_ERR(tlink)) {
163 FreeXid(xid);
164 return PTR_ERR(tlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 }
Jeff Layton7ffec372010-09-29 19:51:11 -0400166 tcon = tlink_tcon(tlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000168 if (oplockEnabled)
169 oplock = REQ_OPLOCK;
170
171 if (nd && (nd->flags & LOOKUP_OPEN))
Jeff Layton608712f2010-10-15 15:33:56 -0400172 oflags = nd->intent.open.file->f_flags;
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000173 else
Jeff Layton608712f2010-10-15 15:33:56 -0400174 oflags = O_RDONLY | O_CREAT;
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000175
Jeff Layton7ffec372010-09-29 19:51:11 -0400176 full_path = build_path_from_dentry(direntry);
177 if (full_path == NULL) {
178 rc = -ENOMEM;
179 goto cifs_create_out;
180 }
181
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000182 if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
183 (CIFS_UNIX_POSIX_PATH_OPS_CAP &
184 le64_to_cpu(tcon->fsUnixInfo.Capability))) {
Steve Frenchfa588e02010-04-22 19:21:55 +0000185 rc = cifs_posix_open(full_path, &newinode,
Steve Frenchfa588e02010-04-22 19:21:55 +0000186 inode->i_sb, mode, oflags, &oplock, &fileHandle, xid);
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000187 /* EIO could indicate that (posix open) operation is not
188 supported, despite what server claimed in capability
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300189 negotiation. EREMOTE indicates DFS junction, which is not
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000190 handled in posix open */
191
Steve French90e4ee52009-05-08 03:04:30 +0000192 if (rc == 0) {
Steve French90e4ee52009-05-08 03:04:30 +0000193 if (newinode == NULL) /* query inode info */
194 goto cifs_create_get_file_info;
195 else /* success, no need to query */
196 goto cifs_create_set_dentry;
197 } else if ((rc != -EIO) && (rc != -EREMOTE) &&
Steve French703a3b82009-05-21 22:21:53 +0000198 (rc != -EOPNOTSUPP) && (rc != -EINVAL))
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000199 goto cifs_create_out;
200 /* else fallthrough to retry, using older open call, this is
201 case where server does not support this SMB level, and
202 falsely claims capability (also get here for DFS case
203 which should be rare for path not covered on files) */
204 }
Steve Frenchf818dd52009-01-19 02:38:35 +0000205
Steve French5fdae1f2007-06-05 18:30:44 +0000206 if (nd && (nd->flags & LOOKUP_OPEN)) {
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000207 /* if the file is going to stay open, then we
208 need to set the desired access properly */
Miklos Szeredie08fc042005-09-06 15:18:26 -0700209 desiredAccess = 0;
Jeff Layton608712f2010-10-15 15:33:56 -0400210 if (OPEN_FMODE(oflags) & FMODE_READ)
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000211 desiredAccess |= GENERIC_READ; /* is this too little? */
Jeff Layton608712f2010-10-15 15:33:56 -0400212 if (OPEN_FMODE(oflags) & FMODE_WRITE)
Miklos Szeredie08fc042005-09-06 15:18:26 -0700213 desiredAccess |= GENERIC_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214
Steve French5fdae1f2007-06-05 18:30:44 +0000215 if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 disposition = FILE_CREATE;
Steve French5fdae1f2007-06-05 18:30:44 +0000217 else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 disposition = FILE_OVERWRITE_IF;
Steve French5fdae1f2007-06-05 18:30:44 +0000219 else if ((oflags & O_CREAT) == O_CREAT)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 disposition = FILE_OPEN_IF;
Steve Frenchad7a2922008-02-07 23:25:02 +0000221 else
Joe Perchesb6b38f72010-04-21 03:50:45 +0000222 cFYI(1, "Create flag not set in create function");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 }
224
Steve French5fdae1f2007-06-05 18:30:44 +0000225 /* BB add processing to set equivalent of mode - e.g. via CreateX with
226 ACLs */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227
Steve French5fdae1f2007-06-05 18:30:44 +0000228 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
229 if (buf == NULL) {
Jeff Layton232341b2010-08-05 13:58:38 -0400230 rc = -ENOMEM;
231 goto cifs_create_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 }
Jeff Layton67750fb2008-05-09 22:28:02 +0000233
Jeff Layton67750fb2008-05-09 22:28:02 +0000234 /*
235 * if we're not using unix extensions, see if we need to set
236 * ATTR_READONLY on the create call
237 */
Steve Frenchf818dd52009-01-19 02:38:35 +0000238 if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
Jeff Layton67750fb2008-05-09 22:28:02 +0000239 create_options |= CREATE_OPTION_READONLY;
240
Jeff Laytona6e8a842010-09-20 16:01:33 -0700241 if (tcon->ses->capabilities & CAP_NT_SMBS)
Steve Frenchf818dd52009-01-19 02:38:35 +0000242 rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
Jeff Layton67750fb2008-05-09 22:28:02 +0000243 desiredAccess, create_options,
Steve French737b7582005-04-28 22:41:06 -0700244 &fileHandle, &oplock, buf, cifs_sb->local_nls,
245 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
Steve French5bafd762006-06-07 00:18:43 +0000246 else
247 rc = -EIO; /* no NT SMB support fall into legacy open below */
248
Steve French5fdae1f2007-06-05 18:30:44 +0000249 if (rc == -EIO) {
Steve Frencha9d02ad2005-08-24 23:06:05 -0700250 /* old server, retry the open legacy style */
Steve Frenchf818dd52009-01-19 02:38:35 +0000251 rc = SMBLegacyOpen(xid, tcon, full_path, disposition,
Jeff Layton67750fb2008-05-09 22:28:02 +0000252 desiredAccess, create_options,
Steve Frencha9d02ad2005-08-24 23:06:05 -0700253 &fileHandle, &oplock, buf, cifs_sb->local_nls,
254 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
Steve French5fdae1f2007-06-05 18:30:44 +0000255 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000257 cFYI(1, "cifs_create returned 0x%x", rc);
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000258 goto cifs_create_out;
259 }
260
261 /* If Open reported that we actually created a file
262 then we now have to set the mode if possible */
263 if ((tcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) {
264 struct cifs_unix_set_info_args args = {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400265 .mode = mode,
266 .ctime = NO_CHANGE_64,
267 .atime = NO_CHANGE_64,
268 .mtime = NO_CHANGE_64,
269 .device = 0,
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000270 };
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400271
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000272 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
273 args.uid = (__u64) current_fsuid();
274 if (inode->i_mode & S_ISGID)
275 args.gid = (__u64) inode->i_gid;
276 else
277 args.gid = (__u64) current_fsgid();
Steve French3ce53fc2007-06-08 14:55:14 +0000278 } else {
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000279 args.uid = NO_CHANGE_64;
280 args.gid = NO_CHANGE_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 }
Jeff Laytond44a9fe2011-01-07 11:30:29 -0500282 CIFSSMBUnixSetFileInfo(xid, tcon, &args, fileHandle,
283 current->tgid);
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000284 } else {
285 /* BB implement mode setting via Windows security
286 descriptors e.g. */
287 /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000289 /* Could set r/o dos attribute if mode & 0222 == 0 */
290 }
291
292cifs_create_get_file_info:
293 /* server might mask mode so we have to query for it */
294 if (tcon->unix_ext)
295 rc = cifs_get_inode_info_unix(&newinode, full_path,
296 inode->i_sb, xid);
297 else {
298 rc = cifs_get_inode_info(&newinode, full_path, buf,
299 inode->i_sb, xid, &fileHandle);
300 if (newinode) {
301 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
302 newinode->i_mode = mode;
303 if ((oplock & CIFS_CREATE_ACTION) &&
304 (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) {
305 newinode->i_uid = current_fsuid();
306 if (inode->i_mode & S_ISGID)
307 newinode->i_gid = inode->i_gid;
308 else
309 newinode->i_gid = current_fsgid();
Steve French6473a552005-11-29 20:20:10 -0800310 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 }
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000312 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000314cifs_create_set_dentry:
315 if (rc == 0)
Al Viro1c929cf2010-12-18 11:43:51 -0500316 d_instantiate(direntry, newinode);
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000317 else
Joe Perchesb6b38f72010-04-21 03:50:45 +0000318 cFYI(1, "Create worked, get_inode_info failed rc = %d", rc);
Steve Frenchf818dd52009-01-19 02:38:35 +0000319
Jeff Layton2422f672010-06-16 13:40:16 -0400320 if (newinode && nd && (nd->flags & LOOKUP_OPEN)) {
Suresh Jayaramanfdb36032010-05-11 09:46:46 +0530321 struct cifsFileInfo *pfile_info;
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400322 struct file *filp;
323
324 filp = lookup_instantiate_filp(nd, direntry, generic_file_open);
325 if (IS_ERR(filp)) {
326 rc = PTR_ERR(filp);
327 CIFSSMBClose(xid, tcon, fileHandle);
328 goto cifs_create_out;
329 }
330
Jeff Laytonabfe1ee2010-10-15 15:33:58 -0400331 pfile_info = cifs_new_fileinfo(fileHandle, filp, tlink, oplock);
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400332 if (pfile_info == NULL) {
333 fput(filp);
334 CIFSSMBClose(xid, tcon, fileHandle);
Suresh Jayaramanfdb36032010-05-11 09:46:46 +0530335 rc = -ENOMEM;
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400336 }
Jeff Layton2422f672010-06-16 13:40:16 -0400337 } else {
338 CIFSSMBClose(xid, tcon, fileHandle);
Steve French5fdae1f2007-06-05 18:30:44 +0000339 }
Jeff Layton2422f672010-06-16 13:40:16 -0400340
Steve Frenchd14537f12005-04-28 22:41:05 -0700341cifs_create_out:
342 kfree(buf);
343 kfree(full_path);
Jeff Layton7ffec372010-09-29 19:51:11 -0400344 cifs_put_tlink(tlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 FreeXid(xid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 return rc;
347}
348
Steve French5fdae1f2007-06-05 18:30:44 +0000349int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
350 dev_t device_number)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351{
352 int rc = -EPERM;
353 int xid;
354 struct cifs_sb_info *cifs_sb;
Jeff Layton7ffec372010-09-29 19:51:11 -0400355 struct tcon_link *tlink;
Steve French96daf2b2011-05-27 04:34:02 +0000356 struct cifs_tcon *pTcon;
Pavel Shilovskyfa2989f2011-05-26 10:01:59 +0400357 struct cifs_io_parms io_parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 char *full_path = NULL;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000359 struct inode *newinode = NULL;
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400360 int oplock = 0;
361 u16 fileHandle;
362 FILE_ALL_INFO *buf = NULL;
363 unsigned int bytes_written;
364 struct win_dev *pdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
366 if (!old_valid_dev(device_number))
367 return -EINVAL;
368
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 cifs_sb = CIFS_SB(inode->i_sb);
Jeff Layton7ffec372010-09-29 19:51:11 -0400370 tlink = cifs_sb_tlink(cifs_sb);
371 if (IS_ERR(tlink))
372 return PTR_ERR(tlink);
373
374 pTcon = tlink_tcon(tlink);
375
376 xid = GetXid();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 full_path = build_path_from_dentry(direntry);
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400379 if (full_path == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 rc = -ENOMEM;
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400381 goto mknod_out;
382 }
383
384 if (pTcon->unix_ext) {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400385 struct cifs_unix_set_info_args args = {
Al Viroce3b0f82009-03-29 19:08:22 -0400386 .mode = mode & ~current_umask(),
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400387 .ctime = NO_CHANGE_64,
388 .atime = NO_CHANGE_64,
389 .mtime = NO_CHANGE_64,
390 .device = device_number,
391 };
Steve French5fdae1f2007-06-05 18:30:44 +0000392 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
David Howellsa001e5b2008-11-14 10:38:47 +1100393 args.uid = (__u64) current_fsuid();
394 args.gid = (__u64) current_fsgid();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 } else {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400396 args.uid = NO_CHANGE_64;
397 args.gid = NO_CHANGE_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 }
Jeff Layton01ea95e2009-07-09 20:02:49 -0400399 rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, &args,
400 cifs_sb->local_nls,
401 cifs_sb->mnt_cifs_flags &
402 CIFS_MOUNT_MAP_SPECIAL_CHR);
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400403 if (rc)
404 goto mknod_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400406 rc = cifs_get_inode_info_unix(&newinode, full_path,
Steve French5fdae1f2007-06-05 18:30:44 +0000407 inode->i_sb, xid);
Steve Frenchd7245c22005-07-14 18:25:12 -0500408
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400409 if (rc == 0)
410 d_instantiate(direntry, newinode);
411 goto mknod_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 }
413
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400414 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
415 goto mknod_out;
416
417
418 cFYI(1, "sfu compat create special file");
419
420 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
421 if (buf == NULL) {
422 kfree(full_path);
423 rc = -ENOMEM;
424 FreeXid(xid);
425 return rc;
426 }
427
428 /* FIXME: would WRITE_OWNER | WRITE_DAC be better? */
429 rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_CREATE,
430 GENERIC_WRITE, CREATE_NOT_DIR | CREATE_OPTION_SPECIAL,
431 &fileHandle, &oplock, buf, cifs_sb->local_nls,
432 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
433 if (rc)
434 goto mknod_out;
435
436 /* BB Do not bother to decode buf since no local inode yet to put
437 * timestamps in, but we can reuse it safely */
438
439 pdev = (struct win_dev *)buf;
Pavel Shilovskyfa2989f2011-05-26 10:01:59 +0400440 io_parms.netfid = fileHandle;
441 io_parms.pid = current->tgid;
442 io_parms.tcon = pTcon;
443 io_parms.offset = 0;
444 io_parms.length = sizeof(struct win_dev);
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400445 if (S_ISCHR(mode)) {
446 memcpy(pdev->type, "IntxCHR", 8);
447 pdev->major =
448 cpu_to_le64(MAJOR(device_number));
449 pdev->minor =
450 cpu_to_le64(MINOR(device_number));
Pavel Shilovskyfa2989f2011-05-26 10:01:59 +0400451 rc = CIFSSMBWrite(xid, &io_parms,
452 &bytes_written, (char *)pdev,
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400453 NULL, 0);
454 } else if (S_ISBLK(mode)) {
455 memcpy(pdev->type, "IntxBLK", 8);
456 pdev->major =
457 cpu_to_le64(MAJOR(device_number));
458 pdev->minor =
459 cpu_to_le64(MINOR(device_number));
Pavel Shilovskyfa2989f2011-05-26 10:01:59 +0400460 rc = CIFSSMBWrite(xid, &io_parms,
461 &bytes_written, (char *)pdev,
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400462 NULL, 0);
463 } /* else if (S_ISFIFO) */
464 CIFSSMBClose(xid, pTcon, fileHandle);
465 d_drop(direntry);
466
467 /* FIXME: add code here to set EAs */
468
469mknod_out:
Steve Frenchd14537f12005-04-28 22:41:05 -0700470 kfree(full_path);
Jeff Layton5d9ac7f2010-08-05 13:58:22 -0400471 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 FreeXid(xid);
Jeff Layton7ffec372010-09-29 19:51:11 -0400473 cifs_put_tlink(tlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 return rc;
475}
476
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477struct dentry *
Steve French5fdae1f2007-06-05 18:30:44 +0000478cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
479 struct nameidata *nd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480{
481 int xid;
482 int rc = 0; /* to get around spurious gcc warning, set to zero here */
Jeff Layton590a3fe2009-09-12 11:54:28 -0400483 __u32 oplock = 0;
Steve Frencha6ce4932009-04-09 01:14:32 +0000484 __u16 fileHandle = 0;
485 bool posix_open = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 struct cifs_sb_info *cifs_sb;
Jeff Layton7ffec372010-09-29 19:51:11 -0400487 struct tcon_link *tlink;
Steve French96daf2b2011-05-27 04:34:02 +0000488 struct cifs_tcon *pTcon;
Jeff Layton2422f672010-06-16 13:40:16 -0400489 struct cifsFileInfo *cfile;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 struct inode *newInode = NULL;
491 char *full_path = NULL;
Steve Frencha6ce4932009-04-09 01:14:32 +0000492 struct file *filp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
494 xid = GetXid();
495
Joe Perchesb6b38f72010-04-21 03:50:45 +0000496 cFYI(1, "parent inode = 0x%p name is: %s and dentry = 0x%p",
497 parent_dir_inode, direntry->d_name.name, direntry);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 /* check whether path exists */
500
501 cifs_sb = CIFS_SB(parent_dir_inode->i_sb);
Jeff Layton7ffec372010-09-29 19:51:11 -0400502 tlink = cifs_sb_tlink(cifs_sb);
503 if (IS_ERR(tlink)) {
504 FreeXid(xid);
505 return (struct dentry *)tlink;
506 }
507 pTcon = tlink_tcon(tlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
Steve French296034f2006-04-21 18:18:37 +0000509 /*
510 * Don't allow the separator character in a path component.
511 * The VFS will not allow "/", but "\" is allowed by posix.
512 */
513 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
514 int i;
515 for (i = 0; i < direntry->d_name.len; i++)
516 if (direntry->d_name.name[i] == '\\') {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000517 cFYI(1, "Invalid file name");
Jeff Layton7ffec372010-09-29 19:51:11 -0400518 rc = -EINVAL;
519 goto lookup_out;
Steve French296034f2006-04-21 18:18:37 +0000520 }
521 }
522
Jeff Layton5ddf1e02009-07-05 11:01:02 -0400523 /*
524 * O_EXCL: optimize away the lookup, but don't hash the dentry. Let
525 * the VFS handle the create.
526 */
Steve French8e6c0332009-11-24 22:17:59 +0000527 if (nd && (nd->flags & LOOKUP_EXCL)) {
Jeff Layton5ddf1e02009-07-05 11:01:02 -0400528 d_instantiate(direntry, NULL);
Jeff Layton7ffec372010-09-29 19:51:11 -0400529 rc = 0;
530 goto lookup_out;
Jeff Layton5ddf1e02009-07-05 11:01:02 -0400531 }
532
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 /* can not grab the rename sem here since it would
534 deadlock in the cases (beginning of sys_rename itself)
535 in which we already have the sb rename sem */
536 full_path = build_path_from_dentry(direntry);
Steve French5fdae1f2007-06-05 18:30:44 +0000537 if (full_path == NULL) {
Jeff Layton7ffec372010-09-29 19:51:11 -0400538 rc = -ENOMEM;
539 goto lookup_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 }
541
542 if (direntry->d_inode != NULL) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000543 cFYI(1, "non-NULL inode in lookup");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 } else {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000545 cFYI(1, "NULL inode in lookup");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 }
Joe Perchesb6b38f72010-04-21 03:50:45 +0000547 cFYI(1, "Full path: %s inode = 0x%p", full_path, direntry->d_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548
Steve French8db14ca2009-05-23 18:57:25 +0000549 /* Posix open is only called (at lookup time) for file create now.
550 * For opens (rather than creates), because we do not know if it
551 * is a file or directory yet, and current Samba no longer allows
552 * us to do posix open on dirs, we could end up wasting an open call
553 * on what turns out to be a dir. For file opens, we wait to call posix
554 * open till cifs_open. It could be added here (lookup) in the future
555 * but the performance tradeoff of the extra network request when EISDIR
556 * or EACCES is returned would have to be weighed against the 50%
557 * reduction in network traffic in the other paths.
558 */
Steve Frencha6ce4932009-04-09 01:14:32 +0000559 if (pTcon->unix_ext) {
Steve French8e6c0332009-11-24 22:17:59 +0000560 if (nd && !(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) &&
Steve French8db14ca2009-05-23 18:57:25 +0000561 (nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open &&
Jeff Layton608712f2010-10-15 15:33:56 -0400562 (nd->intent.open.file->f_flags & O_CREAT)) {
Jeff Layton2422f672010-06-16 13:40:16 -0400563 rc = cifs_posix_open(full_path, &newInode,
Steve Frenchfa588e02010-04-22 19:21:55 +0000564 parent_dir_inode->i_sb,
Steve French703a3b82009-05-21 22:21:53 +0000565 nd->intent.open.create_mode,
Jeff Layton608712f2010-10-15 15:33:56 -0400566 nd->intent.open.file->f_flags, &oplock,
Steve Frencha6ce4932009-04-09 01:14:32 +0000567 &fileHandle, xid);
Steve French8db14ca2009-05-23 18:57:25 +0000568 /*
569 * The check below works around a bug in POSIX
570 * open in samba versions 3.3.1 and earlier where
571 * open could incorrectly fail with invalid parameter.
572 * If either that or op not supported returned, follow
573 * the normal lookup.
574 */
575 if ((rc == 0) || (rc == -ENOENT))
576 posix_open = true;
577 else if ((rc == -EINVAL) || (rc != -EOPNOTSUPP))
578 pTcon->broken_posix_open = true;
Steve Frencha6ce4932009-04-09 01:14:32 +0000579 }
580 if (!posix_open)
581 rc = cifs_get_inode_info_unix(&newInode, full_path,
582 parent_dir_inode->i_sb, xid);
583 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 rc = cifs_get_inode_info(&newInode, full_path, NULL,
Steve Frencha6ce4932009-04-09 01:14:32 +0000585 parent_dir_inode->i_sb, xid, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
587 if ((rc == 0) && (newInode != NULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 d_add(direntry, newInode);
Jeff Layton2422f672010-06-16 13:40:16 -0400589 if (posix_open) {
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400590 filp = lookup_instantiate_filp(nd, direntry,
591 generic_file_open);
592 if (IS_ERR(filp)) {
593 rc = PTR_ERR(filp);
594 CIFSSMBClose(xid, pTcon, fileHandle);
595 goto lookup_out;
596 }
597
Jeff Laytonabfe1ee2010-10-15 15:33:58 -0400598 cfile = cifs_new_fileinfo(fileHandle, filp, tlink,
599 oplock);
Jeff Layton2422f672010-06-16 13:40:16 -0400600 if (cfile == NULL) {
Jeff Layton6ca9f3b2010-06-16 13:40:16 -0400601 fput(filp);
Jeff Layton2422f672010-06-16 13:40:16 -0400602 CIFSSMBClose(xid, pTcon, fileHandle);
603 rc = -ENOMEM;
604 goto lookup_out;
605 }
Jeff Layton2422f672010-06-16 13:40:16 -0400606 }
Steve French5fdae1f2007-06-05 18:30:44 +0000607 /* since paths are not looked up by component - the parent
Steve French3abb9272005-11-28 08:16:13 -0800608 directories are presumed to be good here */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 renew_parental_timestamps(direntry);
610
611 } else if (rc == -ENOENT) {
612 rc = 0;
Steve French3abb9272005-11-28 08:16:13 -0800613 direntry->d_time = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 d_add(direntry, NULL);
Steve French5fdae1f2007-06-05 18:30:44 +0000615 /* if it was once a directory (but how can we tell?) we could do
616 shrink_dcache_parent(direntry); */
Steve Frenched2b9172008-01-20 00:30:29 +0000617 } else if (rc != -EACCES) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000618 cERROR(1, "Unexpected lookup error %d", rc);
Steve Frenched2b9172008-01-20 00:30:29 +0000619 /* We special case check for Access Denied - since that
620 is a common return code */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 }
622
Jeff Layton2422f672010-06-16 13:40:16 -0400623lookup_out:
Steve Frenchd14537f12005-04-28 22:41:05 -0700624 kfree(full_path);
Jeff Layton7ffec372010-09-29 19:51:11 -0400625 cifs_put_tlink(tlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 FreeXid(xid);
627 return ERR_PTR(rc);
628}
629
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630static int
631cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
632{
Nick Piggin34286d62011-01-07 17:49:57 +1100633 if (nd->flags & LOOKUP_RCU)
634 return -ECHILD;
635
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 if (direntry->d_inode) {
Jeff Laytondf2cf172010-02-12 07:44:16 -0500637 if (cifs_revalidate_dentry(direntry))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 return 0;
Nick Piggin262f86a2010-11-11 18:42:16 +1100639 else
640 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 }
642
Nick Piggin262f86a2010-11-11 18:42:16 +1100643 /*
644 * This may be nfsd (or something), anyway, we can't see the
645 * intent of this. So, since this can be for creation, drop it.
646 */
647 if (!nd)
648 return 0;
649
650 /*
651 * Drop the negative dentry, in order to make sure to use the
652 * case sensitive name which is specified by user if this is
653 * for creation.
654 */
655 if (!(nd->flags & (LOOKUP_CONTINUE | LOOKUP_PARENT))) {
656 if (nd->flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
657 return 0;
658 }
659
660 if (time_after(jiffies, direntry->d_time + HZ) || !lookupCacheEnabled)
661 return 0;
662
663 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664}
665
666/* static int cifs_d_delete(struct dentry *direntry)
667{
668 int rc = 0;
669
Joe Perchesb6b38f72010-04-21 03:50:45 +0000670 cFYI(1, "In cifs d_delete, name = %s", direntry->d_name.name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671
672 return rc;
673} */
674
Al Viro4fd03e82009-02-20 05:57:07 +0000675const struct dentry_operations cifs_dentry_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 .d_revalidate = cifs_d_revalidate,
David Howells01c64fe2011-01-14 18:45:47 +0000677 .d_automount = cifs_dfs_d_automount,
Steve French5fdae1f2007-06-05 18:30:44 +0000678/* d_delete: cifs_d_delete, */ /* not needed except for debugging */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679};
Steve Frenchb92327f2005-08-22 20:09:43 -0700680
Nick Pigginb1e6a012011-01-07 17:49:28 +1100681static int cifs_ci_hash(const struct dentry *dentry, const struct inode *inode,
682 struct qstr *q)
Steve Frenchb92327f2005-08-22 20:09:43 -0700683{
Nick Pigginb1e6a012011-01-07 17:49:28 +1100684 struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls;
Steve Frenchb92327f2005-08-22 20:09:43 -0700685 unsigned long hash;
686 int i;
687
688 hash = init_name_hash();
689 for (i = 0; i < q->len; i++)
690 hash = partial_name_hash(nls_tolower(codepage, q->name[i]),
691 hash);
692 q->hash = end_name_hash(hash);
693
694 return 0;
695}
696
Nick Piggin621e1552011-01-07 17:49:27 +1100697static int cifs_ci_compare(const struct dentry *parent,
698 const struct inode *pinode,
699 const struct dentry *dentry, const struct inode *inode,
700 unsigned int len, const char *str, const struct qstr *name)
Steve Frenchb92327f2005-08-22 20:09:43 -0700701{
Nick Piggin621e1552011-01-07 17:49:27 +1100702 struct nls_table *codepage = CIFS_SB(pinode->i_sb)->local_nls;
Steve Frenchb92327f2005-08-22 20:09:43 -0700703
Nick Piggin621e1552011-01-07 17:49:27 +1100704 if ((name->len == len) &&
705 (nls_strnicmp(codepage, name->name, str, len) == 0))
Steve Frenchb92327f2005-08-22 20:09:43 -0700706 return 0;
Steve Frenchb92327f2005-08-22 20:09:43 -0700707 return 1;
708}
709
Al Viro4fd03e82009-02-20 05:57:07 +0000710const struct dentry_operations cifs_ci_dentry_ops = {
Steve Frenchb92327f2005-08-22 20:09:43 -0700711 .d_revalidate = cifs_d_revalidate,
712 .d_hash = cifs_ci_hash,
713 .d_compare = cifs_ci_compare,
David Howells01c64fe2011-01-14 18:45:47 +0000714 .d_automount = cifs_dfs_d_automount,
Steve Frenchb92327f2005-08-22 20:09:43 -0700715};