xref: /openbmc/linux/fs/hfsplus/ioctl.c (revision d5cb9783536a41df9f9cba5b0a1d78047ed787f7)
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/fs.h>
16 #include <linux/sched.h>
17 #include <linux/xattr.h>
18 #include <asm/uaccess.h>
19 #include "hfsplus_fs.h"
20 
21 int hfsplus_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
22 		  unsigned long arg)
23 {
24 	unsigned int flags;
25 
26 	switch (cmd) {
27 	case HFSPLUS_IOC_EXT2_GETFLAGS:
28 		flags = 0;
29 		if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_IMMUTABLE)
30 			flags |= EXT2_FLAG_IMMUTABLE; /* EXT2_IMMUTABLE_FL */
31 		if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_APPEND)
32 			flags |= EXT2_FLAG_APPEND; /* EXT2_APPEND_FL */
33 		if (HFSPLUS_I(inode).userflags & HFSPLUS_FLG_NODUMP)
34 			flags |= EXT2_FLAG_NODUMP; /* EXT2_NODUMP_FL */
35 		return put_user(flags, (int __user *)arg);
36 	case HFSPLUS_IOC_EXT2_SETFLAGS: {
37 		if (IS_RDONLY(inode))
38 			return -EROFS;
39 
40 		if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
41 			return -EACCES;
42 
43 		if (get_user(flags, (int __user *)arg))
44 			return -EFAULT;
45 
46 		if (flags & (EXT2_FLAG_IMMUTABLE|EXT2_FLAG_APPEND) ||
47 		    HFSPLUS_I(inode).rootflags & (HFSPLUS_FLG_IMMUTABLE|HFSPLUS_FLG_APPEND)) {
48 			if (!capable(CAP_LINUX_IMMUTABLE))
49 				return -EPERM;
50 		}
51 
52 		/* don't silently ignore unsupported ext2 flags */
53 		if (flags & ~(EXT2_FLAG_IMMUTABLE|EXT2_FLAG_APPEND|
54 			      EXT2_FLAG_NODUMP))
55 			return -EOPNOTSUPP;
56 
57 		if (flags & EXT2_FLAG_IMMUTABLE) { /* 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 & EXT2_FLAG_APPEND) { /* 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 & EXT2_FLAG_NODUMP) /* 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