xref: /openbmc/linux/fs/hfsplus/ioctl.c (revision e868d61272caa648214046a096e5a6bfc068dc8c)
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 
22 int 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 
85 int 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));
122 out:
123 	hfs_find_exit(&fd);
124 	return res;
125 }
126 
127 ssize_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;
165 out:
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 
173 ssize_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