node.c revision 1.3 1 /* $NetBSD: node.c,v 1.3 2007/01/07 19:31:48 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.3 2007/01/07 19:31:48 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);
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);
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);
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);
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);
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);
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