/* * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to * a file or fs handle. * * XFS_IOC_PATH_TO_FSHANDLE * returns fs handle for a mount point or path within that mount point * XFS_IOC_FD_TO_HANDLE * returns full handle for a FD opened in user space * XFS_IOC_PATH_TO_HANDLE * returns full handle for a path
*/ int
xfs_find_handle( unsignedint cmd,
xfs_fsop_handlereq_t *hreq)
{ int hsize;
xfs_handle_t handle; struct inode *inode; struct path path; int error; struct xfs_inode *ip;
if (cmd == XFS_IOC_FD_TO_HANDLE) { CLASS(fd, f)(hreq->fd);
if (fd_empty(f)) return -EBADF;
path = fd_file(f)->f_path;
path_get(&path);
} else {
error = user_path_at(AT_FDCWD, hreq->path, 0, &path); if (error) return error;
}
inode = d_inode(path.dentry);
ip = XFS_I(inode);
/* * We can only generate handles for inodes residing on a XFS filesystem, * and only for regular files, directories or symbolic links.
*/
error = -EINVAL; if (inode->i_sb->s_magic != XFS_SB_MAGIC) goto out_put;
/* * No need to do permission checks on the various pathname components * as the handle operations are privileged.
*/ STATICint
xfs_handle_acceptable( void *context, struct dentry *dentry)
{ return 1;
}
/* Convert handle already copied to kernel space into a dentry. */ staticstruct dentry *
xfs_khandle_to_dentry( struct file *file, struct xfs_handle *handle)
{ struct xfs_fid64 fid = {
.ino = handle->ha_fid.fid_ino,
.gen = handle->ha_fid.fid_gen,
};
/* * Only allow handle opens under a directory.
*/ if (!S_ISDIR(file_inode(file)->i_mode)) return ERR_PTR(-ENOTDIR);
if (handle->ha_fid.fid_len != xfs_filehandle_fid_len()) return ERR_PTR(-EINVAL);
/* * Format an attribute and copy it out to the user's buffer. * Take care to check values and protect against them changing later, * we may be reading them directly out of a user buffer.
*/ staticvoid
xfs_ioc_attr_put_listent( struct xfs_attr_list_context *context, int flags, unsignedchar *name, int namelen, void *value, int valuelen)
{ struct xfs_attrlist *alist = context->buffer; struct xfs_attrlist_ent *aep; int arraytop;
/* decrement by the actual bytes used by the attr */
context->firstu -= round_up(offsetof(struct xfs_attrlist_ent, a_name) +
namelen + 1, sizeof(uint32_t)); if (context->firstu < arraytop) {
trace_xfs_attr_list_full(context);
alist->al_more = 1;
context->seen_enough = 1; return;
}
/* * We found a parent pointer, but we've filled up the buffer. Signal * to the caller that we did /not/ reach the end of the parent pointer * recordset.
*/ if (context->firstu > context->bufsize - reclen) {
context->seen_enough = 1; return;
}
/* Format the parent pointer directly into the caller buffer. */
gpr->gpr_reclen = reclen;
xfs_filehandle_init(mp, ino, gen, &gpr->gpr_parent);
memcpy(gpr->gpr_name, name, namelen);
gpr->gpr_name[namelen] = 0;
/* Expand the last record to fill the rest of the caller's buffer. */ staticinlinevoid
xfs_getparents_expand_lastrec( struct xfs_getparents_ctx *gpx)
{ struct xfs_getparents *gp = &gpx->gph.gph_request; struct xfs_getparents_rec *gpr = gpx->lastrec;
/* Retrieve the parent pointers for a given inode. */ STATICint
xfs_getparents( struct xfs_getparents_ctx *gpx)
{ struct xfs_getparents *gp = &gpx->gph.gph_request; struct xfs_inode *ip = gpx->ip; struct xfs_mount *mp = ip->i_mount;
size_t bufsize; int error;
/* Check size of buffer requested by user */ if (gp->gp_bufsize > XFS_XATTR_LIST_MAX) return -ENOMEM; if (gp->gp_bufsize < xfs_getparents_rec_sizeof(1)) return -EINVAL;
if (gp->gp_iflags & ~XFS_GETPARENTS_IFLAGS_ALL) return -EINVAL; if (gp->gp_reserved) return -EINVAL;
gpx->context.dp = ip;
gpx->context.resynch = 1;
gpx->context.put_listent = xfs_getparents_put_listent;
gpx->context.bufsize = bufsize; /* firstu is used to track the bytes filled in the buffer */
gpx->context.firstu = 0;
/* Copy the cursor provided by caller */
memcpy(&gpx->context.cursor, &gp->gp_cursor, sizeof(struct xfs_attrlist_cursor));
gpx->count = 0;
gp->gp_oflags = 0;
error = xfs_attr_list(&gpx->context); if (error) goto out_free_buf; if (gpx->context.seen_enough < 0) {
error = gpx->context.seen_enough; goto out_free_buf;
}
xfs_getparents_expand_lastrec(gpx);
/* Update the caller with the current cursor position */
memcpy(&gp->gp_cursor, &gpx->context.cursor, sizeof(struct xfs_attrlist_cursor));
/* Is this the root directory? */ if (ip->i_ino == mp->m_sb.sb_rootino)
gp->gp_oflags |= XFS_GETPARENTS_OFLAG_ROOT;
if (gpx->context.seen_enough == 0) { /* * If we did not run out of buffer space, then we reached the * end of the pptr recordset, so set the DONE flag.
*/
gp->gp_oflags |= XFS_GETPARENTS_OFLAG_DONE;
} elseif (gpx->count == 0) { /* * If we ran out of buffer space before copying any parent * pointers at all, the caller's buffer was too short. Tell * userspace that, erm, the message is too long.
*/
error = -EMSGSIZE; goto out_free_buf;
}
/* Copy the records to userspace. */ if (copy_to_user(u64_to_user_ptr(gpx->gph.gph_request.gp_buffer),
gpx->krecords, gpx->context.firstu))
error = -EFAULT;
/* Retrieve the parents of this file and pass them back to userspace. */ int
xfs_ioc_getparents( struct file *file, struct xfs_getparents __user *ureq)
{ struct xfs_getparents_ctx gpx = {
.ip = XFS_I(file_inode(file)),
}; struct xfs_getparents *kreq = &gpx.gph.gph_request; struct xfs_mount *mp = gpx.ip->i_mount; int error;
if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (!xfs_has_parent(mp)) return -EOPNOTSUPP; if (copy_from_user(kreq, ureq, sizeof(*kreq))) return -EFAULT;
error = xfs_getparents(&gpx); if (error) return error;
if (copy_to_user(ureq, kreq, sizeof(*kreq))) return -EFAULT;
return 0;
}
/* Retrieve the parents of this file handle and pass them back to userspace. */ int
xfs_ioc_getparents_by_handle( struct file *file, struct xfs_getparents_by_handle __user *ureq)
{ struct xfs_getparents_ctx gpx = { }; struct xfs_inode *ip = XFS_I(file_inode(file)); struct xfs_mount *mp = ip->i_mount; struct xfs_getparents_by_handle *kreq = &gpx.gph; struct xfs_handle *handle = &kreq->gph_handle; int error;
if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (!xfs_has_parent(mp)) return -EOPNOTSUPP; if (copy_from_user(kreq, ureq, sizeof(*kreq))) return -EFAULT;
/* * We don't use exportfs_decode_fh because it does too much work here. * If the handle refers to a directory, the exportfs code will walk * upwards through the directory tree to connect the dentries to the * root directory dentry. For GETPARENTS we don't care about that * because we're not actually going to open a file descriptor; we only * want to open an inode and read its parent pointers. * * Note that xfs_scrub uses GETPARENTS to log that it will try to fix a * corrupted file's metadata. For this usecase we would really rather * userspace single-step the path reconstruction to avoid loops or * other strange things if the directory tree is corrupt.
*/
gpx.ip = xfs_khandle_to_inode(file, handle); if (IS_ERR(gpx.ip)) return PTR_ERR(gpx.ip);
error = xfs_getparents(&gpx); if (error) goto out_rele;
if (copy_to_user(ureq, kreq, sizeof(*kreq)))
error = -EFAULT;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.