/* * build_key() * * Build a key for a file by the given name in the given directory. * If the name matches one of the reserved names returns 1 otherwise 0. */ static int build_key(struct hfs_cat_key *key, struct inode *dir, const char *name, int len) { struct hfs_name cname; const struct hfs_name *reserved; /* mangle the name */ hfs_nameout(dir, &cname, name, len); /* check against reserved names */ reserved = HFS_SB(dir->i_sb)->s_reserved1; while (reserved->Len) { if (hfs_streq(reserved, &cname)) { return 1; } ++reserved; } /* check against the names reserved only in the root directory */ if (HFS_I(dir)->entry->cnid == htonl(HFS_ROOT_CNID)) { reserved = HFS_SB(dir->i_sb)->s_reserved2; while (reserved->Len) { if (hfs_streq(reserved, &cname)) { return 1; } ++reserved; } } /* build the key */ hfs_cat_build_key(HFS_I(dir)->entry->cnid, &cname, key); return 0; }
/* * init_file_inode() * * Given an HFS catalog entry initialize an inode for a file. */ static void init_file_inode(struct inode *inode, hfs_u8 fork) { struct hfs_fork *fk; struct hfs_cat_entry *entry = HFS_I(inode)->entry; if (fork == HFS_FK_DATA) { inode->i_mode = S_IRWXUGO | S_IFREG; } else { inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG; } if (fork == HFS_FK_DATA) { #if 0 /* XXX: disable crlf translations for now */ hfs_u32 type = hfs_get_nl(entry->info.file.finfo.fdType); HFS_I(inode)->convert = ((HFS_SB(inode->i_sb)->s_conv == 't') || ((HFS_SB(inode->i_sb)->s_conv == 'a') && ((type == htonl(0x54455854)) || /* "TEXT" */ (type == htonl(0x7474726f))))); /* "ttro" */ #else HFS_I(inode)->convert = 0; #endif fk = &entry->u.file.data_fork; } else { fk = &entry->u.file.rsrc_fork; HFS_I(inode)->convert = 0; } HFS_I(inode)->fork = fk; inode->i_size = fk->lsize; inode->i_blocks = fk->psize; inode->i_nlink = 1; }
/* * hfs_create() * * This is the create() entry in the inode_operations structure for * regular HFS directories. The purpose is to create a new file in * a directory and return a corresponding inode, given the inode for * the directory and the name (and its length) of the new file. */ int hfs_create(struct inode * dir, const char * name, int len, int mode, struct inode ** result) { struct hfs_cat_entry *entry = HFS_I(dir)->entry; struct hfs_cat_entry *new; struct hfs_cat_key key; int error; *result = NULL; /* build the key, checking against reserved names */ if (build_key(&key, dir, name, len)) { error = -EEXIST; } else { /* try to create the file */ error = hfs_cat_create(entry, &key, (mode & S_IWUSR) ? 0 : HFS_FIL_LOCK, HFS_SB(dir->i_sb)->s_type, HFS_SB(dir->i_sb)->s_creator, &new); } if (!error) { update_dirs_plus(entry, 0); /* create an inode for the new file */ *result = __hfs_iget(new, HFS_I(dir)->file_type, 0); if (!(*result)) { /* XXX correct error? */ error = -EIO; } }
/* * cap_lookup() * * This is the lookup() entry in the inode_operations structure for * HFS directories in the CAP scheme. The purpose is to generate the * inode corresponding to an entry in a directory, given the inode for * the directory and the name (and its length) of the entry. */ static struct dentry *cap_lookup(struct inode * dir, struct dentry *dentry) { ino_t dtype; struct hfs_name cname; struct hfs_cat_entry *entry; struct hfs_cat_key key; struct inode *inode = NULL; dentry->d_op = &hfs_dentry_operations; entry = HFS_I(dir)->entry; dtype = HFS_ITYPE(dir->i_ino); /* Perform name-mangling */ hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len); /* no need to check for "." or ".." */ /* Check for special directories if in a normal directory. Note that cap_dupdir() does an iput(dir). */ if (dtype==HFS_CAP_NDIR) { /* Check for ".resource", ".finderinfo" and ".rootinfo" */ if (hfs_streq(cname.Name, cname.Len, DOT_RESOURCE->Name, DOT_RESOURCE_LEN)) { ++entry->count; /* __hfs_iget() eats one */ inode = hfs_iget(entry, HFS_CAP_RDIR, dentry); goto done; } else if (hfs_streq(cname.Name, cname.Len, DOT_FINDERINFO->Name, DOT_FINDERINFO_LEN)) { ++entry->count; /* __hfs_iget() eats one */ inode = hfs_iget(entry, HFS_CAP_FDIR, dentry); goto done; } else if ((entry->cnid == htonl(HFS_ROOT_CNID)) && hfs_streq(cname.Name, cname.Len, DOT_ROOTINFO->Name, DOT_ROOTINFO_LEN)) { ++entry->count; /* __hfs_iget() eats one */ inode = hfs_iget(entry, HFS_CAP_FNDR, dentry); goto done; } } /* Do an hfs_iget() on the mangled name. */ hfs_cat_build_key(entry->cnid, &cname, &key); inode = hfs_iget(hfs_cat_get(entry->mdb, &key), HFS_I(dir)->file_type, dentry); /* Don't return a resource fork for a directory */ if (inode && (dtype == HFS_CAP_RDIR) && (HFS_I(inode)->entry->type == HFS_CDR_DIR)) { iput(inode); /* this does an hfs_cat_put */ inode = NULL; } done: d_add(dentry, inode); return NULL; }
static int hfs_dir_release(struct inode *inode, struct file *file) { struct hfs_readdir_data *rd = file->private_data; if (rd) { spin_lock(&HFS_I(inode)->open_dir_lock); list_del(&rd->list); spin_unlock(&HFS_I(inode)->open_dir_lock); kfree(rd); } return 0; }
/* * hfs_put_inode() * * This is the put_inode() entry in the super_operations for HFS * filesystems. The purpose is to perform any filesystem-dependent * cleanup necessary when the use-count of an inode falls to zero. */ void hfs_put_inode(struct inode * inode) { struct hfs_cat_entry *entry = HFS_I(inode)->entry; hfs_cat_put(entry); if (inode->i_count == 1) { struct hfs_hdr_layout *tmp = HFS_I(inode)->layout; if (tmp) { HFS_I(inode)->layout = NULL; HFS_DELETE(tmp); } } }
static int hfs_ext_read_extent(struct inode *inode, u16 block) { struct hfs_find_data fd; int res; if (block >= HFS_I(inode)->cached_start && block < HFS_I(inode)->cached_start + HFS_I(inode)->cached_blocks) return 0; hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd); res = __hfs_ext_cache_extent(&fd, inode, block); hfs_find_exit(&fd); return res; }
/* * hfs_put_inode() * * This is the put_inode() entry in the super_operations for HFS * filesystems. The purpose is to perform any filesystem-dependent * cleanup necessary when the use-count of an inode falls to zero. */ void hfs_put_inode(struct inode * inode) { struct hfs_cat_entry *entry = HFS_I(inode)->entry; lock_kernel(); hfs_cat_put(entry); if (atomic_read(&inode->i_count) == 1) { struct hfs_hdr_layout *tmp = HFS_I(inode)->layout; if (tmp) { HFS_I(inode)->layout = NULL; HFS_DELETE(tmp); } } unlock_kernel(); }
/* * hfs_rename() * * This is the rename() entry in the inode_operations structure for * regular HFS directories. The purpose is to rename an existing * file or directory, given the inode for the current directory and * the name (and its length) of the existing file/directory and the * inode for the new directory and the name (and its length) of the * new file/directory. * XXX: how do you handle must_be dir? */ static int hfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { int res; if (flags & ~RENAME_NOREPLACE) return -EINVAL; /* Unlink destination if it already exists */ if (d_really_is_positive(new_dentry)) { res = hfs_remove(new_dir, new_dentry); if (res) return res; } res = hfs_cat_move(d_inode(old_dentry)->i_ino, old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); if (!res) hfs_cat_build_key(old_dir->i_sb, (btree_key *)&HFS_I(d_inode(old_dentry))->cat_key, new_dir->i_ino, &new_dentry->d_name); return res; }
/* * nat_hdr_unlink() * * This is the unlink() entry in the inode_operations structure for * Netatalk .AppleDouble directories. The purpose is to delete an * existing file, given the inode for the parent directory and the name * (and its length) of the existing file. * * WE DON'T ACTUALLY DELETE HEADER THE FILE. * In non-afpd-compatible mode: * We return -EPERM. * In afpd-compatible mode: * We return success if the file exists or is .Parent. * Otherwise we return -ENOENT. */ static int nat_hdr_unlink(struct inode *dir, const char *name, int len) { struct hfs_cat_entry *entry = HFS_I(dir)->entry; int error = 0; if (!HFS_SB(dir->i_sb)->s_afpd) { /* Not in AFPD compatibility mode */ error = -EPERM; } else { struct hfs_name cname; hfs_nameout(dir, &cname, name, len); if (!hfs_streq(&cname, DOT_PARENT)) { struct hfs_cat_entry *victim; struct hfs_cat_key key; hfs_cat_build_key(entry->cnid, &cname, &key); victim = hfs_cat_get(entry->mdb, &key); if (victim) { /* pretend to succeed */ hfs_cat_put(victim); } else { error = -ENOENT; } } } iput(dir); return error; }
/* * 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; }
static int hfs_revalidate_dentry(struct dentry *dentry, struct nameidata *nd) { struct inode *inode = dentry->d_inode; int diff; if(!inode) return 1; /* fix up inode on a timezone change */ diff = sys_tz.tz_minuteswest * 60 - HFS_I(inode)->tz_secondswest; if (diff) { inode->i_ctime.tv_sec += diff; inode->i_atime.tv_sec += diff; inode->i_mtime.tv_sec += diff; HFS_I(inode)->tz_secondswest += diff; } return 1; }
static int hfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { *pagep = NULL; return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, hfs_get_block, &HFS_I(mapping->host)->phys_size); }
void hfs_ext_write_extent(struct inode *inode) { struct hfs_find_data fd; if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) { hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd); __hfs_ext_write_extent(inode, &fd); hfs_find_exit(&fd); } }
/* * hfs_dbl_ifill() * * This function serves the same purpose as a read_inode() function does * in other filesystems. It is called by __hfs_iget() to fill in * the missing fields of an uninitialized inode under the AppleDouble * scheme. */ void hfs_dbl_ifill(struct inode * inode, ino_t type, const int version) { struct hfs_cat_entry *entry = HFS_I(inode)->entry; HFS_I(inode)->d_drop_op = hfs_dbl_drop_dentry; if (type == HFS_DBL_HDR) { if (entry->type == HFS_CDR_FIL) { init_file_inode(inode, HFS_FK_RSRC); inode->i_size += HFS_DBL_HDR_LEN; HFS_I(inode)->default_layout = &hfs_dbl_fil_hdr_layout; } else { inode->i_size = HFS_DBL_HDR_LEN; inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG; inode->i_nlink = 1; HFS_I(inode)->default_layout = &hfs_dbl_dir_hdr_layout; } inode->i_op = &hfs_hdr_inode_operations; } else if (entry->type == HFS_CDR_FIL) { init_file_inode(inode, HFS_FK_DATA); inode->i_op = &hfs_file_inode_operations; } else { /* Directory */ struct hfs_dir *hdir = &entry->u.dir; inode->i_blocks = 0; inode->i_nlink = hdir->dirs + 2; inode->i_size = 3 + 2 * (hdir->dirs + hdir->files); inode->i_mode = S_IRWXUGO | S_IFDIR; inode->i_op = &hfs_dbl_dir_inode_operations; HFS_I(inode)->file_type = HFS_DBL_NORM; HFS_I(inode)->dir_size = 2; } }
/* * 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); }
static int hfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { int ret; *pagep = NULL; ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, hfs_get_block, &HFS_I(mapping->host)->phys_size); if (unlikely(ret)) hfs_write_failed(mapping, pos + len); return ret; }
static ssize_t __hfs_getxattr(struct inode *inode, enum hfs_xattr_type type, void *value, size_t size) { struct hfs_find_data fd; hfs_cat_rec rec; struct hfs_cat_file *file; ssize_t res = 0; if (!S_ISREG(inode->i_mode) || HFS_IS_RSRC(inode)) return -EOPNOTSUPP; if (size) { res = hfs_find_init(HFS_SB(inode->i_sb)->cat_tree, &fd); if (res) return res; fd.search_key->cat = HFS_I(inode)->cat_key; res = hfs_brec_find(&fd); if (res) goto out; hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, sizeof(struct hfs_cat_file)); } file = &rec.file; switch (type) { case HFS_TYPE: if (size >= 4) { memcpy(value, &file->UsrWds.fdType, 4); res = 4; } else res = size ? -ERANGE : 4; break; case HFS_CREATOR: if (size >= 4) { memcpy(value, &file->UsrWds.fdCreator, 4); res = 4; } else res = size ? -ERANGE : 4; break; } out: if (size) hfs_find_exit(&fd); return res; }
static inline int __hfs_ext_cache_extent(struct hfs_find_data *fd, struct inode *inode, u32 block) { int res; if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) __hfs_ext_write_extent(inode, fd); res = __hfs_ext_read_extent(fd, HFS_I(inode)->cached_extents, inode->i_ino, block, HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA); if (!res) { HFS_I(inode)->cached_start = be16_to_cpu(fd->key->ext.FABN); HFS_I(inode)->cached_blocks = hfs_ext_block_count(HFS_I(inode)->cached_extents); } else { HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0; HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); } return res; }
static int __hfs_setxattr(struct inode *inode, enum hfs_xattr_type type, const void *value, size_t size, int flags) { struct hfs_find_data fd; hfs_cat_rec rec; struct hfs_cat_file *file; int res; if (!S_ISREG(inode->i_mode) || HFS_IS_RSRC(inode)) return -EOPNOTSUPP; res = hfs_find_init(HFS_SB(inode->i_sb)->cat_tree, &fd); if (res) return res; fd.search_key->cat = HFS_I(inode)->cat_key; res = hfs_brec_find(&fd); if (res) goto out; hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, sizeof(struct hfs_cat_file)); file = &rec.file; switch (type) { case HFS_TYPE: if (size == 4) memcpy(&file->UsrWds.fdType, value, 4); else res = -ERANGE; break; case HFS_CREATOR: if (size == 4) memcpy(&file->UsrWds.fdCreator, value, 4); else res = -ERANGE; break; } if (!res) hfs_bnode_write(fd.bnode, &rec, fd.entryoffset, sizeof(struct hfs_cat_file)); out: hfs_find_exit(&fd); return res; }
ssize_t hfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { struct inode *inode = dentry->d_inode; struct hfs_find_data fd; hfs_cat_rec rec; struct hfs_cat_file *file; ssize_t res = 0; if (!S_ISREG(inode->i_mode) || HFS_IS_RSRC(inode)) return -EOPNOTSUPP; if (size) { res = hfs_find_init(HFS_SB(inode->i_sb)->cat_tree, &fd); if (res) return res; fd.search_key->cat = HFS_I(inode)->cat_key; res = hfs_brec_find(&fd); if (res) goto out; hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, sizeof(struct hfs_cat_file)); } file = &rec.file; if (!strcmp(name, "hfs.type")) { if (size >= 4) { memcpy(value, &file->UsrWds.fdType, 4); res = 4; } else res = size ? -ERANGE : 4; } else if (!strcmp(name, "hfs.creator")) { if (size >= 4) { memcpy(value, &file->UsrWds.fdCreator, 4); res = 4; } else res = size ? -ERANGE : 4; } else res = -ENODATA; out: if (size) hfs_find_exit(&fd); return res; }
/* * hfs_cap_ifill() * * This function serves the same purpose as a read_inode() function does * in other filesystems. It is called by __hfs_iget() to fill in * the missing fields of an uninitialized inode under the CAP scheme. */ void hfs_cap_ifill(struct inode * inode, ino_t type, const int version) { struct hfs_cat_entry *entry = HFS_I(inode)->entry; HFS_I(inode)->d_drop_op = hfs_cap_drop_dentry; if (type == HFS_CAP_FNDR) { inode->i_size = sizeof(struct hfs_cap_info); inode->i_blocks = 0; inode->i_nlink = 1; inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG; inode->i_op = &hfs_cap_info_inode_operations; inode->i_fop = &hfs_cap_info_operations; } else if (entry->type == HFS_CDR_FIL) { init_file_inode(inode, (type == HFS_CAP_DATA) ? HFS_FK_DATA : HFS_FK_RSRC); inode->i_op = &hfs_file_inode_operations; inode->i_fop = &hfs_file_operations; inode->i_mapping->a_ops = &hfs_aops; HFS_I(inode)->mmu_private = inode->i_size; } else { /* Directory */ struct hfs_dir *hdir = &entry->u.dir; inode->i_blocks = 0; inode->i_size = hdir->files + hdir->dirs + 5; HFS_I(inode)->dir_size = 1; if (type == HFS_CAP_NDIR) { inode->i_mode = S_IRWXUGO | S_IFDIR; inode->i_nlink = hdir->dirs + 4; inode->i_op = &hfs_cap_ndir_inode_operations; inode->i_fop = &hfs_cap_dir_operations; HFS_I(inode)->file_type = HFS_CAP_NORM; } else if (type == HFS_CAP_FDIR) { inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR; inode->i_nlink = 2; inode->i_op = &hfs_cap_fdir_inode_operations; inode->i_fop = &hfs_cap_dir_operations; HFS_I(inode)->file_type = HFS_CAP_FNDR; } else if (type == HFS_CAP_RDIR) { inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR; inode->i_nlink = 2; inode->i_op = &hfs_cap_rdir_inode_operations; inode->i_fop = &hfs_cap_dir_operations; HFS_I(inode)->file_type = HFS_CAP_RSRC; } } }
int hfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct inode *inode = dentry->d_inode; struct hfs_find_data fd; hfs_cat_rec rec; struct hfs_cat_file *file; int res; if (!S_ISREG(inode->i_mode) || HFS_IS_RSRC(inode)) return -EOPNOTSUPP; res = hfs_find_init(HFS_SB(inode->i_sb)->cat_tree, &fd); if (res) return res; fd.search_key->cat = HFS_I(inode)->cat_key; res = hfs_brec_find(&fd); if (res) goto out; hfs_bnode_read(fd.bnode, &rec, fd.entryoffset, sizeof(struct hfs_cat_file)); file = &rec.file; if (!strcmp(name, "hfs.type")) { if (size == 4) memcpy(&file->UsrWds.fdType, value, 4); else res = -ERANGE; } else if (!strcmp(name, "hfs.creator")) { if (size == 4) memcpy(&file->UsrWds.fdCreator, value, 4); else res = -ERANGE; } else res = -EOPNOTSUPP; if (!res) hfs_bnode_write(fd.bnode, &rec, fd.entryoffset, sizeof(struct hfs_cat_file)); out: hfs_find_exit(&fd); return res; }
/* * hfs_rename() * * This is the rename() entry in the inode_operations structure for * regular HFS directories. The purpose is to rename an existing * file or directory, given the inode for the current directory and * the name (and its length) of the existing file/directory and the * inode for the new directory and the name (and its length) of the * new file/directory. * XXX: how do you handle must_be dir? */ int hfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int res; /* Unlink destination if it already exists */ if (new_dentry->d_inode) { res = hfs_unlink(new_dir, new_dentry); if (res) return res; } res = hfs_cat_move(old_dentry->d_inode->i_ino, old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); if (!res) hfs_cat_build_key((btree_key *)&HFS_I(old_dentry->d_inode)->cat_key, new_dir->i_ino, &new_dentry->d_name); return res; }
/* * update_dirs_plus() * * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and * 'i_version' of the inodes associated with a directory that has * had a file ('is_dir'==0) or directory ('is_dir'!=0) added to it. */ static inline void update_dirs_plus(struct hfs_cat_entry *dir, int is_dir) { int i; for (i = 0; i < 4; ++i) { struct inode *tmp = dir->sys_entry[i]; if (tmp) { if (S_ISDIR(tmp->i_mode)) { if (is_dir && (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) { /* In "normal" directory only */ ++(tmp->i_nlink); } tmp->i_size += HFS_I(tmp)->dir_size; tmp->i_version = ++event; } tmp->i_ctime = tmp->i_mtime = CURRENT_TIME; } } }
/* * hfs_nat_ifill() * * This function serves the same purpose as a read_inode() function does * in other filesystems. It is called by __hfs_iget() to fill in * the missing fields of an uninitialized inode under the Netatalk * scheme. */ void hfs_nat_ifill(struct inode * inode, ino_t type, const int version) { struct hfs_cat_entry *entry = HFS_I(inode)->entry; HFS_I(inode)->d_drop_op = hfs_nat_drop_dentry; if (type == HFS_NAT_HDR) { if (entry->type == HFS_CDR_FIL) { init_file_inode(inode, HFS_FK_RSRC); inode->i_size += HFS_NAT_HDR_LEN; } else { inode->i_size = HFS_NAT_HDR_LEN; inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG; inode->i_nlink = 1; } inode->i_op = &hfs_hdr_inode_operations; inode->i_fop = &hfs_hdr_operations; HFS_I(inode)->default_layout = (version == 2) ? &hfs_nat2_hdr_layout : &hfs_nat_hdr_layout; } else if (entry->type == HFS_CDR_FIL) { init_file_inode(inode, HFS_FK_DATA); inode->i_op = &hfs_file_inode_operations; inode->i_fop = &hfs_file_operations; inode->i_mapping->a_ops = &hfs_aops; HFS_I(inode)->mmu_private = inode->i_size; } else { /* Directory */ struct hfs_dir *hdir = &entry->u.dir; inode->i_blocks = 0; inode->i_size = hdir->files + hdir->dirs + 4; inode->i_mode = S_IRWXUGO | S_IFDIR; HFS_I(inode)->dir_size = 1; if (type == HFS_NAT_NDIR) { inode->i_nlink = hdir->dirs + 3; inode->i_op = &hfs_nat_ndir_inode_operations; HFS_I(inode)->file_type = HFS_NAT_NORM; } else if (type == HFS_NAT_HDIR) { inode->i_nlink = 2; inode->i_op = &hfs_nat_hdir_inode_operations; HFS_I(inode)->file_type = HFS_NAT_HDR; } inode->i_fop = &hfs_nat_dir_operations; } }
static int hfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { int res; if (new_dentry->d_inode) { res = hfs_remove(new_dir, new_dentry); if (res) return res; } res = hfs_cat_move(old_dentry->d_inode->i_ino, old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); if (!res) hfs_cat_build_key(old_dir->i_sb, (btree_key *)&HFS_I(old_dentry->d_inode)->cat_key, new_dir->i_ino, &new_dentry->d_name); return res; }
/* * nat_hdr_rename() * * This is the rename() entry in the inode_operations structure for * Netatalk header directories. The purpose is to rename an existing * file given the inode for the current directory and the name * (and its length) of the existing file and the inode for the new * directory and the name (and its length) of the new file/directory. * * WE NEVER MOVE ANYTHING. * In non-afpd-compatible mode: * We return -EPERM. * In afpd-compatible mode: * If the source header doesn't exist, we return -ENOENT. * If the destination is not a header directory we return -EPERM. * We return success if the destination is also a header directory * and the header exists or is ".Parent". */ static int nat_hdr_rename(struct inode *old_dir, const char *old_name, int old_len, struct inode *new_dir, const char *new_name, int new_len, int must_be_dir) { struct hfs_cat_entry *entry = HFS_I(old_dir)->entry; int error = 0; if (!HFS_SB(old_dir->i_sb)->s_afpd) { /* Not in AFPD compatibility mode */ error = -EPERM; } else { struct hfs_name cname; hfs_nameout(old_dir, &cname, old_name, old_len); if (!hfs_streq(&cname, DOT_PARENT)) { struct hfs_cat_entry *victim; struct hfs_cat_key key; hfs_cat_build_key(entry->cnid, &cname, &key); victim = hfs_cat_get(entry->mdb, &key); if (victim) { /* pretend to succeed */ hfs_cat_put(victim); } else { error = -ENOENT; } } if (!error && (HFS_ITYPE(new_dir->i_ino) != HFS_NAT_HDIR)) { error = -EPERM; } } iput(old_dir); iput(new_dir); return error; }
/* * nat_rmdir() * * This is the rmdir() entry in the inode_operations structure for * Netatalk directories. The purpose is to delete an existing * directory, given the inode for the parent directory and the name * (and its length) of the existing directory. * * We handle .AppleDouble and call hfs_rmdir() for all other cases. */ static int nat_rmdir(struct inode *parent, const char *name, int len) { struct hfs_cat_entry *entry = HFS_I(parent)->entry; struct hfs_name cname; int error; hfs_nameout(parent, &cname, name, len); if (hfs_streq(&cname, DOT_APPLEDOUBLE)) { if (!HFS_SB(parent->i_sb)->s_afpd) { /* Not in AFPD compatibility mode */ error = -EPERM; } else if (entry->u.dir.files || entry->u.dir.dirs) { /* AFPD compatible, but the directory is not empty */ error = -ENOTEMPTY; } else { /* AFPD compatible, so pretend to succeed */ error = 0; } iput(parent); } else { error = hfs_rmdir(parent, name, len); } return error; }
static void __hfs_ext_write_extent(struct inode *inode, struct hfs_find_data *fd) { int res; hfs_ext_build_key(fd->search_key, inode->i_ino, HFS_I(inode)->cached_start, HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA); res = hfs_brec_find(fd); if (HFS_I(inode)->flags & HFS_FLG_EXT_NEW) { if (res != -ENOENT) return; hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec)); HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); } else { if (res) return; hfs_bnode_write(fd->bnode, HFS_I(inode)->cached_extents, fd->entryoffset, fd->entrylength); HFS_I(inode)->flags &= ~HFS_FLG_EXT_DIRTY; } }