Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* AUTORIGHTS |
2 | Copyright (C) 2003 - 2010 Chaskiel Grundman | |
3 | All rights reserved | |
4 | ||
5 | Redistribution and use in source and binary forms, with or without | |
6 | modification, are permitted provided that the following conditions | |
7 | are met: | |
8 | ||
9 | 1. Redistributions of source code must retain the above copyright | |
10 | notice, this list of conditions and the following disclaimer. | |
11 | ||
12 | 2. Redistributions in binary form must reproduce the above copyright | |
13 | notice, this list of conditions and the following disclaimer in the | |
14 | documentation and/or other materials provided with the distribution. | |
15 | ||
16 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
17 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
18 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
19 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
20 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
21 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
22 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
23 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
26 | */ | |
27 | #include <afsconfig.h> | |
28 | #include <afs/param.h> | |
29 | ||
30 | #include <roken.h> | |
31 | ||
32 | #include <search.h> | |
33 | ||
34 | #include <afs/vlserver.h> | |
35 | #include <afs/vldbint.h> | |
36 | #include <afs/dir.h> | |
37 | #ifdef AFS_NT40_ENV | |
38 | #include <afs/errmap_nt.h> | |
39 | #endif | |
40 | #include "afscp.h" | |
41 | #include "afscp_internal.h" | |
42 | ||
43 | static int dirmode = DIRMODE_CELL; | |
44 | ||
45 | int | |
46 | afscp_SetDirMode(int mode) | |
47 | { | |
48 | if ((mode != DIRMODE_CELL) && (mode != DIRMODE_DYNROOT)) { | |
49 | afscp_errno = EINVAL; | |
50 | return -1; | |
51 | } | |
52 | dirmode = mode; | |
53 | return 0; | |
54 | } | |
55 | ||
56 | /* comparison function for tsearch */ | |
57 | static int | |
58 | dircompare(const void *a, const void *b) | |
59 | { | |
60 | const struct afscp_dircache *sa = a, *sb = b; | |
61 | if (sa->me.fid.Vnode < sb->me.fid.Vnode) | |
62 | return -1; | |
63 | if (sa->me.fid.Vnode > sb->me.fid.Vnode) | |
64 | return 1; | |
65 | if (sa->me.fid.Unique < sb->me.fid.Unique) | |
66 | return -1; | |
67 | if (sa->me.fid.Unique > sb->me.fid.Unique) | |
68 | return 1; | |
69 | return 0; | |
70 | } | |
71 | ||
72 | /* make sure the dirstream contains the most up to date directory contents */ | |
73 | static int | |
74 | _DirUpdate(struct afscp_dirstream *d) | |
75 | { | |
76 | struct AFSFetchStatus s; | |
77 | int code; | |
78 | struct afscp_volume *v; | |
79 | struct afscp_dircache key, *stored; | |
80 | void **cached; | |
81 | ||
82 | ||
83 | code = afscp_GetStatus(&d->fid, &s); | |
84 | if (code != 0) { | |
85 | return code; | |
86 | } | |
87 | ||
88 | if (d->dirbuffer && d->dv == s.DataVersion) { | |
89 | return 0; | |
90 | } | |
91 | v = afscp_VolumeById(d->fid.cell, d->fid.fid.Volume); | |
92 | if (v == NULL) { | |
93 | afscp_errno = ENOENT; | |
94 | return -1; | |
95 | } | |
96 | ||
97 | memcpy(&key.me, &d->fid, sizeof(struct afscp_venusfid)); | |
98 | cached = tfind(&key, &v->dircache, dircompare); | |
99 | if (cached != NULL) { | |
100 | stored = *(struct afscp_dircache **)cached; | |
101 | if (d->dv == s.DataVersion) { | |
102 | d->dirbuffer = stored->dirbuffer; | |
103 | d->buflen = stored->buflen; | |
104 | d->dv = stored->dv; | |
105 | return 0; | |
106 | } | |
107 | pthread_mutex_lock(&(stored->mtx)); | |
108 | tdelete(&key, &v->dircache, dircompare); | |
109 | stored->nwaiters++; | |
110 | while (stored->nwaiters > 1) { | |
111 | pthread_cond_wait(&(stored->cv), &(stored->mtx)); | |
112 | } | |
113 | stored->nwaiters--; | |
114 | pthread_cond_destroy(&(stored->cv)); | |
115 | pthread_mutex_unlock(&(stored->mtx)); | |
116 | pthread_mutex_destroy(&(stored->mtx)); | |
117 | if (d->dirbuffer != stored->dirbuffer) | |
118 | free(stored->dirbuffer); | |
119 | free(stored); | |
120 | } | |
121 | if (s.Length > BIGMAXPAGES * AFS_PAGESIZE) { | |
122 | afscp_errno = EFBIG; | |
123 | return -1; | |
124 | } | |
125 | if (d->buflen != s.Length) { | |
126 | char *new; | |
127 | if (d->dirbuffer) { | |
128 | new = realloc(d->dirbuffer, s.Length); | |
129 | } else { | |
130 | new = malloc(s.Length); | |
131 | } | |
132 | if (new != NULL) { | |
133 | d->dirbuffer = new; | |
134 | } else { | |
135 | afscp_errno = ENOMEM; | |
136 | return -1; | |
137 | } | |
138 | d->buflen = s.Length; | |
139 | } | |
140 | ||
141 | code = afscp_PRead(&d->fid, d->dirbuffer, s.Length, 0); | |
142 | if (code < 0) { | |
143 | return -1; | |
144 | } | |
145 | d->dv = s.DataVersion; | |
146 | cached = tsearch(&key, &v->dircache, dircompare); | |
147 | if (cached != NULL) { | |
148 | stored = malloc(sizeof(struct afscp_dircache)); | |
149 | if (stored != NULL) { | |
150 | memcpy(&stored->me, &d->fid, sizeof(struct afscp_venusfid)); | |
151 | stored->buflen = d->buflen; | |
152 | stored->dirbuffer = d->dirbuffer; | |
153 | stored->dv = d->dv; | |
154 | stored->nwaiters = 0; | |
155 | pthread_mutex_init(&(stored->mtx), NULL); | |
156 | pthread_cond_init(&(stored->cv), NULL); | |
157 | *(struct afscp_dircache **)cached = stored; | |
158 | } else { | |
159 | tdelete(&key, &v->dircache, dircompare); | |
160 | } | |
161 | } | |
162 | return 0; | |
163 | } | |
164 | ||
165 | static struct DirEntry * | |
166 | dir_get_entry(struct afscp_dirstream *d, int entry) | |
167 | { | |
168 | struct DirHeader *h = (struct DirHeader *)d->dirbuffer; | |
169 | /* struct PageHeader *p; */ | |
170 | struct DirEntry *ret; | |
171 | /* int fr; */ | |
172 | int pg, off; | |
173 | ||
174 | pg = entry >> LEPP; | |
175 | off = entry & (EPP - 1); | |
176 | ||
177 | if (pg * AFS_PAGESIZE >= d->buflen) { /* beyond end of file */ | |
178 | return NULL; | |
179 | } | |
180 | if (!off || (!pg && off < DHE + 1)) { /* offset refers to metadata */ | |
181 | return NULL; | |
182 | } | |
183 | if (pg < MAXPAGES && h->alloMap[pg] == EPP) { /* page is empty */ | |
184 | return NULL; | |
185 | } | |
186 | /* p = (struct PageHeader *)&d->dirbuffer[pg * AFS_PAGESIZE]; */ | |
187 | /* p is set but not referenced later */ | |
188 | /* fr = p->freebitmap[off >> 8]; */ | |
189 | /* fr is set but not referenced later */ | |
190 | ret = (struct DirEntry *)&d->dirbuffer[pg * AFS_PAGESIZE + 32 * off]; | |
191 | return ret; | |
192 | } | |
193 | ||
194 | struct afscp_dirstream * | |
195 | afscp_OpenDir(const struct afscp_venusfid *fid) | |
196 | { | |
197 | struct afscp_dirstream *ret; | |
198 | struct AFSFetchStatus s; | |
199 | int code; | |
200 | ||
201 | code = afscp_GetStatus(fid, &s); | |
202 | if (code != 0) { | |
203 | return NULL; | |
204 | } | |
205 | ||
206 | if (s.FileType != Directory) { | |
207 | afscp_errno = ENOTDIR; | |
208 | return NULL; | |
209 | } | |
210 | ret = calloc(1, sizeof(struct afscp_dirstream)); | |
211 | if (ret == NULL) { | |
212 | afscp_errno = ENOMEM; | |
213 | return NULL; | |
214 | } | |
215 | memmove(&ret->fid, fid, sizeof(struct afscp_venusfid)); | |
216 | code = _DirUpdate(ret); | |
217 | if (code < 0) { | |
218 | afscp_CloseDir(ret); | |
219 | return NULL; | |
220 | } | |
221 | ret->hashent = -1; | |
222 | ret->entry = 0; | |
223 | ||
224 | return ret; | |
225 | } | |
226 | ||
227 | struct afscp_dirent * | |
228 | afscp_ReadDir(struct afscp_dirstream *d) | |
229 | { | |
230 | struct DirHeader *h = (struct DirHeader *)d->dirbuffer; | |
231 | struct DirEntry *info; | |
232 | int ent; | |
233 | ||
234 | ||
235 | ent = d->entry; | |
236 | while (ent == 0 && d->hashent < NHASHENT - 1) { | |
237 | d->hashent++; | |
238 | ent = ntohs(h->hashTable[d->hashent]); | |
239 | } | |
240 | if (ent == 0) { | |
241 | afscp_errno = 0; | |
242 | return NULL; | |
243 | } | |
244 | info = dir_get_entry(d, ent); | |
245 | if (info == NULL) { | |
246 | afscp_errno = 0; | |
247 | return NULL; | |
248 | } | |
249 | d->ret.vnode = ntohl(info->fid.vnode); | |
250 | d->ret.unique = ntohl(info->fid.vunique); | |
251 | strlcpy(d->ret.name, info->name, sizeof(d->ret.name)); /* guaranteed to be NULL terminated? */ | |
252 | d->entry = ntohs(info->next); | |
253 | ||
254 | return &d->ret; | |
255 | } | |
256 | ||
257 | /* as it calls _DirUpdate, this may corrupt any previously returned dirent's */ | |
258 | int | |
259 | afscp_RewindDir(struct afscp_dirstream *d) | |
260 | { | |
261 | _DirUpdate(d); | |
262 | d->hashent = -1; | |
263 | d->entry = 0; | |
264 | return 0; | |
265 | } | |
266 | ||
267 | int | |
268 | afscp_CloseDir(struct afscp_dirstream *d) | |
269 | { | |
270 | free(d); | |
271 | return 0; | |
272 | } | |
273 | ||
274 | static int | |
275 | namehash(const char *name) | |
276 | { | |
277 | int hval, tval; | |
278 | ||
279 | hval = 0; | |
280 | while (*name != '\0') | |
281 | hval = (hval * 173) + *name++; | |
282 | tval = hval & (NHASHENT - 1); | |
283 | return tval ? (hval < 0 ? NHASHENT - tval : tval) | |
284 | : 0; | |
285 | } | |
286 | ||
287 | struct afscp_venusfid * | |
288 | afscp_DirLookup(struct afscp_dirstream *d, const char *name) | |
289 | { | |
290 | int code; | |
291 | int hval, entry; | |
292 | struct DirHeader *h = (struct DirHeader *)d->dirbuffer; | |
293 | struct DirEntry *info; | |
294 | ||
295 | code = _DirUpdate(d); | |
296 | if (code != 0) { | |
297 | return NULL; | |
298 | } | |
299 | hval = namehash(name); | |
300 | entry = ntohs(h->hashTable[hval]); | |
301 | ||
302 | while (entry != 0) { | |
303 | info = dir_get_entry(d, entry); | |
304 | if (info == NULL) { | |
305 | afscp_errno = EIO; | |
306 | return NULL; | |
307 | } | |
308 | if (strcmp(info->name, name) == 0) | |
309 | break; | |
310 | entry = ntohs(info->next); | |
311 | } | |
312 | if (entry != 0) { | |
313 | return afscp_MakeFid(d->fid.cell, d->fid.fid.Volume, | |
314 | ntohl(info->fid.vnode), | |
315 | ntohl(info->fid.vunique)); | |
316 | } else { | |
317 | afscp_errno = ENOENT; | |
318 | return NULL; | |
319 | } | |
320 | } | |
321 | ||
322 | struct afscp_venusfid * | |
323 | afscp_ResolveName(const struct afscp_venusfid *dir, const char *name) | |
324 | { | |
325 | struct afscp_venusfid *ret; | |
326 | struct afscp_dirstream *d; | |
327 | ||
328 | d = afscp_OpenDir(dir); | |
329 | if (d == NULL) { | |
330 | return NULL; | |
331 | } | |
332 | ret = afscp_DirLookup(d, name); | |
333 | afscp_CloseDir(d); | |
334 | return ret; | |
335 | } | |
336 | ||
337 | static int | |
338 | gettoproot(struct afscp_cell *cell, char *p, char **q, | |
339 | struct afscp_venusfid **root) | |
340 | { | |
341 | struct afscp_volume *rootvol; | |
342 | char *r; | |
343 | ||
344 | if (dirmode == DIRMODE_DYNROOT && (strcmp(p, "/afs") == 0)) { | |
345 | afscp_errno = EINVAL; | |
346 | return 1; | |
347 | } | |
348 | if (strncmp(p, "/afs", 4) == 0) { | |
349 | afs_dprintf(("gettoproot: path is absolute\n")); | |
350 | p = &p[5]; | |
351 | while (*p == '/') | |
352 | p++; | |
353 | if (dirmode == DIRMODE_DYNROOT) { | |
354 | int voltype; | |
355 | retry_dot: | |
356 | voltype = ROVOL; | |
357 | if (*p == '.') { | |
358 | p++; | |
359 | voltype = RWVOL; | |
360 | } | |
361 | if (*p == '/') { | |
362 | while (*p == '/') | |
363 | p++; | |
364 | goto retry_dot; | |
365 | } | |
366 | if (*p == '.' || *p == 0) { | |
367 | afscp_errno = EINVAL; | |
368 | return 1; | |
369 | } | |
370 | r = p; | |
371 | while (*r && *r != '/') | |
372 | r++; | |
373 | if (*r) | |
374 | *r++ = 0; | |
375 | *q = r; | |
376 | afs_dprintf(("gettoproot: dynroot looking up cell %s\n", p)); | |
377 | cell = afscp_CellByName(p, NULL); | |
378 | if (cell == NULL) { | |
379 | afs_dprintf(("gettoproot: no such cell\n")); | |
380 | afscp_errno = ENODEV; | |
381 | return 1; | |
382 | } | |
383 | rootvol = afscp_VolumeByName(cell, "root.cell", voltype); | |
384 | if (!rootvol && voltype == ROVOL) | |
385 | rootvol = afscp_VolumeByName(cell, "root.cell", RWVOL); | |
386 | } else { | |
387 | *q = p; | |
388 | rootvol = afscp_VolumeByName(cell, "root.afs", ROVOL); | |
389 | if (!rootvol) | |
390 | rootvol = afscp_VolumeByName(cell, "root.afs", RWVOL); | |
391 | } | |
392 | if (!rootvol) | |
393 | afs_dprintf(("gettoproot: volume not found\n")); | |
394 | } else { | |
395 | afs_dprintf(("gettoproot: path is relative\n")); | |
396 | if (p[0] == '/') { | |
397 | afscp_errno = EXDEV; | |
398 | return 1; | |
399 | } | |
400 | rootvol = afscp_VolumeByName(cell, "root.cell", ROVOL); | |
401 | if (!rootvol) | |
402 | rootvol = afscp_VolumeByName(cell, "root.cell", RWVOL); | |
403 | *q = p; | |
404 | } | |
405 | if (rootvol == NULL) { | |
406 | afscp_errno = ENODEV; | |
407 | return 1; | |
408 | } | |
409 | *root = afscp_MakeFid(cell, rootvol->id, 1, 1); | |
410 | return 0; | |
411 | } | |
412 | ||
413 | static int | |
414 | getvolumeroot(struct afscp_cell *cell, int voltype, const char *vname, | |
415 | struct afscp_venusfid **root) | |
416 | { | |
417 | struct afscp_volume *vol; | |
418 | vol = afscp_VolumeByName(cell, vname, voltype); | |
419 | if (!vol && voltype == ROVOL) | |
420 | vol = afscp_VolumeByName(cell, vname, RWVOL); | |
421 | if (vol == NULL) { | |
422 | afscp_errno = ENODEV; | |
423 | return 1; | |
424 | } | |
425 | *root = afscp_MakeFid(cell, vol->id, 1, 1); | |
426 | return 0; | |
427 | } | |
428 | ||
429 | typedef struct fidstack_s { | |
430 | int alloc; | |
431 | int count; | |
432 | struct afscp_venusfid **entries; | |
433 | } *fidstack; | |
434 | ||
435 | static fidstack | |
436 | fidstack_alloc(void) | |
437 | { | |
438 | fidstack ret; | |
439 | ||
440 | ret = malloc(sizeof(struct fidstack_s)); | |
441 | if (ret == NULL) { | |
442 | afscp_errno = ENOMEM; | |
443 | return NULL; | |
444 | } | |
445 | ret->alloc = 10; | |
446 | ret->count = 0; | |
447 | ret->entries = malloc(ret->alloc * sizeof(struct afscp_venusfid *)); | |
448 | if (ret->entries == NULL) { | |
449 | free(ret); | |
450 | afscp_errno = ENOMEM; | |
451 | return NULL; | |
452 | } | |
453 | return ret; | |
454 | } | |
455 | ||
456 | static void | |
457 | fidstack_push(fidstack s, struct afscp_venusfid *entry) | |
458 | { | |
459 | struct afscp_venusfid **new; | |
460 | if (s->count >= s->alloc) { | |
461 | new = realloc(s->entries, (s->alloc + 10) * | |
462 | sizeof(struct afscp_venusfid *)); | |
463 | if (new == NULL) { | |
464 | return; | |
465 | } | |
466 | s->entries = new; | |
467 | s->alloc += 10; | |
468 | } | |
469 | s->entries[s->count++] = entry; | |
470 | return; | |
471 | } | |
472 | ||
473 | static struct afscp_venusfid * | |
474 | fidstack_pop(fidstack s) | |
475 | { | |
476 | if (s->count) | |
477 | return s->entries[--s->count]; | |
478 | return NULL; | |
479 | } | |
480 | ||
481 | static void | |
482 | fidstack_free(fidstack s) | |
483 | { | |
484 | int i; | |
485 | ||
486 | for (i = 0; i < s->count; i++) | |
487 | free(s->entries[i]); | |
488 | free(s->entries); | |
489 | free(s); | |
490 | } | |
491 | ||
492 | static struct afscp_venusfid *_ResolvePath(const struct afscp_venusfid *, | |
493 | fidstack, char *, int); | |
494 | ||
495 | static struct afscp_venusfid * | |
496 | afscp_HandleLink(struct afscp_venusfid *in, | |
497 | const struct afscp_venusfid *parent, fidstack fids, | |
498 | int follow, const struct AFSFetchStatus *s, int terminal) | |
499 | { | |
500 | char *linkbuf, *linkbufq; | |
501 | struct afscp_cell *cell; | |
502 | struct afscp_volume *v; | |
503 | struct afscp_venusfid *root, *ret; | |
504 | int voltype; | |
505 | int code; | |
506 | ssize_t len; | |
507 | if ((s->UnixModeBits & 0111) && (follow == 0) && terminal) { /* normal link */ | |
508 | return in; | |
509 | } | |
510 | linkbuf = malloc(s->Length + 1); | |
511 | code = afscp_PRead(in, linkbuf, s->Length, 0); | |
512 | if (code < 0) { | |
513 | free(linkbuf); | |
514 | free(in); | |
515 | return NULL; | |
516 | } | |
517 | if (code != s->Length) { | |
518 | afscp_errno = EIO; | |
519 | free(linkbuf); | |
520 | free(in); | |
521 | return NULL; | |
522 | } | |
523 | linkbuf[s->Length] = 0; | |
524 | if (s->UnixModeBits & 0111) { /* normal link */ | |
525 | afs_dprintf(("Recursing on symlink %s...\n", linkbuf)); | |
526 | if (linkbuf[0] == '/') { | |
527 | if (gettoproot(in->cell, linkbuf, &linkbufq, &root)) { | |
528 | free(linkbuf); | |
529 | free(in); | |
530 | return NULL; | |
531 | } | |
532 | free(in); | |
533 | ret = _ResolvePath(root, 0, linkbufq, 0); | |
534 | free(root); | |
535 | } else { | |
536 | free(in); | |
537 | ret = _ResolvePath(parent, fids, linkbuf, 0); | |
538 | } | |
539 | free(linkbuf); | |
540 | } else { /* mountpoint */ | |
541 | afs_dprintf(("EvalMountPoint %s...\n", linkbuf)); | |
542 | linkbufq = strchr(linkbuf, ':'); | |
543 | cell = in->cell; | |
544 | v = afscp_VolumeById(cell, in->fid.Volume); | |
545 | free(in); | |
546 | if (v == NULL) { | |
547 | free(linkbuf); | |
548 | afscp_errno = ENODEV; | |
549 | return NULL; | |
550 | } | |
551 | voltype = v->voltype; | |
552 | if (linkbuf[0] == '%') | |
553 | voltype = RWVOL; | |
554 | if (linkbufq == NULL) { | |
555 | linkbufq = linkbuf + 1; | |
556 | } else { | |
557 | *linkbufq++ = 0; | |
558 | cell = afscp_CellByName(linkbuf + 1, NULL); | |
559 | if (linkbuf[0] != '%') | |
560 | voltype = ROVOL; | |
561 | } | |
562 | if (cell == NULL) { | |
563 | free(linkbuf); | |
564 | afscp_errno = ENODEV; | |
565 | return NULL; | |
566 | } | |
567 | len = strnlen(linkbufq, s->Length + 1); | |
568 | if (len < 2) { | |
569 | free(linkbuf); | |
570 | afscp_errno = ENODEV; | |
571 | return NULL; | |
572 | } | |
573 | len = strnlen(linkbufq, s->Length + 1); | |
574 | linkbufq[len - 1] = 0; /* eliminate trailer */ | |
575 | if (getvolumeroot(cell, voltype, linkbufq, &ret)) { | |
576 | free(linkbuf); | |
577 | return NULL; | |
578 | } | |
579 | free(linkbuf); | |
580 | } | |
581 | return ret; | |
582 | } | |
583 | ||
584 | static struct afscp_venusfid * | |
585 | _ResolvePath(const struct afscp_venusfid *start, fidstack infids, | |
586 | char *path, int follow) | |
587 | { | |
588 | struct afscp_venusfid *ret, *cwd; | |
589 | struct AFSFetchStatus s; | |
590 | char *p, *q; | |
591 | int code; | |
592 | int linkcount; | |
593 | fidstack fids; | |
594 | ||
595 | p = path; | |
596 | ret = cwd = afscp_DupFid(start); | |
597 | fids = infids; | |
598 | if (fids == NULL) | |
599 | fids = fidstack_alloc(); | |
600 | if (fids == NULL) { | |
601 | return NULL; | |
602 | } | |
603 | ||
604 | while (p && *p) { | |
605 | q = strchr(p, '/'); | |
606 | if (q) | |
607 | *q++ = 0; | |
608 | if (strcmp(p, ".") == 0) { | |
609 | /* do nothing */ | |
610 | } else if (strcmp(p, "..") == 0) { | |
611 | ret = fidstack_pop(fids); | |
612 | if (ret == NULL) | |
613 | ret = cwd; | |
614 | else | |
615 | free(cwd); | |
616 | } else { | |
617 | ret = afscp_ResolveName(cwd, p); | |
618 | if (ret == NULL) { | |
619 | afs_dprintf(("Lookup %s in %lu.%lu.%lu failed\n", p, | |
620 | cwd->fid.Volume, cwd->fid.Vnode, | |
621 | cwd->fid.Unique)); | |
622 | free(cwd); | |
623 | if (infids == NULL) | |
624 | fidstack_free(fids); | |
625 | return NULL; | |
626 | } | |
627 | afs_dprintf(("Lookup %s in %lu.%lu.%lu->%lu.%lu.%lu\n", p, | |
628 | cwd->fid.Volume, cwd->fid.Vnode, cwd->fid.Unique, | |
629 | ret->fid.Volume, ret->fid.Vnode, ret->fid.Unique)); | |
630 | linkcount = 0; | |
631 | ||
632 | retry: | |
633 | if ((ret->fid.Vnode & 1) == 0) { /* not a directory; check for link */ | |
634 | code = afscp_GetStatus(ret, &s); | |
635 | if (code != 0) { | |
636 | if (infids == NULL) | |
637 | fidstack_free(fids); | |
638 | free(cwd); | |
639 | free(ret); | |
640 | return NULL; | |
641 | } | |
642 | if (s.FileType == SymbolicLink) { | |
643 | if (linkcount++ > 5) { | |
644 | afscp_errno = ELOOP; | |
645 | if (infids == NULL) | |
646 | fidstack_free(fids); | |
647 | free(cwd); | |
648 | free(ret); | |
649 | return NULL; | |
650 | } | |
651 | ret = | |
652 | afscp_HandleLink(ret, cwd, fids, follow, &s, | |
653 | (q == NULL)); | |
654 | if (ret == NULL) { | |
655 | free(cwd); | |
656 | if (infids == NULL) | |
657 | fidstack_free(fids); | |
658 | return NULL; | |
659 | } | |
660 | afs_dprintf((" ....-> %lu.%lu.%lu\n", ret->fid.Volume, | |
661 | ret->fid.Vnode, ret->fid.Unique)); | |
662 | goto retry; | |
663 | } else { | |
664 | if (q != NULL) { | |
665 | afscp_errno = ENOTDIR; | |
666 | free(cwd); | |
667 | free(ret); | |
668 | if (infids == NULL) | |
669 | fidstack_free(fids); | |
670 | return NULL; | |
671 | } | |
672 | } | |
673 | } | |
674 | fidstack_push(fids, cwd); | |
675 | } | |
676 | cwd = ret; | |
677 | ||
678 | while ((q != NULL) && (*q == '/')) | |
679 | q++; | |
680 | p = q; | |
681 | } | |
682 | if (infids == NULL) | |
683 | fidstack_free(fids); | |
684 | return ret; | |
685 | } | |
686 | ||
687 | /*! | |
688 | * Resolve a path to a FID starting from the root volume | |
689 | * | |
690 | * \param[in] path full path | |
691 | * | |
692 | * \post Returns a venusfid representing the final element of path | |
693 | * | |
694 | * \note There are three cases: | |
695 | * (1) begins with /afs: start in root.afs of cell or home cell | |
696 | * (2) else begins with /: error | |
697 | * (3) else start in root.cell of cell or home cell | |
698 | */ | |
699 | struct afscp_venusfid * | |
700 | afscp_ResolvePath(const char *path) | |
701 | { | |
702 | struct afscp_venusfid *root, *ret; | |
703 | struct afscp_cell *cell; | |
704 | int code; | |
705 | char *p, *q; | |
706 | p = strdup(path); /* so we can modify the string */ | |
707 | if (p == NULL) { | |
708 | afscp_errno = ENOMEM; | |
709 | return NULL; | |
710 | } | |
711 | cell = afscp_DefaultCell(); | |
712 | if (cell == NULL) { | |
713 | free(p); | |
714 | afscp_errno = EINVAL; | |
715 | return NULL; | |
716 | } | |
717 | code = gettoproot(cell, p, &q, &root); | |
718 | if (code != 0) { | |
719 | free(p); | |
720 | return NULL; | |
721 | } | |
722 | if (q && *q) { | |
723 | ret = _ResolvePath(root, 0, q, 1); | |
724 | free(root); | |
725 | } else | |
726 | ret = root; | |
727 | free(p); | |
728 | return ret; | |
729 | } | |
730 | ||
731 | /*! | |
732 | * Resolve a path to a FID starting from the given volume | |
733 | * | |
734 | * \param[in] v volume structure containing id and cell info | |
735 | * \param[in] path path relative to volume v | |
736 | * | |
737 | * \post Returns a venusfid representing the final element of path | |
738 | */ | |
739 | struct afscp_venusfid * | |
740 | afscp_ResolvePathFromVol(const struct afscp_volume *v, const char *path) | |
741 | { | |
742 | struct afscp_venusfid *root, *ret; | |
743 | char *origp, *p; | |
744 | ||
745 | /* so we can modify the string */ | |
746 | origp = p = strdup(path); | |
747 | if (p == NULL) { | |
748 | afscp_errno = ENOMEM; | |
749 | return NULL; | |
750 | } | |
751 | root = afscp_MakeFid(v->cell, v->id, 1, 1); | |
752 | while (*p == '/') | |
753 | p++; | |
754 | if (*p != '\0') { | |
755 | ret = _ResolvePath(root, 0, p, 1); | |
756 | free(root); | |
757 | } else | |
758 | ret = root; | |
759 | free(origp); | |
760 | return ret; | |
761 | } |