Home | History | Annotate | Line # | Download | only in mount_psshfs
node.c revision 1.1
      1 /*	$NetBSD: node.c,v 1.1 2006/12/29 15:35:39 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.1 2006/12/29 15:35:39 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 puffs_node *pn = opc;
    122 	struct psshfs_node *psn = pn->pn_data;
    123 
    124 	psbuf_req_str(pb, SSH_FXP_SETSTAT, reqid, pn->pn_path);
    125 	psbuf_put_vattr(pb, va);
    126 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    127 
    128 	puffs_cc_yield(pcc);
    129 
    130 	rv = psbuf_expect_status(pb);
    131 	if (rv == 0)
    132 		psn->hasvattr = 0;
    133 
    134 	PSSHFSRETURN(rv);
    135 }
    136 
    137 int
    138 psshfs_node_create(struct puffs_cc *pcc, void *opc, void **newnode,
    139 	const struct puffs_cn *pcn, const struct vattr *va)
    140 {
    141 	PSSHFSAUTOVAR(pcc);
    142 	struct puffs_node *pn = opc;
    143 	struct puffs_node *pn_new;
    144 	char *fhand = NULL;
    145 	size_t fhandlen;
    146 
    147 	pn_new = allocnode(pu, pn, pcn->pcn_name, va);
    148 	if (!pn) {
    149 		rv = ENOMEM;
    150 		goto out;
    151 	}
    152 
    153 	psbuf_req_data(pb, SSH_FXP_OPEN, reqid, pcn->pcn_fullpath,
    154 	    strlen(pcn->pcn_fullpath));
    155 	psbuf_put_4(pb, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC);
    156 	psbuf_put_vattr(pb, va);
    157 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    158 
    159 	puffs_cc_yield(pcc);
    160 
    161 	rv = psbuf_expect_handle(pb, &fhand, &fhandlen);
    162 	if (rv)
    163 		goto out;
    164 
    165 	reqid = NEXTREQ(pctx);
    166 	psbuf_recycle(pb, PSB_OUT);
    167 	psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen);
    168 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    169 
    170 	puffs_cc_yield(pcc);
    171 	rv = psbuf_expect_status(pb);
    172 
    173 	if (rv == 0)
    174 		*newnode = pn_new;
    175 	else
    176 		nukenode(pn_new, pcn->pcn_name);
    177 
    178  out:
    179 	free(fhand);
    180 	PSSHFSRETURN(rv);
    181 }
    182 
    183 int
    184 psshfs_node_readdir(struct puffs_cc *pcc, void *opc, struct dirent *dent,
    185 	const struct puffs_cred *pcr, off_t *readoff, size_t *reslen)
    186 {
    187 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
    188 	struct psshfs_ctx *pctx = pu->pu_privdata;
    189 	struct puffs_node *pn = opc;
    190 	struct psshfs_node *psn = pn->pn_data;
    191 	struct psshfs_dir *pd;
    192 	int i, rv;
    193 
    194 	rv = sftp_readdir(pcc, pctx, pn);
    195 	if (rv)
    196 		return rv;
    197 
    198 	for (i = *readoff; i < psn->dentnext; i++) {
    199 		pd = &psn->dir[i];
    200 		if (pd->valid == 0)
    201 			continue;
    202 		if (!puffs_nextdent(&dent, pd->entryname,
    203 		    pd->va.va_fileid, puffs_vtype2dt(pd->va.va_type), reslen))
    204 			break;
    205 	}
    206 
    207 	*readoff = i;
    208 	return 0;
    209 }
    210 
    211 int
    212 psshfs_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
    213 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
    214 	int ioflag)
    215 {
    216 	PSSHFSAUTOVAR(pcc);
    217 	struct puffs_node *pn = opc;
    218 	char *fhand = NULL;
    219 	size_t fhandlen;
    220 	struct vattr va;
    221 	uint32_t readlen;
    222 
    223 	if (pn->pn_va.va_type == VDIR) {
    224 		rv = EISDIR;
    225 		goto out;
    226 	}
    227 
    228 	puffs_vattr_null(&va);
    229 	psbuf_req_str(pb, SSH_FXP_OPEN, reqid, pn->pn_path);
    230 	psbuf_put_4(pb, SSH_FXF_READ);
    231 	psbuf_put_vattr(pb, &va);
    232 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    233 
    234 	puffs_cc_yield(pcc);
    235 
    236 	rv = psbuf_expect_handle(pb, &fhand, &fhandlen);
    237 	if (rv)
    238 		goto out;
    239 
    240 	readlen = *resid;
    241 	reqid = NEXTREQ(pctx);
    242 	psbuf_recycle(pb, PSB_OUT);
    243 	psbuf_req_data(pb, SSH_FXP_READ, reqid, fhand, fhandlen);
    244 	psbuf_put_8(pb, offset);
    245 	psbuf_put_4(pb, readlen);
    246 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    247 
    248 	puffs_cc_yield(pcc);
    249 
    250 	rv = psbuf_do_data(pb, buf, &readlen);
    251 	if (rv == 0)
    252 		*resid -= readlen;
    253 
    254 	reqid = NEXTREQ(pctx);
    255 	psbuf_recycle(pb, PSB_OUT);
    256 	psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen);
    257 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    258 
    259 	puffs_cc_yield(pcc);
    260 
    261 	/* don't care */
    262 
    263  out:
    264 	free(fhand);
    265 	PSSHFSRETURN(rv);
    266 }
    267 
    268 /* XXX: we should getattr for size */
    269 int
    270 psshfs_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
    271 	off_t offset, size_t *resid, const struct puffs_cred *cred,
    272 	int ioflag)
    273 {
    274 	PSSHFSAUTOVAR(pcc);
    275 	struct puffs_node *pn = opc;
    276 	char *fhand = NULL;
    277 	size_t fhandlen;
    278 	struct vattr va;
    279 	uint32_t writelen, oflags;
    280 
    281 	if (pn->pn_va.va_type == VDIR) {
    282 		rv = EISDIR;
    283 		goto out;
    284 	}
    285 
    286 	oflags = SSH_FXF_WRITE;
    287 #if 0
    288 	/*
    289 	 * At least OpenSSH doesn't appear to support this, so can't
    290 	 * do it the right way.
    291 	 */
    292 	if (ioflag & PUFFS_IO_APPEND)
    293 		oflags |= SSH_FXF_APPEND;
    294 #endif
    295 	if (ioflag & PUFFS_IO_APPEND)
    296 		offset = pn->pn_va.va_size;
    297 
    298 	puffs_vattr_null(&va);
    299 	va.va_mode = 0666;
    300 
    301 	psbuf_req_str(pb, SSH_FXP_OPEN, reqid, pn->pn_path);
    302 	psbuf_put_4(pb, oflags);
    303 	psbuf_put_vattr(pb, &va);
    304 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    305 
    306 	puffs_cc_yield(pcc);
    307 
    308 	rv = psbuf_expect_handle(pb, &fhand, &fhandlen);
    309 	if (rv)
    310 		goto out;
    311 
    312 	writelen = *resid;
    313 	reqid = NEXTREQ(pctx);
    314 	psbuf_recycle(pb, PSB_OUT);
    315 	psbuf_req_data(pb, SSH_FXP_WRITE, reqid, fhand, fhandlen);
    316 	psbuf_put_8(pb, offset);
    317 	psbuf_put_data(pb, buf, writelen);
    318 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    319 
    320 	puffs_cc_yield(pcc);
    321 
    322 	rv = psbuf_expect_status(pb);
    323 	if (rv == 0)
    324 		*resid = 0;
    325 
    326 	if (pn->pn_va.va_size < offset + writelen)
    327 		pn->pn_va.va_size = offset + writelen;
    328 
    329 	reqid = NEXTREQ(pctx);
    330 	psbuf_recycle(pb, PSB_OUT);
    331 	psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen);
    332 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    333 
    334 	puffs_cc_yield(pcc);
    335 
    336 	/* dontcare */
    337  out:
    338 	free(fhand);
    339 	PSSHFSRETURN(rv);
    340 }
    341 
    342 int
    343 psshfs_node_readlink(struct puffs_cc *pcc, void *opc,
    344 	const struct puffs_cred *cred, char *linkvalue, size_t *linklen)
    345 {
    346 	PSSHFSAUTOVAR(pcc);
    347 	struct puffs_node *pn = opc;
    348 	char *linktmp = NULL;
    349 	uint32_t count;
    350 
    351 	if (pctx->protover < 3) {
    352 		rv = EOPNOTSUPP;
    353 		goto out;
    354 	}
    355 
    356 	psbuf_req_str(pb, SSH_FXP_READLINK, reqid, pn->pn_path);
    357 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    358 
    359 	puffs_cc_yield(pcc);
    360 
    361 	rv = psbuf_expect_name(pb, &count);
    362 	if (count != 1) {
    363 		rv = EPROTO;
    364 		goto out;
    365 	}
    366 	rv = psbuf_get_str(pb, &linktmp, linklen);
    367 	if (rv)
    368 		rv = 0;
    369 	else {
    370 		rv = EPROTO;
    371 		goto out;
    372 	}
    373 	(void) memcpy(linkvalue, linktmp, *linklen);
    374 
    375  out:
    376 	free(linktmp);
    377 	PSSHFSRETURN(rv);
    378 }
    379 
    380 int
    381 psshfs_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
    382 	const struct puffs_cn *pcn)
    383 {
    384 	PSSHFSAUTOVAR(pcc);
    385 	struct puffs_node *pn_targ = targ;
    386 
    387 	if (pn_targ->pn_va.va_type == VDIR) {
    388 		rv = EISDIR;
    389 		goto out;
    390 	}
    391 
    392 	psbuf_req_str(pb, SSH_FXP_REMOVE, reqid, pn_targ->pn_path);
    393 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    394 
    395 	puffs_cc_yield(pcc);
    396 
    397 	rv = psbuf_expect_status(pb);
    398 
    399 	if (rv == 0)
    400 		nukenode(pn_targ, pcn->pcn_name);
    401 
    402  out:
    403 	PSSHFSRETURN(rv);
    404 }
    405 
    406 int
    407 psshfs_node_mkdir(struct puffs_cc *pcc, void *opc, void **newnode,
    408 	const struct puffs_cn *pcn, const struct vattr *va)
    409 {
    410 	PSSHFSAUTOVAR(pcc);
    411 	struct puffs_node *pn = opc;
    412 	struct puffs_node *pn_new;
    413 
    414 	pn_new = allocnode(pu, pn, pcn->pcn_name, va);
    415 	if (!pn_new) {
    416 		rv = ENOMEM;
    417 		goto out;
    418 	}
    419 
    420 	psbuf_req_str(pb, SSH_FXP_MKDIR, reqid, pcn->pcn_fullpath);
    421 	psbuf_put_vattr(pb, va);
    422 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    423 
    424 	puffs_cc_yield(pcc);
    425 
    426 	rv = psbuf_expect_status(pb);
    427 
    428 	if (rv == 0)
    429 		*newnode = pn_new;
    430 	else
    431 		nukenode(pn_new, pcn->pcn_name);
    432 
    433  out:
    434 	PSSHFSRETURN(rv);
    435 }
    436 
    437 int
    438 psshfs_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
    439 	const struct puffs_cn *pcn)
    440 {
    441 	PSSHFSAUTOVAR(pcc);
    442 	struct puffs_node *pn_targ = targ;
    443 
    444 	psbuf_req_str(pb, SSH_FXP_RMDIR, reqid, pn_targ->pn_path);
    445 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    446 
    447 	puffs_cc_yield(pcc);
    448 
    449 	rv = psbuf_expect_status(pb);
    450 	if (rv == 0)
    451 		nukenode(pn_targ, pcn->pcn_name);
    452 
    453 	PSSHFSRETURN(rv);
    454 }
    455 
    456 int
    457 psshfs_node_symlink(struct puffs_cc *pcc, void *opc, void **newnode,
    458 	const struct puffs_cn *pcn, const struct vattr *va,
    459 	const char *link_target)
    460 {
    461 	PSSHFSAUTOVAR(pcc);
    462 	struct puffs_node *pn = opc;
    463 	struct puffs_node *pn_new;
    464 
    465 	if (pctx->protover < 3) {
    466 		rv = EOPNOTSUPP;
    467 		goto out;
    468 	}
    469 
    470 	pn_new = allocnode(pu, pn, pcn->pcn_name, va);
    471 	if (!pn) {
    472 		rv = ENOMEM;
    473 		goto out;
    474 	}
    475 
    476 	/*
    477 	 * XXX: ietf says: source, target.  openssh says: ietf who?
    478 	 * Let's go with openssh and build quirk tables later if we care
    479 	 */
    480 	psbuf_req_str(pb, SSH_FXP_SYMLINK, reqid, link_target);
    481 	psbuf_put_str(pb, pcn->pcn_fullpath);
    482 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    483 
    484 	puffs_cc_yield(pcc);
    485 
    486 	rv = psbuf_expect_status(pb);
    487 	if (rv == 0)
    488 		*newnode = pn_new;
    489 	else
    490 		nukenode(pn_new, pcn->pcn_name);
    491 
    492  out:
    493 	PSSHFSRETURN(rv);
    494 }
    495 
    496 int
    497 psshfs_node_rename(struct puffs_cc *pcc, void *opc, void *src,
    498 	const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
    499 	const struct puffs_cn *pcn_targ)
    500 {
    501 	PSSHFSAUTOVAR(pcc);
    502 	struct puffs_node *pn_sf = src;
    503 	struct puffs_node *pn_td = targ_dir, *pn_tf = targ;
    504 	struct psshfs_node *psn_targdir = pn_td->pn_data;
    505 
    506 	if (pctx->protover < 2) {
    507 		rv = EOPNOTSUPP;
    508 		goto out;
    509 	}
    510 
    511 	if (pn_tf) {
    512 		/* XXX: no backend implementation for now, so call directly */
    513 		rv = psshfs_node_remove(pcc, targ_dir, pn_tf, pcn_targ);
    514 		if (rv)
    515 			goto out;
    516 	}
    517 
    518 	psbuf_req_str(pb, SSH_FXP_RENAME, reqid, pcn_src->pcn_fullpath);
    519 	psbuf_put_str(pb, pcn_targ->pcn_fullpath);
    520 	pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
    521 
    522 	puffs_cc_yield(pcc);
    523 
    524 	rv = psbuf_expect_status(pb);
    525 	if (rv == 0) {
    526 		struct psshfs_dir *pd;
    527 
    528 		/*
    529 		 * XXX: interfaces didn't quite work with rename..
    530 		 * the song remains the same.  go figure .. ;)
    531 		 */
    532 		nukenode(pn_sf, pcn_src->pcn_name);
    533 		pd = direnter(pn_td, pcn_targ->pcn_name);
    534 		pd->entry = pn_sf;
    535 		puffs_setvattr(&pd->va, &pn_sf->pn_va);
    536 
    537 		if (opc != targ_dir) {
    538 			psn_targdir->childcount++;
    539 			if (pn_sf->pn_va.va_type == VDIR)
    540 				pn_td->pn_va.va_nlink++;
    541 		}
    542 	}
    543 
    544  out:
    545 	PSSHFSRETURN(rv);
    546 }
    547