| /* |
| * Copyright 2015-2019 Rivoreo |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer |
| * in this position and unchanged. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| #include <sys/cdefs.h> |
| #include <sys/param.h> |
| #include <sys/systm.h> |
| #include <sys/conf.h> |
| #include <sys/uio.h> |
| #include <sys/kernel.h> |
| #include <sys/malloc.h> |
| #include <sys/module.h> |
| #include <sys/signalvar.h> |
| #include <sys/filio.h> |
| #include <sys/namei.h> |
| #include <sys/mount.h> |
| #include <sys/vnode.h> |
| #include <sys/fcntl.h> |
| #include <sys/buf.h> |
| #include <sys/capsicum.h> |
| |
| // XXX: We can only hack one file system at a time |
| #include <ufs/ufs/quota.h> |
| #include <ufs/ufs/inode.h> |
| #include <ufs/ufs/extattr.h> |
| #include <ufs/ufs/ufsmount.h> |
| #include <ufs/ufs/ufs_extern.h> |
| #define UFS_DIP_SET DIP_SET |
| #define UFS_VTOI VTOI |
| #define ufs_inode inode |
| |
| #define strtoull strtouq |
| |
| #if 0 |
| static char *_strchr(const char *p, int ch) { |
| union { |
| const char *cp; |
| char *p; |
| } u; |
| |
| u.cp = p; |
| while(1) { |
| if (*u.p == ch) |
| return(u.p); |
| if (*u.p == '\0') |
| return(NULL); |
| u.p++; |
| } |
| } |
| #define strchr _strchr |
| #endif |
| |
| /* For use with destroy_dev(9). */ |
| static struct cdev *linki_dev; |
| |
| static d_write_t linki_write; |
| //static d_ioctl_t linki_ioctl; |
| static d_read_t linki_read; |
| |
| static struct cdevsw linki_cdevsw = { |
| .d_version = D_VERSION, |
| .d_read = linki_read, |
| .d_write = linki_write, |
| //.d_ioctl = linki_ioctl, |
| .d_name = "linki", |
| //.d_flags = D_MMAP_ANON, |
| }; |
| |
| static int linki(ino_t ino, const char *new_path) { |
| printf("linki: function: linki(%llu, %p<%s>)\n", (unsigned long long int)ino, new_path, new_path); |
| enum uio_seg segflg = UIO_SYSSPACE; |
| struct thread *td = curthread; |
| struct nameidata nd; |
| bwillwrite(); |
| #ifdef NDINIT_ATRIGHTS |
| NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE2 | NOCACHE, segflg, new_path, AT_FDCWD, |
| &cap_linkat_target_rights, td); |
| #else |
| NDINIT(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE2 | NOCACHE, segflg, new_path, td); |
| #endif |
| int e = namei(&nd); |
| uprintf("linki: debug: namei returned %d\n", e); |
| if(e) return e; |
| if(nd.ni_vp) { |
| NDFREE(&nd, NDF_ONLY_PNBUF); |
| if(nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); |
| vrele(nd.ni_vp); |
| return EEXIST; |
| } |
| struct vnode *vp; |
| e = VFS_VGET(nd.ni_dvp->v_mount, ino, LK_EXCLUSIVE, &vp); |
| uprintf("linki: debug: VFS_VGET returned %d\n", e); |
| if(e) { |
| NDFREE(&nd, NDF_ONLY_PNBUF); |
| vput(nd.ni_dvp); |
| return e; |
| } |
| struct mount *mp; |
| e = vn_start_write(vp, &mp, V_WAIT); |
| uprintf("linki: debug: vn_start_write returned %d\n", e); |
| if(e) { |
| NDFREE(&nd, NDF_ONLY_PNBUF); |
| vput(nd.ni_dvp); |
| vput(vp); |
| return e; |
| } |
| int ufs_hack = 0; |
| struct ufs_inode *inode; |
| if(strncmp(mp->mnt_stat.f_fstypename, "ufs", MFSNAMELEN) == 0) { |
| inode = UFS_VTOI(vp); |
| if(inode->i_effnlink == 0) { |
| inode->i_effnlink++; |
| inode->i_nlink++; |
| ufs_hack = 1; |
| } |
| } |
| e = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); |
| uprintf("linki: debug: VOP_LINK returned %d\n", e); |
| if(ufs_hack) { |
| inode->i_effnlink--; |
| inode->i_nlink--; |
| UFS_DIP_SET(inode, i_nlink, inode->i_nlink); |
| if(e == 0) { |
| if(DOINGSOFTDEP(vp)) softdep_setup_link(UFS_VTOI(nd.ni_dvp), inode); |
| e = UFS_UPDATE(vp, !DOINGSOFTDEP(vp) && !DOINGASYNC(vp)); |
| } |
| } |
| VOP_UNLOCK(vp, 0); |
| vput(nd.ni_dvp); |
| vn_finished_write(mp); |
| NDFREE(&nd, NDF_ONLY_PNBUF); |
| vrele(vp); |
| return e; |
| } |
| |
| static int linki_write(struct cdev *dev __unused, struct uio *uio, int flags __unused) { |
| char *buffer = malloc(1024, M_TEMP, M_WAITOK); |
| int len = MIN(uio->uio_resid, 1024); |
| int error = uiomove(buffer, len, uio); |
| if(error) { |
| free(buffer, M_TEMP); |
| return error; |
| } |
| uprintf("len = %d\n", len); |
| //uio->uio_resid = 0; |
| char *inoden = buffer; |
| size_t inoden_len = strnlen(inoden, len); |
| if(inoden_len + 2 > len) { |
| free(buffer, M_TEMP); |
| return EINVAL; |
| } |
| //uio->uio_resid = 0; |
| char *new_name = inoden + inoden_len + 1; |
| buffer[len] = 0; |
| uprintf("inoden = \"%s\", new_name = \"%s\"\n", inoden, new_name); |
| error = linki(strtoull(inoden, NULL, 0), new_name); |
| free(buffer, M_TEMP); |
| return error; |
| } |
| |
| static int linki_read(struct cdev *dev __unused, struct uio *uio, int flags __unused) { |
| int error; |
| |
| char usage[] = "TODO\n"; |
| size_t usage_len = 5; |
| KASSERT(uio->uio_rw == UIO_READ, |
| ("Can't be in %s for write", __func__)); |
| if(uio->uio_offset >= usage_len) return 0; |
| error = uiomove(usage + uio->uio_offset, MIN(uio->uio_resid, usage_len), uio); |
| uio->uio_resid = 0; |
| |
| return (error); |
| } |
| |
| static int linki_modevent(module_t mod __unused, int type, void *data __unused) |
| { |
| switch(type) { |
| case MOD_LOAD: |
| linki_dev = make_dev(&linki_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "linki"); |
| break; |
| |
| case MOD_UNLOAD: |
| destroy_dev(linki_dev); |
| break; |
| |
| case MOD_SHUTDOWN: |
| break; |
| |
| default: |
| return (EOPNOTSUPP); |
| } |
| |
| return (0); |
| } |
| |
| DEV_MODULE(linki, linki_modevent, NULL); |
| MODULE_VERSION(linki, 1); |