// Copyright (c) Vitaliy Filippov, 2019+ // License: VNPL-1.1 (see README.md for details) // // NFS connection handler for NFS proxy #include #include "libnfs-raw-mount.h" #include "libnfs-raw-nfs.h" #include "base64.h" #include "nfs_proxy.h" static unsigned len_pad4(unsigned len) { return len + (len&3 ? 4-(len&3) : 0); } static int nfs3_null_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { rpc_send_reply(rpc, call, NULL, (zdrproc_t)zdr_void, 0); return 0; } static fattr3 get_dir_attributes(nfs_client_t *self, std::string dir) { return (fattr3){ .type = NF3DIR, .mode = 0755, .nlink = 1, .uid = 0, .gid = 0, .size = 4096, .used = 4096, .rdev = (specdata3){ 0 }, .fsid = self->parent->fsid, .fileid = dir == "" ? 1 : self->parent->dir_ids.at(dir), //.atime = (nfstime3){ .seconds = now.tv_sec, .nseconds = now.tv_nsec }, //.mtime = (nfstime3){ .seconds = now.tv_sec, .nseconds = now.tv_nsec }, //.ctime = (nfstime3){ .seconds = now.tv_sec, .nseconds = now.tv_nsec }, }; } static int nfs3_getattr_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; GETATTR3args *args = (GETATTR3args*)call->body.cbody.args; GETATTR3res reply; std::string dirhash = std::string(args->object.data.data_val, args->object.data.data_len); bool is_dir = false; std::string dir; if (dirhash == "roothandle") is_dir = true; else { auto dir_it = self->parent->dir_by_hash.find(dirhash); if (dir_it != self->parent->dir_by_hash.end()) { is_dir = true; dir = dir_it->second; } } if (is_dir) { // Directory info reply.status = NFS3_OK; reply.GETATTR3res_u.resok.obj_attributes = get_dir_attributes(self, dir); } else { uint64_t inode_num; auto inode_num_it = self->parent->inode_by_hash.find(dirhash); if (inode_num_it != self->parent->inode_by_hash.end()) inode_num = inode_num_it->second; auto inode_it = self->parent->cli->st_cli.inode_config.find(inode_num); if (inode_it != self->parent->cli->st_cli.inode_config.end()) { // File info auto & inode_cfg = inode_it->second; reply.status = NFS3_OK; reply.GETATTR3res_u.resok.obj_attributes = { .type = NF3REG, .mode = 0644, .nlink = 1, .uid = 0, .gid = 0, .size = inode_cfg.size, .used = inode_cfg.size, .rdev = (specdata3){ 0 }, .fsid = self->parent->fsid, .fileid = inode_it->first, //.atime = (nfstime3){ .seconds = now.tv_sec, .nseconds = now.tv_nsec }, //.mtime = (nfstime3){ .seconds = now.tv_sec, .nseconds = now.tv_nsec }, //.ctime = (nfstime3){ .seconds = now.tv_sec, .nseconds = now.tv_nsec }, }; } else { // File not exists reply.status = NFS3ERR_NOENT; } } rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_GETATTR3res, sizeof(GETATTR3res)); return 0; } static int nfs3_setattr_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; SETATTR3args *args = (SETATTR3args*)call->body.cbody.args; SETATTR3res reply; // Not supported yet reply.status = NFS3ERR_NOTSUPP; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_SETATTR3res, sizeof(SETATTR3res)); return 0; } static int nfs3_lookup_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; LOOKUP3args *args = (LOOKUP3args*)call->body.cbody.args; LOOKUP3res reply; std::string dirhash = std::string(args->what.dir.data.data_val, args->what.dir.data.data_len); std::string dir; if (dirhash != "roothandle") { auto dir_it = self->parent->dir_by_hash.find(dirhash); if (dir_it != self->parent->dir_by_hash.end()) dir = dir_it->second; } std::string full_name = self->parent->name_prefix; if (dir != "") { full_name += dir+"/"; } full_name += std::string(args->what.name); for (auto & ic: self->parent->cli->st_cli.inode_config) { if (ic.second.name == full_name) { std::string fh = "S"+base64_encode(sha256(full_name.substr(self->parent->name_prefix.size()))); reply.status = NFS3_OK; reply.LOOKUP3res_u.resok.object.data.data_len = fh.size(); reply.LOOKUP3res_u.resok.object.data.data_val = (char*)fh.c_str(); rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_LOOKUP3res, sizeof(LOOKUP3res)); return 0; } } reply.status = NFS3ERR_NOENT; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_LOOKUP3res, sizeof(LOOKUP3res)); return 0; } static int nfs3_access_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; ACCESS3args *args = (ACCESS3args*)call->body.cbody.args; ACCESS3res reply = { .status = NFS3_OK, .ACCESS3res_u = { .resok = { .access = args->access, } }, }; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_ACCESS3res, sizeof(ACCESS3res)); return 0; } static int nfs3_readlink_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; READLINK3args *args = (READLINK3args*)call->body.cbody.args; READLINK3res reply = {}; // Not supported yet reply.status = NFS3ERR_NOTSUPP; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_READLINK3res, sizeof(READLINK3res)); return 0; } #define MAX_REQUEST_SIZE 128*1024*1024 static int nfs3_read_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; READ3args *args = (READ3args*)call->body.cbody.args; std::string handle = std::string(args->file.data.data_val, args->file.data.data_len); auto ino_it = self->parent->inode_by_hash.find(handle); if (ino_it == self->parent->inode_by_hash.end()) { READ3res reply = { .status = NFS3ERR_NOENT }; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_READ3res, sizeof(READ3res)); return 0; } if (args->count > MAX_REQUEST_SIZE) { READ3res reply = { .status = NFS3ERR_INVAL }; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_READ3res, sizeof(READ3res)); return 0; } void *buf = malloc_or_die(args->count); cluster_op_t *op = new cluster_op_t; op->opcode = OSD_OP_READ; op->inode = ino_it->second; op->offset = args->offset; op->len = args->count; op->iov.push_back(buf, args->count); op->callback = [rpc, call](cluster_op_t *op) { void *buf = op->iov.buf[0].iov_base; READ3res reply = {}; if (op->retval != op->len) { if (op->retval == -EINVAL) reply.status = NFS3ERR_INVAL; else if (op->retval == -ENOSPC) reply.status = NFS3ERR_NOSPC; else reply.status = NFS3ERR_IO; } else { reply.status = NFS3_OK; auto & reply_ok = reply.READ3res_u.resok; reply_ok.count = op->retval; reply_ok.eof = FALSE; reply_ok.data.data_len = reply_ok.count; reply_ok.data.data_val = (char*)buf; } rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_READ3res, sizeof(READ3res)); delete op; free(buf); }; self->parent->cli->execute(op); return 0; } static int nfs3_write_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; WRITE3args *args = (WRITE3args*)call->body.cbody.args; WRITE3res reply; // Not supported yet reply.status = NFS3ERR_NOTSUPP; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_WRITE3res, sizeof(WRITE3res)); return 0; } static int nfs3_create_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; CREATE3args *args = (CREATE3args*)call->body.cbody.args; CREATE3res reply; // Not supported yet reply.status = NFS3ERR_NOTSUPP; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_CREATE3res, sizeof(CREATE3res)); return 0; } static int nfs3_mkdir_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; MKDIR3args *args = (MKDIR3args*)call->body.cbody.args; MKDIR3res reply; // Not supported yet reply.status = NFS3ERR_NOTSUPP; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_MKDIR3res, sizeof(MKDIR3res)); return 0; } static int nfs3_symlink_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; SYMLINK3args *args = (SYMLINK3args*)call->body.cbody.args; SYMLINK3res reply; // Not supported yet reply.status = NFS3ERR_NOTSUPP; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_SYMLINK3res, sizeof(SYMLINK3res)); return 0; } static int nfs3_mknod_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; MKNOD3args *args = (MKNOD3args*)call->body.cbody.args; MKNOD3res reply; // Not supported yet reply.status = NFS3ERR_NOTSUPP; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_MKNOD3res, sizeof(MKNOD3res)); return 0; } static int nfs3_remove_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { REMOVE3args *args = (REMOVE3args*)call->body.cbody.args; REMOVE3res reply; // Not supported yet reply.status = NFS3ERR_NOTSUPP; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_REMOVE3res, sizeof(REMOVE3res)); return 0; } static int nfs3_rmdir_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; RMDIR3args *args = (RMDIR3args*)call->body.cbody.args; RMDIR3res reply; // Not supported yet reply.status = NFS3ERR_NOTSUPP; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_RMDIR3res, sizeof(RMDIR3res)); return 0; } static int nfs3_rename_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; RENAME3args *args = (RENAME3args*)call->body.cbody.args; RENAME3res reply; // Not supported yet reply.status = NFS3ERR_NOTSUPP; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_RENAME3res, sizeof(RENAME3res)); return 0; } static int nfs3_link_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; LINK3args *args = (LINK3args*)call->body.cbody.args; // We don't support hard links LINK3res reply = { NFS3ERR_NOTSUPP }; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_LINK3res, sizeof(LINK3res)); return 0; } static void nfs3_readdir_common(struct rpc_context *rpc, struct rpc_msg *call, void *opaque, bool is_plus) { nfs_client_t *self = (nfs_client_t*)opaque; READDIRPLUS3args plus_args; READDIRPLUS3args *args = NULL; if (is_plus) args = ((READDIRPLUS3args*)call->body.cbody.args); else { args = &plus_args; READDIR3args *in_args = ((READDIR3args*)call->body.cbody.args); args->dir = in_args->dir; args->cookie = in_args->cookie; *((uint64_t*)args->cookieverf) = *((uint64_t*)in_args->cookieverf); args->dircount = 512; args->maxcount = in_args->count; } std::string dirhash = std::string(args->dir.data.data_val, args->dir.data.data_len); std::string dir; if (dirhash != "roothandle") { auto dir_it = self->parent->dir_by_hash.find(dirhash); if (dir_it != self->parent->dir_by_hash.end()) dir = dir_it->second; } std::string prefix = self->parent->name_prefix; if (dir != "") { prefix += dir+"/"; } //struct timespec now; //clock_gettime(CLOCK_REALTIME, &now); std::map entries; std::vector handles; for (auto & ic: self->parent->cli->st_cli.inode_config) { auto & inode_cfg = ic.second; if (prefix != "" && inode_cfg.name.substr(0, prefix.size()) != prefix) continue; std::string subname = inode_cfg.name.substr(prefix.size()); int p = 0; while (p < subname.size() && subname[p] == '/') p++; if (p > 0) subname = subname.substr(p); if (subname.size() == 0) continue; p = 0; while (p < subname.size() && subname[p] != '/') p++; if (p >= subname.size()) { entries[subname] = (struct entryplus3){ // fileid will change when the user creates snapshots // however, we hope that clients tolerate it well // Linux does, even though it complains about "fileid changed" in dmesg .fileid = ic.first, }; if (is_plus) { handles.push_back("S"+base64_encode(sha256(inode_cfg.name))); entries[subname].name_attributes = { .attributes_follow = TRUE, .post_op_attr_u = { .attributes = { .type = NF3REG, .mode = 0644, .nlink = 1, .uid = 0, .gid = 0, .size = inode_cfg.size, .used = inode_cfg.size, // FIXME take from statistics .rdev = (specdata3){ 0 }, .fsid = self->parent->fsid, .fileid = ic.first, //.atime = (nfstime3){ .seconds = now.tv_sec, .nseconds = now.tv_nsec }, //.mtime = (nfstime3){ .seconds = now.tv_sec, .nseconds = now.tv_nsec }, //.ctime = (nfstime3){ .seconds = now.tv_sec, .nseconds = now.tv_nsec }, } }, }; entries[subname].name_handle = { .handle_follows = TRUE, .post_op_fh3_u = { .handle = { .data = { // FIXME: I really want ZDR with std::string .data_len = handles[handles.size()-1].size(), .data_val = (char*)handles[handles.size()-1].c_str(), }, } }, }; } } else { auto subdir = dir == "" ? subname.substr(0, p) : dir+"/"+subname.substr(0, p); entries[subdir] = (struct entryplus3){ // for directories, fileid will change when the user restarts proxy .fileid = self->parent->dir_ids.at(subdir), }; if (is_plus) { handles.push_back("S"+base64_encode(sha256(subdir))); entries[subdir].name_attributes = { .attributes_follow = TRUE, .post_op_attr_u = { .attributes = get_dir_attributes(self, subdir) }, }; entries[subdir].name_handle = { .handle_follows = TRUE, .post_op_fh3_u = { .handle = { .data = { // FIXME: I really want ZDR with std::string .data_len = (unsigned)handles[handles.size()-1].size(), .data_val = (char*)handles[handles.size()-1].c_str(), }, } }, }; } } } // Offset results by the continuation cookie (equal to index in the listing) uint64_t idx = 1; void *prev = NULL; for (auto it = entries.begin(); it != entries.end(); it++) { entryplus3 *entry = &it->second; // First fields of entry3 and entryplus3 are the same: fileid, name, cookie entry->name = (char*)it->first.c_str(); entry->cookie = idx++; if (prev) { if (is_plus) ((entryplus3*)prev)->nextentry = entry; else ((entry3*)prev)->nextentry = (entry3*)entry; } prev = entry; if (args->cookie > 0 && entry->cookie == args->cookie+1) { entries.erase(entries.begin(), it); } } // Now limit results based on maximum reply size // Sadly we have to calculate reply size by hand // reply without entries is 4+4+(dir_attributes ? sizeof(fattr3) : 0)+8+4 bytes int reply_size = 20; if (reply_size > args->maxcount) { // Error, too small max reply size if (is_plus) { READDIRPLUS3res reply = { .status = NFS3ERR_TOOSMALL }; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_READDIRPLUS3res, sizeof(READDIRPLUS3res)); } else { READDIR3res reply = { .status = NFS3ERR_TOOSMALL }; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_READDIR3res, sizeof(READDIR3res)); } return; } // 1 entry3 is (8+4+(filename_len+3)/4*4+8) bytes // 1 entryplus3 is (8+4+(filename_len+3)/4*4+8 // + 4+(name_attributes ? (sizeof(fattr3) = 84) : 0) // + 4+(name_handle ? 4+(handle_len+3)/4*4 : 0)) bytes bool eof = true; for (auto it = entries.begin(); it != entries.end(); it++) { reply_size += 20+len_pad4(it->first.size())+(is_plus ? 8+84+len_pad4(it->second.name_handle.post_op_fh3_u.handle.data.data_len) : 0); if (reply_size > args->maxcount) { // Stop entries.erase(it, entries.end()); eof = false; break; } } if (entries.end() != entries.begin()) { auto last_it = entries.end(); last_it--; if (is_plus) ((entryplus3*)&last_it->second)->nextentry = NULL; else ((entry3*)&last_it->second)->nextentry = NULL; } // Send reply if (is_plus) { READDIRPLUS3res reply = { .status = NFS3_OK }; *(uint64_t*)(reply.READDIRPLUS3res_u.resok.cookieverf) = self->parent->dir_mod_rev.at(dir); reply.READDIRPLUS3res_u.resok.reply.entries = &entries.begin()->second; reply.READDIRPLUS3res_u.resok.reply.eof = eof; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_READDIRPLUS3res, sizeof(READDIRPLUS3res)); } else { READDIR3res reply = { .status = NFS3_OK }; *(uint64_t*)(reply.READDIR3res_u.resok.cookieverf) = self->parent->dir_mod_rev.at(dir); reply.READDIR3res_u.resok.reply.entries = (entry3*)&entries.begin()->second; reply.READDIR3res_u.resok.reply.eof = eof; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_READDIR3res, sizeof(READDIR3res)); } } static int nfs3_readdir_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs3_readdir_common(rpc, call, opaque, false); return 0; } static int nfs3_readdirplus_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs3_readdir_common(rpc, call, opaque, true); return 0; } // Get file system statistics static int nfs3_fsstat_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; FSSTAT3args *args = (FSSTAT3args*)call->body.cbody.args; FSSTAT3res reply; reply.status = NFS3_OK; reply.FSSTAT3res_u.resok.obj_attributes.attributes_follow = TRUE; reply.FSSTAT3res_u.resok.obj_attributes.post_op_attr_u.attributes = get_dir_attributes(self, ""); reply.FSSTAT3res_u.resok.tbytes = 4096; // total bytes reply.FSSTAT3res_u.resok.fbytes = 4096; // free bytes reply.FSSTAT3res_u.resok.abytes = 4096; // available bytes reply.FSSTAT3res_u.resok.tfiles = 1 << 31; // total files reply.FSSTAT3res_u.resok.ffiles = 1 << 31; // free files reply.FSSTAT3res_u.resok.afiles = 1 << 31; // available files reply.FSSTAT3res_u.resok.invarsec = 0; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_FSSTAT3res, sizeof(FSSTAT3res)); return 0; } static int nfs3_fsinfo_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; FSINFO3args *args = (FSINFO3args*)call->body.cbody.args; FSINFO3res reply; if (args->fsroot.data.data_len != 10) { // Example error reply.status = NFS3ERR_INVAL; } else { // Fill info reply.status = NFS3_OK; reply.FSINFO3res_u.resok.obj_attributes.attributes_follow = TRUE; reply.FSINFO3res_u.resok.obj_attributes.post_op_attr_u.attributes = get_dir_attributes(self, ""); reply.FSINFO3res_u.resok.rtmax = 128*1024*1024; reply.FSINFO3res_u.resok.rtpref = 128*1024*1024; reply.FSINFO3res_u.resok.rtmult = 4096; reply.FSINFO3res_u.resok.wtmax = 128*1024*1024; reply.FSINFO3res_u.resok.wtpref = 128*1024*1024; reply.FSINFO3res_u.resok.wtmult = 4096; reply.FSINFO3res_u.resok.dtpref = 128; reply.FSINFO3res_u.resok.maxfilesize = 0x7fffffffffffffff; reply.FSINFO3res_u.resok.time_delta.seconds = 1; reply.FSINFO3res_u.resok.time_delta.nseconds = 0; reply.FSINFO3res_u.resok.properties = FSF3_SYMLINK | FSF3_HOMOGENEOUS; } rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_FSINFO3res, sizeof(FSINFO3res)); return 0; } static int nfs3_pathconf_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; PATHCONF3args *args = (PATHCONF3args*)call->body.cbody.args; PATHCONF3res reply; if (args->object.data.data_len != 10) { // Example error reply.status = NFS3ERR_INVAL; } else { // Fill info reply.status = NFS3_OK; reply.PATHCONF3res_u.resok.obj_attributes.attributes_follow = FALSE; reply.PATHCONF3res_u.resok.linkmax = 0; reply.PATHCONF3res_u.resok.name_max = 255; reply.PATHCONF3res_u.resok.no_trunc = TRUE; reply.PATHCONF3res_u.resok.chown_restricted = FALSE; reply.PATHCONF3res_u.resok.case_insensitive = FALSE; reply.PATHCONF3res_u.resok.case_preserving = TRUE; } rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_PATHCONF3res, sizeof(PATHCONF3res)); return 0; } static int nfs3_commit_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; COMMIT3args *args = (COMMIT3args*)call->body.cbody.args; COMMIT3res reply = {}; // Just pretend we did fsync :-) rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_COMMIT3res, sizeof(COMMIT3res)); return 0; } static int mount3_mnt_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; dirpath *arg = (dirpath*)call->body.cbody.args; int flavor = AUTH_NONE; mountres3 reply; reply.fhs_status = MNT3_OK; reply.mountres3_u.mountinfo.fhandle.fhandle3_len = 10; reply.mountres3_u.mountinfo.fhandle.fhandle3_val = "roothandle"; reply.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = 1; reply.mountres3_u.mountinfo.auth_flavors.auth_flavors_val = &flavor; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_mountres3, sizeof(mountres3)); return 0; } static int mount3_dump_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; mountlist reply; reply = (struct mountbody*)malloc(sizeof(struct mountbody)); reply->ml_hostname = (dirpath)"127.0.0.1"; reply->ml_directory = (dirpath)"/test"; reply->ml_next = NULL; rpc_send_reply(rpc, call, NULL, (zdrproc_t)zdr_mountlist, sizeof(mountlist)); free(reply); return 0; } static int mount3_umnt_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; dirpath *arg = (dirpath*)call->body.cbody.args; // do nothing rpc_send_reply(rpc, call, NULL, (zdrproc_t)zdr_void, 0); return 0; } static int mount3_umntall_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { // do nothing rpc_send_reply(rpc, call, NULL, (zdrproc_t)zdr_void, 0); return 0; } static int mount3_export_proc(struct rpc_context *rpc, struct rpc_msg *call, void *opaque) { nfs_client_t *self = (nfs_client_t*)opaque; exports reply; reply = (struct exportnode*)malloc(sizeof(struct exportnode) + sizeof(struct groupnode)); reply->ex_dir = (dirpath)"/test"; reply->ex_groups = (struct groupnode*)(reply+1); reply->ex_groups->gr_name = (dirpath)"127.0.0.1"; reply->ex_groups->gr_next = NULL; reply->ex_next = NULL; rpc_send_reply(rpc, call, &reply, (zdrproc_t)zdr_exports, sizeof(exports)); free(reply); return 0; } nfs_client_t::nfs_client_t() { struct service_proc nfs3_pt_a[22] = { {NFS3_NULL, nfs3_null_proc, (zdrproc_t)zdr_void, 0, this}, {NFS3_GETATTR, nfs3_getattr_proc, (zdrproc_t)zdr_GETATTR3args, sizeof(GETATTR3args), this}, {NFS3_SETATTR, nfs3_setattr_proc, (zdrproc_t)zdr_SETATTR3args, sizeof(SETATTR3args), this}, {NFS3_LOOKUP, nfs3_lookup_proc, (zdrproc_t)zdr_LOOKUP3args, sizeof(LOOKUP3args), this}, {NFS3_ACCESS, nfs3_access_proc, (zdrproc_t)zdr_ACCESS3args, sizeof(ACCESS3args), this}, {NFS3_READLINK, nfs3_readlink_proc, (zdrproc_t)zdr_READLINK3args, sizeof(READLINK3args), this}, {NFS3_READ, nfs3_read_proc, (zdrproc_t)zdr_READ3args, sizeof(READ3args), this}, {NFS3_WRITE, nfs3_write_proc, (zdrproc_t)zdr_WRITE3args, sizeof(WRITE3args), this}, {NFS3_CREATE, nfs3_create_proc, (zdrproc_t)zdr_CREATE3args, sizeof(CREATE3args), this}, {NFS3_MKDIR, nfs3_mkdir_proc, (zdrproc_t)zdr_MKDIR3args, sizeof(MKDIR3args), this}, {NFS3_SYMLINK, nfs3_symlink_proc, (zdrproc_t)zdr_SYMLINK3args, sizeof(SYMLINK3args), this}, {NFS3_MKNOD, nfs3_mknod_proc, (zdrproc_t)zdr_MKNOD3args, sizeof(MKNOD3args), this}, {NFS3_REMOVE, nfs3_remove_proc, (zdrproc_t)zdr_REMOVE3args, sizeof(REMOVE3args), this}, {NFS3_RMDIR, nfs3_rmdir_proc, (zdrproc_t)zdr_RMDIR3args, sizeof(RMDIR3args), this}, {NFS3_RENAME, nfs3_rename_proc, (zdrproc_t)zdr_RENAME3args, sizeof(RENAME3args), this}, {NFS3_LINK, nfs3_link_proc, (zdrproc_t)zdr_LINK3args, sizeof(LINK3args), this}, {NFS3_READDIR, nfs3_readdir_proc, (zdrproc_t)zdr_READDIR3args, sizeof(READDIR3args), this}, {NFS3_READDIRPLUS, nfs3_readdirplus_proc, (zdrproc_t)zdr_READDIRPLUS3args, sizeof(READDIRPLUS3args), this}, {NFS3_FSSTAT, nfs3_fsstat_proc, (zdrproc_t)zdr_FSSTAT3args, sizeof(FSSTAT3args), this}, {NFS3_FSINFO, nfs3_fsinfo_proc, (zdrproc_t)zdr_FSINFO3args, sizeof(FSINFO3args), this}, {NFS3_PATHCONF, nfs3_pathconf_proc, (zdrproc_t)zdr_PATHCONF3args, sizeof(PATHCONF3args), this}, {NFS3_COMMIT, nfs3_commit_proc, (zdrproc_t)zdr_COMMIT3args, sizeof(COMMIT3args), this}, }; for (int i = 0; i < sizeof(nfs3_pt_a)/sizeof(service_proc); i++) { nfs3_pt.push_back(nfs3_pt_a[i]); } struct service_proc nfs3_mount_pt_a[6] = { {MOUNT3_NULL, nfs3_null_proc, (zdrproc_t)zdr_void, 0, this}, {MOUNT3_MNT, mount3_mnt_proc, (zdrproc_t)zdr_dirpath, sizeof(dirpath), this}, {MOUNT3_DUMP, mount3_dump_proc, (zdrproc_t)zdr_void, 0, this}, {MOUNT3_UMNT, mount3_umnt_proc, (zdrproc_t)zdr_dirpath, sizeof(dirpath), this}, {MOUNT3_UMNTALL, mount3_umntall_proc, (zdrproc_t)zdr_void, 0, this}, {MOUNT3_EXPORT, mount3_export_proc, (zdrproc_t)zdr_void, 0, this}, }; for (int i = 0; i < sizeof(nfs3_mount_pt_a)/sizeof(service_proc); i++) { nfs3_mount_pt.push_back(nfs3_mount_pt_a[i]); } } nfs_client_t::~nfs_client_t() { if (rpc) { rpc_disconnect(rpc, NULL); rpc_destroy_context(rpc); } }