1/* 2 * fs/cifs/readdir.c 3 * 4 * Directory search handling 5 * 6 * Copyright (C) International Business Machines Corp., 2004, 2008 7 * Author(s): Steve French (sfrench@us.ibm.com) 8 * 9 * This library is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU Lesser General Public License as published 11 * by the Free Software Foundation; either version 2.1 of the License, or 12 * (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 17 * the GNU Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public License 20 * along with this library; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 */ 23#include <linux/fs.h> 24#include <linux/pagemap.h> 25#include <linux/slab.h> 26#include <linux/stat.h> 27#include "cifspdu.h" 28#include "cifsglob.h" 29#include "cifsproto.h" 30#include "cifs_unicode.h" 31#include "cifs_debug.h" 32#include "cifs_fs_sb.h" 33#include "cifsfs.h" 34 35/* 36 * To be safe - for UCS to UTF-8 with strings loaded with the rare long 37 * characters alloc more to account for such multibyte target UTF-8 38 * characters. 39 */ 40#define UNICODE_NAME_MAX ((4 * NAME_MAX) + 2) 41 42#ifdef CONFIG_CIFS_DEBUG2 43static void dump_cifs_file_struct(struct file *file, char *label) 44{ 45 struct cifsFileInfo *cf; 46 47 if (file) { 48 cf = file->private_data; 49 if (cf == NULL) { 50 cFYI(1, "empty cifs private file data"); 51 return; 52 } 53 if (cf->invalidHandle) 54 cFYI(1, "invalid handle"); 55 if (cf->srch_inf.endOfSearch) 56 cFYI(1, "end of search"); 57 if (cf->srch_inf.emptyDir) 58 cFYI(1, "empty dir"); 59 } 60} 61#else 62static inline void dump_cifs_file_struct(struct file *file, char *label) 63{ 64} 65#endif /* DEBUG2 */ 66 67/* 68 * Find the dentry that matches "name". If there isn't one, create one. If it's 69 * a negative dentry or the uniqueid changed, then drop it and recreate it. 70 */ 71static struct dentry * 72cifs_readdir_lookup(struct dentry *parent, struct qstr *name, 73 struct cifs_fattr *fattr) 74{ 75 struct dentry *dentry, *alias; 76 struct inode *inode; 77 struct super_block *sb = parent->d_inode->i_sb; 78 79 cFYI(1, "For %s", name->name); 80 81 if (parent->d_op && parent->d_op->d_hash) 82 parent->d_op->d_hash(parent, name); 83 else 84 name->hash = full_name_hash(name->name, name->len); 85 86 dentry = d_lookup(parent, name); 87 if (dentry) { 88 if (dentry->d_inode != NULL) 89 return dentry; 90 d_drop(dentry); 91 dput(dentry); 92 } 93 94 dentry = d_alloc(parent, name); 95 if (dentry == NULL) 96 return NULL; 97 98 inode = cifs_iget(sb, fattr); 99 if (!inode) { 100 dput(dentry); 101 return NULL; 102 } 103 104 if (CIFS_SB(sb)->tcon->nocase) 105 dentry->d_op = &cifs_ci_dentry_ops; 106 else 107 dentry->d_op = &cifs_dentry_ops; 108 109 alias = d_materialise_unique(dentry, inode); 110 if (alias != NULL) { 111 dput(dentry); 112 if (IS_ERR(alias)) 113 return NULL; 114 dentry = alias; 115 } 116 117 return dentry; 118} 119 120static void 121cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) 122{ 123 fattr->cf_uid = cifs_sb->mnt_uid; 124 fattr->cf_gid = cifs_sb->mnt_gid; 125 126 if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { 127 fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; 128 fattr->cf_dtype = DT_DIR; 129 } else { 130 fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; 131 fattr->cf_dtype = DT_REG; 132 } 133 134 if (fattr->cf_cifsattrs & ATTR_READONLY) 135 fattr->cf_mode &= ~S_IWUGO; 136 137 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL && 138 fattr->cf_cifsattrs & ATTR_SYSTEM) { 139 if (fattr->cf_eof == 0) { 140 fattr->cf_mode &= ~S_IFMT; 141 fattr->cf_mode |= S_IFIFO; 142 fattr->cf_dtype = DT_FIFO; 143 } else { 144 /* 145 * trying to get the type and mode via SFU can be slow, 146 * so just call those regular files for now, and mark 147 * for reval 148 */ 149 fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; 150 } 151 } 152} 153 154static void 155cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info, 156 struct cifs_sb_info *cifs_sb) 157{ 158 memset(fattr, 0, sizeof(*fattr)); 159 fattr->cf_cifsattrs = le32_to_cpu(info->ExtFileAttributes); 160 fattr->cf_eof = le64_to_cpu(info->EndOfFile); 161 fattr->cf_bytes = le64_to_cpu(info->AllocationSize); 162 fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); 163 fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); 164 fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); 165 166 cifs_fill_common_info(fattr, cifs_sb); 167} 168 169static void 170cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info, 171 struct cifs_sb_info *cifs_sb) 172{ 173 int offset = cifs_sb->tcon->ses->server->timeAdj; 174 175 memset(fattr, 0, sizeof(*fattr)); 176 fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate, 177 info->LastAccessTime, offset); 178 fattr->cf_ctime = cnvrtDosUnixTm(info->LastWriteDate, 179 info->LastWriteTime, offset); 180 fattr->cf_mtime = cnvrtDosUnixTm(info->LastWriteDate, 181 info->LastWriteTime, offset); 182 183 fattr->cf_cifsattrs = le16_to_cpu(info->Attributes); 184 fattr->cf_bytes = le32_to_cpu(info->AllocationSize); 185 fattr->cf_eof = le32_to_cpu(info->DataSize); 186 187 cifs_fill_common_info(fattr, cifs_sb); 188} 189 190/* BB eventually need to add the following helper function to 191 resolve NT_STATUS_STOPPED_ON_SYMLINK return code when 192 we try to do FindFirst on (NTFS) directory symlinks */ 193/* 194int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb, 195 int xid) 196{ 197 __u16 fid; 198 int len; 199 int oplock = 0; 200 int rc; 201 struct cifsTconInfo *ptcon = cifs_sb->tcon; 202 char *tmpbuffer; 203 204 rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ, 205 OPEN_REPARSE_POINT, &fid, &oplock, NULL, 206 cifs_sb->local_nls, 207 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); 208 if (!rc) { 209 tmpbuffer = kmalloc(maxpath); 210 rc = CIFSSMBQueryReparseLinkInfo(xid, ptcon, full_path, 211 tmpbuffer, 212 maxpath -1, 213 fid, 214 cifs_sb->local_nls); 215 if (CIFSSMBClose(xid, ptcon, fid)) { 216 cFYI(1, "Error closing temporary reparsepoint open"); 217 } 218 } 219} 220 */ 221 222static int initiate_cifs_search(const int xid, struct file *file) 223{ 224 int rc = 0; 225 char *full_path; 226 struct cifsFileInfo *cifsFile; 227 struct cifs_sb_info *cifs_sb; 228 struct cifsTconInfo *pTcon; 229 230 if (file->private_data == NULL) { 231 file->private_data = 232 kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); 233 } 234 235 if (file->private_data == NULL) 236 return -ENOMEM; 237 cifsFile = file->private_data; 238 cifsFile->invalidHandle = true; 239 cifsFile->srch_inf.endOfSearch = false; 240 241 cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); 242 if (cifs_sb == NULL) 243 return -EINVAL; 244 245 pTcon = cifs_sb->tcon; 246 if (pTcon == NULL) 247 return -EINVAL; 248 249 full_path = build_path_from_dentry(file->f_path.dentry); 250 251 if (full_path == NULL) 252 return -ENOMEM; 253 254 cFYI(1, "Full path: %s start at: %lld", full_path, file->f_pos); 255 256ffirst_retry: 257 /* test for Unix extensions */ 258 /* but now check for them on the share/mount not on the SMB session */ 259/* if (pTcon->ses->capabilities & CAP_UNIX) { */ 260 if (pTcon->unix_ext) 261 cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; 262 else if ((pTcon->ses->capabilities & 263 (CAP_NT_SMBS | CAP_NT_FIND)) == 0) { 264 cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD; 265 } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { 266 cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; 267 } else { 268 cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO; 269 } 270 271 rc = CIFSFindFirst(xid, pTcon, full_path, cifs_sb->local_nls, 272 &cifsFile->netfid, &cifsFile->srch_inf, 273 cifs_sb->mnt_cifs_flags & 274 CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb)); 275 if (rc == 0) 276 cifsFile->invalidHandle = false; 277 /* BB add following call to handle readdir on new NTFS symlink errors 278 else if STATUS_STOPPED_ON_SYMLINK 279 call get_symlink_reparse_path and retry with new path */ 280 else if ((rc == -EOPNOTSUPP) && 281 (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { 282 cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; 283 goto ffirst_retry; 284 } 285 kfree(full_path); 286 return rc; 287} 288 289/* return length of unicode string in bytes */ 290static int cifs_unicode_bytelen(char *str) 291{ 292 int len; 293 __le16 *ustr = (__le16 *)str; 294 295 for (len = 0; len <= PATH_MAX; len++) { 296 if (ustr[len] == 0) 297 return len << 1; 298 } 299 cFYI(1, "Unicode string longer than PATH_MAX found"); 300 return len << 1; 301} 302 303static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level) 304{ 305 char *new_entry; 306 FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry; 307 308 if (level == SMB_FIND_FILE_INFO_STANDARD) { 309 FIND_FILE_STANDARD_INFO *pfData; 310 pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo; 311 312 new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) + 313 pfData->FileNameLength; 314 } else 315 new_entry = old_entry + le32_to_cpu(pDirInfo->NextEntryOffset); 316 cFYI(1, "new entry %p old entry %p", new_entry, old_entry); 317 /* validate that new_entry is not past end of SMB */ 318 if (new_entry >= end_of_smb) { 319 cERROR(1, "search entry %p began after end of SMB %p old entry %p", 320 new_entry, end_of_smb, old_entry); 321 return NULL; 322 } else if (((level == SMB_FIND_FILE_INFO_STANDARD) && 323 (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb)) 324 || ((level != SMB_FIND_FILE_INFO_STANDARD) && 325 (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb))) { 326 cERROR(1, "search entry %p extends after end of SMB %p", 327 new_entry, end_of_smb); 328 return NULL; 329 } else 330 return new_entry; 331 332} 333 334#define UNICODE_DOT cpu_to_le16(0x2e) 335 336/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */ 337static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile) 338{ 339 int rc = 0; 340 char *filename = NULL; 341 int len = 0; 342 343 if (cfile->srch_inf.info_level == SMB_FIND_FILE_UNIX) { 344 FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry; 345 filename = &pFindData->FileName[0]; 346 if (cfile->srch_inf.unicode) { 347 len = cifs_unicode_bytelen(filename); 348 } else { 349 /* BB should we make this strnlen of PATH_MAX? */ 350 len = strnlen(filename, 5); 351 } 352 } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_DIRECTORY_INFO) { 353 FILE_DIRECTORY_INFO *pFindData = 354 (FILE_DIRECTORY_INFO *)current_entry; 355 filename = &pFindData->FileName[0]; 356 len = le32_to_cpu(pFindData->FileNameLength); 357 } else if (cfile->srch_inf.info_level == 358 SMB_FIND_FILE_FULL_DIRECTORY_INFO) { 359 FILE_FULL_DIRECTORY_INFO *pFindData = 360 (FILE_FULL_DIRECTORY_INFO *)current_entry; 361 filename = &pFindData->FileName[0]; 362 len = le32_to_cpu(pFindData->FileNameLength); 363 } else if (cfile->srch_inf.info_level == 364 SMB_FIND_FILE_ID_FULL_DIR_INFO) { 365 SEARCH_ID_FULL_DIR_INFO *pFindData = 366 (SEARCH_ID_FULL_DIR_INFO *)current_entry; 367 filename = &pFindData->FileName[0]; 368 len = le32_to_cpu(pFindData->FileNameLength); 369 } else if (cfile->srch_inf.info_level == 370 SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { 371 FILE_BOTH_DIRECTORY_INFO *pFindData = 372 (FILE_BOTH_DIRECTORY_INFO *)current_entry; 373 filename = &pFindData->FileName[0]; 374 len = le32_to_cpu(pFindData->FileNameLength); 375 } else if (cfile->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) { 376 FIND_FILE_STANDARD_INFO *pFindData = 377 (FIND_FILE_STANDARD_INFO *)current_entry; 378 filename = &pFindData->FileName[0]; 379 len = pFindData->FileNameLength; 380 } else { 381 cFYI(1, "Unknown findfirst level %d", 382 cfile->srch_inf.info_level); 383 } 384 385 if (filename) { 386 if (cfile->srch_inf.unicode) { 387 __le16 *ufilename = (__le16 *)filename; 388 if (len == 2) { 389 /* check for . */ 390 if (ufilename[0] == UNICODE_DOT) 391 rc = 1; 392 } else if (len == 4) { 393 /* check for .. */ 394 if ((ufilename[0] == UNICODE_DOT) 395 && (ufilename[1] == UNICODE_DOT)) 396 rc = 2; 397 } 398 } else /* ASCII */ { 399 if (len == 1) { 400 if (filename[0] == '.') 401 rc = 1; 402 } else if (len == 2) { 403 if ((filename[0] == '.') && (filename[1] == '.')) 404 rc = 2; 405 } 406 } 407 } 408 409 return rc; 410} 411 412/* Check if directory that we are searching has changed so we can decide 413 whether we can use the cached search results from the previous search */ 414static int is_dir_changed(struct file *file) 415{ 416 struct inode *inode = file->f_path.dentry->d_inode; 417 struct cifsInodeInfo *cifsInfo = CIFS_I(inode); 418 419 if (cifsInfo->time == 0) 420 return 1; /* directory was changed, perhaps due to unlink */ 421 else 422 return 0; 423 424} 425 426static int cifs_save_resume_key(const char *current_entry, 427 struct cifsFileInfo *cifsFile) 428{ 429 int rc = 0; 430 unsigned int len = 0; 431 __u16 level; 432 char *filename; 433 434 if ((cifsFile == NULL) || (current_entry == NULL)) 435 return -EINVAL; 436 437 level = cifsFile->srch_inf.info_level; 438 439 if (level == SMB_FIND_FILE_UNIX) { 440 FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry; 441 442 filename = &pFindData->FileName[0]; 443 if (cifsFile->srch_inf.unicode) { 444 len = cifs_unicode_bytelen(filename); 445 } else { 446 /* BB should we make this strnlen of PATH_MAX? */ 447 len = strnlen(filename, PATH_MAX); 448 } 449 cifsFile->srch_inf.resume_key = pFindData->ResumeKey; 450 } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) { 451 FILE_DIRECTORY_INFO *pFindData = 452 (FILE_DIRECTORY_INFO *)current_entry; 453 filename = &pFindData->FileName[0]; 454 len = le32_to_cpu(pFindData->FileNameLength); 455 cifsFile->srch_inf.resume_key = pFindData->FileIndex; 456 } else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) { 457 FILE_FULL_DIRECTORY_INFO *pFindData = 458 (FILE_FULL_DIRECTORY_INFO *)current_entry; 459 filename = &pFindData->FileName[0]; 460 len = le32_to_cpu(pFindData->FileNameLength); 461 cifsFile->srch_inf.resume_key = pFindData->FileIndex; 462 } else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) { 463 SEARCH_ID_FULL_DIR_INFO *pFindData = 464 (SEARCH_ID_FULL_DIR_INFO *)current_entry; 465 filename = &pFindData->FileName[0]; 466 len = le32_to_cpu(pFindData->FileNameLength); 467 cifsFile->srch_inf.resume_key = pFindData->FileIndex; 468 } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { 469 FILE_BOTH_DIRECTORY_INFO *pFindData = 470 (FILE_BOTH_DIRECTORY_INFO *)current_entry; 471 filename = &pFindData->FileName[0]; 472 len = le32_to_cpu(pFindData->FileNameLength); 473 cifsFile->srch_inf.resume_key = pFindData->FileIndex; 474 } else if (level == SMB_FIND_FILE_INFO_STANDARD) { 475 FIND_FILE_STANDARD_INFO *pFindData = 476 (FIND_FILE_STANDARD_INFO *)current_entry; 477 filename = &pFindData->FileName[0]; 478 /* one byte length, no name conversion */ 479 len = (unsigned int)pFindData->FileNameLength; 480 cifsFile->srch_inf.resume_key = pFindData->ResumeKey; 481 } else { 482 cFYI(1, "Unknown findfirst level %d", level); 483 return -EINVAL; 484 } 485 cifsFile->srch_inf.resume_name_len = len; 486 cifsFile->srch_inf.presume_name = filename; 487 return rc; 488} 489 490/* find the corresponding entry in the search */ 491/* Note that the SMB server returns search entries for . and .. which 492 complicates logic here if we choose to parse for them and we do not 493 assume that they are located in the findfirst return buffer.*/ 494/* We start counting in the buffer with entry 2 and increment for every 495 entry (do not increment for . or .. entry) */ 496static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, 497 struct file *file, char **ppCurrentEntry, int *num_to_ret) 498{ 499 int rc = 0; 500 int pos_in_buf = 0; 501 loff_t first_entry_in_buffer; 502 loff_t index_to_find = file->f_pos; 503 struct cifsFileInfo *cifsFile = file->private_data; 504 /* check if index in the buffer */ 505 506 if ((cifsFile == NULL) || (ppCurrentEntry == NULL) || 507 (num_to_ret == NULL)) 508 return -ENOENT; 509 510 *ppCurrentEntry = NULL; 511 first_entry_in_buffer = 512 cifsFile->srch_inf.index_of_last_entry - 513 cifsFile->srch_inf.entries_in_buffer; 514 515 /* if first entry in buf is zero then is first buffer 516 in search response data which means it is likely . and .. 517 will be in this buffer, although some servers do not return 518 . and .. for the root of a drive and for those we need 519 to start two entries earlier */ 520 521 dump_cifs_file_struct(file, "In fce "); 522 if (((index_to_find < cifsFile->srch_inf.index_of_last_entry) && 523 is_dir_changed(file)) || 524 (index_to_find < first_entry_in_buffer)) { 525 /* close and restart search */ 526 cFYI(1, "search backing up - close and restart search"); 527 write_lock(&GlobalSMBSeslock); 528 if (!cifsFile->srch_inf.endOfSearch && 529 !cifsFile->invalidHandle) { 530 cifsFile->invalidHandle = true; 531 write_unlock(&GlobalSMBSeslock); 532 CIFSFindClose(xid, pTcon, cifsFile->netfid); 533 } else 534 write_unlock(&GlobalSMBSeslock); 535 if (cifsFile->srch_inf.ntwrk_buf_start) { 536 cFYI(1, "freeing SMB ff cache buf on search rewind"); 537 if (cifsFile->srch_inf.smallBuf) 538 cifs_small_buf_release(cifsFile->srch_inf. 539 ntwrk_buf_start); 540 else 541 cifs_buf_release(cifsFile->srch_inf. 542 ntwrk_buf_start); 543 cifsFile->srch_inf.ntwrk_buf_start = NULL; 544 } 545 rc = initiate_cifs_search(xid, file); 546 if (rc) { 547 cFYI(1, "error %d reinitiating a search on rewind", 548 rc); 549 return rc; 550 } 551 cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile); 552 } 553 554 while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) && 555 (rc == 0) && !cifsFile->srch_inf.endOfSearch) { 556 cFYI(1, "calling findnext2"); 557 rc = CIFSFindNext(xid, pTcon, cifsFile->netfid, 558 &cifsFile->srch_inf); 559 cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile); 560 if (rc) 561 return -ENOENT; 562 } 563 if (index_to_find < cifsFile->srch_inf.index_of_last_entry) { 564 /* we found the buffer that contains the entry */ 565 /* scan and find it */ 566 int i; 567 char *current_entry; 568 char *end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + 569 smbCalcSize((struct smb_hdr *) 570 cifsFile->srch_inf.ntwrk_buf_start); 571 572 current_entry = cifsFile->srch_inf.srch_entries_start; 573 first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry 574 - cifsFile->srch_inf.entries_in_buffer; 575 pos_in_buf = index_to_find - first_entry_in_buffer; 576 cFYI(1, "found entry - pos_in_buf %d", pos_in_buf); 577 578 for (i = 0; (i < (pos_in_buf)) && (current_entry != NULL); i++) { 579 /* go entry by entry figuring out which is first */ 580 current_entry = nxt_dir_entry(current_entry, end_of_smb, 581 cifsFile->srch_inf.info_level); 582 } 583 if ((current_entry == NULL) && (i < pos_in_buf)) { 584 cERROR(1, "reached end of buf searching for pos in buf" 585 " %d index to find %lld rc %d", 586 pos_in_buf, index_to_find, rc); 587 } 588 rc = 0; 589 *ppCurrentEntry = current_entry; 590 } else { 591 cFYI(1, "index not in buffer - could not findnext into it"); 592 return 0; 593 } 594 595 if (pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) { 596 cFYI(1, "can not return entries pos_in_buf beyond last"); 597 *num_to_ret = 0; 598 } else 599 *num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf; 600 601 return rc; 602} 603 604/* inode num, inode type and filename returned */ 605static int cifs_get_name_from_search_buf(struct qstr *pqst, 606 char *current_entry, __u16 level, unsigned int unicode, 607 struct cifs_sb_info *cifs_sb, unsigned int max_len, __u64 *pinum) 608{ 609 int rc = 0; 610 unsigned int len = 0; 611 char *filename; 612 struct nls_table *nlt = cifs_sb->local_nls; 613 614 *pinum = 0; 615 616 if (level == SMB_FIND_FILE_UNIX) { 617 FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry; 618 619 filename = &pFindData->FileName[0]; 620 if (unicode) { 621 len = cifs_unicode_bytelen(filename); 622 } else { 623 /* BB should we make this strnlen of PATH_MAX? */ 624 len = strnlen(filename, PATH_MAX); 625 } 626 627 *pinum = le64_to_cpu(pFindData->basic.UniqueId); 628 } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) { 629 FILE_DIRECTORY_INFO *pFindData = 630 (FILE_DIRECTORY_INFO *)current_entry; 631 filename = &pFindData->FileName[0]; 632 len = le32_to_cpu(pFindData->FileNameLength); 633 } else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) { 634 FILE_FULL_DIRECTORY_INFO *pFindData = 635 (FILE_FULL_DIRECTORY_INFO *)current_entry; 636 filename = &pFindData->FileName[0]; 637 len = le32_to_cpu(pFindData->FileNameLength); 638 } else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) { 639 SEARCH_ID_FULL_DIR_INFO *pFindData = 640 (SEARCH_ID_FULL_DIR_INFO *)current_entry; 641 filename = &pFindData->FileName[0]; 642 len = le32_to_cpu(pFindData->FileNameLength); 643 *pinum = le64_to_cpu(pFindData->UniqueId); 644 } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { 645 FILE_BOTH_DIRECTORY_INFO *pFindData = 646 (FILE_BOTH_DIRECTORY_INFO *)current_entry; 647 filename = &pFindData->FileName[0]; 648 len = le32_to_cpu(pFindData->FileNameLength); 649 } else if (level == SMB_FIND_FILE_INFO_STANDARD) { 650 FIND_FILE_STANDARD_INFO *pFindData = 651 (FIND_FILE_STANDARD_INFO *)current_entry; 652 filename = &pFindData->FileName[0]; 653 /* one byte length, no name conversion */ 654 len = (unsigned int)pFindData->FileNameLength; 655 } else { 656 cFYI(1, "Unknown findfirst level %d", level); 657 return -EINVAL; 658 } 659 660 if (len > max_len) { 661 cERROR(1, "bad search response length %d past smb end", len); 662 return -EINVAL; 663 } 664 665 if (unicode) { 666 pqst->len = cifs_from_ucs2((char *) pqst->name, 667 (__le16 *) filename, 668 UNICODE_NAME_MAX, 669 min(len, max_len), nlt, 670 cifs_sb->mnt_cifs_flags & 671 CIFS_MOUNT_MAP_SPECIAL_CHR); 672 pqst->len -= nls_nullsize(nlt); 673 } else { 674 pqst->name = filename; 675 pqst->len = len; 676 } 677 return rc; 678} 679 680static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir, 681 void *direntry, char *scratch_buf, unsigned int max_len) 682{ 683 int rc = 0; 684 struct qstr qstring; 685 struct cifsFileInfo *pCifsF; 686 u64 inum; 687 ino_t ino; 688 struct super_block *sb; 689 struct cifs_sb_info *cifs_sb; 690 struct dentry *tmp_dentry; 691 struct cifs_fattr fattr; 692 693 /* get filename and len into qstring */ 694 /* get dentry */ 695 /* decide whether to create and populate ionde */ 696 if ((direntry == NULL) || (file == NULL)) 697 return -EINVAL; 698 699 pCifsF = file->private_data; 700 701 if ((scratch_buf == NULL) || (pfindEntry == NULL) || (pCifsF == NULL)) 702 return -ENOENT; 703 704 rc = cifs_entry_is_dot(pfindEntry, pCifsF); 705 /* skip . and .. since we added them first */ 706 if (rc != 0) 707 return 0; 708 709 sb = file->f_path.dentry->d_sb; 710 cifs_sb = CIFS_SB(sb); 711 712 qstring.name = scratch_buf; 713 rc = cifs_get_name_from_search_buf(&qstring, pfindEntry, 714 pCifsF->srch_inf.info_level, 715 pCifsF->srch_inf.unicode, cifs_sb, 716 max_len, &inum /* returned */); 717 718 if (rc) 719 return rc; 720 721 if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) 722 cifs_unix_basic_to_fattr(&fattr, 723 &((FILE_UNIX_INFO *) pfindEntry)->basic, 724 cifs_sb); 725 else if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) 726 cifs_std_info_to_fattr(&fattr, (FIND_FILE_STANDARD_INFO *) 727 pfindEntry, cifs_sb); 728 else 729 cifs_dir_info_to_fattr(&fattr, (FILE_DIRECTORY_INFO *) 730 pfindEntry, cifs_sb); 731 732 if (inum && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { 733 fattr.cf_uniqueid = inum; 734 } else { 735 fattr.cf_uniqueid = iunique(sb, ROOT_I); 736 cifs_autodisable_serverino(cifs_sb); 737 } 738 739 ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid); 740 tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring, &fattr); 741 742 rc = filldir(direntry, qstring.name, qstring.len, file->f_pos, 743 ino, fattr.cf_dtype); 744 745 if (rc) { 746 cFYI(1, "filldir rc = %d", rc); 747 rc = -EOVERFLOW; 748 } 749 dput(tmp_dentry); 750 return rc; 751} 752 753 754int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) 755{ 756 int rc = 0; 757 int xid, i; 758 struct cifs_sb_info *cifs_sb; 759 struct cifsTconInfo *pTcon; 760 struct cifsFileInfo *cifsFile = NULL; 761 char *current_entry; 762 int num_to_fill = 0; 763 char *tmp_buf = NULL; 764 char *end_of_smb; 765 unsigned int max_len; 766 767 xid = GetXid(); 768 769 cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); 770 pTcon = cifs_sb->tcon; 771 if (pTcon == NULL) 772 return -EINVAL; 773 774 switch ((int) file->f_pos) { 775 case 0: 776 if (filldir(direntry, ".", 1, file->f_pos, 777 file->f_path.dentry->d_inode->i_ino, DT_DIR) < 0) { 778 cERROR(1, "Filldir for current dir failed"); 779 rc = -ENOMEM; 780 break; 781 } 782 file->f_pos++; 783 case 1: 784 if (filldir(direntry, "..", 2, file->f_pos, 785 file->f_path.dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) { 786 cERROR(1, "Filldir for parent dir failed"); 787 rc = -ENOMEM; 788 break; 789 } 790 file->f_pos++; 791 default: 792 /* 1) If search is active, 793 is in current search buffer? 794 if it before then restart search 795 if after then keep searching till find it */ 796 797 if (file->private_data == NULL) { 798 rc = initiate_cifs_search(xid, file); 799 cFYI(1, "initiate cifs search rc %d", rc); 800 if (rc) { 801 FreeXid(xid); 802 return rc; 803 } 804 } 805 if (file->private_data == NULL) { 806 rc = -EINVAL; 807 FreeXid(xid); 808 return rc; 809 } 810 cifsFile = file->private_data; 811 if (cifsFile->srch_inf.endOfSearch) { 812 if (cifsFile->srch_inf.emptyDir) { 813 cFYI(1, "End of search, empty dir"); 814 rc = 0; 815 break; 816 } 817 } /* else { 818 cifsFile->invalidHandle = true; 819 CIFSFindClose(xid, pTcon, cifsFile->netfid); 820 } */ 821 822 rc = find_cifs_entry(xid, pTcon, file, 823 ¤t_entry, &num_to_fill); 824 if (rc) { 825 cFYI(1, "fce error %d", rc); 826 goto rddir2_exit; 827 } else if (current_entry != NULL) { 828 cFYI(1, "entry %lld found", file->f_pos); 829 } else { 830 cFYI(1, "could not find entry"); 831 goto rddir2_exit; 832 } 833 cFYI(1, "loop through %d times filling dir for net buf %p", 834 num_to_fill, cifsFile->srch_inf.ntwrk_buf_start); 835 max_len = smbCalcSize((struct smb_hdr *) 836 cifsFile->srch_inf.ntwrk_buf_start); 837 end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; 838 839 tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL); 840 if (tmp_buf == NULL) { 841 rc = -ENOMEM; 842 break; 843 } 844 845 for (i = 0; (i < num_to_fill) && (rc == 0); i++) { 846 if (current_entry == NULL) { 847 /* evaluate whether this case is an error */ 848 cERROR(1, "past SMB end, num to fill %d i %d", 849 num_to_fill, i); 850 break; 851 } 852 /* if buggy server returns . and .. late do 853 we want to check for that here? */ 854 rc = cifs_filldir(current_entry, file, 855 filldir, direntry, tmp_buf, max_len); 856 if (rc == -EOVERFLOW) { 857 rc = 0; 858 break; 859 } 860 861 file->f_pos++; 862 if (file->f_pos == 863 cifsFile->srch_inf.index_of_last_entry) { 864 cFYI(1, "last entry in buf at pos %lld %s", 865 file->f_pos, tmp_buf); 866 cifs_save_resume_key(current_entry, cifsFile); 867 break; 868 } else 869 current_entry = 870 nxt_dir_entry(current_entry, end_of_smb, 871 cifsFile->srch_inf.info_level); 872 } 873 kfree(tmp_buf); 874 break; 875 } /* end switch */ 876 877rddir2_exit: 878 FreeXid(xid); 879 return rc; 880} 881