/* * Copyright (c) 1997-2000 University of Utah and the Flux Group. * All rights reserved. * * This file is part of the Flux OSKit. The OSKit is free software, also known * as "open source;" you can redistribute it and/or modify it under the terms * of the GNU General Public License (GPL), version 2, as published by the Free * Software Foundation (FSF). To explore alternate licensing terms, contact * the University of Utah at csl-dist@cs.utah.edu or +1-801-585-3271. * * The OSKit 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 GPL for more details. You should have * received a copy of the GPL along with the OSKit; see the file COPYING. If * not, write to the FSF, 59 Temple Place #330, Boston, MA 02111-1307, USA. */ /* * Implements oskit_file_t, oskit_dir_t, and oskit_openfile_t for using the * underlying BSD filesystem. * * We only implement the parts that make sense. * * XXXXXX: The problem spots are stat and getdirents. */ #include #include #include #include #include #include #include #include "native.h" #include "support.h" /* XXX Cannot include oskit/c/malloc.h because it pulls in too much other stuff. */ void sfree(void *buf, size_t size); void* smalloc(size_t size); #ifndef VERBOSITY #define VERBOSITY 0 #endif #define OSKIT_ALLPERMS (OSKIT_S_ISUID|OSKIT_S_ISGID|OSKIT_S_ISVTX|\ OSKIT_S_IRWXU|OSKIT_S_IRWXG|OSKIT_S_IRWXO) /* * structures to describe a native file or directory. Note how similar * they are. As it should (better) be. */ struct fs_file { oskit_file_t filei; /* COM file interface */ oskit_absio_t absioi; /* COM absolute I/O interface */ oskit_u32_t count; /* reference count */ int fd; /* Native file descriptor */ char *pathname; /* Path of this file */ char *component; /* Component name */ int complen; /* Length of component name */ queue_chain_t chain; /* siblings in contents chain */ /* * Stuff unique to files. Everything above better be the * same in both. * * The file stuff is used for delaying file creation. * See the long comment in the middle of fs_dir_create() * for more information. */ oskit_mode_t mode; /* Mode for creation */ char create; /* Needs an actual disk file created */ }; struct fs_dir { oskit_dir_t diri; /* COM directory interface */ oskit_absio_t absioi; /* COM absolute I/O interface */ oskit_u32_t count; /* reference count */ int fd; /* Native file descriptor */ char *pathname; /* Path of this directory */ char *component; /* Component name */ int complen; /* Length of component name */ queue_chain_t chain; /* siblings in contents chain */ /* * Stuff unique to directories. Everything above better be the * same in both. * * The contents list is a cache of fs_dir/fs_file entries. It does * not necessarily reflect whats actually in the filesystem. */ oskit_dir_t *parent; /* logical parent; null if root dir */ queue_head_t contents; /* files/dirs in this directory */ }; /* * An open file structure. */ struct fs_open { oskit_openfile_t ofilei; /* COM open file interface */ oskit_absio_t absioi; /* COM absolute I/O interface */ oskit_u32_t count; /* reference count */ struct fs_file *fsf; /* Backpointer to fs_file */ int fd; /* Native file descriptor */ }; #define MAXPATHLEN 1024 static struct oskit_file_ops fs_file_ops; static struct oskit_dir_ops fs_dir_ops; static struct oskit_openfile_ops fs_openfile_ops; static struct fs_dir *fs_dir_rootdir; static oskit_error_t fs_dirents_create(oskit_dirent_t *entries, int count, oskit_dirents_t **out_dirents); /* * Create dir/file impls. */ static struct fs_file * fs_file_allocate(char *pathname, const char *component) { struct fs_file *newfsf = malloc(sizeof(*newfsf)); int pathlen, complen; if (newfsf == NULL) return 0; memset(newfsf, 0, sizeof(*newfsf)); complen = strlen(component); pathlen = strlen(pathname); if ((newfsf->pathname = malloc(complen + pathlen + 1)) == NULL) { free(newfsf); return 0; } strcpy(newfsf->pathname, pathname); strcat(newfsf->pathname, component); newfsf->component = &newfsf->pathname[pathlen]; newfsf->complen = complen; newfsf->count = 1; newfsf->filei.ops = &fs_file_ops; newfsf->fd = -1; return newfsf; } static struct fs_dir * fs_dir_allocate(char *pathname, const char *component, oskit_dir_t *parent) { struct fs_dir *newfsd = malloc(sizeof(*newfsd)); int pathlen, complen; if (newfsd == NULL) return 0; memset(newfsd, 0, sizeof(*newfsd)); complen = strlen(component); pathlen = strlen(pathname); if ((newfsd->pathname = malloc(complen + pathlen + 2)) == NULL) { free(newfsd); return 0; } strcpy(newfsd->pathname, pathname); strcat(newfsd->pathname, component); strcat(newfsd->pathname, "/"); newfsd->component = &newfsd->pathname[pathlen]; newfsd->complen = complen; newfsd->count = 1; newfsd->diri.ops = &fs_dir_ops; newfsd->fd = -1; queue_init(&newfsd->contents); if (parent) { newfsd->parent = parent; oskit_dir_addref(parent); } return newfsd; } static struct fs_open * fs_openfile_allocate(struct fs_file *fsf, int fd) { struct fs_open *ofile = malloc(sizeof(*ofile)); if (ofile == NULL) return 0; memset(ofile, 0, sizeof(*ofile)); ofile->count = 1; ofile->ofilei.ops = &fs_openfile_ops; ofile->fd = fd; ofile->fsf = fsf; oskit_file_addref(&fsf->filei); return ofile; } /* * Methods. */ static OSKIT_COMDECL fs_dir_query(oskit_dir_t *d, const oskit_iid_t *iid, void **out_ihandle) { struct fs_dir *fsd = (struct fs_dir *) d; assert(fsd->count); if (memcmp(iid, &oskit_iunknown_iid, sizeof(*iid)) == 0 || memcmp(iid, &oskit_posixio_iid, sizeof(*iid)) == 0 || memcmp(iid, &oskit_file_iid, sizeof(*iid)) == 0 || memcmp(iid, &oskit_dir_iid, sizeof(*iid)) == 0) { *out_ihandle = fsd; ++fsd->count; return 0; } *out_ihandle = NULL; return OSKIT_E_NOINTERFACE; } static OSKIT_COMDECL fs_file_query(oskit_file_t *f, const oskit_iid_t *iid, void **out_ihandle) { struct fs_file *fsf = (struct fs_file *) f; assert(fsf->count); if (memcmp(iid, &oskit_iunknown_iid, sizeof(*iid)) == 0 || memcmp(iid, &oskit_posixio_iid, sizeof(*iid)) == 0 || memcmp(iid, &oskit_file_iid, sizeof(*iid)) == 0) { *out_ihandle = fsf; ++fsf->count; return 0; } if (memcmp(iid, &oskit_absio_iid, sizeof(*iid)) == 0) { *out_ihandle = &fsf->absioi; ++fsf->count; return 0; } *out_ihandle = NULL; return OSKIT_E_NOINTERFACE; } static OSKIT_COMDECL_U fs_file_addref(oskit_file_t *f) { struct fs_file *fsf = (struct fs_file *) f; assert(fsf->count); return ++fsf->count; } static OSKIT_COMDECL_U fs_file_release(oskit_file_t *f) { struct fs_file *fsf = (struct fs_file *) f; assert(fsf->count); if (--fsf->count) return fsf->count; if (fsf->fd >= 0) NATIVEOS(close)(fsf->fd); /* * If the create flag is set, this was a delayed directory entry * creation. Go ahead and create the file on disk now, with the * mode that was specified when fs_dir_create() was called. */ if (fsf->create) { int fd, flags = O_WRONLY|O_CREAT|O_EXCL; if ((fd = NATIVEOS(open)(fsf->pathname, flags, fsf->mode)) < 0) panic("fs_file_release: Error opening %s: %s!", fsf->pathname, strerror(native_to_oskit_error(NATIVEOS(errno)))); NATIVEOS(close)(fd); } free(fsf->pathname); free(fsf); return 0; } static OSKIT_COMDECL_U fs_dir_release(oskit_dir_t *d) { struct fs_dir *fsd = (struct fs_dir *) d; assert(fsd->count); if (--fsd->count) return fsd->count; if (fsd->parent) oskit_dir_release(fsd->parent); free(fsd->pathname); if (fsd->fd >= 0) NATIVEOS(close)(fsd->fd); free(fsd); return 0; } /*** Operations inherited from oskit_posixio_t ***/ static OSKIT_COMDECL fs_file_stat(oskit_file_t *f, struct oskit_stat *out_stats) { struct fs_file *fsf = (struct fs_file *) f; struct stat sb; #if VERBOSITY > 1 printf(__FUNCTION__": asked to stat `%s'\n", fsf->pathname); #endif if (NATIVEOS(lstat)(fsf->pathname, &sb) < 0) return native_to_oskit_error(NATIVEOS(errno)); #if VERBOSITY > 1 printf(__FUNCTION__": stat completed `%s'\n", fsf->pathname); #endif out_stats->dev = sb.st_dev; out_stats->ino = sb.st_ino; out_stats->mode = sb.st_mode; out_stats->nlink = sb.st_nlink; out_stats->uid = sb.st_uid; out_stats->gid = sb.st_gid; out_stats->rdev = sb.st_rdev; out_stats->atime.tv_sec = STATATIMESEC(sb); out_stats->atime.tv_nsec = STATATIMENSEC(sb); out_stats->mtime.tv_sec = STATMTIMESEC(sb); out_stats->mtime.tv_nsec = STATMTIMENSEC(sb); out_stats->ctime.tv_sec = STATCTIMESEC(sb); out_stats->ctime.tv_nsec = STATCTIMENSEC(sb); out_stats->size = sb.st_size; out_stats->blocks = sb.st_blocks; out_stats->blksize = sb.st_blksize; return 0; } static OSKIT_COMDECL fs_file_setstat(oskit_file_t *f, oskit_u32_t mask, const struct oskit_stat *stats) { struct fs_file *fsf = (struct fs_file *) f; struct stat sb; #if VERBOSITY > 1 printf(__FUNCTION__": asked to setstat `%s'\n", fsf->pathname); #endif if (NATIVEOS(lstat)(fsf->pathname, &sb) < 0) return native_to_oskit_error(NATIVEOS(errno)); /* * These operations cannot be applied to a symbolic link */ if ((sb.st_mode & OSKIT_S_IFLNK) == OSKIT_S_IFLNK) return OSKIT_E_NOTIMPL; /* * Okay, dispatch on how he caller wants to change the stat structure. * Only allow a few things right now. */ if (mask & ~(OSKIT_STAT_MODE|OSKIT_STAT_UID| OSKIT_STAT_SIZE|OSKIT_STAT_GID| OSKIT_STAT_ATIME|OSKIT_STAT_MTIME)) return OSKIT_E_INVALIDARG; if (mask & OSKIT_STAT_MODE) { if (NATIVEOS(chmod)(fsf->pathname, /* 07777 are portable */ stats->mode & OSKIT_ALLPERMS) < 0) return native_to_oskit_error(NATIVEOS(errno)); } if (mask & OSKIT_STAT_UID) { if (NATIVEOS(chown)(fsf->pathname, stats->uid, -1) < 0) return native_to_oskit_error(NATIVEOS(errno)); } if (mask & OSKIT_STAT_GID) { if (NATIVEOS(chown)(fsf->pathname, -1, stats->gid) < 0) return native_to_oskit_error(NATIVEOS(errno)); } if (mask & OSKIT_STAT_SIZE) { if (NATIVEOS(truncate)(fsf->pathname, stats->size) < 0) return native_to_oskit_error(NATIVEOS(errno)); } if (mask & (OSKIT_STAT_ATIME|OSKIT_STAT_MTIME)) { struct timeval timevals[2]; if (mask & OSKIT_STAT_ATIME) { TIMESPEC_TO_TIMEVAL(&timevals[0], &stats->atime); } else { NATIVEOS(gettimeofday)(&timevals[0], NULL); } if (mask & OSKIT_STAT_MTIME) { TIMESPEC_TO_TIMEVAL(&timevals[1], &stats->mtime); } else { NATIVEOS(gettimeofday)(&timevals[1], NULL); } if (NATIVEOS(utimes)(fsf->pathname, timevals) < 0) return native_to_oskit_error(NATIVEOS(errno)); } return 0; } static OSKIT_COMDECL fs_file_pathconf(oskit_file_t *f, oskit_s32_t option, oskit_s32_t *out_val) { return OSKIT_E_NOTIMPL; } static OSKIT_COMDECL fs_file_sync(oskit_file_t *f, oskit_bool_t wait) { struct fs_file *fsf = (struct fs_file *) f; assert(fsf->fd); if (NATIVEOS(fsync)(fsf->fd) < 0) return native_to_oskit_error(NATIVEOS(errno)); return 0; } static OSKIT_COMDECL fs_file_access(oskit_file_t *f, oskit_amode_t mask) { struct fs_file *fsf = (struct fs_file *) f; #if VERBOSITY > 1 printf(__FUNCTION__": asked to access on `%s'\n", fsf->pathname); #endif if (NATIVEOS(access)(fsf->pathname, mask) < 0) return native_to_oskit_error(NATIVEOS(errno)); return 0; } static OSKIT_COMDECL fs_file_readlink(oskit_file_t *f, char *buf, oskit_u32_t len, oskit_u32_t *out_actual) { struct fs_file *fsf = (struct fs_file *) f; int count; if ((count = NATIVEOS(readlink)(fsf->pathname, buf, len)) < 0) return native_to_oskit_error(NATIVEOS(errno)); *out_actual = count; return 0; } static OSKIT_COMDECL fs_file_open(oskit_file_t *f, oskit_oflags_t iflags, struct oskit_openfile **out_openfile) { struct fs_file *fsf = (struct fs_file *) f; struct fs_open *fso; int mode = 0, flags = 0, fd; #if VERBOSITY > 1 printf(__FUNCTION__": asked to open `%s'\n", fsf->pathname); #endif flags = 0; if ((iflags & OSKIT_O_ACCMODE) == OSKIT_O_RDONLY) flags = O_RDONLY; if ((iflags & OSKIT_O_ACCMODE) == OSKIT_O_WRONLY) flags = O_WRONLY; if ((iflags & OSKIT_O_ACCMODE) == OSKIT_O_RDWR) flags = O_RDWR; if (iflags & OSKIT_O_TRUNC) flags |= O_TRUNC; if (iflags & OSKIT_O_APPEND) flags |= O_APPEND; if (iflags & OSKIT_O_CREAT) flags |= O_CREAT; /* * If the create flag is set, this was a delayed directory entry * creation. Use the mode bits that were specified when * fs_dir_create() was called so that file is created with the * proper bits. Add in O_EXCL too just to be safe. */ if (fsf->create) { flags |= O_CREAT|O_EXCL; mode = fsf->mode; fsf->create = 0; } if ((fd = NATIVEOS(open)(fsf->pathname, flags, mode)) < 0) return native_to_oskit_error(NATIVEOS(errno)); /* * Open succeeded, so create an openfile for it. */ if ((fso = fs_openfile_allocate(fsf, fd)) == NULL) { NATIVEOS(close)(fd); return OSKIT_ENOMEM; } *out_openfile = &fso->ofilei; return 0; } static OSKIT_COMDECL fs_dir_open(oskit_dir_t *d, oskit_oflags_t iflags, struct oskit_openfile **out_opendir) { struct fs_dir *fsd = (struct fs_dir *) d; struct fs_open *fso; int flags, fd; flags = 0; if ((iflags & OSKIT_O_ACCMODE) == OSKIT_O_RDONLY) flags = O_RDONLY; if ((iflags & OSKIT_O_ACCMODE) == OSKIT_O_WRONLY) flags = O_WRONLY; if ((iflags & OSKIT_O_ACCMODE) == OSKIT_O_RDWR) flags = O_RDWR; if (iflags & OSKIT_O_TRUNC) flags |= O_TRUNC; if (iflags & OSKIT_O_APPEND) flags |= O_APPEND; if (iflags & OSKIT_O_CREAT) flags |= O_CREAT; if ((fd = NATIVEOS(open)(fsd->pathname, flags, 0)) < 0) return native_to_oskit_error(NATIVEOS(errno)); /* * Open succeeded, so create an openfile for it. */ if ((fso = fs_openfile_allocate((struct fs_file *)fsd, fd)) == NULL) { NATIVEOS(close)(fd); return OSKIT_ENOMEM; } *out_opendir = &fso->ofilei; return 0; } /* * Lookup a file/dir in the contents list for a directory. */ static oskit_file_t * fs_lookup(struct fs_dir *fsd, const char *name) { oskit_size_t len = strlen(name); struct fs_file *fsf; if (len == 0 || (len == 1 && name[0] == '.')) return (oskit_file_t *) &fsd->diri; if (len == 2 && name[0] == '.' && name[1] == '.') return (oskit_file_t *) (fsd->parent ?: &fsd->diri); queue_iterate(&fsd->contents, fsf, struct fs_file *, chain) { if (fsf->complen == len && memcmp(name, fsf->component, len) == 0) return &fsf->filei; } return NULL; } /* * Link F into DIR's contents chain. */ static inline void fs_contents_link(struct fs_dir *fsd, struct fs_dir *f) { queue_enter_first(&fsd->contents, f, struct fs_dir *, chain); } /* * Remove F from its containing directory's contents chain. */ static inline void fs_contents_unlink(struct fs_dir *fsd, struct fs_dir *f) { queue_remove(&fsd->contents, f, struct fs_dir *, chain); } static OSKIT_COMDECL fs_dir_lookup(oskit_dir_t *d, const char *name, oskit_file_t **out_file) { struct fs_dir *fsd = (struct fs_dir *) d; struct fs_file *fsf; oskit_file_t *file; struct stat sb; char fname[MAXPATHLEN]; #if VERBOSITY > 1 printf(__FUNCTION__": asked to look up `%s' in `%s'\n", name, fsd->pathname); #endif file = fs_lookup(fsd, name); if (file) { #if VERBOSITY > 1 struct fs_file *fsf = (struct fs_file *) file; printf(__FUNCTION__": Found it `%s'\n", fsf->pathname); #endif oskit_file_addref(file); *out_file = file; return 0; } /* * Okay, now look on the disk to see if exists. Use stat since we * need to know if the component is a file or directory. */ strcpy(fname, fsd->pathname); strcat(fname, name); if (NATIVEOS(lstat)(fname, &sb) < 0) return native_to_oskit_error(NATIVEOS(errno)); #if VERBOSITY > 1 printf(__FUNCTION__": On disk: `%s'\n", fname); #endif /* * It does. Create a fs_file or fs_dir entry and link it in. */ if (sb.st_mode & OSKIT_S_IFDIR) fsf = (struct fs_file *) fs_dir_allocate(fsd->pathname, name, d); else fsf = fs_file_allocate(fsd->pathname, name); if (fsf == NULL) return OSKIT_ENOMEM; fs_contents_link(fsd, (struct fs_dir *) fsf); /* * The directory holds one ref for the file object, * and we add another for the pointer going out. */ ++fsf->count; *out_file = &fsf->filei; return 0; } static OSKIT_COMDECL fs_dir_create(oskit_dir_t *d, const char *name, oskit_bool_t excl, oskit_mode_t mode, oskit_file_t **out_file) { struct fs_dir *fsd = (struct fs_dir *) d; oskit_file_t *file; oskit_error_t rc; #if VERBOSITY > 1 printf(__FUNCTION__": asked to create `%s'\n", name); #endif file = fs_lookup(fsd, name); if (!file) { struct fs_file *fsf; fsf = fs_file_allocate(fsd->pathname, name); if (!fsf) { rc = OSKIT_ENOMEM; goto done; } fs_contents_link(fsd, (struct fs_dir *) fsf); /* * See if the file already exists on disk. With excl set * it must not exist. */ if (NATIVEOS(access)(fsf->pathname, 0) == 0) { /* * It exists. We created a directory entry, but * that is okay. Return error. */ if (excl) { rc = OSKIT_EEXIST; goto done; } /* * Otherwise, return a handle to the directory entry. * * The directory holds one ref for the file object, * and we add another for the pointer going out. */ ++fsf->count; *out_file = &fsf->filei; rc = 0; goto done; } /* * The file does not exist. We could create it, but that * might result in a situation in which a file is created * with a mode that does not allow it to be opened (easily) * later. This is exactly the situation when called from * libc open(). The entry is first created with a mode, and * then opened with a set of flags. If the mode is rx, but * the flags say writeable, we run into a minor problem. * The easiest thing to do is just delay until until either a file_open is called, or the file is released. * Semantically incorrect, but this *is* supposed to be * a simple emulation. Even worse, all of the file operation * calls should check the flag too (ie: create, followed by * stat), and create the file. But, the only existing use of * oskit_dir_create is libc open, so lets not bother. */ fsf->create = 1; fsf->mode = mode; /* * The directory holds one ref for the file object, * and we add another for the pointer going out. */ ++fsf->count; *out_file = &fsf->filei; rc = 0; } else if (excl) rc = OSKIT_EEXIST; else { oskit_file_addref(file); *out_file = file; rc = 0; } done: return rc; } static OSKIT_COMDECL fs_dir_link(oskit_dir_t *d, const char *name, oskit_file_t *file) { struct fs_dir *fsd = (struct fs_dir *) d; struct fs_file *fsf = (struct fs_file *) file; struct fs_file *newfsf; /* * Target must not exist. */ if ((newfsf = (struct fs_file *) fs_lookup(fsd, name)) != NULL) return OSKIT_EEXIST; if ((newfsf = fs_file_allocate(fsd->pathname, name)) == NULL) return OSKIT_ENOMEM; #if VERBOSITY > 1 printf(__FUNCTION__": asked to link `%s' to `%s'\n", newfsf->pathname, fsf->pathname); #endif /* * Now try making the actual link. */ if (NATIVEOS(link)(fsf->pathname, newfsf->pathname) < 0) { oskit_file_release(&newfsf->filei); return native_to_oskit_error(NATIVEOS(errno)); } /* * Success, so link it into the directory. */ fs_contents_link(fsd, (struct fs_dir *) newfsf); return 0; } static OSKIT_COMDECL fs_dir_unlink(oskit_dir_t *d, const char *name) { struct fs_dir *fsd = (struct fs_dir *) d; struct fs_file *fsf; char fname[MAXPATHLEN]; fsf = (struct fs_file *) fs_lookup(fsd, name); /* * Not finding a file entry does not mean the file does not * exist! Only that we don't have a handle cached. */ if (fsf) { if (fsf->filei.ops == (void *)&fs_dir_ops) return OSKIT_EISDIR; fs_contents_unlink(fsd, (struct fs_dir *) fsf); fs_file_release((oskit_file_t *) fsf); } /* * Now do the disk unlink and let it return error if the file * does not exist. This is slightly inconsistent, but okay. */ strcpy(fname, fsd->pathname); strcat(fname, name); if (NATIVEOS(unlink)(fname) < 0) return native_to_oskit_error(NATIVEOS(errno)); return 0; } static OSKIT_COMDECL fs_dir_rename(oskit_dir_t *old_dir, const char *old_name, oskit_dir_t *new_dir, const char *new_name) { struct fs_dir *old_fsd = (struct fs_dir *) old_dir; struct fs_dir *new_fsd = (struct fs_dir *) new_dir; struct fs_file *new_fsf; /* may be an fs_dir */ struct fs_file *old_fsf; /* may be an fs_dir */ char old_fname[MAXPATHLEN]; char new_fname[MAXPATHLEN]; int old_fsf_is_dir; old_fsf_is_dir = -1; strcpy(old_fname, old_fsd->pathname); strcat(old_fname, old_name); old_fsf = (struct fs_file *) fs_lookup(old_fsd, old_name); if (!old_fsf) { struct stat sb; int rc; /* * Not finding the file does not mean that the file * does not exist! Only that we don't have a handle cached. */ rc = NATIVEOS(lstat)(old_fname, &sb); if (rc) /* doesn't matter what actually went wrong... */ return OSKIT_ENOENT; old_fsf_is_dir = (sb.st_mode & OSKIT_S_IFDIR); } else { old_fsf_is_dir = (old_fsf->filei.ops == (void*)&fs_dir_ops); } new_fsf = (struct fs_file *) fs_lookup(new_fsd, new_name); /* * Release our handle on it before doing the actual disk * operation. */ if (new_fsf) { fs_contents_unlink(new_fsd, (struct fs_dir *) new_fsf); /* new_fsf may be a file or a directory, so free it */ if (new_fsf->filei.ops == (void*)&fs_dir_ops) fs_dir_release((oskit_dir_t *) new_fsf); else fs_file_release((oskit_file_t *) new_fsf); } /* * Now do the disk rename and let it return any errors. * This is slightly inconsistent, but okay. */ strcpy(new_fname, new_fsd->pathname); strcat(new_fname, new_name); if (NATIVEOS(rename)(old_fname, new_fname) < 0) return native_to_oskit_error(NATIVEOS(errno)); /* * Operation success. Release the source file now that disk has * really been changed, if we had a handle, that is. */ if (old_fsf) { fs_contents_unlink(old_fsd, (struct fs_dir *) old_fsf); if (old_fsf_is_dir) fs_dir_release((oskit_dir_t *) old_fsf); else fs_file_release((oskit_file_t *) old_fsf); } return 0; } static OSKIT_COMDECL fs_dir_mkdir(oskit_dir_t *d, const char *name, oskit_mode_t mode) { struct fs_dir *fsd = (struct fs_dir *) d; struct fs_dir *newfsd; oskit_file_t *file; char fname[MAXPATHLEN]; #if VERBOSITY > 1 printf(__FUNCTION__": asked to mkdir `%s'\n", name); #endif if ((file = fs_lookup(fsd, name)) != NULL) return OSKIT_EEXIST; /* * First see if we can actually make the directory on disk. */ strcpy(fname, fsd->pathname); strcat(fname, name); if (NATIVEOS(mkdir)(fname, mode) < 0) return native_to_oskit_error(NATIVEOS(errno)); /* * Yep, so now make the fs_dir structure and link it in. */ if ((newfsd = fs_dir_allocate(fsd->pathname, name, d)) == NULL) return OSKIT_ENOMEM; fs_contents_link(fsd, newfsd); return 0; } static OSKIT_COMDECL fs_dir_rmdir(oskit_dir_t *d, const char *name) { struct fs_dir *fsd = (struct fs_dir *) d; struct fs_dir *remfsd; char fname[MAXPATHLEN]; #if VERBOSITY > 1 printf(__FUNCTION__": asked to rmdir `%s'\n", name); #endif remfsd = (struct fs_dir *) fs_lookup(fsd, name); /* * Not finding a dir entry does not mean the directory does not * exist! Only that we don't have a handle cached. */ if (remfsd) { if (remfsd->diri.ops != (void *)&fs_dir_ops) return OSKIT_ENOTDIR; if (! queue_empty(&remfsd->contents)) return OSKIT_ENOTEMPTY; fs_contents_unlink(fsd, remfsd); fs_dir_release((oskit_dir_t *) remfsd); } /* * Now do the actual rmdir and let it return error. */ strcpy(fname, fsd->pathname); strcat(fname, name); if (NATIVEOS(rmdir)(fname) < 0) return native_to_oskit_error(NATIVEOS(errno)); return 0; } /* oskit/fs/dir.h says: * * Read one or more entries in this directory. * The offset of the entry to be read is specified in 'inout_ofs'; * the caller must initially supply zero as the offset, * and during each successful call the offset increases * by an unspecified amount dependent on the file system. * The file system will return at least 'nentries' * if there are at least this many remaining in the directory; * however, the file system may return more entries. * * The caller must free the dirents array and the names * using the task allocator when they are no longer needed. */ /* * Since nentries doesn't mean much, we'll just return everything * we have. */ static OSKIT_COMDECL fs_dir_getdirentries(oskit_dir_t *d, oskit_u32_t *inout_ofs, oskit_u32_t nentries, struct oskit_dirents **out_dirents) { struct fs_dir *fsd = (struct fs_dir *) d; char buf[0x1000]; #ifdef __linux__ off_t bsize; #else long bsize; #endif int count, doff, dcnt, i; struct oskit_dirent *dirents; oskit_size_t dirents_size; struct dirent *dent; oskit_error_t rc; off_t offset; /* * This is implemented using the underlying filesystem only. * We don't normally open directories, so check for that and * for a directory rewind. Special flag is -1, which says that * the last call returned all there was to read. */ if (fsd->fd < 0) { if ((fsd->fd = NATIVEOS(open)(fsd->pathname, O_RDONLY, 0)) < 0) return native_to_oskit_error(NATIVEOS(errno)); } /* * basep is not read by getdirents. At the end of the call, * it is set to the file position of the first entry. Always * reset file pos, in case the file was closed or seeked. * * This behavior is documented in the freebsd manpage. The * linux manpage says something else, but acts like BSD. */ NATIVEOS(lseek)(fsd->fd, *inout_ofs, SEEK_SET); /* * Ask for a fixed size block of entries, and then convert that * into Oskit dirent structures. */ bsize = *inout_ofs; if ((count = NATIVEOS(getdirentries)(fsd->fd, buf, sizeof buf, &bsize)) < 0) return native_to_oskit_error(NATIVEOS(errno)); /* * No entries at all, so close it down. */ if (count == 0) { NATIVEOS(close)(fsd->fd); fsd->fd = -1; *out_dirents = NULL; return 0; } offset = NATIVEOS(lseek)(fsd->fd, 0, SEEK_CUR); if (offset < 0) return native_to_oskit_error(NATIVEOS(errno)); *inout_ofs = offset; /* * Count them up. */ dent = (struct dirent *) buf; dirents_size = 0; doff = 0; dcnt = 0; while (doff < count) { /* XXX kludge that is always too big, but well-aligned */ dirents_size += offsetof(oskit_dirent_t, name[dent->d_reclen]); doff += dent->d_reclen; dent = (struct dirent *) &buf[doff]; ++dcnt; } dirents = malloc(dirents_size); if (!dirents) { *out_dirents = NULL; return OSKIT_ENOMEM; } /* * Create the dirents COM object first to make sure we can. */ if ((rc = fs_dirents_create(dirents, dcnt, out_dirents))) { free(dirents); *out_dirents = NULL; return rc; } dent = (struct dirent *) buf; for (i = 0, doff = 0; i < dcnt; i++) { /* copy filename in */ dirents->namelen = DIRENTNAMLEN(dent); dirents->ino = DIRENTINO(dent); memcpy(dirents->name, dent->d_name, DIRENTNAMLEN(dent) + 1); dirents = (void *)(&dirents->name[dent->d_reclen]); doff += dent->d_reclen; dent = (struct dirent *) &buf[doff]; } return 0; } static OSKIT_COMDECL fs_dir_mknod(oskit_dir_t *d, const char *name, oskit_mode_t mode, oskit_dev_t dev) { return OSKIT_E_NOTIMPL; } static OSKIT_COMDECL fs_dir_symlink(oskit_dir_t *d, const char *link_name, const char *dest_name) { struct fs_dir *fsd = (struct fs_dir *) d; char fname[MAXPATHLEN]; #if VERBOSITY > 1 printf(__FUNCTION__": asked to create symlink `%s' -> `%s'\n", link_name, dest_name); #endif /* * Lets just create the symlink and not worry about caching * a handle on it. */ strcpy(fname, fsd->pathname); strcat(fname, link_name); if (NATIVEOS(symlink(dest_name, fname)) < 0) return native_to_oskit_error(NATIVEOS(errno)); return 0; } static OSKIT_COMDECL fs_dir_reparent(oskit_dir_t *d, oskit_dir_t *parent, oskit_dir_t **out_dir) { return OSKIT_E_NOTIMPL; } static OSKIT_COMDECL fs_file_getfs(oskit_file_t *f, struct oskit_filesystem **out_fs) { return OSKIT_E_NOTIMPL; /* XXX implement */ } static struct oskit_file_ops fs_file_ops = { fs_file_query, fs_file_addref, fs_file_release, fs_file_stat, fs_file_setstat, fs_file_pathconf, fs_file_sync, fs_file_sync, fs_file_access, fs_file_readlink, fs_file_open, fs_file_getfs }; static struct oskit_dir_ops fs_dir_ops = { fs_dir_query, fs_file_addref, fs_dir_release, fs_file_stat, fs_file_setstat, fs_file_pathconf, fs_file_sync, fs_file_sync, fs_file_access, fs_file_readlink, fs_dir_open, fs_file_getfs, fs_dir_lookup, fs_dir_create, fs_dir_link, fs_dir_unlink, fs_dir_rename, fs_dir_mkdir, fs_dir_rmdir, fs_dir_getdirentries, fs_dir_mknod, fs_dir_symlink, fs_dir_reparent }; /* * oskit_openfile methods */ static OSKIT_COMDECL fs_ofile_query(oskit_openfile_t *f, const struct oskit_guid *iid, void **out_ihandle) { struct fs_open *ofile = (struct fs_open *) f; assert(ofile); assert(ofile->count != 0); if (memcmp(iid, &oskit_iunknown_iid, sizeof(*iid)) == 0 || memcmp(iid, &oskit_stream_iid, sizeof(*iid)) == 0 || memcmp(iid, &oskit_openfile_iid, sizeof(*iid)) == 0) { *out_ihandle = &ofile->ofilei; ++ofile->count; return 0; } *out_ihandle = NULL; return OSKIT_E_NOINTERFACE; } static OSKIT_COMDECL_U fs_ofile_addref(oskit_openfile_t *f) { struct fs_open *ofile = (struct fs_open *) f; assert(ofile); assert(ofile->count != 0); return ++ofile->count; } static OSKIT_COMDECL_U fs_ofile_release(oskit_openfile_t *f) { struct fs_open *ofile = (struct fs_open *) f; unsigned newcount; assert(ofile); assert(ofile->count != 0); newcount = --ofile->count; if (newcount == 0) { oskit_file_release(&ofile->fsf->filei); NATIVEOS(close)(ofile->fd); free(ofile); } return newcount; } static OSKIT_COMDECL fs_ofile_read(oskit_openfile_t *f, void *buf, oskit_u32_t len, oskit_u32_t *out_actual) { struct fs_open *ofile = (struct fs_open *) f; int count; assert(ofile); assert(ofile->count != 0); if ((count = NATIVEOS(read)(ofile->fd, buf, len)) < 0) return native_to_oskit_error(NATIVEOS(errno)); *out_actual = count; return 0; } static OSKIT_COMDECL fs_ofile_write(oskit_openfile_t *f, const void *buf, oskit_u32_t len, oskit_u32_t *out_actual) { struct fs_open *ofile = (struct fs_open *) f; int count; assert(ofile); assert(ofile->count != 0); if ((count = NATIVEOS(write)(ofile->fd, buf, len)) < 0) return native_to_oskit_error(NATIVEOS(errno)); *out_actual = count; return 0; } static OSKIT_COMDECL fs_ofile_seek(oskit_openfile_t *f, oskit_s64_t ofs, oskit_seek_t whence, oskit_u64_t *out_newpos) { struct fs_open *ofile = (struct fs_open *) f; oskit_u64_t newpos; assert(ofile); assert(ofile->count != 0); if ((newpos = NATIVEOS(lseek)(ofile->fd, ofs, whence)) < 0) return native_to_oskit_error(NATIVEOS(errno)); *out_newpos = newpos; return 0; } static OSKIT_COMDECL fs_ofile_setsize(oskit_openfile_t *f, oskit_u64_t new_size) { struct fs_open *ofile = (struct fs_open *) f; oskit_u64_t oldpos; assert(ofile); assert(ofile->count != 0); if ((oldpos = NATIVEOS(lseek)(ofile->fd, new_size, SEEK_SET)) < 0) return native_to_oskit_error(NATIVEOS(errno)); if (NATIVEOS(lseek)(ofile->fd, oldpos, SEEK_SET) < 0) return native_to_oskit_error(NATIVEOS(errno)); return 0; } static OSKIT_COMDECL fs_ofile_copy_to(oskit_openfile_t *f, oskit_stream_t *dst, oskit_u64_t size, oskit_u64_t *out_read, oskit_u64_t *out_written) { return OSKIT_E_NOTIMPL; } static OSKIT_COMDECL fs_ofile_commit(oskit_openfile_t *f, oskit_u32_t commit_flags) { return OSKIT_E_NOTIMPL; } static OSKIT_COMDECL fs_ofile_revert(oskit_openfile_t *f) { return OSKIT_E_NOTIMPL; } static OSKIT_COMDECL fs_ofile_lock_region(oskit_openfile_t *f, oskit_u64_t offset, oskit_u64_t size, oskit_u32_t lock_type) { return OSKIT_E_NOTIMPL; } static OSKIT_COMDECL fs_ofile_unlock_region(oskit_openfile_t *f, oskit_u64_t offset, oskit_u64_t size, oskit_u32_t lock_type) { return OSKIT_E_NOTIMPL; } static OSKIT_COMDECL fs_ofile_stat(oskit_openfile_t *f, oskit_stream_stat_t *out_stat, oskit_u32_t stat_flags) { return OSKIT_E_NOTIMPL; } static OSKIT_COMDECL fs_ofile_clone(oskit_openfile_t *f, oskit_openfile_t **out_stream) { return OSKIT_E_NOTIMPL; } static OSKIT_COMDECL fs_ofile_getfile(oskit_openfile_t *f, struct oskit_file **out_file) { struct fs_open *ofile = (struct fs_open *) f; assert(ofile); assert(ofile->count != 0); oskit_file_addref(&ofile->fsf->filei); *out_file = &ofile->fsf->filei; return 0; } static struct oskit_openfile_ops fs_openfile_ops = { fs_ofile_query, fs_ofile_addref, fs_ofile_release, fs_ofile_read, fs_ofile_write, fs_ofile_seek, fs_ofile_setsize, fs_ofile_copy_to, fs_ofile_commit, fs_ofile_revert, fs_ofile_lock_region, fs_ofile_unlock_region, fs_ofile_stat, fs_ofile_clone, fs_ofile_getfile }; /* * Create a directory, rooted at the given pathname. */ oskit_dir_t * oskit_unix_fs_init(char *rootname) { struct fs_dir *newfsd = malloc(sizeof(*newfsd)); int pathlen; /* * Make sure the path exists. */ if (NATIVEOS(access)(rootname, 0) < 0) panic("oskit_unix_fs_init: Cannot access root `%s'", rootname); if (newfsd == NULL) panic("oskit_unix_fs_init: Out of memory"); memset(newfsd, 0, sizeof(*newfsd)); pathlen = strlen(rootname); if ((newfsd->pathname = malloc(pathlen + 2)) == NULL) { free(newfsd); return 0; } strcpy(newfsd->pathname, rootname); if (newfsd->pathname[strlen(newfsd->pathname) - 1] != '/') strcat(newfsd->pathname, "/"); newfsd->component = NULL; newfsd->complen = 0; newfsd->count = 1; newfsd->diri.ops = &fs_dir_ops; newfsd->fd = -1; queue_init(&newfsd->contents); fs_dir_rootdir = newfsd; return (oskit_dir_t *)newfsd; } /* * Debug. Print the entire tree. */ static void fs_print_file(struct fs_file *fsf) { printf("F: 0x%x fd:%d %s\n", (int) fsf, fsf->fd, fsf->pathname); } static void fs_print_dir(struct fs_dir *fsd) { struct fs_file *fsf; printf("D: 0x%x fd:%d %s\n", (int) fsd, fsd->fd, fsd->pathname); queue_iterate(&fsd->contents, fsf, struct fs_file *, chain) { if (fsf->filei.ops == (void *)&fs_dir_ops) fs_print_dir((struct fs_dir *) fsf); else fs_print_file(fsf); } } void fs_dumptree() { fs_print_dir(fs_dir_rootdir); } /* * oskit_dirents_t COM object implementation. */ struct fs_dirents { oskit_dirents_t direntsi; /* COM dirents interface */ int count; /* Reference count */ oskit_dirent_t *entries; /* Array of entries */ int dircount; /* Number of entries */ int index; /* Current index pointer */ }; /* * Methods. */ static struct oskit_dirents_ops fs_dirents_ops; static OSKIT_COMDECL fs_dirents_query(oskit_dirents_t *d, const oskit_iid_t *iid, void **out_ihandle) { struct fs_dirents *fsdirents = (struct fs_dirents *) d; assert(fsdirents->count); if (memcmp(iid, &oskit_iunknown_iid, sizeof(*iid)) == 0 || memcmp(iid, &oskit_dirents_iid, sizeof(*iid)) == 0) { *out_ihandle = fsdirents; ++fsdirents->count; return 0; } *out_ihandle = NULL; return OSKIT_E_NOINTERFACE; } static OSKIT_COMDECL_U fs_dirents_addref(oskit_dirents_t *d) { struct fs_dirents *fsdirents = (struct fs_dirents *) d; assert(fsdirents->count); return ++fsdirents->count; } static OSKIT_COMDECL_U fs_dirents_release(oskit_dirents_t *d) { struct fs_dirents *fsdirents = (struct fs_dirents *) d; assert(fsdirents->count); if (--fsdirents->count) return fsdirents->count; /* fsdirentries->entries is one contiguous block of variable * sized ents, so don't free individual bits of it. */ free(fsdirents->entries); sfree(fsdirents, sizeof(*fsdirents)); return 0; } static OSKIT_COMDECL fs_dirents_getcount(oskit_dirents_t *d, int *out_count) { struct fs_dirents *fsdirents = (struct fs_dirents *) d; assert(fsdirents->count); *out_count = fsdirents->dircount; return 0; } static OSKIT_COMDECL fs_dirents_getnext(oskit_dirents_t *d, oskit_dirent_t *dirent) { struct fs_dirents *fsdirents = (struct fs_dirents *) d; struct oskit_dirent *idirent; assert(fsdirents->count); if (fsdirents->index >= fsdirents->dircount) return OSKIT_EWOULDBLOCK; idirent = &fsdirents->entries[fsdirents->index]; if (dirent->namelen < idirent->namelen + 1) return OSKIT_ENAMETOOLONG; dirent->ino = idirent->ino; dirent->namelen = idirent->namelen; strcpy(dirent->name, idirent->name); fsdirents->index++; return 0; } static OSKIT_COMDECL fs_dirents_rewind(oskit_dirents_t *d) { struct fs_dirents *fsdirents = (struct fs_dirents *) d; assert(fsdirents->count); fsdirents->index = 0; return 0; } static struct oskit_dirents_ops fs_dirents_ops = { fs_dirents_query, fs_dirents_addref, fs_dirents_release, fs_dirents_getcount, fs_dirents_getnext, fs_dirents_rewind, }; /* * Create a oskit_dirents_t COM object from an array of oskit_dirent_t * entries. */ static oskit_error_t fs_dirents_create(oskit_dirent_t *entries, int count, oskit_dirents_t **out_dirents) { struct fs_dirents *fsdirents = smalloc(sizeof(*fsdirents)); if (fsdirents == NULL) return OSKIT_ENOMEM; fsdirents->count = 1; fsdirents->dircount = count; fsdirents->entries = entries; fsdirents->index = 0; fsdirents->direntsi.ops = &fs_dirents_ops; *out_dirents = &fsdirents->direntsi; return 0; }