Skip to content

Commit ad0bfda

Browse files
committed
fix: handle NULL path in chmod/utimens under nullpath_ok (FUSE 3)
The init callback sets fuse_config.nullpath_ok, so FUSE 3 may invoke the setattr handlers with path == NULL and a valid file handle for an open (possibly unlinked) file. chmod and utimens passed that NULL path into ltfsmsg ("%s") and the path-based fsops, which is undefined behaviour and fails the operation. Dispatch on the file handle like getattr and truncate already do, using ltfs_fsops_set_readonly() and ltfs_fsops_utimens().
1 parent 6218d9d commit ad0bfda

1 file changed

Lines changed: 36 additions & 6 deletions

File tree

src/ltfs_fuse.c

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -663,13 +663,28 @@ int ltfs_fuse_utimens(const char *path, const struct timespec ts[2])
663663
tsTmp[0] = ltfs_timespec_from_timespec(&ts[0]);
664664
tsTmp[1] = ltfs_timespec_from_timespec(&ts[1]);
665665

666-
ltfsmsg(LTFS_DEBUG, 14038D, path);
667-
ret = ltfs_fsops_utimens_path(path, tsTmp, &id, priv->data);
666+
id.uid = 0;
667+
id.ino = 0;
668+
669+
#ifdef HAVE_FUSE3
670+
/* With nullpath_ok set, FUSE 3 may pass a NULL path for a handle-based
671+
* call on an open (possibly unlinked) file; operate on the handle. */
672+
if (fi) {
673+
struct ltfs_file_handle *file = FILEHANDLE_TO_STRUCT(fi->fh);
674+
ltfsmsg(LTFS_DEBUG, 14038D, _dentry_name(path, file->file_info));
675+
ret = ltfs_fsops_utimens(file->file_info->dentry_handle, tsTmp, priv->data);
676+
id.uid = ((struct dentry *)(file->file_info->dentry_handle))->uid;
677+
} else
678+
#endif
679+
{
680+
ltfsmsg(LTFS_DEBUG, 14038D, path);
681+
ret = ltfs_fsops_utimens_path(path, tsTmp, &id, priv->data);
682+
}
668683

669684
ltfs_request_trace(FUSE_REQ_EXIT(REQ_UTIMENS), ret, id.uid);
670685

671686
if (ret)
672-
ltfsmsg(LTFS_ERR, 10020E, "utimens", path, 0, 0);
687+
ltfsmsg(LTFS_ERR, 10020E, "utimens", path ? path : "(fh)", 0, 0);
673688

674689
return errormap_fuse_error(ret);
675690
}
@@ -691,13 +706,28 @@ int ltfs_fuse_chmod(const char *path, mode_t mode)
691706

692707
ltfs_request_trace(FUSE_REQ_ENTER(REQ_CHMOD), (uint64_t)mode, 0);
693708

694-
ltfsmsg(LTFS_DEBUG, 14039D, path);
695-
ret = ltfs_fsops_set_readonly_path(path, new_readonly, &id, priv->data);
709+
id.uid = 0;
710+
id.ino = 0;
711+
712+
#ifdef HAVE_FUSE3
713+
/* With nullpath_ok set, FUSE 3 may pass a NULL path for a handle-based
714+
* call on an open (possibly unlinked) file; operate on the handle. */
715+
if (fi) {
716+
struct ltfs_file_handle *file = FILEHANDLE_TO_STRUCT(fi->fh);
717+
ltfsmsg(LTFS_DEBUG, 14039D, _dentry_name(path, file->file_info));
718+
ret = ltfs_fsops_set_readonly(file->file_info->dentry_handle, new_readonly, priv->data);
719+
id.uid = ((struct dentry *)(file->file_info->dentry_handle))->uid;
720+
} else
721+
#endif
722+
{
723+
ltfsmsg(LTFS_DEBUG, 14039D, path);
724+
ret = ltfs_fsops_set_readonly_path(path, new_readonly, &id, priv->data);
725+
}
696726

697727
ltfs_request_trace(FUSE_REQ_EXIT(REQ_CHMOD), ret, id.uid);
698728

699729
if (ret)
700-
ltfsmsg(LTFS_ERR, 10020E, "chmod", path, mode, 0);
730+
ltfsmsg(LTFS_ERR, 10020E, "chmod", path ? path : "(fh)", mode, 0);
701731

702732
return errormap_fuse_error(ret);
703733
}

0 commit comments

Comments
 (0)