| /* |
| * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. |
| * |
| * Copyright (C) 2002-2011 Aleph One Ltd. |
| * for Toby Churchill Ltd and Brightstar Engineering |
| * |
| * Created by Charles Manning <charles@aleph1.co.uk> |
| * |
| * 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. |
| */ |
| |
| #include <div64.h> |
| #include "yaffsfs.h" |
| #include "yaffs_guts.h" |
| #include "yaffscfg.h" |
| #include "yportenv.h" |
| #include "yaffs_trace.h" |
| |
| #define YAFFSFS_MAX_SYMLINK_DEREFERENCES 5 |
| |
| #ifndef NULL |
| #define NULL ((void *)0) |
| #endif |
| |
| /* YAFFSFS_RW_SIZE must be a power of 2 */ |
| #define YAFFSFS_RW_SHIFT (13) |
| #define YAFFSFS_RW_SIZE (1<<YAFFSFS_RW_SHIFT) |
| |
| /* Some forward references */ |
| static struct yaffs_obj *yaffsfs_FindObject(struct yaffs_obj *relativeDirectory, |
| const YCHAR *path, |
| int symDepth, int getEquiv, |
| struct yaffs_obj **dirOut, |
| int *notDir, int *loop); |
| |
| static void yaffsfs_RemoveObjectCallback(struct yaffs_obj *obj); |
| |
| unsigned int yaffs_wr_attempts; |
| |
| /* |
| * Handle management. |
| * There are open inodes in struct yaffsfs_Inode. |
| * There are open file descriptors in yaffsfs_FileDes. |
| * There are open handles in yaffsfs_FileDes. |
| * |
| * Things are structured this way to be like the Linux VFS model |
| * so that interactions with the yaffs guts calls are similar. |
| * That means more common code paths and less special code. |
| * That means better testing etc. |
| * |
| * We have 3 layers because: |
| * A handle is different than an fd because you can use dup() |
| * to create a new handle that accesses the *same* fd. The two |
| * handles will use the same offset (part of the fd). We only close |
| * down the fd when there are no more handles accessing it. |
| * |
| * More than one fd can currently access one file, but each fd |
| * has its own permsiions and offset. |
| */ |
| |
| struct yaffsfs_Inode { |
| int count; /* Number of handles accessing this inode */ |
| struct yaffs_obj *iObj; |
| }; |
| |
| struct yaffsfs_FileDes { |
| u8 reading:1; |
| u8 writing:1; |
| u8 append:1; |
| u8 shareRead:1; |
| u8 shareWrite:1; |
| int inodeId:12; /* Index to corresponding yaffsfs_Inode */ |
| int handleCount:10; /* Number of handles for this fd */ |
| loff_t position; /* current position in file */ |
| }; |
| |
| struct yaffsfs_Handle { |
| short int fdId; |
| short int useCount; |
| }; |
| |
| |
| struct yaffsfs_DirSearchContxt { |
| struct yaffs_dirent de; /* directory entry */ |
| YCHAR name[NAME_MAX + 1]; /* name of directory being searched */ |
| struct yaffs_obj *dirObj; /* ptr to directory being searched */ |
| struct yaffs_obj *nextReturn; /* obj returned by next readddir */ |
| struct list_head others; |
| int offset:20; |
| unsigned inUse:1; |
| }; |
| |
| static struct yaffsfs_DirSearchContxt yaffsfs_dsc[YAFFSFS_N_DSC]; |
| static struct yaffsfs_Inode yaffsfs_inode[YAFFSFS_N_HANDLES]; |
| static struct yaffsfs_FileDes yaffsfs_fd[YAFFSFS_N_HANDLES]; |
| static struct yaffsfs_Handle yaffsfs_handle[YAFFSFS_N_HANDLES]; |
| |
| static int yaffsfs_handlesInitialised; |
| |
| unsigned yaffs_set_trace(unsigned tm) |
| { |
| yaffs_trace_mask = tm; |
| return yaffs_trace_mask; |
| } |
| |
| unsigned yaffs_get_trace(void) |
| { |
| return yaffs_trace_mask; |
| } |
| |
| /* |
| * yaffsfs_InitHandle |
| * Inilitalise handle management on start-up. |
| */ |
| |
| static void yaffsfs_InitHandles(void) |
| { |
| int i; |
| if (yaffsfs_handlesInitialised) |
| return; |
| |
| memset(yaffsfs_inode, 0, sizeof(yaffsfs_inode)); |
| memset(yaffsfs_fd, 0, sizeof(yaffsfs_fd)); |
| memset(yaffsfs_handle, 0, sizeof(yaffsfs_handle)); |
| memset(yaffsfs_dsc, 0, sizeof(yaffsfs_dsc)); |
| |
| for (i = 0; i < YAFFSFS_N_HANDLES; i++) |
| yaffsfs_fd[i].inodeId = -1; |
| for (i = 0; i < YAFFSFS_N_HANDLES; i++) |
| yaffsfs_handle[i].fdId = -1; |
| } |
| |
| static struct yaffsfs_Handle *yaffsfs_HandleToPointer(int h) |
| { |
| if (h >= 0 && h < YAFFSFS_N_HANDLES) |
| return &yaffsfs_handle[h]; |
| return NULL; |
| } |
| |
| static struct yaffsfs_FileDes *yaffsfs_HandleToFileDes(int handle) |
| { |
| struct yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle); |
| |
| if (h && h->useCount > 0 && h->fdId >= 0 && h->fdId < YAFFSFS_N_HANDLES) |
| return &yaffsfs_fd[h->fdId]; |
| |
| return NULL; |
| } |
| |
| static struct yaffsfs_Inode *yaffsfs_HandleToInode(int handle) |
| { |
| struct yaffsfs_FileDes *fd = yaffsfs_HandleToFileDes(handle); |
| |
| if (fd && fd->handleCount > 0 && |
| fd->inodeId >= 0 && fd->inodeId < YAFFSFS_N_HANDLES) |
| return &yaffsfs_inode[fd->inodeId]; |
| |
| return NULL; |
| } |
| |
| static struct yaffs_obj *yaffsfs_HandleToObject(int handle) |
| { |
| struct yaffsfs_Inode *in = yaffsfs_HandleToInode(handle); |
| |
| if (in) |
| return in->iObj; |
| |
| return NULL; |
| } |
| |
| /* |
| * yaffsfs_FindInodeIdForObject |
| * Find the inode entry for an object, if it exists. |
| */ |
| |
| static int yaffsfs_FindInodeIdForObject(struct yaffs_obj *obj) |
| { |
| int i; |
| int ret = -1; |
| |
| if (obj) |
| obj = yaffs_get_equivalent_obj(obj); |
| |
| /* Look for it in open inode table */ |
| for (i = 0; i < YAFFSFS_N_HANDLES && ret < 0; i++) { |
| if (yaffsfs_inode[i].iObj == obj) |
| ret = i; |
| } |
| return ret; |
| } |
| |
| /* |
| * yaffsfs_GetInodeIdForObject |
| * Grab an inode entry when opening a new inode. |
| */ |
| static int yaffsfs_GetInodeIdForObject(struct yaffs_obj *obj) |
| { |
| int i; |
| int ret; |
| struct yaffsfs_Inode *in = NULL; |
| |
| if (obj) |
| obj = yaffs_get_equivalent_obj(obj); |
| |
| ret = yaffsfs_FindInodeIdForObject(obj); |
| |
| for (i = 0; i < YAFFSFS_N_HANDLES && ret < 0; i++) { |
| if (!yaffsfs_inode[i].iObj) |
| ret = i; |
| } |
| |
| if (ret >= 0) { |
| in = &yaffsfs_inode[ret]; |
| if (!in->iObj) |
| in->count = 0; |
| in->iObj = obj; |
| in->count++; |
| } |
| |
| return ret; |
| } |
| |
| static int yaffsfs_CountHandles(struct yaffs_obj *obj) |
| { |
| int i = yaffsfs_FindInodeIdForObject(obj); |
| |
| if (i >= 0) |
| return yaffsfs_inode[i].count; |
| else |
| return 0; |
| } |
| |
| static void yaffsfs_ReleaseInode(struct yaffsfs_Inode *in) |
| { |
| struct yaffs_obj *obj; |
| |
| obj = in->iObj; |
| |
| if (obj->unlinked) |
| yaffs_del_obj(obj); |
| |
| obj->my_inode = NULL; |
| in->iObj = NULL; |
| |
| } |
| |
| static void yaffsfs_PutInode(int inodeId) |
| { |
| if (inodeId >= 0 && inodeId < YAFFSFS_N_HANDLES) { |
| struct yaffsfs_Inode *in = &yaffsfs_inode[inodeId]; |
| in->count--; |
| if (in->count <= 0) { |
| yaffsfs_ReleaseInode(in); |
| in->count = 0; |
| } |
| } |
| } |
| |
| static int yaffsfs_NewHandle(struct yaffsfs_Handle **hptr) |
| { |
| int i; |
| struct yaffsfs_Handle *h; |
| |
| for (i = 0; i < YAFFSFS_N_HANDLES; i++) { |
| h = &yaffsfs_handle[i]; |
| if (h->useCount < 1) { |
| memset(h, 0, sizeof(struct yaffsfs_Handle)); |
| h->fdId = -1; |
| h->useCount = 1; |
| if (hptr) |
| *hptr = h; |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| static int yaffsfs_NewHandleAndFileDes(void) |
| { |
| int i; |
| struct yaffsfs_FileDes *fd; |
| struct yaffsfs_Handle *h = NULL; |
| int handle = yaffsfs_NewHandle(&h); |
| |
| if (handle < 0) |
| return -1; |
| |
| for (i = 0; i < YAFFSFS_N_HANDLES; i++) { |
| fd = &yaffsfs_fd[i]; |
| if (fd->handleCount < 1) { |
| memset(fd, 0, sizeof(struct yaffsfs_FileDes)); |
| fd->inodeId = -1; |
| fd->handleCount = 1; |
| h->fdId = i; |
| return handle; |
| } |
| } |
| |
| /* Dump the handle because we could not get a fd */ |
| h->useCount = 0; |
| return -1; |
| } |
| |
| /* |
| * yaffs_get_handle |
| * Increase use of handle when reading/writing a file |
| * Also gets the file descriptor. |
| */ |
| |
| static int yaffsfs_GetHandle(int handle) |
| { |
| struct yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle); |
| |
| if (h && h->useCount > 0) { |
| h->useCount++; |
| return 0; |
| } |
| return -1; |
| } |
| |
| /* |
| * yaffs_put_handle |
| * Let go of a handle when closing a file or aborting an open or |
| * ending a read or write. |
| */ |
| |
| static int yaffsfs_PutFileDes(int fdId) |
| { |
| struct yaffsfs_FileDes *fd; |
| |
| if (fdId >= 0 && fdId < YAFFSFS_N_HANDLES) { |
| fd = &yaffsfs_fd[fdId]; |
| fd->handleCount--; |
| if (fd->handleCount < 1) { |
| if (fd->inodeId >= 0) { |
| yaffsfs_PutInode(fd->inodeId); |
| fd->inodeId = -1; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| static int yaffsfs_PutHandle(int handle) |
| { |
| struct yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle); |
| |
| if (h && h->useCount > 0) { |
| h->useCount--; |
| if (h->useCount < 1) { |
| yaffsfs_PutFileDes(h->fdId); |
| h->fdId = -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static void yaffsfs_BreakDeviceHandles(struct yaffs_dev *dev) |
| { |
| struct yaffsfs_FileDes *fd; |
| struct yaffsfs_Handle *h; |
| struct yaffs_obj *obj; |
| int i; |
| for (i = 0; i < YAFFSFS_N_HANDLES; i++) { |
| h = yaffsfs_HandleToPointer(i); |
| fd = yaffsfs_HandleToFileDes(i); |
| obj = yaffsfs_HandleToObject(i); |
| if (h && h->useCount > 0) { |
| h->useCount = 0; |
| h->fdId = 0; |
| } |
| if (fd && fd->handleCount > 0 && obj && obj->my_dev == dev) { |
| fd->handleCount = 0; |
| yaffsfs_PutInode(fd->inodeId); |
| fd->inodeId = -1; |
| } |
| } |
| } |
| |
| /* |
| * Stuff to handle names. |
| */ |
| #ifdef CONFIG_YAFFS_CASE_INSENSITIVE |
| |
| static int yaffs_toupper(YCHAR a) |
| { |
| if (a >= 'a' && a <= 'z') |
| return (a - 'a') + 'A'; |
| else |
| return a; |
| } |
| |
| int yaffsfs_Match(YCHAR a, YCHAR b) |
| { |
| return (yaffs_toupper(a) == yaffs_toupper(b)); |
| } |
| #else |
| int yaffsfs_Match(YCHAR a, YCHAR b) |
| { |
| /* case sensitive */ |
| return (a == b); |
| } |
| #endif |
| |
| int yaffsfs_IsPathDivider(YCHAR ch) |
| { |
| const YCHAR *str = YAFFS_PATH_DIVIDERS; |
| |
| while (*str) { |
| if (*str == ch) |
| return 1; |
| str++; |
| } |
| |
| return 0; |
| } |
| |
| int yaffsfs_CheckNameLength(const char *name) |
| { |
| int retVal = 0; |
| |
| int nameLength = yaffs_strnlen(name, YAFFS_MAX_NAME_LENGTH + 1); |
| |
| if (nameLength == 0) { |
| yaffsfs_SetError(-ENOENT); |
| retVal = -1; |
| } else if (nameLength > YAFFS_MAX_NAME_LENGTH) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| retVal = -1; |
| } |
| |
| return retVal; |
| } |
| |
| static int yaffsfs_alt_dir_path(const YCHAR *path, YCHAR **ret_path) |
| { |
| YCHAR *alt_path = NULL; |
| int path_length; |
| int i; |
| |
| /* |
| * We don't have a definition for max path length. |
| * We will use 3 * max name length instead. |
| */ |
| *ret_path = NULL; |
| path_length = yaffs_strnlen(path, (YAFFS_MAX_NAME_LENGTH + 1) * 3 + 1); |
| |
| /* If the last character is a path divider, then we need to |
| * trim it back so that the name look-up works properly. |
| * eg. /foo/new_dir/ -> /foo/newdir |
| * Curveball: Need to handle multiple path dividers: |
| * eg. /foof/sdfse///// -> /foo/sdfse |
| */ |
| if (path_length > 0 && yaffsfs_IsPathDivider(path[path_length - 1])) { |
| alt_path = kmalloc(path_length + 1, 0); |
| if (!alt_path) |
| return -1; |
| yaffs_strcpy(alt_path, path); |
| for (i = path_length - 1; |
| i >= 0 && yaffsfs_IsPathDivider(alt_path[i]); i--) |
| alt_path[i] = (YCHAR) 0; |
| } |
| *ret_path = alt_path; |
| return 0; |
| } |
| |
| LIST_HEAD(yaffsfs_deviceList); |
| |
| /* |
| * yaffsfs_FindDevice |
| * yaffsfs_FindRoot |
| * Scan the configuration list to find the device |
| * Curveballs: Should match paths that end in '/' too |
| * Curveball2 Might have "/x/ and "/x/y". Need to return the longest match |
| */ |
| static struct yaffs_dev *yaffsfs_FindDevice(const YCHAR *path, |
| YCHAR **restOfPath) |
| { |
| struct list_head *cfg; |
| const YCHAR *leftOver; |
| const YCHAR *p; |
| struct yaffs_dev *retval = NULL; |
| struct yaffs_dev *dev = NULL; |
| int thisMatchLength; |
| int longestMatch = -1; |
| int matching; |
| |
| /* |
| * Check all configs, choose the one that: |
| * 1) Actually matches a prefix (ie /a amd /abc will not match |
| * 2) Matches the longest. |
| */ |
| list_for_each(cfg, &yaffsfs_deviceList) { |
| dev = list_entry(cfg, struct yaffs_dev, dev_list); |
| leftOver = path; |
| p = dev->param.name; |
| thisMatchLength = 0; |
| matching = 1; |
| |
| while (matching && *p && *leftOver) { |
| /* Skip over any /s */ |
| while (yaffsfs_IsPathDivider(*p)) |
| p++; |
| |
| /* Skip over any /s */ |
| while (yaffsfs_IsPathDivider(*leftOver)) |
| leftOver++; |
| |
| /* Now match the text part */ |
| while (matching && |
| *p && !yaffsfs_IsPathDivider(*p) && |
| *leftOver && !yaffsfs_IsPathDivider(*leftOver)) { |
| if (yaffsfs_Match(*p, *leftOver)) { |
| p++; |
| leftOver++; |
| thisMatchLength++; |
| } else { |
| matching = 0; |
| } |
| } |
| } |
| |
| /* Skip over any /s in leftOver */ |
| while (yaffsfs_IsPathDivider(*leftOver)) |
| leftOver++; |
| |
| /*Skip over any /s in p */ |
| while (yaffsfs_IsPathDivider(*p)) |
| p++; |
| |
| /* p should now be at the end of the string if fully matched */ |
| if (*p) |
| matching = 0; |
| |
| if (matching && (thisMatchLength > longestMatch)) { |
| /* Matched prefix */ |
| *restOfPath = (YCHAR *) leftOver; |
| retval = dev; |
| longestMatch = thisMatchLength; |
| } |
| |
| } |
| return retval; |
| } |
| |
| static int yaffsfs_CheckPath(const YCHAR *path) |
| { |
| int n = 0; |
| int divs = 0; |
| |
| while (*path && n < YAFFS_MAX_NAME_LENGTH && divs < 100) { |
| if (yaffsfs_IsPathDivider(*path)) { |
| n = 0; |
| divs++; |
| } else |
| n++; |
| path++; |
| } |
| |
| return (*path) ? -1 : 0; |
| } |
| |
| /* FindMountPoint only returns a dev entry if the path is a mount point */ |
| static struct yaffs_dev *yaffsfs_FindMountPoint(const YCHAR *path) |
| { |
| struct yaffs_dev *dev; |
| YCHAR *restOfPath = NULL; |
| |
| dev = yaffsfs_FindDevice(path, &restOfPath); |
| if (dev && restOfPath && *restOfPath) |
| dev = NULL; |
| return dev; |
| } |
| |
| static struct yaffs_obj *yaffsfs_FindRoot(const YCHAR *path, |
| YCHAR **restOfPath) |
| { |
| struct yaffs_dev *dev; |
| |
| dev = yaffsfs_FindDevice(path, restOfPath); |
| if (dev && dev->is_mounted) |
| return dev->root_dir; |
| |
| return NULL; |
| } |
| |
| static struct yaffs_obj *yaffsfs_FollowLink(struct yaffs_obj *obj, |
| int symDepth, int *loop) |
| { |
| |
| if (obj) |
| obj = yaffs_get_equivalent_obj(obj); |
| |
| while (obj && obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { |
| YCHAR *alias = obj->variant.symlink_variant.alias; |
| |
| if (yaffsfs_IsPathDivider(*alias)) |
| /* Starts with a /, need to scan from root up */ |
| obj = yaffsfs_FindObject(NULL, alias, symDepth++, |
| 1, NULL, NULL, loop); |
| else |
| /* |
| * Relative to here so use the parent of the |
| * symlink as a start |
| */ |
| obj = yaffsfs_FindObject(obj->parent, alias, symDepth++, |
| 1, NULL, NULL, loop); |
| } |
| return obj; |
| } |
| |
| /* |
| * yaffsfs_FindDirectory |
| * Parse a path to determine the directory and the name within the directory. |
| * |
| * eg. "/data/xx/ff" --> puts name="ff" and returns the directory "/data/xx" |
| */ |
| static struct yaffs_obj *yaffsfs_DoFindDirectory(struct yaffs_obj *startDir, |
| const YCHAR *path, |
| YCHAR **name, int symDepth, |
| int *notDir, int *loop) |
| { |
| struct yaffs_obj *dir; |
| YCHAR *restOfPath; |
| YCHAR str[YAFFS_MAX_NAME_LENGTH + 1]; |
| int i; |
| |
| if (symDepth > YAFFSFS_MAX_SYMLINK_DEREFERENCES) { |
| if (loop) |
| *loop = 1; |
| return NULL; |
| } |
| |
| if (startDir) { |
| dir = startDir; |
| restOfPath = (YCHAR *) path; |
| } else |
| dir = yaffsfs_FindRoot(path, &restOfPath); |
| |
| while (dir) { |
| /* |
| * parse off /. |
| * curve ball: also throw away surplus '/' |
| * eg. "/ram/x////ff" gets treated the same as "/ram/x/ff" |
| */ |
| while (yaffsfs_IsPathDivider(*restOfPath)) |
| restOfPath++; /* get rid of '/' */ |
| |
| *name = restOfPath; |
| i = 0; |
| |
| while (*restOfPath && !yaffsfs_IsPathDivider(*restOfPath)) { |
| if (i < YAFFS_MAX_NAME_LENGTH) { |
| str[i] = *restOfPath; |
| str[i + 1] = '\0'; |
| i++; |
| } |
| restOfPath++; |
| } |
| |
| if (!*restOfPath) |
| /* got to the end of the string */ |
| return dir; |
| else { |
| if (yaffs_strcmp(str, _Y(".")) == 0) { |
| /* Do nothing */ |
| } else if (yaffs_strcmp(str, _Y("..")) == 0) { |
| dir = dir->parent; |
| } else { |
| dir = yaffs_find_by_name(dir, str); |
| |
| dir = yaffsfs_FollowLink(dir, symDepth, loop); |
| |
| if (dir && dir->variant_type != |
| YAFFS_OBJECT_TYPE_DIRECTORY) { |
| if (notDir) |
| *notDir = 1; |
| dir = NULL; |
| } |
| |
| } |
| } |
| } |
| /* directory did not exist. */ |
| return NULL; |
| } |
| |
| static struct yaffs_obj *yaffsfs_FindDirectory(struct yaffs_obj *relDir, |
| const YCHAR *path, |
| YCHAR **name, |
| int symDepth, |
| int *notDir, int *loop) |
| { |
| return yaffsfs_DoFindDirectory(relDir, path, name, symDepth, notDir, |
| loop); |
| } |
| |
| /* |
| * yaffsfs_FindObject turns a path for an existing object into the object |
| */ |
| static struct yaffs_obj *yaffsfs_FindObject(struct yaffs_obj *relDir, |
| const YCHAR *path, int symDepth, |
| int getEquiv, |
| struct yaffs_obj **dirOut, |
| int *notDir, int *loop) |
| { |
| struct yaffs_obj *dir; |
| struct yaffs_obj *obj; |
| YCHAR *name; |
| |
| dir = |
| yaffsfs_FindDirectory(relDir, path, &name, symDepth, notDir, loop); |
| |
| if (dirOut) |
| *dirOut = dir; |
| |
| if (dir && *name) |
| obj = yaffs_find_by_name(dir, name); |
| else |
| obj = dir; |
| |
| if (getEquiv) |
| obj = yaffs_get_equivalent_obj(obj); |
| |
| return obj; |
| } |
| |
| /************************************************************************* |
| * Start of yaffsfs visible functions. |
| *************************************************************************/ |
| |
| int yaffs_dup(int handle) |
| { |
| int newHandleNumber = -1; |
| struct yaffsfs_FileDes *existingFD = NULL; |
| struct yaffsfs_Handle *existingHandle = NULL; |
| struct yaffsfs_Handle *newHandle = NULL; |
| |
| yaffsfs_Lock(); |
| existingHandle = yaffsfs_HandleToPointer(handle); |
| existingFD = yaffsfs_HandleToFileDes(handle); |
| if (existingFD) |
| newHandleNumber = yaffsfs_NewHandle(&newHandle); |
| if (newHandle) { |
| newHandle->fdId = existingHandle->fdId; |
| existingFD->handleCount++; |
| } |
| |
| yaffsfs_Unlock(); |
| |
| if (!existingFD) |
| yaffsfs_SetError(-EBADF); |
| else if (!newHandle) |
| yaffsfs_SetError(-ENOMEM); |
| |
| return newHandleNumber; |
| |
| } |
| |
| static int yaffsfs_TooManyObjects(struct yaffs_dev *dev) |
| { |
| int current_objects = dev->n_obj - dev->n_deleted_files; |
| |
| if (dev->param.max_objects && current_objects > dev->param.max_objects) |
| return 1; |
| else |
| return 0; |
| } |
| |
| int yaffs_open_sharing(const YCHAR *path, int oflag, int mode, int sharing) |
| { |
| struct yaffs_obj *obj = NULL; |
| struct yaffs_obj *dir = NULL; |
| YCHAR *name; |
| int handle = -1; |
| struct yaffsfs_FileDes *fd = NULL; |
| int openDenied = 0; |
| int symDepth = 0; |
| int errorReported = 0; |
| int rwflags = oflag & (O_RDWR | O_RDONLY | O_WRONLY); |
| u8 shareRead = (sharing & YAFFS_SHARE_READ) ? 1 : 0; |
| u8 shareWrite = (sharing & YAFFS_SHARE_WRITE) ? 1 : 0; |
| u8 sharedReadAllowed; |
| u8 sharedWriteAllowed; |
| u8 alreadyReading; |
| u8 alreadyWriting; |
| u8 readRequested; |
| u8 writeRequested; |
| int notDir = 0; |
| int loop = 0; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| /* O_EXCL only has meaning if O_CREAT is specified */ |
| if (!(oflag & O_CREAT)) |
| oflag &= ~(O_EXCL); |
| |
| /* O_TRUNC has no meaning if (O_CREAT | O_EXCL) is specified */ |
| if ((oflag & O_CREAT) & (oflag & O_EXCL)) |
| oflag &= ~(O_TRUNC); |
| |
| /* Todo: Are there any more flag combos to sanitise ? */ |
| |
| /* Figure out if reading or writing is requested */ |
| |
| readRequested = (rwflags == O_RDWR || rwflags == O_RDONLY) ? 1 : 0; |
| writeRequested = (rwflags == O_RDWR || rwflags == O_WRONLY) ? 1 : 0; |
| |
| yaffsfs_Lock(); |
| |
| handle = yaffsfs_NewHandleAndFileDes(); |
| |
| if (handle < 0) { |
| yaffsfs_SetError(-ENFILE); |
| errorReported = 1; |
| } else { |
| |
| fd = yaffsfs_HandleToFileDes(handle); |
| |
| /* try to find the exisiting object */ |
| obj = yaffsfs_FindObject(NULL, path, 0, 1, NULL, NULL, NULL); |
| |
| obj = yaffsfs_FollowLink(obj, symDepth++, &loop); |
| |
| if (obj && |
| obj->variant_type != YAFFS_OBJECT_TYPE_FILE && |
| obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) |
| obj = NULL; |
| |
| if (obj) { |
| |
| /* The file already exists or it might be a directory */ |
| |
| /* A directory can't be opened as a file */ |
| if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) { |
| openDenied = 1; |
| yaffsfs_SetError(-EISDIR); |
| errorReported = 1; |
| } |
| |
| /* Open should fail if O_CREAT and O_EXCL are specified |
| * for a file that exists. |
| */ |
| if (!errorReported && |
| (oflag & O_EXCL) && (oflag & O_CREAT)) { |
| openDenied = 1; |
| yaffsfs_SetError(-EEXIST); |
| errorReported = 1; |
| } |
| |
| /* Check file permissions */ |
| if (readRequested && !(obj->yst_mode & S_IREAD)) |
| openDenied = 1; |
| |
| if (writeRequested && !(obj->yst_mode & S_IWRITE)) |
| openDenied = 1; |
| |
| if (!errorReported && writeRequested && |
| obj->my_dev->read_only) { |
| openDenied = 1; |
| yaffsfs_SetError(-EROFS); |
| errorReported = 1; |
| } |
| |
| if (openDenied && !errorReported) { |
| yaffsfs_SetError(-EACCES); |
| errorReported = 1; |
| } |
| |
| /* Check sharing of an existing object. */ |
| if (!openDenied) { |
| struct yaffsfs_FileDes *fdx; |
| int i; |
| |
| sharedReadAllowed = 1; |
| sharedWriteAllowed = 1; |
| alreadyReading = 0; |
| alreadyWriting = 0; |
| for (i = 0; i < YAFFSFS_N_HANDLES; i++) { |
| fdx = &yaffsfs_fd[i]; |
| if (fdx->handleCount > 0 && |
| fdx->inodeId >= 0 && |
| yaffsfs_inode[fdx->inodeId].iObj |
| == obj) { |
| if (!fdx->shareRead) |
| sharedReadAllowed = 0; |
| if (!fdx->shareWrite) |
| sharedWriteAllowed = 0; |
| if (fdx->reading) |
| alreadyReading = 1; |
| if (fdx->writing) |
| alreadyWriting = 1; |
| } |
| } |
| |
| if ((!sharedReadAllowed && readRequested) || |
| (!shareRead && alreadyReading) || |
| (!sharedWriteAllowed && writeRequested) || |
| (!shareWrite && alreadyWriting)) { |
| openDenied = 1; |
| yaffsfs_SetError(-EBUSY); |
| errorReported = 1; |
| } |
| } |
| |
| } |
| |
| /* If we could not open an existing object, then let's see if |
| * the directory exists. If not, error. |
| */ |
| if (!obj && !errorReported) { |
| dir = yaffsfs_FindDirectory(NULL, path, &name, 0, |
| ¬Dir, &loop); |
| if (!dir && notDir) { |
| yaffsfs_SetError(-ENOTDIR); |
| errorReported = 1; |
| } else if (loop) { |
| yaffsfs_SetError(-ELOOP); |
| errorReported = 1; |
| } else if (!dir) { |
| yaffsfs_SetError(-ENOENT); |
| errorReported = 1; |
| } |
| } |
| |
| if (!obj && dir && !errorReported && (oflag & O_CREAT)) { |
| /* Let's see if we can create this file */ |
| if (dir->my_dev->read_only) { |
| yaffsfs_SetError(-EROFS); |
| errorReported = 1; |
| } else if (yaffsfs_TooManyObjects(dir->my_dev)) { |
| yaffsfs_SetError(-ENFILE); |
| errorReported = 1; |
| } else |
| obj = yaffs_create_file(dir, name, mode, 0, 0); |
| |
| if (!obj && !errorReported) { |
| yaffsfs_SetError(-ENOSPC); |
| errorReported = 1; |
| } |
| } |
| |
| if (!obj && dir && !errorReported && !(oflag & O_CREAT)) { |
| yaffsfs_SetError(-ENOENT); |
| errorReported = 1; |
| } |
| |
| if (obj && !openDenied) { |
| int inodeId = yaffsfs_GetInodeIdForObject(obj); |
| |
| if (inodeId < 0) { |
| /* |
| * Todo: Fix any problem if inodes run out, |
| * That can't happen if the number of inode |
| * items >= number of handles. |
| */ |
| } |
| |
| fd->inodeId = inodeId; |
| fd->reading = readRequested; |
| fd->writing = writeRequested; |
| fd->append = (oflag & O_APPEND) ? 1 : 0; |
| fd->position = 0; |
| fd->shareRead = shareRead; |
| fd->shareWrite = shareWrite; |
| |
| /* Hook inode to object */ |
| obj->my_inode = (void *)&yaffsfs_inode[inodeId]; |
| |
| if ((oflag & O_TRUNC) && fd->writing) |
| yaffs_resize_file(obj, 0); |
| } else { |
| yaffsfs_PutHandle(handle); |
| if (!errorReported) |
| yaffsfs_SetError(0); /* Problem */ |
| handle = -1; |
| } |
| } |
| |
| yaffsfs_Unlock(); |
| |
| return handle; |
| } |
| |
| int yaffs_open(const YCHAR *path, int oflag, int mode) |
| { |
| return yaffs_open_sharing(path, oflag, mode, |
| YAFFS_SHARE_READ | YAFFS_SHARE_WRITE); |
| } |
| |
| int yaffs_Dofsync(int handle, int datasync) |
| { |
| int retVal = -1; |
| struct yaffs_obj *obj; |
| |
| yaffsfs_Lock(); |
| |
| obj = yaffsfs_HandleToObject(handle); |
| |
| if (!obj) |
| yaffsfs_SetError(-EBADF); |
| else if (obj->my_dev->read_only) |
| yaffsfs_SetError(-EROFS); |
| else { |
| yaffs_flush_file(obj, 1, datasync); |
| retVal = 0; |
| } |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| } |
| |
| int yaffs_fsync(int handle) |
| { |
| return yaffs_Dofsync(handle, 0); |
| } |
| |
| int yaffs_flush(int handle) |
| { |
| return yaffs_fsync(handle); |
| } |
| |
| int yaffs_fdatasync(int handle) |
| { |
| return yaffs_Dofsync(handle, 1); |
| } |
| |
| int yaffs_close(int handle) |
| { |
| struct yaffsfs_Handle *h = NULL; |
| struct yaffs_obj *obj = NULL; |
| int retVal = -1; |
| |
| yaffsfs_Lock(); |
| |
| h = yaffsfs_HandleToPointer(handle); |
| obj = yaffsfs_HandleToObject(handle); |
| |
| if (!h || !obj) |
| yaffsfs_SetError(-EBADF); |
| else { |
| /* clean up */ |
| yaffs_flush_file(obj, 1, 0); |
| yaffsfs_PutHandle(handle); |
| retVal = 0; |
| } |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| } |
| |
| int yaffsfs_do_read(int handle, void *vbuf, unsigned int nbyte, |
| int isPread, loff_t offset) |
| { |
| struct yaffsfs_FileDes *fd = NULL; |
| struct yaffs_obj *obj = NULL; |
| loff_t pos = 0; |
| loff_t startPos = 0; |
| loff_t endPos = 0; |
| int nRead = 0; |
| int nToRead = 0; |
| int totalRead = 0; |
| loff_t maxRead; |
| u8 *buf = (u8 *) vbuf; |
| |
| if (!vbuf) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| fd = yaffsfs_HandleToFileDes(handle); |
| obj = yaffsfs_HandleToObject(handle); |
| |
| if (!fd || !obj) { |
| /* bad handle */ |
| yaffsfs_SetError(-EBADF); |
| totalRead = -1; |
| } else if (!fd->reading) { |
| /* Not a reading handle */ |
| yaffsfs_SetError(-EINVAL); |
| totalRead = -1; |
| } else if (nbyte > YAFFS_MAX_FILE_SIZE) { |
| yaffsfs_SetError(-EINVAL); |
| totalRead = -1; |
| } else { |
| if (isPread) |
| startPos = offset; |
| else |
| startPos = fd->position; |
| |
| pos = startPos; |
| |
| if (yaffs_get_obj_length(obj) > pos) |
| maxRead = yaffs_get_obj_length(obj) - pos; |
| else |
| maxRead = 0; |
| |
| if (nbyte > maxRead) |
| nbyte = maxRead; |
| |
| yaffsfs_GetHandle(handle); |
| |
| endPos = pos + nbyte; |
| |
| if (pos < 0 || pos > YAFFS_MAX_FILE_SIZE || |
| nbyte > YAFFS_MAX_FILE_SIZE || |
| endPos < 0 || endPos > YAFFS_MAX_FILE_SIZE) { |
| totalRead = -1; |
| nbyte = 0; |
| } |
| |
| while (nbyte > 0) { |
| nToRead = YAFFSFS_RW_SIZE - |
| (pos & (YAFFSFS_RW_SIZE - 1)); |
| if (nToRead > nbyte) |
| nToRead = nbyte; |
| |
| /* Tricky bit... |
| * Need to reverify object in case the device was |
| * unmounted in another thread. |
| */ |
| obj = yaffsfs_HandleToObject(handle); |
| if (!obj) |
| nRead = 0; |
| else |
| nRead = yaffs_file_rd(obj, buf, pos, nToRead); |
| |
| if (nRead > 0) { |
| totalRead += nRead; |
| pos += nRead; |
| buf += nRead; |
| } |
| |
| if (nRead == nToRead) |
| nbyte -= nRead; |
| else |
| nbyte = 0; /* no more to read */ |
| |
| if (nbyte > 0) { |
| yaffsfs_Unlock(); |
| yaffsfs_Lock(); |
| } |
| |
| } |
| |
| yaffsfs_PutHandle(handle); |
| |
| if (!isPread) { |
| if (totalRead >= 0) |
| fd->position = startPos + totalRead; |
| else |
| yaffsfs_SetError(-EINVAL); |
| } |
| |
| } |
| |
| yaffsfs_Unlock(); |
| |
| return (totalRead >= 0) ? totalRead : -1; |
| |
| } |
| |
| int yaffs_read(int handle, void *buf, unsigned int nbyte) |
| { |
| return yaffsfs_do_read(handle, buf, nbyte, 0, 0); |
| } |
| |
| int yaffs_pread(int handle, void *buf, unsigned int nbyte, loff_t offset) |
| { |
| return yaffsfs_do_read(handle, buf, nbyte, 1, offset); |
| } |
| |
| int yaffsfs_do_write(int handle, const void *vbuf, unsigned int nbyte, |
| int isPwrite, loff_t offset) |
| { |
| struct yaffsfs_FileDes *fd = NULL; |
| struct yaffs_obj *obj = NULL; |
| loff_t pos = 0; |
| loff_t startPos = 0; |
| loff_t endPos; |
| int nWritten = 0; |
| int totalWritten = 0; |
| int write_trhrough = 0; |
| int nToWrite = 0; |
| const u8 *buf = (const u8 *)vbuf; |
| |
| if (!vbuf) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| fd = yaffsfs_HandleToFileDes(handle); |
| obj = yaffsfs_HandleToObject(handle); |
| |
| if (!fd || !obj) { |
| /* bad handle */ |
| yaffsfs_SetError(-EBADF); |
| totalWritten = -1; |
| } else if (!fd->writing) { |
| yaffsfs_SetError(-EINVAL); |
| totalWritten = -1; |
| } else if (obj->my_dev->read_only) { |
| yaffsfs_SetError(-EROFS); |
| totalWritten = -1; |
| } else { |
| if (fd->append) |
| startPos = yaffs_get_obj_length(obj); |
| else if (isPwrite) |
| startPos = offset; |
| else |
| startPos = fd->position; |
| |
| yaffsfs_GetHandle(handle); |
| pos = startPos; |
| endPos = pos + nbyte; |
| |
| if (pos < 0 || pos > YAFFS_MAX_FILE_SIZE || |
| nbyte > YAFFS_MAX_FILE_SIZE || |
| endPos < 0 || endPos > YAFFS_MAX_FILE_SIZE) { |
| totalWritten = -1; |
| nbyte = 0; |
| } |
| |
| while (nbyte > 0) { |
| |
| nToWrite = YAFFSFS_RW_SIZE - |
| (pos & (YAFFSFS_RW_SIZE - 1)); |
| if (nToWrite > nbyte) |
| nToWrite = nbyte; |
| |
| /* Tricky bit... |
| * Need to reverify object in case the device was |
| * remounted or unmounted in another thread. |
| */ |
| obj = yaffsfs_HandleToObject(handle); |
| if (!obj || obj->my_dev->read_only) |
| nWritten = 0; |
| else |
| nWritten = |
| yaffs_wr_file(obj, buf, pos, nToWrite, |
| write_trhrough); |
| if (nWritten > 0) { |
| totalWritten += nWritten; |
| pos += nWritten; |
| buf += nWritten; |
| } |
| |
| if (nWritten == nToWrite) |
| nbyte -= nToWrite; |
| else |
| nbyte = 0; |
| |
| if (nWritten < 1 && totalWritten < 1) { |
| yaffsfs_SetError(-ENOSPC); |
| totalWritten = -1; |
| } |
| |
| if (nbyte > 0) { |
| yaffsfs_Unlock(); |
| yaffsfs_Lock(); |
| } |
| } |
| |
| yaffsfs_PutHandle(handle); |
| |
| if (!isPwrite) { |
| if (totalWritten > 0) |
| fd->position = startPos + totalWritten; |
| else |
| yaffsfs_SetError(-EINVAL); |
| } |
| } |
| |
| yaffsfs_Unlock(); |
| |
| return (totalWritten >= 0) ? totalWritten : -1; |
| } |
| |
| int yaffs_write(int fd, const void *buf, unsigned int nbyte) |
| { |
| return yaffsfs_do_write(fd, buf, nbyte, 0, 0); |
| } |
| |
| int yaffs_pwrite(int fd, const void *buf, unsigned int nbyte, loff_t offset) |
| { |
| return yaffsfs_do_write(fd, buf, nbyte, 1, offset); |
| } |
| |
| int yaffs_truncate(const YCHAR *path, loff_t new_size) |
| { |
| struct yaffs_obj *obj = NULL; |
| struct yaffs_obj *dir = NULL; |
| int result = YAFFS_FAIL; |
| int notDir = 0; |
| int loop = 0; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| |
| obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, ¬Dir, &loop); |
| obj = yaffsfs_FollowLink(obj, 0, &loop); |
| |
| if (!dir && notDir) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (loop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!dir || !obj) |
| yaffsfs_SetError(-ENOENT); |
| else if (obj->my_dev->read_only) |
| yaffsfs_SetError(-EROFS); |
| else if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) |
| yaffsfs_SetError(-EISDIR); |
| else if (obj->my_dev->read_only) |
| yaffsfs_SetError(-EROFS); |
| else if (new_size < 0 || new_size > YAFFS_MAX_FILE_SIZE) |
| yaffsfs_SetError(-EINVAL); |
| else |
| result = yaffs_resize_file(obj, new_size); |
| |
| yaffsfs_Unlock(); |
| |
| return (result) ? 0 : -1; |
| } |
| |
| int yaffs_ftruncate(int handle, loff_t new_size) |
| { |
| struct yaffsfs_FileDes *fd = NULL; |
| struct yaffs_obj *obj = NULL; |
| int result = 0; |
| |
| yaffsfs_Lock(); |
| fd = yaffsfs_HandleToFileDes(handle); |
| obj = yaffsfs_HandleToObject(handle); |
| |
| if (!fd || !obj) |
| /* bad handle */ |
| yaffsfs_SetError(-EBADF); |
| else if (!fd->writing) |
| yaffsfs_SetError(-EINVAL); |
| else if (obj->my_dev->read_only) |
| yaffsfs_SetError(-EROFS); |
| else if (new_size < 0 || new_size > YAFFS_MAX_FILE_SIZE) |
| yaffsfs_SetError(-EINVAL); |
| else |
| /* resize the file */ |
| result = yaffs_resize_file(obj, new_size); |
| yaffsfs_Unlock(); |
| |
| return (result) ? 0 : -1; |
| |
| } |
| |
| loff_t yaffs_lseek(int handle, loff_t offset, int whence) |
| { |
| struct yaffsfs_FileDes *fd = NULL; |
| struct yaffs_obj *obj = NULL; |
| loff_t pos = -1; |
| loff_t fSize = -1; |
| |
| yaffsfs_Lock(); |
| fd = yaffsfs_HandleToFileDes(handle); |
| obj = yaffsfs_HandleToObject(handle); |
| |
| if (!fd || !obj) |
| yaffsfs_SetError(-EBADF); |
| else if (offset > YAFFS_MAX_FILE_SIZE) |
| yaffsfs_SetError(-EINVAL); |
| else { |
| if (whence == SEEK_SET) { |
| if (offset >= 0) |
| pos = offset; |
| } else if (whence == SEEK_CUR) { |
| if ((fd->position + offset) >= 0) |
| pos = (fd->position + offset); |
| } else if (whence == SEEK_END) { |
| fSize = yaffs_get_obj_length(obj); |
| if (fSize >= 0 && (fSize + offset) >= 0) |
| pos = fSize + offset; |
| } |
| |
| if (pos >= 0 && pos <= YAFFS_MAX_FILE_SIZE) |
| fd->position = pos; |
| else { |
| yaffsfs_SetError(-EINVAL); |
| pos = -1; |
| } |
| } |
| |
| yaffsfs_Unlock(); |
| |
| return pos; |
| } |
| |
| int yaffsfs_DoUnlink(const YCHAR *path, int isDirectory) |
| { |
| struct yaffs_obj *dir = NULL; |
| struct yaffs_obj *obj = NULL; |
| YCHAR *name; |
| int result = YAFFS_FAIL; |
| int notDir = 0; |
| int loop = 0; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| |
| obj = yaffsfs_FindObject(NULL, path, 0, 0, NULL, NULL, NULL); |
| dir = yaffsfs_FindDirectory(NULL, path, &name, 0, ¬Dir, &loop); |
| |
| if (!dir && notDir) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (loop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!dir) |
| yaffsfs_SetError(-ENOENT); |
| else if (yaffs_strncmp(name, _Y("."), 2) == 0) |
| yaffsfs_SetError(-EINVAL); |
| else if (!obj) |
| yaffsfs_SetError(-ENOENT); |
| else if (obj->my_dev->read_only) |
| yaffsfs_SetError(-EROFS); |
| else if (!isDirectory && |
| obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) |
| yaffsfs_SetError(-EISDIR); |
| else if (isDirectory && |
| obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (isDirectory && obj == obj->my_dev->root_dir) |
| yaffsfs_SetError(-EBUSY); /* Can't rmdir a root */ |
| else { |
| result = yaffs_unlinker(dir, name); |
| |
| if (result == YAFFS_FAIL && isDirectory) |
| yaffsfs_SetError(-ENOTEMPTY); |
| } |
| |
| yaffsfs_Unlock(); |
| |
| return (result == YAFFS_FAIL) ? -1 : 0; |
| } |
| |
| int yaffs_unlink(const YCHAR *path) |
| { |
| return yaffsfs_DoUnlink(path, 0); |
| } |
| |
| int yaffs_rename(const YCHAR *oldPath, const YCHAR *newPath) |
| { |
| struct yaffs_obj *olddir = NULL; |
| struct yaffs_obj *newdir = NULL; |
| struct yaffs_obj *obj = NULL; |
| struct yaffs_obj *newobj = NULL; |
| YCHAR *oldname; |
| YCHAR *newname; |
| int result = YAFFS_FAIL; |
| int rename_allowed = 1; |
| int notOldDir = 0; |
| int notNewDir = 0; |
| int oldLoop = 0; |
| int newLoop = 0; |
| |
| YCHAR *alt_newpath = NULL; |
| |
| if (!oldPath || !newPath) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(oldPath) < 0 || yaffsfs_CheckPath(newPath) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| if (yaffsfs_alt_dir_path(newPath, &alt_newpath) < 0) { |
| yaffsfs_SetError(-ENOMEM); |
| return -1; |
| } |
| if (alt_newpath) |
| newPath = alt_newpath; |
| |
| yaffsfs_Lock(); |
| |
| olddir = yaffsfs_FindDirectory(NULL, oldPath, &oldname, 0, |
| ¬OldDir, &oldLoop); |
| newdir = yaffsfs_FindDirectory(NULL, newPath, &newname, 0, |
| ¬NewDir, &newLoop); |
| obj = yaffsfs_FindObject(NULL, oldPath, 0, 0, NULL, NULL, NULL); |
| newobj = yaffsfs_FindObject(NULL, newPath, 0, 0, NULL, NULL, NULL); |
| |
| /* If the object being renamed is a directory and the |
| * path ended with a "/" then the olddir == obj. |
| * We pass through NULL for the old name to tell the lower layers |
| * to use olddir as the object. |
| */ |
| |
| if (olddir == obj) |
| oldname = NULL; |
| |
| if ((!olddir && notOldDir) || (!newdir && notNewDir)) { |
| yaffsfs_SetError(-ENOTDIR); |
| rename_allowed = 0; |
| } else if (oldLoop || newLoop) { |
| yaffsfs_SetError(-ELOOP); |
| rename_allowed = 0; |
| } else if (olddir && oldname && |
| yaffs_strncmp(oldname, _Y("."), 2) == 0) { |
| yaffsfs_SetError(-EINVAL); |
| rename_allowed = 0; |
| } else if (!olddir || !newdir || !obj) { |
| yaffsfs_SetError(-ENOENT); |
| rename_allowed = 0; |
| } else if (obj->my_dev->read_only) { |
| yaffsfs_SetError(-EROFS); |
| rename_allowed = 0; |
| } else if (yaffs_is_non_empty_dir(newobj)) { |
| yaffsfs_SetError(-ENOTEMPTY); |
| rename_allowed = 0; |
| } else if (olddir->my_dev != newdir->my_dev) { |
| /* Rename must be on same device */ |
| yaffsfs_SetError(-EXDEV); |
| rename_allowed = 0; |
| } else if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) { |
| /* |
| * It is a directory, check that it is not being renamed to |
| * being its own decendent. |
| * Do this by tracing from the new directory back to the root, |
| * checking for obj |
| */ |
| |
| struct yaffs_obj *xx = newdir; |
| |
| while (rename_allowed && xx) { |
| if (xx == obj) |
| rename_allowed = 0; |
| xx = xx->parent; |
| } |
| if (!rename_allowed) |
| yaffsfs_SetError(-EINVAL); |
| } |
| |
| if (rename_allowed) |
| result = yaffs_rename_obj(olddir, oldname, newdir, newname); |
| |
| yaffsfs_Unlock(); |
| |
| kfree(alt_newpath); |
| |
| return (result == YAFFS_FAIL) ? -1 : 0; |
| } |
| |
| static int yaffsfs_DoStat(struct yaffs_obj *obj, struct yaffs_stat *buf) |
| { |
| int retVal = -1; |
| |
| obj = yaffs_get_equivalent_obj(obj); |
| |
| if (obj && buf) { |
| buf->st_dev = (int)obj->my_dev->os_context; |
| buf->st_ino = obj->obj_id; |
| buf->st_mode = obj->yst_mode & ~S_IFMT; |
| |
| if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) |
| buf->st_mode |= S_IFDIR; |
| else if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) |
| buf->st_mode |= S_IFLNK; |
| else if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) |
| buf->st_mode |= S_IFREG; |
| |
| buf->st_nlink = yaffs_get_obj_link_count(obj); |
| buf->st_uid = 0; |
| buf->st_gid = 0; |
| buf->st_rdev = obj->yst_rdev; |
| buf->st_size = yaffs_get_obj_length(obj); |
| buf->st_blksize = obj->my_dev->data_bytes_per_chunk; |
| buf->st_blocks = lldiv(buf->st_size + buf->st_blksize - 1, |
| buf->st_blksize); |
| #if CONFIG_YAFFS_WINCE |
| buf->yst_wince_atime[0] = obj->win_atime[0]; |
| buf->yst_wince_atime[1] = obj->win_atime[1]; |
| buf->yst_wince_ctime[0] = obj->win_ctime[0]; |
| buf->yst_wince_ctime[1] = obj->win_ctime[1]; |
| buf->yst_wince_mtime[0] = obj->win_mtime[0]; |
| buf->yst_wince_mtime[1] = obj->win_mtime[1]; |
| #else |
| buf->yst_atime = obj->yst_atime; |
| buf->yst_ctime = obj->yst_ctime; |
| buf->yst_mtime = obj->yst_mtime; |
| #endif |
| retVal = 0; |
| } |
| return retVal; |
| } |
| |
| static int yaffsfs_DoStatOrLStat(const YCHAR *path, |
| struct yaffs_stat *buf, int doLStat) |
| { |
| struct yaffs_obj *obj = NULL; |
| struct yaffs_obj *dir = NULL; |
| int retVal = -1; |
| int notDir = 0; |
| int loop = 0; |
| |
| if (!path || !buf) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| |
| obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, ¬Dir, &loop); |
| |
| if (!doLStat && obj) |
| obj = yaffsfs_FollowLink(obj, 0, &loop); |
| |
| if (!dir && notDir) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (loop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!dir || !obj) |
| yaffsfs_SetError(-ENOENT); |
| else |
| retVal = yaffsfs_DoStat(obj, buf); |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| |
| } |
| |
| int yaffs_stat(const YCHAR *path, struct yaffs_stat *buf) |
| { |
| return yaffsfs_DoStatOrLStat(path, buf, 0); |
| } |
| |
| int yaffs_lstat(const YCHAR *path, struct yaffs_stat *buf) |
| { |
| return yaffsfs_DoStatOrLStat(path, buf, 1); |
| } |
| |
| int yaffs_fstat(int fd, struct yaffs_stat *buf) |
| { |
| struct yaffs_obj *obj; |
| |
| int retVal = -1; |
| |
| if (!buf) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| obj = yaffsfs_HandleToObject(fd); |
| |
| if (obj) |
| retVal = yaffsfs_DoStat(obj, buf); |
| else |
| /* bad handle */ |
| yaffsfs_SetError(-EBADF); |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| } |
| |
| static int yaffsfs_DoUtime(struct yaffs_obj *obj, |
| const struct yaffs_utimbuf *buf) |
| { |
| int retVal = -1; |
| int result; |
| |
| struct yaffs_utimbuf local; |
| |
| obj = yaffs_get_equivalent_obj(obj); |
| |
| if (obj && obj->my_dev->read_only) { |
| yaffsfs_SetError(-EROFS); |
| return -1; |
| } |
| |
| if (!buf) { |
| local.actime = Y_CURRENT_TIME; |
| local.modtime = local.actime; |
| buf = &local; |
| } |
| |
| if (obj) { |
| obj->yst_atime = buf->actime; |
| obj->yst_mtime = buf->modtime; |
| obj->dirty = 1; |
| result = yaffs_flush_file(obj, 0, 0); |
| retVal = result == YAFFS_OK ? 0 : -1; |
| } |
| |
| return retVal; |
| } |
| |
| int yaffs_utime(const YCHAR *path, const struct yaffs_utimbuf *buf) |
| { |
| struct yaffs_obj *obj = NULL; |
| struct yaffs_obj *dir = NULL; |
| int retVal = -1; |
| int notDir = 0; |
| int loop = 0; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| |
| obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, ¬Dir, &loop); |
| |
| if (!dir && notDir) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (loop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!dir || !obj) |
| yaffsfs_SetError(-ENOENT); |
| else |
| retVal = yaffsfs_DoUtime(obj, buf); |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| |
| } |
| |
| int yaffs_futime(int fd, const struct yaffs_utimbuf *buf) |
| { |
| struct yaffs_obj *obj; |
| |
| int retVal = -1; |
| |
| yaffsfs_Lock(); |
| obj = yaffsfs_HandleToObject(fd); |
| |
| if (obj) |
| retVal = yaffsfs_DoUtime(obj, buf); |
| else |
| /* bad handle */ |
| yaffsfs_SetError(-EBADF); |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| } |
| |
| #ifndef CONFIG_YAFFS_WINCE |
| /* xattrib functions */ |
| |
| static int yaffs_do_setxattr(const YCHAR *path, const char *name, |
| const void *data, int size, int flags, int follow) |
| { |
| struct yaffs_obj *obj; |
| struct yaffs_obj *dir; |
| int notDir = 0; |
| int loop = 0; |
| |
| int retVal = -1; |
| |
| if (!path || !name || !data) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| |
| obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, ¬Dir, &loop); |
| |
| if (follow) |
| obj = yaffsfs_FollowLink(obj, 0, &loop); |
| |
| if (!dir && notDir) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (loop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!dir || !obj) |
| yaffsfs_SetError(-ENOENT); |
| else { |
| retVal = yaffs_set_xattrib(obj, name, data, size, flags); |
| if (retVal < 0) { |
| yaffsfs_SetError(retVal); |
| retVal = -1; |
| } |
| } |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| |
| } |
| |
| int yaffs_setxattr(const YCHAR *path, const char *name, |
| const void *data, int size, int flags) |
| { |
| return yaffs_do_setxattr(path, name, data, size, flags, 1); |
| } |
| |
| int yaffs_lsetxattr(const YCHAR *path, const char *name, |
| const void *data, int size, int flags) |
| { |
| return yaffs_do_setxattr(path, name, data, size, flags, 0); |
| } |
| |
| int yaffs_fsetxattr(int fd, const char *name, |
| const void *data, int size, int flags) |
| { |
| struct yaffs_obj *obj; |
| |
| int retVal = -1; |
| |
| if (!name || !data) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| obj = yaffsfs_HandleToObject(fd); |
| |
| if (!obj) |
| yaffsfs_SetError(-EBADF); |
| else { |
| retVal = yaffs_set_xattrib(obj, name, data, size, flags); |
| if (retVal < 0) { |
| yaffsfs_SetError(retVal); |
| retVal = -1; |
| } |
| } |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| } |
| |
| static int yaffs_do_getxattr(const YCHAR *path, const char *name, |
| void *data, int size, int follow) |
| { |
| struct yaffs_obj *obj; |
| struct yaffs_obj *dir; |
| int retVal = -1; |
| int notDir = 0; |
| int loop = 0; |
| |
| if (!path || !name || !data) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| |
| obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, ¬Dir, &loop); |
| |
| if (follow) |
| obj = yaffsfs_FollowLink(obj, 0, &loop); |
| |
| if (!dir && notDir) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (loop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!dir || !obj) |
| yaffsfs_SetError(-ENOENT); |
| else { |
| retVal = yaffs_get_xattrib(obj, name, data, size); |
| if (retVal < 0) { |
| yaffsfs_SetError(retVal); |
| retVal = -1; |
| } |
| } |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| |
| } |
| |
| int yaffs_getxattr(const YCHAR *path, const char *name, void *data, int size) |
| { |
| return yaffs_do_getxattr(path, name, data, size, 1); |
| } |
| |
| int yaffs_lgetxattr(const YCHAR *path, const char *name, void *data, int size) |
| { |
| return yaffs_do_getxattr(path, name, data, size, 0); |
| } |
| |
| int yaffs_fgetxattr(int fd, const char *name, void *data, int size) |
| { |
| struct yaffs_obj *obj; |
| |
| int retVal = -1; |
| |
| if (!name || !data) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| obj = yaffsfs_HandleToObject(fd); |
| |
| if (obj) { |
| retVal = yaffs_get_xattrib(obj, name, data, size); |
| if (retVal < 0) { |
| yaffsfs_SetError(retVal); |
| retVal = -1; |
| } |
| } else |
| /* bad handle */ |
| yaffsfs_SetError(-EBADF); |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| } |
| |
| static int yaffs_do_listxattr(const YCHAR *path, char *data, |
| int size, int follow) |
| { |
| struct yaffs_obj *obj = NULL; |
| struct yaffs_obj *dir = NULL; |
| int retVal = -1; |
| int notDir = 0; |
| int loop = 0; |
| |
| if (!path || !data) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| |
| obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, ¬Dir, &loop); |
| |
| if (follow) |
| obj = yaffsfs_FollowLink(obj, 0, &loop); |
| |
| if (!dir && notDir) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (loop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!dir || !obj) |
| yaffsfs_SetError(-ENOENT); |
| else { |
| retVal = yaffs_list_xattrib(obj, data, size); |
| if (retVal < 0) { |
| yaffsfs_SetError(retVal); |
| retVal = -1; |
| } |
| } |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| |
| } |
| |
| int yaffs_listxattr(const YCHAR *path, char *data, int size) |
| { |
| return yaffs_do_listxattr(path, data, size, 1); |
| } |
| |
| int yaffs_llistxattr(const YCHAR *path, char *data, int size) |
| { |
| return yaffs_do_listxattr(path, data, size, 0); |
| } |
| |
| int yaffs_flistxattr(int fd, char *data, int size) |
| { |
| struct yaffs_obj *obj; |
| |
| int retVal = -1; |
| |
| if (!data) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| obj = yaffsfs_HandleToObject(fd); |
| |
| if (obj) { |
| retVal = yaffs_list_xattrib(obj, data, size); |
| if (retVal < 0) { |
| yaffsfs_SetError(retVal); |
| retVal = -1; |
| } |
| } else |
| /* bad handle */ |
| yaffsfs_SetError(-EBADF); |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| } |
| |
| static int yaffs_do_removexattr(const YCHAR *path, const char *name, |
| int follow) |
| { |
| struct yaffs_obj *obj = NULL; |
| struct yaffs_obj *dir = NULL; |
| int notDir = 0; |
| int loop = 0; |
| int retVal = -1; |
| |
| if (!path || !name) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| |
| obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, ¬Dir, &loop); |
| |
| if (follow) |
| obj = yaffsfs_FollowLink(obj, 0, &loop); |
| |
| if (!dir && notDir) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (loop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!dir || !obj) |
| yaffsfs_SetError(-ENOENT); |
| else { |
| retVal = yaffs_remove_xattrib(obj, name); |
| if (retVal < 0) { |
| yaffsfs_SetError(retVal); |
| retVal = -1; |
| } |
| } |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| |
| } |
| |
| int yaffs_removexattr(const YCHAR *path, const char *name) |
| { |
| return yaffs_do_removexattr(path, name, 1); |
| } |
| |
| int yaffs_lremovexattr(const YCHAR *path, const char *name) |
| { |
| return yaffs_do_removexattr(path, name, 0); |
| } |
| |
| int yaffs_fremovexattr(int fd, const char *name) |
| { |
| struct yaffs_obj *obj; |
| |
| int retVal = -1; |
| |
| if (!name) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| obj = yaffsfs_HandleToObject(fd); |
| |
| if (obj) { |
| retVal = yaffs_remove_xattrib(obj, name); |
| if (retVal < 0) { |
| yaffsfs_SetError(retVal); |
| retVal = -1; |
| } |
| } else |
| /* bad handle */ |
| yaffsfs_SetError(-EBADF); |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| } |
| #endif |
| |
| #ifdef CONFIG_YAFFS_WINCE |
| int yaffs_get_wince_times(int fd, unsigned *wctime, |
| unsigned *watime, unsigned *wmtime) |
| { |
| struct yaffs_obj *obj; |
| |
| int retVal = -1; |
| |
| yaffsfs_Lock(); |
| obj = yaffsfs_HandleToObject(fd); |
| |
| if (obj) { |
| |
| if (wctime) { |
| wctime[0] = obj->win_ctime[0]; |
| wctime[1] = obj->win_ctime[1]; |
| } |
| if (watime) { |
| watime[0] = obj->win_atime[0]; |
| watime[1] = obj->win_atime[1]; |
| } |
| if (wmtime) { |
| wmtime[0] = obj->win_mtime[0]; |
| wmtime[1] = obj->win_mtime[1]; |
| } |
| |
| retVal = 0; |
| } else |
| /* bad handle */ |
| yaffsfs_SetError(-EBADF); |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| } |
| |
| int yaffs_set_wince_times(int fd, |
| const unsigned *wctime, |
| const unsigned *watime, const unsigned *wmtime) |
| { |
| struct yaffs_obj *obj; |
| int result; |
| int retVal = -1; |
| |
| yaffsfs_Lock(); |
| obj = yaffsfs_HandleToObject(fd); |
| |
| if (obj) { |
| |
| if (wctime) { |
| obj->win_ctime[0] = wctime[0]; |
| obj->win_ctime[1] = wctime[1]; |
| } |
| if (watime) { |
| obj->win_atime[0] = watime[0]; |
| obj->win_atime[1] = watime[1]; |
| } |
| if (wmtime) { |
| obj->win_mtime[0] = wmtime[0]; |
| obj->win_mtime[1] = wmtime[1]; |
| } |
| |
| obj->dirty = 1; |
| result = yaffs_flush_file(obj, 0, 0); |
| retVal = 0; |
| } else |
| /* bad handle */ |
| yaffsfs_SetError(-EBADF); |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| } |
| |
| #endif |
| |
| static int yaffsfs_DoChMod(struct yaffs_obj *obj, mode_t mode) |
| { |
| int result = -1; |
| |
| if (obj) |
| obj = yaffs_get_equivalent_obj(obj); |
| |
| if (obj) { |
| obj->yst_mode = mode; |
| obj->dirty = 1; |
| result = yaffs_flush_file(obj, 0, 0); |
| } |
| |
| return result == YAFFS_OK ? 0 : -1; |
| } |
| |
| int yaffs_access(const YCHAR *path, int amode) |
| { |
| struct yaffs_obj *obj = NULL; |
| struct yaffs_obj *dir = NULL; |
| int notDir = 0; |
| int loop = 0; |
| int retval = -1; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| if (amode & ~(R_OK | W_OK | X_OK)) { |
| yaffsfs_SetError(-EINVAL); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| |
| obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, ¬Dir, &loop); |
| obj = yaffsfs_FollowLink(obj, 0, &loop); |
| |
| if (!dir && notDir) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (loop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!dir || !obj) |
| yaffsfs_SetError(-ENOENT); |
| else if ((amode & W_OK) && obj->my_dev->read_only) |
| yaffsfs_SetError(-EROFS); |
| else { |
| int access_ok = 1; |
| |
| if ((amode & R_OK) && !(obj->yst_mode & S_IREAD)) |
| access_ok = 0; |
| if ((amode & W_OK) && !(obj->yst_mode & S_IWRITE)) |
| access_ok = 0; |
| if ((amode & X_OK) && !(obj->yst_mode & S_IEXEC)) |
| access_ok = 0; |
| |
| if (!access_ok) |
| yaffsfs_SetError(-EACCES); |
| else |
| retval = 0; |
| } |
| |
| yaffsfs_Unlock(); |
| |
| return retval; |
| |
| } |
| |
| int yaffs_chmod(const YCHAR *path, mode_t mode) |
| { |
| struct yaffs_obj *obj = NULL; |
| struct yaffs_obj *dir = NULL; |
| int retVal = -1; |
| int notDir = 0; |
| int loop = 0; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| if (mode & ~(0777)) { |
| yaffsfs_SetError(-EINVAL); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| |
| obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, ¬Dir, &loop); |
| obj = yaffsfs_FollowLink(obj, 0, &loop); |
| |
| if (!dir && notDir) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (loop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!dir || !obj) |
| yaffsfs_SetError(-ENOENT); |
| else if (obj->my_dev->read_only) |
| yaffsfs_SetError(-EROFS); |
| else |
| retVal = yaffsfs_DoChMod(obj, mode); |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| |
| } |
| |
| int yaffs_fchmod(int fd, mode_t mode) |
| { |
| struct yaffs_obj *obj; |
| int retVal = -1; |
| |
| if (mode & ~(0777)) { |
| yaffsfs_SetError(-EINVAL); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| obj = yaffsfs_HandleToObject(fd); |
| |
| if (!obj) |
| yaffsfs_SetError(-EBADF); |
| else if (obj->my_dev->read_only) |
| yaffsfs_SetError(-EROFS); |
| else |
| retVal = yaffsfs_DoChMod(obj, mode); |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| } |
| |
| int yaffs_mkdir(const YCHAR *path, mode_t mode) |
| { |
| struct yaffs_obj *parent = NULL; |
| struct yaffs_obj *dir = NULL; |
| YCHAR *name; |
| YCHAR *alt_path = NULL; |
| int retVal = -1; |
| int notDir = 0; |
| int loop = 0; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| if (yaffsfs_alt_dir_path(path, &alt_path) < 0) { |
| yaffsfs_SetError(-ENOMEM); |
| return -1; |
| } |
| if (alt_path) |
| path = alt_path; |
| |
| yaffsfs_Lock(); |
| parent = yaffsfs_FindDirectory(NULL, path, &name, 0, ¬Dir, &loop); |
| if (!parent && notDir) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (loop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!parent) |
| yaffsfs_SetError(-ENOENT); |
| else if (yaffsfs_TooManyObjects(parent->my_dev)) |
| yaffsfs_SetError(-ENFILE); |
| else if (yaffs_strnlen(name, 5) == 0) { |
| /* Trying to make the root itself */ |
| yaffsfs_SetError(-EEXIST); |
| } else if (parent->my_dev->read_only) |
| yaffsfs_SetError(-EROFS); |
| else { |
| dir = yaffs_create_dir(parent, name, mode, 0, 0); |
| if (dir) |
| retVal = 0; |
| else if (yaffs_find_by_name(parent, name)) |
| yaffsfs_SetError(-EEXIST); /* name exists */ |
| else |
| yaffsfs_SetError(-ENOSPC); /* assume no space */ |
| } |
| |
| yaffsfs_Unlock(); |
| |
| kfree(alt_path); |
| |
| return retVal; |
| } |
| |
| int yaffs_rmdir(const YCHAR *path) |
| { |
| int result; |
| YCHAR *alt_path; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| if (yaffsfs_alt_dir_path(path, &alt_path) < 0) { |
| yaffsfs_SetError(-ENOMEM); |
| return -1; |
| } |
| if (alt_path) |
| path = alt_path; |
| result = yaffsfs_DoUnlink(path, 1); |
| |
| kfree(alt_path); |
| |
| return result; |
| } |
| |
| void *yaffs_getdev(const YCHAR *path) |
| { |
| struct yaffs_dev *dev = NULL; |
| YCHAR *dummy; |
| dev = yaffsfs_FindDevice(path, &dummy); |
| return (void *)dev; |
| } |
| |
| int yaffs_mount_common(const YCHAR *path, int read_only, int skip_checkpt) |
| { |
| int retVal = -1; |
| int result = YAFFS_FAIL; |
| struct yaffs_dev *dev = NULL; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| yaffs_trace(YAFFS_TRACE_MOUNT, "yaffs: Mounting %s", path); |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| |
| yaffsfs_InitHandles(); |
| |
| dev = yaffsfs_FindMountPoint(path); |
| if (dev) { |
| if (!dev->is_mounted) { |
| dev->read_only = read_only ? 1 : 0; |
| if (skip_checkpt) { |
| u8 skip = dev->param.skip_checkpt_rd; |
| dev->param.skip_checkpt_rd = 1; |
| result = yaffs_guts_initialise(dev); |
| dev->param.skip_checkpt_rd = skip; |
| } else { |
| result = yaffs_guts_initialise(dev); |
| } |
| |
| if (result == YAFFS_FAIL) |
| yaffsfs_SetError(-ENOMEM); |
| retVal = result ? 0 : -1; |
| |
| } else |
| yaffsfs_SetError(-EBUSY); |
| } else |
| yaffsfs_SetError(-ENODEV); |
| |
| yaffsfs_Unlock(); |
| return retVal; |
| |
| } |
| |
| int yaffs_mount2(const YCHAR *path, int readonly) |
| { |
| return yaffs_mount_common(path, readonly, 0); |
| } |
| |
| int yaffs_mount(const YCHAR *path) |
| { |
| return yaffs_mount_common(path, 0, 0); |
| } |
| |
| int yaffs_sync(const YCHAR *path) |
| { |
| int retVal = -1; |
| struct yaffs_dev *dev = NULL; |
| YCHAR *dummy; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| dev = yaffsfs_FindDevice(path, &dummy); |
| if (dev) { |
| if (!dev->is_mounted) |
| yaffsfs_SetError(-EINVAL); |
| else if (dev->read_only) |
| yaffsfs_SetError(-EROFS); |
| else { |
| |
| yaffs_flush_whole_cache(dev); |
| yaffs_checkpoint_save(dev); |
| retVal = 0; |
| |
| } |
| } else |
| yaffsfs_SetError(-ENODEV); |
| |
| yaffsfs_Unlock(); |
| return retVal; |
| } |
| |
| static int yaffsfs_IsDevBusy(struct yaffs_dev *dev) |
| { |
| int i; |
| struct yaffs_obj *obj; |
| |
| for (i = 0; i < YAFFSFS_N_HANDLES; i++) { |
| obj = yaffsfs_HandleToObject(i); |
| if (obj && obj->my_dev == dev) |
| return 1; |
| } |
| return 0; |
| } |
| |
| int yaffs_remount(const YCHAR *path, int force, int read_only) |
| { |
| int retVal = -1; |
| struct yaffs_dev *dev = NULL; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| dev = yaffsfs_FindMountPoint(path); |
| if (dev) { |
| if (dev->is_mounted) { |
| yaffs_flush_whole_cache(dev); |
| |
| if (force || !yaffsfs_IsDevBusy(dev)) { |
| if (read_only) |
| yaffs_checkpoint_save(dev); |
| dev->read_only = read_only ? 1 : 0; |
| retVal = 0; |
| } else |
| yaffsfs_SetError(-EBUSY); |
| |
| } else |
| yaffsfs_SetError(-EINVAL); |
| |
| } else |
| yaffsfs_SetError(-ENODEV); |
| |
| yaffsfs_Unlock(); |
| return retVal; |
| |
| } |
| |
| int yaffs_unmount2(const YCHAR *path, int force) |
| { |
| int retVal = -1; |
| struct yaffs_dev *dev = NULL; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| dev = yaffsfs_FindMountPoint(path); |
| if (dev) { |
| if (dev->is_mounted) { |
| int inUse; |
| yaffs_flush_whole_cache(dev); |
| yaffs_checkpoint_save(dev); |
| inUse = yaffsfs_IsDevBusy(dev); |
| if (!inUse || force) { |
| if (inUse) |
| yaffsfs_BreakDeviceHandles(dev); |
| yaffs_deinitialise(dev); |
| |
| retVal = 0; |
| } else |
| yaffsfs_SetError(-EBUSY); |
| |
| } else |
| yaffsfs_SetError(-EINVAL); |
| |
| } else |
| yaffsfs_SetError(-ENODEV); |
| |
| yaffsfs_Unlock(); |
| return retVal; |
| |
| } |
| |
| int yaffs_unmount(const YCHAR *path) |
| { |
| return yaffs_unmount2(path, 0); |
| } |
| |
| loff_t yaffs_freespace(const YCHAR *path) |
| { |
| loff_t retVal = -1; |
| struct yaffs_dev *dev = NULL; |
| YCHAR *dummy; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| dev = yaffsfs_FindDevice(path, &dummy); |
| if (dev && dev->is_mounted) { |
| retVal = yaffs_get_n_free_chunks(dev); |
| retVal *= dev->data_bytes_per_chunk; |
| |
| } else |
| yaffsfs_SetError(-EINVAL); |
| |
| yaffsfs_Unlock(); |
| return retVal; |
| } |
| |
| loff_t yaffs_totalspace(const YCHAR *path) |
| { |
| loff_t retVal = -1; |
| struct yaffs_dev *dev = NULL; |
| YCHAR *dummy; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| dev = yaffsfs_FindDevice(path, &dummy); |
| if (dev && dev->is_mounted) { |
| retVal = (dev->param.end_block - dev->param.start_block + 1) - |
| dev->param.n_reserved_blocks; |
| retVal *= dev->param.chunks_per_block; |
| retVal *= dev->data_bytes_per_chunk; |
| |
| } else |
| yaffsfs_SetError(-EINVAL); |
| |
| yaffsfs_Unlock(); |
| return retVal; |
| } |
| |
| int yaffs_inodecount(const YCHAR *path) |
| { |
| loff_t retVal = -1; |
| struct yaffs_dev *dev = NULL; |
| YCHAR *dummy; |
| |
| if (!path) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(path) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| dev = yaffsfs_FindDevice(path, &dummy); |
| if (dev && dev->is_mounted) { |
| int n_obj = dev->n_obj; |
| if (n_obj > dev->n_hardlinks) |
| retVal = n_obj - dev->n_hardlinks; |
| } |
| |
| if (retVal < 0) |
| yaffsfs_SetError(-EINVAL); |
| |
| yaffsfs_Unlock(); |
| return retVal; |
| } |
| |
| void yaffs_add_device(struct yaffs_dev *dev) |
| { |
| struct list_head *cfg; |
| /* First check that the device is not in the list. */ |
| |
| list_for_each(cfg, &yaffsfs_deviceList) { |
| if (dev == list_entry(cfg, struct yaffs_dev, dev_list)) |
| return; |
| } |
| |
| dev->is_mounted = 0; |
| dev->param.remove_obj_fn = yaffsfs_RemoveObjectCallback; |
| |
| if (!dev->dev_list.next) |
| INIT_LIST_HEAD(&dev->dev_list); |
| |
| list_add(&dev->dev_list, &yaffsfs_deviceList); |
| } |
| |
| void yaffs_remove_device(struct yaffs_dev *dev) |
| { |
| list_del_init(&dev->dev_list); |
| } |
| |
| /* Functions to iterate through devices. NB Use with extreme care! */ |
| |
| static struct list_head *dev_iterator; |
| void yaffs_dev_rewind(void) |
| { |
| dev_iterator = yaffsfs_deviceList.next; |
| } |
| |
| struct yaffs_dev *yaffs_next_dev(void) |
| { |
| struct yaffs_dev *retval; |
| |
| if (!dev_iterator) |
| return NULL; |
| if (dev_iterator == &yaffsfs_deviceList) |
| return NULL; |
| |
| retval = list_entry(dev_iterator, struct yaffs_dev, dev_list); |
| dev_iterator = dev_iterator->next; |
| return retval; |
| } |
| |
| /* Directory search stuff. */ |
| |
| static struct list_head search_contexts; |
| |
| static void yaffsfs_SetDirRewound(struct yaffsfs_DirSearchContxt *dsc) |
| { |
| if (dsc && |
| dsc->dirObj && |
| dsc->dirObj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) { |
| |
| dsc->offset = 0; |
| |
| if (list_empty(&dsc->dirObj->variant.dir_variant.children)) |
| dsc->nextReturn = NULL; |
| else |
| dsc->nextReturn = |
| list_entry(dsc->dirObj->variant.dir_variant. |
| children.next, struct yaffs_obj, |
| siblings); |
| } else { |
| /* Hey someone isn't playing nice! */ |
| } |
| } |
| |
| static void yaffsfs_DirAdvance(struct yaffsfs_DirSearchContxt *dsc) |
| { |
| if (dsc && |
| dsc->dirObj && |
| dsc->dirObj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) { |
| |
| if (dsc->nextReturn == NULL || |
| list_empty(&dsc->dirObj->variant.dir_variant.children)) |
| dsc->nextReturn = NULL; |
| else { |
| struct list_head *next = dsc->nextReturn->siblings.next; |
| |
| if (next == &dsc->dirObj->variant.dir_variant.children) |
| dsc->nextReturn = NULL; /* end of list */ |
| else |
| dsc->nextReturn = list_entry(next, |
| struct yaffs_obj, |
| siblings); |
| } |
| } else { |
| /* Hey someone isn't playing nice! */ |
| } |
| } |
| |
| static void yaffsfs_RemoveObjectCallback(struct yaffs_obj *obj) |
| { |
| |
| struct list_head *i; |
| struct yaffsfs_DirSearchContxt *dsc; |
| |
| /* if search contexts not initilised then skip */ |
| if (!search_contexts.next) |
| return; |
| |
| /* Iterate through the directory search contexts. |
| * If any are the one being removed, then advance the dsc to |
| * the next one to prevent a hanging ptr. |
| */ |
| list_for_each(i, &search_contexts) { |
| dsc = list_entry(i, struct yaffsfs_DirSearchContxt, others); |
| if (dsc->nextReturn == obj) |
| yaffsfs_DirAdvance(dsc); |
| } |
| |
| } |
| |
| yaffs_DIR *yaffs_opendir(const YCHAR *dirname) |
| { |
| yaffs_DIR *dir = NULL; |
| struct yaffs_obj *obj = NULL; |
| struct yaffsfs_DirSearchContxt *dsc = NULL; |
| int notDir = 0; |
| int loop = 0; |
| |
| if (!dirname) { |
| yaffsfs_SetError(-EFAULT); |
| return NULL; |
| } |
| |
| if (yaffsfs_CheckPath(dirname) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return NULL; |
| } |
| |
| yaffsfs_Lock(); |
| |
| obj = yaffsfs_FindObject(NULL, dirname, 0, 1, NULL, ¬Dir, &loop); |
| |
| if (!obj && notDir) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (loop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!obj) |
| yaffsfs_SetError(-ENOENT); |
| else if (obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) |
| yaffsfs_SetError(-ENOTDIR); |
| else { |
| int i; |
| |
| for (i = 0, dsc = NULL; i < YAFFSFS_N_DSC && !dsc; i++) { |
| if (!yaffsfs_dsc[i].inUse) |
| dsc = &yaffsfs_dsc[i]; |
| } |
| |
| dir = (yaffs_DIR *) dsc; |
| |
| if (dsc) { |
| memset(dsc, 0, sizeof(struct yaffsfs_DirSearchContxt)); |
| dsc->inUse = 1; |
| dsc->dirObj = obj; |
| yaffs_strncpy(dsc->name, dirname, NAME_MAX); |
| INIT_LIST_HEAD(&dsc->others); |
| |
| if (!search_contexts.next) |
| INIT_LIST_HEAD(&search_contexts); |
| |
| list_add(&dsc->others, &search_contexts); |
| yaffsfs_SetDirRewound(dsc); |
| } |
| |
| } |
| |
| yaffsfs_Unlock(); |
| |
| return dir; |
| } |
| |
| struct yaffs_dirent *yaffs_readdir(yaffs_DIR * dirp) |
| { |
| struct yaffsfs_DirSearchContxt *dsc; |
| struct yaffs_dirent *retVal = NULL; |
| |
| dsc = (struct yaffsfs_DirSearchContxt *) dirp; |
| yaffsfs_Lock(); |
| |
| if (dsc && dsc->inUse) { |
| yaffsfs_SetError(0); |
| if (dsc->nextReturn) { |
| dsc->de.d_ino = |
| yaffs_get_equivalent_obj(dsc->nextReturn)->obj_id; |
| dsc->de.d_dont_use = (unsigned)dsc->nextReturn; |
| dsc->de.d_off = dsc->offset++; |
| yaffs_get_obj_name(dsc->nextReturn, |
| dsc->de.d_name, NAME_MAX); |
| if (yaffs_strnlen(dsc->de.d_name, NAME_MAX + 1) == 0) { |
| /* this should not happen! */ |
| yaffs_strcpy(dsc->de.d_name, _Y("zz")); |
| } |
| dsc->de.d_reclen = sizeof(struct yaffs_dirent); |
| retVal = &dsc->de; |
| yaffsfs_DirAdvance(dsc); |
| } else |
| retVal = NULL; |
| } else |
| yaffsfs_SetError(-EBADF); |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| |
| } |
| |
| void yaffs_rewinddir(yaffs_DIR *dirp) |
| { |
| struct yaffsfs_DirSearchContxt *dsc; |
| |
| dsc = (struct yaffsfs_DirSearchContxt *) dirp; |
| |
| yaffsfs_Lock(); |
| |
| yaffsfs_SetDirRewound(dsc); |
| |
| yaffsfs_Unlock(); |
| } |
| |
| int yaffs_closedir(yaffs_DIR *dirp) |
| { |
| struct yaffsfs_DirSearchContxt *dsc; |
| |
| dsc = (struct yaffsfs_DirSearchContxt *) dirp; |
| |
| if (!dsc) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| dsc->inUse = 0; |
| list_del(&dsc->others); /* unhook from list */ |
| yaffsfs_Unlock(); |
| return 0; |
| } |
| |
| /* End of directory stuff */ |
| |
| int yaffs_symlink(const YCHAR *oldpath, const YCHAR *newpath) |
| { |
| struct yaffs_obj *parent = NULL; |
| struct yaffs_obj *obj; |
| YCHAR *name; |
| int retVal = -1; |
| int mode = 0; /* ignore for now */ |
| int notDir = 0; |
| int loop = 0; |
| |
| if (!oldpath || !newpath) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(newpath) < 0 || yaffsfs_CheckPath(oldpath) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| parent = yaffsfs_FindDirectory(NULL, newpath, &name, 0, ¬Dir, &loop); |
| if (!parent && notDir) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (loop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!parent || yaffs_strnlen(name, 5) < 1) |
| yaffsfs_SetError(-ENOENT); |
| else if (yaffsfs_TooManyObjects(parent->my_dev)) |
| yaffsfs_SetError(-ENFILE); |
| else if (parent->my_dev->read_only) |
| yaffsfs_SetError(-EROFS); |
| else { |
| obj = yaffs_create_symlink(parent, name, mode, 0, 0, oldpath); |
| if (obj) |
| retVal = 0; |
| else if (yaffsfs_FindObject |
| (NULL, newpath, 0, 0, NULL, NULL, NULL)) |
| yaffsfs_SetError(-EEXIST); |
| else |
| yaffsfs_SetError(-ENOSPC); |
| } |
| |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| |
| } |
| |
| int yaffs_readlink(const YCHAR *path, YCHAR *buf, int bufsiz) |
| { |
| struct yaffs_obj *obj = NULL; |
| struct yaffs_obj *dir = NULL; |
| int retVal = -1; |
| int notDir = 0; |
| int loop = 0; |
| |
| if (!path || !buf) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| |
| obj = yaffsfs_FindObject(NULL, path, 0, 1, &dir, ¬Dir, &loop); |
| |
| if (!dir && notDir) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (loop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!dir || !obj) |
| yaffsfs_SetError(-ENOENT); |
| else if (obj->variant_type != YAFFS_OBJECT_TYPE_SYMLINK) |
| yaffsfs_SetError(-EINVAL); |
| else { |
| YCHAR *alias = obj->variant.symlink_variant.alias; |
| memset(buf, 0, bufsiz); |
| yaffs_strncpy(buf, alias, bufsiz - 1); |
| retVal = 0; |
| } |
| yaffsfs_Unlock(); |
| return retVal; |
| } |
| |
| int yaffs_link(const YCHAR *oldpath, const YCHAR *linkpath) |
| { |
| /* Creates a link called newpath to existing oldpath */ |
| struct yaffs_obj *obj = NULL; |
| struct yaffs_obj *lnk = NULL; |
| struct yaffs_obj *obj_dir = NULL; |
| struct yaffs_obj *lnk_dir = NULL; |
| int retVal = -1; |
| int notDirObj = 0; |
| int notDirLnk = 0; |
| int objLoop = 0; |
| int lnkLoop = 0; |
| YCHAR *newname; |
| |
| if (!oldpath || !linkpath) { |
| yaffsfs_SetError(-EFAULT); |
| return -1; |
| } |
| |
| if (yaffsfs_CheckPath(linkpath) < 0 || yaffsfs_CheckPath(oldpath) < 0) { |
| yaffsfs_SetError(-ENAMETOOLONG); |
| return -1; |
| } |
| |
| yaffsfs_Lock(); |
| |
| obj = yaffsfs_FindObject(NULL, oldpath, 0, 1, |
| &obj_dir, ¬DirObj, &objLoop); |
| lnk = yaffsfs_FindObject(NULL, linkpath, 0, 0, NULL, NULL, NULL); |
| lnk_dir = yaffsfs_FindDirectory(NULL, linkpath, &newname, |
| 0, ¬DirLnk, &lnkLoop); |
| |
| if ((!obj_dir && notDirObj) || (!lnk_dir && notDirLnk)) |
| yaffsfs_SetError(-ENOTDIR); |
| else if (objLoop || lnkLoop) |
| yaffsfs_SetError(-ELOOP); |
| else if (!obj_dir || !lnk_dir || !obj) |
| yaffsfs_SetError(-ENOENT); |
| else if (obj->my_dev->read_only) |
| yaffsfs_SetError(-EROFS); |
| else if (yaffsfs_TooManyObjects(obj->my_dev)) |
| yaffsfs_SetError(-ENFILE); |
| else if (lnk) |
| yaffsfs_SetError(-EEXIST); |
| else if (lnk_dir->my_dev != obj->my_dev) |
| yaffsfs_SetError(-EXDEV); |
| else { |
| retVal = yaffsfs_CheckNameLength(newname); |
| |
| if (retVal == 0) { |
| lnk = yaffs_link_obj(lnk_dir, newname, obj); |
| if (lnk) |
| retVal = 0; |
| else { |
| yaffsfs_SetError(-ENOSPC); |
| retVal = -1; |
| } |
| } |
| } |
| yaffsfs_Unlock(); |
| |
| return retVal; |
| } |
| |
|