int fatfs_unlink(vnode_t * dir, const char * name) { struct fatfs_sb * ffsb = get_ffsb_of_sb(dir->sb); struct fatfs_inode * indir = get_inode_of_vnode(dir); char * in_fpath; FRESULT err; int retval; if (!S_ISDIR(dir->vn_mode)) return -ENOTDIR; in_fpath = format_fpath(indir, name); if (!in_fpath) return -ENOMEM; /* TODO May need checks if file/dir is opened */ err = f_unlink(&ffsb->ff_fs, in_fpath); if (err) retval = fresult2errno(err); else retval = 0; kfree(in_fpath); return retval; }
int fatfs_open(const char *pathname, int flags) { int ret; int fildes; FRESULT result; fildes = file_alloc(); if (fildes < 0) { errno = ENFILE; ret = -1; } else { result = fatfs_open_file_or_dir(pathname, flags, fildes); if (result == FR_OK) { ret = fildes; } else { errno = fresult2errno(result); file_free(fildes); ret = -1; } } return ret; }
ssize_t fatfs_write(file_t * file, struct uio * uio, size_t count) { void * buf; struct fatfs_inode * in = get_inode_of_vnode(file->vnode); size_t count_out; int err; if (!S_ISREG(file->vnode->vn_mode)) return -EOPNOTSUPP; err = f_lseek(&in->fp, file->seek_pos); if (err) return -EIO; err = uio_get_kaddr(uio, &buf); if (err) return err; err = f_write(&in->fp, buf, count, &count_out); if (err) return fresult2errno(err); file->seek_pos = f_tell(&in->fp); return count_out; }
int fatfs_readdir(vnode_t * dir, struct dirent * d, off_t * off) { struct fatfs_inode * in = get_inode_of_vnode(dir); FILINFO fno; int err; if (!S_ISDIR(dir->vn_mode)) return -ENOTDIR; /* Emulate . and .. */ if (*off == DIRENT_SEEK_START) { f_readdir(&in->dp, NULL); /* Rewind */ strlcpy(d->d_name, ".", sizeof(d->d_name)); d->d_ino = dir->vn_num; d->d_type = DT_DIR; *off = DIRENT_SEEK_START + 1; return 0; } else if (*off == DIRENT_SEEK_START + 1) { strlcpy(d->d_name, "..", sizeof(d->d_name)); /* TODO Set d members to proper values */ d->d_ino = 0; /* TODO ino should be properly set */ d->d_type = DT_DIR; *off = DIRENT_SEEK_START + 2; return 0; } #if configFATFS_LFN fno.lfname = d->d_name; fno.lfsize = NAME_MAX + 1; #endif err = f_readdir(&in->dp, &fno); if (err) return fresult2errno(err); if (fno.fname[0] == '\0') return -ESPIPE; d->d_ino = fno.ino; d->d_type = (fno.fattrib & AM_DIR) ? DT_DIR : DT_REG; #if configFATFS_LFN if (!*fno.lfname) #endif strlcpy(d->d_name, fno.fname, sizeof(d->d_name)); return 0; }
static int fatfs_read (int fd, char *ptr, int len) { int ret; struct fd *pfd; pfd = file_struct_get(fd); if (pfd == NULL) { errno = EBADF; ret = -1; } else if (pfd->opaque == NULL) { errno = EBADF; ret = -1; } else if (S_ISREG(pfd->stat.st_mode)) { FIL *filp; FRESULT result; UINT nbytes_read; filp = pfd->opaque; result = f_read(filp, ptr, len, &nbytes_read); if (result == FR_OK) { ret = nbytes_read; } else { errno = fresult2errno(result); ret = -1; } } else if (S_ISDIR(pfd->stat.st_mode)) { errno = EISDIR; ret = -1; } else { errno = EBADF; ret = -1; } return ret; }
int fatfs_unlink(const char *path) { int ret; FRESULT result; result = f_unlink(path); if (result == FR_OK) { ret = 0; } else { errno = fresult2errno(result); ret = -1; } return ret; }
int fatfs_mknod(vnode_t * dir, const char * name, int mode, void * specinfo, vnode_t ** result) { struct fatfs_inode * indir = get_inode_of_vnode(dir); struct fatfs_inode * res = NULL; char * in_fpath; int err; #ifdef configFATFS_DEBUG KERROR(KERROR_DEBUG, "fatfs_mknod(dir %p, name \"%s\", mode %u, specinfo %p, result %p)\n", dir, name, mode, specinfo, result); #endif if (!S_ISDIR(dir->vn_mode)) return -ENOTDIR; if ((mode & S_IFMT) != S_IFREG) return -ENOTSUP; /* FAT only suports regular files. */ if (specinfo) return -EINVAL; /* specinfo not supported. */ in_fpath = format_fpath(indir, name); if (!in_fpath) return -ENOMEM; err = create_inode(&res, get_ffsb_of_sb(dir->sb), in_fpath, hash32_str(in_fpath, 0), O_CREAT); if (err) { kfree(in_fpath); return fresult2errno(err); } if (result) *result = &res->in_vnode; fatfs_chmod(&res->in_vnode, mode); #ifdef configFATFS_DEBUG KERROR(KERROR_DEBUG, "mkdod() ok\n"); #endif return 0; }
int fatfs_mkdir(vnode_t * dir, const char * name, mode_t mode) { struct fatfs_sb * ffsb = get_ffsb_of_sb(dir->sb); struct fatfs_inode * indir = get_inode_of_vnode(dir); char * in_fpath; FRESULT err; int retval = 0; if (!S_ISDIR(dir->vn_mode)) return -ENOTDIR; in_fpath = format_fpath(indir, name); if (!in_fpath) return -ENOMEM; err = f_mkdir(&ffsb->ff_fs, in_fpath); if (err) retval = fresult2errno(err); kfree(in_fpath); return retval; }
int fatfs_rmdir(vnode_t * dir, const char * name) { int err; vnode_t * result; mode_t mode; FRESULT ferr; /* TODO Should fail if name is a mount point */ if (!S_ISDIR(dir->vn_mode)) return -ENOTDIR; err = fatfs_lookup(dir, name, &result); if (err) return err; mode = result->vn_mode; vrele_nunlink(result); if (!S_ISDIR(mode)) return -ENOTDIR; ferr = fatfs_unlink(dir, name); return fresult2errno(ferr); }
int fatfs_stat(vnode_t * vnode, struct stat * buf) { struct fatfs_sb * ffsb = get_ffsb_of_sb(vnode->sb); struct fatfs_inode * in = get_inode_of_vnode(vnode); FILINFO fno; struct stat mp_stat = { .st_uid = 0, .st_gid = 0 }; size_t blksize = ffsb->ff_fs.ssize; int err; memset(&fno, 0, sizeof(fno)); err = get_mp_stat(vnode, &mp_stat); if (err) { if (err == -EINPROGRESS) { #ifdef configFATFS_DEBUG KERROR(KERROR_WARN, "vnode->sb->mountpoint should be set\n"); #endif } else { #ifdef configFATFS_DEBUG KERROR(KERROR_WARN, "get_mp_stat() returned error (%d)\n", err); #endif return err; } } /* Can't stat FAT root */ if (vnode == vnode->sb->root) { memcpy(buf, &mp_stat, sizeof(struct stat)); return 0; } err = f_stat(&ffsb->ff_fs, in->in_fpath, &fno); if (err) { #ifdef configFATFS_DEBUG KERROR(KERROR_DEBUG, "f_stat(fs %p, fpath \"%s\", fno %p) failed\n", &ffsb->ff_fs, in->in_fpath, &fno); #endif return fresult2errno(err); } buf->st_dev = vnode->sb->vdev_id; buf->st_ino = vnode->vn_num; buf->st_mode = vnode->vn_mode; buf->st_nlink = 1; /* Always one link on FAT. */ buf->st_uid = mp_stat.st_uid; buf->st_gid = mp_stat.st_gid; buf->st_size = fno.fsize; /* TODO Times */ #if 0 buf->st_atim; buf->st_mtim; buf->st_ctim; buf->st_birthtime; #endif buf->st_flags = fattrib2uflags(fno.fattrib); buf->st_blksize = blksize; buf->st_blocks = fno.fsize / blksize + 1; /* Best guess. */ return 0; } int fatfs_chmod(vnode_t * vnode, mode_t mode) { struct fatfs_sb * ffsb = get_ffsb_of_sb(vnode->sb); struct fatfs_inode * in = get_inode_of_vnode(vnode); uint8_t attr = 0; const uint8_t mask = AM_RDO; int err; if (!(mode & (S_IWUSR | S_IWGRP | S_IWOTH))) attr |= AM_RDO; err = fresult2errno(f_chmod(&ffsb->ff_fs, in->in_fpath, attr, mask)); if (!err) vnode->vn_mode = mode; return err; } /* * Note: Thre is practically two ways to set AM_RDO, either * by using chmod() or by this chflags(). */ int fatfs_chflags(vnode_t * vnode, fflags_t flags) { struct fatfs_sb * ffsb = get_ffsb_of_sb(vnode->sb); struct fatfs_inode * in = get_inode_of_vnode(vnode); uint8_t attr = 0; const uint8_t mask = AM_RDO | AM_ARC | AM_SYS | AM_HID; FRESULT fresult; if (flags & UF_SYSTEM) attr |= AM_SYS; if (flags & UF_ARCHIVE) attr |= AM_ARC; if (flags & UF_READONLY) attr |= AM_RDO; if (flags & UF_HIDDEN) attr |= AM_HID; fresult = f_chmod(&ffsb->ff_fs, in->in_fpath, attr, mask); return fresult2errno(fresult); } /** * Initialize fatfs vnode data. * @param vnode is the target vnode to be initialized. */ static void init_fatfs_vnode(vnode_t * vnode, ino_t inum, mode_t mode, long vn_hash, struct fs_superblock * sb) { struct stat stat; #ifdef configFATFS_DEBUG KERROR(KERROR_DEBUG, "init_fatfs_vnode(vnode %p, inum %l, mode %o, vn_hash %u, sb %p)\n", vnode, (uint64_t)inum, mode, (uint32_t)vn_hash, sb); #endif fs_vnode_init(vnode, inum, sb, &fatfs_vnode_ops); vnode->vn_hash = vn_hash; if (S_ISDIR(mode)) mode |= S_IRWXU | S_IXGRP | S_IXOTH; vnode->vn_mode = mode | S_IRUSR | S_IRGRP | S_IROTH; fatfs_stat(vnode, &stat); vnode->vn_len = stat.st_size; if ((stat.st_flags & UF_READONLY) == 0) vnode->vn_mode |= S_IWUSR | S_IWGRP | S_IWOTH; } /** * Get mountpoint stat. */ static int get_mp_stat(vnode_t * vnode, struct stat * st) { struct fs_superblock * sb; vnode_t * mp; #ifdef configFATFS_DEBUG KASSERT(vnode, "Vnode was given"); KASSERT(vnode->sb, "Superblock is set"); #endif sb = vnode->sb; mp = sb->mountpoint; if (!mp) { /* We are probably mounting and mountpoint is not yet set. */ #ifdef configFATFS_DEBUG KERROR(KERROR_DEBUG, "mp not set\n"); #endif return -EINPROGRESS; } #ifdef configFATFS_DEBUG KASSERT(mp->vnode_ops->stat, "stat() is defined"); #endif return mp->vnode_ops->stat(mp, st); } static int fresult2errno(int fresult) { switch (fresult) { case FR_DISK_ERR: case FR_INVALID_OBJECT: case FR_INT_ERR: return -EIO; case FR_NOT_ENABLED: return -ENODEV; case FR_NO_FILESYSTEM: return -ENXIO; case FR_NO_FILE: case FR_NO_PATH: return -ENOENT; case FR_DENIED: return -EACCES; case FR_EXIST: return -EEXIST; case FR_WRITE_PROTECTED: return -EPERM; case FR_NOT_READY: return -EBUSY; case FR_INVALID_NAME: case FR_INVALID_DRIVE: case FR_MKFS_ABORTED: case FR_INVALID_PARAMETER: return -EINVAL; case FR_TIMEOUT: return -EWOULDBLOCK; case FR_NOT_ENOUGH_CORE: return -ENOMEM; case FR_TOO_MANY_OPEN_FILES: return -ENFILE; default: if (fresult != 0) /* Unknown error */ return -EIO; } return 0; }
/** * Create a inode. * @param fpath won't be duplicated. * @param oflags O_CREAT, O_DIRECTORY, O_RDONLY, O_WRONLY and O_RDWR * currently supported. * O_WRONLY/O_RDWR creates in write mode if possible, so this * should be always verified with stat. */ static int create_inode(struct fatfs_inode ** result, struct fatfs_sb * sb, char * fpath, long vn_hash, int oflags) { struct fatfs_inode * in = NULL; FILINFO fno; vnode_t * vn; vnode_t * xvp; mode_t vn_mode; ino_t inum; int err = 0, retval = 0; #ifdef configFATFS_DEBUG KERROR(KERROR_DEBUG, "create_inode(fpath \"%s\", vn_hash %u)\n", fpath, (uint32_t)vn_hash); #endif in = kzalloc(sizeof(struct fatfs_inode)); if (!in) { retval = -ENOMEM; goto fail; } in->in_fpath = fpath; vn = &in->in_vnode; in->open_count = ATOMIC_INIT(0); memset(&fno, 0, sizeof(fno)); if (oflags & O_DIRECTORY) { /* O_DIRECTORY was specified. */ /* TODO Maybe get mp stat? */ fno.fattrib = AM_DIR; } else if (oflags & O_CREAT) { if (sb->sb.mode_flags & MNT_RDONLY) return -EROFS; } else { err = f_stat(&sb->ff_fs, fpath, &fno); if (err) { retval = fresult2errno(err); goto fail; } } /* Try open */ if (fno.fattrib & AM_DIR) { /* it's a directory */ vn_mode = S_IFDIR; err = f_opendir(&in->dp, &sb->ff_fs, in->in_fpath); if (err) { #ifdef configFATFS_DEBUG KERROR(KERROR_DEBUG, "Can't open a dir (err: %d)\n", err); #endif retval = fresult2errno(err); goto fail; } inum = in->dp.ino; } else { /* it's a file */ unsigned char fomode = 0; fomode |= (oflags & O_CREAT) ? FA_OPEN_ALWAYS : FA_OPEN_EXISTING; /* The kernel should always have RW if possible. */ if (sb->sb.mode_flags & MNT_RDONLY) { fomode |= FA_READ; } else { fomode |= FA_READ | FA_WRITE; } vn_mode = S_IFREG; err = f_open(&in->fp, &sb->ff_fs, in->in_fpath, fomode); if (err) { #ifdef configFATFS_DEBUG KERROR(KERROR_DEBUG, "Can't open a file (err: %d)\n", err); #endif retval = fresult2errno(err); goto fail; } inum = in->fp.ino; } #ifdef configFATFS_DEBUG if (oflags & O_CREAT) KERROR(KERROR_DEBUG, "ff: Create & open ok\n"); else KERROR(KERROR_DEBUG, "ff: Open ok\n"); #endif init_fatfs_vnode(vn, inum, vn_mode, vn_hash, &(sb->sb)); /* Insert to the cache */ err = vfs_hash_insert(vn, vn_hash, &xvp, fatfs_vncmp, fpath); if (err) { retval = -ENOMEM; goto fail; } if (xvp) { /* TODO No idea what to do now */ KERROR(KERROR_WARN, "create_inode(): Found it during insert: \"%s\"\n", fpath); } #ifdef configFATFS_DEBUG KERROR(KERROR_DEBUG, "create_inode(): ok\n"); #endif *result = in; vrefset(vn, 1); /* Make ref for the caller. */ return 0; fail: #ifdef configFATFS_DEBUG KERROR(KERROR_DEBUG, "create_inode(): retval %i\n", retval); #endif kfree(in); return retval; }
/** * Mount a new fatfs. * @param mode mount flags. * @param param contains optional mount parameters. * @param parm_len length of param string. * @param[out] sb Returns the superblock of the new mount. * @return error code, -errno. */ static int fatfs_mount(const char * source, uint32_t mode, const char * parm, int parm_len, struct fs_superblock ** sb) { static dev_t fatfs_vdev_minor; struct fatfs_sb * fatfs_sb = NULL; vnode_t * vndev; char pdrv; int err, retval = 0; /* Get device vnode */ err = lookup_vnode(&vndev, curproc->croot, source, 0); if (err) { #ifdef configFATFS_DEBUG KERROR(KERROR_DEBUG, "fatfs source not found\n"); #endif return err; } if (!S_ISBLK(vndev->vn_mode)) return -ENOTBLK; /* Allocate superblock */ fatfs_sb = kzalloc(sizeof(struct fatfs_sb)); if (!fatfs_sb) return -ENOMEM; fs_fildes_set(&fatfs_sb->ff_devfile, vndev, O_RDWR); fatfs_sb->sb.vdev_id = DEV_MMTODEV(VDEV_MJNR_FATFS, fatfs_vdev_minor++); /* Insert sb to fatfs_sb_arr lookup array */ fatfs_sb_arr[DEV_MINOR(fatfs_sb->sb.vdev_id)] = fatfs_sb; /* Mount */ pdrv = (char)DEV_MINOR(fatfs_sb->sb.vdev_id); err = f_mount(&fatfs_sb->ff_fs, 0); if (err) { #ifdef configFATFS_DEBUG KERROR(KERROR_DEBUG, "Can't init a work area for FAT (%d)\n", err); #endif retval = fresult2errno(err); goto fail; } #ifdef configFATFS_DEBUG KERROR(KERROR_DEBUG, "Initialized a work area for FAT\n"); #endif #if (_FS_NOFSINFO == 0) /* Commit full scan of free clusters */ DWORD nclst; f_getfree(&fatfs_sb->ff_fs, &nclst); #endif /* Init super block */ fs_init_superblock(&fatfs_sb->sb, &fatfs_fs); /* TODO Detect if target dev is rdonly */ fatfs_sb->sb.mode_flags = mode; fatfs_sb->sb.root = create_root(fatfs_sb); fatfs_sb->sb.sb_dev = vndev; fatfs_sb->sb.sb_hashseed = fatfs_sb->sb.vdev_id; /* Function pointers to superblock methods */ fatfs_sb->sb.get_vnode = NULL; /* Not implemented for FAT. */ fatfs_sb->sb.delete_vnode = fatfs_delete_vnode; fatfs_sb->sb.umount = NULL; if (!fatfs_sb->sb.root) { KERROR(KERROR_ERR, "Root of fatfs not found\n"); return -EIO; } fs_insert_superblock(&fatfs_fs, &fatfs_sb->sb); fail: if (retval) { fatfs_sb_arr[DEV_MINOR(fatfs_sb->sb.vdev_id)] = NULL; kfree(fatfs_sb); } *sb = &fatfs_sb->sb; return retval; }
off_t fatfs_lseek(int fd, off_t offset, int whence ) { off_t ret; struct fd *pfd; pfd = file_struct_get(fd); if (pfd == NULL) { errno = EBADF; ret = -1; } else if (pfd->opaque == NULL) { errno = EBADF; ret = -1; } else if (S_ISREG(pfd->stat.st_mode)) { FIL *filp; FRESULT result; DWORD pos; filp = pfd->opaque; if (whence == SEEK_CUR) { pos = f_tell(filp); } else if (whence == SEEK_END) { pos = f_size(filp); } else if (whence == SEEK_CUR) { pos = 0; } else { /* TODO: error */ pos = 0; } pos += offset; result = f_lseek(filp, pos); if (result == FR_OK) { ret = pos; } else { errno = fresult2errno(result); ret = -1; } } else { errno = EINVAL; ret = -1; } return ret; }
static int fatfs_close (int fd) { int ret; struct fd *pfd; pfd = file_struct_get(fd); if (pfd == NULL) { errno = EBADF; ret = -1; } else if (pfd->opaque == NULL) { errno = EBADF; ret = -1; } else if (S_ISREG(pfd->stat.st_mode)) { FIL *filp; FRESULT result; filp = pfd->opaque; result = f_close(filp); if (result == FR_OK) { fatfs_fil_free(filp); file_free(fd); ret = 0; } else { errno = fresult2errno(result); ret = -1; } } else if (S_ISDIR(pfd->stat.st_mode)) { DIR *dp; FRESULT result; dp = pfd->opaque; result = f_closedir(&dp->ffdir); if (result == FR_OK) { fatfs_dir_free(dp); file_free(fd); ret = 0; } else { errno = fresult2errno(result); ret = -1; } } else { errno = EBADF; ret = -1; } return ret; }
static int fatfs_write (int fd, char *ptr, int len) { int ret; struct fd *pfd; pfd = file_struct_get(fd); if (pfd == NULL) { errno = EBADF; ret = -1; } else if (pfd->opaque == NULL) { errno = EBADF; ret = -1; } else if (S_ISREG(pfd->stat.st_mode)) { FIL *filp; FRESULT result; UINT written; filp = pfd->opaque; if (pfd->status_flags & O_APPEND) { DWORD size; size = f_size(filp); result = f_lseek(filp, size); } else { result = FR_OK; } if (result == FR_OK) { result = f_write(filp, ptr, len, &written); } if (result == FR_OK) { ret = written; } else { errno = fresult2errno(result); ret = -1; } } else if (S_ISDIR(pfd->stat.st_mode)) { errno = EISDIR; ret = -1; } else { errno = EBADF; ret = -1; } return ret; }