aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/readdir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs/readdir.c')
-rw-r--r--fs/cifs/readdir.c429
1 files changed, 183 insertions, 246 deletions
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 6751e745bbc6..5de03ec20144 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -4,6 +4,7 @@
4 * Directory search handling 4 * Directory search handling
5 * 5 *
6 * Copyright (C) International Business Machines Corp., 2004, 2008 6 * Copyright (C) International Business Machines Corp., 2004, 2008
7 * Copyright (C) Red Hat, Inc., 2011
7 * Author(s): Steve French (sfrench@us.ibm.com) 8 * Author(s): Steve French (sfrench@us.ibm.com)
8 * 9 *
9 * This library is free software; you can redistribute it and/or modify 10 * This library is free software; you can redistribute it and/or modify
@@ -290,10 +291,10 @@ error_exit:
290} 291}
291 292
292/* return length of unicode string in bytes */ 293/* return length of unicode string in bytes */
293static int cifs_unicode_bytelen(char *str) 294static int cifs_unicode_bytelen(const char *str)
294{ 295{
295 int len; 296 int len;
296 __le16 *ustr = (__le16 *)str; 297 const __le16 *ustr = (const __le16 *)str;
297 298
298 for (len = 0; len <= PATH_MAX; len++) { 299 for (len = 0; len <= PATH_MAX; len++) {
299 if (ustr[len] == 0) 300 if (ustr[len] == 0)
@@ -334,78 +335,128 @@ static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
334 335
335} 336}
336 337
338struct cifs_dirent {
339 const char *name;
340 size_t namelen;
341 u32 resume_key;
342 u64 ino;
343};
344
345static void cifs_fill_dirent_unix(struct cifs_dirent *de,
346 const FILE_UNIX_INFO *info, bool is_unicode)
347{
348 de->name = &info->FileName[0];
349 if (is_unicode)
350 de->namelen = cifs_unicode_bytelen(de->name);
351 else
352 de->namelen = strnlen(de->name, PATH_MAX);
353 de->resume_key = info->ResumeKey;
354 de->ino = le64_to_cpu(info->basic.UniqueId);
355}
356
357static void cifs_fill_dirent_dir(struct cifs_dirent *de,
358 const FILE_DIRECTORY_INFO *info)
359{
360 de->name = &info->FileName[0];
361 de->namelen = le32_to_cpu(info->FileNameLength);
362 de->resume_key = info->FileIndex;
363}
364
365static void cifs_fill_dirent_full(struct cifs_dirent *de,
366 const FILE_FULL_DIRECTORY_INFO *info)
367{
368 de->name = &info->FileName[0];
369 de->namelen = le32_to_cpu(info->FileNameLength);
370 de->resume_key = info->FileIndex;
371}
372
373static void cifs_fill_dirent_search(struct cifs_dirent *de,
374 const SEARCH_ID_FULL_DIR_INFO *info)
375{
376 de->name = &info->FileName[0];
377 de->namelen = le32_to_cpu(info->FileNameLength);
378 de->resume_key = info->FileIndex;
379 de->ino = le64_to_cpu(info->UniqueId);
380}
381
382static void cifs_fill_dirent_both(struct cifs_dirent *de,
383 const FILE_BOTH_DIRECTORY_INFO *info)
384{
385 de->name = &info->FileName[0];
386 de->namelen = le32_to_cpu(info->FileNameLength);
387 de->resume_key = info->FileIndex;
388}
389
390static void cifs_fill_dirent_std(struct cifs_dirent *de,
391 const FIND_FILE_STANDARD_INFO *info)
392{
393 de->name = &info->FileName[0];
394 /* one byte length, no endianess conversion */
395 de->namelen = info->FileNameLength;
396 de->resume_key = info->ResumeKey;
397}
398
399static int cifs_fill_dirent(struct cifs_dirent *de, const void *info,
400 u16 level, bool is_unicode)
401{
402 memset(de, 0, sizeof(*de));
403
404 switch (level) {
405 case SMB_FIND_FILE_UNIX:
406 cifs_fill_dirent_unix(de, info, is_unicode);
407 break;
408 case SMB_FIND_FILE_DIRECTORY_INFO:
409 cifs_fill_dirent_dir(de, info);
410 break;
411 case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
412 cifs_fill_dirent_full(de, info);
413 break;
414 case SMB_FIND_FILE_ID_FULL_DIR_INFO:
415 cifs_fill_dirent_search(de, info);
416 break;
417 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
418 cifs_fill_dirent_both(de, info);
419 break;
420 case SMB_FIND_FILE_INFO_STANDARD:
421 cifs_fill_dirent_std(de, info);
422 break;
423 default:
424 cFYI(1, "Unknown findfirst level %d", level);
425 return -EINVAL;
426 }
427
428 return 0;
429}
430
337#define UNICODE_DOT cpu_to_le16(0x2e) 431#define UNICODE_DOT cpu_to_le16(0x2e)
338 432
339/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */ 433/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */
340static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile) 434static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode)
341{ 435{
342 int rc = 0; 436 int rc = 0;
343 char *filename = NULL;
344 int len = 0;
345
346 if (cfile->srch_inf.info_level == SMB_FIND_FILE_UNIX) {
347 FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
348 filename = &pFindData->FileName[0];
349 if (cfile->srch_inf.unicode) {
350 len = cifs_unicode_bytelen(filename);
351 } else {
352 /* BB should we make this strnlen of PATH_MAX? */
353 len = strnlen(filename, 5);
354 }
355 } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_DIRECTORY_INFO) {
356 FILE_DIRECTORY_INFO *pFindData =
357 (FILE_DIRECTORY_INFO *)current_entry;
358 filename = &pFindData->FileName[0];
359 len = le32_to_cpu(pFindData->FileNameLength);
360 } else if (cfile->srch_inf.info_level ==
361 SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
362 FILE_FULL_DIRECTORY_INFO *pFindData =
363 (FILE_FULL_DIRECTORY_INFO *)current_entry;
364 filename = &pFindData->FileName[0];
365 len = le32_to_cpu(pFindData->FileNameLength);
366 } else if (cfile->srch_inf.info_level ==
367 SMB_FIND_FILE_ID_FULL_DIR_INFO) {
368 SEARCH_ID_FULL_DIR_INFO *pFindData =
369 (SEARCH_ID_FULL_DIR_INFO *)current_entry;
370 filename = &pFindData->FileName[0];
371 len = le32_to_cpu(pFindData->FileNameLength);
372 } else if (cfile->srch_inf.info_level ==
373 SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
374 FILE_BOTH_DIRECTORY_INFO *pFindData =
375 (FILE_BOTH_DIRECTORY_INFO *)current_entry;
376 filename = &pFindData->FileName[0];
377 len = le32_to_cpu(pFindData->FileNameLength);
378 } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) {
379 FIND_FILE_STANDARD_INFO *pFindData =
380 (FIND_FILE_STANDARD_INFO *)current_entry;
381 filename = &pFindData->FileName[0];
382 len = pFindData->FileNameLength;
383 } else {
384 cFYI(1, "Unknown findfirst level %d",
385 cfile->srch_inf.info_level);
386 }
387 437
388 if (filename) { 438 if (!de->name)
389 if (cfile->srch_inf.unicode) { 439 return 0;
390 __le16 *ufilename = (__le16 *)filename; 440
391 if (len == 2) { 441 if (is_unicode) {
392 /* check for . */ 442 __le16 *ufilename = (__le16 *)de->name;
393 if (ufilename[0] == UNICODE_DOT) 443 if (de->namelen == 2) {
394 rc = 1; 444 /* check for . */
395 } else if (len == 4) { 445 if (ufilename[0] == UNICODE_DOT)
396 /* check for .. */ 446 rc = 1;
397 if ((ufilename[0] == UNICODE_DOT) 447 } else if (de->namelen == 4) {
398 && (ufilename[1] == UNICODE_DOT)) 448 /* check for .. */
399 rc = 2; 449 if (ufilename[0] == UNICODE_DOT &&
400 } 450 ufilename[1] == UNICODE_DOT)
401 } else /* ASCII */ { 451 rc = 2;
402 if (len == 1) { 452 }
403 if (filename[0] == '.') 453 } else /* ASCII */ {
404 rc = 1; 454 if (de->namelen == 1) {
405 } else if (len == 2) { 455 if (de->name[0] == '.')
406 if ((filename[0] == '.') && (filename[1] == '.')) 456 rc = 1;
407 rc = 2; 457 } else if (de->namelen == 2) {
408 } 458 if (de->name[0] == '.' && de->name[1] == '.')
459 rc = 2;
409 } 460 }
410 } 461 }
411 462
@@ -427,66 +478,18 @@ static int is_dir_changed(struct file *file)
427} 478}
428 479
429static int cifs_save_resume_key(const char *current_entry, 480static int cifs_save_resume_key(const char *current_entry,
430 struct cifsFileInfo *cifsFile) 481 struct cifsFileInfo *file_info)
431{ 482{
432 int rc = 0; 483 struct cifs_dirent de;
433 unsigned int len = 0; 484 int rc;
434 __u16 level;
435 char *filename;
436
437 if ((cifsFile == NULL) || (current_entry == NULL))
438 return -EINVAL;
439
440 level = cifsFile->srch_inf.info_level;
441
442 if (level == SMB_FIND_FILE_UNIX) {
443 FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
444 485
445 filename = &pFindData->FileName[0]; 486 rc = cifs_fill_dirent(&de, current_entry, file_info->srch_inf.info_level,
446 if (cifsFile->srch_inf.unicode) { 487 file_info->srch_inf.unicode);
447 len = cifs_unicode_bytelen(filename); 488 if (!rc) {
448 } else { 489 file_info->srch_inf.presume_name = de.name;
449 /* BB should we make this strnlen of PATH_MAX? */ 490 file_info->srch_inf.resume_name_len = de.namelen;
450 len = strnlen(filename, PATH_MAX); 491 file_info->srch_inf.resume_key = de.resume_key;
451 }
452 cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
453 } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
454 FILE_DIRECTORY_INFO *pFindData =
455 (FILE_DIRECTORY_INFO *)current_entry;
456 filename = &pFindData->FileName[0];
457 len = le32_to_cpu(pFindData->FileNameLength);
458 cifsFile->srch_inf.resume_key = pFindData->FileIndex;
459 } else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
460 FILE_FULL_DIRECTORY_INFO *pFindData =
461 (FILE_FULL_DIRECTORY_INFO *)current_entry;
462 filename = &pFindData->FileName[0];
463 len = le32_to_cpu(pFindData->FileNameLength);
464 cifsFile->srch_inf.resume_key = pFindData->FileIndex;
465 } else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
466 SEARCH_ID_FULL_DIR_INFO *pFindData =
467 (SEARCH_ID_FULL_DIR_INFO *)current_entry;
468 filename = &pFindData->FileName[0];
469 len = le32_to_cpu(pFindData->FileNameLength);
470 cifsFile->srch_inf.resume_key = pFindData->FileIndex;
471 } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
472 FILE_BOTH_DIRECTORY_INFO *pFindData =
473 (FILE_BOTH_DIRECTORY_INFO *)current_entry;
474 filename = &pFindData->FileName[0];
475 len = le32_to_cpu(pFindData->FileNameLength);
476 cifsFile->srch_inf.resume_key = pFindData->FileIndex;
477 } else if (level == SMB_FIND_FILE_INFO_STANDARD) {
478 FIND_FILE_STANDARD_INFO *pFindData =
479 (FIND_FILE_STANDARD_INFO *)current_entry;
480 filename = &pFindData->FileName[0];
481 /* one byte length, no name conversion */
482 len = (unsigned int)pFindData->FileNameLength;
483 cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
484 } else {
485 cFYI(1, "Unknown findfirst level %d", level);
486 return -EINVAL;
487 } 492 }
488 cifsFile->srch_inf.resume_name_len = len;
489 cifsFile->srch_inf.presume_name = filename;
490 return rc; 493 return rc;
491} 494}
492 495
@@ -605,136 +608,70 @@ static int find_cifs_entry(const int xid, struct cifs_tcon *pTcon,
605 return rc; 608 return rc;
606} 609}
607 610
608/* inode num, inode type and filename returned */ 611static int cifs_filldir(char *find_entry, struct file *file, filldir_t filldir,
609static int cifs_get_name_from_search_buf(struct qstr *pqst, 612 void *dirent, char *scratch_buf, unsigned int max_len)
610 char *current_entry, __u16 level, unsigned int unicode,
611 struct cifs_sb_info *cifs_sb, unsigned int max_len, __u64 *pinum)
612{ 613{
614 struct cifsFileInfo *file_info = file->private_data;
615 struct super_block *sb = file->f_path.dentry->d_sb;
616 struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
617 struct cifs_dirent de = { NULL, };
618 struct cifs_fattr fattr;
619 struct dentry *dentry;
620 struct qstr name;
613 int rc = 0; 621 int rc = 0;
614 unsigned int len = 0; 622 ino_t ino;
615 char *filename;
616 struct nls_table *nlt = cifs_sb->local_nls;
617
618 *pinum = 0;
619
620 if (level == SMB_FIND_FILE_UNIX) {
621 FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
622
623 filename = &pFindData->FileName[0];
624 if (unicode) {
625 len = cifs_unicode_bytelen(filename);
626 } else {
627 /* BB should we make this strnlen of PATH_MAX? */
628 len = strnlen(filename, PATH_MAX);
629 }
630 623
631 *pinum = le64_to_cpu(pFindData->basic.UniqueId); 624 rc = cifs_fill_dirent(&de, find_entry, file_info->srch_inf.info_level,
632 } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) { 625 file_info->srch_inf.unicode);
633 FILE_DIRECTORY_INFO *pFindData = 626 if (rc)
634 (FILE_DIRECTORY_INFO *)current_entry; 627 return rc;
635 filename = &pFindData->FileName[0];
636 len = le32_to_cpu(pFindData->FileNameLength);
637 } else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
638 FILE_FULL_DIRECTORY_INFO *pFindData =
639 (FILE_FULL_DIRECTORY_INFO *)current_entry;
640 filename = &pFindData->FileName[0];
641 len = le32_to_cpu(pFindData->FileNameLength);
642 } else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
643 SEARCH_ID_FULL_DIR_INFO *pFindData =
644 (SEARCH_ID_FULL_DIR_INFO *)current_entry;
645 filename = &pFindData->FileName[0];
646 len = le32_to_cpu(pFindData->FileNameLength);
647 *pinum = le64_to_cpu(pFindData->UniqueId);
648 } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
649 FILE_BOTH_DIRECTORY_INFO *pFindData =
650 (FILE_BOTH_DIRECTORY_INFO *)current_entry;
651 filename = &pFindData->FileName[0];
652 len = le32_to_cpu(pFindData->FileNameLength);
653 } else if (level == SMB_FIND_FILE_INFO_STANDARD) {
654 FIND_FILE_STANDARD_INFO *pFindData =
655 (FIND_FILE_STANDARD_INFO *)current_entry;
656 filename = &pFindData->FileName[0];
657 /* one byte length, no name conversion */
658 len = (unsigned int)pFindData->FileNameLength;
659 } else {
660 cFYI(1, "Unknown findfirst level %d", level);
661 return -EINVAL;
662 }
663 628
664 if (len > max_len) { 629 if (de.namelen > max_len) {
665 cERROR(1, "bad search response length %d past smb end", len); 630 cERROR(1, "bad search response length %zd past smb end",
631 de.namelen);
666 return -EINVAL; 632 return -EINVAL;
667 } 633 }
668 634
669 if (unicode) {
670 pqst->len = cifs_from_ucs2((char *) pqst->name,
671 (__le16 *) filename,
672 UNICODE_NAME_MAX,
673 min(len, max_len), nlt,
674 cifs_sb->mnt_cifs_flags &
675 CIFS_MOUNT_MAP_SPECIAL_CHR);
676 pqst->len -= nls_nullsize(nlt);
677 } else {
678 pqst->name = filename;
679 pqst->len = len;
680 }
681 return rc;
682}
683
684static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,
685 void *direntry, char *scratch_buf, unsigned int max_len)
686{
687 int rc = 0;
688 struct qstr qstring;
689 struct cifsFileInfo *pCifsF;
690 u64 inum;
691 ino_t ino;
692 struct super_block *sb;
693 struct cifs_sb_info *cifs_sb;
694 struct dentry *tmp_dentry;
695 struct cifs_fattr fattr;
696
697 /* get filename and len into qstring */
698 /* get dentry */
699 /* decide whether to create and populate ionde */
700 if ((direntry == NULL) || (file == NULL))
701 return -EINVAL;
702
703 pCifsF = file->private_data;
704
705 if ((scratch_buf == NULL) || (pfindEntry == NULL) || (pCifsF == NULL))
706 return -ENOENT;
707
708 rc = cifs_entry_is_dot(pfindEntry, pCifsF);
709 /* skip . and .. since we added them first */ 635 /* skip . and .. since we added them first */
710 if (rc != 0) 636 if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode))
711 return 0; 637 return 0;
712 638
713 sb = file->f_path.dentry->d_sb; 639 if (file_info->srch_inf.unicode) {
714 cifs_sb = CIFS_SB(sb); 640 struct nls_table *nlt = cifs_sb->local_nls;
715
716 qstring.name = scratch_buf;
717 rc = cifs_get_name_from_search_buf(&qstring, pfindEntry,
718 pCifsF->srch_inf.info_level,
719 pCifsF->srch_inf.unicode, cifs_sb,
720 max_len, &inum /* returned */);
721 641
722 if (rc) 642 name.name = scratch_buf;
723 return rc; 643 name.len =
644 cifs_from_ucs2((char *)name.name, (__le16 *)de.name,
645 UNICODE_NAME_MAX,
646 min(de.namelen, (size_t)max_len), nlt,
647 cifs_sb->mnt_cifs_flags &
648 CIFS_MOUNT_MAP_SPECIAL_CHR);
649 name.len -= nls_nullsize(nlt);
650 } else {
651 name.name = de.name;
652 name.len = de.namelen;
653 }
724 654
725 if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) 655 switch (file_info->srch_inf.info_level) {
656 case SMB_FIND_FILE_UNIX:
726 cifs_unix_basic_to_fattr(&fattr, 657 cifs_unix_basic_to_fattr(&fattr,
727 &((FILE_UNIX_INFO *) pfindEntry)->basic, 658 &((FILE_UNIX_INFO *)find_entry)->basic,
728 cifs_sb); 659 cifs_sb);
729 else if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) 660 break;
730 cifs_std_info_to_fattr(&fattr, (FIND_FILE_STANDARD_INFO *) 661 case SMB_FIND_FILE_INFO_STANDARD:
731 pfindEntry, cifs_sb); 662 cifs_std_info_to_fattr(&fattr,
732 else 663 (FIND_FILE_STANDARD_INFO *)find_entry,
733 cifs_dir_info_to_fattr(&fattr, (FILE_DIRECTORY_INFO *) 664 cifs_sb);
734 pfindEntry, cifs_sb); 665 break;
666 default:
667 cifs_dir_info_to_fattr(&fattr,
668 (FILE_DIRECTORY_INFO *)find_entry,
669 cifs_sb);
670 break;
671 }
735 672
736 if (inum && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { 673 if (de.ino && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
737 fattr.cf_uniqueid = inum; 674 fattr.cf_uniqueid = de.ino;
738 } else { 675 } else {
739 fattr.cf_uniqueid = iunique(sb, ROOT_I); 676 fattr.cf_uniqueid = iunique(sb, ROOT_I);
740 cifs_autodisable_serverino(cifs_sb); 677 cifs_autodisable_serverino(cifs_sb);
@@ -750,12 +687,12 @@ static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,
750 fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; 687 fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
751 688
752 ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid); 689 ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
753 tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring, &fattr); 690 dentry = cifs_readdir_lookup(file->f_dentry, &name, &fattr);
754 691
755 rc = filldir(direntry, qstring.name, qstring.len, file->f_pos, 692 rc = filldir(dirent, name.name, name.len, file->f_pos, ino,
756 ino, fattr.cf_dtype); 693 fattr.cf_dtype);
757 694
758 dput(tmp_dentry); 695 dput(dentry);
759 return rc; 696 return rc;
760} 697}
761 698
@@ -796,7 +733,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
796 file->f_pos++; 733 file->f_pos++;
797 case 1: 734 case 1:
798 if (filldir(direntry, "..", 2, file->f_pos, 735 if (filldir(direntry, "..", 2, file->f_pos,
799 file->f_path.dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) { 736 parent_ino(file->f_path.dentry), DT_DIR) < 0) {
800 cERROR(1, "Filldir for parent dir failed"); 737 cERROR(1, "Filldir for parent dir failed");
801 rc = -ENOMEM; 738 rc = -ENOMEM;
802 break; 739 break;