patch-2.1.78 linux/fs/hfs/file_hdr.c

Next file: linux/fs/hfs/hfs.h
Previous file: linux/fs/hfs/file_cap.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.77/linux/fs/hfs/file_hdr.c linux/fs/hfs/file_hdr.c
@@ -0,0 +1,937 @@
+/*
+ * 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);
+		} 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);
+		}
+	}
+}
+
+/*
+ * 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;
+			entry->dirt = 1;
+		}
+		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]);
+			entry->dirt = 1;
+		}
+		tmp = hfs_u_to_mtime(ntohl(dates[2]));
+		if (entry->backup_date != tmp) {
+			entry->backup_date = tmp;
+			entry->dirt = 1;
+		}
+	} else {
+		tmp = hfs_h_to_mtime(dates[0]);
+		if (entry->create_date != tmp) {
+			entry->create_date = tmp;
+			entry->dirt = 1;
+		}
+		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]);
+			entry->dirt = 1;
+		}
+		tmp = hfs_h_to_mtime(dates[2]);
+		if (entry->backup_date != tmp) {
+			entry->backup_date = tmp;
+			entry->dirt = 1;
+		}
+	}
+}
+
+/*
+ * 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;
+					entry->dirt = 1;
+					hfs_file_fix_mode(entry);
+				}
+			}
+			break;
+
+		case HFS_HDR_DATES:
+			set_dates(entry, inode, (hfs_u32 *)tmp);
+			break;
+
+		case HFS_HDR_FINFO:
+			entry->dirt = 1;
+			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;
+					entry->dirt = 1;
+				}
+			} else {
+				hfs_u8 new_flags = tmp[3];
+				hfs_u8 changed = entry->u.file.flags^new_flags;
+				
+				if (changed) {
+					entry->u.file.flags = new_flags;
+					entry->dirt = 1;
+					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);
+		}
+	}
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov