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