@@ -228,9 +228,29 @@ struct buffer {
228228 size_t size ;
229229};
230230
231+ struct dir_entry {
232+ char * name ;
233+ struct stat stbuf ;
234+ };
235+
231236struct dir_handle {
232237 struct buffer buf ;
233238 struct conn * conn ;
239+ /*
240+ * Snapshot of the *current* enumeration. sshfs uses old-style readdir
241+ * over a single-pass SFTP directory handle. Each new enumeration
242+ * (offset == 0, e.g. a fresh listing or a rewinddir) re-reads the
243+ * directory from the server so newly created files and updated
244+ * attributes show up; a continuation (offset != 0) is served from this
245+ * snapshot so the listing never comes back empty when the handle has
246+ * already been exhausted. See:
247+ * https://github.com/libfuse/sshfs/issues/338
248+ */
249+ GPtrArray * entries ; /* array of struct dir_entry * */
250+ char * path ; /* directory path, for reopening the handle */
251+ int read_err ; /* error from the last read, if any */
252+ int has_handle ; /* buf holds an open SFTP dir handle to close */
253+ int buf_exhausted ; /* the SFTP handle has been read to EOF */
234254};
235255
236256struct list_head {
@@ -958,8 +978,7 @@ static int buf_get_statvfs(struct buffer *buf, struct statvfs *stbuf)
958978 return 0 ;
959979}
960980
961- static int buf_get_entries (struct buffer * buf , void * dbuf ,
962- fuse_fill_dir_t filler )
981+ static int buf_get_entries (struct buffer * buf , GPtrArray * entries )
963982{
964983 uint32_t count ;
965984 unsigned i ;
@@ -978,11 +997,16 @@ static int buf_get_entries(struct buffer *buf, void *dbuf,
978997 free (longname );
979998 err = buf_get_attrs (buf , & stbuf , NULL );
980999 if (!err ) {
1000+ struct dir_entry * entry ;
9811001 if (sshfs .follow_symlinks &&
9821002 S_ISLNK (stbuf .st_mode )) {
9831003 stbuf .st_mode = 0 ;
9841004 }
985- filler (dbuf , name , & stbuf , 0 , 0 );
1005+ entry = g_new (struct dir_entry , 1 );
1006+ entry -> name = name ;
1007+ entry -> stbuf = stbuf ;
1008+ g_ptr_array_add (entries , entry );
1009+ name = NULL ; /* ownership passed to entry */
9861010 }
9871011 }
9881012 free (name );
@@ -2309,7 +2333,7 @@ static int sshfs_req_pending(struct request *req)
23092333}
23102334
23112335static int sftp_readdir_async (struct conn * conn , struct buffer * handle ,
2312- void * buf , off_t offset , fuse_fill_dir_t filler )
2336+ GPtrArray * entries )
23132337{
23142338 int err = 0 ;
23152339 int outstanding = 0 ;
@@ -2318,7 +2342,6 @@ static int sftp_readdir_async(struct conn *conn, struct buffer *handle,
23182342
23192343 int done = 0 ;
23202344
2321- assert (offset == 0 );
23222345 while (!done || outstanding ) {
23232346 struct request * req ;
23242347 struct buffer name ;
@@ -2369,7 +2392,7 @@ static int sftp_readdir_async(struct conn *conn, struct buffer *handle,
23692392 done = 1 ;
23702393 }
23712394 if (!done ) {
2372- err = buf_get_entries (& name , buf , filler );
2395+ err = buf_get_entries (& name , entries );
23732396 buf_free (& name );
23742397
23752398 /* increase number of outstanding requests */
@@ -2387,15 +2410,14 @@ static int sftp_readdir_async(struct conn *conn, struct buffer *handle,
23872410}
23882411
23892412static int sftp_readdir_sync (struct conn * conn , struct buffer * handle ,
2390- void * buf , off_t offset , fuse_fill_dir_t filler )
2413+ GPtrArray * entries )
23912414{
23922415 int err ;
2393- assert (offset == 0 );
23942416 do {
23952417 struct buffer name ;
23962418 err = sftp_request (conn , SSH_FXP_READDIR , handle , SSH_FXP_NAME , & name );
23972419 if (!err ) {
2398- err = buf_get_entries (& name , buf , filler );
2420+ err = buf_get_entries (& name , entries );
23992421 buf_free (& name );
24002422 }
24012423 } while (!err );
@@ -2427,31 +2449,104 @@ static int sshfs_opendir(const char *path, struct fuse_file_info *fi)
24272449 handle -> conn = conn ;
24282450 handle -> conn -> dir_count ++ ;
24292451 pthread_mutex_unlock (& sshfs .lock );
2452+ handle -> has_handle = 1 ;
2453+ handle -> path = g_strdup (path );
24302454 fi -> fh = (unsigned long ) handle ;
24312455 } else
24322456 g_free (handle );
24332457 buf_free (& buf );
24342458 return err ;
24352459}
24362460
2461+ static void free_dir_entry (gpointer data )
2462+ {
2463+ struct dir_entry * entry = data ;
2464+ free (entry -> name );
2465+ g_free (entry );
2466+ }
2467+
2468+ /*
2469+ * Reopen the SFTP directory handle. SFTP has no rewind, so re-reading a
2470+ * directory from the start means closing the exhausted handle and opening a
2471+ * fresh one.
2472+ */
2473+ static int sshfs_reopen_dir (struct dir_handle * handle )
2474+ {
2475+ struct buffer buf ;
2476+ int err ;
2477+
2478+ if (handle -> has_handle ) {
2479+ sftp_request (handle -> conn , SSH_FXP_CLOSE , & handle -> buf , 0 , NULL );
2480+ /* free + reset to a clean state so sftp_request can refill it */
2481+ buf_clear (& handle -> buf );
2482+ handle -> has_handle = 0 ;
2483+ }
2484+ buf_init (& buf , 0 );
2485+ buf_add_path (& buf , handle -> path );
2486+ err = sftp_request (handle -> conn , SSH_FXP_OPENDIR , & buf ,
2487+ SSH_FXP_HANDLE , & handle -> buf );
2488+ buf_free (& buf );
2489+ if (!err ) {
2490+ buf_finish (& handle -> buf );
2491+ handle -> has_handle = 1 ;
2492+ handle -> buf_exhausted = 0 ;
2493+ }
2494+ return err ;
2495+ }
2496+
24372497static int sshfs_readdir (const char * path , void * dbuf , fuse_fill_dir_t filler ,
24382498 off_t offset , struct fuse_file_info * fi ,
24392499 enum fuse_readdir_flags flags )
24402500{
24412501 (void ) path ; (void ) flags ;
2442- int err ;
2502+ unsigned i ;
24432503 struct dir_handle * handle ;
24442504
24452505 handle = (struct dir_handle * ) fi -> fh ;
24462506
2447- if (sshfs .sync_readdir )
2448- err = sftp_readdir_sync (handle -> conn , & handle -> buf , dbuf ,
2449- offset , filler );
2450- else
2451- err = sftp_readdir_async (handle -> conn , & handle -> buf , dbuf ,
2452- offset , filler );
2507+ /*
2508+ * offset == 0 starts a fresh enumeration (a new listing or a
2509+ * rewinddir), so re-read the directory from the server to pick up newly
2510+ * created files and updated attributes. The SFTP handle is single-pass:
2511+ * once read to EOF, reusing it would read nothing and the directory
2512+ * would appear empty, so we reopen a fresh handle first. offset != 0 is
2513+ * a continuation of the current enumeration and is served from the
2514+ * snapshot taken at offset 0.
2515+ * See: https://github.com/libfuse/sshfs/issues/338
2516+ */
2517+ if (offset == 0 || handle -> entries == NULL ) {
2518+ if (handle -> buf_exhausted )
2519+ handle -> read_err = sshfs_reopen_dir (handle );
2520+ if (!handle -> read_err ) {
2521+ if (handle -> entries )
2522+ g_ptr_array_free (handle -> entries , TRUE);
2523+ handle -> entries =
2524+ g_ptr_array_new_with_free_func (free_dir_entry );
2525+ if (sshfs .sync_readdir )
2526+ handle -> read_err = sftp_readdir_sync (
2527+ handle -> conn , & handle -> buf ,
2528+ handle -> entries );
2529+ else
2530+ handle -> read_err = sftp_readdir_async (
2531+ handle -> conn , & handle -> buf ,
2532+ handle -> entries );
2533+ handle -> buf_exhausted = 1 ;
2534+ }
2535+ }
24532536
2454- return err ;
2537+ if (handle -> read_err )
2538+ return handle -> read_err ;
2539+
2540+ /*
2541+ * Old-style readdir: hand FUSE the complete listing with offset 0 and
2542+ * let it do the offset-based slicing itself.
2543+ */
2544+ for (i = 0 ; i < handle -> entries -> len ; i ++ ) {
2545+ struct dir_entry * entry = g_ptr_array_index (handle -> entries , i );
2546+ filler (dbuf , entry -> name , & entry -> stbuf , 0 , 0 );
2547+ }
2548+
2549+ return 0 ;
24552550}
24562551
24572552static int sshfs_releasedir (const char * path , struct fuse_file_info * fi )
@@ -2461,11 +2556,18 @@ static int sshfs_releasedir(const char *path, struct fuse_file_info *fi)
24612556 struct dir_handle * handle ;
24622557
24632558 handle = (struct dir_handle * ) fi -> fh ;
2464- err = sftp_request (handle -> conn , SSH_FXP_CLOSE , & handle -> buf , 0 , NULL );
2559+ err = 0 ;
2560+ if (handle -> has_handle ) {
2561+ err = sftp_request (handle -> conn , SSH_FXP_CLOSE , & handle -> buf ,
2562+ 0 , NULL );
2563+ buf_free (& handle -> buf );
2564+ }
24652565 pthread_mutex_lock (& sshfs .lock );
24662566 handle -> conn -> dir_count -- ;
24672567 pthread_mutex_unlock (& sshfs .lock );
2468- buf_free (& handle -> buf );
2568+ if (handle -> entries )
2569+ g_ptr_array_free (handle -> entries , TRUE);
2570+ g_free (handle -> path );
24692571 g_free (handle );
24702572 return err ;
24712573}
0 commit comments