blob: 50a05753cd8c035a607af64b3fe052ef3ea31b09 [file] [log] [blame] [raw]
/*
* 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);