blob: e937da7522ef61fc85390c8ae84a94f8a6400da2 [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 Frencha6ce4932009-04-09 01:14:32 +0000132static void
133cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle,
134 struct cifsTconInfo *tcon, bool write_only)
135{
136 int oplock = 0;
137 struct cifsFileInfo *pCifsFile;
138 struct cifsInodeInfo *pCifsInode;
139
140 pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
141
142 if (pCifsFile == NULL)
143 return;
144
145 if (oplockEnabled)
146 oplock = REQ_OPLOCK;
147
148 pCifsFile->netfid = fileHandle;
149 pCifsFile->pid = current->tgid;
150 pCifsFile->pInode = newinode;
151 pCifsFile->invalidHandle = false;
Steve Frenchbc8cd432009-04-12 18:18:40 +0000152 pCifsFile->closePend = false;
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);
156 atomic_set(&pCifsFile->wrtPending, 0);
157
158 /* set the following in open now
159 pCifsFile->pfile = file; */
160 write_lock(&GlobalSMBSeslock);
161 list_add(&pCifsFile->tlist, &tcon->openFileList);
162 pCifsInode = CIFS_I(newinode);
163 if (pCifsInode) {
164 /* if readable file instance put first in list*/
Steve Frenchbc8cd432009-04-12 18:18:40 +0000165 if (write_only)
Steve Frencha6ce4932009-04-09 01:14:32 +0000166 list_add_tail(&pCifsFile->flist,
167 &pCifsInode->openFileList);
Steve Frenchbc8cd432009-04-12 18:18:40 +0000168 else
169 list_add(&pCifsFile->flist, &pCifsInode->openFileList);
170
Steve Frencha6ce4932009-04-09 01:14:32 +0000171 if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
172 pCifsInode->clientCanCacheAll = true;
173 pCifsInode->clientCanCacheRead = true;
Steve Frenchbc8cd432009-04-12 18:18:40 +0000174 cFYI(1, ("Exclusive Oplock inode %p", newinode));
Steve Frencha6ce4932009-04-09 01:14:32 +0000175 } else if ((oplock & 0xF) == OPLOCK_READ)
Steve Frenchbc8cd432009-04-12 18:18:40 +0000176 pCifsInode->clientCanCacheRead = true;
Steve Frencha6ce4932009-04-09 01:14:32 +0000177 }
178 write_unlock(&GlobalSMBSeslock);
179}
180
Steve French7fc8f4e2009-02-23 20:43:11 +0000181int cifs_posix_open(char *full_path, struct inode **pinode,
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000182 struct super_block *sb, int mode, int oflags,
183 int *poplock, __u16 *pnetfid, int xid)
184{
185 int rc;
186 __u32 oplock;
Steve Frencha6ce4932009-04-09 01:14:32 +0000187 bool write_only = false;
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000188 FILE_UNIX_BASIC_INFO *presp_data;
189 __u32 posix_flags = 0;
190 struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
191
192 cFYI(1, ("posix open %s", full_path));
193
194 presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
195 if (presp_data == NULL)
196 return -ENOMEM;
197
198/* So far cifs posix extensions can only map the following flags.
199 There are other valid fmode oflags such as FMODE_LSEEK, FMODE_PREAD, but
200 so far we do not seem to need them, and we can treat them as local only */
201 if ((oflags & (FMODE_READ | FMODE_WRITE)) ==
202 (FMODE_READ | FMODE_WRITE))
203 posix_flags = SMB_O_RDWR;
204 else if (oflags & FMODE_READ)
205 posix_flags = SMB_O_RDONLY;
206 else if (oflags & FMODE_WRITE)
207 posix_flags = SMB_O_WRONLY;
208 if (oflags & O_CREAT)
209 posix_flags |= SMB_O_CREAT;
210 if (oflags & O_EXCL)
211 posix_flags |= SMB_O_EXCL;
212 if (oflags & O_TRUNC)
213 posix_flags |= SMB_O_TRUNC;
214 if (oflags & O_APPEND)
215 posix_flags |= SMB_O_APPEND;
216 if (oflags & O_SYNC)
217 posix_flags |= SMB_O_SYNC;
218 if (oflags & O_DIRECTORY)
219 posix_flags |= SMB_O_DIRECTORY;
220 if (oflags & O_NOFOLLOW)
221 posix_flags |= SMB_O_NOFOLLOW;
222 if (oflags & O_DIRECT)
223 posix_flags |= SMB_O_DIRECT;
224
Steve Frencha6ce4932009-04-09 01:14:32 +0000225 if (!(oflags & FMODE_READ))
226 write_only = true;
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000227
228 rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode,
229 pnetfid, presp_data, &oplock, full_path,
230 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
231 CIFS_MOUNT_MAP_SPECIAL_CHR);
232 if (rc)
233 goto posix_open_ret;
234
235 if (presp_data->Type == cpu_to_le32(-1))
236 goto posix_open_ret; /* open ok, caller does qpathinfo */
237
238 /* get new inode and set it up */
239 if (!pinode)
240 goto posix_open_ret; /* caller does not need info */
241
Steve French85a6dac2009-04-01 05:22:00 +0000242 if (*pinode == NULL) {
243 __u64 unique_id = le64_to_cpu(presp_data->UniqueId);
244 *pinode = cifs_new_inode(sb, &unique_id);
245 }
Steve French7fc8f4e2009-02-23 20:43:11 +0000246 /* else an inode was passed in. Update its info, don't create one */
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000247
248 /* We do not need to close the file if new_inode fails since
249 the caller will retry qpathinfo as long as inode is null */
250 if (*pinode == NULL)
251 goto posix_open_ret;
252
253 posix_fill_in_inode(*pinode, presp_data, 1);
254
Steve Frencha6ce4932009-04-09 01:14:32 +0000255 cifs_fill_fileinfo(*pinode, *pnetfid, cifs_sb->tcon, write_only);
256
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000257posix_open_ret:
258 kfree(presp_data);
259 return rc;
260}
261
Steve Frenchf818dd52009-01-19 02:38:35 +0000262static void setup_cifs_dentry(struct cifsTconInfo *tcon,
263 struct dentry *direntry,
264 struct inode *newinode)
265{
266 if (tcon->nocase)
267 direntry->d_op = &cifs_ci_dentry_ops;
268 else
269 direntry->d_op = &cifs_dentry_ops;
270 d_instantiate(direntry, newinode);
271}
272
Steve French39798772006-05-31 22:40:51 +0000273/* Inode operations in similar order to how they appear in Linux file fs.h */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274
275int
276cifs_create(struct inode *inode, struct dentry *direntry, int mode,
277 struct nameidata *nd)
278{
279 int rc = -ENOENT;
280 int xid;
Jeff Layton67750fb2008-05-09 22:28:02 +0000281 int create_options = CREATE_NOT_DIR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 int oplock = 0;
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000283 int oflags;
284 /*
285 * BB below access is probably too much for mknod to request
286 * but we have to do query and setpathinfo so requesting
287 * less could fail (unless we want to request getatr and setatr
288 * permissions (only). At least for POSIX we do not have to
289 * request so much.
290 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 int desiredAccess = GENERIC_READ | GENERIC_WRITE;
292 __u16 fileHandle;
293 struct cifs_sb_info *cifs_sb;
Steve Frenchf818dd52009-01-19 02:38:35 +0000294 struct cifsTconInfo *tcon;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 char *full_path = NULL;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000296 FILE_ALL_INFO *buf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 struct inode *newinode = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 int disposition = FILE_OVERWRITE_IF;
Steve French4b18f2a2008-04-29 00:06:05 +0000299 bool write_only = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300
301 xid = GetXid();
302
303 cifs_sb = CIFS_SB(inode->i_sb);
Steve Frenchf818dd52009-01-19 02:38:35 +0000304 tcon = cifs_sb->tcon;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 full_path = build_path_from_dentry(direntry);
Steve French5fdae1f2007-06-05 18:30:44 +0000307 if (full_path == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 FreeXid(xid);
309 return -ENOMEM;
310 }
311
Al Viroce3b0f82009-03-29 19:08:22 -0400312 mode &= ~current_umask();
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000313 if (oplockEnabled)
314 oplock = REQ_OPLOCK;
315
316 if (nd && (nd->flags & LOOKUP_OPEN))
317 oflags = nd->intent.open.flags;
318 else
319 oflags = FMODE_READ;
320
321 if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
322 (CIFS_UNIX_POSIX_PATH_OPS_CAP &
323 le64_to_cpu(tcon->fsUnixInfo.Capability))) {
324 rc = cifs_posix_open(full_path, &newinode, inode->i_sb,
325 mode, oflags, &oplock, &fileHandle, xid);
326 /* EIO could indicate that (posix open) operation is not
327 supported, despite what server claimed in capability
328 negotation. EREMOTE indicates DFS junction, which is not
329 handled in posix open */
330
331 if ((rc == 0) && (newinode == NULL))
332 goto cifs_create_get_file_info; /* query inode info */
333 else if (rc == 0) /* success, no need to query */
334 goto cifs_create_set_dentry;
335 else if ((rc != -EIO) && (rc != -EREMOTE) &&
336 (rc != -EOPNOTSUPP)) /* path not found or net err */
337 goto cifs_create_out;
338 /* else fallthrough to retry, using older open call, this is
339 case where server does not support this SMB level, and
340 falsely claims capability (also get here for DFS case
341 which should be rare for path not covered on files) */
342 }
Steve Frenchf818dd52009-01-19 02:38:35 +0000343
Steve French5fdae1f2007-06-05 18:30:44 +0000344 if (nd && (nd->flags & LOOKUP_OPEN)) {
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000345 /* if the file is going to stay open, then we
346 need to set the desired access properly */
Miklos Szeredie08fc042005-09-06 15:18:26 -0700347 desiredAccess = 0;
348 if (oflags & FMODE_READ)
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000349 desiredAccess |= GENERIC_READ; /* is this too little? */
Miklos Szeredie08fc042005-09-06 15:18:26 -0700350 if (oflags & FMODE_WRITE) {
351 desiredAccess |= GENERIC_WRITE;
352 if (!(oflags & FMODE_READ))
Steve French4b18f2a2008-04-29 00:06:05 +0000353 write_only = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 }
355
Steve French5fdae1f2007-06-05 18:30:44 +0000356 if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 disposition = FILE_CREATE;
Steve French5fdae1f2007-06-05 18:30:44 +0000358 else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 disposition = FILE_OVERWRITE_IF;
Steve French5fdae1f2007-06-05 18:30:44 +0000360 else if ((oflags & O_CREAT) == O_CREAT)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 disposition = FILE_OPEN_IF;
Steve Frenchad7a2922008-02-07 23:25:02 +0000362 else
Steve French5fdae1f2007-06-05 18:30:44 +0000363 cFYI(1, ("Create flag not set in create function"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 }
365
Steve French5fdae1f2007-06-05 18:30:44 +0000366 /* BB add processing to set equivalent of mode - e.g. via CreateX with
367 ACLs */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368
Steve French5fdae1f2007-06-05 18:30:44 +0000369 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
370 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 kfree(full_path);
372 FreeXid(xid);
373 return -ENOMEM;
374 }
Jeff Layton67750fb2008-05-09 22:28:02 +0000375
Jeff Layton67750fb2008-05-09 22:28:02 +0000376 /*
377 * if we're not using unix extensions, see if we need to set
378 * ATTR_READONLY on the create call
379 */
Steve Frenchf818dd52009-01-19 02:38:35 +0000380 if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
Jeff Layton67750fb2008-05-09 22:28:02 +0000381 create_options |= CREATE_OPTION_READONLY;
382
Steve French5fdae1f2007-06-05 18:30:44 +0000383 if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS)
Steve Frenchf818dd52009-01-19 02:38:35 +0000384 rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
Jeff Layton67750fb2008-05-09 22:28:02 +0000385 desiredAccess, create_options,
Steve French737b7582005-04-28 22:41:06 -0700386 &fileHandle, &oplock, buf, cifs_sb->local_nls,
387 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
Steve French5bafd762006-06-07 00:18:43 +0000388 else
389 rc = -EIO; /* no NT SMB support fall into legacy open below */
390
Steve French5fdae1f2007-06-05 18:30:44 +0000391 if (rc == -EIO) {
Steve Frencha9d02ad2005-08-24 23:06:05 -0700392 /* old server, retry the open legacy style */
Steve Frenchf818dd52009-01-19 02:38:35 +0000393 rc = SMBLegacyOpen(xid, tcon, full_path, disposition,
Jeff Layton67750fb2008-05-09 22:28:02 +0000394 desiredAccess, create_options,
Steve Frencha9d02ad2005-08-24 23:06:05 -0700395 &fileHandle, &oplock, buf, cifs_sb->local_nls,
396 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
Steve French5fdae1f2007-06-05 18:30:44 +0000397 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 if (rc) {
Steve French26a21b92006-05-31 18:05:34 +0000399 cFYI(1, ("cifs_create returned 0x%x", rc));
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000400 goto cifs_create_out;
401 }
402
403 /* If Open reported that we actually created a file
404 then we now have to set the mode if possible */
405 if ((tcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) {
406 struct cifs_unix_set_info_args args = {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400407 .mode = mode,
408 .ctime = NO_CHANGE_64,
409 .atime = NO_CHANGE_64,
410 .mtime = NO_CHANGE_64,
411 .device = 0,
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000412 };
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400413
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000414 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
415 args.uid = (__u64) current_fsuid();
416 if (inode->i_mode & S_ISGID)
417 args.gid = (__u64) inode->i_gid;
418 else
419 args.gid = (__u64) current_fsgid();
Steve French3ce53fc2007-06-08 14:55:14 +0000420 } else {
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000421 args.uid = NO_CHANGE_64;
422 args.gid = NO_CHANGE_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 }
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000424 CIFSSMBUnixSetInfo(xid, tcon, full_path, &args,
425 cifs_sb->local_nls,
426 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
427 } else {
428 /* BB implement mode setting via Windows security
429 descriptors e.g. */
430 /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000432 /* Could set r/o dos attribute if mode & 0222 == 0 */
433 }
434
435cifs_create_get_file_info:
436 /* server might mask mode so we have to query for it */
437 if (tcon->unix_ext)
438 rc = cifs_get_inode_info_unix(&newinode, full_path,
439 inode->i_sb, xid);
440 else {
441 rc = cifs_get_inode_info(&newinode, full_path, buf,
442 inode->i_sb, xid, &fileHandle);
443 if (newinode) {
444 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
445 newinode->i_mode = mode;
446 if ((oplock & CIFS_CREATE_ACTION) &&
447 (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) {
448 newinode->i_uid = current_fsuid();
449 if (inode->i_mode & S_ISGID)
450 newinode->i_gid = inode->i_gid;
451 else
452 newinode->i_gid = current_fsgid();
Steve French6473a552005-11-29 20:20:10 -0800453 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 }
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000455 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000457cifs_create_set_dentry:
458 if (rc == 0)
459 setup_cifs_dentry(tcon, direntry, newinode);
460 else
461 cFYI(1, ("Create worked, get_inode_info failed rc = %d", rc));
Steve Frenchf818dd52009-01-19 02:38:35 +0000462
Steve Frenchc3b2a0c2009-02-20 04:32:45 +0000463 /* nfsd case - nfs srv does not set nd */
464 if ((nd == NULL) || (!(nd->flags & LOOKUP_OPEN))) {
465 /* mknod case - do not leave file open */
466 CIFSSMBClose(xid, tcon, fileHandle);
467 } else if (newinode) {
Steve Frencha6ce4932009-04-09 01:14:32 +0000468 cifs_fill_fileinfo(newinode, fileHandle,
469 cifs_sb->tcon, write_only);
Steve French5fdae1f2007-06-05 18:30:44 +0000470 }
Steve Frenchd14537f12005-04-28 22:41:05 -0700471cifs_create_out:
472 kfree(buf);
473 kfree(full_path);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 FreeXid(xid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 return rc;
476}
477
Steve French5fdae1f2007-06-05 18:30:44 +0000478int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
479 dev_t device_number)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480{
481 int rc = -EPERM;
482 int xid;
483 struct cifs_sb_info *cifs_sb;
484 struct cifsTconInfo *pTcon;
485 char *full_path = NULL;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000486 struct inode *newinode = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487
488 if (!old_valid_dev(device_number))
489 return -EINVAL;
490
491 xid = GetXid();
492
493 cifs_sb = CIFS_SB(inode->i_sb);
494 pTcon = cifs_sb->tcon;
495
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 full_path = build_path_from_dentry(direntry);
Steve French5fdae1f2007-06-05 18:30:44 +0000497 if (full_path == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 rc = -ENOMEM;
Steve Frenchc18c8422007-07-18 23:21:09 +0000499 else if (pTcon->unix_ext) {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400500 struct cifs_unix_set_info_args args = {
Al Viroce3b0f82009-03-29 19:08:22 -0400501 .mode = mode & ~current_umask(),
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400502 .ctime = NO_CHANGE_64,
503 .atime = NO_CHANGE_64,
504 .mtime = NO_CHANGE_64,
505 .device = device_number,
506 };
Steve French5fdae1f2007-06-05 18:30:44 +0000507 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
David Howellsa001e5b2008-11-14 10:38:47 +1100508 args.uid = (__u64) current_fsuid();
509 args.gid = (__u64) current_fsgid();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 } else {
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400511 args.uid = NO_CHANGE_64;
512 args.gid = NO_CHANGE_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 }
Jeff Layton4e1e7fb2008-08-02 07:26:12 -0400514 rc = CIFSSMBUnixSetInfo(xid, pTcon, full_path,
515 &args, cifs_sb->local_nls,
516 cifs_sb->mnt_cifs_flags &
517 CIFS_MOUNT_MAP_SPECIAL_CHR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
Steve French5fdae1f2007-06-05 18:30:44 +0000519 if (!rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 rc = cifs_get_inode_info_unix(&newinode, full_path,
Steve French5fdae1f2007-06-05 18:30:44 +0000521 inode->i_sb, xid);
Steve Frenchb92327f2005-08-22 20:09:43 -0700522 if (pTcon->nocase)
523 direntry->d_op = &cifs_ci_dentry_ops;
524 else
525 direntry->d_op = &cifs_dentry_ops;
Steve French5fdae1f2007-06-05 18:30:44 +0000526 if (rc == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 d_instantiate(direntry, newinode);
528 }
Steve Frenchd7245c22005-07-14 18:25:12 -0500529 } else {
Steve French5fdae1f2007-06-05 18:30:44 +0000530 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
Steve Frencheda3c022005-07-21 15:20:28 -0700531 int oplock = 0;
532 u16 fileHandle;
Steve Frenchad7a2922008-02-07 23:25:02 +0000533 FILE_ALL_INFO *buf;
Steve Frenchd7245c22005-07-14 18:25:12 -0500534
Steve French5fdae1f2007-06-05 18:30:44 +0000535 cFYI(1, ("sfu compat create special file"));
Steve Frenchd7245c22005-07-14 18:25:12 -0500536
Steve French5fdae1f2007-06-05 18:30:44 +0000537 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
538 if (buf == NULL) {
Steve Frencheda3c022005-07-21 15:20:28 -0700539 kfree(full_path);
540 FreeXid(xid);
541 return -ENOMEM;
542 }
543
544 rc = CIFSSMBOpen(xid, pTcon, full_path,
545 FILE_CREATE, /* fail if exists */
Steve French5fdae1f2007-06-05 18:30:44 +0000546 GENERIC_WRITE /* BB would
Steve Frencheda3c022005-07-21 15:20:28 -0700547 WRITE_OWNER | WRITE_DAC be better? */,
548 /* Create a file and set the
549 file attribute to SYSTEM */
550 CREATE_NOT_DIR | CREATE_OPTION_SPECIAL,
551 &fileHandle, &oplock, buf,
552 cifs_sb->local_nls,
Steve French5fdae1f2007-06-05 18:30:44 +0000553 cifs_sb->mnt_cifs_flags &
Steve Frencheda3c022005-07-21 15:20:28 -0700554 CIFS_MOUNT_MAP_SPECIAL_CHR);
555
Steve French5bafd762006-06-07 00:18:43 +0000556 /* BB FIXME - add handling for backlevel servers
557 which need legacy open and check for all
Steve French5fdae1f2007-06-05 18:30:44 +0000558 calls to SMBOpen for fallback to SMBLeagcyOpen */
559 if (!rc) {
Steve Frencheda3c022005-07-21 15:20:28 -0700560 /* BB Do not bother to decode buf since no
Steve French86c96b42005-11-18 20:25:31 -0800561 local inode yet to put timestamps in,
562 but we can reuse it safely */
Steve French77159b42007-08-31 01:10:17 +0000563 unsigned int bytes_written;
Steve French86c96b42005-11-18 20:25:31 -0800564 struct win_dev *pdev;
565 pdev = (struct win_dev *)buf;
Steve French5fdae1f2007-06-05 18:30:44 +0000566 if (S_ISCHR(mode)) {
Steve French86c96b42005-11-18 20:25:31 -0800567 memcpy(pdev->type, "IntxCHR", 8);
568 pdev->major =
569 cpu_to_le64(MAJOR(device_number));
Steve French5fdae1f2007-06-05 18:30:44 +0000570 pdev->minor =
Steve French86c96b42005-11-18 20:25:31 -0800571 cpu_to_le64(MINOR(device_number));
572 rc = CIFSSMBWrite(xid, pTcon,
573 fileHandle,
574 sizeof(struct win_dev),
575 0, &bytes_written, (char *)pdev,
576 NULL, 0);
Steve French5fdae1f2007-06-05 18:30:44 +0000577 } else if (S_ISBLK(mode)) {
Steve French86c96b42005-11-18 20:25:31 -0800578 memcpy(pdev->type, "IntxBLK", 8);
579 pdev->major =
580 cpu_to_le64(MAJOR(device_number));
581 pdev->minor =
582 cpu_to_le64(MINOR(device_number));
583 rc = CIFSSMBWrite(xid, pTcon,
584 fileHandle,
585 sizeof(struct win_dev),
586 0, &bytes_written, (char *)pdev,
587 NULL, 0);
588 } /* else if(S_ISFIFO */
Steve Frencheda3c022005-07-21 15:20:28 -0700589 CIFSSMBClose(xid, pTcon, fileHandle);
590 d_drop(direntry);
591 }
592 kfree(buf);
Steve Frenchd7245c22005-07-14 18:25:12 -0500593 /* add code here to set EAs */
594 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 }
596
Steve Frenchd14537f12005-04-28 22:41:05 -0700597 kfree(full_path);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 FreeXid(xid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 return rc;
600}
601
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602struct dentry *
Steve French5fdae1f2007-06-05 18:30:44 +0000603cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
604 struct nameidata *nd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605{
606 int xid;
607 int rc = 0; /* to get around spurious gcc warning, set to zero here */
Steve Frencha6ce4932009-04-09 01:14:32 +0000608 int oplock = 0;
609 int mode;
610 __u16 fileHandle = 0;
611 bool posix_open = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 struct cifs_sb_info *cifs_sb;
613 struct cifsTconInfo *pTcon;
614 struct inode *newInode = NULL;
615 char *full_path = NULL;
Steve Frencha6ce4932009-04-09 01:14:32 +0000616 struct file *filp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617
618 xid = GetXid();
619
Steve French61e74802008-12-03 00:57:54 +0000620 cFYI(1, ("parent inode = 0x%p name is: %s and dentry = 0x%p",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 parent_dir_inode, direntry->d_name.name, direntry));
622
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 /* check whether path exists */
624
625 cifs_sb = CIFS_SB(parent_dir_inode->i_sb);
626 pTcon = cifs_sb->tcon;
627
Steve French296034f2006-04-21 18:18:37 +0000628 /*
629 * Don't allow the separator character in a path component.
630 * The VFS will not allow "/", but "\" is allowed by posix.
631 */
632 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
633 int i;
634 for (i = 0; i < direntry->d_name.len; i++)
635 if (direntry->d_name.name[i] == '\\') {
636 cFYI(1, ("Invalid file name"));
637 FreeXid(xid);
638 return ERR_PTR(-EINVAL);
639 }
640 }
641
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 /* can not grab the rename sem here since it would
643 deadlock in the cases (beginning of sys_rename itself)
644 in which we already have the sb rename sem */
645 full_path = build_path_from_dentry(direntry);
Steve French5fdae1f2007-06-05 18:30:44 +0000646 if (full_path == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 FreeXid(xid);
648 return ERR_PTR(-ENOMEM);
649 }
650
651 if (direntry->d_inode != NULL) {
Steve French61e74802008-12-03 00:57:54 +0000652 cFYI(1, ("non-NULL inode in lookup"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 } else {
Steve French61e74802008-12-03 00:57:54 +0000654 cFYI(1, ("NULL inode in lookup"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 }
Steve French61e74802008-12-03 00:57:54 +0000656 cFYI(1, ("Full path: %s inode = 0x%p", full_path, direntry->d_inode));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
Steve Frencha6ce4932009-04-09 01:14:32 +0000658 if (pTcon->unix_ext) {
659 if (!(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) &&
660 (nd->flags & LOOKUP_OPEN)) {
661 if (!((nd->intent.open.flags & O_CREAT) &&
662 (nd->intent.open.flags & O_EXCL))) {
663 mode = nd->intent.open.create_mode &
664 ~current->fs->umask;
665 rc = cifs_posix_open(full_path, &newInode,
666 parent_dir_inode->i_sb, mode,
667 nd->intent.open.flags, &oplock,
668 &fileHandle, xid);
Steve Frenchbc8cd432009-04-12 18:18:40 +0000669 /*
670 * This code works around a bug in
671 * samba posix open in samba versions 3.3.1
672 * and earlier where create works
673 * but open fails with invalid parameter.
674 * If either of these error codes are
675 * returned, follow the normal lookup.
676 * Otherwise, the error during posix open
677 * is handled.
678 */
Steve Frencha6ce4932009-04-09 01:14:32 +0000679 if ((rc != -EINVAL) && (rc != -EOPNOTSUPP))
680 posix_open = true;
681 }
682 }
683 if (!posix_open)
684 rc = cifs_get_inode_info_unix(&newInode, full_path,
685 parent_dir_inode->i_sb, xid);
686 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 rc = cifs_get_inode_info(&newInode, full_path, NULL,
Steve Frencha6ce4932009-04-09 01:14:32 +0000688 parent_dir_inode->i_sb, xid, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689
690 if ((rc == 0) && (newInode != NULL)) {
Steve Frenchb92327f2005-08-22 20:09:43 -0700691 if (pTcon->nocase)
692 direntry->d_op = &cifs_ci_dentry_ops;
693 else
694 direntry->d_op = &cifs_dentry_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 d_add(direntry, newInode);
Steve Frencha6ce4932009-04-09 01:14:32 +0000696 if (posix_open)
697 filp = lookup_instantiate_filp(nd, direntry, NULL);
Steve French5fdae1f2007-06-05 18:30:44 +0000698 /* since paths are not looked up by component - the parent
Steve French3abb9272005-11-28 08:16:13 -0800699 directories are presumed to be good here */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 renew_parental_timestamps(direntry);
701
702 } else if (rc == -ENOENT) {
703 rc = 0;
Steve French3abb9272005-11-28 08:16:13 -0800704 direntry->d_time = jiffies;
705 if (pTcon->nocase)
706 direntry->d_op = &cifs_ci_dentry_ops;
707 else
708 direntry->d_op = &cifs_dentry_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 d_add(direntry, NULL);
Steve French5fdae1f2007-06-05 18:30:44 +0000710 /* if it was once a directory (but how can we tell?) we could do
711 shrink_dcache_parent(direntry); */
Steve Frenched2b9172008-01-20 00:30:29 +0000712 } else if (rc != -EACCES) {
713 cERROR(1, ("Unexpected lookup error %d", rc));
714 /* We special case check for Access Denied - since that
715 is a common return code */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 }
717
Steve Frenchd14537f12005-04-28 22:41:05 -0700718 kfree(full_path);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 FreeXid(xid);
720 return ERR_PTR(rc);
721}
722
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723static int
724cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
725{
726 int isValid = 1;
727
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 if (direntry->d_inode) {
Steve Frenchad7a2922008-02-07 23:25:02 +0000729 if (cifs_revalidate(direntry))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 } else {
Steve French3abb9272005-11-28 08:16:13 -0800732 cFYI(1, ("neg dentry 0x%p name = %s",
733 direntry, direntry->d_name.name));
Steve French5fdae1f2007-06-05 18:30:44 +0000734 if (time_after(jiffies, direntry->d_time + HZ) ||
Steve French3abb9272005-11-28 08:16:13 -0800735 !lookupCacheEnabled) {
736 d_drop(direntry);
737 isValid = 0;
Steve French5fdae1f2007-06-05 18:30:44 +0000738 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 }
740
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 return isValid;
742}
743
744/* static int cifs_d_delete(struct dentry *direntry)
745{
746 int rc = 0;
747
748 cFYI(1, ("In cifs d_delete, name = %s", direntry->d_name.name));
749
750 return rc;
751} */
752
Al Viro4fd03e82009-02-20 05:57:07 +0000753const struct dentry_operations cifs_dentry_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 .d_revalidate = cifs_d_revalidate,
Steve French5fdae1f2007-06-05 18:30:44 +0000755/* d_delete: cifs_d_delete, */ /* not needed except for debugging */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756};
Steve Frenchb92327f2005-08-22 20:09:43 -0700757
758static int cifs_ci_hash(struct dentry *dentry, struct qstr *q)
759{
760 struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
761 unsigned long hash;
762 int i;
763
764 hash = init_name_hash();
765 for (i = 0; i < q->len; i++)
766 hash = partial_name_hash(nls_tolower(codepage, q->name[i]),
767 hash);
768 q->hash = end_name_hash(hash);
769
770 return 0;
771}
772
773static int cifs_ci_compare(struct dentry *dentry, struct qstr *a,
774 struct qstr *b)
775{
776 struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
777
778 if ((a->len == b->len) &&
779 (nls_strnicmp(codepage, a->name, b->name, a->len) == 0)) {
780 /*
781 * To preserve case, don't let an existing negative dentry's
782 * case take precedence. If a is not a negative dentry, this
783 * should have no side effects
784 */
Steve Frenchc3291632008-05-15 05:41:54 +0000785 memcpy((void *)a->name, b->name, a->len);
Steve Frenchb92327f2005-08-22 20:09:43 -0700786 return 0;
787 }
788 return 1;
789}
790
Al Viro4fd03e82009-02-20 05:57:07 +0000791const struct dentry_operations cifs_ci_dentry_ops = {
Steve Frenchb92327f2005-08-22 20:09:43 -0700792 .d_revalidate = cifs_d_revalidate,
793 .d_hash = cifs_ci_hash,
794 .d_compare = cifs_ci_compare,
795};