Skip to content

Commit 033a1d4

Browse files
fix conntab lookup after rename in sshfs_release
sshfs_release looked up the conntab entry by path, but sshfs_rename moves that entry to the new path. Closing the original handle after a rename can miss the entry and dereference NULL. Store the conntab_entry pointer on sshfs_file and use it for release and open-failure cleanup. Add a regression test for open -> rename -> close.
1 parent d749988 commit 033a1d4

2 files changed

Lines changed: 42 additions & 14 deletions

File tree

sshfs.c

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,11 @@ struct read_chunk {
280280
struct sshfs_io sio;
281281
};
282282

283+
struct conntab_entry {
284+
unsigned refcount;
285+
struct conn *conn;
286+
};
287+
283288
struct sshfs_file {
284289
struct buffer handle;
285290
struct list_head write_reqs;
@@ -289,15 +294,11 @@ struct sshfs_file {
289294
off_t next_pos;
290295
int is_seq;
291296
struct conn *conn;
297+
struct conntab_entry *ce;
292298
int connver;
293299
int modifver;
294300
};
295301

296-
struct conntab_entry {
297-
unsigned refcount;
298-
struct conn *conn;
299-
};
300-
301302
struct sshfs {
302303
char *directport;
303304
char *ssh_command;
@@ -2763,6 +2764,12 @@ static int sshfs_utimens(const char *path, const struct timespec tv[2],
27632764
return err;
27642765
}
27652766

2767+
static gboolean conntab_entry_is(gpointer key, gpointer value, gpointer data)
2768+
{
2769+
(void) key;
2770+
return value == data;
2771+
}
2772+
27662773
static int sshfs_open_common(const char *path, mode_t mode,
27672774
struct fuse_file_info *fi)
27682775
{
@@ -2823,12 +2830,13 @@ static int sshfs_open_common(const char *path, mode_t mode,
28232830
g_hash_table_insert(sshfs.conntab, g_strdup(path), ce);
28242831
}
28252832
sf->conn = ce->conn;
2833+
sf->ce = ce;
28262834
ce->refcount++;
28272835
sf->conn->file_count++;
28282836
assert(sf->conn->file_count > 0);
28292837
} else {
28302838
sf->conn = &sshfs.conns[0];
2831-
ce = NULL; // only to silence compiler warning
2839+
sf->ce = NULL;
28322840
}
28332841
sf->connver = sf->conn->connver;
28342842
pthread_mutex_unlock(&sshfs.lock);
@@ -2868,10 +2876,12 @@ static int sshfs_open_common(const char *path, mode_t mode,
28682876
if (sshfs.max_conns > 1) {
28692877
pthread_mutex_lock(&sshfs.lock);
28702878
sf->conn->file_count--;
2871-
ce->refcount--;
2872-
if(ce->refcount == 0) {
2873-
g_hash_table_remove(sshfs.conntab, path);
2874-
g_free(ce);
2879+
sf->ce->refcount--;
2880+
if (sf->ce->refcount == 0) {
2881+
g_hash_table_foreach_remove(sshfs.conntab,
2882+
conntab_entry_is,
2883+
sf->ce);
2884+
g_free(sf->ce);
28752885
}
28762886
pthread_mutex_unlock(&sshfs.lock);
28772887
}
@@ -2942,20 +2952,20 @@ static int sshfs_release(const char *path, struct fuse_file_info *fi)
29422952
{
29432953
struct sshfs_file *sf = get_sshfs_file(fi);
29442954
struct buffer *handle = &sf->handle;
2945-
struct conntab_entry *ce;
29462955
if (sshfs_file_is_conn(sf)) {
29472956
sshfs_flush(path, fi);
29482957
sftp_request(sf->conn, SSH_FXP_CLOSE, handle, 0, NULL);
29492958
}
29502959
buf_free(handle);
29512960
chunk_put_locked(sf->readahead);
29522961
if (sshfs.max_conns > 1) {
2962+
struct conntab_entry *ce = sf->ce;
29532963
pthread_mutex_lock(&sshfs.lock);
29542964
sf->conn->file_count--;
2955-
ce = g_hash_table_lookup(sshfs.conntab, path);
29562965
ce->refcount--;
2957-
if(ce->refcount == 0) {
2958-
g_hash_table_remove(sshfs.conntab, path);
2966+
if (ce->refcount == 0) {
2967+
g_hash_table_foreach_remove(sshfs.conntab,
2968+
conntab_entry_is, ce);
29592969
g_free(ce);
29602970
}
29612971
pthread_mutex_unlock(&sshfs.lock);

test/test_sshfs.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ def test_sshfs(
168168
tst_mkdir_exist(mnt_dir)
169169
tst_readdir_repeated(mnt_dir)
170170
tst_rename_sibling(mnt_dir)
171+
tst_rename_open_release(mnt_dir)
171172
except Exception as exc:
172173
cleanup(mount_process, mnt_dir)
173174
raise exc
@@ -503,6 +504,23 @@ def tst_rename_sibling(mnt_dir):
503504
os.unlink(name_c)
504505

505506

507+
def tst_rename_open_release(mnt_dir):
508+
src = pjoin(mnt_dir, name_generator())
509+
dst = pjoin(mnt_dir, name_generator())
510+
511+
fd = os.open(src, os.O_CREAT | os.O_RDWR)
512+
try:
513+
os.write(fd, b"data")
514+
os.rename(src, dst)
515+
finally:
516+
os.close(fd)
517+
518+
assert not os.path.exists(src)
519+
with open(dst, "rb") as fh:
520+
assert fh.read() == b"data"
521+
os.unlink(dst)
522+
523+
506524
def tst_link(mnt_dir, cache_timeout):
507525
name1 = pjoin(mnt_dir, name_generator())
508526
name2 = pjoin(mnt_dir, name_generator())

0 commit comments

Comments
 (0)