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