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>
34 #include <afs/vlserver.h>
35 #include <afs/vldbint.h>
38 #include <afs/errmap_nt.h>
41 #include "afscp_internal.h"
43 static int dirmode
= DIRMODE_CELL
;
46 afscp_SetDirMode(int mode
)
48 if ((mode
!= DIRMODE_CELL
) && (mode
!= DIRMODE_DYNROOT
)) {
56 /* comparison function for tsearch */
58 dircompare(const void *a
, const void *b
)
60 const struct afscp_dircache
*sa
= a
, *sb
= b
;
61 if (sa
->me
.fid
.Vnode
< sb
->me
.fid
.Vnode
)
63 if (sa
->me
.fid
.Vnode
> sb
->me
.fid
.Vnode
)
65 if (sa
->me
.fid
.Unique
< sb
->me
.fid
.Unique
)
67 if (sa
->me
.fid
.Unique
> sb
->me
.fid
.Unique
)
72 /* make sure the dirstream contains the most up to date directory contents */
74 _DirUpdate(struct afscp_dirstream
*d
)
76 struct AFSFetchStatus s
;
78 struct afscp_volume
*v
;
79 struct afscp_dircache key
, *stored
;
83 code
= afscp_GetStatus(&d
->fid
, &s
);
88 if (d
->dirbuffer
&& d
->dv
== s
.DataVersion
) {
91 v
= afscp_VolumeById(d
->fid
.cell
, d
->fid
.fid
.Volume
);
97 memcpy(&key
.me
, &d
->fid
, sizeof(struct afscp_venusfid
));
98 cached
= tfind(&key
, &v
->dircache
, dircompare
);
100 stored
= *(struct afscp_dircache
**)cached
;
101 if (d
->dv
== s
.DataVersion
) {
102 d
->dirbuffer
= stored
->dirbuffer
;
103 d
->buflen
= stored
->buflen
;
107 pthread_mutex_lock(&(stored
->mtx
));
108 tdelete(&key
, &v
->dircache
, dircompare
);
110 while (stored
->nwaiters
> 1) {
111 pthread_cond_wait(&(stored
->cv
), &(stored
->mtx
));
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
);
121 if (s
.Length
> BIGMAXPAGES
* AFS_PAGESIZE
) {
125 if (d
->buflen
!= s
.Length
) {
128 new = realloc(d
->dirbuffer
, s
.Length
);
130 new = malloc(s
.Length
);
135 afscp_errno
= ENOMEM
;
138 d
->buflen
= s
.Length
;
141 code
= afscp_PRead(&d
->fid
, d
->dirbuffer
, s
.Length
, 0);
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
;
154 stored
->nwaiters
= 0;
155 pthread_mutex_init(&(stored
->mtx
), NULL
);
156 pthread_cond_init(&(stored
->cv
), NULL
);
157 *(struct afscp_dircache
**)cached
= stored
;
159 tdelete(&key
, &v
->dircache
, dircompare
);
165 static struct DirEntry
*
166 dir_get_entry(struct afscp_dirstream
*d
, int entry
)
168 struct DirHeader
*h
= (struct DirHeader
*)d
->dirbuffer
;
169 /* struct PageHeader *p; */
170 struct DirEntry
*ret
;
175 off
= entry
& (EPP
- 1);
177 if (pg
* AFS_PAGESIZE
>= d
->buflen
) { /* beyond end of file */
180 if (!off
|| (!pg
&& off
< DHE
+ 1)) { /* offset refers to metadata */
183 if (pg
< MAXPAGES
&& h
->alloMap
[pg
] == EPP
) { /* page is empty */
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
];
194 struct afscp_dirstream
*
195 afscp_OpenDir(const struct afscp_venusfid
*fid
)
197 struct afscp_dirstream
*ret
;
198 struct AFSFetchStatus s
;
201 code
= afscp_GetStatus(fid
, &s
);
206 if (s
.FileType
!= Directory
) {
207 afscp_errno
= ENOTDIR
;
210 ret
= calloc(1, sizeof(struct afscp_dirstream
));
212 afscp_errno
= ENOMEM
;
215 memmove(&ret
->fid
, fid
, sizeof(struct afscp_venusfid
));
216 code
= _DirUpdate(ret
);
227 struct afscp_dirent
*
228 afscp_ReadDir(struct afscp_dirstream
*d
)
230 struct DirHeader
*h
= (struct DirHeader
*)d
->dirbuffer
;
231 struct DirEntry
*info
;
236 while (ent
== 0 && d
->hashent
< NHASHENT
- 1) {
238 ent
= ntohs(h
->hashTable
[d
->hashent
]);
244 info
= dir_get_entry(d
, ent
);
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
);
257 /* as it calls _DirUpdate, this may corrupt any previously returned dirent's */
259 afscp_RewindDir(struct afscp_dirstream
*d
)
268 afscp_CloseDir(struct afscp_dirstream
*d
)
275 namehash(const char *name
)
280 while (*name
!= '\0')
281 hval
= (hval
* 173) + *name
++;
282 tval
= hval
& (NHASHENT
- 1);
283 return tval
? (hval
< 0 ? NHASHENT
- tval
: tval
)
287 struct afscp_venusfid
*
288 afscp_DirLookup(struct afscp_dirstream
*d
, const char *name
)
292 struct DirHeader
*h
= (struct DirHeader
*)d
->dirbuffer
;
293 struct DirEntry
*info
;
295 code
= _DirUpdate(d
);
299 hval
= namehash(name
);
300 entry
= ntohs(h
->hashTable
[hval
]);
303 info
= dir_get_entry(d
, entry
);
308 if (strcmp(info
->name
, name
) == 0)
310 entry
= ntohs(info
->next
);
313 return afscp_MakeFid(d
->fid
.cell
, d
->fid
.fid
.Volume
,
314 ntohl(info
->fid
.vnode
),
315 ntohl(info
->fid
.vunique
));
317 afscp_errno
= ENOENT
;
322 struct afscp_venusfid
*
323 afscp_ResolveName(const struct afscp_venusfid
*dir
, const char *name
)
325 struct afscp_venusfid
*ret
;
326 struct afscp_dirstream
*d
;
328 d
= afscp_OpenDir(dir
);
332 ret
= afscp_DirLookup(d
, name
);
338 gettoproot(struct afscp_cell
*cell
, char *p
, char **q
,
339 struct afscp_venusfid
**root
)
341 struct afscp_volume
*rootvol
;
344 if (dirmode
== DIRMODE_DYNROOT
&& (strcmp(p
, "/afs") == 0)) {
345 afscp_errno
= EINVAL
;
348 if (strncmp(p
, "/afs", 4) == 0) {
349 afs_dprintf(("gettoproot: path is absolute\n"));
353 if (dirmode
== DIRMODE_DYNROOT
) {
366 if (*p
== '.' || *p
== 0) {
367 afscp_errno
= EINVAL
;
371 while (*r
&& *r
!= '/')
376 afs_dprintf(("gettoproot: dynroot looking up cell %s\n", p
));
377 cell
= afscp_CellByName(p
, NULL
);
379 afs_dprintf(("gettoproot: no such cell\n"));
380 afscp_errno
= ENODEV
;
383 rootvol
= afscp_VolumeByName(cell
, "root.cell", voltype
);
384 if (!rootvol
&& voltype
== ROVOL
)
385 rootvol
= afscp_VolumeByName(cell
, "root.cell", RWVOL
);
388 rootvol
= afscp_VolumeByName(cell
, "root.afs", ROVOL
);
390 rootvol
= afscp_VolumeByName(cell
, "root.afs", RWVOL
);
393 afs_dprintf(("gettoproot: volume not found\n"));
395 afs_dprintf(("gettoproot: path is relative\n"));
400 rootvol
= afscp_VolumeByName(cell
, "root.cell", ROVOL
);
402 rootvol
= afscp_VolumeByName(cell
, "root.cell", RWVOL
);
405 if (rootvol
== NULL
) {
406 afscp_errno
= ENODEV
;
409 *root
= afscp_MakeFid(cell
, rootvol
->id
, 1, 1);
414 getvolumeroot(struct afscp_cell
*cell
, int voltype
, const char *vname
,
415 struct afscp_venusfid
**root
)
417 struct afscp_volume
*vol
;
418 vol
= afscp_VolumeByName(cell
, vname
, voltype
);
419 if (!vol
&& voltype
== ROVOL
)
420 vol
= afscp_VolumeByName(cell
, vname
, RWVOL
);
422 afscp_errno
= ENODEV
;
425 *root
= afscp_MakeFid(cell
, vol
->id
, 1, 1);
429 typedef struct fidstack_s
{
432 struct afscp_venusfid
**entries
;
440 ret
= malloc(sizeof(struct fidstack_s
));
442 afscp_errno
= ENOMEM
;
447 ret
->entries
= malloc(ret
->alloc
* sizeof(struct afscp_venusfid
*));
448 if (ret
->entries
== NULL
) {
450 afscp_errno
= ENOMEM
;
457 fidstack_push(fidstack s
, struct afscp_venusfid
*entry
)
459 struct afscp_venusfid
**new;
460 if (s
->count
>= s
->alloc
) {
461 new = realloc(s
->entries
, (s
->alloc
+ 10) *
462 sizeof(struct afscp_venusfid
*));
469 s
->entries
[s
->count
++] = entry
;
473 static struct afscp_venusfid
*
474 fidstack_pop(fidstack s
)
477 return s
->entries
[--s
->count
];
482 fidstack_free(fidstack s
)
486 for (i
= 0; i
< s
->count
; i
++)
492 static struct afscp_venusfid
*_ResolvePath(const struct afscp_venusfid
*,
493 fidstack
, char *, int);
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
)
500 char *linkbuf
, *linkbufq
;
501 struct afscp_cell
*cell
;
502 struct afscp_volume
*v
;
503 struct afscp_venusfid
*root
, *ret
;
507 if ((s
->UnixModeBits
& 0111) && (follow
== 0) && terminal
) { /* normal link */
510 linkbuf
= malloc(s
->Length
+ 1);
511 code
= afscp_PRead(in
, linkbuf
, s
->Length
, 0);
517 if (code
!= s
->Length
) {
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
)) {
533 ret
= _ResolvePath(root
, 0, linkbufq
, 0);
537 ret
= _ResolvePath(parent
, fids
, linkbuf
, 0);
540 } else { /* mountpoint */
541 afs_dprintf(("EvalMountPoint %s...\n", linkbuf
));
542 linkbufq
= strchr(linkbuf
, ':');
544 v
= afscp_VolumeById(cell
, in
->fid
.Volume
);
548 afscp_errno
= ENODEV
;
551 voltype
= v
->voltype
;
552 if (linkbuf
[0] == '%')
554 if (linkbufq
== NULL
) {
555 linkbufq
= linkbuf
+ 1;
558 cell
= afscp_CellByName(linkbuf
+ 1, NULL
);
559 if (linkbuf
[0] != '%')
564 afscp_errno
= ENODEV
;
567 len
= strnlen(linkbufq
, s
->Length
+ 1);
570 afscp_errno
= ENODEV
;
573 len
= strnlen(linkbufq
, s
->Length
+ 1);
574 linkbufq
[len
- 1] = 0; /* eliminate trailer */
575 if (getvolumeroot(cell
, voltype
, linkbufq
, &ret
)) {
584 static struct afscp_venusfid
*
585 _ResolvePath(const struct afscp_venusfid
*start
, fidstack infids
,
586 char *path
, int follow
)
588 struct afscp_venusfid
*ret
, *cwd
;
589 struct AFSFetchStatus s
;
596 ret
= cwd
= afscp_DupFid(start
);
599 fids
= fidstack_alloc();
608 if (strcmp(p
, ".") == 0) {
610 } else if (strcmp(p
, "..") == 0) {
611 ret
= fidstack_pop(fids
);
617 ret
= afscp_ResolveName(cwd
, p
);
619 afs_dprintf(("Lookup %s in %lu.%lu.%lu failed\n", p
,
620 cwd
->fid
.Volume
, cwd
->fid
.Vnode
,
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
));
633 if ((ret
->fid
.Vnode
& 1) == 0) { /* not a directory; check for link */
634 code
= afscp_GetStatus(ret
, &s
);
642 if (s
.FileType
== SymbolicLink
) {
643 if (linkcount
++ > 5) {
652 afscp_HandleLink(ret
, cwd
, fids
, follow
, &s
,
660 afs_dprintf((" ....-> %lu.%lu.%lu\n", ret
->fid
.Volume
,
661 ret
->fid
.Vnode
, ret
->fid
.Unique
));
665 afscp_errno
= ENOTDIR
;
674 fidstack_push(fids
, cwd
);
678 while ((q
!= NULL
) && (*q
== '/'))
688 * Resolve a path to a FID starting from the root volume
690 * \param[in] path full path
692 * \post Returns a venusfid representing the final element of path
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
699 struct afscp_venusfid
*
700 afscp_ResolvePath(const char *path
)
702 struct afscp_venusfid
*root
, *ret
;
703 struct afscp_cell
*cell
;
706 p
= strdup(path
); /* so we can modify the string */
708 afscp_errno
= ENOMEM
;
711 cell
= afscp_DefaultCell();
714 afscp_errno
= EINVAL
;
717 code
= gettoproot(cell
, p
, &q
, &root
);
723 ret
= _ResolvePath(root
, 0, q
, 1);
732 * Resolve a path to a FID starting from the given volume
734 * \param[in] v volume structure containing id and cell info
735 * \param[in] path path relative to volume v
737 * \post Returns a venusfid representing the final element of path
739 struct afscp_venusfid
*
740 afscp_ResolvePathFromVol(const struct afscp_volume
*v
, const char *path
)
742 struct afscp_venusfid
*root
, *ret
;
745 /* so we can modify the string */
746 origp
= p
= strdup(path
);
748 afscp_errno
= ENOMEM
;
751 root
= afscp_MakeFid(v
->cell
, v
->id
, 1, 1);
755 ret
= _ResolvePath(root
, 0, p
, 1);