/* $Id: sfsops.c,v 1.13 2001/10/30 19:15:01 kaminsky Exp $ */ /* * * Copyright (C) 1999 David Mazieres (dm@uun.org) * Copyright (C) 2000 Kevin Fu (fubob@mit.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "sfs-internal.h" #include "sfsagent.h" #include "hashtab.h" #include #define SFSPREF ".SFS " #define SFSFH SFSPREF "FH" #define SFSFS SFSPREF "FS" #define SFSSPECLEN (2 + sizeof (SFSFH)) #define MIN2(a, b) ((a) < (b) ? (a) : (b)) struct id_dat { char name [sfs_idnamelen + 2]; unsigned int id; struct hashtab_entry id_link; struct hashtab_entry name_link; }; hashtab_decl (id2name, id_dat, id_link); hashtab_decl (name2id, id_dat, name_link); struct id_cache { dev_t dev; struct id2name uidtab; struct id2name gidtab; struct name2id unametab; struct name2id gnametab; struct hashtab_entry link; }; hashtab_decl (dev2ic, id_cache, link) ictab; struct id_cache * lookup_ic (dev_t dev) { struct id_cache *ic_elm; for (ic_elm = dev2ic_chain (&ictab, dev); ic_elm && (dev != ic_elm->dev); ic_elm = dev2ic_next (ic_elm)) ; if (ic_elm) return ic_elm; ic_elm = malloc (sizeof (*ic_elm)); if (!ic_elm) return NULL; ic_elm->dev = dev; id2name_init (&ic_elm->uidtab); id2name_init (&ic_elm->gidtab); name2id_init (&ic_elm->unametab); name2id_init (&ic_elm->gnametab); dev2ic_insert (&ictab, ic_elm, dev); return ic_elm; } static void namecpy (char *dst, const char *src) { strncpy (dst, src, sfs_idnamelen); dst[sfs_idnamelen] = '\0'; } int lookup_id (struct id_cache *ic, bool_t gid, char *name) { struct id_dat *id_elm; char *orig_name = name; const char *fs; int cdfd; u_int hval; if (*name == '%') name++; hval = hash_string (name); for (id_elm = name2id_chain ((gid ? &ic->gnametab : &ic->unametab), hval); id_elm && strcmp (name, id_elm->name); id_elm = name2id_next (id_elm)) ; if (id_elm) return id_elm->id; id_elm = malloc (sizeof (*id_elm)); if (!id_elm) return -1; bzero (id_elm->name, sizeof (id_elm->name)); namecpy (id_elm->name, name); name = orig_name; /* Get the id either locally or via SFS */ if (*name != '%' || (++name, !devcon_lookup (&cdfd, &fs, ic->dev))) { if (gid) { struct group *gr = getgrnam (name); id_elm->id = gr ? gr->gr_gid : -1; } else { struct passwd *pw = getpwnam (name); if (pw) id_elm->id = pw->pw_uid; else id_elm->id = -1; } } else if (strlen (name) > sfs_idnamelen) { free (id_elm); return -1; } else { sfsctl_getidnums_arg arg; sfsctl_getidnums_res res; sfs_opt_idname *sid; bzero (&arg, sizeof (arg)); bzero (&res, sizeof (res)); arg.filesys = (char *) fs; sid = gid ? &arg.names.gidname : &arg.names.uidname; sid->present = TRUE; sid->u.name = (char *) name; srpc_call (&sfsctl_prog_1, cdfd, SFSCTL_GETIDNUMS, &arg, &res); if (!res.status) id_elm->id = gid ? res.u.nums.gid : res.u.nums.uid; else { free (id_elm); xdr_free ((xdrproc_t) xdr_sfsctl_getidnums_res, (char *) &res); return -1; } xdr_free ((xdrproc_t) xdr_sfsctl_getidnums_res, (char *) &res); } id2name_insert (gid ? &ic->gidtab : &ic->uidtab, id_elm, id_elm->id); name2id_insert (gid ? &ic->gnametab : &ic->unametab, id_elm, hval); return id_elm->id; } void delid (void *_ic, struct id_dat *i) { free (i); } void delic (void *_null, struct id_cache *ic) { id2name_traverse (&ic->uidtab, delid, ic); id2name_clear (&ic->uidtab); id2name_init (&ic->uidtab); id2name_traverse (&ic->gidtab, delid, &ic->gidtab); id2name_clear (&ic->gidtab); id2name_init (&ic->gidtab); dev2ic_delete (&ictab, ic); free (ic); } /* Delete the entire cache and free all cache memory */ void sfs_flush_idcache () { dev2ic_traverse (&ictab, delic, NULL); } #if 0 static int sfs_do_getdesc (struct sfs_desc *sdp, const char *path, int (*statfn) (const char *, struct stat *), int (*chownfn) (const char *, uid_t, gid_t)) { struct stat sb; const char *fs; int cdfd; enum clnt_stat err; sfsctl_getfh_arg arg; sfsctl_getfh_res res; int saved_errno; int ret; if (statfn (path, &sb) < 0) return -1; if (!devcon_lookup (&cdfd, &fs, sb.st_dev)) { errno = EINVAL; return -1; } saved_errno = errno; chownfn (path, -2, getpid ()); if (errno != EPERM) return -1; errno = saved_errno; bzero (&arg, sizeof (arg)); bzero (&res, sizeof (res)); arg.filesys = (char *) fs; arg.fileid = sb.st_ino; err = srpc_call (&sfsctl_prog_1, cdfd, SFSCTL_GETFH, &arg, &res); if (!err && !res.status) { strcpy (sdp->fsname, fs); sdp->fhsize = res.u.fh.data.len; memcpy (sdp->fhdata, res.u.fh.data.val, sdp->fhsize); ret = 0; } else { errno = res.status == NFS3ERR_JUKEBOX ? EAGAIN : EIO; ret = -1; } xdr_free ((xdrproc_t) xdr_sfsctl_getfh_res, (char *) &res); return ret; } int sfs_getdesc (struct sfs_desc *sdp, const char *path) { assert (!"unsafe"); return sfs_do_getdesc (sdp, path, stat, chown); } int sfs_lgetdesc (struct sfs_desc *sdp, const char *path) { assert (!"unsafe"); return sfs_do_getdesc (sdp, path, lstat, lchown); } int sfs_fgetdesc (sfs_desc *sdp, int fd) { struct stat sb; const char *fs; int cdfd; enum clnt_stat err; sfsctl_getfh_arg arg; sfsctl_getfh_res res; int saved_errno; int ret; assert (!"unsafe"); if (fstat (fd, &sb) < 0) return -1; if (!devcon_lookup (&cdfd, &fs, sb.st_dev)) { errno = EINVAL; return -1; } saved_errno = errno; fchown (fd, -2, getpid ()); if (errno != EPERM) { close (fd); return -1; } errno = saved_errno; bzero (&arg, sizeof (arg)); bzero (&res, sizeof (res)); arg.filesys = (char *) fs; arg.fileid = sb.st_ino; err = srpc_call (&sfsctl_prog_1, cdfd, SFSCTL_GETFH, &arg, &res); if (!err && !res.status) { strcpy (sdp->fsname, fs); sdp->fhsize = res.u.fh.data.len; memcpy (sdp->fhdata, res.u.fh.data.val, sdp->fhsize); ret = 0; } else { errno = res.status == NFS3ERR_JUKEBOX ? EAGAIN : EIO; ret = -1; } xdr_free ((xdrproc_t) xdr_sfsctl_getfh_res, (char *) &res); return ret; } #endif /* Return -1 on error, 0 otherwise */ int sfs_stat2names (sfs_names *snp, const struct stat *sb) { const char *fs; int cdfd; sfsctl_getidnames_res res; struct passwd *pw; struct group *gr; bool_t remote = FALSE; bool_t uname = FALSE; bool_t gname = FALSE; struct id_dat *id_elm; struct id_cache *ic; u_int hval; ic = lookup_ic (sb->st_dev); if (!ic) return -1; for (id_elm = id2name_chain (&ic->uidtab, sb->st_uid); id_elm && (sb->st_uid != id_elm->id); id_elm = id2name_next (id_elm)) ; if (id_elm) { namecpy (snp->uidname, id_elm->name); uname = TRUE; } for (id_elm = id2name_chain (&ic->gidtab, sb->st_gid); id_elm && (sb->st_gid != id_elm->id); id_elm = id2name_next (id_elm)) ; if (id_elm) { namecpy (snp->gidname, id_elm->name); gname = TRUE; } if (gname && uname) return 0; bzero (&res, sizeof (res)); if (devcon_lookup (&cdfd, &fs, sb->st_dev)) { sfsctl_getidnames_arg arg; remote = TRUE; arg.filesys = (char *) fs; arg.nums.uid = sb->st_uid; arg.nums.gid = sb->st_gid; srpc_call (&sfsctl_prog_1, cdfd, SFSCTL_GETIDNAMES, &arg, &res); } if (!uname) { pw = getpwuid (sb->st_uid); if (remote && !res.status && res.u.names.uidname.present) { if (pw && !strcmp (pw->pw_name, res.u.names.uidname.u.name)) namecpy (snp->uidname, pw->pw_name); else { snp->uidname[0] = '%'; namecpy (snp->uidname + 1, res.u.names.uidname.u.name); } } else if (!remote && pw) namecpy (snp->uidname, pw->pw_name); else sprintf (snp->uidname, "%u", sb->st_uid); id_elm = malloc (sizeof (*id_elm)); if (!id_elm) { xdr_free ((xdrproc_t) xdr_sfsctl_getidnames_res, (char *) &res); return -1; } bzero (id_elm->name, sizeof (id_elm->name)); namecpy (id_elm->name, snp->uidname); id_elm->id = sb->st_uid; hval = hash_string (id_elm->name); id2name_insert (&ic->uidtab, id_elm, id_elm->id); name2id_insert (&ic->unametab, id_elm, hval); } if (!gname) { gr = getgrgid (sb->st_gid); if (remote && !res.status && res.u.names.gidname.present) { if (gr && !strcmp (gr->gr_name, res.u.names.gidname.u.name)) namecpy (snp->gidname, gr->gr_name); else { snp->gidname[0] = '%'; namecpy (snp->gidname + 1, res.u.names.gidname.u.name); } } else if (!remote && gr) namecpy (snp->gidname, gr->gr_name); else sprintf (snp->gidname, "%u", sb->st_gid); id_elm = malloc (sizeof (*id_elm)); if (!id_elm) { xdr_free ((xdrproc_t) xdr_sfsctl_getidnames_res, (char *) &res); return -1; } bzero (id_elm->name, sizeof (id_elm->name)); namecpy (id_elm->name, snp->gidname); id_elm->id = sb->st_gid; hval = hash_string (id_elm->name); id2name_insert (&ic->gidtab, id_elm, id_elm->id); name2id_insert (&ic->gnametab, id_elm, hval); } xdr_free ((xdrproc_t) xdr_sfsctl_getidnames_res, (char *) &res); return 0; } int sfs_getremoteid (sfs_remoteid *rip, dev_t dev) { const char *fs; int cdfd; sfsctl_getcred_res res; unsigned int i; if (!devcon_lookup (&cdfd, &fs, dev)) { GETGROUPS_T gidset[sfs_maxgroups]; rip->valid = SFS_UNIXCRED; rip->uid = geteuid (); rip->gid = getegid (); rip->ngroups = getgroups (sfs_maxgroups, gidset); if (rip->ngroups < 0) rip->ngroups = 0; for (i = 0; i < rip->ngroups; i++) rip->groups[i] = gidset[i]; return 0; } bzero (rip, sizeof (*rip)); bzero (&res, sizeof (res)); if (srpc_call (&sfsctl_prog_1, cdfd, SFSCTL_GETCRED, &fs, &res)) { xdr_free ((xdrproc_t) xdr_sfsauth_cred, (char *) &res); return -1; } if (res.u.cred.type == SFS_UNIXCRED) { rip->valid = SFS_UNIXCRED; rip->uid = res.u.cred.u.unixcred.uid; rip->gid = res.u.cred.u.unixcred.gid; rip->ngroups = res.u.cred.u.unixcred.groups.len; if (rip->ngroups > sfs_maxgroups) rip->ngroups = sfs_maxgroups; for (i = 0; i < rip->ngroups; i++) rip->groups[i] = res.u.cred.u.unixcred.groups.val[i]; } xdr_free ((xdrproc_t) xdr_sfsctl_getcred_res, (char *) &res); return 0; } static int sfs_idbyname (const char *id, bool_t gid, dev_t dev) { struct id_cache *ic_elm; ic_elm = lookup_ic (dev); if (!ic_elm) return -1; return lookup_id (ic_elm, gid, (char *)id); } int sfs_uidbyname (const char *uidname, dev_t dev) { return sfs_idbyname (uidname, FALSE, dev); } int sfs_gidbyname (const char *gidname, dev_t dev) { return sfs_idbyname (gidname, TRUE, dev); }