usbloadergx/libcustomext2fs/source/ext2_internal.c
dimok321 0f17471b27 *Removed ntfs/fat source and added them as custom libs (makes them easier to update later)
*Added sources of the custom libs to the branches
*Fixed crash when switching from list layout to grid/carousel layout
*Removed 1:1 copy option because its meaningless and almost the same as installing all partitions
*Fixed install partition selection. This option needs a reset. Go to settings and reselect your option for this.
*Fixed schinese and tchinese language modes (filename bugs. has to be schinese.lang and tchinese.lang like on SVN)
*Fixed bug in sound buffer circle
*Fixed incorrect behaviour of x-flip when selecting system like (thx Cyan for the patch)
*Accept ios revision 65535 for Waninkokos IOSes (thx to PPSainity for pointing it out)
*Merged the new theming style branch into trunk. Just as a reminder: ALL old themes will not work until the themers did port it to the new style!
*Removed old theme style completely

Theme example:
The example file of the theme is the Default.them file. It can be found in the SVN trunk.

Change in loading of themes:
When selecting a theme now a list of all .them files in a folder is displayed. The image folder of that theme has to be in the same folder as the .them file. The image path is defined in the head of the .them file in the line with "Image-Folder: Example\n".
2010-12-26 17:02:14 +00:00

1077 lines
24 KiB
C

/**
* ext2_internal.c - Internal support routines for EXT2-based devices.
*
* Copyright (c) 2006 Michael "Chishm" Chisholm
* Copyright (c) 2009 Rhys "Shareese" Koedijk
* Copyright (c) 2010 Dimok
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file 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 02111-1307 USA
*/
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "ext2_internal.h"
#include "ext2dir.h"
#include "ext2file.h"
#include "gekko_io.h"
// EXT2 device driver devoptab
static const devoptab_t devops_ext2 =
{
NULL, /* Device name */
sizeof(ext2_file_state),
ext2_open_r,
ext2_close_r,
ext2_write_r,
ext2_read_r,
ext2_seek_r,
ext2_fstat_r,
ext2_stat_r,
ext2_link_r,
ext2_unlink_r,
ext2_chdir_r,
ext2_rename_r,
ext2_mkdir_r,
sizeof(ext2_dir_state),
ext2_diropen_r,
ext2_dirreset_r,
ext2_dirnext_r,
ext2_dirclose_r,
ext2_statvfs_r,
ext2_ftruncate_r,
ext2_fsync_r,
NULL /* Device data */
};
const devoptab_t *ext2GetDevOpTab()
{
return &devops_ext2;
}
int ext2AddDevice (const char *name, void *deviceData)
{
const devoptab_t *devoptab_ext2 = ext2GetDevOpTab();
devoptab_t *dev = NULL;
char *devname = NULL;
int i;
// Sanity check
if (!name || !deviceData || !devoptab_ext2) {
errno = EINVAL;
return -1;
}
// Allocate a devoptab for this device
dev = (devoptab_t *) mem_alloc(sizeof(devoptab_t) + strlen(name) + 1);
if (!dev) {
errno = ENOMEM;
return -1;
}
// Use the space allocated at the end of the devoptab for storing the device name
devname = (char*)(dev + 1);
strcpy(devname, name);
// Setup the devoptab
memcpy(dev, devoptab_ext2, sizeof(devoptab_t));
dev->name = devname;
dev->deviceData = deviceData;
// Add the device to the devoptab table (if there is a free slot)
for (i = 0; i < STD_MAX; i++) {
if (devoptab_list[i] == devoptab_list[0] && i != 0) {
devoptab_list[i] = dev;
return 0;
}
}
// If we reach here then there are no free slots in the devoptab table for this device
errno = EADDRNOTAVAIL;
return -1;
}
void ext2RemoveDevice (const char *path)
{
const devoptab_t *devoptab = NULL;
char name[128] = {0};
int i;
// Get the device name from the path
strncpy(name, path, 127);
strtok(name, ":/");
// Find and remove the specified device from the devoptab table
// NOTE: We do this manually due to a 'bug' in RemoveDevice
// which ignores names with suffixes
for (i = 0; i < STD_MAX; i++) {
devoptab = devoptab_list[i];
if (devoptab && devoptab->name) {
if (strcmp(name, devoptab->name) == 0) {
devoptab_list[i] = devoptab_list[0];
mem_free((devoptab_t*)devoptab);
break;
}
}
}
return;
}
const devoptab_t *ext2GetDevice (const char *path)
{
const devoptab_t *devoptab = NULL;
char name[128] = {0};
int i;
// Get the device name from the path
strncpy(name, path, 127);
strtok(name, ":/");
// Search the devoptab table for the specified device name
// NOTE: We do this manually due to a 'bug' in GetDeviceOpTab
// which ignores names with suffixes
for (i = 0; i < STD_MAX; i++) {
devoptab = devoptab_list[i];
if (devoptab && devoptab->name) {
if (strcmp(name, devoptab->name) == 0) {
return devoptab;
}
}
}
return NULL;
}
ext2_vd *ext2GetVolume (const char *path)
{
// Get the volume descriptor from the paths associated devoptab (if found)
const devoptab_t *devoptab_ext2 = ext2GetDevOpTab();
const devoptab_t *devoptab = ext2GetDevice(path);
if (devoptab && devoptab_ext2 && (devoptab->open_r == devoptab_ext2->open_r))
return (ext2_vd*)devoptab->deviceData;
return NULL;
}
int ext2InitVolume (ext2_vd *vd)
{
// Sanity check
if (!vd) {
errno = ENODEV;
return -1;
}
// Reset the volumes data
memset(vd, 0, sizeof(ext2_vd));
// Initialise the volume lock
LWP_MutexInit(&vd->lock, false);
return 0;
}
void ext2DeinitVolume (ext2_vd *vd)
{
// Sanity check
if (!vd) {
errno = ENODEV;
return;
}
// Lock
ext2Lock(vd);
// Close any directories which are still open (lazy programmers!)
ext2_dir_state *nextDir = vd->firstOpenDir;
while (nextDir) {
ext2CloseDir(nextDir);
nextDir = nextDir->nextOpenDir;
}
// Close any files which are still open (lazy programmers!)
ext2_file_state *nextFile = vd->firstOpenFile;
while (nextFile) {
ext2CloseFile(nextFile);
nextFile = nextFile->nextOpenFile;
}
// Reset open directory and file stats
vd->openDirCount = 0;
vd->openFileCount = 0;
vd->firstOpenDir = NULL;
vd->firstOpenFile = NULL;
// Force the underlying device to sync
ext2Sync(vd, NULL);
// Unlock
ext2Unlock(vd);
// Deinitialise the volume lock
LWP_MutexDestroy(vd->lock);
}
static ext2_ino_t ext2PathToInode(ext2_vd *vd, const char * path)
{
//Sanity check
if(!vd || !path)
return 0;
char filename[EXT2_NAME_LEN];
errcode_t errorcode = 0;
ext2_ino_t ino = 0, parent = vd->cwd_ni && *path != '/' && *path != '\0' ? vd->cwd_ni->ino : vd->root;
const char * ptr = path;
int i;
while(*ptr == '/') ++ptr;
if(*ptr == '\0')
return parent;
while(*ptr != '\0')
{
for(i = 0; *ptr != '\0' && *ptr != '/' && (i < EXT2_NAME_LEN-1); ++ptr, ++i)
filename[i] = *ptr;
filename[i] = '\0';
errorcode = ext2fs_namei(vd->fs, vd->root, parent, filename, &ino);
if(errorcode != EXT2_ET_OK)
return 0;
parent = ino;
while(*ptr == '/') ++ptr;
}
return ino;
}
ext2_inode_t *ext2OpenEntry (ext2_vd *vd, const char *path)
{
errcode_t errorcode = 0;
ext2_inode_t * ni = 0;
// Sanity check
if (!vd) {
errno = ENODEV;
return NULL;
}
// Get the actual path of the entry
path = ext2RealPath(path);
if (!path)
{
errno = EINVAL;
return NULL;
}
ni = mem_alloc(sizeof(ext2_inode_t));
if(!ni)
{
errno = ENOMEM;
return NULL;
}
memset(ni, 0, sizeof(ext2_inode_t));
// Find the entry, taking into account our current directory (if any)
ni->ino = ext2PathToInode(vd, path);
if(ni->ino == 0)
{
mem_free(ni);
return NULL;
}
errorcode = ext2fs_read_inode(vd->fs, ni->ino, &ni->ni);
if(errorcode)
{
mem_free(ni);
return NULL;
}
return ni;
}
void ext2CloseEntry(ext2_vd *vd, ext2_inode_t * ni)
{
// Sanity check
if (!vd || !ni) {
errno = ENODEV;
return;
}
// Lock
ext2Lock(vd);
// Sync the entry (if it is dirty)
if(ni && ni->dirty)
ext2fs_write_inode(vd->fs, ni->ino, &ni->ni);
// Close the entry
if(ni)
mem_free(ni);
// Unlock
ext2Unlock(vd);
return;
}
static ext2_ino_t ext2CreateSymlink(ext2_vd *vd, const char *path, const char * targetdir, const char * name, mode_t type)
{
ext2_inode_t *target_ni = NULL;
ext2_ino_t newentry = 0;
ext2_ino_t ino = 0;
// Check if it does exist
target_ni = ext2OpenEntry(vd, targetdir);
if (!target_ni)
goto cleanup;
int err = ext2fs_new_inode(vd->fs, target_ni->ino, type, 0, &ino);
if (err)
goto cleanup;
do
{
err = ext2fs_link(vd->fs, target_ni->ino, name, ino, EXT2_FT_SYMLINK);
if (err == EXT2_ET_DIR_NO_SPACE)
{
err = ext2fs_expand_dir(vd->fs, target_ni->ino);
if (err)
goto cleanup;
}
}
while(err == EXT2_ET_DIR_NO_SPACE);
ext2fs_inode_alloc_stats2(vd->fs, ino, +1, 0);
struct ext2_inode inode;
memset(&inode, 0, sizeof(inode));
inode.i_mode = type;
inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
inode.i_links_count = 1;
inode.i_size = strlen(path); //initial size of file
inode.i_uid = target_ni->ni.i_uid;
inode.i_gid = target_ni->ni.i_gid;
if (strlen(path) <= sizeof(inode.i_block))
{
/* fast symlink */
strncpy((char *)&(inode.i_block[0]),path,sizeof(inode.i_blocks));
}
else
{
/* slow symlink */
char * buffer = mem_alloc(vd->fs->blocksize);
if (buffer)
{
blk_t blk;
strncpy(buffer, path, vd->fs->blocksize);
err = ext2fs_new_block(vd->fs, 0, 0, &blk);
if (!err)
{
inode.i_block[0] = blk;
inode.i_blocks = vd->fs->blocksize / BYTES_PER_SECTOR;
vd->fs->io->manager->write_blk(vd->fs->io, blk, 1, buffer);
ext2fs_block_alloc_stats(vd->fs, blk, +1);
}
mem_free(buffer);
}
}
if(ext2fs_write_new_inode(vd->fs, ino, &inode) != 0)
newentry = ino;
cleanup:
if(target_ni)
ext2CloseEntry(vd, target_ni);
return newentry;
}
static ext2_ino_t ext2CreateMkDir(ext2_vd *vd, ext2_inode_t * parent, int type, const char * name)
{
ext2_ino_t newentry = 0;
ext2_ino_t existing;
if(ext2fs_namei(vd->fs, vd->root, parent->ino, name, &existing) == 0)
return 0;
errcode_t err = ext2fs_new_inode(vd->fs, parent->ino, type, 0, &newentry);
if(err != EXT2_ET_OK)
return 0;
do
{
err = ext2fs_mkdir(vd->fs, parent->ino, newentry, name);
if(err == EXT2_ET_DIR_NO_SPACE)
{
if(ext2fs_expand_dir(vd->fs, parent->ino) != 0)
return 0;
}
}
while(err == EXT2_ET_DIR_NO_SPACE);
if(err != EXT2_ET_OK)
return 0;
struct ext2_inode inode;
if(ext2fs_read_inode(vd->fs, newentry, &inode) == EXT2_ET_OK)
{
inode.i_mode = type;
inode.i_uid = parent->ni.i_uid;
inode.i_gid = parent->ni.i_gid;
ext2fs_write_new_inode(vd->fs, newentry, &inode);
}
return newentry;
}
static ext2_ino_t ext2CreateFile(ext2_vd *vd, ext2_inode_t * parent, int type, const char * name)
{
errcode_t retval = -1;
ext2_ino_t newfile = 0;
ext2_ino_t existing;
if(ext2fs_namei(vd->fs, vd->root, parent->ino, name, &existing) == 0)
return 0;
retval = ext2fs_new_inode(vd->fs, parent->ino, type, 0, &newfile);
if (retval)
return 0;
do
{
retval = ext2fs_link(vd->fs, parent->ino, name, newfile, EXT2_FT_REG_FILE);
if (retval == EXT2_ET_DIR_NO_SPACE)
{
if (ext2fs_expand_dir(vd->fs, parent->ino) != 0)
return 0;
}
}
while(retval == EXT2_ET_DIR_NO_SPACE);
if (retval)
return 0;
ext2fs_inode_alloc_stats2(vd->fs, newfile, +1, 0);
struct ext2_inode inode;
memset(&inode, 0, sizeof(inode));
inode.i_mode = type;
inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
inode.i_links_count = 1;
inode.i_size = 0;
inode.i_uid = parent->ni.i_uid;
inode.i_gid = parent->ni.i_gid;
if (ext2fs_write_new_inode(vd->fs, newfile, &inode) != 0)
return 0;
return newfile;
}
ext2_inode_t *ext2Create(ext2_vd *vd, const char *path, mode_t type, const char *target)
{
ext2_inode_t *dir_ni = NULL, *ni = NULL;
char *dir = NULL;
char *targetdir = NULL;
char *name = NULL;
ext2_ino_t newentry = 0;
// Sanity check
if (!vd || !vd->fs) {
errno = ENODEV;
return NULL;
}
if(!(vd->fs->flags & EXT2_FLAG_RW))
return NULL;
// You cannot link between devices
if(target) {
if(vd != ext2GetVolume(target)) {
errno = EXDEV;
return NULL;
}
// Check if existing
dir_ni = ext2OpenEntry(vd, target);
if (dir_ni) {
goto cleanup;
}
ext2CloseEntry(vd, dir_ni);
dir_ni = NULL;
targetdir = strdup(target);
if (!targetdir) {
errno = EINVAL;
goto cleanup;
}
}
// Get the actual paths of the entry
path = ext2RealPath(path);
target = ext2RealPath(target);
if (!path) {
errno = EINVAL;
return NULL;
}
// Lock
ext2Lock(vd);
// Clean me
// NOTE: this looks horrible right now and need a cleanup
dir = strdup(path);
if (!dir) {
errno = EINVAL;
goto cleanup;
}
char * tmp_path = (targetdir && (type == S_IFLNK)) ? targetdir : dir;
if (strrchr(tmp_path, '/') != NULL)
{
char * ptr = strrchr(tmp_path, '/');
name = strdup(ptr+1);
*ptr = '\0';
}
else
name = strdup(tmp_path);
// Open the entries parent directory
dir_ni = ext2OpenEntry(vd, dir);
if (!dir_ni) {
goto cleanup;
}
// If not yet read, read the inode and block bitmap
if(!vd->fs->inode_map || !vd->fs->block_map)
ext2fs_read_bitmaps(vd->fs);
// Symbolic link
if(type == S_IFLNK)
{
if (!target) {
errno = EINVAL;
goto cleanup;
}
newentry = ext2CreateSymlink(vd, path, targetdir, name, type);
}
// Directory
else if(type == S_IFDIR)
{
newentry = ext2CreateMkDir(vd, dir_ni, LINUX_S_IFDIR | (0755 & ~vd->fs->umask), name);
}
// File
else if(type == S_IFREG)
{
newentry = ext2CreateFile(vd, dir_ni, LINUX_S_IFREG | (0755 & ~vd->fs->umask), name);
}
// If the entry was created
if (newentry != 0)
{
// Sync the entry to disc
ext2Sync(vd, NULL);
ni = ext2OpenEntry(vd, target ? target : path);
}
cleanup:
if(dir_ni)
ext2CloseEntry(vd, dir_ni);
if(name)
mem_free(name);
if(dir)
mem_free(dir);
if(targetdir)
mem_free(targetdir);
// Unlock
ext2Unlock(vd);
return ni;
}
/*
* Given a mode, return the ext2 file type
*/
static int ext2_file_type(unsigned int mode)
{
if (LINUX_S_ISREG(mode))
return EXT2_FT_REG_FILE;
if (LINUX_S_ISDIR(mode))
return EXT2_FT_DIR;
if (LINUX_S_ISCHR(mode))
return EXT2_FT_CHRDEV;
if (LINUX_S_ISBLK(mode))
return EXT2_FT_BLKDEV;
if (LINUX_S_ISLNK(mode))
return EXT2_FT_SYMLINK;
if (LINUX_S_ISFIFO(mode))
return EXT2_FT_FIFO;
if (LINUX_S_ISSOCK(mode))
return EXT2_FT_SOCK;
return 0;
}
int ext2Link(ext2_vd *vd, const char *old_path, const char *new_path)
{
ext2_inode_t *dir_ni = NULL, *ni = NULL;
char *dir = NULL;
char *name = NULL;
errcode_t err = 0;
// Sanity check
if (!vd || !vd->fs) {
errno = ENODEV;
return -1;
}
if(!(vd->fs->flags & EXT2_FLAG_RW))
return -1;
// You cannot link between devices
if(vd != ext2GetVolume(new_path)) {
errno = EXDEV;
return -1;
}
// Get the actual paths of the entry
old_path = ext2RealPath(old_path);
new_path = ext2RealPath(new_path);
if (!old_path || !new_path) {
errno = EINVAL;
return -1;
}
// Lock
ext2Lock(vd);
//check for existing in new path
ni = ext2OpenEntry(vd, new_path);
if (ni) {
ext2CloseEntry(vd, ni);
ni = NULL;
errno = EINVAL;
return -1;
}
dir = strdup(new_path);
if (!dir) {
errno = EINVAL;
err = -1;
goto cleanup;
}
char * ptr = strrchr(dir, '/');
if (ptr)
{
name = strdup(ptr+1);
*ptr = 0;
}
else
name = strdup(dir);
// Find the entry
ni = ext2OpenEntry(vd, old_path);
if (!ni) {
errno = ENOENT;
err = -1;
goto cleanup;
}
// Open the entries new parent directory
dir_ni = ext2OpenEntry(vd, dir);
if (!dir_ni) {
errno = ENOENT;
err = -1;
goto cleanup;
}
do
{
// Link the entry to its new parent
err = ext2fs_link(vd->fs, dir_ni->ino, name, ni->ino, ext2_file_type(ni->ni.i_mode));
if (err == EXT2_ET_DIR_NO_SPACE)
{
if (ext2fs_expand_dir(vd->fs, dir_ni->ino) != 0)
goto cleanup;
}
else if(err != 0)
{
errno = ENOMEM;
goto cleanup;
}
}
while(err == EXT2_ET_DIR_NO_SPACE);
ni->ni.i_links_count++;
// Update entry times
ext2UpdateTimes(vd, ni, EXT2_UPDATE_MCTIME);
// Sync the entry to disc
ext2Sync(vd, ni);
cleanup:
if(dir_ni)
ext2CloseEntry(vd, dir_ni);
if(ni)
ext2CloseEntry(vd, ni);
if(dir)
mem_free(dir);
if(name)
mem_free(name);
// Unlock
ext2Unlock(vd);
return err;
}
typedef struct _rd_struct
{
ext2_ino_t parent;
int empty;
} rd_struct;
static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr, int blockcnt EXT2FS_ATTR((unused)), void *private EXT2FS_ATTR((unused)))
{
blk_t block;
block = *blocknr;
ext2fs_block_alloc_stats(fs, block, -1);
*blocknr = 0;
return 0;
}
static int unlink_proc(ext2_ino_t dir EXT2FS_ATTR((unused)), int entry EXT2FS_ATTR((unused)),
struct ext2_dir_entry *dirent, int offset EXT2FS_ATTR((unused)),
int blocksize EXT2FS_ATTR((unused)), char *buf EXT2FS_ATTR((unused)),
void *private_data)
{
rd_struct *rds = (rd_struct *) private_data;
if (dirent->inode == 0)
return 0;
if (((dirent->name_len & 0xFF) == 1) && (dirent->name[0] == '.'))
return 0;
if (((dirent->name_len & 0xFF) == 2) && (dirent->name[0] == '.') &&
(dirent->name[1] == '.')) {
rds->parent = dirent->inode;
return 0;
}
rds->empty = 0;
return 0;
}
int ext2Unlink (ext2_vd *vd, const char *path)
{
ext2_inode_t *dir_ni = NULL, *ni = NULL;
char *dir = NULL;
char *name = NULL;
errcode_t err = -1;
// Sanity check
if (!vd || !vd->fs) {
errno = ENODEV;
return -1;
}
if(!(vd->fs->flags & EXT2_FLAG_RW))
return -1;
// Get the actual path of the entry
path = ext2RealPath(path);
if (!path) {
errno = EINVAL;
return -1;
}
// Lock
ext2Lock(vd);
dir = strdup(path);
if (!dir) {
errno = EINVAL;
goto cleanup;
}
char * ptr = strrchr(dir, '/');
if (ptr)
{
name = strdup(ptr+1);
*ptr = 0;
}
else
name = dir;
// Find the entry
ni = ext2OpenEntry(vd, path);
if (!ni) {
errno = ENOENT;
goto cleanup;
}
// Open the entries parent directory
dir_ni = ext2OpenEntry(vd, dir);
if (!dir_ni) {
errno = ENOENT;
goto cleanup;
}
// Directory
if(LINUX_S_ISDIR(ni->ni.i_mode))
{
rd_struct rds;
rds.parent = 0;
rds.empty = 1;
if (ext2fs_dir_iterate2(vd->fs, ni->ino, 0, 0, unlink_proc, &rds) != 0)
goto cleanup;
if(!rds.empty)
goto cleanup;
if (rds.parent)
{
struct ext2_inode inode;
if (ext2fs_read_inode(vd->fs, rds.parent, &inode) == 0)
{
if(inode.i_links_count > 1)
inode.i_links_count--;
ext2fs_write_inode(vd->fs, rds.parent, &inode);
}
}
// set link count 0
ni->ni.i_links_count = 0;
}
// File
else
{
ni->ni.i_links_count--;
}
if(ni->ni.i_links_count <= 0)
{
ni->ni.i_size = 0;
ni->ni.i_size_high = 0;
ni->ni.i_links_count = 0;
ni->ni.i_dtime = (u32) time(0);
}
ext2fs_write_inode(vd->fs, ni->ino, &ni->ni);
// Unlink the entry from its parent
if(ext2fs_unlink(vd->fs, dir_ni->ino, name, 0, 0) != 0)
goto cleanup;
if (ext2fs_inode_has_valid_blocks(&ni->ni))
{
ext2fs_block_iterate(vd->fs, ni->ino, 0, NULL, release_blocks_proc, NULL);
ext2fs_inode_alloc_stats2(vd->fs, ni->ino, -1, LINUX_S_ISDIR(ni->ni.i_mode));
}
if(ni->ni.i_links_count == 0)
{
// It's odd that i have to do this on my own and the lib is not doing that for me
blk64_t truncate_block = ((vd->fs->blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(vd->fs->super)) + 1;
ext2fs_punch(vd->fs, ni->ino, &ni->ni, 0, truncate_block, ~0ULL);
}
// Sync the entry to disc
ext2Sync(vd, NULL);
err = 0;
cleanup:
if(dir_ni)
ext2CloseEntry(vd, dir_ni);
if(ni)
ext2CloseEntry(vd, ni);
if(name)
mem_free(name);
if(dir)
mem_free(dir);
// Unlock
ext2Unlock(vd);
return err;
}
int ext2Sync(ext2_vd *vd, ext2_inode_t *ni)
{
errcode_t res = 0;
// Sanity check
if (!vd || !vd->fs) {
errno = ENODEV;
return -1;
}
if(!(vd->fs->flags & EXT2_FLAG_RW))
return -1;
// Lock
ext2Lock(vd);
if(ni && ni->dirty)
{
ext2fs_write_inode(vd->fs, ni->ino, &ni->ni);
ni->dirty = false;
}
// Sync the entry
res = ext2fs_flush(vd->fs);
// Force the underlying device to sync
vd->io->manager->flush(vd->io);
// Unlock
ext2Unlock(vd);
return res;
}
int ext2Stat (ext2_vd *vd, ext2_inode_t *ni_main, struct stat *st)
{
int res = 0;
// Sanity check
if (!vd) {
errno = ENODEV;
return -1;
}
struct ext2_inode * ni = ni_main ? &ni_main->ni : 0;
// Sanity check
if (!ni) {
errno = ENOENT;
return -1;
}
// Short circuit cases were we don't actually have to do anything
if (!st)
return 0;
// Lock
ext2Lock(vd);
// Zero out the stat buffer
memset(st, 0, sizeof(struct stat));
if(LINUX_S_ISDIR(ni->i_mode))
{
st->st_nlink = 1;
st->st_size = ni->i_size;
}
else
{
st->st_nlink = ni->i_links_count;
st->st_size = EXT2_I_SIZE(ni);
}
st->st_mode = ni->i_mode;
st->st_blocks = ni->i_blocks;
st->st_blksize = vd->fs->blocksize;
// Fill in the generic entry stats
st->st_dev = (dev_t) ((long) vd->fs);
st->st_uid = ni->i_uid | (((u32) ni->osd2.linux2.l_i_uid_high) << 16);
st->st_gid = ni->i_gid | (((u32) ni->osd2.linux2.l_i_gid_high) << 16);
st->st_ino = ni_main->ino;
st->st_atime = ni->i_atime;
st->st_ctime = ni->i_ctime;
st->st_mtime = ni->i_mtime;
// Update entry times
ext2UpdateTimes(vd, ni_main, EXT2_UPDATE_ATIME);
// Unlock
ext2Unlock(vd);
return res;
}
void ext2UpdateTimes(ext2_vd *vd, ext2_inode_t *ni, ext2_time_update_flags mask)
{
// Sanity check
if(!ni || !mask)
return;
if(!(vd->fs->flags & EXT2_FLAG_RW))
return;
u32 now = (u32) time(0);
if(mask & EXT2_UPDATE_ATIME)
ni->ni.i_atime = now;
if(mask & EXT2_UPDATE_MTIME)
ni->ni.i_mtime = now;
if(mask & EXT2_UPDATE_CTIME)
ni->ni.i_ctime = now;
ni->dirty = true;
}
const char *ext2RealPath (const char *path)
{
// Sanity check
if (!path)
return NULL;
// Move the path pointer to the start of the actual path
if (strchr(path, ':') != NULL) {
path = strchr(path, ':')+1;
}
if (strchr(path, ':') != NULL) {
return NULL;
}
return path;
}