/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * extent_map.c * * In-memory extent map 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 #include #include #include "ocfs2/ocfs2.h" #include "extent_map.h" /* * Return the 1st index within el which contains an extent start * larger than v_cluster. */ static int ocfs2_search_for_hole_index(struct ocfs2_extent_list *el, uint32_t v_cluster) { int i; struct ocfs2_extent_rec *rec; for(i = 0; i < el->l_next_free_rec; i++) { rec = &el->l_recs[i]; if (v_cluster < rec->e_cpos) break; } return i; } /* * Figure out the size of a hole which starts at v_cluster within the given * extent list. * * If there is no more allocation past v_cluster, we return the maximum * cluster size minus v_cluster. * * If we have in-inode extents, then el points to the dinode list and * eb_buf is NULL. Otherwise, eb_buf should point to the extent block * containing el. */ static int ocfs2_figure_hole_clusters(ocfs2_cached_inode *cinode, struct ocfs2_extent_list *el, char *eb_buf, uint32_t v_cluster, uint32_t *num_clusters) { int ret, i; char *next_eb_buf = NULL; struct ocfs2_extent_block *eb, *next_eb; i = ocfs2_search_for_hole_index(el, v_cluster); if (i == el->l_next_free_rec && eb_buf) { eb = (struct ocfs2_extent_block *)eb_buf; /* * Check the next leaf for any extents. */ if (eb->h_next_leaf_blk == 0) goto no_more_extents; ret = ocfs2_malloc_block(cinode->ci_fs->fs_io, &next_eb_buf); if (ret) goto out; ret = ocfs2_read_extent_block(cinode->ci_fs, eb->h_next_leaf_blk, next_eb_buf); if (ret) goto out; next_eb = (struct ocfs2_extent_block *)next_eb_buf; el = &next_eb->h_list; i = ocfs2_search_for_hole_index(el, v_cluster); if (i > 0) { if ((i > 1) || ocfs2_rec_clusters(el->l_tree_depth, &el->l_recs[0])) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } } } no_more_extents: if (i == el->l_next_free_rec) { /* * We're at the end of our existing allocation. Just * return the maximum number of clusters we could * possibly allocate. */ *num_clusters = UINT32_MAX - v_cluster; } else *num_clusters = el->l_recs[i].e_cpos - v_cluster; ret = 0; out: if (next_eb_buf) ocfs2_free(&next_eb_buf); return ret; } errcode_t ocfs2_get_clusters(ocfs2_cached_inode *cinode, uint32_t v_cluster, uint32_t *p_cluster, uint32_t *num_clusters, uint16_t *extent_flags) { int i; uint16_t flags = 0; errcode_t ret = 0; ocfs2_filesys *fs = cinode->ci_fs; struct ocfs2_dinode *di; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *el; struct ocfs2_extent_rec *rec; char *eb_buf = NULL; uint32_t coff; di = cinode->ci_inode; el = &di->id2.i_list; if (el->l_tree_depth) { ret = ocfs2_find_leaf(fs, di, v_cluster, &eb_buf); if (ret) goto out; eb = (struct ocfs2_extent_block *) eb_buf; el = &eb->h_list; if (el->l_tree_depth) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } } i = ocfs2_search_extent_list(el, v_cluster); if (i == -1) { /* * A hole was found. Return some canned values that * callers can key on. If asked for, num_clusters will * be populated with the size of the hole. */ *p_cluster = 0; if (num_clusters) { ret = ocfs2_figure_hole_clusters(cinode, el, eb_buf, v_cluster, num_clusters); if (ret) goto out; } } else { rec = &el->l_recs[i]; assert(v_cluster >= rec->e_cpos); if (!rec->e_blkno) { ret = OCFS2_ET_BAD_BLKNO; goto out; } coff = v_cluster - rec->e_cpos; *p_cluster = ocfs2_blocks_to_clusters(fs, rec->e_blkno); *p_cluster = *p_cluster + coff; if (num_clusters) *num_clusters = ocfs2_rec_clusters(el->l_tree_depth, rec) - coff; flags = rec->e_flags; } if (extent_flags) *extent_flags = flags; out: if (eb_buf) ocfs2_free(&eb_buf); return ret; } errcode_t ocfs2_xattr_get_clusters(ocfs2_filesys *fs, struct ocfs2_extent_list *el, uint64_t el_blkno, char *el_blk, uint32_t v_cluster, uint32_t *p_cluster, uint32_t *num_clusters, uint16_t *extent_flags) { int i; errcode_t ret = 0; struct ocfs2_extent_block *eb; struct ocfs2_extent_rec *rec; char *eb_buf = NULL; uint32_t coff; if (el->l_tree_depth) { ret = ocfs2_tree_find_leaf(fs, el, el_blkno, el_blk, v_cluster, &eb_buf); if (ret) goto out; eb = (struct ocfs2_extent_block *)eb_buf; el = &eb->h_list; if (el->l_tree_depth) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto out; } } i = ocfs2_search_extent_list(el, v_cluster); if (i == -1) { ret = -1; goto out; } else { rec = &el->l_recs[i]; assert(v_cluster >= rec->e_cpos); if (!rec->e_blkno) { ret = OCFS2_ET_BAD_BLKNO; goto out; } coff = v_cluster - rec->e_cpos; *p_cluster = ocfs2_blocks_to_clusters(fs, rec->e_blkno); *p_cluster = *p_cluster + coff; if (num_clusters) *num_clusters = ocfs2_rec_clusters(el->l_tree_depth, rec) - coff; if (extent_flags) *extent_flags = rec->e_flags; } out: if (eb_buf) ocfs2_free(&eb_buf); return ret; } errcode_t ocfs2_extent_map_get_blocks(ocfs2_cached_inode *cinode, uint64_t v_blkno, int count, uint64_t *p_blkno, uint64_t *ret_count, uint16_t *extent_flags) { errcode_t ret; int bpc; uint32_t cpos, num_clusters = -1, p_cluster = -1; uint64_t boff = 0; ocfs2_filesys *fs = cinode->ci_fs; bpc = ocfs2_clusters_to_blocks(fs, 1); cpos = ocfs2_blocks_to_clusters(fs, v_blkno); ret = ocfs2_get_clusters(cinode, cpos, &p_cluster, &num_clusters, extent_flags); if (ret) goto out; /* * p_cluster == 0 indicates a hole. */ if (p_cluster) { boff = ocfs2_clusters_to_blocks(fs, p_cluster); boff += (v_blkno & (uint64_t)(bpc - 1)); } *p_blkno = boff; if (ret_count) { *ret_count = ocfs2_clusters_to_blocks(fs, num_clusters); *ret_count -= v_blkno & (uint64_t)(bpc - 1); } out: return ret; } errcode_t ocfs2_get_last_cluster_offset(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint32_t *v_cluster) { errcode_t ret = 0; char *buf = NULL; struct ocfs2_extent_list *el = NULL; struct ocfs2_extent_rec *er = NULL; el = &di->id2.i_list; *v_cluster = 0; if (!el->l_next_free_rec) return 0; if (el->l_tree_depth) { ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto bail; ret = ocfs2_read_extent_block(fs, di->i_last_eb_blk, buf); if (ret) goto bail; el = &((struct ocfs2_extent_block *)buf)->h_list; if (!el->l_next_free_rec || (el->l_next_free_rec == 1 && ocfs2_is_empty_extent(&el->l_recs[0]))) { ret = OCFS2_ET_CORRUPT_EXTENT_BLOCK; goto bail; } } er = &el->l_recs[el->l_next_free_rec - 1]; *v_cluster = er->e_cpos + er->e_leaf_clusters - 1; bail: if (buf) ocfs2_free(&buf); return ret; } #ifdef DEBUG_EXE #include #include #include enum debug_op { OP_NONE = 0, OP_LOOKUP_BLOCK, }; 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; } static int read_b_numbers(const char *num, uint64_t *blkno, int *count) { uint64_t val; char *ptr; val = strtoull(num, &ptr, 0); if (!ptr) return 1; if (*ptr != ':') return 1; *blkno = val; ptr++; val = strtoull(ptr, &ptr, 0); if (!ptr || *ptr) return 1; if (val > INT_MAX) return 1; *count = (int)val; return 0; } static void print_usage(void) { fprintf(stderr, "Usage: extent_map -i -b : \n"); } extern int opterr, optind; extern char *optarg; int main(int argc, char *argv[]) { errcode_t ret; uint64_t blkno, contig, blkoff = 0; uint16_t ext_flags; int count = 0; int c, op = 0; char *filename; ocfs2_filesys *fs; ocfs2_cached_inode *cinode; blkno = OCFS2_SUPER_BLOCK_BLKNO; initialize_ocfs_error_table(); while ((c = getopt(argc, argv, "i:b:")) != EOF) { switch (c) { case 'i': blkno = read_number(optarg); if (blkno <= OCFS2_SUPER_BLOCK_BLKNO) { fprintf(stderr, "Invalid inode block: %s\n", optarg); print_usage(); return 1; } break; case 'b': if (op) { fprintf(stderr, "Cannot specify more than one operation\n"); print_usage(); return 1; } if (read_b_numbers(optarg, &blkoff, &count)) { fprintf(stderr, "Invalid block range: %s\n", optarg); print_usage(); return 1; } op = OP_LOOKUP_BLOCK; break; default: print_usage(); return 1; break; } } if (!op) { fprintf(stderr, "Missing operation\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_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } ret = ocfs2_read_cached_inode(fs, blkno, &cinode); if (ret) { com_err(argv[0], ret, "while reading inode %"PRIu64, blkno); goto out_close; } fprintf(stdout, "OCFS2 inode %"PRIu64" on \"%s\" has depth %"PRId16"\n", blkno, filename, cinode->ci_inode->id2.i_list.l_tree_depth); ret = ocfs2_extent_map_get_blocks(cinode, blkoff, count, &blkno, &contig, &ext_flags); if (ret) { com_err(argv[0], ret, "looking up block range %"PRIu64":%d", blkoff, count); goto out_free; } fprintf(stdout, "Lookup of block range %"PRIu64":%d returned %"PRIu64":%"PRIu64"\n", blkoff, count, blkno, contig); out_free: ocfs2_free_cached_inode(fs, cinode); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; } #endif /* DEBUG_EXE */