/* * cap_info_write() * * This is the write() entry in the file_operations structure for CAP * metadata files. The purpose is to transfer up to 'count' bytes * to the file corresponding to 'inode' beginning at offset * '*ppos' from user-space at the address 'buf'. * The return value is the number of bytes actually transferred. */ static hfs_rwret_t cap_info_write(struct file *filp, const char *buf, hfs_rwarg_t count, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; hfs_u32 pos; if (!S_ISREG(inode->i_mode)) { hfs_warn("hfs_file_write: mode = %07o\n", inode->i_mode); return -EINVAL; } if (count <= 0) { return 0; } pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos; if (pos > HFS_FORK_MAX) { return 0; } *ppos += count; if (*ppos > HFS_FORK_MAX) { *ppos = HFS_FORK_MAX; count = HFS_FORK_MAX - pos; } if (*ppos > inode->i_size) inode->i_size = *ppos; /* Only deal with the part we store in memory */ if (pos < sizeof(struct hfs_cap_info)) { int end, mem_count; struct hfs_cat_entry *entry = HFS_I(inode)->entry; struct hfs_cap_info meta; mem_count = sizeof(struct hfs_cap_info) - pos; if (mem_count > count) { mem_count = count; } end = pos + mem_count; cap_build_meta(&meta, entry); mem_count -= copy_from_user(((char *)&meta) + pos, buf, mem_count); /* Update finder attributes if changed */ if (OVERLAPS(pos, end, struct hfs_cap_info, fi_fndr)) { memcpy(&entry->info, meta.fi_fndr, 32); hfs_cat_mark_dirty(entry); }
static int __hfs_notify_change(struct dentry *dentry, struct iattr * attr, int kind) { struct inode *inode = dentry->d_inode; struct hfs_cat_entry *entry = HFS_I(inode)->entry; struct dentry **de = entry->sys_entry; struct hfs_sb_info *hsb = HFS_SB(inode->i_sb); int error=0, i; lock_kernel(); error = inode_change_ok(inode, attr); /* basic permission checks */ if (error) { /* Let netatalk's afpd think chmod() always succeeds */ if (hsb->s_afpd && (attr->ia_valid == (ATTR_MODE | ATTR_CTIME))) { error = 0; } goto out; } /* no uig/gid changes and limit which mode bits can be set */ if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != hsb->s_uid)) || ((attr->ia_valid & ATTR_GID) && (attr->ia_gid != hsb->s_gid)) || ((attr->ia_valid & ATTR_MODE) && (((entry->type == HFS_CDR_DIR) && (attr->ia_mode != inode->i_mode))|| (attr->ia_mode & ~HFS_VALID_MODE_BITS)))) { if( hsb->s_quiet ) { error = 0; goto out; } } if (entry->type == HFS_CDR_DIR) { attr->ia_valid &= ~ATTR_MODE; } else if (attr->ia_valid & ATTR_MODE) { /* Only the 'w' bits can ever change and only all together. */ if (attr->ia_mode & S_IWUSR) { attr->ia_mode = inode->i_mode | S_IWUGO; } else { attr->ia_mode = inode->i_mode & ~S_IWUGO; } attr->ia_mode &= ~hsb->s_umask; } /* * Normal files handle size change in normal way. * Oddballs are served here. */ if (attr->ia_valid & ATTR_SIZE) { if (kind == HFS_CAP) { inode->i_size = attr->ia_size; if (inode->i_size > HFS_FORK_MAX) inode->i_size = HFS_FORK_MAX; mark_inode_dirty(inode); attr->ia_valid &= ~ATTR_SIZE; } else if (kind == HFS_HDR) { hdr_truncate(inode, attr->ia_size); attr->ia_valid &= ~ATTR_SIZE; } } error = inode_setattr(inode, attr); if (error) goto out; /* We wouldn't want to mess with the sizes of the other fork */ attr->ia_valid &= ~ATTR_SIZE; /* We must change all in-core inodes corresponding to this file. */ for (i = 0; i < 4; ++i) { if (de[i] && (de[i] != dentry)) { inode_setattr(de[i]->d_inode, attr); } } /* Change the catalog entry if needed */ if (attr->ia_valid & ATTR_MTIME) { entry->modify_date = hfs_u_to_mtime(inode->i_mtime.tv_sec); hfs_cat_mark_dirty(entry); } if (attr->ia_valid & ATTR_MODE) { hfs_u8 new_flags; if (inode->i_mode & S_IWUSR) { new_flags = entry->u.file.flags & ~HFS_FIL_LOCK; } else { new_flags = entry->u.file.flags | HFS_FIL_LOCK; } if (new_flags != entry->u.file.flags) { entry->u.file.flags = new_flags; hfs_cat_mark_dirty(entry); } } /* size changes handled in hfs_extent_adj() */ out: unlock_kernel(); return error; }