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