1/*
2 *  linux/fs/hfsplus/ioctl.c
3 *
4 * Copyright (C) 2003
5 * Ethan Benson <erbenson@alaska.net>
6 * partially derived from linux/fs/ext2/ioctl.c
7 * Copyright (C) 1993, 1994, 1995
8 * Remy Card (card@masi.ibp.fr)
9 * Laboratoire MASI - Institut Blaise Pascal
10 * Universite Pierre et Marie Curie (Paris VI)
11 *
12 * hfsplus ioctls
13 */
14
15#include <linux/capability.h>
16#include <linux/fs.h>
17#include <linux/sched.h>
18#include <linux/xattr.h>
19#include <asm/uaccess.h>
20#include "hfsplus_fs.h"
21
22int hfsplus_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
23		  unsigned long arg)
24{
25	unsigned int flags;
26
27	switch (cmd) {
28	case HFSPLUS_IOC_EXT2_GETFLAGS:
29		flags = 0;
30		if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_IMMUTABLE)
31			flags |= FS_IMMUTABLE_FL; /* EXT2_IMMUTABLE_FL */
32		if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_APPEND)
33			flags |= FS_APPEND_FL; /* EXT2_APPEND_FL */
34		if (HFSPLUS_I(inode).userflags & HFSPLUS_FLG_NODUMP)
35			flags |= FS_NODUMP_FL; /* EXT2_NODUMP_FL */
36		return put_user(flags, (int __user *)arg);
37	case HFSPLUS_IOC_EXT2_SETFLAGS: {
38		if (IS_RDONLY(inode))
39			return -EROFS;
40
41		if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
42			return -EACCES;
43
44		if (get_user(flags, (int __user *)arg))
45			return -EFAULT;
46
47		if (flags & (FS_IMMUTABLE_FL|FS_APPEND_FL) ||
48		    HFSPLUS_I(inode).rootflags & (HFSPLUS_FLG_IMMUTABLE|HFSPLUS_FLG_APPEND)) {
49			if (!capable(CAP_LINUX_IMMUTABLE))
50				return -EPERM;
51		}
52
53		/* don't silently ignore unsupported ext2 flags */
54		if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL))
55			return -EOPNOTSUPP;
56
57		if (flags & FS_IMMUTABLE_FL) { /* EXT2_IMMUTABLE_FL */
58			inode->i_flags |= S_IMMUTABLE;
59			HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_IMMUTABLE;
60		} else {
61			inode->i_flags &= ~S_IMMUTABLE;
62			HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_IMMUTABLE;
63		}
64		if (flags & FS_APPEND_FL) { /* EXT2_APPEND_FL */
65			inode->i_flags |= S_APPEND;
66			HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_APPEND;
67		} else {
68			inode->i_flags &= ~S_APPEND;
69			HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_APPEND;
70		}
71		if (flags & FS_NODUMP_FL) /* EXT2_NODUMP_FL */
72			HFSPLUS_I(inode).userflags |= HFSPLUS_FLG_NODUMP;
73		else
74			HFSPLUS_I(inode).userflags &= ~HFSPLUS_FLG_NODUMP;
75
76		inode->i_ctime = CURRENT_TIME_SEC;
77		mark_inode_dirty(inode);
78		return 0;
79	}
80	default:
81		return -ENOTTY;
82	}
83}
84
85int hfsplus_setxattr(struct dentry *dentry, const char *name,
86		     const void *value, size_t size, int flags)
87{
88	struct inode *inode = dentry->d_inode;
89	struct hfs_find_data fd;
90	hfsplus_cat_entry entry;
91	struct hfsplus_cat_file *file;
92	int res;
93
94	if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
95		return -EOPNOTSUPP;
96
97	res = hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd);
98	if (res)
99		return res;
100	res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
101	if (res)
102		goto out;
103	hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
104			sizeof(struct hfsplus_cat_file));
105	file = &entry.file;
106
107	if (!strcmp(name, "hfs.type")) {
108		if (size == 4)
109			memcpy(&file->user_info.fdType, value, 4);
110		else
111			res = -ERANGE;
112	} else if (!strcmp(name, "hfs.creator")) {
113		if (size == 4)
114			memcpy(&file->user_info.fdCreator, value, 4);
115		else
116			res = -ERANGE;
117	} else
118		res = -EOPNOTSUPP;
119	if (!res)
120		hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
121				sizeof(struct hfsplus_cat_file));
122out:
123	hfs_find_exit(&fd);
124	return res;
125}
126
127ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name,
128			 void *value, size_t size)
129{
130	struct inode *inode = dentry->d_inode;
131	struct hfs_find_data fd;
132	hfsplus_cat_entry entry;
133	struct hfsplus_cat_file *file;
134	ssize_t res = 0;
135
136	if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
137		return -EOPNOTSUPP;
138
139	if (size) {
140		res = hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd);
141		if (res)
142			return res;
143		res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
144		if (res)
145			goto out;
146		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
147				sizeof(struct hfsplus_cat_file));
148	}
149	file = &entry.file;
150
151	if (!strcmp(name, "hfs.type")) {
152		if (size >= 4) {
153			memcpy(value, &file->user_info.fdType, 4);
154			res = 4;
155		} else
156			res = size ? -ERANGE : 4;
157	} else if (!strcmp(name, "hfs.creator")) {
158		if (size >= 4) {
159			memcpy(value, &file->user_info.fdCreator, 4);
160			res = 4;
161		} else
162			res = size ? -ERANGE : 4;
163	} else
164		res = -ENODATA;
165out:
166	if (size)
167		hfs_find_exit(&fd);
168	return res;
169}
170
171#define HFSPLUS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type"))
172
173ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
174{
175	struct inode *inode = dentry->d_inode;
176
177	if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
178		return -EOPNOTSUPP;
179
180	if (!buffer || !size)
181		return HFSPLUS_ATTRLIST_SIZE;
182	if (size < HFSPLUS_ATTRLIST_SIZE)
183		return -ERANGE;
184	strcpy(buffer, "hfs.type");
185	strcpy(buffer + sizeof("hfs.type"), "hfs.creator");
186
187	return HFSPLUS_ATTRLIST_SIZE;
188}
189