| 1 | |
| 2 | |
| 3 | *
|
| 4 | * Directory search handling
|
| 5 | *
|
| 6 | * Copyright (C) International Business Machines Corp., 2004, 2008
|
| 7 | * Copyright (C) Red Hat, Inc., 2011
|
| 8 | * Author(s): Steve French ([email protected])
|
| 9 | *
|
| 10 |
|
| 11 | #include <linux/fs.h>
|
| 12 | #include <linux/namei.h>
|
| 13 | #include <linux/pagemap.h>
|
| 14 | #include <linux/slab.h>
|
| 15 | #include <linux/stat.h>
|
| 16 | #include "cifsglob.h"
|
| 17 | #include "cifsproto.h"
|
| 18 | #include "cifs_unicode.h"
|
| 19 | #include "cifs_debug.h"
|
| 20 | #include "cifs_fs_sb.h"
|
| 21 | #include "cifsfs.h"
|
| 22 | #include "smb2proto.h"
|
| 23 | #include "fs_context.h"
|
| 24 | #include "cached_dir.h"
|
| 25 | #include "reparse.h"
|
| 26 |
|
| 27 | |
| 28 | * To be safe - for UCS to UTF-8 with strings loaded with the rare long
|
| 29 | * characters alloc more to account for such multibyte target UTF-8
|
| 30 | * characters.
|
| 31 |
|
| 32 | #define UNICODE_NAME_MAX ((4 * NAME_MAX) + 2)
|
| 33 |
|
| 34 | #ifdef CONFIG_CIFS_DEBUG2
|
| 35 | static void dump_cifs_file_struct(struct file *file, char *label)
|
| 36 | {
|
| 37 | struct cifsFileInfo *cf;
|
| 38 |
|
| 39 | if (file) {
|
| 40 | cf = file->private_data;
|
| 41 | if (cf == NULL) {
|
| 42 | cifs_dbg(FYI, "empty cifs private file data\n");
|
| 43 | return;
|
| 44 | }
|
| 45 | if (cf->invalidHandle)
|
| 46 | cifs_dbg(FYI, "Invalid handle\n");
|
| 47 | if (cf->srch_inf.endOfSearch)
|
| 48 | cifs_dbg(FYI, "end of search\n");
|
| 49 | if (cf->srch_inf.emptyDir)
|
| 50 | cifs_dbg(FYI, "empty dir\n");
|
| 51 | }
|
| 52 | }
|
| 53 | #else
|
| 54 | static inline void dump_cifs_file_struct(struct file *file, char *label)
|
| 55 | {
|
| 56 | }
|
| 57 | #endif
|
| 58 |
|
| 59 | |
| 60 | * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT
|
| 61 | *
|
| 62 | * Find the dentry that matches "name". If there isn't one, create one. If it's
|
| 63 | * a negative dentry or the uniqueid or filetype(mode) changed,
|
| 64 | * then drop it and recreate it.
|
| 65 |
|
| 66 | static void
|
| 67 | cifs_prime_dcache(struct dentry *parent, struct qstr *name,
|
| 68 | struct cifs_fattr *fattr)
|
| 69 | {
|
| 70 | struct dentry *dentry, *alias;
|
| 71 | struct inode *inode;
|
| 72 | struct super_block *sb = parent->d_sb;
|
| 73 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
| 74 | bool posix = cifs_sb_master_tcon(cifs_sb)->posix_extensions;
|
| 75 | bool reparse_need_reval = false;
|
| 76 | int rc;
|
| 77 |
|
| 78 | cifs_dbg(FYI, "%s: for %s\n", __func__, name->name);
|
| 79 |
|
| 80 | dentry = try_lookup_noperm(name, parent);
|
| 81 | if (!dentry) {
|
| 82 | |
| 83 | * If we know that the inode will need to be revalidated
|
| 84 | * immediately, then don't create a new dentry for it.
|
| 85 | * We'll end up doing an on the wire call either way and
|
| 86 | * this spares us an invalidation.
|
| 87 |
|
| 88 | retry:
|
| 89 | if (posix) {
|
| 90 | switch (fattr->cf_mode & S_IFMT) {
|
| 91 | case S_IFLNK:
|
| 92 | case S_IFBLK:
|
| 93 | case S_IFCHR:
|
| 94 | reparse_need_reval = true;
|
| 95 | break;
|
| 96 | default:
|
| 97 | break;
|
| 98 | }
|
| 99 | } else if (fattr->cf_cifsattrs & ATTR_REPARSE_POINT) {
|
| 100 | reparse_need_reval = true;
|
| 101 | }
|
| 102 |
|
| 103 | if (reparse_need_reval ||
|
| 104 | (fattr->cf_flags & CIFS_FATTR_NEED_REVAL))
|
| 105 | return;
|
| 106 |
|
| 107 | dentry = d_alloc_parallel(parent, name);
|
| 108 | }
|
| 109 | if (IS_ERR(dentry))
|
| 110 | return;
|
| 111 | if (!d_in_lookup(dentry)) {
|
| 112 | inode = d_inode(dentry);
|
| 113 | if (inode) {
|
| 114 | if (d_mountpoint(dentry)) {
|
| 115 | dput(dentry);
|
| 116 | return;
|
| 117 | }
|
| 118 | |
| 119 | * If we're generating inode numbers, then we don't
|
| 120 | * want to clobber the existing one with the one that
|
| 121 | * the readdir code created.
|
| 122 |
|
| 123 | if (!(cifs_sb_flags(cifs_sb) & CIFS_MOUNT_SERVER_INUM))
|
| 124 | fattr->cf_uniqueid = CIFS_I(inode)->uniqueid;
|
| 125 |
|
| 126 | |
| 127 | * Update inode in place if both i_ino and i_mode didn't
|
| 128 | * change.
|
| 129 |
|
| 130 | if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid) {
|
| 131 | |
| 132 | * Query dir responses don't provide enough
|
| 133 | * information about reparse points other than
|
| 134 | * their reparse tags. Save an invalidation by
|
| 135 | * not clobbering some existing attributes when
|
| 136 | * reparse tag and ctime haven't changed.
|
| 137 |
|
| 138 | rc = 0;
|
| 139 | if (fattr->cf_cifsattrs & ATTR_REPARSE_POINT) {
|
| 140 | if (likely(reparse_inode_match(inode, fattr))) {
|
| 141 | fattr->cf_mode = inode->i_mode;
|
| 142 | fattr->cf_rdev = inode->i_rdev;
|
| 143 | fattr->cf_uid = inode->i_uid;
|
| 144 | fattr->cf_gid = inode->i_gid;
|
| 145 | fattr->cf_eof =
|
| 146 | netfs_read_remote_i_size(inode);
|
| 147 | fattr->cf_symlink_target = NULL;
|
| 148 | } else {
|
| 149 | CIFS_I(inode)->time = 0;
|
| 150 | rc = -ESTALE;
|
| 151 | }
|
| 152 | }
|
| 153 | if (!rc && !cifs_fattr_to_inode(inode, fattr, true)) {
|
| 154 | dput(dentry);
|
| 155 | return;
|
| 156 | }
|
| 157 | }
|
| 158 | }
|
| 159 | d_invalidate(dentry);
|
| 160 | dput(dentry);
|
| 161 | goto retry;
|
| 162 | } else {
|
| 163 | inode = cifs_iget(sb, fattr);
|
| 164 | if (!inode)
|
| 165 | inode = ERR_PTR(-ENOMEM);
|
| 166 | alias = d_splice_alias(inode, dentry);
|
| 167 | d_lookup_done(dentry);
|
| 168 | if (alias && !IS_ERR(alias))
|
| 169 | dput(alias);
|
| 170 | }
|
| 171 | dput(dentry);
|
| 172 | }
|
| 173 |
|
| 174 | static void
|
| 175 | cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
|
| 176 | {
|
| 177 | struct cifs_open_info_data data = {
|
| 178 | .reparse = { .tag = fattr->cf_cifstag, },
|
| 179 | };
|
| 180 | unsigned int sbflags;
|
| 181 |
|
| 182 | fattr->cf_uid = cifs_sb->ctx->linux_uid;
|
| 183 | fattr->cf_gid = cifs_sb->ctx->linux_gid;
|
| 184 |
|
| 185 | |
| 186 | * The IO_REPARSE_TAG_LX_ tags originally were used by WSL but they
|
| 187 | * are preferred by the Linux client in some cases since, unlike
|
| 188 | * the NFS reparse tag (or EAs), they don't require an extra query
|
| 189 | * to determine which type of special file they represent.
|
| 190 | * TODO: go through all documented reparse tags to see if we can
|
| 191 | * reasonably map some of them to directories vs. files vs. symlinks
|
| 192 |
|
| 193 | if ((fattr->cf_cifsattrs & ATTR_REPARSE_POINT) &&
|
| 194 | cifs_reparse_point_to_fattr(cifs_sb, fattr, &data))
|
| 195 | goto out_reparse;
|
| 196 |
|
| 197 | if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
|
| 198 | fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode;
|
| 199 | fattr->cf_dtype = DT_DIR;
|
| 200 | } else {
|
| 201 | fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode;
|
| 202 | fattr->cf_dtype = DT_REG;
|
| 203 | }
|
| 204 |
|
| 205 | out_reparse:
|
| 206 |
|
| 207 | fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
|
| 208 |
|
| 209 | if (fattr->cf_cifsattrs & ATTR_READONLY)
|
| 210 | fattr->cf_mode &= ~S_IWUGO;
|
| 211 |
|
| 212 | |
| 213 | * We of course don't get ACL info in FIND_FIRST/NEXT results, so
|
| 214 | * mark it for revalidation so that "ls -l" will look right. It might
|
| 215 | * be super-slow, but if we don't do this then the ownership of files
|
| 216 | * may look wrong since the inodes may not have timed out by the time
|
| 217 | * "ls" does a stat() call on them.
|
| 218 |
|
| 219 | sbflags = cifs_sb_flags(cifs_sb);
|
| 220 | if (sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID))
|
| 221 | fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
|
| 222 |
|
| 223 | if ((sbflags & CIFS_MOUNT_UNX_EMUL) &&
|
| 224 | (fattr->cf_cifsattrs & ATTR_SYSTEM)) {
|
| 225 | if (fattr->cf_eof == 0) {
|
| 226 | fattr->cf_mode &= ~S_IFMT;
|
| 227 | fattr->cf_mode |= S_IFIFO;
|
| 228 | fattr->cf_dtype = DT_FIFO;
|
| 229 | } else {
|
| 230 | |
| 231 | * trying to get the type and mode via SFU can be slow,
|
| 232 | * so just call those regular files for now, and mark
|
| 233 | * for reval
|
| 234 |
|
| 235 | fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
|
| 236 | }
|
| 237 | }
|
| 238 | }
|
| 239 |
|
| 240 |
|
| 241 | static void
|
| 242 | cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info,
|
| 243 | struct cifs_sb_info *cifs_sb)
|
| 244 | {
|
| 245 | unsigned int sbflags = cifs_sb_flags(cifs_sb);
|
| 246 | struct smb2_posix_info_parsed parsed;
|
| 247 | int rc;
|
| 248 |
|
| 249 | rc = posix_info_parse(info, NULL, &parsed);
|
| 250 |
|
| 251 | memset(fattr, 0, sizeof(*fattr));
|
| 252 | fattr->cf_uniqueid = le64_to_cpu(info->Inode);
|
| 253 | fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
|
| 254 | fattr->cf_eof = le64_to_cpu(info->EndOfFile);
|
| 255 |
|
| 256 | fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
|
| 257 | fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
|
| 258 | fattr->cf_ctime = cifs_NTtimeToUnix(info->CreationTime);
|
| 259 |
|
| 260 | fattr->cf_nlink = le32_to_cpu(info->HardLinks);
|
| 261 | fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes);
|
| 262 |
|
| 263 | if (fattr->cf_cifsattrs & ATTR_REPARSE_POINT)
|
| 264 | fattr->cf_cifstag = le32_to_cpu(info->ReparseTag);
|
| 265 |
|
| 266 |
|
| 267 | fattr->cf_mode = wire_mode_to_posix(le32_to_cpu(info->Mode),
|
| 268 | fattr->cf_cifsattrs & ATTR_DIRECTORY);
|
| 269 | fattr->cf_dtype = S_DT(fattr->cf_mode);
|
| 270 |
|
| 271 | switch (fattr->cf_mode & S_IFMT) {
|
| 272 | case S_IFLNK:
|
| 273 | case S_IFBLK:
|
| 274 | case S_IFCHR:
|
| 275 | fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
|
| 276 | break;
|
| 277 | default:
|
| 278 | break;
|
| 279 | }
|
| 280 |
|
| 281 | cifs_dbg(FYI, "posix fattr: dev %d, reparse %d, mode %o\n",
|
| 282 | le32_to_cpu(info->DeviceId),
|
| 283 | le32_to_cpu(info->ReparseTag),
|
| 284 | le32_to_cpu(info->Mode));
|
| 285 |
|
| 286 | fattr->cf_uid = cifs_sb->ctx->linux_uid;
|
| 287 | fattr->cf_gid = cifs_sb->ctx->linux_gid;
|
| 288 | if (rc < 0) {
|
| 289 | cifs_dbg(VFS, "%s: failed to parse SIDs: %d\n",
|
| 290 | __func__, rc);
|
| 291 | } else {
|
| 292 | if (!(sbflags & CIFS_MOUNT_OVERR_UID))
|
| 293 | sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER);
|
| 294 | if (!(sbflags & CIFS_MOUNT_OVERR_GID))
|
| 295 | sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP);
|
| 296 | }
|
| 297 | }
|
| 298 |
|
| 299 | static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info)
|
| 300 | {
|
| 301 | const FILE_DIRECTORY_INFO *fi = info;
|
| 302 |
|
| 303 | memset(fattr, 0, sizeof(*fattr));
|
| 304 | fattr->cf_cifsattrs = le32_to_cpu(fi->ExtFileAttributes);
|
| 305 | fattr->cf_eof = le64_to_cpu(fi->EndOfFile);
|
| 306 | fattr->cf_bytes = le64_to_cpu(fi->AllocationSize);
|
| 307 | fattr->cf_createtime = le64_to_cpu(fi->CreationTime);
|
| 308 | fattr->cf_atime = cifs_NTtimeToUnix(fi->LastAccessTime);
|
| 309 | fattr->cf_ctime = cifs_NTtimeToUnix(fi->ChangeTime);
|
| 310 | fattr->cf_mtime = cifs_NTtimeToUnix(fi->LastWriteTime);
|
| 311 | }
|
| 312 |
|
| 313 | void
|
| 314 | cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
|
| 315 | struct cifs_sb_info *cifs_sb)
|
| 316 | {
|
| 317 | __dir_info_to_fattr(fattr, info);
|
| 318 | cifs_fill_common_info(fattr, cifs_sb);
|
| 319 | }
|
| 320 |
|
| 321 | static void cifs_fulldir_info_to_fattr(struct cifs_fattr *fattr,
|
| 322 | const void *info,
|
| 323 | struct cifs_sb_info *cifs_sb)
|
| 324 | {
|
| 325 | const FILE_FULL_DIRECTORY_INFO *di = info;
|
| 326 |
|
| 327 | __dir_info_to_fattr(fattr, info);
|
| 328 |
|
| 329 |
|
| 330 | if (fattr->cf_cifsattrs & ATTR_REPARSE_POINT)
|
| 331 | fattr->cf_cifstag = le32_to_cpu(di->EaSize);
|
| 332 | cifs_fill_common_info(fattr, cifs_sb);
|
| 333 | }
|
| 334 |
|
| 335 | static void
|
| 336 | cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info,
|
| 337 | struct cifs_sb_info *cifs_sb)
|
| 338 | {
|
| 339 | int offset = cifs_sb_master_tcon(cifs_sb)->ses->server->timeAdj;
|
| 340 |
|
| 341 | memset(fattr, 0, sizeof(*fattr));
|
| 342 | fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate,
|
| 343 | info->LastAccessTime, offset);
|
| 344 | fattr->cf_ctime = cnvrtDosUnixTm(info->LastWriteDate,
|
| 345 | info->LastWriteTime, offset);
|
| 346 | fattr->cf_mtime = cnvrtDosUnixTm(info->LastWriteDate,
|
| 347 | info->LastWriteTime, offset);
|
| 348 |
|
| 349 | fattr->cf_cifsattrs = le16_to_cpu(info->Attributes);
|
| 350 | fattr->cf_bytes = le32_to_cpu(info->AllocationSize);
|
| 351 | fattr->cf_eof = le32_to_cpu(info->DataSize);
|
| 352 |
|
| 353 | cifs_fill_common_info(fattr, cifs_sb);
|
| 354 | }
|
| 355 |
|
| 356 | static int
|
| 357 | _initiate_cifs_search(const unsigned int xid, struct file *file,
|
| 358 | const char *full_path)
|
| 359 | {
|
| 360 | struct cifs_sb_info *cifs_sb = CIFS_SB(file);
|
| 361 | struct tcon_link *tlink = NULL;
|
| 362 | struct TCP_Server_Info *server;
|
| 363 | struct cifsFileInfo *cifsFile;
|
| 364 | struct cifs_tcon *tcon;
|
| 365 | unsigned int sbflags;
|
| 366 | __u16 search_flags;
|
| 367 | int rc = 0;
|
| 368 |
|
| 369 | if (file->private_data == NULL) {
|
| 370 | tlink = cifs_sb_tlink(cifs_sb);
|
| 371 | if (IS_ERR(tlink))
|
| 372 | return PTR_ERR(tlink);
|
| 373 |
|
| 374 | cifsFile = kzalloc_obj(struct cifsFileInfo);
|
| 375 | if (cifsFile == NULL) {
|
| 376 | rc = -ENOMEM;
|
| 377 | goto error_exit;
|
| 378 | }
|
| 379 | spin_lock_init(&cifsFile->file_info_lock);
|
| 380 | file->private_data = cifsFile;
|
| 381 | cifsFile->tlink = cifs_get_tlink(tlink);
|
| 382 | tcon = tlink_tcon(tlink);
|
| 383 | } else {
|
| 384 | cifsFile = file->private_data;
|
| 385 | tcon = tlink_tcon(cifsFile->tlink);
|
| 386 | }
|
| 387 |
|
| 388 | server = tcon->ses->server;
|
| 389 |
|
| 390 | if (!server->ops->query_dir_first) {
|
| 391 | rc = -ENOSYS;
|
| 392 | goto error_exit;
|
| 393 | }
|
| 394 |
|
| 395 | cifsFile->invalidHandle = true;
|
| 396 | cifsFile->srch_inf.endOfSearch = false;
|
| 397 |
|
| 398 | cifs_dbg(FYI, "Full path: %s start at: %lld\n", full_path, file->f_pos);
|
| 399 |
|
| 400 | ffirst_retry:
|
| 401 | sbflags = cifs_sb_flags(cifs_sb);
|
| 402 |
|
| 403 |
|
| 404 |
|
| 405 | if (tcon->unix_ext)
|
| 406 | cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
|
| 407 | else if (tcon->posix_extensions)
|
| 408 | cifsFile->srch_inf.info_level = SMB_FIND_FILE_POSIX_INFO;
|
| 409 | else if ((tcon->ses->capabilities &
|
| 410 | tcon->ses->server->vals->cap_nt_find) == 0) {
|
| 411 | cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
|
| 412 | } else if (sbflags & CIFS_MOUNT_SERVER_INUM) {
|
| 413 | cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
|
| 414 | } else {
|
| 415 | cifsFile->srch_inf.info_level = SMB_FIND_FILE_FULL_DIRECTORY_INFO;
|
| 416 | }
|
| 417 |
|
| 418 | search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
|
| 419 | if (backup_cred(cifs_sb))
|
| 420 | search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
|
| 421 |
|
| 422 | rc = server->ops->query_dir_first(xid, tcon, full_path, cifs_sb,
|
| 423 | &cifsFile->fid, search_flags,
|
| 424 | &cifsFile->srch_inf);
|
| 425 |
|
| 426 | if (rc == 0) {
|
| 427 | cifsFile->invalidHandle = false;
|
| 428 | } else if (rc == -EOPNOTSUPP && (sbflags & CIFS_MOUNT_SERVER_INUM)) {
|
| 429 | cifs_autodisable_serverino(cifs_sb, "Cannot retrieve inode number via query_dir_first", rc);
|
| 430 | goto ffirst_retry;
|
| 431 | }
|
| 432 | error_exit:
|
| 433 | cifs_put_tlink(tlink);
|
| 434 | return rc;
|
| 435 | }
|
| 436 |
|
| 437 | static int
|
| 438 | initiate_cifs_search(const unsigned int xid, struct file *file,
|
| 439 | const char *full_path)
|
| 440 | {
|
| 441 | int rc, retry_count = 0;
|
| 442 |
|
| 443 | do {
|
| 444 | rc = _initiate_cifs_search(xid, file, full_path);
|
| 445 | |
| 446 | * If we don't have enough credits to start reading the
|
| 447 | * directory just try again after short wait.
|
| 448 |
|
| 449 | if (rc != -EDEADLK)
|
| 450 | break;
|
| 451 |
|
| 452 | usleep_range(512, 2048);
|
| 453 | } while (retry_count++ < 5);
|
| 454 |
|
| 455 | return rc;
|
| 456 | }
|
| 457 |
|
| 458 |
|
| 459 | static size_t cifs_unicode_bytelen(const char *str, size_t maxlen)
|
| 460 | {
|
| 461 | const __le16 *ustr = (const __le16 *)str;
|
| 462 | size_t len;
|
| 463 |
|
| 464 | maxlen = umin(PATH_MAX + 1, maxlen / sizeof(__le16));
|
| 465 | for (len = 0; len < maxlen; len++) {
|
| 466 | if (ustr[len] == 0)
|
| 467 | return len * sizeof(__le16);
|
| 468 | }
|
| 469 | len *= sizeof(__le16);
|
| 470 | cifs_dbg(FYI, "%s: Unicode string not terminated within %zu bytes\n",
|
| 471 | __func__, len);
|
| 472 | return len;
|
| 473 | }
|
| 474 |
|
| 475 | static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
|
| 476 | {
|
| 477 | char *new_entry;
|
| 478 | FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry;
|
| 479 |
|
| 480 | if (level == SMB_FIND_FILE_INFO_STANDARD) {
|
| 481 | FIND_FILE_STANDARD_INFO *pfData;
|
| 482 | pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo;
|
| 483 |
|
| 484 | new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) + 1 +
|
| 485 | pfData->FileNameLength;
|
| 486 | } else {
|
| 487 | u32 next_offset = le32_to_cpu(pDirInfo->NextEntryOffset);
|
| 488 |
|
| 489 | if (old_entry + next_offset < old_entry) {
|
| 490 | cifs_dbg(VFS, "Invalid offset %u\n", next_offset);
|
| 491 | return NULL;
|
| 492 | }
|
| 493 | new_entry = old_entry + next_offset;
|
| 494 | }
|
| 495 | cifs_dbg(FYI, "new entry %p old entry %p\n", new_entry, old_entry);
|
| 496 |
|
| 497 | if (new_entry >= end_of_smb) {
|
| 498 | cifs_dbg(VFS, "search entry %p began after end of SMB %p old entry %p\n",
|
| 499 | new_entry, end_of_smb, old_entry);
|
| 500 | return NULL;
|
| 501 | } else if (((level == SMB_FIND_FILE_INFO_STANDARD) &&
|
| 502 | (new_entry + sizeof(FIND_FILE_STANDARD_INFO) + 1 > end_of_smb))
|
| 503 | || ((level != SMB_FIND_FILE_INFO_STANDARD) &&
|
| 504 | (new_entry + sizeof(FILE_DIRECTORY_INFO) + 1 > end_of_smb))) {
|
| 505 | cifs_dbg(VFS, "search entry %p extends after end of SMB %p\n",
|
| 506 | new_entry, end_of_smb);
|
| 507 | return NULL;
|
| 508 | } else
|
| 509 | return new_entry;
|
| 510 |
|
| 511 | }
|
| 512 |
|
| 513 | struct cifs_dirent {
|
| 514 | const char *name;
|
| 515 | size_t namelen;
|
| 516 | u32 resume_key;
|
| 517 | u64 ino;
|
| 518 | };
|
| 519 |
|
| 520 | static void cifs_fill_dirent_posix(struct cifs_dirent *de,
|
| 521 | const struct smb2_posix_info *info)
|
| 522 | {
|
| 523 | struct smb2_posix_info_parsed parsed;
|
| 524 |
|
| 525 |
|
| 526 | if (posix_info_parse(info, NULL, &parsed) < 0) {
|
| 527 | cifs_dbg(VFS, "Invalid POSIX info payload\n");
|
| 528 | return;
|
| 529 | }
|
| 530 |
|
| 531 | de->name = parsed.name;
|
| 532 | de->namelen = parsed.name_len;
|
| 533 | de->resume_key = info->Ignored;
|
| 534 | de->ino = le64_to_cpu(info->Inode);
|
| 535 | }
|
| 536 |
|
| 537 | static void cifs_fill_dirent_unix(struct cifs_dirent *de,
|
| 538 | const FILE_UNIX_INFO *info, bool is_unicode, size_t maxlen)
|
| 539 | {
|
| 540 | de->name = &info->FileName[0];
|
| 541 | if (is_unicode)
|
| 542 | de->namelen = cifs_unicode_bytelen(de->name, maxlen);
|
| 543 | else
|
| 544 | de->namelen = strnlen(de->name, min_t(size_t, maxlen, PATH_MAX));
|
| 545 | de->resume_key = info->ResumeKey;
|
| 546 | de->ino = le64_to_cpu(info->basic.UniqueId);
|
| 547 | }
|
| 548 |
|
| 549 | static void cifs_fill_dirent_dir(struct cifs_dirent *de,
|
| 550 | const FILE_DIRECTORY_INFO *info)
|
| 551 | {
|
| 552 | de->name = &info->FileName[0];
|
| 553 | de->namelen = le32_to_cpu(info->FileNameLength);
|
| 554 | de->resume_key = info->FileIndex;
|
| 555 | }
|
| 556 |
|
| 557 | static void cifs_fill_dirent_full(struct cifs_dirent *de,
|
| 558 | const FILE_FULL_DIRECTORY_INFO *info)
|
| 559 | {
|
| 560 | de->name = &info->FileName[0];
|
| 561 | de->namelen = le32_to_cpu(info->FileNameLength);
|
| 562 | de->resume_key = info->FileIndex;
|
| 563 | }
|
| 564 |
|
| 565 | static void cifs_fill_dirent_search(struct cifs_dirent *de,
|
| 566 | const FILE_ID_FULL_DIR_INFO *info)
|
| 567 | {
|
| 568 | de->name = &info->FileName[0];
|
| 569 | de->namelen = le32_to_cpu(info->FileNameLength);
|
| 570 | de->resume_key = info->FileIndex;
|
| 571 | de->ino = le64_to_cpu(info->UniqueId);
|
| 572 | }
|
| 573 |
|
| 574 | static void cifs_fill_dirent_both(struct cifs_dirent *de,
|
| 575 | const FILE_BOTH_DIRECTORY_INFO *info)
|
| 576 | {
|
| 577 | de->name = &info->FileName[0];
|
| 578 | de->namelen = le32_to_cpu(info->FileNameLength);
|
| 579 | de->resume_key = info->FileIndex;
|
| 580 | }
|
| 581 |
|
| 582 | static void cifs_fill_dirent_std(struct cifs_dirent *de,
|
| 583 | const FIND_FILE_STANDARD_INFO *info)
|
| 584 | {
|
| 585 | de->name = &info->FileName[0];
|
| 586 |
|
| 587 | de->namelen = info->FileNameLength;
|
| 588 | de->resume_key = info->ResumeKey;
|
| 589 | }
|
| 590 |
|
| 591 | static int cifs_fill_dirent(struct cifs_dirent *de, const void *info,
|
| 592 | const char *end, u16 level, bool is_unicode)
|
| 593 | {
|
| 594 | size_t len = end > (const char *)info ? end - (const char *)info : 0;
|
| 595 | size_t nul_len = 0;
|
| 596 |
|
| 597 | memset(de, 0, sizeof(*de));
|
| 598 |
|
| 599 | switch (level) {
|
| 600 | case SMB_FIND_FILE_POSIX_INFO:
|
| 601 | if (len < sizeof(struct smb2_posix_info))
|
| 602 | goto too_short;
|
| 603 | cifs_fill_dirent_posix(de, info);
|
| 604 | break;
|
| 605 | case SMB_FIND_FILE_UNIX:
|
| 606 | if (len < offsetof(FILE_UNIX_INFO, FileName))
|
| 607 | goto too_short;
|
| 608 | cifs_fill_dirent_unix(de, info, is_unicode,
|
| 609 | len - offsetof(FILE_UNIX_INFO, FileName));
|
| 610 | nul_len = is_unicode ? sizeof(__le16) : sizeof(__u8);
|
| 611 | break;
|
| 612 | case SMB_FIND_FILE_DIRECTORY_INFO:
|
| 613 | if (len < offsetof(FILE_DIRECTORY_INFO, FileName))
|
| 614 | goto too_short;
|
| 615 | cifs_fill_dirent_dir(de, info);
|
| 616 | break;
|
| 617 | case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
|
| 618 | if (len < offsetof(FILE_FULL_DIRECTORY_INFO, FileName))
|
| 619 | goto too_short;
|
| 620 | cifs_fill_dirent_full(de, info);
|
| 621 | break;
|
| 622 | case SMB_FIND_FILE_ID_FULL_DIR_INFO:
|
| 623 | if (len < offsetof(FILE_ID_FULL_DIR_INFO, FileName))
|
| 624 | goto too_short;
|
| 625 | cifs_fill_dirent_search(de, info);
|
| 626 | break;
|
| 627 | case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
|
| 628 | if (len < offsetof(FILE_BOTH_DIRECTORY_INFO, FileName))
|
| 629 | goto too_short;
|
| 630 | cifs_fill_dirent_both(de, info);
|
| 631 | break;
|
| 632 | case SMB_FIND_FILE_INFO_STANDARD:
|
| 633 | if (len < offsetof(FIND_FILE_STANDARD_INFO, FileName))
|
| 634 | goto too_short;
|
| 635 | cifs_fill_dirent_std(de, info);
|
| 636 | break;
|
| 637 | default:
|
| 638 | cifs_dbg(FYI, "Unknown findfirst level %d\n", level);
|
| 639 | return -EINVAL;
|
| 640 | }
|
| 641 |
|
| 642 | if (de->name &&
|
| 643 | (end < de->name ||
|
| 644 | (size_t)(end - de->name) < de->namelen + nul_len)) {
|
| 645 | cifs_dbg(VFS, "%s: search entry name extends past end of SMB\n",
|
| 646 | __func__);
|
| 647 | return -EINVAL;
|
| 648 | }
|
| 649 |
|
| 650 | return 0;
|
| 651 |
|
| 652 | too_short:
|
| 653 | cifs_dbg(VFS, "%s: search entry extends past end of SMB\n",
|
| 654 | __func__);
|
| 655 | return -EINVAL;
|
| 656 | }
|
| 657 |
|
| 658 | #define UNICODE_DOT cpu_to_le16(0x2e)
|
| 659 |
|
| 660 |
|
| 661 | static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode)
|
| 662 | {
|
| 663 | int rc = 0;
|
| 664 |
|
| 665 | if (!de->name)
|
| 666 | return 0;
|
| 667 |
|
| 668 | if (is_unicode) {
|
| 669 | __le16 *ufilename = (__le16 *)de->name;
|
| 670 | if (de->namelen == 2) {
|
| 671 |
|
| 672 | if (ufilename[0] == UNICODE_DOT)
|
| 673 | rc = 1;
|
| 674 | } else if (de->namelen == 4) {
|
| 675 |
|
| 676 | if (ufilename[0] == UNICODE_DOT &&
|
| 677 | ufilename[1] == UNICODE_DOT)
|
| 678 | rc = 2;
|
| 679 | }
|
| 680 | } else {
|
| 681 | if (de->namelen == 1) {
|
| 682 | if (de->name[0] == '.')
|
| 683 | rc = 1;
|
| 684 | } else if (de->namelen == 2) {
|
| 685 | if (de->name[0] == '.' && de->name[1] == '.')
|
| 686 | rc = 2;
|
| 687 | }
|
| 688 | }
|
| 689 |
|
| 690 | return rc;
|
| 691 | }
|
| 692 |
|
| 693 | |
| 694 | whether we can use the cached search results from the previous search
|
| 695 | static int is_dir_changed(struct file *file)
|
| 696 | {
|
| 697 | struct inode *inode = file_inode(file);
|
| 698 | struct cifsInodeInfo *cifs_inode_info = CIFS_I(inode);
|
| 699 |
|
| 700 | if (cifs_inode_info->time == 0)
|
| 701 | return 1;
|
| 702 | else
|
| 703 | return 0;
|
| 704 |
|
| 705 | }
|
| 706 |
|
| 707 | static int cifs_save_resume_key(const char *current_entry,
|
| 708 | struct cifsFileInfo *file_info)
|
| 709 | {
|
| 710 | struct TCP_Server_Info *server = tlink_tcon(file_info->tlink)->ses->server;
|
| 711 | char *buf = file_info->srch_inf.ntwrk_buf_start;
|
| 712 | const char *end = buf + server->ops->calc_smb_size(buf);
|
| 713 | struct cifs_dirent de;
|
| 714 | int rc;
|
| 715 |
|
| 716 | rc = cifs_fill_dirent(&de, current_entry, end,
|
| 717 | file_info->srch_inf.info_level,
|
| 718 | file_info->srch_inf.unicode);
|
| 719 | if (!rc) {
|
| 720 | file_info->srch_inf.presume_name = de.name;
|
| 721 | file_info->srch_inf.resume_name_len = de.namelen;
|
| 722 | file_info->srch_inf.resume_key = de.resume_key;
|
| 723 | }
|
| 724 | return rc;
|
| 725 | }
|
| 726 |
|
| 727 | |
| 728 | * Find the corresponding entry in the search. Note that the SMB server returns
|
| 729 | * search entries for . and .. which complicates logic here if we choose to
|
| 730 | * parse for them and we do not assume that they are located in the findfirst
|
| 731 | * return buffer. We start counting in the buffer with entry 2 and increment for
|
| 732 | * every entry (do not increment for . or .. entry).
|
| 733 |
|
| 734 | static int
|
| 735 | find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
|
| 736 | struct file *file, const char *full_path,
|
| 737 | char **current_entry, int *num_to_ret)
|
| 738 | {
|
| 739 | __u16 search_flags;
|
| 740 | int rc = 0;
|
| 741 | int pos_in_buf = 0;
|
| 742 | loff_t first_entry_in_buffer;
|
| 743 | loff_t index_to_find = pos;
|
| 744 | struct cifsFileInfo *cfile = file->private_data;
|
| 745 | struct cifs_sb_info *cifs_sb = CIFS_SB(file);
|
| 746 | struct TCP_Server_Info *server = tcon->ses->server;
|
| 747 |
|
| 748 |
|
| 749 | if (!server->ops->query_dir_first || !server->ops->query_dir_next)
|
| 750 | return -ENOSYS;
|
| 751 |
|
| 752 | if ((cfile == NULL) || (current_entry == NULL) || (num_to_ret == NULL))
|
| 753 | return -ENOENT;
|
| 754 |
|
| 755 | *current_entry = NULL;
|
| 756 | first_entry_in_buffer = cfile->srch_inf.index_of_last_entry -
|
| 757 | cfile->srch_inf.entries_in_buffer;
|
| 758 |
|
| 759 | |
| 760 | * If first entry in buf is zero then is first buffer
|
| 761 | * in search response data which means it is likely . and ..
|
| 762 | * will be in this buffer, although some servers do not return
|
| 763 | * . and .. for the root of a drive and for those we need
|
| 764 | * to start two entries earlier.
|
| 765 |
|
| 766 |
|
| 767 | dump_cifs_file_struct(file, "In fce ");
|
| 768 | if (((index_to_find < cfile->srch_inf.index_of_last_entry) &&
|
| 769 | is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) {
|
| 770 |
|
| 771 | cifs_dbg(FYI, "search backing up - close and restart search\n");
|
| 772 | spin_lock(&cfile->file_info_lock);
|
| 773 | if (server->ops->dir_needs_close(cfile)) {
|
| 774 | cfile->invalidHandle = true;
|
| 775 | spin_unlock(&cfile->file_info_lock);
|
| 776 | if (server->ops->close_dir)
|
| 777 | server->ops->close_dir(xid, tcon, &cfile->fid);
|
| 778 | } else
|
| 779 | spin_unlock(&cfile->file_info_lock);
|
| 780 | if (cfile->srch_inf.ntwrk_buf_start) {
|
| 781 | cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n");
|
| 782 | if (cfile->srch_inf.smallBuf)
|
| 783 | cifs_small_buf_release(cfile->srch_inf.
|
| 784 | ntwrk_buf_start);
|
| 785 | else if (cfile->srch_inf.is_dynamic_buf)
|
| 786 | kfree(cfile->srch_inf.ntwrk_buf_start);
|
| 787 | else
|
| 788 | cifs_buf_release(cfile->srch_inf.
|
| 789 | ntwrk_buf_start);
|
| 790 |
|
| 791 | cfile->srch_inf.ntwrk_buf_start = NULL;
|
| 792 | cfile->srch_inf.srch_entries_start = NULL;
|
| 793 | cfile->srch_inf.last_entry = NULL;
|
| 794 | cfile->srch_inf.presume_name = NULL;
|
| 795 | cfile->srch_inf.resume_name_len = 0;
|
| 796 | cfile->srch_inf.resume_key = 0;
|
| 797 | }
|
| 798 | rc = initiate_cifs_search(xid, file, full_path);
|
| 799 | if (rc) {
|
| 800 | cifs_dbg(FYI, "error %d reinitiating a search on rewind\n",
|
| 801 | rc);
|
| 802 | return rc;
|
| 803 | }
|
| 804 |
|
| 805 | if (cfile->srch_inf.last_entry)
|
| 806 | cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
|
| 807 | }
|
| 808 |
|
| 809 | search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
|
| 810 | if (backup_cred(cifs_sb))
|
| 811 | search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
|
| 812 |
|
| 813 | while ((index_to_find >= cfile->srch_inf.index_of_last_entry) &&
|
| 814 | (rc == 0) && !cfile->srch_inf.endOfSearch) {
|
| 815 | cifs_dbg(FYI, "calling findnext2\n");
|
| 816 | rc = server->ops->query_dir_next(xid, tcon, &cfile->fid,
|
| 817 | search_flags,
|
| 818 | &cfile->srch_inf);
|
| 819 | if (rc)
|
| 820 | return -ENOENT;
|
| 821 |
|
| 822 | if (cfile->srch_inf.last_entry)
|
| 823 | cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
|
| 824 | }
|
| 825 | if (index_to_find < cfile->srch_inf.index_of_last_entry) {
|
| 826 |
|
| 827 |
|
| 828 | int i;
|
| 829 | char *cur_ent;
|
| 830 | char *end_of_smb;
|
| 831 |
|
| 832 | if (cfile->srch_inf.ntwrk_buf_start == NULL) {
|
| 833 | cifs_dbg(VFS, "ntwrk_buf_start is NULL during readdir\n");
|
| 834 | return smb_EIO(smb_eio_trace_null_pointers);
|
| 835 | }
|
| 836 |
|
| 837 | end_of_smb = cfile->srch_inf.ntwrk_buf_start +
|
| 838 | server->ops->calc_smb_size(
|
| 839 | cfile->srch_inf.ntwrk_buf_start);
|
| 840 |
|
| 841 | cur_ent = cfile->srch_inf.srch_entries_start;
|
| 842 | first_entry_in_buffer = cfile->srch_inf.index_of_last_entry
|
| 843 | - cfile->srch_inf.entries_in_buffer;
|
| 844 | pos_in_buf = index_to_find - first_entry_in_buffer;
|
| 845 | cifs_dbg(FYI, "found entry - pos_in_buf %d\n", pos_in_buf);
|
| 846 |
|
| 847 | for (i = 0; (i < (pos_in_buf)) && (cur_ent != NULL); i++) {
|
| 848 |
|
| 849 | cur_ent = nxt_dir_entry(cur_ent, end_of_smb,
|
| 850 | cfile->srch_inf.info_level);
|
| 851 | }
|
| 852 | if ((cur_ent == NULL) && (i < pos_in_buf)) {
|
| 853 |
|
| 854 | cifs_dbg(VFS, "reached end of buf searching for pos in buf %d index to find %lld rc %d\n",
|
| 855 | pos_in_buf, index_to_find, rc);
|
| 856 | }
|
| 857 | rc = 0;
|
| 858 | *current_entry = cur_ent;
|
| 859 | } else {
|
| 860 | cifs_dbg(FYI, "index not in buffer - could not findnext into it\n");
|
| 861 | return 0;
|
| 862 | }
|
| 863 |
|
| 864 | if (pos_in_buf >= cfile->srch_inf.entries_in_buffer) {
|
| 865 | cifs_dbg(FYI, "can not return entries pos_in_buf beyond last\n");
|
| 866 | *num_to_ret = 0;
|
| 867 | } else
|
| 868 | *num_to_ret = cfile->srch_inf.entries_in_buffer - pos_in_buf;
|
| 869 |
|
| 870 | return rc;
|
| 871 | }
|
| 872 |
|
| 873 | static bool emit_cached_dirents(struct cached_dirents *cde,
|
| 874 | struct dir_context *ctx)
|
| 875 | {
|
| 876 | struct cached_dirent *dirent;
|
| 877 | bool rc;
|
| 878 |
|
| 879 | list_for_each_entry(dirent, &cde->entries, entry) {
|
| 880 | |
| 881 | * Skip all early entries prior to the current lseek()
|
| 882 | * position.
|
| 883 |
|
| 884 | if (ctx->pos > dirent->pos)
|
| 885 | continue;
|
| 886 | |
| 887 | * We recorded the current ->pos value for the dirent
|
| 888 | * when we stored it in the cache.
|
| 889 | * However, this sequence of ->pos values may have holes
|
| 890 | * in it, for example dot-dirs returned from the server
|
| 891 | * are suppressed.
|
| 892 | * Handle this by forcing ctx->pos to be the same as the
|
| 893 | * ->pos of the current dirent we emit from the cache.
|
| 894 | * This means that when we emit these entries from the cache
|
| 895 | * we now emit them with the same ->pos value as in the
|
| 896 | * initial scan.
|
| 897 |
|
| 898 | ctx->pos = dirent->pos;
|
| 899 | rc = dir_emit(ctx, dirent->name, dirent->namelen,
|
| 900 | dirent->fattr.cf_uniqueid,
|
| 901 | dirent->fattr.cf_dtype);
|
| 902 | if (!rc)
|
| 903 | return rc;
|
| 904 | ctx->pos++;
|
| 905 | }
|
| 906 | return true;
|
| 907 | }
|
| 908 |
|
| 909 | static void update_cached_dirents_count(struct cached_dirents *cde,
|
| 910 | struct file *file)
|
| 911 | {
|
| 912 | if (cde->file != file)
|
| 913 | return;
|
| 914 | if (cde->is_valid || cde->is_failed)
|
| 915 | return;
|
| 916 |
|
| 917 | cde->pos++;
|
| 918 | }
|
| 919 |
|
| 920 | static void finished_cached_dirents_count(struct cached_dirents *cde,
|
| 921 | struct dir_context *ctx, struct file *file)
|
| 922 | {
|
| 923 | if (cde->file != file)
|
| 924 | return;
|
| 925 | if (cde->is_valid || cde->is_failed)
|
| 926 | return;
|
| 927 | if (ctx->pos != cde->pos)
|
| 928 | return;
|
| 929 |
|
| 930 | cde->is_valid = 1;
|
| 931 | }
|
| 932 |
|
| 933 | static bool add_cached_dirent(struct cached_dirents *cde,
|
| 934 | struct dir_context *ctx, const char *name,
|
| 935 | int namelen, struct cifs_fattr *fattr,
|
| 936 | struct file *file)
|
| 937 | {
|
| 938 | struct cached_dirent *de;
|
| 939 |
|
| 940 | if (cde->file != file)
|
| 941 | return false;
|
| 942 | if (cde->is_valid || cde->is_failed)
|
| 943 | return false;
|
| 944 | if (ctx->pos != cde->pos) {
|
| 945 | cde->is_failed = 1;
|
| 946 | return false;
|
| 947 | }
|
| 948 | de = kzalloc_obj(*de, GFP_ATOMIC);
|
| 949 | if (de == NULL) {
|
| 950 | cde->is_failed = 1;
|
| 951 | return false;
|
| 952 | }
|
| 953 | de->namelen = namelen;
|
| 954 | de->name = kstrndup(name, namelen, GFP_ATOMIC);
|
| 955 | if (de->name == NULL) {
|
| 956 | kfree(de);
|
| 957 | cde->is_failed = 1;
|
| 958 | return false;
|
| 959 | }
|
| 960 | de->pos = ctx->pos;
|
| 961 |
|
| 962 | memcpy(&de->fattr, fattr, sizeof(struct cifs_fattr));
|
| 963 |
|
| 964 | list_add_tail(&de->entry, &cde->entries);
|
| 965 |
|
| 966 | cde->entries_count++;
|
| 967 | cde->bytes_used += sizeof(*de) + (size_t)namelen + 1;
|
| 968 | return true;
|
| 969 | }
|
| 970 |
|
| 971 | static bool cifs_dir_emit(struct dir_context *ctx,
|
| 972 | const char *name, int namelen,
|
| 973 | struct cifs_fattr *fattr,
|
| 974 | struct cached_fid *cfid,
|
| 975 | struct file *file)
|
| 976 | {
|
| 977 | size_t delta_bytes = 0;
|
| 978 | bool rc, added = false;
|
| 979 | ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
|
| 980 |
|
| 981 | rc = dir_emit(ctx, name, namelen, ino, fattr->cf_dtype);
|
| 982 | if (!rc)
|
| 983 | return rc;
|
| 984 |
|
| 985 | if (cfid) {
|
| 986 |
|
| 987 | delta_bytes = sizeof(struct cached_dirent) + (size_t)namelen + 1;
|
| 988 |
|
| 989 | mutex_lock(&cfid->dirents.de_mutex);
|
| 990 | added = add_cached_dirent(&cfid->dirents, ctx, name, namelen,
|
| 991 | fattr, file);
|
| 992 | mutex_unlock(&cfid->dirents.de_mutex);
|
| 993 |
|
| 994 | if (added) {
|
| 995 |
|
| 996 | atomic64_add((long long)delta_bytes, &cfid->cfids->total_dirents_bytes);
|
| 997 | atomic_long_inc(&cfid->cfids->total_dirents_entries);
|
| 998 | atomic64_add((long long)delta_bytes, &cifs_dircache_bytes_used);
|
| 999 | }
|
| 1000 | }
|
| 1001 |
|
| 1002 | return rc;
|
| 1003 | }
|
| 1004 |
|
| 1005 | static int cifs_filldir(char *find_entry, struct file *file,
|
| 1006 | struct dir_context *ctx,
|
| 1007 | char *scratch_buf, unsigned int max_len,
|
| 1008 | char *end_of_smb, struct cached_fid *cfid)
|
| 1009 | {
|
| 1010 | struct cifsFileInfo *file_info = file->private_data;
|
| 1011 | struct super_block *sb = file_inode(file)->i_sb;
|
| 1012 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
| 1013 | struct cifs_dirent de = { NULL, };
|
| 1014 | struct cifs_fattr fattr;
|
| 1015 | unsigned int sbflags;
|
| 1016 | struct qstr name;
|
| 1017 | int rc = 0;
|
| 1018 |
|
| 1019 | rc = cifs_fill_dirent(&de, find_entry, end_of_smb,
|
| 1020 | file_info->srch_inf.info_level,
|
| 1021 | file_info->srch_inf.unicode);
|
| 1022 | if (rc)
|
| 1023 | return rc;
|
| 1024 |
|
| 1025 |
|
| 1026 | if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode))
|
| 1027 | return 0;
|
| 1028 |
|
| 1029 | if (file_info->srch_inf.unicode) {
|
| 1030 | struct nls_table *nlt = cifs_sb->local_nls;
|
| 1031 | int map_type;
|
| 1032 |
|
| 1033 | map_type = cifs_remap(cifs_sb);
|
| 1034 | name.name = scratch_buf;
|
| 1035 | name.len =
|
| 1036 | cifs_from_utf16((char *)name.name, (__le16 *)de.name,
|
| 1037 | UNICODE_NAME_MAX,
|
| 1038 | min_t(size_t, de.namelen,
|
| 1039 | (size_t)max_len), nlt, map_type);
|
| 1040 | name.len -= nls_nullsize(nlt);
|
| 1041 | } else {
|
| 1042 | name.name = de.name;
|
| 1043 | name.len = de.namelen;
|
| 1044 | }
|
| 1045 |
|
| 1046 | switch (file_info->srch_inf.info_level) {
|
| 1047 | case SMB_FIND_FILE_POSIX_INFO:
|
| 1048 | cifs_posix_to_fattr(&fattr,
|
| 1049 | (struct smb2_posix_info *)find_entry,
|
| 1050 | cifs_sb);
|
| 1051 | break;
|
| 1052 | case SMB_FIND_FILE_UNIX:
|
| 1053 | cifs_unix_basic_to_fattr(&fattr,
|
| 1054 | &((FILE_UNIX_INFO *)find_entry)->basic,
|
| 1055 | cifs_sb);
|
| 1056 | if (S_ISLNK(fattr.cf_mode))
|
| 1057 | fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
|
| 1058 | break;
|
| 1059 | case SMB_FIND_FILE_INFO_STANDARD:
|
| 1060 | cifs_std_info_to_fattr(&fattr,
|
| 1061 | (FIND_FILE_STANDARD_INFO *)find_entry,
|
| 1062 | cifs_sb);
|
| 1063 | break;
|
| 1064 | case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
|
| 1065 | case SMB_FIND_FILE_ID_FULL_DIR_INFO:
|
| 1066 | cifs_fulldir_info_to_fattr(&fattr, find_entry, cifs_sb);
|
| 1067 | break;
|
| 1068 | default:
|
| 1069 | cifs_dir_info_to_fattr(&fattr,
|
| 1070 | (FILE_DIRECTORY_INFO *)find_entry,
|
| 1071 | cifs_sb);
|
| 1072 | break;
|
| 1073 | }
|
| 1074 |
|
| 1075 | sbflags = cifs_sb_flags(cifs_sb);
|
| 1076 | if (de.ino && (sbflags & CIFS_MOUNT_SERVER_INUM)) {
|
| 1077 | fattr.cf_uniqueid = de.ino;
|
| 1078 | } else {
|
| 1079 | fattr.cf_uniqueid = iunique(sb, ROOT_I);
|
| 1080 | cifs_autodisable_serverino(cifs_sb, "Cannot retrieve inode number from readdir", 0);
|
| 1081 | }
|
| 1082 |
|
| 1083 | if ((sbflags & CIFS_MOUNT_MF_SYMLINKS) && couldbe_mf_symlink(&fattr))
|
| 1084 | |
| 1085 | * trying to get the type and mode can be slow,
|
| 1086 | * so just call those regular files for now, and mark
|
| 1087 | * for reval
|
| 1088 |
|
| 1089 | fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
|
| 1090 |
|
| 1091 | cifs_prime_dcache(file_dentry(file), &name, &fattr);
|
| 1092 |
|
| 1093 | return !cifs_dir_emit(ctx, name.name, name.len,
|
| 1094 | &fattr, cfid, file);
|
| 1095 | }
|
| 1096 |
|
| 1097 |
|
| 1098 | int cifs_readdir(struct file *file, struct dir_context *ctx)
|
| 1099 | {
|
| 1100 | int rc = 0;
|
| 1101 | unsigned int xid;
|
| 1102 | int i;
|
| 1103 | struct tcon_link *tlink = NULL;
|
| 1104 | struct cifs_tcon *tcon;
|
| 1105 | struct cifsFileInfo *cifsFile;
|
| 1106 | char *current_entry;
|
| 1107 | int num_to_fill = 0;
|
| 1108 | char *tmp_buf = NULL;
|
| 1109 | char *end_of_smb;
|
| 1110 | unsigned int max_len;
|
| 1111 | const char *full_path;
|
| 1112 | void *page = alloc_dentry_path();
|
| 1113 | struct cached_fid *cfid = NULL;
|
| 1114 | struct cifs_sb_info *cifs_sb = CIFS_SB(file);
|
| 1115 |
|
| 1116 | xid = get_xid();
|
| 1117 |
|
| 1118 | full_path = build_path_from_dentry(file_dentry(file), page);
|
| 1119 | if (IS_ERR(full_path)) {
|
| 1120 | rc = PTR_ERR(full_path);
|
| 1121 | goto rddir2_exit;
|
| 1122 | }
|
| 1123 |
|
| 1124 | if (file->private_data == NULL) {
|
| 1125 | tlink = cifs_sb_tlink(cifs_sb);
|
| 1126 | if (IS_ERR(tlink))
|
| 1127 | goto cache_not_found;
|
| 1128 | tcon = tlink_tcon(tlink);
|
| 1129 | } else {
|
| 1130 | cifsFile = file->private_data;
|
| 1131 | tcon = tlink_tcon(cifsFile->tlink);
|
| 1132 | }
|
| 1133 |
|
| 1134 | rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
|
| 1135 | cifs_put_tlink(tlink);
|
| 1136 | if (rc)
|
| 1137 | goto cache_not_found;
|
| 1138 |
|
| 1139 | mutex_lock(&cfid->dirents.de_mutex);
|
| 1140 | |
| 1141 | * If this was reading from the start of the directory
|
| 1142 | * we need to initialize scanning and storing the
|
| 1143 | * directory content.
|
| 1144 |
|
| 1145 | if (ctx->pos == 0 && cfid->dirents.file == NULL) {
|
| 1146 | cfid->dirents.file = file;
|
| 1147 | cfid->dirents.pos = 2;
|
| 1148 | }
|
| 1149 | |
| 1150 | * If we already have the entire directory cached then
|
| 1151 | * we can just serve the cache.
|
| 1152 |
|
| 1153 | if (cfid->dirents.is_valid) {
|
| 1154 | if (!dir_emit_dots(file, ctx)) {
|
| 1155 | mutex_unlock(&cfid->dirents.de_mutex);
|
| 1156 | goto rddir2_exit;
|
| 1157 | }
|
| 1158 | emit_cached_dirents(&cfid->dirents, ctx);
|
| 1159 | mutex_unlock(&cfid->dirents.de_mutex);
|
| 1160 | goto rddir2_exit;
|
| 1161 | }
|
| 1162 | mutex_unlock(&cfid->dirents.de_mutex);
|
| 1163 |
|
| 1164 | |
| 1165 | * find_cifs_entry in case there will be reconnects during
|
| 1166 | * query_directory.
|
| 1167 |
|
| 1168 | close_cached_dir(cfid);
|
| 1169 | cfid = NULL;
|
| 1170 |
|
| 1171 | cache_not_found:
|
| 1172 | |
| 1173 | * Ensure FindFirst doesn't fail before doing filldir() for '.' and
|
| 1174 | * '..'. Otherwise we won't be able to notify VFS in case of failure.
|
| 1175 |
|
| 1176 | if (file->private_data == NULL) {
|
| 1177 | rc = initiate_cifs_search(xid, file, full_path);
|
| 1178 | cifs_dbg(FYI, "initiate cifs search rc %d\n", rc);
|
| 1179 | if (rc)
|
| 1180 | goto rddir2_exit;
|
| 1181 | }
|
| 1182 |
|
| 1183 | if (!dir_emit_dots(file, ctx))
|
| 1184 | goto rddir2_exit;
|
| 1185 |
|
| 1186 | |
| 1187 | is in current search buffer?
|
| 1188 | if it before then restart search
|
| 1189 | if after then keep searching till find it
|
| 1190 | cifsFile = file->private_data;
|
| 1191 | if (cifsFile->srch_inf.endOfSearch) {
|
| 1192 | if (cifsFile->srch_inf.emptyDir) {
|
| 1193 | cifs_dbg(FYI, "End of search, empty dir\n");
|
| 1194 | rc = 0;
|
| 1195 | goto rddir2_exit;
|
| 1196 | }
|
| 1197 | } |
| 1198 | cifsFile->invalidHandle = true;
|
| 1199 | tcon->ses->server->close(xid, tcon, &cifsFile->fid);
|
| 1200 | }
|
| 1201 |
|
| 1202 | tcon = tlink_tcon(cifsFile->tlink);
|
| 1203 | rc = find_cifs_entry(xid, tcon, ctx->pos, file, full_path,
|
| 1204 | ¤t_entry, &num_to_fill);
|
| 1205 | open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
|
| 1206 | if (rc) {
|
| 1207 | cifs_dbg(FYI, "fce error %d\n", rc);
|
| 1208 | goto rddir2_exit;
|
| 1209 | } else if (current_entry != NULL) {
|
| 1210 | cifs_dbg(FYI, "entry %lld found\n", ctx->pos);
|
| 1211 | } else {
|
| 1212 | if (cfid) {
|
| 1213 | mutex_lock(&cfid->dirents.de_mutex);
|
| 1214 | finished_cached_dirents_count(&cfid->dirents, ctx, file);
|
| 1215 | mutex_unlock(&cfid->dirents.de_mutex);
|
| 1216 | }
|
| 1217 | cifs_dbg(FYI, "Could not find entry\n");
|
| 1218 | goto rddir2_exit;
|
| 1219 | }
|
| 1220 | cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n",
|
| 1221 | num_to_fill, cifsFile->srch_inf.ntwrk_buf_start);
|
| 1222 | max_len = tcon->ses->server->ops->calc_smb_size(
|
| 1223 | cifsFile->srch_inf.ntwrk_buf_start);
|
| 1224 | end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len;
|
| 1225 |
|
| 1226 | tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL);
|
| 1227 | if (tmp_buf == NULL) {
|
| 1228 | rc = -ENOMEM;
|
| 1229 | goto rddir2_exit;
|
| 1230 | }
|
| 1231 |
|
| 1232 | for (i = 0; i < num_to_fill; i++) {
|
| 1233 | if (current_entry == NULL) {
|
| 1234 |
|
| 1235 | cifs_dbg(VFS, "past SMB end, num to fill %d i %d\n",
|
| 1236 | num_to_fill, i);
|
| 1237 | break;
|
| 1238 | }
|
| 1239 | |
| 1240 | * if buggy server returns . and .. late do we want to
|
| 1241 | * check for that here?
|
| 1242 |
|
| 1243 | *tmp_buf = 0;
|
| 1244 | rc = cifs_filldir(current_entry, file, ctx,
|
| 1245 | tmp_buf, max_len, end_of_smb, cfid);
|
| 1246 | if (rc) {
|
| 1247 | if (rc > 0)
|
| 1248 | rc = 0;
|
| 1249 | break;
|
| 1250 | }
|
| 1251 |
|
| 1252 | ctx->pos++;
|
| 1253 | if (cfid) {
|
| 1254 | mutex_lock(&cfid->dirents.de_mutex);
|
| 1255 | update_cached_dirents_count(&cfid->dirents, file);
|
| 1256 | mutex_unlock(&cfid->dirents.de_mutex);
|
| 1257 | }
|
| 1258 |
|
| 1259 | if (ctx->pos ==
|
| 1260 | cifsFile->srch_inf.index_of_last_entry) {
|
| 1261 | cifs_dbg(FYI, "last entry in buf at pos %lld %s\n",
|
| 1262 | ctx->pos, tmp_buf);
|
| 1263 | cifs_save_resume_key(current_entry, cifsFile);
|
| 1264 | break;
|
| 1265 | }
|
| 1266 | current_entry =
|
| 1267 | nxt_dir_entry(current_entry, end_of_smb,
|
| 1268 | cifsFile->srch_inf.info_level);
|
| 1269 | }
|
| 1270 | kfree(tmp_buf);
|
| 1271 |
|
| 1272 | rddir2_exit:
|
| 1273 | if (cfid)
|
| 1274 | close_cached_dir(cfid);
|
| 1275 | free_dentry_path(page);
|
| 1276 | free_xid(xid);
|
| 1277 | return rc;
|
| 1278 | }
|