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