vserver 1.9.3
[linux-2.6.git] / fs / cifs / inode.c
1 /*
2  *   fs/cifs/inode.c
3  *
4  *   Copyright (C) International Business Machines  Corp., 2002,2003
5  *   Author(s): Steve French (sfrench@us.ibm.com)
6  *
7  *   This library is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU Lesser General Public License as published
9  *   by the Free Software Foundation; either version 2.1 of the License, or
10  *   (at your option) any later version.
11  *
12  *   This library is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
15  *   the GNU Lesser General Public License for more details.
16  *
17  *   You should have received a copy of the GNU Lesser General Public License
18  *   along with this library; if not, write to the Free Software
19  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21 #include <linux/fs.h>
22 #include <linux/buffer_head.h>
23 #include <linux/stat.h>
24 #include <linux/pagemap.h>
25 #include <asm/div64.h>
26 #include "cifsfs.h"
27 #include "cifspdu.h"
28 #include "cifsglob.h"
29 #include "cifsproto.h"
30 #include "cifs_debug.h"
31 #include "cifs_fs_sb.h"
32
33 extern int is_size_safe_to_change(struct cifsInodeInfo *);
34
35 int
36 cifs_get_inode_info_unix(struct inode **pinode,
37                          const unsigned char *search_path,
38                          struct super_block *sb,int xid)
39 {
40         int rc = 0;
41         FILE_UNIX_BASIC_INFO findData;
42         struct cifsTconInfo *pTcon;
43         struct inode *inode;
44         struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
45         char *tmp_path;
46
47         pTcon = cifs_sb->tcon;
48         cFYI(1, (" Getting info on %s ", search_path));
49         /* we could have done a find first instead but this returns more info */
50         rc = CIFSSMBUnixQPathInfo(xid, pTcon, search_path, &findData,
51                                   cifs_sb->local_nls);
52         /* dump_mem("\nUnixQPathInfo return data", &findData, sizeof(findData)); */
53         if (rc) {
54                 if (rc == -EREMOTE) {
55                         tmp_path =
56                             kmalloc(strnlen
57                                     (pTcon->treeName,
58                                      MAX_TREE_SIZE + 1) +
59                                     strnlen(search_path, MAX_PATHCONF) + 1,
60                                     GFP_KERNEL);
61                         if (tmp_path == NULL) {
62                                 return -ENOMEM;
63                         }
64         /* have to skip first of the double backslash of UNC name */
65                         strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE);      
66                         strncat(tmp_path, search_path, MAX_PATHCONF);
67                         rc = connect_to_dfs_path(xid, pTcon->ses,
68                                                  /* treename + */ tmp_path,
69                                                  cifs_sb->local_nls);
70                         kfree(tmp_path);
71
72                         /* BB fix up inode etc. */
73                 } else if (rc) {
74                         return rc;
75                 }
76
77         } else {
78                 struct cifsInodeInfo *cifsInfo;
79                 __u32 type = le32_to_cpu(findData.Type);
80                 __u64 num_of_bytes = le64_to_cpu(findData.NumOfBytes);
81                 __u64 end_of_file = le64_to_cpu(findData.EndOfFile);
82
83                 /* get new inode */
84                 if (*pinode == NULL) {
85                         *pinode = new_inode(sb);
86                         if(*pinode == NULL) 
87                                 return -ENOMEM;
88                         insert_inode_hash(*pinode);
89                 }
90                         
91                 inode = *pinode;
92                 cifsInfo = CIFS_I(inode);
93
94                 cFYI(1, (" Old time %ld ", cifsInfo->time));
95                 cifsInfo->time = jiffies;
96                 cFYI(1, (" New time %ld ", cifsInfo->time));
97                 atomic_set(&cifsInfo->inUse,1); /* ok to set on every refresh of inode */
98
99                 inode->i_atime =
100                     cifs_NTtimeToUnix(le64_to_cpu(findData.LastAccessTime));
101                 inode->i_mtime =
102                     cifs_NTtimeToUnix(le64_to_cpu
103                                 (findData.LastModificationTime));
104                 inode->i_ctime =
105                     cifs_NTtimeToUnix(le64_to_cpu(findData.LastStatusChange));
106                 inode->i_mode = le64_to_cpu(findData.Permissions);
107                 if (type == UNIX_FILE) {
108                         inode->i_mode |= S_IFREG;
109                 } else if (type == UNIX_SYMLINK) {
110                         inode->i_mode |= S_IFLNK;
111                 } else if (type == UNIX_DIR) {
112                         inode->i_mode |= S_IFDIR;
113                 } else if (type == UNIX_CHARDEV) {
114                         inode->i_mode |= S_IFCHR;
115                         inode->i_rdev = MKDEV(le64_to_cpu(findData.DevMajor),
116                                 le64_to_cpu(findData.DevMinor) & MINORMASK);
117                 } else if (type == UNIX_BLOCKDEV) {
118                         inode->i_mode |= S_IFBLK;
119                         inode->i_rdev = MKDEV(le64_to_cpu(findData.DevMajor),
120                                 le64_to_cpu(findData.DevMinor) & MINORMASK);
121                 } else if (type == UNIX_FIFO) {
122                         inode->i_mode |= S_IFIFO;
123                 } else if (type == UNIX_SOCKET) {
124                         inode->i_mode |= S_IFSOCK;
125                 }
126                 inode->i_uid = le64_to_cpu(findData.Uid);
127                 inode->i_gid = le64_to_cpu(findData.Gid);
128                 inode->i_nlink = le64_to_cpu(findData.Nlinks);
129
130                 if(is_size_safe_to_change(cifsInfo)) {
131                 /* can not safely change the file size here if the 
132                    client is writing to it due to potential races */
133
134                         i_size_write(inode, end_of_file);
135 /* blksize needs to be multiple of two. So safer to default to blksize
136         and blkbits set in superblock so 2**blkbits and blksize will match */
137 /*              inode->i_blksize =
138                     (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/
139
140                 /* This seems incredibly stupid but it turns out that
141                 i_blocks is not related to (i_size / i_blksize), instead a
142                 size of 512 is required to be used for calculating num blocks */
143                  
144
145 /*              inode->i_blocks = 
146                         (inode->i_blksize - 1 + num_of_bytes) >> inode->i_blkbits;*/
147
148                 /* 512 bytes (2**9) is the fake blocksize that must be used */
149                 /* for this calculation */
150                         inode->i_blocks = (512 - 1 + num_of_bytes) >> 9;
151                 }
152
153                 if (num_of_bytes < end_of_file)
154                         cFYI(1, ("Server inconsistency Error: it says allocation size less than end of file "));
155                 cFYI(1,
156                      ("Size %ld and blocks %ld ",
157                       (unsigned long) inode->i_size, inode->i_blocks));
158                 if (S_ISREG(inode->i_mode)) {
159                         cFYI(1, (" File inode "));
160                         inode->i_op = &cifs_file_inode_ops;
161                         inode->i_fop = &cifs_file_ops;
162                         inode->i_data.a_ops = &cifs_addr_ops;
163                 } else if (S_ISDIR(inode->i_mode)) {
164                         cFYI(1, (" Directory inode"));
165                         inode->i_op = &cifs_dir_inode_ops;
166                         inode->i_fop = &cifs_dir_ops;
167                 } else if (S_ISLNK(inode->i_mode)) {
168                         cFYI(1, (" Symbolic Link inode "));
169                         inode->i_op = &cifs_symlink_inode_ops;
170 /* tmp_inode->i_fop = *//* do not need to set to anything */
171                 } else {
172                         cFYI(1, (" Init special inode "));
173                         init_special_inode(inode, inode->i_mode,
174                                            inode->i_rdev);
175                 }
176         }
177         return rc;
178 }
179
180 int
181 cifs_get_inode_info(struct inode **pinode, const unsigned char *search_path, 
182                 FILE_ALL_INFO * pfindData, struct super_block *sb, int xid)
183 {
184         int rc = 0;
185         struct cifsTconInfo *pTcon;
186         struct inode *inode;
187         struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
188         char *tmp_path;
189         char *buf = NULL;
190
191         pTcon = cifs_sb->tcon;
192         cFYI(1,("Getting info on %s ", search_path));
193
194         if((pfindData == NULL) && (*pinode != NULL)) {
195                 if(CIFS_I(*pinode)->clientCanCacheRead) {
196                         cFYI(1,("No need to revalidate inode sizes on cached file "));
197                         return rc;
198                 }
199         }
200
201         /* if file info not passed in then get it from server */
202         if(pfindData == NULL) {
203                 buf = kmalloc(sizeof(FILE_ALL_INFO),GFP_KERNEL);
204                 if(buf == NULL)
205                         return -ENOMEM;
206                 pfindData = (FILE_ALL_INFO *)buf;
207         /* could do find first instead but this returns more info */
208                 rc = CIFSSMBQPathInfo(xid, pTcon, search_path, pfindData,
209                               cifs_sb->local_nls);
210         }
211         /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */
212         if (rc) {
213                 if (rc == -EREMOTE) {
214                         tmp_path =
215                             kmalloc(strnlen
216                                     (pTcon->treeName,
217                                      MAX_TREE_SIZE + 1) +
218                                     strnlen(search_path, MAX_PATHCONF) + 1,
219                                     GFP_KERNEL);
220                         if (tmp_path == NULL) {
221                                 if(buf)
222                                         kfree(buf);
223                                 return -ENOMEM;
224                         }
225
226                         strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE);
227                         strncat(tmp_path, search_path, MAX_PATHCONF);
228                         rc = connect_to_dfs_path(xid, pTcon->ses,
229                                                  /* treename + */ tmp_path,
230                                                  cifs_sb->local_nls);
231                         kfree(tmp_path);
232                         /* BB fix up inode etc. */
233                 } else if (rc) {
234                         if(buf)
235                                 kfree(buf);
236                         return rc;
237                 }
238         } else {
239                 struct cifsInodeInfo *cifsInfo;
240                 __u32 attr = le32_to_cpu(pfindData->Attributes);
241
242                 /* get new inode */
243                 if (*pinode == NULL) {
244                         *pinode = new_inode(sb);
245                         if(*pinode == NULL)
246                                 return -ENOMEM;
247                         insert_inode_hash(*pinode);
248                 }
249                 inode = *pinode;
250                 cifsInfo = CIFS_I(inode);
251                 cifsInfo->cifsAttrs = attr;
252                 cFYI(1, (" Old time %ld ", cifsInfo->time));
253                 cifsInfo->time = jiffies;
254                 cFYI(1, (" New time %ld ", cifsInfo->time));
255
256 /* blksize needs to be multiple of two. So safer to default to blksize
257         and blkbits set in superblock so 2**blkbits and blksize will match */
258 /*              inode->i_blksize =
259                     (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/
260
261                 /* Linux can not store file creation time unfortunately so we ignore it */
262                 inode->i_atime =
263                     cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime));
264                 inode->i_mtime =
265                     cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime));
266                 inode->i_ctime =
267                     cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime));
268                 cFYI(0,
269                      (" Attributes came in as 0x%x ", attr));
270
271                 /* set default mode. will override for dirs below */
272                 if(atomic_read(&cifsInfo->inUse) == 0)
273                         /* new inode, can safely set these fields */
274                         inode->i_mode = cifs_sb->mnt_file_mode;
275
276                 if (attr & ATTR_REPARSE) {
277         /* Can IFLNK be set as it basically is on windows with IFREG or IFDIR? */
278                         inode->i_mode |= S_IFLNK;
279                 } else if (attr & ATTR_DIRECTORY) {
280         /* override default perms since we do not do byte range locking on dirs */
281                         inode->i_mode = cifs_sb->mnt_dir_mode;
282                         inode->i_mode |= S_IFDIR;
283                 } else {
284                         inode->i_mode |= S_IFREG;
285                         /* treat the dos attribute of read-only as read-only mode e.g. 555 */
286                         if(cifsInfo->cifsAttrs & ATTR_READONLY)
287                                 inode->i_mode &= ~(S_IWUGO);
288    /* BB add code here - validate if device or weird share or device type? */
289                 }
290                 if(is_size_safe_to_change(cifsInfo)) {
291                 /* can not safely change the file size here if the 
292                 client is writing to it due to potential races */
293
294                         i_size_write(inode,le64_to_cpu(pfindData->EndOfFile));
295
296                 /* 512 bytes (2**9) is the fake blocksize that must be used */
297                 /* for this calculation */
298                         inode->i_blocks = (512 - 1 + le64_to_cpu(pfindData->AllocationSize))
299                                  >> 9;
300                 }
301
302                 inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks);
303
304                 /* BB fill in uid and gid here? with help from winbind? 
305                         or retrieve from NTFS stream extended attribute */
306                 if(atomic_read(&cifsInfo->inUse) == 0) {
307                         inode->i_uid = cifs_sb->mnt_uid;
308                         inode->i_gid = cifs_sb->mnt_gid;
309                         /* set so we do not keep refreshing these fields with
310                         bad data after user has changed them in memory */
311                         atomic_set(&cifsInfo->inUse,1);
312                 }
313                 
314                 if (S_ISREG(inode->i_mode)) {
315                         cFYI(1, (" File inode "));
316                         inode->i_op = &cifs_file_inode_ops;
317                         inode->i_fop = &cifs_file_ops;
318                         inode->i_data.a_ops = &cifs_addr_ops;
319                 } else if (S_ISDIR(inode->i_mode)) {
320                         cFYI(1, (" Directory inode "));
321                         inode->i_op = &cifs_dir_inode_ops;
322                         inode->i_fop = &cifs_dir_ops;
323                 } else if (S_ISLNK(inode->i_mode)) {
324                         cFYI(1, (" Symbolic Link inode "));
325                         inode->i_op = &cifs_symlink_inode_ops;
326                 } else {
327                         init_special_inode(inode, inode->i_mode,
328                                            inode->i_rdev);
329                 }
330         }
331         if(buf)
332             kfree(buf);
333         return rc;
334 }
335
336 void
337 cifs_read_inode(struct inode *inode)
338 {                               /* gets root inode */
339         int xid;
340         struct cifs_sb_info *cifs_sb;
341
342         cifs_sb = CIFS_SB(inode->i_sb);
343         xid = GetXid();
344         if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
345                 cifs_get_inode_info_unix(&inode, "", inode->i_sb,xid);
346         else
347                 cifs_get_inode_info(&inode, "", NULL, inode->i_sb,xid);
348         /* can not call macro FreeXid here since in a void func */
349         _FreeXid(xid);
350 }
351
352 int
353 cifs_unlink(struct inode *inode, struct dentry *direntry)
354 {
355         int rc = 0;
356         int xid;
357         struct cifs_sb_info *cifs_sb;
358         struct cifsTconInfo *pTcon;
359         char *full_path = NULL;
360         struct cifsInodeInfo *cifsInode;
361         FILE_BASIC_INFO * pinfo_buf;
362
363         cFYI(1, (" cifs_unlink, inode = 0x%p with ", inode));
364
365         xid = GetXid();
366
367         cifs_sb = CIFS_SB(inode->i_sb);
368         pTcon = cifs_sb->tcon;
369
370 /* Unlink can be called from rename so we can not grab
371         the sem here since we deadlock otherwise */
372 /*      down(&direntry->d_sb->s_vfs_rename_sem);*/
373         full_path = build_path_from_dentry(direntry);
374 /*      up(&direntry->d_sb->s_vfs_rename_sem);*/
375         if(full_path == NULL) {
376                 FreeXid(xid);
377                 return -ENOMEM;
378         }
379         rc = CIFSSMBDelFile(xid, pTcon, full_path, cifs_sb->local_nls);
380
381         if (!rc) {
382                 direntry->d_inode->i_nlink--;
383         } else if (rc == -ENOENT) {
384                 d_drop(direntry);
385         } else if (rc == -ETXTBSY) {
386                 int oplock = FALSE;
387                 __u16 netfid;
388
389                 rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, DELETE, 
390                                 CREATE_NOT_DIR | CREATE_DELETE_ON_CLOSE,
391                                 &netfid, &oplock, NULL, cifs_sb->local_nls);
392                 if(rc==0) {
393                         CIFSSMBRenameOpenFile(xid,pTcon,netfid,
394                                 NULL, cifs_sb->local_nls);
395                         CIFSSMBClose(xid, pTcon, netfid);
396                         direntry->d_inode->i_nlink--;
397                 }
398         } else if (rc == -EACCES) {
399                 /* try only if r/o attribute set in local lookup data? */
400                 pinfo_buf = (FILE_BASIC_INFO *)kmalloc(sizeof(FILE_BASIC_INFO),GFP_KERNEL);
401                 if(pinfo_buf) {
402                         memset(pinfo_buf,0,sizeof(FILE_BASIC_INFO));        
403                 /* ATTRS set to normal clears r/o bit */
404                         pinfo_buf->Attributes = cpu_to_le32(ATTR_NORMAL);
405                         rc = CIFSSMBSetTimes(xid, pTcon, full_path, pinfo_buf,
406                                 cifs_sb->local_nls);
407                         kfree(pinfo_buf);
408                 }
409                 if(rc==0) {
410                         rc = CIFSSMBDelFile(xid, pTcon, full_path, cifs_sb->local_nls);
411                         if (!rc) {
412                                 direntry->d_inode->i_nlink--;
413                         } else if (rc == -ETXTBSY) {
414                                 int oplock = FALSE;
415                                 __u16 netfid;
416
417                                 rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, DELETE,
418                                                 CREATE_NOT_DIR | CREATE_DELETE_ON_CLOSE,
419                                                 &netfid, &oplock, NULL, cifs_sb->local_nls);
420                                 if(rc==0) {
421                                         CIFSSMBRenameOpenFile(xid,pTcon,netfid,NULL,cifs_sb->local_nls);
422                                         CIFSSMBClose(xid, pTcon, netfid);
423                                         direntry->d_inode->i_nlink--;
424                                 }
425                         /* BB if rc = -ETXTBUSY goto the rename logic BB */
426                         }
427                 }
428         }
429         cifsInode = CIFS_I(direntry->d_inode);
430         cifsInode->time = 0;    /* will force revalidate to get info when needed */
431         direntry->d_inode->i_ctime = inode->i_ctime = inode->i_mtime =
432             CURRENT_TIME;
433         cifsInode = CIFS_I(inode);
434         cifsInode->time = 0;    /* force revalidate of dir as well */
435
436         if (full_path)
437                 kfree(full_path);
438         FreeXid(xid);
439         return rc;
440 }
441
442 int
443 cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
444 {
445         int rc = 0;
446         int xid;
447         struct cifs_sb_info *cifs_sb;
448         struct cifsTconInfo *pTcon;
449         char *full_path = NULL;
450         struct inode *newinode = NULL;
451
452         cFYI(1, ("In cifs_mkdir, mode = 0x%x inode = 0x%p ", mode, inode));
453
454         xid = GetXid();
455
456         cifs_sb = CIFS_SB(inode->i_sb);
457         pTcon = cifs_sb->tcon;
458
459         down(&inode->i_sb->s_vfs_rename_sem);
460         full_path = build_path_from_dentry(direntry);
461         up(&inode->i_sb->s_vfs_rename_sem);
462         if(full_path == NULL) {
463                 FreeXid(xid);
464                 return -ENOMEM;
465         }
466         /* BB add setting the equivalent of mode via CreateX w/ACLs */
467         rc = CIFSSMBMkDir(xid, pTcon, full_path, cifs_sb->local_nls);
468         if (rc) {
469                 cFYI(1, ("cifs_mkdir returned 0x%x ", rc));
470                 d_drop(direntry);
471         } else {
472                 inode->i_nlink++;
473                 if (pTcon->ses->capabilities & CAP_UNIX)
474                         rc = cifs_get_inode_info_unix(&newinode, full_path,
475                                                       inode->i_sb,xid);
476                 else
477                         rc = cifs_get_inode_info(&newinode, full_path,NULL,
478                                                  inode->i_sb,xid);
479
480                 direntry->d_op = &cifs_dentry_ops;
481                 d_instantiate(direntry, newinode);
482                 if(direntry->d_inode)
483                         direntry->d_inode->i_nlink = 2;
484                 if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
485                         if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
486                                 CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode,
487                                                 (__u64)current->euid,  
488                                                 (__u64)current->egid,
489                                                 0 /* dev_t */,
490                                                 cifs_sb->local_nls);
491                         } else {
492                                 CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode,
493                                                 (__u64)-1,  
494                                                 (__u64)-1,
495                                                 0 /* dev_t */,
496                                                 cifs_sb->local_nls);
497                         }
498                 else { /* BB to be implemented via Windows secrty descriptors*/
499                 /* eg CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,-1,-1,local_nls);*/
500                 }
501         }
502         if (full_path)
503                 kfree(full_path);
504         FreeXid(xid);
505
506         return rc;
507 }
508
509 int
510 cifs_rmdir(struct inode *inode, struct dentry *direntry)
511 {
512         int rc = 0;
513         int xid;
514         struct cifs_sb_info *cifs_sb;
515         struct cifsTconInfo *pTcon;
516         char *full_path = NULL;
517         struct cifsInodeInfo *cifsInode;
518
519         cFYI(1, (" cifs_rmdir, inode = 0x%p with ", inode));
520
521         xid = GetXid();
522
523         cifs_sb = CIFS_SB(inode->i_sb);
524         pTcon = cifs_sb->tcon;
525
526         down(&inode->i_sb->s_vfs_rename_sem);
527         full_path = build_path_from_dentry(direntry);
528         up(&inode->i_sb->s_vfs_rename_sem);
529         if(full_path == NULL) {
530                 FreeXid(xid);
531                 return -ENOMEM;
532         }
533
534         rc = CIFSSMBRmDir(xid, pTcon, full_path, cifs_sb->local_nls);
535
536         if (!rc) {
537                 inode->i_nlink--;
538                 i_size_write(direntry->d_inode,0);
539                 direntry->d_inode->i_nlink = 0;
540         }
541
542         cifsInode = CIFS_I(direntry->d_inode);
543         cifsInode->time = 0;    /* force revalidate to go get info when needed */
544         direntry->d_inode->i_ctime = inode->i_ctime = inode->i_mtime =
545             CURRENT_TIME;
546
547         if (full_path)
548                 kfree(full_path);
549         FreeXid(xid);
550         return rc;
551 }
552
553 int
554 cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
555             struct inode *target_inode, struct dentry *target_direntry)
556 {
557         char *fromName;
558         char *toName;
559         struct cifs_sb_info *cifs_sb_source;
560         struct cifs_sb_info *cifs_sb_target;
561         struct cifsTconInfo *pTcon;
562         int xid;
563         int rc = 0;
564
565         xid = GetXid();
566
567         cifs_sb_target = CIFS_SB(target_inode->i_sb);
568         cifs_sb_source = CIFS_SB(source_inode->i_sb);
569         pTcon = cifs_sb_source->tcon;
570
571         if (pTcon != cifs_sb_target->tcon) {
572                 FreeXid(xid);    
573                 return -EXDEV;  /* BB actually could be allowed if same server, but
574                      different share. Might eventually add support for this */
575         }
576
577         /* we already  have the rename sem so we do not need
578         to grab it again here to protect the path integrity */
579         fromName = build_path_from_dentry(source_direntry);
580         toName = build_path_from_dentry(target_direntry);
581         if((fromName == NULL) || (toName == NULL)) {
582                 rc = -ENOMEM;
583                 goto cifs_rename_exit;
584         }
585
586         rc = CIFSSMBRename(xid, pTcon, fromName, toName,
587                            cifs_sb_source->local_nls);
588         if(rc == -EEXIST) {
589                 /* check if they are the same file 
590                 because rename of hardlinked files is a noop */
591                 FILE_UNIX_BASIC_INFO * info_buf_source;
592                 FILE_UNIX_BASIC_INFO * info_buf_target;
593
594                 info_buf_source = 
595                         kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),GFP_KERNEL);
596                 if(info_buf_source != NULL) {
597                         info_buf_target = info_buf_source+1;
598                         rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName, 
599                                 info_buf_source, cifs_sb_source->local_nls);
600                         if(rc == 0) {
601                                 rc = CIFSSMBUnixQPathInfo(xid,pTcon,toName,
602                                                 info_buf_target,
603                                                 cifs_sb_target->local_nls);
604                         }
605                         if((rc == 0) && 
606                                 (info_buf_source->UniqueId == 
607                                  info_buf_target->UniqueId)) {
608                         /* do not rename since the files are hardlinked 
609                            which is a noop */
610                         } else {
611                         /* we either can not tell the files are hardlinked
612                         (as with Windows servers) or files are not hardlinked 
613                         so delete the target manually before renaming to
614                         follow POSIX rather than Windows semantics */
615                                 cifs_unlink(target_inode, target_direntry);
616                                 rc = CIFSSMBRename(xid, pTcon, fromName, toName,
617                                         cifs_sb_source->local_nls);
618                         }
619                         kfree(info_buf_source);
620                 } /* if we can not get memory just leave rc as EEXIST */
621         }
622
623         if((rc == -EIO)||(rc == -EEXIST)) {
624                 int oplock = FALSE;
625                 __u16 netfid;
626
627                 rc = CIFSSMBOpen(xid, pTcon, fromName, FILE_OPEN, GENERIC_READ,
628                                         CREATE_NOT_DIR,
629                                         &netfid, &oplock, NULL, cifs_sb_source->local_nls);
630                 if(rc==0) {
631                         CIFSSMBRenameOpenFile(xid,pTcon,netfid,
632                                         toName, cifs_sb_source->local_nls);
633                         CIFSSMBClose(xid, pTcon, netfid);
634                 }
635         }
636
637 cifs_rename_exit:
638         if (fromName)
639                 kfree(fromName);
640         if (toName)
641                 kfree(toName);
642
643         FreeXid(xid);
644         return rc;
645 }
646
647 int
648 cifs_revalidate(struct dentry *direntry)
649 {
650         int xid;
651         int rc = 0;
652         char *full_path;
653         struct cifs_sb_info *cifs_sb;
654         struct cifsInodeInfo *cifsInode;
655         loff_t local_size;
656         struct timespec local_mtime;
657         int invalidate_inode = FALSE;
658
659         if(direntry->d_inode == NULL)
660                 return -ENOENT;
661
662         cifsInode = CIFS_I(direntry->d_inode);
663
664         if(cifsInode == NULL)
665                 return -ENOENT;
666
667         /* no sense revalidating inode info on file that no one can write */
668         if(CIFS_I(direntry->d_inode)->clientCanCacheRead)
669                 return rc;
670
671         xid = GetXid();
672
673         cifs_sb = CIFS_SB(direntry->d_sb);
674
675         /* can not safely grab the rename sem here if
676         rename calls revalidate since that would deadlock */
677         full_path = build_path_from_dentry(direntry);
678         if(full_path == NULL) {
679                 FreeXid(xid);
680                 return -ENOMEM;
681         }
682         cFYI(1,
683              ("Revalidate: %s inode 0x%p count %d dentry: 0x%p d_time %ld jiffies %ld",
684               full_path, direntry->d_inode,
685               direntry->d_inode->i_count.counter, direntry,
686               direntry->d_time, jiffies));
687
688         if (cifsInode->time == 0){
689                 /* was set to zero previously to force revalidate */
690         } else if (time_before(jiffies, cifsInode->time + HZ) && lookupCacheEnabled) {
691             if((S_ISREG(direntry->d_inode->i_mode) == 0) || 
692                         (direntry->d_inode->i_nlink == 1)) {  
693                         if (full_path)
694                                 kfree(full_path);
695                         FreeXid(xid);
696                         return rc;
697                 } else {
698                         cFYI(1,("Have to revalidate file due to hardlinks"));
699                 }            
700         }
701         
702         /* save mtime and size */
703         local_mtime = direntry->d_inode->i_mtime;
704         local_size  = direntry->d_inode->i_size;
705
706         if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) {
707                 rc = cifs_get_inode_info_unix(&direntry->d_inode, full_path,
708                                          direntry->d_sb,xid);
709                 if(rc) {
710                         cFYI(1,("error on getting revalidate info %d",rc));
711 /*                      if(rc != -ENOENT)
712                                 rc = 0; */ /* BB should we cache info on certain errors? */
713                 }
714         } else {
715                 rc = cifs_get_inode_info(&direntry->d_inode, full_path, NULL,
716                                     direntry->d_sb,xid);
717                 if(rc) {
718                         cFYI(1,("error on getting revalidate info %d",rc));
719 /*                      if(rc != -ENOENT)
720                                 rc = 0; */  /* BB should we cache info on certain errors? */
721                 }
722         }
723         /* should we remap certain errors, access denied?, to zero */
724
725         /* if not oplocked, we invalidate inode pages if mtime 
726            or file size had changed on server */
727
728         if(timespec_equal(&local_mtime,&direntry->d_inode->i_mtime) && 
729                 (local_size == direntry->d_inode->i_size)) {
730                 cFYI(1,("cifs_revalidate - inode unchanged"));
731         } else {
732                 /* file may have changed on server */
733                 if(cifsInode->clientCanCacheRead) {
734                         /* no need to invalidate inode pages since we were
735                            the only ones who could have modified the file and
736                            the server copy is staler than ours */
737                 } else {
738                         invalidate_inode = TRUE;
739                 }
740         }
741
742         /* can not grab this sem since kernel filesys locking
743                 documentation indicates i_sem may be taken by the kernel 
744                 on lookup and rename which could deadlock if we grab
745                 the i_sem here as well */
746 /*      down(&direntry->d_inode->i_sem);*/
747         /* need to write out dirty pages here  */
748         if(direntry->d_inode->i_mapping) {
749                 /* do we need to lock inode until after invalidate completes below? */
750                 filemap_fdatawrite(direntry->d_inode->i_mapping);
751         }
752         if(invalidate_inode) {
753                 filemap_fdatawait(direntry->d_inode->i_mapping);
754                 /* may eventually have to do this for open files too */
755                 if(list_empty(&(cifsInode->openFileList))) {
756                         /* Has changed on server - flush read ahead pages */
757                         cFYI(1,("Invalidating read ahead data on closed file"));
758                         invalidate_remote_inode(direntry->d_inode);
759                 }
760         }
761 /*      up(&direntry->d_inode->i_sem);*/
762         
763         if (full_path)
764                 kfree(full_path);
765         FreeXid(xid);
766
767         return rc;
768 }
769
770 int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
771 {
772         int err = cifs_revalidate(dentry);
773         if (!err)
774                 generic_fillattr(dentry->d_inode, stat);
775         return err;
776 }
777
778 static int cifs_truncate_page(struct address_space *mapping, loff_t from)
779 {
780         pgoff_t index = from >> PAGE_CACHE_SHIFT;
781         unsigned offset = from & (PAGE_CACHE_SIZE-1);
782         struct page *page;
783         char *kaddr;
784         int rc = 0;
785
786         page = grab_cache_page(mapping, index);
787         if (!page)
788                 return -ENOMEM;
789
790         kaddr = kmap_atomic(page, KM_USER0);
791         memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset);
792         flush_dcache_page(page);
793         kunmap_atomic(kaddr, KM_USER0);
794         unlock_page(page);
795         page_cache_release(page);
796         return rc;
797 }
798
799 int
800 cifs_setattr(struct dentry *direntry, struct iattr *attrs)
801 {
802         int xid;
803         struct cifs_sb_info *cifs_sb;
804         struct cifsTconInfo *pTcon;
805         char *full_path = NULL;
806         int rc = -EACCES;
807         int found = FALSE;
808         struct cifsFileInfo *open_file = NULL;
809         FILE_BASIC_INFO time_buf;
810         int set_time = FALSE;
811         __u64 mode = 0xFFFFFFFFFFFFFFFFULL;
812         __u64 uid = 0xFFFFFFFFFFFFFFFFULL;
813         __u64 gid = 0xFFFFFFFFFFFFFFFFULL;
814         struct cifsInodeInfo *cifsInode;
815         struct list_head * tmp;
816
817         xid = GetXid();
818
819         cFYI(1,
820              (" In cifs_setattr, name = %s attrs->iavalid 0x%x ",
821               direntry->d_name.name, attrs->ia_valid));
822         cifs_sb = CIFS_SB(direntry->d_inode->i_sb);
823         pTcon = cifs_sb->tcon;
824
825         down(&direntry->d_sb->s_vfs_rename_sem);
826         full_path = build_path_from_dentry(direntry);
827         up(&direntry->d_sb->s_vfs_rename_sem);
828         if(full_path == NULL) {
829                 FreeXid(xid);
830                 return -ENOMEM;
831         }
832         cifsInode = CIFS_I(direntry->d_inode);
833
834         /* BB check if we need to refresh inode from server now ? BB */
835
836         /* need to flush data before changing file size on server */
837         filemap_fdatawrite(direntry->d_inode->i_mapping); 
838         filemap_fdatawait(direntry->d_inode->i_mapping);
839
840         if (attrs->ia_valid & ATTR_SIZE) {
841                 read_lock(&GlobalSMBSeslock); 
842                 /* To avoid spurious oplock breaks from server, in the case
843                         of inodes that we already have open, avoid doing path
844                         based setting of file size if we can do it by handle.
845                         This keeps our caching token (oplock) and avoids
846                         timeouts when the local oplock break takes longer to flush
847                         writebehind data than the SMB timeout for the SetPathInfo 
848                         request would allow */
849                 list_for_each(tmp, &cifsInode->openFileList) {            
850                         open_file = list_entry(tmp,struct cifsFileInfo, flist);
851                         /* We check if file is open for writing first */
852                         if((open_file->pfile) &&
853                                 ((open_file->pfile->f_flags & O_RDWR) || 
854                                  (open_file->pfile->f_flags & O_WRONLY))) {
855                                 if(open_file->invalidHandle == FALSE) {
856                                         /* we found a valid, writeable network file 
857                                         handle to use to try to set the file size */
858                                         __u16 nfid = open_file->netfid;
859                                         __u32 npid = open_file->pid;
860                                         read_unlock(&GlobalSMBSeslock);
861                                         found = TRUE;
862                                         rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size,
863                                            nfid,npid,FALSE);
864                                         cFYI(1,("SetFileSize by handle (setattrs) rc = %d",rc));
865                                 /* Do not need reopen and retry on EAGAIN since we will
866                                         retry by pathname below */
867
868                                         break;  /* now that we found one valid file handle no
869                                                 sense continuing to loop trying others */
870                                 }
871                         }
872                 }
873                 if(found == FALSE) {
874                         read_unlock(&GlobalSMBSeslock);
875                 }
876
877
878                 if(rc != 0) {
879                         /* Set file size by pathname rather than by handle either
880                         because no valid, writeable file handle for it was found or
881                         because there was an error setting it by handle */
882                         rc = CIFSSMBSetEOF(xid, pTcon, full_path, attrs->ia_size,FALSE,
883                                    cifs_sb->local_nls);
884                         cFYI(1,(" SetEOF by path (setattrs) rc = %d",rc));
885                 }
886         
887         /*  Server is ok setting allocation size implicitly - no need to call: */
888         /*CIFSSMBSetEOF(xid, pTcon, full_path, attrs->ia_size, TRUE, cifs_sb->local_nls);*/
889
890                 if (rc == 0) {
891                         rc = vmtruncate(direntry->d_inode, attrs->ia_size);
892                         cifs_truncate_page(direntry->d_inode->i_mapping, direntry->d_inode->i_size);
893                 }
894         }
895         if (attrs->ia_valid & ATTR_UID) {
896                 cFYI(1, (" CIFS - UID changed to %d", attrs->ia_uid));
897                 uid = attrs->ia_uid;
898                 /*        entry->uid = cpu_to_le16(attr->ia_uid); */
899         }
900         if (attrs->ia_valid & ATTR_GID) {
901                 cFYI(1, (" CIFS - GID changed to %d", attrs->ia_gid));
902                 gid = attrs->ia_gid;
903                 /*      entry->gid = cpu_to_le16(attr->ia_gid); */
904         }
905
906         time_buf.Attributes = 0;
907         if (attrs->ia_valid & ATTR_MODE) {
908                 cFYI(1, (" CIFS - Mode changed to 0x%x", attrs->ia_mode));
909                 mode = attrs->ia_mode;
910                 /* entry->mode = cpu_to_le16(attr->ia_mode); */
911         }
912
913         if ((cifs_sb->tcon->ses->capabilities & CAP_UNIX)
914             && (attrs->ia_valid & (ATTR_MODE | ATTR_GID | ATTR_UID)))
915                 rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode, uid, gid,
916                                 0 /* dev_t */, cifs_sb->local_nls);
917         else if (attrs->ia_valid & ATTR_MODE) {
918                 if((mode & S_IWUGO) == 0) /* not writeable */ {
919                         if((cifsInode->cifsAttrs & ATTR_READONLY) == 0)
920                                 time_buf.Attributes = 
921                                         cpu_to_le32(cifsInode->cifsAttrs | ATTR_READONLY);
922                 } else if((mode & S_IWUGO) == S_IWUGO) {
923                         if(cifsInode->cifsAttrs & ATTR_READONLY)
924                                 time_buf.Attributes = 
925                                         cpu_to_le32(cifsInode->cifsAttrs & (~ATTR_READONLY));
926                 }
927                 /* BB to be implemented - via Windows security descriptors or streams */
928                 /* CIFSSMBWinSetPerms(xid,pTcon,full_path,mode,uid,gid,cifs_sb->local_nls);*/
929         }
930
931         if (attrs->ia_valid & ATTR_ATIME) {
932                 set_time = TRUE;
933                 time_buf.LastAccessTime =
934                     cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime));
935         } else
936                 time_buf.LastAccessTime = 0;
937
938         if (attrs->ia_valid & ATTR_MTIME) {
939                 set_time = TRUE;
940                 time_buf.LastWriteTime =
941                     cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime));
942         } else
943                 time_buf.LastWriteTime = 0;
944
945         if (attrs->ia_valid & ATTR_CTIME) {
946                 set_time = TRUE;
947                 cFYI(1, (" CIFS - CTIME changed ")); /* BB probably do not need */
948                 time_buf.ChangeTime =
949                     cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));
950         } else
951                 time_buf.ChangeTime = 0;
952
953         if (set_time || time_buf.Attributes) {
954                 /* BB what if setting one attribute fails  
955                         (such as size) but time setting works */
956                 time_buf.CreationTime = 0;      /* do not change */
957                 /* In the future we should experiment - try setting timestamps
958                          via Handle (SetFileInfo) instead of by path */
959                 rc = CIFSSMBSetTimes(xid, pTcon, full_path, &time_buf,
960                                 cifs_sb->local_nls);
961         }
962
963         /* do not  need local check to inode_check_ok since the server does that */
964         if (!rc)
965                 rc = inode_setattr(direntry->d_inode, attrs);
966         if (full_path)
967                 kfree(full_path);
968         FreeXid(xid);
969         return rc;
970 }
971
972 void
973 cifs_delete_inode(struct inode *inode)
974 {
975         cFYI(1, ("In cifs_delete_inode, inode = 0x%p ", inode));
976         /* may have to add back in if and when safe distributed caching of
977                 directories added e.g. via FindNotify */
978 }