]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - fs/cifs/inode.c
Merge with /pub/scm/linux/kernel/git/torvalds/linux-2.6.git
[linux-2.6-omap-h63xx.git] / fs / cifs / inode.c
1 /*
2  *   fs/cifs/inode.c
3  *
4  *   Copyright (C) International Business Machines  Corp., 2002,2005
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 cifs_get_inode_info_unix(struct inode **pinode,
34         const unsigned char *search_path, struct super_block *sb, int xid)
35 {
36         int rc = 0;
37         FILE_UNIX_BASIC_INFO findData;
38         struct cifsTconInfo *pTcon;
39         struct inode *inode;
40         struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
41         char *tmp_path;
42
43         pTcon = cifs_sb->tcon;
44         cFYI(1, (" Getting info on %s ", search_path));
45         /* could have done a find first instead but this returns more info */
46         rc = CIFSSMBUnixQPathInfo(xid, pTcon, search_path, &findData,
47                                   cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
48                                         CIFS_MOUNT_MAP_SPECIAL_CHR);
49 /*      dump_mem("\nUnixQPathInfo return data", &findData,
50                  sizeof(findData)); */
51         if (rc) {
52                 if (rc == -EREMOTE) {
53                         tmp_path =
54                             kmalloc(strnlen(pTcon->treeName,
55                                             MAX_TREE_SIZE + 1) +
56                                     strnlen(search_path, MAX_PATHCONF) + 1,
57                                     GFP_KERNEL);
58                         if (tmp_path == NULL) {
59                                 return -ENOMEM;
60                         }
61                         /* have to skip first of the double backslash of
62                            UNC name */
63                         strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE);
64                         strncat(tmp_path, search_path, MAX_PATHCONF);
65                         rc = connect_to_dfs_path(xid, pTcon->ses,
66                                                  /* treename + */ tmp_path,
67                                                  cifs_sb->local_nls, 
68                                                  cifs_sb->mnt_cifs_flags & 
69                                                     CIFS_MOUNT_MAP_SPECIAL_CHR);
70                         kfree(tmp_path);
71
72                         /* BB fix up inode etc. */
73                 } else if (rc) {
74                         return rc;
75                 }
76         } else {
77                 struct cifsInodeInfo *cifsInfo;
78                 __u32 type = le32_to_cpu(findData.Type);
79                 __u64 num_of_bytes = le64_to_cpu(findData.NumOfBytes);
80                 __u64 end_of_file = le64_to_cpu(findData.EndOfFile);
81
82                 /* get new inode */
83                 if (*pinode == NULL) {
84                         *pinode = new_inode(sb);
85                         if (*pinode == NULL) 
86                                 return -ENOMEM;
87                         /* Is an i_ino of zero legal? */
88                         /* Are there sanity checks we can use to ensure that
89                            the server is really filling in that field? */
90                         if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
91                                 (*pinode)->i_ino =
92                                         (unsigned long)findData.UniqueId;
93                         } /* note ino incremented to unique num in new_inode */
94                         insert_inode_hash(*pinode);
95                 }
96
97                 inode = *pinode;
98                 cifsInfo = CIFS_I(inode);
99
100                 cFYI(1, ("Old time %ld ", cifsInfo->time));
101                 cifsInfo->time = jiffies;
102                 cFYI(1, ("New time %ld ", cifsInfo->time));
103                 /* this is ok to set on every inode revalidate */
104                 atomic_set(&cifsInfo->inUse,1);
105
106                 inode->i_atime =
107                     cifs_NTtimeToUnix(le64_to_cpu(findData.LastAccessTime));
108                 inode->i_mtime =
109                     cifs_NTtimeToUnix(le64_to_cpu
110                                 (findData.LastModificationTime));
111                 inode->i_ctime =
112                     cifs_NTtimeToUnix(le64_to_cpu(findData.LastStatusChange));
113                 inode->i_mode = le64_to_cpu(findData.Permissions);
114                 if (type == UNIX_FILE) {
115                         inode->i_mode |= S_IFREG;
116                 } else if (type == UNIX_SYMLINK) {
117                         inode->i_mode |= S_IFLNK;
118                 } else if (type == UNIX_DIR) {
119                         inode->i_mode |= S_IFDIR;
120                 } else if (type == UNIX_CHARDEV) {
121                         inode->i_mode |= S_IFCHR;
122                         inode->i_rdev = MKDEV(le64_to_cpu(findData.DevMajor),
123                                 le64_to_cpu(findData.DevMinor) & MINORMASK);
124                 } else if (type == UNIX_BLOCKDEV) {
125                         inode->i_mode |= S_IFBLK;
126                         inode->i_rdev = MKDEV(le64_to_cpu(findData.DevMajor),
127                                 le64_to_cpu(findData.DevMinor) & MINORMASK);
128                 } else if (type == UNIX_FIFO) {
129                         inode->i_mode |= S_IFIFO;
130                 } else if (type == UNIX_SOCKET) {
131                         inode->i_mode |= S_IFSOCK;
132                 }
133                 inode->i_uid = le64_to_cpu(findData.Uid);
134                 inode->i_gid = le64_to_cpu(findData.Gid);
135                 inode->i_nlink = le64_to_cpu(findData.Nlinks);
136
137                 if (is_size_safe_to_change(cifsInfo)) {
138                 /* can not safely change the file size here if the
139                    client is writing to it due to potential races */
140
141                         i_size_write(inode, end_of_file);
142
143                 /* blksize needs to be multiple of two. So safer to default to
144                 blksize and blkbits set in superblock so 2**blkbits and blksize
145                 will match rather than setting to:
146                 (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/
147
148                 /* This seems incredibly stupid but it turns out that i_blocks
149                    is not related to (i_size / i_blksize), instead 512 byte size
150                    is required for calculating num blocks */
151
152                 /* 512 bytes (2**9) is the fake blocksize that must be used */
153                 /* for this calculation */
154                         inode->i_blocks = (512 - 1 + num_of_bytes) >> 9;
155                 }
156
157                 if (num_of_bytes < end_of_file)
158                         cFYI(1, ("allocation size less than end of file"));
159                 cFYI(1,
160                      ("Size %ld and blocks %ld",
161                       (unsigned long) inode->i_size, inode->i_blocks));
162                 if (S_ISREG(inode->i_mode)) {
163                         cFYI(1, ("File inode"));
164                         inode->i_op = &cifs_file_inode_ops;
165                         if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
166                                 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
167                                         inode->i_fop = 
168                                                 &cifs_file_direct_nobrl_ops;
169                                 else
170                                         inode->i_fop = &cifs_file_direct_ops;
171                         } else if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
172                                 inode->i_fop = &cifs_file_nobrl_ops;
173                         else /* not direct, send byte range locks */ 
174                                 inode->i_fop = &cifs_file_ops;
175
176                         inode->i_data.a_ops = &cifs_addr_ops;
177                         /* check if server can support readpages */
178                         if(pTcon->ses->server->maxBuf < 
179                             4096 + MAX_CIFS_HDR_SIZE)
180                                 inode->i_data.a_ops->readpages = NULL;
181                 } else if (S_ISDIR(inode->i_mode)) {
182                         cFYI(1, ("Directory inode"));
183                         inode->i_op = &cifs_dir_inode_ops;
184                         inode->i_fop = &cifs_dir_ops;
185                 } else if (S_ISLNK(inode->i_mode)) {
186                         cFYI(1, ("Symbolic Link inode"));
187                         inode->i_op = &cifs_symlink_inode_ops;
188                 /* tmp_inode->i_fop = */ /* do not need to set to anything */
189                 } else {
190                         cFYI(1, ("Init special inode"));
191                         init_special_inode(inode, inode->i_mode,
192                                            inode->i_rdev);
193                 }
194         }
195         return rc;
196 }
197
198 static int decode_sfu_inode(struct inode * inode, __u64 size,
199                             const unsigned char *path,
200                             struct cifs_sb_info *cifs_sb, int xid)
201 {
202         int rc;
203         int oplock = FALSE;
204         __u16 netfid;
205         struct cifsTconInfo *pTcon = cifs_sb->tcon;
206         char buf[8];
207         unsigned int bytes_read;
208         char * pbuf;
209
210         pbuf = buf;
211
212         if(size == 0) {
213                 inode->i_mode |= S_IFIFO;
214                 return 0;
215         } else if (size < 8) {
216                 return -EINVAL;  /* EOPNOTSUPP? */
217         }
218                 
219         rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ,
220                          CREATE_NOT_DIR, &netfid, &oplock, NULL,
221                          cifs_sb->local_nls,
222                          cifs_sb->mnt_cifs_flags &
223                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
224         if (rc==0) {
225                         /* Read header */
226                 rc = CIFSSMBRead(xid, pTcon,
227                                  netfid,
228                                  8 /* length */, 0 /* offset */,
229                                  &bytes_read, &pbuf);
230                 if((rc == 0) && (bytes_read == 8)) {
231                         /* if memcmp(IntxCHR\000, pbuf, 8)
232                            else if memcmp(IntxBLK\000, pbuf, 8)
233                            else if memcmp(IntxLNK\001, pbuf, 8) */
234                 }
235                 
236                 CIFSSMBClose(xid, pTcon, netfid);
237         
238
239         /* inode->i_rdev = MKDEV(le64_to_cpu(DevMajor),
240                             le64_to_cpu(DevMinor) & MINORMASK);*/
241 /*      inode->i_mode |= S_IFBLK; */
242         }
243         return rc;
244         
245 }
246
247 int cifs_get_inode_info(struct inode **pinode,
248         const unsigned char *search_path, FILE_ALL_INFO *pfindData,
249         struct super_block *sb, int xid)
250 {
251         int rc = 0;
252         struct cifsTconInfo *pTcon;
253         struct inode *inode;
254         struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
255         char *tmp_path;
256         char *buf = NULL;
257
258         pTcon = cifs_sb->tcon;
259         cFYI(1,("Getting info on %s", search_path));
260
261         if ((pfindData == NULL) && (*pinode != NULL)) {
262                 if (CIFS_I(*pinode)->clientCanCacheRead) {
263                         cFYI(1,("No need to revalidate cached inode sizes"));
264                         return rc;
265                 }
266         }
267
268         /* if file info not passed in then get it from server */
269         if (pfindData == NULL) {
270                 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
271                 if (buf == NULL)
272                         return -ENOMEM;
273                 pfindData = (FILE_ALL_INFO *)buf;
274                 /* could do find first instead but this returns more info */
275                 rc = CIFSSMBQPathInfo(xid, pTcon, search_path, pfindData,
276                               cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
277                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
278                 /* BB optimize code so we do not make the above call
279                 when server claims no NT SMB support and the above call
280                 failed at least once - set flag in tcon or mount */
281                 if((rc == -EOPNOTSUPP) || (rc == -EINVAL)) {
282                         rc = SMBQueryInformation(xid, pTcon, search_path,
283                                         pfindData, cifs_sb->local_nls, 
284                                         cifs_sb->mnt_cifs_flags &
285                                           CIFS_MOUNT_MAP_SPECIAL_CHR);
286                 }
287                 
288         }
289         /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */
290         if (rc) {
291                 if (rc == -EREMOTE) {
292                         tmp_path =
293                             kmalloc(strnlen
294                                     (pTcon->treeName,
295                                      MAX_TREE_SIZE + 1) +
296                                     strnlen(search_path, MAX_PATHCONF) + 1,
297                                     GFP_KERNEL);
298                         if (tmp_path == NULL) {
299                                 kfree(buf);
300                                 return -ENOMEM;
301                         }
302
303                         strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE);
304                         strncat(tmp_path, search_path, MAX_PATHCONF);
305                         rc = connect_to_dfs_path(xid, pTcon->ses,
306                                                  /* treename + */ tmp_path,
307                                                  cifs_sb->local_nls, 
308                                                  cifs_sb->mnt_cifs_flags & 
309                                                    CIFS_MOUNT_MAP_SPECIAL_CHR);
310                         kfree(tmp_path);
311                         /* BB fix up inode etc. */
312                 } else if (rc) {
313                         kfree(buf);
314                         return rc;
315                 }
316         } else {
317                 struct cifsInodeInfo *cifsInfo;
318                 __u32 attr = le32_to_cpu(pfindData->Attributes);
319
320                 /* get new inode */
321                 if (*pinode == NULL) {
322                         *pinode = new_inode(sb);
323                         if (*pinode == NULL)
324                                 return -ENOMEM;
325                         /* Is an i_ino of zero legal? Can we use that to check
326                            if the server supports returning inode numbers?  Are
327                            there other sanity checks we can use to ensure that
328                            the server is really filling in that field? */
329
330                         /* We can not use the IndexNumber field by default from
331                            Windows or Samba (in ALL_INFO buf) but we can request
332                            it explicitly.  It may not be unique presumably if
333                            the server has multiple devices mounted under one
334                            share */
335
336                         /* There may be higher info levels that work but are
337                            there Windows server or network appliances for which
338                            IndexNumber field is not guaranteed unique? */
339
340                         if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM){
341                                 int rc1 = 0;
342                                 __u64 inode_num;
343
344                                 rc1 = CIFSGetSrvInodeNumber(xid, pTcon, 
345                                         search_path, &inode_num, 
346                                         cifs_sb->local_nls,
347                                         cifs_sb->mnt_cifs_flags &
348                                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
349                                 if (rc1) {
350                                         cFYI(1,("GetSrvInodeNum rc %d", rc1));
351                                         /* BB EOPNOSUPP disable SERVER_INUM? */
352                                 } else /* do we need cast or hash to ino? */
353                                         (*pinode)->i_ino = inode_num;
354                         } /* else ino incremented to unique num in new_inode*/
355                         insert_inode_hash(*pinode);
356                 }
357                 inode = *pinode;
358                 cifsInfo = CIFS_I(inode);
359                 cifsInfo->cifsAttrs = attr;
360                 cFYI(1, (" Old time %ld ", cifsInfo->time));
361                 cifsInfo->time = jiffies;
362                 cFYI(1, (" New time %ld ", cifsInfo->time));
363
364                 /* blksize needs to be multiple of two. So safer to default to
365                 blksize and blkbits set in superblock so 2**blkbits and blksize
366                 will match rather than setting to:
367                 (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/
368
369                 /* Linux can not store file creation time unfortunately so we ignore it */
370                 inode->i_atime =
371                     cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime));
372                 inode->i_mtime =
373                     cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime));
374                 inode->i_ctime =
375                     cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime));
376                 cFYI(0, (" Attributes came in as 0x%x ", attr));
377
378                 /* set default mode. will override for dirs below */
379                 if (atomic_read(&cifsInfo->inUse) == 0)
380                         /* new inode, can safely set these fields */
381                         inode->i_mode = cifs_sb->mnt_file_mode;
382
383 /*              if (attr & ATTR_REPARSE)  */
384                 /* We no longer handle these as symlinks because we could not
385                    follow them due to the absolute path with drive letter */
386                 if (attr & ATTR_DIRECTORY) {
387                 /* override default perms since we do not do byte range locking
388                    on dirs */
389                         inode->i_mode = cifs_sb->mnt_dir_mode;
390                         inode->i_mode |= S_IFDIR;
391                 } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) &&
392                            (cifsInfo->cifsAttrs & ATTR_SYSTEM) &&
393                            /* No need to le64 convert size of zero */
394                            (pfindData->EndOfFile == 0)) {
395                         inode->i_mode = cifs_sb->mnt_file_mode;
396                         inode->i_mode |= S_IFIFO;
397 /* BB Finish for SFU style symlinks and devices */
398                 } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) &&
399                            (cifsInfo->cifsAttrs & ATTR_SYSTEM)) {
400                         if (decode_sfu_inode(inode, 
401                                          le64_to_cpu(pfindData->EndOfFile),
402                                          search_path,
403                                          cifs_sb, xid)) {
404                                 cFYI(1,("Unrecognized sfu inode type"));
405                         }
406                 } else {
407                         inode->i_mode |= S_IFREG;
408                         /* treat the dos attribute of read-only as read-only
409                            mode e.g. 555 */
410                         if (cifsInfo->cifsAttrs & ATTR_READONLY)
411                                 inode->i_mode &= ~(S_IWUGO);
412                 /* BB add code here -
413                    validate if device or weird share or device type? */
414                 }
415                 if (is_size_safe_to_change(cifsInfo)) {
416                         /* can not safely change the file size here if the
417                            client is writing to it due to potential races */
418                         i_size_write(inode,le64_to_cpu(pfindData->EndOfFile));
419
420                         /* 512 bytes (2**9) is the fake blocksize that must be
421                            used for this calculation */
422                         inode->i_blocks = (512 - 1 + le64_to_cpu(
423                                            pfindData->AllocationSize)) >> 9;
424                 }
425
426                 inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks);
427
428                 /* BB fill in uid and gid here? with help from winbind? 
429                    or retrieve from NTFS stream extended attribute */
430                 if (atomic_read(&cifsInfo->inUse) == 0) {
431                         inode->i_uid = cifs_sb->mnt_uid;
432                         inode->i_gid = cifs_sb->mnt_gid;
433                         /* set so we do not keep refreshing these fields with
434                            bad data after user has changed them in memory */
435                         atomic_set(&cifsInfo->inUse,1);
436                 }
437
438                 if (S_ISREG(inode->i_mode)) {
439                         cFYI(1, ("File inode"));
440                         inode->i_op = &cifs_file_inode_ops;
441                         if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
442                                 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
443                                         inode->i_fop =
444                                                 &cifs_file_direct_nobrl_ops;
445                                 else
446                                         inode->i_fop = &cifs_file_direct_ops;
447                         } else if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
448                                 inode->i_fop = &cifs_file_nobrl_ops;
449                         else /* not direct, send byte range locks */
450                                 inode->i_fop = &cifs_file_ops;
451
452                         inode->i_data.a_ops = &cifs_addr_ops;
453                         if(pTcon->ses->server->maxBuf < 
454                              4096 + MAX_CIFS_HDR_SIZE)
455                                 inode->i_data.a_ops->readpages = NULL;
456                 } else if (S_ISDIR(inode->i_mode)) {
457                         cFYI(1, ("Directory inode"));
458                         inode->i_op = &cifs_dir_inode_ops;
459                         inode->i_fop = &cifs_dir_ops;
460                 } else if (S_ISLNK(inode->i_mode)) {
461                         cFYI(1, ("Symbolic Link inode"));
462                         inode->i_op = &cifs_symlink_inode_ops;
463                 } else {
464                         init_special_inode(inode, inode->i_mode,
465                                            inode->i_rdev);
466                 }
467         }
468         kfree(buf);
469         return rc;
470 }
471
472 /* gets root inode */
473 void cifs_read_inode(struct inode *inode)
474 {
475         int xid;
476         struct cifs_sb_info *cifs_sb;
477
478         cifs_sb = CIFS_SB(inode->i_sb);
479         xid = GetXid();
480         if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
481                 cifs_get_inode_info_unix(&inode, "", inode->i_sb,xid);
482         else
483                 cifs_get_inode_info(&inode, "", NULL, inode->i_sb,xid);
484         /* can not call macro FreeXid here since in a void func */
485         _FreeXid(xid);
486 }
487
488 int cifs_unlink(struct inode *inode, struct dentry *direntry)
489 {
490         int rc = 0;
491         int xid;
492         struct cifs_sb_info *cifs_sb;
493         struct cifsTconInfo *pTcon;
494         char *full_path = NULL;
495         struct cifsInodeInfo *cifsInode;
496         FILE_BASIC_INFO *pinfo_buf;
497
498         cFYI(1, (" cifs_unlink, inode = 0x%p with ", inode));
499
500         xid = GetXid();
501
502         cifs_sb = CIFS_SB(inode->i_sb);
503         pTcon = cifs_sb->tcon;
504
505         /* Unlink can be called from rename so we can not grab the sem here
506            since we deadlock otherwise */
507 /*      down(&direntry->d_sb->s_vfs_rename_sem);*/
508         full_path = build_path_from_dentry(direntry);
509 /*      up(&direntry->d_sb->s_vfs_rename_sem);*/
510         if (full_path == NULL) {
511                 FreeXid(xid);
512                 return -ENOMEM;
513         }
514         rc = CIFSSMBDelFile(xid, pTcon, full_path, cifs_sb->local_nls,
515                         cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
516
517         if (!rc) {
518                 if (direntry->d_inode)
519                         direntry->d_inode->i_nlink--;
520         } else if (rc == -ENOENT) {
521                 d_drop(direntry);
522         } else if (rc == -ETXTBSY) {
523                 int oplock = FALSE;
524                 __u16 netfid;
525
526                 rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, DELETE,
527                                  CREATE_NOT_DIR | CREATE_DELETE_ON_CLOSE,
528                                  &netfid, &oplock, NULL, cifs_sb->local_nls,
529                                  cifs_sb->mnt_cifs_flags & 
530                                         CIFS_MOUNT_MAP_SPECIAL_CHR);
531                 if (rc==0) {
532                         CIFSSMBRenameOpenFile(xid, pTcon, netfid, NULL,
533                                               cifs_sb->local_nls, 
534                                               cifs_sb->mnt_cifs_flags & 
535                                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
536                         CIFSSMBClose(xid, pTcon, netfid);
537                         if (direntry->d_inode)
538                                 direntry->d_inode->i_nlink--;
539                 }
540         } else if (rc == -EACCES) {
541                 /* try only if r/o attribute set in local lookup data? */
542                 pinfo_buf = kmalloc(sizeof(FILE_BASIC_INFO), GFP_KERNEL);
543                 if (pinfo_buf) {
544                         memset(pinfo_buf, 0, sizeof(FILE_BASIC_INFO));
545                         /* ATTRS set to normal clears r/o bit */
546                         pinfo_buf->Attributes = cpu_to_le32(ATTR_NORMAL);
547                         if (!(pTcon->ses->flags & CIFS_SES_NT4))
548                                 rc = CIFSSMBSetTimes(xid, pTcon, full_path,
549                                                      pinfo_buf,
550                                                      cifs_sb->local_nls,
551                                                      cifs_sb->mnt_cifs_flags & 
552                                                         CIFS_MOUNT_MAP_SPECIAL_CHR);
553                         else
554                                 rc = -EOPNOTSUPP;
555
556                         if (rc == -EOPNOTSUPP) {
557                                 int oplock = FALSE;
558                                 __u16 netfid;
559                         /*      rc = CIFSSMBSetAttrLegacy(xid, pTcon,
560                                                           full_path,
561                                                           (__u16)ATTR_NORMAL,
562                                                           cifs_sb->local_nls); 
563                            For some strange reason it seems that NT4 eats the
564                            old setattr call without actually setting the
565                            attributes so on to the third attempted workaround
566                            */
567
568                         /* BB could scan to see if we already have it open
569                            and pass in pid of opener to function */
570                                 rc = CIFSSMBOpen(xid, pTcon, full_path,
571                                                  FILE_OPEN, SYNCHRONIZE |
572                                                  FILE_WRITE_ATTRIBUTES, 0,
573                                                  &netfid, &oplock, NULL,
574                                                  cifs_sb->local_nls,
575                                                  cifs_sb->mnt_cifs_flags & 
576                                                     CIFS_MOUNT_MAP_SPECIAL_CHR);
577                                 if (rc==0) {
578                                         rc = CIFSSMBSetFileTimes(xid, pTcon,
579                                                                  pinfo_buf,
580                                                                  netfid);
581                                         CIFSSMBClose(xid, pTcon, netfid);
582                                 }
583                         }
584                         kfree(pinfo_buf);
585                 }
586                 if (rc==0) {
587                         rc = CIFSSMBDelFile(xid, pTcon, full_path, 
588                                             cifs_sb->local_nls, 
589                                             cifs_sb->mnt_cifs_flags & 
590                                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
591                         if (!rc) {
592                                 if (direntry->d_inode)
593                                         direntry->d_inode->i_nlink--;
594                         } else if (rc == -ETXTBSY) {
595                                 int oplock = FALSE;
596                                 __u16 netfid;
597
598                                 rc = CIFSSMBOpen(xid, pTcon, full_path,
599                                                  FILE_OPEN, DELETE,
600                                                  CREATE_NOT_DIR |
601                                                  CREATE_DELETE_ON_CLOSE,
602                                                  &netfid, &oplock, NULL,
603                                                  cifs_sb->local_nls, 
604                                                  cifs_sb->mnt_cifs_flags & 
605                                                     CIFS_MOUNT_MAP_SPECIAL_CHR);
606                                 if (rc==0) {
607                                         CIFSSMBRenameOpenFile(xid, pTcon,
608                                                 netfid, NULL,
609                                                 cifs_sb->local_nls,
610                                                 cifs_sb->mnt_cifs_flags &
611                                                     CIFS_MOUNT_MAP_SPECIAL_CHR);
612                                         CIFSSMBClose(xid, pTcon, netfid);
613                                         if (direntry->d_inode)
614                                                 direntry->d_inode->i_nlink--;
615                                 }
616                         /* BB if rc = -ETXTBUSY goto the rename logic BB */
617                         }
618                 }
619         }
620         if (direntry->d_inode) {
621                 cifsInode = CIFS_I(direntry->d_inode);
622                 cifsInode->time = 0;    /* will force revalidate to get info
623                                            when needed */
624                 direntry->d_inode->i_ctime = current_fs_time(inode->i_sb);
625         }
626         inode->i_ctime = inode->i_mtime = current_fs_time(inode->i_sb);
627         cifsInode = CIFS_I(inode);
628         cifsInode->time = 0;    /* force revalidate of dir as well */
629
630         kfree(full_path);
631         FreeXid(xid);
632         return rc;
633 }
634
635 int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
636 {
637         int rc = 0;
638         int xid;
639         struct cifs_sb_info *cifs_sb;
640         struct cifsTconInfo *pTcon;
641         char *full_path = NULL;
642         struct inode *newinode = NULL;
643
644         cFYI(1, ("In cifs_mkdir, mode = 0x%x inode = 0x%p ", mode, inode));
645
646         xid = GetXid();
647
648         cifs_sb = CIFS_SB(inode->i_sb);
649         pTcon = cifs_sb->tcon;
650
651         down(&inode->i_sb->s_vfs_rename_sem);
652         full_path = build_path_from_dentry(direntry);
653         up(&inode->i_sb->s_vfs_rename_sem);
654         if (full_path == NULL) {
655                 FreeXid(xid);
656                 return -ENOMEM;
657         }
658         /* BB add setting the equivalent of mode via CreateX w/ACLs */
659         rc = CIFSSMBMkDir(xid, pTcon, full_path, cifs_sb->local_nls,
660                           cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
661         if (rc) {
662                 cFYI(1, ("cifs_mkdir returned 0x%x ", rc));
663                 d_drop(direntry);
664         } else {
665                 inode->i_nlink++;
666                 if (pTcon->ses->capabilities & CAP_UNIX)
667                         rc = cifs_get_inode_info_unix(&newinode, full_path,
668                                                       inode->i_sb,xid);
669                 else
670                         rc = cifs_get_inode_info(&newinode, full_path, NULL,
671                                                  inode->i_sb,xid);
672
673                 if (pTcon->nocase)
674                         direntry->d_op = &cifs_ci_dentry_ops;
675                 else
676                         direntry->d_op = &cifs_dentry_ops;
677                 d_instantiate(direntry, newinode);
678                 if (direntry->d_inode)
679                         direntry->d_inode->i_nlink = 2;
680                 if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
681                         if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
682                                 CIFSSMBUnixSetPerms(xid, pTcon, full_path,
683                                                     mode,
684                                                     (__u64)current->euid,
685                                                     (__u64)current->egid,
686                                                     0 /* dev_t */,
687                                                     cifs_sb->local_nls,
688                                                     cifs_sb->mnt_cifs_flags &
689                                                     CIFS_MOUNT_MAP_SPECIAL_CHR);
690                         } else {
691                                 CIFSSMBUnixSetPerms(xid, pTcon, full_path,
692                                                     mode, (__u64)-1,
693                                                     (__u64)-1, 0 /* dev_t */,
694                                                     cifs_sb->local_nls,
695                                                     cifs_sb->mnt_cifs_flags & 
696                                                     CIFS_MOUNT_MAP_SPECIAL_CHR);
697                         }
698                 else {
699                         /* BB to be implemented via Windows secrty descriptors
700                            eg CIFSSMBWinSetPerms(xid, pTcon, full_path, mode,
701                                                  -1, -1, local_nls); */
702                 }
703         }
704         kfree(full_path);
705         FreeXid(xid);
706         return rc;
707 }
708
709 int cifs_rmdir(struct inode *inode, struct dentry *direntry)
710 {
711         int rc = 0;
712         int xid;
713         struct cifs_sb_info *cifs_sb;
714         struct cifsTconInfo *pTcon;
715         char *full_path = NULL;
716         struct cifsInodeInfo *cifsInode;
717
718         cFYI(1, (" cifs_rmdir, inode = 0x%p with ", inode));
719
720         xid = GetXid();
721
722         cifs_sb = CIFS_SB(inode->i_sb);
723         pTcon = cifs_sb->tcon;
724
725         down(&inode->i_sb->s_vfs_rename_sem);
726         full_path = build_path_from_dentry(direntry);
727         up(&inode->i_sb->s_vfs_rename_sem);
728         if (full_path == NULL) {
729                 FreeXid(xid);
730                 return -ENOMEM;
731         }
732
733         rc = CIFSSMBRmDir(xid, pTcon, full_path, cifs_sb->local_nls,
734                           cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
735
736         if (!rc) {
737                 inode->i_nlink--;
738                 i_size_write(direntry->d_inode,0);
739                 direntry->d_inode->i_nlink = 0;
740         }
741
742         cifsInode = CIFS_I(direntry->d_inode);
743         cifsInode->time = 0;    /* force revalidate to go get info when
744                                    needed */
745         direntry->d_inode->i_ctime = inode->i_ctime = inode->i_mtime =
746                 current_fs_time(inode->i_sb);
747
748         kfree(full_path);
749         FreeXid(xid);
750         return rc;
751 }
752
753 int cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
754         struct inode *target_inode, struct dentry *target_direntry)
755 {
756         char *fromName;
757         char *toName;
758         struct cifs_sb_info *cifs_sb_source;
759         struct cifs_sb_info *cifs_sb_target;
760         struct cifsTconInfo *pTcon;
761         int xid;
762         int rc = 0;
763
764         xid = GetXid();
765
766         cifs_sb_target = CIFS_SB(target_inode->i_sb);
767         cifs_sb_source = CIFS_SB(source_inode->i_sb);
768         pTcon = cifs_sb_source->tcon;
769
770         if (pTcon != cifs_sb_target->tcon) {
771                 FreeXid(xid);
772                 return -EXDEV;  /* BB actually could be allowed if same server,
773                                    but different share.
774                                    Might eventually add support for this */
775         }
776
777         /* we already  have the rename sem so we do not need to grab it again
778            here to protect the path integrity */
779         fromName = build_path_from_dentry(source_direntry);
780         toName = build_path_from_dentry(target_direntry);
781         if ((fromName == NULL) || (toName == NULL)) {
782                 rc = -ENOMEM;
783                 goto cifs_rename_exit;
784         }
785
786         rc = CIFSSMBRename(xid, pTcon, fromName, toName,
787                            cifs_sb_source->local_nls,
788                            cifs_sb_source->mnt_cifs_flags &
789                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
790         if (rc == -EEXIST) {
791                 /* check if they are the same file because rename of hardlinked
792                    files is a noop */
793                 FILE_UNIX_BASIC_INFO *info_buf_source;
794                 FILE_UNIX_BASIC_INFO *info_buf_target;
795
796                 info_buf_source =
797                         kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
798                 if (info_buf_source != NULL) {
799                         info_buf_target = info_buf_source + 1;
800                         rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName,
801                                 info_buf_source, cifs_sb_source->local_nls, 
802                                 cifs_sb_source->mnt_cifs_flags &
803                                         CIFS_MOUNT_MAP_SPECIAL_CHR);
804                         if (rc == 0) {
805                                 rc = CIFSSMBUnixQPathInfo(xid, pTcon, toName,
806                                                 info_buf_target,
807                                                 cifs_sb_target->local_nls,
808                                                 /* remap based on source sb */
809                                                 cifs_sb_source->mnt_cifs_flags &
810                                                     CIFS_MOUNT_MAP_SPECIAL_CHR);
811                         }
812                         if ((rc == 0) &&
813                             (info_buf_source->UniqueId ==
814                              info_buf_target->UniqueId)) {
815                         /* do not rename since the files are hardlinked which
816                            is a noop */
817                         } else {
818                         /* we either can not tell the files are hardlinked
819                            (as with Windows servers) or files are not
820                            hardlinked so delete the target manually before
821                            renaming to follow POSIX rather than Windows
822                            semantics */
823                                 cifs_unlink(target_inode, target_direntry);
824                                 rc = CIFSSMBRename(xid, pTcon, fromName,
825                                                    toName,
826                                                    cifs_sb_source->local_nls,
827                                                    cifs_sb_source->mnt_cifs_flags
828                                                    & CIFS_MOUNT_MAP_SPECIAL_CHR);
829                         }
830                         kfree(info_buf_source);
831                 } /* if we can not get memory just leave rc as EEXIST */
832         }
833
834         if (rc) {
835                 cFYI(1, ("rename rc %d", rc));
836         }
837
838         if ((rc == -EIO) || (rc == -EEXIST)) {
839                 int oplock = FALSE;
840                 __u16 netfid;
841
842                 /* BB FIXME Is Generic Read correct for rename? */
843                 /* if renaming directory - we should not say CREATE_NOT_DIR,
844                    need to test renaming open directory, also GENERIC_READ
845                    might not right be right access to request */
846                 rc = CIFSSMBOpen(xid, pTcon, fromName, FILE_OPEN, GENERIC_READ,
847                                  CREATE_NOT_DIR, &netfid, &oplock, NULL,
848                                  cifs_sb_source->local_nls, 
849                                  cifs_sb_source->mnt_cifs_flags & 
850                                         CIFS_MOUNT_MAP_SPECIAL_CHR);
851                 if (rc==0) {
852                         CIFSSMBRenameOpenFile(xid, pTcon, netfid, toName,
853                                               cifs_sb_source->local_nls, 
854                                               cifs_sb_source->mnt_cifs_flags &
855                                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
856                         CIFSSMBClose(xid, pTcon, netfid);
857                 }
858         }
859
860 cifs_rename_exit:
861         kfree(fromName);
862         kfree(toName);
863         FreeXid(xid);
864         return rc;
865 }
866
867 int cifs_revalidate(struct dentry *direntry)
868 {
869         int xid;
870         int rc = 0;
871         char *full_path;
872         struct cifs_sb_info *cifs_sb;
873         struct cifsInodeInfo *cifsInode;
874         loff_t local_size;
875         struct timespec local_mtime;
876         int invalidate_inode = FALSE;
877
878         if (direntry->d_inode == NULL)
879                 return -ENOENT;
880
881         cifsInode = CIFS_I(direntry->d_inode);
882
883         if (cifsInode == NULL)
884                 return -ENOENT;
885
886         /* no sense revalidating inode info on file that no one can write */
887         if (CIFS_I(direntry->d_inode)->clientCanCacheRead)
888                 return rc;
889
890         xid = GetXid();
891
892         cifs_sb = CIFS_SB(direntry->d_sb);
893
894         /* can not safely grab the rename sem here if rename calls revalidate
895            since that would deadlock */
896         full_path = build_path_from_dentry(direntry);
897         if (full_path == NULL) {
898                 FreeXid(xid);
899                 return -ENOMEM;
900         }
901         cFYI(1, ("Revalidate: %s inode 0x%p count %d dentry: 0x%p d_time %ld "
902                  "jiffies %ld", full_path, direntry->d_inode,
903                  direntry->d_inode->i_count.counter, direntry,
904                  direntry->d_time, jiffies));
905
906         if (cifsInode->time == 0) {
907                 /* was set to zero previously to force revalidate */
908         } else if (time_before(jiffies, cifsInode->time + HZ) &&
909                    lookupCacheEnabled) {
910                 if ((S_ISREG(direntry->d_inode->i_mode) == 0) ||
911                     (direntry->d_inode->i_nlink == 1)) {
912                         kfree(full_path);
913                         FreeXid(xid);
914                         return rc;
915                 } else {
916                         cFYI(1, ("Have to revalidate file due to hardlinks"));
917                 }
918         }
919
920         /* save mtime and size */
921         local_mtime = direntry->d_inode->i_mtime;
922         local_size = direntry->d_inode->i_size;
923
924         if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) {
925                 rc = cifs_get_inode_info_unix(&direntry->d_inode, full_path,
926                                               direntry->d_sb,xid);
927                 if (rc) {
928                         cFYI(1, ("error on getting revalidate info %d", rc));
929 /*                      if (rc != -ENOENT)
930                                 rc = 0; */      /* BB should we cache info on
931                                                    certain errors? */
932                 }
933         } else {
934                 rc = cifs_get_inode_info(&direntry->d_inode, full_path, NULL,
935                                          direntry->d_sb,xid);
936                 if (rc) {
937                         cFYI(1, ("error on getting revalidate info %d", rc));
938 /*                      if (rc != -ENOENT)
939                                 rc = 0; */      /* BB should we cache info on
940                                                    certain errors? */
941                 }
942         }
943         /* should we remap certain errors, access denied?, to zero */
944
945         /* if not oplocked, we invalidate inode pages if mtime or file size
946            had changed on server */
947
948         if (timespec_equal(&local_mtime,&direntry->d_inode->i_mtime) && 
949             (local_size == direntry->d_inode->i_size)) {
950                 cFYI(1, ("cifs_revalidate - inode unchanged"));
951         } else {
952                 /* file may have changed on server */
953                 if (cifsInode->clientCanCacheRead) {
954                         /* no need to invalidate inode pages since we were the
955                            only ones who could have modified the file and the
956                            server copy is staler than ours */
957                 } else {
958                         invalidate_inode = TRUE;
959                 }
960         }
961
962         /* can not grab this sem since kernel filesys locking documentation
963            indicates i_sem may be taken by the kernel on lookup and rename
964            which could deadlock if we grab the i_sem here as well */
965 /*      down(&direntry->d_inode->i_sem);*/
966         /* need to write out dirty pages here  */
967         if (direntry->d_inode->i_mapping) {
968                 /* do we need to lock inode until after invalidate completes
969                    below? */
970                 filemap_fdatawrite(direntry->d_inode->i_mapping);
971         }
972         if (invalidate_inode) {
973                 if (direntry->d_inode->i_mapping)
974                         filemap_fdatawait(direntry->d_inode->i_mapping);
975                 /* may eventually have to do this for open files too */
976                 if (list_empty(&(cifsInode->openFileList))) {
977                         /* Has changed on server - flush read ahead pages */
978                         cFYI(1, ("Invalidating read ahead data on "
979                                  "closed file"));
980                         invalidate_remote_inode(direntry->d_inode);
981                 }
982         }
983 /*      up(&direntry->d_inode->i_sem); */
984         
985         kfree(full_path);
986         FreeXid(xid);
987         return rc;
988 }
989
990 int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
991         struct kstat *stat)
992 {
993         int err = cifs_revalidate(dentry);
994         if (!err)
995                 generic_fillattr(dentry->d_inode, stat);
996         return err;
997 }
998
999 static int cifs_truncate_page(struct address_space *mapping, loff_t from)
1000 {
1001         pgoff_t index = from >> PAGE_CACHE_SHIFT;
1002         unsigned offset = from & (PAGE_CACHE_SIZE - 1);
1003         struct page *page;
1004         char *kaddr;
1005         int rc = 0;
1006
1007         page = grab_cache_page(mapping, index);
1008         if (!page)
1009                 return -ENOMEM;
1010
1011         kaddr = kmap_atomic(page, KM_USER0);
1012         memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset);
1013         flush_dcache_page(page);
1014         kunmap_atomic(kaddr, KM_USER0);
1015         unlock_page(page);
1016         page_cache_release(page);
1017         return rc;
1018 }
1019
1020 int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
1021 {
1022         int xid;
1023         struct cifs_sb_info *cifs_sb;
1024         struct cifsTconInfo *pTcon;
1025         char *full_path = NULL;
1026         int rc = -EACCES;
1027         struct cifsFileInfo *open_file = NULL;
1028         FILE_BASIC_INFO time_buf;
1029         int set_time = FALSE;
1030         __u64 mode = 0xFFFFFFFFFFFFFFFFULL;
1031         __u64 uid = 0xFFFFFFFFFFFFFFFFULL;
1032         __u64 gid = 0xFFFFFFFFFFFFFFFFULL;
1033         struct cifsInodeInfo *cifsInode;
1034
1035         xid = GetXid();
1036
1037         cFYI(1, (" In cifs_setattr, name = %s attrs->iavalid 0x%x ",
1038                  direntry->d_name.name, attrs->ia_valid));
1039         cifs_sb = CIFS_SB(direntry->d_inode->i_sb);
1040         pTcon = cifs_sb->tcon;
1041
1042         down(&direntry->d_sb->s_vfs_rename_sem);
1043         full_path = build_path_from_dentry(direntry);
1044         up(&direntry->d_sb->s_vfs_rename_sem);
1045         if (full_path == NULL) {
1046                 FreeXid(xid);
1047                 return -ENOMEM;
1048         }
1049         cifsInode = CIFS_I(direntry->d_inode);
1050
1051         /* BB check if we need to refresh inode from server now ? BB */
1052
1053         /* need to flush data before changing file size on server */
1054         filemap_fdatawrite(direntry->d_inode->i_mapping);
1055         filemap_fdatawait(direntry->d_inode->i_mapping);
1056
1057         if (attrs->ia_valid & ATTR_SIZE) {
1058                 /* To avoid spurious oplock breaks from server, in the case of
1059                    inodes that we already have open, avoid doing path based
1060                    setting of file size if we can do it by handle.
1061                    This keeps our caching token (oplock) and avoids timeouts
1062                    when the local oplock break takes longer to flush
1063                    writebehind data than the SMB timeout for the SetPathInfo
1064                    request would allow */
1065                 open_file = find_writable_file(cifsInode);
1066                 if (open_file) {
1067                         __u16 nfid = open_file->netfid;
1068                         __u32 npid = open_file->pid;
1069                         rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size,
1070                                                 nfid, npid, FALSE);
1071                         atomic_dec(&open_file->wrtPending);
1072                         cFYI(1,("SetFSize for attrs rc = %d", rc));
1073                         if(rc == -EINVAL) {
1074                                 int bytes_written;
1075                                 rc = CIFSSMBWrite(xid, pTcon,
1076                                                   nfid, 0, attrs->ia_size,
1077                                                   &bytes_written, NULL, NULL,
1078                                                   1 /* 45 seconds */);
1079                                 cFYI(1,("Wrt seteof rc %d", rc));
1080                         }
1081                 }
1082                 if (rc != 0) {
1083                         /* Set file size by pathname rather than by handle
1084                            either because no valid, writeable file handle for
1085                            it was found or because there was an error setting
1086                            it by handle */
1087                         rc = CIFSSMBSetEOF(xid, pTcon, full_path,
1088                                            attrs->ia_size, FALSE,
1089                                            cifs_sb->local_nls, 
1090                                            cifs_sb->mnt_cifs_flags &
1091                                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
1092                         cFYI(1, ("SetEOF by path (setattrs) rc = %d", rc));
1093                         if(rc == -EINVAL) {
1094                                 __u16 netfid;
1095                                 int oplock = FALSE;
1096
1097                                 rc = SMBLegacyOpen(xid, pTcon, full_path,
1098                                         FILE_OPEN,
1099                                         SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
1100                                         CREATE_NOT_DIR, &netfid, &oplock,
1101                                         NULL, cifs_sb->local_nls,
1102                                         cifs_sb->mnt_cifs_flags &
1103                                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
1104                                 if (rc==0) {
1105                                         int bytes_written;
1106                                         rc = CIFSSMBWrite(xid, pTcon,
1107                                                         netfid, 0,
1108                                                         attrs->ia_size,
1109                                                         &bytes_written, NULL,
1110                                                         NULL, 1 /* 45 sec */);
1111                                         cFYI(1,("wrt seteof rc %d",rc));
1112                                         CIFSSMBClose(xid, pTcon, netfid);
1113                                 }
1114
1115                         }
1116                 }
1117
1118                 /* Server is ok setting allocation size implicitly - no need
1119                    to call:
1120                 CIFSSMBSetEOF(xid, pTcon, full_path, attrs->ia_size, TRUE,
1121                          cifs_sb->local_nls);
1122                    */
1123
1124                 if (rc == 0) {
1125                         rc = vmtruncate(direntry->d_inode, attrs->ia_size);
1126                         cifs_truncate_page(direntry->d_inode->i_mapping,
1127                                            direntry->d_inode->i_size);
1128                 } else 
1129                         goto cifs_setattr_exit;
1130         }
1131         if (attrs->ia_valid & ATTR_UID) {
1132                 cFYI(1, ("UID changed to %d", attrs->ia_uid));
1133                 uid = attrs->ia_uid;
1134         }
1135         if (attrs->ia_valid & ATTR_GID) {
1136                 cFYI(1, ("GID changed to %d", attrs->ia_gid));
1137                 gid = attrs->ia_gid;
1138         }
1139
1140         time_buf.Attributes = 0;
1141         if (attrs->ia_valid & ATTR_MODE) {
1142                 cFYI(1, ("Mode changed to 0x%x", attrs->ia_mode));
1143                 mode = attrs->ia_mode;
1144         }
1145
1146         if ((cifs_sb->tcon->ses->capabilities & CAP_UNIX)
1147             && (attrs->ia_valid & (ATTR_MODE | ATTR_GID | ATTR_UID)))
1148                 rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode, uid, gid,
1149                                          0 /* dev_t */, cifs_sb->local_nls,
1150                                          cifs_sb->mnt_cifs_flags & 
1151                                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
1152         else if (attrs->ia_valid & ATTR_MODE) {
1153                 if ((mode & S_IWUGO) == 0) /* not writeable */ {
1154                         if ((cifsInode->cifsAttrs & ATTR_READONLY) == 0)
1155                                 time_buf.Attributes =
1156                                         cpu_to_le32(cifsInode->cifsAttrs |
1157                                                     ATTR_READONLY);
1158                 } else if ((mode & S_IWUGO) == S_IWUGO) {
1159                         if (cifsInode->cifsAttrs & ATTR_READONLY)
1160                                 time_buf.Attributes =
1161                                         cpu_to_le32(cifsInode->cifsAttrs &
1162                                                     (~ATTR_READONLY));
1163                 }
1164                 /* BB to be implemented -
1165                    via Windows security descriptors or streams */
1166                 /* CIFSSMBWinSetPerms(xid, pTcon, full_path, mode, uid, gid,
1167                                       cifs_sb->local_nls); */
1168         }
1169
1170         if (attrs->ia_valid & ATTR_ATIME) {
1171                 set_time = TRUE;
1172                 time_buf.LastAccessTime =
1173                     cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime));
1174         } else
1175                 time_buf.LastAccessTime = 0;
1176
1177         if (attrs->ia_valid & ATTR_MTIME) {
1178                 set_time = TRUE;
1179                 time_buf.LastWriteTime =
1180                     cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime));
1181         } else
1182                 time_buf.LastWriteTime = 0;
1183         /* Do not set ctime explicitly unless other time
1184            stamps are changed explicitly (i.e. by utime()
1185            since we would then have a mix of client and
1186            server times */
1187            
1188         if (set_time && (attrs->ia_valid & ATTR_CTIME)) {
1189                 set_time = TRUE;
1190                 /* Although Samba throws this field away
1191                 it may be useful to Windows - but we do
1192                 not want to set ctime unless some other
1193                 timestamp is changing */
1194                 cFYI(1, ("CIFS - CTIME changed "));
1195                 time_buf.ChangeTime =
1196                     cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));
1197         } else
1198                 time_buf.ChangeTime = 0;
1199
1200         if (set_time || time_buf.Attributes) {
1201                 time_buf.CreationTime = 0;      /* do not change */
1202                 /* In the future we should experiment - try setting timestamps
1203                    via Handle (SetFileInfo) instead of by path */
1204                 if (!(pTcon->ses->flags & CIFS_SES_NT4))
1205                         rc = CIFSSMBSetTimes(xid, pTcon, full_path, &time_buf,
1206                                              cifs_sb->local_nls,
1207                                              cifs_sb->mnt_cifs_flags &
1208                                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
1209                 else
1210                         rc = -EOPNOTSUPP;
1211
1212                 if (rc == -EOPNOTSUPP) {
1213                         int oplock = FALSE;
1214                         __u16 netfid;
1215
1216                         cFYI(1, ("calling SetFileInfo since SetPathInfo for "
1217                                  "times not supported by this server"));
1218                         /* BB we could scan to see if we already have it open
1219                            and pass in pid of opener to function */
1220                         rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN,
1221                                          SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
1222                                          CREATE_NOT_DIR, &netfid, &oplock,
1223                                          NULL, cifs_sb->local_nls,
1224                                          cifs_sb->mnt_cifs_flags &
1225                                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
1226                         if (rc==0) {
1227                                 rc = CIFSSMBSetFileTimes(xid, pTcon, &time_buf,
1228                                                          netfid);
1229                                 CIFSSMBClose(xid, pTcon, netfid);
1230                         } else {
1231                         /* BB For even older servers we could convert time_buf
1232                            into old DOS style which uses two second
1233                            granularity */
1234
1235                         /* rc = CIFSSMBSetTimesLegacy(xid, pTcon, full_path,
1236                                         &time_buf, cifs_sb->local_nls); */
1237                         }
1238                 }
1239                 /* Even if error on time set, no sense failing the call if
1240                 the server would set the time to a reasonable value anyway,
1241                 and this check ensures that we are not being called from
1242                 sys_utimes in which case we ought to fail the call back to
1243                 the user when the server rejects the call */
1244                 if((rc) && (attrs->ia_valid &&
1245                          (ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE)))
1246                         rc = 0;
1247         }
1248
1249         /* do not need local check to inode_check_ok since the server does
1250            that */
1251         if (!rc)
1252                 rc = inode_setattr(direntry->d_inode, attrs);
1253 cifs_setattr_exit:
1254         kfree(full_path);
1255         FreeXid(xid);
1256         return rc;
1257 }
1258
1259 void cifs_delete_inode(struct inode *inode)
1260 {
1261         cFYI(1, ("In cifs_delete_inode, inode = 0x%p ", inode));
1262         /* may have to add back in if and when safe distributed caching of
1263            directories added e.g. via FindNotify */
1264 }