summaryrefslogtreecommitdiffstats
path: root/fs/hfs/file_hdr.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/hfs/file_hdr.c')
-rw-r--r--fs/hfs/file_hdr.c940
1 files changed, 940 insertions, 0 deletions
diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c
new file mode 100644
index 000000000..468a3f518
--- /dev/null
+++ b/fs/hfs/file_hdr.c
@@ -0,0 +1,940 @@
+/*
+ * linux/fs/hfs/file_hdr.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 AppleDouble and Netatalk representations.
+ *
+ * The source code distributions of Netatalk, versions 1.3.3b2 and
+ * 1.4b2, were used as a specification of the location and format of
+ * files used by Netatalk's afpd. No code from Netatalk appears in
+ * hfs_fs. hfs_fs is not a work ``derived'' from Netatalk 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.
+ *
+ * XXX: Note the reason that there is not bmap() for AppleDouble
+ * header files is that dynamic nature of their structure make it
+ * very difficult to safely mmap them. Maybe in the distant future
+ * I'll get bored enough to implement it.
+ */
+
+#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 hdr_read(struct file *, char *, hfs_rwarg_t, loff_t *);
+static hfs_rwret_t hdr_write(struct file *, const char *,
+ hfs_rwarg_t, loff_t *);
+static void hdr_truncate(struct inode *);
+
+/*================ Global variables ================*/
+
+static struct file_operations hfs_hdr_operations = {
+ NULL, /* lseek - default */
+ hdr_read, /* read */
+ hdr_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap - XXX: 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_hdr_inode_operations = {
+ &hfs_hdr_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 - XXX: not available since
+ header part has no disk block */
+ hdr_truncate, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL /* revalidate */
+};
+
+const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout = {
+ __constant_htonl(HFS_DBL_MAGIC), /* magic */
+ __constant_htonl(HFS_HDR_VERSION_2), /* version */
+ 5, /* entries */
+ { /* descr[] */
+ {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16},
+ {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32},
+ {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4},
+ {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0},
+ {HFS_HDR_RSRC, HFS_DBL_HDR_LEN, ~0},
+ },
+ { /* order[] */
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[0],
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[1],
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[2],
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[3],
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[4],
+ }
+};
+
+const struct hfs_hdr_layout hfs_dbl_dir_hdr_layout = {
+ __constant_htonl(HFS_DBL_MAGIC), /* magic */
+ __constant_htonl(HFS_HDR_VERSION_2), /* version */
+ 4, /* entries */
+ { /* descr[] */
+ {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16},
+ {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32},
+ {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4},
+ {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0},
+ },
+ { /* order[] */
+ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[0],
+ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[1],
+ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[2],
+ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[3],
+ }
+};
+
+const struct hfs_hdr_layout hfs_nat_hdr_layout = {
+ __constant_htonl(HFS_DBL_MAGIC), /* magic */
+ __constant_htonl(HFS_HDR_VERSION_1), /* version */
+ 5, /* entries */
+ { /* descr[] */
+ {HFS_HDR_RSRC, HFS_NAT_HDR_LEN, ~0},
+ {HFS_HDR_FNAME, offsetof(struct hfs_nat_hdr, real_name), ~0},
+ {HFS_HDR_COMNT, offsetof(struct hfs_nat_hdr, comment), 0},
+ {HFS_HDR_OLDI, offsetof(struct hfs_nat_hdr, create_time), 16},
+ {HFS_HDR_FINFO, offsetof(struct hfs_nat_hdr, finderinfo), 32},
+ },
+ { /* order[] */
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[1],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[2],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[3],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[4],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[0],
+ }
+};
+
+/*================ File-local variables ================*/
+
+static const char fstype[16] =
+ {'M','a','c','i','n','t','o','s','h',' ',' ',' ',' ',' ',' ',' '};
+
+/*================ File-local data types ================*/
+
+struct hdr_hdr {
+ hfs_lword_t magic;
+ hfs_lword_t version;
+ hfs_byte_t filler[16];
+ hfs_word_t entries;
+ hfs_byte_t descrs[12*HFS_HDR_MAX];
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * dlength()
+ */
+static int dlength(const struct hfs_hdr_descr *descr,
+ const struct hfs_cat_entry *entry)
+{
+ hfs_u32 length = descr->length;
+
+ /* handle auto-sized entries */
+ if (length == ~0) {
+ switch (descr->id) {
+ case HFS_HDR_DATA:
+ if (entry->type == HFS_CDR_FIL) {
+ length = entry->u.file.data_fork.lsize;
+ } else {
+ length = 0;
+ }
+ break;
+
+ case HFS_HDR_RSRC:
+ if (entry->type == HFS_CDR_FIL) {
+ length = entry->u.file.rsrc_fork.lsize;
+ } else {
+ length = 0;
+ }
+ break;
+
+ case HFS_HDR_FNAME:
+ length = entry->key.CName.Len;
+ break;
+
+ default:
+ length = 0;
+ }
+ }
+ return length;
+}
+
+/*
+ * hdr_build_meta()
+ */
+static void hdr_build_meta(struct hdr_hdr *meta,
+ const struct hfs_hdr_layout *layout,
+ const struct hfs_cat_entry *entry)
+{
+ const struct hfs_hdr_descr *descr;
+ hfs_byte_t *ptr;
+ int lcv;
+
+ hfs_put_nl(layout->magic, meta->magic);
+ hfs_put_nl(layout->version, meta->version);
+ if (layout->version == htonl(HFS_HDR_VERSION_1)) {
+ memcpy(meta->filler, fstype, 16);
+ } else {
+ memset(meta->filler, 0, 16);
+ }
+ hfs_put_hs(layout->entries, meta->entries);
+ memset(meta->descrs, 0, sizeof(meta->descrs));
+ for (lcv = 0, descr = layout->descr, ptr = meta->descrs;
+ lcv < layout->entries; ++lcv, ++descr, ptr += 12) {
+ hfs_put_hl(descr->id, ptr);
+ hfs_put_hl(descr->offset, ptr + 4);
+ hfs_put_hl(dlength(descr, entry), ptr + 8);
+ }
+}
+
+/*
+ * dup_layout ()
+ */
+static struct hfs_hdr_layout *dup_layout(const struct hfs_hdr_layout *old)
+{
+ struct hfs_hdr_layout *new;
+ int lcv;
+
+ if (HFS_NEW(new)) {
+ memcpy(new, old, sizeof(*new));
+ for (lcv = 0; lcv < new->entries; ++lcv) {
+ (char *)(new->order[lcv]) += (char *)new - (char *)old;
+ }
+ }
+ return new;
+}
+
+/*
+ * init_layout()
+ */
+static inline void init_layout(struct hfs_hdr_layout *layout,
+ const hfs_byte_t *descrs)
+{
+ struct hfs_hdr_descr **base, **p, **q, *tmp;
+ int lcv, entries = layout->entries;
+
+ for (lcv = 0; lcv < entries; ++lcv, descrs += 12) {
+ layout->order[lcv] = &layout->descr[lcv];
+ layout->descr[lcv].id = hfs_get_hl(descrs);
+ layout->descr[lcv].offset = hfs_get_hl(descrs + 4);
+ layout->descr[lcv].length = hfs_get_hl(descrs + 8);
+ }
+ for (lcv = layout->entries; lcv < HFS_HDR_MAX; ++lcv) {
+ layout->order[lcv] = NULL;
+ layout->descr[lcv].id = 0;
+ layout->descr[lcv].offset = 0;
+ layout->descr[lcv].length = 0;
+ }
+
+ /* Sort the 'order' array using an insertion sort */
+ base = &layout->order[0];
+ for (p = (base+1); p < (base+entries); ++p) {
+ q=p;
+ while ((*q)->offset < (*(q-1))->offset) {
+ tmp = *q;
+ *q = *(q-1);
+ *(--q) = tmp;
+ if (q == base) break;
+ }
+ }
+}
+
+/*
+ * adjust_forks()
+ */
+static inline void adjust_forks(struct hfs_cat_entry *entry,
+ const struct hfs_hdr_layout *layout)
+{
+ int lcv;
+
+ for (lcv = 0; lcv < layout->entries; ++lcv) {
+ const struct hfs_hdr_descr *descr = &layout->descr[lcv];
+
+ if ((descr->id == HFS_HDR_DATA) &&
+ (descr->length != entry->u.file.data_fork.lsize)) {
+ entry->u.file.data_fork.lsize = descr->length;
+ hfs_extent_adj(&entry->u.file.data_fork);
+ hfs_cat_mark_dirty(entry);
+ } else if ((descr->id == HFS_HDR_RSRC) &&
+ (descr->length != entry->u.file.rsrc_fork.lsize)) {
+ entry->u.file.rsrc_fork.lsize = descr->length;
+ hfs_extent_adj(&entry->u.file.rsrc_fork);
+ hfs_cat_mark_dirty(entry);
+ }
+ }
+}
+
+/*
+ * get_dates()
+ */
+static void get_dates(const struct hfs_cat_entry *entry,
+ const struct inode *inode, hfs_u32 dates[3])
+{
+ if (HFS_SB(inode->i_sb)->s_afpd) {
+ /* AFPD compatible: use un*x times */
+ dates[0] = htonl(hfs_m_to_utime(entry->create_date));
+ dates[1] = htonl(hfs_m_to_utime(entry->modify_date));
+ dates[2] = htonl(hfs_m_to_utime(entry->backup_date));
+ } else {
+ dates[0] = hfs_m_to_htime(entry->create_date);
+ dates[1] = hfs_m_to_htime(entry->modify_date);
+ dates[2] = hfs_m_to_htime(entry->backup_date);
+ }
+}
+
+/*
+ * set_dates()
+ */
+static void set_dates(struct hfs_cat_entry *entry, struct inode *inode,
+ const hfs_u32 *dates)
+{
+ hfs_u32 tmp;
+ if (HFS_SB(inode->i_sb)->s_afpd) {
+ /* AFPD compatible: use un*x times */
+ tmp = hfs_u_to_mtime(ntohl(dates[0]));
+ if (entry->create_date != tmp) {
+ entry->create_date = tmp;
+ hfs_cat_mark_dirty(entry);
+ }
+ tmp = hfs_u_to_mtime(ntohl(dates[1]));
+ if (entry->modify_date != tmp) {
+ entry->modify_date = tmp;
+ inode->i_ctime = inode->i_atime = inode->i_mtime =
+ ntohl(dates[1]);
+ hfs_cat_mark_dirty(entry);
+ }
+ tmp = hfs_u_to_mtime(ntohl(dates[2]));
+ if (entry->backup_date != tmp) {
+ entry->backup_date = tmp;
+ hfs_cat_mark_dirty(entry);
+ }
+ } else {
+ tmp = hfs_h_to_mtime(dates[0]);
+ if (entry->create_date != tmp) {
+ entry->create_date = tmp;
+ hfs_cat_mark_dirty(entry);
+ }
+ tmp = hfs_h_to_mtime(dates[1]);
+ if (entry->modify_date != tmp) {
+ entry->modify_date = tmp;
+ inode->i_ctime = inode->i_atime = inode->i_mtime =
+ hfs_h_to_utime(dates[1]);
+ hfs_cat_mark_dirty(entry);
+ }
+ tmp = hfs_h_to_mtime(dates[2]);
+ if (entry->backup_date != tmp) {
+ entry->backup_date = tmp;
+ hfs_cat_mark_dirty(entry);
+ }
+ }
+}
+
+/*
+ * hdr_read()
+ *
+ * This is the read field in the inode_operations structure for
+ * header files. The purpose is to transfer up to 'count' bytes
+ * from the file corresponding to 'inode', beginning at
+ * 'filp->offset' bytes into the file. The data is transfered to
+ * user-space at the address 'buf'. Returns the number of bytes
+ * successfully transfered.
+ */
+/* XXX: what about the entry count changing on us? */
+static hfs_rwret_t hdr_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;
+ const struct hfs_hdr_layout *layout;
+ off_t start, length, offset;
+ off_t pos = *ppos;
+ int left, lcv, read = 0;
+
+ if (!S_ISREG(inode->i_mode)) {
+ hfs_warn("hfs_hdr_read: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+
+ if (HFS_I(inode)->layout) {
+ layout = HFS_I(inode)->layout;
+ } else {
+ layout = HFS_I(inode)->default_layout;
+ }
+
+ /* Adjust count to fit within the bounds of the file */
+ if ((pos >= inode->i_size) || (count <= 0)) {
+ return 0;
+ } else if (count > inode->i_size - pos) {
+ count = inode->i_size - pos;
+ }
+
+ /* Handle the fixed-location portion */
+ length = sizeof(hfs_u32) + sizeof(hfs_u32) + 16 +
+ sizeof(hfs_u16) + layout->entries * (3 * sizeof(hfs_u32));
+ if (pos < length) {
+ struct hdr_hdr meta;
+
+ left = length - pos;
+ if (left > count) {
+ left = count;
+ }
+
+ hdr_build_meta(&meta, layout, entry);
+ copy_to_user(buf, ((char *)&meta) + pos, left);
+ count -= left;
+ read += left;
+ pos += left;
+ buf += left;
+ }
+ if (!count) {
+ goto done;
+ }
+
+ /* Handle the actual data */
+ for (lcv = 0; count && (lcv < layout->entries); ++lcv) {
+ const struct hfs_hdr_descr *descr = layout->order[lcv];
+ struct hfs_fork *fork;
+ char tmp[16], *p;
+ off_t limit;
+
+ /* stop reading if we run out of descriptors early */
+ if (!descr) {
+ break;
+ }
+
+ /* find start and length of this entry */
+ start = descr->offset;
+ length = dlength(descr, entry);
+
+ /* Skip to next entry if this one is empty or isn't needed */
+ if (!length || (pos >= start + length)) {
+ continue;
+ }
+
+ /* Pad with zeros to the start of this entry if needed */
+ if (pos < start) {
+ left = start - pos;
+ if (left > count) {
+ left = count;
+ }
+ clear_user(buf, left);
+ count -= left;
+ read += left;
+ pos += left;
+ buf += left;
+ }
+ if (!count) {
+ goto done;
+ }
+
+ /* locate and/or construct the data for this entry */
+ fork = NULL;
+ p = NULL;
+ switch (descr->id) {
+ case HFS_HDR_DATA:
+ fork = &entry->u.file.data_fork;
+ limit = fork->lsize;
+ break;
+
+ case HFS_HDR_RSRC:
+ fork = &entry->u.file.rsrc_fork;
+ limit = fork->lsize;
+ break;
+
+ case HFS_HDR_FNAME:
+ p = entry->key.CName.Name;
+ limit = entry->key.CName.Len;
+ break;
+
+ case HFS_HDR_OLDI:
+ case HFS_HDR_DATES:
+ get_dates(entry, inode, (hfs_u32 *)tmp);
+ if (descr->id == HFS_HDR_DATES) {
+ memcpy(tmp + 12, tmp + 4, 4);
+ } else if ((entry->type == HFS_CDR_FIL) &&
+ (entry->u.file.flags & HFS_FIL_LOCK)) {
+ hfs_put_hl(HFS_AFP_RDONLY, tmp + 12);
+ } else {
+ hfs_put_nl(0, tmp + 12);
+ }
+ p = tmp;
+ limit = 16;
+ break;
+
+ case HFS_HDR_FINFO:
+ p = (char *)&entry->info;
+ limit = 32;
+ break;
+
+ case HFS_HDR_MACI:
+ hfs_put_ns(0, tmp);
+ if (entry->type == HFS_CDR_FIL) {
+ hfs_put_hs(entry->u.file.flags, tmp + 2);
+ } else {
+ hfs_put_ns(entry->u.dir.flags, tmp + 2);
+ }
+ p = tmp;
+ limit = 4;
+ break;
+
+ default:
+ limit = 0;
+ }
+
+ /* limit the transfer to the available data
+ of to the stated length of the entry. */
+ if (length > limit) {
+ length = limit;
+ }
+ offset = pos - start;
+ left = length - offset;
+ if (left > count) {
+ left = count;
+ }
+ if (left <= 0) {
+ continue;
+ }
+
+ /* transfer the data */
+ if (p) {
+ copy_to_user(buf, p + offset, left);
+ } else if (fork) {
+ left = hfs_do_read(inode, fork, offset, buf, left,
+ filp->f_reada != 0);
+ if (left > 0) {
+ filp->f_reada = 1;
+ } else if (!read) {
+ return left;
+ } else {
+ goto done;
+ }
+ }
+ count -= left;
+ read += left;
+ pos += left;
+ buf += left;
+ }
+
+ /* Pad the file out with zeros */
+ if (count) {
+ clear_user(buf, count);
+ read += count;
+ pos += count;
+ }
+
+done:
+ if (read) {
+ inode->i_atime = CURRENT_TIME;
+ *ppos = pos;
+ }
+ return read;
+}
+
+/*
+ * hdr_write()
+ *
+ * This is the write() entry in the file_operations structure for
+ * header 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 hdr_write(struct file *filp, const 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;
+ struct hfs_hdr_layout *layout;
+ off_t start, length, offset;
+ int left, lcv, written = 0;
+ struct hdr_hdr meta;
+ int built_meta = 0;
+ off_t pos;
+
+ if (!S_ISREG(inode->i_mode)) {
+ hfs_warn("hfs_hdr_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 (!HFS_I(inode)->layout) {
+ HFS_I(inode)->layout = dup_layout(HFS_I(inode)->default_layout);
+ }
+ layout = HFS_I(inode)->layout;
+
+ /* Handle the 'magic', 'version', 'filler' and 'entries' fields */
+ length = sizeof(hfs_u32) + sizeof(hfs_u32) + 16 + sizeof(hfs_u16);
+ if (pos < length) {
+ hdr_build_meta(&meta, layout, entry);
+ built_meta = 1;
+
+ left = length - pos;
+ if (left > count) {
+ left = count;
+ }
+
+ copy_from_user(((char *)&meta) + pos, buf, left);
+ layout->magic = hfs_get_nl(meta.magic);
+ layout->version = hfs_get_nl(meta.version);
+ layout->entries = hfs_get_hs(meta.entries);
+ if (layout->entries > HFS_HDR_MAX) {
+ /* XXX: should allocate slots dynamically */
+ hfs_warn("hfs_hdr_write: TRUNCATING TO %d "
+ "DESCRIPTORS\n", HFS_HDR_MAX);
+ layout->entries = HFS_HDR_MAX;
+ }
+
+ count -= left;
+ written += left;
+ pos += left;
+ buf += left;
+ }
+ if (!count) {
+ goto done;
+ }
+
+ /* We know for certain how many entries we have, so process them */
+ length += layout->entries * 3 * sizeof(hfs_u32);
+ if (pos < length) {
+ if (!built_meta) {
+ hdr_build_meta(&meta, layout, entry);
+ }
+
+ left = length - pos;
+ if (left > count) {
+ left = count;
+ }
+
+ copy_from_user(((char *)&meta) + pos, buf, left);
+ init_layout(layout, meta.descrs);
+
+ count -= left;
+ written += left;
+ pos += left;
+ buf += left;
+
+ /* Handle possible size changes for the forks */
+ if (entry->type == HFS_CDR_FIL) {
+ adjust_forks(entry, layout);
+ }
+ }
+
+ /* Handle the actual data */
+ for (lcv = 0; count && (lcv < layout->entries); ++lcv) {
+ struct hfs_hdr_descr *descr = layout->order[lcv];
+ struct hfs_fork *fork;
+ char tmp[16], *p;
+ off_t limit;
+
+ /* stop writing if we run out of descriptors early */
+ if (!descr) {
+ break;
+ }
+
+ /* find start and length of this entry */
+ start = descr->offset;
+ if ((descr->id == HFS_HDR_DATA) ||
+ (descr->id == HFS_HDR_RSRC)) {
+ if (entry->type == HFS_CDR_FIL) {
+ length = 0x7fffffff - start;
+ } else {
+ continue;
+ }
+ } else {
+ length = dlength(descr, entry);
+ }
+
+ /* Trim length to avoid overlap with the next entry */
+ if (layout->order[lcv+1] &&
+ ((start + length) > layout->order[lcv+1]->offset)) {
+ length = layout->order[lcv+1]->offset - start;
+ }
+
+ /* Skip to next entry if this one is empty or isn't needed */
+ if (!length || (pos >= start + length)) {
+ continue;
+ }
+
+ /* Skip any padding that may exist between entries */
+ if (pos < start) {
+ left = start - pos;
+ if (left > count) {
+ left = count;
+ }
+ count -= left;
+ written += left;
+ pos += left;
+ buf += left;
+ }
+ if (!count) {
+ goto done;
+ }
+
+ /* locate and/or construct the data for this entry */
+ fork = NULL;
+ p = NULL;
+ switch (descr->id) {
+ case HFS_HDR_DATA:
+#if 0
+/* Can't yet write to the data fork via a header file, since there is the
+ * possibility to write via the data file, and the only locking is at the
+ * inode level.
+ */
+ fork = &entry->u.file.data_fork;
+ limit = length;
+#else
+ limit = 0;
+#endif
+ break;
+
+ case HFS_HDR_RSRC:
+ fork = &entry->u.file.rsrc_fork;
+ limit = length;
+ break;
+
+ case HFS_HDR_OLDI:
+ case HFS_HDR_DATES:
+ get_dates(entry, inode, (hfs_u32 *)tmp);
+ if (descr->id == HFS_HDR_DATES) {
+ memcpy(tmp + 12, tmp + 4, 4);
+ } else if ((entry->type == HFS_CDR_FIL) &&
+ (entry->u.file.flags & HFS_FIL_LOCK)) {
+ hfs_put_hl(HFS_AFP_RDONLY, tmp + 12);
+ } else {
+ hfs_put_nl(0, tmp + 12);
+ }
+ p = tmp;
+ limit = 16;
+ break;
+
+ case HFS_HDR_FINFO:
+ p = (char *)&entry->info;
+ limit = 32;
+ break;
+
+ case HFS_HDR_MACI:
+ hfs_put_ns(0, tmp);
+ if (entry->type == HFS_CDR_FIL) {
+ hfs_put_hs(entry->u.file.flags, tmp + 2);
+ } else {
+ hfs_put_ns(entry->u.dir.flags, tmp + 2);
+ }
+ p = tmp;
+ limit = 4;
+ break;
+
+ case HFS_HDR_FNAME: /* Can't rename a file this way */
+ default:
+ limit = 0;
+ }
+
+ /* limit the transfer to the available data
+ of to the stated length of the entry. */
+ if (length > limit) {
+ length = limit;
+ }
+ offset = pos - start;
+ left = length - offset;
+ if (left > count) {
+ left = count;
+ }
+ if (left <= 0) {
+ continue;
+ }
+
+ /* transfer the data from user space */
+ if (p) {
+ copy_from_user(p + offset, buf, left);
+ } else if (fork) {
+ left = hfs_do_write(inode, fork, offset, buf, left);
+ }
+
+ /* process the data */
+ switch (descr->id) {
+ case HFS_HDR_OLDI:
+ set_dates(entry, inode, (hfs_u32 *)tmp);
+ if (entry->type == HFS_CDR_FIL) {
+ hfs_u8 new_flags = entry->u.file.flags;
+
+ if (hfs_get_nl(tmp+12) & htonl(HFS_AFP_WRI)) {
+ new_flags |= HFS_FIL_LOCK;
+ } else {
+ new_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);
+ }
+ }
+ break;
+
+ case HFS_HDR_DATES:
+ set_dates(entry, inode, (hfs_u32 *)tmp);
+ break;
+
+ case HFS_HDR_FINFO:
+ hfs_cat_mark_dirty(entry);
+ break;
+
+ case HFS_HDR_MACI:
+ if (entry->type == HFS_CDR_DIR) {
+ hfs_u16 new_flags = hfs_get_ns(tmp + 2);
+
+ if (entry->u.dir.flags != new_flags) {
+ entry->u.dir.flags = new_flags;
+ hfs_cat_mark_dirty(entry);
+ }
+ } else {
+ hfs_u8 new_flags = tmp[3];
+ hfs_u8 changed = entry->u.file.flags^new_flags;
+
+ if (changed) {
+ entry->u.file.flags = new_flags;
+ hfs_cat_mark_dirty(entry);
+ if (changed & HFS_FIL_LOCK) {
+ hfs_file_fix_mode(entry);
+ }
+ }
+ }
+ break;
+
+ case HFS_HDR_DATA:
+ case HFS_HDR_RSRC:
+ if (left <= 0) {
+ if (!written) {
+ return left;
+ } else {
+ goto done;
+ }
+ } else if (fork->lsize > descr->length) {
+ descr->length = fork->lsize;
+ }
+ break;
+
+ case HFS_HDR_FNAME: /* Can't rename a file this way */
+ default:
+ break;
+ }
+
+ count -= left;
+ written += left;
+ pos += left;
+ buf += left;
+ }
+
+ /* Skip any padding at the end */
+ if (count) {
+ written += count;
+ pos += count;
+ }
+
+done:
+ *ppos = pos;
+ if (written > 0) {
+ if (pos > inode->i_size)
+ inode->i_size = pos;
+ inode->i_mtime = inode->i_atime = CURRENT_TIME;
+ }
+ return written;
+}
+
+/*
+ * hdr_truncate()
+ *
+ * This is the truncate field in the inode_operations structure for
+ * header files. The purpose is to allocate or release blocks as needed
+ * to satisfy a change in file length.
+ */
+static void hdr_truncate(struct inode *inode)
+{
+ struct hfs_hdr_layout *layout;
+ size_t size = inode->i_size;
+ int lcv, last;
+
+ if (!HFS_I(inode)->layout) {
+ HFS_I(inode)->layout = dup_layout(HFS_I(inode)->default_layout);
+ }
+ layout = HFS_I(inode)->layout;
+
+ last = layout->entries - 1;
+ for (lcv = 0; lcv <= last; ++lcv) {
+ struct hfs_hdr_descr *descr = layout->order[lcv];
+ struct hfs_fork *fork;
+ hfs_u32 offset;
+
+ if (!descr) {
+ break;
+ }
+
+ if (descr->id == HFS_HDR_RSRC) {
+ fork = &HFS_I(inode)->entry->u.file.rsrc_fork;
+#if 0
+/* Can't yet truncate the data fork via a header file, since there is the
+ * possibility to truncate via the data file, and the only locking is at
+ * the inode level.
+ */
+ } else if (descr->id == HFS_HDR_DATA) {
+ fork = &HFS_I(inode)->entry->u.file.data_fork;
+#endif
+ } else {
+ continue;
+ }
+
+ offset = descr->offset;
+
+ if ((lcv != last) && ((offset + descr->length) <= size)) {
+ continue;
+ }
+
+ if (offset < size) {
+ descr->length = size - offset;
+ } else {
+ descr->length = 0;
+ }
+ if (fork->lsize != descr->length) {
+ fork->lsize = descr->length;
+ hfs_extent_adj(fork);
+ hfs_cat_mark_dirty(HFS_I(inode)->entry);
+ }
+ }
+}