+/**
+ * tomoyo_path_number_acl - Check permission for ioctl/chmod/chown/chgrp operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @type: Operation.
+ * @filename: Filename to check.
+ * @number: Number.
+ *
+ * Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_path_number_acl(struct tomoyo_request_info *r, const u8 type,
+ const struct tomoyo_path_info *filename,
+ const unsigned long number)
+{
+ struct tomoyo_domain_info *domain = r->domain;
+ struct tomoyo_acl_info *ptr;
+ const u8 perm = 1 << type;
+ int error = -EPERM;
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_path_number_acl *acl;
+ if (ptr->type != TOMOYO_TYPE_PATH_NUMBER_ACL)
+ continue;
+ acl = container_of(ptr, struct tomoyo_path_number_acl,
+ head);
+ if (!(acl->perm & perm) ||
+ !tomoyo_compare_number_union(number, &acl->number) ||
+ !tomoyo_compare_name_union(filename, &acl->name))
+ continue;
+ error = 0;
+ break;
+ }
+ return error;
+}
+
+/**
+ * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL.
+ *
+ * @type: Type of operation.
+ * @filename: Filename.
+ * @number: Number.
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static inline int tomoyo_update_path_number_acl(const u8 type,
+ const char *filename,
+ char *number,
+ struct tomoyo_domain_info *
+ const domain,
+ const bool is_delete)
+{
+ const u8 perm = 1 << type;
+ struct tomoyo_acl_info *ptr;
+ struct tomoyo_path_number_acl e = {
+ .head.type = TOMOYO_TYPE_PATH_NUMBER_ACL,
+ .perm = perm
+ };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!domain)
+ return -EINVAL;
+ if (!tomoyo_parse_name_union(filename, &e.name))
+ return -EINVAL;
+ if (!tomoyo_parse_number_union(number, &e.number))
+ goto out;
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ goto out;
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_path_number_acl *acl =
+ container_of(ptr, struct tomoyo_path_number_acl, head);
+ if (!tomoyo_is_same_path_number_acl(acl, &e))
+ continue;
+ if (is_delete)
+ acl->perm &= ~perm;
+ else
+ acl->perm |= perm;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error) {
+ struct tomoyo_path_number_acl *entry =
+ tomoyo_commit_ok(&e, sizeof(e));
+ if (entry) {
+ list_add_tail_rcu(&entry->head.list,
+ &domain->acl_info_list);
+ error = 0;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name_union(&e.name);
+ tomoyo_put_number_union(&e.number);
+ return error;
+}
+
+/**
+ * tomoyo_path_number_perm2 - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp".
+ *
+ * @r: Pointer to "strct tomoyo_request_info".
+ * @filename: Filename to check.
+ * @number: Number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_path_number_perm2(struct tomoyo_request_info *r,
+ const u8 type,
+ const struct tomoyo_path_info *filename,
+ const unsigned long number)
+{
+ char buffer[64];
+ int error;
+ u8 radix;
+
+ if (!filename)
+ return 0;
+ switch (type) {
+ case TOMOYO_TYPE_CREATE:
+ case TOMOYO_TYPE_MKDIR:
+ case TOMOYO_TYPE_MKFIFO:
+ case TOMOYO_TYPE_MKSOCK:
+ case TOMOYO_TYPE_CHMOD:
+ radix = TOMOYO_VALUE_TYPE_OCTAL;
+ break;
+ case TOMOYO_TYPE_IOCTL:
+ radix = TOMOYO_VALUE_TYPE_HEXADECIMAL;
+ break;
+ default:
+ radix = TOMOYO_VALUE_TYPE_DECIMAL;
+ break;
+ }
+ tomoyo_print_ulong(buffer, sizeof(buffer), number, radix);
+ error = tomoyo_path_number_acl(r, type, filename, number);
+ if (!error)
+ return 0;
+ tomoyo_warn_log(r, "%s %s %s", tomoyo_path_number2keyword(type),
+ filename->name, buffer);
+ if (tomoyo_domain_quota_is_ok(r))
+ tomoyo_update_path_number_acl(type,
+ tomoyo_get_file_pattern(filename)
+ ->name, buffer, r->domain, false);
+ if (r->mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp".
+ *
+ * @type: Type of operation.
+ * @path: Pointer to "struct path".
+ * @number: Number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_path_number_perm(const u8 type, struct path *path,
+ unsigned long number)
+{
+ struct tomoyo_request_info r;
+ int error = -ENOMEM;
+ struct tomoyo_path_info *buf;
+ int idx;
+
+ if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
+ !path->mnt || !path->dentry)
+ return 0;
+ idx = tomoyo_read_lock();
+ buf = tomoyo_get_path(path);
+ if (!buf)
+ goto out;
+ if (type == TOMOYO_TYPE_MKDIR && !buf->is_dir) {
+ /*
+ * tomoyo_get_path() reserves space for appending "/."
+ */
+ strcat((char *) buf->name, "/");
+ tomoyo_fill_path_info(buf);
+ }
+ error = tomoyo_path_number_perm2(&r, type, buf, number);
+ out:
+ kfree(buf);
+ tomoyo_read_unlock(idx);
+ if (r.mode != TOMOYO_CONFIG_ENFORCING)
+ error = 0;
+ return error;
+}
+