/* * linux/fs/hfs/file_cap.c * * Copyright (C) 1995-1997 Paul H. Hargrove * This file may be distributed under the terms of the GNU General 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 #include #include /*================ Forward declarations ================*/ static loff_t cap_info_llseek(struct file *, loff_t, int); 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 *); /*================ 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 ================*/ struct file_operations hfs_cap_info_operations = { llseek: cap_info_llseek, read: cap_info_read, write: cap_info_write, fsync: file_fsync, }; struct inode_operations hfs_cap_info_inode_operations = { setattr: hfs_notify_change_cap, }; /*================ 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); } static loff_t cap_info_llseek(struct file *file, loff_t offset, int origin) { long long retval; switch (origin) { case 2: offset += file->f_dentry->d_inode->i_size; break; case 1: offset += file->f_pos; } retval = -EINVAL; if (offset>=0 && offset<=HFS_FORK_MAX) { if (offset != file->f_pos) { file->f_pos = offset; file->f_reada = 0; file->f_version = ++event; } retval = offset; } return retval; } /* * 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); 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; mark_inode_dirty(inode); } 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); 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); } /* 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; mark_inode_dirty(inode); return count; }