/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * Copyright (C) 2005 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 XOPEN2K in features.h */ #define _LARGEFILE64_SOURCE #include "ocfs2/byteorder.h" #include "ocfs2/ocfs2.h" /* Just so we can pass it about */ union ocfs2_slot_map_wrapper { struct ocfs2_slot_map *mw_map; struct ocfs2_slot_map_extended *mw_map_extended; }; void ocfs2_swap_slot_map(struct ocfs2_slot_map *sm, int num_slots) { int i; if (!cpu_is_big_endian) return; for (i = 0; i < num_slots; i++) sm->sm_slots[i] = bswap_16(sm->sm_slots[i]); } void ocfs2_swap_slot_map_extended(struct ocfs2_slot_map_extended *se, int num_slots) { int i; if (!cpu_is_big_endian) return; for (i = 0; i < num_slots; i++) se->se_slots[i].es_node_num = bswap_32(se->se_slots[i].es_node_num); } static errcode_t __ocfs2_read_slot_map(ocfs2_filesys *fs, int num_slots, union ocfs2_slot_map_wrapper *wrap) { errcode_t ret; uint64_t blkno; char *slot_map_buf; struct ocfs2_slot_map *sm; struct ocfs2_slot_map_extended *se; int bytes_needed, len; int extended = ocfs2_uses_extended_slot_map(OCFS2_RAW_SB(fs->fs_super)); ret = ocfs2_lookup_system_inode(fs, SLOT_MAP_SYSTEM_INODE, 0, &blkno); if (ret) return ret; ret = ocfs2_read_whole_file(fs, blkno, &slot_map_buf, &len); if (ret) return ret; if (extended) bytes_needed = num_slots * sizeof(struct ocfs2_extended_slot); else bytes_needed = num_slots * sizeof(__le16); if (bytes_needed > len) { ocfs2_free(&slot_map_buf); return OCFS2_ET_SHORT_READ; } if (extended) { se = (struct ocfs2_slot_map_extended *)slot_map_buf; ocfs2_swap_slot_map_extended(se, num_slots); wrap->mw_map_extended = se; } else { sm = (struct ocfs2_slot_map *)slot_map_buf; ocfs2_swap_slot_map(sm, num_slots); wrap->mw_map = sm; } return 0; } errcode_t ocfs2_read_slot_map(ocfs2_filesys *fs, int num_slots, struct ocfs2_slot_map **map_ret) { errcode_t ret; union ocfs2_slot_map_wrapper wrap = { .mw_map = NULL, }; ret = __ocfs2_read_slot_map(fs, num_slots, &wrap); if (ret) return ret; *map_ret = wrap.mw_map; return 0; } errcode_t ocfs2_read_slot_map_extended(ocfs2_filesys *fs, int num_slots, struct ocfs2_slot_map_extended **map_ret) { errcode_t ret; union ocfs2_slot_map_wrapper wrap = { .mw_map_extended = NULL, }; ret = __ocfs2_read_slot_map(fs, num_slots, &wrap); if (ret) return ret; *map_ret = wrap.mw_map_extended; return 0; } static errcode_t __ocfs2_write_slot_map(ocfs2_filesys *fs, int num_slots, union ocfs2_slot_map_wrapper *wrap) { errcode_t ret, tret; ocfs2_cached_inode *ci = NULL; uint64_t blkno; unsigned int size, bytes, wrote, blocks; void *buf = NULL; int extended = ocfs2_uses_extended_slot_map(OCFS2_RAW_SB(fs->fs_super)); ret = ocfs2_lookup_system_inode(fs, SLOT_MAP_SYSTEM_INODE, 0, &blkno); if (ret) goto out; if (extended) size = num_slots * sizeof(struct ocfs2_extended_slot); else size = num_slots * sizeof(__le16); blocks = ocfs2_blocks_in_bytes(fs, size); bytes = blocks << OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; ret = ocfs2_malloc_blocks(fs->fs_io, blocks, &buf); if (ret) goto out; memset(buf, 0, bytes); if (extended) { memcpy(buf, wrap->mw_map_extended, size); ocfs2_swap_slot_map_extended(buf, num_slots); } else { memcpy(buf, wrap->mw_map, size); ocfs2_swap_slot_map(buf, num_slots); } ret = ocfs2_read_cached_inode(fs, blkno, &ci); if (ret) goto out; ret = ocfs2_file_write(ci, buf, bytes, 0, &wrote); if (ret) goto out; /* * This is wacky. We have to write a block (bytes), but &wrote * might return only i_size (size). Handle both. */ if ((wrote != bytes) && (wrote != size)) ret = OCFS2_ET_SHORT_WRITE; out: if (ci) { tret = ocfs2_free_cached_inode(fs, ci); /* * The error from free_cached_inode() is only important if * there were no other problems. */ if (!ret) ret = tret; } if (buf) ocfs2_free(&buf); return ret; } errcode_t ocfs2_write_slot_map(ocfs2_filesys *fs, int num_slots, struct ocfs2_slot_map *sm) { union ocfs2_slot_map_wrapper wrap = { .mw_map = sm, }; return __ocfs2_write_slot_map(fs, num_slots, &wrap); } errcode_t ocfs2_write_slot_map_extended(ocfs2_filesys *fs, int num_slots, struct ocfs2_slot_map_extended *se) { union ocfs2_slot_map_wrapper wrap = { .mw_map_extended = se, }; return __ocfs2_write_slot_map(fs, num_slots, &wrap); } static void ocfs2_slot_map_to_data(ocfs2_filesys *fs, struct ocfs2_slot_map_data *md, union ocfs2_slot_map_wrapper *wrap) { int i; struct ocfs2_slot_map *sm = wrap->mw_map; struct ocfs2_slot_map_extended *se = wrap->mw_map_extended; int extended = ocfs2_uses_extended_slot_map(OCFS2_RAW_SB(fs->fs_super)); for (i = 0; i < md->md_num_slots; i++) { if (extended) { if (se->se_slots[i].es_valid) { md->md_slots[i].sd_valid = 1; md->md_slots[i].sd_node_num = se->se_slots[i].es_node_num; } else md->md_slots[i].sd_valid = 0; } else { if (sm->sm_slots[i] != (uint16_t)OCFS2_INVALID_SLOT) { md->md_slots[i].sd_valid = 1; md->md_slots[i].sd_node_num = sm->sm_slots[i]; } else md->md_slots[i].sd_valid = 0; } } } static void ocfs2_slot_data_to_map(ocfs2_filesys *fs, struct ocfs2_slot_map_data *md, union ocfs2_slot_map_wrapper *wrap) { int i; struct ocfs2_slot_map *sm = wrap->mw_map; struct ocfs2_slot_map_extended *se = wrap->mw_map_extended; int extended = ocfs2_uses_extended_slot_map(OCFS2_RAW_SB(fs->fs_super)); for (i = 0; i < md->md_num_slots; i++) { if (extended) { if (md->md_slots[i].sd_valid) { se->se_slots[i].es_valid = 1; se->se_slots[i].es_node_num = md->md_slots[i].sd_node_num; } else se->se_slots[i].es_valid = 0; } else { if (md->md_slots[i].sd_valid) sm->sm_slots[i] = (uint16_t)md->md_slots[i].sd_node_num; else sm->sm_slots[i] = (uint16_t)OCFS2_INVALID_SLOT; } } } static errcode_t ocfs2_alloc_slot_map_data(int num_slots, struct ocfs2_slot_map_data **md_ret) { errcode_t ret; struct ocfs2_slot_map_data *md; ret = ocfs2_malloc0(sizeof(struct ocfs2_slot_map_data) + (sizeof(struct ocfs2_slot_data) * num_slots), &md); if (ret) return ret; md->md_num_slots = num_slots; md->md_slots = (struct ocfs2_slot_data *)((char *)md + sizeof(struct ocfs2_slot_map_data)); *md_ret = md; return 0; } errcode_t ocfs2_load_slot_map(ocfs2_filesys *fs, struct ocfs2_slot_map_data **data_ret) { errcode_t ret; int num_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots;; struct ocfs2_slot_map_data *md; union ocfs2_slot_map_wrapper wrap = { .mw_map = NULL, }; ret = ocfs2_alloc_slot_map_data(num_slots, &md); if (ret) return ret; ret = __ocfs2_read_slot_map(fs, num_slots, &wrap); if (ret) { ocfs2_free(&md); return ret; } ocfs2_slot_map_to_data(fs, md, &wrap); *data_ret = md; return 0; } errcode_t ocfs2_store_slot_map(ocfs2_filesys *fs, struct ocfs2_slot_map_data *md) { errcode_t ret; char *slot_map_buf; int bytes; int extended = ocfs2_uses_extended_slot_map(OCFS2_RAW_SB(fs->fs_super)); union ocfs2_slot_map_wrapper wrap; if (extended) bytes = md->md_num_slots * sizeof(struct ocfs2_extended_slot); else bytes = md->md_num_slots * sizeof(__le16); ret = ocfs2_malloc0(bytes, &slot_map_buf); if (ret) return ret; wrap.mw_map = (struct ocfs2_slot_map *)slot_map_buf; ocfs2_slot_data_to_map(fs, md, &wrap); ret = __ocfs2_write_slot_map(fs, md->md_num_slots, &wrap); ocfs2_free(&slot_map_buf); return ret; } struct slotmap_format { int extended; int needed_slots; int actual_slots; unsigned int needed_bytes; ocfs2_cached_inode *ci; }; static errcode_t ocfs2_size_slot_map(ocfs2_filesys *fs, struct slotmap_format *sf) { errcode_t ret ; struct ocfs2_dinode *di; unsigned int clusters; uint64_t new_size; uint64_t blkno; di = sf->ci->ci_inode; blkno = sf->ci->ci_blkno; clusters = sf->needed_bytes + fs->fs_clustersize - 1; clusters = clusters >> OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; /* Zero slots not allowed - even local mounts have a slot */ if (!clusters) { ret = OCFS2_ET_INTERNAL_FAILURE; goto out; } /* * We ensure that slotmaps are formatted to the end of the * allocation. If the allocation hasn't changed, we don't have * anything to do. */ if (clusters == di->i_clusters) { ret = 0; goto out; } if (clusters > di->i_clusters) { ret = ocfs2_extend_allocation(fs, blkno, (clusters - di->i_clusters)); if (ret) goto out; /* We don't cache in the library right now, so any * work done in extend_allocation won't be reflected * in our now stale copy. */ ocfs2_free_cached_inode(fs, sf->ci); ret = ocfs2_read_cached_inode(fs, blkno, &sf->ci); if (ret) { sf->ci = NULL; goto out; } di = sf->ci->ci_inode; } else if (clusters < di->i_clusters) { new_size = clusters << OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; ret = ocfs2_truncate(fs, blkno, new_size); if (ret) goto out; ocfs2_free_cached_inode(fs, sf->ci); ret = ocfs2_read_cached_inode(fs, blkno, &sf->ci); if (ret) { sf->ci = NULL; goto out; } di = sf->ci->ci_inode; } /* * Now that we've adjusted the allocation, write out the * correct i_size. By design, the slot map's i_size encompasses * the full allocation. */ di->i_size = di->i_clusters << OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits; di->i_mtime = time(NULL); ret = ocfs2_write_inode(fs, blkno, (char *)di); if (ret) goto out; out: return ret; } errcode_t ocfs2_format_slot_map(ocfs2_filesys *fs) { errcode_t ret; uint64_t blkno; struct slotmap_format sf; struct ocfs2_slot_map_data *md = NULL; ret = ocfs2_lookup_system_inode(fs, SLOT_MAP_SYSTEM_INODE, 0, &blkno); if (ret) goto out; ret = ocfs2_read_cached_inode(fs, blkno, &sf.ci); if (ret) goto out; /* verify it is a system file */ if (!(sf.ci->ci_inode->i_flags & OCFS2_VALID_FL) || !(sf.ci->ci_inode->i_flags & OCFS2_SYSTEM_FL)) { ret = OCFS2_ET_INTERNAL_FAILURE; goto out; } sf.extended = ocfs2_uses_extended_slot_map(OCFS2_RAW_SB(fs->fs_super)); sf.needed_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots; if (!sf.extended && (sf.needed_slots > OCFS2_MAX_SLOTS)) { ret = OCFS2_ET_TOO_MANY_SLOTS; goto out; } if (sf.extended) sf.needed_bytes = sf.needed_slots * sizeof(struct ocfs2_extended_slot); else sf.needed_bytes = sf.needed_slots * sizeof(__le16); ret = ocfs2_size_slot_map(fs, &sf); if (ret) goto out; if (sf.extended) sf.actual_slots = sf.ci->ci_inode->i_size / sizeof(struct ocfs2_extended_slot); else sf.actual_slots = sf.ci->ci_inode->i_size / sizeof(__le16); /* This returns an empty map that covers the entire allocation */ ret = ocfs2_alloc_slot_map_data(sf.actual_slots, &md); if (ret) return ret; ret = ocfs2_store_slot_map(fs, md); out: if (sf.ci) ocfs2_free_cached_inode(fs, sf.ci); if (md) ocfs2_free(&md); return ret; }