#include "am.h"
#define NFS
#define NFSCLIENT
#include <unistd.h>
#include <sys/stat.h>
#include "mount.h"
#define RETRY_INTERVAL 2
static char *
afs_match(am_opts *fo)
{
char *p = fo->opt_rfs;
if (!fo->opt_rfs) {
plog(XLOG_USER, "auto: no mount point named (rfs:=)");
return 0;
}
if (!fo->opt_fs) {
plog(XLOG_USER, "auto: no map named (fs:=)");
return 0;
}
fo->opt_rfs = fo->opt_fs;
fo->opt_fs = p;
return strdup(fo->opt_rfs ? fo->opt_rfs : ".");
}
static int
mount_toplvl(char *dir, char *opts)
{
struct nfs_args nfs_args;
struct mntent mnt;
int retry;
struct sockaddr_in sin;
unsigned short port;
int flags;
nfs_fh *fhp;
char fs_hostname[HOST_NAME_MAX+1 + PATH_MAX+1];
const char *type = MOUNT_NFS;
bzero(&nfs_args, sizeof(nfs_args));
mnt.mnt_dir = dir;
mnt.mnt_fsname = pid_fsname;
mnt.mnt_type = "auto";
mnt.mnt_opts = opts;
mnt.mnt_freq = 0;
mnt.mnt_passno = 0;
retry = hasmntval(&mnt, "retry");
if (retry <= 0)
retry = 2;
fhp = root_fh(dir);
if (!fhp) {
plog(XLOG_FATAL, "Can't find root file handle for %s", dir);
return EINVAL;
}
nfs_args.fh = (void *)fhp;
nfs_args.fhsize = NFSX_V2FH;
nfs_args.version = NFS_ARGSVERSION;
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr = myipaddr;
if ((port = hasmntval(&mnt, "port"))) {
sin.sin_port = htons(port);
} else {
plog(XLOG_ERROR, "no port number specified for %s", dir);
return EINVAL;
}
nfs_args.addr = (struct sockaddr *)&sin;
nfs_args.addrlen = sizeof sin;
nfs_args.sotype = SOCK_DGRAM;
nfs_args.proto = 0;
#ifndef HOSTNAMESZ
#define SHORT_MOUNT_NAME
#endif
snprintf(fs_hostname, sizeof(fs_hostname), "amd:%ld",
foreground ? (long)mypid : (long)getppid());
nfs_args.hostname = fs_hostname;
#ifdef HOSTNAMESZ
if (strlen(fs_hostname) >= HOSTNAMESZ)
strlcpy(fs_hostname + HOSTNAMESZ - 3, "..", 3);
#endif
#ifdef NFSMNT_DUMBTIMR
nfs_args.flags |= NFSMNT_DUMBTIMR;
plog(XLOG_INFO, "defeating nfs window computation");
#endif
if ((nfs_args.timeo = hasmntval(&mnt, "timeo")))
nfs_args.flags |= NFSMNT_TIMEO;
if ((nfs_args.retrans = hasmntval(&mnt, "retrans")))
nfs_args.flags |= NFSMNT_RETRANS;
#ifdef NFSMNT_BIODS
if (nfs_args.biods = hasmntval(&mnt, "biods"))
nfs_args.flags |= NFSMNT_BIODS;
#endif
#if defined(NFSMNT_ACREGMIN) && defined(NFSMNT_ACREGMAX)
nfs_args.acregmin = nfs_args.acregmax = 1;
nfs_args.flags |= NFSMNT_ACREGMIN|NFSMNT_ACREGMAX;
#endif
if (hasmntopt(&mnt, "soft") != NULL)
nfs_args.flags |= NFSMNT_SOFT;
if (hasmntopt(&mnt, "intr") != NULL)
nfs_args.flags |= NFSMNT_INT;
flags = compute_mount_flags(&mnt);
return mount_fs(&mnt, flags, (caddr_t) &nfs_args, retry, type);
}
static void
afs_mkcacheref(mntfs *mf)
{
char *cache;
if (mf->mf_fo && mf->mf_fo->opt_cache)
cache = mf->mf_fo->opt_cache;
else
cache = "none";
mf->mf_private = mapc_find(mf->mf_info, cache);
mf->mf_prfree = mapc_free;
}
static int
root_mount(am_node *mp)
{
mntfs *mf = mp->am_mnt;
mf->mf_mount = strealloc(mf->mf_mount, pid_fsname);
mf->mf_private = mapc_find(mf->mf_info, "");
mf->mf_prfree = mapc_free;
return 0;
}
static int
afs_mount(am_node *mp)
{
mntfs *mf = mp->am_mnt;
mp->am_parent->am_fattr.nlink++;
if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0')
mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info);
if (mf->mf_fo->opt_pref) {
mp->am_pref = strdup(mf->mf_fo->opt_pref);
} else {
char *ppref = mp->am_parent->am_pref;
if (ppref == 0)
ppref = "";
mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/");
}
afs_mkcacheref(mf);
return 0;
}
static int
toplvl_mount(am_node *mp)
{
mntfs *mf = mp->am_mnt;
struct stat stb;
char opts[256];
int error;
char *mnttype;
if (stat(mp->am_path, &stb) < 0) {
return errno;
} else if ((stb.st_mode & S_IFMT) != S_IFDIR) {
plog(XLOG_WARNING, "%s is not a directory", mp->am_path);
return ENOTDIR;
}
if (mf->mf_ops == &toplvl_ops) mnttype = "indirect";
else if (mf->mf_ops == &dfs_ops) mnttype = "direct";
#ifdef HAS_UNION_FS
else if (mf->mf_ops == &union_ops) mnttype = "union";
#endif
else mnttype = "auto";
snprintf(opts, sizeof(opts),
"%s,%s,%s=%d,%s=%d,%s=%d,%s",
"intr",
"rw",
"port", nfs_port,
"timeo", afs_timeo,
"retrans", afs_retrans,
mnttype);
error = mount_toplvl(mf->mf_mount, opts);
if (error) {
errno = error;
plog(XLOG_FATAL, "mount_toplvl: %m");
return error;
}
return 0;
}
static void
toplvl_mounted(mntfs *mf)
{
afs_mkcacheref(mf);
}
#ifdef HAS_UNION_FS
static int
create_union_node(char *dir, void *arg)
{
if (strcmp(dir, "/defaults") != 0) {
int error = 0;
(void) toplvl_ops.lookuppn(arg, dir, &error, VLOOK_CREATE);
if (error > 0) {
errno = error;
plog(XLOG_ERROR, "Could not mount %s: %m", dir);
}
return error;
}
return 0;
}
static void
union_mounted(mntfs *mf)
{
int i;
afs_mkcacheref(mf);
for (i = 0; i <= last_used_map; i++) {
am_node *mp = exported_ap[i];
if (mp && mp->am_mnt == mf) {
(void) mapc_keyiter((mnt_map *) mp->am_mnt->mf_private,
(void (*)(char *, void *)) create_union_node, mp);
break;
}
}
#ifdef notdef
mapc_free(mf->mf_private);
mf->mf_private = mapc_find(mf->mf_info, "inc");
#endif
}
#endif
static int
afs_umount(am_node *mp)
{
return 0;
}
static int
toplvl_umount(am_node *mp)
{
int error;
struct stat stb;
again:
if (lstat(mp->am_path, &stb) < 0) {
#ifdef DEBUG
dlog("lstat(%s): %m", mp->am_path);
#endif
}
error = umount_fs(mp->am_path);
if (error == EBUSY) {
plog(XLOG_WARNING, "afs_unmount retrying %s in 1s", mp->am_path);
sleep(1);
goto again;
}
return error;
}
static void
afs_umounted(am_node *mp)
{
if (mp->am_parent && mp->am_parent->am_parent)
--mp->am_parent->am_fattr.nlink;
}
struct continuation {
char **ivec;
am_node *mp;
char *key;
char *info;
char **xivec;
char *auto_opts;
am_opts fs_opts;
char *def_opts;
int retry;
int tried;
time_t start;
int callout;
};
#define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)
static void
free_continuation(struct continuation *cp)
{
if (cp->callout)
untimeout(cp->callout);
free(cp->key);
free(cp->xivec);
free(cp->info);
free(cp->auto_opts);
free(cp->def_opts);
free_opts(&cp->fs_opts);
free(cp);
}
static int afs_bgmount(struct continuation *, int);
static void
assign_error_mntfs(am_node *mp)
{
if (mp->am_error > 0) {
int error = mp->am_error;
if (error <= 0)
error = mp->am_mnt->mf_error;
free_mntfs(mp->am_mnt);
mp->am_mnt = new_mntfs();
mp->am_mnt->mf_error = error;
mp->am_mnt->mf_flags |= MFF_ERROR;
mp->am_error = 0;
}
}
static void
afs_cont(int rc, int term, void *closure)
{
struct continuation *cp = (struct continuation *) closure;
mntfs *mf = cp->mp->am_mnt;
mf->mf_flags &= ~MFF_MOUNTING;
new_ttl(cp->mp);
wakeup(mf);
if (rc || term) {
am_node *xmp;
if (term) {
mf->mf_error = EIO;
mf->mf_flags |= MFF_ERROR;
plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term);
} else {
mf->mf_error = rc;
mf->mf_flags |= MFF_ERROR;
errno = rc;
plog(XLOG_ERROR, "%s: mount (afs_cont): %m", cp->mp->am_path);
}
amd_stats.d_merr++;
cp->ivec++;
xmp = cp->mp;
(void) afs_bgmount(cp, 0);
assign_error_mntfs(xmp);
} else {
am_mounted(cp->mp);
free_continuation(cp);
}
reschedule_timeout_mp();
}
static void
afs_retry(int rc, int term, void *closure)
{
struct continuation *cp = (struct continuation *) closure;
int error = 0;
#ifdef DEBUG
dlog("Commencing retry for mount of %s", cp->mp->am_path);
#endif
new_ttl(cp->mp);
if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path);
error = ETIMEDOUT;
while (*cp->ivec)
cp->ivec++;
}
if (error || !IN_PROGRESS(cp)) {
(void) afs_bgmount(cp, error);
}
reschedule_timeout_mp();
}
static int
try_mount(void *mvp)
{
int error;
am_node *mp = (am_node *) mvp;
mntfs *mf = mp->am_mnt;
if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) {
error = mkdirs(mf->mf_mount, 0555);
if (!error)
mf->mf_flags |= MFF_MKMNT;
}
error = mount_node(mp);
#ifdef DEBUG
if (error > 0) {
errno = error;
dlog("afs call to mount_node failed: %m");
}
#endif
return error;
}
static int
afs_bgmount(struct continuation *cp, int mpe)
{
mntfs *mf = cp->mp->am_mnt;
mntfs *mf_retry = 0;
int this_error = -1;
int hard_error = -1;
int mp_error = mpe;
for (; this_error && *cp->ivec; cp->ivec++) {
am_ops *p;
am_node *mp = cp->mp;
char *link_dir;
int dont_retry;
if (hard_error < 0)
hard_error = this_error;
this_error = -1;
if (**cp->ivec == '-') {
if (cp->auto_opts && *cp->auto_opts)
cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec+1);
else
cp->def_opts = strealloc(cp->def_opts, *cp->ivec+1);
#ifdef DEBUG
dlog("Setting def_opts to \"%s\"", cp->def_opts);
#endif
continue;
}
if (strcmp(*cp->ivec, "/") == 0 || strcmp(*cp->ivec, "||") == 0) {
if (cp->tried) {
#ifdef DEBUG
dlog("Cut: not trying any more locations for %s",
mp->am_path);
#endif
break;
}
continue;
}
#ifdef SUNOS4_COMPAT
#ifdef nomore
if (strchr(*cp->ivec, '=') == 0)
p = sunos4_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
else
#endif
#endif
p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts, cp->fs_opts.opt_fs,
cp->fs_opts.fs_mtab, cp->auto_opts, cp->fs_opts.opt_opts, cp->fs_opts.opt_remopts);
p = mf->mf_ops;
#ifdef DEBUG
dlog("Got a hit with %s", p->fs_type);
#endif
if (p == &efs_ops) {
plog(XLOG_MAP, "Map entry %s for %s failed to match", *cp->ivec, mp->am_path);
if (this_error <= 0)
this_error = ENOENT;
continue;
} else {
if (cp->fs_opts.fs_mtab) {
plog(XLOG_MAP, "Trying mount of %s on %s fstype %s",
cp->fs_opts.fs_mtab, mp->am_path, p->fs_type);
}
cp->tried = TRUE;
}
this_error = 0;
dont_retry = FALSE;
if (mp->am_link) {
free(mp->am_link);
mp->am_link = 0;
}
link_dir = mf->mf_fo->opt_sublink;
if (link_dir && *link_dir) {
if (*link_dir == '/') {
mp->am_link = strdup(link_dir);
} else {
mp->am_link = str3cat((char *) 0,
mf->mf_fo->opt_fs, "/", link_dir);
normalize_slash(mp->am_link);
}
}
if (mf->mf_error > 0) {
this_error = mf->mf_error;
} else if (mf->mf_flags & (MFF_MOUNTING|MFF_UNMOUNTING)) {
#ifdef DEBUG
dlog("Duplicate pending mount fstype %s", p->fs_type);
#endif
this_error = -1;
} else if (FSRV_ISDOWN(mf->mf_server)) {
#ifdef DEBUG
dlog("%s is already hung - giving up", mf->mf_mount);
#endif
mp_error = EWOULDBLOCK;
dont_retry = TRUE;
this_error = -1;
} else if (mf->mf_flags & MFF_MOUNTED) {
#ifdef DEBUG
dlog("duplicate mount of \"%s\" ...", mf->mf_info);
#endif
am_mounted(mp);
this_error = 0;
break;
}
if (!this_error) {
if (mf->mf_ops->fs_flags & FS_DIRECTORY)
mk_fattr(mp, NFDIR);
else
mk_fattr(mp, NFLNK);
mp->am_fattr.fileid = mp->am_gen;
if (p->fs_init)
this_error = (*p->fs_init)(mf);
}
if (!FSRV_ISUP(mf->mf_server)) {
#ifdef DEBUG
dlog("waiting for server %s to become available", mf->mf_server->fs_host);
#endif
this_error = -1;
}
if (!this_error && mf->mf_fo->opt_delay) {
int i = atoi(mf->mf_fo->opt_delay);
if (i > 0 && clocktime() < (cp->start + i)) {
#ifdef DEBUG
dlog("Mount of %s delayed by %ds", mf->mf_mount, i - clocktime() + cp->start);
#endif
this_error = -1;
}
}
if (this_error < 0 && !dont_retry) {
if (!mf_retry)
mf_retry = dup_mntfs(mf);
cp->retry = TRUE;
}
if (!this_error) {
if ((p->fs_flags & FS_MBACKGROUND)) {
mf->mf_flags |= MFF_MOUNTING;
#ifdef DEBUG
dlog("backgrounding mount of \"%s\"", mf->mf_mount);
#endif
if (cp->callout) {
untimeout(cp->callout);
cp->callout = 0;
}
run_task(try_mount, mp, afs_cont, cp);
mf->mf_flags |= MFF_MKMNT;
if (mf_retry) free_mntfs(mf_retry);
return -1;
} else {
#ifdef DEBUG
dlog("foreground mount of \"%s\" ...", mf->mf_info);
#endif
this_error = try_mount(mp);
if (this_error < 0) {
if (!mf_retry)
mf_retry = dup_mntfs(mf);
cp->retry = TRUE;
}
}
}
if (this_error >= 0) {
if (this_error > 0) {
amd_stats.d_merr++;
if (mf != mf_retry) {
mf->mf_error = this_error;
mf->mf_flags |= MFF_ERROR;
}
}
wakeup(mf);
}
}
if (this_error && cp->retry) {
free_mntfs(mf);
mf = cp->mp->am_mnt = mf_retry;
cp->retry = FALSE;
cp->tried = FALSE;
cp->ivec = cp->xivec;
cp->def_opts = strealloc(cp->def_opts, cp->auto_opts);
#ifdef DEBUG
dlog("Arranging to retry mount of %s", cp->mp->am_path);
#endif
sched_task(afs_retry, cp, mf);
if (cp->callout)
untimeout(cp->callout);
cp->callout = timeout(RETRY_INTERVAL, wakeup, mf);
cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
return -1;
}
if (hard_error < 0 || this_error == 0)
hard_error = this_error;
if (mf_retry) {
if (hard_error)
plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
free_mntfs(mf_retry);
}
if (hard_error < 0 && mp_error)
hard_error = cp->mp->am_error = mp_error;
if (hard_error > 0) {
switch (hard_error) {
case ETIMEDOUT:
case EWOULDBLOCK:
cp->mp->am_timeo = 5;
break;
default:
cp->mp->am_timeo = 17;
break;
}
new_ttl(cp->mp);
}
if (mf->mf_error < 0) {
mf->mf_error = hard_error;
if (hard_error)
mf->mf_flags |= MFF_ERROR;
}
free_continuation(cp);
return hard_error;
}
static am_node *
afs_lookuppn(am_node *mp, char *fname, int *error_return, int op)
{
#define ereturn(x) { *error_return = x; return 0; }
am_node *ap, *new_mp, *ap_hung;
char *info;
char **ivec, **xivec;
char *auto_opts;
int error = 0;
char path_name[PATH_MAX];
char *pfname;
struct continuation *cp;
int in_progress = 0;
char *dflts;
mntfs *mf;
#ifdef DEBUG
dlog("in afs_lookuppn");
#endif
if (amd_state == Finishing) {
#ifdef DEBUG
if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &dfs_ops)
dlog("%s mount ignored - going down", fname);
else
dlog("%s/%s mount ignored - going down", mp->am_path, fname);
#endif
ereturn(ENOENT);
}
if (fname[0] == '.') {
if (fname[1] == '\0')
return mp;
if (fname[1] == '.' && fname[2] == '\0') {
if (mp->am_parent) {
#ifdef DEBUG
dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
#endif
return mp->am_parent;
}
ereturn(ESTALE);
}
}
if (!valid_key(fname)) {
plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
ereturn(ENOENT);
}
fname = expand_key(fname);
for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
if (FSTREQ(ap->am_name, fname)) {
mf = ap->am_mnt;
if (ap->am_error) {
error = ap->am_error;
continue;
}
if (mf->mf_error < 0)
goto in_progrss;
if (FSRV_ISDOWN(mf->mf_server)) {
#ifdef DEBUG
dlog("server hung");
#endif
error = ap->am_error;
ap_hung = ap;
continue;
}
if (mf->mf_flags & MFF_ERROR) {
error = mf->mf_error;
continue;
}
if (!(mf->mf_flags & MFF_MOUNTED) ) {
in_progrss:
#ifdef DEBUG
dlog("ignoring mount of %s in %s -- in progress",
fname, mf->mf_mount);
#endif
in_progress++;
continue;
}
#ifdef DEBUG
dlog("matched %s in %s", fname, ap->am_path);
#endif
free(fname);
return ap;
}
}
if (in_progress) {
#ifdef DEBUG
dlog("Waiting while %d mount(s) in progress", in_progress);
#endif
free(fname);
ereturn(-1);
}
if (error) {
#ifdef DEBUG
errno = error;
dlog("Returning error: %m", error);
#endif
free(fname);
ereturn(error);
}
switch (op) {
case VLOOK_DELETE:
ereturn(ENOENT);
break;
case VLOOK_CREATE:
break;
default:
plog(XLOG_FATAL, "Unknown op to afs_lookuppn: 0x%x", op);
ereturn(EINVAL);
break;
}
if ((int)amd_state >= (int)Finishing) {
#ifdef DEBUG
dlog("not found - server going down anyway");
#endif
free(fname);
ereturn(ENOENT);
}
if (mp->am_pref) {
snprintf(path_name, sizeof(path_name), "%s%s", mp->am_pref, fname);
pfname = path_name;
} else {
pfname = fname;
}
mf = mp->am_mnt;
#ifdef DEBUG
dlog("will search map info in %s to find %s", mf->mf_info, pfname);
#endif
error = mapc_search((mnt_map*) mf->mf_private, pfname, &info);
if (error) {
if (error > 0)
plog(XLOG_MAP, "No map entry for %s", pfname);
else
plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
free(fname);
ereturn(error);
}
#ifdef DEBUG
dlog("mount info is %s", info);
#endif
xivec = ivec = strsplit(info, ' ', '\"');
if (ap_hung)
error = EWOULDBLOCK;
else
error = ENOENT;
new_mp = exported_ap_alloc();
if (new_mp == 0) {
free(xivec);
free(info);
free(fname);
ereturn(ENOSPC);
}
if (mf->mf_auto)
auto_opts = mf->mf_auto;
else
auto_opts = "";
auto_opts = strdup(auto_opts);
#ifdef DEBUG
dlog("searching for /defaults entry");
#endif
if (mapc_search((mnt_map*) mf->mf_private, "/defaults", &dflts) == 0) {
char *dfl;
char **rvec;
#ifdef DEBUG
dlog("/defaults gave %s", dflts);
#endif
if (*dflts == '-')
dfl = dflts+1;
else
dfl = dflts;
rvec = strsplit(dfl, ' ', '\"');
dfl = rvec[0];
if (dfl) {
if (rvec[1]) {
#ifdef DEBUG
dlog("/defaults chopped into %s", dfl);
#endif
plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
}
if (*auto_opts && *dfl) {
char *nopts = xmalloc(strlen(auto_opts)+strlen(dfl)+2);
snprintf(nopts,
strlen(auto_opts) + strlen(dfl) + 2,
"%s;%s", dfl, auto_opts);
free(auto_opts);
auto_opts = nopts;
} else if (*dfl) {
auto_opts = strealloc(auto_opts, dfl);
}
}
free(dflts);
free(rvec);
}
init_map(new_mp, fname);
insert_am(new_mp, mp);
new_mp->am_path = str3cat(new_mp->am_path,
mf->mf_ops == &dfs_ops ? "" : mp->am_path,
*fname == '/' ? "" : "/", fname);
#ifdef DEBUG
dlog("setting path to %s", new_mp->am_path);
#endif
pfname = strdup(pfname);
cp = ALLOC(continuation);
cp->mp = new_mp;
cp->xivec = xivec;
cp->ivec = ivec;
cp->info = info;
cp->key = pfname;
cp->auto_opts = auto_opts;
cp->retry = FALSE;
cp->tried = FALSE;
cp->start = clocktime();
cp->def_opts = strdup(auto_opts);
bzero(&cp->fs_opts, sizeof(cp->fs_opts));
error = afs_bgmount(cp, error);
reschedule_timeout_mp();
if (!error) {
free(fname);
return new_mp;
}
if (error && (new_mp->am_mnt->mf_ops == &efs_ops))
new_mp->am_error = error;
assign_error_mntfs(new_mp);
free(fname);
ereturn(error);
#undef ereturn
}
static am_node *
next_nonerror_node(am_node *xp)
{
mntfs *mf;
while (xp &&
(!(mf = xp->am_mnt) ||
mf->mf_error != 0 ||
xp->am_error != 0 ||
!(mf->mf_flags & MFF_MOUNTED) ||
(mf->mf_server->fs_flags & FSF_DOWN))
)
xp = xp->am_osib;
return xp;
}
static int
afs_readdir(am_node *mp, nfscookie cookie, struct dirlist *dp,
struct entry *ep, int count)
{
unsigned int gen = *(unsigned int*) cookie;
am_node *xp;
dp->eof = FALSE;
if (gen == 0) {
#ifdef DEBUG
dlog("default search");
#endif
if (count <
(2 * (2 * (sizeof(*ep) + sizeof("..") + 4)
+ sizeof(*dp))))
return EINVAL;
xp = next_nonerror_node(mp->am_child);
dp->entries = ep;
ep[0].fileid = mp->am_gen;
ep[0].name = ".";
ep[0].nextentry = &ep[1];
*(unsigned int *) ep[0].cookie = 0;
if (mp->am_parent)
ep[1].fileid = mp->am_parent->am_gen;
else
ep[1].fileid = mp->am_gen;
ep[1].name = "..";
ep[1].nextentry = 0;
*(unsigned int *) ep[1].cookie =
xp ? xp->am_gen : ~(unsigned int)0;
if (!xp) dp->eof = TRUE;
return 0;
}
#ifdef DEBUG
dlog("real child");
#endif
if (gen == ~(unsigned int)0) {
#ifdef DEBUG
dlog("End of readdir in %s", mp->am_path);
#endif
dp->eof = TRUE;
dp->entries = 0;
return 0;
}
xp = mp->am_child;
while (xp && xp->am_gen != gen)
xp = xp->am_osib;
if (xp) {
int nbytes = count / 2;
int todo = MAX_READDIR_ENTRIES;
dp->entries = ep;
do {
am_node *xp_next = next_nonerror_node(xp->am_osib);
if (xp_next) {
*(unsigned int *) ep->cookie = xp_next->am_gen;
} else {
*(unsigned int *) ep->cookie = ~(unsigned int)0;
dp->eof = TRUE;
}
ep->fileid = xp->am_gen;
ep->name = xp->am_name;
nbytes -= sizeof(*ep) + strlen(xp->am_name) + 1;
xp = xp_next;
if (nbytes > 0 && !dp->eof && todo > 1) {
ep->nextentry = ep + 1;
ep++;
--todo;
} else {
todo = 0;
}
} while (todo > 0);
ep->nextentry = 0;
return 0;
}
return ESTALE;
}
static am_node *
dfs_readlink(am_node *mp, int *error_return)
{
am_node *xp;
int rc = 0;
xp = next_nonerror_node(mp->am_child);
if (!xp) {
if (!mp->am_mnt->mf_private)
afs_mkcacheref(mp->am_mnt);
xp = afs_lookuppn(mp, mp->am_path+1, &rc, VLOOK_CREATE);
}
if (xp) {
new_ttl(xp);
return xp;
}
if (amd_state == Finishing)
rc = ENOENT;
*error_return = rc;
return 0;
}
am_ops root_ops = {
"root",
0,
0,
root_mount,
0,
afs_umount,
0,
afs_lookuppn,
afs_readdir,
0,
0,
0,
find_afs_srvr,
FS_NOTIMEOUT|FS_AMQINFO|FS_DIRECTORY
};
am_ops afs_ops = {
"auto",
afs_match,
0,
afs_mount,
0,
afs_umount,
0,
afs_lookuppn,
afs_readdir,
0,
0,
afs_umounted,
find_afs_srvr,
FS_AMQINFO|FS_DIRECTORY
};
am_ops toplvl_ops = {
"toplvl",
afs_match,
0,
toplvl_mount,
0,
toplvl_umount,
0,
afs_lookuppn,
afs_readdir,
0,
toplvl_mounted,
0,
find_afs_srvr,
FS_MKMNT|FS_NOTIMEOUT|FS_BACKGROUND|FS_AMQINFO|FS_DIRECTORY
};
am_ops dfs_ops = {
"direct",
afs_match,
0,
toplvl_mount,
0,
toplvl_umount,
0,
efs_lookuppn,
efs_readdir,
dfs_readlink,
toplvl_mounted,
0,
find_afs_srvr,
FS_MKMNT|FS_NOTIMEOUT|FS_BACKGROUND|FS_AMQINFO
};
#ifdef HAS_UNION_FS
am_ops union_ops = {
"union",
afs_match,
0,
toplvl_mount,
0,
toplvl_umount,
0,
afs_lookuppn,
afs_readdir,
0,
union_mounted,
0,
find_afs_srvr,
FS_MKMNT|FS_NOTIMEOUT|FS_BACKGROUND|FS_AMQINFO|FS_DIRECTORY
};
#endif