/* $Id$ * * Original version of this file from p3nfsd-5.4 by * Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de) * * Modifications for plputils by Fritz Elfert * */ #include "OSdefs.h" #include #include #include #if defined(__SVR4) || defined(__GLIBC__) || defined(__NetBSD__) #include #include #include #endif #include "nfs_prot.h" #include "mp.h" #include "rfsv_api.h" #include "builtins.h" static device *devices; struct cache *attrcache = NULL; long param_read(builtin_node *node, char *buf, unsigned long offset, long len) { char tmp[10]; unsigned long val = 0; if (!strcmp(node->name, "acache")) val = cache_keep; if (!strcmp(node->name, "dcache")) val = devcache_keep; if (!strcmp(node->name, "debuglevel")) val = debug; sprintf(tmp, "%ld\n", val); if (offset >= strlen(tmp)) return 0; strncpy(buf, &tmp[offset], len); return strlen(buf); } long param_write(builtin_node *node, char *buf, unsigned long offset, long len) { unsigned long val; if (len > 1 && offset == 0) { int res = sscanf(buf, "%ld", &val); if (res == 1) { if (!strcmp(node->name, "acache")) cache_keep = val; if (!strcmp(node->name, "dcache")) devcache_keep = val; if (!strcmp(node->name, "debuglevel")) { if (val != debug) { if (val > debug) debug = val; debuglog("Set debug level to %d\n", val); debug = val; } } } } return len; } long param_getsize(builtin_node *node) { char tmp[10]; return param_read(node, tmp, 0, sizeof(tmp)); } long exit_write(builtin_node *node, char *buf, unsigned long offset, long len) { if (len >= 4 && offset == 0) { if (!strncmp(buf, "stop", 4)) exiting = 5; /* Lets try it 5 times (10 sec) */ } return len; } long exit_read(builtin_node *node, char *buf, unsigned long offset, long len) { if (offset > 4) return 0; if ((len + offset) > 5) len = 5 - offset; strncpy(buf, "----\n", len); return strlen(buf); } long user_write(builtin_node *node, char *buf, unsigned long offset, long len) { char tmp[256]; if (len > 1 && offset == 0) { char *p; if (len > (sizeof(tmp) - 1)) len = sizeof(tmp) - 1; strncpy(tmp, buf, len); debuglog("pwrite: %d\n", len); tmp[len] = '\0'; if ((p = strchr(tmp, '\n'))) *p = '\0'; set_owner(tmp, 0); } return len; } long user_read(builtin_node *node, char *buf, unsigned long offset, long len) { struct passwd *pw = getpwuid(root_fattr.uid); char tmp[255]; if (pw) sprintf(tmp, "%s\n", pw->pw_name); else sprintf(tmp, "???\n"); endpwent(); if (offset >= strlen(tmp)) return 0; strncpy(buf, &tmp[offset], len); return strlen(buf); } long user_getsize(builtin_node *node) { char tmp[255]; return user_read(node, tmp, 0, sizeof(tmp)); } static long generic_sattr(builtin_node *p, unsigned long sa, unsigned long da) { p->attr |= sa; p->attr &= ~da; return 0; } long generic_getlinks(builtin_node *node) { builtin_child *cp = node->childs; long ncount = 0; while (cp) { if ((cp->node->flags & BF_EXISTS_ALWAYS) || rfsv_isalive()) ncount++; cp = cp->next; } return ncount; } long generic_getdents(builtin_node *node, dentry **e) { builtin_child *cp = node->childs; while (cp) { if ((cp->node->flags & BF_EXISTS_ALWAYS) || rfsv_isalive()) { dentry *tmp = (dentry *)malloc(sizeof(dentry)); if (!tmp) return -1; tmp->time = time(0); tmp->size = cp->node->getsize ? cp->node->getsize(cp->node) : cp->node->size; tmp->attr = cp->node->attr; tmp->name = strdup(cp->node->name); tmp->next = *e; *e = tmp; } cp = cp->next; } return 0; } static builtin_node *builtins = NULL; static void dump_proctree(builtin_node *n); static void clear_procs(builtin_child **childs) { builtin_child **cp = childs; debuglog("Before clear_procs\n"); dump_proctree(builtins); while (*cp) { if ((*cp)->node->flags & BF_ISPROCESS) { builtin_child *tmp = *cp; *cp = (*cp)->next; unregister_builtin(tmp->node); free(tmp); } else cp = &((*cp)->next); } debuglog("After clear_procs\n"); dump_proctree(builtins); } static time_t procs_stamp = 0; static long procs_keep = 10; static long proc_getlinks(builtin_node *node) { builtin_child *cp; long ncount = 0; if ((time(0) - procs_stamp) > procs_keep) { debuglog("PROCESSLIST\n"); clear_procs(&node->childs); rpcs_ps(); procs_stamp = time(0); debuglog("After rpcs_ps\n"); dump_proctree(builtins); } cp = node->childs; while (cp) { if ((cp->node->flags & BF_EXISTS_ALWAYS) || rfsv_isalive()) ncount++; cp = cp->next; } return ncount; } static long proc_getdents(builtin_node *node, dentry **e) { builtin_child *cp = node->childs; while (cp) { if ((cp->node->flags & BF_EXISTS_ALWAYS) || rfsv_isalive()) { dentry *tmp = (dentry *)malloc(sizeof(dentry)); if (!tmp) return -1; tmp->time = time(0); tmp->size = cp->node->getsize ? cp->node->getsize(cp->node) : cp->node->size; tmp->attr = cp->node->attr; tmp->name = strdup(cp->node->name); tmp->next = *e; *e = tmp; } cp = cp->next; } return 0; } static builtin_node proc_node = { NULL, NULL, NULL, NULL, "proc", BF_EXISTS_ALWAYS|BF_NOCACHE, PSI_A_DIR, 0, NULL, NULL, NULL, NULL, proc_getlinks, proc_getdents }; static builtin_node fixed_builtins[] = { { NULL, NULL, NULL, NULL, "owner", 0, PSI_A_READ | PSI_A_RDONLY, 0, rpcs_ownerSize, rpcs_ownerRead, NULL, NULL, NULL, NULL }, { NULL, NULL, NULL, NULL, "debuglevel", BF_EXISTS_ALWAYS|BF_NOCACHE, PSI_A_READ, 0, param_getsize, param_read, param_write, generic_sattr, NULL, NULL }, { NULL, NULL, NULL, NULL, "acache", BF_EXISTS_ALWAYS|BF_NOCACHE, PSI_A_READ, 0, param_getsize, param_read, param_write, generic_sattr, NULL, NULL }, { NULL, NULL, NULL, NULL, "dcache", BF_EXISTS_ALWAYS|BF_NOCACHE, PSI_A_READ, 0, param_getsize, param_read, param_write, generic_sattr, NULL, NULL }, { NULL, NULL, NULL, NULL, "unixowner", BF_EXISTS_ALWAYS|BF_NOCACHE, PSI_A_READ, 0, user_getsize, user_read, user_write, NULL, NULL, NULL }, { NULL, NULL, NULL, NULL, "exit", BF_EXISTS_ALWAYS|BF_NOCACHE, PSI_A_READ, 5, NULL, exit_read, exit_write, NULL, NULL, NULL }, }; static int num_fixed_builtins = sizeof(fixed_builtins) / sizeof(builtin_node); static void dump_proctree(builtin_node *n) { while (n) { if (n->name) debuglog("node@%p, \"%s\"\n", n, n->name); else debuglog("node@%p, \"(null)\"\n", n); if (n->childs) { builtin_child *c = n->childs; debuglog("Childs:\n"); while (c) { if (c->node) debuglog(" %s\n", c->node->name ? c->node->name : "(null)"); c = c->next; } } if (n->parent) debuglog("Parent: %s\n", n->parent->name); n = n->next; } } static int add_child(builtin_child **childs, builtin_node *node) { builtin_child *newc = malloc(sizeof(builtin_child)); if (!newc) { errorlog("Out of memory in add_child %s\n", node->name); return -1; } newc->next = *childs; newc->node = node; *childs = newc; return 0; } static int remove_child(builtin_child **childs, builtin_node *node) { builtin_child **cp = childs; if (debug) debuglog("remove_child %s\n", node->name); while (*cp) { if ((*cp)->node == node) { builtin_child *tmp = *cp; *cp = (*cp)->next; free(tmp); return 0; } cp = &((*cp)->next); } return -1; } char *builtin_path(builtin_node *node) { static char tmp[1024]; char tmp2[1024]; strcpy(tmp, node->name); node = node->parent; while (node) { sprintf(tmp2, "%s\\%s", node->name, tmp); strcpy(tmp, tmp2); node = node->parent; } return tmp; } int unregister_builtin(builtin_node *node) { builtin_child *cp = node->childs; builtin_node **n; /** * Unlink and free childs. */ if (debug) debuglog("unregister_builtin %s\n", node->name); while (cp) { builtin_child *old = cp; cp->node->parent = 0L; unregister_builtin(cp->node); cp = cp->next; free(old); } /** * Unlink ourselves from parent child list. */ if (node->parent) remove_child(&node->parent->childs, node); n = &builtins; while (*n) { if (*n == node) { *n = node->next; break; } else n = &(*n)->next; } if (node->name) free(node->name); if (node->private_data) free(node->private_data); free(node); return 0; } builtin_node *register_builtin(char *parent, builtin_node *node) { builtin_node *bn; builtin_node *parent_node = 0L; debuglog("register_builtin node=%p\n", node); if (!node) { errorlog("register_builtin called with NULL node\n"); return NULL; } if (!node->name) { errorlog("register_builtin called without name\n"); return NULL; } if (parent) { debuglog("register_builtin parent: %s\n", parent); for (bn = builtins; bn; bn = bn->next) { debuglog("cmp parent: %s %s\n", builtin_path(bn), parent); if (!strcmp(builtin_path(bn), parent)) { debuglog("cmp parent found bn=%s\n", bn->name); break; } } if (!bn) { errorlog("register_builtin for %s called with nonexistent parent %s\n", node->name, parent); return NULL; } parent_node = bn; } bn = malloc(sizeof(builtin_node)); if (!bn) { errorlog("out of memory while registering builtin %s\n", node->name); return NULL; } memset(bn, 0, sizeof(builtin_node)); bn->name = strdup(node->name); if (!bn->name) { errorlog("out of memory while registering builtin %s\n", node->name); free(bn); return NULL; } if (parent_node) debuglog("register_builtin %s in %s\n", node->name, builtin_path(parent_node)); else debuglog("register_builtin %s\n", node->name); bn->parent = parent_node; bn->flags = node->flags; bn->attr = node->attr; bn->size = node->size; bn->getsize = node->getsize; bn->read = node->read; bn->write = node->write; bn->sattr = node->sattr; bn->getlinks = node->getlinks; bn->getdents = node->getdents; bn->private_data = node->private_data; if (parent_node) { debuglog("Add child %s in %s\n", bn->name, bn->parent->name); if (add_child(&(parent_node->childs), bn)) { errorlog("Couldn't add child %s\n", bn->name); free(bn->name); free(bn); return NULL; } } bn->next = builtins; builtins = bn; debuglog("After register_builtin\n"); dump_proctree(builtins); debuglog("register_builtin new node=%p\n", bn); return bn; } /* * Nfsd returned NFSERR_STALE if the psion wasn't present, but I didn't like * it because the kernel returns the same when the nfsd itself is absent */ #define NO_PSION NFSERR_NXIO /* FIXME: Send create attributes */ static struct diropres * create_it(createargs *ca, int isdir) { static struct diropres res; p_inode *dirinode = get_num(fh2inode(ca->where.dir.data)); char *name = dirinode->name; fattr *fp; p_inode *inode; u_int32_t phandle; int rfsv_ret; debuglog("create: in %s %s (%#o, %d)\n", name, ca->where.name, ca->attributes.mode, isdir); name = build_path(name, ca->where.name); if (isdir) rfsv_ret = rfsv_mkdir(name); else { rfsv_ret = rfsv_fcreate(0x200, name, &phandle); if (rfsv_ret == 0) rfsv_ret = rfsv_fclose(phandle); } if (rfsv_ret) { res.status = rfsv_isalive() ? NFSERR_NAMETOOLONG : NO_PSION; return &res; } inode = get_nam(name); inode2fh(inode->inode, res.diropres_u.diropres.file.data); fp = &res.diropres_u.diropres.attributes; bzero((char *) fp, sizeof(fp)); if (isdir) { fp->type = NFDIR; fp->mode = NFSMODE_DIR | 0700; fp->nlink = 2; } else { fp->type = NFREG; fp->mode = NFSMODE_REG | 0600; fp->nlink = 1; } fp->uid = root_fattr.uid; fp->gid = root_fattr.gid; fp->blocksize = BLOCKSIZE; fp->fileid = inode->inode; fp->atime.seconds = fp->mtime.seconds = fp->ctime.seconds = time((time_t *) 0); res.status = NFS_OK; rem_cache(&attrcache, dirinode->inode); rem_cache(&attrcache, inode->inode); if (rfsv_isalive()) add_cache(&attrcache, inode->inode, fp); return &res; } struct diropres * nfsproc_create_2(createargs *ca) { return create_it(ca, 0); } struct diropres * nfsproc_mkdir_2(createargs *ca) { return create_it(ca, 1); } static void attr2pattr(long oattr, long nattr, long *psisattr, long *psidattr) { /* * Following flags have to be set in order to let backups * work properly */ *psisattr = *psidattr = 0; if ((oattr & 0400) != (nattr & 0400)) { if (nattr & 0400) /* readable */ *psidattr |= PSI_A_READ; else *psisattr |= PSI_A_READ; } if ((oattr & 0200) != (nattr & 0200)) { if (nattr & 0200) /* readonly */ *psidattr |= PSI_A_RDONLY; else *psisattr |= PSI_A_RDONLY; } if ((oattr & 0020) != (nattr & 0020)) { if (nattr & 0020) /* group-write -> archive */ *psisattr |= PSI_A_ARCHIVE; else *psidattr |= PSI_A_ARCHIVE; } if ((oattr & 0004) != (nattr & 0004)) { if (nattr & 0004) /* Not world-read -> hidden */ *psidattr |= PSI_A_HIDDEN; else *psisattr |= PSI_A_HIDDEN; } if ((oattr & 0002) != (nattr & 0002)) { if (nattr & 0002) /* world-write -> system */ *psisattr |= PSI_A_SYSTEM; else *psidattr |= PSI_A_SYSTEM; } } static void pattr2attr(long psiattr, long size, long ftime, fattr *fp, int inode) { bzero((char *) fp, sizeof(*fp)); if (psiattr & PSI_A_DIR) { fp->type = NFDIR; fp->mode = NFSMODE_DIR | 0700; /* * Damned filesystem. * We have to count the number of subdirectories * on the psion. */ fp->nlink = 0; fp->size = BLOCKSIZE; fp->blocks = 1; } else { fp->type = NFREG; fp->mode = NFSMODE_REG; fp->nlink = 1; fp->size = size; fp->blocks = (fp->size + BLOCKSIZE - 1) / BLOCKSIZE; /* * Following flags have to be set in order to let backups * work properly */ if (psiattr & PSI_A_READ) fp->mode |= 0400; /* File readable (?) */ if (!(psiattr & PSI_A_RDONLY)) fp->mode |= 0200; /* File writeable */ /* fp->mode |= 0100; File executable */ if (!(psiattr & PSI_A_HIDDEN)) fp->mode |= 0004; /* Not Hidden <-> world read */ if (psiattr & PSI_A_SYSTEM) fp->mode |= 0002; /* System <-> world write */ if (psiattr & PSI_A_VOLUME) fp->mode |= 0001; /* Volume <-> world exec */ if (psiattr & PSI_A_ARCHIVE) fp->mode |= 0020; /* Modified <-> group write */ /* fp->mode |= 0040; Byte <-> group read */ /* fp->mode |= 0010; Text <-> group exec */ } fp->uid = root_fattr.uid; fp->gid = root_fattr.gid; fp->blocksize = BLOCKSIZE; fp->fileid = inode; fp->rdev = fp->fsid = FID; fp->atime.seconds = ftime; fp->mtime.seconds = fp->ctime.seconds = fp->atime.seconds; } static int proc_top = 0; static int query_devices() { device *dp, *np; int link_count = 2; /* set the root link count */ if (!proc_top) { int i; if (register_builtin(NULL, &proc_node)) { for (i = 0; i < num_fixed_builtins; i++) if (register_builtin(proc_node.name, &fixed_builtins[i])) proc_top++; } else errorlog("Couldn't register /proc\n"); if (proc_top != num_fixed_builtins) proc_top = 0; } if (query_cache) return 0; for (dp = devices; dp; dp = np) { np = dp->next; free(dp->name); free(dp); } devices = 0; debuglog("RFSV drivelist\n"); if (rfsv_drivelist(&link_count, &devices)) return 1; query_cache = 1; devcache_stamp = time(0); root_fattr.nlink = link_count + 1; return 0; } static int mp_dircount(p_inode *inode, long *count) { dentry *e = NULL; long ret; builtin_node *bn; *count = 0; debuglog("dircount: %s\n", inode->name); for (bn = builtins; bn; bn = bn->next) { if ((!strcmp(builtin_path(bn), inode->name) && (bn->attr & PSI_A_DIR))) { if (bn->getlinks) *count = bn->getlinks(bn); return 0; } } debuglog("RFSV dir %s\n", inode->name); if ((ret = rfsv_dir(dirname(inode->name), &e))) return ret; while (e) { fattr fp; char *bp; dentry *o; int ni; bp = filname(e->name); ni = get_nam(build_path(inode->name, bp))->inode; free(e->name); if (!search_cache(attrcache, ni)) { pattr2attr(e->attr, e->size, e->time, &fp, ni); if (rfsv_isalive()) add_cache(&attrcache, ni, &fp); } o = e; e = e->next; free(o); (*count)++; } return 0; } struct attrstat * nfsproc_getattr_2(struct nfs_fh *fh) { static struct attrstat res; p_inode *inode = get_num(fh2inode(fh->data)); fattr *fp = &res.attrstat_u.attributes; struct cache *cp; long pattr; long psize; long ptime; long dcount; int builtin = 0; int l; debuglog("getattr:'%s',%d\n", inode->name, inode->inode); res.status = NFS_OK; if ((cp = search_cache(attrcache, inode->inode))) { debuglog("getattr: cache hit\n"); *fp = cp->attr; /* gotcha */ if (fp->nlink > 0) return &res; } l = strlen(inode->name); if (inode->inode == root_fattr.fileid) { /* It's the root inode */ debuglog("getattr:root inode (%#o)\n", root_fattr.mode); if (query_devices()) /* root inode and proc is always there */ root_fattr.nlink = 3; *fp = root_fattr; } else if (l == 2 && inode->name[1] == ':') { debuglog("getattr:device\n"); res.status = NO_PSION; if (!query_devices()) { device *dp; for (dp = devices; dp; dp = dp->next) { debuglog("cmp '%c', '%s'\n", dp->letter, inode->name); if (dp->letter == inode->name[0]) break; } debuglog("device: %s exists\n", (dp)?"":"not"); if (dp) { res.status = NFS_OK; *fp = root_fattr; /* If it's writeable... */ if (dp->attrib != 7) fp->mode |= 0200; fp->fileid = inode->inode; if (mp_dircount(inode, &dcount)) { res.status = rfsv_isalive() ? NFSERR_NOENT : NO_PSION; return &res; } if (fp->nlink != (dcount + 2)) fp->mtime.seconds = time(0); fp->nlink = dcount + 2; } } } else { builtin_node *bn; for (bn = builtins; bn; bn = bn->next) { if (!strcmp(inode->name, builtin_path(bn))) { pattr = bn->attr; if (pattr & PSI_A_DIR) psize = 0; else psize = (bn->getsize) ? bn->getsize(bn) : bn->size; ptime = time(0); res.status = NFS_OK; builtin = 1; if (bn->flags && BF_NOCACHE) builtin++; break; } } if (!builtin) { debuglog("getattr:fileordir\n"); /* It's a normal file/dir */ debuglog("RFSV getattr %s\n", inode->name); if (rfsv_getattr(inode->name, &pattr, &psize, &ptime)) { res.status = rfsv_isalive() ? NFSERR_NOENT : NO_PSION; return &res; } } pattr2attr(pattr, psize, ptime, fp, fh2inode((char *) fh->data)); if (fp->type == NFDIR) { if (mp_dircount(inode, &dcount)) { res.status = rfsv_isalive() ? NFSERR_NOENT : NO_PSION; return &res; } if (fp->nlink != (dcount + 2)) fp->mtime.seconds = time(0); fp->nlink = dcount + 2; } } if (rfsv_isalive() && builtin < 2) add_cache(&attrcache, inode->inode, fp); return &res; } struct diropres * nfsproc_lookup_2(diropargs *da) { static struct diropres res; struct attrstat *gres; p_inode *inode = get_num(fh2inode(da->dir.data)); char *fp = res.diropres_u.diropres.file.data; if (!inode) { debuglog("lookup: stale fh\n"); res.status = NO_PSION; return &res; } debuglog("lookup: in '%s'(%d) searching '%s'\n", inode->name, inode->inode, da->name); if (!strcmp(da->name, ".")) inode2fh(fh2inode(da->dir.data), fp); else if (!strcmp(da->name, "..")) inode2fh(getpinode(inode), fp); else inode2fh(get_nam(build_path(inode->name, da->name))->inode, fp); gres = nfsproc_getattr_2((struct nfs_fh *) fp); res.status = gres->status; res.diropres_u.diropres.attributes = gres->attrstat_u.attributes; return &res; } #define RA_MAXCOUNT ~0 static void addentry(readdirargs *ra, entry ***where, int *searchi, int inode, char *name) { int l, rndup; if (*searchi) { if (*searchi == inode) *searchi = 0; return; } if (ra->count == RA_MAXCOUNT) return; /* * From ddan@au.stratus.com Wed Feb 8 04:14 MET 1995 * Modifed in pl7.a by Dan Danz to fix problem of missing files in readdir * * In a shotgun attempt at fixing this, I surmised that perhaps addentry * was putting one too many entries in the result, so I increased * the number of bytes for each entry by +8 ... my reasoning was that * you need to account for the filename pointer and for the cookie int ... * but in retrospect, I'm not sure about this reasoning. HOWEVER, it appears * that this fixes the problem. * FIXED: See next comment (Rudi) */ /* * Count the bytes needed for the xdr encoded data. Xdr is trickier than * one (me :-) might think: Xdr converts a string into * length (4 bytes) + data (rounded up to 4 bytes). */ #define XDR_UNIT 4 l = strlen(name); if ((rndup = l % XDR_UNIT) > 0) l += XDR_UNIT - rndup; l += XDR_UNIT; /* Length of name */ l += sizeof(entry); if (l > ra->count) { ra->count = RA_MAXCOUNT; return; } ra->count -= l; **where = (entry *) malloc(sizeof(entry)); (**where)->fileid = inode; (**where)->name = (char *) strdup(name); *(int *) (**where)->cookie = inode; (**where)->nextentry = 0; *where = &(**where)->nextentry; } struct readdirres * nfsproc_readdir_2(readdirargs *ra) { static readdirres res; p_inode *inode = get_num(fh2inode(ra->dir.data)); entry *centry, *fentry, **cp; char *bp; int searchinode; if (!inode) { debuglog("readdir: stale fh\n"); res.status = NO_PSION; return &res; } cp = &res.readdirres_u.reply.entries; for (fentry = *cp; fentry; fentry = centry) { centry = fentry->nextentry; free(fentry->name); free(fentry); } *cp = 0; searchinode = *(int *) ra->cookie; debuglog("readdir: %s, cookie:%x, count:%d\n", inode->name, searchinode, ra->count); /* . & .. */ addentry(ra, &cp, &searchinode, inode->inode, "."); addentry(ra, &cp, &searchinode, getpinode(inode), ".."); if (inode->inode == root_fattr.fileid) { /* Root directory */ device *dp; addentry(ra, &cp, &searchinode, get_nam("/proc")->inode, "proc"); if (query_devices()) { res.readdirres_u.reply.eof = ra->count == RA_MAXCOUNT ? 0 : 1; res.status = NFS_OK; debuglog("readdir: eof=%d\n", res.readdirres_u.reply.eof); return &res; } for (dp = devices; dp; dp = dp->next) { char n[3]; sprintf(n, "%c:", dp->letter); addentry(ra, &cp, &searchinode, get_nam(dp->name)->inode, n); } } else { builtin_node *bn; int builtin = 0; dentry *e = NULL; debuglog("nfsdir: dir\n"); for (bn = builtins; bn ; bn = bn->next) if (!strcmp(inode->name, builtin_path(bn))) { if (bn->attr & PSI_A_DIR) { if (bn->getdents) bn->getdents(bn, &e); builtin = 1; } else { res.status = NFSERR_NOTDIR; return &res; } } if (!builtin) { debuglog("RFSV dir2 %s\n", inode->name); if (rfsv_dir(dirname(inode->name), &e)) { res.status = rfsv_isalive() ? NFSERR_NOENT : NO_PSION; return &res; } } while (e) { fattr fp; dentry *o; int ni; bp = filname(e->name); ni = get_nam(build_path(inode->name, bp))->inode; addentry(ra, &cp, &searchinode, ni, (char *) bp); free(e->name); pattr2attr(e->attr, e->size, e->time, &fp, ni); if (rfsv_isalive()) add_cache(&attrcache, ni, &fp); o = e; e = e->next; free(o); } } res.readdirres_u.reply.eof = ra->count == RA_MAXCOUNT ? 0 : 1; res.status = NFS_OK; debuglog("readdir: eof=%d\n", res.readdirres_u.reply.eof); return &res; } struct attrstat * nfsproc_setattr_2(sattrargs *sa) { static struct attrstat res; p_inode *inode = get_num(fh2inode(sa->file.data)); fattr *fp; builtin_node *bn; int builtin = 0; if (!inode) { debuglog("setattr: stale fh\n"); res.status = NO_PSION; return &res; } debuglog("setattr %s called\n", inode->name); res = *nfsproc_getattr_2(&sa->file); if (res.status != NFS_OK) return &res; fp = &res.attrstat_u.attributes; for (bn = builtins; bn; bn = bn->next) if (!strcmp(inode->name, builtin_path(bn))) { builtin = 1; break; } if ((fp->type == NFREG) && (sa->attributes.size != -1) && (sa->attributes.size != fp->size)) { debuglog("RFSV setsize %s %d\n", inode->name, sa->attributes.size); if (builtin) { if (bn->attr & PSI_A_RDONLY) { res.status = NFSERR_ACCES; return &res; } } else { if (rfsv_setsize(inode->name, sa->attributes.size) != 0) { // FIXME: more different error codes res.status = rfsv_isalive() ? NFSERR_ROFS : NO_PSION; return &res; } } fp->size = sa->attributes.size; rem_cache(&attrcache, inode->inode); if (rfsv_isalive()) add_cache(&attrcache, inode->inode, fp); } if ((sa->attributes.mtime.seconds != fp->mtime.seconds) && (sa->attributes.mtime.seconds != -1) && !builtin) { debuglog("RFSV setmtime %s %d\n", inode->name, sa->attributes.mtime.seconds); if (rfsv_setmtime(inode->name, sa->attributes.mtime.seconds)) { res.status = (rfsv_isalive()) ? NFSERR_ACCES : NO_PSION; return &res; } fp->mtime.seconds = fp->atime.seconds = sa->attributes.mtime.seconds; rem_cache(&attrcache, inode->inode); if (rfsv_isalive()) add_cache(&attrcache, inode->inode, fp); } if ((sa->attributes.mode != fp->mode) && (sa->attributes.mode != -1)) { long psisattr, psidattr; attr2pattr(sa->attributes.mode, fp->mode, &psisattr, &psidattr); debuglog("RFSV setattr %s %d %d\n", inode->name, psisattr, psidattr); if (builtin) { if ((bn->sattr == NULL) || bn->sattr(bn, psisattr, psidattr)) { res.status = NFSERR_ACCES; return &res; } } else { if (rfsv_setattr(inode->name, psisattr, psidattr)) { res.status = (rfsv_isalive()) ? NFSERR_ACCES : NO_PSION; return &res; } } fp->mode = sa->attributes.mode; rem_cache(&attrcache, inode->inode); if (rfsv_isalive() && !builtin) add_cache(&attrcache, inode->inode, fp); } res.status = NFS_OK; return &res; } static nfsstat * remove_it(diropargs *da, int isdir) { static nfsstat res; p_inode *inode = get_num(fh2inode(da->dir.data)); long rfsv_res; if (!inode) { debuglog("setattr: stale fh\n"); res = NO_PSION; return &res; } debuglog("remove_it: in %s: %s (%d)\n", inode->name, da->name, isdir); if (isdir) { debuglog("RFSV rmdir %s\n", build_path(inode->name, da->name)); rfsv_res = rfsv_rmdir(build_path(inode->name, da->name)); } else { debuglog("RFSV remove %s\n", build_path(inode->name, da->name)); rfsv_res = rfsv_remove(build_path(inode->name, da->name)); } if (rfsv_res != 0) { res = rfsv_isalive() ? NFSERR_ACCES : NO_PSION; return &res; } rem_cache(&attrcache, inode->inode); res = NFS_OK; return &res; } nfsstat * nfsproc_remove_2(diropargs *da) { return remove_it(da, 0); } nfsstat * nfsproc_rmdir_2(diropargs *da) { return remove_it(da, 1); } nfsstat * nfsproc_rename_2(renameargs *ra) { static nfsstat res; p_inode *from = get_num(fh2inode(ra->from.dir.data)); p_inode *to = get_num(fh2inode(ra->to.dir.data)); char ldata[300], *old, c; if (!from || !to) { debuglog("rename: stale fh\n"); res = NO_PSION; return &res; } strcpy(ldata + 1, build_path(to->name, ra->to.name)); *ldata = strlen(ldata + 1); c = *ldata + 1; old = build_path(from->name, ra->from.name); res = NFS_OK; debuglog("RFSV rename %s -> %s\n", old, ldata + 1); if (rfsv_rename(old, ldata + 1)) { res = (rfsv_isalive()) ? NFSERR_ACCES : NO_PSION; return &res; } if (res == NFS_OK) { /* Preserve inode */ strcpy((char *) ldata, build_path(to->name, ra->to.name)); (void) re_nam(build_path(from->name, ra->from.name), ldata); } rem_cache(&attrcache, from->inode); rem_cache(&attrcache, to->inode); return &res; } /* ARGSUSED */ struct statfsres * nfsproc_statfs_2(struct nfs_fh *fh) { static statfsres res; statfsokres *rp; device *dp; debuglog("statfs..\n"); rp = &res.statfsres_u.reply; rp->tsize = PBUFSIZE; rp->bsize = BLOCKSIZE; rp->blocks = rp->bfree = 0; res.status = NFS_OK; if (query_devices()) { /* Allow to mount it whithout the psion attached */ if (rfsv_isalive()) return &res; /* res.status = NO_PSION; Hmm */ } for (dp = devices; dp; dp = dp->next) { rp->blocks += (dp->total + BLOCKSIZE - 1) / BLOCKSIZE; rp->bfree += (dp->free + BLOCKSIZE - 1) / BLOCKSIZE; } rp->bavail = rp->bfree; return &res; } /* * Problem: * Since we are slow (Max 2Kbyte/s) and the biods are very impatient, * we receive each request (number of biods + 1 for the kernel itself) * times, this number can be lower for the last block. :-( * Solution: (not the best, probably) * Cache the read data. This cache will be invalidated if there are * no more requests in the queue for at least XXX seconds. * See also write :-( */ struct readres * nfsproc_read_2(struct readargs *ra) { static struct readres res; static unsigned char rop[NFS_MAXDATA]; p_inode *inode = get_num(fh2inode(ra->file.data)); fattr *fp; struct cache *cp; struct dcache *dcp; long pattr; long psize; long ptime; int len = 0; builtin_node *bn; int builtin = 0; if (!inode) { debuglog("read: stale fh\n"); res.status = NO_PSION; return &res; } debuglog("read: %s off:%d count:%d\n", inode->name, ra->offset, ra->count); cp = search_cache(attrcache, inode->inode); if (cp && (dcp = search_dcache(cp, ra->offset, ra->count))) { debuglog("read: dcache hit\n"); res.readres_u.reply.attributes = cp->attr; bcopy(dcp->data, res.readres_u.reply.data.data_val, ra->count); res.readres_u.reply.data.data_len = ra->count; res.status = NFS_OK; return &res; } debuglog("RFSV read %s\n", inode->name); for (bn = builtins; bn; bn = bn->next) { if (!strcmp(inode->name, builtin_path(bn))) { if (bn->attr & PSI_A_DIR) { res.status = NFSERR_ISDIR; return &res; } if (bn->read) { len = bn->read(bn, rop, ra->offset, ra->count); builtin = 1; break; } else { res.status = NFSERR_IO; return &res; } } } if (!builtin) { if (rfsv_read(rop, ra->offset, ra->count, inode->name) < 0) { res.status = rfsv_isalive() ? NFSERR_NOENT : NO_PSION; return &res; } } fp = &res.readres_u.reply.attributes; if (!cp) { // Problem: if an epoc process is enlarging the file, we wont recognize it debuglog("RFSV getattr %s\n", inode->name); if (builtin) { pattr = bn->attr; psize = (bn->getsize) ? bn->getsize(bn) : bn->size; ptime = time(0); } else rfsv_getattr(inode->name, &pattr, &psize, &ptime); pattr2attr(pattr, psize, ptime, fp, fh2inode((char *)ra->file.data)); cp = add_cache(&attrcache, inode->inode, fp); } else { *fp = cp->attr; } len = cp->actual_size - ra->offset; if (len > ra->count) len = ra->count; if (cp->actual_size < ra->offset) len = 0; if (debug > 1) debuglog("Read: filesize %d read %d @ %d\n", cp->actual_size, len, ra->offset); if (len) { dcp = add_dcache(cp, ra->offset, ra->count, rop); dcp->towrite = 0; /* don't write it back */ } if (builtin) rem_cache(&attrcache, inode->inode); res.readres_u.reply.data.data_len = len; res.readres_u.reply.data.data_val = (char *) rop; res.status = NFS_OK; return &res; } /* * Returns cachepointer on full hit, 0 on partial or no hit, * see below solaris comment */ static int addwritecache(struct cache *cp, int doff, int dlen, unsigned char *dp) { struct dcache *dcp; int len, changed, os, oe; unsigned char *pd, *pc; /* * do the cachesearch: we are interested in partial hits, as we don't * want to write anything twice (flash ram) */ for (dcp = cp->dcache; dcp; dcp = dcp->next) if (doff < dcp->offset + dcp->len && doff + dlen > dcp->offset) break; if (!dcp) { /* phew, nothing fancy to do */ add_dcache(cp, doff, dlen, dp); return 0; } os = doff > dcp->offset ? doff : dcp->offset; oe = doff + dlen < dcp->offset + dcp->len ? doff + dlen : dcp->offset + dcp->len; pd = dp + os - doff; pc = dcp->data + os - dcp->offset; len = oe - os; changed = 0; if (bcmp(pd, pc, len)) { bcopy(pd, pc, len); dcp->towrite = 1; changed = 1; } if (doff >= dcp->offset && doff + dlen <= dcp->offset + dcp->len) { debuglog("write: full cache hit\n"); return !changed; } debuglog("write: partial cache hit (off %d len %d)\n", dcp->offset, dcp->len); /* Do we have some data below the cached area... */ if (doff < dcp->offset) (void) addwritecache(cp, doff, dcp->offset - doff, dp); /* ...or some above? */ len = (doff + dlen) - (dcp->offset + dcp->len); if (len > 0) (void) addwritecache(cp, dcp->offset + dcp->len, len, dp + dlen - len); return 0; } /* * The same problem here as above: we receive numerous requests, * not even in a specified order. The only good thing is that each request * up to the last is exactly the same size. * A new problem: * Since I dunno how to seek to a point beyond the end of the file (psion), * I can't sopport the exact semantics of write. Such files will never be * written completely. :-( * * Another dumb solaris (sysv?) problem: if the client is writing 512 byte * blocks we receive following write requests: * off 0 len 512, off 0 len 1024, off 0 len 1536, ... so on till len 4096, * that means 4 times as much data as actually needed. * We should check if the block was partially written, and write only the * difference */ struct attrstat * nfsproc_write_2(writeargs *wa) { static struct attrstat res; p_inode *inode = get_num(fh2inode(wa->file.data)); struct cache *cp; struct dcache *dcp; fattr *fp; struct attrstat *gres; int len = 0; int dlen, doff; builtin_node *bn; if (!inode) { debuglog("write: stale fh\n"); res.status = NO_PSION; return &res; } debuglog("write:%s off:%d l:%d\n", inode->name, wa->offset, wa->data.data_len); dlen = wa->data.data_len; doff = wa->offset; for (bn = builtins; bn ; bn = bn->next) { if (!strcmp(inode->name, builtin_path(bn))) { if (bn->attr & PSI_A_DIR) res.status = NFSERR_ISDIR; else { debuglog("builtin write %s %d@%d\n", inode->name, dlen, doff); if (bn->write) { int l = bn->write(bn, (unsigned char *)wa->data.data_val, doff, dlen); res.status = (l == dlen) ? NFS_OK : NFSERR_IO; } else res.status = NFSERR_ACCES; } return &res; } } /* fetch attributes */ if ((cp = search_cache(attrcache, inode->inode)) == 0) { gres = nfsproc_getattr_2((struct nfs_fh *) wa->file.data); if (gres->status != NFS_OK) { res.status = gres->status; return &res; } cp = search_cache(attrcache, inode->inode); if (!cp) { errorlog("nfsproc_write_2: cache is NULL\n"); res.status = NFSERR_IO; return &res; } } fp = &cp->attr; if (fp->size < doff + dlen) fp->size = doff + dlen; fp->blocks = (fp->size + (BLOCKSIZE - 1)) / BLOCKSIZE; fp->atime.seconds = fp->mtime.seconds = fp->ctime.seconds = time(0); res.attrstat_u.attributes = *fp; if (addwritecache(cp, doff, dlen, (unsigned char *) wa->data.data_val)) { res.status = NFS_OK; return &res; } /* Write out as many blocks from the cache as we can */ for (;;) { if (debug > 2) for (dcp = cp->dcache; dcp; dcp = dcp->next) debuglog("Check: %d=%d,%d,%d>=%d\n", inode->inode, cp->inode, dcp->towrite, cp->actual_size, dcp->offset); for (dcp = cp->dcache; dcp; dcp = dcp->next) if (dcp->towrite && cp->actual_size >= dcp->offset) break; if (!dcp) /* Can't write any blocks */ break; debuglog("writing off: %d, len: %d, act: %d\n", dcp->offset, dcp->len, cp->actual_size); debuglog("RFSV write %s\n", inode->name); if (rfsv_write(dcp->data, dcp->offset, dcp->len, inode->name) != dcp->len) { debuglog("write: dump failed\n"); res.status = rfsv_isalive() ? NFSERR_NOSPC : NO_PSION; return &res; } dcp->towrite = 0; len = dcp->offset + dcp->len; if (len > cp->actual_size) cp->actual_size = len; debuglog("written: new length: %d\n", cp->actual_size); } debuglog("write -> ISOK (%d, %d %d)\n", res.attrstat_u.attributes.size, res.attrstat_u.attributes.fileid, res.attrstat_u.attributes.fsid); res.status = NFS_OK; return &res; } /* Dummy routines */ void * nfsproc_writecache_2() { static char res; debuglog("writecache???\n"); res = (char) NFSERR_FBIG; return (void *) &res; } void * nfsproc_null_2() { static char res; debuglog("null.\n"); res = (char) NFSERR_FBIG; return (void *) &res; } void * nfsproc_root_2() { static char res; debuglog("root????\n"); res = (char) NFSERR_FBIG; return (void *) &res; } /* * Links and symlinks are not supported on Psion */ /* ARGSUSED */ nfsstat * nfsproc_link_2(linkargs *la) { static nfsstat res; debuglog("link..\n"); res = NFSERR_ACCES; return &res; } /* ARGSUSED */ struct readlinkres * nfsproc_readlink_2(struct nfs_fh *fh) { static readlinkres res; debuglog("readlink...\n"); res.status = NFSERR_ACCES; return &res; } /* ARGSUSED */ nfsstat * nfsproc_symlink_2(symlinkargs *sa) { static nfsstat res; debuglog("symlink..\n"); res = NFSERR_ACCES; return &res; }