/* * * Copyright (C) 2000 Frans Kaashoek (kaashoek@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 "sfsusrv.h" #include "filesrv.h" #include "rxx.h" #include #include #include #include #include #include #define NFS_MAXDATA 8192 static char iobuf[NFS_MAXDATA]; struct { nfsstat3 error; int errnum; } nfs_errtbl[]= { { NFS3ERR_PERM, EPERM }, { NFS3ERR_NOENT, ENOENT }, { NFS3ERR_IO, EIO }, { NFS3ERR_NXIO, ENXIO }, { NFS3ERR_ACCES, EACCES }, { NFS3ERR_EXIST, EEXIST }, { NFS3ERR_NODEV, ENODEV }, { NFS3ERR_NOTDIR, ENOTDIR }, { NFS3ERR_ISDIR, EISDIR }, { NFS3ERR_INVAL, EINVAL }, { NFS3ERR_FBIG, EFBIG }, { NFS3ERR_NOSPC, ENOSPC }, { NFS3ERR_ROFS, EROFS }, { NFS3ERR_NAMETOOLONG, ENAMETOOLONG }, { NFS3ERR_NOTEMPTY, ENOTEMPTY }, { NFS3ERR_STALE, ESTALE }, { NFS3ERR_SERVERFAULT, EMFILE }, // NFS3ERR_JUKEBOX XXXX { NFS3ERR_SERVERFAULT, ENFILE }, // NFS3ERR_JUKEBOX XXXX { NFS3_OK, EIO } }; static nfsstat3 nfs_errno (void) { for (int i = 0; nfs_errtbl[i].error != NFS3_OK; i++) { if (nfs_errtbl[i].errnum == errno) { return (nfs_errtbl[i].error); } } warnx << NFS3ERR_IO << " (don't know) \n"; return (NFS3ERR_IO); } void trans_attr (ex_fattr3 *fa, struct stat *sb) { ftype3 t; if (S_ISREG (sb->st_mode)) t = NF3REG; else if (S_ISDIR (sb->st_mode)) t = NF3DIR; else if (S_ISLNK (sb->st_mode)) t = NF3LNK; else { t = ftype3 (0); warn << "Non-supported file type " << sb->st_mode << "\n"; } fa->type = t; fa->mode = sb->st_mode & 0xFFF; fa->nlink = sb->st_nlink; fa->uid = sb->st_uid; fa->gid = sb->st_gid; fa->size = sb->st_size; fa->used = sb->st_blocks * 512; fa->rdev.major = 0; fa->rdev.minor = 0; fa->fsid = 0; fa->fileid = sb->st_ino; // fa->fileid = (fa->fileid << 32) | sb->st_dev; fa->mtime.seconds = sb->st_mtime; fa->mtime.nseconds = 0; fa->ctime.seconds = sb->st_ctime; fa->ctime.nseconds = 0; fa->atime.seconds = sb->st_atime; fa->atime.nseconds = 0; fa->expire = 0; // NFS semantics for for now. } client::client (ref x) : sfsserv (x) { rwsrv = asrv::alloc (x, ex_nfs_program_3, wrap (this, &client::nfs3dispatch)); // nfscbc = aclnt::alloc (x, ex_nfscb_program_3); authid_valid = false; } bool client::dirlookup (str dir, filename3 *name) { DIR *dirp; struct dirent *de = NULL; if ((dirp = opendir (dir)) == 0) { warn << dir << " is not a directory\n"; return 0; } while ((de = readdir (dirp))) { str n (de->d_name); if (n == *name) { closedir (dirp); return 1; } } closedir (dirp); errno = ENOENT; return 0; } void client::nfs3_getattr (svccb *sbp) { nfs_fh3 *fh = sbp->template getarg (); fh_entry *fhe = fsrv->lookup (fh); if (!fhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } ex_getattr3res nfsres (NFS3_OK); *nfsres.attributes = fhe->fa; sbp->reply (&nfsres); } void client::nfs3_fsinfo (svccb *sbp) { ex_fsinfo3res res (NFS3_OK); res.resok->rtmax = 8192; res.resok->rtpref = 8192; res.resok->rtmult = 512; res.resok->wtmax = 8192; res.resok->wtpref = 8192; res.resok->wtmult = 8192; res.resok->dtpref = 8192; res.resok->maxfilesize = INT64 (0x7fffffffffffffff); res.resok->time_delta.seconds = 0; res.resok->time_delta.nseconds = 1; res.resok->properties = (FSF3_LINK | FSF3_SYMLINK | FSF3_HOMOGENEOUS); sbp->reply (&res); } void client::nfs3_fsstat (svccb *sbp) { ex_fsstat3res res (NFS3_OK); rpc_clear (res); sbp->reply (&res); } uint32 client::access_check(ex_fattr3 *fa, uint32 access_req) { // XXX implement this. return access_req; } void client::nfs3_access (svccb *sbp) { access3args *aa = sbp->template getarg (); uint32 access_req = aa->access; ex_access3res nfsres (NFS3_OK); fh_entry *fhe = fsrv->lookup (&aa->object); if (!fhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } nfsres.resok->access = access_check (&fhe->fa, access_req); nfsres.resok->obj_attributes.set_present (true); *nfsres.resok->obj_attributes.attributes = fhe->fa; sbp->reply (&nfsres); } void client::nfs3_lookup (svccb *sbp) { diropargs3 *dirop = sbp->template getarg (); fh_entry *dhe = fsrv->lookup (&dirop->dir); if (!dhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } if (!fsrv->checkfd ()) { // dirlookup needs a fd nfs3exp_err (sbp, NFS3ERR_STALE); return; } if (dirlookup (dhe->path, &(dirop->name))) { str p = dhe->path << "/" << dirop->name; fh_entry *fhe = fsrv->lookup_add (p); if (!fhe) { nfs3exp_err (sbp, nfs_errno()); return; } ex_lookup3res nfsres (NFS3_OK); nfsres.resok->object = fhe->fh; nfsres.resok->obj_attributes.set_present (true); *nfsres.resok->obj_attributes.attributes = fhe->fa; sbp->reply (&nfsres); } else { nfs3exp_err (sbp, nfs_errno()); } } void client::nfs3_readdir (svccb *sbp) { readdir3args *readdirop = sbp->template getarg (); fh_entry *dhe = fsrv->lookup (&readdirop->dir); if (!dhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } if (!fsrv->checkfd ()) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } DIR *dirp; if ((dirp = opendir (dhe->path)) == 0) { warn << dhe->path << " is not a directory\n"; nfs3exp_err (sbp, nfs_errno ()); return; } struct dirent *de; uint64 i = 1; if(readdirop->cookie != 0){ // The cookie is the index of the last file previously returned. // We use 1-origin cookies. // So skip over cookie files. for(i = 1; i <= readdirop->cookie; i++) readdir(dirp); } ex_readdir3res nfsres (NFS3_OK); nfsres.resok->reply.eof = true; rpc_ptr < entry3 > *direntp = &nfsres.resok->reply.entries; // Set up to approximate stopping after readdirop->count bytes. unsigned size_guess = 200; // XXX guess at size of fixed part of reply. while ((de = readdir (dirp))) { str n (de->d_name); str p = dhe->path << "/" << n; fh_entry *fhe = fsrv->lookup_add (p); if (fhe) { size_guess += n.len() + 32; // XXX if(size_guess > readdirop->count){ nfsres.resok->reply.eof = false; break; } (*direntp).alloc (); (*direntp)->name = n; (*direntp)->fileid = fhe->fa.fileid; (*direntp)->cookie = i++; direntp = &(*direntp)->nextentry; } else { (void) closedir (dirp); nfs3exp_err (sbp, nfs_errno()); return; } } (void) closedir (dirp); sbp->reply (&nfsres); } void client::nfs3_read (svccb *sbp) { read3args *ra = sbp->template getarg (); fh_entry *fhe = fsrv->lookup (&ra->file); ex_read3res nfsres (NFS3_OK); int len; if (!fhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } int fd = fsrv->getfd (fhe, O_RDONLY); if (fd < 0) { nfs3exp_err (sbp, nfs_errno ()); return; } len = -1; if (lseek (fd, (long) ra->offset, L_SET) >= 0) { if ((len = ra->count) > NFS_MAXDATA) len = NFS_MAXDATA; len = read(fd, iobuf, len); } if (len < 0) { nfs3exp_err (sbp, nfs_errno ()); } else { nfsres.resok->count = len; nfsres.resok->data.setsize (len); memcpy (nfsres.resok->data.base (), iobuf, len); if (ra->offset + ra->count >= fhe->fa.size) nfsres.resok->eof = true; else nfsres.resok->eof = false; sbp->reply (&nfsres); } } void client::nfs3_create (svccb *sbp) { create3args *ca = sbp->template getarg (); fh_entry *dhe = fsrv->lookup (&ca->where.dir); fh_entry *fhe; uint32 m = 0; if (!dhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } if (!fsrv->checkfhe ()) { nfs3exp_err (sbp, nfs_errno ()); return; } switch (ca->how.mode) { case UNCHECKED: case GUARDED: if (ca->how.obj_attributes->mode.set) m = *ca->how.obj_attributes->mode.val; break; case EXCLUSIVE: break; } str n = dhe->path << "/" << ca->where.name; int fd = fsrv->getfd (n, O_CREAT | O_TRUNC | O_RDWR, m); if (fd >= 0) { fhe = fsrv->lookup_add (n); dhe->update_attr (dhe->path); fhe->setfd (fd); ex_diropres3 nfsres (NFS3_OK); nfsres.resok->obj.set_present (true); *nfsres.resok->obj.handle = fhe->fh; sbp->reply (&nfsres); } else { nfs3exp_err (sbp, nfs_errno ()); } } void client::nfs3_write (svccb *sbp) { write3args *wa = sbp->template getarg (); fh_entry *fhe = fsrv->lookup (&wa->file); int len; if (!fhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } int fd = fsrv->getfd (fhe, O_WRONLY); if (fd < 0) { nfs3exp_err (sbp, nfs_errno ()); return; } len = -1; if (lseek (fd, (long) wa->offset, L_SET) >= 0) { len = write(fd, wa->data.base (), wa->count); } if (len < 0) { nfs3exp_err (sbp, nfs_errno ()); } else { if (wa->stable == FILE_SYNC) { if (fsync (fd) != 0) warnx << "fsync failed; weird\n"; } fhe->update_attr (fd); ex_write3res nfsres (NFS3_OK); nfsres.resok->count = len; nfsres.resok->committed = wa->stable; sbp->reply (&nfsres); } } void client::nfs3_commit (svccb *sbp) { commit3args *ca = sbp->template getarg (); fh_entry *fhe = fsrv->lookup (&ca->file); if (!fhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } int fd = fsrv->getfd (fhe, O_WRONLY); if (fd < 0) { nfs3exp_err (sbp, nfs_errno ()); return; } if (fsync (fd) == 0) { ex_commit3res nfsres (NFS3_OK); sbp->reply (&nfsres); } else { warnx << "fsync failed; weird\n"; nfs3exp_err (sbp, nfs_errno ()); } } void client::nfs3_remove (svccb *sbp) { diropargs3 *dirop = sbp->template getarg (); fh_entry *dhe = fsrv->lookup (&dirop->dir); str n = dhe->path << "/" << dirop->name; if (!fsrv->checkfhe ()) { nfs3exp_err (sbp, nfs_errno()); return; } fh_entry *fhe = fsrv->lookup_add (n); if (!dhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } if (!fhe) { nfs3exp_err (sbp, nfs_errno()); return; } if (unlink (n) == 0) { fsrv->remove (fhe); ex_wccstat3 nfsres (NFS3_OK); sbp->reply (&nfsres); } else { nfs3exp_err (sbp, nfs_errno ()); } } void client::nfs3_rmdir (svccb *sbp) { diropargs3 *dirop = sbp->template getarg (); fh_entry *dhe = fsrv->lookup (&dirop->dir); if (!fsrv->checkfhe ()) { nfs3exp_err (sbp, nfs_errno()); return; } if (!dhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } str n = dhe->path << "/" << dirop->name; fh_entry *fhe = fsrv->lookup_add (n); if (!fhe) { nfs3exp_err (sbp, nfs_errno()); return; } if (rmdir (n) == 0) { dhe->update_attr (dhe->path); // update link count of parent fsrv->remove (fhe); ex_wccstat3 nfsres (NFS3_OK); sbp->reply (&nfsres); } else { warnx << "rmdir: " << n << " " << errno << "\n"; nfs3exp_err (sbp, nfs_errno ()); } } void client::nfs3_rename (svccb *sbp) { rename3args *rn = sbp->template getarg (); fh_entry *fhefrom = fsrv->lookup (&rn->from.dir); fh_entry *fheto = fsrv->lookup (&rn->to.dir); str from = fhefrom->path << "/" << rn->from.name; str to = fheto->path << "/" << rn->to.name; if (!fsrv->checkfhe ()) { nfs3exp_err (sbp, nfs_errno()); return; } fh_entry *fh = fsrv->lookup_add (from); if (!fhefrom || !fheto) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } if (!fh) { nfs3exp_err (sbp, nfs_errno()); return; } if (rename (from, to) == 0) { fhefrom->update_attr (fhefrom->path); fheto->update_attr (fheto->path); fh->path = to; fh->update_attr (fh->path); ex_rename3res nfsres (NFS3_OK); sbp->reply (&nfsres); } else { nfs3exp_err (sbp, nfs_errno ()); } } void client::nfs3_link (svccb *sbp) { link3args *lk = sbp->template getarg (); fh_entry *fhe = fsrv->lookup (&lk->file); fh_entry *dhe = fsrv->lookup (&lk->link.dir); if (!fhe || !dhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } if (!fsrv->checkfhe ()) { nfs3exp_err (sbp, nfs_errno()); return; } str n = dhe->path << "/" << lk->link.name; if (link (fhe->path, n) == 0) { fsrv->lookup_add (n); // XXX we have same fh; this is a problem fhe->update_attr (n); ex_link3res nfsres (NFS3_OK); sbp->reply (&nfsres); } else { nfs3exp_err (sbp, nfs_errno ()); } } void client::nfs3_mkdir (svccb *sbp) { mkdir3args *mk = sbp->template getarg (); fh_entry *dhe = fsrv->lookup (&mk->where.dir); fh_entry *fhe; uint32 m = 0; if (!dhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } if (!fsrv->checkfhe ()) { nfs3exp_err (sbp, nfs_errno()); return; } if (mk->attributes.mode.set) m = *mk->attributes.mode.val; str n = dhe->path << "/" << mk->where.name; if (mkdir (n, m) == 0) { ex_diropres3 nfsres (NFS3_OK); dhe->update_attr (dhe->path); // update link count of parent fhe = fsrv->lookup_add (n); nfsres.resok->obj.set_present (true); *nfsres.resok->obj.handle = fhe->fh; sbp->reply (&nfsres); } else { nfs3exp_err (sbp, nfs_errno ()); } } void client::nfs3_symlink (svccb *sbp) { symlink3args *sl = sbp->template getarg (); fh_entry *dhe = fsrv->lookup (&sl->where.dir); fh_entry *fhe; if (!dhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } if (!fsrv->checkfhe ()) { nfs3exp_err (sbp, nfs_errno()); return; } str p = dhe->path << "/" << sl->where.name; // the name of the file to create if (symlink (sl->symlink.symlink_data, p) == 0) { ex_diropres3 nfsres (NFS3_OK); fhe = fsrv->lookup_add (p); nfsres.resok->obj.set_present (true); *nfsres.resok->obj.handle = fhe->fh; sbp->reply (&nfsres); } else { nfs3exp_err (sbp, nfs_errno ()); } } void client::nfs3_readlink (svccb *sbp) { nfs_fh3 *fh = sbp->template getarg (); fh_entry *fhe = fsrv->lookup (fh); char dest [PATH_MAX + 1]; str p; if (!fhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } bzero (dest, PATH_MAX + 1); if (readlink (fhe->path, dest, PATH_MAX + 1) != -1) { str p (dest); ex_readlink3res nfsres (NFS3_OK); nfsres.resok->data = p; sbp->reply (&nfsres); } else { nfs3exp_err (sbp, nfs_errno ()); } } void client:: nfs3_setattr (svccb *sbp) { setattr3args *sa = sbp->template getarg (); fh_entry *fhe = fsrv->lookup (&sa->object); if (!fhe) { nfs3exp_err (sbp, NFS3ERR_STALE); return; } if (sa->new_attributes.mode.set) { uint32 m = *sa->new_attributes.mode.val; if (chmod (fhe->path, m) == 0) { fhe->update_attr (fhe->path); ex_wccstat3 nfsres (NFS3_OK); sbp->reply (&nfsres); } else { nfs3exp_err (sbp, nfs_errno ()); } } else if (sa->new_attributes.size.set) { uint64 s = *sa->new_attributes.size.val; if (truncate (fhe->path, s) == 0) { fhe->update_attr (fhe->path); ex_wccstat3 nfsres (NFS3_OK); sbp->reply (&nfsres); } else { nfs3exp_err (sbp, nfs_errno ()); } } else if (sa->new_attributes.atime.set || sa->new_attributes.mtime.set) { nfstime3 t; struct timeval tim[2]; switch (sa->new_attributes.atime.set) { case SET_TO_CLIENT_TIME: t = *sa->new_attributes.atime.time; tim[0].tv_sec = t.seconds; tim[0].tv_usec = t.nseconds; break; case SET_TO_SERVER_TIME: gettimeofday(&tim[0], NULL); break; default: t = fhe->fa.atime; tim[0].tv_sec = t.seconds; tim[0].tv_usec = t.nseconds; } switch (sa->new_attributes.mtime.set) { case SET_TO_CLIENT_TIME: t = *sa->new_attributes.mtime.time; tim[1].tv_sec = t.seconds; tim[1].tv_usec = t.nseconds; break; case SET_TO_SERVER_TIME: gettimeofday(&tim[1], NULL); break; default: t = fhe->fa.mtime; tim[1].tv_sec = t.seconds; tim[1].tv_usec = t.nseconds; } if (utimes (fhe->path, tim) == 0) { fhe->update_attr (fhe->path); ex_wccstat3 nfsres (NFS3_OK); sbp->reply (&nfsres); } else { nfs3exp_err (sbp, nfs_errno ()); } } else { nfs3exp_err (sbp, NFS3ERR_NOTSUPP); } } void client::nfs3dispatch (svccb *sbp) { if (!sbp) { delete this; return; } if (!rwsrv) { nfs3exp_err (sbp, NFS3ERR_BADHANDLE); return; } switch (sbp->proc()) { case NFSPROC3_NULL: sbp->reply (NULL); break; case NFSPROC3_GETATTR: nfs3_getattr (sbp); break; case NFSPROC3_FSINFO: nfs3_fsinfo (sbp); break; case NFSPROC3_FSSTAT: nfs3_fsstat (sbp); break; case NFSPROC3_ACCESS: nfs3_access (sbp); break; case NFSPROC3_LOOKUP: nfs3_lookup (sbp); break; case NFSPROC3_READDIR: nfs3_readdir (sbp); break; case NFSPROC3_READ: nfs3_read (sbp); break; case NFSPROC3_CREATE: nfs3_create (sbp); break; case NFSPROC3_WRITE: nfs3_write (sbp); break; case NFSPROC3_COMMIT: nfs3_commit (sbp); break; case NFSPROC3_REMOVE: nfs3_remove (sbp); break; case NFSPROC3_RMDIR: nfs3_rmdir (sbp); break; case NFSPROC3_RENAME: nfs3_rename (sbp); break; case NFSPROC3_LINK: nfs3_link (sbp); break; case NFSPROC3_MKDIR: nfs3_mkdir (sbp); break; case NFSPROC3_SYMLINK: nfs3_symlink (sbp); break; case NFSPROC3_READLINK: nfs3_readlink (sbp); break; case NFSPROC3_SETATTR: nfs3_setattr (sbp); break; default: nfs3exp_err (sbp, NFS3ERR_NOTSUPP); } } void client::sfs_getfsinfo (svccb *sbp) { sfs_fsinfo fsinfo; fsinfo.set_prog (ex_NFS_PROGRAM); fsinfo.nfs->set_vers (ex_NFS_V3); fsinfo.nfs->v3->root = fsrv->root->fh; if (rwsrv) { sbp->replyref (fsinfo); } else sbp->reject (PROC_UNAVAIL); } ptr client::doconnect (const sfs_connectarg *ci, sfs_servinfo *si) { fsrv = defsrv; *si = fsrv->servinfo; return fsrv->sk; }