original development tree for Linux kernel GTP module; now long in mainline.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

439 lines
9.8 KiB

  1. /*
  2. * linux/fs/ext4/acl.c
  3. *
  4. * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
  5. */
  6. #include <linux/init.h>
  7. #include <linux/sched.h>
  8. #include <linux/slab.h>
  9. #include <linux/capability.h>
  10. #include <linux/fs.h>
  11. #include "ext4_jbd2.h"
  12. #include "ext4.h"
  13. #include "xattr.h"
  14. #include "acl.h"
  15. /*
  16. * Convert from filesystem to in-memory representation.
  17. */
  18. static struct posix_acl *
  19. ext4_acl_from_disk(const void *value, size_t size)
  20. {
  21. const char *end = (char *)value + size;
  22. int n, count;
  23. struct posix_acl *acl;
  24. if (!value)
  25. return NULL;
  26. if (size < sizeof(ext4_acl_header))
  27. return ERR_PTR(-EINVAL);
  28. if (((ext4_acl_header *)value)->a_version !=
  29. cpu_to_le32(EXT4_ACL_VERSION))
  30. return ERR_PTR(-EINVAL);
  31. value = (char *)value + sizeof(ext4_acl_header);
  32. count = ext4_acl_count(size);
  33. if (count < 0)
  34. return ERR_PTR(-EINVAL);
  35. if (count == 0)
  36. return NULL;
  37. acl = posix_acl_alloc(count, GFP_NOFS);
  38. if (!acl)
  39. return ERR_PTR(-ENOMEM);
  40. for (n = 0; n < count; n++) {
  41. ext4_acl_entry *entry =
  42. (ext4_acl_entry *)value;
  43. if ((char *)value + sizeof(ext4_acl_entry_short) > end)
  44. goto fail;
  45. acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag);
  46. acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
  47. switch (acl->a_entries[n].e_tag) {
  48. case ACL_USER_OBJ:
  49. case ACL_GROUP_OBJ:
  50. case ACL_MASK:
  51. case ACL_OTHER:
  52. value = (char *)value +
  53. sizeof(ext4_acl_entry_short);
  54. acl->a_entries[n].e_id = ACL_UNDEFINED_ID;
  55. break;
  56. case ACL_USER:
  57. case ACL_GROUP:
  58. value = (char *)value + sizeof(ext4_acl_entry);
  59. if ((char *)value > end)
  60. goto fail;
  61. acl->a_entries[n].e_id =
  62. le32_to_cpu(entry->e_id);
  63. break;
  64. default:
  65. goto fail;
  66. }
  67. }
  68. if (value != end)
  69. goto fail;
  70. return acl;
  71. fail:
  72. posix_acl_release(acl);
  73. return ERR_PTR(-EINVAL);
  74. }
  75. /*
  76. * Convert from in-memory to filesystem representation.
  77. */
  78. static void *
  79. ext4_acl_to_disk(const struct posix_acl *acl, size_t *size)
  80. {
  81. ext4_acl_header *ext_acl;
  82. char *e;
  83. size_t n;
  84. *size = ext4_acl_size(acl->a_count);
  85. ext_acl = kmalloc(sizeof(ext4_acl_header) + acl->a_count *
  86. sizeof(ext4_acl_entry), GFP_NOFS);
  87. if (!ext_acl)
  88. return ERR_PTR(-ENOMEM);
  89. ext_acl->a_version = cpu_to_le32(EXT4_ACL_VERSION);
  90. e = (char *)ext_acl + sizeof(ext4_acl_header);
  91. for (n = 0; n < acl->a_count; n++) {
  92. ext4_acl_entry *entry = (ext4_acl_entry *)e;
  93. entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag);
  94. entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
  95. switch (acl->a_entries[n].e_tag) {
  96. case ACL_USER:
  97. case ACL_GROUP:
  98. entry->e_id = cpu_to_le32(acl->a_entries[n].e_id);
  99. e += sizeof(ext4_acl_entry);
  100. break;
  101. case ACL_USER_OBJ:
  102. case ACL_GROUP_OBJ:
  103. case ACL_MASK:
  104. case ACL_OTHER:
  105. e += sizeof(ext4_acl_entry_short);
  106. break;
  107. default:
  108. goto fail;
  109. }
  110. }
  111. return (char *)ext_acl;
  112. fail:
  113. kfree(ext_acl);
  114. return ERR_PTR(-EINVAL);
  115. }
  116. /*
  117. * Inode operation get_posix_acl().
  118. *
  119. * inode->i_mutex: don't care
  120. */
  121. struct posix_acl *
  122. ext4_get_acl(struct inode *inode, int type)
  123. {
  124. int name_index;
  125. char *value = NULL;
  126. struct posix_acl *acl;
  127. int retval;
  128. if (!test_opt(inode->i_sb, POSIX_ACL))
  129. return NULL;
  130. acl = get_cached_acl(inode, type);
  131. if (acl != ACL_NOT_CACHED)
  132. return acl;
  133. switch (type) {
  134. case ACL_TYPE_ACCESS:
  135. name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
  136. break;
  137. case ACL_TYPE_DEFAULT:
  138. name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
  139. break;
  140. default:
  141. BUG();
  142. }
  143. retval = ext4_xattr_get(inode, name_index, "", NULL, 0);
  144. if (retval > 0) {
  145. value = kmalloc(retval, GFP_NOFS);
  146. if (!value)
  147. return ERR_PTR(-ENOMEM);
  148. retval = ext4_xattr_get(inode, name_index, "", value, retval);
  149. }
  150. if (retval > 0)
  151. acl = ext4_acl_from_disk(value, retval);
  152. else if (retval == -ENODATA || retval == -ENOSYS)
  153. acl = NULL;
  154. else
  155. acl = ERR_PTR(retval);
  156. kfree(value);
  157. if (!IS_ERR(acl))
  158. set_cached_acl(inode, type, acl);
  159. return acl;
  160. }
  161. /*
  162. * Set the access or default ACL of an inode.
  163. *
  164. * inode->i_mutex: down unless called from ext4_new_inode
  165. */
  166. static int
  167. ext4_set_acl(handle_t *handle, struct inode *inode, int type,
  168. struct posix_acl *acl)
  169. {
  170. int name_index;
  171. void *value = NULL;
  172. size_t size = 0;
  173. int error;
  174. if (S_ISLNK(inode->i_mode))
  175. return -EOPNOTSUPP;
  176. switch (type) {
  177. case ACL_TYPE_ACCESS:
  178. name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
  179. if (acl) {
  180. error = posix_acl_equiv_mode(acl, &inode->i_mode);
  181. if (error < 0)
  182. return error;
  183. else {
  184. inode->i_ctime = ext4_current_time(inode);
  185. ext4_mark_inode_dirty(handle, inode);
  186. if (error == 0)
  187. acl = NULL;
  188. }
  189. }
  190. break;
  191. case ACL_TYPE_DEFAULT:
  192. name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;
  193. if (!S_ISDIR(inode->i_mode))
  194. return acl ? -EACCES : 0;
  195. break;
  196. default:
  197. return -EINVAL;
  198. }
  199. if (acl) {
  200. value = ext4_acl_to_disk(acl, &size);
  201. if (IS_ERR(value))
  202. return (int)PTR_ERR(value);
  203. }
  204. error = ext4_xattr_set_handle(handle, inode, name_index, "",
  205. value, size, 0);
  206. kfree(value);
  207. if (!error)
  208. set_cached_acl(inode, type, acl);
  209. return error;
  210. }
  211. /*
  212. * Initialize the ACLs of a new inode. Called from ext4_new_inode.
  213. *
  214. * dir->i_mutex: down
  215. * inode->i_mutex: up (access to inode is still exclusive)
  216. */
  217. int
  218. ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
  219. {
  220. struct posix_acl *acl = NULL;
  221. int error = 0;
  222. if (!S_ISLNK(inode->i_mode)) {
  223. if (test_opt(dir->i_sb, POSIX_ACL)) {
  224. acl = ext4_get_acl(dir, ACL_TYPE_DEFAULT);
  225. if (IS_ERR(acl))
  226. return PTR_ERR(acl);
  227. }
  228. if (!acl)
  229. inode->i_mode &= ~current_umask();
  230. }
  231. if (test_opt(inode->i_sb, POSIX_ACL) && acl) {
  232. if (S_ISDIR(inode->i_mode)) {
  233. error = ext4_set_acl(handle, inode,
  234. ACL_TYPE_DEFAULT, acl);
  235. if (error)
  236. goto cleanup;
  237. }
  238. error = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
  239. if (error < 0)
  240. return error;
  241. if (error > 0) {
  242. /* This is an extended ACL */
  243. error = ext4_set_acl(handle, inode, ACL_TYPE_ACCESS, acl);
  244. }
  245. }
  246. cleanup:
  247. posix_acl_release(acl);
  248. return error;
  249. }
  250. /*
  251. * Does chmod for an inode that may have an Access Control List. The
  252. * inode->i_mode field must be updated to the desired value by the caller
  253. * before calling this function.
  254. * Returns 0 on success, or a negative error number.
  255. *
  256. * We change the ACL rather than storing some ACL entries in the file
  257. * mode permission bits (which would be more efficient), because that
  258. * would break once additional permissions (like ACL_APPEND, ACL_DELETE
  259. * for directories) are added. There are no more bits available in the
  260. * file mode.
  261. *
  262. * inode->i_mutex: down
  263. */
  264. int
  265. ext4_acl_chmod(struct inode *inode)
  266. {
  267. struct posix_acl *acl;
  268. handle_t *handle;
  269. int retries = 0;
  270. int error;
  271. if (S_ISLNK(inode->i_mode))
  272. return -EOPNOTSUPP;
  273. if (!test_opt(inode->i_sb, POSIX_ACL))
  274. return 0;
  275. acl = ext4_get_acl(inode, ACL_TYPE_ACCESS);
  276. if (IS_ERR(acl) || !acl)
  277. return PTR_ERR(acl);
  278. error = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
  279. if (error)
  280. return error;
  281. retry:
  282. handle = ext4_journal_start(inode,
  283. EXT4_DATA_TRANS_BLOCKS(inode->i_sb));
  284. if (IS_ERR(handle)) {
  285. error = PTR_ERR(handle);
  286. ext4_std_error(inode->i_sb, error);
  287. goto out;
  288. }
  289. error = ext4_set_acl(handle, inode, ACL_TYPE_ACCESS, acl);
  290. ext4_journal_stop(handle);
  291. if (error == -ENOSPC &&
  292. ext4_should_retry_alloc(inode->i_sb, &retries))
  293. goto retry;
  294. out:
  295. posix_acl_release(acl);
  296. return error;
  297. }
  298. /*
  299. * Extended attribute handlers
  300. */
  301. static size_t
  302. ext4_xattr_list_acl_access(struct dentry *dentry, char *list, size_t list_len,
  303. const char *name, size_t name_len, int type)
  304. {
  305. const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS);
  306. if (!test_opt(dentry->d_sb, POSIX_ACL))
  307. return 0;
  308. if (list && size <= list_len)
  309. memcpy(list, POSIX_ACL_XATTR_ACCESS, size);
  310. return size;
  311. }
  312. static size_t
  313. ext4_xattr_list_acl_default(struct dentry *dentry, char *list, size_t list_len,
  314. const char *name, size_t name_len, int type)
  315. {
  316. const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT);
  317. if (!test_opt(dentry->d_sb, POSIX_ACL))
  318. return 0;
  319. if (list && size <= list_len)
  320. memcpy(list, POSIX_ACL_XATTR_DEFAULT, size);
  321. return size;
  322. }
  323. static int
  324. ext4_xattr_get_acl(struct dentry *dentry, const char *name, void *buffer,
  325. size_t size, int type)
  326. {
  327. struct posix_acl *acl;
  328. int error;
  329. if (strcmp(name, "") != 0)
  330. return -EINVAL;
  331. if (!test_opt(dentry->d_sb, POSIX_ACL))
  332. return -EOPNOTSUPP;
  333. acl = ext4_get_acl(dentry->d_inode, type);
  334. if (IS_ERR(acl))
  335. return PTR_ERR(acl);
  336. if (acl == NULL)
  337. return -ENODATA;
  338. error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
  339. posix_acl_release(acl);
  340. return error;
  341. }
  342. static int
  343. ext4_xattr_set_acl(struct dentry *dentry, const char *name, const void *value,
  344. size_t size, int flags, int type)
  345. {
  346. struct inode *inode = dentry->d_inode;
  347. handle_t *handle;
  348. struct posix_acl *acl;
  349. int error, retries = 0;
  350. if (strcmp(name, "") != 0)
  351. return -EINVAL;
  352. if (!test_opt(inode->i_sb, POSIX_ACL))
  353. return -EOPNOTSUPP;
  354. if (!inode_owner_or_capable(inode))
  355. return -EPERM;
  356. if (value) {
  357. acl = posix_acl_from_xattr(&init_user_ns, value, size);
  358. if (IS_ERR(acl))
  359. return PTR_ERR(acl);
  360. else if (acl) {
  361. error = posix_acl_valid(acl);
  362. if (error)
  363. goto release_and_out;
  364. }
  365. } else
  366. acl = NULL;
  367. retry:
  368. handle = ext4_journal_start(inode, EXT4_DATA_TRANS_BLOCKS(inode->i_sb));
  369. if (IS_ERR(handle))
  370. return PTR_ERR(handle);
  371. error = ext4_set_acl(handle, inode, type, acl);
  372. ext4_journal_stop(handle);
  373. if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
  374. goto retry;
  375. release_and_out:
  376. posix_acl_release(acl);
  377. return error;
  378. }
  379. const struct xattr_handler ext4_xattr_acl_access_handler = {
  380. .prefix = POSIX_ACL_XATTR_ACCESS,
  381. .flags = ACL_TYPE_ACCESS,
  382. .list = ext4_xattr_list_acl_access,
  383. .get = ext4_xattr_get_acl,
  384. .set = ext4_xattr_set_acl,
  385. };
  386. const struct xattr_handler ext4_xattr_acl_default_handler = {
  387. .prefix = POSIX_ACL_XATTR_DEFAULT,
  388. .flags = ACL_TYPE_DEFAULT,
  389. .list = ext4_xattr_list_acl_default,
  390. .get = ext4_xattr_get_acl,
  391. .set = ext4_xattr_set_acl,
  392. };