NetBSD/usr.sbin/puffs/mount_psshfs/fs.c
pooka 326638b53f In case we create a file, reclaim the vnode, and lookup the file
without the directory timeout expiring, we get vattr_null as the
attributes for that file.  Ensure that we always report sane
attributes by getting them from the server if this is the case.
(also, sprinkle some const)

Fixes problem reported by dyoung.   But wait!  The bug's medallion
begins to glow!  The bug looks much better!  I crumble to dust.

There's probably another similar bug related to "lazy open".  It
will trigger if we reclaim a node before the response to the open
arrives.  Even the comments (typoless) know about this bug ...
But verifying it exists and fixing it will have to wait for another
day.
2010-04-01 02:34:09 +00:00

282 lines
7.2 KiB
C

/* $NetBSD: fs.c,v 1.22 2010/04/01 02:34:09 pooka Exp $ */
/*
* Copyright (c) 2006-2009 Antti Kantee. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: fs.c,v 1.22 2010/04/01 02:34:09 pooka Exp $");
#endif /* !lint */
#include <err.h>
#include <errno.h>
#include <puffs.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "psshfs.h"
#include "sftp_proto.h"
#define DO_IO(fname, a1, a2, a3, a4, rv) \
do { \
puffs_framebuf_seekset(a2, 0); \
*(a4) = 0; \
rv = fname(a1, a2, a3, a4); \
if (rv || a4 == 0) { \
fprintf(stderr, "psshfs_handshake failed %d (%s) %d\n", \
rv, strerror(rv), *a4); \
return rv ? rv : EPROTO; \
} \
} while (/*CONSTCOND*/0)
#define reterr(str, rv) \
do { \
fprintf str; \
return rv; \
} while (/*CONSTCOND*/0)
/* openssh extensions */
static const struct extunit {
const char *ext;
const char *val;
int extflag;
} exttable[] = {
{
"posix-rename@openssh.com",
"1",
SFTP_EXT_POSIX_RENAME,
},{
"statvfs@openssh.com",
"2",
SFTP_EXT_STATVFS,
},{
"fstatvfs@openssh.com",
"2",
SFTP_EXT_FSTATVFS,
},{
NULL,
NULL,
0
}};
int
psshfs_handshake(struct puffs_usermount *pu, int fd)
{
struct psshfs_ctx *pctx = puffs_getspecific(pu);
struct puffs_framebuf *pb;
struct puffs_pathobj *po_root;
struct puffs_node *pn_root;
struct vattr va, *rva;
const struct extunit *extu;
char *rootpath;
char *ext, *val;
uint32_t count;
int rv, done;
pb = psbuf_makeout();
psbuf_put_1(pb, SSH_FXP_INIT);
psbuf_put_4(pb, SFTP_PROTOVERSION);
DO_IO(psbuf_write, pu, pb, fd, &done, rv);
puffs_framebuf_recycle(pb);
DO_IO(psbuf_read, pu, pb, fd, &done, rv);
if (psbuf_get_type(pb) != SSH_FXP_VERSION)
reterr((stderr, "invalid server response: %d",
psbuf_get_type(pb)), EPROTO);
pctx->protover = psbuf_get_reqid(pb);
/*
* Check out which extensions are available. Currently
* we are only interested in the openssh statvfs extension.
*/
for (;;) {
if (psbuf_get_str(pb, &ext, NULL) != 0)
break;
if (psbuf_get_str(pb, &val, NULL) != 0)
break;
for (extu = exttable; extu->ext; extu++)
if (strcmp(ext, extu->ext) == 0
&& strcmp(val, extu->val) == 0)
pctx->extensions |= extu->extflag;
}
/* scope out our rootpath */
psbuf_recycleout(pb);
psbuf_put_1(pb, SSH_FXP_REALPATH);
psbuf_put_4(pb, NEXTREQ(pctx));
psbuf_put_str(pb, pctx->mountpath);
DO_IO(psbuf_write, pu, pb, fd, &done, rv);
puffs_framebuf_recycle(pb);
DO_IO(psbuf_read, pu, pb, fd, &done, rv);
if (psbuf_get_type(pb) != SSH_FXP_NAME)
reterr((stderr, "invalid server realpath response for \"%s\"",
pctx->mountpath), EPROTO);
if (psbuf_get_4(pb, &count) == -1)
reterr((stderr, "invalid realpath response: count"), EPROTO);
if (psbuf_get_str(pb, &rootpath, NULL) == -1)
reterr((stderr, "invalid realpath response: rootpath"), EPROTO);
/* stat the rootdir so that we know it's a dir */
psbuf_recycleout(pb);
psbuf_req_str(pb, SSH_FXP_LSTAT, NEXTREQ(pctx), rootpath);
DO_IO(psbuf_write, pu, pb, fd, &done, rv);
puffs_framebuf_recycle(pb);
DO_IO(psbuf_read, pu, pb, fd, &done, rv);
rv = psbuf_expect_attrs(pb, &va);
if (rv)
reterr((stderr, "couldn't stat rootpath"), rv);
puffs_framebuf_destroy(pb);
if (puffs_mode2vt(va.va_mode) != VDIR)
reterr((stderr, "remote path (%s) not a directory", rootpath),
ENOTDIR);
pn_root = puffs_getroot(pu);
rva = &pn_root->pn_va;
puffs_setvattr(rva, &va);
po_root = puffs_getrootpathobj(pu);
if (po_root == NULL)
err(1, "getrootpathobj");
po_root->po_path = rootpath;
po_root->po_len = strlen(rootpath);
return 0;
}
int
psshfs_fs_statvfs(struct puffs_usermount *pu, struct statvfs *sbp)
{
PSSHFSAUTOVAR(pu);
uint64_t tmpval;
uint8_t type;
memset(sbp, 0, sizeof(*sbp));
sbp->f_bsize = sbp->f_frsize = sbp->f_iosize = 512;
if ((pctx->extensions & SFTP_EXT_STATVFS) == 0)
goto out;
psbuf_req_str(pb, SSH_FXP_EXTENDED, reqid, "statvfs@openssh.com");
psbuf_put_str(pb, pctx->mountpath);
GETRESPONSE(pb, pctx->sshfd);
type = psbuf_get_type(pb);
if (type != SSH_FXP_EXTENDED_REPLY) {
/* use the default */
goto out;
}
psbuf_get_8(pb, &tmpval);
sbp->f_bsize = tmpval;
psbuf_get_8(pb, &tmpval);
sbp->f_frsize = tmpval;
psbuf_get_8(pb, &sbp->f_blocks);
psbuf_get_8(pb, &sbp->f_bfree);
psbuf_get_8(pb, &sbp->f_bavail);
psbuf_get_8(pb, &sbp->f_files);
psbuf_get_8(pb, &sbp->f_ffree);
psbuf_get_8(pb, &sbp->f_favail);
psbuf_get_8(pb, &tmpval); /* fsid */
psbuf_get_8(pb, &tmpval); /* flag */
psbuf_get_8(pb, &tmpval);
sbp->f_namemax = tmpval;
sbp->f_bresvd = sbp->f_bfree - sbp->f_bavail;
sbp->f_fresvd = sbp->f_ffree - sbp->f_favail;
out:
PSSHFSRETURN(rv);
}
int
psshfs_fs_unmount(struct puffs_usermount *pu, int flags)
{
struct psshfs_ctx *pctx = puffs_getspecific(pu);
kill(pctx->sshpid, SIGTERM);
close(pctx->sshfd);
if (pctx->numconnections == 2) {
kill(pctx->sshpid_data, SIGTERM);
close(pctx->sshfd_data);
}
return 0;
}
int
psshfs_fs_nodetofh(struct puffs_usermount *pu, puffs_cookie_t cookie,
void *fid, size_t *fidsize)
{
struct psshfs_ctx *pctx = puffs_getspecific(pu);
struct puffs_node *pn = cookie;
struct psshfs_node *psn = pn->pn_data;
struct psshfs_fid *pf = fid;
pf->mounttime = pctx->mounttime;
pf->node = pn;
psn->stat |= PSN_HASFH;
return 0;
}
int
psshfs_fs_fhtonode(struct puffs_usermount *pu, void *fid, size_t fidsize,
struct puffs_newinfo *pni)
{
struct psshfs_ctx *pctx = puffs_getspecific(pu);
struct psshfs_fid *pf = fid;
struct puffs_node *pn = pf->node;
struct psshfs_node *psn;
int rv;
if (pf->mounttime != pctx->mounttime)
return EINVAL;
if (pn == 0)
return EINVAL;
psn = pn->pn_data;
if ((psn->stat & PSN_HASFH) == 0)
return EINVAL;
/* update node attributes */
rv = getnodeattr(pu, pn, NULL);
if (rv)
return EINVAL;
puffs_newinfo_setcookie(pni, pn);
puffs_newinfo_setvtype(pni, pn->pn_va.va_type);
puffs_newinfo_setsize(pni, pn->pn_va.va_size);
return 0;
}