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