cifs: fix skipping to incorrect offset in emit_cached_dirents
authorRonnie Sahlberg <lsahlber@redhat.com>
Tue, 11 Oct 2022 22:07:29 +0000 (08:07 +1000)
committerSteve French <stfrench@microsoft.com>
Tue, 11 Oct 2022 22:23:00 +0000 (17:23 -0500)
When application has done lseek() to a different offset on a directory fd
we skipped one entry too many before we start emitting directory entries
from the cache.

We need to also make sure that when we are starting to emit directory
entries from the cache, the ->pos sequence might have holes and skip
some indices.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Reviewed-by: Tom Talpey <tom@talpey.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/readdir.c

index 8e060c00c969011bd2c6a8f5415c672d0dca8cfb..1bb4624e768bfc2b7ac556cf50c9e41d5ab36c9f 100644 (file)
@@ -844,17 +844,34 @@ static bool emit_cached_dirents(struct cached_dirents *cde,
                                struct dir_context *ctx)
 {
        struct cached_dirent *dirent;
-       int rc;
+       bool rc;
 
        list_for_each_entry(dirent, &cde->entries, entry) {
-               if (ctx->pos >= dirent->pos)
+               /*
+                * Skip all early entries prior to the current lseek()
+                * position.
+                */
+               if (ctx->pos > dirent->pos)
                        continue;
+               /*
+                * We recorded the current ->pos value for the dirent
+                * when we stored it in the cache.
+                * However, this sequence of ->pos values may have holes
+                * in it, for example dot-dirs returned from the server
+                * are suppressed.
+                * Handle this bu forcing ctx->pos to be the same as the
+                * ->pos of the current dirent we emit from the cache.
+                * This means that when we emit these entries from the cache
+                * we now emit them with the same ->pos value as in the
+                * initial scan.
+                */
                ctx->pos = dirent->pos;
                rc = dir_emit(ctx, dirent->name, dirent->namelen,
                              dirent->fattr.cf_uniqueid,
                              dirent->fattr.cf_dtype);
                if (!rc)
                        return rc;
+               ctx->pos++;
        }
        return true;
 }
@@ -1202,10 +1219,10 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
                                 ctx->pos, tmp_buf);
                        cifs_save_resume_key(current_entry, cifsFile);
                        break;
-               } else
-                       current_entry =
-                               nxt_dir_entry(current_entry, end_of_smb,
-                                       cifsFile->srch_inf.info_level);
+               }
+               current_entry =
+                       nxt_dir_entry(current_entry, end_of_smb,
+                                     cifsFile->srch_inf.info_level);
        }
        kfree(tmp_buf);