int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) { int status = 0, size_change; struct inode *inode = dentry->d_inode; struct super_block *sb = inode->i_sb; struct ocfs2_super *osb = OCFS2_SB(sb); struct buffer_head *bh = NULL; handle_t *handle = NULL; mlog_entry("(0x%p, '%.*s')\n", dentry, dentry->d_name.len, dentry->d_name.name); if (attr->ia_valid & ATTR_MODE) mlog(0, "mode change: %d\n", attr->ia_mode); if (attr->ia_valid & ATTR_UID) mlog(0, "uid change: %d\n", attr->ia_uid); if (attr->ia_valid & ATTR_GID) mlog(0, "gid change: %d\n", attr->ia_gid); if (attr->ia_valid & ATTR_SIZE) mlog(0, "size change...\n"); if (attr->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_CTIME)) mlog(0, "time change...\n"); #define OCFS2_VALID_ATTRS (ATTR_ATIME | ATTR_MTIME | ATTR_CTIME | ATTR_SIZE \ | ATTR_GID | ATTR_UID | ATTR_MODE) if (!(attr->ia_valid & OCFS2_VALID_ATTRS)) { mlog(0, "can't handle attrs: 0x%x\n", attr->ia_valid); return 0; } status = inode_change_ok(inode, attr); if (status) return status; size_change = S_ISREG(inode->i_mode) && attr->ia_valid & ATTR_SIZE; if (size_change) { status = ocfs2_rw_lock(inode, 1); if (status < 0) { mlog_errno(status); goto bail; } } status = ocfs2_meta_lock(inode, &bh, 1); if (status < 0) { if (status != -ENOENT) mlog_errno(status); goto bail_unlock_rw; } if (size_change && attr->ia_size != i_size_read(inode)) { if (i_size_read(inode) > attr->ia_size) status = ocfs2_truncate_file(inode, bh, attr->ia_size); else status = ocfs2_extend_file(inode, bh, attr->ia_size, 0); if (status < 0) { if (status != -ENOSPC) mlog_errno(status); status = -ENOSPC; goto bail_unlock; } } handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); if (IS_ERR(handle)) { status = PTR_ERR(handle); mlog_errno(status); goto bail_unlock; } status = inode_setattr(inode, attr); if (status < 0) { mlog_errno(status); goto bail_commit; } status = ocfs2_mark_inode_dirty(handle, inode, bh); if (status < 0) mlog_errno(status); bail_commit: ocfs2_commit_trans(osb, handle); bail_unlock: ocfs2_meta_unlock(inode, 1); bail_unlock_rw: if (size_change) ocfs2_rw_unlock(inode, 1); bail: if (bh) brelse(bh); mlog_exit(status); return status; }
/* XXX care about zeroing new clusters and final partially truncated * clusters */ errcode_t ocfs2_truncate_full(ocfs2_filesys *fs, uint64_t ino, uint64_t new_i_size, errcode_t (*free_clusters)(ocfs2_filesys *fs, uint32_t len, uint64_t start, void *free_data), void *free_data) { errcode_t ret; uint32_t new_clusters; ocfs2_cached_inode *ci = NULL; ret = ocfs2_read_cached_inode(fs, ino, &ci); if (ret) goto out; /* in case of dio crashed, force do trucate since blocks may already * be allocated */ if (ci->ci_inode->i_flags & cpu_to_le32(OCFS2_DIO_ORPHANED_FL)) { ci->ci_inode->i_flags &= ~cpu_to_le32(OCFS2_DIO_ORPHANED_FL); ci->ci_inode->i_dio_orphaned_slot = 0; new_i_size = ci->ci_inode->i_size; goto truncate; } if (ci->ci_inode->i_size == new_i_size) goto out; if (ci->ci_inode->i_size < new_i_size) { ret = ocfs2_extend_file(fs, ino, new_i_size); goto out; } truncate: if ((S_ISLNK(ci->ci_inode->i_mode) && !ci->ci_inode->i_clusters) || (ci->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL)) ret = ocfs2_truncate_inline(fs, ino, new_i_size); else { ret = ocfs2_zero_tail_and_truncate_full(fs, ci, new_i_size, &new_clusters, free_clusters, free_data); if (ret) goto out; ci->ci_inode->i_clusters = new_clusters; /* now all the clusters and extent blocks are freed. * only when the file's content is empty, should the tree depth * change. */ if (new_clusters == 0) ci->ci_inode->id2.i_list.l_tree_depth = 0; ci->ci_inode->i_size = new_i_size; ret = ocfs2_write_cached_inode(fs, ci); } if (!ret && !new_i_size && ci->ci_inode->i_refcount_loc && (ci->ci_inode->i_dyn_features & OCFS2_HAS_REFCOUNT_FL)) ret = ocfs2_detach_refcount_tree(fs, ino, ci->ci_inode->i_refcount_loc); out: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; }
static int ocfs2_prepare_inode_for_write(struct dentry *dentry, loff_t *ppos, size_t count, int appending, int *direct_io) { int ret = 0, meta_level = appending; struct inode *inode = dentry->d_inode; u32 clusters; loff_t newsize, saved_pos; /* * We sample i_size under a read level meta lock to see if our write * is extending the file, if it is we back off and get a write level * meta lock. */ for(;;) { ret = ocfs2_meta_lock(inode, NULL, meta_level); if (ret < 0) { meta_level = -1; mlog_errno(ret); goto out; } /* Clear suid / sgid if necessary. We do this here * instead of later in the write path because * remove_suid() calls ->setattr without any hint that * we may have already done our cluster locking. Since * ocfs2_setattr() *must* take cluster locks to * proceeed, this will lead us to recursively lock the * inode. There's also the dinode i_size state which * can be lost via setattr during extending writes (we * set inode->i_size at the end of a write. */ if (should_remove_suid(dentry)) { if (meta_level == 0) { ocfs2_meta_unlock(inode, meta_level); meta_level = 1; continue; } ret = ocfs2_write_remove_suid(inode); if (ret < 0) { mlog_errno(ret); goto out_unlock; } } /* work on a copy of ppos until we're sure that we won't have * to recalculate it due to relocking. */ if (appending) { saved_pos = i_size_read(inode); mlog(0, "O_APPEND: inode->i_size=%llu\n", saved_pos); } else { saved_pos = *ppos; } if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) { loff_t end = saved_pos + count; /* * Skip the O_DIRECT checks if we don't need * them. */ if (!direct_io || !(*direct_io)) break; /* * Allowing concurrent direct writes means * i_size changes wouldn't be synchronized, so * one node could wind up truncating another * nodes writes. */ if (end > i_size_read(inode)) { *direct_io = 0; break; } /* * We don't fill holes during direct io, so * check for them here. If any are found, the * caller will have to retake some cluster * locks and initiate the io as buffered. */ ret = ocfs2_check_range_for_holes(inode, saved_pos, count); if (ret == 1) { *direct_io = 0; ret = 0; } else if (ret < 0) mlog_errno(ret); break; } /* * The rest of this loop is concerned with legacy file * systems which don't support sparse files. */ newsize = count + saved_pos; mlog(0, "pos=%lld newsize=%lld cursize=%lld\n", (long long) saved_pos, (long long) newsize, (long long) i_size_read(inode)); /* No need for a higher level metadata lock if we're * never going past i_size. */ if (newsize <= i_size_read(inode)) break; if (meta_level == 0) { ocfs2_meta_unlock(inode, meta_level); meta_level = 1; continue; } spin_lock(&OCFS2_I(inode)->ip_lock); clusters = ocfs2_clusters_for_bytes(inode->i_sb, newsize) - OCFS2_I(inode)->ip_clusters; spin_unlock(&OCFS2_I(inode)->ip_lock); mlog(0, "Writing at EOF, may need more allocation: " "i_size = %lld, newsize = %lld, need %u clusters\n", (long long) i_size_read(inode), (long long) newsize, clusters); /* We only want to continue the rest of this loop if * our extend will actually require more * allocation. */ if (!clusters) break; ret = ocfs2_extend_file(inode, NULL, newsize, count); if (ret < 0) { if (ret != -ENOSPC) mlog_errno(ret); goto out_unlock; } break; } if (appending) *ppos = saved_pos; out_unlock: ocfs2_meta_unlock(inode, meta_level); out: return ret; }