| @@ -1,949 +1,949 @@ | | | @@ -1,949 +1,949 @@ |
1 | /* $NetBSD: node.c,v 1.64 2012/01/20 22:07:58 jakllsch Exp $ */ | | 1 | /* $NetBSD: node.c,v 1.65 2016/10/07 21:09:57 christos Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 2006-2009 Antti Kantee. All Rights Reserved. | | 4 | * Copyright (c) 2006-2009 Antti Kantee. All Rights Reserved. |
5 | * | | 5 | * |
6 | * Redistribution and use in source and binary forms, with or without | | 6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions | | 7 | * modification, are permitted provided that the following conditions |
8 | * are met: | | 8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright | | 9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. | | 10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright | | 11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the | | 12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. | | 13 | * documentation and/or other materials provided with the distribution. |
14 | * | | 14 | * |
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS | | 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
16 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | | 16 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | | 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
18 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | | 18 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 | * SUCH DAMAGE. | | 25 | * SUCH DAMAGE. |
26 | */ | | 26 | */ |
27 | | | 27 | |
28 | #include <sys/cdefs.h> | | 28 | #include <sys/cdefs.h> |
29 | #ifndef lint | | 29 | #ifndef lint |
30 | __RCSID("$NetBSD: node.c,v 1.64 2012/01/20 22:07:58 jakllsch Exp $"); | | 30 | __RCSID("$NetBSD: node.c,v 1.65 2016/10/07 21:09:57 christos Exp $"); |
31 | #endif /* !lint */ | | 31 | #endif /* !lint */ |
32 | | | 32 | |
33 | #include <assert.h> | | 33 | #include <assert.h> |
34 | #include <errno.h> | | 34 | #include <errno.h> |
35 | #include <stdio.h> | | 35 | #include <stdio.h> |
36 | #include <stdlib.h> | | 36 | #include <stdlib.h> |
37 | | | 37 | |
38 | #include "psshfs.h" | | 38 | #include "psshfs.h" |
39 | #include "sftp_proto.h" | | 39 | #include "sftp_proto.h" |
40 | | | 40 | |
41 | int | | 41 | int |
42 | psshfs_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc, | | 42 | psshfs_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc, |
43 | struct puffs_newinfo *pni, const struct puffs_cn *pcn) | | 43 | struct puffs_newinfo *pni, const struct puffs_cn *pcn) |
44 | { | | 44 | { |
45 | struct psshfs_ctx *pctx = puffs_getspecific(pu); | | 45 | struct psshfs_ctx *pctx = puffs_getspecific(pu); |
46 | struct puffs_node *pn_dir = opc; | | 46 | struct puffs_node *pn_dir = opc; |
47 | struct psshfs_node *psn, *psn_dir = pn_dir->pn_data; | | 47 | struct psshfs_node *psn, *psn_dir = pn_dir->pn_data; |
48 | struct puffs_node *pn; | | 48 | struct puffs_node *pn; |
49 | struct psshfs_dir *pd; | | 49 | struct psshfs_dir *pd; |
50 | struct vattr va; | | 50 | struct vattr va; |
51 | int rv; | | 51 | int rv; |
52 | | | 52 | |
53 | if (PCNISDOTDOT(pcn)) { | | 53 | if (PCNISDOTDOT(pcn)) { |
54 | psn = psn_dir->parent->pn_data; | | 54 | psn = psn_dir->parent->pn_data; |
55 | psn->stat &= ~PSN_RECLAIMED; | | 55 | psn->stat &= ~PSN_RECLAIMED; |
56 | | | 56 | |
57 | puffs_newinfo_setcookie(pni, psn_dir->parent); | | 57 | puffs_newinfo_setcookie(pni, psn_dir->parent); |
58 | puffs_newinfo_setvtype(pni, VDIR); | | 58 | puffs_newinfo_setvtype(pni, VDIR); |
59 | return 0; | | 59 | return 0; |
60 | } | | 60 | } |
61 | | | 61 | |
62 | rv = sftp_readdir(pu, pctx, pn_dir); | | 62 | rv = sftp_readdir(pu, pctx, pn_dir); |
63 | if (rv) { | | 63 | if (rv) { |
64 | if (rv != EPERM) | | 64 | if (rv != EPERM) |
65 | return rv; | | 65 | return rv; |
66 | | | 66 | |
67 | /* | | 67 | /* |
68 | * Can't read the directory. We still might be | | 68 | * Can't read the directory. We still might be |
69 | * able to find the node with getattr in -r+x dirs | | 69 | * able to find the node with getattr in -r+x dirs |
70 | */ | | 70 | */ |
71 | rv = getpathattr(pu, PCNPATH(pcn), &va); | | 71 | rv = getpathattr(pu, PCNPATH(pcn), &va); |
72 | if (rv) | | 72 | if (rv) |
73 | return rv; | | 73 | return rv; |
74 | | | 74 | |
75 | /* guess */ | | 75 | /* guess */ |
76 | if (va.va_type == VDIR) | | 76 | if (va.va_type == VDIR) |
77 | va.va_nlink = 2; | | 77 | va.va_nlink = 2; |
78 | else | | 78 | else |
79 | va.va_nlink = 1; | | 79 | va.va_nlink = 1; |
80 | | | 80 | |
81 | pn = allocnode(pu, pn_dir, pcn->pcn_name, &va); | | 81 | pn = allocnode(pu, pn_dir, pcn->pcn_name, &va); |
82 | psn = pn->pn_data; | | 82 | psn = pn->pn_data; |
83 | psn->attrread = time(NULL); | | 83 | psn->attrread = time(NULL); |
84 | } else { | | 84 | } else { |
85 | pd = lookup(psn_dir->dir, psn_dir->dentnext, pcn->pcn_name); | | 85 | pd = lookup(psn_dir->dir, psn_dir->dentnext, pcn->pcn_name); |
86 | if (!pd) { | | 86 | if (!pd) { |
87 | return ENOENT; | | 87 | return ENOENT; |
88 | } | | 88 | } |
89 | | | 89 | |
90 | if (pd->entry) | | 90 | if (pd->entry) |
91 | pn = pd->entry; | | 91 | pn = pd->entry; |
92 | else | | 92 | else |
93 | pd->entry = pn = makenode(pu, pn_dir, pd, &pd->va); | | 93 | pd->entry = pn = makenode(pu, pn_dir, pd, &pd->va); |
94 | | | 94 | |
95 | /* | | 95 | /* |
96 | * sure sure we have fresh attributes. most likely we will | | 96 | * sure sure we have fresh attributes. most likely we will |
97 | * have them cached. we might not if we go through: | | 97 | * have them cached. we might not if we go through: |
98 | * create - reclaim - lookup (this). | | 98 | * create - reclaim - lookup (this). |
99 | */ | | 99 | */ |
100 | rv = getnodeattr(pu, pn, PCNPATH(pcn)); | | 100 | rv = getnodeattr(pu, pn, PCNPATH(pcn)); |
101 | if (rv) | | 101 | if (rv) |
102 | return rv; | | 102 | return rv; |
103 | | | 103 | |
104 | psn = pn->pn_data; | | 104 | psn = pn->pn_data; |
105 | } | | 105 | } |
106 | | | 106 | |
107 | psn->stat &= ~PSN_RECLAIMED; | | 107 | psn->stat &= ~PSN_RECLAIMED; |
108 | | | 108 | |
109 | puffs_newinfo_setcookie(pni, pn); | | 109 | puffs_newinfo_setcookie(pni, pn); |
110 | puffs_newinfo_setvtype(pni, pn->pn_va.va_type); | | 110 | puffs_newinfo_setvtype(pni, pn->pn_va.va_type); |
111 | puffs_newinfo_setsize(pni, pn->pn_va.va_size); | | 111 | puffs_newinfo_setsize(pni, pn->pn_va.va_size); |
112 | | | 112 | |
113 | return 0; | | 113 | return 0; |
114 | } | | 114 | } |
115 | | | 115 | |
116 | int | | 116 | int |
117 | psshfs_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc, | | 117 | psshfs_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc, |
118 | struct vattr *vap, const struct puffs_cred *pcr) | | 118 | struct vattr *vap, const struct puffs_cred *pcr) |
119 | { | | 119 | { |
120 | struct puffs_node *pn = opc; | | 120 | struct puffs_node *pn = opc; |
121 | int rv; | | 121 | int rv; |
122 | | | 122 | |
123 | rv = getnodeattr(pu, pn, NULL); | | 123 | rv = getnodeattr(pu, pn, NULL); |
124 | if (rv) | | 124 | if (rv) |
125 | return rv; | | 125 | return rv; |
126 | | | 126 | |
127 | memcpy(vap, &pn->pn_va, sizeof(struct vattr)); | | 127 | memcpy(vap, &pn->pn_va, sizeof(struct vattr)); |
128 | | | 128 | |
129 | return 0; | | 129 | return 0; |
130 | } | | 130 | } |
131 | | | 131 | |
132 | int | | 132 | int |
133 | psshfs_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc, | | 133 | psshfs_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc, |
134 | const struct vattr *va, const struct puffs_cred *pcr) | | 134 | const struct vattr *va, const struct puffs_cred *pcr) |
135 | { | | 135 | { |
136 | struct puffs_cc *pcc = puffs_cc_getcc(pu); | | 136 | struct puffs_cc *pcc = puffs_cc_getcc(pu); |
137 | struct psshfs_ctx *pctx = puffs_getspecific(pu); | | 137 | struct psshfs_ctx *pctx = puffs_getspecific(pu); |
138 | uint32_t reqid; | | 138 | uint32_t reqid; |
139 | struct puffs_framebuf *pb; | | 139 | struct puffs_framebuf *pb; |
140 | struct vattr kludgeva; | | 140 | struct vattr kludgeva; |
141 | struct puffs_node *pn = opc; | | 141 | struct puffs_node *pn = opc; |
142 | struct psshfs_node *psn = pn->pn_data; | | 142 | struct psshfs_node *psn = pn->pn_data; |
143 | int rv; | | 143 | int rv; |
144 | | | 144 | |
145 | /* | | 145 | /* |
146 | * If we cached the remote attributes recently enough, and this | | 146 | * If we cached the remote attributes recently enough, and this |
147 | * setattr operation would change nothing that sftp actually | | 147 | * setattr operation would change nothing that sftp actually |
148 | * records, then we can skip the sftp request. So first check | | 148 | * records, then we can skip the sftp request. So first check |
149 | * whether we have the attributes cached, and then compare | | 149 | * whether we have the attributes cached, and then compare |
150 | * every field that we might send to the sftp server. | | 150 | * every field that we might send to the sftp server. |
151 | */ | | 151 | */ |
152 | | | 152 | |
153 | if (!psn->attrread || REFRESHTIMEOUT(pctx, time(NULL)-psn->attrread)) | | 153 | if (!psn->attrread || REFRESHTIMEOUT(pctx, time(NULL)-psn->attrread)) |
154 | goto setattr; | | 154 | goto setattr; |
155 | | | 155 | |
156 | #define CHECK(FIELD, TYPE) do { \ | | 156 | #define CHECK(FIELD, TYPE) do { \ |
157 | if ((va->FIELD != (TYPE)PUFFS_VNOVAL) && \ | | 157 | if ((va->FIELD != (TYPE)PUFFS_VNOVAL) && \ |
158 | (va->FIELD != pn->pn_va.FIELD)) \ | | 158 | (va->FIELD != pn->pn_va.FIELD)) \ |
159 | goto setattr; \ | | 159 | goto setattr; \ |
160 | } while (0) | | 160 | } while (0) |
161 | | | 161 | |
162 | #define CHECKID(FIELD, TYPE, DOMANGLE, MINE, MANGLED) do { \ | | 162 | #define CHECKID(FIELD, TYPE, DOMANGLE, MINE, MANGLED) do { \ |
163 | if ((va->FIELD != (TYPE)PUFFS_VNOVAL) && \ | | 163 | if ((va->FIELD != (TYPE)PUFFS_VNOVAL) && \ |
164 | (pn->pn_va.FIELD != \ | | 164 | (pn->pn_va.FIELD != \ |
165 | ((pctx->DOMANGLE && (va->FIELD == pctx->MINE)) \ | | 165 | ((pctx->DOMANGLE && (va->FIELD == pctx->MINE)) \ |
166 | ? pctx->MANGLED \ | | 166 | ? pctx->MANGLED \ |
167 | : va->FIELD))) \ | | 167 | : va->FIELD))) \ |
168 | goto setattr; \ | | 168 | goto setattr; \ |
169 | } while (0) | | 169 | } while (0) |
170 | | | 170 | |
171 | CHECK(va_size, uint64_t); | | 171 | CHECK(va_size, uint64_t); |
172 | CHECKID(va_uid, uid_t, domangleuid, myuid, mangleuid); | | 172 | CHECKID(va_uid, uid_t, domangleuid, myuid, mangleuid); |
173 | CHECKID(va_gid, gid_t, domanglegid, mygid, manglegid); | | 173 | CHECKID(va_gid, gid_t, domanglegid, mygid, manglegid); |
174 | CHECK(va_mode, mode_t); | | 174 | CHECK(va_mode, mode_t); |
175 | CHECK(va_atime.tv_sec, time_t); | | 175 | CHECK(va_atime.tv_sec, time_t); |
176 | CHECK(va_mtime.tv_sec, time_t); | | 176 | CHECK(va_mtime.tv_sec, time_t); |
177 | | | 177 | |
178 | /* Nothing to change. */ | | 178 | /* Nothing to change. */ |
179 | return 0; | | 179 | return 0; |
180 | | | 180 | |
181 | #undef CHECK | | 181 | #undef CHECK |
182 | #undef CHECKID | | 182 | #undef CHECKID |
183 | | | 183 | |
184 | setattr: | | 184 | setattr: |
185 | reqid = NEXTREQ(pctx); | | 185 | reqid = NEXTREQ(pctx); |
186 | pb = psbuf_makeout(); | | 186 | pb = psbuf_makeout(); |
187 | | | 187 | |
188 | psbuf_req_str(pb, SSH_FXP_SETSTAT, reqid, PNPATH(pn)); | | 188 | psbuf_req_str(pb, SSH_FXP_SETSTAT, reqid, PNPATH(pn)); |
189 | | | 189 | |
190 | memcpy(&kludgeva, va, sizeof(struct vattr)); | | 190 | memcpy(&kludgeva, va, sizeof(struct vattr)); |
191 | | | 191 | |
192 | /* XXX: kludge due to openssh server implementation */ | | 192 | /* XXX: kludge due to openssh server implementation */ |
193 | if (va->va_atime.tv_sec != PUFFS_VNOVAL | | 193 | if (va->va_atime.tv_sec != PUFFS_VNOVAL |
194 | && va->va_mtime.tv_sec == PUFFS_VNOVAL) { | | 194 | && va->va_mtime.tv_sec == PUFFS_VNOVAL) { |
195 | if (pn->pn_va.va_mtime.tv_sec != PUFFS_VNOVAL) | | 195 | if (pn->pn_va.va_mtime.tv_sec != PUFFS_VNOVAL) |
196 | kludgeva.va_mtime.tv_sec = pn->pn_va.va_mtime.tv_sec; | | 196 | kludgeva.va_mtime.tv_sec = pn->pn_va.va_mtime.tv_sec; |
197 | else | | 197 | else |
198 | kludgeva.va_mtime.tv_sec = va->va_atime.tv_sec; | | 198 | kludgeva.va_mtime.tv_sec = va->va_atime.tv_sec; |
199 | } | | 199 | } |
200 | if (va->va_mtime.tv_sec != PUFFS_VNOVAL | | 200 | if (va->va_mtime.tv_sec != PUFFS_VNOVAL |
201 | && va->va_atime.tv_sec == PUFFS_VNOVAL) { | | 201 | && va->va_atime.tv_sec == PUFFS_VNOVAL) { |
202 | if (pn->pn_va.va_atime.tv_sec != PUFFS_VNOVAL) | | 202 | if (pn->pn_va.va_atime.tv_sec != PUFFS_VNOVAL) |
203 | kludgeva.va_atime.tv_sec = pn->pn_va.va_atime.tv_sec; | | 203 | kludgeva.va_atime.tv_sec = pn->pn_va.va_atime.tv_sec; |
204 | else | | 204 | else |
205 | kludgeva.va_atime.tv_sec = va->va_mtime.tv_sec; | | 205 | kludgeva.va_atime.tv_sec = va->va_mtime.tv_sec; |
206 | } | | 206 | } |
207 | | | 207 | |
208 | psbuf_put_vattr(pb, &kludgeva, pctx); | | 208 | psbuf_put_vattr(pb, &kludgeva, pctx); |
209 | GETRESPONSE(pb, pctx->sshfd); | | 209 | GETRESPONSE(pb, pctx->sshfd); |
210 | | | 210 | |
211 | rv = psbuf_expect_status(pb); | | 211 | rv = psbuf_expect_status(pb); |
212 | if (rv == 0) { | | 212 | if (rv == 0) { |
213 | puffs_setvattr(&pn->pn_va, &kludgeva); | | 213 | puffs_setvattr(&pn->pn_va, &kludgeva); |
214 | psn->attrread = time(NULL); | | 214 | psn->attrread = time(NULL); |
215 | } | | 215 | } |
216 | | | 216 | |
217 | out: | | 217 | out: |
218 | puffs_framebuf_destroy(pb); | | 218 | puffs_framebuf_destroy(pb); |
219 | return rv; | | 219 | return rv; |
220 | } | | 220 | } |
221 | | | 221 | |
222 | int | | 222 | int |
223 | psshfs_node_create(struct puffs_usermount *pu, puffs_cookie_t opc, | | 223 | psshfs_node_create(struct puffs_usermount *pu, puffs_cookie_t opc, |
224 | struct puffs_newinfo *pni, const struct puffs_cn *pcn, | | 224 | struct puffs_newinfo *pni, const struct puffs_cn *pcn, |
225 | const struct vattr *va) | | 225 | const struct vattr *va) |
226 | { | | 226 | { |
227 | PSSHFSAUTOVAR(pu); | | 227 | PSSHFSAUTOVAR(pu); |
228 | struct puffs_node *pn = opc; | | 228 | struct puffs_node *pn = opc; |
229 | struct puffs_node *pn_new; | | 229 | struct puffs_node *pn_new; |
230 | char *fhand = NULL; | | 230 | char *fhand = NULL; |
231 | uint32_t fhandlen; | | 231 | uint32_t fhandlen; |
232 | | | 232 | |
233 | /* Create node on server first */ | | 233 | /* Create node on server first */ |
234 | psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PCNPATH(pcn)); | | 234 | psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PCNPATH(pcn)); |
235 | psbuf_put_4(pb, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC); | | 235 | psbuf_put_4(pb, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC); |
236 | psbuf_put_vattr(pb, va, pctx); | | 236 | psbuf_put_vattr(pb, va, pctx); |
237 | GETRESPONSE(pb, pctx->sshfd); | | 237 | GETRESPONSE(pb, pctx->sshfd); |
238 | rv = psbuf_expect_handle(pb, &fhand, &fhandlen); | | 238 | rv = psbuf_expect_handle(pb, &fhand, &fhandlen); |
239 | if (rv) | | 239 | if (rv) |
240 | goto out; | | 240 | goto out; |
241 | | | 241 | |
242 | /* | | 242 | /* |
243 | * Do *not* create the local node before getting a response | | 243 | * Do *not* create the local node before getting a response |
244 | * from the server. Otherwise we might screw up consistency, | | 244 | * from the server. Otherwise we might screw up consistency, |
245 | * namely that the node can be looked up before create has | | 245 | * namely that the node can be looked up before create has |
246 | * returned (mind you, the kernel will unlock the directory | | 246 | * returned (mind you, the kernel will unlock the directory |
247 | * before the create call from userspace returns). | | 247 | * before the create call from userspace returns). |
248 | */ | | 248 | */ |
249 | pn_new = allocnode(pu, pn, pcn->pcn_name, va); | | 249 | pn_new = allocnode(pu, pn, pcn->pcn_name, va); |
250 | if (!pn_new) { | | 250 | if (!pn_new) { |
251 | struct puffs_framebuf *pb2 = psbuf_makeout(); | | 251 | struct puffs_framebuf *pb2 = psbuf_makeout(); |
252 | reqid = NEXTREQ(pctx); | | 252 | reqid = NEXTREQ(pctx); |
253 | psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn)); | | 253 | psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn)); |
254 | JUSTSEND(pb2, pctx->sshfd); | | 254 | JUSTSEND(pb2, pctx->sshfd); |
255 | rv = ENOMEM; | | 255 | rv = ENOMEM; |
256 | } | | 256 | } |
257 | | | 257 | |
258 | if (pn_new) | | 258 | if (pn_new) |
259 | puffs_newinfo_setcookie(pni, pn_new); | | 259 | puffs_newinfo_setcookie(pni, pn_new); |
260 | | | 260 | |
261 | reqid = NEXTREQ(pctx); | | 261 | reqid = NEXTREQ(pctx); |
262 | psbuf_recycleout(pb); | | 262 | psbuf_recycleout(pb); |
263 | psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen); | | 263 | psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen); |
264 | JUSTSEND(pb, pctx->sshfd); | | 264 | JUSTSEND(pb, pctx->sshfd); |
265 | free(fhand); | | 265 | free(fhand); |
266 | return rv; | | 266 | return rv; |
267 | | | 267 | |
268 | out: | | 268 | out: |
269 | free(fhand); | | 269 | free(fhand); |
270 | PSSHFSRETURN(rv); | | 270 | PSSHFSRETURN(rv); |
271 | } | | 271 | } |
272 | | | 272 | |
273 | /* | | 273 | /* |
274 | * Open a file handle. This is used for read and write. We do not | | 274 | * Open a file handle. This is used for read and write. We do not |
275 | * wait here for the success or failure of this operation. This is | | 275 | * wait here for the success or failure of this operation. This is |
276 | * because otherwise opening and closing file handles would block | | 276 | * because otherwise opening and closing file handles would block |
277 | * reading potentially cached information. Rather, we defer the wait | | 277 | * reading potentially cached information. Rather, we defer the wait |
278 | * to read/write and therefore allow cached access without a wait. | | 278 | * to read/write and therefore allow cached access without a wait. |
279 | * | | 279 | * |
280 | * If we have not yet succesfully opened a type of handle, we do wait | | 280 | * If we have not yet succesfully opened a type of handle, we do wait |
281 | * here. Also, if a lazy open fails, we revert back to the same | | 281 | * here. Also, if a lazy open fails, we revert back to the same |
282 | * state of waiting. | | 282 | * state of waiting. |
283 | */ | | 283 | */ |
284 | int | | 284 | int |
285 | psshfs_node_open(struct puffs_usermount *pu, puffs_cookie_t opc, int mode, | | 285 | psshfs_node_open(struct puffs_usermount *pu, puffs_cookie_t opc, int mode, |
286 | const struct puffs_cred *pcr) | | 286 | const struct puffs_cred *pcr) |
287 | { | | 287 | { |
288 | struct puffs_cc *pcc = puffs_cc_getcc(pu); | | 288 | struct puffs_cc *pcc = puffs_cc_getcc(pu); |
289 | struct psshfs_ctx *pctx = puffs_getspecific(pu); | | 289 | struct psshfs_ctx *pctx = puffs_getspecific(pu); |
290 | struct puffs_framebuf *pb, *pb2; | | 290 | struct puffs_framebuf *pb, *pb2; |
291 | struct vattr va; | | 291 | struct vattr va; |
292 | struct puffs_node *pn = opc; | | 292 | struct puffs_node *pn = opc; |
293 | struct psshfs_node *psn = pn->pn_data; | | 293 | struct psshfs_node *psn = pn->pn_data; |
294 | uint32_t reqid; | | 294 | uint32_t reqid; |
295 | int didread, didwrite; | | 295 | int didread, didwrite; |
296 | int rv = 0; | | 296 | int rv = 0; |
297 | | | 297 | |
298 | if (pn->pn_va.va_type == VDIR) | | 298 | if (pn->pn_va.va_type == VDIR) |
299 | return 0; | | 299 | return 0; |
300 | | | 300 | |
301 | puffs_setback(pcc, PUFFS_SETBACK_INACT_N1); | | 301 | puffs_setback(pcc, PUFFS_SETBACK_INACT_N1); |
302 | puffs_vattr_null(&va); | | 302 | puffs_vattr_null(&va); |
303 | didread = didwrite = 0; | | 303 | didread = didwrite = 0; |
304 | if (mode & FREAD && psn->fhand_r == NULL && psn->lazyopen_r == NULL) { | | 304 | if (mode & FREAD && psn->fhand_r == NULL && psn->lazyopen_r == NULL) { |
305 | pb = psbuf_makeout(); | | 305 | pb = psbuf_makeout(); |
306 | | | 306 | |
307 | reqid = NEXTREQ(pctx); | | 307 | reqid = NEXTREQ(pctx); |
308 | psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PNPATH(pn)); | | 308 | psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PNPATH(pn)); |
309 | psbuf_put_4(pb, SSH_FXF_READ); | | 309 | psbuf_put_4(pb, SSH_FXF_READ); |
310 | psbuf_put_vattr(pb, &va, pctx); | | 310 | psbuf_put_vattr(pb, &va, pctx); |
311 | | | 311 | |
312 | if (puffs_framev_enqueue_cb(pu, pctx->sshfd_data, pb, | | 312 | if (puffs_framev_enqueue_cb(pu, pctx->sshfd_data, pb, |
313 | lazyopen_rresp, psn, 0) == -1) { | | 313 | lazyopen_rresp, psn, 0) == -1) { |
314 | rv = errno; | | 314 | rv = errno; |
315 | puffs_framebuf_destroy(pb); | | 315 | puffs_framebuf_destroy(pb); |
316 | goto out; | | 316 | goto out; |
317 | } | | 317 | } |
318 | | | 318 | |
319 | psn->lazyopen_r = pb; | | 319 | psn->lazyopen_r = pb; |
320 | didread = 1; | | 320 | didread = 1; |
321 | } | | 321 | } |
322 | if (mode & FWRITE && psn->fhand_w == NULL && psn->lazyopen_w == NULL) { | | 322 | if (mode & FWRITE && psn->fhand_w == NULL && psn->lazyopen_w == NULL) { |
323 | pb2 = psbuf_makeout(); | | 323 | pb2 = psbuf_makeout(); |
324 | | | 324 | |
325 | reqid = NEXTREQ(pctx); | | 325 | reqid = NEXTREQ(pctx); |
326 | psbuf_req_str(pb2, SSH_FXP_OPEN, reqid, PNPATH(pn)); | | 326 | psbuf_req_str(pb2, SSH_FXP_OPEN, reqid, PNPATH(pn)); |
327 | psbuf_put_4(pb2, SSH_FXF_WRITE); | | 327 | psbuf_put_4(pb2, SSH_FXF_WRITE); |
328 | psbuf_put_vattr(pb2, &va, pctx); | | 328 | psbuf_put_vattr(pb2, &va, pctx); |
329 | | | 329 | |
330 | if (puffs_framev_enqueue_cb(pu, pctx->sshfd_data, pb2, | | 330 | if (puffs_framev_enqueue_cb(pu, pctx->sshfd_data, pb2, |
331 | lazyopen_wresp, psn, 0) == -1) { | | 331 | lazyopen_wresp, psn, 0) == -1) { |
332 | rv = errno; | | 332 | rv = errno; |
333 | puffs_framebuf_destroy(pb2); | | 333 | puffs_framebuf_destroy(pb2); |
334 | goto out; | | 334 | goto out; |
335 | } | | 335 | } |
336 | | | 336 | |
337 | psn->lazyopen_w = pb2; | | 337 | psn->lazyopen_w = pb2; |
338 | didwrite = 1; | | 338 | didwrite = 1; |
339 | } | | 339 | } |
340 | psn->stat &= ~PSN_HANDLECLOSE; | | 340 | psn->stat &= ~PSN_HANDLECLOSE; |
341 | | | 341 | |
342 | out: | | 342 | out: |
343 | /* wait? */ | | 343 | /* wait? */ |
344 | if (didread && (psn->stat & PSN_DOLAZY_R) == 0) { | | 344 | if (didread && (psn->stat & PSN_DOLAZY_R) == 0) { |
345 | assert(psn->lazyopen_r); | | 345 | assert(psn->lazyopen_r); |
346 | | | 346 | |
347 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc); | | 347 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc); |
348 | lazyopen_rresp(pu, psn->lazyopen_r, psn, rv); | | 348 | lazyopen_rresp(pu, psn->lazyopen_r, psn, rv); |
349 | if (psn->fhand_r) { | | 349 | if (psn->fhand_r) { |
350 | psn->stat |= PSN_DOLAZY_R; | | 350 | psn->stat |= PSN_DOLAZY_R; |
351 | } else { | | 351 | } else { |
352 | if (psn->lazyopen_err_r) | | 352 | if (psn->lazyopen_err_r) |
353 | return psn->lazyopen_err_r; | | 353 | return psn->lazyopen_err_r; |
354 | return EINVAL; | | 354 | return EINVAL; |
355 | } | | 355 | } |
356 | } | | 356 | } |
357 | | | 357 | |
358 | /* wait? */ | | 358 | /* wait? */ |
359 | if (didwrite && (psn->stat & PSN_DOLAZY_W) == 0) { | | 359 | if (didwrite && (psn->stat & PSN_DOLAZY_W) == 0) { |
360 | assert(psn->lazyopen_w); | | 360 | assert(psn->lazyopen_w); |
361 | | | 361 | |
362 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc); | | 362 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc); |
363 | lazyopen_wresp(pu, psn->lazyopen_w, psn, rv); | | 363 | lazyopen_wresp(pu, psn->lazyopen_w, psn, rv); |
364 | if (psn->fhand_w) { | | 364 | if (psn->fhand_w) { |
365 | psn->stat |= PSN_DOLAZY_W; | | 365 | psn->stat |= PSN_DOLAZY_W; |
366 | } else { | | 366 | } else { |
367 | if (psn->lazyopen_err_w) | | 367 | if (psn->lazyopen_err_w) |
368 | return psn->lazyopen_err_w; | | 368 | return psn->lazyopen_err_w; |
369 | return EINVAL; | | 369 | return EINVAL; |
370 | } | | 370 | } |
371 | } | | 371 | } |
372 | | | 372 | |
373 | return rv; | | 373 | return rv; |
374 | } | | 374 | } |
375 | | | 375 | |
376 | int | | 376 | int |
377 | psshfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc) | | 377 | psshfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc) |
378 | { | | 378 | { |
379 | struct puffs_node *pn = opc; | | 379 | struct puffs_node *pn = opc; |
380 | | | 380 | |
381 | closehandles(pu, pn->pn_data, HANDLE_READ | HANDLE_WRITE); | | 381 | closehandles(pu, pn->pn_data, HANDLE_READ | HANDLE_WRITE); |
382 | return 0; | | 382 | return 0; |
383 | } | | 383 | } |
384 | | | 384 | |
385 | int | | 385 | int |
386 | psshfs_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc, | | 386 | psshfs_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc, |
387 | struct dirent *dent, off_t *readoff, size_t *reslen, | | 387 | struct dirent *dent, off_t *readoff, size_t *reslen, |
388 | const struct puffs_cred *pcr, int *eofflag, | | 388 | const struct puffs_cred *pcr, int *eofflag, |
389 | off_t *cookies, size_t *ncookies) | | 389 | off_t *cookies, size_t *ncookies) |
390 | { | | 390 | { |
391 | struct puffs_cc *pcc = puffs_cc_getcc(pu); | | 391 | struct puffs_cc *pcc = puffs_cc_getcc(pu); |
392 | struct psshfs_ctx *pctx = puffs_getspecific(pu); | | 392 | struct psshfs_ctx *pctx = puffs_getspecific(pu); |
393 | struct puffs_node *pn = opc; | | 393 | struct puffs_node *pn = opc; |
394 | struct psshfs_node *psn = pn->pn_data; | | 394 | struct psshfs_node *psn = pn->pn_data; |
395 | struct psshfs_dir *pd; | | 395 | struct psshfs_dir *pd; |
396 | size_t i; | | 396 | size_t i; |
397 | int rv, set_readdir; | | 397 | int rv, set_readdir; |
398 | | | 398 | |
399 | restart: | | 399 | restart: |
400 | if (psn->stat & PSN_READDIR) { | | 400 | if (psn->stat & PSN_READDIR) { |
401 | struct psshfs_wait pw; | | 401 | struct psshfs_wait pw; |
402 | | | 402 | |
403 | set_readdir = 0; | | 403 | set_readdir = 0; |
404 | pw.pw_cc = pcc; | | 404 | pw.pw_cc = pcc; |
405 | pw.pw_type = PWTYPE_READDIR; | | 405 | pw.pw_type = PWTYPE_READDIR; |
406 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); | | 406 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); |
407 | puffs_cc_yield(pcc); | | 407 | puffs_cc_yield(pcc); |
408 | goto restart; | | 408 | goto restart; |
409 | } else { | | 409 | } else { |
410 | psn->stat |= PSN_READDIR; | | 410 | psn->stat |= PSN_READDIR; |
411 | set_readdir = 1; | | 411 | set_readdir = 1; |
412 | } | | 412 | } |
413 | | | 413 | |
414 | *ncookies = 0; | | 414 | *ncookies = 0; |
415 | rv = sftp_readdir(pu, pctx, pn); | | 415 | rv = sftp_readdir(pu, pctx, pn); |
416 | if (rv) { | | 416 | if (rv) { |
417 | goto out; | | 417 | goto out; |
418 | } | | 418 | } |
419 | | | 419 | |
420 | /* find next dirent */ | | 420 | /* find next dirent */ |
421 | for (i = *readoff;;i++) { | | 421 | for (i = *readoff;;i++) { |
422 | if (i >= psn->dentnext) | | 422 | if (i >= psn->dentnext) |
423 | goto out; | | 423 | goto out; |
424 | pd = &psn->dir[i]; | | 424 | pd = &psn->dir[i]; |
425 | if (pd->valid) | | 425 | if (pd->valid) |
426 | break; | | 426 | break; |
427 | } | | 427 | } |
428 | | | 428 | |
429 | for (;;) { | | 429 | for (;;) { |
430 | *readoff = i; | | 430 | *readoff = i; |
431 | if (!puffs_nextdent(&dent, pd->entryname, | | 431 | if (!puffs_nextdent(&dent, pd->entryname, |
432 | pd->va.va_fileid, puffs_vtype2dt(pd->va.va_type), reslen)) { | | 432 | pd->va.va_fileid, puffs_vtype2dt(pd->va.va_type), reslen)) { |
433 | rv = 0; | | 433 | rv = 0; |
434 | goto out; | | 434 | goto out; |
435 | } | | 435 | } |
436 | | | 436 | |
437 | /* find next entry, store possible nfs key */ | | 437 | /* find next entry, store possible nfs key */ |
438 | do { | | 438 | do { |
439 | if (++i >= psn->dentnext) | | 439 | if (++i >= psn->dentnext) |
440 | goto out; | | 440 | goto out; |
441 | pd = &psn->dir[i]; | | 441 | pd = &psn->dir[i]; |
442 | } while (pd->valid == 0); | | 442 | } while (pd->valid == 0); |
443 | PUFFS_STORE_DCOOKIE(cookies, ncookies, (off_t)i); | | 443 | PUFFS_STORE_DCOOKIE(cookies, ncookies, (off_t)i); |
444 | } | | 444 | } |
445 | | | 445 | |
446 | out: | | 446 | out: |
447 | if (rv == 0) { | | 447 | if (rv == 0) { |
448 | if (i >= psn->dentnext) | | 448 | if (i >= psn->dentnext) |
449 | *eofflag = 1; | | 449 | *eofflag = 1; |
450 | | | 450 | |
451 | *readoff = i; | | 451 | *readoff = i; |
452 | } | | 452 | } |
453 | | | 453 | |
454 | if (set_readdir) { | | 454 | if (set_readdir) { |
455 | struct psshfs_wait *pw; | | 455 | struct psshfs_wait *pw; |
456 | | | 456 | |
457 | /* all will likely run to completion because of cache */ | | 457 | /* all will likely run to completion because of cache */ |
458 | TAILQ_FOREACH(pw, &psn->pw, pw_entries) { | | 458 | TAILQ_FOREACH(pw, &psn->pw, pw_entries) { |
459 | assert(pw->pw_type == PWTYPE_READDIR); | | 459 | assert(pw->pw_type == PWTYPE_READDIR); |
460 | puffs_cc_schedule(pw->pw_cc); | | 460 | puffs_cc_schedule(pw->pw_cc); |
461 | TAILQ_REMOVE(&psn->pw, pw, pw_entries); | | 461 | TAILQ_REMOVE(&psn->pw, pw, pw_entries); |
462 | } | | 462 | } |
463 | | | 463 | |
464 | psn->stat &= ~PSN_READDIR; | | 464 | psn->stat &= ~PSN_READDIR; |
465 | } | | 465 | } |
466 | | | 466 | |
467 | return rv; | | 467 | return rv; |
468 | } | | 468 | } |
469 | | | 469 | |
470 | int | | 470 | int |
471 | psshfs_node_read(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf, | | 471 | psshfs_node_read(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf, |
472 | off_t offset, size_t *resid, const struct puffs_cred *pcr, | | 472 | off_t offset, size_t *resid, const struct puffs_cred *pcr, |
473 | int ioflag) | | 473 | int ioflag) |
474 | { | | 474 | { |
475 | PSSHFSAUTOVAR(pu); | | 475 | PSSHFSAUTOVAR(pu); |
476 | struct puffs_node *pn = opc; | | 476 | struct puffs_node *pn = opc; |
477 | struct psshfs_node *psn = pn->pn_data; | | 477 | struct psshfs_node *psn = pn->pn_data; |
478 | struct psshfs_wait *pwp; | | 478 | struct psshfs_wait *pwp; |
479 | uint32_t readlen; | | 479 | uint32_t readlen; |
480 | | | 480 | |
481 | if (pn->pn_va.va_type == VDIR) { | | 481 | if (pn->pn_va.va_type == VDIR) { |
482 | rv = EISDIR; | | 482 | rv = EISDIR; |
483 | goto farout; | | 483 | goto farout; |
484 | } | | 484 | } |
485 | | | 485 | |
486 | /* check that a lazyopen didn't fail */ | | 486 | /* check that a lazyopen didn't fail */ |
487 | if (!psn->fhand_r && !psn->lazyopen_r) { | | 487 | if (!psn->fhand_r && !psn->lazyopen_r) { |
488 | rv = psn->lazyopen_err_r; | | 488 | rv = psn->lazyopen_err_r; |
489 | goto farout; | | 489 | goto farout; |
490 | } | | 490 | } |
491 | | | 491 | |
492 | /* if someone is already waiting for the lazyopen, "just" wait */ | | 492 | /* if someone is already waiting for the lazyopen, "just" wait */ |
493 | if (psn->stat & PSN_LAZYWAIT_R) { | | 493 | if (psn->stat & PSN_LAZYWAIT_R) { |
494 | struct psshfs_wait pw; | | 494 | struct psshfs_wait pw; |
495 | | | 495 | |
496 | assert(psn->lazyopen_r); | | 496 | assert(psn->lazyopen_r); |
497 | | | 497 | |
498 | pw.pw_cc = pcc; | | 498 | pw.pw_cc = pcc; |
499 | pw.pw_type = PWTYPE_READ1; | | 499 | pw.pw_type = PWTYPE_READ1; |
500 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); | | 500 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); |
501 | puffs_cc_yield(pcc); | | 501 | puffs_cc_yield(pcc); |
502 | } | | 502 | } |
503 | | | 503 | |
504 | /* if lazyopening, wait for the result */ | | 504 | /* if lazyopening, wait for the result */ |
505 | if (psn->lazyopen_r) { | | 505 | if (psn->lazyopen_r) { |
506 | psn->stat |= PSN_LAZYWAIT_R; | | 506 | psn->stat |= PSN_LAZYWAIT_R; |
507 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc); | | 507 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc); |
508 | lazyopen_rresp(pu, psn->lazyopen_r, psn, rv); | | 508 | lazyopen_rresp(pu, psn->lazyopen_r, psn, rv); |
509 | | | 509 | |
510 | /* schedule extra waiters */ | | 510 | /* schedule extra waiters */ |
511 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) | | 511 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) |
512 | if (pwp->pw_type == PWTYPE_READ1) { | | 512 | if (pwp->pw_type == PWTYPE_READ1) { |
513 | puffs_cc_schedule(pwp->pw_cc); | | 513 | puffs_cc_schedule(pwp->pw_cc); |
514 | TAILQ_REMOVE(&psn->pw, pwp, pw_entries); | | 514 | TAILQ_REMOVE(&psn->pw, pwp, pw_entries); |
515 | } | | 515 | } |
516 | psn->stat &= ~PSN_LAZYWAIT_R; | | 516 | psn->stat &= ~PSN_LAZYWAIT_R; |
517 | | | 517 | |
518 | if ((rv = psn->lazyopen_err_r) != 0) | | 518 | if ((rv = psn->lazyopen_err_r) != 0) |
519 | goto farout; | | 519 | goto farout; |
520 | } | | 520 | } |
521 | | | 521 | |
522 | /* if there is still no handle, just refuse to live with this */ | | 522 | /* if there is still no handle, just refuse to live with this */ |
523 | if (!psn->fhand_r) { | | 523 | if (!psn->fhand_r) { |
524 | rv = EINVAL; | | 524 | rv = EINVAL; |
525 | goto farout; | | 525 | goto farout; |
526 | } | | 526 | } |
527 | | | 527 | |
528 | again: | | 528 | again: |
529 | readlen = *resid; | | 529 | readlen = *resid; |
530 | psbuf_req_data(pb, SSH_FXP_READ, reqid, psn->fhand_r, psn->fhand_r_len); | | 530 | psbuf_req_data(pb, SSH_FXP_READ, reqid, psn->fhand_r, psn->fhand_r_len); |
531 | psbuf_put_8(pb, offset); | | 531 | psbuf_put_8(pb, offset); |
532 | psbuf_put_4(pb, readlen); | | 532 | psbuf_put_4(pb, readlen); |
533 | | | 533 | |
534 | /* | | 534 | /* |
535 | * Do this *after* accessing the file, the handle might not | | 535 | * Do this *after* accessing the file, the handle might not |
536 | * exist after blocking. | | 536 | * exist after blocking. |
537 | */ | | 537 | */ |
538 | if (max_reads && ++psn->readcount > max_reads) { | | 538 | if (max_reads && ++psn->readcount > max_reads) { |
539 | struct psshfs_wait pw; | | 539 | struct psshfs_wait pw; |
540 | | | 540 | |
541 | pw.pw_cc = pcc; | | 541 | pw.pw_cc = pcc; |
542 | pw.pw_type = PWTYPE_READ2; | | 542 | pw.pw_type = PWTYPE_READ2; |
543 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); | | 543 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); |
544 | puffs_cc_yield(pcc); | | 544 | puffs_cc_yield(pcc); |
545 | } | | 545 | } |
546 | | | 546 | |
547 | GETRESPONSE(pb, pctx->sshfd_data); | | 547 | GETRESPONSE(pb, pctx->sshfd_data); |
548 | | | 548 | |
549 | rv = psbuf_do_data(pb, buf, &readlen); | | 549 | rv = psbuf_do_data(pb, buf, &readlen); |
550 | if (rv == 0) { | | 550 | if (rv == 0) { |
551 | *resid -= readlen; | | 551 | *resid -= readlen; |
552 | buf += readlen; | | 552 | buf += readlen; |
553 | offset += readlen; | | 553 | offset += readlen; |
554 | } | | 554 | } |
555 | | | 555 | |
556 | out: | | 556 | out: |
557 | if (max_reads && --psn->readcount >= max_reads) { | | 557 | if (max_reads && --psn->readcount >= max_reads) { |
558 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) | | 558 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) |
559 | if (pwp->pw_type == PWTYPE_READ2) | | 559 | if (pwp->pw_type == PWTYPE_READ2) |
560 | break; | | 560 | break; |
561 | assert(pwp != NULL); | | 561 | assert(pwp != NULL); |
562 | puffs_cc_schedule(pwp->pw_cc); | | 562 | puffs_cc_schedule(pwp->pw_cc); |
563 | TAILQ_REMOVE(&psn->pw, pwp, pw_entries); | | 563 | TAILQ_REMOVE(&psn->pw, pwp, pw_entries); |
564 | } | | 564 | } |
565 | | | 565 | |
566 | if (rv == 0 && *resid > 0) { | | 566 | if (rv == 0 && *resid > 0 && readlen > 0) { |
567 | reqid = NEXTREQ(pctx); | | 567 | reqid = NEXTREQ(pctx); |
568 | psbuf_recycleout(pb); | | 568 | psbuf_recycleout(pb); |
569 | goto again; | | 569 | goto again; |
570 | } | | 570 | } |
571 | | | 571 | |
572 | farout: | | 572 | farout: |
573 | /* check if we need a lazyclose */ | | 573 | /* check if we need a lazyclose */ |
574 | if (psn->stat & PSN_HANDLECLOSE && psn->fhand_r) { | | 574 | if (psn->stat & PSN_HANDLECLOSE && psn->fhand_r) { |
575 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) | | 575 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) |
576 | if (pwp->pw_type == PWTYPE_READ1) | | 576 | if (pwp->pw_type == PWTYPE_READ1) |
577 | break; | | 577 | break; |
578 | if (pwp == NULL) | | 578 | if (pwp == NULL) |
579 | closehandles(pu, psn, HANDLE_READ); | | 579 | closehandles(pu, psn, HANDLE_READ); |
580 | } | | 580 | } |
581 | PSSHFSRETURN(rv); | | 581 | PSSHFSRETURN(rv); |
582 | } | | 582 | } |
583 | | | 583 | |
584 | /* XXX: we should getattr for size */ | | 584 | /* XXX: we should getattr for size */ |
585 | int | | 585 | int |
586 | psshfs_node_write(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf, | | 586 | psshfs_node_write(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf, |
587 | off_t offset, size_t *resid, const struct puffs_cred *cred, | | 587 | off_t offset, size_t *resid, const struct puffs_cred *cred, |
588 | int ioflag) | | 588 | int ioflag) |
589 | { | | 589 | { |
590 | PSSHFSAUTOVAR(pu); | | 590 | PSSHFSAUTOVAR(pu); |
591 | struct psshfs_wait *pwp; | | 591 | struct psshfs_wait *pwp; |
592 | struct puffs_node *pn = opc; | | 592 | struct puffs_node *pn = opc; |
593 | struct psshfs_node *psn = pn->pn_data; | | 593 | struct psshfs_node *psn = pn->pn_data; |
594 | uint32_t writelen; | | 594 | uint32_t writelen; |
595 | | | 595 | |
596 | if (pn->pn_va.va_type == VDIR) { | | 596 | if (pn->pn_va.va_type == VDIR) { |
597 | rv = EISDIR; | | 597 | rv = EISDIR; |
598 | goto out; | | 598 | goto out; |
599 | } | | 599 | } |
600 | | | 600 | |
601 | /* check that a lazyopen didn't fail */ | | 601 | /* check that a lazyopen didn't fail */ |
602 | if (!psn->fhand_w && !psn->lazyopen_w) { | | 602 | if (!psn->fhand_w && !psn->lazyopen_w) { |
603 | rv = psn->lazyopen_err_w; | | 603 | rv = psn->lazyopen_err_w; |
604 | goto out; | | 604 | goto out; |
605 | } | | 605 | } |
606 | | | 606 | |
607 | if (psn->stat & PSN_LAZYWAIT_W) { | | 607 | if (psn->stat & PSN_LAZYWAIT_W) { |
608 | struct psshfs_wait pw; | | 608 | struct psshfs_wait pw; |
609 | | | 609 | |
610 | assert(psn->lazyopen_w); | | 610 | assert(psn->lazyopen_w); |
611 | | | 611 | |
612 | pw.pw_cc = pcc; | | 612 | pw.pw_cc = pcc; |
613 | pw.pw_type = PWTYPE_WRITE; | | 613 | pw.pw_type = PWTYPE_WRITE; |
614 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); | | 614 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); |
615 | puffs_cc_yield(pcc); | | 615 | puffs_cc_yield(pcc); |
616 | } | | 616 | } |
617 | | | 617 | |
618 | /* | | 618 | /* |
619 | * If lazyopening, wait for the result. | | 619 | * If lazyopening, wait for the result. |
620 | * There can still be more than oen writer at a time in case | | 620 | * There can still be more than oen writer at a time in case |
621 | * the kernel issues write FAFs. | | 621 | * the kernel issues write FAFs. |
622 | */ | | 622 | */ |
623 | if (psn->lazyopen_w) { | | 623 | if (psn->lazyopen_w) { |
624 | psn->stat |= PSN_LAZYWAIT_W; | | 624 | psn->stat |= PSN_LAZYWAIT_W; |
625 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc); | | 625 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc); |
626 | lazyopen_wresp(pu, psn->lazyopen_w, psn, rv); | | 626 | lazyopen_wresp(pu, psn->lazyopen_w, psn, rv); |
627 | | | 627 | |
628 | /* schedule extra waiters */ | | 628 | /* schedule extra waiters */ |
629 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) | | 629 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) |
630 | if (pwp->pw_type == PWTYPE_WRITE) { | | 630 | if (pwp->pw_type == PWTYPE_WRITE) { |
631 | puffs_cc_schedule(pwp->pw_cc); | | 631 | puffs_cc_schedule(pwp->pw_cc); |
632 | TAILQ_REMOVE(&psn->pw, pwp, pw_entries); | | 632 | TAILQ_REMOVE(&psn->pw, pwp, pw_entries); |
633 | } | | 633 | } |
634 | psn->stat &= ~PSN_LAZYWAIT_W; | | 634 | psn->stat &= ~PSN_LAZYWAIT_W; |
635 | | | 635 | |
636 | if ((rv = psn->lazyopen_err_w) != 0) | | 636 | if ((rv = psn->lazyopen_err_w) != 0) |
637 | goto out; | | 637 | goto out; |
638 | } | | 638 | } |
639 | | | 639 | |
640 | if (!psn->fhand_w) { | | 640 | if (!psn->fhand_w) { |
641 | abort(); | | 641 | abort(); |
642 | rv = EINVAL; | | 642 | rv = EINVAL; |
643 | goto out; | | 643 | goto out; |
644 | } | | 644 | } |
645 | | | 645 | |
646 | writelen = *resid; | | 646 | writelen = *resid; |
647 | psbuf_req_data(pb, SSH_FXP_WRITE, reqid, psn->fhand_w,psn->fhand_w_len); | | 647 | psbuf_req_data(pb, SSH_FXP_WRITE, reqid, psn->fhand_w,psn->fhand_w_len); |
648 | psbuf_put_8(pb, offset); | | 648 | psbuf_put_8(pb, offset); |
649 | psbuf_put_data(pb, buf, writelen); | | 649 | psbuf_put_data(pb, buf, writelen); |
650 | GETRESPONSE(pb, pctx->sshfd_data); | | 650 | GETRESPONSE(pb, pctx->sshfd_data); |
651 | | | 651 | |
652 | rv = psbuf_expect_status(pb); | | 652 | rv = psbuf_expect_status(pb); |
653 | if (rv == 0) | | 653 | if (rv == 0) |
654 | *resid = 0; | | 654 | *resid = 0; |
655 | | | 655 | |
656 | if (pn->pn_va.va_size < (uint64_t)offset + writelen) | | 656 | if (pn->pn_va.va_size < (uint64_t)offset + writelen) |
657 | pn->pn_va.va_size = offset + writelen; | | 657 | pn->pn_va.va_size = offset + writelen; |
658 | | | 658 | |
659 | out: | | 659 | out: |
660 | /* check if we need a lazyclose */ | | 660 | /* check if we need a lazyclose */ |
661 | if (psn->stat & PSN_HANDLECLOSE && psn->fhand_w) { | | 661 | if (psn->stat & PSN_HANDLECLOSE && psn->fhand_w) { |
662 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) | | 662 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) |
663 | if (pwp->pw_type == PWTYPE_WRITE) | | 663 | if (pwp->pw_type == PWTYPE_WRITE) |
664 | break; | | 664 | break; |
665 | if (pwp == NULL) | | 665 | if (pwp == NULL) |
666 | closehandles(pu, psn, HANDLE_WRITE); | | 666 | closehandles(pu, psn, HANDLE_WRITE); |
667 | } | | 667 | } |
668 | PSSHFSRETURN(rv); | | 668 | PSSHFSRETURN(rv); |
669 | } | | 669 | } |
670 | | | 670 | |
671 | int | | 671 | int |
672 | psshfs_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc, | | 672 | psshfs_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc, |
673 | const struct puffs_cred *cred, char *linkvalue, size_t *linklen) | | 673 | const struct puffs_cred *cred, char *linkvalue, size_t *linklen) |
674 | { | | 674 | { |
675 | PSSHFSAUTOVAR(pu); | | 675 | PSSHFSAUTOVAR(pu); |
676 | struct puffs_node *pn = opc; | | 676 | struct puffs_node *pn = opc; |
677 | struct psshfs_node *psn = pn->pn_data; | | 677 | struct psshfs_node *psn = pn->pn_data; |
678 | uint32_t count; | | 678 | uint32_t count; |
679 | | | 679 | |
680 | if (pctx->protover < 3) { | | 680 | if (pctx->protover < 3) { |
681 | rv = EOPNOTSUPP; | | 681 | rv = EOPNOTSUPP; |
682 | goto out; | | 682 | goto out; |
683 | } | | 683 | } |
684 | | | 684 | |
685 | /* | | 685 | /* |
686 | * check if we can use a cached version | | 686 | * check if we can use a cached version |
687 | * | | 687 | * |
688 | * XXX: we might end up reading the same link multiple times | | 688 | * XXX: we might end up reading the same link multiple times |
689 | * from the server if we get many requests at once, but that's | | 689 | * from the server if we get many requests at once, but that's |
690 | * quite harmless as this routine is reentrant. | | 690 | * quite harmless as this routine is reentrant. |
691 | */ | | 691 | */ |
692 | if (psn->symlink && !REFRESHTIMEOUT(pctx, time(NULL) - psn->slread)) | | 692 | if (psn->symlink && !REFRESHTIMEOUT(pctx, time(NULL) - psn->slread)) |
693 | goto copy; | | 693 | goto copy; |
694 | | | 694 | |
695 | if (psn->symlink) { | | 695 | if (psn->symlink) { |
696 | free(psn->symlink); | | 696 | free(psn->symlink); |
697 | psn->symlink = NULL; | | 697 | psn->symlink = NULL; |
698 | psn->slread = 0; | | 698 | psn->slread = 0; |
699 | } | | 699 | } |
700 | | | 700 | |
701 | psbuf_req_str(pb, SSH_FXP_READLINK, reqid, PNPATH(pn)); | | 701 | psbuf_req_str(pb, SSH_FXP_READLINK, reqid, PNPATH(pn)); |
702 | GETRESPONSE(pb, pctx->sshfd); | | 702 | GETRESPONSE(pb, pctx->sshfd); |
703 | | | 703 | |
704 | rv = psbuf_expect_name(pb, &count); | | 704 | rv = psbuf_expect_name(pb, &count); |
705 | if (rv) | | 705 | if (rv) |
706 | goto out; | | 706 | goto out; |
707 | if (count != 1) { | | 707 | if (count != 1) { |
708 | rv = EPROTO; | | 708 | rv = EPROTO; |
709 | goto out; | | 709 | goto out; |
710 | } | | 710 | } |
711 | | | 711 | |
712 | rv = psbuf_get_str(pb, &psn->symlink, NULL); | | 712 | rv = psbuf_get_str(pb, &psn->symlink, NULL); |
713 | if (rv) | | 713 | if (rv) |
714 | goto out; | | 714 | goto out; |
715 | psn->slread = time(NULL); | | 715 | psn->slread = time(NULL); |
716 | | | 716 | |
717 | copy: | | 717 | copy: |
718 | *linklen = strlen(psn->symlink); | | 718 | *linklen = strlen(psn->symlink); |
719 | (void) memcpy(linkvalue, psn->symlink, *linklen); | | 719 | (void) memcpy(linkvalue, psn->symlink, *linklen); |
720 | | | 720 | |
721 | out: | | 721 | out: |
722 | PSSHFSRETURN(rv); | | 722 | PSSHFSRETURN(rv); |
723 | } | | 723 | } |
724 | | | 724 | |
725 | static int | | 725 | static int |
726 | doremove(struct puffs_usermount *pu, struct puffs_node *pn_dir, | | 726 | doremove(struct puffs_usermount *pu, struct puffs_node *pn_dir, |
727 | struct puffs_node *pn, const char *name) | | 727 | struct puffs_node *pn, const char *name) |
728 | { | | 728 | { |
729 | PSSHFSAUTOVAR(pu); | | 729 | PSSHFSAUTOVAR(pu); |
730 | int op; | | 730 | int op; |
731 | | | 731 | |
732 | if (pn->pn_va.va_type == VDIR) | | 732 | if (pn->pn_va.va_type == VDIR) |
733 | op = SSH_FXP_RMDIR; | | 733 | op = SSH_FXP_RMDIR; |
734 | else | | 734 | else |
735 | op = SSH_FXP_REMOVE; | | 735 | op = SSH_FXP_REMOVE; |
736 | | | 736 | |
737 | psbuf_req_str(pb, op, reqid, PNPATH(pn)); | | 737 | psbuf_req_str(pb, op, reqid, PNPATH(pn)); |
738 | GETRESPONSE(pb, pctx->sshfd); | | 738 | GETRESPONSE(pb, pctx->sshfd); |
739 | | | 739 | |
740 | rv = psbuf_expect_status(pb); | | 740 | rv = psbuf_expect_status(pb); |
741 | if (rv == 0) | | 741 | if (rv == 0) |
742 | nukenode(pn, name, 0); | | 742 | nukenode(pn, name, 0); |
743 | | | 743 | |
744 | out: | | 744 | out: |
745 | PSSHFSRETURN(rv); | | 745 | PSSHFSRETURN(rv); |
746 | } | | 746 | } |
747 | | | 747 | |
748 | int | | 748 | int |
749 | psshfs_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc, | | 749 | psshfs_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc, |
750 | puffs_cookie_t targ, const struct puffs_cn *pcn) | | 750 | puffs_cookie_t targ, const struct puffs_cn *pcn) |
751 | { | | 751 | { |
752 | struct puffs_node *pn_targ = targ; | | 752 | struct puffs_node *pn_targ = targ; |
753 | int rv; | | 753 | int rv; |
754 | | | 754 | |
755 | assert(pn_targ->pn_va.va_type != VDIR); | | 755 | assert(pn_targ->pn_va.va_type != VDIR); |
756 | | | 756 | |
757 | rv = doremove(pu, opc, targ, pcn->pcn_name); | | 757 | rv = doremove(pu, opc, targ, pcn->pcn_name); |
758 | if (rv == 0) | | 758 | if (rv == 0) |
759 | puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); | | 759 | puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); |
760 | | | 760 | |
761 | return rv; | | 761 | return rv; |
762 | } | | 762 | } |
763 | | | 763 | |
764 | int | | 764 | int |
765 | psshfs_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc, | | 765 | psshfs_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc, |
766 | puffs_cookie_t targ, const struct puffs_cn *pcn) | | 766 | puffs_cookie_t targ, const struct puffs_cn *pcn) |
767 | { | | 767 | { |
768 | struct puffs_node *pn_targ = targ; | | 768 | struct puffs_node *pn_targ = targ; |
769 | int rv; | | 769 | int rv; |
770 | | | 770 | |
771 | assert(pn_targ->pn_va.va_type == VDIR); | | 771 | assert(pn_targ->pn_va.va_type == VDIR); |
772 | | | 772 | |
773 | rv = doremove(pu, opc, targ, pcn->pcn_name); | | 773 | rv = doremove(pu, opc, targ, pcn->pcn_name); |
774 | if (rv == 0) | | 774 | if (rv == 0) |
775 | puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); | | 775 | puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); |
776 | | | 776 | |
777 | return rv; | | 777 | return rv; |
778 | } | | 778 | } |
779 | | | 779 | |
780 | int | | 780 | int |
781 | psshfs_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc, | | 781 | psshfs_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc, |
782 | struct puffs_newinfo *pni, const struct puffs_cn *pcn, | | 782 | struct puffs_newinfo *pni, const struct puffs_cn *pcn, |
783 | const struct vattr *va) | | 783 | const struct vattr *va) |
784 | { | | 784 | { |
785 | PSSHFSAUTOVAR(pu); | | 785 | PSSHFSAUTOVAR(pu); |
786 | struct puffs_node *pn = opc; | | 786 | struct puffs_node *pn = opc; |
787 | struct puffs_node *pn_new; | | 787 | struct puffs_node *pn_new; |
788 | | | 788 | |
789 | psbuf_req_str(pb, SSH_FXP_MKDIR, reqid, PCNPATH(pcn)); | | 789 | psbuf_req_str(pb, SSH_FXP_MKDIR, reqid, PCNPATH(pcn)); |
790 | psbuf_put_vattr(pb, va, pctx); | | 790 | psbuf_put_vattr(pb, va, pctx); |
791 | GETRESPONSE(pb, pctx->sshfd); | | 791 | GETRESPONSE(pb, pctx->sshfd); |
792 | | | 792 | |
793 | rv = psbuf_expect_status(pb); | | 793 | rv = psbuf_expect_status(pb); |
794 | if (rv) | | 794 | if (rv) |
795 | goto out; | | 795 | goto out; |
796 | | | 796 | |
797 | pn_new = allocnode(pu, pn, pcn->pcn_name, va); | | 797 | pn_new = allocnode(pu, pn, pcn->pcn_name, va); |
798 | if (pn_new) { | | 798 | if (pn_new) { |
799 | puffs_newinfo_setcookie(pni, pn_new); | | 799 | puffs_newinfo_setcookie(pni, pn_new); |
800 | } else { | | 800 | } else { |
801 | struct puffs_framebuf *pb2 = psbuf_makeout(); | | 801 | struct puffs_framebuf *pb2 = psbuf_makeout(); |
802 | reqid = NEXTREQ(pctx); | | 802 | reqid = NEXTREQ(pctx); |
803 | psbuf_recycleout(pb2); | | 803 | psbuf_recycleout(pb2); |
804 | psbuf_req_str(pb2, SSH_FXP_RMDIR, reqid, PCNPATH(pcn)); | | 804 | psbuf_req_str(pb2, SSH_FXP_RMDIR, reqid, PCNPATH(pcn)); |
805 | JUSTSEND(pb2, pctx->sshfd); | | 805 | JUSTSEND(pb2, pctx->sshfd); |
806 | rv = ENOMEM; | | 806 | rv = ENOMEM; |
807 | } | | 807 | } |
808 | | | 808 | |
809 | out: | | 809 | out: |
810 | PSSHFSRETURN(rv); | | 810 | PSSHFSRETURN(rv); |
811 | } | | 811 | } |
812 | | | 812 | |
813 | int | | 813 | int |
814 | psshfs_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc, | | 814 | psshfs_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc, |
815 | struct puffs_newinfo *pni, const struct puffs_cn *pcn, | | 815 | struct puffs_newinfo *pni, const struct puffs_cn *pcn, |
816 | const struct vattr *va, const char *link_target) | | 816 | const struct vattr *va, const char *link_target) |
817 | { | | 817 | { |
818 | PSSHFSAUTOVAR(pu); | | 818 | PSSHFSAUTOVAR(pu); |
819 | struct puffs_node *pn = opc; | | 819 | struct puffs_node *pn = opc; |
820 | struct puffs_node *pn_new; | | 820 | struct puffs_node *pn_new; |
821 | | | 821 | |
822 | if (pctx->protover < 3) { | | 822 | if (pctx->protover < 3) { |
823 | rv = EOPNOTSUPP; | | 823 | rv = EOPNOTSUPP; |
824 | goto out; | | 824 | goto out; |
825 | } | | 825 | } |
826 | | | 826 | |
827 | /* | | 827 | /* |
828 | * XXX: ietf says: source, target. openssh says: ietf who? | | 828 | * XXX: ietf says: source, target. openssh says: ietf who? |
829 | * Let's go with openssh and build quirk tables later if we care | | 829 | * Let's go with openssh and build quirk tables later if we care |
830 | */ | | 830 | */ |
831 | psbuf_req_str(pb, SSH_FXP_SYMLINK, reqid, link_target); | | 831 | psbuf_req_str(pb, SSH_FXP_SYMLINK, reqid, link_target); |
832 | psbuf_put_str(pb, PCNPATH(pcn)); | | 832 | psbuf_put_str(pb, PCNPATH(pcn)); |
833 | GETRESPONSE(pb, pctx->sshfd); | | 833 | GETRESPONSE(pb, pctx->sshfd); |
834 | | | 834 | |
835 | rv = psbuf_expect_status(pb); | | 835 | rv = psbuf_expect_status(pb); |
836 | if (rv) | | 836 | if (rv) |
837 | goto out; | | 837 | goto out; |
838 | | | 838 | |
839 | pn_new = allocnode(pu, pn, pcn->pcn_name, va); | | 839 | pn_new = allocnode(pu, pn, pcn->pcn_name, va); |
840 | if (pn_new) { | | 840 | if (pn_new) { |
841 | puffs_newinfo_setcookie(pni, pn_new); | | 841 | puffs_newinfo_setcookie(pni, pn_new); |
842 | } else { | | 842 | } else { |
843 | struct puffs_framebuf *pb2 = psbuf_makeout(); | | 843 | struct puffs_framebuf *pb2 = psbuf_makeout(); |
844 | reqid = NEXTREQ(pctx); | | 844 | reqid = NEXTREQ(pctx); |
845 | psbuf_recycleout(pb2); | | 845 | psbuf_recycleout(pb2); |
846 | psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn)); | | 846 | psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn)); |
847 | JUSTSEND(pb2, pctx->sshfd); | | 847 | JUSTSEND(pb2, pctx->sshfd); |
848 | rv = ENOMEM; | | 848 | rv = ENOMEM; |
849 | } | | 849 | } |
850 | | | 850 | |
851 | out: | | 851 | out: |
852 | PSSHFSRETURN(rv); | | 852 | PSSHFSRETURN(rv); |
853 | } | | 853 | } |
854 | | | 854 | |
855 | int | | 855 | int |
856 | psshfs_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc, | | 856 | psshfs_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc, |
857 | puffs_cookie_t src, const struct puffs_cn *pcn_src, | | 857 | puffs_cookie_t src, const struct puffs_cn *pcn_src, |
858 | puffs_cookie_t targ_dir, puffs_cookie_t targ, | | 858 | puffs_cookie_t targ_dir, puffs_cookie_t targ, |
859 | const struct puffs_cn *pcn_targ) | | 859 | const struct puffs_cn *pcn_targ) |
860 | { | | 860 | { |
861 | PSSHFSAUTOVAR(pu); | | 861 | PSSHFSAUTOVAR(pu); |
862 | struct puffs_node *pn_sf = src; | | 862 | struct puffs_node *pn_sf = src; |
863 | struct puffs_node *pn_td = targ_dir, *pn_tf = targ; | | 863 | struct puffs_node *pn_td = targ_dir, *pn_tf = targ; |
864 | struct psshfs_node *psn_src = pn_sf->pn_data; | | 864 | struct psshfs_node *psn_src = pn_sf->pn_data; |
865 | struct psshfs_node *psn_targdir = pn_td->pn_data; | | 865 | struct psshfs_node *psn_targdir = pn_td->pn_data; |
866 | | | 866 | |
867 | if (pctx->protover < 2) { | | 867 | if (pctx->protover < 2) { |
868 | rv = EOPNOTSUPP; | | 868 | rv = EOPNOTSUPP; |
869 | goto out; | | 869 | goto out; |
870 | } | | 870 | } |
871 | | | 871 | |
872 | if (pn_tf) { | | 872 | if (pn_tf) { |
873 | rv = doremove(pu, targ_dir, pn_tf, pcn_targ->pcn_name); | | 873 | rv = doremove(pu, targ_dir, pn_tf, pcn_targ->pcn_name); |
874 | if (rv) | | 874 | if (rv) |
875 | goto out; | | 875 | goto out; |
876 | } | | 876 | } |
877 | | | 877 | |
878 | psbuf_req_str(pb, SSH_FXP_RENAME, reqid, PCNPATH(pcn_src)); | | 878 | psbuf_req_str(pb, SSH_FXP_RENAME, reqid, PCNPATH(pcn_src)); |
879 | psbuf_put_str(pb, PCNPATH(pcn_targ)); | | 879 | psbuf_put_str(pb, PCNPATH(pcn_targ)); |
880 | GETRESPONSE(pb, pctx->sshfd); | | 880 | GETRESPONSE(pb, pctx->sshfd); |
881 | | | 881 | |
882 | rv = psbuf_expect_status(pb); | | 882 | rv = psbuf_expect_status(pb); |
883 | if (rv == 0) { | | 883 | if (rv == 0) { |
884 | struct psshfs_dir *pd; | | 884 | struct psshfs_dir *pd; |
885 | | | 885 | |
886 | /* | | 886 | /* |
887 | * XXX: interfaces didn't quite work with rename.. | | 887 | * XXX: interfaces didn't quite work with rename.. |
888 | * the song remains the same. go figure .. ;) | | 888 | * the song remains the same. go figure .. ;) |
889 | */ | | 889 | */ |
890 | nukenode(pn_sf, pcn_src->pcn_name, 0); | | 890 | nukenode(pn_sf, pcn_src->pcn_name, 0); |
891 | pd = direnter(pn_td, pcn_targ->pcn_name); | | 891 | pd = direnter(pn_td, pcn_targ->pcn_name); |
892 | pd->entry = pn_sf; | | 892 | pd->entry = pn_sf; |
893 | puffs_setvattr(&pd->va, &pn_sf->pn_va); | | 893 | puffs_setvattr(&pd->va, &pn_sf->pn_va); |
894 | | | 894 | |
895 | if (opc != targ_dir) { | | 895 | if (opc != targ_dir) { |
896 | psn_targdir->childcount++; | | 896 | psn_targdir->childcount++; |
897 | psn_src->parent = pn_td; | | 897 | psn_src->parent = pn_td; |
898 | if (pn_sf->pn_va.va_type == VDIR) | | 898 | if (pn_sf->pn_va.va_type == VDIR) |
899 | pn_td->pn_va.va_nlink++; | | 899 | pn_td->pn_va.va_nlink++; |
900 | } | | 900 | } |
901 | } | | 901 | } |
902 | | | 902 | |
903 | out: | | 903 | out: |
904 | PSSHFSRETURN(rv); | | 904 | PSSHFSRETURN(rv); |
905 | } | | 905 | } |
906 | | | 906 | |
907 | /* | | 907 | /* |
908 | * So this file system happened to be written in such a way that | | 908 | * So this file system happened to be written in such a way that |
909 | * lookup for ".." is hard if we lose the in-memory node. We'd | | 909 | * lookup for ".." is hard if we lose the in-memory node. We'd |
910 | * need to recreate the entire directory structure from the root | | 910 | * need to recreate the entire directory structure from the root |
911 | * node up to the ".." node we're looking up. | | 911 | * node up to the ".." node we're looking up. |
912 | * | | 912 | * |
913 | * And since our entire fs structure is purely fictional (i.e. it's | | 913 | * And since our entire fs structure is purely fictional (i.e. it's |
914 | * only in-memory, not fetchable from the server), the easiest way | | 914 | * only in-memory, not fetchable from the server), the easiest way |
915 | * to deal with it is to not allow nodes with children to be | | 915 | * to deal with it is to not allow nodes with children to be |
916 | * reclaimed. | | 916 | * reclaimed. |
917 | * | | 917 | * |
918 | * If a node with children is being attempted to be reclaimed, we | | 918 | * If a node with children is being attempted to be reclaimed, we |
919 | * just mark it "reclaimed" but leave it as is until all its children | | 919 | * just mark it "reclaimed" but leave it as is until all its children |
920 | * have been reclaimed. If a lookup for that node is done meanwhile, | | 920 | * have been reclaimed. If a lookup for that node is done meanwhile, |
921 | * it will be found by lookup() and we just remove the "reclaimed" | | 921 | * it will be found by lookup() and we just remove the "reclaimed" |
922 | * bit. | | 922 | * bit. |
923 | */ | | 923 | */ |
924 | int | | 924 | int |
925 | psshfs_node_reclaim(struct puffs_usermount *pu, puffs_cookie_t opc) | | 925 | psshfs_node_reclaim(struct puffs_usermount *pu, puffs_cookie_t opc) |
926 | { | | 926 | { |
927 | struct puffs_node *pn = opc, *pn_next, *pn_root; | | 927 | struct puffs_node *pn = opc, *pn_next, *pn_root; |
928 | struct psshfs_node *psn = pn->pn_data; | | 928 | struct psshfs_node *psn = pn->pn_data; |
929 | | | 929 | |
930 | /* | | 930 | /* |
931 | * don't reclaim if we have file handle issued, otherwise | | 931 | * don't reclaim if we have file handle issued, otherwise |
932 | * we can't do fhtonode | | 932 | * we can't do fhtonode |
933 | */ | | 933 | */ |
934 | if (psn->stat & PSN_HASFH) | | 934 | if (psn->stat & PSN_HASFH) |
935 | return 0; | | 935 | return 0; |
936 | | | 936 | |
937 | psn->stat |= PSN_RECLAIMED; | | 937 | psn->stat |= PSN_RECLAIMED; |
938 | pn_root = puffs_getroot(pu); | | 938 | pn_root = puffs_getroot(pu); |
939 | for (; pn != pn_root; pn = pn_next) { | | 939 | for (; pn != pn_root; pn = pn_next) { |
940 | psn = pn->pn_data; | | 940 | psn = pn->pn_data; |
941 | if ((psn->stat & PSN_RECLAIMED) == 0 || psn->childcount != 0) | | 941 | if ((psn->stat & PSN_RECLAIMED) == 0 || psn->childcount != 0) |
942 | break; | | 942 | break; |
943 | | | 943 | |
944 | pn_next = psn->parent; | | 944 | pn_next = psn->parent; |
945 | doreclaim(pn); | | 945 | doreclaim(pn); |
946 | } | | 946 | } |
947 | | | 947 | |
948 | return 0; | | 948 | return 0; |
949 | } | | 949 | } |