Home | History | Annotate | Line # | Download | only in mount_psshfs
node.c revision 1.4
      1 /*	$NetBSD: node.c,v 1.4 2007/01/11 18:50:42 pooka Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2006  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  * 3. The name of the company nor the name of the author may be used to
     15  *    endorse or promote products derived from this software without specific
     16  *    prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     19  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     21  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     28  * SUCH DAMAGE.
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 #ifndef lint
     33 __RCSID("$NetBSD: node.c,v 1.4 2007/01/11 18:50:42 pooka Exp $");
     34 #endif /* !lint */
     35 
     36 #include <assert.h>
     37 #include <errno.h>
     38 #include <stdio.h>
     39 #include <stdlib.h>
     40 
     41 #include "psshfs.h"
     42 #include "sftp_proto.h"
     43 
     44 int
     45 psshfs_node_lookup(struct puffs_cc *pcc, void *opc, void **newnode,
     46 	enum vtype *newtype, voff_t *newsize, dev_t *newrdev,
     47 	const struct puffs_cn *pcn)
     48 {
     49         struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
     50 	struct psshfs_ctx *pctx = pu->pu_privdata;
     51 	struct puffs_node *pn_dir = opc;
     52 	struct psshfs_node *psn_dir = pn_dir->pn_data;
     53 	struct puffs_node *pn;
     54 	struct psshfs_dir *pd;
     55 	int rv;
     56 
     57 	if (pcn->pcn_flags & PUFFS_ISDOTDOT) {
     58 		*newnode = psn_dir->parent;
     59 		*newtype = VDIR;
     60 		return 0;
     61 	}
     62 
     63 	rv = sftp_readdir(pcc, pctx, pn_dir);
     64 	if (rv) {
     65 		assert(rv != ENOENT);
     66 		return rv;
     67 	}
     68 
     69 	pd = lookup(psn_dir->dir, psn_dir->dentnext, pcn->pcn_name);
     70 	if (!pd)
     71 		return ENOENT;
     72 
     73 	if (pd->entry)
     74 		pn = pd->entry;
     75 	else
     76 		pn = makenode(pu, pn_dir, pd, &pd->va);
     77 
     78 	*newnode = pn;
     79 	*newsize = pn->pn_va.va_size;
     80 	*newtype = pn->pn_va.va_type;
     81 
     82 	return 0;
     83 }
     84 
     85 int
     86 psshfs_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *vap,
     87 	const struct puffs_cred *pcr, pid_t pid)
     88 {
     89 	PSSHFSAUTOVAR(pcc);
     90 	struct puffs_node *pn = opc;
     91 	struct psshfs_node *psn = pn->pn_data;
     92 	struct vattr va;
     93 
     94 	rv = 0;
     95 
     96 	/* XXX: expire by time */
     97 	if (!psn->hasvattr) {
     98 		psbuf_req_str(pb, SSH_FXP_LSTAT, reqid, pn->pn_path);
     99 		pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    100 		puffs_cc_yield(pcc);
    101 
    102 		rv = psbuf_expect_attrs(pb, &va);
    103 		if (rv)
    104 			goto out;
    105 
    106 		puffs_setvattr(&pn->pn_va, &va);
    107 	}
    108 
    109 	memcpy(vap, &pn->pn_va, sizeof(struct vattr));
    110 	psn->hasvattr = 1;
    111 
    112  out:
    113 	PSSHFSRETURN(rv);
    114 }
    115 
    116 int
    117 psshfs_node_setattr(struct puffs_cc *pcc, void *opc,
    118 	const struct vattr *va, const struct puffs_cred *pcr, pid_t pid)
    119 {
    120 	PSSHFSAUTOVAR(pcc);
    121 	struct vattr kludgeva;
    122 	struct puffs_node *pn = opc;
    123 
    124 	psbuf_req_str(pb, SSH_FXP_SETSTAT, reqid, pn->pn_path);
    125 
    126 	memcpy(&kludgeva, va, sizeof(struct vattr));
    127 
    128 	/* XXX: kludge due to openssh server implementation */
    129 	if (va->va_atime.tv_sec != PUFFS_VNOVAL
    130 	    && va->va_mtime.tv_sec == PUFFS_VNOVAL) {
    131 		if (pn->pn_va.va_mtime.tv_sec != PUFFS_VNOVAL)
    132 			kludgeva.va_mtime.tv_sec = pn->pn_va.va_mtime.tv_sec;
    133 		else
    134 			kludgeva.va_mtime.tv_sec = va->va_atime.tv_sec;
    135 	}
    136 	if (va->va_mtime.tv_sec != PUFFS_VNOVAL
    137 	    && va->va_atime.tv_sec == PUFFS_VNOVAL) {
    138 		if (pn->pn_va.va_atime.tv_sec != PUFFS_VNOVAL)
    139 			kludgeva.va_atime.tv_sec = pn->pn_va.va_atime.tv_sec;
    140 		else
    141 			kludgeva.va_atime.tv_sec = va->va_mtime.tv_sec;
    142 	}
    143 
    144 	psbuf_put_vattr(pb, &kludgeva);
    145 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    146 
    147 	puffs_cc_yield(pcc);
    148 
    149 	rv = psbuf_expect_status(pb);
    150 	if (rv == 0)
    151 		puffs_setvattr(&pn->pn_va, &kludgeva);
    152 
    153 	PSSHFSRETURN(rv);
    154 }
    155 
    156 int
    157 psshfs_node_create(struct puffs_cc *pcc, void *opc, void **newnode,
    158 	const struct puffs_cn *pcn, const struct vattr *va)
    159 {
    160 	PSSHFSAUTOVAR(pcc);
    161 	struct puffs_node *pn = opc;
    162 	struct puffs_node *pn_new;
    163 	char *fhand = NULL;
    164 	size_t fhandlen;
    165 
    166 	pn_new = allocnode(pu, pn, pcn->pcn_name, va);
    167 	if (!pn) {
    168 		rv = ENOMEM;
    169 		goto out;
    170 	}
    171 
    172 	psbuf_req_data(pb, SSH_FXP_OPEN, reqid, pcn->pcn_fullpath,
    173 	    strlen(pcn->pcn_fullpath));
    174 	psbuf_put_4(pb, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC);
    175 	psbuf_put_vattr(pb, va);
    176 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    177 
    178 	puffs_cc_yield(pcc);
    179 
    180 	rv = psbuf_expect_handle(pb, &fhand, &fhandlen);
    181 	if (rv)
    182 		goto out;
    183 
    184 	reqid = NEXTREQ(pctx);
    185 	psbuf_recycle(pb, PSB_OUT);
    186 	psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen);
    187 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    188 
    189 	puffs_cc_yield(pcc);
    190 	rv = psbuf_expect_status(pb);
    191 
    192 	if (rv == 0)
    193 		*newnode = pn_new;
    194 	else
    195 		nukenode(pn_new, pcn->pcn_name, 1);
    196 
    197  out:
    198 	free(fhand);
    199 	PSSHFSRETURN(rv);
    200 }
    201 
    202 int
    203 psshfs_node_readdir(struct puffs_cc *pcc, void *opc, struct dirent *dent,
    204 	const struct puffs_cred *pcr, off_t *readoff, size_t *reslen)
    205 {
    206 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
    207 	struct psshfs_ctx *pctx = pu->pu_privdata;
    208 	struct puffs_node *pn = opc;
    209 	struct psshfs_node *psn = pn->pn_data;
    210 	struct psshfs_dir *pd;
    211 	int i, rv;
    212 
    213 	rv = sftp_readdir(pcc, pctx, pn);
    214 	if (rv)
    215 		return rv;
    216 
    217 	for (i = *readoff; i < psn->dentnext; i++) {
    218 		pd = &psn->dir[i];
    219 		if (pd->valid == 0)
    220 			continue;
    221 		if (!puffs_nextdent(&dent, pd->entryname,
    222 		    pd->va.va_fileid, puffs_vtype2dt(pd->va.va_type), reslen))
    223 			break;
    224 	}
    225 
    226 	*readoff = i;
    227 	return 0;
    228 }
    229 
    230 int
    231 psshfs_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
    232 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
    233 	int ioflag)
    234 {
    235 	PSSHFSAUTOVAR(pcc);
    236 	struct puffs_node *pn = opc;
    237 	char *fhand = NULL;
    238 	size_t fhandlen;
    239 	struct vattr va;
    240 	uint32_t readlen;
    241 
    242 	if (pn->pn_va.va_type == VDIR) {
    243 		rv = EISDIR;
    244 		goto out;
    245 	}
    246 
    247 	puffs_vattr_null(&va);
    248 	psbuf_req_str(pb, SSH_FXP_OPEN, reqid, pn->pn_path);
    249 	psbuf_put_4(pb, SSH_FXF_READ);
    250 	psbuf_put_vattr(pb, &va);
    251 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    252 
    253 	puffs_cc_yield(pcc);
    254 
    255 	rv = psbuf_expect_handle(pb, &fhand, &fhandlen);
    256 	if (rv)
    257 		goto out;
    258 
    259 	readlen = *resid;
    260 	reqid = NEXTREQ(pctx);
    261 	psbuf_recycle(pb, PSB_OUT);
    262 	psbuf_req_data(pb, SSH_FXP_READ, reqid, fhand, fhandlen);
    263 	psbuf_put_8(pb, offset);
    264 	psbuf_put_4(pb, readlen);
    265 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    266 
    267 	puffs_cc_yield(pcc);
    268 
    269 	rv = psbuf_do_data(pb, buf, &readlen);
    270 	if (rv == 0)
    271 		*resid -= readlen;
    272 
    273 	reqid = NEXTREQ(pctx);
    274 	psbuf_recycle(pb, PSB_OUT);
    275 	psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen);
    276 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    277 
    278 	puffs_cc_yield(pcc);
    279 
    280 	/* don't care */
    281 
    282  out:
    283 	free(fhand);
    284 	PSSHFSRETURN(rv);
    285 }
    286 
    287 /* XXX: we should getattr for size */
    288 int
    289 psshfs_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
    290 	off_t offset, size_t *resid, const struct puffs_cred *cred,
    291 	int ioflag)
    292 {
    293 	PSSHFSAUTOVAR(pcc);
    294 	struct puffs_node *pn = opc;
    295 	char *fhand = NULL;
    296 	size_t fhandlen;
    297 	struct vattr va, kludgeva1, kludgeva2;
    298 	uint32_t writelen, oflags;
    299 
    300 	if (pn->pn_va.va_type == VDIR) {
    301 		rv = EISDIR;
    302 		goto out;
    303 	}
    304 
    305 	/*
    306 	 * XXXX: this is wrong - we shouldn't muck the file permissions
    307 	 * at this stage any more.  However, we need this, since currently
    308 	 * we can't tell the sftp server "hey, this data was already
    309 	 * authenticated to UBC, it's ok to let us write this".  Yes, it
    310 	 * will fail e.g. if we don't own the file.  Tough love.
    311 	 *
    312 	 * TODO-point: Investigate solving this with open filehandles
    313 	 * or something like that.
    314 	 */
    315 	kludgeva1 = pn->pn_va;
    316 
    317 	puffs_vattr_null(&kludgeva2);
    318 	kludgeva2.va_mode = 0700;
    319 	rv = psshfs_node_setattr(pcc, opc, &kludgeva2, cred, 0);
    320 	if (rv)
    321 		goto out;
    322 
    323 	/* XXXcontinuation: ok, file is mode 700 now, we can open it rw */
    324 
    325 	oflags = SSH_FXF_WRITE;
    326 #if 0
    327 	/*
    328 	 * At least OpenSSH doesn't appear to support this, so can't
    329 	 * do it the right way.
    330 	 */
    331 	if (ioflag & PUFFS_IO_APPEND)
    332 		oflags |= SSH_FXF_APPEND;
    333 #endif
    334 	if (ioflag & PUFFS_IO_APPEND)
    335 		offset = pn->pn_va.va_size;
    336 
    337 	puffs_vattr_null(&va);
    338 	psbuf_req_str(pb, SSH_FXP_OPEN, reqid, pn->pn_path);
    339 	psbuf_put_4(pb, oflags);
    340 	psbuf_put_vattr(pb, &va);
    341 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    342 
    343 	puffs_cc_yield(pcc);
    344 
    345 	rv = psbuf_expect_handle(pb, &fhand, &fhandlen);
    346 	if (rv)
    347 		goto out;
    348 
    349 	/* moreXXX: file is open, revert old creds for crying out loud! */
    350 	rv = psshfs_node_setattr(pcc, opc, &kludgeva1, cred, 0);
    351 
    352 	/* are we screwed a la royal jelly? */
    353 	if (rv)
    354 		goto closefile;
    355 
    356 	writelen = *resid;
    357 	reqid = NEXTREQ(pctx);
    358 	psbuf_recycle(pb, PSB_OUT);
    359 	psbuf_req_data(pb, SSH_FXP_WRITE, reqid, fhand, fhandlen);
    360 	psbuf_put_8(pb, offset);
    361 	psbuf_put_data(pb, buf, writelen);
    362 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    363 
    364 	puffs_cc_yield(pcc);
    365 
    366 	rv = psbuf_expect_status(pb);
    367 	if (rv == 0)
    368 		*resid = 0;
    369 
    370 	if (pn->pn_va.va_size < offset + writelen)
    371 		pn->pn_va.va_size = offset + writelen;
    372 
    373  closefile:
    374 	reqid = NEXTREQ(pctx);
    375 	psbuf_recycle(pb, PSB_OUT);
    376 	psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen);
    377 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    378 
    379 	puffs_cc_yield(pcc);
    380 
    381 	/* dontcare */
    382  out:
    383 	free(fhand);
    384 	PSSHFSRETURN(rv);
    385 }
    386 
    387 int
    388 psshfs_node_readlink(struct puffs_cc *pcc, void *opc,
    389 	const struct puffs_cred *cred, char *linkvalue, size_t *linklen)
    390 {
    391 	PSSHFSAUTOVAR(pcc);
    392 	struct puffs_node *pn = opc;
    393 	char *linktmp = NULL;
    394 	uint32_t count;
    395 
    396 	if (pctx->protover < 3) {
    397 		rv = EOPNOTSUPP;
    398 		goto out;
    399 	}
    400 
    401 	psbuf_req_str(pb, SSH_FXP_READLINK, reqid, pn->pn_path);
    402 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    403 
    404 	puffs_cc_yield(pcc);
    405 
    406 	rv = psbuf_expect_name(pb, &count);
    407 	if (count != 1) {
    408 		rv = EPROTO;
    409 		goto out;
    410 	}
    411 	rv = psbuf_get_str(pb, &linktmp, linklen);
    412 	if (rv)
    413 		rv = 0;
    414 	else {
    415 		rv = EPROTO;
    416 		goto out;
    417 	}
    418 	(void) memcpy(linkvalue, linktmp, *linklen);
    419 
    420  out:
    421 	free(linktmp);
    422 	PSSHFSRETURN(rv);
    423 }
    424 
    425 int
    426 psshfs_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
    427 	const struct puffs_cn *pcn)
    428 {
    429 	PSSHFSAUTOVAR(pcc);
    430 	struct puffs_node *pn_targ = targ;
    431 
    432 	if (pn_targ->pn_va.va_type == VDIR) {
    433 		rv = EPERM;
    434 		goto out;
    435 	}
    436 
    437 	psbuf_req_str(pb, SSH_FXP_REMOVE, reqid, pn_targ->pn_path);
    438 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    439 
    440 	puffs_cc_yield(pcc);
    441 
    442 	rv = psbuf_expect_status(pb);
    443 
    444 	if (rv == 0)
    445 		nukenode(pn_targ, pcn->pcn_name, 1);
    446 
    447  out:
    448 	PSSHFSRETURN(rv);
    449 }
    450 
    451 int
    452 psshfs_node_mkdir(struct puffs_cc *pcc, void *opc, void **newnode,
    453 	const struct puffs_cn *pcn, const struct vattr *va)
    454 {
    455 	PSSHFSAUTOVAR(pcc);
    456 	struct puffs_node *pn = opc;
    457 	struct puffs_node *pn_new;
    458 
    459 	pn_new = allocnode(pu, pn, pcn->pcn_name, va);
    460 	if (!pn_new) {
    461 		rv = ENOMEM;
    462 		goto out;
    463 	}
    464 
    465 	psbuf_req_str(pb, SSH_FXP_MKDIR, reqid, pcn->pcn_fullpath);
    466 	psbuf_put_vattr(pb, va);
    467 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    468 
    469 	puffs_cc_yield(pcc);
    470 
    471 	rv = psbuf_expect_status(pb);
    472 
    473 	if (rv == 0)
    474 		*newnode = pn_new;
    475 	else
    476 		nukenode(pn_new, pcn->pcn_name, 1);
    477 
    478  out:
    479 	PSSHFSRETURN(rv);
    480 }
    481 
    482 int
    483 psshfs_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
    484 	const struct puffs_cn *pcn)
    485 {
    486 	PSSHFSAUTOVAR(pcc);
    487 	struct puffs_node *pn_targ = targ;
    488 
    489 	psbuf_req_str(pb, SSH_FXP_RMDIR, reqid, pn_targ->pn_path);
    490 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    491 
    492 	puffs_cc_yield(pcc);
    493 
    494 	rv = psbuf_expect_status(pb);
    495 	if (rv == 0)
    496 		nukenode(pn_targ, pcn->pcn_name, 1);
    497 
    498 	PSSHFSRETURN(rv);
    499 }
    500 
    501 int
    502 psshfs_node_symlink(struct puffs_cc *pcc, void *opc, void **newnode,
    503 	const struct puffs_cn *pcn, const struct vattr *va,
    504 	const char *link_target)
    505 {
    506 	PSSHFSAUTOVAR(pcc);
    507 	struct puffs_node *pn = opc;
    508 	struct puffs_node *pn_new;
    509 
    510 	if (pctx->protover < 3) {
    511 		rv = EOPNOTSUPP;
    512 		goto out;
    513 	}
    514 
    515 	pn_new = allocnode(pu, pn, pcn->pcn_name, va);
    516 	if (!pn) {
    517 		rv = ENOMEM;
    518 		goto out;
    519 	}
    520 
    521 	/*
    522 	 * XXX: ietf says: source, target.  openssh says: ietf who?
    523 	 * Let's go with openssh and build quirk tables later if we care
    524 	 */
    525 	psbuf_req_str(pb, SSH_FXP_SYMLINK, reqid, link_target);
    526 	psbuf_put_str(pb, pcn->pcn_fullpath);
    527 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    528 
    529 	puffs_cc_yield(pcc);
    530 
    531 	rv = psbuf_expect_status(pb);
    532 	if (rv == 0)
    533 		*newnode = pn_new;
    534 	else
    535 		nukenode(pn_new, pcn->pcn_name, 1);
    536 
    537  out:
    538 	PSSHFSRETURN(rv);
    539 }
    540 
    541 int
    542 psshfs_node_rename(struct puffs_cc *pcc, void *opc, void *src,
    543 	const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
    544 	const struct puffs_cn *pcn_targ)
    545 {
    546 	PSSHFSAUTOVAR(pcc);
    547 	struct puffs_node *pn_sf = src;
    548 	struct puffs_node *pn_td = targ_dir, *pn_tf = targ;
    549 	struct psshfs_node *psn_targdir = pn_td->pn_data;
    550 
    551 	if (pctx->protover < 2) {
    552 		rv = EOPNOTSUPP;
    553 		goto out;
    554 	}
    555 
    556 	if (pn_tf) {
    557 		/* XXX: no backend implementation for now, so call directly */
    558 		rv = psshfs_node_remove(pcc, targ_dir, pn_tf, pcn_targ);
    559 		if (rv)
    560 			goto out;
    561 	}
    562 
    563 	psbuf_req_str(pb, SSH_FXP_RENAME, reqid, pcn_src->pcn_fullpath);
    564 	psbuf_put_str(pb, pcn_targ->pcn_fullpath);
    565 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    566 
    567 	puffs_cc_yield(pcc);
    568 
    569 	rv = psbuf_expect_status(pb);
    570 	if (rv == 0) {
    571 		struct psshfs_dir *pd;
    572 
    573 		/*
    574 		 * XXX: interfaces didn't quite work with rename..
    575 		 * the song remains the same.  go figure .. ;)
    576 		 */
    577 		nukenode(pn_sf, pcn_src->pcn_name, 0);
    578 		pd = direnter(pn_td, pcn_targ->pcn_name);
    579 		pd->entry = pn_sf;
    580 		puffs_setvattr(&pd->va, &pn_sf->pn_va);
    581 
    582 		if (opc != targ_dir) {
    583 			psn_targdir->childcount++;
    584 			if (pn_sf->pn_va.va_type == VDIR)
    585 				pn_td->pn_va.va_nlink++;
    586 		}
    587 	}
    588 
    589  out:
    590 	PSSHFSRETURN(rv);
    591 }
    592