blob: e457e1434349376a293338768991ba69cf73ebba [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>
27#include "cifsfs.h"
28#include "cifspdu.h"
29#include "cifsglob.h"
30#include "cifsproto.h"
31#include "cifs_debug.h"
32#include "cifs_fs_sb.h"
33
Steve French99ee4db2007-02-27 05:35:17 +000034static void
Linus Torvalds1da177e2005-04-16 15:20:36 -070035renew_parental_timestamps(struct dentry *direntry)
36{
Steve French5fdae1f2007-06-05 18:30:44 +000037 /* BB check if there is a way to get the kernel to do this or if we
38 really need this */
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 do {
40 direntry->d_time = jiffies;
41 direntry = direntry->d_parent;
Steve French5fdae1f2007-06-05 18:30:44 +000042 } while (!IS_ROOT(direntry));
Linus Torvalds1da177e2005-04-16 15:20:36 -070043}
44
45/* Note: caller must free return buffer */
46char *
47build_path_from_dentry(struct dentry *direntry)
48{
49 struct dentry *temp;
Steve French2fe87f02006-09-21 07:02:52 +000050 int namelen;
51 int pplen;
Steve French646dd532008-05-15 01:50:56 +000052 int dfsplen;
Linus Torvalds1da177e2005-04-16 15:20:36 -070053 char *full_path;
Steve French88274812006-03-09 22:21:45 +000054 char dirsep;
Steve French646dd532008-05-15 01:50:56 +000055 struct cifs_sb_info *cifs_sb;
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
Steve French5fdae1f2007-06-05 18:30:44 +000057 if (direntry == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 return NULL; /* not much we can do if dentry is freed and
59 we need to reopen the file after it was closed implicitly
60 when the server crashed */
61
Steve French646dd532008-05-15 01:50:56 +000062 cifs_sb = CIFS_SB(direntry->d_sb);
63 dirsep = CIFS_DIR_SEP(cifs_sb);
64 pplen = cifs_sb->prepathlen;
65 if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS))
66 dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1);
67 else
68 dfsplen = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070069cifs_bp_rename_retry:
Steve French646dd532008-05-15 01:50:56 +000070 namelen = pplen + 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) {
75 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);
Steve French2fe87f02006-09-21 07:02:52 +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) {
96 cERROR(1, ("corrupt dentry"));
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 kfree(full_path);
98 return NULL;
99 }
100 }
Steve French646dd532008-05-15 01:50:56 +0000101 if (namelen != pplen + dfsplen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 cERROR(1,
Steve French2fe87f02006-09-21 07:02:52 +0000103 ("did not end path lookup where expected namelen is %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 namelen));
Steve French5fdae1f2007-06-05 18:30:44 +0000105 /* presumably this is only possible if racing with a rename
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 of one of the parent directories (we can not lock the dentries
107 above us to prevent this, but retrying should be harmless) */
108 kfree(full_path);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 goto cifs_bp_rename_retry;
110 }
Steve French2fe87f02006-09-21 07:02:52 +0000111 /* DIR_SEP already set for byte 0 / vs \ but not for
112 subsequent slashes in prepath which currently must
113 be entered the right way - not sure if there is an alternative
114 since the '\' is a valid posix character so we can not switch
115 those safely to '/' if any are found in the middle of the prepath */
116 /* BB test paths to Windows with '/' in the midst of prepath */
Steve French646dd532008-05-15 01:50:56 +0000117
118 if (dfsplen) {
119 strncpy(full_path, cifs_sb->tcon->treeName, dfsplen);
120 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
121 int i;
122 for (i = 0; i < dfsplen; i++) {
123 if (full_path[i] == '\\')
124 full_path[i] = '/';
125 }
126 }
127 }
128 strncpy(full_path + dfsplen, CIFS_SB(direntry->d_sb)->prepath, pplen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 return full_path;
130}
131
Steve French7fc8f4e2009-02-23 20:43:11 +0000132int cifs_posix_open(char *full_path, struct inode **pinode,
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000133 struct super_block *sb, int mode, int oflags,
134 int *poplock, __u16 *pnetfid, int xid)
135{
136 int rc;
137 __u32 oplock;
138 FILE_UNIX_BASIC_INFO *presp_data;
139 __u32 posix_flags = 0;
140 struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
141
142 cFYI(1, ("posix open %s", full_path));
143
144 presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
145 if (presp_data == NULL)
146 return -ENOMEM;
147
148/* So far cifs posix extensions can only map the following flags.
149 There are other valid fmode oflags such as FMODE_LSEEK, FMODE_PREAD, but
150 so far we do not seem to need them, and we can treat them as local only */
151 if ((oflags & (FMODE_READ | FMODE_WRITE)) ==
152 (FMODE_READ | FMODE_WRITE))
153 posix_flags = SMB_O_RDWR;
154 else if (oflags & FMODE_READ)
155 posix_flags = SMB_O_RDONLY;
156 else if (oflags & FMODE_WRITE)
157 posix_flags = SMB_O_WRONLY;
158 if (oflags & O_CREAT)
159 posix_flags |= SMB_O_CREAT;
160 if (oflags & O_EXCL)
161 posix_flags |= SMB_O_EXCL;
162 if (oflags & O_TRUNC)
163 posix_flags |= SMB_O_TRUNC;
164 if (oflags & O_APPEND)
165 posix_flags |= SMB_O_APPEND;
166 if (oflags & O_SYNC)
167 posix_flags |= SMB_O_SYNC;
168 if (oflags & O_DIRECTORY)
169 posix_flags |= SMB_O_DIRECTORY;
170 if (oflags & O_NOFOLLOW)
171 posix_flags |= SMB_O_NOFOLLOW;
172 if (oflags & O_DIRECT)
173 posix_flags |= SMB_O_DIRECT;
174
175
176 rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode,
177 pnetfid, presp_data, &oplock, full_path,
178 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
179 CIFS_MOUNT_MAP_SPECIAL_CHR);
180 if (rc)
181 goto posix_open_ret;
182
183 if (presp_data->Type == cpu_to_le32(-1))
184 goto posix_open_ret; /* open ok, caller does qpathinfo */
185
186 /* get new inode and set it up */
187 if (!pinode)
188 goto posix_open_ret; /* caller does not need info */
189
Steve French85a6dac2009-04-01 05:22:00 +0000190 if (*pinode == NULL) {
191 __u64 unique_id = le64_to_cpu(presp_data->UniqueId);
192 *pinode = cifs_new_inode(sb, &unique_id);
193 }
Steve French7fc8f4e2009-02-23 20:43:11 +0000194 /* else an inode was passed in. Update its info, don't create one */
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000195
196 /* We do not need to close the file if new_inode fails since
197 the caller will retry qpathinfo as long as inode is null */
198 if (*pinode == NULL)
199 goto posix_open_ret;
200
201 posix_fill_in_inode(*pinode, presp_data, 1);
202
203posix_open_ret:
204 kfree(presp_data);
205 return rc;
206}
207
Steve Frenchf818dd52009-01-19 02:38:35 +0000208static void setup_cifs_dentry(struct cifsTconInfo *tcon,
209 struct dentry *direntry,
210 struct inode *newinode)
211{
212 if (tcon->nocase)
213 direntry->d_op = &cifs_ci_dentry_ops;
214 else
215 direntry->d_op = &cifs_dentry_ops;
216 d_instantiate(direntry, newinode);
217}
218
Steve French39798772006-05-31 22:40:51 +0000219/* Inode operations in similar order to how they appear in Linux file fs.h */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220
221int
222cifs_create(struct inode *inode, struct dentry *direntry, int mode,
223 struct nameidata *nd)
224{
225 int rc = -ENOENT;
226 int xid;
Jeff Layton67750fb2008-05-09 22:28:02 +0000227 int create_options = CREATE_NOT_DIR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 int oplock = 0;
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000229 int oflags;
230 /*
231 * BB below access is probably too much for mknod to request
232 * but we have to do query and setpathinfo so requesting
233 * less could fail (unless we want to request getatr and setatr
234 * permissions (only). At least for POSIX we do not have to
235 * request so much.
236 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 int desiredAccess = GENERIC_READ | GENERIC_WRITE;
238 __u16 fileHandle;
239 struct cifs_sb_info *cifs_sb;
Steve Frenchf818dd52009-01-19 02:38:35 +0000240 struct cifsTconInfo *tcon;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 char *full_path = NULL;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000242 FILE_ALL_INFO *buf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 struct inode *newinode = NULL;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000244 struct cifsInodeInfo *pCifsInode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 int disposition = FILE_OVERWRITE_IF;
Steve French4b18f2a2008-04-29 00:06:05 +0000246 bool write_only = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
248 xid = GetXid();
249
250 cifs_sb = CIFS_SB(inode->i_sb);
Steve Frenchf818dd52009-01-19 02:38:35 +0000251 tcon = cifs_sb->tcon;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 full_path = build_path_from_dentry(direntry);
Steve French5fdae1f2007-06-05 18:30:44 +0000254 if (full_path == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 FreeXid(xid);
256 return -ENOMEM;
257 }
258
Al Viroce3b0f82009-03-29 19:08:22 -0400259 mode &= ~current_umask();
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000260 if (oplockEnabled)
261 oplock = REQ_OPLOCK;
262
263 if (nd && (nd->flags & LOOKUP_OPEN))
264 oflags = nd->intent.open.flags;
265 else
266 oflags = FMODE_READ;
267
268 if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
269 (CIFS_UNIX_POSIX_PATH_OPS_CAP &
270 le64_to_cpu(tcon->fsUnixInfo.Capability))) {
271 rc = cifs_posix_open(full_path, &newinode, inode->i_sb,
272 mode, oflags, &oplock, &fileHandle, xid);
273 /* EIO could indicate that (posix open) operation is not
274 supported, despite what server claimed in capability
275 negotation. EREMOTE indicates DFS junction, which is not
276 handled in posix open */
277
278 if ((rc == 0) && (newinode == NULL))
279 goto cifs_create_get_file_info; /* query inode info */
280 else if (rc == 0) /* success, no need to query */
281 goto cifs_create_set_dentry;
282 else if ((rc != -EIO) && (rc != -EREMOTE) &&
283 (rc != -EOPNOTSUPP)) /* path not found or net err */
284 goto cifs_create_out;
285 /* else fallthrough to retry, using older open call, this is
286 case where server does not support this SMB level, and
287 falsely claims capability (also get here for DFS case
288 which should be rare for path not covered on files) */
289 }
Steve Frenchf818dd52009-01-19 02:38:35 +0000290
Steve French5fdae1f2007-06-05 18:30:44 +0000291 if (nd && (nd->flags & LOOKUP_OPEN)) {
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000292 /* if the file is going to stay open, then we
293 need to set the desired access properly */
Miklos Szeredie08fc042005-09-06 15:18:26 -0700294 desiredAccess = 0;
295 if (oflags & FMODE_READ)
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000296 desiredAccess |= GENERIC_READ; /* is this too little? */
Miklos Szeredie08fc042005-09-06 15:18:26 -0700297 if (oflags & FMODE_WRITE) {
298 desiredAccess |= GENERIC_WRITE;
299 if (!(oflags & FMODE_READ))
Steve French4b18f2a2008-04-29 00:06:05 +0000300 write_only = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 }
302
Steve French5fdae1f2007-06-05 18:30:44 +0000303 if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 disposition = FILE_CREATE;
Steve French5fdae1f2007-06-05 18:30:44 +0000305 else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 disposition = FILE_OVERWRITE_IF;
Steve French5fdae1f2007-06-05 18:30:44 +0000307 else if ((oflags & O_CREAT) == O_CREAT)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 disposition = FILE_OPEN_IF;
Steve Frenchad7a2922008-02-07 23:25:02 +0000309 else
Steve French5fdae1f2007-06-05 18:30:44 +0000310 cFYI(1, ("Create flag not set in create function"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 }
312
Steve French5fdae1f2007-06-05 18:30:44 +0000313 /* BB add processing to set equivalent of mode - e.g. via CreateX with
314 ACLs */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
Steve French5fdae1f2007-06-05 18:30:44 +0000316 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
317 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 kfree(full_path);
319 FreeXid(xid);
320 return -ENOMEM;
321 }
Jeff Layton67750fb2008-05-09 22:28:02 +0000322
Jeff Layton67750fb2008-05-09 22:28:02 +0000323 /*
324 * if we're not using unix extensions, see if we need to set
325 * ATTR_READONLY on the create call
326 */
Steve Frenchf818dd52009-01-19 02:38:35 +0000327 if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
Jeff Layton67750fb2008-05-09 22:28:02 +0000328 create_options |= CREATE_OPTION_READONLY;
329
Steve French5fdae1f2007-06-05 18:30:44 +0000330 if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS)
Steve Frenchf818dd52009-01-19 02:38:35 +0000331 rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
Jeff Layton67750fb2008-05-09 22:28:02 +0000332 desiredAccess, create_options,
Steve French737b7582005-04-28 22:41:06 -0700333 &fileHandle, &oplock, buf, cifs_sb->local_nls,
334 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
Steve French5bafd762006-06-07 00:18:43 +0000335 else
336 rc = -EIO; /* no NT SMB support fall into legacy open below */
337
Steve French5fdae1f2007-06-05 18:30:44 +0000338 if (rc == -EIO) {
Steve Frencha9d02ad2005-08-24 23:06:05 -0700339 /* old server, retry the open legacy style */
Steve Frenchf818dd52009-01-19 02:38:35 +0000340 rc = SMBLegacyOpen(xid, tcon, full_path, disposition,
Jeff Layton67750fb2008-05-09 22:28:02 +0000341 desiredAccess, create_options,
Steve Frencha9d02ad2005-08-24 23:06:05 -0700342 &fileHandle, &oplock, buf, cifs_sb->local_nls,
343 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
Steve French5fdae1f2007-06-05 18:30:44 +0000344 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 if (rc) {
Steve French26a21b92006-05-31 18:05:34 +0000346 cFYI(1, ("cifs_create returned 0x%x", rc));
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000347 goto cifs_create_out;
348 }
349
350 /* If Open reported that we actually created a file
351 then we now have to set the mode if possible */
352 if ((tcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) {
353 struct cifs_unix_set_info_args args = {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400354 .mode = mode,
355 .ctime = NO_CHANGE_64,
356 .atime = NO_CHANGE_64,
357 .mtime = NO_CHANGE_64,
358 .device = 0,
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000359 };
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400360
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000361 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
362 args.uid = (__u64) current_fsuid();
363 if (inode->i_mode & S_ISGID)
364 args.gid = (__u64) inode->i_gid;
365 else
366 args.gid = (__u64) current_fsgid();
Steve French3ce53fc2007-06-08 14:55:14 +0000367 } else {
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000368 args.uid = NO_CHANGE_64;
369 args.gid = NO_CHANGE_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 }
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000371 CIFSSMBUnixSetInfo(xid, tcon, full_path, &args,
372 cifs_sb->local_nls,
373 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
374 } else {
375 /* BB implement mode setting via Windows security
376 descriptors e.g. */
377 /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000379 /* Could set r/o dos attribute if mode & 0222 == 0 */
380 }
381
382cifs_create_get_file_info:
383 /* server might mask mode so we have to query for it */
384 if (tcon->unix_ext)
385 rc = cifs_get_inode_info_unix(&newinode, full_path,
386 inode->i_sb, xid);
387 else {
388 rc = cifs_get_inode_info(&newinode, full_path, buf,
389 inode->i_sb, xid, &fileHandle);
390 if (newinode) {
391 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
392 newinode->i_mode = mode;
393 if ((oplock & CIFS_CREATE_ACTION) &&
394 (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) {
395 newinode->i_uid = current_fsuid();
396 if (inode->i_mode & S_ISGID)
397 newinode->i_gid = inode->i_gid;
398 else
399 newinode->i_gid = current_fsgid();
Steve French6473a552005-11-29 20:20:10 -0800400 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 }
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000402 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000404cifs_create_set_dentry:
405 if (rc == 0)
406 setup_cifs_dentry(tcon, direntry, newinode);
407 else
408 cFYI(1, ("Create worked, get_inode_info failed rc = %d", rc));
Steve Frenchf818dd52009-01-19 02:38:35 +0000409
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000410 /* nfsd case - nfs srv does not set nd */
411 if ((nd == NULL) || (!(nd->flags & LOOKUP_OPEN))) {
412 /* mknod case - do not leave file open */
413 CIFSSMBClose(xid, tcon, fileHandle);
414 } else if (newinode) {
415 struct cifsFileInfo *pCifsFile =
416 kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
Steve French221601c2007-06-05 20:35:06 +0000417
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000418 if (pCifsFile == NULL)
419 goto cifs_create_out;
420 pCifsFile->netfid = fileHandle;
421 pCifsFile->pid = current->tgid;
422 pCifsFile->pInode = newinode;
423 pCifsFile->invalidHandle = false;
424 pCifsFile->closePend = false;
425 init_MUTEX(&pCifsFile->fh_sem);
426 mutex_init(&pCifsFile->lock_mutex);
427 INIT_LIST_HEAD(&pCifsFile->llist);
428 atomic_set(&pCifsFile->wrtPending, 0);
Steve Frenche466e482006-08-15 13:07:18 +0000429
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000430 /* set the following in open now
Steve Frenchd14537f12005-04-28 22:41:05 -0700431 pCifsFile->pfile = file; */
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000432 write_lock(&GlobalSMBSeslock);
433 list_add(&pCifsFile->tlist, &tcon->openFileList);
434 pCifsInode = CIFS_I(newinode);
435 if (pCifsInode) {
436 /* if readable file instance put first in list*/
437 if (write_only) {
438 list_add_tail(&pCifsFile->flist,
439 &pCifsInode->openFileList);
440 } else {
441 list_add(&pCifsFile->flist,
442 &pCifsInode->openFileList);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 }
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000444 if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
445 pCifsInode->clientCanCacheAll = true;
446 pCifsInode->clientCanCacheRead = true;
447 cFYI(1, ("Exclusive Oplock inode %p",
448 newinode));
449 } else if ((oplock & 0xF) == OPLOCK_READ)
450 pCifsInode->clientCanCacheRead = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 }
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000452 write_unlock(&GlobalSMBSeslock);
Steve French5fdae1f2007-06-05 18:30:44 +0000453 }
Steve Frenchd14537f12005-04-28 22:41:05 -0700454cifs_create_out:
455 kfree(buf);
456 kfree(full_path);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 FreeXid(xid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 return rc;
459}
460
Steve French5fdae1f2007-06-05 18:30:44 +0000461int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
462 dev_t device_number)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463{
464 int rc = -EPERM;
465 int xid;
466 struct cifs_sb_info *cifs_sb;
467 struct cifsTconInfo *pTcon;
468 char *full_path = NULL;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000469 struct inode *newinode = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
471 if (!old_valid_dev(device_number))
472 return -EINVAL;
473
474 xid = GetXid();
475
476 cifs_sb = CIFS_SB(inode->i_sb);
477 pTcon = cifs_sb->tcon;
478
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 full_path = build_path_from_dentry(direntry);
Steve French5fdae1f2007-06-05 18:30:44 +0000480 if (full_path == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 rc = -ENOMEM;
Steve Frenchc18c8422007-07-18 23:21:09 +0000482 else if (pTcon->unix_ext) {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400483 struct cifs_unix_set_info_args args = {
Al Viroce3b0f82009-03-29 19:08:22 -0400484 .mode = mode & ~current_umask(),
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400485 .ctime = NO_CHANGE_64,
486 .atime = NO_CHANGE_64,
487 .mtime = NO_CHANGE_64,
488 .device = device_number,
489 };
Steve French5fdae1f2007-06-05 18:30:44 +0000490 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
David Howellsa001e5b2008-11-14 10:38:47 +1100491 args.uid = (__u64) current_fsuid();
492 args.gid = (__u64) current_fsgid();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 } else {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400494 args.uid = NO_CHANGE_64;
495 args.gid = NO_CHANGE_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 }
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400497 rc = CIFSSMBUnixSetInfo(xid, pTcon, full_path,
498 &args, cifs_sb->local_nls,
499 cifs_sb->mnt_cifs_flags &
500 CIFS_MOUNT_MAP_SPECIAL_CHR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
Steve French5fdae1f2007-06-05 18:30:44 +0000502 if (!rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 rc = cifs_get_inode_info_unix(&newinode, full_path,
Steve French5fdae1f2007-06-05 18:30:44 +0000504 inode->i_sb, xid);
Steve Frenchb92327f2005-08-22 20:09:43 -0700505 if (pTcon->nocase)
506 direntry->d_op = &cifs_ci_dentry_ops;
507 else
508 direntry->d_op = &cifs_dentry_ops;
Steve French5fdae1f2007-06-05 18:30:44 +0000509 if (rc == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 d_instantiate(direntry, newinode);
511 }
Steve Frenchd7245c22005-07-14 18:25:12 -0500512 } else {
Steve French5fdae1f2007-06-05 18:30:44 +0000513 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
Steve Frencheda3c0292005-07-21 15:20:28 -0700514 int oplock = 0;
515 u16 fileHandle;
Steve Frenchad7a2922008-02-07 23:25:02 +0000516 FILE_ALL_INFO *buf;
Steve Frenchd7245c22005-07-14 18:25:12 -0500517
Steve French5fdae1f2007-06-05 18:30:44 +0000518 cFYI(1, ("sfu compat create special file"));
Steve Frenchd7245c22005-07-14 18:25:12 -0500519
Steve French5fdae1f2007-06-05 18:30:44 +0000520 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
521 if (buf == NULL) {
Steve Frencheda3c0292005-07-21 15:20:28 -0700522 kfree(full_path);
523 FreeXid(xid);
524 return -ENOMEM;
525 }
526
527 rc = CIFSSMBOpen(xid, pTcon, full_path,
528 FILE_CREATE, /* fail if exists */
Steve French5fdae1f2007-06-05 18:30:44 +0000529 GENERIC_WRITE /* BB would
Steve Frencheda3c0292005-07-21 15:20:28 -0700530 WRITE_OWNER | WRITE_DAC be better? */,
531 /* Create a file and set the
532 file attribute to SYSTEM */
533 CREATE_NOT_DIR | CREATE_OPTION_SPECIAL,
534 &fileHandle, &oplock, buf,
535 cifs_sb->local_nls,
Steve French5fdae1f2007-06-05 18:30:44 +0000536 cifs_sb->mnt_cifs_flags &
Steve Frencheda3c0292005-07-21 15:20:28 -0700537 CIFS_MOUNT_MAP_SPECIAL_CHR);
538
Steve French5bafd762006-06-07 00:18:43 +0000539 /* BB FIXME - add handling for backlevel servers
540 which need legacy open and check for all
Steve French5fdae1f2007-06-05 18:30:44 +0000541 calls to SMBOpen for fallback to SMBLeagcyOpen */
542 if (!rc) {
Steve Frencheda3c0292005-07-21 15:20:28 -0700543 /* BB Do not bother to decode buf since no
Steve French86c96b42005-11-18 20:25:31 -0800544 local inode yet to put timestamps in,
545 but we can reuse it safely */
Steve French77159b42007-08-31 01:10:17 +0000546 unsigned int bytes_written;
Steve French86c96b42005-11-18 20:25:31 -0800547 struct win_dev *pdev;
548 pdev = (struct win_dev *)buf;
Steve French5fdae1f2007-06-05 18:30:44 +0000549 if (S_ISCHR(mode)) {
Steve French86c96b42005-11-18 20:25:31 -0800550 memcpy(pdev->type, "IntxCHR", 8);
551 pdev->major =
552 cpu_to_le64(MAJOR(device_number));
Steve French5fdae1f2007-06-05 18:30:44 +0000553 pdev->minor =
Steve French86c96b42005-11-18 20:25:31 -0800554 cpu_to_le64(MINOR(device_number));
555 rc = CIFSSMBWrite(xid, pTcon,
556 fileHandle,
557 sizeof(struct win_dev),
558 0, &bytes_written, (char *)pdev,
559 NULL, 0);
Steve French5fdae1f2007-06-05 18:30:44 +0000560 } else if (S_ISBLK(mode)) {
Steve French86c96b42005-11-18 20:25:31 -0800561 memcpy(pdev->type, "IntxBLK", 8);
562 pdev->major =
563 cpu_to_le64(MAJOR(device_number));
564 pdev->minor =
565 cpu_to_le64(MINOR(device_number));
566 rc = CIFSSMBWrite(xid, pTcon,
567 fileHandle,
568 sizeof(struct win_dev),
569 0, &bytes_written, (char *)pdev,
570 NULL, 0);
571 } /* else if(S_ISFIFO */
Steve Frencheda3c0292005-07-21 15:20:28 -0700572 CIFSSMBClose(xid, pTcon, fileHandle);
573 d_drop(direntry);
574 }
575 kfree(buf);
Steve Frenchd7245c22005-07-14 18:25:12 -0500576 /* add code here to set EAs */
577 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 }
579
Steve Frenchd14537f12005-04-28 22:41:05 -0700580 kfree(full_path);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 FreeXid(xid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 return rc;
583}
584
585
586struct dentry *
Steve French5fdae1f2007-06-05 18:30:44 +0000587cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
588 struct nameidata *nd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589{
590 int xid;
591 int rc = 0; /* to get around spurious gcc warning, set to zero here */
592 struct cifs_sb_info *cifs_sb;
593 struct cifsTconInfo *pTcon;
594 struct inode *newInode = NULL;
595 char *full_path = NULL;
596
597 xid = GetXid();
598
Steve French61e74802008-12-03 00:57:54 +0000599 cFYI(1, ("parent inode = 0x%p name is: %s and dentry = 0x%p",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 parent_dir_inode, direntry->d_name.name, direntry));
601
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 /* check whether path exists */
603
604 cifs_sb = CIFS_SB(parent_dir_inode->i_sb);
605 pTcon = cifs_sb->tcon;
606
Steve French296034f2006-04-21 18:18:37 +0000607 /*
608 * Don't allow the separator character in a path component.
609 * The VFS will not allow "/", but "\" is allowed by posix.
610 */
611 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
612 int i;
613 for (i = 0; i < direntry->d_name.len; i++)
614 if (direntry->d_name.name[i] == '\\') {
615 cFYI(1, ("Invalid file name"));
616 FreeXid(xid);
617 return ERR_PTR(-EINVAL);
618 }
619 }
620
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 /* can not grab the rename sem here since it would
622 deadlock in the cases (beginning of sys_rename itself)
623 in which we already have the sb rename sem */
624 full_path = build_path_from_dentry(direntry);
Steve French5fdae1f2007-06-05 18:30:44 +0000625 if (full_path == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 FreeXid(xid);
627 return ERR_PTR(-ENOMEM);
628 }
629
630 if (direntry->d_inode != NULL) {
Steve French61e74802008-12-03 00:57:54 +0000631 cFYI(1, ("non-NULL inode in lookup"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 } else {
Steve French61e74802008-12-03 00:57:54 +0000633 cFYI(1, ("NULL inode in lookup"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 }
Steve French61e74802008-12-03 00:57:54 +0000635 cFYI(1, ("Full path: %s inode = 0x%p", full_path, direntry->d_inode));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
Steve Frenchc18c8422007-07-18 23:21:09 +0000637 if (pTcon->unix_ext)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 rc = cifs_get_inode_info_unix(&newInode, full_path,
Steve French5fdae1f2007-06-05 18:30:44 +0000639 parent_dir_inode->i_sb, xid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 else
641 rc = cifs_get_inode_info(&newInode, full_path, NULL,
Steve French8b1327f2008-03-14 22:37:16 +0000642 parent_dir_inode->i_sb, xid, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643
644 if ((rc == 0) && (newInode != NULL)) {
Steve Frenchb92327f2005-08-22 20:09:43 -0700645 if (pTcon->nocase)
646 direntry->d_op = &cifs_ci_dentry_ops;
647 else
648 direntry->d_op = &cifs_dentry_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 d_add(direntry, newInode);
650
Steve French5fdae1f2007-06-05 18:30:44 +0000651 /* since paths are not looked up by component - the parent
Steve French3abb9272005-11-28 08:16:13 -0800652 directories are presumed to be good here */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 renew_parental_timestamps(direntry);
654
655 } else if (rc == -ENOENT) {
656 rc = 0;
Steve French3abb9272005-11-28 08:16:13 -0800657 direntry->d_time = jiffies;
658 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, NULL);
Steve French5fdae1f2007-06-05 18:30:44 +0000663 /* if it was once a directory (but how can we tell?) we could do
664 shrink_dcache_parent(direntry); */
Steve Frenched2b9172008-01-20 00:30:29 +0000665 } else if (rc != -EACCES) {
666 cERROR(1, ("Unexpected lookup error %d", rc));
667 /* We special case check for Access Denied - since that
668 is a common return code */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 }
670
Steve Frenchd14537f12005-04-28 22:41:05 -0700671 kfree(full_path);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 FreeXid(xid);
673 return ERR_PTR(rc);
674}
675
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676static int
677cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
678{
679 int isValid = 1;
680
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 if (direntry->d_inode) {
Steve Frenchad7a2922008-02-07 23:25:02 +0000682 if (cifs_revalidate(direntry))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 } else {
Steve French3abb9272005-11-28 08:16:13 -0800685 cFYI(1, ("neg dentry 0x%p name = %s",
686 direntry, direntry->d_name.name));
Steve French5fdae1f2007-06-05 18:30:44 +0000687 if (time_after(jiffies, direntry->d_time + HZ) ||
Steve French3abb9272005-11-28 08:16:13 -0800688 !lookupCacheEnabled) {
689 d_drop(direntry);
690 isValid = 0;
Steve French5fdae1f2007-06-05 18:30:44 +0000691 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 }
693
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 return isValid;
695}
696
697/* static int cifs_d_delete(struct dentry *direntry)
698{
699 int rc = 0;
700
701 cFYI(1, ("In cifs d_delete, name = %s", direntry->d_name.name));
702
703 return rc;
704} */
705
Al Viro4fd03e82009-02-20 05:57:07 +0000706const struct dentry_operations cifs_dentry_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 .d_revalidate = cifs_d_revalidate,
Steve French5fdae1f2007-06-05 18:30:44 +0000708/* d_delete: cifs_d_delete, */ /* not needed except for debugging */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709};
Steve Frenchb92327f2005-08-22 20:09:43 -0700710
711static int cifs_ci_hash(struct dentry *dentry, struct qstr *q)
712{
713 struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
714 unsigned long hash;
715 int i;
716
717 hash = init_name_hash();
718 for (i = 0; i < q->len; i++)
719 hash = partial_name_hash(nls_tolower(codepage, q->name[i]),
720 hash);
721 q->hash = end_name_hash(hash);
722
723 return 0;
724}
725
726static int cifs_ci_compare(struct dentry *dentry, struct qstr *a,
727 struct qstr *b)
728{
729 struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
730
731 if ((a->len == b->len) &&
732 (nls_strnicmp(codepage, a->name, b->name, a->len) == 0)) {
733 /*
734 * To preserve case, don't let an existing negative dentry's
735 * case take precedence. If a is not a negative dentry, this
736 * should have no side effects
737 */
Steve Frenchc3291632008-05-15 05:41:54 +0000738 memcpy((void *)a->name, b->name, a->len);
Steve Frenchb92327f2005-08-22 20:09:43 -0700739 return 0;
740 }
741 return 1;
742}
743
Al Viro4fd03e82009-02-20 05:57:07 +0000744const struct dentry_operations cifs_ci_dentry_ops = {
Steve Frenchb92327f2005-08-22 20:09:43 -0700745 .d_revalidate = cifs_d_revalidate,
746 .d_hash = cifs_ci_hash,
747 .d_compare = cifs_ci_compare,
748};