diff -Nur linux-2.2.0-pre9/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S --- linux-2.2.0-pre9/arch/i386/kernel/entry.S Wed Jan 20 20:05:59 1999 +++ linux/arch/i386/kernel/entry.S Mon Jan 25 14:05:32 1999 @@ -560,6 +560,7 @@ .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ .long SYMBOL_NAME(sys_vfork) /* 190 */ + .long SYMBOL_NAME(sys_acl_ctl) /* * NOTE!! This doesn't have to be exact - we just have @@ -567,6 +568,6 @@ * entries. Don't panic if you notice that this hasn't * been shrunk every time we add a new system call. */ - .rept NR_syscalls-190 + .rept NR_syscalls-191 .long SYMBOL_NAME(sys_ni_syscall) .endr diff -Nur linux-2.2.0-pre9/fs/Makefile linux/fs/Makefile --- linux-2.2.0-pre9/fs/Makefile Mon Aug 31 22:01:35 1998 +++ linux/fs/Makefile Mon Jan 25 14:05:32 1999 @@ -10,7 +10,7 @@ L_TARGET := filesystems.a L_OBJS = $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o)) O_TARGET := fs.o -O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \ +O_OBJS = acl.o open.o read_write.o devices.o file_table.o buffer.o \ super.o block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \ dcache.o inode.o attr.o bad_inode.o $(BINFMTS) diff -Nur linux-2.2.0-pre9/fs/acl.c linux/fs/acl.c --- linux-2.2.0-pre9/fs/acl.c Thu Jan 1 01:00:00 1970 +++ linux/fs/acl.c Tue Mar 2 11:47:29 1999 @@ -0,0 +1,302 @@ +/* + * linux/fs/acl.c + * + * Copyright (C) 1997, 1998 + * Remy Card (card@Linux.EU.Org) + */ + +/* + * This file contains the VFS Access Control Lists management + * + * Actually, this is a wrapper that copies the arguments in kernel + * space, checks them and calls the acl_ctl superblock operation + */ + +#include + +#include +#include +#include +#include + +/* + * Define ACL_DEBUG to include debugging code + */ +#define ACL_DEBUG + +#ifdef ACL_DEBUG +# define acl_debug(f, a...) { \ + printk ("ACL DEBUG (%s, %d): %s: ", \ + __FILE__, __LINE__, __FUNCTION__); \ + printk (f, ## a); \ + } +#else +# define acl_debug(f, a...) /**/ +#endif + +/* + * check_acl () + * + * Check that an ACL is valid: + * - the valid flags must be set + * - the three required entries must be present + * - the ACL must be sorted: + * ACL_USER_OBJ, ACL_USER, ACL_GROUP_OBJ, ACL_GROUP, ACL_OTHER_OBJ + * - the permissions contained in the entries must be valid + */ +static +int check_acl (struct linux_acl * lacl) +{ + int acl_user_obj = 0; + int acl_group_obj = 0; + int acl_other_obj = 0; + int acl_mask = 0; + int i = 0; + int error = 0; + + if (lacl->acl_valid != LINUX_ACL_VALID) { + acl_debug ("No valid flag for ACL\n"); + error = -EINVAL; + } + for (i = 0; i < lacl->acl_acle_count; i++) { + if (lacl->acl_acle[i].acle_valid != LINUX_ACL_VALID) { + acl_debug ("No valid flag at pos %d\n", i); + error = -EINVAL; + } + + switch (lacl->acl_acle[i].acle_type) { + case ACL_USER_OBJ: + if (i != 0) { + acl_debug ("USER_OBJ at pos #%d\n", i); + error = -EINVAL; + } + acl_user_obj++; + break; + case ACL_MASK: + if(!acl_user_obj || acl_group_obj || + acl_other_obj) { + acl_debug ("Bad ACL_MASK at pos %d\n", i); + error = -EINVAL; + } + acl_mask++; + break; + case ACL_GROUP_OBJ: + if (acl_group_obj) { + acl_debug ("2nd GROUP_OBJ at pos %d\n", + i); + error = -EINVAL; + } + acl_group_obj++; + break; + case ACL_OTHER_OBJ: + if (acl_other_obj) { + acl_debug ("2nd OTHER_OBJ at pos %d\n", + i); + error = -EINVAL; + } + acl_other_obj++; + break; + case ACL_USER: + if (!acl_user_obj || !acl_mask || acl_group_obj || + acl_other_obj) { + acl_debug ("Bad USER at pos %d\n", i); + error = -EINVAL; + } + break; + case ACL_GROUP: + if (!acl_user_obj || !acl_mask || !acl_group_obj || + acl_other_obj) { + acl_debug ("Bad GROUP at pos %d\n", i); + error = -EINVAL; + } + break; + default: + acl_debug ("Bad type %d at pos %d\n", + lacl->acl_acle[i].acle_type, i); + error = -EINVAL; + } + if (lacl->acl_acle[i].acle_perms & ~S_IRWXO) { + acl_debug ("Bad permissions at pos %d\n", i); + error = -EINVAL; + } + } + if (!acl_user_obj || !acl_group_obj || !acl_other_obj) { + acl_debug ("Missing a required entry\n"); + error = -EINVAL; + } + return error; +} + +/* + * copy_acl_in () + * + * Allocate an ACL in kernel space and copy the argument into it + */ +static +int copy_acl_in (void * data, struct linux_acl ** lacl, int copy) +{ + int n1; + int n2; + int error; + + error = verify_area (VERIFY_READ, data, sizeof (struct linux_acl)); + if (error) + return error; + if (get_user (n1, &((struct linux_acl *)data)->acl_acle_count)) + return -EFAULT; + n2 = sizeof (struct linux_acl) + sizeof (struct linux_acl_entry) * n1; + if (copy) { + error = verify_area (VERIFY_READ, data, n2); + if (error) + return error; + } + *lacl = vmalloc (n2); + if (!*lacl) + return -ENOMEM; + if (copy_from_user (*lacl, data, + copy ? n2 : sizeof (struct linux_acl))) { + vfree (*lacl); + *lacl = NULL; + return -EFAULT; + } + return 0; +} + +/* + * copy_acl_out () + * + * Copy the kernel space ACL to the argument in user space + */ +static +int copy_acl_out (void * data, struct linux_acl * lacl) +{ + int n1; + int n2; + int error; + + n1 = lacl->acl_acle_count; + n2 = sizeof (struct linux_acl) + sizeof (struct linux_acl_entry) * n1; + error = verify_area (VERIFY_WRITE, data, n2); + if (error) + return error; + if (copy_to_user (data, lacl, n2)) { + return -EFAULT; + } + return 0; +} + +/* + * sys_acl_ctl () + * + * Implementation of the acl_ctl system call + */ +int sys_acl_ctl (int operation, void * filename, int acl_type, void * data) +{ + int error = 0; + struct dentry * dentry = NULL; + struct inode * inode = NULL; + struct file * file; + struct linux_acl * lacl = NULL; + int fd; + int read = 0; + int write = 0; + int get = 0; + int set = 0; + int delete = 0; + int f = 0; + int i = 0; + + printk ("sys_acl_ctl called\n"); + if (acl_type != ACL_TYPE_ACCESS && acl_type != ACL_TYPE_DEFAULT) { + acl_debug ("Bad ACL type (%d)\n", acl_type); + return -EINVAL; + } + + switch (operation) { + case LINUX_ACL_DELETE_FD: + delete = 1; + i = 1; + write = 1; + break; + case LINUX_ACL_DELETE_FILE: + delete = 1; + f = 1; + write = 1; + break; + case LINUX_ACL_GET_FD: + get = 1; + i = 1; + read = 1; + break; + case LINUX_ACL_GET_FILE: + get = 1; + f = 1; + read = 1; + break; + case LINUX_ACL_SET_FD: + set = 1; + i = 1; + write = 1; + break; + case LINUX_ACL_SET_FILE: + set = 1; + f = 1; + write = 1; + break; + default: + acl_debug ("Bad ACL operation (%d)\n", operation); + return -EINVAL; + break; + } + if (i) { + fd = (int)filename; + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + error = -EBADF; + else if (!file->f_dentry || !(inode = file->f_dentry->d_inode)) + error = -ENOENT; + } else { + dentry = namei (filename); + if (IS_ERR(dentry)) + error = PTR_ERR(dentry); + else + inode = dentry->d_inode; + } + + if (inode) { + if (!inode->i_sb || !inode->i_sb->s_op || + !inode->i_sb->s_op->acl_ctl) { + acl_debug ("No acl_ctl for inode %s:%ld\n", + kdevname (inode->i_dev), inode->i_ino); + error = -EINVAL; + } else if (write && IS_RDONLY(inode)) + error = -EROFS; + else if (write && (IS_IMMUTABLE(inode) || IS_APPEND(inode))) + error = -EPERM; + else if (write && (current->fsuid != inode->i_uid) && !fsuser()) + error = -EPERM; + else if (acl_type == ACL_TYPE_DEFAULT && + !S_ISDIR(inode->i_mode)) + error = -ENOTDIR; + } + if (!error) { + if (get) { + error = copy_acl_in (data, &lacl, 0); + } else if (set) { + error = copy_acl_in (data, &lacl, 1); + if (!error) + error = check_acl (lacl); + } + if (!error) { + error = inode->i_sb->s_op->acl_ctl (inode, operation, + acl_type, lacl); + if (!error && get) + error = copy_acl_out (data, lacl); + if (lacl) + vfree (lacl); + } + } + /* Added !IS_ERR(dentry) (segmentation fault if file doesnīt exist) */ + if (dentry && !IS_ERR(dentry)) + dput (dentry); + return error; +} diff -Nur linux-2.2.0-pre9/fs/exec.c linux/fs/exec.c --- linux-2.2.0-pre9/fs/exec.c Mon Jan 18 22:47:38 1999 +++ linux/fs/exec.c Fri Apr 9 12:32:58 1999 @@ -566,7 +566,7 @@ mode = inode->i_mode; if (!S_ISREG(mode)) /* must be regular file */ return -EACCES; - if (!(mode & 0111)) /* with at least _one_ execute bit set */ + if (!(mode & (0111 | S_IXC))) /* with at least _one_ execute bit set in mode or ACL */ return -EACCES; if (IS_NOEXEC(inode)) /* FS mustn't be mounted noexec */ return -EACCES; diff -Nur linux-2.2.0-pre9/fs/ext2/acl.c linux/fs/ext2/acl.c --- linux-2.2.0-pre9/fs/ext2/acl.c Wed May 6 19:56:08 1998 +++ linux/fs/ext2/acl.c Fri May 21 15:50:08 1999 @@ -1,22 +1,1072 @@ /* * linux/fs/ext2/acl.c * - * Copyright (C) 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) + * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998 + * Remy Card (card@Linux.EU.Org) */ /* - * This file will contain the Access Control Lists management for the - * second extended file system. + * This file contains the Access Control Lists management (and the standard + * permission check) for the second extended file system. */ +#include #include #include #include #include #include +#include + +#include + +#ifdef EXT2FS_ACL_CHECK +/* + * check_ext2_acl() + * + * Check that an ACL is valid: + * - the valid flags must be set + * - the three required entries must be present + * - the ACL must be sorted: + * ACL_USER_OBJ, ACL_MASK, ACL_USER, ACL_GROUP_OBJ, ACL_GROUP, ACL_OTHER_OBJ + * - the permissions contained in the entries must be valid + * - the acl header number in the entries must match + */ +static +int check_ext2_acl (struct inode * inode, struct ext2_acl_header * aclh, + struct ext2_acl_entry * acle, int n) +{ + struct ext2_acl_entry * aclp; + int i; + int retval = 1; + int state = 0; + + if (le32_to_cpu (aclh->aclh_valid) != LINUX_ACL_VALID) { + ext2_error (inode->i_sb, "check_ext2_acl", + "Valid flag wrong in ACL header for inode %s:%ld", + kdevname (inode->i_dev), inode->i_ino); + retval = 0; + } + for (aclp = acle, i = 0; retval == 1 && i < n; aclp++, i++) { + if (le32_to_cpu (aclp->acle_valid) != LINUX_ACL_VALID) { + ext2_error (inode->i_sb, "check_ext2_acl", + "Valid flag wrong for inode %s:%ld" + " in ACL entry at pos %d", + kdevname (inode->i_dev), inode->i_ino, i); + retval = 0; + } + switch (le16_to_cpu (aclp->acle_type)) { + case ACL_USER_OBJ: + if(state == 0){ + state = 1; + } else { + ext2_error (inode->i_sb, "check_ext2_acl", + "Unexpected ACL_USER_OBJ entry for inode %s:%ld" + " in ACL entry at pos %d", + kdevname (inode->i_dev), inode->i_ino, i); + retval = 0; + } + break; + case ACL_MASK: + if(state == 1){ + state = 2; + } else { + ext2_error (inode->i_sb, "check_ext2_acl", + "Unexpected ACL_MASK entry for inode %s:%ld" + " in ACL entry at pos %d", + kdevname (inode->i_dev), inode->i_ino, i); + retval = 0; + } + break; + case ACL_USER: + if(state != 2){ + ext2_error (inode->i_sb, "check_ext2_acl", + "Unexpected ACL_USER entry for inode %s:%ld" + " in ACL entry at pos %d", + kdevname (inode->i_dev), inode->i_ino, i); + retval = 0; + } + break; + case ACL_GROUP_OBJ: + if(state == 1){ + state = 3; + } else if (state == 2){ + state = 4; + } else { + ext2_error (inode->i_sb, "check_ext2_acl", + "Unexpected ACL_GROUP_OBJ entry for inode %s:%ld" + " in ACL entry at pos %d", + kdevname (inode->i_dev), inode->i_ino, i); + retval = 0; + } + break; + case ACL_GROUP: + if(state != 4){ + ext2_error (inode->i_sb, "check_ext2_acl", + "Unexpected ACL_GROUP entry for inode %s:%ld" + " in ACL entry at pos %d", + kdevname (inode->i_dev), inode->i_ino, i); + retval = 0; + } + break; + case ACL_OTHER_OBJ: + if(state == 3 || state == 4){ + ext2_error (inode->i_sb, "check_ext2_acl", + "Unexpected ACL_OTHER_OBJ entry for inode %s:%ld" + " in ACL entry at pos %d", + kdevname (inode->i_dev), inode->i_ino, i); + retval = 0; + } + state = 5; + break; + default: + ext2_error (inode->i_sb, "check_ext2_acl", + "Bad ACL type (%d) for inode %s:%ld" + " at position %d", + le16_to_cpu (aclp->acle_type), + kdevname (inode->i_dev), + inode->i_ino, i); + retval = 0; + break; + } + if (le16_to_cpu (aclp->acle_perms) & ~S_IRWXO) { + ext2_error (inode->i_sb, "check_ext2_acl", + "Bad permission (%o) in ACL for inode" + " %s:%ld at position %d", + le16_to_cpu (aclp->acle_perms), + kdevname (inode->i_dev), inode->i_ino, i); + retval = 0; + } + /* No need to convert values. Both are in little-endian format */ + if (aclp->acle_aclh != aclh->aclh_no) { + ext2_error (inode->i_sb, "check_ext2_acl", + "Bad pointer to header (%d != %d) in ACL" + " for inode %s:%ld at position %d", + le32_to_cpu (aclp->acle_aclh), + le32_to_cpu (aclh->aclh_no), + kdevname (inode->i_dev), inode->i_ino, i); + retval = 0; + } + } + if (state != 5){ + retval = 0; + } + return retval; +} + +/* + * check_acl_descriptor () + * + * Check that a descriptor is valid + */ +int check_acl_descriptor (struct super_block * sb, struct buffer_head * bh, + unsigned long ino, unsigned long block) +{ + struct ext2_acl_desc * acld; + void * bitmap; + int min; + int max; + int i; + int j; + int free_count = 0; + int ok; + + acld = (struct ext2_acl_desc *)bh->b_data; + bitmap = (void *)(bh->b_data + sizeof (struct ext2_acl_desc)); + max = 0; + min = sb->u.ext2_sb.s_max_acle_block; + + for (i = 0; i < EXT2_ACLE_PER_BLOCK(sb); i++) + if (!test_bit (i, bitmap)) { + free_count++; + /* bugfix in the following loop */ + for (j = 1; i + j < EXT2_ACLE_PER_BLOCK(sb) && + !test_bit(i + j, bitmap); j++) { + free_count++; + } + if (j > max) + max = j; + if (j < min) + min = j; + /* increase i by j */ + i+= j ; + } + ok = 1; + if (free_count != le16_to_cpu (acld->acld_free_acl)) { + ext2_error (sb, "check_acl_descriptor", + "Inode %lu, block %lu, free count wrong: " + "stored=%d, counted=%d\n", ino, block, + le16_to_cpu (acld->acld_free_acl), free_count); + ok = 0; + } + /* min and max are only valid in acl-data-inode, no need to update + them in a block of acl_idx-inode */ + if(ino == EXT2_ACL_DATA_INO) { + if (min != le16_to_cpu (acld->acld_min_acl)) { + ext2_error (sb, "check_acl_descriptor", + "Inode %lu, block %lu, smallest fragment count " + "wrong: stored=%d, counted=%d\n", ino, block, + le16_to_cpu (acld->acld_min_acl), min); + ok = 0; + } + /* bugfix: changed min to max */ + if (max != le16_to_cpu (acld->acld_max_acl)) { + ext2_error (sb, "check_acl_descriptor", + "INode %lu, block %lu, largest fragment count " + "wrong: stored=%d, counted=%d\n", ino, block, + le16_to_cpu (acld->acld_max_acl), max); + ok = 0; + } + } + return ok; +} +#endif + +/* + * update_descriptor () + * + * Rescan the bitmap to update the descriptor + */ +static +void update_descriptor (struct super_block * sb, struct ext2_acl_desc * acld, + void * bitmap) +{ + int i; + int j; + __u32 min, max; + + max = 0; + min = sb->u.ext2_sb.s_max_acle_block; + for (i = 0; i < EXT2_ACLE_PER_BLOCK(sb); i++) + if (!test_bit (i, bitmap)) { + /* bugfix: changed algorithm see above */ + for (j = 1; i + j < EXT2_ACLE_PER_BLOCK(sb) && + !test_bit (i + j, bitmap); j++); + if (j > max) + max = j; + if (j < min) + min = j; + i+= j; + } + acld->acld_max_acl = le16_to_cpu (max); + acld->acld_min_acl = le16_to_cpu (min); +} + +/* + * + * allocate_acl_header () + * + * Allocate an ACL header in the ACL index inode + * + */ +static +struct ext2_acl_header * allocate_acl_header (struct inode * inode, + struct buffer_head ** bh, + int * err) +{ + unsigned long i = 0; + int j; + int n; + struct super_block * sb = inode->i_sb; + struct ext2_acl_desc * acld; + struct ext2_acl_header * aclh; + void * bitmap; + + ext2_acl_debug ("Allocating ACL header for inode %s:%ld\n", + kdevname (inode->i_dev), inode->i_ino); + *err = 0; + n = 0; + /* + * Find a block with a free acl header + */ + *bh = ext2_bread (sb->u.ext2_sb.s_acl_idx_inode, i, 0, err); + while (*bh != NULL && + (le16_to_cpu (((struct ext2_acl_desc *)(*bh)->b_data)->acld_free_acl)) == 0) { +#ifdef EXT2FS_ACL_CHECK + check_acl_descriptor (sb, *bh, EXT2_ACL_IDX_INO, i); +#endif + brelse (*bh); + *bh = ext2_bread (sb->u.ext2_sb.s_acl_idx_inode, ++i, 0, err); + n++; + } + if (!*bh) { + ext2_acl_debug ("Allocating block (%ld) in ACL index inode\n", + i); + /* + * No block with a free acl header, allocate a new block + */ + *bh = ext2_bread (sb->u.ext2_sb.s_acl_idx_inode, i, 1, err); + if (!*bh) + return NULL; + /* + * Create the descriptor + */ + acld = (struct ext2_acl_desc *) (*bh)->b_data; + acld->acld_free_acl = cpu_to_le16 (sb->u.ext2_sb.s_max_aclh_block); + acld->acld_max_acl = cpu_to_le16 (sb->u.ext2_sb.s_max_aclh_block); + acld->acld_min_acl = cpu_to_le16 (sb->u.ext2_sb.s_max_aclh_block); + /* + * Initialize the bitmap + */ + bitmap = (void *)((*bh)->b_data + sizeof (struct ext2_acl_desc)); + memset (bitmap, 0xff, sizeof (struct ext2_acl_desc) /* FIXME! */); + for (j = 2; j < EXT2_ACLH_PER_BLOCK(sb); j++) + clear_bit (j, bitmap); + /* + * First free header should be 2 (FIXME!) + */ + j = 2; + } else { + ext2_acl_debug ("Using block %ld in ACL index inode\n", i); +#ifdef EXT2FS_ACL_CHECK + check_acl_descriptor (sb, *bh, EXT2_ACL_IDX_INO, i); +#endif + acld = (struct ext2_acl_desc *) (*bh)->b_data; + /* + * Search a free header in the bitmap + */ + bitmap = (void *)((*bh)->b_data + sizeof (struct ext2_acl_desc)); + j = find_first_zero_bit (bitmap, EXT2_ACLH_PER_BLOCK(sb)); + } + if (j >= EXT2_ACLH_PER_BLOCK(sb)) { + ext2_error (sb, "allocate_acl_header", + "block %lu of ACL index inode is corrupted", i); + *err = -ENOSPC; + brelse (*bh); + return NULL; + } + /* + * Ok, allocate the header + */ + set_bit (j, bitmap); + acld->acld_free_acl = cpu_to_le16 (le16_to_cpu (acld->acld_free_acl) - 1); + aclh = (struct ext2_acl_header *)((*bh)->b_data) + j; + aclh->aclh_valid = cpu_to_le32 (LINUX_ACL_VALID); + aclh->aclh_version = cpu_to_le16 (EXT2_ACLH_VERSION); + aclh->aclh_acle_count = cpu_to_le16 (0); + /* bugfix: Ref_count must be 1 not 0 */ + aclh->aclh_ref_count = cpu_to_le32 (1); + aclh->aclh_first_acle = cpu_to_le32 (0); + aclh->aclh_no = cpu_to_le32 (n * EXT2_ACLH_PER_BLOCK(sb) + j); + aclh->aclh_reserved[0] = cpu_to_le32 (0); + aclh->aclh_reserved[1] = cpu_to_le32 (0); + aclh->aclh_reserved[2] = cpu_to_le32 (0); + mark_buffer_dirty (*bh, 0); + return aclh; +} + +/* + * allocate_acl_entries () + * + * Allocate contiguous ACL entries in the ACL data inode + */ +static +struct ext2_acl_entry * allocate_acl_entries (struct inode * inode, + struct ext2_acl_header * aclh, + int entry_count, + struct buffer_head ** bh, + int * err) +{ + /* This counter runs over the blocks of EXT2_ACL_DATA_INO inode. */ + unsigned long i = 0; + /* Did we found enough free entries in some block? */ + int ok = 0; + /* Gets the start of free entries. */ + int j = 0; + /* Some counters. */ + int k = 0, n = 0, cont = 0; + /* Shortcut. */ + struct super_block * sb = inode->i_sb; + /* This gets the descriptor of a new allocated block + * or an old one with enough free entries. */ + struct ext2_acl_desc * acld; + struct ext2_acl_entry * acle; + struct ext2_acl_entry * p; + void * bitmap; + + ext2_acl_debug ("Allocating %d ACL entries for inode %s:%ld\n", + entry_count, kdevname (inode->i_dev), inode->i_ino); + /* + * Check that the ACL can be stored as contiguous entries + */ + if (entry_count > sb->u.ext2_sb.s_max_acle_block) { + *err = -EACL2BIG; + return NULL; + } + *err = 0; + n = 0; + /* + * Find a block with enough contiguous free acl entries + */ + *bh = ext2_bread (sb->u.ext2_sb.s_acl_data_inode, i, 0, err); + while (*bh != NULL && + (le16_to_cpu (((struct ext2_acl_desc *)(*bh)->b_data)->acld_max_acl)) /* bugfix: changed >= to :*/ < + entry_count) { +#ifdef EXT2FS_ACL_CHECK + check_acl_descriptor (sb, *bh, EXT2_ACL_DATA_INO, i); +#endif + brelse (*bh); +/* bugfix: changed acl_idx_inode to acl_data_inode + *bh = ext2_bread (sb->u.ext2_sb.s_acl_idx_inode, ++i, 0, err); + ^^^ +*/ + *bh = ext2_bread (sb->u.ext2_sb.s_acl_data_inode, ++i, 0, err); + n++; + } + if (!*bh) { + ext2_acl_debug ("Allocating block %ld in ACL data inode\n", i); + /* + * No block with enough contiguous free acl entries, + * allocate a new block + */ + *bh = ext2_bread (sb->u.ext2_sb.s_acl_data_inode, i, 1, err); + if (!*bh) + return NULL; + /* + * Create the descriptor + */ + acld = (struct ext2_acl_desc *) (*bh)->b_data; + acld->acld_free_acl = cpu_to_le16 (sb->u.ext2_sb.s_max_acle_block); + acld->acld_max_acl = cpu_to_le16 (sb->u.ext2_sb.s_max_acle_block); + acld->acld_min_acl = cpu_to_le16 (sb->u.ext2_sb.s_max_acle_block); + /* + * Initialize the bitmap + */ + bitmap = (void *)((*bh)->b_data + sizeof (struct ext2_acl_desc)); + memset (bitmap, 0xff, sizeof (struct ext2_acl_desc) /* FIXME! */); + for (j = 2; j < EXT2_ACLE_PER_BLOCK(sb); j++) + clear_bit (j, bitmap); + /* + * First free entry should be 2 (FIXME!) + */ + j = 2; + ok = 1; + } else { + ext2_acl_debug ("Using block %ld in ACL data inode\n", i); +#ifdef EXT2FS_ACL_CHECK + check_acl_descriptor (sb, *bh, EXT2_ACL_DATA_INO, i); +#endif + acld = (struct ext2_acl_desc *) (*bh)->b_data; + /* + * Search enough contiguous free entries in the bitmap. + * cont is the number of contiguous free entries found by now, + * k runs over all maybe free entries. + * j is the start index of the contiguous free entries, if + * ok is true, finally. + */ + bitmap = (void *)((*bh)->b_data + sizeof (struct ext2_acl_desc)); + for(k = 2; k < EXT2_ACLE_PER_BLOCK(sb) && cont < entry_count; k++) { + if(test_bit(k, bitmap)) { + cont = 0; + } else { + if(cont == 0) + j = k; + cont++; + } + } + ok = (cont == entry_count); + } + if (!ok) { + ext2_error (sb, "allocate_acl_entries", + "block %lu of ACL data inode is corrupted", i); + *err = -ENOSPC; + brelse (*bh); + return NULL; + } + /* + * Ok, allocate the entries + */ + for (k = 0; k < entry_count; k++) + set_bit (j + k, bitmap); + acld->acld_free_acl = cpu_to_le16 (le16_to_cpu (acld->acld_free_acl) - entry_count); + acle = (struct ext2_acl_entry *)((*bh)->b_data) + j; + for (p = acle, k = 0; k < entry_count; p++, k++) { + p->acle_valid = cpu_to_le32 (LINUX_ACL_VALID); + p->acle_version = cpu_to_le16 (EXT2_ACLE_VERSION); + p->acle_perms = cpu_to_le16 (0); + p->acle_acle_count = cpu_to_le16 (entry_count); + p->acle_type = cpu_to_le16 (0); + p->acle_tag = cpu_to_le32 (0); + /* No need to convert values. Both are in little-endian format */ + p->acle_aclh = aclh->aclh_no; + p->acle_no = cpu_to_le32 (n * EXT2_ACLE_PER_BLOCK(sb) + j + k); + p->acle_next = cpu_to_le32 (le32_to_cpu (p->acle_no) + 1); + p->acle_reserved = cpu_to_le32 (0); + } + /* No need to convert values. Both are in little-endian format */ + aclh->aclh_first_acle = acle->acle_no; + /* + * Update the descriptor + */ + update_descriptor (inode->i_sb, acld, bitmap); + mark_buffer_dirty (*bh, 0); + return acle; +} + +/* + * free_acl_header () + * + * Deallocate an ACL header + */ +static +int free_acl_header (struct inode * inode, struct ext2_acl_header * aclh, + struct buffer_head * bh) +{ + int i; + void * bitmap; + struct ext2_acl_desc * acld; + struct super_block * sb = inode->i_sb; + + ext2_acl_debug ("Deallocating ACL header %d for inode %s:%ld\n", + aclh->aclh_no, kdevname (inode->i_dev), inode->i_ino); +#ifdef EXT2FS_ACL_CHECK + /* + * Check that the ACL header has to be deallocated + */ + if (le32_to_cpu (aclh->aclh_ref_count)) { + ext2_error (inode->i_sb, "free_acl_header", + "aclh_ref_count != 0"); + return 0; + } +#endif + i = aclh->aclh_no % EXT2_ACLH_PER_BLOCK(inode->i_sb); + acld = (struct ext2_acl_desc *) bh->b_data; + bitmap = (void *)(bh->b_data + sizeof (struct ext2_acl_desc)); +#ifdef EXT2FS_ACL_CHECK + /* + * Check that the ACL header is allocated + */ + if (!test_bit (i, bitmap)) { + ext2_error (inode->i_sb, "free_acl_header", + "Bit %d is not set", i); + return 0; + } +#endif + /* + * Clear the header + */ + aclh->aclh_valid = cpu_to_le32 (0); + aclh->aclh_acle_count = cpu_to_le16 (0); + aclh->aclh_first_acle = cpu_to_le32 (0); + aclh->aclh_no = cpu_to_le32 (0); + /* + * Deallocate the header + */ + clear_bit (i, bitmap); + acld->acld_free_acl++; + update_descriptor(sb,acld,bitmap); + mark_buffer_dirty (bh, 0); + return 1; +} + +/* + * free_acl_entries () + * + * Deallocate contiguous ACL entries + */ +static +int free_acl_entries (struct inode * inode, struct ext2_acl_entry * acle, + int n, struct buffer_head * bh) +{ + int i; + int j; + void * bitmap; + struct ext2_acl_desc * acld; + struct ext2_acl_entry * p; + + ext2_acl_debug ("Deallocating %d ACL entries (at %d)" + " for inode %s:%ld\n", n, acle->acle_no, + kdevname (inode->i_dev), inode->i_ino); + i = le32_to_cpu (acle->acle_no) % EXT2_ACLE_PER_BLOCK(inode->i_sb); + acld = (struct ext2_acl_desc *) bh->b_data; + bitmap = (void *)(bh->b_data + sizeof (struct ext2_acl_desc)); +#ifdef EXT2FS_ACL_CHECK + /* + * Check that the ACL entries are allocated + */ + for (j = 0; j < n; j++) + if (!test_bit (i + j, bitmap)) { + ext2_error (inode->i_sb, "free_acl_entries", + "Bit %d is not set", i + j); + return 0; + } +#endif + /* + * Clear the entries + */ + for (p = acle, j = 0; j < n; p++, j++) { + p->acle_valid = cpu_to_le32 (0); + p->acle_version = cpu_to_le16 (0); + p->acle_perms = cpu_to_le16 (0); + p->acle_acle_count = cpu_to_le16 (0); + p->acle_type = cpu_to_le16 (0); + p->acle_tag = cpu_to_le32 (0); + p->acle_aclh = cpu_to_le32 (0); + p->acle_no = cpu_to_le32 (0); + p->acle_next = cpu_to_le32 (0); + p->acle_reserved = cpu_to_le32 (0); + } + /* + * Deallocate the entries + */ + for (j = 0; j < n; j++) + clear_bit (i + j, bitmap); + acld->acld_free_acl = cpu_to_le16 (le16_to_cpu (acld->acld_free_acl) + n); + /* + * Update the descriptor + */ + update_descriptor (inode->i_sb, acld, bitmap); + mark_buffer_dirty (bh, 0); + return 1; +} + +/* + * get_acl_header () + * + * Read an ACL header + */ +static +struct ext2_acl_header * get_acl_header (struct inode * inode, int acl_type, + struct buffer_head ** bh, int * err) +{ + struct ext2_acl_header * aclh; + struct super_block * sb = inode->i_sb; + int i; + + ext2_acl_debug ("Reading ACL header (type = %d) for inode %s:%ld\n", + acl_type, kdevname (inode->i_dev), inode->i_ino); + *bh = NULL; + if (!sb->u.ext2_sb.s_acl_idx_inode) { + ext2_acl_debug ("No ACL index inode on device %s\n", + kdevname (inode->i_dev)); + *err = 0; + return NULL; + } + if (acl_type == ACL_TYPE_ACCESS) + i = inode->u.ext2_i.i_file_acl; + else + i = inode->u.ext2_i.i_dir_acl; + if (!i) { + ext2_acl_debug ("Null ACL pointer for inode %s:%ld\n", + kdevname (inode->i_dev), inode->i_ino); + *err = 0; + return NULL; + } + *bh = ext2_bread (sb->u.ext2_sb.s_acl_idx_inode, + i / EXT2_ACLH_PER_BLOCK(sb), 0, err); + if (!*bh) { + ext2_acl_debug ("Cannot read block for ACL %d\n", i); + return NULL; + } + aclh = ((struct ext2_acl_header *)(*bh)->b_data) + + i % EXT2_ACLH_PER_BLOCK(sb); +#ifdef EXT2FS_ACL_CHECK + check_acl_descriptor (inode->i_sb, *bh, EXT2_ACL_IDX_INO, + i / EXT2_ACLH_PER_BLOCK(sb)); + if (le32_to_cpu (aclh->aclh_no) != i) + ext2_error (inode->i_sb, "get_acl_header", + "aclh_no is wrong (%d != %d)", + le32_to_cpu (aclh->aclh_no), i); + if (le32_to_cpu (aclh->aclh_valid) != LINUX_ACL_VALID) + ext2_error (inode->i_sb, "get_acl_header", + "Valid flag wrong in ACL header for inode %s:%ld", + kdevname (inode->i_dev), inode->i_ino); +#endif + return aclh; + +} + +/* + * get_acl_entries () + * + * Read ACL entries + */ +static +struct ext2_acl_entry * get_acl_entries (struct inode * inode, + struct ext2_acl_header * aclh, + struct buffer_head ** bh, int * err) +{ + struct ext2_acl_entry * acle; + struct super_block * sb = inode->i_sb; + + ext2_acl_debug ("Reading ACL entries for inode %s:%ld\n", + kdevname (inode->i_dev), inode->i_ino); + *bh = NULL; + if (!le16_to_cpu (aclh->aclh_acle_count) || + !le32_to_cpu (aclh->aclh_first_acle)) { + ext2_acl_debug ("No ACL entries for inode %s:%ld\n", + kdevname (inode->i_dev), inode->i_ino); + *err = 0; + return NULL; + } + *bh = ext2_bread (sb->u.ext2_sb.s_acl_data_inode, + le32_to_cpu (aclh->aclh_first_acle) / + EXT2_ACLE_PER_BLOCK(sb), 0, err); + if (!*bh) { + ext2_acl_debug ("Cannot read block for ACL %d\n", + le32_to_cpu (aclh->aclh_first_acle)); + return NULL; + } + acle = ((struct ext2_acl_entry *)(*bh)->b_data) + + le32_to_cpu (aclh->aclh_first_acle) % EXT2_ACLE_PER_BLOCK(sb); +#ifdef EXT2FS_ACL_CHECK + check_acl_descriptor (inode->i_sb, *bh, EXT2_ACL_DATA_INO, + le32_to_cpu (aclh->aclh_first_acle) / + EXT2_ACLE_PER_BLOCK(sb)); + check_ext2_acl (inode, aclh, acle, aclh->aclh_acle_count); +#endif + return acle; +} + +/* + * ext2_inherit_acl () + * + * Inherit an ACL from a inode + */ +int ext2_inherit_acl (struct inode * from, struct inode * to, int from_type, + int to_type, int creatmode) +{ + struct ext2_acl_header * aclh; + struct ext2_acl_entry * acle; + struct buffer_head * bh; + struct buffer_head * bh1; + int mode = 0; + int got_mask = 0; + int err, i, mask; + + ext2_acl_debug ("Copying ACL from inode %s:%ld (type %d) to " + "inode %s:%ld (type %d)\n", + kdevname (from->i_dev), from->i_ino, from_type, + kdevname (to->i_dev), to->i_ino, to_type); + err = 0; + /* + * Get the ACL + */ + aclh = get_acl_header (from, from_type, &bh, &err); + if (!aclh) + return err; + /* + * Increment the ACL reference count + */ + aclh->aclh_ref_count = cpu_to_le32 (le32_to_cpu (aclh->aclh_ref_count) + 1); + /* + * Record the ACL for the new inode + */ + if (to_type == ACL_TYPE_ACCESS) { + to->u.ext2_i.i_file_acl = le32_to_cpu (aclh->aclh_no); + /* Update mode from ACL */ + acle = get_acl_entries ( to, aclh, &bh1, &err); + if(!acle) { + brelse (bh); + return err; + } + for( i= 0; i < le16_to_cpu (aclh->aclh_acle_count); i++) { + switch (le16_to_cpu (acle->acle_type)) { + case ACL_USER_OBJ: + mode |= (cpu_to_le16 (acle->acle_perms) << 6) & creatmode; + break; + case ACL_GROUP_OBJ: + mode |= (cpu_to_le16 (acle->acle_perms) << 3) & creatmode; + break; + case ACL_MASK: + got_mask = 1; + mask = cpu_to_le16 (acle->acle_perms); + break; + case ACL_OTHER_OBJ: + mode |= cpu_to_le16 (acle->acle_perms) & creatmode; + break; + } + acle++; + } + to->i_mode &= ~S_IRWXUGO; + to->i_mode |= mode; + to->i_ctime = CURRENT_TIME; + brelse (bh1); + } else { + to->u.ext2_i.i_dir_acl = le32_to_cpu (aclh->aclh_no); + } + /* Set ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_MASK and ACL_OTHER_OBJ + * according to creatmode + */ + err = ext2_update_acl_from_mode (to, mode, to_type); + /* + * Release the ACL header + * Isnīt this already made in ext2_update_acl_from_mode ? + */ + mark_buffer_dirty (bh, 0); + brelse (bh); + return err; +} + +/* + * ext2_copy_acl () + * + * Copy an ACL because a change is made to a shared ACL + * (kind of copy-on-write routine) + */ +int ext2_copy_acl (struct inode * inode, int acl_type) +{ + struct ext2_acl_header * aclh1; + struct ext2_acl_header * aclh2; + struct ext2_acl_entry * acle1; + struct ext2_acl_entry * acle2; + struct buffer_head * bh1; + struct buffer_head * bh2; + struct buffer_head * bh3; + struct buffer_head * bh4; + int i; + int err; + + ext2_acl_debug ("Copying ACL (type %d) of inode %s:%ld\n", + acl_type, kdevname (inode->i_dev), inode->i_ino); + err = 0; + /* + * Get the original ACL + */ + aclh1 = get_acl_header (inode, acl_type, &bh1, &err); + if (!aclh1) + return err; + if (le32_to_cpu (aclh1->aclh_ref_count) == 1) { + /* + * The ACL is not shared. No need to copy it + */ + brelse (bh1); + return 0; + } + acle1 = get_acl_entries (inode, aclh1, &bh2, &err); + if (!acle1) { + brelse (bh1); + return err; + } + /* + * Allocate a new ACL + */ + aclh2 = allocate_acl_header (inode, &bh3, &err); + if (!aclh2) { + brelse (bh1); + brelse (bh2); + return err; + } + acle2 = allocate_acl_entries (inode, aclh2, + le16_to_cpu (aclh1->aclh_acle_count), + &bh4, &err); + if (!acle2) { + free_acl_header (inode, aclh2, bh3); + brelse (bh1); + brelse (bh2); + brelse (bh3); + return err; + } + /* + * Copy the ACL + */ + /* No need to convert values. Both are in little-endian format */ + aclh2->aclh_acle_count = aclh1->aclh_acle_count; + aclh2->aclh_first_acle = acle2->acle_no; + for (i = 0; i < le16_to_cpu (aclh1->aclh_acle_count); i++) { + /* No need to convert values. Both are in little-endian format */ + acle2[i].acle_perms = acle1[i].acle_perms; + acle2[i].acle_acle_count = acle1[i].acle_acle_count; + acle2[i].acle_type = acle1[i].acle_type; + acle2[i].acle_tag = acle1[i].acle_tag; + acle2[i].acle_aclh = aclh2->aclh_no; + } + /* + * Update the reference counts + */ + aclh1->aclh_ref_count = cpu_to_le32 (le32_to_cpu (aclh1->aclh_ref_count) - 1); + /* + * Record the new ACL + */ + if (acl_type == ACL_TYPE_ACCESS) + inode->u.ext2_i.i_file_acl = le32_to_cpu (aclh2->aclh_no); + else + inode->u.ext2_i.i_dir_acl = le32_to_cpu (aclh2->aclh_no); + /* + * Release the ACLs + */ + mark_buffer_dirty (bh1, 0); + mark_buffer_dirty (bh3, 0); + mark_buffer_dirty (bh4, 0); + brelse (bh1); + brelse (bh2); + brelse (bh3); + brelse (bh4); + return 0; +} + +/* + * ext2_update_acl_from_mode () + * + * Update the three entries XXX_OBJ in an ACL when the mode of a file is + * changed + */ +int ext2_update_acl_from_mode (struct inode * inode, int mode, int acl_type) +{ + struct ext2_acl_header * aclh; + struct ext2_acl_entry * acle; + struct buffer_head * bh1; + struct buffer_head * bh2; + int err; + int i; + int got_mask = 0; + + ext2_acl_debug ("Updating ACL (type %d) for inode %s:%ld from %o\n", + acl_type, kdevname (inode->i_dev), inode->i_ino, mode); + /* + * Copy the ACL if it is shared + */ + if ((err = ext2_copy_acl (inode, acl_type))) + return err; + /* + * Get the ACL + */ + aclh = get_acl_header (inode, acl_type, &bh1, &err); + if (!aclh) + return err; + acle = get_acl_entries (inode, aclh, &bh2, &err); + if (!acle) { + brelse (bh1); + return err; + } + /* + * Update the entries + */ + for (i = 0; i < le16_to_cpu (aclh->aclh_acle_count); i++) + switch (le16_to_cpu (acle[i].acle_type)) { + case ACL_USER_OBJ: + acle[i].acle_perms = cpu_to_le16 (((mode >> 6) & S_IRWXO)); + break; + case ACL_MASK: + acle[i].acle_perms = cpu_to_le16 (((mode >> 3) & S_IRWXO)); + got_mask = 1; + break; + case ACL_GROUP_OBJ: + if(!got_mask) + acle[i].acle_perms = cpu_to_le16 (((mode >> 3) & S_IRWXO)); + break; + case ACL_OTHER_OBJ: + acle[i].acle_perms = cpu_to_le16 ((mode & S_IRWXO)); + break; + } + /* + * Release the ACL + */ + mark_buffer_dirty (bh2, 0); + brelse (bh1); + brelse (bh2); + return 0; +} + +/* + * update_mode_from_acl () + * + * Update the inode mode from an ACL that contains only the three required + * entries. The general version is implemented in copy_acl_in. + */ +static +int update_mode_from_acl (struct inode * inode, void * data) +{ + struct linux_acl_entry * lacle; + int mode; + + ext2_acl_debug ("Updating mode from acl for inode %s:%ld\n", + kdevname (inode->i_dev), inode->i_ino); + lacle = (struct linux_acl_entry *) ((char *) data + + sizeof (struct linux_acl)); + mode = lacle->acle_perms << 6; + lacle++; + mode |= lacle->acle_perms << 3; + lacle++; + mode |= lacle->acle_perms; + + inode->i_mode &= ~S_IRWXUGO; + inode->i_mode |= mode; + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty (inode); + return 0; /* bugfix: return 0 not 1 */ +} + + +/* + * ext2_acl_permission () + * + * Use ACL to check access rights + * fixed: le16_to_cpu was sometimes used wrong + * the permissions of all matching groups are granted + * (like solaris) + */ +static +int ext2_acl_permission (struct inode * inode, struct ext2_acl_entry * acle, + int n, int mask) +{ + struct ext2_acl_entry * aclp; + int in_group = 0; + int i; + int acl_mask = S_IRWXO; + + for (aclp = acle, i = 0; aclp != NULL && i < n; aclp++, i++) + switch (le16_to_cpu (aclp->acle_type)) { + case ACL_USER_OBJ: + if (current->fsuid == inode->i_uid) { + if ((le16_to_cpu (aclp->acle_perms) & mask & S_IRWXO) == mask) + return 0; + else + return -EACCES; + } + break; + + case ACL_MASK: + acl_mask = le16_to_cpu(aclp->acle_perms); + break; + + case ACL_USER: + if (current->fsuid == le32_to_cpu (aclp->acle_tag)) { + if ((le16_to_cpu (aclp->acle_perms) & mask & acl_mask & S_IRWXO) == mask) + return 0; + else + return -EACCES; + } + break; + + case ACL_GROUP_OBJ: + if (in_group_p (inode->i_gid)) { + in_group = 1; + if ((le16_to_cpu (aclp->acle_perms) & mask & acl_mask & S_IRWXO) == mask) + return 0; + } + break; + + case ACL_GROUP: + if (in_group_p (le32_to_cpu (aclp->acle_tag))) { + in_group = 1; + if ((le16_to_cpu (aclp->acle_perms) & mask & acl_mask & S_IRWXO) == mask) + return 0; + } + break; + + case ACL_OTHER_OBJ: + if (in_group){ + return -EACCES; + } + if ((le16_to_cpu (aclp->acle_perms) & mask & S_IRWXO) == mask) + return 0; + else + return -EACCES; + break; + + default: + ext2_error (inode->i_sb, "ext2_acl_permission", + "Bad ACL type (%d) for inode %s:%ld" + " at position %d", + le16_to_cpu (aclp->acle_type), + kdevname (inode->i_dev), + inode->i_ino, i); + break; + } + + return -EACCES; +} /* * ext2_permission () @@ -26,6 +1076,11 @@ int ext2_permission (struct inode * inode, int mask) { unsigned short mode = inode->i_mode; + struct ext2_acl_header * aclh = NULL; + struct ext2_acl_entry * acle = NULL; + struct buffer_head * bh1 = NULL; + struct buffer_head * bh2 = NULL; + int err; /* * Nobody gets write access to a file on a readonly-fs @@ -40,10 +1095,41 @@ if ((mask & S_IWOTH) && IS_IMMUTABLE(inode)) return -EACCES; + if (test_opt (inode->i_sb, USE_ACL)) { + /* + * Try to get ACL + */ + aclh = get_acl_header (inode, ACL_TYPE_ACCESS, &bh1, &err); +#if 0 + if (!aclh) + aclh = get_acl_header (inode, ACL_TYPE_DEFAULT, &bh1, + &err); +#endif + if (aclh) { + acle = get_acl_entries (inode, aclh, &bh2, &err); + if (!acle) { + brelse (bh1); + return err; + } + } + if (acle) { + /* + * Use the ACL to check permissions + */ + err = ext2_acl_permission (inode, acle, + aclh->aclh_acle_count, mask); + brelse (bh1); + brelse (bh2); + if (err == -EACCES && fsuser ()) + err = 0; + return err; + } + } + /* * If no ACL, checks using the file mode */ - else if (current->fsuid == inode->i_uid) + if (current->fsuid == inode->i_uid) mode >>= 6; else if (in_group_p (inode->i_gid)) mode >>= 3; @@ -51,11 +1137,378 @@ * Access is always granted for root. We now check last, * though, for BSD process accounting correctness */ - if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE)) + if (((mode & mask & S_IRWXO) == mask) || fsuser()) return 0; - if ((mask == S_IROTH) || - (S_ISDIR(mode) && !(mask & ~(S_IROTH | S_IXOTH)))) - if (capable(CAP_DAC_READ_SEARCH)) - return 0; - return -EACCES; + else + return -EACCES; +} + +/* + * copy_mode_to_acl () + * + * Copy the normal permissions into an ACL with the three required entries + */ +static +int copy_mode_to_acl (struct inode * inode, void * data) +{ + struct linux_acl * lacl; + struct linux_acl_entry * lacle; + + ext2_acl_debug ("Copying mode into an ACL for inode %s:%ld\n", + kdevname (inode->i_dev), inode->i_ino); + lacl = (struct linux_acl *)data; + lacl->acl_valid = LINUX_ACL_VALID; + lacl->acl_acle_count = 3; + lacle = (struct linux_acl_entry *) ((char *) data + + sizeof (struct linux_acl)); + lacle->acle_valid = LINUX_ACL_VALID; + lacle->acle_type = ACL_USER_OBJ; + lacle->acle_tag = inode->i_uid; + lacle->acle_perms = (inode->i_mode >> 6) & S_IRWXO; + lacle++; + lacle->acle_valid = LINUX_ACL_VALID; + lacle->acle_type = ACL_GROUP_OBJ; + lacle->acle_tag = inode->i_gid; + lacle->acle_perms = (inode->i_mode >> 3) & S_IRWXO; + lacle++; + lacle->acle_valid = LINUX_ACL_VALID; + lacle->acle_type = ACL_OTHER_OBJ; + lacle->acle_tag = inode->i_uid; + lacle->acle_perms = inode->i_mode & S_IRWXO; + return 0; +} + +/* + * make_null_acl () + * + * Return an empty ACL + */ +static +int make_null_acl (struct inode * inode, void * data) +{ + struct linux_acl * lacl; + + ext2_acl_debug ("Creating null ACL for inode %s:%ld\n", + kdevname (inode->i_dev), inode->i_ino); + lacl = (struct linux_acl *)data; + lacl->acl_valid = LINUX_ACL_VALID; + lacl->acl_acle_count = 0; + return 0; +} + +/* + * copy_acl_out () + * + * Copy an ACL to user space + */ +static +int copy_acl_out (struct inode * inode, int acl_type, + struct ext2_acl_header * aclh, void * data) +{ + struct ext2_acl_entry * acle; + struct buffer_head * bh; + struct linux_acl * lacl; + struct linux_acl_entry * lacle; + int i; + int err; + + ext2_acl_debug ("Copying ACL out for inode %s:%ld\n", + kdevname (inode->i_dev), inode->i_ino); + if (!aclh || !le16_to_cpu (aclh->aclh_acle_count) || + !le32_to_cpu (aclh->aclh_first_acle)) { + if (le16_to_cpu (acl_type) == ACL_TYPE_ACCESS) + return copy_mode_to_acl (inode, data); + else + return make_null_acl (inode, data); + } + acle = get_acl_entries (inode, aclh, &bh, &err); + if (!acle) + return err; + lacl = (struct linux_acl *)data; + lacl->acl_valid = LINUX_ACL_VALID; + lacl->acl_acle_count = le16_to_cpu (aclh->aclh_acle_count); + lacle = (struct linux_acl_entry *) ((char *) data + + sizeof (struct linux_acl)); + for (i = 0; i < le16_to_cpu (aclh->aclh_acle_count); i++) { + lacle->acle_valid = LINUX_ACL_VALID; + lacle->acle_type = le16_to_cpu (acle[i].acle_type); + lacle->acle_tag = le32_to_cpu (acle[i].acle_tag); + lacle->acle_perms = le16_to_cpu (acle[i].acle_perms); + lacle++; + } + if (bh) + brelse (bh); + return 0; +} + +/* + * copy_acl_in () + * + * Copy an ACL from user space and update the inode mode + */ +static +int copy_acl_in (struct inode * inode, int acl_type, + struct ext2_acl_header * aclh, struct ext2_acl_entry * acle, + void * data) +{ + struct linux_acl * lacl; + struct linux_acl_entry * lacle; + int i; + int mode = 0; + int got_mask = 0; + int isexec = 0; + + ext2_acl_debug ("Copying ACL in for inode %s:%ld\n", + kdevname (inode->i_dev), inode->i_ino); + lacl = (struct linux_acl *)data; + aclh->aclh_valid = cpu_to_le32 (LINUX_ACL_VALID); + aclh->aclh_acle_count = cpu_to_le16 (lacl->acl_acle_count); + lacle = (struct linux_acl_entry *) ((char *) data + + sizeof (struct linux_acl)); + for (i = 0; i < le16_to_cpu (aclh->aclh_acle_count); i++) { + acle[i].acle_valid = cpu_to_le32 (LINUX_ACL_VALID); + acle[i].acle_type = cpu_to_le16 (lacle->acle_type); + acle[i].acle_tag = cpu_to_le32 (lacle->acle_tag); + acle[i].acle_perms = cpu_to_le16 (lacle->acle_perms); + isexec |= lacle->acle_perms & 0x111; + if (acl_type == ACL_TYPE_ACCESS) + switch (lacle->acle_type) { + case ACL_USER_OBJ: + mode |= lacle->acle_perms << 6; + break; + case ACL_MASK: + mode |= lacle->acle_perms << 3; + got_mask = 1; + break; + case ACL_GROUP_OBJ: + if(!got_mask) + mode |= lacle->acle_perms << 3; + break; + case ACL_OTHER_OBJ: + mode |= lacle->acle_perms; + break; + } + lacle++; + } + if (acl_type == ACL_TYPE_ACCESS) { + inode->i_mode &= ~S_IRWXUGO; + if(isexec){ + inode->i_mode |= S_IXC; + } else { + inode->i_mode &= ~S_IXC; + } + inode->i_mode |= mode; + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty (inode); + } + return 0; +} + +/* + * ext2_remove_acl () + * + * Remove an ACL + */ +int ext2_remove_acl (struct inode * inode, int acl_type) +{ + struct ext2_acl_header * aclh; + struct ext2_acl_entry * acle; + struct buffer_head * bh1; + struct buffer_head * bh2; + int err; + + ext2_acl_debug ("Removing ACL (type %d) on inode %s:%ld\n", + acl_type, kdevname (inode->i_dev), inode->i_ino); + /* + * Get the ACL header + */ + aclh = get_acl_header (inode, acl_type, &bh1, &err); + if (!aclh) + return err; + /* + * Decrement reference count + */ + + aclh->aclh_ref_count = cpu_to_le32 (le32_to_cpu (aclh->aclh_ref_count) - 1); + if(!le32_to_cpu (aclh->aclh_ref_count)) { + /* + * If the reference count is nul, remove the ACL entries and the ACL + * header + */ + acle = get_acl_entries (inode, aclh, &bh2, &err); + if (acle) { + err = free_acl_entries (inode, acle, + le16_to_cpu (aclh->aclh_acle_count), + bh2); + } + err = free_acl_header (inode, aclh, bh1); + } + /* + * Update the pointers in the inode + */ + if (acl_type == ACL_TYPE_ACCESS) + inode->u.ext2_i.i_file_acl = 0; + else + inode->u.ext2_i.i_dir_acl = 0; + inode->i_mode &= ~S_IXC; + mark_inode_dirty (inode); + return 0; +} + +/* + * set_acl () + * + * Set an ACL on an inode + */ +static +int set_acl (struct inode * inode, int acl_type, void * data) +{ + struct ext2_acl_header * aclh; + struct ext2_acl_entry * acle; + struct buffer_head * bh1; + struct buffer_head * bh2; + unsigned long acle_count; + int err; + + ext2_acl_debug ("Setting ACL (type %d) on inode %s:%ld\n", + acl_type, kdevname (inode->i_dev), inode->i_ino); + /* + * Check that the ACL is valid + */ + if (((struct linux_acl *)data)->acl_valid != LINUX_ACL_VALID) + return -EINVAL; + acle_count = ((struct linux_acl *)data)->acl_acle_count; + + /* + * Check that the ACL can be stored as contiguous entries + */ + if (acle_count > inode->i_sb->u.ext2_sb.s_max_acle_block) { + ext2_acl_debug ("Setting ACL (type %d) on inode %s:%ld: %s\n", + acl_type, kdevname (inode->i_dev), inode->i_ino, + "acl is too big"); + err = -EACL2BIG; + return err; + } + + /* + * Remove any existing ACL on this file + */ + err = ext2_remove_acl (inode, acl_type); + if (err) + return err; + /* + * If the ACL contains only the three required entries, just update + * the mode + */ + if (acle_count == 3 && acl_type == ACL_TYPE_ACCESS) + return update_mode_from_acl (inode, data); + /* + * Allocate an ACL header + */ + aclh = allocate_acl_header (inode, &bh1, &err); + if (!aclh) + return err; + /* + * Allocate ACL entries + */ + acle = allocate_acl_entries (inode, aclh, acle_count, &bh2, &err); + if (!acle) { + free_acl_header (inode, aclh, bh1); + brelse (bh1); + return err; + } + /* + * Copy the ACL + */ + if ((err = copy_acl_in (inode, acl_type, aclh, acle, data))) { + free_acl_entries (inode, acle, + le16_to_cpu (aclh->aclh_acle_count), bh2); + free_acl_header (inode, aclh, bh1); + brelse (bh1); + brelse (bh2); + return err; + } + /* + * Record the ACL + */ + if (acl_type == ACL_TYPE_ACCESS) + inode->u.ext2_i.i_file_acl = aclh->aclh_no; + else + inode->u.ext2_i.i_dir_acl = aclh->aclh_no; + /* + * Release the ACL + */ + mark_buffer_dirty (bh1, 0); + mark_buffer_dirty (bh2, 0); + brelse (bh1); + brelse (bh2); + return 0; +} + +/* + * ext2_acl_ctl () + * + * ACL control + */ +int ext2_acl_ctl (struct inode * inode, int operation, int acl_type, + void * data) +{ + struct ext2_acl_header * aclh; + struct buffer_head * bh = NULL; + int n1, n2, n3; + int error = 0; + + ext2_acl_debug ("inode = %s:%ld, operation = %d, acl_type = %d\n", + kdevname (inode->i_dev), inode->i_ino, operation, + acl_type); + if (!test_opt (inode->i_sb, USE_ACL)) { + ext2_warning (inode->i_sb, "ext2_acl_ctl", + "ext2_acl_ctl called on FS without ACL"); + return -ENOTSUPP; + } + switch (operation) { + case LINUX_ACL_GET_SIZE_FD: + case LINUX_ACL_GET_SIZE_FILE: + aclh = get_acl_header (inode, acl_type, &bh, &error); + if (aclh) { + n1 = le16_to_cpu (aclh->aclh_acle_count); + brelse (bh); + } else { + if (acl_type == ACL_TYPE_ACCESS) + n1 = 3; /* Normal permissions */ + else + n1 = 0; /* No default ACL */ + } + return n1; + case LINUX_ACL_GET_FD: + case LINUX_ACL_GET_FILE: + aclh = get_acl_header (inode, acl_type, &bh, &error); + if (aclh) + n1 = le16_to_cpu (aclh->aclh_acle_count); + else { + if (acl_type == ACL_TYPE_ACCESS) + n1 = 3; /* Normal permissions */ + else + n1 = 0; /* No default ACL */ + } + n2 = sizeof (struct linux_acl) + + sizeof (struct linux_acl_entry) * n1; + n3 = ((struct linux_acl *)data)->acl_acle_count; + if (n1 > n3) + error = -ERANGE; + else { + error = copy_acl_out (inode, acl_type, + aclh, data); + } + if (bh) + brelse (bh); + return error; + case LINUX_ACL_SET_FD: + case LINUX_ACL_SET_FILE: + return set_acl (inode, acl_type, data); + case LINUX_ACL_DELETE_FD: + case LINUX_ACL_DELETE_FILE: + return ext2_remove_acl (inode, acl_type); + } + return 0; } diff -Nur linux-2.2.0-pre9/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c --- linux-2.2.0-pre9/fs/ext2/ialloc.c Tue Oct 20 23:08:14 1998 +++ linux/fs/ext2/ialloc.c Tue Apr 13 13:55:23 1999 @@ -27,6 +27,7 @@ * when a file system is mounted (see ext2_read_super). */ +#include #include #include #include @@ -236,6 +237,12 @@ is_directory = S_ISDIR(inode->i_mode); + if(inode->u.ext2_i.i_file_acl > 0) + ext2_remove_acl (inode, ACL_TYPE_ACCESS); + + if(is_directory && inode->u.ext2_i.i_dir_acl > 0) + ext2_remove_acl (inode, ACL_TYPE_DEFAULT); + /* Do this BEFORE marking the inode not in use */ clear_inode (inode); @@ -492,6 +499,25 @@ inode->i_op = NULL; if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) inode->i_flags |= MS_SYNCHRONOUS; + if (test_opt (dir->i_sb, USE_ACL)) { + if (S_ISDIR(mode)) { + *err = ext2_inherit_acl ((struct inode *)dir, inode, + ACL_TYPE_DEFAULT, ACL_TYPE_DEFAULT, mode); + if (*err) { + inode->i_nlink = 0; + iput (inode); + return NULL; + } + } + *err = ext2_inherit_acl ((struct inode *)dir, inode, + ACL_TYPE_DEFAULT, ACL_TYPE_ACCESS, mode); + if (*err) { + inode->i_nlink = 0; + iput (inode); + return NULL; + } + } + insert_inode_hash(inode); mark_inode_dirty(inode); inc_inode_version (inode, gdp, mode); diff -Nur linux-2.2.0-pre9/fs/ext2/inode.c linux/fs/ext2/inode.c --- linux-2.2.0-pre9/fs/ext2/inode.c Wed May 6 19:56:05 1998 +++ linux/fs/ext2/inode.c Mon Jan 25 14:05:32 1999 @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -479,7 +480,8 @@ unsigned long offset; struct ext2_group_desc * gdp; - if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO && + if ((inode->i_ino != EXT2_ROOT_INO && + inode->i_ino != EXT2_ACL_IDX_INO && inode->i_ino != EXT2_ACL_DATA_INO && inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) || inode->i_ino > le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) { @@ -620,6 +622,8 @@ struct ext2_group_desc * gdp; if ((inode->i_ino != EXT2_ROOT_INO && + inode->i_ino != EXT2_ACL_IDX_INO && + inode->i_ino != EXT2_ACL_DATA_INO && inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) || inode->i_ino > le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) { ext2_error (inode->i_sb, "ext2_write_inode", @@ -720,23 +724,36 @@ struct inode *inode = dentry->d_inode; int retval; unsigned int flags; - + /* retval = -EPERM; - if ((iattr->ia_attr_flags & - (ATTR_FLAG_APPEND | ATTR_FLAG_IMMUTABLE)) ^ - (inode->u.ext2_i.i_flags & - (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) { - if (!capable(CAP_LINUX_IMMUTABLE)) - goto out; - } else if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) - goto out; + if (iattr->ia_valid & ATTR_ATTR_FLAG) { + if ((iattr->ia_attr_flags & + (ATTR_FLAG_APPEND | ATTR_FLAG_IMMUTABLE)) ^ + (inode->u.ext2_i.i_flags & + (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) { + if (!fsuser()) + goto out; + } + } else if ((current->fsuid != inode->i_uid) && !fsuser()) + goto out; + */ retval = inode_change_ok(inode, iattr); if (retval != 0) goto out; + if (test_opt (inode->i_sb, USE_ACL)) { + if (iattr->ia_valid & ATTR_MODE) { + retval = ext2_update_acl_from_mode (inode, + iattr->ia_mode, + ACL_TYPE_ACCESS); + if (retval) + goto out; + } + } + inode_setattr(inode, iattr); - + /* flags = iattr->ia_attr_flags; if (flags & ATTR_FLAG_SYNCRONOUS) { inode->i_flags |= MS_SYNCHRONOUS; @@ -767,6 +784,7 @@ inode->u.ext2_i.i_flags &= ~EXT2_IMMUTABLE_FL; } mark_inode_dirty(inode); + */ out: return retval; } diff -Nur linux-2.2.0-pre9/fs/ext2/namei.c linux/fs/ext2/namei.c --- linux-2.2.0-pre9/fs/ext2/namei.c Fri Dec 18 16:09:35 1998 +++ linux/fs/ext2/namei.c Tue Apr 13 14:06:41 1999 @@ -378,7 +378,10 @@ return err; inode->i_op = &ext2_file_inode_operations; - inode->i_mode = mode; + /* + * This is already done in ext2_new_inode with respect to ACLs + * inode->i_mode = mode; + */ mark_inode_dirty(inode); bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) { @@ -418,7 +421,10 @@ goto out; inode->i_uid = current->fsuid; - inode->i_mode = mode; + /* + * This is already done in ext2_new_inode with respect to ACLs + * inode->i_mode = mode; + */ inode->i_op = NULL; bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) @@ -483,7 +489,7 @@ goto out; err = -EIO; - inode = ext2_new_inode (dir, S_IFDIR, &err); + inode = ext2_new_inode (dir, S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask), &err); if (!inode) goto out; @@ -516,7 +522,10 @@ inode->i_nlink = 2; mark_buffer_dirty(dir_block, 1); brelse (dir_block); - inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask); + /* + * This is already done in ext2_new_inode + * inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask); + */ if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; mark_inode_dirty(inode); @@ -734,6 +743,9 @@ if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) { return err; } + /* + * Overwrite perms set by ext2_new_inode + */ inode->i_mode = S_IFLNK | S_IRWXUGO; inode->i_op = &ext2_symlink_inode_operations; for (l = 0; l < inode->i_sb->s_blocksize - 1 && diff -Nur linux-2.2.0-pre9/fs/ext2/super.c linux/fs/ext2/super.c --- linux-2.2.0-pre9/fs/ext2/super.c Thu Oct 29 06:54:56 1998 +++ linux/fs/ext2/super.c Mon Jan 25 14:05:32 1999 @@ -111,6 +111,10 @@ sb->u.ext2_sb.s_es->s_state = le16_to_cpu(sb->u.ext2_sb.s_mount_state); mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); } + if (sb->u.ext2_sb.s_acl_idx_inode) + iput (sb->u.ext2_sb.s_acl_idx_inode); + if (sb->u.ext2_sb.s_acl_data_inode) + iput (sb->u.ext2_sb.s_acl_data_inode); db_count = sb->u.ext2_sb.s_db_per_group; for (i = 0; i < db_count; i++) if (sb->u.ext2_sb.s_group_desc[i]) @@ -134,11 +138,14 @@ ext2_write_inode, ext2_put_inode, ext2_delete_inode, - NULL, + ext2_notify_change, ext2_put_super, ext2_write_super, ext2_statfs, - ext2_remount + ext2_remount, + NULL, + NULL, + ext2_acl_ctl }; /* @@ -158,7 +165,14 @@ this_char = strtok (NULL, ",")) { if ((value = strchr (this_char, '=')) != NULL) *value++ = 0; - if (!strcmp (this_char, "bsddf")) + if (!strcmp (this_char, "acl")) { + if (!value || !*value) + set_opt (*mount_options, USE_ACL); + else if (!strcmp (value, "yes")) + set_opt (*mount_options, USE_ACL); + else + clear_opt (*mount_options, USE_ACL); + } else if (!strcmp (this_char, "bsddf")) clear_opt (*mount_options, MINIX_DF); else if (!strcmp (this_char, "check")) { if (!value || !*value) @@ -644,6 +658,28 @@ MOD_DEC_USE_COUNT; return NULL; } + if (test_opt(sb, USE_ACL)) { + sb->u.ext2_sb.s_acl_idx_inode = iget (sb, EXT2_ACL_IDX_INO); + sb->u.ext2_sb.s_acl_data_inode = iget (sb, EXT2_ACL_DATA_INO); + if (!sb->u.ext2_sb.s_acl_idx_inode || + !sb->u.ext2_sb.s_acl_data_inode) { + iput (sb->u.ext2_sb.s_acl_idx_inode); + iput (sb->u.ext2_sb.s_acl_data_inode); + dput (sb->s_root); + sb->s_dev = 0; + for (i = 0; i < db_count; i++) + if (sb->u.ext2_sb.s_group_desc[i]) + brelse (sb->u.ext2_sb.s_group_desc[i]); + kfree_s (sb->u.ext2_sb.s_group_desc, + db_count * sizeof (struct buffer_head *)); + brelse (bh); + printk ("EXT2-fs: get ACL inodes failed\n"); + MOD_DEC_USE_COUNT; + return NULL; + } + } + sb->u.ext2_sb.s_max_aclh_block = EXT2_ACLH_PER_BLOCK(sb) - 2; /* FIXME */ + sb->u.ext2_sb.s_max_acle_block = EXT2_ACLE_PER_BLOCK(sb) - 2; /* FIXME */ ext2_setup_super (sb, es); return sb; } diff -Nur linux-2.2.0-pre9/include/asm-i386/errno.h linux/include/asm-i386/errno.h --- linux-2.2.0-pre9/include/asm-i386/errno.h Tue Apr 15 01:28:18 1997 +++ linux/include/asm-i386/errno.h Fri May 21 15:46:15 1999 @@ -128,5 +128,7 @@ #define ENOMEDIUM 123 /* No medium found */ #define EMEDIUMTYPE 124 /* Wrong medium type */ +#define EACL2BIG 125 /* ACL too big */ +#define ENOTSUPP 126 /* Operation not supported */ #endif diff -Nur linux-2.2.0-pre9/include/asm-i386/unistd.h linux/include/asm-i386/unistd.h --- linux-2.2.0-pre9/include/asm-i386/unistd.h Wed Jan 20 20:06:24 1999 +++ linux/include/asm-i386/unistd.h Tue Jan 26 11:57:10 1999 @@ -195,6 +195,7 @@ #define __NR_getpmsg 188 /* some people actually want streams */ #define __NR_putpmsg 189 /* some people actually want streams */ #define __NR_vfork 190 +#define __NR_acl_ctl 191 /* user-visible error numbers are in the range -1 - -122: see */ diff -Nur linux-2.2.0-pre9/include/linux/acl.h linux/include/linux/acl.h --- linux-2.2.0-pre9/include/linux/acl.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/acl.h Mon Apr 19 10:16:44 1999 @@ -0,0 +1,68 @@ +/* + * linux/acl.h + */ + +#ifndef _LINUX_ACL_H +#define _LINUX_ACL_H + +#include +#include + +/* + * Constants required by P1003.6 / D13 + */ +#define ACL_USER_OBJ 1 +#define ACL_USER 2 +#define ACL_GROUP_OBJ 3 +#define ACL_GROUP 4 +#define ACL_OTHER_OBJ 5 +#define ACL_MASK 6 + +#define ACL_EXECUTE S_IXOTH +#define ACL_WRITE S_IWOTH +#define ACL_READ S_IROTH + +#define ACL_TYPE_ACCESS 1 +#define ACL_TYPE_DEFAULT 2 + +#define _POSIX_ACL_PATH_MAX 30 +#define _POSIX_ACL + +/* + * Structures used by the kernel + */ +struct linux_acl_entry { + __u32 acle_valid; /* Valid flag */ + __u16 acle_type; /* Type of ACL entry */ + __u16 acle_pad; + __u32 acle_perms; /* Permissions */ + __u32 acle_tag; /* uid/gid */ + __u32 acle_reserved[4]; +}; + +struct linux_acl { + __u32 acl_valid; /* Valid flag */ + __u32 acl_acle_count; /* # of ACL entries */ + __u32 acl_reserved1; + __u32 acl_reserved2; + struct linux_acl_entry acl_acle[0]; /* Array of ACL entries */ +}; + +#define LINUX_ACL_VALID 0x14091969 + +/* + * In this implementation of ACL, a single system call ``acl_ctl()'' is used + * to act on ACL: + * int acl_ctl (int operation, void * file, int acl_type, void * data); + * Operation codes are: + */ +#define LINUX_ACL_GET_SIZE_FD 1 /* Get the ACL size from open file */ +#define LINUX_ACL_GET_SIZE_FILE 2 /* Get the ACL size from file */ +#define LINUX_ACL_GET_FD 3 /* Get the ACL from open file */ +#define LINUX_ACL_GET_FILE 4 /* Get the ACL from file */ +#define LINUX_ACL_SET_FD 5 /* Set the ACL on open file */ +#define LINUX_ACL_SET_FILE 6 /* Set the ACL on file */ +#define LINUX_ACL_DELETE_FD 7 /* Delete the ACL on open file */ +#define LINUX_ACL_DELETE_FILE 8 /* Delete the ACL on file */ + +#endif diff -Nur linux-2.2.0-pre9/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h --- linux-2.2.0-pre9/include/linux/ext2_fs.h Mon Dec 28 07:18:28 1998 +++ linux/include/linux/ext2_fs.h Tue Apr 13 13:54:46 1999 @@ -28,6 +28,17 @@ #undef EXT2FS_DEBUG /* + * Define EXT2FS_ACL_DEBUG to produce debug messages during ACL management + */ +#define EXT2FS_ACL_DEBUG + +/* + * Define EXT2FS_ACL_CHECK to activate additional checks in ACL management + */ +#define EXT2FS_ACL_CHECK + + +/* * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files */ #define EXT2_PREALLOCATE @@ -36,8 +47,8 @@ /* * The second extended file system version */ -#define EXT2FS_DATE "95/08/09" -#define EXT2FS_VERSION "0.5b" +#define EXT2FS_DATE "98/01/15" +#define EXT2FS_VERSION "0.5b-live" /* * Debug code @@ -53,12 +64,26 @@ #endif /* + * ACL debug code + */ +#ifdef EXT2FS_ACL_DEBUG +# define ext2_acl_debug(f, a...) { \ + printk ("EXT2-fs ACL DEBUG (%s, %d): %s: ", \ + __FILE__, __LINE__, __FUNCTION__); \ + printk (f, ## a); \ + } +#else +# define ext2_acl_debug(f, a...) /**/ +#endif + + +/* * Special inodes numbers */ #define EXT2_BAD_INO 1 /* Bad blocks inode */ #define EXT2_ROOT_INO 2 /* Root inode */ -#define EXT2_ACL_IDX_INO 3 /* ACL inode */ -#define EXT2_ACL_DATA_INO 4 /* ACL inode */ +#define EXT2_ACL_IDX_INO 3 /* ACL index inode */ +#define EXT2_ACL_DATA_INO 4 /* ACL data inode */ #define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ #define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ @@ -86,6 +111,7 @@ #else # define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) #endif +#define EXT2_ACLH_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_header)) #define EXT2_ACLE_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry)) #define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) #ifdef __KERNEL__ @@ -120,6 +146,7 @@ # define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s)) #endif +#if 0 /* * ACL structures */ @@ -142,6 +169,62 @@ /* same inode or on next free entry */ }; +#else +/* + * ACL structures (They must all be the same size!) + * + * Each block in the ACL inodes have the following format: + * + * +---------------------------------+ + * |Block descriptor (ext2_acl_desc) | + * +---------------------------------+ + * | Bitmap | + * +---------------------------------+ + * | Header or Entry #1 | + * +---------------------------------+ + * | ... | + * +---------------------------------+ + */ +struct ext2_acl_desc /* Descriptor of blocks in the ACL inodes */ +{ + __u16 acld_max_acl; /* Size of largest free fragment */ + __u16 acld_min_acl; /* Size of smallest free fragment */ + __u16 acld_free_acl; /* Count of free acl */ + __u16 acld_pad; + __u32 acld_reserved[6]; +}; + +struct ext2_acl_header /* Header of Access Control Lists */ +{ + __u32 aclh_valid; /* Valid flag */ + __u16 aclh_version; /* Version of header */ + __u16 aclh_acle_count;/* Count of ACL entries */ + __u32 aclh_ref_count; /* Count of files that point to this header */ + __u32 aclh_first_acle;/* Address of first ACL entry */ + __u32 aclh_no; /* Number of this header (used for checks) */ + __u32 aclh_reserved[3]; +}; + +struct ext2_acl_entry /* Access Control List Entry */ +{ + __u32 acle_valid; /* Valid flag */ + __u16 acle_version; /* Version of entry */ + __u16 acle_perms; /* Access permissions */ + __u16 acle_acle_count;/* Count of ACL entries */ + __u16 acle_type; /* Type of entry */ + __u32 acle_tag; /* User or group identity */ + __u32 acle_aclh; /* Pointer to the header (used for checks) */ + __u32 acle_no; /* Number of this entry (used for checks) */ + __u32 acle_next; /* Pointer to next entry for the */ + /* same inode or to next free entry */ + __u32 acle_reserved; +}; + +#define EXT2_ACLH_VERSION 1 +#define EXT2_ACLE_VERSION 1 + +#endif + /* * Structure of a blocks group descriptor */ @@ -310,6 +393,7 @@ #define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ #define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ #define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ +#define EXT2_MOUNT_USE_ACL 0x0100 /* Use ACL */ #define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt #define set_opt(o, opt) o |= EXT2_MOUNT_##opt @@ -517,7 +601,12 @@ # define NORET_AND noreturn, /* acl.c */ +extern int ext2_inherit_acl (struct inode *, struct inode *, int, int, int); +extern int ext2_copy_acl (struct inode *, int); +extern int ext2_update_acl_from_mode (struct inode *, int, int); extern int ext2_permission (struct inode *, int); +extern int ext2_remove_acl (struct inode *, int); +extern int ext2_acl_ctl (struct inode *, int, int, void *); /* balloc.c */ extern int ext2_group_sparse(int group); @@ -564,6 +653,7 @@ extern void ext2_put_inode (struct inode *); extern void ext2_delete_inode (struct inode *); extern int ext2_sync_inode (struct inode *); +extern int ext2_notify_change (struct dentry *, struct iattr *); extern void ext2_discard_prealloc (struct inode *); /* ioctl.c */ diff -Nur linux-2.2.0-pre9/include/linux/ext2_fs_sb.h linux/include/linux/ext2_fs_sb.h --- linux-2.2.0-pre9/include/linux/ext2_fs_sb.h Mon Dec 28 07:18:28 1998 +++ linux/include/linux/ext2_fs_sb.h Tue Apr 13 14:08:28 1999 @@ -63,6 +63,10 @@ int s_feature_compat; int s_feature_incompat; int s_feature_ro_compat; + struct inode * s_acl_idx_inode; /* ACL Index inode */ + struct inode * s_acl_data_inode;/* ACL data inode */ + unsigned short s_max_aclh_block;/* Max # of ACL headers in a block */ + unsigned short s_max_acle_block;/* Max # of ACL entries in a block */ }; #endif /* _LINUX_EXT2_FS_SB */ diff -Nur linux-2.2.0-pre9/include/linux/fs.h linux/include/linux/fs.h --- linux-2.2.0-pre9/include/linux/fs.h Thu Jan 21 01:23:30 1999 +++ linux/include/linux/fs.h Fri May 21 15:47:29 1999 @@ -627,6 +627,7 @@ int (*remount_fs) (struct super_block *, int *, char *); void (*clear_inode) (struct inode *); void (*umount_begin) (struct super_block *); + int (*acl_ctl) (struct inode *, int, int, void *); }; struct dquot_operations { diff -Nur linux-2.2.0-pre9/include/linux/stat.h linux/include/linux/stat.h --- linux-2.2.0-pre9/include/linux/stat.h Mon May 25 19:32:52 1998 +++ linux/include/linux/stat.h Fri Apr 9 12:28:54 1999 @@ -9,6 +9,8 @@ #if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) +#define S_IXC 0200000 + #define S_IFMT 00170000 #define S_IFSOCK 0140000 #define S_IFLNK 0120000