/* * Copyright (c) 2011-2012 The DragonFly Project. All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Matthew Dillon * by Venkatesh Srinivas * * 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. * 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. * 3. Neither the name of The DragonFly Project nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific, prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``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 * COPYRIGHT HOLDERS OR CONTRIBUTORS 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. */ /* * Ioctl Functions. * * WARNING! The ioctl functions which manipulate the connection state need * to be able to run without deadlock on the volume's chain lock. * Most of these functions use a separate lock. */ #include "hammer2.h" static int hammer2_ioctl_version_get(hammer2_inode_t *ip, void *data); static int hammer2_ioctl_remote_get(hammer2_inode_t *ip, void *data); static int hammer2_ioctl_remote_add(hammer2_inode_t *ip, void *data); static int hammer2_ioctl_remote_del(hammer2_inode_t *ip, void *data); static int hammer2_ioctl_remote_rep(hammer2_inode_t *ip, void *data); static int hammer2_ioctl_socket_get(hammer2_inode_t *ip, void *data); static int hammer2_ioctl_socket_set(hammer2_inode_t *ip, void *data); static int hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data); static int hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data); static int hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data); int hammer2_ioctl(hammer2_inode_t *ip, u_long com, void *data, int fflag, struct ucred *cred) { int error; /* * Standard root cred checks, will be selectively ignored below * for ioctls that do not require root creds. */ error = priv_check_cred(cred, PRIV_HAMMER_IOCTL, 0); switch(com) { case HAMMER2IOC_VERSION_GET: error = hammer2_ioctl_version_get(ip, data); break; case HAMMER2IOC_REMOTE_GET: if (error == 0) error = hammer2_ioctl_remote_get(ip, data); break; case HAMMER2IOC_REMOTE_ADD: if (error == 0) error = hammer2_ioctl_remote_add(ip, data); break; case HAMMER2IOC_REMOTE_DEL: if (error == 0) error = hammer2_ioctl_remote_del(ip, data); break; case HAMMER2IOC_REMOTE_REP: if (error == 0) error = hammer2_ioctl_remote_rep(ip, data); break; case HAMMER2IOC_SOCKET_GET: if (error == 0) error = hammer2_ioctl_socket_get(ip, data); break; case HAMMER2IOC_SOCKET_SET: if (error == 0) error = hammer2_ioctl_socket_set(ip, data); break; case HAMMER2IOC_PFS_GET: if (error == 0) error = hammer2_ioctl_pfs_get(ip, data); break; case HAMMER2IOC_PFS_CREATE: if (error == 0) error = hammer2_ioctl_pfs_create(ip, data); break; case HAMMER2IOC_PFS_DELETE: if (error == 0) error = hammer2_ioctl_pfs_delete(ip, data); break; default: error = EOPNOTSUPP; break; } return (error); } /* * Retrieve version and basic info */ static int hammer2_ioctl_version_get(hammer2_inode_t *ip, void *data) { hammer2_mount_t *hmp = ip->hmp; hammer2_ioc_version_t *version = data; version->version = hmp->voldata.version; return 0; } /* * Retrieve information about a remote */ static int hammer2_ioctl_remote_get(hammer2_inode_t *ip, void *data) { hammer2_mount_t *hmp = ip->hmp; hammer2_ioc_remote_t *remote = data; int copyid = remote->copyid; if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT) return (EINVAL); hammer2_voldata_lock(hmp); remote->copy1 = hmp->voldata.copyinfo[copyid]; hammer2_voldata_unlock(hmp); /* * Adjust nextid (GET only) */ while (++copyid < HAMMER2_COPYID_COUNT && hmp->voldata.copyinfo[copyid].copyid == 0) { ++copyid; } if (copyid == HAMMER2_COPYID_COUNT) remote->nextid = -1; else remote->nextid = copyid; return(0); } /* * Add new remote entry */ static int hammer2_ioctl_remote_add(hammer2_inode_t *ip, void *data) { hammer2_mount_t *hmp = ip->hmp; hammer2_ioc_remote_t *remote = data; int copyid = remote->copyid; int error = 0; if (copyid >= HAMMER2_COPYID_COUNT) return (EINVAL); hammer2_voldata_lock(hmp); if (copyid < 0) { for (copyid = 1; copyid < HAMMER2_COPYID_COUNT; ++copyid) { if (hmp->voldata.copyinfo[copyid].copyid == 0) break; } if (copyid == HAMMER2_COPYID_COUNT) { error = ENOSPC; goto failed; } } hammer2_modify_volume(hmp); kprintf("copyid %d\n", copyid); remote->copy1.copyid = copyid; hmp->voldata.copyinfo[copyid] = remote->copy1; failed: hammer2_voldata_unlock(hmp); return (error); } /* * Delete existing remote entry */ static int hammer2_ioctl_remote_del(hammer2_inode_t *ip, void *data) { hammer2_mount_t *hmp = ip->hmp; hammer2_ioc_remote_t *remote = data; int copyid = remote->copyid; int error = 0; if (copyid >= HAMMER2_COPYID_COUNT) return (EINVAL); remote->copy1.path[sizeof(remote->copy1.path) - 1] = 0; hammer2_voldata_lock(hmp); if (copyid < 0) { for (copyid = 1; copyid < HAMMER2_COPYID_COUNT; ++copyid) { if (hmp->voldata.copyinfo[copyid].copyid == 0) continue; if (strcmp(remote->copy1.path, hmp->voldata.copyinfo[copyid].path) == 0) { break; } } if (copyid == HAMMER2_COPYID_COUNT) { error = ENOENT; goto failed; } } hammer2_modify_volume(hmp); hmp->voldata.copyinfo[copyid].copyid = 0; failed: hammer2_voldata_unlock(hmp); return (error); } /* * Replace existing remote entry */ static int hammer2_ioctl_remote_rep(hammer2_inode_t *ip, void *data) { hammer2_mount_t *hmp = ip->hmp; hammer2_ioc_remote_t *remote = data; int copyid = remote->copyid; if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT) return (EINVAL); hammer2_voldata_lock(hmp); hammer2_voldata_unlock(hmp); return(0); } /* * Retrieve communications socket */ static int hammer2_ioctl_socket_get(hammer2_inode_t *ip, void *data) { return (EOPNOTSUPP); } /* * Set communications socket for connection */ static int hammer2_ioctl_socket_set(hammer2_inode_t *ip, void *data) { hammer2_mount_t *hmp = ip->hmp; hammer2_ioc_remote_t *remote = data; int copyid = remote->copyid; if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT) return (EINVAL); hammer2_voldata_lock(hmp); hammer2_voldata_unlock(hmp); return(0); } /* * Used to scan PFSs, which are directories under the super-root. */ static int hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data) { hammer2_mount_t *hmp = ip->hmp; hammer2_ioc_pfs_t *pfs = data; hammer2_chain_t *parent; hammer2_chain_t *chain; hammer2_inode_t *xip; int error = 0; parent = hmp->schain; error = hammer2_chain_lock(hmp, parent, HAMMER2_RESOLVE_ALWAYS); if (error) goto done; /* * Search for the first key or specific key. Remember that keys * can be returned in any order. */ if (pfs->name_key == 0) { chain = hammer2_chain_lookup(hmp, &parent, 0, (hammer2_key_t)-1, 0); } else { chain = hammer2_chain_lookup(hmp, &parent, pfs->name_key, pfs->name_key, 0); } while (chain && chain->bref.type != HAMMER2_BREF_TYPE_INODE) { chain = hammer2_chain_next(hmp, &parent, chain, 0, (hammer2_key_t)-1, 0); } if (chain) { /* * Load the data being returned by the ioctl. */ xip = chain->u.ip; pfs->name_key = xip->ip_data.name_key; pfs->pfs_type = xip->ip_data.pfs_type; pfs->pfs_id = xip->ip_data.pfs_id; pfs->pfs_fsid = xip->ip_data.pfs_fsid; KKASSERT(xip->ip_data.name_len < sizeof(pfs->name)); bcopy(xip->ip_data.filename, pfs->name, xip->ip_data.name_len); pfs->name[xip->ip_data.name_len] = 0; /* * Calculate the next field */ do { chain = hammer2_chain_next(hmp, &parent, chain, 0, (hammer2_key_t)-1, 0); } while (chain && chain->bref.type != HAMMER2_BREF_TYPE_INODE); if (chain) { pfs->name_next = chain->u.ip->ip_data.name_key; hammer2_chain_unlock(hmp, chain); } else { pfs->name_next = (hammer2_key_t)-1; } } else { pfs->name_next = (hammer2_key_t)-1; error = ENOENT; } done: hammer2_chain_unlock(hmp, parent); return (error); } /* * Create a new PFS under the super-root */ static int hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data) { hammer2_mount_t *hmp = ip->hmp; hammer2_ioc_pfs_t *pfs = data; hammer2_inode_t *nip = NULL; int error; pfs->name[sizeof(pfs->name) - 1] = 0; /* ensure 0-termination */ error = hammer2_inode_create(hmp->schain->u.ip, NULL, NULL, pfs->name, strlen(pfs->name), &nip); if (error == 0) { hammer2_chain_modify(hmp, &nip->chain, 0); nip->ip_data.pfs_type = pfs->pfs_type; nip->ip_data.pfs_id = pfs->pfs_id; nip->ip_data.pfs_fsid = pfs->pfs_fsid; hammer2_chain_unlock(hmp, &nip->chain); } return (error); } /* * Destroy an existing PFS under the super-root */ static int hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data) { hammer2_mount_t *hmp = ip->hmp; hammer2_ioc_pfs_t *pfs = data; int error; error = hammer2_unlink_file(hmp->schain->u.ip, pfs->name, strlen(pfs->name), 0, NULL); return (error); }