/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * expanddir.c * * Expands an OCFS2 directory. 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. * * This code is a port of e2fsprogs/lib/ext2fs/expanddir.c * Copyright (C) 1993, 1999 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #if HAVE_UNISTD_H #include #endif #include "ocfs2/ocfs2.h" /* * ocfs2_expand_dir() * */ errcode_t ocfs2_expand_dir(ocfs2_filesys *fs, uint64_t dir) { errcode_t ret = 0; ocfs2_cached_inode *cinode = NULL; uint64_t used_blks; uint64_t totl_blks; uint64_t new_blk; uint64_t contig; char *buf = NULL; struct ocfs2_dir_entry *de; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; /* ensure it is a dir */ ret = ocfs2_check_directory(fs, dir); if (ret) goto bail; /* read inode */ ret = ocfs2_read_cached_inode(fs, dir, &cinode); if (ret) goto bail; if (ocfs2_support_inline_data(OCFS2_RAW_SB(fs->fs_super)) && cinode->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL) { ret = ocfs2_convert_inline_data_to_extents(cinode); if ((ret == 0) && ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(fs->fs_super))) { ret = ocfs2_dx_dir_build(fs, dir); } goto bail; } /* This relies on the fact that i_size of a directory is a * multiple of blocksize */ used_blks = cinode->ci_inode->i_size >> OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; totl_blks = ocfs2_clusters_to_blocks(fs, cinode->ci_inode->i_clusters); if (used_blks >= totl_blks) { ocfs2_free_cached_inode(fs, cinode); cinode = NULL; /* extend the directory */ ret = ocfs2_extend_allocation(fs, dir, 1); if (ret) goto bail; ret = ocfs2_read_cached_inode(fs, dir, &cinode); if (ret) goto bail; } /* get the next free block */ ret = ocfs2_extent_map_get_blocks(cinode, used_blks, 1, &new_blk, &contig, NULL); if (ret) goto bail; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto bail; memset(buf, 0, fs->fs_blocksize); de = (struct ocfs2_dir_entry *)buf; if (ocfs2_dir_has_trailer(fs, cinode->ci_inode)) { de->rec_len = ocfs2_dir_trailer_blk_off(fs); ocfs2_init_dir_trailer(fs, cinode->ci_inode, new_blk, buf); } else de->rec_len = fs->fs_blocksize; /* write new dir block */ ret = ocfs2_write_dir_block(fs, cinode->ci_inode, new_blk, buf); if (ret) goto bail; /* increase the size */ cinode->ci_inode->i_size += fs->fs_blocksize; /* update the size of the inode */ ret = ocfs2_write_cached_inode(fs, cinode); if (ret) goto bail; bail: if (buf) ocfs2_free(&buf); if (cinode) ocfs2_free_cached_inode(fs, cinode); return ret; } static void ocfs2_fill_initial_dirents(uint64_t dir, uint64_t parent, char *start, uint16_t size) { struct ocfs2_dir_entry *de = (struct ocfs2_dir_entry *)start; de->inode = dir; de->name_len = 1; de->rec_len = OCFS2_DIR_REC_LEN(de->name_len); de->name[0] = '.'; de->file_type = OCFS2_FT_DIR; de = (struct ocfs2_dir_entry *) ((char *)de + de->rec_len); de->inode = parent; de->rec_len = size - OCFS2_DIR_REC_LEN(1); de->name_len = 2; strcpy(de->name, ".."); de->file_type = OCFS2_FT_DIR; } errcode_t ocfs2_init_dir(ocfs2_filesys *fs, uint64_t dir, uint64_t parent_dir) { errcode_t ret = 0; ocfs2_cached_inode *cinode = NULL; struct ocfs2_dinode *parent; uint16_t size; uint64_t blkno, contig; char *buf = NULL, *data = NULL; struct ocfs2_inline_data *inline_data; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; /* ensure it is a dir */ ret = ocfs2_check_directory(fs, dir); if (ret) goto bail; /* read inode */ ret = ocfs2_read_cached_inode(fs, dir, &cinode); if (ret) goto bail; ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto bail; if (ocfs2_support_inline_data(OCFS2_RAW_SB(fs->fs_super))) { if (!(cinode->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL)) return OCFS2_ET_DIR_CORRUPTED; inline_data = &cinode->ci_inode->id2.i_data; data = (char *)inline_data->id_data; size = inline_data->id_count; } else { if (cinode->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL) return OCFS2_ET_DIR_CORRUPTED; ret = ocfs2_expand_dir(fs, dir); if (ret) goto bail; ocfs2_free_cached_inode(fs, cinode); cinode = NULL; ret = ocfs2_read_cached_inode(fs, dir, &cinode); if (ret) goto bail; ret = ocfs2_extent_map_get_blocks(cinode, 0, 1, &blkno, &contig, NULL); if (ret) goto bail; data = buf; size = fs->fs_blocksize; if (ocfs2_supports_dir_trailer(fs)) size = ocfs2_dir_trailer_blk_off(fs); } /* set '..' and '.' in dir. */ ocfs2_fill_initial_dirents(dir, parent_dir, data, size); /* And set the trailer if necessary */ if (!(cinode->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL) && ocfs2_supports_dir_trailer(fs)) ocfs2_init_dir_trailer(fs, cinode->ci_inode, blkno, data); if (!(cinode->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL)) { ret = ocfs2_write_dir_block(fs, cinode->ci_inode, blkno, buf); if (ret) goto bail; } /* * Only build indexed tree if the directory is initiated as non-inline. * Otherwise, the indexed tree will be build when convert the inlined * directory to extent in ocfs2_expand_dir() */ if (ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(fs->fs_super)) && !(cinode->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL)) { ret = ocfs2_dx_dir_build(fs, dir); if (ret) goto bail; /* * Re-read the 'cached inode' as ocfs2_dx_dir_build() * may have written out changes which won't be * reflected in our copy. */ ret = ocfs2_read_cached_inode(fs, dir, &cinode); if (ret) goto bail; } /* set link count of the parent */ ret = ocfs2_read_inode(fs, parent_dir, buf); if (ret) goto bail; parent = (struct ocfs2_dinode *)buf; parent->i_links_count++; ret = ocfs2_write_inode(fs, parent_dir, buf); if (ret) goto bail; /* increase the size */ cinode->ci_inode->i_size = size; /* update the inode */ ret = ocfs2_write_cached_inode(fs, cinode); bail: if (buf) ocfs2_free(&buf); if (cinode) ocfs2_free_cached_inode(fs, cinode); return ret; }