int nwfs_writevnode(struct vnode *vp, struct uio *uiop, struct ucred *cred, int ioflag) { struct nwmount *nmp = VTONWFS(vp); struct nwnode *np = VTONW(vp); struct thread *td; /* struct vattr vattr;*/ int error = 0; if (vp->v_type != VREG) { kprintf("%s: vn types other than VREG unsupported !\n",__func__); return EIO; } NCPVNDEBUG("ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid); if (uiop->uio_offset < 0) return EINVAL; td = uiop->uio_td; if (ioflag & (IO_APPEND | IO_SYNC)) { if (np->n_flag & NMODIFIED) { nwfs_attr_cacheremove(vp); error = nwfs_vinvalbuf(vp, V_SAVE, 1); if (error) return (error); } if (ioflag & IO_APPEND) { /* We can relay only on local information about file size, * because until file is closed NetWare will not return * the correct size. */ #if 0 /* notyet */ nwfs_attr_cacheremove(vp); error = VOP_GETATTR(vp, &vattr); if (error) return (error); #endif uiop->uio_offset = np->n_size; } } if (uiop->uio_resid == 0) return 0; if (td->td_proc && uiop->uio_offset + uiop->uio_resid > td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) { lwpsignal(td->td_proc, td->td_lwp, SIGXFSZ); return (EFBIG); } error = ncp_write(NWFSTOCONN(nmp), &np->n_fh, uiop, cred); NCPVNDEBUG("after: ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid); if (!error) { if (uiop->uio_offset > np->n_size) { np->n_vattr.va_size = np->n_size = uiop->uio_offset; vnode_pager_setsize(vp, np->n_size); } } return (error); }
static int ncp_file_write(struct inode *inode, struct file *file, const char *buf, int count) { int bufsize, already_written; off_t pos; int errno; if (inode == NULL) { DPRINTK("ncp_file_write: inode = NULL\n"); return -EINVAL; } if (!ncp_conn_valid(NCP_SERVER(inode))) { return -EIO; } if (!S_ISREG(inode->i_mode)) { DPRINTK("ncp_file_write: write to non-file, mode %07o\n", inode->i_mode); return -EINVAL; } DPRINTK("ncp_file_write: enter %s\n", NCP_ISTRUCT(inode)->entryName); if (count <= 0) { return 0; } if ((errno = ncp_make_open(inode, O_RDWR)) != 0) { return errno; } pos = file->f_pos; if (file->f_flags & O_APPEND) { pos = inode->i_size; } bufsize = NCP_SERVER(inode)->buffer_size; already_written = 0; while (already_written < count) { int written_this_time; int to_write = min(bufsize - (pos % bufsize), count - already_written); if (ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_write, buf, &written_this_time) != 0) { return -EIO; } pos += written_this_time; buf += written_this_time; already_written += written_this_time; if (written_this_time < to_write) { break; } } inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_dirt = 1; file->f_pos = pos; if (pos > inode->i_size) { inode->i_size = pos; ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); } DPRINTK("ncp_file_write: exit %s\n", NCP_ISTRUCT(inode)->entryName); return already_written; }
static int ncp_notify_change(struct inode *inode, struct iattr *attr) { int result = 0; int info_mask; struct nw_modify_dos_info info; if (!ncp_conn_valid(NCP_SERVER(inode))) { return -EIO; } if ((result = inode_change_ok(inode, attr)) < 0) return result; if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != NCP_SERVER(inode)->m.uid))) return -EPERM; if (((attr->ia_valid & ATTR_GID) && (attr->ia_uid != NCP_SERVER(inode)->m.gid))) return -EPERM; if (((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)))) return -EPERM; info_mask = 0; memset(&info, 0, sizeof(info)); if ((attr->ia_valid & ATTR_CTIME) != 0) { info_mask |= (DM_CREATE_TIME|DM_CREATE_DATE); ncp_date_unix2dos(attr->ia_ctime, &(info.creationTime), &(info.creationDate)); } if ((attr->ia_valid & ATTR_MTIME) != 0) { info_mask |= (DM_MODIFY_TIME|DM_MODIFY_DATE); ncp_date_unix2dos(attr->ia_mtime, &(info.modifyTime), &(info.modifyDate)); } if ((attr->ia_valid & ATTR_ATIME) != 0) { __u16 dummy; info_mask |= (DM_LAST_ACCESS_DATE); ncp_date_unix2dos(attr->ia_ctime, &(dummy), &(info.lastAccessDate)); } if (info_mask != 0) { if ((result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode), NCP_ISTRUCT(inode), info_mask, &info)) != 0) { result = -EACCES; if (info_mask == (DM_CREATE_TIME|DM_CREATE_DATE)) { /* NetWare seems not to allow this. I do not know why. So, just tell the user everything went fine. This is a terrible hack, but I do not know how to do this correctly. */ result = 0; } } } if ((attr->ia_valid & ATTR_SIZE) != 0) { int written; DPRINTK("ncpfs: trying to change size of %s to %ld\n", NCP_ISTRUCT(inode)->entryName, attr->ia_size); if ((result = ncp_make_open(inode, O_RDWR)) < 0) { return -EACCES; } ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, attr->ia_size, 0, "", &written); /* According to ndir, the changes only take effect after closing the file */ ncp_close_file(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle); NCP_FINFO(inode)->opened = 0; result = 0; } ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); return result; }
/* * Vnode op for VM putpages. * possible bug: all IO done in sync mode * Note that vop_close always invalidate pages before close, so it's * not necessary to open vnode. * * nwfs_putpages(struct vnode *a_vp, vm_page_t *a_m, int a_count, * int a_sync, int *a_rtvals, vm_ooffset_t a_offset) */ int nwfs_putpages(struct vop_putpages_args *ap) { int error; struct thread *td = curthread; /* XXX */ struct vnode *vp = ap->a_vp; struct ucred *cred; #ifndef NWFS_RWCACHE KKASSERT(td->td_proc); cred = td->td_proc->p_ucred; /* XXX */ VOP_OPEN(vp, FWRITE, cred, NULL); error = vnode_pager_generic_putpages(ap->a_vp, ap->a_m, ap->a_count, ap->a_sync, ap->a_rtvals); VOP_CLOSE(vp, FWRITE, cred); return error; #else struct uio uio; struct iovec iov; vm_offset_t kva; struct buf *bp; int i, npages, count; int *rtvals; struct nwmount *nmp; struct nwnode *np; vm_page_t *pages; KKASSERT(td->td_proc); cred = td->td_proc->p_ucred; /* XXX */ /* VOP_OPEN(vp, FWRITE, cred, NULL);*/ np = VTONW(vp); nmp = VFSTONWFS(vp->v_mount); pages = ap->a_m; count = ap->a_count; rtvals = ap->a_rtvals; npages = btoc(count); for (i = 0; i < npages; i++) { rtvals[i] = VM_PAGER_AGAIN; } bp = getpbuf_kva(&nwfs_pbuf_freecnt); kva = (vm_offset_t) bp->b_data; pmap_qenter(kva, pages, npages); iov.iov_base = (caddr_t) kva; iov.iov_len = count; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = IDX_TO_OFF(pages[0]->pindex); uio.uio_resid = count; uio.uio_segflg = UIO_SYSSPACE; uio.uio_rw = UIO_WRITE; uio.uio_td = td; NCPVNDEBUG("ofs=%d,resid=%d\n",(int)uio.uio_offset, uio.uio_resid); error = ncp_write(NWFSTOCONN(nmp), &np->n_fh, &uio, cred); /* VOP_CLOSE(vp, FWRITE, cred);*/ NCPVNDEBUG("paged write done: %d\n", error); pmap_qremove(kva, npages); relpbuf(bp, &nwfs_pbuf_freecnt); if (!error) { int nwritten = round_page(count - uio.uio_resid) / PAGE_SIZE; for (i = 0; i < nwritten; i++) { rtvals[i] = VM_PAGER_OK; vm_page_undirty(pages[i]); } } return rtvals[0]; #endif /* NWFS_RWCACHE */ }
/* * Do an I/O operation to/from a cache block. */ int nwfs_doio(struct vnode *vp, struct bio *bio, struct ucred *cr, struct thread *td) { struct buf *bp = bio->bio_buf; struct uio *uiop; struct nwnode *np; struct nwmount *nmp; int error = 0; struct uio uio; struct iovec io; np = VTONW(vp); nmp = VFSTONWFS(vp->v_mount); uiop = &uio; uiop->uio_iov = &io; uiop->uio_iovcnt = 1; uiop->uio_segflg = UIO_SYSSPACE; uiop->uio_td = td; if (bp->b_cmd == BUF_CMD_READ) { io.iov_len = uiop->uio_resid = (size_t)bp->b_bcount; io.iov_base = bp->b_data; uiop->uio_rw = UIO_READ; switch (vp->v_type) { case VREG: uiop->uio_offset = bio->bio_offset; error = ncp_read(NWFSTOCONN(nmp), &np->n_fh, uiop, cr); if (error) break; if (uiop->uio_resid) { size_t left = uiop->uio_resid; size_t nread = bp->b_bcount - left; if (left > 0) bzero((char *)bp->b_data + nread, left); } break; /* case VDIR: nfsstats.readdir_bios++; uiop->uio_offset = bio->bio_offset; if (nmp->nm_flag & NFSMNT_RDIRPLUS) { error = nfs_readdirplusrpc(vp, uiop, cr); if (error == NFSERR_NOTSUPP) nmp->nm_flag &= ~NFSMNT_RDIRPLUS; } if ((nmp->nm_flag & NFSMNT_RDIRPLUS) == 0) error = nfs_readdirrpc(vp, uiop, cr); if (error == 0 && uiop->uio_resid == (size_t)bp->b_bcount) bp->b_flags |= B_INVAL; break; */ default: kprintf("nwfs_doio: type %x unexpected\n",vp->v_type); break; } if (error) { bp->b_flags |= B_ERROR; bp->b_error = error; } } else { /* write */ KKASSERT(bp->b_cmd == BUF_CMD_WRITE); if (bio->bio_offset + bp->b_dirtyend > np->n_size) bp->b_dirtyend = np->n_size - bio->bio_offset; if (bp->b_dirtyend > bp->b_dirtyoff) { io.iov_len = uiop->uio_resid = (size_t)(bp->b_dirtyend - bp->b_dirtyoff); uiop->uio_offset = bio->bio_offset + bp->b_dirtyoff; io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; uiop->uio_rw = UIO_WRITE; error = ncp_write(NWFSTOCONN(nmp), &np->n_fh, uiop, cr); /* * For an interrupted write, the buffer is still valid * and the write hasn't been pushed to the server yet, * so we can't set B_ERROR and report the interruption * by setting B_EINTR. For the async case, B_EINTR * is not relevant, so the rpc attempt is essentially * a noop. For the case of a V3 write rpc not being * committed to stable storage, the block is still * dirty and requires either a commit rpc or another * write rpc with iomode == NFSV3WRITE_FILESYNC before * the block is reused. This is indicated by setting * the B_DELWRI and B_NEEDCOMMIT flags. */ if (error == EINTR || (!error && (bp->b_flags & B_NEEDCOMMIT))) { crit_enter(); bp->b_flags &= ~(B_INVAL|B_NOCACHE); if ((bp->b_flags & B_PAGING) == 0) bdirty(bp); bp->b_flags |= B_EINTR; crit_exit(); } else { if (error) { bp->b_flags |= B_ERROR; bp->b_error /*= np->n_error */= error; /* np->n_flag |= NWRITEERR;*/ } bp->b_dirtyoff = bp->b_dirtyend = 0; } } else { bp->b_resid = 0; biodone(bio); return (0); } } bp->b_resid = (int)uiop->uio_resid; biodone(bio); return (error); }
int ncp_notify_change(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; int result = 0; int info_mask; struct nw_modify_dos_info info; result = -EIO; if (!ncp_conn_valid(NCP_SERVER(inode))) goto out; result = inode_change_ok(inode, attr); if (result < 0) goto out; result = -EPERM; if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != NCP_SERVER(inode)->m.uid))) goto out; if (((attr->ia_valid & ATTR_GID) && (attr->ia_uid != NCP_SERVER(inode)->m.gid))) goto out; if (((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)))) goto out; info_mask = 0; memset(&info, 0, sizeof(info)); #if 1 if ((attr->ia_valid & ATTR_MODE) != 0) { if (!S_ISREG(inode->i_mode)) { return -EPERM; } else { umode_t newmode; info_mask |= DM_ATTRIBUTES; newmode=attr->ia_mode; newmode &= NCP_SERVER(inode)->m.file_mode; if (newmode & 0222) /* any write bit set */ { info.attributes &= ~0x60001; } else { info.attributes |= 0x60001; } } } #endif if ((attr->ia_valid & ATTR_CTIME) != 0) { info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE); ncp_date_unix2dos(attr->ia_ctime, &(info.creationTime), &(info.creationDate)); info.creationTime = le16_to_cpu(info.creationTime); info.creationDate = le16_to_cpu(info.creationDate); } if ((attr->ia_valid & ATTR_MTIME) != 0) { info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE); ncp_date_unix2dos(attr->ia_mtime, &(info.modifyTime), &(info.modifyDate)); info.modifyTime = le16_to_cpu(info.modifyTime); info.modifyDate = le16_to_cpu(info.modifyDate); } if ((attr->ia_valid & ATTR_ATIME) != 0) { __u16 dummy; info_mask |= (DM_LAST_ACCESS_DATE); ncp_date_unix2dos(attr->ia_ctime, &(dummy), &(info.lastAccessDate)); info.lastAccessDate = le16_to_cpu(info.lastAccessDate); } if (info_mask != 0) { result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode), inode, info_mask, &info); if (result != 0) { result = -EACCES; if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) { /* NetWare seems not to allow this. I do not know why. So, just tell the user everything went fine. This is a terrible hack, but I do not know how to do this correctly. */ result = 0; } } } if ((attr->ia_valid & ATTR_SIZE) != 0) { int written; DPRINTK(KERN_DEBUG "ncpfs: trying to change size to %ld\n", attr->ia_size); if ((result = ncp_make_open(inode, O_RDWR)) < 0) { return -EACCES; } ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, attr->ia_size, 0, "", &written); /* According to ndir, the changes only take effect after closing the file */ result = ncp_make_closed(inode); } ncp_invalid_dir_cache(dentry->d_parent->d_inode); out: return result; }
static int ncp_conn_handler(struct proc *p, struct sncp_request_args *uap, struct ncp_conn *conn, struct ncp_handle *hp) { int error=0, rqsize, subfn; struct ucred *cred; char *pdata; cred = p->p_ucred; error = copyin(&uap->ncpbuf->rqsize, &rqsize, sizeof(int)); if (error) return(error); error = 0; pdata = uap->ncpbuf->packet; subfn = *(pdata++) & 0xff; rqsize--; switch (subfn) { case NCP_CONN_READ: case NCP_CONN_WRITE: { struct ncp_rw rwrq; struct uio auio; struct iovec iov; if (rqsize != sizeof(rwrq)) return (EBADRPC); error = copyin(pdata,&rwrq,rqsize); if (error) return (error); iov.iov_base = rwrq.nrw_base; iov.iov_len = rwrq.nrw_cnt; auio.uio_iov = &iov; auio.uio_iovcnt = 1; auio.uio_offset = rwrq.nrw_offset; auio.uio_resid = rwrq.nrw_cnt; auio.uio_segflg = UIO_USERSPACE; auio.uio_rw = (subfn == NCP_CONN_READ) ? UIO_READ : UIO_WRITE; auio.uio_procp = p; error = ncp_conn_lock(conn,p,cred,NCPM_EXECUTE); if (error) return(error); if (subfn == NCP_CONN_READ) error = ncp_read(conn, &rwrq.nrw_fh, &auio, cred); else error = ncp_write(conn, &rwrq.nrw_fh, &auio, cred); rwrq.nrw_cnt -= auio.uio_resid; ncp_conn_unlock(conn,p); p->p_retval[0] = rwrq.nrw_cnt; break; } /* case int_read/write */ case NCP_CONN_SETFLAGS: { u_int16_t mask, flags; error = copyin(pdata,&mask, sizeof(mask)); if (error) return error; pdata += sizeof(mask); error = copyin(pdata,&flags,sizeof(flags)); if (error) return error; error = ncp_conn_lock(conn,p,cred,NCPM_WRITE); if (error) return error; if (mask & NCPFL_PERMANENT) { conn->flags &= ~NCPFL_PERMANENT; conn->flags |= (flags & NCPFL_PERMANENT); } if (mask & NCPFL_PRIMARY) { error = ncp_conn_setprimary(conn, flags & NCPFL_PRIMARY); if (error) { ncp_conn_unlock(conn,p); break; } } ncp_conn_unlock(conn,p); break; } case NCP_CONN_LOGIN: { struct ncp_conn_login la; if (rqsize != sizeof(la)) return (EBADRPC); if ((error = copyin(pdata,&la,rqsize)) != 0) break; error = ncp_conn_lock(conn, p, cred, NCPM_EXECUTE | NCPM_WRITE); if (error) return error; error = ncp_login(conn, la.username, la.objtype, la.password, p, p->p_ucred); ncp_conn_unlock(conn, p); p->p_retval[0] = error; break; } case NCP_CONN_GETINFO: { struct ncp_conn_stat ncs; int len = sizeof(ncs); error = ncp_conn_lock(conn, p, p->p_ucred, NCPM_READ); if (error) return error; ncp_conn_getinfo(conn, &ncs); copyout(&len, &uap->ncpbuf->rpsize, sizeof(int)); error = copyout(&ncs, &uap->ncpbuf->packet, len); ncp_conn_unlock(conn, p); break; } case NCP_CONN_GETUSER: { int len; error = ncp_conn_lock(conn, p, p->p_ucred, NCPM_READ); if (error) return error; len = (conn->li.user) ? strlen(conn->li.user) + 1 : 0; copyout(&len, &uap->ncpbuf->rpsize, sizeof(int)); if (len) { error = copyout(conn->li.user, &uap->ncpbuf->packet, len); } ncp_conn_unlock(conn, p); break; } case NCP_CONN_CONN2REF: { int len = sizeof(int); error = ncp_conn_lock(conn, p, p->p_ucred, NCPM_READ); if (error) return error; copyout(&len, &uap->ncpbuf->rpsize, sizeof(int)); if (len) { error = copyout(&conn->nc_id, &uap->ncpbuf->packet, len); } ncp_conn_unlock(conn, p); break; } case NCP_CONN_FRAG: { struct ncp_conn_frag nf; if (rqsize != sizeof(nf)) return (EBADRPC); if ((error = copyin(pdata, &nf, rqsize)) != 0) break; error = ncp_conn_lock(conn, p, cred, NCPM_EXECUTE); if (error) return error; error = ncp_conn_frag_rq(conn, p, &nf); ncp_conn_unlock(conn, p); copyout(&nf, &pdata, sizeof(nf)); p->p_retval[0] = error; break; } case NCP_CONN_DUP: { struct ncp_handle *newhp; int len = sizeof(NWCONN_HANDLE); error = ncp_conn_lock(conn, p, cred, NCPM_READ); if (error) break; copyout(&len, &uap->ncpbuf->rpsize, len); error = ncp_conn_gethandle(conn, p, &newhp); if (!error) error = copyout(&newhp->nh_id, uap->ncpbuf->packet, len); ncp_conn_unlock(conn,p); break; } case NCP_CONN_CONNCLOSE: { error = ncp_conn_lock(conn, p, cred, NCPM_EXECUTE); if (error) break; ncp_conn_puthandle(hp, p, 0); error = ncp_disconnect(conn); if (error) ncp_conn_unlock(conn, p); break; } default: error = EOPNOTSUPP; } return error; }