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