#include #include "nfs_add_on.h" #include #include "rpc.h" #include "pmap.h" #include "nfs.h" #include "mount.h" #include #include #include #include #include #include #include #include #include #ifndef UDP_SIZE_MAX #define UDP_SIZE_MAX 65515 #endif #define B_UDP_MAX_SIZE UDP_SIZE_MAX static status_t fs_rmdir(fs_volume *_volume, fs_vnode *_dir, const char *name); /* *** configuration *** */ //#define NFS_FS_FLAGS B_FS_IS_SHARED #define NFS_FS_FLAGS B_FS_IS_SHARED|B_FS_IS_PERSISTENT /* port numbers: most NFS servers insist on the client port to be < 1024 (secure option) */ /* ports to bind() to; we start at conf_high_port, then go down */ static int16 conf_high_port = 1023; static int16 conf_low_port = 900; /* Allow open() to open directories too */ static bool conf_allow_dir_open = true; /* Do we list ".." in readdir(rootid) ? (the VFS corrects the dirents anyway) */ /* this seems to be mandatory for Dano... BEntry::GetPath() needs that */ static bool conf_ls_root_parent = true; /* timeout when waiting for an answer to a call */ static bigtime_t conf_call_timeout = 2000000; /* number of retries when waiting for an anwser to a call */ static unsigned long conf_call_tries = 3; /* don't check who the answers come from for requests */ bool conf_no_check_ip_xid = false; static vint32 refcount = 0; /* we only want to read the config once ? */ static status_t read_config(void) { void *handle; const char *str, *endptr; handle = load_driver_settings("nfs"); if (handle == NULL) return ENOENT; str = get_driver_parameter(handle, "high_port", NULL, NULL); if (str) { endptr = str + strlen(str); conf_high_port = (int16)strtoul(str, (char **)&endptr, 10); } str = get_driver_parameter(handle, "low_port", NULL, NULL); if (str) { endptr = str + strlen(str); conf_low_port = (int16)strtoul(str, (char **)&endptr, 10); } conf_allow_dir_open = get_driver_boolean_parameter(handle, "allow_dir_open", conf_allow_dir_open, true); conf_ls_root_parent = get_driver_boolean_parameter(handle, "ls_root_parent", conf_ls_root_parent, true); conf_no_check_ip_xid = get_driver_boolean_parameter(handle, "no_check_ip_xid", conf_no_check_ip_xid, true); str = get_driver_parameter(handle, "call_timeout", NULL, NULL); if (str) { endptr = str + strlen(str); conf_call_timeout = (bigtime_t)1000 * strtoul(str, (char **)&endptr, 10); if (conf_call_timeout < 1000) conf_call_timeout = 1000; } str = get_driver_parameter(handle, "call_tries", NULL, NULL); if (str) { endptr = str + strlen(str); conf_call_tries = strtoul(str, (char **)&endptr, 10); } unload_driver_settings(handle); return B_OK; } status_t create_socket(fs_nspace *ns) { struct sockaddr_in addr; uint16 port=conf_high_port; ns->s=socket(AF_INET,SOCK_DGRAM,0); if (ns->s<0) return errno; do { addr.sin_family=AF_INET; addr.sin_addr.s_addr=htonl(INADDR_ANY); //addr.sin_addr.s_addr=htonl(INADDR_LOOPBACK); addr.sin_port=htons(port); memset (addr.sin_zero,0,sizeof(addr.sin_zero)); if (bind(ns->s,(const struct sockaddr *)&addr,sizeof(addr))<0) { if (errno!=EADDRINUSE) { int result=errno; close(ns->s); return result; } port--; if (port==conf_low_port) { close(ns->s); return B_ERROR; } } else break;//return B_OK; } while (true); // doesn't seem to help with autoincrementing port on source address... addr.sin_addr = ns->mountAddr.sin_addr; addr.sin_port = htons(111); //kconnect(ns->s,(const struct sockaddr *)&addr,sizeof(addr)); return B_OK; } #if 0 static status_t connect_socket(fs_nspace *ns) { uint16 port = conf_high_port; struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); //addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.sin_port = htons(port); memset(addr.sin_zero,0,sizeof(addr.sin_zero)); if (kconnect(ns->s,(const struct sockaddr *)&ns->nfsAddr,sizeof(ns->nfsAddr))<0) { return -1; } return B_OK; } #endif status_t init_postoffice(fs_nspace *ns) { status_t result; ns->tid=spawn_kernel_thread ((thread_func)postoffice_func,"NFSv2 Postoffice",B_NORMAL_PRIORITY,ns); if (ns->tidtid; ns->quit=false; result=resume_thread (ns->tid); if (resulttid); return result; } return B_OK; } void shutdown_postoffice(fs_nspace *ns) { status_t result; ns->quit=true; close(ns->s); wait_for_thread (ns->tid,&result); } status_t postoffice_func(fs_nspace *ns) { uint8 *buffer=(uint8 *)malloc(B_UDP_MAX_SIZE); while (!ns->quit) { struct sockaddr_in from; socklen_t fromLen=sizeof(from); ssize_t bytes = recvfrom(ns->s, buffer, B_UDP_MAX_SIZE, 0, (struct sockaddr *)&from, &fromLen); if (bytes >= 4) { struct PendingCall *call; int32 xid=B_BENDIAN_TO_HOST_INT32(*((int32 *)buffer)); call=RPCPendingCallsFindAndRemovePendingCall(&ns->pendingCalls, xid, &from); if (call) { call->buffer=(uint8 *)malloc(bytes); memcpy(call->buffer, buffer, bytes); while (release_sem (call->sem) == B_INTERRUPTED); } else { dprintf("nfs: postoffice: can't find pending call to remove " "for xid %" B_PRId32 "\n", xid); } } } free (buffer); return B_OK; } uint8 * send_rpc_call(fs_nspace *ns, const struct sockaddr_in *addr, int32 prog, int32 vers, int32 proc, const struct XDROutPacket *packet) { int32 xid; size_t authSize; struct PendingCall *pending; int32 retries=conf_call_tries; status_t result; struct PendingCall *call; struct XDROutPacket rpc_call; XDROutPacketInit(&rpc_call); xid=atomic_add(&ns->xid, 1); #ifdef DEBUG_XID //dbgprintxid(logfd1, xid); #endif XDROutPacketAddInt32(&rpc_call, xid); XDROutPacketAddInt32(&rpc_call, RPC_CALL); XDROutPacketAddInt32(&rpc_call, RPC_VERSION); XDROutPacketAddInt32(&rpc_call, prog); XDROutPacketAddInt32(&rpc_call, vers); XDROutPacketAddInt32(&rpc_call, proc); #if !defined(USE_SYSTEM_AUTHENTICATION) XDROutPacketAddInt32(&rpc_call, RPC_AUTH_NONE); XDROutPacketAddDynamic (&rpc_call, NULL, 0); #else XDROutPacketAddInt32(&rpc_call, RPC_AUTH_SYS); authSize = 4 + 4 + ((strlen(ns->params.server) + 3) &~3) + 4 + 4 + 4; XDROutPacketAddInt32(&rpc_call, authSize); XDROutPacketAddInt32(&rpc_call, 0); XDROutPacketAddString(&rpc_call, ns->params.server); XDROutPacketAddInt32(&rpc_call, ns->params.uid); XDROutPacketAddInt32(&rpc_call, ns->params.gid); XDROutPacketAddInt32(&rpc_call, 0); #endif XDROutPacketAddInt32(&rpc_call, RPC_AUTH_NONE); XDROutPacketAddDynamic (&rpc_call, NULL, 0); XDROutPacketAppend (&rpc_call, packet); pending = RPCPendingCallsAddPendingCall(&ns->pendingCalls, xid, addr); #ifdef DEBUG_XID checksemstate(xid, pending->sem, 0); #endif do { ssize_t bytes; do { bytes = sendto(ns->s,(const void *)XDROutPacketBuffer(&rpc_call), XDROutPacketLength(&rpc_call), 0, (const struct sockaddr *)addr, sizeof(*addr)); } while (bytes < 0 && errno == EINTR); do { result = acquire_sem_etc (pending->sem, 1, B_TIMEOUT, (retries) ? (conf_call_timeout) : (2*conf_call_timeout)); } while (result == B_INTERRUPTED); retries--; } while (result == B_TIMED_OUT && retries >= 0); if (result >= B_OK) { uint8 *buffer = pending->buffer; pending->buffer = NULL; SemaphorePoolPut(&ns->pendingCalls.fPool, pending->sem); PendingCallDestroy(pending); free(pending); XDROutPacketDestroy(&rpc_call); return buffer; } // we timed out call = RPCPendingCallsFindAndRemovePendingCall(&ns->pendingCalls, xid, addr); dprintf("nfs: xid %" B_PRId32 " timed out, removing from queue", xid); #if 0 if (call==NULL) { #if 1 //XXX:mmu_man:??? while (acquire_sem(pending->sem)==B_INTERRUPTED); #else status_t err; /* NOTE(mmu_man): there can be a race condition here where the sem is returned * to the pool without the correct value, compromising the next call using it. * however it seems waiting forever can lead to lockups... */ while ((err = acquire_sem_etc(pending->sem,1,B_TIMEOUT,5000000))==B_INTERRUPTED); dprintf("nfs: acquire(pending->sem) = 0x%08lx\n", err); if (err == B_TIMED_OUT) dprintf("nfs: timed out waiting on sem\n"); #endif } #endif /* mmu_man */ if (call) /* if the call has been found and removed (atomic op), the sem hasn't been released */ SemaphorePoolPut(&ns->pendingCalls.fPool, pending->sem); else delete_sem(pending->sem); /* else it's in an unknown state, forget it */ PendingCallDestroy(pending); free(pending); XDROutPacketDestroy (&rpc_call); return NULL; } bool is_successful_reply(struct XDRInPacket *reply) { bool success = false; int32 xid = XDRInPacketGetInt32(reply); rpc_msg_type mtype=(rpc_msg_type)XDRInPacketGetInt32(reply); rpc_reply_stat replyStat=(rpc_reply_stat)XDRInPacketGetInt32(reply); (void)xid; (void)mtype; if (replyStat == RPC_MSG_DENIED) { rpc_reject_stat rejectStat = (rpc_reject_stat)XDRInPacketGetInt32(reply); if (rejectStat == RPC_RPC_MISMATCH) { int32 low = XDRInPacketGetInt32(reply); int32 high = XDRInPacketGetInt32(reply); dprintf("nfs: RPC_MISMATCH (%" B_PRId32 ",%" B_PRId32 ")", low, high); } else { rpc_auth_stat authStat = (rpc_auth_stat)XDRInPacketGetInt32(reply); dprintf("nfs: RPC_AUTH_ERROR (%d)", authStat); } } else { rpc_auth_flavor flavor = (rpc_auth_flavor)XDRInPacketGetInt32(reply); char body[400]; size_t bodyLength = XDRInPacketGetDynamic(reply, body); rpc_accept_stat acceptStat = (rpc_accept_stat)XDRInPacketGetInt32(reply); (void)flavor; (void)bodyLength; if (acceptStat == RPC_PROG_MISMATCH) { int32 low = XDRInPacketGetInt32(reply); int32 high = XDRInPacketGetInt32(reply); dprintf("nfs: RPC_PROG_MISMATCH (%" B_PRId32 ",%" B_PRId32 ")", low, high); } else if (acceptStat != RPC_SUCCESS) dprintf("nfs: Accepted but failed (%d)", acceptStat); else success = true; } return success; } status_t get_remote_address(fs_nspace *ns, int32 prog, int32 vers, int32 prot, struct sockaddr_in *addr) { struct XDROutPacket call; uint8 *replyBuf; XDROutPacketInit(&call); addr->sin_port = htons(PMAP_PORT); XDROutPacketAddInt32(&call, prog); XDROutPacketAddInt32(&call, vers); XDROutPacketAddInt32(&call, prot); XDROutPacketAddInt32(&call, 0); replyBuf = send_rpc_call(ns, addr, PMAP_PROGRAM, PMAP_VERSION, PMAPPROC_GETPORT, &call); if (replyBuf) { struct XDRInPacket reply; XDRInPacketInit(&reply); XDRInPacketSetTo(&reply,replyBuf,0); if (is_successful_reply(&reply)) { addr->sin_port = htons(XDRInPacketGetInt32(&reply)); memset(addr->sin_zero, 0, sizeof(addr->sin_zero)); XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_OK; } XDRInPacketDestroy(&reply); } XDROutPacketDestroy (&call); return EHOSTUNREACH; } status_t nfs_mount(fs_nspace *ns, const char *path, nfs_fhandle *fhandle) { struct XDROutPacket call; struct XDRInPacket reply; uint8 *replyBuf; int32 fhstatus; XDROutPacketInit(&call); XDRInPacketInit(&reply); XDROutPacketAddString(&call,path); replyBuf = send_rpc_call(ns, &ns->mountAddr, MOUNT_PROGRAM, MOUNT_VERSION, MOUNTPROC_MNT, &call); if (!replyBuf) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return EHOSTUNREACH; } XDRInPacketSetTo(&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } fhstatus = XDRInPacketGetInt32(&reply); if (fhstatus != 0) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return map_nfs_to_system_error(fhstatus); } XDRInPacketGetFixed(&reply, fhandle->opaque, NFS_FHSIZE); XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_OK; } status_t nfs_lookup (fs_nspace *ns, const nfs_fhandle *dir, const char *filename, nfs_fhandle *fhandle, struct stat *st) { struct XDROutPacket call; struct XDRInPacket reply; int32 status; uint8 *replyBuf; XDROutPacketInit(&call); XDRInPacketInit(&reply); XDROutPacketAddFixed(&call, dir->opaque, NFS_FHSIZE); XDROutPacketAddString(&call, filename); replyBuf = send_rpc_call(ns, &ns->nfsAddr, NFS_PROGRAM, NFS_VERSION, NFSPROC_LOOKUP, &call); if (!replyBuf) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return EHOSTUNREACH; } XDRInPacketSetTo(&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); if (status != NFS_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return map_nfs_to_system_error(status); } XDRInPacketGetFixed(&reply, fhandle->opaque, NFS_FHSIZE); if (st) get_nfs_attr(&reply, st); XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_OK; } status_t nfs_getattr(fs_nspace *ns, const nfs_fhandle *fhandle, struct stat *st) { struct XDROutPacket call; struct XDRInPacket reply; uint8 *replyBuf; int32 status; XDROutPacketInit(&call); XDRInPacketInit(&reply); XDROutPacketAddFixed(&call, fhandle->opaque, NFS_FHSIZE); replyBuf = send_rpc_call(ns, &ns->nfsAddr, NFS_PROGRAM, NFS_VERSION, NFSPROC_GETATTR, &call); if (replyBuf == NULL) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return EHOSTUNREACH; } XDRInPacketSetTo(&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); if (status != NFS_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return map_nfs_to_system_error(status); } get_nfs_attr(&reply, st); XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_OK; } status_t nfs_truncate_file(fs_nspace *ns, const nfs_fhandle *fhandle, struct stat *st) { struct XDROutPacket call; struct XDRInPacket reply; uint8 *replyBuf; int32 status; XDROutPacketInit(&call); XDRInPacketInit(&reply); XDROutPacketAddFixed(&call, fhandle->opaque, NFS_FHSIZE); XDROutPacketAddInt32(&call, -1); XDROutPacketAddInt32(&call, -1); XDROutPacketAddInt32(&call, -1); XDROutPacketAddInt32(&call, 0); XDROutPacketAddInt32(&call, time(NULL)); XDROutPacketAddInt32(&call, 0); XDROutPacketAddInt32(&call, time(NULL)); XDROutPacketAddInt32(&call, 0); replyBuf = send_rpc_call(ns, &ns->nfsAddr, NFS_PROGRAM, NFS_VERSION, NFSPROC_SETATTR, &call); if (replyBuf == NULL) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return EHOSTUNREACH; } XDRInPacketSetTo(&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); if (status != NFS_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return map_nfs_to_system_error(status); } if (st) get_nfs_attr(&reply,st); XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_OK; } void get_nfs_attr(struct XDRInPacket *reply, struct stat *st) { nfs_ftype ftype=(nfs_ftype)XDRInPacketGetInt32(reply); (void) ftype; st->st_mode=XDRInPacketGetInt32(reply); st->st_dev=0; // just to be sure st->st_nlink=XDRInPacketGetInt32(reply); st->st_uid=XDRInPacketGetInt32(reply); st->st_gid=XDRInPacketGetInt32(reply); st->st_size=XDRInPacketGetInt32(reply); st->st_blksize=XDRInPacketGetInt32(reply); st->st_rdev=XDRInPacketGetInt32(reply); st->st_blocks=XDRInPacketGetInt32(reply); XDRInPacketGetInt32(reply); // fsid st->st_ino=XDRInPacketGetInt32(reply); st->st_atime=XDRInPacketGetInt32(reply); XDRInPacketGetInt32(reply); // usecs st->st_mtime=st->st_crtime=XDRInPacketGetInt32(reply); XDRInPacketGetInt32(reply); // usecs st->st_ctime=XDRInPacketGetInt32(reply); XDRInPacketGetInt32(reply); // usecs } status_t map_nfs_to_system_error(status_t nfsstatus) { switch (nfsstatus) { case NFS_OK: return B_OK; case NFSERR_PERM: return EPERM; case NFSERR_NOENT: return ENOENT; case NFSERR_IO: return EIO; case NFSERR_NXIO: return ENXIO; case NFSERR_ACCES: return EACCES; case NFSERR_EXIST: return EEXIST; case NFSERR_NODEV: return ENODEV; case NFSERR_NOTDIR: return ENOTDIR; case NFSERR_ISDIR: return EISDIR; case NFSERR_FBIG: return EFBIG; case NFSERR_NOSPC: return ENOSPC; case NFSERR_ROFS: return EROFS; case NFSERR_NAMETOOLONG: return ENAMETOOLONG; case NFSERR_NOTEMPTY: return ENOTEMPTY; case NFSERR_STALE: return C_ERROR_STALE; default: return B_ERROR; } } nfs_fhandle handle_from_vnid(fs_nspace *ns, ino_t vnid) { fs_node *current; while (acquire_sem(ns->sem) == B_INTERRUPTED); current = ns->first; while (current != NULL && current->vnid != vnid) current = current->next; while (release_sem(ns->sem) == B_INTERRUPTED); return current->fhandle; } void insert_node(fs_nspace *ns, fs_node *node) { fs_node *current; while (acquire_sem(ns->sem) == B_INTERRUPTED); current = ns->first; while (current != NULL && current->vnid != node->vnid) current = current->next; if (current) { free(node); while (release_sem(ns->sem) == B_INTERRUPTED); return; } node->next = ns->first; ns->first = node; while (release_sem (ns->sem) == B_INTERRUPTED); } void remove_node(fs_nspace *ns, ino_t vnid) { fs_node *current; fs_node *previous; while (acquire_sem(ns->sem) == B_INTERRUPTED); current = ns->first; previous = NULL; while (current != NULL && current->vnid != vnid) { previous = current; current = current->next; } if (current) { if (previous) previous->next = current->next; else ns->first = current->next; free(current); } while (release_sem(ns->sem) == B_INTERRUPTED); } // #pragma mark - static status_t fs_read_vnode(fs_volume *_volume, ino_t vnid, fs_vnode *_node, int *_type, uint32 *_flags, bool r) { fs_nspace *ns; fs_node *current; ns = _volume->private_volume; if (!r) { while (acquire_sem(ns->sem) == B_INTERRUPTED); } current = ns->first; while (current != NULL && current->vnid != vnid) current = current->next; if (!current) return EINVAL; current->vnid = vnid; _node->private_node = current; _node->ops = &sNFSVnodeOps; *_type = current->mode; *_flags = 0; if (!r) { while (release_sem(ns->sem) == B_INTERRUPTED); } return B_OK; } static status_t fs_release_vnode(fs_volume *_volume, fs_vnode *node, bool r) { (void) _volume; (void) node; (void) r; return B_OK; } static status_t fs_walk(fs_volume *_volume, fs_vnode *_base, const char *file, ino_t *vnid) { fs_node *dummy; status_t result; fs_nspace *ns; fs_node *base; //dprintf("nfs: walk(%s)\n", file);//XXX:mmu_man:debug ns = _volume->private_volume; base = _base->private_node; if (!strcmp(".", file)) *vnid = base->vnid; else { fs_node *newNode = (fs_node *)malloc(sizeof(fs_node)); struct stat st; if ((result = nfs_lookup(ns, &base->fhandle, file, &newNode->fhandle, &st)) < B_OK) { free(newNode); return result; } newNode->vnid = st.st_ino; newNode->mode = st.st_mode; *vnid = newNode->vnid; insert_node(ns, newNode); } if ((result = get_vnode (_volume, *vnid, (void **)&dummy)) < B_OK) return result; return B_OK; } static status_t fs_opendir(fs_volume *_volume, fs_vnode *_node, void **_cookie) { fs_nspace *ns; fs_node *node; nfs_cookie **cookie; struct stat st; status_t result; ns = _volume->private_volume; node = _node->private_node; cookie = (nfs_cookie **)_cookie; if ((result = nfs_getattr(ns, &node->fhandle, &st)) < B_OK) return result; if (!S_ISDIR(st.st_mode)) return ENOTDIR; *cookie = (nfs_cookie *)malloc(sizeof(nfs_cookie)); memset((*cookie)->opaque,0,NFS_COOKIESIZE); return B_OK; } static status_t fs_closedir(fs_volume *_volume, fs_vnode *_node, void *cookie) { (void) _volume; (void) _node; (void) cookie; return B_OK; } static status_t fs_rewinddir(fs_volume *_volume, fs_vnode *_node, void *_cookie) { nfs_cookie *cookie = (nfs_cookie *)_cookie; (void) _volume; (void) _node; memset (cookie->opaque, 0, NFS_COOKIESIZE); return B_OK; } static status_t fs_readdir(fs_volume *_volume, fs_vnode *_node, void *_cookie, struct dirent *buf, size_t bufsize, uint32 *num) { nfs_cookie *cookie = (nfs_cookie *)_cookie; uint32 max = *num; int32 eof; fs_nspace *ns; fs_node *node; size_t count = min_c(6000, max * 300); *num = 0; ns = _volume->private_volume; node = _node->private_node; do { ino_t vnid; char *filename; uint8 *replyBuf; struct XDROutPacket call; struct XDRInPacket reply; int32 status; XDROutPacketInit(&call); XDRInPacketInit(&reply); XDROutPacketAddFixed(&call, node->fhandle.opaque, NFS_FHSIZE); XDROutPacketAddFixed(&call, cookie->opaque, NFS_COOKIESIZE); XDROutPacketAddInt32(&call, count); replyBuf = send_rpc_call (ns, &ns->nfsAddr, NFS_PROGRAM, NFS_VERSION, NFSPROC_READDIR, &call); if (!replyBuf) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } XDRInPacketSetTo(&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); if (status != NFS_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return map_nfs_to_system_error(status); } while (XDRInPacketGetInt32(&reply) == 1) { nfs_cookie newCookie; vnid=XDRInPacketGetInt32(&reply); filename=XDRInPacketGetString(&reply); XDRInPacketGetFixed(&reply, newCookie.opaque, NFS_COOKIESIZE); //if (strcmp(".",filename)&&strcmp("..",filename)) //if ((ns->rootid != node->vnid) || (strcmp(".",filename)&&strcmp("..",filename))) if (conf_ls_root_parent || ((ns->rootid != node->vnid) || strcmp("..", filename))) { status_t result; struct stat st; fs_node *newNode = (fs_node *)malloc(sizeof(fs_node)); newNode->vnid = vnid; if ((result = nfs_lookup(ns, &node->fhandle, filename, &newNode->fhandle, &st)) < B_OK) { free (filename); free(newNode); XDRInPacketDestroy (&reply); XDROutPacketDestroy (&call); return result; } newNode->mode = st.st_mode; insert_node(ns,newNode); if (bufsize < 2 * (sizeof(dev_t) + sizeof(ino_t)) + sizeof(unsigned short) + strlen(filename) + 1) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_OK; } buf->d_dev = ns->nsid; buf->d_pdev = ns->nsid; buf->d_ino = vnid; buf->d_pino = node->vnid; buf->d_reclen = 2 * (sizeof(dev_t) + sizeof(ino_t)) + sizeof(unsigned short) + strlen(filename) + 1; strcpy (buf->d_name,filename); // if ((ns->rootid == node->vnid))//XXX:mmu_man:test // dprintf("nfs: dirent %d {d:%ld pd:%ld i:%lld pi:%lld '%s'}\n", *num, buf->d_dev, buf->d_pdev, buf->d_ino, buf->d_pino, buf->d_name); bufsize -= buf->d_reclen; buf = (struct dirent *)((char *)buf + buf->d_reclen); memcpy(cookie->opaque, newCookie.opaque, NFS_COOKIESIZE); (*num)++; } else { memcpy(cookie->opaque, newCookie.opaque, NFS_COOKIESIZE); } free (filename); if ((*num) == max) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_OK; } } eof=XDRInPacketGetInt32(&reply); XDRInPacketDestroy (&reply); XDROutPacketDestroy (&call); } while (eof == 0); return B_OK; } static status_t fs_free_dircookie(fs_volume *_volume, fs_vnode *_node, void *cookie) { (void) _volume; (void) _node; free(cookie); return B_OK; } static status_t fs_rstat(fs_volume *_volume, fs_vnode *_node, struct stat *st) { fs_nspace *ns; fs_node *node; status_t result; ns = _volume->private_volume; node = _node->private_node; //dprintf("nfs: rstat()\n");//XXX:mmu_man:debug if ((result = nfs_getattr(ns, &node->fhandle, st)) < B_OK) return result; st->st_dev = ns->nsid; //st->st_nlink = 1; //XXX:mmu_man:test return B_OK; } void fs_nspaceInit(struct fs_nspace *nspace) { RPCPendingCallsInit(&nspace->pendingCalls); } void fs_nspaceDestroy(struct fs_nspace *nspace) { RPCPendingCallsDestroy(&nspace->pendingCalls); } static status_t parse_nfs_params(const char *str, struct mount_nfs_params *params) { const char *p, *e; long v; int i; // sprintf(buf, "nfs:%s:%s,uid=%u,gid=%u,hostname=%s", if (!str || !params) return EINVAL; if (strncmp(str, "nfs:", 4)) return EINVAL; dprintf("nfs:ip!\n"); p = str + 4; e = strchr(p, ':'); if (!e) return EINVAL; params->server = malloc(e - p + 1); params->server[e - p] = '\0'; strncpy(params->server, p, e - p); // hack params->serverIP = 0; v = strtol(p, (char **)&p, 10); dprintf("IP:%ld.", v); if (!p) return EINVAL; params->serverIP |= (v << 24); p++; v = strtol(p, (char **)&p, 10); dprintf("%ld.", v); if (!p) return EINVAL; params->serverIP |= (v << 16); p++; v = strtol(p, (char **)&p, 10); dprintf("%ld.", v); if (!p) return EINVAL; params->serverIP |= (v << 8); p++; v = strtol(p, (char **)&p, 10); dprintf("%ld\n", v); if (!p) return EINVAL; params->serverIP |= (v); if (*p++ != ':') return EINVAL; e = strchr(p, ','); i = (e) ? (e - p) : (strlen(p)); params->_export = malloc(i + 1); params->_export[i] = '\0'; strncpy(params->_export, p, i); p = strstr(str, "hostname="); if (!p) return EINVAL; dprintf("nfs:hn!\n"); p += 9; e = strchr(p, ','); i = (e) ? (e - p) : (strlen(p)); params->hostname = malloc(i + 1); params->hostname[i] = '\0'; strncpy(params->hostname, p, i); p = strstr(str, "uid="); dprintf("nfs:uid!\n"); if (p) { p += 4; v = strtol(p, (char **)&p, 10); params->uid = v; } dprintf("nfs:gid!\n"); p = strstr(str, "gid="); if (p) { p += 4; v = strtol(p, (char **)&p, 10); params->gid = v; } dprintf("nfs: ip:%08x server:'%s' export:'%s' hostname:'%s' uid=%d gid=%d \n", params->serverIP, params->server, params->_export, params->hostname, params->uid, params->gid); return B_OK; } static status_t fs_mount(fs_volume *_vol, const char *devname, uint32 flags, const char *_parms, ino_t *vnid) { status_t result; fs_nspace *ns; fs_node *rootNode; struct stat st; if (_parms == NULL) return EINVAL; dprintf("nfs: mount(%" B_PRId32 ", %s, %08" B_PRIx32 ")\n", _vol->id, devname, flags); dprintf("nfs: nfs_params: %s\n", _parms); // HAIKU: this should go to std_ops if (!refcount) read_config(); result = ENOMEM; ns = (fs_nspace *)malloc(sizeof(fs_nspace)); if (!ns) goto err_nspace; fs_nspaceInit(ns); ns->nsid = _vol->id; ns->params.server = NULL; ns->params._export = NULL; ns->params.hostname = NULL; if ((result = parse_nfs_params(_parms, &ns->params)) < 0) goto err_params; ns->xid = 0; ns->mountAddr.sin_family = AF_INET; ns->mountAddr.sin_addr.s_addr = htonl(ns->params.serverIP); memset(ns->mountAddr.sin_zero, 0, sizeof(ns->mountAddr.sin_zero)); if ((result = create_socket(ns)) < B_OK) { dprintf("nfs: could not create socket (%" B_PRId32 ")\n", result); goto err_socket; } if ((result = init_postoffice(ns)) < B_OK) { dprintf("nfs: could not init_postoffice() (%" B_PRId32 ")\n", result); goto err_postoffice; } if ((result = get_remote_address(ns, MOUNT_PROGRAM, MOUNT_VERSION, PMAP_IPPROTO_UDP, &ns->mountAddr)) < B_OK) { dprintf("could not get_remote_address() (%" B_PRId32 ")\n", result); goto err_sem; } memcpy(&ns->nfsAddr, &ns->mountAddr, sizeof(ns->mountAddr)); dprintf("nfs: mountd at %08x:%d\n", ns->mountAddr.sin_addr.s_addr, ntohs(ns->mountAddr.sin_port)); if ((result = get_remote_address(ns, NFS_PROGRAM, NFS_VERSION, PMAP_IPPROTO_UDP, &ns->nfsAddr)) < B_OK) goto err_sem; dprintf("nfs: nfsd at %08x:%d\n", ns->nfsAddr.sin_addr.s_addr, ntohs(ns->nfsAddr.sin_port)); // result = connect_socket(ns); //dprintf("nfs: connect: %s\n", strerror(result)); if ((result = create_sem(1, "nfs_sem")) < B_OK) goto err_sem; ns->sem = result; set_sem_owner(ns->sem, B_SYSTEM_TEAM); result = ENOMEM; rootNode = (fs_node *)malloc(sizeof(fs_node)); if (!rootNode) goto err_rootvn; rootNode->next = NULL; if ((result = nfs_mount(ns, ns->params._export, &rootNode->fhandle)) < B_OK) goto err_mount; if ((result = nfs_getattr(ns, &rootNode->fhandle, &st)) < B_OK) goto err_publish; ns->rootid = st.st_ino; rootNode->vnid = ns->rootid; *vnid = ns->rootid; _vol->private_volume = ns; _vol->ops = &sNFSVolumeOps; // TODO: set right mode if ((result = publish_vnode(_vol, *vnid, rootNode, &sNFSVnodeOps, S_IFDIR, 0)) < B_OK) goto err_publish; ns->first = rootNode; return B_OK; err_publish: // XXX: unmount ?? err_mount: free(rootNode); err_rootvn: delete_sem (ns->sem); err_sem: shutdown_postoffice(ns); goto err_socket; err_postoffice: close(ns->s); err_socket: err_params: free(ns->params.hostname); free(ns->params._export); free(ns->params.server); fs_nspaceDestroy(ns); free(ns); err_nspace: if (result >= 0) { dprintf("nfs:bad error from mount!\n"); result = EINVAL; } dprintf("nfs: error in nfs_mount: %s\n", strerror(result)); return result; } static status_t fs_unmount(fs_volume *_volume) { fs_nspace *ns = (fs_nspace *)_volume->private_volume; free(ns->params.hostname); free(ns->params._export); free(ns->params.server); while (ns->first) { fs_node *next = ns->first->next; free(ns->first); ns->first = next; } // We need to put the reference to our root node ourselves put_vnode(_volume, ns->rootid); delete_sem(ns->sem); shutdown_postoffice(ns); fs_nspaceDestroy(ns); free(ns); return B_OK; } static status_t fs_rfsstat(fs_volume *_volume, struct fs_info *info) { fs_nspace *ns; struct XDROutPacket call; struct XDRInPacket reply; nfs_fhandle rootHandle; uint8 *replyBuf; int32 status; ns = (fs_nspace *)_volume->private_volume; rootHandle = handle_from_vnid (ns,ns->rootid); //dprintf("nfs: rfsstat()\n");//XXX:mmu_man:debug XDROutPacketInit(&call); XDRInPacketInit(&reply); XDROutPacketAddFixed(&call, rootHandle.opaque, NFS_FHSIZE); replyBuf = send_rpc_call(ns, &ns->nfsAddr, NFS_PROGRAM, NFS_VERSION, NFSPROC_STATFS, &call); if (replyBuf == NULL) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return EHOSTUNREACH; } XDRInPacketSetTo(&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); if (status != NFS_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); //dprintf("nfs: rfsstat() error 0x%08lx\n", map_nfs_to_system_error(status)); return map_nfs_to_system_error(status); } info->dev = ns->nsid; info->root = ns->rootid; info->flags = NFS_FS_FLAGS; XDRInPacketGetInt32(&reply); // tsize info->block_size = XDRInPacketGetInt32(&reply); info->io_size = 8192; info->total_blocks = XDRInPacketGetInt32(&reply); info->free_blocks = XDRInPacketGetInt32(&reply); info->total_nodes = 100; info->free_nodes = 100; strcpy(info->volume_name, "nfs://"); strcat(info->volume_name, ns->params.server); strcat(info->volume_name, ns->params._export); strcpy(info->fsh_name, "nfs"); XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_OK; } static status_t fs_open(fs_volume *_volume, fs_vnode *_node, int omode, void **_cookie) { fs_nspace *ns; fs_node *node; struct stat st; status_t result; fs_file_cookie **cookie; ns = _volume->private_volume; node = _node->private_node; cookie = (fs_file_cookie **)_cookie; if ((result = nfs_getattr(ns, &node->fhandle, &st)) < B_OK) return result; if (S_ISDIR(st.st_mode)) { /* permit opening of directories */ if (conf_allow_dir_open) { *cookie = NULL; return B_OK; } else return EISDIR; } *cookie = (fs_file_cookie *)malloc(sizeof(fs_file_cookie)); (*cookie)->omode = omode; (*cookie)->original_size = st.st_size; (*cookie)->st = st; return B_OK; } static status_t fs_close(fs_volume *_volume, fs_vnode *_node, void *cookie) { (void) _volume; (void) _node; (void) cookie; /* //XXX:mmu_man:why that ?? makes Tracker go nuts updating the stats if ((cookie->omode & O_RDWR)||(cookie->omode & O_WRONLY)) return my_notify_listener (B_STAT_CHANGED,ns->nsid,0,0,node->vnid,NULL); */ return B_OK; } static status_t fs_free_cookie(fs_volume *_volume, fs_vnode *_node, void *cookie) { (void) _volume; (void) _node; free(cookie); return B_OK; } static status_t fs_read(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos, void *buf, size_t *len) { fs_nspace *ns; fs_node *node; fs_file_cookie *cookie; size_t max = *len; *len = 0; ns = _volume->private_volume; node = _node->private_node; cookie = (fs_file_cookie *)_cookie; if (!cookie) return EISDIR; /* do not permit reading of directories */ while ((*len) < max) { size_t count = min_c(NFS_MAXDATA, max - (*len)); struct XDROutPacket call; struct XDRInPacket reply; int32 status; uint8 *replyBuf; struct stat st; size_t readbytes; XDROutPacketInit(&call); XDRInPacketInit(&reply); XDROutPacketAddFixed(&call, &node->fhandle.opaque, NFS_FHSIZE); XDROutPacketAddInt32(&call, pos); XDROutPacketAddInt32(&call, count); XDROutPacketAddInt32(&call, 0); replyBuf = send_rpc_call(ns, &ns->nfsAddr, NFS_PROGRAM, NFS_VERSION, NFSPROC_READ, &call); if (replyBuf == NULL) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } XDRInPacketSetTo(&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); if (status != NFS_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return map_nfs_to_system_error(status); } get_nfs_attr(&reply, &st); cookie->st = st; readbytes = XDRInPacketGetDynamic(&reply, buf); buf = (char *)buf + readbytes; (*len) += readbytes; pos += readbytes; XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); if (pos >= st.st_size) break; } return B_OK; } static status_t fs_write(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos, const void *buf, size_t *len) { fs_nspace *ns; fs_node *node; fs_file_cookie *cookie; size_t bytesWritten = 0; ns = _volume->private_volume; node = _node->private_node; cookie = (fs_file_cookie *)_cookie; if (!cookie) return EISDIR; /* do not permit reading of directories */ if (cookie->omode & O_APPEND) pos += cookie->original_size; while (bytesWritten < *len) { size_t count = min_c(NFS_MAXDATA,(*len) - bytesWritten); struct XDROutPacket call; struct XDRInPacket reply; int32 status; uint8 *replyBuf; struct stat st; XDROutPacketInit(&call); XDRInPacketInit(&reply); XDROutPacketAddFixed(&call, &node->fhandle.opaque, NFS_FHSIZE); XDROutPacketAddInt32(&call, 0); XDROutPacketAddInt32(&call, pos + bytesWritten); XDROutPacketAddInt32(&call, 0); XDROutPacketAddDynamic(&call, (const char *)buf + bytesWritten, count); replyBuf = send_rpc_call(ns, &ns->nfsAddr, NFS_PROGRAM, NFS_VERSION, NFSPROC_WRITE, &call); if (!replyBuf) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } XDRInPacketSetTo(&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); if (status != NFS_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return map_nfs_to_system_error(status); } get_nfs_attr(&reply, &st); cookie->st = st; bytesWritten += count; XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); } return B_OK; } static status_t fs_wstat(fs_volume *_volume, fs_vnode *_node, const struct stat *st, uint32 mask) { fs_nspace *ns; fs_node *node; struct XDROutPacket call; struct XDRInPacket reply; uint8 *replyBuf; int32 status; ns = _volume->private_volume; node = _node->private_node; XDROutPacketInit(&call); XDRInPacketInit(&reply); XDROutPacketAddFixed(&call,node->fhandle.opaque,NFS_FHSIZE); XDROutPacketAddInt32(&call, (mask & WSTAT_MODE) ? st->st_mode : -1); XDROutPacketAddInt32(&call, (mask & WSTAT_UID) ? st->st_uid : -1); XDROutPacketAddInt32(&call, (mask & WSTAT_GID) ? st->st_gid : -1); XDROutPacketAddInt32(&call, (mask & WSTAT_SIZE) ? st->st_size : -1); XDROutPacketAddInt32(&call, (mask & WSTAT_ATIME) ? st->st_atime : -1); XDROutPacketAddInt32(&call, (mask & WSTAT_ATIME) ? 0 : -1); XDROutPacketAddInt32(&call, (mask & WSTAT_MTIME) ? st->st_mtime : -1); XDROutPacketAddInt32(&call, (mask & WSTAT_MTIME) ? 0 : -1); replyBuf = send_rpc_call(ns, &ns->nfsAddr, NFS_PROGRAM, NFS_VERSION, NFSPROC_SETATTR, &call); if (!replyBuf) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return EHOSTUNREACH; } XDRInPacketSetTo(&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); if (status != NFS_OK) return map_nfs_to_system_error(status); XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return notify_stat_changed(_volume->id, -1, node->vnid, mask); } static status_t fs_wfsstat(fs_volume *_volume, const struct fs_info *info, uint32 mask) { (void) _volume; (void) info; (void) mask; return B_OK; } static status_t fs_create(fs_volume *_volume, fs_vnode *_dir, const char *name, int omode, int perms, void **_cookie, ino_t *vnid) { nfs_fhandle fhandle; struct stat st; fs_file_cookie **cookie; fs_nspace *ns; fs_node *dir; status_t result; ns = _volume->private_volume; dir = _dir->private_node; cookie = (fs_file_cookie **)_cookie; result = nfs_lookup(ns,&dir->fhandle,name,&fhandle,&st); if (result == B_OK) { void *dummy; fs_node *newNode = (fs_node *)malloc(sizeof(fs_node)); if (newNode == NULL) return B_NO_MEMORY; newNode->fhandle = fhandle; newNode->vnid = st.st_ino; newNode->mode = st.st_mode; insert_node(ns, newNode); *vnid = st.st_ino; if ((result = get_vnode(_volume,*vnid,&dummy)) < B_OK) return result; if (S_ISDIR(st.st_mode)) return EISDIR; if (omode & O_EXCL) return EEXIST; if (omode & O_TRUNC) { if ((result = nfs_truncate_file(ns, &fhandle, NULL)) < B_OK) return result; } *cookie=(fs_file_cookie *)malloc(sizeof(fs_file_cookie)); if (*cookie == NULL) return B_NO_MEMORY; (*cookie)->omode=omode; (*cookie)->original_size=st.st_size; (*cookie)->st=st; return B_OK; } else if (result != ENOENT) { return result; } else { struct XDROutPacket call; struct XDRInPacket reply; uint8 *replyBuf; int32 status; fs_node *newNode; if (!(omode & O_CREAT)) return ENOENT; XDROutPacketInit(&call); XDRInPacketInit(&reply); XDROutPacketAddFixed(&call, dir->fhandle.opaque, NFS_FHSIZE); XDROutPacketAddString(&call, name); XDROutPacketAddInt32(&call, perms | S_IFREG); XDROutPacketAddInt32(&call, -1); XDROutPacketAddInt32(&call, -1); XDROutPacketAddInt32(&call, 0); XDROutPacketAddInt32(&call, time(NULL)); XDROutPacketAddInt32(&call, 0); XDROutPacketAddInt32(&call, time(NULL)); XDROutPacketAddInt32(&call, 0); replyBuf = send_rpc_call(ns, &ns->nfsAddr, NFS_PROGRAM, NFS_VERSION, NFSPROC_CREATE, &call); if (!replyBuf) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } XDRInPacketSetTo(&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); if (status != NFS_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return map_nfs_to_system_error(status); } XDRInPacketGetFixed(&reply, fhandle.opaque, NFS_FHSIZE); get_nfs_attr(&reply,&st); newNode = (fs_node *)malloc(sizeof(fs_node)); if (newNode == NULL) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_NO_MEMORY; } newNode->fhandle = fhandle; newNode->vnid = st.st_ino; newNode->mode = st.st_mode; insert_node (ns, newNode); *vnid = st.st_ino; *cookie = (fs_file_cookie *)malloc(sizeof(fs_file_cookie)); if (*cookie == NULL) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_NO_MEMORY; } (*cookie)->omode = omode; (*cookie)->original_size = st.st_size; (*cookie)->st = st; result = new_vnode(_volume, *vnid, newNode, &sNFSVnodeOps); if (result < B_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return result; } XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return notify_entry_created(_volume->id, dir->vnid, name, *vnid); } } static status_t fs_unlink(fs_volume *_volume, fs_vnode *_dir, const char *name) { status_t result; fs_nspace *ns; fs_node *dir; fs_node *newNode; fs_node *dummy; struct XDROutPacket call; struct XDRInPacket reply; struct stat st; nfs_fhandle fhandle; uint8 *replyBuf; int32 status; ns = _volume->private_volume; dir = _dir->private_node; XDROutPacketInit(&call); XDRInPacketInit(&reply); if ((result = nfs_lookup(ns, &dir->fhandle, name, &fhandle, &st)) < B_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return result; } newNode = (fs_node *)malloc(sizeof(fs_node)); if (newNode == NULL) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_NO_MEMORY; } newNode->fhandle = fhandle; newNode->vnid = st.st_ino; newNode->mode = st.st_mode; insert_node(ns, newNode); if ((result = get_vnode(_volume, st.st_ino, (void **)&dummy)) < B_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return result; } if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return EISDIR; } if ((result=remove_vnode(_volume,st.st_ino)) < B_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return result; } if ((result=put_vnode(_volume, st.st_ino)) < B_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return result; } XDROutPacketAddFixed(&call, dir->fhandle.opaque, NFS_FHSIZE); XDROutPacketAddString(&call, name); replyBuf=send_rpc_call (ns,&ns->nfsAddr,NFS_PROGRAM,NFS_VERSION,NFSPROC_REMOVE,&call); if (!replyBuf) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return EHOSTUNREACH; } XDRInPacketSetTo(&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); if (status != NFS_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return map_nfs_to_system_error(status); } XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return notify_entry_removed(_volume->id, dir->vnid, name, st.st_ino); } static status_t fs_remove_vnode(fs_volume *_volume, fs_vnode *_node, bool r) { fs_nspace *ns = _volume->private_volume; fs_node *node = _node->private_node; (void) r; remove_node (ns, node->vnid); return B_OK; } static status_t fs_mkdir(fs_volume *_volume, fs_vnode *_dir, const char *name, int perms) { fs_nspace *ns; fs_node *dir; nfs_fhandle fhandle; struct stat st; fs_node *newNode; status_t result; uint8 *replyBuf; int32 status; struct XDROutPacket call; struct XDRInPacket reply; ns = _volume->private_volume; dir = _dir->private_node; XDROutPacketInit(&call); XDRInPacketInit(&reply); result = nfs_lookup(ns, &dir->fhandle, name, &fhandle, &st); if (result == B_OK) { //void *dummy; XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); // XXX: either OK or not get_vnode !!! ?? //if ((result=get_vnode(_volume,st.st_ino,&dummy))fhandle.opaque, NFS_FHSIZE); XDROutPacketAddString(&call, name); XDROutPacketAddInt32(&call, perms | S_IFDIR); XDROutPacketAddInt32(&call, -1); XDROutPacketAddInt32(&call, -1); XDROutPacketAddInt32(&call, -1); XDROutPacketAddInt32(&call, time(NULL)); XDROutPacketAddInt32(&call, 0); XDROutPacketAddInt32(&call, time(NULL)); XDROutPacketAddInt32(&call, 0); replyBuf = send_rpc_call(ns, &ns->nfsAddr, NFS_PROGRAM, NFS_VERSION, NFSPROC_MKDIR, &call); if (!replyBuf) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } XDRInPacketSetTo(&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); if (status != NFS_OK) { XDROutPacketDestroy(&call); return map_nfs_to_system_error(status); } XDRInPacketGetFixed(&reply, fhandle.opaque, NFS_FHSIZE); get_nfs_attr(&reply, &st); newNode=(fs_node *)malloc(sizeof(fs_node)); if (newNode == NULL) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_NO_MEMORY; } newNode->fhandle = fhandle; newNode->vnid = st.st_ino; newNode->mode = st.st_mode; insert_node(ns, newNode); XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return notify_entry_created(_volume->id, dir->vnid, name, st.st_ino); } static status_t fs_rename(fs_volume *_volume, fs_vnode *_olddir, const char *oldname, fs_vnode *_newdir, const char *newname) { struct stat st; nfs_fhandle fhandle; status_t result; struct XDROutPacket call; struct XDRInPacket reply; int32 status; uint8 *replyBuf; fs_nspace *ns; fs_node *olddir; fs_node *newdir; ns = _volume->private_volume; olddir = _olddir->private_node; newdir = _newdir->private_node; XDROutPacketInit(&call); XDRInPacketInit(&reply); if ((result = nfs_lookup(ns, &newdir->fhandle, newname, &fhandle, &st)) == B_OK) { if (S_ISREG(st.st_mode)) result = fs_unlink (_volume,_newdir,newname); else result = fs_rmdir (_volume,_newdir,newname); if (result < B_OK) { XDRInPacketDestroy (&reply); XDROutPacketDestroy (&call); return result; } } if ((result = nfs_lookup(ns, &olddir->fhandle, oldname, &fhandle, &st)) < B_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return result; } XDROutPacketAddFixed(&call, olddir->fhandle.opaque, NFS_FHSIZE); XDROutPacketAddString(&call, oldname); XDROutPacketAddFixed(&call, newdir->fhandle.opaque, NFS_FHSIZE); XDROutPacketAddString(&call, newname); replyBuf = send_rpc_call(ns, &ns->nfsAddr, NFS_PROGRAM, NFS_VERSION, NFSPROC_RENAME, &call); if (!replyBuf) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return EHOSTUNREACH; } XDRInPacketSetTo(&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); if (status != NFS_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return map_nfs_to_system_error(status); } XDRInPacketDestroy (&reply); XDROutPacketDestroy (&call); return notify_entry_moved(_volume->id, olddir->vnid, oldname, newdir->vnid, newname, st.st_ino); } static status_t fs_rmdir(fs_volume *_volume, fs_vnode *_dir, const char *name) { fs_nspace *ns; fs_node *dir; status_t result; fs_node *newNode; fs_node *dummy; struct XDROutPacket call; struct XDRInPacket reply; int32 status; uint8 *replyBuf; struct stat st; nfs_fhandle fhandle; ns = _volume->private_volume; dir = _dir->private_node; XDROutPacketInit(&call); XDRInPacketInit(&reply); if ((result = nfs_lookup(ns, &dir->fhandle, name, &fhandle, &st)) < B_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return result; } newNode = (fs_node *)malloc(sizeof(fs_node)); if (newNode == NULL) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_NO_MEMORY; } newNode->fhandle = fhandle; newNode->vnid = st.st_ino; newNode->mode = st.st_mode; insert_node(ns, newNode); if ((result = get_vnode(_volume, st.st_ino, (void **)&dummy)) < B_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return result; } if (!S_ISDIR(st.st_mode)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return ENOTDIR; } if ((result = remove_vnode(_volume, st.st_ino)) < B_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return result; } if ((result = put_vnode(_volume, st.st_ino)) < B_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return result; } XDROutPacketAddFixed (&call, dir->fhandle.opaque, NFS_FHSIZE); XDROutPacketAddString(&call, name); replyBuf = send_rpc_call(ns, &ns->nfsAddr, NFS_PROGRAM, NFS_VERSION, NFSPROC_RMDIR, &call); if (!replyBuf) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return EHOSTUNREACH; } XDRInPacketSetTo (&reply,replyBuf,0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); if (status != NFS_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return map_nfs_to_system_error(status); } XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return notify_entry_removed(_volume->id, dir->vnid, name, st.st_ino); } static status_t fs_readlink(fs_volume *_volume, fs_vnode *_node, char *buf, size_t *bufsize) { struct XDROutPacket call; uint8 *replyBuf; int32 status; size_t length; char data[NFS_MAXPATHLEN]; struct XDRInPacket reply; fs_nspace *ns; fs_node *node; ns = _volume->private_volume; node = _node->private_node; XDROutPacketInit(&call); XDRInPacketInit(&reply); XDROutPacketAddFixed(&call, node->fhandle.opaque, NFS_FHSIZE); replyBuf = send_rpc_call(ns, &ns->nfsAddr, NFS_PROGRAM, NFS_VERSION, NFSPROC_READLINK, &call); if (!replyBuf) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return EHOSTUNREACH; } XDRInPacketSetTo (&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); if (status != NFS_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy (&call); return map_nfs_to_system_error(status); } length = XDRInPacketGetDynamic(&reply, data); memcpy(buf, data, min_c(length, *bufsize)); *bufsize = length; XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_OK; } static status_t fs_symlink(fs_volume *_volume, fs_vnode *_dir, const char *name, const char *path, int mode) { fs_nspace *ns; fs_node *dir; nfs_fhandle fhandle; struct stat st; struct XDROutPacket call; struct XDRInPacket reply; status_t result; uint8 *replyBuf; int32 status; fs_node *newNode; ns = _volume->private_volume; dir = _dir->private_node; XDROutPacketInit(&call); XDRInPacketInit(&reply); result = nfs_lookup(ns, &dir->fhandle, name, &fhandle, &st); if (result == B_OK) { void *dummy; if ((result = get_vnode(_volume, st.st_ino, &dummy)) < B_OK) return result; XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return EEXIST; } else if (result != ENOENT) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return result; } XDROutPacketAddFixed(&call, dir->fhandle.opaque, NFS_FHSIZE); XDROutPacketAddString(&call, name); XDROutPacketAddString(&call, path); XDROutPacketAddInt32(&call, S_IFLNK); XDROutPacketAddInt32(&call, -1); XDROutPacketAddInt32(&call, -1); XDROutPacketAddInt32(&call, -1); XDROutPacketAddInt32(&call, time(NULL)); XDROutPacketAddInt32(&call, 0); XDROutPacketAddInt32(&call, time(NULL)); XDROutPacketAddInt32(&call, 0); replyBuf = send_rpc_call (ns, &ns->nfsAddr, NFS_PROGRAM, NFS_VERSION, NFSPROC_SYMLINK, &call); if (!replyBuf) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } XDRInPacketSetTo(&reply, replyBuf, 0); if (!is_successful_reply(&reply)) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_ERROR; } status = XDRInPacketGetInt32(&reply); /* if (status!=NFS_OK) return map_nfs_to_system_error(status); ignore status here, weird thing, nfsservers that is */ (void)status; result = nfs_lookup(ns, &dir->fhandle, name, &fhandle, &st); if (result < B_OK) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return result; } newNode = (fs_node *)malloc(sizeof(fs_node)); if (newNode == NULL) { XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return B_NO_MEMORY; } newNode->fhandle = fhandle; newNode->vnid = st.st_ino; insert_node(ns, newNode); result = notify_entry_created (_volume->id, dir->vnid, name, st.st_ino); XDRInPacketDestroy(&reply); XDROutPacketDestroy(&call); return result; } static status_t fs_access(fs_volume *_volume, fs_vnode *node, int mode) { (void) _volume; (void) node; (void) mode; /* XXX */ return B_OK; } static status_t nfs_std_ops(int32 op, ...) { switch (op) { case B_MODULE_INIT: return B_OK; case B_MODULE_UNINIT: return B_OK; default: return B_ERROR; } } fs_volume_ops sNFSVolumeOps = { &fs_unmount, &fs_rfsstat, &fs_wfsstat, NULL, // no sync! &fs_read_vnode, /* index directory & index operations */ NULL, // &fs_open_index_dir NULL, // &fs_close_index_dir NULL, // &fs_free_index_dir_cookie NULL, // &fs_read_index_dir NULL, // &fs_rewind_index_dir NULL, // &fs_create_index NULL, // &fs_remove_index NULL, // &fs_stat_index /* query operations */ NULL, // &fs_open_query, NULL, // &fs_close_query, NULL, // &fs_free_query_cookie, NULL, // &fs_read_query, NULL, // &fs_rewind_query, }; fs_vnode_ops sNFSVnodeOps = { /* vnode operations */ &fs_walk, NULL, // fs_get_vnode_name &fs_release_vnode, &fs_remove_vnode, /* VM file access */ NULL, // &fs_can_page NULL, // &fs_read_pages NULL, // &fs_write_pages NULL, // io() NULL, // cancel_io() NULL, // &fs_get_file_map, NULL, // &fs_ioctl NULL, // &fs_setflags, NULL, // &fs_select NULL, // &fs_deselect NULL, // &fs_fsync &fs_readlink, &fs_symlink, NULL, // &fs_link, &fs_unlink, &fs_rename, &fs_access, &fs_rstat, &fs_wstat, NULL, // fs_preallocate() /* file operations */ &fs_create, &fs_open, &fs_close, &fs_free_cookie, &fs_read, &fs_write, /* directory operations */ &fs_mkdir, &fs_rmdir, &fs_opendir, &fs_closedir, &fs_free_dircookie, &fs_readdir, &fs_rewinddir, /* attribute directory operations */ NULL, // &fs_open_attrdir, NULL, // &fs_close_attrdir, NULL, // &fs_free_attrdircookie, NULL, // &fs_read_attrdir, NULL, // &fs_rewind_attrdir, /* attribute operations */ NULL, // &fs_create_attr NULL, // &fs_open_attr_h, NULL, // &fs_close_attr_h, NULL, // &fs_free_attr_cookie_h, NULL, // &fs_read_attr_h, NULL, // &fs_write_attr_h, NULL, // &fs_read_attr_stat_h, NULL, // &fs_write_attr_stat NULL, // &fs_rename_attr NULL, // &fs_remove_attr }; file_system_module_info sNFSFileSystem = { { "file_systems/nfs" B_CURRENT_FS_API_VERSION, 0, nfs_std_ops, }, "nfs", // short name "Network File System v2", // pretty name B_DISK_SYSTEM_SUPPORTS_WRITING, // DDM flags // scanning NULL, // fs_identify_partition, NULL, // fs_scan_partition, NULL, // fs_free_identify_partition_cookie, NULL, // free_partition_content_cookie() &fs_mount, }; module_info *modules[] = { (module_info *)&sNFSFileSystem, NULL, };