blob: 966ed74d5ba25c5ecb88494eae36086e283175b3 [file] [log] [blame]
/*
* Copyright (C) 2017 Oracle. All Rights Reserved.
*
* Author: Darrick J. Wong <darrick.wong@oracle.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "platform_defs.h"
#include "xfs.h"
#include "xfs_arch.h"
#include "list.h"
#include "libfrog/paths.h"
#include "handle.h"
#include "parent.h"
/* Allocate a buffer large enough for some parent pointer records. */
static inline struct xfs_pptr_info *
xfs_pptr_alloc(
size_t bufsize)
{
struct xfs_pptr_info *pi;
pi = calloc(bufsize, 1);
if (!pi)
return NULL;
pi->pi_ptrs_size = bufsize;
return pi;
}
/*
* Walk all parents of the given file handle.
* If pino is set, print only the parent pointer
* of that inode. If pname is set, print only the
* parent pointer of that filename
*/
static int
handle_walk_parents(
int fd,
struct xfs_handle *handle,
uint64_t pino,
char *pname,
walk_pptr_fn fn,
void *arg,
int flags)
{
struct xfs_pptr_info *pi;
struct xfs_parent_ptr *p;
unsigned int i;
ssize_t ret = -1;
pi = xfs_pptr_alloc(XFS_XATTR_LIST_MAX);
if (!pi)
return -1;
if (handle) {
memcpy(&pi->pi_handle, handle, sizeof(struct xfs_handle));
pi->pi_flags = XFS_PPTR_IFLAG_HANDLE;
}
ret = ioctl(fd, XFS_IOC_GETPARENTS, pi);
while (!ret) {
if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT) {
ret = fn(pi, NULL, arg, flags);
break;
}
for (i = 0; i < pi->pi_count; i++) {
p = xfs_ppinfo_to_pp(pi, i);
if ((pino != 0) && (pino != p->xpp_ino))
continue;
if ((pname != NULL) && (strcmp(pname,
(char *)p->xpp_name) != 0))
continue;
ret = fn(pi, p, arg, flags);
if (ret)
goto out_pi;
}
if (pi->pi_flags & XFS_PPTR_OFLAG_DONE)
break;
ret = ioctl(fd, XFS_IOC_GETPARENTS, pi);
}
out_pi:
free(pi);
return ret;
}
/* Walk all parent pointers of this handle. */
int
handle_walk_pptrs(
void *hanp,
size_t hlen,
uint64_t pino,
char *pname,
walk_pptr_fn fn,
void *arg,
int flags)
{
char *mntpt;
int fd;
if (hlen != sizeof(struct xfs_handle)) {
errno = EINVAL;
return -1;
}
fd = handle_to_fsfd(hanp, &mntpt);
if (fd < 0)
return -1;
return handle_walk_parents(fd, hanp, pino, pname, fn, arg, flags);
}
/* Walk all parent pointers of this fd. */
int
fd_walk_pptrs(
int fd,
uint64_t pino,
char *pname,
walk_pptr_fn fn,
void *arg,
int flags)
{
return handle_walk_parents(fd, NULL, pino, pname, fn, arg, flags);
}
struct walk_ppaths_info {
walk_ppath_fn fn;
void *arg;
char *mntpt;
struct path_list *path;
int fd;
};
struct walk_ppath_level_info {
struct xfs_handle newhandle;
struct path_component *pc;
struct walk_ppaths_info *wpi;
};
static int handle_walk_parent_paths(struct walk_ppaths_info *wpi,
struct xfs_handle *handle, uint64_t pino, char *pname,
int flags);
static int
handle_walk_parent_path_ptr(
struct xfs_pptr_info *pi,
struct xfs_parent_ptr *p,
void *arg,
int flags)
{
struct walk_ppath_level_info *wpli = arg;
struct walk_ppaths_info *wpi = wpli->wpi;
unsigned int i;
int ret = 0;
if (pi->pi_flags & XFS_PPTR_OFLAG_ROOT)
return wpi->fn(wpi->mntpt, wpi->path, wpi->arg);
for (i = 0; i < pi->pi_count; i++) {
p = xfs_ppinfo_to_pp(pi, i);
ret = path_component_change(wpli->pc, p->xpp_name,
strlen((char *)p->xpp_name));
if (ret)
break;
wpli->newhandle.ha_fid.fid_ino = p->xpp_ino;
wpli->newhandle.ha_fid.fid_gen = p->xpp_gen;
path_list_add_parent_component(wpi->path, wpli->pc);
ret = handle_walk_parent_paths(wpi, &wpli->newhandle, 0, NULL, 0);
path_list_del_component(wpi->path, wpli->pc);
if (ret)
break;
}
return ret;
}
/*
* Recursively walk all parents of the given file handle; if we hit the
* fs root then we call the associated function with the constructed path.
*/
static int
handle_walk_parent_paths(
struct walk_ppaths_info *wpi,
struct xfs_handle *handle,
uint64_t pino,
char *pname,
int flags)
{
struct walk_ppath_level_info *wpli;
int ret;
wpli = malloc(sizeof(struct walk_ppath_level_info));
if (!wpli)
return -1;
wpli->pc = path_component_init("");
if (!wpli->pc) {
free(wpli);
return -1;
}
wpli->wpi = wpi;
memcpy(&wpli->newhandle, handle, sizeof(struct xfs_handle));
ret = handle_walk_parents(wpi->fd, handle, pino, pname,
handle_walk_parent_path_ptr, wpli, flags);
path_component_free(wpli->pc);
free(wpli);
return ret;
}
/*
* Call the given function on all known paths from the vfs root to the inode
* described in the handle.
*/
int
handle_walk_ppaths(
void *hanp,
size_t hlen,
uint64_t pino,
char *pname,
walk_ppath_fn fn,
void *arg,
int flags)
{
struct walk_ppaths_info wpi;
ssize_t ret;
if (hlen != sizeof(struct xfs_handle)) {
errno = EINVAL;
return -1;
}
wpi.fd = handle_to_fsfd(hanp, &wpi.mntpt);
if (wpi.fd < 0)
return -1;
wpi.path = path_list_init();
if (!wpi.path)
return -1;
wpi.fn = fn;
wpi.arg = arg;
ret = handle_walk_parent_paths(&wpi, hanp, pino, pname, flags);
path_list_free(wpi.path);
return ret;
}
/*
* Call the given function on all known paths from the vfs root to the inode
* referred to by the file description.
*/
int
fd_walk_ppaths(
int fd,
uint64_t pino,
char *pname,
walk_ppath_fn fn,
void *arg,
int flags)
{
struct walk_ppaths_info wpi;
void *hanp;
size_t hlen;
int fsfd;
int ret;
ret = fd_to_handle(fd, &hanp, &hlen);
if (ret)
return ret;
fsfd = handle_to_fsfd(hanp, &wpi.mntpt);
if (fsfd < 0)
return -1;
wpi.fd = fd;
wpi.path = path_list_init();
if (!wpi.path)
return -1;
wpi.fn = fn;
wpi.arg = arg;
ret = handle_walk_parent_paths(&wpi, hanp, pino, pname, flags);
path_list_free(wpi.path);
return ret;
}
struct path_walk_info {
char *buf;
size_t len;
};
/* Helper that stringifies the first full path that we find. */
static int
handle_to_path_walk(
const char *mntpt,
struct path_list *path,
void *arg)
{
struct path_walk_info *pwi = arg;
int ret;
ret = snprintf(pwi->buf, pwi->len, "%s", mntpt);
if (ret != strlen(mntpt)) {
errno = ENOMEM;
return -1;
}
ret = path_list_to_string(path, pwi->buf + ret, pwi->len - ret);
if (ret < 0)
return ret;
return WALK_PPATHS_ABORT;
}
/* Return any eligible path to this file handle. */
int
handle_to_path(
void *hanp,
size_t hlen,
char *path,
size_t pathlen)
{
struct path_walk_info pwi;
pwi.buf = path;
pwi.len = pathlen;
return handle_walk_ppaths(hanp, hlen, 0, NULL, handle_to_path_walk,
&pwi, 0);
}
/* Return any eligible path to this file description. */
int
fd_to_path(
int fd,
char *path,
size_t pathlen)
{
struct path_walk_info pwi;
pwi.buf = path;
pwi.len = pathlen;
return fd_walk_ppaths(fd, 0, NULL, handle_to_path_walk, &pwi, 0);
}