| @@ -1,887 +1,888 @@ | | | @@ -1,887 +1,888 @@ |
1 | /* $NetBSD: node.c,v 1.61 2010/04/01 02:34:09 pooka Exp $ */ | | 1 | /* $NetBSD: node.c,v 1.62 2010/10/29 16:13:51 pooka 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.61 2010/04/01 02:34:09 pooka Exp $"); | | 30 | __RCSID("$NetBSD: node.c,v 1.62 2010/10/29 16:13:51 pooka 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 | PSSHFSAUTOVAR(pu); | | 136 | PSSHFSAUTOVAR(pu); |
137 | struct vattr kludgeva; | | 137 | struct vattr kludgeva; |
138 | struct puffs_node *pn = opc; | | 138 | struct puffs_node *pn = opc; |
139 | | | 139 | |
140 | psbuf_req_str(pb, SSH_FXP_SETSTAT, reqid, PNPATH(pn)); | | 140 | psbuf_req_str(pb, SSH_FXP_SETSTAT, reqid, PNPATH(pn)); |
141 | | | 141 | |
142 | memcpy(&kludgeva, va, sizeof(struct vattr)); | | 142 | memcpy(&kludgeva, va, sizeof(struct vattr)); |
143 | | | 143 | |
144 | /* XXX: kludge due to openssh server implementation */ | | 144 | /* XXX: kludge due to openssh server implementation */ |
145 | if (va->va_atime.tv_sec != PUFFS_VNOVAL | | 145 | if (va->va_atime.tv_sec != PUFFS_VNOVAL |
146 | && va->va_mtime.tv_sec == PUFFS_VNOVAL) { | | 146 | && va->va_mtime.tv_sec == PUFFS_VNOVAL) { |
147 | if (pn->pn_va.va_mtime.tv_sec != PUFFS_VNOVAL) | | 147 | if (pn->pn_va.va_mtime.tv_sec != PUFFS_VNOVAL) |
148 | kludgeva.va_mtime.tv_sec = pn->pn_va.va_mtime.tv_sec; | | 148 | kludgeva.va_mtime.tv_sec = pn->pn_va.va_mtime.tv_sec; |
149 | else | | 149 | else |
150 | kludgeva.va_mtime.tv_sec = va->va_atime.tv_sec; | | 150 | kludgeva.va_mtime.tv_sec = va->va_atime.tv_sec; |
151 | } | | 151 | } |
152 | if (va->va_mtime.tv_sec != PUFFS_VNOVAL | | 152 | if (va->va_mtime.tv_sec != PUFFS_VNOVAL |
153 | && va->va_atime.tv_sec == PUFFS_VNOVAL) { | | 153 | && va->va_atime.tv_sec == PUFFS_VNOVAL) { |
154 | if (pn->pn_va.va_atime.tv_sec != PUFFS_VNOVAL) | | 154 | if (pn->pn_va.va_atime.tv_sec != PUFFS_VNOVAL) |
155 | kludgeva.va_atime.tv_sec = pn->pn_va.va_atime.tv_sec; | | 155 | kludgeva.va_atime.tv_sec = pn->pn_va.va_atime.tv_sec; |
156 | else | | 156 | else |
157 | kludgeva.va_atime.tv_sec = va->va_mtime.tv_sec; | | 157 | kludgeva.va_atime.tv_sec = va->va_mtime.tv_sec; |
158 | } | | 158 | } |
159 | | | 159 | |
160 | psbuf_put_vattr(pb, &kludgeva, pctx); | | 160 | psbuf_put_vattr(pb, &kludgeva, pctx); |
161 | GETRESPONSE(pb, pctx->sshfd); | | 161 | GETRESPONSE(pb, pctx->sshfd); |
162 | | | 162 | |
163 | rv = psbuf_expect_status(pb); | | 163 | rv = psbuf_expect_status(pb); |
164 | if (rv == 0) | | 164 | if (rv == 0) |
165 | puffs_setvattr(&pn->pn_va, &kludgeva); | | 165 | puffs_setvattr(&pn->pn_va, &kludgeva); |
166 | | | 166 | |
167 | out: | | 167 | out: |
168 | PSSHFSRETURN(rv); | | 168 | PSSHFSRETURN(rv); |
169 | } | | 169 | } |
170 | | | 170 | |
171 | int | | 171 | int |
172 | psshfs_node_create(struct puffs_usermount *pu, puffs_cookie_t opc, | | 172 | psshfs_node_create(struct puffs_usermount *pu, puffs_cookie_t opc, |
173 | struct puffs_newinfo *pni, const struct puffs_cn *pcn, | | 173 | struct puffs_newinfo *pni, const struct puffs_cn *pcn, |
174 | const struct vattr *va) | | 174 | const struct vattr *va) |
175 | { | | 175 | { |
176 | PSSHFSAUTOVAR(pu); | | 176 | PSSHFSAUTOVAR(pu); |
177 | struct puffs_node *pn = opc; | | 177 | struct puffs_node *pn = opc; |
178 | struct puffs_node *pn_new; | | 178 | struct puffs_node *pn_new; |
179 | char *fhand = NULL; | | 179 | char *fhand = NULL; |
180 | uint32_t fhandlen; | | 180 | uint32_t fhandlen; |
181 | | | 181 | |
182 | /* Create node on server first */ | | 182 | /* Create node on server first */ |
183 | psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PCNPATH(pcn)); | | 183 | psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PCNPATH(pcn)); |
184 | psbuf_put_4(pb, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC); | | 184 | psbuf_put_4(pb, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC); |
185 | psbuf_put_vattr(pb, va, pctx); | | 185 | psbuf_put_vattr(pb, va, pctx); |
186 | GETRESPONSE(pb, pctx->sshfd); | | 186 | GETRESPONSE(pb, pctx->sshfd); |
187 | rv = psbuf_expect_handle(pb, &fhand, &fhandlen); | | 187 | rv = psbuf_expect_handle(pb, &fhand, &fhandlen); |
188 | if (rv) | | 188 | if (rv) |
189 | goto out; | | 189 | goto out; |
190 | | | 190 | |
191 | /* | | 191 | /* |
192 | * Do *not* create the local node before getting a response | | 192 | * Do *not* create the local node before getting a response |
193 | * from the server. Otherwise we might screw up consistency, | | 193 | * from the server. Otherwise we might screw up consistency, |
194 | * namely that the node can be looked up before create has | | 194 | * namely that the node can be looked up before create has |
195 | * returned (mind you, the kernel will unlock the directory | | 195 | * returned (mind you, the kernel will unlock the directory |
196 | * before the create call from userspace returns). | | 196 | * before the create call from userspace returns). |
197 | */ | | 197 | */ |
198 | pn_new = allocnode(pu, pn, pcn->pcn_name, va); | | 198 | pn_new = allocnode(pu, pn, pcn->pcn_name, va); |
199 | if (!pn_new) { | | 199 | if (!pn_new) { |
200 | struct puffs_framebuf *pb2 = psbuf_makeout(); | | 200 | struct puffs_framebuf *pb2 = psbuf_makeout(); |
201 | reqid = NEXTREQ(pctx); | | 201 | reqid = NEXTREQ(pctx); |
202 | psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn)); | | 202 | psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn)); |
203 | JUSTSEND(pb2, pctx->sshfd); | | 203 | JUSTSEND(pb2, pctx->sshfd); |
204 | rv = ENOMEM; | | 204 | rv = ENOMEM; |
205 | } | | 205 | } |
206 | | | 206 | |
207 | if (pn_new) | | 207 | if (pn_new) |
208 | puffs_newinfo_setcookie(pni, pn_new); | | 208 | puffs_newinfo_setcookie(pni, pn_new); |
209 | | | 209 | |
210 | reqid = NEXTREQ(pctx); | | 210 | reqid = NEXTREQ(pctx); |
211 | psbuf_recycleout(pb); | | 211 | psbuf_recycleout(pb); |
212 | psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen); | | 212 | psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen); |
213 | JUSTSEND(pb, pctx->sshfd); | | 213 | JUSTSEND(pb, pctx->sshfd); |
214 | free(fhand); | | 214 | free(fhand); |
215 | return rv; | | 215 | return rv; |
216 | | | 216 | |
217 | out: | | 217 | out: |
218 | free(fhand); | | 218 | free(fhand); |
219 | PSSHFSRETURN(rv); | | 219 | PSSHFSRETURN(rv); |
220 | } | | 220 | } |
221 | | | 221 | |
222 | /* | | 222 | /* |
223 | * Open a file handle. This is used for read and write. We do not | | 223 | * Open a file handle. This is used for read and write. We do not |
224 | * wait here for the success or failure of this operation. This is | | 224 | * wait here for the success or failure of this operation. This is |
225 | * because otherwise opening and closing file handles would block | | 225 | * because otherwise opening and closing file handles would block |
226 | * reading potentially cached information. Rather, we defer the wait | | 226 | * reading potentially cached information. Rather, we defer the wait |
227 | * to read/write and therefore allow cached access without a wait. | | 227 | * to read/write and therefore allow cached access without a wait. |
228 | * | | 228 | * |
229 | * If we have not yet succesfully opened a type of handle, we do wait | | 229 | * If we have not yet succesfully opened a type of handle, we do wait |
230 | * here. Also, if a lazy open fails, we revert back to the same | | 230 | * here. Also, if a lazy open fails, we revert back to the same |
231 | * state of waiting. | | 231 | * state of waiting. |
232 | */ | | 232 | */ |
233 | int | | 233 | int |
234 | psshfs_node_open(struct puffs_usermount *pu, puffs_cookie_t opc, int mode, | | 234 | psshfs_node_open(struct puffs_usermount *pu, puffs_cookie_t opc, int mode, |
235 | const struct puffs_cred *pcr) | | 235 | const struct puffs_cred *pcr) |
236 | { | | 236 | { |
237 | struct puffs_cc *pcc = puffs_cc_getcc(pu); | | 237 | struct puffs_cc *pcc = puffs_cc_getcc(pu); |
238 | struct psshfs_ctx *pctx = puffs_getspecific(pu); | | 238 | struct psshfs_ctx *pctx = puffs_getspecific(pu); |
239 | struct puffs_framebuf *pb, *pb2; | | 239 | struct puffs_framebuf *pb, *pb2; |
240 | struct vattr va; | | 240 | struct vattr va; |
241 | struct puffs_node *pn = opc; | | 241 | struct puffs_node *pn = opc; |
242 | struct psshfs_node *psn = pn->pn_data; | | 242 | struct psshfs_node *psn = pn->pn_data; |
243 | uint32_t reqid; | | 243 | uint32_t reqid; |
244 | int didread, didwrite; | | 244 | int didread, didwrite; |
245 | int rv = 0; | | 245 | int rv = 0; |
246 | | | 246 | |
247 | if (pn->pn_va.va_type == VDIR) | | 247 | if (pn->pn_va.va_type == VDIR) |
248 | return 0; | | 248 | return 0; |
249 | | | 249 | |
250 | puffs_setback(pcc, PUFFS_SETBACK_INACT_N1); | | 250 | puffs_setback(pcc, PUFFS_SETBACK_INACT_N1); |
251 | puffs_vattr_null(&va); | | 251 | puffs_vattr_null(&va); |
252 | didread = didwrite = 0; | | 252 | didread = didwrite = 0; |
253 | if (mode & FREAD && psn->fhand_r == NULL && psn->lazyopen_r == NULL) { | | 253 | if (mode & FREAD && psn->fhand_r == NULL && psn->lazyopen_r == NULL) { |
254 | pb = psbuf_makeout(); | | 254 | pb = psbuf_makeout(); |
255 | | | 255 | |
256 | reqid = NEXTREQ(pctx); | | 256 | reqid = NEXTREQ(pctx); |
257 | psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PNPATH(pn)); | | 257 | psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PNPATH(pn)); |
258 | psbuf_put_4(pb, SSH_FXF_READ); | | 258 | psbuf_put_4(pb, SSH_FXF_READ); |
259 | psbuf_put_vattr(pb, &va, pctx); | | 259 | psbuf_put_vattr(pb, &va, pctx); |
260 | | | 260 | |
261 | if (puffs_framev_enqueue_cb(pu, pctx->sshfd_data, pb, | | 261 | if (puffs_framev_enqueue_cb(pu, pctx->sshfd_data, pb, |
262 | lazyopen_rresp, psn, 0) == -1) { | | 262 | lazyopen_rresp, psn, 0) == -1) { |
263 | rv = errno; | | 263 | rv = errno; |
264 | puffs_framebuf_destroy(pb); | | 264 | puffs_framebuf_destroy(pb); |
265 | goto out; | | 265 | goto out; |
266 | } | | 266 | } |
267 | | | 267 | |
268 | psn->lazyopen_r = pb; | | 268 | psn->lazyopen_r = pb; |
269 | didread = 1; | | 269 | didread = 1; |
270 | } | | 270 | } |
271 | if (mode & FWRITE && psn->fhand_w == NULL && psn->lazyopen_w == NULL) { | | 271 | if (mode & FWRITE && psn->fhand_w == NULL && psn->lazyopen_w == NULL) { |
272 | pb2 = psbuf_makeout(); | | 272 | pb2 = psbuf_makeout(); |
273 | | | 273 | |
274 | reqid = NEXTREQ(pctx); | | 274 | reqid = NEXTREQ(pctx); |
275 | psbuf_req_str(pb2, SSH_FXP_OPEN, reqid, PNPATH(pn)); | | 275 | psbuf_req_str(pb2, SSH_FXP_OPEN, reqid, PNPATH(pn)); |
276 | psbuf_put_4(pb2, SSH_FXF_WRITE); | | 276 | psbuf_put_4(pb2, SSH_FXF_WRITE); |
277 | psbuf_put_vattr(pb2, &va, pctx); | | 277 | psbuf_put_vattr(pb2, &va, pctx); |
278 | | | 278 | |
279 | if (puffs_framev_enqueue_cb(pu, pctx->sshfd_data, pb2, | | 279 | if (puffs_framev_enqueue_cb(pu, pctx->sshfd_data, pb2, |
280 | lazyopen_wresp, psn, 0) == -1) { | | 280 | lazyopen_wresp, psn, 0) == -1) { |
281 | rv = errno; | | 281 | rv = errno; |
282 | puffs_framebuf_destroy(pb2); | | 282 | puffs_framebuf_destroy(pb2); |
283 | goto out; | | 283 | goto out; |
284 | } | | 284 | } |
285 | | | 285 | |
286 | psn->lazyopen_w = pb2; | | 286 | psn->lazyopen_w = pb2; |
287 | didwrite = 1; | | 287 | didwrite = 1; |
288 | } | | 288 | } |
289 | psn->stat &= ~PSN_HANDLECLOSE; | | 289 | psn->stat &= ~PSN_HANDLECLOSE; |
290 | | | 290 | |
291 | out: | | 291 | out: |
292 | /* wait? */ | | 292 | /* wait? */ |
293 | if (didread && (psn->stat & PSN_DOLAZY_R) == 0) { | | 293 | if (didread && (psn->stat & PSN_DOLAZY_R) == 0) { |
294 | assert(psn->lazyopen_r); | | 294 | assert(psn->lazyopen_r); |
295 | | | 295 | |
296 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc); | | 296 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc); |
297 | lazyopen_rresp(pu, psn->lazyopen_r, psn, rv); | | 297 | lazyopen_rresp(pu, psn->lazyopen_r, psn, rv); |
298 | if (psn->fhand_r) { | | 298 | if (psn->fhand_r) { |
299 | psn->stat |= PSN_DOLAZY_R; | | 299 | psn->stat |= PSN_DOLAZY_R; |
300 | } else { | | 300 | } else { |
301 | if (psn->lazyopen_err_r) | | 301 | if (psn->lazyopen_err_r) |
302 | return psn->lazyopen_err_r; | | 302 | return psn->lazyopen_err_r; |
303 | return EINVAL; | | 303 | return EINVAL; |
304 | } | | 304 | } |
305 | } | | 305 | } |
306 | | | 306 | |
307 | /* wait? */ | | 307 | /* wait? */ |
308 | if (didwrite && (psn->stat & PSN_DOLAZY_W) == 0) { | | 308 | if (didwrite && (psn->stat & PSN_DOLAZY_W) == 0) { |
309 | assert(psn->lazyopen_w); | | 309 | assert(psn->lazyopen_w); |
310 | | | 310 | |
311 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc); | | 311 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc); |
312 | lazyopen_wresp(pu, psn->lazyopen_w, psn, rv); | | 312 | lazyopen_wresp(pu, psn->lazyopen_w, psn, rv); |
313 | if (psn->fhand_w) { | | 313 | if (psn->fhand_w) { |
314 | psn->stat |= PSN_DOLAZY_W; | | 314 | psn->stat |= PSN_DOLAZY_W; |
315 | } else { | | 315 | } else { |
316 | if (psn->lazyopen_err_w) | | 316 | if (psn->lazyopen_err_w) |
317 | return psn->lazyopen_err_w; | | 317 | return psn->lazyopen_err_w; |
318 | return EINVAL; | | 318 | return EINVAL; |
319 | } | | 319 | } |
320 | } | | 320 | } |
321 | | | 321 | |
322 | return rv; | | 322 | return rv; |
323 | } | | 323 | } |
324 | | | 324 | |
325 | int | | 325 | int |
326 | psshfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc) | | 326 | psshfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc) |
327 | { | | 327 | { |
328 | struct puffs_node *pn = opc; | | 328 | struct puffs_node *pn = opc; |
329 | | | 329 | |
330 | closehandles(pu, pn->pn_data, HANDLE_READ | HANDLE_WRITE); | | 330 | closehandles(pu, pn->pn_data, HANDLE_READ | HANDLE_WRITE); |
331 | return 0; | | 331 | return 0; |
332 | } | | 332 | } |
333 | | | 333 | |
334 | int | | 334 | int |
335 | psshfs_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc, | | 335 | psshfs_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc, |
336 | struct dirent *dent, off_t *readoff, size_t *reslen, | | 336 | struct dirent *dent, off_t *readoff, size_t *reslen, |
337 | const struct puffs_cred *pcr, int *eofflag, | | 337 | const struct puffs_cred *pcr, int *eofflag, |
338 | off_t *cookies, size_t *ncookies) | | 338 | off_t *cookies, size_t *ncookies) |
339 | { | | 339 | { |
340 | struct puffs_cc *pcc = puffs_cc_getcc(pu); | | 340 | struct puffs_cc *pcc = puffs_cc_getcc(pu); |
341 | struct psshfs_ctx *pctx = puffs_getspecific(pu); | | 341 | struct psshfs_ctx *pctx = puffs_getspecific(pu); |
342 | struct puffs_node *pn = opc; | | 342 | struct puffs_node *pn = opc; |
343 | struct psshfs_node *psn = pn->pn_data; | | 343 | struct psshfs_node *psn = pn->pn_data; |
344 | struct psshfs_dir *pd; | | 344 | struct psshfs_dir *pd; |
345 | size_t i; | | 345 | size_t i; |
346 | int rv, set_readdir; | | 346 | int rv, set_readdir; |
347 | | | 347 | |
348 | restart: | | 348 | restart: |
349 | if (psn->stat & PSN_READDIR) { | | 349 | if (psn->stat & PSN_READDIR) { |
350 | struct psshfs_wait pw; | | 350 | struct psshfs_wait pw; |
351 | | | 351 | |
352 | set_readdir = 0; | | 352 | set_readdir = 0; |
353 | pw.pw_cc = pcc; | | 353 | pw.pw_cc = pcc; |
354 | pw.pw_type = PWTYPE_READDIR; | | 354 | pw.pw_type = PWTYPE_READDIR; |
355 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); | | 355 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); |
356 | puffs_cc_yield(pcc); | | 356 | puffs_cc_yield(pcc); |
357 | goto restart; | | 357 | goto restart; |
358 | } else { | | 358 | } else { |
359 | psn->stat |= PSN_READDIR; | | 359 | psn->stat |= PSN_READDIR; |
360 | set_readdir = 1; | | 360 | set_readdir = 1; |
361 | } | | 361 | } |
362 | | | 362 | |
363 | *ncookies = 0; | | 363 | *ncookies = 0; |
364 | rv = sftp_readdir(pu, pctx, pn); | | 364 | rv = sftp_readdir(pu, pctx, pn); |
365 | if (rv) | | 365 | if (rv) { |
366 | goto out; | | 366 | goto out; |
| | | 367 | } |
367 | | | 368 | |
368 | /* find next dirent */ | | 369 | /* find next dirent */ |
369 | for (i = *readoff;;i++) { | | 370 | for (i = *readoff;;i++) { |
370 | if (i >= psn->dentnext) | | 371 | if (i >= psn->dentnext) |
371 | goto out; | | 372 | goto out; |
372 | pd = &psn->dir[i]; | | 373 | pd = &psn->dir[i]; |
373 | if (pd->valid) | | 374 | if (pd->valid) |
374 | break; | | 375 | break; |
375 | } | | 376 | } |
376 | | | 377 | |
377 | for (;;) { | | 378 | for (;;) { |
378 | *readoff = i; | | 379 | *readoff = i; |
379 | if (!puffs_nextdent(&dent, pd->entryname, | | 380 | if (!puffs_nextdent(&dent, pd->entryname, |
380 | pd->va.va_fileid, puffs_vtype2dt(pd->va.va_type), reslen)) { | | 381 | pd->va.va_fileid, puffs_vtype2dt(pd->va.va_type), reslen)) { |
381 | rv = 0; | | 382 | rv = 0; |
382 | goto out; | | 383 | goto out; |
383 | } | | 384 | } |
384 | | | 385 | |
385 | /* find next entry, store possible nfs key */ | | 386 | /* find next entry, store possible nfs key */ |
386 | do { | | 387 | do { |
387 | if (++i >= psn->dentnext) | | 388 | if (++i >= psn->dentnext) |
388 | goto out; | | 389 | goto out; |
389 | pd = &psn->dir[i]; | | 390 | pd = &psn->dir[i]; |
390 | } while (pd->valid == 0); | | 391 | } while (pd->valid == 0); |
391 | PUFFS_STORE_DCOOKIE(cookies, ncookies, (off_t)i); | | 392 | PUFFS_STORE_DCOOKIE(cookies, ncookies, (off_t)i); |
392 | } | | 393 | } |
393 | | | 394 | |
394 | out: | | 395 | out: |
395 | if (rv == 0) { | | 396 | if (rv == 0) { |
396 | if (i >= psn->dentnext) | | 397 | if (i >= psn->dentnext) |
397 | *eofflag = 1; | | 398 | *eofflag = 1; |
398 | | | 399 | |
399 | *readoff = i; | | 400 | *readoff = i; |
400 | } | | 401 | } |
401 | | | 402 | |
402 | if (set_readdir) { | | 403 | if (set_readdir) { |
403 | struct psshfs_wait *pw; | | 404 | struct psshfs_wait *pw; |
404 | | | 405 | |
405 | /* all will likely run to completion because of cache */ | | 406 | /* all will likely run to completion because of cache */ |
406 | TAILQ_FOREACH(pw, &psn->pw, pw_entries) { | | 407 | TAILQ_FOREACH(pw, &psn->pw, pw_entries) { |
407 | assert(pw->pw_type == PWTYPE_READDIR); | | 408 | assert(pw->pw_type == PWTYPE_READDIR); |
408 | puffs_cc_schedule(pw->pw_cc); | | 409 | puffs_cc_schedule(pw->pw_cc); |
409 | TAILQ_REMOVE(&psn->pw, pw, pw_entries); | | 410 | TAILQ_REMOVE(&psn->pw, pw, pw_entries); |
410 | } | | 411 | } |
411 | | | 412 | |
412 | psn->stat &= ~PSN_READDIR; | | 413 | psn->stat &= ~PSN_READDIR; |
413 | } | | 414 | } |
414 | | | 415 | |
415 | return rv; | | 416 | return rv; |
416 | } | | 417 | } |
417 | | | 418 | |
418 | int | | 419 | int |
419 | psshfs_node_read(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf, | | 420 | psshfs_node_read(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf, |
420 | off_t offset, size_t *resid, const struct puffs_cred *pcr, | | 421 | off_t offset, size_t *resid, const struct puffs_cred *pcr, |
421 | int ioflag) | | 422 | int ioflag) |
422 | { | | 423 | { |
423 | PSSHFSAUTOVAR(pu); | | 424 | PSSHFSAUTOVAR(pu); |
424 | struct puffs_node *pn = opc; | | 425 | struct puffs_node *pn = opc; |
425 | struct psshfs_node *psn = pn->pn_data; | | 426 | struct psshfs_node *psn = pn->pn_data; |
426 | struct psshfs_wait *pwp; | | 427 | struct psshfs_wait *pwp; |
427 | uint32_t readlen; | | 428 | uint32_t readlen; |
428 | | | 429 | |
429 | if (pn->pn_va.va_type == VDIR) { | | 430 | if (pn->pn_va.va_type == VDIR) { |
430 | rv = EISDIR; | | 431 | rv = EISDIR; |
431 | goto farout; | | 432 | goto farout; |
432 | } | | 433 | } |
433 | | | 434 | |
434 | /* check that a lazyopen didn't fail */ | | 435 | /* check that a lazyopen didn't fail */ |
435 | if (!psn->fhand_r && !psn->lazyopen_r) { | | 436 | if (!psn->fhand_r && !psn->lazyopen_r) { |
436 | rv = psn->lazyopen_err_r; | | 437 | rv = psn->lazyopen_err_r; |
437 | goto farout; | | 438 | goto farout; |
438 | } | | 439 | } |
439 | | | 440 | |
440 | /* if someone is already waiting for the lazyopen, "just" wait */ | | 441 | /* if someone is already waiting for the lazyopen, "just" wait */ |
441 | if (psn->stat & PSN_LAZYWAIT_R) { | | 442 | if (psn->stat & PSN_LAZYWAIT_R) { |
442 | struct psshfs_wait pw; | | 443 | struct psshfs_wait pw; |
443 | | | 444 | |
444 | assert(psn->lazyopen_r); | | 445 | assert(psn->lazyopen_r); |
445 | | | 446 | |
446 | pw.pw_cc = pcc; | | 447 | pw.pw_cc = pcc; |
447 | pw.pw_type = PWTYPE_READ1; | | 448 | pw.pw_type = PWTYPE_READ1; |
448 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); | | 449 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); |
449 | puffs_cc_yield(pcc); | | 450 | puffs_cc_yield(pcc); |
450 | } | | 451 | } |
451 | | | 452 | |
452 | /* if lazyopening, wait for the result */ | | 453 | /* if lazyopening, wait for the result */ |
453 | if (psn->lazyopen_r) { | | 454 | if (psn->lazyopen_r) { |
454 | psn->stat |= PSN_LAZYWAIT_R; | | 455 | psn->stat |= PSN_LAZYWAIT_R; |
455 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc); | | 456 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc); |
456 | lazyopen_rresp(pu, psn->lazyopen_r, psn, rv); | | 457 | lazyopen_rresp(pu, psn->lazyopen_r, psn, rv); |
457 | | | 458 | |
458 | /* schedule extra waiters */ | | 459 | /* schedule extra waiters */ |
459 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) | | 460 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) |
460 | if (pwp->pw_type == PWTYPE_READ1) { | | 461 | if (pwp->pw_type == PWTYPE_READ1) { |
461 | puffs_cc_schedule(pwp->pw_cc); | | 462 | puffs_cc_schedule(pwp->pw_cc); |
462 | TAILQ_REMOVE(&psn->pw, pwp, pw_entries); | | 463 | TAILQ_REMOVE(&psn->pw, pwp, pw_entries); |
463 | } | | 464 | } |
464 | psn->stat &= ~PSN_LAZYWAIT_R; | | 465 | psn->stat &= ~PSN_LAZYWAIT_R; |
465 | | | 466 | |
466 | if ((rv = psn->lazyopen_err_r) != 0) | | 467 | if ((rv = psn->lazyopen_err_r) != 0) |
467 | goto farout; | | 468 | goto farout; |
468 | } | | 469 | } |
469 | | | 470 | |
470 | /* if there is still no handle, just refuse to live with this */ | | 471 | /* if there is still no handle, just refuse to live with this */ |
471 | if (!psn->fhand_r) { | | 472 | if (!psn->fhand_r) { |
472 | rv = EINVAL; | | 473 | rv = EINVAL; |
473 | goto farout; | | 474 | goto farout; |
474 | } | | 475 | } |
475 | | | 476 | |
476 | readlen = *resid; | | 477 | readlen = *resid; |
477 | psbuf_req_data(pb, SSH_FXP_READ, reqid, psn->fhand_r, psn->fhand_r_len); | | 478 | psbuf_req_data(pb, SSH_FXP_READ, reqid, psn->fhand_r, psn->fhand_r_len); |
478 | psbuf_put_8(pb, offset); | | 479 | psbuf_put_8(pb, offset); |
479 | psbuf_put_4(pb, readlen); | | 480 | psbuf_put_4(pb, readlen); |
480 | | | 481 | |
481 | /* | | 482 | /* |
482 | * Do this *after* accessing the file, the handle might not | | 483 | * Do this *after* accessing the file, the handle might not |
483 | * exist after blocking. | | 484 | * exist after blocking. |
484 | */ | | 485 | */ |
485 | if (max_reads && ++psn->readcount > max_reads) { | | 486 | if (max_reads && ++psn->readcount > max_reads) { |
486 | struct psshfs_wait pw; | | 487 | struct psshfs_wait pw; |
487 | | | 488 | |
488 | pw.pw_cc = pcc; | | 489 | pw.pw_cc = pcc; |
489 | pw.pw_type = PWTYPE_READ2; | | 490 | pw.pw_type = PWTYPE_READ2; |
490 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); | | 491 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); |
491 | puffs_cc_yield(pcc); | | 492 | puffs_cc_yield(pcc); |
492 | } | | 493 | } |
493 | | | 494 | |
494 | GETRESPONSE(pb, pctx->sshfd_data); | | 495 | GETRESPONSE(pb, pctx->sshfd_data); |
495 | | | 496 | |
496 | rv = psbuf_do_data(pb, buf, &readlen); | | 497 | rv = psbuf_do_data(pb, buf, &readlen); |
497 | if (rv == 0) | | 498 | if (rv == 0) |
498 | *resid -= readlen; | | 499 | *resid -= readlen; |
499 | | | 500 | |
500 | out: | | 501 | out: |
501 | if (max_reads && --psn->readcount >= max_reads) { | | 502 | if (max_reads && --psn->readcount >= max_reads) { |
502 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) | | 503 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) |
503 | if (pwp->pw_type == PWTYPE_READ2) | | 504 | if (pwp->pw_type == PWTYPE_READ2) |
504 | break; | | 505 | break; |
505 | assert(pwp != NULL); | | 506 | assert(pwp != NULL); |
506 | puffs_cc_schedule(pwp->pw_cc); | | 507 | puffs_cc_schedule(pwp->pw_cc); |
507 | TAILQ_REMOVE(&psn->pw, pwp, pw_entries); | | 508 | TAILQ_REMOVE(&psn->pw, pwp, pw_entries); |
508 | } | | 509 | } |
509 | | | 510 | |
510 | farout: | | 511 | farout: |
511 | /* check if we need a lazyclose */ | | 512 | /* check if we need a lazyclose */ |
512 | if (psn->stat & PSN_HANDLECLOSE && psn->fhand_r) { | | 513 | if (psn->stat & PSN_HANDLECLOSE && psn->fhand_r) { |
513 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) | | 514 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) |
514 | if (pwp->pw_type == PWTYPE_READ1) | | 515 | if (pwp->pw_type == PWTYPE_READ1) |
515 | break; | | 516 | break; |
516 | if (pwp == NULL) | | 517 | if (pwp == NULL) |
517 | closehandles(pu, psn, HANDLE_READ); | | 518 | closehandles(pu, psn, HANDLE_READ); |
518 | } | | 519 | } |
519 | PSSHFSRETURN(rv); | | 520 | PSSHFSRETURN(rv); |
520 | } | | 521 | } |
521 | | | 522 | |
522 | /* XXX: we should getattr for size */ | | 523 | /* XXX: we should getattr for size */ |
523 | int | | 524 | int |
524 | psshfs_node_write(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf, | | 525 | psshfs_node_write(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf, |
525 | off_t offset, size_t *resid, const struct puffs_cred *cred, | | 526 | off_t offset, size_t *resid, const struct puffs_cred *cred, |
526 | int ioflag) | | 527 | int ioflag) |
527 | { | | 528 | { |
528 | PSSHFSAUTOVAR(pu); | | 529 | PSSHFSAUTOVAR(pu); |
529 | struct psshfs_wait *pwp; | | 530 | struct psshfs_wait *pwp; |
530 | struct puffs_node *pn = opc; | | 531 | struct puffs_node *pn = opc; |
531 | struct psshfs_node *psn = pn->pn_data; | | 532 | struct psshfs_node *psn = pn->pn_data; |
532 | uint32_t writelen; | | 533 | uint32_t writelen; |
533 | | | 534 | |
534 | if (pn->pn_va.va_type == VDIR) { | | 535 | if (pn->pn_va.va_type == VDIR) { |
535 | rv = EISDIR; | | 536 | rv = EISDIR; |
536 | goto out; | | 537 | goto out; |
537 | } | | 538 | } |
538 | | | 539 | |
539 | /* check that a lazyopen didn't fail */ | | 540 | /* check that a lazyopen didn't fail */ |
540 | if (!psn->fhand_w && !psn->lazyopen_w) { | | 541 | if (!psn->fhand_w && !psn->lazyopen_w) { |
541 | rv = psn->lazyopen_err_w; | | 542 | rv = psn->lazyopen_err_w; |
542 | goto out; | | 543 | goto out; |
543 | } | | 544 | } |
544 | | | 545 | |
545 | if (psn->stat & PSN_LAZYWAIT_W) { | | 546 | if (psn->stat & PSN_LAZYWAIT_W) { |
546 | struct psshfs_wait pw; | | 547 | struct psshfs_wait pw; |
547 | | | 548 | |
548 | assert(psn->lazyopen_w); | | 549 | assert(psn->lazyopen_w); |
549 | | | 550 | |
550 | pw.pw_cc = pcc; | | 551 | pw.pw_cc = pcc; |
551 | pw.pw_type = PWTYPE_WRITE; | | 552 | pw.pw_type = PWTYPE_WRITE; |
552 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); | | 553 | TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); |
553 | puffs_cc_yield(pcc); | | 554 | puffs_cc_yield(pcc); |
554 | } | | 555 | } |
555 | | | 556 | |
556 | /* | | 557 | /* |
557 | * If lazyopening, wait for the result. | | 558 | * If lazyopening, wait for the result. |
558 | * There can still be more than oen writer at a time in case | | 559 | * There can still be more than oen writer at a time in case |
559 | * the kernel issues write FAFs. | | 560 | * the kernel issues write FAFs. |
560 | */ | | 561 | */ |
561 | if (psn->lazyopen_w) { | | 562 | if (psn->lazyopen_w) { |
562 | psn->stat |= PSN_LAZYWAIT_W; | | 563 | psn->stat |= PSN_LAZYWAIT_W; |
563 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc); | | 564 | rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc); |
564 | lazyopen_wresp(pu, psn->lazyopen_w, psn, rv); | | 565 | lazyopen_wresp(pu, psn->lazyopen_w, psn, rv); |
565 | | | 566 | |
566 | /* schedule extra waiters */ | | 567 | /* schedule extra waiters */ |
567 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) | | 568 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) |
568 | if (pwp->pw_type == PWTYPE_WRITE) { | | 569 | if (pwp->pw_type == PWTYPE_WRITE) { |
569 | puffs_cc_schedule(pwp->pw_cc); | | 570 | puffs_cc_schedule(pwp->pw_cc); |
570 | TAILQ_REMOVE(&psn->pw, pwp, pw_entries); | | 571 | TAILQ_REMOVE(&psn->pw, pwp, pw_entries); |
571 | } | | 572 | } |
572 | psn->stat &= ~PSN_LAZYWAIT_W; | | 573 | psn->stat &= ~PSN_LAZYWAIT_W; |
573 | | | 574 | |
574 | if ((rv = psn->lazyopen_err_w) != 0) | | 575 | if ((rv = psn->lazyopen_err_w) != 0) |
575 | goto out; | | 576 | goto out; |
576 | } | | 577 | } |
577 | | | 578 | |
578 | if (!psn->fhand_w) { | | 579 | if (!psn->fhand_w) { |
579 | abort(); | | 580 | abort(); |
580 | rv = EINVAL; | | 581 | rv = EINVAL; |
581 | goto out; | | 582 | goto out; |
582 | } | | 583 | } |
583 | | | 584 | |
584 | writelen = *resid; | | 585 | writelen = *resid; |
585 | psbuf_req_data(pb, SSH_FXP_WRITE, reqid, psn->fhand_w,psn->fhand_w_len); | | 586 | psbuf_req_data(pb, SSH_FXP_WRITE, reqid, psn->fhand_w,psn->fhand_w_len); |
586 | psbuf_put_8(pb, offset); | | 587 | psbuf_put_8(pb, offset); |
587 | psbuf_put_data(pb, buf, writelen); | | 588 | psbuf_put_data(pb, buf, writelen); |
588 | GETRESPONSE(pb, pctx->sshfd_data); | | 589 | GETRESPONSE(pb, pctx->sshfd_data); |
589 | | | 590 | |
590 | rv = psbuf_expect_status(pb); | | 591 | rv = psbuf_expect_status(pb); |
591 | if (rv == 0) | | 592 | if (rv == 0) |
592 | *resid = 0; | | 593 | *resid = 0; |
593 | | | 594 | |
594 | if (pn->pn_va.va_size < (uint64_t)offset + writelen) | | 595 | if (pn->pn_va.va_size < (uint64_t)offset + writelen) |
595 | pn->pn_va.va_size = offset + writelen; | | 596 | pn->pn_va.va_size = offset + writelen; |
596 | | | 597 | |
597 | out: | | 598 | out: |
598 | /* check if we need a lazyclose */ | | 599 | /* check if we need a lazyclose */ |
599 | if (psn->stat & PSN_HANDLECLOSE && psn->fhand_w) { | | 600 | if (psn->stat & PSN_HANDLECLOSE && psn->fhand_w) { |
600 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) | | 601 | TAILQ_FOREACH(pwp, &psn->pw, pw_entries) |
601 | if (pwp->pw_type == PWTYPE_WRITE) | | 602 | if (pwp->pw_type == PWTYPE_WRITE) |
602 | break; | | 603 | break; |
603 | if (pwp == NULL) | | 604 | if (pwp == NULL) |
604 | closehandles(pu, psn, HANDLE_WRITE); | | 605 | closehandles(pu, psn, HANDLE_WRITE); |
605 | } | | 606 | } |
606 | PSSHFSRETURN(rv); | | 607 | PSSHFSRETURN(rv); |
607 | } | | 608 | } |
608 | | | 609 | |
609 | int | | 610 | int |
610 | psshfs_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc, | | 611 | psshfs_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc, |
611 | const struct puffs_cred *cred, char *linkvalue, size_t *linklen) | | 612 | const struct puffs_cred *cred, char *linkvalue, size_t *linklen) |
612 | { | | 613 | { |
613 | PSSHFSAUTOVAR(pu); | | 614 | PSSHFSAUTOVAR(pu); |
614 | struct puffs_node *pn = opc; | | 615 | struct puffs_node *pn = opc; |
615 | struct psshfs_node *psn = pn->pn_data; | | 616 | struct psshfs_node *psn = pn->pn_data; |
616 | uint32_t count; | | 617 | uint32_t count; |
617 | | | 618 | |
618 | if (pctx->protover < 3) { | | 619 | if (pctx->protover < 3) { |
619 | rv = EOPNOTSUPP; | | 620 | rv = EOPNOTSUPP; |
620 | goto out; | | 621 | goto out; |
621 | } | | 622 | } |
622 | | | 623 | |
623 | /* | | 624 | /* |
624 | * check if we can use a cached version | | 625 | * check if we can use a cached version |
625 | * | | 626 | * |
626 | * XXX: we might end up reading the same link multiple times | | 627 | * XXX: we might end up reading the same link multiple times |
627 | * from the server if we get many requests at once, but that's | | 628 | * from the server if we get many requests at once, but that's |
628 | * quite harmless as this routine is reentrant. | | 629 | * quite harmless as this routine is reentrant. |
629 | */ | | 630 | */ |
630 | if (psn->symlink && !REFRESHTIMEOUT(pctx, time(NULL) - psn->slread)) | | 631 | if (psn->symlink && !REFRESHTIMEOUT(pctx, time(NULL) - psn->slread)) |
631 | goto copy; | | 632 | goto copy; |
632 | | | 633 | |
633 | if (psn->symlink) { | | 634 | if (psn->symlink) { |
634 | free(psn->symlink); | | 635 | free(psn->symlink); |
635 | psn->symlink = NULL; | | 636 | psn->symlink = NULL; |
636 | psn->slread = 0; | | 637 | psn->slread = 0; |
637 | } | | 638 | } |
638 | | | 639 | |
639 | psbuf_req_str(pb, SSH_FXP_READLINK, reqid, PNPATH(pn)); | | 640 | psbuf_req_str(pb, SSH_FXP_READLINK, reqid, PNPATH(pn)); |
640 | GETRESPONSE(pb, pctx->sshfd); | | 641 | GETRESPONSE(pb, pctx->sshfd); |
641 | | | 642 | |
642 | rv = psbuf_expect_name(pb, &count); | | 643 | rv = psbuf_expect_name(pb, &count); |
643 | if (rv) | | 644 | if (rv) |
644 | goto out; | | 645 | goto out; |
645 | if (count != 1) { | | 646 | if (count != 1) { |
646 | rv = EPROTO; | | 647 | rv = EPROTO; |
647 | goto out; | | 648 | goto out; |
648 | } | | 649 | } |
649 | | | 650 | |
650 | rv = psbuf_get_str(pb, &psn->symlink, NULL); | | 651 | rv = psbuf_get_str(pb, &psn->symlink, NULL); |
651 | if (rv) | | 652 | if (rv) |
652 | goto out; | | 653 | goto out; |
653 | psn->slread = time(NULL); | | 654 | psn->slread = time(NULL); |
654 | | | 655 | |
655 | copy: | | 656 | copy: |
656 | *linklen = strlen(psn->symlink); | | 657 | *linklen = strlen(psn->symlink); |
657 | (void) memcpy(linkvalue, psn->symlink, *linklen); | | 658 | (void) memcpy(linkvalue, psn->symlink, *linklen); |
658 | | | 659 | |
659 | out: | | 660 | out: |
660 | PSSHFSRETURN(rv); | | 661 | PSSHFSRETURN(rv); |
661 | } | | 662 | } |
662 | | | 663 | |
663 | static int | | 664 | static int |
664 | doremove(struct puffs_usermount *pu, struct puffs_node *pn_dir, | | 665 | doremove(struct puffs_usermount *pu, struct puffs_node *pn_dir, |
665 | struct puffs_node *pn, const char *name) | | 666 | struct puffs_node *pn, const char *name) |
666 | { | | 667 | { |
667 | PSSHFSAUTOVAR(pu); | | 668 | PSSHFSAUTOVAR(pu); |
668 | int op; | | 669 | int op; |
669 | | | 670 | |
670 | if (pn->pn_va.va_type == VDIR) | | 671 | if (pn->pn_va.va_type == VDIR) |
671 | op = SSH_FXP_RMDIR; | | 672 | op = SSH_FXP_RMDIR; |
672 | else | | 673 | else |
673 | op = SSH_FXP_REMOVE; | | 674 | op = SSH_FXP_REMOVE; |
674 | | | 675 | |
675 | psbuf_req_str(pb, op, reqid, PNPATH(pn)); | | 676 | psbuf_req_str(pb, op, reqid, PNPATH(pn)); |
676 | GETRESPONSE(pb, pctx->sshfd); | | 677 | GETRESPONSE(pb, pctx->sshfd); |
677 | | | 678 | |
678 | rv = psbuf_expect_status(pb); | | 679 | rv = psbuf_expect_status(pb); |
679 | if (rv == 0) | | 680 | if (rv == 0) |
680 | nukenode(pn, name, 0); | | 681 | nukenode(pn, name, 0); |
681 | | | 682 | |
682 | out: | | 683 | out: |
683 | PSSHFSRETURN(rv); | | 684 | PSSHFSRETURN(rv); |
684 | } | | 685 | } |
685 | | | 686 | |
686 | int | | 687 | int |
687 | psshfs_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc, | | 688 | psshfs_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc, |
688 | puffs_cookie_t targ, const struct puffs_cn *pcn) | | 689 | puffs_cookie_t targ, const struct puffs_cn *pcn) |
689 | { | | 690 | { |
690 | struct puffs_node *pn_targ = targ; | | 691 | struct puffs_node *pn_targ = targ; |
691 | int rv; | | 692 | int rv; |
692 | | | 693 | |
693 | assert(pn_targ->pn_va.va_type != VDIR); | | 694 | assert(pn_targ->pn_va.va_type != VDIR); |
694 | | | 695 | |
695 | rv = doremove(pu, opc, targ, pcn->pcn_name); | | 696 | rv = doremove(pu, opc, targ, pcn->pcn_name); |
696 | if (rv == 0) | | 697 | if (rv == 0) |
697 | puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); | | 698 | puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); |
698 | | | 699 | |
699 | return rv; | | 700 | return rv; |
700 | } | | 701 | } |
701 | | | 702 | |
702 | int | | 703 | int |
703 | psshfs_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc, | | 704 | psshfs_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc, |
704 | puffs_cookie_t targ, const struct puffs_cn *pcn) | | 705 | puffs_cookie_t targ, const struct puffs_cn *pcn) |
705 | { | | 706 | { |
706 | struct puffs_node *pn_targ = targ; | | 707 | struct puffs_node *pn_targ = targ; |
707 | int rv; | | 708 | int rv; |
708 | | | 709 | |
709 | assert(pn_targ->pn_va.va_type == VDIR); | | 710 | assert(pn_targ->pn_va.va_type == VDIR); |
710 | | | 711 | |
711 | rv = doremove(pu, opc, targ, pcn->pcn_name); | | 712 | rv = doremove(pu, opc, targ, pcn->pcn_name); |
712 | if (rv == 0) | | 713 | if (rv == 0) |
713 | puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); | | 714 | puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); |
714 | | | 715 | |
715 | return rv; | | 716 | return rv; |
716 | } | | 717 | } |
717 | | | 718 | |
718 | int | | 719 | int |
719 | psshfs_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc, | | 720 | psshfs_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc, |
720 | struct puffs_newinfo *pni, const struct puffs_cn *pcn, | | 721 | struct puffs_newinfo *pni, const struct puffs_cn *pcn, |
721 | const struct vattr *va) | | 722 | const struct vattr *va) |
722 | { | | 723 | { |
723 | PSSHFSAUTOVAR(pu); | | 724 | PSSHFSAUTOVAR(pu); |
724 | struct puffs_node *pn = opc; | | 725 | struct puffs_node *pn = opc; |
725 | struct puffs_node *pn_new; | | 726 | struct puffs_node *pn_new; |
726 | | | 727 | |
727 | psbuf_req_str(pb, SSH_FXP_MKDIR, reqid, PCNPATH(pcn)); | | 728 | psbuf_req_str(pb, SSH_FXP_MKDIR, reqid, PCNPATH(pcn)); |
728 | psbuf_put_vattr(pb, va, pctx); | | 729 | psbuf_put_vattr(pb, va, pctx); |
729 | GETRESPONSE(pb, pctx->sshfd); | | 730 | GETRESPONSE(pb, pctx->sshfd); |
730 | | | 731 | |
731 | rv = psbuf_expect_status(pb); | | 732 | rv = psbuf_expect_status(pb); |
732 | if (rv) | | 733 | if (rv) |
733 | goto out; | | 734 | goto out; |
734 | | | 735 | |
735 | pn_new = allocnode(pu, pn, pcn->pcn_name, va); | | 736 | pn_new = allocnode(pu, pn, pcn->pcn_name, va); |
736 | if (pn_new) { | | 737 | if (pn_new) { |
737 | puffs_newinfo_setcookie(pni, pn_new); | | 738 | puffs_newinfo_setcookie(pni, pn_new); |
738 | } else { | | 739 | } else { |
739 | struct puffs_framebuf *pb2 = psbuf_makeout(); | | 740 | struct puffs_framebuf *pb2 = psbuf_makeout(); |
740 | reqid = NEXTREQ(pctx); | | 741 | reqid = NEXTREQ(pctx); |
741 | psbuf_recycleout(pb2); | | 742 | psbuf_recycleout(pb2); |
742 | psbuf_req_str(pb2, SSH_FXP_RMDIR, reqid, PCNPATH(pcn)); | | 743 | psbuf_req_str(pb2, SSH_FXP_RMDIR, reqid, PCNPATH(pcn)); |
743 | JUSTSEND(pb2, pctx->sshfd); | | 744 | JUSTSEND(pb2, pctx->sshfd); |
744 | rv = ENOMEM; | | 745 | rv = ENOMEM; |
745 | } | | 746 | } |
746 | | | 747 | |
747 | out: | | 748 | out: |
748 | PSSHFSRETURN(rv); | | 749 | PSSHFSRETURN(rv); |
749 | } | | 750 | } |
750 | | | 751 | |
751 | int | | 752 | int |
752 | psshfs_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc, | | 753 | psshfs_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc, |
753 | struct puffs_newinfo *pni, const struct puffs_cn *pcn, | | 754 | struct puffs_newinfo *pni, const struct puffs_cn *pcn, |
754 | const struct vattr *va, const char *link_target) | | 755 | const struct vattr *va, const char *link_target) |
755 | { | | 756 | { |
756 | PSSHFSAUTOVAR(pu); | | 757 | PSSHFSAUTOVAR(pu); |
757 | struct puffs_node *pn = opc; | | 758 | struct puffs_node *pn = opc; |
758 | struct puffs_node *pn_new; | | 759 | struct puffs_node *pn_new; |
759 | | | 760 | |
760 | if (pctx->protover < 3) { | | 761 | if (pctx->protover < 3) { |
761 | rv = EOPNOTSUPP; | | 762 | rv = EOPNOTSUPP; |
762 | goto out; | | 763 | goto out; |
763 | } | | 764 | } |
764 | | | 765 | |
765 | /* | | 766 | /* |
766 | * XXX: ietf says: source, target. openssh says: ietf who? | | 767 | * XXX: ietf says: source, target. openssh says: ietf who? |
767 | * Let's go with openssh and build quirk tables later if we care | | 768 | * Let's go with openssh and build quirk tables later if we care |
768 | */ | | 769 | */ |
769 | psbuf_req_str(pb, SSH_FXP_SYMLINK, reqid, link_target); | | 770 | psbuf_req_str(pb, SSH_FXP_SYMLINK, reqid, link_target); |
770 | psbuf_put_str(pb, PCNPATH(pcn)); | | 771 | psbuf_put_str(pb, PCNPATH(pcn)); |
771 | GETRESPONSE(pb, pctx->sshfd); | | 772 | GETRESPONSE(pb, pctx->sshfd); |
772 | | | 773 | |
773 | rv = psbuf_expect_status(pb); | | 774 | rv = psbuf_expect_status(pb); |
774 | if (rv) | | 775 | if (rv) |
775 | goto out; | | 776 | goto out; |
776 | | | 777 | |
777 | pn_new = allocnode(pu, pn, pcn->pcn_name, va); | | 778 | pn_new = allocnode(pu, pn, pcn->pcn_name, va); |
778 | if (pn_new) { | | 779 | if (pn_new) { |
779 | puffs_newinfo_setcookie(pni, pn_new); | | 780 | puffs_newinfo_setcookie(pni, pn_new); |
780 | } else { | | 781 | } else { |
781 | struct puffs_framebuf *pb2 = psbuf_makeout(); | | 782 | struct puffs_framebuf *pb2 = psbuf_makeout(); |
782 | reqid = NEXTREQ(pctx); | | 783 | reqid = NEXTREQ(pctx); |
783 | psbuf_recycleout(pb2); | | 784 | psbuf_recycleout(pb2); |
784 | psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn)); | | 785 | psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn)); |
785 | JUSTSEND(pb2, pctx->sshfd); | | 786 | JUSTSEND(pb2, pctx->sshfd); |
786 | rv = ENOMEM; | | 787 | rv = ENOMEM; |
787 | } | | 788 | } |
788 | | | 789 | |
789 | out: | | 790 | out: |
790 | PSSHFSRETURN(rv); | | 791 | PSSHFSRETURN(rv); |
791 | } | | 792 | } |
792 | | | 793 | |
793 | int | | 794 | int |
794 | psshfs_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc, | | 795 | psshfs_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc, |
795 | puffs_cookie_t src, const struct puffs_cn *pcn_src, | | 796 | puffs_cookie_t src, const struct puffs_cn *pcn_src, |
796 | puffs_cookie_t targ_dir, puffs_cookie_t targ, | | 797 | puffs_cookie_t targ_dir, puffs_cookie_t targ, |
797 | const struct puffs_cn *pcn_targ) | | 798 | const struct puffs_cn *pcn_targ) |
798 | { | | 799 | { |
799 | PSSHFSAUTOVAR(pu); | | 800 | PSSHFSAUTOVAR(pu); |
800 | struct puffs_node *pn_sf = src; | | 801 | struct puffs_node *pn_sf = src; |
801 | struct puffs_node *pn_td = targ_dir, *pn_tf = targ; | | 802 | struct puffs_node *pn_td = targ_dir, *pn_tf = targ; |
802 | struct psshfs_node *psn_src = pn_sf->pn_data; | | 803 | struct psshfs_node *psn_src = pn_sf->pn_data; |
803 | struct psshfs_node *psn_targdir = pn_td->pn_data; | | 804 | struct psshfs_node *psn_targdir = pn_td->pn_data; |
804 | | | 805 | |
805 | if (pctx->protover < 2) { | | 806 | if (pctx->protover < 2) { |
806 | rv = EOPNOTSUPP; | | 807 | rv = EOPNOTSUPP; |
807 | goto out; | | 808 | goto out; |
808 | } | | 809 | } |
809 | | | 810 | |
810 | if (pn_tf) { | | 811 | if (pn_tf) { |
811 | rv = doremove(pu, targ_dir, pn_tf, pcn_targ->pcn_name); | | 812 | rv = doremove(pu, targ_dir, pn_tf, pcn_targ->pcn_name); |
812 | if (rv) | | 813 | if (rv) |
813 | goto out; | | 814 | goto out; |
814 | } | | 815 | } |
815 | | | 816 | |
816 | psbuf_req_str(pb, SSH_FXP_RENAME, reqid, PCNPATH(pcn_src)); | | 817 | psbuf_req_str(pb, SSH_FXP_RENAME, reqid, PCNPATH(pcn_src)); |
817 | psbuf_put_str(pb, PCNPATH(pcn_targ)); | | 818 | psbuf_put_str(pb, PCNPATH(pcn_targ)); |
818 | GETRESPONSE(pb, pctx->sshfd); | | 819 | GETRESPONSE(pb, pctx->sshfd); |
819 | | | 820 | |
820 | rv = psbuf_expect_status(pb); | | 821 | rv = psbuf_expect_status(pb); |
821 | if (rv == 0) { | | 822 | if (rv == 0) { |
822 | struct psshfs_dir *pd; | | 823 | struct psshfs_dir *pd; |
823 | | | 824 | |
824 | /* | | 825 | /* |
825 | * XXX: interfaces didn't quite work with rename.. | | 826 | * XXX: interfaces didn't quite work with rename.. |
826 | * the song remains the same. go figure .. ;) | | 827 | * the song remains the same. go figure .. ;) |
827 | */ | | 828 | */ |
828 | nukenode(pn_sf, pcn_src->pcn_name, 0); | | 829 | nukenode(pn_sf, pcn_src->pcn_name, 0); |
829 | pd = direnter(pn_td, pcn_targ->pcn_name); | | 830 | pd = direnter(pn_td, pcn_targ->pcn_name); |
830 | pd->entry = pn_sf; | | 831 | pd->entry = pn_sf; |
831 | puffs_setvattr(&pd->va, &pn_sf->pn_va); | | 832 | puffs_setvattr(&pd->va, &pn_sf->pn_va); |
832 | | | 833 | |
833 | if (opc != targ_dir) { | | 834 | if (opc != targ_dir) { |
834 | psn_targdir->childcount++; | | 835 | psn_targdir->childcount++; |
835 | psn_src->parent = pn_td; | | 836 | psn_src->parent = pn_td; |
836 | if (pn_sf->pn_va.va_type == VDIR) | | 837 | if (pn_sf->pn_va.va_type == VDIR) |
837 | pn_td->pn_va.va_nlink++; | | 838 | pn_td->pn_va.va_nlink++; |
838 | } | | 839 | } |
839 | } | | 840 | } |
840 | | | 841 | |
841 | out: | | 842 | out: |
842 | PSSHFSRETURN(rv); | | 843 | PSSHFSRETURN(rv); |
843 | } | | 844 | } |
844 | | | 845 | |
845 | /* | | 846 | /* |
846 | * So this file system happened to be written in such a way that | | 847 | * So this file system happened to be written in such a way that |
847 | * lookup for ".." is hard if we lose the in-memory node. We'd | | 848 | * lookup for ".." is hard if we lose the in-memory node. We'd |
848 | * need to recreate the entire directory structure from the root | | 849 | * need to recreate the entire directory structure from the root |
849 | * node up to the ".." node we're looking up. | | 850 | * node up to the ".." node we're looking up. |
850 | * | | 851 | * |
851 | * And since our entire fs structure is purely fictional (i.e. it's | | 852 | * And since our entire fs structure is purely fictional (i.e. it's |
852 | * only in-memory, not fetchable from the server), the easiest way | | 853 | * only in-memory, not fetchable from the server), the easiest way |
853 | * to deal with it is to not allow nodes with children to be | | 854 | * to deal with it is to not allow nodes with children to be |
854 | * reclaimed. | | 855 | * reclaimed. |
855 | * | | 856 | * |
856 | * If a node with children is being attempted to be reclaimed, we | | 857 | * If a node with children is being attempted to be reclaimed, we |
857 | * just mark it "reclaimed" but leave it as is until all its children | | 858 | * just mark it "reclaimed" but leave it as is until all its children |
858 | * have been reclaimed. If a lookup for that node is done meanwhile, | | 859 | * have been reclaimed. If a lookup for that node is done meanwhile, |
859 | * it will be found by lookup() and we just remove the "reclaimed" | | 860 | * it will be found by lookup() and we just remove the "reclaimed" |
860 | * bit. | | 861 | * bit. |
861 | */ | | 862 | */ |
862 | int | | 863 | int |
863 | psshfs_node_reclaim(struct puffs_usermount *pu, puffs_cookie_t opc) | | 864 | psshfs_node_reclaim(struct puffs_usermount *pu, puffs_cookie_t opc) |
864 | { | | 865 | { |
865 | struct puffs_node *pn = opc, *pn_next, *pn_root; | | 866 | struct puffs_node *pn = opc, *pn_next, *pn_root; |
866 | struct psshfs_node *psn = pn->pn_data; | | 867 | struct psshfs_node *psn = pn->pn_data; |
867 | | | 868 | |
868 | /* | | 869 | /* |
869 | * don't reclaim if we have file handle issued, otherwise | | 870 | * don't reclaim if we have file handle issued, otherwise |
870 | * we can't do fhtonode | | 871 | * we can't do fhtonode |
871 | */ | | 872 | */ |
872 | if (psn->stat & PSN_HASFH) | | 873 | if (psn->stat & PSN_HASFH) |
873 | return 0; | | 874 | return 0; |
874 | | | 875 | |
875 | psn->stat |= PSN_RECLAIMED; | | 876 | psn->stat |= PSN_RECLAIMED; |
876 | pn_root = puffs_getroot(pu); | | 877 | pn_root = puffs_getroot(pu); |
877 | for (; pn != pn_root; pn = pn_next) { | | 878 | for (; pn != pn_root; pn = pn_next) { |
878 | psn = pn->pn_data; | | 879 | psn = pn->pn_data; |
879 | if ((psn->stat & PSN_RECLAIMED) == 0 || psn->childcount != 0) | | 880 | if ((psn->stat & PSN_RECLAIMED) == 0 || psn->childcount != 0) |
880 | break; | | 881 | break; |
881 | | | 882 | |
882 | pn_next = psn->parent; | | 883 | pn_next = psn->parent; |
883 | doreclaim(pn); | | 884 | doreclaim(pn); |
884 | } | | 885 | } |
885 | | | 886 | |
886 | return 0; | | 887 | return 0; |
887 | } | | 888 | } |