/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * extend_file.c * * Adds extents to an OCFS2 inode. For the OCFS2 userspace library. * * Copyright (C) 2004 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License, version 2, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #if HAVE_UNISTD_H #include #endif #include #include #include #include "ocfs2/ocfs2.h" #include "extent_tree.h" /* * Insert an extent into an inode btree. */ errcode_t ocfs2_inode_insert_extent(ocfs2_filesys *fs, uint64_t ino, uint32_t cpos, uint64_t c_blkno, uint32_t clusters, uint16_t flag) { errcode_t ret; ocfs2_cached_inode *ci = NULL; ret = ocfs2_read_cached_inode(fs, ino, &ci); if (ret) goto bail; ret = ocfs2_cached_inode_insert_extent(ci, cpos, c_blkno, clusters, flag); if (ret) goto bail; ret = ocfs2_write_cached_inode(fs, ci); bail: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } errcode_t ocfs2_cached_inode_insert_extent(ocfs2_cached_inode *ci, uint32_t cpos, uint64_t c_blkno, uint32_t clusters, uint16_t flag) { struct ocfs2_extent_tree et; ocfs2_init_dinode_extent_tree(&et, ci->ci_fs, (char *)ci->ci_inode, ci->ci_inode->i_blkno); return ocfs2_tree_insert_extent(ci->ci_fs, &et, cpos, c_blkno, clusters, flag); } errcode_t ocfs2_cached_inode_extend_allocation(ocfs2_cached_inode *ci, uint32_t new_clusters) { errcode_t ret = 0; uint32_t n_clusters = 0, cpos; uint64_t blkno, file_size; ocfs2_filesys *fs = ci->ci_fs; file_size = ci->ci_inode->i_size; cpos = (file_size + fs->fs_clustersize - 1) / fs->fs_clustersize; while (new_clusters) { n_clusters = 1; ret = ocfs2_new_clusters(fs, 1, new_clusters, &blkno, &n_clusters); if (ret) break; ret = ocfs2_cached_inode_insert_extent(ci, cpos, blkno, n_clusters, 0); if (ret) { /* XXX: We don't wan't to overwrite the error * from insert_extent(). But we probably need * to BE LOUDLY UPSET. */ ocfs2_free_clusters(fs, n_clusters, blkno); break; } new_clusters -= n_clusters; cpos += n_clusters; } return ret; } errcode_t ocfs2_extend_allocation(ocfs2_filesys *fs, uint64_t ino, uint32_t new_clusters) { errcode_t ret; ocfs2_cached_inode *ci = NULL; ret = ocfs2_read_cached_inode(fs, ino, &ci); if (ret) goto bail; ret = ocfs2_cached_inode_extend_allocation(ci, new_clusters); if (ret) goto bail; ret = ocfs2_write_cached_inode(fs, ci); bail: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } errcode_t ocfs2_extend_file(ocfs2_filesys *fs, uint64_t ino, uint64_t new_size) { errcode_t ret = 0; char *buf = NULL; struct ocfs2_dinode* di = NULL; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) return ret; ret = ocfs2_read_inode(fs, ino, buf); if (ret) goto out_free_buf; di = (struct ocfs2_dinode *)buf; if (di->i_size >= new_size) { ret = EINVAL; goto out_free_buf; } di->i_size = new_size; ret = ocfs2_write_inode(fs, ino, buf); out_free_buf: if (buf) ocfs2_free(&buf); return ret; } errcode_t ocfs2_allocate_unwritten_extents(ocfs2_filesys *fs, uint64_t ino, uint64_t offset, uint64_t len) { errcode_t ret = 0; uint32_t n_clusters = 0, cpos; uint64_t p_blkno, v_blkno, v_end, contig_blocks, wanted_blocks; ocfs2_cached_inode *ci = NULL; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if (!ocfs2_writes_unwritten_extents(OCFS2_RAW_SB(fs->fs_super))) return OCFS2_ET_RO_UNSUPP_FEATURE; ret = ocfs2_read_cached_inode(fs, ino, &ci); if (ret) goto out; if (!(ci->ci_inode->i_flags & OCFS2_VALID_FL)) return OCFS2_ET_INODE_NOT_VALID; if (ci->ci_inode->i_flags & OCFS2_SYSTEM_FL) return OCFS2_ET_INVALID_ARGUMENT; if (!S_ISREG(ci->ci_inode->i_mode)) return OCFS2_ET_INVALID_ARGUMENT; v_blkno = offset / fs->fs_blocksize; v_end = (offset + len - 1) / fs->fs_blocksize; while (v_blkno <= v_end) { ret = ocfs2_extent_map_get_blocks(ci, v_blkno, 1, &p_blkno, &contig_blocks, NULL); if (p_blkno) { v_blkno += contig_blocks; continue; } /* * There is a hole, so we have to allocate the space and * insert the unwritten extents. */ wanted_blocks = ocfs2_min(contig_blocks, v_end - v_blkno + 1); n_clusters = ocfs2_clusters_in_blocks(fs, wanted_blocks); ret = ocfs2_new_clusters(fs, 1, n_clusters, &p_blkno, &n_clusters); if (ret || n_clusters == 0) break; cpos = ocfs2_blocks_to_clusters(fs, v_blkno); ret = ocfs2_cached_inode_insert_extent(ci, cpos, p_blkno, n_clusters, OCFS2_EXT_UNWRITTEN); if (ret) { /* * XXX: We don't wan't to overwrite the error * from insert_extent(). But we probably need * to BE LOUDLY UPSET. */ ocfs2_free_clusters(fs, n_clusters, p_blkno); goto out; } /* save up what we have done. */ ret = ocfs2_write_cached_inode(fs, ci); if (ret) goto out; v_blkno = ocfs2_clusters_to_blocks(fs, cpos + n_clusters); } if (ci->ci_inode->i_size <= offset + len) { ci->ci_inode->i_size = offset + len; ret = ocfs2_write_cached_inode(fs, ci); } out: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; } /* * Mark the already-existing extent at cpos as written for len clusters. * * If the existing extent is larger than the request, initiate a * split. An attempt will be made at merging with adjacent extents. * */ int ocfs2_mark_extent_written(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint32_t cpos, uint32_t len, uint64_t p_blkno) { struct ocfs2_extent_tree et; if (!ocfs2_writes_unwritten_extents(OCFS2_RAW_SB(fs->fs_super))) return OCFS2_ET_UNSUPP_FEATURE; ocfs2_init_dinode_extent_tree(&et, fs, (char *)di, di->i_blkno); return ocfs2_change_extent_flag(fs, &et, cpos, len, p_blkno, 0, OCFS2_EXT_UNWRITTEN); } #ifdef DEBUG_EXE #include #include #include #include static void print_usage(void) { fprintf(stdout, "debug_extend_file -i -c \n"); } static uint64_t read_number(const char *num) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr || *ptr) return 0; return val; } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; char *filename; ocfs2_filesys *fs; uint64_t ino = 0; uint32_t new_clusters = 0; int c; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "i:c:")) != EOF) { switch (c) { case 'i': ino = read_number(optarg); if (ino <= OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid inode block: %s\n", optarg); print_usage(); return 1; } break; case 'c': new_clusters = read_number(optarg); if (!new_clusters) { fprintf(stderr, "Invalid cluster count: %s\n", optarg); print_usage(); return 1; } break; default: print_usage(); return 1; break; } } if (!ino) { fprintf(stderr, "You must specify an inode block\n"); print_usage(); return 1; } if (!new_clusters) { fprintf(stderr, "You must specify how many clusters to extend\n"); print_usage(); return 1; } if (optind >= argc) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[optind]; ret = ocfs2_open(filename, OCFS2_FLAG_RW, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_extend_allocation(fs, ino, new_clusters); if (ret) { com_err(argv[0], ret, "while extending inode %"PRIu64, ino); } ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return !!ret; } #endif /* DEBUG_EXE */