/* * linux/fs/affs/inode.c * * (c) 1996 Hans-Joachim Widmaier - Rewritten * * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. * * (C) 1992 Eric Youngdale Modified for ISO9660 filesystem. * * (C) 1991 Linus Torvalds - minix filesystem */ #define DEBUG 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int *blk_size[]; extern struct timezone sys_tz; #define MIN(a,b) (((a)<(b))?(a):(b)) unsigned long affs_parent_ino(struct inode *dir) { int root_ino = (dir->i_sb->u.affs_sb.s_root_block); if (!S_ISDIR(dir->i_mode)) { affs_error(dir->i_sb,"parent_ino","Trying to get parent of non-directory"); return root_ino; } if (dir->i_ino == root_ino) return root_ino; return dir->u.affs_i.i_parent; } void affs_read_inode(struct inode *inode) { struct buffer_head *bh; struct file_front *file_front; struct file_end *file_end; s32 block; unsigned long prot; s32 ptype, stype; unsigned short id; pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino); block = inode->i_ino; if (!(bh = affs_bread(inode->i_dev,block,AFFS_I2BSIZE(inode)))) { affs_error(inode->i_sb,"read_inode","Cannot read block %d",block); return; } if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) || ptype != T_SHORT) { affs_error(inode->i_sb,"read_inode", "Checksum or type (ptype=%d) error on inode %d",ptype,block); affs_brelse(bh); return; } file_front = (struct file_front *)bh->b_data; file_end = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode)); prot = (be32_to_cpu(file_end->protect) & ~0x10) ^ FIBF_OWNER; inode->u.affs_i.i_protect = prot; inode->u.affs_i.i_parent = be32_to_cpu(file_end->parent); inode->u.affs_i.i_original = 0; inode->u.affs_i.i_zone = 0; inode->u.affs_i.i_hlink = 0; inode->u.affs_i.i_pa_cnt = 0; inode->u.affs_i.i_pa_next = 0; inode->u.affs_i.i_pa_last = 0; inode->u.affs_i.i_ec = NULL; inode->u.affs_i.i_lastblock = -1; inode->i_nlink = 1; inode->i_mode = 0; if (inode->i_sb->u.affs_sb.s_flags & SF_SETMODE) inode->i_mode = inode->i_sb->u.affs_sb.s_mode; else inode->i_mode = prot_to_mode(prot); if (inode->i_sb->u.affs_sb.s_flags & SF_SETUID) inode->i_uid = inode->i_sb->u.affs_sb.s_uid; id = be16_to_cpu(file_end->owner_uid); if (id == 0 || inode->i_sb->u.affs_sb.s_flags & SF_SETUID) inode->i_uid = inode->i_sb->u.affs_sb.s_uid; else if (id == 0xFFFF && inode->i_sb->u.affs_sb.s_flags & SF_MUFS) inode->i_uid = 0; else inode->i_uid = id; id = be16_to_cpu(file_end->owner_gid); if (id == 0 || inode->i_sb->u.affs_sb.s_flags & SF_SETGID) inode->i_gid = inode->i_sb->u.affs_sb.s_gid; else if (id == 0xFFFF && inode->i_sb->u.affs_sb.s_flags & SF_MUFS) inode->i_gid = 0; else inode->i_gid = id; switch (be32_to_cpu(file_end->secondary_type)) { case ST_ROOT: inode->i_uid = inode->i_sb->u.affs_sb.s_uid; inode->i_gid = inode->i_sb->u.affs_sb.s_gid; case ST_USERDIR: if (be32_to_cpu(file_end->secondary_type) == ST_USERDIR || inode->i_sb->u.affs_sb.s_flags & SF_SETMODE) { if (inode->i_mode & S_IRUSR) inode->i_mode |= S_IXUSR; if (inode->i_mode & S_IRGRP) inode->i_mode |= S_IXGRP; if (inode->i_mode & S_IROTH) inode->i_mode |= S_IXOTH; inode->i_mode |= S_IFDIR; } else inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR; inode->i_size = 0; break; case ST_LINKDIR: affs_error(inode->i_sb,"read_inode","inode is LINKDIR"); affs_brelse(bh); return; case ST_LINKFILE: affs_error(inode->i_sb,"read_inode","inode is LINKFILE"); affs_brelse(bh); return; case ST_FILE: inode->i_mode |= S_IFREG; inode->i_size = be32_to_cpu(file_end->byte_size); if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) block = AFFS_I2BSIZE(inode) - 24; else block = AFFS_I2BSIZE(inode); inode->u.affs_i.i_lastblock = ((inode->i_size + block - 1) / block) - 1; break; case ST_SOFTLINK: inode->i_mode |= S_IFLNK; inode->i_size = 0; break; } inode->i_mtime = inode->i_atime = inode->i_ctime = (be32_to_cpu(file_end->created.ds_Days) * (24 * 60 * 60) + be32_to_cpu(file_end->created.ds_Minute) * 60 + be32_to_cpu(file_end->created.ds_Tick) / 50 + ((8 * 365 + 2) * 24 * 60 * 60)) + sys_tz.tz_minuteswest * 60; affs_brelse(bh); inode->i_op = NULL; if (S_ISREG(inode->i_mode)) { if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) { inode->i_op = &affs_file_inode_operations_ofs; } else { inode->i_op = &affs_file_inode_operations; } } else if (S_ISDIR(inode->i_mode)) { /* Maybe it should be controlled by mount parameter? */ inode->i_mode |= S_ISVTX; inode->i_op = &affs_dir_inode_operations; } else if (S_ISLNK(inode->i_mode)) inode->i_op = &affs_symlink_inode_operations; } void affs_write_inode(struct inode *inode) { struct buffer_head *bh; struct file_end *file_end; uid_t uid; gid_t gid; pr_debug("AFFS: write_inode(%lu)\n",inode->i_ino); if (!inode->i_nlink) return; if (!(bh = bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode)))) { affs_error(inode->i_sb,"write_inode","Cannot read block %lu",inode->i_ino); return; } file_end = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode)); if (file_end->secondary_type == be32_to_cpu(ST_ROOT)) { secs_to_datestamp(inode->i_mtime,&ROOT_END(bh->b_data,inode)->disk_altered); } else { file_end->protect = cpu_to_be32(inode->u.affs_i.i_protect ^ FIBF_OWNER); file_end->byte_size = cpu_to_be32(inode->i_size); secs_to_datestamp(inode->i_mtime,&file_end->created); if (!(inode->i_ino == inode->i_sb->u.affs_sb.s_root_block)) { uid = inode->i_uid; gid = inode->i_gid; if (inode->i_sb->u.affs_sb.s_flags & SF_MUFS) { if (inode->i_uid == 0 || inode->i_uid == 0xFFFF) uid = inode->i_uid ^ ~0; if (inode->i_gid == 0 || inode->i_gid == 0xFFFF) gid = inode->i_gid ^ ~0; } if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETUID)) file_end->owner_uid = cpu_to_be16(uid); if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETGID)) file_end->owner_gid = cpu_to_be16(gid); } } affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5); mark_buffer_dirty(bh,1); brelse(bh); } int affs_notify_change(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; int error; pr_debug("AFFS: notify_change(%lu,0x%x)\n",inode->i_ino,attr->ia_valid); error = inode_change_ok(inode,attr); if (error) goto out; if (((attr->ia_valid & ATTR_UID) && (inode->i_sb->u.affs_sb.s_flags & SF_SETUID)) || ((attr->ia_valid & ATTR_GID) && (inode->i_sb->u.affs_sb.s_flags & SF_SETGID)) || ((attr->ia_valid & ATTR_MODE) && (inode->i_sb->u.affs_sb.s_flags & (SF_SETMODE | SF_IMMUTABLE)))) { if (!(inode->i_sb->u.affs_sb.s_flags & SF_QUIET)) error = -EPERM; goto out; } if (attr->ia_valid & ATTR_MODE) inode->u.affs_i.i_protect = mode_to_prot(attr->ia_mode); inode_setattr(inode, attr); mark_inode_dirty(inode); error = 0; out: return error; } void affs_put_inode(struct inode *inode) { pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n", inode->i_ino,inode->i_nlink); affs_free_prealloc(inode); if (inode->i_count == 1) { unsigned long cache_page = (unsigned long) inode->u.affs_i.i_ec; if (cache_page) { pr_debug("AFFS: freeing ext cache\n"); inode->u.affs_i.i_ec = NULL; free_page(cache_page); } } } void affs_delete_inode(struct inode *inode) { pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n",inode->i_ino,inode->i_nlink); inode->i_size = 0; if (S_ISREG(inode->i_mode) && !inode->u.affs_i.i_hlink) affs_truncate(inode); affs_free_block(inode->i_sb,inode->i_ino); clear_inode(inode); } struct inode * affs_new_inode(const struct inode *dir) { struct inode *inode; struct super_block *sb; s32 block; if (!dir || !(inode = get_empty_inode())) return NULL; sb = dir->i_sb; inode->i_sb = sb; inode->i_flags = 0; if (!(block = affs_new_header((struct inode *)dir))) { iput(inode); return NULL; } inode->i_count = 1; inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; inode->i_gid = current->fsgid; inode->i_ino = block; inode->i_op = NULL; inode->i_blocks = 0; inode->i_size = 0; inode->i_mode = 0; inode->i_blksize = 0; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->u.affs_i.i_original = 0; inode->u.affs_i.i_parent = dir->i_ino; inode->u.affs_i.i_zone = 0; inode->u.affs_i.i_hlink = 0; inode->u.affs_i.i_pa_cnt = 0; inode->u.affs_i.i_pa_next = 0; inode->u.affs_i.i_pa_last = 0; inode->u.affs_i.i_ec = NULL; inode->u.affs_i.i_lastblock = -1; insert_inode_hash(inode); mark_inode_dirty(inode); return inode; } /* * Add an entry to a directory. Create the header block * and insert it into the hash table. */ int affs_add_entry(struct inode *dir, struct inode *link, struct inode *inode, struct dentry *dentry, int type) { struct buffer_head *dir_bh; struct buffer_head *inode_bh; struct buffer_head *link_bh; int retval; const unsigned char *name = dentry->d_name.name; int len = dentry->d_name.len; pr_debug("AFFS: add_entry(dir=%lu,inode=%lu,\"%*s\",type=%d)\n",dir->i_ino,inode->i_ino, len,name,type); if ((retval = affs_check_name(name,len))) return retval; if (len > 30) len = 30; dir_bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir)); inode_bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode)); link_bh = NULL; retval = -EIO; if (!dir_bh || !inode_bh) goto addentry_done; if (link) { link_bh = affs_bread(link->i_dev,link->i_ino,AFFS_I2BSIZE(link)); if (!link_bh) goto addentry_done; } ((struct dir_front *)inode_bh->b_data)->primary_type = cpu_to_be32(T_SHORT); ((struct dir_front *)inode_bh->b_data)->own_key = cpu_to_be32(inode->i_ino); DIR_END(inode_bh->b_data,inode)->dir_name[0] = len; strncpy(DIR_END(inode_bh->b_data,inode)->dir_name + 1,name,len); DIR_END(inode_bh->b_data,inode)->secondary_type = cpu_to_be32(type); DIR_END(inode_bh->b_data,inode)->parent = cpu_to_be32(dir->i_ino); lock_super(inode->i_sb); retval = affs_insert_hash(dir->i_ino,inode_bh,dir); if (link_bh) { LINK_END(inode_bh->b_data,inode)->original = cpu_to_be32(link->i_ino); LINK_END(inode_bh->b_data,inode)->link_chain = FILE_END(link_bh->b_data,link)->link_chain; FILE_END(link_bh->b_data,link)->link_chain = cpu_to_be32(inode->i_ino); affs_fix_checksum(AFFS_I2BSIZE(link),link_bh->b_data,5); link->i_version = ++event; mark_inode_dirty(link); mark_buffer_dirty(link_bh,1); } affs_fix_checksum(AFFS_I2BSIZE(inode),inode_bh->b_data,5); affs_fix_checksum(AFFS_I2BSIZE(dir),dir_bh->b_data,5); dir->i_version = ++event; dir->i_mtime = dir->i_atime = dir->i_ctime = CURRENT_TIME; unlock_super(inode->i_sb); mark_inode_dirty(dir); mark_inode_dirty(inode); mark_buffer_dirty(dir_bh,1); mark_buffer_dirty(inode_bh,1); addentry_done: affs_brelse(dir_bh); affs_brelse(inode_bh); affs_brelse(link_bh); return retval; }