/* $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 #if defined(__SVR4) || defined(__GLIBC__) #include #include #include #endif #include "nfs_prot.h" #include "mp.h" #include "rfsv_api.h" static device *devices; struct cache *attrcache; /* * 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; long 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 dpattr2attr(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 void pattr2attr(long psiattr, long size, long ftime, fattr *fp, unsigned char *fh) { 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 = fh2inode((char *) fh); fp->rdev = fp->fsid = FID; fp->atime.seconds = ftime; fp->mtime.seconds = fp->ctime.seconds = fp->atime.seconds; } static int query_devices() { device *dp, *np; int link_count = 2; /* set the root link count */ if (query_cache) return 0; for (dp = devices; dp; dp = np) { np = dp->next; free(dp->name); free(dp); } devices = 0; if (rfsv_drivelist(&link_count, &devices)) return 1; query_cache = 1; root_fattr.nlink = link_count; return 0; } static int mp_dircount(p_inode *inode, long *count) { dentry *e = NULL; long ret; *count = 0; debuglog("dircount: dir\n"); 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)) { dpattr2attr(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 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->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; cp->attr = *fp; } 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 is always there */ root_fattr.nlink = 2; *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 { debuglog("getattr:fileordir\n"); /* It's a normal file/dir */ if (rfsv_getattr(inode->name, &pattr, &psize, &ptime)) { res.status = rfsv_isalive() ? NFSERR_NOENT : NO_PSION; return &res; } pattr2attr(pattr, psize, ptime, fp, (unsigned 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()) 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 (inode->inode == root_fattr.fileid && !strcmp(da->name, "exit")) { exiting = 5; /* Lets try it 5 times (10 sec) */ res.status = NFSERR_EXIST; return &res; } if (inode->inode == root_fattr.fileid && !strcmp(da->name, "debug")) { debug = (debug + 1) & 3; /* debug level of 0,1,2 & 3 */ debuglog("Set debug level to %d\n", debug); res.status = NFSERR_EXIST; return &res; } 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; if (query_devices()) { res.status = NO_PSION; 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 { dentry *e = NULL; debuglog("nfsdir: dir\n"); 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); dpattr2attr(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; return &res; } struct attrstat * nfsproc_setattr_2(sattrargs *sa) { static struct attrstat res; p_inode *inode = get_num(fh2inode(sa->file.data)); fattr *fp; 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; if ((fp->type == NFREG) && (sa->attributes.size != -1) && (sa->attributes.size != fp->size)) { debuglog("setattr truncating to %d bytes\n", sa->attributes.size); if (rfsv_setsize(inode->name, sa->attributes.size) != 0) { 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)) { 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); 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()) 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) rfsv_res = rfsv_rmdir(build_path(inode->name, da->name)); else 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); debuglog("Rename: %s -> %s\n", old, ldata + 1); res = NFS_OK; 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; 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; } 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 { rfsv_getattr(inode->name, &pattr, &psize, &ptime); pattr2attr(pattr, psize, ptime, fp, (unsigned 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); res.readres_u.reply.data.data_len = len; res.readres_u.reply.data.data_val = (char *) rop; if (len) { dcp = add_dcache(cp, ra->offset, ra->count, rop); dcp->towrite = 0; /* don't write it back */ } 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, dlen, doff; 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; /* 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) abort(); } 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); 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; }