File Index
Files and directories
Sign in
FILE

inode.c

File preview with syntax highlighting.

linux-6.19.14/fs/hfsplus/inode.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 *  linux/fs/hfsplus/inode.c
  4 *
  5 * Copyright (C) 2001
  6 * Brad Boyer (flar@allandria.com)
  7 * (C) 2003 Ardis Technologies <roman@ardistech.com>
  8 *
  9 * Inode handling routines
 10 */
 11
 12#include <linux/blkdev.h>
 13#include <linux/mm.h>
 14#include <linux/fs.h>
 15#include <linux/pagemap.h>
 16#include <linux/mpage.h>
 17#include <linux/sched.h>
 18#include <linux/cred.h>
 19#include <linux/uio.h>
 20#include <linux/fileattr.h>
 21
 22#include "hfsplus_fs.h"
 23#include "hfsplus_raw.h"
 24#include "xattr.h"
 25
 26static int hfsplus_read_folio(struct file *file, struct folio *folio)
 27{
 28	return block_read_full_folio(folio, hfsplus_get_block);
 29}
 30
 31static void hfsplus_write_failed(struct address_space *mapping, loff_t to)
 32{
 33	struct inode *inode = mapping->host;
 34
 35	if (to > inode->i_size) {
 36		truncate_pagecache(inode, inode->i_size);
 37		hfsplus_file_truncate(inode);
 38	}
 39}
 40
 41int hfsplus_write_begin(const struct kiocb *iocb,
 42			struct address_space *mapping, loff_t pos,
 43			unsigned len, struct folio **foliop,
 44			void **fsdata)
 45{
 46	int ret;
 47
 48	ret = cont_write_begin(iocb, mapping, pos, len, foliop, fsdata,
 49				hfsplus_get_block,
 50				&HFSPLUS_I(mapping->host)->phys_size);
 51	if (unlikely(ret))
 52		hfsplus_write_failed(mapping, pos + len);
 53
 54	return ret;
 55}
 56
 57static sector_t hfsplus_bmap(struct address_space *mapping, sector_t block)
 58{
 59	return generic_block_bmap(mapping, block, hfsplus_get_block);
 60}
 61
 62static bool hfsplus_release_folio(struct folio *folio, gfp_t mask)
 63{
 64	struct inode *inode = folio->mapping->host;
 65	struct super_block *sb = inode->i_sb;
 66	struct hfs_btree *tree;
 67	struct hfs_bnode *node;
 68	u32 nidx;
 69	int i;
 70	bool res = true;
 71
 72	switch (inode->i_ino) {
 73	case HFSPLUS_EXT_CNID:
 74		tree = HFSPLUS_SB(sb)->ext_tree;
 75		break;
 76	case HFSPLUS_CAT_CNID:
 77		tree = HFSPLUS_SB(sb)->cat_tree;
 78		break;
 79	case HFSPLUS_ATTR_CNID:
 80		tree = HFSPLUS_SB(sb)->attr_tree;
 81		break;
 82	default:
 83		BUG();
 84		return false;
 85	}
 86	if (!tree)
 87		return false;
 88	if (tree->node_size >= PAGE_SIZE) {
 89		nidx = folio->index >>
 90			(tree->node_size_shift - PAGE_SHIFT);
 91		spin_lock(&tree->hash_lock);
 92		node = hfs_bnode_findhash(tree, nidx);
 93		if (!node)
 94			;
 95		else if (atomic_read(&node->refcnt))
 96			res = false;
 97		if (res && node) {
 98			hfs_bnode_unhash(node);
 99			hfs_bnode_free(node);
100		}
101		spin_unlock(&tree->hash_lock);
102	} else {
103		nidx = folio->index <<
104			(PAGE_SHIFT - tree->node_size_shift);
105		i = 1 << (PAGE_SHIFT - tree->node_size_shift);
106		spin_lock(&tree->hash_lock);
107		do {
108			node = hfs_bnode_findhash(tree, nidx++);
109			if (!node)
110				continue;
111			if (atomic_read(&node->refcnt)) {
112				res = false;
113				break;
114			}
115			hfs_bnode_unhash(node);
116			hfs_bnode_free(node);
117		} while (--i && nidx < tree->node_count);
118		spin_unlock(&tree->hash_lock);
119	}
120	return res ? try_to_free_buffers(folio) : false;
121}
122
123static ssize_t hfsplus_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
124{
125	struct file *file = iocb->ki_filp;
126	struct address_space *mapping = file->f_mapping;
127	struct inode *inode = mapping->host;
128	size_t count = iov_iter_count(iter);
129	ssize_t ret;
130
131	ret = blockdev_direct_IO(iocb, inode, iter, hfsplus_get_block);
132
133	/*
134	 * In case of error extending write may have instantiated a few
135	 * blocks outside i_size. Trim these off again.
136	 */
137	if (unlikely(iov_iter_rw(iter) == WRITE && ret < 0)) {
138		loff_t isize = i_size_read(inode);
139		loff_t end = iocb->ki_pos + count;
140
141		if (end > isize)
142			hfsplus_write_failed(mapping, end);
143	}
144
145	return ret;
146}
147
148static int hfsplus_writepages(struct address_space *mapping,
149			      struct writeback_control *wbc)
150{
151	return mpage_writepages(mapping, wbc, hfsplus_get_block);
152}
153
154const struct address_space_operations hfsplus_btree_aops = {
155	.dirty_folio	= block_dirty_folio,
156	.invalidate_folio = block_invalidate_folio,
157	.read_folio	= hfsplus_read_folio,
158	.writepages	= hfsplus_writepages,
159	.write_begin	= hfsplus_write_begin,
160	.write_end	= generic_write_end,
161	.migrate_folio	= buffer_migrate_folio,
162	.bmap		= hfsplus_bmap,
163	.release_folio	= hfsplus_release_folio,
164};
165
166const struct address_space_operations hfsplus_aops = {
167	.dirty_folio	= block_dirty_folio,
168	.invalidate_folio = block_invalidate_folio,
169	.read_folio	= hfsplus_read_folio,
170	.write_begin	= hfsplus_write_begin,
171	.write_end	= generic_write_end,
172	.bmap		= hfsplus_bmap,
173	.direct_IO	= hfsplus_direct_IO,
174	.writepages	= hfsplus_writepages,
175	.migrate_folio	= buffer_migrate_folio,
176};
177
178const struct dentry_operations hfsplus_dentry_operations = {
179	.d_hash       = hfsplus_hash_dentry,
180	.d_compare    = hfsplus_compare_dentry,
181};
182
183static int hfsplus_get_perms(struct inode *inode,
184			     struct hfsplus_perm *perms, int dir)
185{
186	struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
187	u16 mode;
188
189	mode = be16_to_cpu(perms->mode);
190	if (dir) {
191		if (mode && !S_ISDIR(mode))
192			goto bad_type;
193	} else if (mode) {
194		switch (mode & S_IFMT) {
195		case S_IFREG:
196		case S_IFLNK:
197		case S_IFCHR:
198		case S_IFBLK:
199		case S_IFIFO:
200		case S_IFSOCK:
201			break;
202		default:
203			goto bad_type;
204		}
205	}
206
207	i_uid_write(inode, be32_to_cpu(perms->owner));
208	if ((test_bit(HFSPLUS_SB_UID, &sbi->flags)) || (!i_uid_read(inode) && !mode))
209		inode->i_uid = sbi->uid;
210
211	i_gid_write(inode, be32_to_cpu(perms->group));
212	if ((test_bit(HFSPLUS_SB_GID, &sbi->flags)) || (!i_gid_read(inode) && !mode))
213		inode->i_gid = sbi->gid;
214
215	if (dir) {
216		mode = mode ? (mode & S_IALLUGO) : (S_IRWXUGO & ~(sbi->umask));
217		mode |= S_IFDIR;
218	} else if (!mode)
219		mode = S_IFREG | ((S_IRUGO|S_IWUGO) & ~(sbi->umask));
220	inode->i_mode = mode;
221
222	HFSPLUS_I(inode)->userflags = perms->userflags;
223	if (perms->rootflags & HFSPLUS_FLG_IMMUTABLE)
224		inode->i_flags |= S_IMMUTABLE;
225	else
226		inode->i_flags &= ~S_IMMUTABLE;
227	if (perms->rootflags & HFSPLUS_FLG_APPEND)
228		inode->i_flags |= S_APPEND;
229	else
230		inode->i_flags &= ~S_APPEND;
231	return 0;
232bad_type:
233	pr_err("invalid file type 0%04o for inode %lu\n", mode, inode->i_ino);
234	return -EIO;
235}
236
237static int hfsplus_file_open(struct inode *inode, struct file *file)
238{
239	if (HFSPLUS_IS_RSRC(inode))
240		inode = HFSPLUS_I(inode)->rsrc_inode;
241	if (!(file->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
242		return -EOVERFLOW;
243	atomic_inc(&HFSPLUS_I(inode)->opencnt);
244	return 0;
245}
246
247static int hfsplus_file_release(struct inode *inode, struct file *file)
248{
249	struct super_block *sb = inode->i_sb;
250
251	if (HFSPLUS_IS_RSRC(inode))
252		inode = HFSPLUS_I(inode)->rsrc_inode;
253	if (atomic_dec_and_test(&HFSPLUS_I(inode)->opencnt)) {
254		inode_lock(inode);
255		hfsplus_file_truncate(inode);
256		if (inode->i_flags & S_DEAD) {
257			hfsplus_delete_cat(inode->i_ino,
258					   HFSPLUS_SB(sb)->hidden_dir, NULL);
259			hfsplus_delete_inode(inode);
260		}
261		inode_unlock(inode);
262	}
263	return 0;
264}
265
266static int hfsplus_setattr(struct mnt_idmap *idmap,
267			   struct dentry *dentry, struct iattr *attr)
268{
269	struct inode *inode = d_inode(dentry);
270	int error;
271
272	error = setattr_prepare(&nop_mnt_idmap, dentry, attr);
273	if (error)
274		return error;
275
276	if ((attr->ia_valid & ATTR_SIZE) &&
277	    attr->ia_size != i_size_read(inode)) {
278		inode_dio_wait(inode);
279		if (attr->ia_size > inode->i_size) {
280			error = generic_cont_expand_simple(inode,
281							   attr->ia_size);
282			if (error)
283				return error;
284		}
285		truncate_setsize(inode, attr->ia_size);
286		hfsplus_file_truncate(inode);
287		inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
288	}
289
290	setattr_copy(&nop_mnt_idmap, inode, attr);
291	mark_inode_dirty(inode);
292
293	return 0;
294}
295
296int hfsplus_getattr(struct mnt_idmap *idmap, const struct path *path,
297		    struct kstat *stat, u32 request_mask,
298		    unsigned int query_flags)
299{
300	struct inode *inode = d_inode(path->dentry);
301	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
302
303	if (request_mask & STATX_BTIME) {
304		stat->result_mask |= STATX_BTIME;
305		stat->btime = hfsp_mt2ut(hip->create_date);
306	}
307
308	if (inode->i_flags & S_APPEND)
309		stat->attributes |= STATX_ATTR_APPEND;
310	if (inode->i_flags & S_IMMUTABLE)
311		stat->attributes |= STATX_ATTR_IMMUTABLE;
312	if (hip->userflags & HFSPLUS_FLG_NODUMP)
313		stat->attributes |= STATX_ATTR_NODUMP;
314
315	stat->attributes_mask |= STATX_ATTR_APPEND | STATX_ATTR_IMMUTABLE |
316				 STATX_ATTR_NODUMP;
317
318	generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
319	return 0;
320}
321
322int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end,
323		       int datasync)
324{
325	struct inode *inode = file->f_mapping->host;
326	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
327	struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
328	struct hfsplus_vh *vhdr = sbi->s_vhdr;
329	int error = 0, error2;
330
331	hfs_dbg("inode->i_ino %lu, start %llu, end %llu\n",
332		inode->i_ino, start, end);
333
334	error = file_write_and_wait_range(file, start, end);
335	if (error)
336		return error;
337	inode_lock(inode);
338
339	/*
340	 * Sync inode metadata into the catalog and extent trees.
341	 */
342	sync_inode_metadata(inode, 1);
343
344	/*
345	 * And explicitly write out the btrees.
346	 */
347	if (test_and_clear_bit(HFSPLUS_I_CAT_DIRTY, &hip->flags))
348		error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping);
349
350	if (test_and_clear_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags)) {
351		error2 =
352			filemap_write_and_wait(sbi->ext_tree->inode->i_mapping);
353		if (!error)
354			error = error2;
355	}
356
357	if (test_and_clear_bit(HFSPLUS_I_ATTR_DIRTY, &hip->flags)) {
358		if (sbi->attr_tree) {
359			error2 =
360				filemap_write_and_wait(
361					    sbi->attr_tree->inode->i_mapping);
362			if (!error)
363				error = error2;
364		} else {
365			pr_err("sync non-existent attributes tree\n");
366		}
367	}
368
369	if (test_and_clear_bit(HFSPLUS_I_ALLOC_DIRTY, &hip->flags)) {
370		error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping);
371		if (!error)
372			error = error2;
373	}
374
375	mutex_lock(&sbi->vh_mutex);
376	hfsplus_prepare_volume_header_for_commit(vhdr);
377	mutex_unlock(&sbi->vh_mutex);
378
379	error2 = hfsplus_commit_superblock(inode->i_sb);
380	if (!error)
381		error = error2;
382
383	if (!test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags))
384		blkdev_issue_flush(inode->i_sb->s_bdev);
385
386	inode_unlock(inode);
387
388	return error;
389}
390
391static const struct inode_operations hfsplus_file_inode_operations = {
392	.setattr	= hfsplus_setattr,
393	.getattr	= hfsplus_getattr,
394	.listxattr	= hfsplus_listxattr,
395	.fileattr_get	= hfsplus_fileattr_get,
396	.fileattr_set	= hfsplus_fileattr_set,
397};
398
399static const struct file_operations hfsplus_file_operations = {
400	.llseek		= generic_file_llseek,
401	.read_iter	= generic_file_read_iter,
402	.write_iter	= generic_file_write_iter,
403	.mmap_prepare	= generic_file_mmap_prepare,
404	.splice_read	= filemap_splice_read,
405	.splice_write	= iter_file_splice_write,
406	.fsync		= hfsplus_file_fsync,
407	.open		= hfsplus_file_open,
408	.release	= hfsplus_file_release,
409	.unlocked_ioctl = hfsplus_ioctl,
410};
411
412struct inode *hfsplus_new_inode(struct super_block *sb, struct inode *dir,
413				umode_t mode)
414{
415	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
416	struct inode *inode = new_inode(sb);
417	struct hfsplus_inode_info *hip;
418
419	if (!inode)
420		return NULL;
421
422	inode->i_ino = sbi->next_cnid++;
423	inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
424	set_nlink(inode, 1);
425	simple_inode_init_ts(inode);
426
427	hip = HFSPLUS_I(inode);
428	INIT_LIST_HEAD(&hip->open_dir_list);
429	spin_lock_init(&hip->open_dir_lock);
430	mutex_init(&hip->extents_lock);
431	atomic_set(&hip->opencnt, 0);
432	hip->extent_state = 0;
433	hip->flags = 0;
434	hip->userflags = 0;
435	hip->subfolders = 0;
436	memset(hip->first_extents, 0, sizeof(hfsplus_extent_rec));
437	memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec));
438	hip->alloc_blocks = 0;
439	hip->first_blocks = 0;
440	hip->cached_start = 0;
441	hip->cached_blocks = 0;
442	hip->phys_size = 0;
443	hip->fs_blocks = 0;
444	hip->rsrc_inode = NULL;
445	if (S_ISDIR(inode->i_mode)) {
446		inode->i_size = 2;
447		sbi->folder_count++;
448		inode->i_op = &hfsplus_dir_inode_operations;
449		inode->i_fop = &hfsplus_dir_operations;
450	} else if (S_ISREG(inode->i_mode)) {
451		sbi->file_count++;
452		inode->i_op = &hfsplus_file_inode_operations;
453		inode->i_fop = &hfsplus_file_operations;
454		inode->i_mapping->a_ops = &hfsplus_aops;
455		hip->clump_blocks = sbi->data_clump_blocks;
456	} else if (S_ISLNK(inode->i_mode)) {
457		sbi->file_count++;
458		inode->i_op = &page_symlink_inode_operations;
459		inode_nohighmem(inode);
460		inode->i_mapping->a_ops = &hfsplus_aops;
461		hip->clump_blocks = 1;
462	} else
463		sbi->file_count++;
464	insert_inode_hash(inode);
465	mark_inode_dirty(inode);
466	hfsplus_mark_mdb_dirty(sb);
467
468	return inode;
469}
470
471void hfsplus_delete_inode(struct inode *inode)
472{
473	struct super_block *sb = inode->i_sb;
474
475	if (S_ISDIR(inode->i_mode)) {
476		HFSPLUS_SB(sb)->folder_count--;
477		hfsplus_mark_mdb_dirty(sb);
478		return;
479	}
480	HFSPLUS_SB(sb)->file_count--;
481	if (S_ISREG(inode->i_mode)) {
482		if (!inode->i_nlink) {
483			inode->i_size = 0;
484			hfsplus_file_truncate(inode);
485		}
486	} else if (S_ISLNK(inode->i_mode)) {
487		inode->i_size = 0;
488		hfsplus_file_truncate(inode);
489	}
490	hfsplus_mark_mdb_dirty(sb);
491}
492
493void hfsplus_inode_read_fork(struct inode *inode, struct hfsplus_fork_raw *fork)
494{
495	struct super_block *sb = inode->i_sb;
496	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
497	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
498	u32 count;
499	int i;
500
501	memcpy(&hip->first_extents, &fork->extents, sizeof(hfsplus_extent_rec));
502	for (count = 0, i = 0; i < 8; i++)
503		count += be32_to_cpu(fork->extents[i].block_count);
504	hip->first_blocks = count;
505	memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec));
506	hip->cached_start = 0;
507	hip->cached_blocks = 0;
508
509	hip->alloc_blocks = be32_to_cpu(fork->total_blocks);
510	hip->phys_size = inode->i_size = be64_to_cpu(fork->total_size);
511	hip->fs_blocks =
512		(inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
513	inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits);
514	hip->clump_blocks =
515		be32_to_cpu(fork->clump_size) >> sbi->alloc_blksz_shift;
516	if (!hip->clump_blocks) {
517		hip->clump_blocks = HFSPLUS_IS_RSRC(inode) ?
518			sbi->rsrc_clump_blocks :
519			sbi->data_clump_blocks;
520	}
521}
522
523void hfsplus_inode_write_fork(struct inode *inode,
524		struct hfsplus_fork_raw *fork)
525{
526	memcpy(&fork->extents, &HFSPLUS_I(inode)->first_extents,
527	       sizeof(hfsplus_extent_rec));
528	fork->total_size = cpu_to_be64(inode->i_size);
529	fork->total_blocks = cpu_to_be32(HFSPLUS_I(inode)->alloc_blocks);
530}
531
532int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
533{
534	hfsplus_cat_entry entry;
535	int res = 0;
536	u16 type;
537
538	type = hfs_bnode_read_u16(fd->bnode, fd->entryoffset);
539
540	HFSPLUS_I(inode)->linkid = 0;
541	if (type == HFSPLUS_FOLDER) {
542		struct hfsplus_cat_folder *folder = &entry.folder;
543
544		if (fd->entrylength < sizeof(struct hfsplus_cat_folder)) {
545			pr_err("bad catalog folder entry\n");
546			res = -EIO;
547			goto out;
548		}
549		hfs_bnode_read(fd->bnode, &entry, fd->entryoffset,
550					sizeof(struct hfsplus_cat_folder));
551		res = hfsplus_get_perms(inode, &folder->permissions, 1);
552		if (res)
553			goto out;
554		set_nlink(inode, 1);
555		inode->i_size = 2 + be32_to_cpu(folder->valence);
556		inode_set_atime_to_ts(inode, hfsp_mt2ut(folder->access_date));
557		inode_set_mtime_to_ts(inode,
558				      hfsp_mt2ut(folder->content_mod_date));
559		inode_set_ctime_to_ts(inode,
560				      hfsp_mt2ut(folder->attribute_mod_date));
561		HFSPLUS_I(inode)->create_date = folder->create_date;
562		HFSPLUS_I(inode)->fs_blocks = 0;
563		if (folder->flags & cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT)) {
564			HFSPLUS_I(inode)->subfolders =
565				be32_to_cpu(folder->subfolders);
566		}
567		inode->i_op = &hfsplus_dir_inode_operations;
568		inode->i_fop = &hfsplus_dir_operations;
569	} else if (type == HFSPLUS_FILE) {
570		struct hfsplus_cat_file *file = &entry.file;
571
572		if (fd->entrylength < sizeof(struct hfsplus_cat_file)) {
573			pr_err("bad catalog file entry\n");
574			res = -EIO;
575			goto out;
576		}
577		hfs_bnode_read(fd->bnode, &entry, fd->entryoffset,
578					sizeof(struct hfsplus_cat_file));
579
580		hfsplus_inode_read_fork(inode, HFSPLUS_IS_RSRC(inode) ?
581					&file->rsrc_fork : &file->data_fork);
582		res = hfsplus_get_perms(inode, &file->permissions, 0);
583		if (res)
584			goto out;
585		set_nlink(inode, 1);
586		if (S_ISREG(inode->i_mode)) {
587			if (file->permissions.dev)
588				set_nlink(inode,
589					  be32_to_cpu(file->permissions.dev));
590			inode->i_op = &hfsplus_file_inode_operations;
591			inode->i_fop = &hfsplus_file_operations;
592			inode->i_mapping->a_ops = &hfsplus_aops;
593		} else if (S_ISLNK(inode->i_mode)) {
594			inode->i_op = &page_symlink_inode_operations;
595			inode_nohighmem(inode);
596			inode->i_mapping->a_ops = &hfsplus_aops;
597		} else {
598			init_special_inode(inode, inode->i_mode,
599					   be32_to_cpu(file->permissions.dev));
600		}
601		inode_set_atime_to_ts(inode, hfsp_mt2ut(file->access_date));
602		inode_set_mtime_to_ts(inode,
603				      hfsp_mt2ut(file->content_mod_date));
604		inode_set_ctime_to_ts(inode,
605				      hfsp_mt2ut(file->attribute_mod_date));
606		HFSPLUS_I(inode)->create_date = file->create_date;
607	} else {
608		pr_err("bad catalog entry used to create inode\n");
609		res = -EIO;
610	}
611out:
612	return res;
613}
614
615int hfsplus_cat_write_inode(struct inode *inode)
616{
617	struct inode *main_inode = inode;
618	struct hfs_btree *tree = HFSPLUS_SB(inode->i_sb)->cat_tree;
619	struct hfs_find_data fd;
620	hfsplus_cat_entry entry;
621	int res = 0;
622
623	hfs_dbg("inode->i_ino %lu\n", inode->i_ino);
624
625	if (HFSPLUS_IS_RSRC(inode))
626		main_inode = HFSPLUS_I(inode)->rsrc_inode;
627
628	if (!main_inode->i_nlink)
629		return 0;
630
631	if (hfs_find_init(tree, &fd))
632		/* panic? */
633		return -EIO;
634
635	if (hfsplus_find_cat(main_inode->i_sb, main_inode->i_ino, &fd))
636		/* panic? */
637		goto out;
638
639	if (S_ISDIR(main_inode->i_mode)) {
640		struct hfsplus_cat_folder *folder = &entry.folder;
641
642		if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
643			pr_err("bad catalog folder entry\n");
644			res = -EIO;
645			goto out;
646		}
647		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
648					sizeof(struct hfsplus_cat_folder));
649		/* simple node checks? */
650		hfsplus_cat_set_perms(inode, &folder->permissions);
651		folder->access_date = hfsp_ut2mt(inode_get_atime(inode));
652		folder->content_mod_date = hfsp_ut2mt(inode_get_mtime(inode));
653		folder->attribute_mod_date = hfsp_ut2mt(inode_get_ctime(inode));
654		folder->valence = cpu_to_be32(inode->i_size - 2);
655		if (folder->flags & cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT)) {
656			folder->subfolders =
657				cpu_to_be32(HFSPLUS_I(inode)->subfolders);
658		}
659		hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
660					 sizeof(struct hfsplus_cat_folder));
661	} else if (HFSPLUS_IS_RSRC(inode)) {
662		struct hfsplus_cat_file *file = &entry.file;
663		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
664			       sizeof(struct hfsplus_cat_file));
665		hfsplus_inode_write_fork(inode, &file->rsrc_fork);
666		hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
667				sizeof(struct hfsplus_cat_file));
668	} else {
669		struct hfsplus_cat_file *file = &entry.file;
670
671		if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
672			pr_err("bad catalog file entry\n");
673			res = -EIO;
674			goto out;
675		}
676		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
677					sizeof(struct hfsplus_cat_file));
678		hfsplus_inode_write_fork(inode, &file->data_fork);
679		hfsplus_cat_set_perms(inode, &file->permissions);
680		if (HFSPLUS_FLG_IMMUTABLE &
681				(file->permissions.rootflags |
682					file->permissions.userflags))
683			file->flags |= cpu_to_be16(HFSPLUS_FILE_LOCKED);
684		else
685			file->flags &= cpu_to_be16(~HFSPLUS_FILE_LOCKED);
686		file->access_date = hfsp_ut2mt(inode_get_atime(inode));
687		file->content_mod_date = hfsp_ut2mt(inode_get_mtime(inode));
688		file->attribute_mod_date = hfsp_ut2mt(inode_get_ctime(inode));
689		hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
690					 sizeof(struct hfsplus_cat_file));
691	}
692
693	set_bit(HFSPLUS_I_CAT_DIRTY, &HFSPLUS_I(inode)->flags);
694out:
695	hfs_find_exit(&fd);
696
697	if (!res) {
698		res = hfs_btree_write(tree);
699		if (res) {
700			pr_err("b-tree write err: %d, ino %lu\n",
701			       res, inode->i_ino);
702		}
703	}
704
705	return res;
706}
707
708int hfsplus_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
709{
710	struct inode *inode = d_inode(dentry);
711	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
712	unsigned int flags = 0;
713
714	if (inode->i_flags & S_IMMUTABLE)
715		flags |= FS_IMMUTABLE_FL;
716	if (inode->i_flags & S_APPEND)
717		flags |= FS_APPEND_FL;
718	if (hip->userflags & HFSPLUS_FLG_NODUMP)
719		flags |= FS_NODUMP_FL;
720
721	fileattr_fill_flags(fa, flags);
722
723	return 0;
724}
725
726int hfsplus_fileattr_set(struct mnt_idmap *idmap,
727			 struct dentry *dentry, struct file_kattr *fa)
728{
729	struct inode *inode = d_inode(dentry);
730	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
731	unsigned int new_fl = 0;
732
733	if (fileattr_has_fsx(fa))
734		return -EOPNOTSUPP;
735
736	/* don't silently ignore unsupported ext2 flags */
737	if (fa->flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL))
738		return -EOPNOTSUPP;
739
740	if (fa->flags & FS_IMMUTABLE_FL)
741		new_fl |= S_IMMUTABLE;
742
743	if (fa->flags & FS_APPEND_FL)
744		new_fl |= S_APPEND;
745
746	inode_set_flags(inode, new_fl, S_IMMUTABLE | S_APPEND);
747
748	if (fa->flags & FS_NODUMP_FL)
749		hip->userflags |= HFSPLUS_FLG_NODUMP;
750	else
751		hip->userflags &= ~HFSPLUS_FLG_NODUMP;
752
753	inode_set_ctime_current(inode);
754	mark_inode_dirty(inode);
755
756	return 0;
757}
~/ Read-only file preview.