18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/fs/hfsplus/ioctl.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2003
68c2ecf20Sopenharmony_ci * Ethan Benson <erbenson@alaska.net>
78c2ecf20Sopenharmony_ci * partially derived from linux/fs/ext2/ioctl.c
88c2ecf20Sopenharmony_ci * Copyright (C) 1993, 1994, 1995
98c2ecf20Sopenharmony_ci * Remy Card (card@masi.ibp.fr)
108c2ecf20Sopenharmony_ci * Laboratoire MASI - Institut Blaise Pascal
118c2ecf20Sopenharmony_ci * Universite Pierre et Marie Curie (Paris VI)
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * hfsplus ioctls
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/capability.h>
178c2ecf20Sopenharmony_ci#include <linux/fs.h>
188c2ecf20Sopenharmony_ci#include <linux/mount.h>
198c2ecf20Sopenharmony_ci#include <linux/sched.h>
208c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
218c2ecf20Sopenharmony_ci#include "hfsplus_fs.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci * "Blessing" an HFS+ filesystem writes metadata to the superblock informing
258c2ecf20Sopenharmony_ci * the platform firmware which file to boot from
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_cistatic int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct dentry *dentry = file->f_path.dentry;
308c2ecf20Sopenharmony_ci	struct inode *inode = d_inode(dentry);
318c2ecf20Sopenharmony_ci	struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
328c2ecf20Sopenharmony_ci	struct hfsplus_vh *vh = sbi->s_vhdr;
338c2ecf20Sopenharmony_ci	struct hfsplus_vh *bvh = sbi->s_backup_vhdr;
348c2ecf20Sopenharmony_ci	u32 cnid = (unsigned long)dentry->d_fsdata;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
378c2ecf20Sopenharmony_ci		return -EPERM;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	mutex_lock(&sbi->vh_mutex);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/* Directory containing the bootable system */
428c2ecf20Sopenharmony_ci	vh->finder_info[0] = bvh->finder_info[0] =
438c2ecf20Sopenharmony_ci		cpu_to_be32(parent_ino(dentry));
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/*
468c2ecf20Sopenharmony_ci	 * Bootloader. Just using the inode here breaks in the case of
478c2ecf20Sopenharmony_ci	 * hard links - the firmware wants the ID of the hard link file,
488c2ecf20Sopenharmony_ci	 * but the inode points at the indirect inode
498c2ecf20Sopenharmony_ci	 */
508c2ecf20Sopenharmony_ci	vh->finder_info[1] = bvh->finder_info[1] = cpu_to_be32(cnid);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* Per spec, the OS X system folder - same as finder_info[0] here */
538c2ecf20Sopenharmony_ci	vh->finder_info[5] = bvh->finder_info[5] =
548c2ecf20Sopenharmony_ci		cpu_to_be32(parent_ino(dentry));
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	mutex_unlock(&sbi->vh_mutex);
578c2ecf20Sopenharmony_ci	return 0;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic inline unsigned int hfsplus_getflags(struct inode *inode)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
638c2ecf20Sopenharmony_ci	unsigned int flags = 0;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (inode->i_flags & S_IMMUTABLE)
668c2ecf20Sopenharmony_ci		flags |= FS_IMMUTABLE_FL;
678c2ecf20Sopenharmony_ci	if (inode->i_flags & S_APPEND)
688c2ecf20Sopenharmony_ci		flags |= FS_APPEND_FL;
698c2ecf20Sopenharmony_ci	if (hip->userflags & HFSPLUS_FLG_NODUMP)
708c2ecf20Sopenharmony_ci		flags |= FS_NODUMP_FL;
718c2ecf20Sopenharmony_ci	return flags;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
778c2ecf20Sopenharmony_ci	unsigned int flags = hfsplus_getflags(inode);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return put_user(flags, user_flags);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
858c2ecf20Sopenharmony_ci	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
868c2ecf20Sopenharmony_ci	unsigned int flags, new_fl = 0;
878c2ecf20Sopenharmony_ci	unsigned int oldflags = hfsplus_getflags(inode);
888c2ecf20Sopenharmony_ci	int err = 0;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	err = mnt_want_write_file(file);
918c2ecf20Sopenharmony_ci	if (err)
928c2ecf20Sopenharmony_ci		goto out;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (!inode_owner_or_capable(inode)) {
958c2ecf20Sopenharmony_ci		err = -EACCES;
968c2ecf20Sopenharmony_ci		goto out_drop_write;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (get_user(flags, user_flags)) {
1008c2ecf20Sopenharmony_ci		err = -EFAULT;
1018c2ecf20Sopenharmony_ci		goto out_drop_write;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	inode_lock(inode);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	err = vfs_ioc_setflags_prepare(inode, oldflags, flags);
1078c2ecf20Sopenharmony_ci	if (err)
1088c2ecf20Sopenharmony_ci		goto out_unlock_inode;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/* don't silently ignore unsupported ext2 flags */
1118c2ecf20Sopenharmony_ci	if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
1128c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
1138c2ecf20Sopenharmony_ci		goto out_unlock_inode;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (flags & FS_IMMUTABLE_FL)
1178c2ecf20Sopenharmony_ci		new_fl |= S_IMMUTABLE;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (flags & FS_APPEND_FL)
1208c2ecf20Sopenharmony_ci		new_fl |= S_APPEND;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	inode_set_flags(inode, new_fl, S_IMMUTABLE | S_APPEND);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (flags & FS_NODUMP_FL)
1258c2ecf20Sopenharmony_ci		hip->userflags |= HFSPLUS_FLG_NODUMP;
1268c2ecf20Sopenharmony_ci	else
1278c2ecf20Sopenharmony_ci		hip->userflags &= ~HFSPLUS_FLG_NODUMP;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	inode->i_ctime = current_time(inode);
1308c2ecf20Sopenharmony_ci	mark_inode_dirty(inode);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ciout_unlock_inode:
1338c2ecf20Sopenharmony_ci	inode_unlock(inode);
1348c2ecf20Sopenharmony_ciout_drop_write:
1358c2ecf20Sopenharmony_ci	mnt_drop_write_file(file);
1368c2ecf20Sopenharmony_ciout:
1378c2ecf20Sopenharmony_ci	return err;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cilong hfsplus_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)arg;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	switch (cmd) {
1458c2ecf20Sopenharmony_ci	case HFSPLUS_IOC_EXT2_GETFLAGS:
1468c2ecf20Sopenharmony_ci		return hfsplus_ioctl_getflags(file, argp);
1478c2ecf20Sopenharmony_ci	case HFSPLUS_IOC_EXT2_SETFLAGS:
1488c2ecf20Sopenharmony_ci		return hfsplus_ioctl_setflags(file, argp);
1498c2ecf20Sopenharmony_ci	case HFSPLUS_IOC_BLESS:
1508c2ecf20Sopenharmony_ci		return hfsplus_ioctl_bless(file, argp);
1518c2ecf20Sopenharmony_ci	default:
1528c2ecf20Sopenharmony_ci		return -ENOTTY;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci}
155