mirror of
https://github.com/qemu/qemu.git
synced 2025-08-15 05:06:56 +00:00
9pfs changes:
* Fixes for file descriptor reclaiming algorithm (i.e. when running towards host's allowed limit of max. open file descriptors). * Additional fixes on use-after-unlink idiom (i.e. client operations on a file descriptor after file has been removed). -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEltjREM96+AhPiFkBNMK1h2Wkc5UFAmgYie0XHHFlbXVfb3Nz QGNydWRlYnl0ZS5jb20ACgkQNMK1h2Wkc5XbDRAAq5SW7Hxifdhf1ZRBtkVOD88q Iw/OrMLIke4pCQwRElCDrE0mhycqyUpNX67eIye7qx0dJl2btFQUI9L6YuCDFtcG fPORZl51V81BOXqS8MhbK1oDxidl+cnpA8GcA1OyhYjxBifOy/x/0KG0pZVwzi0Y jhAIdsfeSenTE0Zzb02oh9mVmlMtKnwrSz7R0IB3Sv575CQiO76OM5B9sps1TUPu NrnQYBIB+EwJnI+l9NOKzNa7AUxV/S73OFCyJkQCON2ZHWiVadgXxjlX3kHyh9oL 3uiiTdC2694jU0RaVMMSNLfdIG4YK2GkKPHM7qLYF8Kdc5QogEJifS/RoihCnZFR X72G7mOVo8/7goRBt3DGQCwz3eUgqTO9iPFn1hJRvx9x/CVlFi2eOP+5nHR5PMEO qSY2of6LziCslNXvxjjhf7HmRhlugkHqpr+UGTxwMGazr88bHKNFbsh/3BcTmWwW /wGRfEFse3exgFiCtoebavxbJaUeI0Y93S4KidOhhqrQFz24k2AElgFrb1gEpbht GWW8YEblL7Lj8mecFATXKiInHCyhVPFmuAO//Wbu9juJVcNPtl67f017bCR+90H3 GrRJqorHrp6icGQmXSM+Qdrr3B21RZwqb3W4mdMOWN3Zg5bHPHJ6rx8BRe7qDHBH mWtvrsUfcL0sRW0nkgc= =hfW6 -----END PGP SIGNATURE----- Merge tag 'pull-9p-20250505' of https://github.com/cschoenebeck/qemu into staging 9pfs changes: * Fixes for file descriptor reclaiming algorithm (i.e. when running towards host's allowed limit of max. open file descriptors). * Additional fixes on use-after-unlink idiom (i.e. client operations on a file descriptor after file has been removed). # -----BEGIN PGP SIGNATURE----- # # iQJLBAABCgA1FiEEltjREM96+AhPiFkBNMK1h2Wkc5UFAmgYie0XHHFlbXVfb3Nz # QGNydWRlYnl0ZS5jb20ACgkQNMK1h2Wkc5XbDRAAq5SW7Hxifdhf1ZRBtkVOD88q # Iw/OrMLIke4pCQwRElCDrE0mhycqyUpNX67eIye7qx0dJl2btFQUI9L6YuCDFtcG # fPORZl51V81BOXqS8MhbK1oDxidl+cnpA8GcA1OyhYjxBifOy/x/0KG0pZVwzi0Y # jhAIdsfeSenTE0Zzb02oh9mVmlMtKnwrSz7R0IB3Sv575CQiO76OM5B9sps1TUPu # NrnQYBIB+EwJnI+l9NOKzNa7AUxV/S73OFCyJkQCON2ZHWiVadgXxjlX3kHyh9oL # 3uiiTdC2694jU0RaVMMSNLfdIG4YK2GkKPHM7qLYF8Kdc5QogEJifS/RoihCnZFR # X72G7mOVo8/7goRBt3DGQCwz3eUgqTO9iPFn1hJRvx9x/CVlFi2eOP+5nHR5PMEO # qSY2of6LziCslNXvxjjhf7HmRhlugkHqpr+UGTxwMGazr88bHKNFbsh/3BcTmWwW # /wGRfEFse3exgFiCtoebavxbJaUeI0Y93S4KidOhhqrQFz24k2AElgFrb1gEpbht # GWW8YEblL7Lj8mecFATXKiInHCyhVPFmuAO//Wbu9juJVcNPtl67f017bCR+90H3 # GrRJqorHrp6icGQmXSM+Qdrr3B21RZwqb3W4mdMOWN3Zg5bHPHJ6rx8BRe7qDHBH # mWtvrsUfcL0sRW0nkgc= # =hfW6 # -----END PGP SIGNATURE----- # gpg: Signature made Mon 05 May 2025 05:50:37 EDT # gpg: using RSA key 96D8D110CF7AF8084F88590134C2B58765A47395 # gpg: issuer "qemu_oss@crudebyte.com" # gpg: Good signature from "Christian Schoenebeck <qemu_oss@crudebyte.com>" [unknown] # gpg: WARNING: The key's User ID is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: ECAB 1A45 4014 1413 BA38 4926 30DB 47C3 A012 D5F4 # Subkey fingerprint: 96D8 D110 CF7A F808 4F88 5901 34C2 B587 65A4 7395 * tag 'pull-9p-20250505' of https://github.com/cschoenebeck/qemu: 9pfs: fix 'total_open_fd' decrementation tests/9p: Test `Tsetattr` can truncate unlinked file tests/9p: add 'Tsetattr' request to test client 9pfs: Introduce futimens file op 9pfs: Introduce ftruncate file op 9pfs: Don't use file descriptors in core code 9pfs: local : Introduce local_fid_fd() helper 9pfs: fix FD leak and reduce latency of v9fs_reclaim_fd() 9pfs: fix concurrent v9fs_reclaim_fd() calls Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
a9e0c9c0f1
@ -129,6 +129,8 @@ struct FileOperations {
|
|||||||
int (*chown)(FsContext *, V9fsPath *, FsCred *);
|
int (*chown)(FsContext *, V9fsPath *, FsCred *);
|
||||||
int (*mknod)(FsContext *, V9fsPath *, const char *, FsCred *);
|
int (*mknod)(FsContext *, V9fsPath *, const char *, FsCred *);
|
||||||
int (*utimensat)(FsContext *, V9fsPath *, const struct timespec *);
|
int (*utimensat)(FsContext *, V9fsPath *, const struct timespec *);
|
||||||
|
int (*futimens)(FsContext *ctx, int fid_type, V9fsFidOpenState *fs,
|
||||||
|
const struct timespec *times);
|
||||||
int (*remove)(FsContext *, const char *);
|
int (*remove)(FsContext *, const char *);
|
||||||
int (*symlink)(FsContext *, const char *, V9fsPath *,
|
int (*symlink)(FsContext *, const char *, V9fsPath *,
|
||||||
const char *, FsCred *);
|
const char *, FsCred *);
|
||||||
@ -152,6 +154,8 @@ struct FileOperations {
|
|||||||
int (*fstat)(FsContext *, int, V9fsFidOpenState *, struct stat *);
|
int (*fstat)(FsContext *, int, V9fsFidOpenState *, struct stat *);
|
||||||
int (*rename)(FsContext *, const char *, const char *);
|
int (*rename)(FsContext *, const char *, const char *);
|
||||||
int (*truncate)(FsContext *, V9fsPath *, off_t);
|
int (*truncate)(FsContext *, V9fsPath *, off_t);
|
||||||
|
int (*ftruncate)(FsContext *ctx, int fid_type, V9fsFidOpenState *fs,
|
||||||
|
off_t size);
|
||||||
int (*fsync)(FsContext *, int, V9fsFidOpenState *, int);
|
int (*fsync)(FsContext *, int, V9fsFidOpenState *, int);
|
||||||
int (*statfs)(FsContext *s, V9fsPath *path, struct statfs *stbuf);
|
int (*statfs)(FsContext *s, V9fsPath *path, struct statfs *stbuf);
|
||||||
ssize_t (*lgetxattr)(FsContext *, V9fsPath *,
|
ssize_t (*lgetxattr)(FsContext *, V9fsPath *,
|
||||||
@ -164,6 +168,7 @@ struct FileOperations {
|
|||||||
int (*renameat)(FsContext *ctx, V9fsPath *olddir, const char *old_name,
|
int (*renameat)(FsContext *ctx, V9fsPath *olddir, const char *old_name,
|
||||||
V9fsPath *newdir, const char *new_name);
|
V9fsPath *newdir, const char *new_name);
|
||||||
int (*unlinkat)(FsContext *ctx, V9fsPath *dir, const char *name, int flags);
|
int (*unlinkat)(FsContext *ctx, V9fsPath *dir, const char *name, int flags);
|
||||||
|
bool (*has_valid_file_handle)(int fid_type, V9fsFidOpenState *fs);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -766,16 +766,19 @@ out:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int local_fid_fd(int fid_type, V9fsFidOpenState *fs)
|
||||||
|
{
|
||||||
|
if (fid_type == P9_FID_DIR) {
|
||||||
|
return dirfd(fs->dir.stream);
|
||||||
|
} else {
|
||||||
|
return fs->fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int local_fstat(FsContext *fs_ctx, int fid_type,
|
static int local_fstat(FsContext *fs_ctx, int fid_type,
|
||||||
V9fsFidOpenState *fs, struct stat *stbuf)
|
V9fsFidOpenState *fs, struct stat *stbuf)
|
||||||
{
|
{
|
||||||
int err, fd;
|
int err, fd = local_fid_fd(fid_type, fs);
|
||||||
|
|
||||||
if (fid_type == P9_FID_DIR) {
|
|
||||||
fd = dirfd(fs->dir.stream);
|
|
||||||
} else {
|
|
||||||
fd = fs->fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = fstat(fd, stbuf);
|
err = fstat(fd, stbuf);
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -1039,6 +1042,14 @@ static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int local_ftruncate(FsContext *ctx, int fid_type, V9fsFidOpenState *fs,
|
||||||
|
off_t size)
|
||||||
|
{
|
||||||
|
int fd = local_fid_fd(fid_type, fs);
|
||||||
|
|
||||||
|
return ftruncate(fd, size);
|
||||||
|
}
|
||||||
|
|
||||||
static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
|
static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
|
||||||
{
|
{
|
||||||
char *dirpath = g_path_get_dirname(fs_path->data);
|
char *dirpath = g_path_get_dirname(fs_path->data);
|
||||||
@ -1089,6 +1100,14 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int local_futimens(FsContext *s, int fid_type, V9fsFidOpenState *fs,
|
||||||
|
const struct timespec *times)
|
||||||
|
{
|
||||||
|
int fd = local_fid_fd(fid_type, fs);
|
||||||
|
|
||||||
|
return qemu_futimens(fd, times);
|
||||||
|
}
|
||||||
|
|
||||||
static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
|
static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
|
||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
@ -1167,13 +1186,7 @@ out:
|
|||||||
static int local_fsync(FsContext *ctx, int fid_type,
|
static int local_fsync(FsContext *ctx, int fid_type,
|
||||||
V9fsFidOpenState *fs, int datasync)
|
V9fsFidOpenState *fs, int datasync)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd = local_fid_fd(fid_type, fs);
|
||||||
|
|
||||||
if (fid_type == P9_FID_DIR) {
|
|
||||||
fd = dirfd(fs->dir.stream);
|
|
||||||
} else {
|
|
||||||
fd = fs->fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (datasync) {
|
if (datasync) {
|
||||||
return qemu_fdatasync(fd);
|
return qemu_fdatasync(fd);
|
||||||
@ -1575,6 +1588,13 @@ static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool local_has_valid_file_handle(int fid_type, V9fsFidOpenState *fs)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
(fid_type == P9_FID_FILE && fs->fd != -1) ||
|
||||||
|
(fid_type == P9_FID_DIR && fs->dir.stream != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
FileOperations local_ops = {
|
FileOperations local_ops = {
|
||||||
.parse_opts = local_parse_opts,
|
.parse_opts = local_parse_opts,
|
||||||
.init = local_init,
|
.init = local_init,
|
||||||
@ -1612,4 +1632,7 @@ FileOperations local_ops = {
|
|||||||
.name_to_path = local_name_to_path,
|
.name_to_path = local_name_to_path,
|
||||||
.renameat = local_renameat,
|
.renameat = local_renameat,
|
||||||
.unlinkat = local_unlinkat,
|
.unlinkat = local_unlinkat,
|
||||||
|
.has_valid_file_handle = local_has_valid_file_handle,
|
||||||
|
.ftruncate = local_ftruncate,
|
||||||
|
.futimens = local_futimens,
|
||||||
};
|
};
|
||||||
|
@ -356,6 +356,13 @@ static int synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int synth_ftruncate(FsContext *ctx, int fid_type, V9fsFidOpenState *fs,
|
||||||
|
off_t size)
|
||||||
|
{
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
|
static int synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
|
||||||
{
|
{
|
||||||
errno = EPERM;
|
errno = EPERM;
|
||||||
@ -417,6 +424,13 @@ static int synth_utimensat(FsContext *fs_ctx, V9fsPath *path,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int synth_futimens(FsContext *fs_ctx, int fid_type, V9fsFidOpenState *fs,
|
||||||
|
const struct timespec *buf)
|
||||||
|
{
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int synth_remove(FsContext *ctx, const char *path)
|
static int synth_remove(FsContext *ctx, const char *path)
|
||||||
{
|
{
|
||||||
errno = EPERM;
|
errno = EPERM;
|
||||||
@ -615,6 +629,11 @@ static int synth_init(FsContext *ctx, Error **errp)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool synth_has_valid_file_handle(int fid_type, V9fsFidOpenState *fs)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
FileOperations synth_ops = {
|
FileOperations synth_ops = {
|
||||||
.init = synth_init,
|
.init = synth_init,
|
||||||
.lstat = synth_lstat,
|
.lstat = synth_lstat,
|
||||||
@ -650,4 +669,7 @@ FileOperations synth_ops = {
|
|||||||
.name_to_path = synth_name_to_path,
|
.name_to_path = synth_name_to_path,
|
||||||
.renameat = synth_renameat,
|
.renameat = synth_renameat,
|
||||||
.unlinkat = synth_unlinkat,
|
.unlinkat = synth_unlinkat,
|
||||||
|
.has_valid_file_handle = synth_has_valid_file_handle,
|
||||||
|
.ftruncate = synth_ftruncate,
|
||||||
|
.futimens = synth_futimens,
|
||||||
};
|
};
|
||||||
|
@ -103,6 +103,7 @@ static inline int errno_to_dotl(int err) {
|
|||||||
#define qemu_renameat renameat
|
#define qemu_renameat renameat
|
||||||
#define qemu_utimensat utimensat
|
#define qemu_utimensat utimensat
|
||||||
#define qemu_unlinkat unlinkat
|
#define qemu_unlinkat unlinkat
|
||||||
|
#define qemu_futimens futimens
|
||||||
|
|
||||||
static inline void close_preserve_errno(int fd)
|
static inline void close_preserve_errno(int fd)
|
||||||
{
|
{
|
||||||
|
64
hw/9pfs/9p.c
64
hw/9pfs/9p.c
@ -434,16 +434,24 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu)
|
|||||||
V9fsFidState *f;
|
V9fsFidState *f;
|
||||||
GHashTableIter iter;
|
GHashTableIter iter;
|
||||||
gpointer fid;
|
gpointer fid;
|
||||||
|
int err;
|
||||||
|
int nclosed = 0;
|
||||||
|
|
||||||
|
/* prevent multiple coroutines running this function simultaniously */
|
||||||
|
if (s->reclaiming) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s->reclaiming = true;
|
||||||
|
|
||||||
g_hash_table_iter_init(&iter, s->fids);
|
g_hash_table_iter_init(&iter, s->fids);
|
||||||
|
|
||||||
QSLIST_HEAD(, V9fsFidState) reclaim_list =
|
QSLIST_HEAD(, V9fsFidState) reclaim_list =
|
||||||
QSLIST_HEAD_INITIALIZER(reclaim_list);
|
QSLIST_HEAD_INITIALIZER(reclaim_list);
|
||||||
|
|
||||||
|
/* Pick FIDs to be closed, collect them on reclaim_list. */
|
||||||
while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &f)) {
|
while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &f)) {
|
||||||
/*
|
/*
|
||||||
* Unlink fids cannot be reclaimed. Check
|
* Unlinked fids cannot be reclaimed, skip those, and also skip fids
|
||||||
* for them and skip them. Also skip fids
|
|
||||||
* currently being operated on.
|
* currently being operated on.
|
||||||
*/
|
*/
|
||||||
if (f->ref || f->flags & FID_NON_RECLAIMABLE) {
|
if (f->ref || f->flags & FID_NON_RECLAIMABLE) {
|
||||||
@ -493,23 +501,42 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Now close the fid in reclaim list. Free them if they
|
* Close the picked FIDs altogether on a background I/O driver thread. Do
|
||||||
* are already clunked.
|
* this all at once to keep latency (i.e. amount of thread hops between main
|
||||||
|
* thread <-> fs driver background thread) as low as possible.
|
||||||
*/
|
*/
|
||||||
|
v9fs_co_run_in_worker({
|
||||||
|
QSLIST_FOREACH(f, &reclaim_list, reclaim_next) {
|
||||||
|
err = (f->fid_type == P9_FID_DIR) ?
|
||||||
|
s->ops->closedir(&s->ctx, &f->fs_reclaim) :
|
||||||
|
s->ops->close(&s->ctx, &f->fs_reclaim);
|
||||||
|
|
||||||
|
/* 'man 2 close' suggests to ignore close() errors except of EBADF */
|
||||||
|
if (unlikely(err && errno == EBADF)) {
|
||||||
|
/*
|
||||||
|
* unexpected case as FIDs were picked above by having a valid
|
||||||
|
* file descriptor
|
||||||
|
*/
|
||||||
|
error_report("9pfs: v9fs_reclaim_fd() WARNING: close() failed with EBADF");
|
||||||
|
} else {
|
||||||
|
/* total_open_fd must only be mutated on main thread */
|
||||||
|
nclosed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
total_open_fd -= nclosed;
|
||||||
|
/* Free the closed FIDs. */
|
||||||
while (!QSLIST_EMPTY(&reclaim_list)) {
|
while (!QSLIST_EMPTY(&reclaim_list)) {
|
||||||
f = QSLIST_FIRST(&reclaim_list);
|
f = QSLIST_FIRST(&reclaim_list);
|
||||||
QSLIST_REMOVE(&reclaim_list, f, V9fsFidState, reclaim_next);
|
QSLIST_REMOVE(&reclaim_list, f, V9fsFidState, reclaim_next);
|
||||||
if (f->fid_type == P9_FID_FILE) {
|
|
||||||
v9fs_co_close(pdu, &f->fs_reclaim);
|
|
||||||
} else if (f->fid_type == P9_FID_DIR) {
|
|
||||||
v9fs_co_closedir(pdu, &f->fs_reclaim);
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* Now drop the fid reference, free it
|
* Now drop the fid reference, free it
|
||||||
* if clunked.
|
* if clunked.
|
||||||
*/
|
*/
|
||||||
put_fid(pdu, f);
|
put_fid(pdu, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s->reclaiming = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1574,6 +1601,11 @@ out_nofid:
|
|||||||
pdu_complete(pdu, err);
|
pdu_complete(pdu, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool fid_has_valid_file_handle(V9fsState *s, V9fsFidState *fidp)
|
||||||
|
{
|
||||||
|
return s->ops->has_valid_file_handle(fidp->fid_type, &fidp->fs);
|
||||||
|
}
|
||||||
|
|
||||||
static void coroutine_fn v9fs_getattr(void *opaque)
|
static void coroutine_fn v9fs_getattr(void *opaque)
|
||||||
{
|
{
|
||||||
int32_t fid;
|
int32_t fid;
|
||||||
@ -1596,9 +1628,7 @@ static void coroutine_fn v9fs_getattr(void *opaque)
|
|||||||
retval = -ENOENT;
|
retval = -ENOENT;
|
||||||
goto out_nofid;
|
goto out_nofid;
|
||||||
}
|
}
|
||||||
if ((fidp->fid_type == P9_FID_FILE && fidp->fs.fd != -1) ||
|
if (fid_has_valid_file_handle(pdu->s, fidp)) {
|
||||||
(fidp->fid_type == P9_FID_DIR && fidp->fs.dir.stream))
|
|
||||||
{
|
|
||||||
retval = v9fs_co_fstat(pdu, fidp, &stbuf);
|
retval = v9fs_co_fstat(pdu, fidp, &stbuf);
|
||||||
} else {
|
} else {
|
||||||
retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
|
retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
|
||||||
@ -1705,7 +1735,11 @@ static void coroutine_fn v9fs_setattr(void *opaque)
|
|||||||
} else {
|
} else {
|
||||||
times[1].tv_nsec = UTIME_OMIT;
|
times[1].tv_nsec = UTIME_OMIT;
|
||||||
}
|
}
|
||||||
|
if (fid_has_valid_file_handle(pdu->s, fidp)) {
|
||||||
|
err = v9fs_co_futimens(pdu, fidp, times);
|
||||||
|
} else {
|
||||||
err = v9fs_co_utimensat(pdu, &fidp->path, times);
|
err = v9fs_co_utimensat(pdu, &fidp->path, times);
|
||||||
|
}
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -1730,7 +1764,11 @@ static void coroutine_fn v9fs_setattr(void *opaque)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (v9iattr.valid & (P9_ATTR_SIZE)) {
|
if (v9iattr.valid & (P9_ATTR_SIZE)) {
|
||||||
|
if (fid_has_valid_file_handle(pdu->s, fidp)) {
|
||||||
|
err = v9fs_co_ftruncate(pdu, fidp, v9iattr.size);
|
||||||
|
} else {
|
||||||
err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size);
|
err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size);
|
||||||
|
}
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -4324,6 +4362,8 @@ int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t,
|
|||||||
s->ctx.fst = &fse->fst;
|
s->ctx.fst = &fse->fst;
|
||||||
fsdev_throttle_init(s->ctx.fst);
|
fsdev_throttle_init(s->ctx.fst);
|
||||||
|
|
||||||
|
s->reclaiming = false;
|
||||||
|
|
||||||
rc = 0;
|
rc = 0;
|
||||||
out:
|
out:
|
||||||
if (rc) {
|
if (rc) {
|
||||||
|
@ -362,6 +362,7 @@ struct V9fsState {
|
|||||||
uint64_t qp_ndevices; /* Amount of entries in qpd_table. */
|
uint64_t qp_ndevices; /* Amount of entries in qpd_table. */
|
||||||
uint16_t qp_affix_next;
|
uint16_t qp_affix_next;
|
||||||
uint64_t qp_fullpath_next;
|
uint64_t qp_fullpath_next;
|
||||||
|
bool reclaiming;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* 9p2000.L open flags */
|
/* 9p2000.L open flags */
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "fsdev/qemu-fsdev.h"
|
#include "fsdev/qemu-fsdev.h"
|
||||||
#include "qemu/thread.h"
|
#include "qemu/thread.h"
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
#include "coth.h"
|
#include "coth.h"
|
||||||
#include "9p-xattr.h"
|
#include "9p-xattr.h"
|
||||||
#include "9p-util.h"
|
#include "9p-util.h"
|
||||||
@ -353,7 +354,11 @@ int coroutine_fn v9fs_co_closedir(V9fsPDU *pdu, V9fsFidOpenState *fs)
|
|||||||
err = -errno;
|
err = -errno;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!err) {
|
/* 'man 2 close' suggests to ignore close() errors except of EBADF */
|
||||||
|
if (unlikely(err && errno == EBADF)) {
|
||||||
|
/* unexpected case as we should have checked for a valid file handle */
|
||||||
|
error_report("9pfs: WARNING: v9fs_co_closedir() failed with EBADF");
|
||||||
|
} else {
|
||||||
total_open_fd--;
|
total_open_fd--;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "fsdev/qemu-fsdev.h"
|
#include "fsdev/qemu-fsdev.h"
|
||||||
#include "qemu/thread.h"
|
#include "qemu/thread.h"
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
#include "coth.h"
|
#include "coth.h"
|
||||||
|
|
||||||
int coroutine_fn v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode,
|
int coroutine_fn v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode,
|
||||||
@ -197,7 +198,11 @@ int coroutine_fn v9fs_co_close(V9fsPDU *pdu, V9fsFidOpenState *fs)
|
|||||||
err = -errno;
|
err = -errno;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!err) {
|
/* 'man 2 close' suggests to ignore close() errors except of EBADF */
|
||||||
|
if (unlikely(err && errno == EBADF)) {
|
||||||
|
/* unexpected case as we should have checked for a valid file handle */
|
||||||
|
error_report("9pfs: WARNING: v9fs_co_close() failed with EBADF");
|
||||||
|
} else {
|
||||||
total_open_fd--;
|
total_open_fd--;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
|
@ -139,6 +139,25 @@ int coroutine_fn v9fs_co_utimensat(V9fsPDU *pdu, V9fsPath *path,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int coroutine_fn v9fs_co_futimens(V9fsPDU *pdu, V9fsFidState *fidp,
|
||||||
|
struct timespec times[2])
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
V9fsState *s = pdu->s;
|
||||||
|
|
||||||
|
if (v9fs_request_cancelled(pdu)) {
|
||||||
|
return -EINTR;
|
||||||
|
}
|
||||||
|
v9fs_co_run_in_worker(
|
||||||
|
{
|
||||||
|
err = s->ops->futimens(&s->ctx, fidp->fid_type, &fidp->fs, times);
|
||||||
|
if (err < 0) {
|
||||||
|
err = -errno;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
int coroutine_fn v9fs_co_chown(V9fsPDU *pdu, V9fsPath *path, uid_t uid,
|
int coroutine_fn v9fs_co_chown(V9fsPDU *pdu, V9fsPath *path, uid_t uid,
|
||||||
gid_t gid)
|
gid_t gid)
|
||||||
{
|
{
|
||||||
@ -184,6 +203,24 @@ int coroutine_fn v9fs_co_truncate(V9fsPDU *pdu, V9fsPath *path, off_t size)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int coroutine_fn v9fs_co_ftruncate(V9fsPDU *pdu, V9fsFidState *fidp, off_t size)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
V9fsState *s = pdu->s;
|
||||||
|
|
||||||
|
if (v9fs_request_cancelled(pdu)) {
|
||||||
|
return -EINTR;
|
||||||
|
}
|
||||||
|
v9fs_co_run_in_worker(
|
||||||
|
{
|
||||||
|
err = s->ops->ftruncate(&s->ctx, fidp->fid_type, &fidp->fs, size);
|
||||||
|
if (err < 0) {
|
||||||
|
err = -errno;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
int coroutine_fn v9fs_co_mknod(V9fsPDU *pdu, V9fsFidState *fidp,
|
int coroutine_fn v9fs_co_mknod(V9fsPDU *pdu, V9fsFidState *fidp,
|
||||||
V9fsString *name, uid_t uid, gid_t gid,
|
V9fsString *name, uid_t uid, gid_t gid,
|
||||||
dev_t dev, mode_t mode, struct stat *stbuf)
|
dev_t dev, mode_t mode, struct stat *stbuf)
|
||||||
|
@ -71,8 +71,12 @@ int coroutine_fn v9fs_co_statfs(V9fsPDU *, V9fsPath *, struct statfs *);
|
|||||||
int coroutine_fn v9fs_co_lstat(V9fsPDU *, V9fsPath *, struct stat *);
|
int coroutine_fn v9fs_co_lstat(V9fsPDU *, V9fsPath *, struct stat *);
|
||||||
int coroutine_fn v9fs_co_chmod(V9fsPDU *, V9fsPath *, mode_t);
|
int coroutine_fn v9fs_co_chmod(V9fsPDU *, V9fsPath *, mode_t);
|
||||||
int coroutine_fn v9fs_co_utimensat(V9fsPDU *, V9fsPath *, struct timespec [2]);
|
int coroutine_fn v9fs_co_utimensat(V9fsPDU *, V9fsPath *, struct timespec [2]);
|
||||||
|
int coroutine_fn v9fs_co_futimens(V9fsPDU *pdu, V9fsFidState *fidp,
|
||||||
|
struct timespec times[2]);
|
||||||
int coroutine_fn v9fs_co_chown(V9fsPDU *, V9fsPath *, uid_t, gid_t);
|
int coroutine_fn v9fs_co_chown(V9fsPDU *, V9fsPath *, uid_t, gid_t);
|
||||||
int coroutine_fn v9fs_co_truncate(V9fsPDU *, V9fsPath *, off_t);
|
int coroutine_fn v9fs_co_truncate(V9fsPDU *, V9fsPath *, off_t);
|
||||||
|
int coroutine_fn v9fs_co_ftruncate(V9fsPDU *pdu, V9fsFidState *fidp,
|
||||||
|
off_t size);
|
||||||
int coroutine_fn v9fs_co_llistxattr(V9fsPDU *, V9fsPath *, void *, size_t);
|
int coroutine_fn v9fs_co_llistxattr(V9fsPDU *, V9fsPath *, void *, size_t);
|
||||||
int coroutine_fn v9fs_co_lgetxattr(V9fsPDU *, V9fsPath *,
|
int coroutine_fn v9fs_co_lgetxattr(V9fsPDU *, V9fsPath *,
|
||||||
V9fsString *, void *, size_t);
|
V9fsString *, void *, size_t);
|
||||||
|
@ -557,6 +557,55 @@ void v9fs_rgetattr(P9Req *req, v9fs_attr *attr)
|
|||||||
v9fs_req_free(req);
|
v9fs_req_free(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* size[4] Tsetattr tag[2] fid[4] valid[4] mode[4] uid[4] gid[4] size[8]
|
||||||
|
* atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8]
|
||||||
|
*/
|
||||||
|
TSetAttrRes v9fs_tsetattr(TSetAttrOpt opt)
|
||||||
|
{
|
||||||
|
P9Req *req;
|
||||||
|
uint32_t err;
|
||||||
|
|
||||||
|
g_assert(opt.client);
|
||||||
|
|
||||||
|
req = v9fs_req_init(
|
||||||
|
opt.client, 4/*fid*/ + 4/*valid*/ + 4/*mode*/ + 4/*uid*/ + 4/*gid*/ +
|
||||||
|
8/*size*/ + 8/*atime_sec*/ + 8/*atime_nsec*/ + 8/*mtime_sec*/ +
|
||||||
|
8/*mtime_nsec*/, P9_TSETATTR, opt.tag
|
||||||
|
);
|
||||||
|
v9fs_uint32_write(req, opt.fid);
|
||||||
|
v9fs_uint32_write(req, (uint32_t) opt.attr.valid);
|
||||||
|
v9fs_uint32_write(req, opt.attr.mode);
|
||||||
|
v9fs_uint32_write(req, opt.attr.uid);
|
||||||
|
v9fs_uint32_write(req, opt.attr.gid);
|
||||||
|
v9fs_uint64_write(req, opt.attr.size);
|
||||||
|
v9fs_uint64_write(req, opt.attr.atime_sec);
|
||||||
|
v9fs_uint64_write(req, opt.attr.atime_nsec);
|
||||||
|
v9fs_uint64_write(req, opt.attr.mtime_sec);
|
||||||
|
v9fs_uint64_write(req, opt.attr.mtime_nsec);
|
||||||
|
v9fs_req_send(req);
|
||||||
|
|
||||||
|
if (!opt.requestOnly) {
|
||||||
|
v9fs_req_wait_for_reply(req, NULL);
|
||||||
|
if (opt.expectErr) {
|
||||||
|
v9fs_rlerror(req, &err);
|
||||||
|
g_assert_cmpint(err, ==, opt.expectErr);
|
||||||
|
} else {
|
||||||
|
v9fs_rsetattr(req);
|
||||||
|
}
|
||||||
|
req = NULL; /* request was freed */
|
||||||
|
}
|
||||||
|
|
||||||
|
return (TSetAttrRes) { .req = req };
|
||||||
|
}
|
||||||
|
|
||||||
|
/* size[4] Rsetattr tag[2] */
|
||||||
|
void v9fs_rsetattr(P9Req *req)
|
||||||
|
{
|
||||||
|
v9fs_req_recv(req, P9_RSETATTR);
|
||||||
|
v9fs_req_free(req);
|
||||||
|
}
|
||||||
|
|
||||||
/* size[4] Treaddir tag[2] fid[4] offset[8] count[4] */
|
/* size[4] Treaddir tag[2] fid[4] offset[8] count[4] */
|
||||||
TReadDirRes v9fs_treaddir(TReadDirOpt opt)
|
TReadDirRes v9fs_treaddir(TReadDirOpt opt)
|
||||||
{
|
{
|
||||||
|
@ -65,6 +65,16 @@ typedef struct v9fs_attr {
|
|||||||
#define P9_GETATTR_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */
|
#define P9_GETATTR_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */
|
||||||
#define P9_GETATTR_ALL 0x00003fffULL /* Mask for ALL fields */
|
#define P9_GETATTR_ALL 0x00003fffULL /* Mask for ALL fields */
|
||||||
|
|
||||||
|
#define P9_SETATTR_MODE 0x00000001UL
|
||||||
|
#define P9_SETATTR_UID 0x00000002UL
|
||||||
|
#define P9_SETATTR_GID 0x00000004UL
|
||||||
|
#define P9_SETATTR_SIZE 0x00000008UL
|
||||||
|
#define P9_SETATTR_ATIME 0x00000010UL
|
||||||
|
#define P9_SETATTR_MTIME 0x00000020UL
|
||||||
|
#define P9_SETATTR_CTIME 0x00000040UL
|
||||||
|
#define P9_SETATTR_ATIME_SET 0x00000080UL
|
||||||
|
#define P9_SETATTR_MTIME_SET 0x00000100UL
|
||||||
|
|
||||||
struct V9fsDirent {
|
struct V9fsDirent {
|
||||||
v9fs_qid qid;
|
v9fs_qid qid;
|
||||||
uint64_t offset;
|
uint64_t offset;
|
||||||
@ -182,6 +192,28 @@ typedef struct TGetAttrRes {
|
|||||||
P9Req *req;
|
P9Req *req;
|
||||||
} TGetAttrRes;
|
} TGetAttrRes;
|
||||||
|
|
||||||
|
/* options for 'Tsetattr' 9p request */
|
||||||
|
typedef struct TSetAttrOpt {
|
||||||
|
/* 9P client being used (mandatory) */
|
||||||
|
QVirtio9P *client;
|
||||||
|
/* user supplied tag number being returned with response (optional) */
|
||||||
|
uint16_t tag;
|
||||||
|
/* file ID of file/dir whose attributes shall be modified (required) */
|
||||||
|
uint32_t fid;
|
||||||
|
/* new attribute values to be set by 9p server */
|
||||||
|
v9fs_attr attr;
|
||||||
|
/* only send Tsetattr request but not wait for a reply? (optional) */
|
||||||
|
bool requestOnly;
|
||||||
|
/* do we expect an Rlerror response, if yes which error code? (optional) */
|
||||||
|
uint32_t expectErr;
|
||||||
|
} TSetAttrOpt;
|
||||||
|
|
||||||
|
/* result of 'Tsetattr' 9p request */
|
||||||
|
typedef struct TSetAttrRes {
|
||||||
|
/* if requestOnly was set: request object for further processing */
|
||||||
|
P9Req *req;
|
||||||
|
} TSetAttrRes;
|
||||||
|
|
||||||
/* options for 'Treaddir' 9p request */
|
/* options for 'Treaddir' 9p request */
|
||||||
typedef struct TReadDirOpt {
|
typedef struct TReadDirOpt {
|
||||||
/* 9P client being used (mandatory) */
|
/* 9P client being used (mandatory) */
|
||||||
@ -470,6 +502,8 @@ TWalkRes v9fs_twalk(TWalkOpt opt);
|
|||||||
void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid);
|
void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid);
|
||||||
TGetAttrRes v9fs_tgetattr(TGetAttrOpt);
|
TGetAttrRes v9fs_tgetattr(TGetAttrOpt);
|
||||||
void v9fs_rgetattr(P9Req *req, v9fs_attr *attr);
|
void v9fs_rgetattr(P9Req *req, v9fs_attr *attr);
|
||||||
|
TSetAttrRes v9fs_tsetattr(TSetAttrOpt opt);
|
||||||
|
void v9fs_rsetattr(P9Req *req);
|
||||||
TReadDirRes v9fs_treaddir(TReadDirOpt);
|
TReadDirRes v9fs_treaddir(TReadDirOpt);
|
||||||
void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries,
|
void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries,
|
||||||
struct V9fsDirent **entries);
|
struct V9fsDirent **entries);
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#define tversion(...) v9fs_tversion((TVersionOpt) __VA_ARGS__)
|
#define tversion(...) v9fs_tversion((TVersionOpt) __VA_ARGS__)
|
||||||
#define tattach(...) v9fs_tattach((TAttachOpt) __VA_ARGS__)
|
#define tattach(...) v9fs_tattach((TAttachOpt) __VA_ARGS__)
|
||||||
#define tgetattr(...) v9fs_tgetattr((TGetAttrOpt) __VA_ARGS__)
|
#define tgetattr(...) v9fs_tgetattr((TGetAttrOpt) __VA_ARGS__)
|
||||||
|
#define tsetattr(...) v9fs_tsetattr((TSetAttrOpt) __VA_ARGS__)
|
||||||
#define treaddir(...) v9fs_treaddir((TReadDirOpt) __VA_ARGS__)
|
#define treaddir(...) v9fs_treaddir((TReadDirOpt) __VA_ARGS__)
|
||||||
#define tlopen(...) v9fs_tlopen((TLOpenOpt) __VA_ARGS__)
|
#define tlopen(...) v9fs_tlopen((TLOpenOpt) __VA_ARGS__)
|
||||||
#define twrite(...) v9fs_twrite((TWriteOpt) __VA_ARGS__)
|
#define twrite(...) v9fs_twrite((TWriteOpt) __VA_ARGS__)
|
||||||
@ -735,6 +736,20 @@ static void fs_use_after_unlink(void *obj, void *data,
|
|||||||
.data = buf
|
.data = buf
|
||||||
}).count;
|
}).count;
|
||||||
g_assert_cmpint(count, ==, write_count);
|
g_assert_cmpint(count, ==, write_count);
|
||||||
|
|
||||||
|
/* truncate file to (arbitrarily chosen) size 2001 */
|
||||||
|
tsetattr({
|
||||||
|
.client = v9p, .fid = fid_file, .attr = (v9fs_attr) {
|
||||||
|
.valid = P9_SETATTR_SIZE,
|
||||||
|
.size = 2001
|
||||||
|
}
|
||||||
|
});
|
||||||
|
/* truncate apparently succeeded, let's double-check the size */
|
||||||
|
tgetattr({
|
||||||
|
.client = v9p, .fid = fid_file, .request_mask = P9_GETATTR_BASIC,
|
||||||
|
.rgetattr.attr = &attr
|
||||||
|
});
|
||||||
|
g_assert_cmpint(attr.size, ==, 2001);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanup_9p_local_driver(void *data)
|
static void cleanup_9p_local_driver(void *data)
|
||||||
|
Loading…
Reference in New Issue
Block a user