/* * 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 #include #include /*================ 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); } } }