/** * Set the location of the next direntry to be read via onefs_readdir(). * * This function should only pass in locations retrieved from onefs_telldir(). * * Ideally the seek point will still be in the readdirplus cache, and we'll * just update our cursors. If the seek location is outside of the current * cache we must do an expensive re-enumeration of the entire directory up * to the offset. * * @param[in] handle vfs handle given in most VFS calls * @param[in] dirp system DIR handle to set offset on * @param[in] offset from the start of the directory where the next read * will take place * * @return no return value */ void onefs_seekdir(vfs_handle_struct *handle, SMB_STRUCT_DIR *dirp, long offset) { struct rdp_dir_state *dsp = NULL; bool same_as_last; bool outside_cache = false; int ret = -1, i; /* Fallback to default system routines if readdirplus is disabled */ if (!lp_parm_bool(SNUM(handle->conn), PARM_ONEFS_TYPE, PARM_USE_READDIRPLUS, PARM_USE_READDIRPLUS_DEFAULT)) { return sys_seekdir(dirp, offset); } /* Validate inputs */ if (offset < 0) { DEBUG(1, ("Invalid offset %ld passed.\n", offset)); return; } /* Retrieve state based off DIR handle */ ret = rdp_retrieve_dir_state(dirp, &dsp, &same_as_last); if (ret) { DEBUG(1, ("Could not retrieve dir_state struct for " "SMB_STRUCT_DIR pointer.\n")); /* XXX: we can't return an error, should we ABORT rather than * return without actually seeking? */ return; } /* Short cut if no work needs to be done */ if (offset == dsp->location) return; /* If DIR is different from last call, reset all buffers and cursors, * and refill the global cache from the new DIR */ if (!same_as_last) { ret = rdp_fill_cache(dsp); if (ret <= 0) goto out; DEBUG(8, ("Switched global rdp cache to new DIR entry.\n")); } /* Check if location is outside the currently cached entries */ if (offset < dsp->location - dsp->stat_cursor) { /* offset is before the current cache */ /* reset to the beginning of the directory */ ret = rdp_init(dsp); if (ret) { DEBUG(0, ("Error initializing readdirplus() buffers: " "%s\n", strerror(ret))); goto out; } outside_cache = true; } else if (offset > dsp->location + (dsp->stat_count - 1 - dsp->stat_cursor)) { /* offset is after the current cache * advance the cookie to the end of the cache */ dsp->resume_cookie = rdp_cookies[dsp->stat_count - 1]; outside_cache = true; } if (outside_cache) { /* start reading from the directory, until we have the * specified offset in our cache */ do { dsp->location += dsp->stat_count - dsp->stat_cursor; ret = rdp_fill_cache(dsp); if (ret <= 0) { DEBUG(1, ("Error seeking to offset outside the " "cached directory entries. Offset " "%ld \n", dsp->location)); goto out; } dsp->resume_cookie = rdp_cookies[dsp->stat_count - 1]; } while (offset >= dsp->location + dsp->stat_count); } /* Location should be within the currently cached entries */ if (offset < dsp->location && offset >= dsp->location - dsp->stat_cursor) { /* offset is within the current cache, before the cursor. * update cursors to the new location */ int new_cursor = dsp->stat_cursor - (dsp->location - offset); dsp->direntries_cursor = rdp_direntries; for (i=0; i < new_cursor; i++) { dsp->direntries_cursor += ((SMB_STRUCT_DIRENT *) dsp->direntries_cursor)->d_reclen; } dsp->stat_cursor = new_cursor; dsp->resume_cookie = rdp_cookies[dsp->stat_cursor]; dsp->location = offset; } else if (offset >= dsp->location && offset <= dsp->location + (dsp->stat_count - 1 - dsp->stat_cursor)) { /* offset is within the current cache, at or after the cursor. * update cursors to the new location */ int add_to_cursor = offset - dsp->location - 1; for (i=0; i < add_to_cursor; i++) { dsp->direntries_cursor += ((SMB_STRUCT_DIRENT *) dsp->direntries_cursor)->d_reclen; } dsp->stat_cursor += add_to_cursor; dsp->resume_cookie = rdp_cookies[dsp->stat_cursor]; dsp->location = offset; } DEBUG(9, ("Seek DIR %p, location: %ld, cache cursor: %zu\n", dsp->dirp, dsp->location, dsp->stat_cursor)); /* FALLTHROUGH */ out: /* Set rdp_last_dirp at the end of every VFS call where the cache was * reloaded */ rdp_last_dirp = dirp; return; }
void vfswrap_seekdir(vfs_handle_struct *handle, connection_struct *conn, SMB_STRUCT_DIR *dirp, long offset) { START_PROFILE(syscall_seekdir); sys_seekdir(dirp, offset); END_PROFILE(syscall_seekdir); }
int sys_rewinddir(DIR* d) { sys_seekdir(d, 0); }