// SPDX-License-Identifier: GPL-2.0-only /* * This file contains vfs inode ops for the 9P2000 protocol. * * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
*/
/** * unixmode2p9mode - convert unix mode bits to plan 9 * @v9ses: v9fs session information * @mode: mode to convert *
*/
static u32 unixmode2p9mode(struct v9fs_session_info *v9ses, umode_t mode)
{ int res;
res = mode & 0777; if (S_ISDIR(mode))
res |= P9_DMDIR; if (v9fs_proto_dotu(v9ses)) { if (v9ses->nodev == 0) { if (S_ISSOCK(mode))
res |= P9_DMSOCKET; if (S_ISFIFO(mode))
res |= P9_DMNAMEDPIPE; if (S_ISBLK(mode))
res |= P9_DMDEVICE; if (S_ISCHR(mode))
res |= P9_DMDEVICE;
}
if ((mode & S_ISUID) == S_ISUID)
res |= P9_DMSETUID; if ((mode & S_ISGID) == S_ISGID)
res |= P9_DMSETGID; if ((mode & S_ISVTX) == S_ISVTX)
res |= P9_DMSETVTX;
} return res;
}
/** * p9mode2perm- convert plan9 mode bits to unix permission bits * @v9ses: v9fs session information * @stat: p9_wstat from which mode need to be derived *
*/ staticint p9mode2perm(struct v9fs_session_info *v9ses, struct p9_wstat *stat)
{ int res; int mode = stat->mode;
res = mode & 0777; /* S_IRWXUGO */ if (v9fs_proto_dotu(v9ses)) { if ((mode & P9_DMSETUID) == P9_DMSETUID)
res |= S_ISUID;
if ((mode & P9_DMSETGID) == P9_DMSETGID)
res |= S_ISGID;
if ((mode & P9_DMSETVTX) == P9_DMSETVTX)
res |= S_ISVTX;
} return res;
}
/** * p9mode2unixmode- convert plan9 mode bits to unix mode bits * @v9ses: v9fs session information * @stat: p9_wstat from which mode need to be derived * @rdev: major number, minor number in case of device files. *
*/ static umode_t p9mode2unixmode(struct v9fs_session_info *v9ses, struct p9_wstat *stat, dev_t *rdev)
{ int res, r;
u32 mode = stat->mode;
*rdev = 0;
res = p9mode2perm(v9ses, stat);
if ((mode & P9_DMDIR) == P9_DMDIR)
res |= S_IFDIR; elseif ((mode & P9_DMSYMLINK) && (v9fs_proto_dotu(v9ses)))
res |= S_IFLNK; elseif ((mode & P9_DMSOCKET) && (v9fs_proto_dotu(v9ses))
&& (v9ses->nodev == 0))
res |= S_IFSOCK; elseif ((mode & P9_DMNAMEDPIPE) && (v9fs_proto_dotu(v9ses))
&& (v9ses->nodev == 0))
res |= S_IFIFO; elseif ((mode & P9_DMDEVICE) && (v9fs_proto_dotu(v9ses))
&& (v9ses->nodev == 0)) { char type = 0; int major = -1, minor = -1;
r = sscanf(stat->extension, "%c %i %i", &type, &major, &minor); if (r != 3) {
p9_debug(P9_DEBUG_ERROR, "invalid device string, umode will be bogus: %s\n",
stat->extension); return res;
} switch (type) { case'c':
res |= S_IFCHR; break; case'b':
res |= S_IFBLK; break; default:
p9_debug(P9_DEBUG_ERROR, "Unknown special type %c %s\n",
type, stat->extension);
}
*rdev = MKDEV(major, minor);
} else
res |= S_IFREG;
return res;
}
/** * v9fs_uflags2omode- convert posix open flags to plan 9 mode bits * @uflags: flags to convert * @extended: if .u extensions are active
*/
int v9fs_uflags2omode(int uflags, int extended)
{ int ret;
switch (uflags&3) { default: case O_RDONLY:
ret = P9_OREAD; break;
case O_WRONLY:
ret = P9_OWRITE; break;
case O_RDWR:
ret = P9_ORDWR; break;
}
if (uflags & O_TRUNC)
ret |= P9_OTRUNC;
if (extended) { if (uflags & O_EXCL)
ret |= P9_OEXCL;
if (uflags & O_APPEND)
ret |= P9_OAPPEND;
}
return ret;
}
/** * v9fs_blank_wstat - helper function to setup a 9P stat structure * @wstat: structure to initialize *
*/
/** * v9fs_alloc_inode - helper function to allocate an inode * @sb: The superblock to allocate the inode from
*/ struct inode *v9fs_alloc_inode(struct super_block *sb)
{ struct v9fs_inode *v9inode;
if (new)
test = v9fs_test_new_inode; else
test = v9fs_test_inode;
inode = iget5_locked(sb, QID2INO(qid), test, v9fs_set_inode, st); if (!inode) return ERR_PTR(-ENOMEM); if (!(inode->i_state & I_NEW)) return inode; /* * initialize the inode with the stat info * FIXME!! we may need support for stale inodes * later.
*/
inode->i_ino = QID2INO(qid);
umode = p9mode2unixmode(v9ses, st, &rdev);
retval = v9fs_init_inode(v9ses, inode, umode, rdev); if (retval) goto error;
/** * v9fs_at_to_dotl_flags- convert Linux specific AT flags to * plan 9 AT flag. * @flags: flags to convert
*/ staticint v9fs_at_to_dotl_flags(int flags)
{ int rflags = 0;
if (flags & AT_REMOVEDIR)
rflags |= P9_DOTL_AT_REMOVEDIR;
return rflags;
}
/** * v9fs_dec_count - helper functon to drop i_nlink. * * If a directory had nlink <= 2 (including . and ..), then we should not drop * the link count, which indicates the underlying exported fs doesn't maintain * nlink accurately. e.g. * - overlayfs sets nlink to 1 for merged dir * - ext4 (with dir_nlink feature enabled) sets nlink to 1 if a dir has more * than EXT4_LINK_MAX (65000) links. * * @inode: inode whose nlink is being dropped
*/ staticvoid v9fs_dec_count(struct inode *inode)
{ if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2)
drop_nlink(inode);
}
/** * v9fs_remove - helper function to remove files and directories * @dir: directory inode that is being deleted * @dentry: dentry that is being deleted * @flags: removing a directory *
*/
if (!(perm & P9_DMLINK)) { /* now walk from the parent so we can get unopened fid */
fid = p9_client_walk(dfid, 1, &name, 1); if (IS_ERR(fid)) {
err = PTR_ERR(fid);
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err); goto error;
} /* * instantiate inode and assign the unopened fid to the dentry
*/
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb); if (IS_ERR(inode)) {
err = PTR_ERR(inode);
p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n", err); goto error;
}
v9fs_fid_add(dentry, &fid);
d_instantiate(dentry, inode);
}
p9_fid_put(dfid); return ofid;
error:
p9_fid_put(dfid);
p9_fid_put(ofid);
p9_fid_put(fid); return ERR_PTR(err);
}
/** * v9fs_vfs_create - VFS hook to create a regular file * @idmap: idmap of the mount * @dir: The parent directory * @dentry: The name of file to be created * @mode: The UNIX file mode to set * @excl: True if the file must not yet exist * * open(.., O_CREAT) is handled in v9fs_vfs_atomic_open(). This is only called * for mknod(2). *
*/
/** * v9fs_vfs_mkdir - VFS mkdir hook to create a directory * @idmap: idmap of the mount * @dir: inode that is being unlinked * @dentry: dentry that is being unlinked * @mode: mode for new directory *
*/
/** * v9fs_vfs_lookup - VFS lookup hook to "walk" to a new inode * @dir: inode that is being walked from * @dentry: dentry that is being walked to? * @flags: lookup flags (unused) *
*/
if (dentry->d_name.len > NAME_MAX) return ERR_PTR(-ENAMETOOLONG);
v9ses = v9fs_inode2v9ses(dir); /* We can walk d_parent because we hold the dir->i_mutex */
dfid = v9fs_parent_fid(dentry); if (IS_ERR(dfid)) return ERR_CAST(dfid);
/* * Make sure we don't use a wrong inode due to parallel * unlink. For cached mode create calls request for new * inode. But with cache disabled, lookup should do this.
*/
name = dentry->d_name.name;
fid = p9_client_walk(dfid, 1, &name, 1);
p9_fid_put(dfid); if (fid == ERR_PTR(-ENOENT))
inode = NULL; elseif (IS_ERR(fid))
inode = ERR_CAST(fid); elseif (v9ses->cache & (CACHE_META|CACHE_LOOSE))
inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb); else
inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb); /* * If we had a rename on the server and a parallel lookup * for the new name, then make sure we instantiate with * the new name. ie look up for a/b, while on server somebody * moved b under k and client parallely did a lookup for * k/b.
*/
res = d_splice_alias(inode, dentry); if (!IS_ERR(fid)) { if (!res)
v9fs_fid_add(dentry, &fid); elseif (!IS_ERR(res))
v9fs_fid_add(res, &fid); else
p9_fid_put(fid);
} return res;
}
/** * v9fs_vfs_rename - VFS hook to rename an inode * @idmap: The idmap of the mount * @old_dir: old dir inode * @old_dentry: old dentry * @new_dir: new dir inode * @new_dentry: new dentry * @flags: RENAME_* flags *
*/
if (IS_ERR(newdirfid)) {
retval = PTR_ERR(newdirfid); goto error;
}
down_write(&v9ses->rename_sem); if (v9fs_proto_dotl(v9ses)) {
retval = p9_client_renameat(olddirfid, old_dentry->d_name.name,
newdirfid, new_dentry->d_name.name); if (retval == -EOPNOTSUPP)
retval = p9_client_rename(oldfid, newdirfid,
new_dentry->d_name.name); if (retval != -EOPNOTSUPP) goto error_locked;
} if (old_dentry->d_parent != new_dentry->d_parent) { /* * 9P .u can only handle file rename in the same directory
*/
p9_debug(P9_DEBUG_ERROR, "old dir and new dir are different\n");
retval = -EXDEV; goto error_locked;
}
v9fs_blank_wstat(&wstat);
wstat.muid = v9ses->uname;
wstat.name = new_dentry->d_name.name;
retval = p9_client_wstat(oldfid, &wstat);
error_locked: if (!retval) { if (new_inode) { if (S_ISDIR(new_inode->i_mode))
clear_nlink(new_inode); else
v9fs_dec_count(new_inode);
} if (S_ISDIR(old_inode->i_mode)) { if (!new_inode)
inc_nlink(new_dir);
v9fs_dec_count(old_dir);
}
v9fs_invalidate_inode_attr(old_inode);
v9fs_invalidate_inode_attr(old_dir);
v9fs_invalidate_inode_attr(new_dir);
if (retval)
p9_debug(P9_DEBUG_ERROR, "flushing writeback during getattr returned %d\n", retval);
}
}
fid = v9fs_fid_lookup(dentry); if (IS_ERR(fid)) return PTR_ERR(fid);
st = p9_client_stat(fid);
p9_fid_put(fid); if (IS_ERR(st)) return PTR_ERR(st);
/** * v9fs_vfs_setattr - set file metadata * @idmap: idmap of the mount * @dentry: file whose metadata to set * @iattr: metadata assignment structure *
*/
v9ses = v9fs_dentry2v9ses(dentry); if (iattr->ia_valid & ATTR_FILE) {
fid = iattr->ia_file->private_data;
WARN_ON(!fid);
} if (!fid) {
fid = v9fs_fid_lookup(dentry);
use_dentry = 1;
} if (IS_ERR(fid)) return PTR_ERR(fid);
v9fs_blank_wstat(&wstat); if (iattr->ia_valid & ATTR_MODE)
wstat.mode = unixmode2p9mode(v9ses, iattr->ia_mode);
if (iattr->ia_valid & ATTR_MTIME)
wstat.mtime = iattr->ia_mtime.tv_sec;
if (iattr->ia_valid & ATTR_ATIME)
wstat.atime = iattr->ia_atime.tv_sec;
if (iattr->ia_valid & ATTR_SIZE)
wstat.length = iattr->ia_size;
if (v9fs_proto_dotu(v9ses)) { if (iattr->ia_valid & ATTR_UID)
wstat.n_uid = iattr->ia_uid;
if (iattr->ia_valid & ATTR_GID)
wstat.n_gid = iattr->ia_gid;
}
/* Write all dirty data */ if (d_is_reg(dentry)) {
retval = filemap_fdatawrite(inode->i_mapping); if (retval)
p9_debug(P9_DEBUG_ERROR, "flushing writeback during setattr returned %d\n", retval);
}
if (v9fs_proto_dotu(v9ses)) {
inode->i_uid = stat->n_uid;
inode->i_gid = stat->n_gid;
} if ((S_ISREG(inode->i_mode)) || (S_ISDIR(inode->i_mode))) { if (v9fs_proto_dotu(v9ses)) { unsignedint i_nlink; /* * Hadlink support got added later to the .u extension. * So there can be a server out there that doesn't * support this even with .u extension. That would * just leave us with stat->extension being an empty * string, though.
*/ /* HARDLINKCOUNT %u */ if (sscanf(stat->extension, " HARDLINKCOUNT %u", &i_nlink) == 1)
set_nlink(inode, i_nlink);
}
}
mode = p9mode2perm(v9ses, stat);
mode |= inode->i_mode & ~S_IALLUGO;
inode->i_mode = mode;
v9inode->netfs.remote_i_size = stat->length; if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE))
v9fs_i_size_write(inode, stat->length); /* not real number of blocks, but 512 byte ones ... */
inode->i_blocks = (stat->length + 512 - 1) >> 9;
v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR;
}
/** * v9fs_vfs_get_link - follow a symlink path * @dentry: dentry for symlink * @inode: inode for symlink * @done: delayed call for when we are done with the return value
*/
/** * v9fs_vfs_mkspecial - create a special file * @dir: inode to create special file in * @dentry: dentry to create * @perm: mode to create special file * @extension: 9p2000.u format extension string representing special file *
*/
/** * v9fs_vfs_symlink - helper function to create symlinks * @idmap: idmap of the mount * @dir: directory inode containing symlink * @dentry: dentry for symlink * @symname: symlink data * * See Also: 9P2000.u RFC for more information *
*/
/** * v9fs_vfs_link - create a hardlink * @old_dentry: dentry for file to link to * @dir: inode destination for new link * @dentry: dentry for link *
*/
/** * v9fs_vfs_mknod - create a special file * @idmap: idmap of the mount * @dir: inode destination for new link * @dentry: dentry for file * @mode: mode for creation * @rdev: device associated with special file *
*/
int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode)
{ int umode;
dev_t rdev; struct p9_wstat *st; struct v9fs_session_info *v9ses; unsignedint flags;
v9ses = v9fs_inode2v9ses(inode);
st = p9_client_stat(fid); if (IS_ERR(st)) return PTR_ERR(st); /* * Don't update inode if the file type is different
*/
umode = p9mode2unixmode(v9ses, st, &rdev); if (inode_wrong_type(inode, umode)) goto out;
/* * We don't want to refresh inode->i_size, * because we may have cached data
*/
flags = (v9ses->cache & CACHE_LOOSE) ?
V9FS_STAT2INODE_KEEP_ISIZE : 0;
v9fs_stat2inode(st, inode, inode->i_sb, flags);
out:
p9stat_free(st);
kfree(st); return 0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.