diff options
Diffstat (limited to 'contrib/amd/hlfsd/homedir.c')
-rw-r--r-- | contrib/amd/hlfsd/homedir.c | 799 |
1 files changed, 799 insertions, 0 deletions
diff --git a/contrib/amd/hlfsd/homedir.c b/contrib/amd/hlfsd/homedir.c new file mode 100644 index 0000000..d6df58e --- /dev/null +++ b/contrib/amd/hlfsd/homedir.c @@ -0,0 +1,799 @@ +/* + * Copyright (c) 1997-1998 Erez Zadok + * Copyright (c) 1989 Jan-Simon Pendry + * Copyright (c) 1989 Imperial College of Science, Technology & Medicine + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * %W% (Berkeley) %G% + * + * $Id: homedir.c,v 1.16 1993/09/13 15:11:00 ezk Exp $ + * + * HLFSD was written at Columbia University Computer Science Department, by + * Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@cs.columbia.edu> + * It is being distributed under the same terms and conditions as amd does. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <hlfsd.h> + + +/* + * STATIC VARIABLES AND FUNCTIONS: + */ +static FILE *passwd_fp = NULL; +static char pw_name[16], pw_dir[128]; +static int cur_pwtab_num = 0, max_pwtab_num = 0; +static int hlfsd_diskspace(char *); +static int hlfsd_stat(char *, struct stat *); +static int passwd_line = 0; +static int plt_reset(void); +static struct passwd passwd_ent; +static uid2home_t *lastchild; +static uid2home_t *pwtab; +static void delay(uid2home_t *, int); +static void table_add(int, char *, char *); + +/* GLOBAL FUNCTIONS */ +char *homeof(char *username); +int uidof(char *username); + +/* GLOBALS VARIABLES */ +char mboxfile[MAXPATHLEN]; +username2uid_t *untab; /* user name table */ + + +/* + * Return the home directory pathname for the user with uid "userid". + */ +char * +homedir(int userid) +{ + static char linkval[MAXPATHLEN + 1]; + static struct timeval tp; + uid2home_t *found; + char *homename; + struct stat homestat; + + clock_valid = 0; /* invalidate logging clock */ + + if ((int) userid == 0) { /* force superuser to use "/" as home */ + sprintf(linkval, "/%s", home_subdir); + return linkval; + } + if ((found = plt_search(userid)) == (uid2home_t *) NULL) { + return alt_spooldir; /* use alt spool for unknown uid */ + } + homename = found->home; + + if (homename[0] != '/' || homename[1] == '\0') { + found->last_status = 1; + return alt_spooldir; /* use alt spool for / or rel. home */ + } + sprintf(linkval, "%s/%s", homename, home_subdir); + + if (noverify) { + found->last_status = 0; + return linkval; + } + + /* + * To optimize hlfsd, we don't actually check the validity of the + * symlink if it has been in checked in the last N seconds. It is + * very likely that the link, machine, and filesystem are still + * valid, as long as N is small. But if N ls large, that may not be + * true. That's why the default N is 5 minutes, but we allow the + * user to override this value via a command line option. Note that + * we do not update the last_access_time each time it is accessed, + * but only once every N seconds. + */ + if (gettimeofday(&tp, (struct timezone *) NULL) < 0) { + tp.tv_sec = 0; + } else { + if ((tp.tv_sec - found->last_access_time) < cache_interval) { + if (found->last_status == 0) { + return linkval; + } else { + return alt_spooldir; + } + } else { + found->last_access_time = tp.tv_sec; + } + } + +#ifdef DEBUG + /* + * only run this forking code if asked for -D fork + * or if did not ask for -D nofork + */ + amuDebug(D_FORK) { +#endif /* DEBUG */ + /* fork child to process request if none in progress */ + if (found->child && kill(found->child, 0)) + found->child = 0; + + if (found->child) + delay(found, 5); /* wait a bit if in progress */ + if (found->child) { /* better safe than sorry - maybe */ + found->last_status = 1; + return alt_spooldir; + } + if ((found->child = fork()) < 0) { + found->last_status = 1; + return alt_spooldir; + } + if (found->child) { /* PARENT */ +#ifdef DEBUG + if (lastchild) + plog(XLOG_INFO, "cache spill uid = %d, pid = %d, home = %s", + lastchild->uid, lastchild->child, + lastchild->home); +#endif /* DEBUG */ + lastchild = found; + return (char *) NULL; /* return NULL to parent, so it can continue */ + } +#ifdef DEBUG + } /* end of Debug(D_FORK) */ +#endif /* DEBUG */ + + /* + * CHILD: (or parent if -D nofork) + * + * Check and create dir if needed. + * Check disk space and/or quotas too. + * + * We don't need to set the _last_status field of found after the fork + * in the child, b/c that information would be later determined in + * nfsproc_readlink_2() and the correct exit status would be returned + * to the parent upon SIGCHLD in interlock(). + * + */ + mypid = getpid(); /* for logging routines */ + if (seteuid(userid) < 0) { + plog(XLOG_WARNING, "could not seteuid to %d: %m", userid); + return linkval; + } + if (hlfsd_stat(linkval, &homestat) < 0) { + if (errno == ENOENT) { /* make the spool dir if possible */ + /* don't use recursive mkdirs here */ + if (mkdir(linkval, PERS_SPOOLMODE) < 0) { + seteuid(0); + plog(XLOG_WARNING, "can't make directory %s: %m", linkval); + return alt_spooldir; + } + /* fall through to testing the disk space / quota */ + } else { /* the home dir itself must not exist then */ + seteuid(0); + plog(XLOG_WARNING, "bad link to %s: %m", linkval); + return alt_spooldir; + } + } + + /* + * If gets here, then either the spool dir in the home dir exists, + * or it was just created. In either case, we now need to + * test if we can create a small file and write at least one + * byte into it. This will test that we have both enough inodes + * and disk blocks to spare, or they fall within the user's quotas too. + * We are still seteuid to the user at this point. + */ + if (hlfsd_diskspace(linkval) < 0) { + seteuid(0); + plog(XLOG_WARNING, "no more space in %s: %m", linkval); + return alt_spooldir; + } else { + seteuid(0); + return linkval; + } +} + + +static int +hlfsd_diskspace(char *path) +{ + char buf[MAXPATHLEN]; + int fd, len; + + clock_valid = 0; /* invalidate logging clock */ + + sprintf(buf, "%s/._hlfstmp_%lu", path, (long) getpid()); + if ((fd = open(buf, O_RDWR | O_CREAT, 0600)) < 0) { + plog(XLOG_ERROR, "cannot open %s: %m", buf); + return -1; + } + len = strlen(buf); + if (write(fd, buf, len) < len) { + plog(XLOG_ERROR, "cannot write \"%s\" (%d bytes) to %s : %m", buf, len, buf); + close(fd); + unlink(buf); /* cleanup just in case */ + return -1; + } + if (unlink(buf) < 0) { + plog(XLOG_ERROR, "cannot unlink %s : %m", buf); + } + close(fd); + return 0; +} + + +static int +hlfsd_stat(char *path, struct stat *statp) +{ + if (stat(path, statp) < 0) + return -1; + else if (!S_ISDIR(statp->st_mode)) { + errno = ENOTDIR; + return -1; + } + return 0; +} + + +static void +delay(uid2home_t *found, int secs) +{ + struct timeval tv; + +#ifdef DEBUG + if (found) + dlog("delaying on child %d for %d seconds", found->child, secs); +#endif /* DEBUG */ + + tv.tv_usec = 0; + + do { + tv.tv_sec = secs; + if (select(0, 0, 0, 0, &tv) == 0) + break; + } while (--secs && found->child); +} + + +/* + * This function is called when a child has terminated after + * servicing an nfs request. We need to check the exit status and + * update the last_status field of the requesting user. + */ +RETSIGTYPE +interlock(int signum) +{ + int child; + uid2home_t *lostchild; + int status; + +#ifdef HAVE_WAITPID + while ((child = waitpid((pid_t) -1, &status, WNOHANG)) > 0) { +#else /* not HAVE_WAITPID */ + while ((child = wait3(&status, WNOHANG, (struct rusage *) 0)) > 0) { +#endif /* not HAVE_WAITPID */ + + /* high chances this was the last child forked */ + if (lastchild && lastchild->child == child) { + lastchild->child = 0; + + if (WIFEXITED(status)) + lastchild->last_status = WEXITSTATUS(status); + lastchild = (uid2home_t *) NULL; + } else { + /* and if not, we have to search for it... */ + for (lostchild = pwtab; lostchild < &pwtab[cur_pwtab_num]; lostchild++) { + if (lostchild->child == child) { + if (WIFEXITED(status)) + lostchild->last_status = WEXITSTATUS(status); + lostchild->child = 0; + break; + } + } + } + } +} + + +/* + * PASSWORD AND USERNAME LOOKUP TABLES FUNCTIONS + */ + +/* + * get index of UserName table entry which matches username. + * must not return uid_t because we want to return a negative number. + */ +int +untab_index(char *username) +{ + int max, min, mid, cmp; + + max = cur_pwtab_num - 1; + min = 0; + + do { + mid = (max + min) / 2; + cmp = strcmp(untab[mid].username, username); + if (cmp == 0) /* record found! */ + return mid; + if (cmp > 0) + max = mid; + else + min = mid; + } while (max > min + 1); + + if (STREQ(untab[max].username, username)) + return max; + if (STREQ(untab[min].username, username)) + return min; + + /* if gets here then record was not found */ + return -1; +} + + +/* + * Don't make this return a uid_t, because we need to return negative + * numbers as well (error codes.) + */ +int +uidof(char *username) +{ + int idx; + + if ((idx = untab_index(username)) < 0) /* not found */ + return INVALIDID; /* an invalid user id */ + return untab[idx].uid; +} + + +/* + * Don't make this return a uid_t, because we need to return negative + * numbers as well (error codes.) + */ +char * +homeof(char *username) +{ + int idx; + + if ((idx = untab_index(username)) < 0) /* not found */ + return (char *) NULL; /* an invalid user id */ + return untab[idx].home; +} + + +char * +mailbox(int uid, char *username) +{ + char *home; + + if (uid < 0) + return (char *) NULL; /* not found */ + + if ((home = homeof(username)) == (char *) NULL) + return (char *) NULL; + if (STREQ(home, "/")) + sprintf(mboxfile, "/%s/%s", home_subdir, username); + else + sprintf(mboxfile, "%s/%s/%s", home, home_subdir, username); + return mboxfile; +} + + +static int +plt_compare_fxn(const voidp x, const voidp y) + +{ + uid2home_t *i = (uid2home_t *) x; + uid2home_t *j = (uid2home_t *) y; + + return i->uid - j->uid; +} + + +static int +unt_compare_fxn(const voidp x, const voidp y) +{ + username2uid_t *i = (username2uid_t *) x; + username2uid_t *j = (username2uid_t *) y; + + return strcmp(i->username, j->username); +} + + +/* perform initialization of user passwd database */ +static void +hlfsd_setpwent(void) +{ + if (!passwdfile) { + setpwent(); + return; + } + + passwd_fp = fopen(passwdfile, "r"); + if (!passwd_fp) { + plog(XLOG_ERROR, "unable to read passwd file %s: %m", passwdfile); + return; + } + plog(XLOG_INFO, "reading password entries from file %s", passwdfile); + + passwd_line = 0; + memset((char *) &passwd_ent, 0, sizeof(struct passwd)); + passwd_ent.pw_name = (char *) &pw_name; + passwd_ent.pw_dir = (char *) &pw_dir; +} + + +/* perform de-initialization of user passwd database */ +static void +hlfsd_endpwent(void) +{ + if (!passwdfile) { + /* + * Don't actually run this because we will be making more passwd calls + * afterwards. On Solaris 2.5.1, making getpwent() calls after calling + * endpwent() results in a memory leak! (and no, even Purify didn't + * detect it...) + * + endpwent(); + */ + return; + } + + if (passwd_fp) { + fclose(passwd_fp); + } +} + + +/* perform record reading/parsing of individual passwd database records */ +static struct passwd * +hlfsd_getpwent(void) +{ + char buf[256], *cp; + + /* check if to perform standard unix function */ + if (!passwdfile) { + return getpwent(); + } + + clock_valid = 0; /* invalidate logging clock */ + + /* return here to read another entry */ +readent: + + /* return NULL if reached end of file */ + if (feof(passwd_fp)) + return NULL; + + pw_name[0] = pw_dir[0] = '\0'; + + /* read records */ + buf[0] = '\0'; + fgets(buf, 256, passwd_fp); + passwd_line++; + if (!buf || buf[0] == '\0') + goto readent; + + /* read user name */ + cp = strtok(buf, ":"); + if (!cp || cp[0] == '\0') { + plog(XLOG_ERROR, "no user name on line %d of %s", passwd_line, passwdfile); + goto readent; + } + strcpy(pw_name, cp); /* will show up in passwd_ent.pw_name */ + + /* skip passwd */ + strtok(NULL, ":"); + + /* read uid */ + cp = strtok(NULL, ":"); + if (!cp || cp[0] == '\0') { + plog(XLOG_ERROR, "no uid on line %d of %s", passwd_line, passwdfile); + goto readent; + } + passwd_ent.pw_uid = atoi(cp); + + /* skip gid and gcos */ + strtok(NULL, ":"); + strtok(NULL, ":"); + + /* read home dir */ + cp = strtok(NULL, ":"); + if (!cp || cp[0] == '\0') { + plog(XLOG_ERROR, "no home dir on line %d of %s", passwd_line, passwdfile); + goto readent; + } + strcpy(pw_dir, cp); /* will show up in passwd_ent.pw_dir */ + + /* the rest of the fields are unimportant and not being considered */ + + plog(XLOG_USER, "hlfsd_getpwent: name=%s, uid=%d, dir=%s", + passwd_ent.pw_name, passwd_ent.pw_uid, passwd_ent.pw_dir); + + return &passwd_ent; +} + + +/* + * read and hash the passwd file or NIS map + */ +void +plt_init(void) +{ + struct passwd *pent_p; + + if (plt_reset() < 0) /* could not reset table. skip. */ + return; + + plog(XLOG_INFO, "reading password map"); + + hlfsd_setpwent(); /* prepare to read passwd entries */ + while ((pent_p = hlfsd_getpwent()) != (struct passwd *) NULL) { + table_add(pent_p->pw_uid, pent_p->pw_dir, pent_p->pw_name); + } + hlfsd_endpwent(); + + qsort((char *) pwtab, cur_pwtab_num, sizeof(uid2home_t), + plt_compare_fxn); + qsort((char *) untab, cur_pwtab_num, sizeof(username2uid_t), + unt_compare_fxn); + + plog(XLOG_INFO, "password map read and sorted"); +} + + +/* + * This is essentially so that we don't reset known good lookup tables when a + * YP server goes down. + */ +static int +plt_reset(void) +{ + int i; + + clock_valid = 0; /* invalidate logging clock */ + + hlfsd_setpwent(); + if (hlfsd_getpwent() == (struct passwd *) NULL) { + hlfsd_endpwent(); + return -1; /* did not reset table */ + } + hlfsd_endpwent(); + + lastchild = (uid2home_t *) NULL; + + if (max_pwtab_num > 0) /* was used already. cleanup old table */ + for (i = 0; i < cur_pwtab_num; ++i) { + if (pwtab[i].home) { + XFREE(pwtab[i].home); + pwtab[i].home = (char *) NULL; + } + pwtab[i].uid = INVALIDID; /* not a valid uid (yet...) */ + pwtab[i].child = (pid_t) 0; + pwtab[i].uname = (char *) NULL; /* only a ptr to untab[i].username */ + if (untab[i].username) { + XFREE(untab[i].username); + untab[i].username = (char *) NULL; + } + untab[i].uid = INVALIDID; /* invalid uid */ + untab[i].home = (char *) NULL; /* only a ptr to pwtab[i].home */ + } + cur_pwtab_num = 0; /* zero current size */ + + return 0; /* resetting ok */ +} + + +/* + * u: uid number + * h: home directory + * n: user ID name + */ +static void +table_add(int u, char *h, char *n) +{ + int i; + + clock_valid = 0; /* invalidate logging clock */ + + if (max_pwtab_num <= 0) { /* was never initialized */ + max_pwtab_num = 1; + pwtab = (uid2home_t *) xmalloc(max_pwtab_num * + sizeof(uid2home_t)); + memset((char *) &pwtab[0], 0, max_pwtab_num * sizeof(uid2home_t)); + untab = (username2uid_t *) xmalloc(max_pwtab_num * + sizeof(username2uid_t)); + memset((char *) &untab[0], 0, max_pwtab_num * sizeof(username2uid_t)); + } + + /* check if need more space. */ + if (cur_pwtab_num + 1 > max_pwtab_num) { + /* need more space in table */ + max_pwtab_num *= 2; + plog(XLOG_INFO, "reallocating table spaces to %d entries", max_pwtab_num); + pwtab = (uid2home_t *) xrealloc(pwtab, + sizeof(uid2home_t) * max_pwtab_num); + untab = (username2uid_t *) xrealloc(untab, + sizeof(username2uid_t) * + max_pwtab_num); + /* zero out newly added entries */ + for (i=cur_pwtab_num; i<max_pwtab_num; ++i) { + memset((char *) &pwtab[i], 0, sizeof(uid2home_t)); + memset((char *) &untab[i], 0, sizeof(username2uid_t)); + } + } + + /* do NOT add duplicate entries (this is an O(N^2) algorithm... */ + for (i=0; i<cur_pwtab_num; ++i) + if (u == pwtab[i].uid && u != 0 ) { +#ifdef DEBUG + dlog("ignoring duplicate home %s for uid %d (already %s)", + h, u, pwtab[i].home); +#endif /* DEBUG */ + return; + } + + /* add new password entry */ + pwtab[cur_pwtab_num].home = strdup(h); + pwtab[cur_pwtab_num].child = 0; + pwtab[cur_pwtab_num].last_access_time = 0; + pwtab[cur_pwtab_num].last_status = 0; /* assume best: used homedir */ + pwtab[cur_pwtab_num].uid = u; + + /* add new userhome entry */ + untab[cur_pwtab_num].username = strdup(n); + + /* just a second pointer */ + pwtab[cur_pwtab_num].uname = untab[cur_pwtab_num].username; + untab[cur_pwtab_num].uid = u; + untab[cur_pwtab_num].home = pwtab[cur_pwtab_num].home; /* a ptr */ + + /* increment counter */ + ++cur_pwtab_num; +} + + +/* + * return entry in lookup table + */ +uid2home_t * +plt_search(int u) +{ + int max, min, mid; + + /* + * empty table should not happen, + * but I have a bug with signals to trace... + */ + if (pwtab == (uid2home_t *) NULL) + return (uid2home_t *) NULL; + + max = cur_pwtab_num - 1; + min = 0; + + do { + mid = (max + min) / 2; + if (pwtab[mid].uid == u) /* record found! */ + return &pwtab[mid]; + if (pwtab[mid].uid > u) + max = mid; + else + min = mid; + } while (max > min + 1); + + if (pwtab[max].uid == u) + return &pwtab[max]; + if (pwtab[min].uid == u) + return &pwtab[min]; + + /* if gets here then record was not found */ + return (uid2home_t *) NULL; +} + + +#if defined(DEBUG) || defined(DEBUG_PRINT) +void +plt_print(int signum) +{ + FILE *dumpfile; + int dumpfd; + char dumptmp[] = "/usr/tmp/hlfsd.dump.XXXXXX"; + int i; + +#ifdef HAVE_MKSTEMP + dumpfd = mkstemp(dumptmp); +#else /* not HAVE_MKSTEMP */ + mktemp(dumptmp); + if (!dumptmp) { + plot(XLOG_ERROR, "cannot create temporary dump file"); + return; + } + dumpfd = open(dumptmp, O_RDONLY); +#endif /* not HAVE_MKSTEMP */ + if (dumpfd < 0) { + plog(XLOG_ERROR, "cannot open temporary dump file"); + return; + } + if ((dumpfile = fdopen(dumpfd, "a")) != NULL) { + plog(XLOG_INFO, "dumping internal state to file %s", dumptmp); + fprintf(dumpfile, "\n\nNew plt_dump():\n"); + for (i = 0; i < cur_pwtab_num; ++i) + fprintf(dumpfile, + "%4d %5lu %10lu %1d %4lu \"%s\" uname=\"%s\"\n", + i, + (long) pwtab[i].child, + pwtab[i].last_access_time, + pwtab[i].last_status, + (long) pwtab[i].uid, + pwtab[i].home, + pwtab[i].uname); + fprintf(dumpfile, "\nUserName table by plt_print():\n"); + for (i = 0; i < cur_pwtab_num; ++i) + fprintf(dumpfile, "%4d : \"%s\" %4lu \"%s\"\n", i, + untab[i].username, (long) untab[i].uid, untab[i].home); + close(dumpfd); + fclose(dumpfile); + } +} + + +void +plt_dump(uid2home_t *lastc, pid_t this) +{ + FILE *dumpfile; + int i; + + if ((dumpfile = fopen("/var/tmp/hlfsdump", "a")) != NULL) { + fprintf(dumpfile, "\n\nNEW PLT_DUMP -- "); + fprintf(dumpfile, "lastchild->child=%d ", + (int) (lastc ? lastc->child : -999)); + fprintf(dumpfile, ", child from wait3=%lu:\n", (long) this); + for (i = 0; i < cur_pwtab_num; ++i) + fprintf(dumpfile, "%4d %5lu: %4lu \"%s\" uname=\"%s\"\n", i, + (long) pwtab[i].child, (long) pwtab[i].uid, + pwtab[i].home, pwtab[i].uname); + fprintf(dumpfile, "\nUserName table by plt_dump():\n"); + for (i = 0; i < cur_pwtab_num; ++i) + fprintf(dumpfile, "%4d : \"%s\" %4lu \"%s\"\n", i, + untab[i].username, (long) untab[i].uid, untab[i].home); + fprintf(dumpfile, "ezk: ent=%d, uid=%lu, home=\"%s\"\n", + untab_index("ezk"), + (long) untab[untab_index("ezk")].uid, + pwtab[untab[untab_index("ezk")].uid].home); + fprintf(dumpfile, "rezk: ent=%d, uid=%lu, home=\"%s\"\n", + untab_index("rezk"), + (long) untab[untab_index("rezk")].uid, + pwtab[untab[untab_index("rezk")].uid].home); + fclose(dumpfile); + } +} +#endif /* defined(DEBUG) || defined(DEBUG_PRINT) */ |