// kern/fs/vfs/vfsfile.c
// open file in vfs, get/create inode for file with filename path.
int vfs_open(char *path, uint32_t open_flags, struct inode **node_store) {
bool can_write = 0;
// 解析open_flags并做合法性检查
switch (open_flags & O_ACCMODE) {
case O_RDONLY:
break;
case O_WRONLY:
case O_RDWR:
can_write = 1;
break;
default:
return -E_INVAL;
}
if (open_flags & O_TRUNC) {
if (!can_write) {
return -E_INVAL;
}
}
/*
linux manual
O_TRUNC
If the file already exists and is a regular file and the
access mode allows writing (i.e., is O_RDWR or O_WRONLY) it
will be truncated to length 0. If the file is a FIFO or ter‐
minal device file, the O_TRUNC flag is ignored. Otherwise,
the effect of O_TRUNC is unspecified.
*/
int ret;
struct inode *node;
bool excl = (open_flags & O_EXCL) != 0;
bool create = (open_flags & O_CREAT) != 0;
ret = vfs_lookup(path, &node); // vfs_lookup根据路径构造inode
if (ret != 0) {//要打开的文件还不存在,可能出错,也可能需要创建新文件
if (ret == -16 && (create)) {
char *name;
struct inode *dir;
if ((ret = vfs_lookup_parent(path, &dir, &name)) != 0) {
return ret;//需要在已经存在的目录下创建文件,目录不存在,则出错
}
ret = vop_create(dir, name, excl, &node);//创建新文件
} else return ret;
} else if (excl && create) {
return -E_EXISTS;
/*
linux manual
O_EXCL Ensure that this call creates the file: if this flag is
specified in conjunction with O_CREAT, and pathname already
exists, then open() fails with the error EEXIST.
*/
}
assert(node != NULL);
if ((ret = vop_open(node, open_flags)) != 0) {
vop_ref_dec(node);
return ret;
}
vop_open_inc(node);
if (open_flags & O_TRUNC || create) {
if ((ret = vop_truncate(node, 0)) != 0) {
vop_open_dec(node);
vop_ref_dec(node);
return ret;
}
}
*node_store = node;
return 0;
}
/*
* get_device- Common code to pull the device name, if any, off the front of a
* path and choose the inode to begin the name lookup relative to.
*/
static int get_device(char *path, char **subpath, struct inode **node_store) {
int i, slash = -1, colon = -1;
for (i = 0; path[i] != '\0'; i ++) {
if (path[i] == ':') { colon = i; break; }
if (path[i] == '/') { slash = i; break; }
}
if (colon < 0 && slash != 0) {
/* *
* No colon before a slash, so no device name specified, and the slash isn't leading
* or is also absent, so this is a relative path or just a bare filename. Start from
* the current directory, and use the whole thing as the subpath.
* */
*subpath = path;
return vfs_get_curdir(node_store); //把当前目录的inode存到node_store
}
if (colon > 0) {
/* device:path - get root of device's filesystem */
path[colon] = '\0';
/* device:/path - skip slash, treat as device:path */
while (path[++ colon] == '/');
*subpath = path + colon;
return vfs_get_root(path, node_store);
}
/* *
* we have either /path or :path
* /path is a path relative to the root of the "boot filesystem"
* :path is a path relative to the root of the current filesystem
* */
int ret;
if (*path == '/') {
if ((ret = vfs_get_bootfs(node_store)) != 0) {
return ret;
}
}
else {
assert(*path == ':');
struct inode *node;
if ((ret = vfs_get_curdir(&node)) != 0) {
return ret;
}
/* The current directory may not be a device, so it must have a fs. */
assert(node->in_fs != NULL);
*node_store = fsop_get_root(node->in_fs);
vop_ref_dec(node);
}
/* ///... or :/... */
while (*(++ path) == '/');
*subpath = path;
return 0;
}
/*
* vfs_lookup - get the inode according to the path filename
*/
int vfs_lookup(char *path, struct inode **node_store) {
int ret;
struct inode *node;
if ((ret = get_device(path, &path, &node)) != 0) {
return ret;
}
if (*path != '\0') {
ret = vop_lookup(node, path, node_store);
vop_ref_dec(node);
return ret;
}
*node_store = node;
return 0;
}
/*
* vfs_lookup_parent - Name-to-vnode translation.
* (In BSD, both of these are subsumed by namei().)
*/
int vfs_lookup_parent(char *path, struct inode **node_store, char **endp){
int ret;
struct inode *node;
if ((ret = get_device(path, &path, &node)) != 0) {
return ret;
}
*endp = path;
*node_store = node;
return 0;
}
// kern/fs/sfs/sfs_inode.c
// The sfs specific DIR operations correspond to the abstract operations on a inode.
static const struct inode_ops sfs_node_dirops = {
.vop_magic = VOP_MAGIC,
.vop_open = sfs_opendir,
.vop_close = sfs_close,
.vop_fstat = sfs_fstat,
.vop_fsync = sfs_fsync,
.vop_namefile = sfs_namefile,
.vop_getdirentry = sfs_getdirentry,
.vop_reclaim = sfs_reclaim,
.vop_gettype = sfs_gettype,
.vop_lookup = sfs_lookup,
};
/// The sfs specific FILE operations correspond to the abstract operations on a inode.
static const struct inode_ops sfs_node_fileops = {
.vop_magic = VOP_MAGIC,
.vop_open = sfs_openfile,
.vop_close = sfs_close,
.vop_read = sfs_read,
.vop_write = sfs_write,
.vop_fstat = sfs_fstat,
.vop_fsync = sfs_fsync,
.vop_reclaim = sfs_reclaim,
.vop_gettype = sfs_gettype,
.vop_tryseek = sfs_tryseek,
.vop_truncate = sfs_truncfile,
};