Sigrid Solveig Haflínudóttirindentation fixef15b742 years agoHistory
3246 lines · 65.0 KB · C
RawDownload
/* * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright *   notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright *   notice, this list of conditions and the following disclaimer in the *   documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products *   derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup lwext4 * @{ *//** * @file  ext4.h * @brief Ext4 high level operations (file, directory, mountpoints...) */ #include <ext4.h>#include <ext4_trans.h>#include <ext4_fs.h>#include <ext4_dir.h>#include <ext4_inode.h>#include <ext4_super.h>#include <ext4_block_group.h>#include <ext4_dir_idx.h>#include <ext4_xattr.h>#include <ext4_journal.h>  #include <stdlib.h>#include <string.h> /**@brief   Mount point OS dependent lock*/#define EXT4_MP_LOCK(_m)                                               \	do {                                                               \		if ((_m)->os_locks)                                            \			(_m)->os_locks->lock((_m)->os_locks->p_user);              \	} while (0) /**@brief   Mount point OS dependent unlock*/#define EXT4_MP_UNLOCK(_m)                                             \	do {                                                               \		if ((_m)->os_locks)                                            \			(_m)->os_locks->unlock((_m)->os_locks->p_user);            \	} while (0) /**@brief   Mount point descriptor.*/struct ext4_mountpoint { 	/**@brief   Mount done flag.*/	bool mounted; 	/**@brief   Mount point name (@ref ext4_mount)*/	char name[CONFIG_EXT4_MAX_MP_NAME + 1]; 	/**@brief   OS dependent lock/unlock functions.*/	const struct ext4_lock *os_locks; 	/**@brief   Ext4 filesystem internals.*/	struct ext4_fs fs; 	/**@brief   JBD fs.*/	struct jbd_fs jbd_fs; 	/**@brief   Journal.*/	struct jbd_journal jbd_journal; 	/**@brief   Block cache.*/	struct ext4_bcache bc;}; /**@brief   Block devices descriptor.*/struct ext4_block_devices { 	/**@brief   Block device name.*/	char name[CONFIG_EXT4_MAX_BLOCKDEV_NAME + 1]; 	/**@brief   Block device handle.*/	struct ext4_blockdev *bd;}; /**@brief   Block devices.*/static struct ext4_block_devices s_bdevices[CONFIG_EXT4_BLOCKDEVS_COUNT]; /**@brief   Mountpoints.*/static struct ext4_mountpoint s_mp[CONFIG_EXT4_MOUNTPOINTS_COUNT]; int ext4_device_register(struct ext4_blockdev *bd,			 const char *dev_name){	ext4_assert(bd && dev_name); 	if (strlen(dev_name) > CONFIG_EXT4_MAX_BLOCKDEV_NAME)		return EINVAL; 	for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {		if (!strcmp(s_bdevices[i].name, dev_name))			return EEXIST;	} 	for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {		if (!s_bdevices[i].bd) {			strcpy(s_bdevices[i].name, dev_name);			s_bdevices[i].bd = bd;			return EOK;		}	} 	return ENOSPC;} int ext4_device_unregister(const char *dev_name){	ext4_assert(dev_name); 	for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {		if (strcmp(s_bdevices[i].name, dev_name) == 0) {            memset(&s_bdevices[i], 0, sizeof(s_bdevices[i]));            return EOK;        }	} 	return ENOENT;} int ext4_device_unregister_all(void){	memset(s_bdevices, 0, sizeof(s_bdevices)); 	return EOK;} /****************************************************************************/ static bool ext4_is_dots(const uint8_t *name, size_t name_size){	if ((name_size == 1) && (name[0] == '.'))		return true; 	if ((name_size == 2) && (name[0] == '.') && (name[1] == '.'))		return true; 	return false;} static int ext4_has_children(bool *has_children, struct ext4_inode_ref *enode){	struct ext4_sblock *sb = &enode->fs->sb; 	/* Check if node is directory */	if (!ext4_inode_is_type(sb, enode->inode, EXT4_INODE_MODE_DIRECTORY)) {		*has_children = false;		return EOK;	} 	struct ext4_dir_iter it;	int rc = ext4_dir_iterator_init(&it, enode, 0);	if (rc != EOK)		return rc; 	/* Find a non-empty directory entry */	bool found = false;	while (it.curr != NULL) {		if (ext4_dir_en_get_inode(it.curr) != 0) {			uint16_t nsize;			nsize = ext4_dir_en_get_name_len(sb, it.curr);			if (!ext4_is_dots(it.curr->name, nsize)) {				found = true;				break;			}		} 		rc = ext4_dir_iterator_next(&it);		if (rc != EOK) {			ext4_dir_iterator_fini(&it);			return rc;		}	} 	rc = ext4_dir_iterator_fini(&it);	if (rc != EOK)		return rc; 	*has_children = found; 	return EOK;} static int ext4_link(struct ext4_mountpoint *mp, struct ext4_inode_ref *parent,		     struct ext4_inode_ref *ch, const char *n,		     uint32_t len, bool rename){	/* Check maximum name length */	if (len > EXT4_DIRECTORY_FILENAME_LEN)		return EINVAL; 	/* Add entry to parent directory */	int r = ext4_dir_add_entry(parent, n, len, ch);	if (r != EOK)		return r; 	/* Fill new dir -> add '.' and '..' entries.	 * Also newly allocated inode should have 0 link count.	 */ 	bool is_dir = ext4_inode_is_type(&mp->fs.sb, ch->inode,			       EXT4_INODE_MODE_DIRECTORY);	if (is_dir && !rename) { #if CONFIG_DIR_INDEX_ENABLE		/* Initialize directory index if supported */		if (ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_DIR_INDEX)) {			r = ext4_dir_dx_init(ch, parent);			if (r != EOK)				return r; 			ext4_inode_set_flag(ch->inode, EXT4_INODE_FLAG_INDEX);			ch->dirty = true;		} else#endif		r = ext4_dir_add_entry(ch, ".", strlen("."), ch);		if (r != EOK) {			ext4_dir_remove_entry(parent, n, strlen(n));			return r;		} 		r = ext4_dir_add_entry(ch, "..", strlen(".."), parent);		if (r != EOK) {			ext4_dir_remove_entry(parent, n, strlen(n));			ext4_dir_remove_entry(ch, ".", strlen("."));			return r;		} 		/*New empty directory. Two links (. and ..) */		ext4_inode_set_links_cnt(ch->inode, 2);		ext4_fs_inode_links_count_inc(parent);		ch->dirty = true;		parent->dirty = true;		return r;	}	/*	 * In case we want to rename a directory,	 * we reset the original '..' pointer.	 */	if (is_dir) {		bool idx;		idx = ext4_inode_has_flag(ch->inode, EXT4_INODE_FLAG_INDEX);		struct ext4_dir_search_result res;		if (!idx) {			r = ext4_dir_find_entry(&res, ch, "..", strlen(".."));			if (r != EOK)				return EIO; 			ext4_dir_en_set_inode(res.dentry, parent->index);			ext4_trans_set_block_dirty(res.block.buf);			r = ext4_dir_destroy_result(ch, &res);			if (r != EOK)				return r; 		} else {#if CONFIG_DIR_INDEX_ENABLE			r = ext4_dir_dx_reset_parent_inode(ch, parent->index);			if (r != EOK)				return r; #endif		} 		ext4_fs_inode_links_count_inc(parent);		parent->dirty = true;	}	if (!rename) {		ext4_fs_inode_links_count_inc(ch);		ch->dirty = true;	} 	return r;} static int ext4_unlink(struct ext4_mountpoint *mp,		       struct ext4_inode_ref *parent,		       struct ext4_inode_ref *child, const char *name,		       uint32_t name_len){	bool has_children;	int rc = ext4_has_children(&has_children, child);	if (rc != EOK)		return rc; 	/* Cannot unlink non-empty node */	if (has_children)		return ENOTEMPTY; 	/* Remove entry from parent directory */	rc = ext4_dir_remove_entry(parent, name, name_len);	if (rc != EOK)		return rc; 	bool is_dir = ext4_inode_is_type(&mp->fs.sb, child->inode,					 EXT4_INODE_MODE_DIRECTORY); 	/* If directory - handle links from parent */	if (is_dir) {		ext4_fs_inode_links_count_dec(parent);		parent->dirty = true;	} 	/*	 * TODO: Update timestamps of the parent	 * (when we have wall-clock time).	 *	 * ext4_inode_set_change_inode_time(parent->inode, (uint32_t) now);	 * ext4_inode_set_modification_time(parent->inode, (uint32_t) now);	 * parent->dirty = true;	 */ 	/*	 * TODO: Update timestamp for inode.	 *	 * ext4_inode_set_change_inode_time(child->inode,	 *     (uint32_t) now);	 */	if (ext4_inode_get_links_cnt(child->inode)) {		ext4_fs_inode_links_count_dec(child);		child->dirty = true;	} 	return EOK;} /****************************************************************************/ int ext4_mount(const char *dev_name, const char *mount_point,	       bool read_only){	int r;	uint32_t bsize;	struct ext4_bcache *bc;	struct ext4_blockdev *bd = 0;	struct ext4_mountpoint *mp = 0; 	ext4_assert(mount_point && dev_name); 	size_t mp_len = strlen(mount_point); 	if (mp_len > CONFIG_EXT4_MAX_MP_NAME)		return EINVAL; 	if (mount_point[mp_len - 1] != '/')		return ENOTSUP; 	for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {		if (!strcmp(dev_name, s_bdevices[i].name)) {			bd = s_bdevices[i].bd;			break;		}	} 	if (!bd)		return ENODEV; 	for (size_t i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {		if (!s_mp[i].mounted) {			strcpy(s_mp[i].name, mount_point);			s_mp[i].mounted = 1;			mp = &s_mp[i];			break;		} 		if (!strcmp(s_mp[i].name, mount_point))			return EOK;	} 	if (!mp)		return ENOMEM; 	r = ext4_block_init(bd);	if (r != EOK)		return r; 	r = ext4_fs_init(&mp->fs, bd, read_only);	if (r != EOK) {		ext4_block_fini(bd);		return r;	} 	bsize = ext4_sb_get_block_size(&mp->fs.sb);	ext4_block_set_lb_size(bd, bsize);	bc = &mp->bc; 	r = ext4_bcache_init_dynamic(bc, CONFIG_BLOCK_DEV_CACHE_SIZE, bsize);	if (r != EOK) {		ext4_block_fini(bd);		return r;	} 	if (bsize != bc->itemsize)		return ENOTSUP; 	/*Bind block cache to block device*/	r = ext4_block_bind_bcache(bd, bc);	if (r != EOK) {		ext4_bcache_cleanup(bc);		ext4_block_fini(bd);		ext4_bcache_fini_dynamic(bc);		return r;	} 	bd->fs = &mp->fs;	return r;}  int ext4_umount(const char *mount_point){	int i;	int r;	struct ext4_mountpoint *mp = 0; 	for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {		if (!strcmp(s_mp[i].name, mount_point)) {			mp = &s_mp[i];			break;		}	} 	if (!mp)		return ENODEV; 	r = ext4_fs_fini(&mp->fs);	if (r != EOK)		goto Finish; 	mp->mounted = 0; 	ext4_bcache_cleanup(mp->fs.bdev->bc);	ext4_bcache_fini_dynamic(mp->fs.bdev->bc); 	r = ext4_block_fini(mp->fs.bdev);Finish:	mp->fs.bdev->fs = NULL;	return r;} static struct ext4_mountpoint *ext4_get_mount(const char *path){	for (size_t i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) { 		if (!s_mp[i].mounted)			continue; 		if (!strncmp(s_mp[i].name, path, strlen(s_mp[i].name)))			return &s_mp[i];	} 	return NULL;} __unusedstatic int __ext4_journal_start(const char *mount_point){	int r = EOK;	struct ext4_mountpoint *mp = ext4_get_mount(mount_point); 	if (!mp)		return ENOENT; 	if (mp->fs.read_only)		return EOK; 	if (ext4_sb_feature_com(&mp->fs.sb,				EXT4_FCOM_HAS_JOURNAL)) {		r = jbd_get_fs(&mp->fs, &mp->jbd_fs);		if (r != EOK)			goto Finish; 		r = jbd_journal_start(&mp->jbd_fs, &mp->jbd_journal);		if (r != EOK) {			mp->jbd_fs.dirty = false;			jbd_put_fs(&mp->jbd_fs);			goto Finish;

Lines 1–500 of 3246.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t