diff options
Diffstat (limited to 'fs/hfs/file_cap.c')
-rw-r--r-- | fs/hfs/file_cap.c | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/fs/hfs/file_cap.c b/fs/hfs/file_cap.c new file mode 100644 index 000000000..7c298264a --- /dev/null +++ b/fs/hfs/file_cap.c @@ -0,0 +1,297 @@ +/* + * linux/fs/hfs/file_cap.c + * + * Copyright (C) 1995-1997 Paul H. Hargrove + * This file may be distributed under the terms of the GNU Public License. + * + * This file contains the file_ops and inode_ops for the metadata + * files under the CAP representation. + * + * The source code distribution of the Columbia AppleTalk Package for + * UNIX, version 6.0, (CAP) was used as a specification of the + * location and format of files used by CAP's Aufs. No code from CAP + * appears in hfs_fs. hfs_fs is not a work ``derived'' from CAP in + * the sense of intellectual property law. + * + * "XXX" in a comment is a note to myself to consider changing something. + * + * In function preconditions the term "valid" applied to a pointer to + * a structure means that the pointer is non-NULL and the structure it + * points to has all fields initialized to consistent values. + */ + +#include "hfs.h" +#include <linux/hfs_fs_sb.h> +#include <linux/hfs_fs_i.h> +#include <linux/hfs_fs.h> + +/*================ Forward declarations ================*/ + +static hfs_rwret_t cap_info_read(struct file *, char *, + hfs_rwarg_t, loff_t *); +static hfs_rwret_t cap_info_write(struct file *, const char *, + hfs_rwarg_t, loff_t *); +static void cap_info_truncate(struct inode *); + +/*================ Function-like macros ================*/ + +/* + * OVERLAPS() + * + * Determines if a given range overlaps the specified structure member + */ +#define OVERLAPS(START, END, TYPE, MEMB) \ + ((END > offsetof(TYPE, MEMB)) && \ + (START < offsetof(TYPE, MEMB) + sizeof(((TYPE *)0)->MEMB))) + +/*================ Global variables ================*/ + +static struct file_operations hfs_cap_info_operations = { + NULL, /* lseek - default */ + cap_info_read, /* read */ + cap_info_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + NULL, /* mmap - not yet */ + NULL, /* no special open code */ + NULL, /* no special release code */ + file_fsync, /* fsync - default */ + NULL, /* fasync - default */ + NULL, /* check_media_change - none */ + NULL, /* revalidate - none */ + NULL /* lock - none */ +}; + +struct inode_operations hfs_cap_info_inode_operations = { + &hfs_cap_info_operations, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap - none */ + cap_info_truncate, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL /* revalidata */ +}; + +/*================ File-local functions ================*/ + +/* + * cap_build_meta() + * + * Build the metadata structure. + */ +static void cap_build_meta(struct hfs_cap_info *meta, + struct hfs_cat_entry *entry) +{ + memset(meta, 0, sizeof(*meta)); + memcpy(meta->fi_fndr, &entry->info, 32); + if ((entry->type == HFS_CDR_FIL) && + (entry->u.file.flags & HFS_FIL_LOCK)) { + /* Couple the locked bit of the file to the + AFP {write,rename,delete} inhibit bits. */ + hfs_put_hs(HFS_AFP_RDONLY, meta->fi_attr); + } + meta->fi_magic1 = HFS_CAP_MAGIC1; + meta->fi_version = HFS_CAP_VERSION; + meta->fi_magic = HFS_CAP_MAGIC; + meta->fi_bitmap = HFS_CAP_LONGNAME; + memcpy(meta->fi_macfilename, entry->key.CName.Name, + entry->key.CName.Len); + meta->fi_datemagic = HFS_CAP_DMAGIC; + meta->fi_datevalid = HFS_CAP_MDATE | HFS_CAP_CDATE; + hfs_put_nl(hfs_m_to_htime(entry->create_date), meta->fi_ctime); + hfs_put_nl(hfs_m_to_htime(entry->modify_date), meta->fi_mtime); + hfs_put_nl(CURRENT_TIME, meta->fi_utime); +} + +/* + * cap_info_read() + * + * This is the read() entry in the file_operations structure for CAP + * metadata files. The purpose is to transfer up to 'count' bytes + * from the file corresponding to 'inode' beginning at offset + * 'file->f_pos' to user-space at the address 'buf'. The return value + * is the number of bytes actually transferred. + */ +static hfs_rwret_t cap_info_read(struct file *filp, char *buf, + hfs_rwarg_t count, loff_t *ppos) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct hfs_cat_entry *entry = HFS_I(inode)->entry; + hfs_s32 left, size, read = 0; + hfs_u32 pos; + + if (!S_ISREG(inode->i_mode)) { + hfs_warn("hfs_cap_info_read: mode = %07o\n", inode->i_mode); + return -EINVAL; + } + + pos = *ppos; + if (pos > HFS_FORK_MAX) { + return 0; + } + size = inode->i_size; + if (pos > size) { + left = 0; + } else { + left = size - pos; + } + if (left > count) { + left = count; + } + if (left <= 0) { + return 0; + } + + if (pos < sizeof(struct hfs_cap_info)) { + int memcount = sizeof(struct hfs_cap_info) - pos; + struct hfs_cap_info meta; + + if (memcount > left) { + memcount = left; + } + cap_build_meta(&meta, entry); + /* is copy_to_user guaranteed to write memcount? */ + copy_to_user(buf, ((char *)&meta) + pos, memcount); + left -= memcount; + read += memcount; + pos += memcount; + buf += memcount; + } + + if (left > 0) { + clear_user(buf, left); + pos += left; + } + + if (read) { + inode->i_atime = CURRENT_TIME; + *ppos = pos; + } + + return read; +} + +/* + * 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); + 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); + } + + /* Update file flags if changed */ + if (OVERLAPS(pos, end, struct hfs_cap_info, fi_attr) && + (entry->type == HFS_CDR_FIL)) { + int locked = hfs_get_ns(&meta.fi_attr) & + htons(HFS_AFP_WRI); + hfs_u8 new_flags; + + if (locked) { + 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); + hfs_file_fix_mode(entry); + } + } + + /* Update CrDat if changed */ + if (OVERLAPS(pos, end, struct hfs_cap_info, fi_ctime)) { + entry->create_date = + hfs_h_to_mtime(hfs_get_nl(meta.fi_ctime)); + hfs_cat_mark_dirty(entry); + } + + /* Update MdDat if changed */ + if (OVERLAPS(pos, end, struct hfs_cap_info, fi_mtime)) { + entry->modify_date = + hfs_h_to_mtime(hfs_get_nl(meta.fi_mtime)); + hfs_cat_mark_dirty(entry); + } + } + + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + return count; +} + +/* + * cap_info_truncate() + * + * This is the truncate field in the inode_operations structure for + * CAP metadata files. + */ +static void cap_info_truncate(struct inode *inode) +{ + if (inode->i_size > HFS_FORK_MAX) { + inode->i_size = HFS_FORK_MAX; + } +} |