1/* $NetBSD: fs.c,v 1.23 2010/10/29 16:13:51 pooka Exp $ */ 2 3/* 4 * Copyright (c) 2006-2009 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29#ifndef lint 30__RCSID("$NetBSD: fs.c,v 1.23 2010/10/29 16:13:51 pooka Exp $"); 31#endif /* !lint */ 32 33#include <err.h> 34#include <errno.h> 35#include <puffs.h> 36#include <signal.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <unistd.h> 40 41#include "psshfs.h" 42#include "sftp_proto.h" 43 44#define DO_IO(fname, a1, a2, a3, a4, rv) \ 45do { \ 46 puffs_framebuf_seekset(a2, 0); \ 47 *(a4) = 0; \ 48 rv = fname(a1, a2, a3, a4); \ 49 if (rv) { \ 50 return rv ? rv : EPROTO; \ 51 } \ 52} while (/*CONSTCOND*/0) 53 54#define reterr(str, rv) \ 55do { \ 56 fprintf str; \ 57 return rv; \ 58} while (/*CONSTCOND*/0) 59 60/* openssh extensions */ 61static const struct extunit { 62 const char *ext; 63 const char *val; 64 int extflag; 65} exttable[] = { 66{ 67 "posix-rename@openssh.com", 68 "1", 69 SFTP_EXT_POSIX_RENAME, 70},{ 71 "statvfs@openssh.com", 72 "2", 73 SFTP_EXT_STATVFS, 74},{ 75 "fstatvfs@openssh.com", 76 "2", 77 SFTP_EXT_FSTATVFS, 78},{ 79 NULL, 80 NULL, 81 0 82}}; 83 84int 85psshfs_handshake(struct puffs_usermount *pu, int fd) 86{ 87 struct psshfs_ctx *pctx = puffs_getspecific(pu); 88 struct puffs_framebuf *pb; 89 struct puffs_pathobj *po_root; 90 struct puffs_node *pn_root; 91 struct vattr va, *rva; 92 const struct extunit *extu; 93 char *rootpath; 94 char *ext, *val; 95 uint32_t count; 96 int rv, done; 97 98 pb = psbuf_makeout(); 99 psbuf_put_1(pb, SSH_FXP_INIT); 100 psbuf_put_4(pb, SFTP_PROTOVERSION); 101 DO_IO(psbuf_write, pu, pb, fd, &done, rv); 102 103 puffs_framebuf_recycle(pb); 104 DO_IO(psbuf_read, pu, pb, fd, &done, rv); 105 if (psbuf_get_type(pb) != SSH_FXP_VERSION) 106 reterr((stderr, "invalid server response: %d", 107 psbuf_get_type(pb)), EPROTO); 108 pctx->protover = psbuf_get_reqid(pb); 109 110 /* 111 * Check out which extensions are available. Currently 112 * we are only interested in the openssh statvfs extension. 113 */ 114 for (;;) { 115 if (psbuf_get_str(pb, &ext, NULL) != 0) 116 break; 117 if (psbuf_get_str(pb, &val, NULL) != 0) 118 break; 119 120 for (extu = exttable; extu->ext; extu++) 121 if (strcmp(ext, extu->ext) == 0 122 && strcmp(val, extu->val) == 0) 123 pctx->extensions |= extu->extflag; 124 } 125 126 /* scope out our rootpath */ 127 psbuf_recycleout(pb); 128 psbuf_put_1(pb, SSH_FXP_REALPATH); 129 psbuf_put_4(pb, NEXTREQ(pctx)); 130 psbuf_put_str(pb, pctx->mountpath); 131 DO_IO(psbuf_write, pu, pb, fd, &done, rv); 132 133 puffs_framebuf_recycle(pb); 134 DO_IO(psbuf_read, pu, pb, fd, &done, rv); 135 if (psbuf_get_type(pb) != SSH_FXP_NAME) 136 reterr((stderr, "invalid server realpath response for \"%s\"", 137 pctx->mountpath), EPROTO); 138 if (psbuf_get_4(pb, &count) == -1) 139 reterr((stderr, "invalid realpath response: count"), EPROTO); 140 if (psbuf_get_str(pb, &rootpath, NULL) == -1) 141 reterr((stderr, "invalid realpath response: rootpath"), EPROTO); 142 143 /* stat the rootdir so that we know it's a dir */ 144 psbuf_recycleout(pb); 145 psbuf_req_str(pb, SSH_FXP_LSTAT, NEXTREQ(pctx), rootpath); 146 DO_IO(psbuf_write, pu, pb, fd, &done, rv); 147 148 puffs_framebuf_recycle(pb); 149 DO_IO(psbuf_read, pu, pb, fd, &done, rv); 150 151 rv = psbuf_expect_attrs(pb, &va); 152 if (rv) 153 reterr((stderr, "couldn't stat rootpath"), rv); 154 puffs_framebuf_destroy(pb); 155 156 if (puffs_mode2vt(va.va_mode) != VDIR) 157 reterr((stderr, "remote path (%s) not a directory", rootpath), 158 ENOTDIR); 159 160 pn_root = puffs_getroot(pu); 161 rva = &pn_root->pn_va; 162 puffs_setvattr(rva, &va); 163 164 po_root = puffs_getrootpathobj(pu); 165 if (po_root == NULL) 166 err(1, "getrootpathobj"); 167 po_root->po_path = rootpath; 168 po_root->po_len = strlen(rootpath); 169 170 return 0; 171} 172 173int 174psshfs_fs_statvfs(struct puffs_usermount *pu, struct statvfs *sbp) 175{ 176 PSSHFSAUTOVAR(pu); 177 uint64_t tmpval; 178 uint8_t type; 179 180 memset(sbp, 0, sizeof(*sbp)); 181 sbp->f_bsize = sbp->f_frsize = sbp->f_iosize = 512; 182 183 if ((pctx->extensions & SFTP_EXT_STATVFS) == 0) 184 goto out; 185 186 psbuf_req_str(pb, SSH_FXP_EXTENDED, reqid, "statvfs@openssh.com"); 187 psbuf_put_str(pb, pctx->mountpath); 188 GETRESPONSE(pb, pctx->sshfd); 189 190 type = psbuf_get_type(pb); 191 if (type != SSH_FXP_EXTENDED_REPLY) { 192 /* use the default */ 193 goto out; 194 } 195 196 psbuf_get_8(pb, &tmpval); 197 sbp->f_bsize = tmpval; 198 psbuf_get_8(pb, &tmpval); 199 sbp->f_frsize = tmpval; 200 psbuf_get_8(pb, &sbp->f_blocks); 201 psbuf_get_8(pb, &sbp->f_bfree); 202 psbuf_get_8(pb, &sbp->f_bavail); 203 psbuf_get_8(pb, &sbp->f_files); 204 psbuf_get_8(pb, &sbp->f_ffree); 205 psbuf_get_8(pb, &sbp->f_favail); 206 207 psbuf_get_8(pb, &tmpval); /* fsid */ 208 psbuf_get_8(pb, &tmpval); /* flag */ 209 psbuf_get_8(pb, &tmpval); 210 sbp->f_namemax = tmpval; 211 212 sbp->f_bresvd = sbp->f_bfree - sbp->f_bavail; 213 sbp->f_fresvd = sbp->f_ffree - sbp->f_favail; 214 215 out: 216 PSSHFSRETURN(rv); 217} 218 219int 220psshfs_fs_unmount(struct puffs_usermount *pu, int flags) 221{ 222 struct psshfs_ctx *pctx = puffs_getspecific(pu); 223 224 kill(pctx->sshpid, SIGTERM); 225 close(pctx->sshfd); 226 if (pctx->numconnections == 2) { 227 kill(pctx->sshpid_data, SIGTERM); 228 close(pctx->sshfd_data); 229 } 230 231 return 0; 232} 233 234int 235psshfs_fs_nodetofh(struct puffs_usermount *pu, puffs_cookie_t cookie, 236 void *fid, size_t *fidsize) 237{ 238 struct psshfs_ctx *pctx = puffs_getspecific(pu); 239 struct puffs_node *pn = cookie; 240 struct psshfs_node *psn = pn->pn_data; 241 struct psshfs_fid *pf = fid; 242 243 pf->mounttime = pctx->mounttime; 244 pf->node = pn; 245 246 psn->stat |= PSN_HASFH; 247 248 return 0; 249} 250 251int 252psshfs_fs_fhtonode(struct puffs_usermount *pu, void *fid, size_t fidsize, 253 struct puffs_newinfo *pni) 254{ 255 struct psshfs_ctx *pctx = puffs_getspecific(pu); 256 struct psshfs_fid *pf = fid; 257 struct puffs_node *pn = pf->node; 258 struct psshfs_node *psn; 259 int rv; 260 261 if (pf->mounttime != pctx->mounttime) 262 return EINVAL; 263 if (pn == 0) 264 return EINVAL; 265 psn = pn->pn_data; 266 if ((psn->stat & PSN_HASFH) == 0) 267 return EINVAL; 268 269 /* update node attributes */ 270 rv = getnodeattr(pu, pn, NULL); 271 if (rv) 272 return EINVAL; 273 274 puffs_newinfo_setcookie(pni, pn); 275 puffs_newinfo_setvtype(pni, pn->pn_va.va_type); 276 puffs_newinfo_setsize(pni, pn->pn_va.va_size); 277 278 return 0; 279} 280