paths.c revision 1.4 1 /* $NetBSD: paths.c,v 1.4 2007/02/15 17:04:46 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2007 Antti Kantee. All Rights Reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the company nor the name of the author may be used to
15 * endorse or promote products derived from this software without specific
16 * prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 #if !defined(lint)
33 __RCSID("$NetBSD: paths.c,v 1.4 2007/02/15 17:04:46 pooka Exp $");
34 #endif /* !lint */
35
36 #include <assert.h>
37 #include <errno.h>
38 #include <puffs.h>
39 #include <stdlib.h>
40
41 #include "puffs_priv.h"
42
43 /*
44 * Generic routines for pathbuilding code
45 */
46
47 int
48 puffs_path_pcnbuild(struct puffs_usermount *pu, struct puffs_cn *pcn,
49 void *parent)
50 {
51 struct puffs_node *pn_parent = PU_CMAP(pu, parent);
52 struct puffs_cn pcn_orig;
53 struct puffs_pathobj po;
54 int rv;
55
56 assert(pn_parent->pn_po.po_path != NULL);
57
58 if (pu->pu_pathtransform) {
59 rv = pu->pu_pathtransform(pu, &pn_parent->pn_po, pcn, &po);
60 if (rv)
61 return rv;
62 } else {
63 po.po_path = pcn->pcn_name;
64 po.po_len = pcn->pcn_namelen;
65 }
66
67 if (pu->pu_namemod) {
68 /* XXX: gcc complains if I do assignment */
69 memcpy(&pcn_orig, pcn, sizeof(pcn_orig));
70 rv = pu->pu_namemod(pu, &pn_parent->pn_po, pcn);
71 if (rv)
72 return rv;
73 }
74
75 rv = pu->pu_pathbuild(pu, &pn_parent->pn_po, &po, 0,
76 &pcn->pcn_po_full);
77
78 if (pu->pu_pathtransform)
79 pu->pu_pathfree(pu, &po);
80
81 if (pu->pu_namemod && rv)
82 *pcn = pcn_orig;
83
84 return rv;
85 }
86
87 /*
88 * substitute all (child) patch prefixes. called from nodewalk, which
89 * in turn is called from rename
90 */
91 void *
92 puffs_path_prefixadj(struct puffs_usermount *pu, struct puffs_node *pn,
93 void *arg)
94 {
95 struct puffs_pathinfo *pi = arg;
96 struct puffs_pathobj localpo;
97 struct puffs_pathobj oldpo;
98 int rv;
99
100 /* can't be a path prefix */
101 if (pn->pn_po.po_len < pi->pi_old->po_len)
102 return NULL;
103
104 if (pu->pu_pathcmp(pu, &pn->pn_po, pi->pi_old, pi->pi_old->po_len, 1))
105 return NULL;
106
107 /* otherwise we'd have two nodes with an equal path */
108 assert(pn->pn_po.po_len > pi->pi_old->po_len);
109
110 /* found a matching prefix */
111 rv = pu->pu_pathbuild(pu, pi->pi_new, &pn->pn_po,
112 pi->pi_old->po_len, &localpo);
113 /*
114 * XXX: technically we shouldn't fail, but this is the only
115 * sensible thing to do here. If the buildpath routine fails,
116 * we will have paths in an inconsistent state. Should fix this,
117 * either by having two separate passes or by doing other tricks
118 * to make an invalid path with BUILDPATHS acceptable.
119 */
120 if (rv != 0)
121 abort();
122
123 /* out with the old and in with the new */
124 oldpo = pn->pn_po;
125 pn->pn_po = localpo;
126 pu->pu_pathfree(pu, &oldpo);
127
128 /* continue the walk */
129 return NULL;
130 }
131
132 /*
133 * called from nodewalk, checks for exact match
134 */
135 void *
136 puffs_path_walkcmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
137 {
138 struct puffs_pathobj *po = arg;
139 struct puffs_pathobj po2;
140
141 if (po->po_len != PNPLEN(pn))
142 return NULL;
143
144 po2.po_path = PNPATH(pn);
145 po2.po_len = PNPLEN(pn);
146
147 if (pu->pu_pathcmp(pu, po, &po2, PNPLEN(pn), 0) == 0)
148 return pn;
149 return NULL;
150 }
151
152 /*
153 * Routines provided to file systems which consider a path a tuple of
154 * strings and / the component separator.
155 */
156
157 /*ARGSUSED*/
158 int
159 puffs_stdpath_cmppath(struct puffs_usermount *pu, struct puffs_pathobj *c1,
160 struct puffs_pathobj *c2, size_t clen, int checkprefix)
161 {
162 char *p;
163 int rv;
164
165 rv = strncmp(c1->po_path, c2->po_path, clen);
166 if (rv)
167 return 1;
168
169 if (checkprefix == 0)
170 return 0;
171
172 /* sanity for next step */
173 if (!(c1->po_len > c2->po_len))
174 return 1;
175
176 /* check if it's really a complete path prefix */
177 p = c1->po_path;
178 if ((*(p + clen)) != '/')
179 return 1;
180
181 return 0;
182 }
183
184 /*ARGSUSED*/
185 int
186 puffs_stdpath_buildpath(struct puffs_usermount *pu,
187 const struct puffs_pathobj *po_pre, const struct puffs_pathobj *po_comp,
188 size_t offset, struct puffs_pathobj *newpath)
189 {
190 char *path, *pcomp;
191 size_t plen, complen;
192 size_t prelen;
193 int isdotdot;
194
195 complen = po_comp->po_len - offset;
196
197 /* seek to correct place & remove all leading '/' from component */
198 pcomp = po_comp->po_path;
199 pcomp += offset;
200 while (*pcomp == '/') {
201 pcomp++;
202 complen--;
203 }
204
205 /* todotdot or nottodotdot */
206 if (complen == 2 && strcmp(pcomp, "..") == 0)
207 isdotdot = 1;
208 else
209 isdotdot = 0;
210
211 /*
212 * Strip trailing components from the preceending component.
213 * This is an issue only for the root node, which we might want
214 * to be at path "/" for some file systems.
215 */
216 prelen = po_pre->po_len;
217 while (prelen > 0 && *((char *)po_pre->po_path + (prelen-1)) == '/') {
218 assert(isdotdot == 0);
219 prelen--;
220 }
221
222 if (isdotdot) {
223 char *slash; /* sweet char of mine */
224
225 slash = strrchr(po_pre->po_path, '/');
226 assert(slash != NULL);
227
228 plen = slash - (char *)po_pre->po_path;
229 path = malloc(plen + 1);
230 if (path == NULL)
231 return errno;
232
233 strlcpy(path, po_pre->po_path, plen+1);
234 } else {
235 /* + '/' + '\0' */
236 plen = prelen + 1 + complen;
237 path = malloc(plen + 1);
238 if (path == NULL)
239 return errno;
240
241 strlcpy(path, po_pre->po_path, prelen+1);
242 strcat(path, "/");
243 strncat(path, pcomp, complen);
244 }
245
246 newpath->po_path = path;
247 newpath->po_len = plen;
248
249 return 0;
250 }
251
252 /*ARGSUSED*/
253 void
254 puffs_stdpath_freepath(struct puffs_usermount *pu, struct puffs_pathobj *po)
255 {
256
257 free(po->po_path);
258 }
259