1 |
62 |
marcus.erl |
/*
|
2 |
|
|
* fs/cifs/dir.c
|
3 |
|
|
*
|
4 |
|
|
* vfs operations that deal with dentries
|
5 |
|
|
*
|
6 |
|
|
* Copyright (C) International Business Machines Corp., 2002,2007
|
7 |
|
|
* 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 |
|
|
|
34 |
|
|
static void
|
35 |
|
|
renew_parental_timestamps(struct dentry *direntry)
|
36 |
|
|
{
|
37 |
|
|
/* BB check if there is a way to get the kernel to do this or if we
|
38 |
|
|
really need this */
|
39 |
|
|
do {
|
40 |
|
|
direntry->d_time = jiffies;
|
41 |
|
|
direntry = direntry->d_parent;
|
42 |
|
|
} while (!IS_ROOT(direntry));
|
43 |
|
|
}
|
44 |
|
|
|
45 |
|
|
/* Note: caller must free return buffer */
|
46 |
|
|
char *
|
47 |
|
|
build_path_from_dentry(struct dentry *direntry)
|
48 |
|
|
{
|
49 |
|
|
struct dentry *temp;
|
50 |
|
|
int namelen;
|
51 |
|
|
int pplen;
|
52 |
|
|
char *full_path;
|
53 |
|
|
char dirsep;
|
54 |
|
|
|
55 |
|
|
if (direntry == NULL)
|
56 |
|
|
return NULL; /* not much we can do if dentry is freed and
|
57 |
|
|
we need to reopen the file after it was closed implicitly
|
58 |
|
|
when the server crashed */
|
59 |
|
|
|
60 |
|
|
dirsep = CIFS_DIR_SEP(CIFS_SB(direntry->d_sb));
|
61 |
|
|
pplen = CIFS_SB(direntry->d_sb)->prepathlen;
|
62 |
|
|
cifs_bp_rename_retry:
|
63 |
|
|
namelen = pplen;
|
64 |
|
|
for (temp = direntry; !IS_ROOT(temp);) {
|
65 |
|
|
namelen += (1 + temp->d_name.len);
|
66 |
|
|
temp = temp->d_parent;
|
67 |
|
|
if (temp == NULL) {
|
68 |
|
|
cERROR(1, ("corrupt dentry"));
|
69 |
|
|
return NULL;
|
70 |
|
|
}
|
71 |
|
|
}
|
72 |
|
|
|
73 |
|
|
full_path = kmalloc(namelen+1, GFP_KERNEL);
|
74 |
|
|
if (full_path == NULL)
|
75 |
|
|
return full_path;
|
76 |
|
|
full_path[namelen] = 0; /* trailing null */
|
77 |
|
|
for (temp = direntry; !IS_ROOT(temp);) {
|
78 |
|
|
namelen -= 1 + temp->d_name.len;
|
79 |
|
|
if (namelen < 0) {
|
80 |
|
|
break;
|
81 |
|
|
} else {
|
82 |
|
|
full_path[namelen] = dirsep;
|
83 |
|
|
strncpy(full_path + namelen + 1, temp->d_name.name,
|
84 |
|
|
temp->d_name.len);
|
85 |
|
|
cFYI(0, ("name: %s", full_path + namelen));
|
86 |
|
|
}
|
87 |
|
|
temp = temp->d_parent;
|
88 |
|
|
if (temp == NULL) {
|
89 |
|
|
cERROR(1, ("corrupt dentry"));
|
90 |
|
|
kfree(full_path);
|
91 |
|
|
return NULL;
|
92 |
|
|
}
|
93 |
|
|
}
|
94 |
|
|
if (namelen != pplen) {
|
95 |
|
|
cERROR(1,
|
96 |
|
|
("did not end path lookup where expected namelen is %d",
|
97 |
|
|
namelen));
|
98 |
|
|
/* presumably this is only possible if racing with a rename
|
99 |
|
|
of one of the parent directories (we can not lock the dentries
|
100 |
|
|
above us to prevent this, but retrying should be harmless) */
|
101 |
|
|
kfree(full_path);
|
102 |
|
|
goto cifs_bp_rename_retry;
|
103 |
|
|
}
|
104 |
|
|
/* DIR_SEP already set for byte 0 / vs \ but not for
|
105 |
|
|
subsequent slashes in prepath which currently must
|
106 |
|
|
be entered the right way - not sure if there is an alternative
|
107 |
|
|
since the '\' is a valid posix character so we can not switch
|
108 |
|
|
those safely to '/' if any are found in the middle of the prepath */
|
109 |
|
|
/* BB test paths to Windows with '/' in the midst of prepath */
|
110 |
|
|
strncpy(full_path, CIFS_SB(direntry->d_sb)->prepath, pplen);
|
111 |
|
|
return full_path;
|
112 |
|
|
}
|
113 |
|
|
|
114 |
|
|
/* char * build_wildcard_path_from_dentry(struct dentry *direntry)
|
115 |
|
|
{
|
116 |
|
|
if(full_path == NULL)
|
117 |
|
|
return full_path;
|
118 |
|
|
|
119 |
|
|
full_path[namelen] = '\\';
|
120 |
|
|
full_path[namelen+1] = '*';
|
121 |
|
|
full_path[namelen+2] = 0;
|
122 |
|
|
BB remove above eight lines BB */
|
123 |
|
|
|
124 |
|
|
/* Inode operations in similar order to how they appear in Linux file fs.h */
|
125 |
|
|
|
126 |
|
|
int
|
127 |
|
|
cifs_create(struct inode *inode, struct dentry *direntry, int mode,
|
128 |
|
|
struct nameidata *nd)
|
129 |
|
|
{
|
130 |
|
|
int rc = -ENOENT;
|
131 |
|
|
int xid;
|
132 |
|
|
int oplock = 0;
|
133 |
|
|
int desiredAccess = GENERIC_READ | GENERIC_WRITE;
|
134 |
|
|
__u16 fileHandle;
|
135 |
|
|
struct cifs_sb_info *cifs_sb;
|
136 |
|
|
struct cifsTconInfo *pTcon;
|
137 |
|
|
char *full_path = NULL;
|
138 |
|
|
FILE_ALL_INFO *buf = NULL;
|
139 |
|
|
struct inode *newinode = NULL;
|
140 |
|
|
struct cifsFileInfo *pCifsFile = NULL;
|
141 |
|
|
struct cifsInodeInfo *pCifsInode;
|
142 |
|
|
int disposition = FILE_OVERWRITE_IF;
|
143 |
|
|
int write_only = FALSE;
|
144 |
|
|
|
145 |
|
|
xid = GetXid();
|
146 |
|
|
|
147 |
|
|
cifs_sb = CIFS_SB(inode->i_sb);
|
148 |
|
|
pTcon = cifs_sb->tcon;
|
149 |
|
|
|
150 |
|
|
full_path = build_path_from_dentry(direntry);
|
151 |
|
|
if (full_path == NULL) {
|
152 |
|
|
FreeXid(xid);
|
153 |
|
|
return -ENOMEM;
|
154 |
|
|
}
|
155 |
|
|
|
156 |
|
|
if (nd && (nd->flags & LOOKUP_OPEN)) {
|
157 |
|
|
int oflags = nd->intent.open.flags;
|
158 |
|
|
|
159 |
|
|
desiredAccess = 0;
|
160 |
|
|
if (oflags & FMODE_READ)
|
161 |
|
|
desiredAccess |= GENERIC_READ;
|
162 |
|
|
if (oflags & FMODE_WRITE) {
|
163 |
|
|
desiredAccess |= GENERIC_WRITE;
|
164 |
|
|
if (!(oflags & FMODE_READ))
|
165 |
|
|
write_only = TRUE;
|
166 |
|
|
}
|
167 |
|
|
|
168 |
|
|
if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
|
169 |
|
|
disposition = FILE_CREATE;
|
170 |
|
|
else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
|
171 |
|
|
disposition = FILE_OVERWRITE_IF;
|
172 |
|
|
else if ((oflags & O_CREAT) == O_CREAT)
|
173 |
|
|
disposition = FILE_OPEN_IF;
|
174 |
|
|
else {
|
175 |
|
|
cFYI(1, ("Create flag not set in create function"));
|
176 |
|
|
}
|
177 |
|
|
}
|
178 |
|
|
|
179 |
|
|
/* BB add processing to set equivalent of mode - e.g. via CreateX with
|
180 |
|
|
ACLs */
|
181 |
|
|
if (oplockEnabled)
|
182 |
|
|
oplock = REQ_OPLOCK;
|
183 |
|
|
|
184 |
|
|
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
185 |
|
|
if (buf == NULL) {
|
186 |
|
|
kfree(full_path);
|
187 |
|
|
FreeXid(xid);
|
188 |
|
|
return -ENOMEM;
|
189 |
|
|
}
|
190 |
|
|
if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS)
|
191 |
|
|
rc = CIFSSMBOpen(xid, pTcon, full_path, disposition,
|
192 |
|
|
desiredAccess, CREATE_NOT_DIR,
|
193 |
|
|
&fileHandle, &oplock, buf, cifs_sb->local_nls,
|
194 |
|
|
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
195 |
|
|
else
|
196 |
|
|
rc = -EIO; /* no NT SMB support fall into legacy open below */
|
197 |
|
|
|
198 |
|
|
if (rc == -EIO) {
|
199 |
|
|
/* old server, retry the open legacy style */
|
200 |
|
|
rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
|
201 |
|
|
desiredAccess, CREATE_NOT_DIR,
|
202 |
|
|
&fileHandle, &oplock, buf, cifs_sb->local_nls,
|
203 |
|
|
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
204 |
|
|
}
|
205 |
|
|
if (rc) {
|
206 |
|
|
cFYI(1, ("cifs_create returned 0x%x", rc));
|
207 |
|
|
} else {
|
208 |
|
|
/* If Open reported that we actually created a file
|
209 |
|
|
then we now have to set the mode if possible */
|
210 |
|
|
if ((pTcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) {
|
211 |
|
|
mode &= ~current->fs->umask;
|
212 |
|
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
|
213 |
|
|
CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode,
|
214 |
|
|
(__u64)current->fsuid,
|
215 |
|
|
(__u64)current->fsgid,
|
216 |
|
|
|
217 |
|
|
cifs_sb->local_nls,
|
218 |
|
|
cifs_sb->mnt_cifs_flags &
|
219 |
|
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
220 |
|
|
} else {
|
221 |
|
|
CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode,
|
222 |
|
|
(__u64)-1,
|
223 |
|
|
(__u64)-1,
|
224 |
|
|
|
225 |
|
|
cifs_sb->local_nls,
|
226 |
|
|
cifs_sb->mnt_cifs_flags &
|
227 |
|
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
228 |
|
|
}
|
229 |
|
|
} else {
|
230 |
|
|
/* BB implement mode setting via Windows security
|
231 |
|
|
descriptors e.g. */
|
232 |
|
|
/* CIFSSMBWinSetPerms(xid,pTcon,path,mode,-1,-1,nls);*/
|
233 |
|
|
|
234 |
|
|
/* Could set r/o dos attribute if mode & 0222 == 0 */
|
235 |
|
|
}
|
236 |
|
|
|
237 |
|
|
/* server might mask mode so we have to query for it */
|
238 |
|
|
if (pTcon->unix_ext)
|
239 |
|
|
rc = cifs_get_inode_info_unix(&newinode, full_path,
|
240 |
|
|
inode->i_sb, xid);
|
241 |
|
|
else {
|
242 |
|
|
rc = cifs_get_inode_info(&newinode, full_path,
|
243 |
|
|
buf, inode->i_sb, xid);
|
244 |
|
|
if (newinode) {
|
245 |
|
|
newinode->i_mode = mode;
|
246 |
|
|
if ((oplock & CIFS_CREATE_ACTION) &&
|
247 |
|
|
(cifs_sb->mnt_cifs_flags &
|
248 |
|
|
CIFS_MOUNT_SET_UID)) {
|
249 |
|
|
newinode->i_uid = current->fsuid;
|
250 |
|
|
newinode->i_gid = current->fsgid;
|
251 |
|
|
}
|
252 |
|
|
}
|
253 |
|
|
}
|
254 |
|
|
|
255 |
|
|
if (rc != 0) {
|
256 |
|
|
cFYI(1,
|
257 |
|
|
("Create worked but get_inode_info failed rc = %d",
|
258 |
|
|
rc));
|
259 |
|
|
} else {
|
260 |
|
|
if (pTcon->nocase)
|
261 |
|
|
direntry->d_op = &cifs_ci_dentry_ops;
|
262 |
|
|
else
|
263 |
|
|
direntry->d_op = &cifs_dentry_ops;
|
264 |
|
|
d_instantiate(direntry, newinode);
|
265 |
|
|
}
|
266 |
|
|
if ((nd == NULL /* nfsd case - nfs srv does not set nd */) ||
|
267 |
|
|
((nd->flags & LOOKUP_OPEN) == FALSE)) {
|
268 |
|
|
/* mknod case - do not leave file open */
|
269 |
|
|
CIFSSMBClose(xid, pTcon, fileHandle);
|
270 |
|
|
} else if (newinode) {
|
271 |
|
|
pCifsFile =
|
272 |
|
|
kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
|
273 |
|
|
|
274 |
|
|
if (pCifsFile == NULL)
|
275 |
|
|
goto cifs_create_out;
|
276 |
|
|
pCifsFile->netfid = fileHandle;
|
277 |
|
|
pCifsFile->pid = current->tgid;
|
278 |
|
|
pCifsFile->pInode = newinode;
|
279 |
|
|
pCifsFile->invalidHandle = FALSE;
|
280 |
|
|
pCifsFile->closePend = FALSE;
|
281 |
|
|
init_MUTEX(&pCifsFile->fh_sem);
|
282 |
|
|
mutex_init(&pCifsFile->lock_mutex);
|
283 |
|
|
INIT_LIST_HEAD(&pCifsFile->llist);
|
284 |
|
|
atomic_set(&pCifsFile->wrtPending, 0);
|
285 |
|
|
|
286 |
|
|
/* set the following in open now
|
287 |
|
|
pCifsFile->pfile = file; */
|
288 |
|
|
write_lock(&GlobalSMBSeslock);
|
289 |
|
|
list_add(&pCifsFile->tlist, &pTcon->openFileList);
|
290 |
|
|
pCifsInode = CIFS_I(newinode);
|
291 |
|
|
if (pCifsInode) {
|
292 |
|
|
/* if readable file instance put first in list*/
|
293 |
|
|
if (write_only == TRUE) {
|
294 |
|
|
list_add_tail(&pCifsFile->flist,
|
295 |
|
|
&pCifsInode->openFileList);
|
296 |
|
|
} else {
|
297 |
|
|
list_add(&pCifsFile->flist,
|
298 |
|
|
&pCifsInode->openFileList);
|
299 |
|
|
}
|
300 |
|
|
if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
|
301 |
|
|
pCifsInode->clientCanCacheAll = TRUE;
|
302 |
|
|
pCifsInode->clientCanCacheRead = TRUE;
|
303 |
|
|
cFYI(1, ("Exclusive Oplock inode %p",
|
304 |
|
|
newinode));
|
305 |
|
|
} else if ((oplock & 0xF) == OPLOCK_READ)
|
306 |
|
|
pCifsInode->clientCanCacheRead = TRUE;
|
307 |
|
|
}
|
308 |
|
|
write_unlock(&GlobalSMBSeslock);
|
309 |
|
|
}
|
310 |
|
|
}
|
311 |
|
|
cifs_create_out:
|
312 |
|
|
kfree(buf);
|
313 |
|
|
kfree(full_path);
|
314 |
|
|
FreeXid(xid);
|
315 |
|
|
return rc;
|
316 |
|
|
}
|
317 |
|
|
|
318 |
|
|
int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
|
319 |
|
|
dev_t device_number)
|
320 |
|
|
{
|
321 |
|
|
int rc = -EPERM;
|
322 |
|
|
int xid;
|
323 |
|
|
struct cifs_sb_info *cifs_sb;
|
324 |
|
|
struct cifsTconInfo *pTcon;
|
325 |
|
|
char *full_path = NULL;
|
326 |
|
|
struct inode *newinode = NULL;
|
327 |
|
|
|
328 |
|
|
if (!old_valid_dev(device_number))
|
329 |
|
|
return -EINVAL;
|
330 |
|
|
|
331 |
|
|
xid = GetXid();
|
332 |
|
|
|
333 |
|
|
cifs_sb = CIFS_SB(inode->i_sb);
|
334 |
|
|
pTcon = cifs_sb->tcon;
|
335 |
|
|
|
336 |
|
|
full_path = build_path_from_dentry(direntry);
|
337 |
|
|
if (full_path == NULL)
|
338 |
|
|
rc = -ENOMEM;
|
339 |
|
|
else if (pTcon->unix_ext) {
|
340 |
|
|
mode &= ~current->fs->umask;
|
341 |
|
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
|
342 |
|
|
rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path,
|
343 |
|
|
mode, (__u64)current->fsuid,
|
344 |
|
|
(__u64)current->fsgid,
|
345 |
|
|
device_number, cifs_sb->local_nls,
|
346 |
|
|
cifs_sb->mnt_cifs_flags &
|
347 |
|
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
348 |
|
|
} else {
|
349 |
|
|
rc = CIFSSMBUnixSetPerms(xid, pTcon,
|
350 |
|
|
full_path, mode, (__u64)-1, (__u64)-1,
|
351 |
|
|
device_number, cifs_sb->local_nls,
|
352 |
|
|
cifs_sb->mnt_cifs_flags &
|
353 |
|
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
354 |
|
|
}
|
355 |
|
|
|
356 |
|
|
if (!rc) {
|
357 |
|
|
rc = cifs_get_inode_info_unix(&newinode, full_path,
|
358 |
|
|
inode->i_sb, xid);
|
359 |
|
|
if (pTcon->nocase)
|
360 |
|
|
direntry->d_op = &cifs_ci_dentry_ops;
|
361 |
|
|
else
|
362 |
|
|
direntry->d_op = &cifs_dentry_ops;
|
363 |
|
|
if (rc == 0)
|
364 |
|
|
d_instantiate(direntry, newinode);
|
365 |
|
|
}
|
366 |
|
|
} else {
|
367 |
|
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
|
368 |
|
|
int oplock = 0;
|
369 |
|
|
u16 fileHandle;
|
370 |
|
|
FILE_ALL_INFO * buf;
|
371 |
|
|
|
372 |
|
|
cFYI(1, ("sfu compat create special file"));
|
373 |
|
|
|
374 |
|
|
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
375 |
|
|
if (buf == NULL) {
|
376 |
|
|
kfree(full_path);
|
377 |
|
|
FreeXid(xid);
|
378 |
|
|
return -ENOMEM;
|
379 |
|
|
}
|
380 |
|
|
|
381 |
|
|
rc = CIFSSMBOpen(xid, pTcon, full_path,
|
382 |
|
|
FILE_CREATE, /* fail if exists */
|
383 |
|
|
GENERIC_WRITE /* BB would
|
384 |
|
|
WRITE_OWNER | WRITE_DAC be better? */,
|
385 |
|
|
/* Create a file and set the
|
386 |
|
|
file attribute to SYSTEM */
|
387 |
|
|
CREATE_NOT_DIR | CREATE_OPTION_SPECIAL,
|
388 |
|
|
&fileHandle, &oplock, buf,
|
389 |
|
|
cifs_sb->local_nls,
|
390 |
|
|
cifs_sb->mnt_cifs_flags &
|
391 |
|
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
392 |
|
|
|
393 |
|
|
/* BB FIXME - add handling for backlevel servers
|
394 |
|
|
which need legacy open and check for all
|
395 |
|
|
calls to SMBOpen for fallback to SMBLeagcyOpen */
|
396 |
|
|
if (!rc) {
|
397 |
|
|
/* BB Do not bother to decode buf since no
|
398 |
|
|
local inode yet to put timestamps in,
|
399 |
|
|
but we can reuse it safely */
|
400 |
|
|
unsigned int bytes_written;
|
401 |
|
|
struct win_dev *pdev;
|
402 |
|
|
pdev = (struct win_dev *)buf;
|
403 |
|
|
if (S_ISCHR(mode)) {
|
404 |
|
|
memcpy(pdev->type, "IntxCHR", 8);
|
405 |
|
|
pdev->major =
|
406 |
|
|
cpu_to_le64(MAJOR(device_number));
|
407 |
|
|
pdev->minor =
|
408 |
|
|
cpu_to_le64(MINOR(device_number));
|
409 |
|
|
rc = CIFSSMBWrite(xid, pTcon,
|
410 |
|
|
fileHandle,
|
411 |
|
|
sizeof(struct win_dev),
|
412 |
|
|
0, &bytes_written, (char *)pdev,
|
413 |
|
|
NULL, 0);
|
414 |
|
|
} else if (S_ISBLK(mode)) {
|
415 |
|
|
memcpy(pdev->type, "IntxBLK", 8);
|
416 |
|
|
pdev->major =
|
417 |
|
|
cpu_to_le64(MAJOR(device_number));
|
418 |
|
|
pdev->minor =
|
419 |
|
|
cpu_to_le64(MINOR(device_number));
|
420 |
|
|
rc = CIFSSMBWrite(xid, pTcon,
|
421 |
|
|
fileHandle,
|
422 |
|
|
sizeof(struct win_dev),
|
423 |
|
|
0, &bytes_written, (char *)pdev,
|
424 |
|
|
NULL, 0);
|
425 |
|
|
} /* else if(S_ISFIFO */
|
426 |
|
|
CIFSSMBClose(xid, pTcon, fileHandle);
|
427 |
|
|
d_drop(direntry);
|
428 |
|
|
}
|
429 |
|
|
kfree(buf);
|
430 |
|
|
/* add code here to set EAs */
|
431 |
|
|
}
|
432 |
|
|
}
|
433 |
|
|
|
434 |
|
|
kfree(full_path);
|
435 |
|
|
FreeXid(xid);
|
436 |
|
|
return rc;
|
437 |
|
|
}
|
438 |
|
|
|
439 |
|
|
|
440 |
|
|
struct dentry *
|
441 |
|
|
cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
|
442 |
|
|
struct nameidata *nd)
|
443 |
|
|
{
|
444 |
|
|
int xid;
|
445 |
|
|
int rc = 0; /* to get around spurious gcc warning, set to zero here */
|
446 |
|
|
struct cifs_sb_info *cifs_sb;
|
447 |
|
|
struct cifsTconInfo *pTcon;
|
448 |
|
|
struct inode *newInode = NULL;
|
449 |
|
|
char *full_path = NULL;
|
450 |
|
|
|
451 |
|
|
xid = GetXid();
|
452 |
|
|
|
453 |
|
|
cFYI(1, (" parent inode = 0x%p name is: %s and dentry = 0x%p",
|
454 |
|
|
parent_dir_inode, direntry->d_name.name, direntry));
|
455 |
|
|
|
456 |
|
|
/* check whether path exists */
|
457 |
|
|
|
458 |
|
|
cifs_sb = CIFS_SB(parent_dir_inode->i_sb);
|
459 |
|
|
pTcon = cifs_sb->tcon;
|
460 |
|
|
|
461 |
|
|
/*
|
462 |
|
|
* Don't allow the separator character in a path component.
|
463 |
|
|
* The VFS will not allow "/", but "\" is allowed by posix.
|
464 |
|
|
*/
|
465 |
|
|
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
|
466 |
|
|
int i;
|
467 |
|
|
for (i = 0; i < direntry->d_name.len; i++)
|
468 |
|
|
if (direntry->d_name.name[i] == '\\') {
|
469 |
|
|
cFYI(1, ("Invalid file name"));
|
470 |
|
|
FreeXid(xid);
|
471 |
|
|
return ERR_PTR(-EINVAL);
|
472 |
|
|
}
|
473 |
|
|
}
|
474 |
|
|
|
475 |
|
|
/* can not grab the rename sem here since it would
|
476 |
|
|
deadlock in the cases (beginning of sys_rename itself)
|
477 |
|
|
in which we already have the sb rename sem */
|
478 |
|
|
full_path = build_path_from_dentry(direntry);
|
479 |
|
|
if (full_path == NULL) {
|
480 |
|
|
FreeXid(xid);
|
481 |
|
|
return ERR_PTR(-ENOMEM);
|
482 |
|
|
}
|
483 |
|
|
|
484 |
|
|
if (direntry->d_inode != NULL) {
|
485 |
|
|
cFYI(1, (" non-NULL inode in lookup"));
|
486 |
|
|
} else {
|
487 |
|
|
cFYI(1, (" NULL inode in lookup"));
|
488 |
|
|
}
|
489 |
|
|
cFYI(1,
|
490 |
|
|
(" Full path: %s inode = 0x%p", full_path, direntry->d_inode));
|
491 |
|
|
|
492 |
|
|
if (pTcon->unix_ext)
|
493 |
|
|
rc = cifs_get_inode_info_unix(&newInode, full_path,
|
494 |
|
|
parent_dir_inode->i_sb, xid);
|
495 |
|
|
else
|
496 |
|
|
rc = cifs_get_inode_info(&newInode, full_path, NULL,
|
497 |
|
|
parent_dir_inode->i_sb, xid);
|
498 |
|
|
|
499 |
|
|
if ((rc == 0) && (newInode != NULL)) {
|
500 |
|
|
if (pTcon->nocase)
|
501 |
|
|
direntry->d_op = &cifs_ci_dentry_ops;
|
502 |
|
|
else
|
503 |
|
|
direntry->d_op = &cifs_dentry_ops;
|
504 |
|
|
d_add(direntry, newInode);
|
505 |
|
|
|
506 |
|
|
/* since paths are not looked up by component - the parent
|
507 |
|
|
directories are presumed to be good here */
|
508 |
|
|
renew_parental_timestamps(direntry);
|
509 |
|
|
|
510 |
|
|
} else if (rc == -ENOENT) {
|
511 |
|
|
rc = 0;
|
512 |
|
|
direntry->d_time = jiffies;
|
513 |
|
|
if (pTcon->nocase)
|
514 |
|
|
direntry->d_op = &cifs_ci_dentry_ops;
|
515 |
|
|
else
|
516 |
|
|
direntry->d_op = &cifs_dentry_ops;
|
517 |
|
|
d_add(direntry, NULL);
|
518 |
|
|
/* if it was once a directory (but how can we tell?) we could do
|
519 |
|
|
shrink_dcache_parent(direntry); */
|
520 |
|
|
} else {
|
521 |
|
|
cERROR(1, ("Error 0x%x on cifs_get_inode_info in lookup of %s",
|
522 |
|
|
rc, full_path));
|
523 |
|
|
/* BB special case check for Access Denied - watch security
|
524 |
|
|
exposure of returning dir info implicitly via different rc
|
525 |
|
|
if file exists or not but no access BB */
|
526 |
|
|
}
|
527 |
|
|
|
528 |
|
|
kfree(full_path);
|
529 |
|
|
FreeXid(xid);
|
530 |
|
|
return ERR_PTR(rc);
|
531 |
|
|
}
|
532 |
|
|
|
533 |
|
|
static int
|
534 |
|
|
cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
|
535 |
|
|
{
|
536 |
|
|
int isValid = 1;
|
537 |
|
|
|
538 |
|
|
if (direntry->d_inode) {
|
539 |
|
|
if (cifs_revalidate(direntry)) {
|
540 |
|
|
return 0;
|
541 |
|
|
}
|
542 |
|
|
} else {
|
543 |
|
|
cFYI(1, ("neg dentry 0x%p name = %s",
|
544 |
|
|
direntry, direntry->d_name.name));
|
545 |
|
|
if (time_after(jiffies, direntry->d_time + HZ) ||
|
546 |
|
|
!lookupCacheEnabled) {
|
547 |
|
|
d_drop(direntry);
|
548 |
|
|
isValid = 0;
|
549 |
|
|
}
|
550 |
|
|
}
|
551 |
|
|
|
552 |
|
|
return isValid;
|
553 |
|
|
}
|
554 |
|
|
|
555 |
|
|
/* static int cifs_d_delete(struct dentry *direntry)
|
556 |
|
|
{
|
557 |
|
|
int rc = 0;
|
558 |
|
|
|
559 |
|
|
cFYI(1, ("In cifs d_delete, name = %s", direntry->d_name.name));
|
560 |
|
|
|
561 |
|
|
return rc;
|
562 |
|
|
} */
|
563 |
|
|
|
564 |
|
|
struct dentry_operations cifs_dentry_ops = {
|
565 |
|
|
.d_revalidate = cifs_d_revalidate,
|
566 |
|
|
/* d_delete: cifs_d_delete, */ /* not needed except for debugging */
|
567 |
|
|
};
|
568 |
|
|
|
569 |
|
|
static int cifs_ci_hash(struct dentry *dentry, struct qstr *q)
|
570 |
|
|
{
|
571 |
|
|
struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
|
572 |
|
|
unsigned long hash;
|
573 |
|
|
int i;
|
574 |
|
|
|
575 |
|
|
hash = init_name_hash();
|
576 |
|
|
for (i = 0; i < q->len; i++)
|
577 |
|
|
hash = partial_name_hash(nls_tolower(codepage, q->name[i]),
|
578 |
|
|
hash);
|
579 |
|
|
q->hash = end_name_hash(hash);
|
580 |
|
|
|
581 |
|
|
return 0;
|
582 |
|
|
}
|
583 |
|
|
|
584 |
|
|
static int cifs_ci_compare(struct dentry *dentry, struct qstr *a,
|
585 |
|
|
struct qstr *b)
|
586 |
|
|
{
|
587 |
|
|
struct nls_table *codepage = CIFS_SB(dentry->d_inode->i_sb)->local_nls;
|
588 |
|
|
|
589 |
|
|
if ((a->len == b->len) &&
|
590 |
|
|
(nls_strnicmp(codepage, a->name, b->name, a->len) == 0)) {
|
591 |
|
|
/*
|
592 |
|
|
* To preserve case, don't let an existing negative dentry's
|
593 |
|
|
* case take precedence. If a is not a negative dentry, this
|
594 |
|
|
* should have no side effects
|
595 |
|
|
*/
|
596 |
|
|
memcpy(a->name, b->name, a->len);
|
597 |
|
|
return 0;
|
598 |
|
|
}
|
599 |
|
|
return 1;
|
600 |
|
|
}
|
601 |
|
|
|
602 |
|
|
struct dentry_operations cifs_ci_dentry_ops = {
|
603 |
|
|
.d_revalidate = cifs_d_revalidate,
|
604 |
|
|
.d_hash = cifs_ci_hash,
|
605 |
|
|
.d_compare = cifs_ci_compare,
|
606 |
|
|
};
|