Upstream-Status: Inappropriate [other] Upstream is not making further releases of this software. Signed-off-by: Scott Garman # Patch origin: nfs-server source RPM from openSUSE 10.3 --- nfs-server/Makefile.in +++ nfs-server/Makefile.in 2002/11/08 13:59:16 @@ -100,7 +100,7 @@ utimes.c mkdir.c rename.c getopt.c getopt_long.c \ alloca.c mountlist.c xmalloc.c \ xstrdup.c strdup.c strstr.c nfsmounted.c faccess.c \ - haccess.c daemon.c signals.c + haccess.c daemon.c signals.c teahash3.c XDRFILES = mount.x nfs_prot.x GENFILES = mount.h mount_xdr.c mount_svc.c nfs_prot.h nfs_prot_xdr.c \ ugid.h ugid_xdr.c ugid_clnt.c @@ -112,7 +112,7 @@ MANPAGES8 = showmount MANPAGES = $(MANPAGES5) $(MANPAGES8p) $(MANPAGES8) LIBOBJS = version.o fsusage.o mountlist.o xmalloc.o xstrdup.o \ - nfsmounted.o faccess.o haccess.o daemon.o \ + nfsmounted.o faccess.o haccess.o daemon.o teahash3.o \ signals.o @LIBOBJS@ @ALLOCA@ OBJS = logging.o fh.o devtab.o auth_init.o auth_clnt.o auth.o NFSD_OBJS = nfsd.o rpcmisc.o nfs_dispatch.o getattr.o setattr.o \ --- nfs-server/auth.c +++ nfs-server/auth.c 2002/11/08 13:59:16 @@ -83,6 +83,7 @@ 0, /* read-only */ 0, /* relative links */ 0, /* noaccess */ + 0, /* hashed inodes */ 1, /* cross_mounts */ 1, /* allow setuid */ 65534, /* default uid */ @@ -100,6 +101,7 @@ 0, /* relative links */ 0, /* noaccess */ 1, /* cross_mounts */ + 0, /* hashed inodes */ 0, /* allow setuid */ 65534, /* default uid */ 65534, /* default gid */ @@ -991,6 +993,7 @@ if (mp == 0) { mp = (nfs_mount*) xmalloc(sizeof(nfs_mount)); memset(mp, 0, sizeof(*mp)); + mp->mount_dev = 0; mp->origin = cp; mp->client = cp; mp->path = xstrdup(path); @@ -1169,6 +1172,8 @@ default_options.nobody_gid = anon_gid; anonymous_options.nobody_uid = anon_uid; anonymous_options.nobody_gid = anon_gid; + default_options.cross_mounts = cross_mounts; + default_options.hashed_inodes = hashed_inodes; memset(cached_clients, 0, sizeof(cached_clients)); cached_next = 0; --- nfs-server/auth.h +++ nfs-server/auth.h 2002/11/08 13:59:16 @@ -43,15 +43,16 @@ typedef struct nfs_options { ugid_mapping_t uidmap; /* uid/gid mapping behavior */ - int root_squash; - int all_squash; - int some_squash; /* speed up luid() etc. */ - int secure_port; - int read_only; - int link_relative; - int noaccess; - int cross_mounts; - int allow_setuid; + unsigned root_squash : 1; + unsigned all_squash : 1; + unsigned some_squash : 1; /* speed up luid() etc. */ + unsigned secure_port : 1; + unsigned read_only : 1; + unsigned link_relative : 1; + unsigned noaccess : 1; + unsigned cross_mounts : 1; + unsigned hashed_inodes : 1; + unsigned allow_setuid : 1; uid_t nobody_uid; gid_t nobody_gid; char * clnt_nisdomain; @@ -64,6 +65,7 @@ int length; char * path; nfs_options o; + dev_t mount_dev; /* Original NFS client */ struct nfs_client * origin; } nfs_mount; @@ -121,6 +123,8 @@ extern void auth_check_all_netmasks(void); extern void auth_sort_all_mountlists(void); extern void auth_log_all(void); +extern int auth_checkdev(nfs_mount *, dev_t dev); +extern int auth_checkpathdev(char *, dev_t dev); /* This function lets us set our euid/fsuid temporarily */ extern void auth_override_uid(uid_t); --- nfs-server/auth_clnt.c +++ nfs-server/auth_clnt.c 2002/11/08 13:59:16 @@ -89,6 +89,13 @@ return NULL; } + if (!mp->o.cross_mounts && !mp->mount_dev) { + struct stat st; + if (!lstat(mp->path, &st) < 0) + return NULL; + mp->mount_dev = st.st_dev; + } + /* Check request originated on a privileged port. */ if (!allow_non_root && mp->o.secure_port && !SECURE_PORT(svc_getcaller(rqstp->rq_xprt)->sin_port)) { @@ -350,3 +357,28 @@ return 1; } #endif + +int auth_checkpathdev(char *path, dev_t dev) +{ + nfs_mount *mp = auth_match_mount(nfsclient, path); + if (!mp) + return 0; + return auth_checkdev(mp, dev); +} + +int auth_checkdev(nfs_mount *mp, dev_t dev) +{ + if (!mp->mount_dev) + return 1; + if (mp->mount_dev != dev) { + struct stat st; + /* Restat in case the cd switched */ + if (efs_lstat(mp->path, &st) < 0) { + Dprintf(L_ERROR, "Unable to stat mount point %s\n", mp->path); + return 0; + } + mp->mount_dev = st.st_dev; + } + return mp->mount_dev == dev; +} + --- nfs-server/auth_init.c +++ nfs-server/auth_init.c 2002/11/08 13:59:16 @@ -320,6 +320,14 @@ /* knfsd compatibility, ignore */; else ifkwd(4, "sync") /* knfsd compatibility, ignore */; + else ifkwd(13, "hashed_inodes") + mp->o.hashed_inodes = 1; + else ifkwd(16, "no_hashed_inodes") + mp->o.hashed_inodes = 0; + else ifkwd(12, "cross_mounts") + mp->o.cross_mounts = 1; + else ifkwd(15, "no_cross_mounts") + mp->o.cross_mounts = 0; else { Dprintf(L_ERROR, "Unknown keyword \"%.*s\" in export file\n", --- nfs-server/exports.man +++ nfs-server/exports.man 2002/11/08 13:59:16 @@ -208,6 +208,17 @@ .IR no_all_squash , which is the default setting. .TP +.IR hashed_inodes +Use a special scheme to generate inode numbers that may work better with +reiserfs filesystems. +.IR no_hashed_inodes +which uses a direct mapping is the default. +.TP +.IR cross_mounts +Do not cross mount points in exports. Turning this off with +.IR no_cross_mounts +avoids inode number space conflicts when there are too many files. +.TP .IR map_daemon This option turns on dynamic uid/gid mapping. Each uid in an NFS request will be translated to the equivalent server uid, and each uid in an --- nfs-server/fh.c +++ nfs-server/fh.c 2002/11/08 14:11:31 @@ -4,8 +4,9 @@ * * Interfaces: * pseudo_inode - * mostly used internally, but also called from unfsd.c - * when reporting directory contents. + * mostly used internally, for hash tables + * visible_inode + * generate visible inode shown to the client in the fattr. * fh_init * Initializes the queues and 'flush' timer * fh_pr @@ -47,6 +48,8 @@ * Note: the original code mistakenly assumes that the overall path * length remains within the value given by PATH_MAX... that leads * to interesting buffer overflows all over the place. + * + * Depends that dev_t only uses 16bits. */ #include @@ -137,9 +140,9 @@ }; /* Forward declared local functions */ -static psi_t path_psi(char *, nfsstat *, struct stat *, int); +static psi_t path_psi(char *, nfsstat *, struct stat *, int, int *); static psi_t path_psi_m(char *, nfsstat *, struct stat *, - struct stat *, int); + struct stat *, int, int *); static int fh_flush_fds(void); static char * fh_dump(svc_fh *); static void fh_insert_fdcache(fhcache *fhc); @@ -173,19 +176,22 @@ fh_list_size++; /* Insert into hash tab. */ - hash_slot = &(fh_hashed[fhc->h.psi % HASH_TAB_SIZE]); + hash_slot = &(fh_hashed[pseudo_inode(fhc->h.ino,fhc->h.dev) % HASH_TAB_SIZE]); fhc->hash_next = *hash_slot; *hash_slot = fhc; } static fhcache * -fh_lookup(psi_t psi) +fh_lookup(ino_t ino, dev_t dev) { register fhcache *fhc; - fhc = fh_hashed[psi % HASH_TAB_SIZE]; - while (fhc != NULL && fhc->h.psi != psi) + fhc = fh_hashed[pseudo_inode(ino,dev) % HASH_TAB_SIZE]; + while (fhc != NULL) { + if (fhc->h.ino == ino && fhc->h.dev == dev) + break; fhc = fhc->hash_next; + } return (fhc); } @@ -193,7 +199,8 @@ fh_insert_fdcache(fhcache *fhc) { #ifdef FHTRACE - Dprintf(D_FHTRACE, "insert fh %x into fdcache @%d\n", fhc->h.psi, fhc->fd); + Dprintf(D_FHTRACE, "insert fh %x,%x into fdcache @%d\n", + fhc->h.ino, fhc->h.dev, fhc->fd); if (fhc->fd < 0) { fh_complain("fd cache bug: bad fd", fhc); return; @@ -289,8 +296,9 @@ #endif Dprintf(D_FHTRACE|D_FHCACHE, - "fh_delete: deleting handle %x ('%s', fd=%d)\n", - fhc, fhc->path ? fhc->path : "", fhc->fd); + "fh_delete: deleting handle %x [%x,%x] ('%s', fd=%d)\n", + fhc, fhc->h.dev, fhc->h.ino, fhc->path ? fhc->path : "", + fhc->fd); /* Remove from current posn */ fhc->prev->next = fhc->next; @@ -298,7 +306,7 @@ fh_list_size--; /* Remove from hash tab */ - hash_slot = &(fh_hashed[fhc->h.psi % HASH_TAB_SIZE]); + hash_slot = &(fh_hashed[pseudo_inode(fhc->h.ino,fhc->h.dev) % HASH_TAB_SIZE]); while (*hash_slot != NULL && *hash_slot != fhc) hash_slot = &((*hash_slot)->hash_next); if (*hash_slot == NULL) @@ -528,6 +536,7 @@ index -= 8; } +#if 0 /* If we have an XXL inode number, spew out warning (but at most * once a second) */ if (inode & ~mask) { @@ -541,14 +550,34 @@ } inode &= mask; } - +#endif return (psi_t) (prefix | inode); #endif } +/* Inode as handed out by attr calls. */ +psi_t +visible_inode(ino_t ino, dev_t dev, nfs_mount *mount) +{ + if (!mount->o.cross_mounts) + return ino; + + if (mount->o.hashed_inodes) { + extern __u32 teahash3(/*u32 k[2], *//*u8*/const char *msg, int len); + + struct { + ino_t ino; + dev_t dev; + } tup = { ino,dev }; + return teahash3((char *) &tup, sizeof tup); + } + + return pseudo_inode(ino, dev); +} + #if 1 static char * -fh_buildpath(svc_fh *h) +fh_buildpath(svc_fh *h, dev_t basedev) { char pathbuf[PATH_MAX + NAME_MAX + 1], *path; long cookie_stack[HP_LEN + 1]; @@ -565,13 +594,17 @@ if (efs_stat("/", &sbuf) < 0) return (NULL); - psi = pseudo_inode(sbuf.st_ino, sbuf.st_dev); if (h->hash_path[0] == 0) { - if (psi != h->psi) - return (NULL); - return xstrdup("/"); + if (sbuf.st_ino == h->ino && sbuf.st_dev == h->dev) + ; + else + return NULL; + strcpy(pathbuf,"/"); + path = xstrdup(pathbuf); + return (path); } + psi = pseudo_inode(sbuf.st_ino, sbuf.st_dev); if (hash_psi(psi) != h->hash_path[1]) return (NULL); @@ -599,11 +632,18 @@ psi = pseudo_inode(dp->d_ino, sbuf.st_dev); if (i == h->hash_path[0] + 1) { - if (psi != h->psi) + if (sbuf.st_dev != h->dev || dp->d_ino != h->ino) continue; /* GOT IT */ strcpy(pathbuf + pathlen, dp->d_name); - path = xstrdup(pathbuf); + if (!basedev || sbuf.st_dev == basedev || + auth_checkpathdev(pathbuf, sbuf.st_dev)) { + path = xstrdup(pathbuf); + } else { + dprintf(L_ERROR, "fh_buildpath: basedev %x != dev %x for %s\n", + (unsigned)basedev,(unsigned)sbuf.st_dev,pathbuf); + path = NULL; + } efs_closedir(dir); auth_override_uid(auth_uid); return (path); @@ -754,16 +794,16 @@ #endif static psi_t -path_psi(char *path, nfsstat *status, struct stat *sbp, int svalid) +path_psi(char *path, nfsstat *status, struct stat *sbp, int svalid, int *mp) { struct stat smounted; - return path_psi_m(path, status, sbp, &smounted, svalid); + return path_psi_m(path, status, sbp, &smounted, svalid, mp); } static psi_t path_psi_m(char *path, nfsstat *status, - struct stat *sbp, struct stat *mbp, int svalid) + struct stat *sbp, struct stat *mbp, int svalid, int *mp) { struct stat sbuf, ddbuf; @@ -815,6 +855,8 @@ DIR *dirp; struct dirent *dp; + if (mp) *mp = 1; + errno = 0; dirp = efs_opendir(dname); fname[-1] = '/'; /* Restore path */ @@ -860,9 +902,70 @@ } fhcache * -fh_find(svc_fh *h, int mode) +fh_newfh(svc_fh *h, int mode, dev_t basedev) +{ + fhcache *fhc, *flush; + + ex_state = active; + for (flush = fh_tail.prev; fh_list_size > FH_CACHE_LIMIT; flush = fhc) { + /* Don't flush current head. */ + if (flush == &fh_head) + break; + fhc = flush->prev; + fh_delete(flush); + } + fhc = (fhcache *) xmalloc(sizeof *fhc); + if (mode == FHFIND_FCREATE) { + /* File will be created */ + fhc->path = NULL; + } else { + /* File must exist. Attempt to construct from hash_path */ + char *path; + + if ((path = fh_buildpath(h, basedev)) == NULL) { +#ifdef FHTRACE + Dprintf(D_FHTRACE, "fh_find: stale fh (hash path)\n"); + Dprintf(D_FHTRACE, "\tdata: %s\n", fh_dump(h)); +#endif + free(fhc); + ex_state = inactive; + return NULL; + } + fhc->path = path; + } + fhc->flags = 0; + if (fhc->path && efs_lstat(fhc->path, &fhc->attrs) >= 0) { + if (re_export && nfsmounted(fhc->path, &fhc->attrs)) + fhc->flags |= FHC_NFSMOUNTED; + fhc->flags |= FHC_ATTRVALID; + } + fhc->fd = -1; + fhc->last_used = curtime; + fhc->h = *h; + fhc->last_clnt = NULL; + fhc->last_mount = NULL; + fhc->last_uid = (uid_t)-1; + fhc->fd_next = fhc->fd_prev = NULL; + fh_inserthead(fhc); + Dprintf(D_FHCACHE, + "fh_find: created new handle %x (path `%s' ino:%x dev:%x)\n", + fhc, fhc->path ? fhc->path : "", fhc->h.ino, fhc->h.dev); + ex_state = inactive; + if (fh_list_size > FH_CACHE_LIMIT) + flush_cache(0); +#ifdef FHTRACE + if (fhc->h.hash_path[0] == 0xFF) { + Dprintf(L_ERROR, "newly created fh instantly flushed?!"); + return NULL; + } +#endif + return (fhc); +} + +fhcache * +fh_find(svc_fh *h, int mode, dev_t basedev) { - register fhcache *fhc, *flush; + register fhcache *fhc; int check; check = (mode & FHFIND_CHECK); @@ -877,12 +980,12 @@ ex_state = active; time(&curtime); - while ((fhc = fh_lookup(h->psi)) != NULL) { + while ((fhc = fh_lookup(h->ino,h->dev)) != NULL) { struct stat sbuf, *s = NULL; nfsstat dummy; - Dprintf(D_FHCACHE, "fh_find: psi=%lx... found '%s', fd=%d\n", - (unsigned long) h->psi, + Dprintf(D_FHCACHE, "fh_find: (%u,%u)... found '%s', fd=%d\n", + h->ino, h->dev, fhc->path ? fhc->path : "", fhc->fd); @@ -905,6 +1008,7 @@ Dprintf(D_FHTRACE, "fh_find: stale fh: lstat: %m\n"); } else { + int mp = 0; /* If device/ino don't match, fhc->path may * be a mount point (hence lstat() returns * a different inode number than the readdir() @@ -915,19 +1019,26 @@ /* Get the dev/ino of the underlying * mount point. */ - path_psi(fhc->path, &dummy, s, 1); - if (fh_attrmatch(fhc, s)) - goto fh_return; + if (path_psi(fhc->path, &dummy, s, 1, &mp) && + fh_attrmatch(fhc, s)) { + if (!mp) + Dprintf(D_FHTRACE,"fh_find: should be mount point %x,%x\n", + h->dev,h->ino); + + } - Dprintf(D_FHTRACE, "fh_find: stale fh: %lx", - (unsigned long) h->psi); + Dprintf(D_FHTRACE, "fh_find: stale fh: " + "dev/ino %x/%lx ino:%x dev:%x", + s->st_dev, s->st_ino, + (unsigned)h->ino, (unsigned)h->dev); } fh_discard: #ifdef FHTRACE Dprintf(D_FHTRACE, "\tdata: %s\n", fh_dump(h)); #endif - Dprintf(D_FHCACHE, "fh_find: delete cached handle\n"); + Dprintf(D_FHCACHE, "fh_find: delete cached handle %x,%x <%x>\n", + fhc->h.dev,fhc->h.ino,fhc->path ? fhc->path : "no path"); fh_delete(fhc); break; } @@ -947,88 +1058,13 @@ return (fhc); } - Dprintf(D_FHCACHE, "fh_find: psi=%lx... not found\n", - (unsigned long) h->psi); - - if (mode == FHFIND_FCACHED) { - ex_state = inactive; - return NULL; - } - - for (flush = fh_tail.prev; fh_list_size > FH_CACHE_LIMIT; flush = fhc) { - /* Don't flush current head. */ - if (flush == &fh_head) - break; - fhc = flush->prev; - fh_delete(flush); - } - - fhc = (fhcache *) xmalloc(sizeof *fhc); - if (mode == FHFIND_FCREATE) { - /* File will be created */ - fhc->path = NULL; - } else { - /* File must exist. Attempt to construct from hash_path */ - char *path; - - if ((path = fh_buildpath(h)) == NULL) { -#ifdef FHTRACE - Dprintf(D_FHTRACE, "fh_find: stale fh (hash path)\n"); - Dprintf(D_FHTRACE, "\tdata: %s\n", fh_dump(h)); -#endif - free(fhc); - ex_state = inactive; - return NULL; - } - fhc->path = path; - } - - fhc->flags = 0; - if (fhc->path && efs_lstat(fhc->path, &fhc->attrs) >= 0) { - if (nfsmounted(fhc->path, &fhc->attrs)) { - fhc->flags |= FHC_NFSMOUNTED; -#if 0 - /* We must allow the client to send us the - * file handle for the NFS mount point itself, - * but not for entries within an NFS mount. - * XXX: needs fixing. - */ - if (!re_export) { - Dprintf(D_FHTRACE, - "Attempt to use %s (non-exportable)\n", - fhc->path); - free(fhc); - ex_state = inactive; - return NULL; - } -#endif - } - fhc->flags |= FHC_ATTRVALID; - fhc->dev = fhc->attrs.st_dev; - fhc->ino = fhc->attrs.st_ino; - fhc->type = fhc->attrs.st_mode & S_IFMT; - } - fhc->fd = -1; - fhc->last_used = curtime; - fhc->h = *h; - fhc->last_clnt = NULL; - fhc->last_mount = NULL; - fhc->last_uid = (uid_t)-1; - fhc->fd_next = fhc->fd_prev = NULL; - fh_inserthead(fhc); - Dprintf(D_FHCACHE, - "fh_find: created new handle %x (path `%s' psi %08x)\n", - fhc, fhc->path ? fhc->path : "", fhc->h.psi); ex_state = inactive; - if (fh_list_size > FH_CACHE_LIMIT) - flush_cache(0); -#ifdef FHTRACE - if (fhc->h.hash_path[0] == 0xFF) { - Dprintf(L_ERROR, "newly created fh instantly flushed?!"); + + Dprintf(D_FHCACHE, "fh_find: (%u,%u) ... not found\n", + h->ino, h->dev); + if (mode == FHFIND_FCACHED) return NULL; - } -#endif - return (fhc); + return fh_newfh(h, mode, basedev); } /* @@ -1040,7 +1076,7 @@ { fhcache *h; - if ((h = fh_find((svc_fh *) fh, FHFIND_FCACHED)) == NULL) + if ((h = fh_find((svc_fh *) fh, FHFIND_FCACHED, 0)) == NULL) return fh_dump((svc_fh *) fh); return (h->path); } @@ -1050,10 +1086,10 @@ { static char buf[65]; char *sp; - int i, n = fh->hash_path[0]; + int i, n = fh->hash_path[0], l; - sprintf(buf, "%08x %02x ", fh->psi, fh->hash_path[0]); - for (i = 1, sp = buf + 12; i <= n && i < HP_LEN; i++, sp += 2) + l = sprintf(buf, "%08x %04x %02x ", fh->ino, fh->dev, fh->hash_path[0]); + for (i = 1, sp = buf + l; i <= n && i < HP_LEN; i++, sp += 2) sprintf(sp, "%02x", fh->hash_path[i]); return buf; } @@ -1082,7 +1118,7 @@ memset(&key, 0, sizeof(key)); status = NFS_OK; - if ((psi = path_psi("/", &status, &stb, 0)) == 0) + if ((psi = path_psi("/", &status, &stb, 0, NULL)) == 0) return ((int) status); s = path; @@ -1091,7 +1127,7 @@ return ((int) NFSERR_NAMETOOLONG); key.hash_path[key.hash_path[0]] = hash_psi(psi); *s = '\0'; - if ((psi = path_psi(path, &status, &stb, 0)) == 0) + if ((psi = path_psi(path, &status, &stb, 0, NULL)) == 0) return ((int) status); *s = '/'; } @@ -1099,11 +1135,12 @@ if (++(key.hash_path[0]) >= HP_LEN) return ((int) NFSERR_NAMETOOLONG); key.hash_path[key.hash_path[0]] = hash_psi(psi); - if ((psi = path_psi(path, &status, &stb, 0)) == 0) + if ((psi = path_psi(path, &status, &stb, 0, NULL)) == 0) return ((int) status); } - key.psi = psi; - h = fh_find(&key, FHFIND_FCREATE); + key.dev = stb.st_dev; + key.ino = stb.st_ino; + h = fh_find(&key, FHFIND_FCREATE, 0); #ifdef FHTRACE if (!h) @@ -1123,6 +1160,7 @@ return ((int) status); } +#if 0 char * fh_path(nfs_fh *fh, nfsstat *status) { @@ -1135,6 +1173,7 @@ *status = NFS_OK; return (h->path); } +#endif nfs_fh * fh_handle(fhcache *h) @@ -1349,7 +1388,7 @@ if (sbp == NULL) sbp = &sbuf; - if ((dirh = fh_find((svc_fh *) &dopa->dir, FHFIND_FEXISTS)) == NULL) + if ((dirh = fh_find((svc_fh *) &dopa->dir, FHFIND_FEXISTS, 0)) == NULL) return NFSERR_STALE; /* @@ -1419,8 +1458,22 @@ *new_fh = dopa->dir; key = (svc_fh *) new_fh; - if ((key->psi = path_psi_m(pathbuf, &ret, sbp, &smount, 0)) == 0) + + if (path_psi_m(pathbuf, &ret, sbp, &smount, 0, NULL) == 0) return (ret); + key->ino = sbp->st_ino; + key->dev = sbp->st_dev; + + if (sbp->st_dev != dirh->h.dev) { + nfs_mount *mp = dirh->last_mount; + if (!mp) + Dprintf(L_ERROR, "no last mount in fh_compose for %s\n", pathbuf); + else if (auth_checkdev(mp, sbp->st_dev) == 0) { + Dprintf(L_ERROR, "access to no cross path below mountpoint (<%s>, %x<->%x)\n", + pathbuf, mp->mount_dev, sbp->st_dev); + return NFSERR_STALE; + } + } if (is_dd) { /* Don't cd .. from root, or mysterious ailments will @@ -1430,11 +1483,12 @@ } else { if (++(key->hash_path[0]) >= HP_LEN) return NFSERR_NAMETOOLONG; - key->hash_path[key->hash_path[0]] = hash_psi(dirh->h.psi); + key->hash_path[key->hash_path[0]] = hash_psi(pseudo_inode(dirh->h.ino, + dirh->h.dev)); } /* FIXME: when crossing a mount point, we'll find the real * dev/ino in sbp and can store it in h... */ - h = fh_find(key, FHFIND_FCREATE); + h = fh_find(key, FHFIND_FCREATE, 0); #ifdef FHTRACE if (h == NULL) @@ -1456,7 +1510,7 @@ /* We must have cached an old file under the same inode # */ Dprintf(D_FHTRACE, "Disposing of fh with bad path.\n"); fh_delete(h); - h = fh_find(key, FHFIND_FCREATE); + h = fh_find(key, FHFIND_FCREATE, dirh->last_mount ? dirh->last_mount->mount_dev : 0); #ifdef FHTRACE if (!h) return NFSERR_STALE; #endif @@ -1511,12 +1565,14 @@ return (NFS_OK); } +#if 0 psi_t fh_psi(nfs_fh *fh) { svc_fh *h = (svc_fh *) fh; return (h->psi); } +#endif void fh_remove(char *path) @@ -1524,12 +1580,13 @@ psi_t psi; nfsstat status; fhcache *fhc; + struct stat st; - psi = path_psi(path, &status, NULL, 0); + psi = path_psi(path, &status, &st, 0, NULL); if (psi == 0) return; ex_state = active; - fhc = fh_lookup(psi); + fhc = fh_lookup(st.st_ino,st.st_dev); if (fhc != NULL) fh_delete(fhc); @@ -1634,6 +1691,11 @@ fh_init(void) { static int initialized = 0; + + if (sizeof(svc_fh) > 32) { + fprintf(stderr, "filehandle wrong size %d\n", sizeof(svc_fh)); + exit(10); + } if (initialized) return; --- nfs-server/fh.h +++ nfs-server/fh.h 2002/11/08 13:59:16 @@ -20,6 +20,7 @@ #define FHC_XONLY_PATH 001 /* NOT USED ANYMORE */ #define FHC_ATTRVALID 002 #define FHC_NFSMOUNTED 004 +#define FHC_CROSS 010 /* Modes for fh_find */ #define FHFIND_FEXISTS 0 /* file must exist */ @@ -65,11 +66,12 @@ * * hash_path[hash_path[0]+1] ... hash_path[HP_LEN-1] == 0 */ -#define HP_LEN (NFS_FHSIZE - sizeof(psi_t)) +#define HP_LEN (NFS_FHSIZE-sizeof(u_int32_t)-sizeof(u_int16_t)) typedef struct { - psi_t psi; + u_int32_t ino; + u_int16_t dev; __u8 hash_path[HP_LEN]; -} svc_fh; +} svc_fh __attribute__((packed)); typedef enum { inactive, active } mutex; @@ -100,6 +102,7 @@ /* These are fixed during the lifetime of this object */ svc_fh h; + psi_t psi; dev_t dev; ino_t ino; mode_t type; /* st_mode & S_IFMT */ @@ -122,10 +125,11 @@ /* Global function prototypes. */ extern nfsstat nfs_errno(void); extern psi_t pseudo_inode(ino_t inode, dev_t dev); +extern psi_t visible_inode(ino_t inode, dev_t dev, nfs_mount *); extern void fh_init(void); extern char *fh_pr(nfs_fh *fh); extern int fh_create(nfs_fh *fh, char *path); -extern fhcache *fh_find(svc_fh *h, int create); +extern fhcache *fh_find(svc_fh *h, int create, dev_t basedev); extern char *fh_path(nfs_fh *fh, nfsstat *status); extern int path_open(char *path, int omode, int perm); extern int fh_fd(fhcache *fhc, nfsstat *status, int omode); @@ -139,6 +143,7 @@ extern void fh_flush(int force); extern RETSIGTYPE flush_cache(int sig); extern int nfsmounted(const char *path, struct stat *sbp); +extern fhcache *fh_newfh(svc_fh *fh, int mode, dev_t basedev); #ifdef ENABLE_DEVTAB extern unsigned int devtab_index(dev_t); --- nfs-server/getattr.c +++ nfs-server/getattr.c 2002/11/08 13:59:16 @@ -43,7 +43,7 @@ { fhcache *fhc; - if ((fhc = fh_find((svc_fh*)fh, FHFIND_FEXISTS)) == NULL) { + if ((fhc = fh_find((svc_fh*)fh, FHFIND_FEXISTS, 0)) == NULL) { Dprintf(D_CALL, "getattr: failed! No such file.\n"); return (NFSERR_STALE); } @@ -103,18 +103,8 @@ #else attr->blocks = st_blocks(s); #endif -#if 0 - if (nfsmount->o.cross_mounts) { - attr->fsid = 1; - attr->fileid = fh_psi((nfs_fh *)&(fhc->h)); - } else { - attr->fsid = s->st_dev; - attr->fileid = covered_ino(fhc->path); - } -#else - attr->fsid = 1; - attr->fileid = fh_psi((nfs_fh *)&(fhc->h)); -#endif + attr->fsid = 1; // XXX + attr->fileid = visible_inode(fhc->h.ino, fhc->h.dev, nfsmount); /* This may be needed by some Suns... testing */ #define MINTIME (24 * 2600) --- nfs-server/mountd.c +++ nfs-server/mountd.c 2002/11/08 13:59:16 @@ -36,6 +36,8 @@ #include "signals.h" #include +int cross_mounts = 1; +int hashed_inodes; /* dummy */ static void usage(FILE *, int); static void terminate(void); @@ -58,9 +60,9 @@ { "no-spoof-trace", 0, 0, 't' }, { "version", 0, 0, 'v' }, { "fail-safe", optional_argument, 0, 'z' }, + { "no-cross-mounts", 0, 0, 'x' }, { "no-tcp", 0, 0, OPT_NOTCP }, { "loopback-only", 0, 0, OPT_LOOPBACK }, - { NULL, 0, 0, 0 } }; static const char * shortopts = "Fd:f:hnpP:rtvz::"; @@ -80,6 +82,7 @@ int need_reinit = 0; int need_flush = 0; extern char version[]; +nfs_client *nfsclient; /* dummy */ /* * NULL @@ -319,6 +322,9 @@ opterr = 0; while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != EOF) switch (c) { + case 'x': + cross_mounts = 0; + break; case 'F': foreground = 1; break; @@ -444,7 +450,7 @@ program_name); fprintf(fp, " [--debug kind] [--help] [--allow-non-root]\n"); fprintf(fp, " [--promiscuous] [--version] [--port portnum]\n"); - fprintf(fp, " [--exports-file=file]\n"); + fprintf(fp, " [--exports-file=file] [--no-cross-mounts]\n"); exit(n); } --- nfs-server/nfsd.c +++ nfs-server/nfsd.c 2002/11/08 14:20:57 @@ -72,7 +72,7 @@ { "no-tcp", 0, 0, OPT_NOTCP }, { "udp-only", 0, 0, OPT_NOTCP }, { "loopback-only", 0, 0, OPT_LOOPBACK }, - + { "hashed-inodes", 0, 0, 'I' }, { NULL, 0, 0, 0 } }; static const char * shortopts = "a:d:Ff:hlnP:prR:tvz::"; @@ -91,6 +91,7 @@ int need_flush = 0; /* flush fh cache */ int read_only = 0; /* Global ro forced */ int cross_mounts = 1; /* Transparently cross mnts */ +int hashed_inodes = 0; int log_transfers = 0; /* Log transfers */ static svc_fh public_fh; /* Public NFSv2 FH */ @@ -122,12 +123,17 @@ { static int total = 0, cached = 0; fhcache *fhc; + int newfh = 0; - /* Try to map FH. If not cached, reconstruct path with root priv */ - fhc = fh_find((svc_fh *)fh, FHFIND_FEXISTS|FHFIND_CHECK); - if (fhc == NULL) { - *statp = NFSERR_STALE; - return NULL; + /* Try to map FH. */ + fhc = fh_find((svc_fh *)fh, FHFIND_FCACHED|FHFIND_CHECK, 0); + if (!fhc) { + fhc = fh_newfh((svc_fh*)fh, FHFIND_FEXISTS|FHFIND_CHECK, 0); + if (!fhc) { + *statp = NFSERR_STALE; + return NULL; + } + newfh = 1; } /* Try to retrieve last client who accessed this fh */ @@ -163,6 +169,16 @@ 100 * (double) cached / total); */ + /* Trust the crossmount check of the parent directory for creates */ + if (newfh && + (fhc->flags & FHC_ATTRVALID) && + auth_checkdev(nfsmount, fhc->attrs.st_dev) == 0) { + Dprintf(L_ERROR, "auth_fh: fh crossed mount %s: %x<->%x\n", + fhc->path ? fhc->path : "???", nfsmount->mount_dev, fhc->attrs.st_dev); + *statp = NFSERR_STALE; /* or ACCES? */ + return NULL; + } + if (nfsmount->o.noaccess && ((flags & CHK_NOACCESS) || strcmp(nfsmount->path, fhc->path))) { struct in_addr addr = svc_getcaller(rqstp->rq_xprt)->sin_addr; @@ -195,6 +211,7 @@ fhcache *fhc; nfsstat status; char *path = buf, *sp; + struct stat st; /* Authenticate directory file handle */ if ((fhc = auth_fh(rqstp, &dopa->dir, &status, flags)) == NULL) @@ -219,6 +236,9 @@ if ((nfsmount = auth_path(nfsclient, rqstp, path)) == NULL) return NFSERR_ACCES; + if (efs_lstat(path, &st) >= 0 && !auth_checkdev(nfsmount, st.st_dev)) + return NFSERR_ACCES; + /* XXX: really need to call it again here? * Already invoked in auth_fh */ if (!auth_user(nfsmount, rqstp)) @@ -318,7 +338,8 @@ int ispublic = 0; /* First check whether this is the public FH */ - if (((svc_fh *) fh)->psi == 0 && !memcmp(fh, &public_fh, FHSIZE)) { + if (((svc_fh *) fh)->dev == 0 && ((svc_fh*)fh)->ino == 0 && + !memcmp(fh, &public_fh, FHSIZE)) { if (public_root_path == NULL) return NFSERR_ACCES; memcpy(&argp->dir, &public_root, NFS_FHSIZE); @@ -333,6 +354,7 @@ if (!(fhc = auth_fh(rqstp, fh, &status, CHK_READ))) return status; + /* FIXME: does too many stats */ status = fh_compose(argp, &dp->file, &sbuf, -1, -1, ispublic); if (status != NFS_OK) return status; @@ -896,6 +918,9 @@ errno = 0; if (efs_lstat(h->path, &sbuf) < 0 || !(S_ISDIR(sbuf.st_mode))) return (NFSERR_NOTDIR); + if (!auth_checkdev(h->last_mount, sbuf.st_dev)) + dotsonly = 1; + if ((dirp = efs_opendir(h->path)) == NULL) return ((errno ? nfs_errno() : NFSERR_NAMETOOLONG)); @@ -923,7 +948,7 @@ } e = *ep = (entry *) xmalloc(sizeof(entry)); - e->fileid = pseudo_inode(dp->d_ino, sbuf.st_dev); + e->fileid = visible_inode(dp->d_ino, sbuf.st_dev, h->last_mount); e->name = xmalloc(NLENGTH(dp) + 1); strcpy(e->name, dp->d_name); dloc = htonl(efs_telldir(dirp)); @@ -1033,6 +1058,9 @@ case 'x': cross_mounts = 0; break; + case 'I': + hashed_inodes = 1; + break; case 'z': if (optarg) failsafe_level = atoi(optarg); @@ -1189,7 +1217,7 @@ " [--debug kind] [--exports-file=file] [--port port]\n" " [--allow-non-root] [--promiscuous] [--version] [--foreground]\n" " [--re-export] [--log-transfers] [--public-root path]\n" -" [--no-spoof-trace] [--help]\n" +" [--no-spoof-trace] [--no-cross-mounts] [--hashed-inodes] [--help]\n" , program_name); exit(n); } --- nfs-server/nfsd.h +++ nfs-server/nfsd.h 2002/11/08 13:59:16 @@ -51,6 +51,7 @@ extern int need_reinit; extern int need_flush; extern time_t nfs_dispatch_time; +extern int cross_mounts, hashed_inodes; /* Include the other module definitions. */ #include "auth.h" --- nfs-server/setattr.c +++ nfs-server/setattr.c 2002/11/08 13:59:16 @@ -17,6 +17,7 @@ #define IGNORE_TIME ((unsigned int) -1) +#if 0 /* * Set file attributes based on file handle */ @@ -33,6 +34,7 @@ } return setattr(path, attr, s, rqstp, flags); } +#endif /* * Set file attributes given the path. The flags argument --- nfs-server/teahash3.c +++ nfs-server/teahash3.c 2002/11/08 13:59:16 @@ -0,0 +1,168 @@ +/* Taken from the reiserfs source code and hacked slightly by AK. + * This is GPLed. */ +/* + * Keyed 32-bit hash function using TEA in a Davis-Meyer function + * H0 = Key + * Hi = E Mi(Hi-1) + Hi-1 + * + * (see Applied Cryptography, 2nd edition, p448). + * + * Jeremy Fitzhardinge 1998 + * + * Jeremy has agreed to the contents of reiserfs/README. -Hans + */ + +#include + +#if 0 +/* OK for Intel */ +typedef unsigned long u32; +typedef const unsigned char u8; +#else +#include +typedef uint32_t u32; +typedef uint8_t u8; +#endif + + +#define DELTA 0x9E3779B9 +#define FULLROUNDS 10 /* 32 is overkill, 16 is strong crypto */ +#define PARTROUNDS 6 /* 6 gets complete mixing */ + +/* a, b, c, d - data; h0, h1 - accumulated hash */ +#define TEACORE(rounds) \ + do { \ + u32 sum = 0; \ + int n = rounds; \ + u32 b0, b1; \ + \ + b0 = h0; \ + b1 = h1; \ + \ + do \ + { \ + sum += DELTA; \ + b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); \ + b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); \ + } while(--n); \ + \ + h0 += b0; \ + h1 += b1; \ + } while(0) + +u32 teahash3(/*u32 k[2], *//*u8*/const char *msg, int len) +{ + u32 k[] = { 0x9464a485, 0x542e1a94, 0x3e846bff, 0xb75bcfc3}; + + u32 h0 = k[0], h1 = k[1]; + u32 a, b, c, d; + u32 pad; + int i; + + assert(len >= 0 && len < 256); + + pad = (u32)len | ((u32)len << 8); + pad |= pad << 16; + + while(len >= 16) + { + a = (u32)msg[ 0] | + (u32)msg[ 1] << 8 | + (u32)msg[ 2] << 16| + (u32)msg[ 3] << 24; + b = (u32)msg[ 4] | + (u32)msg[ 5] << 8 | + (u32)msg[ 6] << 16| + (u32)msg[ 7] << 24; + c = (u32)msg[ 8] | + (u32)msg[ 9] << 8 | + (u32)msg[10] << 16| + (u32)msg[11] << 24; + d = (u32)msg[12] | + (u32)msg[13] << 8 | + (u32)msg[14] << 16| + (u32)msg[15] << 24; + + TEACORE(PARTROUNDS); + + len -= 16; + msg += 16; + } + + if (len >= 12) + { + assert(len < 16); + + a = (u32)msg[ 0] | + (u32)msg[ 1] << 8 | + (u32)msg[ 2] << 16| + (u32)msg[ 3] << 24; + b = (u32)msg[ 4] | + (u32)msg[ 5] << 8 | + (u32)msg[ 6] << 16| + (u32)msg[ 7] << 24; + c = (u32)msg[ 8] | + (u32)msg[ 9] << 8 | + (u32)msg[10] << 16| + (u32)msg[11] << 24; + + d = pad; + for(i = 12; i < len; i++) + { + d <<= 8; + d |= msg[i]; + } + } + else if (len >= 8) + { + assert(len < 12); + + a = (u32)msg[ 0] | + (u32)msg[ 1] << 8 | + (u32)msg[ 2] << 16| + (u32)msg[ 3] << 24; + b = (u32)msg[ 4] | + (u32)msg[ 5] << 8 | + (u32)msg[ 6] << 16| + (u32)msg[ 7] << 24; + + c = d = pad; + for(i = 8; i < len; i++) + { + c <<= 8; + c |= msg[i]; + } + } + else if (len >= 4) + { + assert(len < 8); + + a = (u32)msg[ 0] | + (u32)msg[ 1] << 8 | + (u32)msg[ 2] << 16| + (u32)msg[ 3] << 24; + + b = c = d = pad; + for(i = 4; i < len; i++) + { + b <<= 8; + b |= msg[i]; + } + } + else + { + assert(len < 4); + + a = b = c = d = pad; + for(i = 0; i < len; i++) + { + a <<= 8; + a |= msg[i]; + } + } + + TEACORE(FULLROUNDS); + +/* return 0;*/ + return h0^h1; +} --- nfs-server/ugid_map.c +++ nfs-server/ugid_map.c 2002/11/08 13:59:16 @@ -276,8 +276,10 @@ if ((gid == 0 && mountp->o.root_squash) || mountp->o.all_squash) retgid = mountp->o.nobody_gid; +#if 0 Dprintf(D_UGID, "lgid(%s, %d) = %d\n", inet_ntoa(mountp->client->clnt_addr), gid, retgid); +#endif return retgid; }