summaryrefslogtreecommitdiffstats
path: root/fs/hfs/file_cap.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/hfs/file_cap.c')
-rw-r--r--fs/hfs/file_cap.c297
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;
+ }
+}