mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-16 00:15:08 +01:00
0f17471b27
*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".
1077 lines
24 KiB
C
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;
|
|
}
|