2 Copyright (C) 2003 - 2010 Chaskiel Grundman
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
9 1. Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
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.
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.
27 #include <afsconfig.h>
28 #include <afs/param.h>
33 #include "afscp_search.h"
36 #include <afs/vlserver.h>
37 #include <afs/vldbint.h>
40 #include <afs/errmap_nt.h>
43 #include "afscp_internal.h"
45 static int dirmode
= DIRMODE_CELL
;
48 afscp_SetDirMode(int mode
)
50 if ((mode
!= DIRMODE_CELL
) && (mode
!= DIRMODE_DYNROOT
)) {
58 /* comparison function for tsearch */
60 dircompare(const void *a
, const void *b
)
62 const struct afscp_dircache
*sa
= a
, *sb
= b
;
63 if (sa
->me
.fid
.Vnode
< sb
->me
.fid
.Vnode
)
65 if (sa
->me
.fid
.Vnode
> sb
->me
.fid
.Vnode
)
67 if (sa
->me
.fid
.Unique
< sb
->me
.fid
.Unique
)
69 if (sa
->me
.fid
.Unique
> sb
->me
.fid
.Unique
)
74 /* make sure the dirstream contains the most up to date directory contents */
76 _DirUpdate(struct afscp_dirstream
*d
)
78 struct AFSFetchStatus s
;
80 struct afscp_volume
*v
;
81 struct afscp_dircache key
, *stored
;
85 code
= afscp_GetStatus(&d
->fid
, &s
);
90 if (d
->dirbuffer
&& d
->dv
== s
.DataVersion
) {
93 v
= afscp_VolumeById(d
->fid
.cell
, d
->fid
.fid
.Volume
);
99 memcpy(&key
.me
, &d
->fid
, sizeof(struct afscp_venusfid
));
100 cached
= tfind(&key
, &v
->dircache
, dircompare
);
101 if (cached
!= NULL
) {
102 stored
= *(struct afscp_dircache
**)cached
;
103 if (d
->dv
== s
.DataVersion
) {
104 d
->dirbuffer
= stored
->dirbuffer
;
105 d
->buflen
= stored
->buflen
;
109 pthread_mutex_lock(&(stored
->mtx
));
110 tdelete(&key
, &v
->dircache
, dircompare
);
112 while (stored
->nwaiters
> 1) {
113 pthread_cond_wait(&(stored
->cv
), &(stored
->mtx
));
116 pthread_cond_destroy(&(stored
->cv
));
117 pthread_mutex_unlock(&(stored
->mtx
));
118 pthread_mutex_destroy(&(stored
->mtx
));
119 if (d
->dirbuffer
!= stored
->dirbuffer
)
120 free(stored
->dirbuffer
);
123 if (s
.Length
> BIGMAXPAGES
* AFS_PAGESIZE
) {
127 if (d
->buflen
!= s
.Length
) {
130 new = realloc(d
->dirbuffer
, s
.Length
);
132 new = malloc(s
.Length
);
137 afscp_errno
= ENOMEM
;
140 d
->buflen
= s
.Length
;
143 code
= afscp_PRead(&d
->fid
, d
->dirbuffer
, s
.Length
, 0);
147 d
->dv
= s
.DataVersion
;
148 cached
= tsearch(&key
, &v
->dircache
, dircompare
);
149 if (cached
!= NULL
) {
150 stored
= malloc(sizeof(struct afscp_dircache
));
151 if (stored
!= NULL
) {
152 memcpy(&stored
->me
, &d
->fid
, sizeof(struct afscp_venusfid
));
153 stored
->buflen
= d
->buflen
;
154 stored
->dirbuffer
= d
->dirbuffer
;
156 stored
->nwaiters
= 0;
157 pthread_mutex_init(&(stored
->mtx
), NULL
);
158 pthread_cond_init(&(stored
->cv
), NULL
);
159 *(struct afscp_dircache
**)cached
= stored
;
161 tdelete(&key
, &v
->dircache
, dircompare
);
167 static struct DirEntry
*
168 dir_get_entry(struct afscp_dirstream
*d
, int entry
)
170 struct DirHeader
*h
= (struct DirHeader
*)d
->dirbuffer
;
171 /* struct PageHeader *p; */
172 struct DirEntry
*ret
;
177 off
= entry
& (EPP
- 1);
179 if (pg
* AFS_PAGESIZE
>= d
->buflen
) { /* beyond end of file */
182 if (!off
|| (!pg
&& off
< DHE
+ 1)) { /* offset refers to metadata */
185 if (pg
< MAXPAGES
&& h
->alloMap
[pg
] == EPP
) { /* page is empty */
188 /* p = (struct PageHeader *)&d->dirbuffer[pg * AFS_PAGESIZE]; */
189 /* p is set but not referenced later */
190 /* fr = p->freebitmap[off >> 8]; */
191 /* fr is set but not referenced later */
192 ret
= (struct DirEntry
*)&d
->dirbuffer
[pg
* AFS_PAGESIZE
+ 32 * off
];
196 struct afscp_dirstream
*
197 afscp_OpenDir(const struct afscp_venusfid
*fid
)
199 struct afscp_dirstream
*ret
;
200 struct AFSFetchStatus s
;
203 code
= afscp_GetStatus(fid
, &s
);
208 if (s
.FileType
!= Directory
) {
209 afscp_errno
= ENOTDIR
;
212 ret
= malloc(sizeof(struct afscp_dirstream
));
214 afscp_errno
= ENOMEM
;
217 memset(ret
, 0, sizeof(struct afscp_dirstream
));
218 memmove(&ret
->fid
, fid
, sizeof(struct afscp_venusfid
));
219 code
= _DirUpdate(ret
);
230 struct afscp_dirent
*
231 afscp_ReadDir(struct afscp_dirstream
*d
)
233 struct DirHeader
*h
= (struct DirHeader
*)d
->dirbuffer
;
234 struct DirEntry
*info
;
239 while (ent
== 0 && d
->hashent
< NHASHENT
- 1) {
241 ent
= ntohs(h
->hashTable
[d
->hashent
]);
247 info
= dir_get_entry(d
, ent
);
252 d
->ret
.vnode
= ntohl(info
->fid
.vnode
);
253 d
->ret
.unique
= ntohl(info
->fid
.vunique
);
254 strlcpy(d
->ret
.name
, info
->name
, sizeof(d
->ret
.name
)); /* guaranteed to be NULL terminated? */
255 d
->entry
= ntohs(info
->next
);
260 /* as it calls _DirUpdate, this may corrupt any previously returned dirent's */
262 afscp_RewindDir(struct afscp_dirstream
*d
)
271 afscp_CloseDir(struct afscp_dirstream
*d
)
278 namehash(const char *name
)
283 while (*name
!= '\0')
284 hval
= (hval
* 173) + *name
++;
285 tval
= hval
& (NHASHENT
- 1);
286 return tval
? (hval
< 0 ? NHASHENT
- tval
: tval
)
290 struct afscp_venusfid
*
291 afscp_DirLookup(struct afscp_dirstream
*d
, const char *name
)
295 struct DirHeader
*h
= (struct DirHeader
*)d
->dirbuffer
;
296 struct DirEntry
*info
;
298 code
= _DirUpdate(d
);
302 hval
= namehash(name
);
303 entry
= ntohs(h
->hashTable
[hval
]);
306 info
= dir_get_entry(d
, entry
);
311 if (strcmp(info
->name
, name
) == 0)
313 entry
= ntohs(info
->next
);
316 return afscp_MakeFid(d
->fid
.cell
, d
->fid
.fid
.Volume
,
317 ntohl(info
->fid
.vnode
),
318 ntohl(info
->fid
.vunique
));
320 afscp_errno
= ENOENT
;
325 struct afscp_venusfid
*
326 afscp_ResolveName(const struct afscp_venusfid
*dir
, const char *name
)
328 struct afscp_venusfid
*ret
;
329 struct afscp_dirstream
*d
;
331 d
= afscp_OpenDir(dir
);
335 ret
= afscp_DirLookup(d
, name
);
341 gettoproot(struct afscp_cell
*cell
, char *p
, char **q
,
342 struct afscp_venusfid
**root
)
344 struct afscp_volume
*rootvol
;
347 if (dirmode
== DIRMODE_DYNROOT
&& (strcmp(p
, "/afs") == 0)) {
348 afscp_errno
= EINVAL
;
351 if (strncmp(p
, "/afs", 4) == 0) {
352 afs_dprintf(("gettoproot: path is absolute\n"));
356 if (dirmode
== DIRMODE_DYNROOT
) {
369 if (*p
== '.' || *p
== 0) {
370 afscp_errno
= EINVAL
;
374 while (*r
&& *r
!= '/')
379 afs_dprintf(("gettoproot: dynroot looking up cell %s\n", p
));
380 cell
= afscp_CellByName(p
, NULL
);
382 afs_dprintf(("gettoproot: no such cell\n"));
383 afscp_errno
= ENODEV
;
386 rootvol
= afscp_VolumeByName(cell
, "root.cell", voltype
);
387 if (!rootvol
&& voltype
== ROVOL
)
388 rootvol
= afscp_VolumeByName(cell
, "root.cell", RWVOL
);
391 rootvol
= afscp_VolumeByName(cell
, "root.afs", ROVOL
);
393 rootvol
= afscp_VolumeByName(cell
, "root.afs", RWVOL
);
396 afs_dprintf(("gettoproot: volume not found\n"));
398 afs_dprintf(("gettoproot: path is relative\n"));
403 rootvol
= afscp_VolumeByName(cell
, "root.cell", ROVOL
);
405 rootvol
= afscp_VolumeByName(cell
, "root.cell", RWVOL
);
408 if (rootvol
== NULL
) {
409 afscp_errno
= ENODEV
;
412 *root
= afscp_MakeFid(cell
, rootvol
->id
, 1, 1);
417 getvolumeroot(struct afscp_cell
*cell
, int voltype
, const char *vname
,
418 struct afscp_venusfid
**root
)
420 struct afscp_volume
*vol
;
421 vol
= afscp_VolumeByName(cell
, vname
, voltype
);
422 if (!vol
&& voltype
== ROVOL
)
423 vol
= afscp_VolumeByName(cell
, vname
, RWVOL
);
425 afscp_errno
= ENODEV
;
428 *root
= afscp_MakeFid(cell
, vol
->id
, 1, 1);
432 typedef struct fidstack_s
{
435 struct afscp_venusfid
**entries
;
443 ret
= malloc(sizeof(struct fidstack_s
));
445 afscp_errno
= ENOMEM
;
450 ret
->entries
= malloc(ret
->alloc
* sizeof(struct afscp_venusfid
*));
451 if (ret
->entries
== NULL
) {
453 afscp_errno
= ENOMEM
;
460 fidstack_push(fidstack s
, struct afscp_venusfid
*entry
)
462 struct afscp_venusfid
**new;
463 if (s
->count
>= s
->alloc
) {
464 new = realloc(s
->entries
, (s
->alloc
+ 10) *
465 sizeof(struct afscp_venusfid
*));
472 s
->entries
[s
->count
++] = entry
;
476 static struct afscp_venusfid
*
477 fidstack_pop(fidstack s
)
480 return s
->entries
[--s
->count
];
485 fidstack_free(fidstack s
)
489 for (i
= 0; i
< s
->count
; i
++)
495 static struct afscp_venusfid
*_ResolvePath(const struct afscp_venusfid
*,
496 fidstack
, char *, int);
498 static struct afscp_venusfid
*
499 afscp_HandleLink(struct afscp_venusfid
*in
,
500 const struct afscp_venusfid
*parent
, fidstack fids
,
501 int follow
, const struct AFSFetchStatus
*s
, int terminal
)
503 char *linkbuf
, *linkbufq
;
504 struct afscp_cell
*cell
;
505 struct afscp_volume
*v
;
506 struct afscp_venusfid
*root
, *ret
;
510 if ((s
->UnixModeBits
& 0111) && (follow
== 0) && terminal
) { /* normal link */
513 linkbuf
= malloc(s
->Length
+ 1);
514 code
= afscp_PRead(in
, linkbuf
, s
->Length
, 0);
520 if (code
!= s
->Length
) {
526 linkbuf
[s
->Length
] = 0;
527 if (s
->UnixModeBits
& 0111) { /* normal link */
528 afs_dprintf(("Recursing on symlink %s...\n", linkbuf
));
529 if (linkbuf
[0] == '/') {
530 if (gettoproot(in
->cell
, linkbuf
, &linkbufq
, &root
)) {
536 ret
= _ResolvePath(root
, 0, linkbufq
, 0);
540 ret
= _ResolvePath(parent
, fids
, linkbuf
, 0);
543 } else { /* mountpoint */
544 afs_dprintf(("EvalMountPoint %s...\n", linkbuf
));
545 linkbufq
= strchr(linkbuf
, ':');
547 v
= afscp_VolumeById(cell
, in
->fid
.Volume
);
551 afscp_errno
= ENODEV
;
554 voltype
= v
->voltype
;
555 if (linkbuf
[0] == '%')
557 if (linkbufq
== NULL
) {
558 linkbufq
= linkbuf
+ 1;
561 cell
= afscp_CellByName(linkbuf
+ 1, NULL
);
562 if (linkbuf
[0] != '%')
567 afscp_errno
= ENODEV
;
570 len
= strnlen(linkbufq
, s
->Length
+ 1);
573 afscp_errno
= ENODEV
;
576 len
= strnlen(linkbufq
, s
->Length
+ 1);
577 linkbufq
[len
- 1] = 0; /* eliminate trailer */
578 if (getvolumeroot(cell
, voltype
, linkbufq
, &ret
)) {
587 static struct afscp_venusfid
*
588 _ResolvePath(const struct afscp_venusfid
*start
, fidstack infids
,
589 char *path
, int follow
)
591 struct afscp_venusfid
*ret
, *cwd
;
592 struct AFSFetchStatus s
;
599 ret
= cwd
= afscp_DupFid(start
);
602 fids
= fidstack_alloc();
611 if (strcmp(p
, ".") == 0) {
613 } else if (strcmp(p
, "..") == 0) {
614 ret
= fidstack_pop(fids
);
620 ret
= afscp_ResolveName(cwd
, p
);
622 afs_dprintf(("Lookup %s in %lu.%lu.%lu failed\n", p
,
623 cwd
->fid
.Volume
, cwd
->fid
.Vnode
,
630 afs_dprintf(("Lookup %s in %lu.%lu.%lu->%lu.%lu.%lu\n", p
,
631 cwd
->fid
.Volume
, cwd
->fid
.Vnode
, cwd
->fid
.Unique
,
632 ret
->fid
.Volume
, ret
->fid
.Vnode
, ret
->fid
.Unique
));
636 if ((ret
->fid
.Vnode
& 1) == 0) { /* not a directory; check for link */
637 code
= afscp_GetStatus(ret
, &s
);
645 if (s
.FileType
== SymbolicLink
) {
646 if (linkcount
++ > 5) {
655 afscp_HandleLink(ret
, cwd
, fids
, follow
, &s
,
663 afs_dprintf((" ....-> %lu.%lu.%lu\n", ret
->fid
.Volume
,
664 ret
->fid
.Vnode
, ret
->fid
.Unique
));
668 afscp_errno
= ENOTDIR
;
677 fidstack_push(fids
, cwd
);
681 while ((q
!= NULL
) && (*q
== '/'))
691 * Resolve a path to a FID starting from the root volume
693 * \param[in] path full path
695 * \post Returns a venusfid representing the final element of path
697 * \note There are three cases:
698 * (1) begins with /afs: start in root.afs of cell or home cell
699 * (2) else begins with /: error
700 * (3) else start in root.cell of cell or home cell
702 struct afscp_venusfid
*
703 afscp_ResolvePath(const char *path
)
705 struct afscp_venusfid
*root
, *ret
;
706 struct afscp_cell
*cell
;
709 p
= strdup(path
); /* so we can modify the string */
711 afscp_errno
= ENOMEM
;
714 cell
= afscp_DefaultCell();
716 afscp_errno
= EINVAL
;
719 code
= gettoproot(cell
, p
, &q
, &root
);
725 ret
= _ResolvePath(root
, 0, q
, 1);
734 * Resolve a path to a FID starting from the given volume
736 * \param[in] v volume structure containing id and cell info
737 * \param[in] path path relative to volume v
739 * \post Returns a venusfid representing the final element of path
741 struct afscp_venusfid
*
742 afscp_ResolvePathFromVol(const struct afscp_volume
*v
, const char *path
)
744 struct afscp_venusfid
*root
, *ret
;
747 /* so we can modify the string */
750 afscp_errno
= ENOMEM
;
753 root
= afscp_MakeFid(v
->cell
, v
->id
, 1, 1);
757 ret
= _ResolvePath(root
, 0, p
, 1);