diff options
Diffstat (limited to 'fs/cifs/inode.c')
-rw-r--r-- | fs/cifs/inode.c | 573 |
1 files changed, 306 insertions, 267 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index fcbdbb6ad7bf..2e904bd111c8 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
@@ -161,118 +161,115 @@ static void cifs_unix_info_to_inode(struct inode *inode, | |||
161 | spin_unlock(&inode->i_lock); | 161 | spin_unlock(&inode->i_lock); |
162 | } | 162 | } |
163 | 163 | ||
164 | static const unsigned char *cifs_get_search_path(struct cifs_sb_info *cifs_sb, | ||
165 | const char *search_path) | ||
166 | { | ||
167 | int tree_len; | ||
168 | int path_len; | ||
169 | int i; | ||
170 | char *tmp_path; | ||
171 | struct cifsTconInfo *pTcon = cifs_sb->tcon; | ||
172 | |||
173 | if (!(pTcon->Flags & SMB_SHARE_IS_IN_DFS)) | ||
174 | return search_path; | ||
175 | |||
176 | /* use full path name for working with DFS */ | ||
177 | tree_len = strnlen(pTcon->treeName, MAX_TREE_SIZE + 1); | ||
178 | path_len = strnlen(search_path, MAX_PATHCONF); | ||
179 | 164 | ||
180 | tmp_path = kmalloc(tree_len+path_len+1, GFP_KERNEL); | 165 | /* |
181 | if (tmp_path == NULL) | 166 | * Needed to setup inode data for the directory which is the |
182 | return search_path; | 167 | * junction to the new submount (ie to setup the fake directory |
168 | * which represents a DFS referral) | ||
169 | */ | ||
170 | static void fill_fake_finddataunix(FILE_UNIX_BASIC_INFO *pfnd_dat, | ||
171 | struct super_block *sb) | ||
172 | { | ||
173 | struct inode *pinode = NULL; | ||
174 | |||
175 | memset(pfnd_dat, 0, sizeof(FILE_UNIX_BASIC_INFO)); | ||
176 | |||
177 | /* __le64 pfnd_dat->EndOfFile = cpu_to_le64(0); | ||
178 | __le64 pfnd_dat->NumOfBytes = cpu_to_le64(0); | ||
179 | __u64 UniqueId = 0; */ | ||
180 | pfnd_dat->LastStatusChange = | ||
181 | cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); | ||
182 | pfnd_dat->LastAccessTime = | ||
183 | cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); | ||
184 | pfnd_dat->LastModificationTime = | ||
185 | cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); | ||
186 | pfnd_dat->Type = cpu_to_le32(UNIX_DIR); | ||
187 | pfnd_dat->Permissions = cpu_to_le64(S_IXUGO | S_IRWXU); | ||
188 | pfnd_dat->Nlinks = cpu_to_le64(2); | ||
189 | if (sb->s_root) | ||
190 | pinode = sb->s_root->d_inode; | ||
191 | if (pinode == NULL) | ||
192 | return; | ||
183 | 193 | ||
184 | strncpy(tmp_path, pTcon->treeName, tree_len); | 194 | /* fill in default values for the remaining based on root |
185 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) | 195 | inode since we can not query the server for this inode info */ |
186 | for (i = 0; i < tree_len; i++) { | 196 | pfnd_dat->DevMajor = cpu_to_le64(MAJOR(pinode->i_rdev)); |
187 | if (tmp_path[i] == '\\') | 197 | pfnd_dat->DevMinor = cpu_to_le64(MINOR(pinode->i_rdev)); |
188 | tmp_path[i] = '/'; | 198 | pfnd_dat->Uid = cpu_to_le64(pinode->i_uid); |
189 | } | 199 | pfnd_dat->Gid = cpu_to_le64(pinode->i_gid); |
190 | strncpy(tmp_path+tree_len, search_path, path_len); | ||
191 | tmp_path[tree_len+path_len] = 0; | ||
192 | return tmp_path; | ||
193 | } | 200 | } |
194 | 201 | ||
195 | int cifs_get_inode_info_unix(struct inode **pinode, | 202 | int cifs_get_inode_info_unix(struct inode **pinode, |
196 | const unsigned char *search_path, struct super_block *sb, int xid) | 203 | const unsigned char *full_path, struct super_block *sb, int xid) |
197 | { | 204 | { |
198 | int rc = 0; | 205 | int rc = 0; |
199 | FILE_UNIX_BASIC_INFO findData; | 206 | FILE_UNIX_BASIC_INFO find_data; |
200 | struct cifsTconInfo *pTcon; | 207 | struct cifsTconInfo *pTcon; |
201 | struct inode *inode; | 208 | struct inode *inode; |
202 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); | 209 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); |
203 | const unsigned char *full_path; | ||
204 | bool is_dfs_referral = false; | 210 | bool is_dfs_referral = false; |
211 | struct cifsInodeInfo *cifsInfo; | ||
212 | __u64 num_of_bytes; | ||
213 | __u64 end_of_file; | ||
205 | 214 | ||
206 | pTcon = cifs_sb->tcon; | 215 | pTcon = cifs_sb->tcon; |
207 | cFYI(1, ("Getting info on %s", search_path)); | 216 | cFYI(1, ("Getting info on %s", full_path)); |
208 | |||
209 | full_path = cifs_get_search_path(cifs_sb, search_path); | ||
210 | 217 | ||
211 | try_again_CIFSSMBUnixQPathInfo: | ||
212 | /* could have done a find first instead but this returns more info */ | 218 | /* could have done a find first instead but this returns more info */ |
213 | rc = CIFSSMBUnixQPathInfo(xid, pTcon, full_path, &findData, | 219 | rc = CIFSSMBUnixQPathInfo(xid, pTcon, full_path, &find_data, |
214 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & | 220 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & |
215 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 221 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
216 | /* dump_mem("\nUnixQPathInfo return data", &findData, | 222 | if (rc == -EREMOTE && !is_dfs_referral) { |
217 | sizeof(findData)); */ | 223 | is_dfs_referral = true; |
218 | if (rc) { | 224 | cFYI(DBG2, ("DFS ref")); |
219 | if (rc == -EREMOTE && !is_dfs_referral) { | 225 | /* for DFS, server does not give us real inode data */ |
220 | is_dfs_referral = true; | 226 | fill_fake_finddataunix(&find_data, sb); |
221 | if (full_path != search_path) { | 227 | rc = 0; |
222 | kfree(full_path); | 228 | } else if (rc) |
223 | full_path = search_path; | ||
224 | } | ||
225 | goto try_again_CIFSSMBUnixQPathInfo; | ||
226 | } | ||
227 | goto cgiiu_exit; | 229 | goto cgiiu_exit; |
228 | } else { | ||
229 | struct cifsInodeInfo *cifsInfo; | ||
230 | __u64 num_of_bytes = le64_to_cpu(findData.NumOfBytes); | ||
231 | __u64 end_of_file = le64_to_cpu(findData.EndOfFile); | ||
232 | 230 | ||
233 | /* get new inode */ | 231 | num_of_bytes = le64_to_cpu(find_data.NumOfBytes); |
234 | if (*pinode == NULL) { | 232 | end_of_file = le64_to_cpu(find_data.EndOfFile); |
235 | *pinode = new_inode(sb); | ||
236 | if (*pinode == NULL) { | ||
237 | rc = -ENOMEM; | ||
238 | goto cgiiu_exit; | ||
239 | } | ||
240 | /* Is an i_ino of zero legal? */ | ||
241 | /* Are there sanity checks we can use to ensure that | ||
242 | the server is really filling in that field? */ | ||
243 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { | ||
244 | (*pinode)->i_ino = | ||
245 | (unsigned long)findData.UniqueId; | ||
246 | } /* note ino incremented to unique num in new_inode */ | ||
247 | if (sb->s_flags & MS_NOATIME) | ||
248 | (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME; | ||
249 | 233 | ||
250 | insert_inode_hash(*pinode); | 234 | /* get new inode */ |
235 | if (*pinode == NULL) { | ||
236 | *pinode = new_inode(sb); | ||
237 | if (*pinode == NULL) { | ||
238 | rc = -ENOMEM; | ||
239 | goto cgiiu_exit; | ||
251 | } | 240 | } |
241 | /* Is an i_ino of zero legal? */ | ||
242 | /* note ino incremented to unique num in new_inode */ | ||
243 | /* Are there sanity checks we can use to ensure that | ||
244 | the server is really filling in that field? */ | ||
245 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) | ||
246 | (*pinode)->i_ino = (unsigned long)find_data.UniqueId; | ||
252 | 247 | ||
253 | inode = *pinode; | 248 | if (sb->s_flags & MS_NOATIME) |
254 | cifsInfo = CIFS_I(inode); | 249 | (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME; |
255 | 250 | ||
256 | cFYI(1, ("Old time %ld", cifsInfo->time)); | 251 | insert_inode_hash(*pinode); |
257 | cifsInfo->time = jiffies; | 252 | } |
258 | cFYI(1, ("New time %ld", cifsInfo->time)); | ||
259 | /* this is ok to set on every inode revalidate */ | ||
260 | atomic_set(&cifsInfo->inUse, 1); | ||
261 | 253 | ||
262 | cifs_unix_info_to_inode(inode, &findData, 0); | 254 | inode = *pinode; |
255 | cifsInfo = CIFS_I(inode); | ||
263 | 256 | ||
257 | cFYI(1, ("Old time %ld", cifsInfo->time)); | ||
258 | cifsInfo->time = jiffies; | ||
259 | cFYI(1, ("New time %ld", cifsInfo->time)); | ||
260 | /* this is ok to set on every inode revalidate */ | ||
261 | atomic_set(&cifsInfo->inUse, 1); | ||
264 | 262 | ||
265 | if (num_of_bytes < end_of_file) | 263 | cifs_unix_info_to_inode(inode, &find_data, 0); |
266 | cFYI(1, ("allocation size less than end of file")); | ||
267 | cFYI(1, ("Size %ld and blocks %llu", | ||
268 | (unsigned long) inode->i_size, | ||
269 | (unsigned long long)inode->i_blocks)); | ||
270 | 264 | ||
271 | cifs_set_ops(inode, is_dfs_referral); | 265 | if (num_of_bytes < end_of_file) |
272 | } | 266 | cFYI(1, ("allocation size less than end of file")); |
267 | cFYI(1, ("Size %ld and blocks %llu", | ||
268 | (unsigned long) inode->i_size, | ||
269 | (unsigned long long)inode->i_blocks)); | ||
270 | |||
271 | cifs_set_ops(inode, is_dfs_referral); | ||
273 | cgiiu_exit: | 272 | cgiiu_exit: |
274 | if (full_path != search_path) | ||
275 | kfree(full_path); | ||
276 | return rc; | 273 | return rc; |
277 | } | 274 | } |
278 | 275 | ||
@@ -379,21 +376,52 @@ static int get_sfu_mode(struct inode *inode, | |||
379 | #endif | 376 | #endif |
380 | } | 377 | } |
381 | 378 | ||
379 | /* | ||
380 | * Needed to setup inode data for the directory which is the | ||
381 | * junction to the new submount (ie to setup the fake directory | ||
382 | * which represents a DFS referral) | ||
383 | */ | ||
384 | static void fill_fake_finddata(FILE_ALL_INFO *pfnd_dat, | ||
385 | struct super_block *sb) | ||
386 | { | ||
387 | memset(pfnd_dat, 0, sizeof(FILE_ALL_INFO)); | ||
388 | |||
389 | /* __le64 pfnd_dat->AllocationSize = cpu_to_le64(0); | ||
390 | __le64 pfnd_dat->EndOfFile = cpu_to_le64(0); | ||
391 | __u8 pfnd_dat->DeletePending = 0; | ||
392 | __u8 pfnd_data->Directory = 0; | ||
393 | __le32 pfnd_dat->EASize = 0; | ||
394 | __u64 pfnd_dat->IndexNumber = 0; | ||
395 | __u64 pfnd_dat->IndexNumber1 = 0; */ | ||
396 | pfnd_dat->CreationTime = | ||
397 | cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); | ||
398 | pfnd_dat->LastAccessTime = | ||
399 | cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); | ||
400 | pfnd_dat->LastWriteTime = | ||
401 | cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); | ||
402 | pfnd_dat->ChangeTime = | ||
403 | cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); | ||
404 | pfnd_dat->Attributes = cpu_to_le32(ATTR_DIRECTORY); | ||
405 | pfnd_dat->NumberOfLinks = cpu_to_le32(2); | ||
406 | } | ||
407 | |||
382 | int cifs_get_inode_info(struct inode **pinode, | 408 | int cifs_get_inode_info(struct inode **pinode, |
383 | const unsigned char *search_path, FILE_ALL_INFO *pfindData, | 409 | const unsigned char *full_path, FILE_ALL_INFO *pfindData, |
384 | struct super_block *sb, int xid, const __u16 *pfid) | 410 | struct super_block *sb, int xid, const __u16 *pfid) |
385 | { | 411 | { |
386 | int rc = 0; | 412 | int rc = 0; |
413 | __u32 attr; | ||
414 | struct cifsInodeInfo *cifsInfo; | ||
387 | struct cifsTconInfo *pTcon; | 415 | struct cifsTconInfo *pTcon; |
388 | struct inode *inode; | 416 | struct inode *inode; |
389 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); | 417 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); |
390 | const unsigned char *full_path = NULL; | ||
391 | char *buf = NULL; | 418 | char *buf = NULL; |
392 | bool adjustTZ = false; | 419 | bool adjustTZ = false; |
393 | bool is_dfs_referral = false; | 420 | bool is_dfs_referral = false; |
421 | umode_t default_mode; | ||
394 | 422 | ||
395 | pTcon = cifs_sb->tcon; | 423 | pTcon = cifs_sb->tcon; |
396 | cFYI(1, ("Getting info on %s", search_path)); | 424 | cFYI(1, ("Getting info on %s", full_path)); |
397 | 425 | ||
398 | if ((pfindData == NULL) && (*pinode != NULL)) { | 426 | if ((pfindData == NULL) && (*pinode != NULL)) { |
399 | if (CIFS_I(*pinode)->clientCanCacheRead) { | 427 | if (CIFS_I(*pinode)->clientCanCacheRead) { |
@@ -409,9 +437,6 @@ int cifs_get_inode_info(struct inode **pinode, | |||
409 | return -ENOMEM; | 437 | return -ENOMEM; |
410 | pfindData = (FILE_ALL_INFO *)buf; | 438 | pfindData = (FILE_ALL_INFO *)buf; |
411 | 439 | ||
412 | full_path = cifs_get_search_path(cifs_sb, search_path); | ||
413 | |||
414 | try_again_CIFSSMBQPathInfo: | ||
415 | /* could do find first instead but this returns more info */ | 440 | /* could do find first instead but this returns more info */ |
416 | rc = CIFSSMBQPathInfo(xid, pTcon, full_path, pfindData, | 441 | rc = CIFSSMBQPathInfo(xid, pTcon, full_path, pfindData, |
417 | 0 /* not legacy */, | 442 | 0 /* not legacy */, |
@@ -429,178 +454,163 @@ try_again_CIFSSMBQPathInfo: | |||
429 | } | 454 | } |
430 | } | 455 | } |
431 | /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */ | 456 | /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */ |
432 | if (rc) { | 457 | if (rc == -EREMOTE) { |
433 | if (rc == -EREMOTE && !is_dfs_referral) { | 458 | is_dfs_referral = true; |
434 | is_dfs_referral = true; | 459 | fill_fake_finddata(pfindData, sb); |
435 | if (full_path != search_path) { | 460 | rc = 0; |
436 | kfree(full_path); | 461 | } else if (rc) |
437 | full_path = search_path; | ||
438 | } | ||
439 | goto try_again_CIFSSMBQPathInfo; | ||
440 | } | ||
441 | goto cgii_exit; | 462 | goto cgii_exit; |
442 | } else { | ||
443 | struct cifsInodeInfo *cifsInfo; | ||
444 | __u32 attr = le32_to_cpu(pfindData->Attributes); | ||
445 | |||
446 | /* get new inode */ | ||
447 | if (*pinode == NULL) { | ||
448 | *pinode = new_inode(sb); | ||
449 | if (*pinode == NULL) { | ||
450 | rc = -ENOMEM; | ||
451 | goto cgii_exit; | ||
452 | } | ||
453 | /* Is an i_ino of zero legal? Can we use that to check | ||
454 | if the server supports returning inode numbers? Are | ||
455 | there other sanity checks we can use to ensure that | ||
456 | the server is really filling in that field? */ | ||
457 | |||
458 | /* We can not use the IndexNumber field by default from | ||
459 | Windows or Samba (in ALL_INFO buf) but we can request | ||
460 | it explicitly. It may not be unique presumably if | ||
461 | the server has multiple devices mounted under one | ||
462 | share */ | ||
463 | |||
464 | /* There may be higher info levels that work but are | ||
465 | there Windows server or network appliances for which | ||
466 | IndexNumber field is not guaranteed unique? */ | ||
467 | 463 | ||
468 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { | 464 | attr = le32_to_cpu(pfindData->Attributes); |
469 | int rc1 = 0; | ||
470 | __u64 inode_num; | ||
471 | 465 | ||
472 | rc1 = CIFSGetSrvInodeNumber(xid, pTcon, | 466 | /* get new inode */ |
473 | search_path, &inode_num, | 467 | if (*pinode == NULL) { |
468 | *pinode = new_inode(sb); | ||
469 | if (*pinode == NULL) { | ||
470 | rc = -ENOMEM; | ||
471 | goto cgii_exit; | ||
472 | } | ||
473 | /* Is an i_ino of zero legal? Can we use that to check | ||
474 | if the server supports returning inode numbers? Are | ||
475 | there other sanity checks we can use to ensure that | ||
476 | the server is really filling in that field? */ | ||
477 | |||
478 | /* We can not use the IndexNumber field by default from | ||
479 | Windows or Samba (in ALL_INFO buf) but we can request | ||
480 | it explicitly. It may not be unique presumably if | ||
481 | the server has multiple devices mounted under one share */ | ||
482 | |||
483 | /* There may be higher info levels that work but are | ||
484 | there Windows server or network appliances for which | ||
485 | IndexNumber field is not guaranteed unique? */ | ||
486 | |||
487 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { | ||
488 | int rc1 = 0; | ||
489 | __u64 inode_num; | ||
490 | |||
491 | rc1 = CIFSGetSrvInodeNumber(xid, pTcon, | ||
492 | full_path, &inode_num, | ||
474 | cifs_sb->local_nls, | 493 | cifs_sb->local_nls, |
475 | cifs_sb->mnt_cifs_flags & | 494 | cifs_sb->mnt_cifs_flags & |
476 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 495 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
477 | if (rc1) { | 496 | if (rc1) { |
478 | cFYI(1, ("GetSrvInodeNum rc %d", rc1)); | 497 | cFYI(1, ("GetSrvInodeNum rc %d", rc1)); |
479 | /* BB EOPNOSUPP disable SERVER_INUM? */ | 498 | /* BB EOPNOSUPP disable SERVER_INUM? */ |
480 | } else /* do we need cast or hash to ino? */ | 499 | } else /* do we need cast or hash to ino? */ |
481 | (*pinode)->i_ino = inode_num; | 500 | (*pinode)->i_ino = inode_num; |
482 | } /* else ino incremented to unique num in new_inode*/ | 501 | } /* else ino incremented to unique num in new_inode*/ |
483 | if (sb->s_flags & MS_NOATIME) | 502 | if (sb->s_flags & MS_NOATIME) |
484 | (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME; | 503 | (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME; |
485 | insert_inode_hash(*pinode); | 504 | insert_inode_hash(*pinode); |
486 | } | 505 | } |
487 | inode = *pinode; | 506 | inode = *pinode; |
488 | cifsInfo = CIFS_I(inode); | 507 | cifsInfo = CIFS_I(inode); |
489 | cifsInfo->cifsAttrs = attr; | 508 | cifsInfo->cifsAttrs = attr; |
490 | cFYI(1, ("Old time %ld", cifsInfo->time)); | 509 | cFYI(1, ("Old time %ld", cifsInfo->time)); |
491 | cifsInfo->time = jiffies; | 510 | cifsInfo->time = jiffies; |
492 | cFYI(1, ("New time %ld", cifsInfo->time)); | 511 | cFYI(1, ("New time %ld", cifsInfo->time)); |
493 | 512 | ||
494 | /* blksize needs to be multiple of two. So safer to default to | 513 | /* blksize needs to be multiple of two. So safer to default to |
495 | blksize and blkbits set in superblock so 2**blkbits and blksize | 514 | blksize and blkbits set in superblock so 2**blkbits and blksize |
496 | will match rather than setting to: | 515 | will match rather than setting to: |
497 | (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/ | 516 | (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/ |
498 | 517 | ||
499 | /* Linux can not store file creation time so ignore it */ | 518 | /* Linux can not store file creation time so ignore it */ |
500 | if (pfindData->LastAccessTime) | 519 | if (pfindData->LastAccessTime) |
501 | inode->i_atime = cifs_NTtimeToUnix | 520 | inode->i_atime = cifs_NTtimeToUnix |
502 | (le64_to_cpu(pfindData->LastAccessTime)); | 521 | (le64_to_cpu(pfindData->LastAccessTime)); |
503 | else /* do not need to use current_fs_time - time not stored */ | 522 | else /* do not need to use current_fs_time - time not stored */ |
504 | inode->i_atime = CURRENT_TIME; | 523 | inode->i_atime = CURRENT_TIME; |
505 | inode->i_mtime = | 524 | inode->i_mtime = |
506 | cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime)); | 525 | cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime)); |
507 | inode->i_ctime = | 526 | inode->i_ctime = |
508 | cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime)); | 527 | cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime)); |
509 | cFYI(0, ("Attributes came in as 0x%x", attr)); | 528 | cFYI(DBG2, ("Attributes came in as 0x%x", attr)); |
510 | if (adjustTZ && (pTcon->ses) && (pTcon->ses->server)) { | 529 | if (adjustTZ && (pTcon->ses) && (pTcon->ses->server)) { |
511 | inode->i_ctime.tv_sec += pTcon->ses->server->timeAdj; | 530 | inode->i_ctime.tv_sec += pTcon->ses->server->timeAdj; |
512 | inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj; | 531 | inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj; |
513 | } | 532 | } |
514 | 533 | ||
515 | /* set default mode. will override for dirs below */ | 534 | /* get default inode mode */ |
516 | if (atomic_read(&cifsInfo->inUse) == 0) | 535 | if (attr & ATTR_DIRECTORY) |
517 | /* new inode, can safely set these fields */ | 536 | default_mode = cifs_sb->mnt_dir_mode; |
518 | inode->i_mode = cifs_sb->mnt_file_mode; | 537 | else |
519 | else /* since we set the inode type below we need to mask off | 538 | default_mode = cifs_sb->mnt_file_mode; |
520 | to avoid strange results if type changes and both | 539 | |
521 | get orred in */ | 540 | /* set permission bits */ |
541 | if (atomic_read(&cifsInfo->inUse) == 0 || | ||
542 | (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) | ||
543 | inode->i_mode = default_mode; | ||
544 | else { | ||
545 | /* just reenable write bits if !ATTR_READONLY */ | ||
546 | if ((inode->i_mode & S_IWUGO) == 0 && | ||
547 | (attr & ATTR_READONLY) == 0) | ||
548 | inode->i_mode |= (S_IWUGO & default_mode); | ||
522 | inode->i_mode &= ~S_IFMT; | 549 | inode->i_mode &= ~S_IFMT; |
523 | /* if (attr & ATTR_REPARSE) */ | 550 | } |
524 | /* We no longer handle these as symlinks because we could not | 551 | /* clear write bits if ATTR_READONLY is set */ |
525 | follow them due to the absolute path with drive letter */ | 552 | if (attr & ATTR_READONLY) |
526 | if (attr & ATTR_DIRECTORY) { | 553 | inode->i_mode &= ~S_IWUGO; |
527 | /* override default perms since we do not do byte range locking | 554 | |
528 | on dirs */ | 555 | /* set inode type */ |
529 | inode->i_mode = cifs_sb->mnt_dir_mode; | 556 | if ((attr & ATTR_SYSTEM) && |
530 | inode->i_mode |= S_IFDIR; | 557 | (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) { |
531 | } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && | 558 | /* no need to fix endianness on 0 */ |
532 | (cifsInfo->cifsAttrs & ATTR_SYSTEM) && | 559 | if (pfindData->EndOfFile == 0) |
533 | /* No need to le64 convert size of zero */ | ||
534 | (pfindData->EndOfFile == 0)) { | ||
535 | inode->i_mode = cifs_sb->mnt_file_mode; | ||
536 | inode->i_mode |= S_IFIFO; | 560 | inode->i_mode |= S_IFIFO; |
537 | /* BB Finish for SFU style symlinks and devices */ | 561 | else if (decode_sfu_inode(inode, |
538 | } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && | 562 | le64_to_cpu(pfindData->EndOfFile), |
539 | (cifsInfo->cifsAttrs & ATTR_SYSTEM)) { | 563 | full_path, cifs_sb, xid)) |
540 | if (decode_sfu_inode(inode, | 564 | cFYI(1, ("unknown SFU file type\n")); |
541 | le64_to_cpu(pfindData->EndOfFile), | 565 | } else { |
542 | search_path, | 566 | if (attr & ATTR_DIRECTORY) |
543 | cifs_sb, xid)) | 567 | inode->i_mode |= S_IFDIR; |
544 | cFYI(1, ("Unrecognized sfu inode type")); | 568 | else |
545 | |||
546 | cFYI(1, ("sfu mode 0%o", inode->i_mode)); | ||
547 | } else { | ||
548 | inode->i_mode |= S_IFREG; | 569 | inode->i_mode |= S_IFREG; |
549 | /* treat the dos attribute of read-only as read-only | 570 | } |
550 | mode e.g. 555 */ | ||
551 | if (cifsInfo->cifsAttrs & ATTR_READONLY) | ||
552 | inode->i_mode &= ~(S_IWUGO); | ||
553 | else if ((inode->i_mode & S_IWUGO) == 0) | ||
554 | /* the ATTR_READONLY flag may have been */ | ||
555 | /* changed on server -- set any w bits */ | ||
556 | /* allowed by mnt_file_mode */ | ||
557 | inode->i_mode |= (S_IWUGO & | ||
558 | cifs_sb->mnt_file_mode); | ||
559 | /* BB add code here - | ||
560 | validate if device or weird share or device type? */ | ||
561 | } | ||
562 | 571 | ||
563 | spin_lock(&inode->i_lock); | 572 | spin_lock(&inode->i_lock); |
564 | if (is_size_safe_to_change(cifsInfo, | 573 | if (is_size_safe_to_change(cifsInfo, |
565 | le64_to_cpu(pfindData->EndOfFile))) { | 574 | le64_to_cpu(pfindData->EndOfFile))) { |
566 | /* can not safely shrink the file size here if the | 575 | /* can not safely shrink the file size here if the |
567 | client is writing to it due to potential races */ | 576 | client is writing to it due to potential races */ |
568 | i_size_write(inode, le64_to_cpu(pfindData->EndOfFile)); | 577 | i_size_write(inode, le64_to_cpu(pfindData->EndOfFile)); |
569 | 578 | ||
570 | /* 512 bytes (2**9) is the fake blocksize that must be | 579 | /* 512 bytes (2**9) is the fake blocksize that must be |
571 | used for this calculation */ | 580 | used for this calculation */ |
572 | inode->i_blocks = (512 - 1 + le64_to_cpu( | 581 | inode->i_blocks = (512 - 1 + le64_to_cpu( |
573 | pfindData->AllocationSize)) >> 9; | 582 | pfindData->AllocationSize)) >> 9; |
574 | } | 583 | } |
575 | spin_unlock(&inode->i_lock); | 584 | spin_unlock(&inode->i_lock); |
576 | 585 | ||
577 | inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks); | 586 | inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks); |
578 | 587 | ||
579 | /* BB fill in uid and gid here? with help from winbind? | 588 | /* BB fill in uid and gid here? with help from winbind? |
580 | or retrieve from NTFS stream extended attribute */ | 589 | or retrieve from NTFS stream extended attribute */ |
581 | #ifdef CONFIG_CIFS_EXPERIMENTAL | 590 | #ifdef CONFIG_CIFS_EXPERIMENTAL |
582 | /* fill in 0777 bits from ACL */ | 591 | /* fill in 0777 bits from ACL */ |
583 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { | 592 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { |
584 | cFYI(1, ("Getting mode bits from ACL")); | 593 | cFYI(1, ("Getting mode bits from ACL")); |
585 | acl_to_uid_mode(inode, search_path, pfid); | 594 | acl_to_uid_mode(inode, full_path, pfid); |
586 | } | 595 | } |
587 | #endif | 596 | #endif |
588 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { | 597 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { |
589 | /* fill in remaining high mode bits e.g. SUID, VTX */ | 598 | /* fill in remaining high mode bits e.g. SUID, VTX */ |
590 | get_sfu_mode(inode, search_path, cifs_sb, xid); | 599 | get_sfu_mode(inode, full_path, cifs_sb, xid); |
591 | } else if (atomic_read(&cifsInfo->inUse) == 0) { | 600 | } else if (atomic_read(&cifsInfo->inUse) == 0) { |
592 | inode->i_uid = cifs_sb->mnt_uid; | 601 | inode->i_uid = cifs_sb->mnt_uid; |
593 | inode->i_gid = cifs_sb->mnt_gid; | 602 | inode->i_gid = cifs_sb->mnt_gid; |
594 | /* set so we do not keep refreshing these fields with | 603 | /* set so we do not keep refreshing these fields with |
595 | bad data after user has changed them in memory */ | 604 | bad data after user has changed them in memory */ |
596 | atomic_set(&cifsInfo->inUse, 1); | 605 | atomic_set(&cifsInfo->inUse, 1); |
597 | } | ||
598 | |||
599 | cifs_set_ops(inode, is_dfs_referral); | ||
600 | } | 606 | } |
607 | |||
608 | cifs_set_ops(inode, is_dfs_referral); | ||
609 | |||
610 | |||
611 | |||
612 | |||
601 | cgii_exit: | 613 | cgii_exit: |
602 | if (full_path != search_path) | ||
603 | kfree(full_path); | ||
604 | kfree(buf); | 614 | kfree(buf); |
605 | return rc; | 615 | return rc; |
606 | } | 616 | } |
@@ -1005,8 +1015,11 @@ mkdir_get_info: | |||
1005 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 1015 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
1006 | } | 1016 | } |
1007 | if (direntry->d_inode) { | 1017 | if (direntry->d_inode) { |
1008 | direntry->d_inode->i_mode = mode; | 1018 | if (cifs_sb->mnt_cifs_flags & |
1009 | direntry->d_inode->i_mode |= S_IFDIR; | 1019 | CIFS_MOUNT_DYNPERM) |
1020 | direntry->d_inode->i_mode = | ||
1021 | (mode | S_IFDIR); | ||
1022 | |||
1010 | if (cifs_sb->mnt_cifs_flags & | 1023 | if (cifs_sb->mnt_cifs_flags & |
1011 | CIFS_MOUNT_SET_UID) { | 1024 | CIFS_MOUNT_SET_UID) { |
1012 | direntry->d_inode->i_uid = | 1025 | direntry->d_inode->i_uid = |
@@ -1502,8 +1515,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
1502 | int oplock = 0; | 1515 | int oplock = 0; |
1503 | 1516 | ||
1504 | rc = SMBLegacyOpen(xid, pTcon, full_path, | 1517 | rc = SMBLegacyOpen(xid, pTcon, full_path, |
1505 | FILE_OPEN, | 1518 | FILE_OPEN, GENERIC_WRITE, |
1506 | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, | ||
1507 | CREATE_NOT_DIR, &netfid, &oplock, | 1519 | CREATE_NOT_DIR, &netfid, &oplock, |
1508 | NULL, cifs_sb->local_nls, | 1520 | NULL, cifs_sb->local_nls, |
1509 | cifs_sb->mnt_cifs_flags & | 1521 | cifs_sb->mnt_cifs_flags & |
@@ -1534,13 +1546,26 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
1534 | } else | 1546 | } else |
1535 | goto cifs_setattr_exit; | 1547 | goto cifs_setattr_exit; |
1536 | } | 1548 | } |
1537 | if (attrs->ia_valid & ATTR_UID) { | 1549 | |
1538 | cFYI(1, ("UID changed to %d", attrs->ia_uid)); | 1550 | /* |
1539 | uid = attrs->ia_uid; | 1551 | * Without unix extensions we can't send ownership changes to the |
1540 | } | 1552 | * server, so silently ignore them. This is consistent with how |
1541 | if (attrs->ia_valid & ATTR_GID) { | 1553 | * local DOS/Windows filesystems behave (VFAT, NTFS, etc). With |
1542 | cFYI(1, ("GID changed to %d", attrs->ia_gid)); | 1554 | * CIFSACL support + proper Windows to Unix idmapping, we may be |
1543 | gid = attrs->ia_gid; | 1555 | * able to support this in the future. |
1556 | */ | ||
1557 | if (!pTcon->unix_ext && | ||
1558 | !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) { | ||
1559 | attrs->ia_valid &= ~(ATTR_UID | ATTR_GID); | ||
1560 | } else { | ||
1561 | if (attrs->ia_valid & ATTR_UID) { | ||
1562 | cFYI(1, ("UID changed to %d", attrs->ia_uid)); | ||
1563 | uid = attrs->ia_uid; | ||
1564 | } | ||
1565 | if (attrs->ia_valid & ATTR_GID) { | ||
1566 | cFYI(1, ("GID changed to %d", attrs->ia_gid)); | ||
1567 | gid = attrs->ia_gid; | ||
1568 | } | ||
1544 | } | 1569 | } |
1545 | 1570 | ||
1546 | time_buf.Attributes = 0; | 1571 | time_buf.Attributes = 0; |
@@ -1550,7 +1575,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
1550 | attrs->ia_valid &= ~ATTR_MODE; | 1575 | attrs->ia_valid &= ~ATTR_MODE; |
1551 | 1576 | ||
1552 | if (attrs->ia_valid & ATTR_MODE) { | 1577 | if (attrs->ia_valid & ATTR_MODE) { |
1553 | cFYI(1, ("Mode changed to 0x%x", attrs->ia_mode)); | 1578 | cFYI(1, ("Mode changed to 0%o", attrs->ia_mode)); |
1554 | mode = attrs->ia_mode; | 1579 | mode = attrs->ia_mode; |
1555 | } | 1580 | } |
1556 | 1581 | ||
@@ -1565,18 +1590,18 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
1565 | #ifdef CONFIG_CIFS_EXPERIMENTAL | 1590 | #ifdef CONFIG_CIFS_EXPERIMENTAL |
1566 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) | 1591 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) |
1567 | rc = mode_to_acl(inode, full_path, mode); | 1592 | rc = mode_to_acl(inode, full_path, mode); |
1568 | else if ((mode & S_IWUGO) == 0) { | 1593 | else |
1569 | #else | ||
1570 | if ((mode & S_IWUGO) == 0) { | ||
1571 | #endif | 1594 | #endif |
1572 | /* not writeable */ | 1595 | if (((mode & S_IWUGO) == 0) && |
1573 | if ((cifsInode->cifsAttrs & ATTR_READONLY) == 0) { | 1596 | (cifsInode->cifsAttrs & ATTR_READONLY) == 0) { |
1574 | set_dosattr = true; | 1597 | set_dosattr = true; |
1575 | time_buf.Attributes = | 1598 | time_buf.Attributes = cpu_to_le32(cifsInode->cifsAttrs | |
1576 | cpu_to_le32(cifsInode->cifsAttrs | | 1599 | ATTR_READONLY); |
1577 | ATTR_READONLY); | 1600 | /* fix up mode if we're not using dynperm */ |
1578 | } | 1601 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) |
1579 | } else if (cifsInode->cifsAttrs & ATTR_READONLY) { | 1602 | attrs->ia_mode = inode->i_mode & ~S_IWUGO; |
1603 | } else if ((mode & S_IWUGO) && | ||
1604 | (cifsInode->cifsAttrs & ATTR_READONLY)) { | ||
1580 | /* If file is readonly on server, we would | 1605 | /* If file is readonly on server, we would |
1581 | not be able to write to it - so if any write | 1606 | not be able to write to it - so if any write |
1582 | bit is enabled for user or group or other we | 1607 | bit is enabled for user or group or other we |
@@ -1587,6 +1612,20 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
1587 | /* Windows ignores set to zero */ | 1612 | /* Windows ignores set to zero */ |
1588 | if (time_buf.Attributes == 0) | 1613 | if (time_buf.Attributes == 0) |
1589 | time_buf.Attributes |= cpu_to_le32(ATTR_NORMAL); | 1614 | time_buf.Attributes |= cpu_to_le32(ATTR_NORMAL); |
1615 | |||
1616 | /* reset local inode permissions to normal */ | ||
1617 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { | ||
1618 | attrs->ia_mode &= ~(S_IALLUGO); | ||
1619 | if (S_ISDIR(inode->i_mode)) | ||
1620 | attrs->ia_mode |= | ||
1621 | cifs_sb->mnt_dir_mode; | ||
1622 | else | ||
1623 | attrs->ia_mode |= | ||
1624 | cifs_sb->mnt_file_mode; | ||
1625 | } | ||
1626 | } else if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { | ||
1627 | /* ignore mode change - ATTR_READONLY hasn't changed */ | ||
1628 | attrs->ia_valid &= ~ATTR_MODE; | ||
1590 | } | 1629 | } |
1591 | } | 1630 | } |
1592 | 1631 | ||