node.c revision 1.5 1 /* $NetBSD: node.c,v 1.5 2007/01/15 00:42:21 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.5 2007/01/15 00:42:21 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, PNPATH(pn));
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, PNPATH(pn));
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_str(pb, SSH_FXP_OPEN, reqid, PCNPATH(pcn));
173 psbuf_put_4(pb, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC);
174 psbuf_put_vattr(pb, va);
175 pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
176
177 puffs_cc_yield(pcc);
178
179 rv = psbuf_expect_handle(pb, &fhand, &fhandlen);
180 if (rv)
181 goto out;
182
183 reqid = NEXTREQ(pctx);
184 psbuf_recycle(pb, PSB_OUT);
185 psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen);
186 pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
187
188 puffs_cc_yield(pcc);
189 rv = psbuf_expect_status(pb);
190
191 if (rv == 0)
192 *newnode = pn_new;
193 else
194 nukenode(pn_new, pcn->pcn_name, 1);
195
196 out:
197 free(fhand);
198 PSSHFSRETURN(rv);
199 }
200
201 int
202 psshfs_node_readdir(struct puffs_cc *pcc, void *opc, struct dirent *dent,
203 const struct puffs_cred *pcr, off_t *readoff, size_t *reslen)
204 {
205 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
206 struct psshfs_ctx *pctx = pu->pu_privdata;
207 struct puffs_node *pn = opc;
208 struct psshfs_node *psn = pn->pn_data;
209 struct psshfs_dir *pd;
210 int i, rv;
211
212 rv = sftp_readdir(pcc, pctx, pn);
213 if (rv)
214 return rv;
215
216 for (i = *readoff; i < psn->dentnext; i++) {
217 pd = &psn->dir[i];
218 if (pd->valid == 0)
219 continue;
220 if (!puffs_nextdent(&dent, pd->entryname,
221 pd->va.va_fileid, puffs_vtype2dt(pd->va.va_type), reslen))
222 break;
223 }
224
225 *readoff = i;
226 return 0;
227 }
228
229 int
230 psshfs_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
231 off_t offset, size_t *resid, const struct puffs_cred *pcr,
232 int ioflag)
233 {
234 PSSHFSAUTOVAR(pcc);
235 struct puffs_node *pn = opc;
236 char *fhand = NULL;
237 size_t fhandlen;
238 struct vattr va;
239 uint32_t readlen;
240
241 if (pn->pn_va.va_type == VDIR) {
242 rv = EISDIR;
243 goto out;
244 }
245
246 puffs_vattr_null(&va);
247 psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PNPATH(pn));
248 psbuf_put_4(pb, SSH_FXF_READ);
249 psbuf_put_vattr(pb, &va);
250 pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
251
252 puffs_cc_yield(pcc);
253
254 rv = psbuf_expect_handle(pb, &fhand, &fhandlen);
255 if (rv)
256 goto out;
257
258 readlen = *resid;
259 reqid = NEXTREQ(pctx);
260 psbuf_recycle(pb, PSB_OUT);
261 psbuf_req_data(pb, SSH_FXP_READ, reqid, fhand, fhandlen);
262 psbuf_put_8(pb, offset);
263 psbuf_put_4(pb, readlen);
264 pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
265
266 puffs_cc_yield(pcc);
267
268 rv = psbuf_do_data(pb, buf, &readlen);
269 if (rv == 0)
270 *resid -= readlen;
271
272 reqid = NEXTREQ(pctx);
273 psbuf_recycle(pb, PSB_OUT);
274 psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen);
275 pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
276
277 puffs_cc_yield(pcc);
278
279 /* don't care */
280
281 out:
282 free(fhand);
283 PSSHFSRETURN(rv);
284 }
285
286 /* XXX: we should getattr for size */
287 int
288 psshfs_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
289 off_t offset, size_t *resid, const struct puffs_cred *cred,
290 int ioflag)
291 {
292 PSSHFSAUTOVAR(pcc);
293 struct puffs_node *pn = opc;
294 char *fhand = NULL;
295 size_t fhandlen;
296 struct vattr va, kludgeva1, kludgeva2;
297 uint32_t writelen, oflags;
298
299 if (pn->pn_va.va_type == VDIR) {
300 rv = EISDIR;
301 goto out;
302 }
303
304 /*
305 * XXXX: this is wrong - we shouldn't muck the file permissions
306 * at this stage any more. However, we need this, since currently
307 * we can't tell the sftp server "hey, this data was already
308 * authenticated to UBC, it's ok to let us write this". Yes, it
309 * will fail e.g. if we don't own the file. Tough love.
310 *
311 * TODO-point: Investigate solving this with open filehandles
312 * or something like that.
313 */
314 kludgeva1 = pn->pn_va;
315
316 puffs_vattr_null(&kludgeva2);
317 kludgeva2.va_mode = 0700;
318 rv = psshfs_node_setattr(pcc, opc, &kludgeva2, cred, 0);
319 if (rv)
320 goto out;
321
322 /* XXXcontinuation: ok, file is mode 700 now, we can open it rw */
323
324 oflags = SSH_FXF_WRITE;
325 #if 0
326 /*
327 * At least OpenSSH doesn't appear to support this, so can't
328 * do it the right way.
329 */
330 if (ioflag & PUFFS_IO_APPEND)
331 oflags |= SSH_FXF_APPEND;
332 #endif
333 if (ioflag & PUFFS_IO_APPEND)
334 offset = pn->pn_va.va_size;
335
336 puffs_vattr_null(&va);
337 psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PNPATH(pn));
338 psbuf_put_4(pb, oflags);
339 psbuf_put_vattr(pb, &va);
340 pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
341
342 puffs_cc_yield(pcc);
343
344 rv = psbuf_expect_handle(pb, &fhand, &fhandlen);
345 if (rv)
346 goto out;
347
348 /* moreXXX: file is open, revert old creds for crying out loud! */
349 rv = psshfs_node_setattr(pcc, opc, &kludgeva1, cred, 0);
350
351 /* are we screwed a la royal jelly? */
352 if (rv)
353 goto closefile;
354
355 writelen = *resid;
356 reqid = NEXTREQ(pctx);
357 psbuf_recycle(pb, PSB_OUT);
358 psbuf_req_data(pb, SSH_FXP_WRITE, reqid, fhand, fhandlen);
359 psbuf_put_8(pb, offset);
360 psbuf_put_data(pb, buf, writelen);
361 pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
362
363 puffs_cc_yield(pcc);
364
365 rv = psbuf_expect_status(pb);
366 if (rv == 0)
367 *resid = 0;
368
369 if (pn->pn_va.va_size < offset + writelen)
370 pn->pn_va.va_size = offset + writelen;
371
372 closefile:
373 reqid = NEXTREQ(pctx);
374 psbuf_recycle(pb, PSB_OUT);
375 psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen);
376 pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
377
378 puffs_cc_yield(pcc);
379
380 /* dontcare */
381 out:
382 free(fhand);
383 PSSHFSRETURN(rv);
384 }
385
386 int
387 psshfs_node_readlink(struct puffs_cc *pcc, void *opc,
388 const struct puffs_cred *cred, char *linkvalue, size_t *linklen)
389 {
390 PSSHFSAUTOVAR(pcc);
391 struct puffs_node *pn = opc;
392 char *linktmp = NULL;
393 uint32_t count;
394
395 if (pctx->protover < 3) {
396 rv = EOPNOTSUPP;
397 goto out;
398 }
399
400 psbuf_req_str(pb, SSH_FXP_READLINK, reqid, PNPATH(pn));
401 pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
402
403 puffs_cc_yield(pcc);
404
405 rv = psbuf_expect_name(pb, &count);
406 if (count != 1) {
407 rv = EPROTO;
408 goto out;
409 }
410 rv = psbuf_get_str(pb, &linktmp, linklen);
411 if (rv)
412 rv = 0;
413 else {
414 rv = EPROTO;
415 goto out;
416 }
417 (void) memcpy(linkvalue, linktmp, *linklen);
418
419 out:
420 free(linktmp);
421 PSSHFSRETURN(rv);
422 }
423
424 int
425 psshfs_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
426 const struct puffs_cn *pcn)
427 {
428 PSSHFSAUTOVAR(pcc);
429 struct puffs_node *pn_targ = targ;
430
431 if (pn_targ->pn_va.va_type == VDIR) {
432 rv = EPERM;
433 goto out;
434 }
435
436 psbuf_req_str(pb, SSH_FXP_REMOVE, reqid, PNPATH(pn_targ));
437 pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
438
439 puffs_cc_yield(pcc);
440
441 rv = psbuf_expect_status(pb);
442
443 if (rv == 0)
444 nukenode(pn_targ, pcn->pcn_name, 1);
445
446 out:
447 PSSHFSRETURN(rv);
448 }
449
450 int
451 psshfs_node_mkdir(struct puffs_cc *pcc, void *opc, void **newnode,
452 const struct puffs_cn *pcn, const struct vattr *va)
453 {
454 PSSHFSAUTOVAR(pcc);
455 struct puffs_node *pn = opc;
456 struct puffs_node *pn_new;
457
458 pn_new = allocnode(pu, pn, pcn->pcn_name, va);
459 if (!pn_new) {
460 rv = ENOMEM;
461 goto out;
462 }
463
464 psbuf_req_str(pb, SSH_FXP_MKDIR, reqid, PCNPATH(pcn));
465 psbuf_put_vattr(pb, va);
466 pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
467
468 puffs_cc_yield(pcc);
469
470 rv = psbuf_expect_status(pb);
471
472 if (rv == 0)
473 *newnode = pn_new;
474 else
475 nukenode(pn_new, pcn->pcn_name, 1);
476
477 out:
478 PSSHFSRETURN(rv);
479 }
480
481 int
482 psshfs_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
483 const struct puffs_cn *pcn)
484 {
485 PSSHFSAUTOVAR(pcc);
486 struct puffs_node *pn_targ = targ;
487
488 psbuf_req_str(pb, SSH_FXP_RMDIR, reqid, PNPATH(pn_targ));
489 pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
490
491 puffs_cc_yield(pcc);
492
493 rv = psbuf_expect_status(pb);
494 if (rv == 0)
495 nukenode(pn_targ, pcn->pcn_name, 1);
496
497 PSSHFSRETURN(rv);
498 }
499
500 int
501 psshfs_node_symlink(struct puffs_cc *pcc, void *opc, void **newnode,
502 const struct puffs_cn *pcn, const struct vattr *va,
503 const char *link_target)
504 {
505 PSSHFSAUTOVAR(pcc);
506 struct puffs_node *pn = opc;
507 struct puffs_node *pn_new;
508
509 if (pctx->protover < 3) {
510 rv = EOPNOTSUPP;
511 goto out;
512 }
513
514 pn_new = allocnode(pu, pn, pcn->pcn_name, va);
515 if (!pn) {
516 rv = ENOMEM;
517 goto out;
518 }
519
520 /*
521 * XXX: ietf says: source, target. openssh says: ietf who?
522 * Let's go with openssh and build quirk tables later if we care
523 */
524 psbuf_req_str(pb, SSH_FXP_SYMLINK, reqid, link_target);
525 psbuf_put_str(pb, PCNPATH(pcn));
526 pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
527
528 puffs_cc_yield(pcc);
529
530 rv = psbuf_expect_status(pb);
531 if (rv == 0)
532 *newnode = pn_new;
533 else
534 nukenode(pn_new, pcn->pcn_name, 1);
535
536 out:
537 PSSHFSRETURN(rv);
538 }
539
540 int
541 psshfs_node_rename(struct puffs_cc *pcc, void *opc, void *src,
542 const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
543 const struct puffs_cn *pcn_targ)
544 {
545 PSSHFSAUTOVAR(pcc);
546 struct puffs_node *pn_sf = src;
547 struct puffs_node *pn_td = targ_dir, *pn_tf = targ;
548 struct psshfs_node *psn_targdir = pn_td->pn_data;
549
550 if (pctx->protover < 2) {
551 rv = EOPNOTSUPP;
552 goto out;
553 }
554
555 if (pn_tf) {
556 /* XXX: no backend implementation for now, so call directly */
557 rv = psshfs_node_remove(pcc, targ_dir, pn_tf, pcn_targ);
558 if (rv)
559 goto out;
560 }
561
562 psbuf_req_str(pb, SSH_FXP_RENAME, reqid, PCNPATH(pcn_src));
563 psbuf_put_str(pb, PCNPATH(pcn_targ));
564 pssh_outbuf_enqueue(pctx, pb, pcc, reqid);
565
566 puffs_cc_yield(pcc);
567
568 rv = psbuf_expect_status(pb);
569 if (rv == 0) {
570 struct psshfs_dir *pd;
571
572 /*
573 * XXX: interfaces didn't quite work with rename..
574 * the song remains the same. go figure .. ;)
575 */
576 nukenode(pn_sf, pcn_src->pcn_name, 0);
577 pd = direnter(pn_td, pcn_targ->pcn_name);
578 pd->entry = pn_sf;
579 puffs_setvattr(&pd->va, &pn_sf->pn_va);
580
581 if (opc != targ_dir) {
582 psn_targdir->childcount++;
583 if (pn_sf->pn_va.va_type == VDIR)
584 pn_td->pn_va.va_nlink++;
585 }
586 }
587
588 out:
589 PSSHFSRETURN(rv);
590 }
591