Newer
Older
wstat.name = (char *) new_dentry->d_name.name;
retval = p9_client_wstat(oldfid, &wstat);
if (!retval) {
if (new_inode) {
if (S_ISDIR(new_inode->i_mode))
clear_nlink(new_inode);
else
drop_nlink(new_inode);
}
if (S_ISDIR(old_inode->i_mode)) {
if (!new_inode)
inc_nlink(new_dir);
drop_nlink(old_dir);
}
v9fs_invalidate_inode_attr(old_inode);
v9fs_invalidate_inode_attr(old_dir);
v9fs_invalidate_inode_attr(new_dir);
/* successful rename */
d_move(old_dentry, new_dentry);
}
up_write(&v9ses->rename_sem);
p9_client_clunk(newdirfid);
p9_client_clunk(olddirfid);

Eric Van Hensbergen
committed
return retval;
}
/**
* v9fs_vfs_getattr - retrieve file metadata
* @mnt: mount information
* @dentry: file to get attributes on
* @stat: metadata structure to populate
*
*/
static int
v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
{
int err;
struct v9fs_session_info *v9ses;
struct p9_fid *fid;
struct p9_wstat *st;
p9_debug(P9_DEBUG_VFS, "dentry: %p\n", dentry);
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
generic_fillattr(dentry->d_inode, stat);
return 0;
}
fid = v9fs_fid_lookup(dentry);
if (IS_ERR(fid))

Eric Van Hensbergen
committed
return PTR_ERR(fid);
st = p9_client_stat(fid);
if (IS_ERR(st))
return PTR_ERR(st);
v9fs_stat2inode(st, dentry->d_inode, dentry->d_inode->i_sb);
generic_fillattr(dentry->d_inode, stat);
}
/**
* v9fs_vfs_setattr - set file metadata
* @dentry: file whose metadata to set
* @iattr: metadata assignment structure
*
*/
static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
{
int retval;
struct v9fs_session_info *v9ses;
struct p9_fid *fid;
struct p9_wstat wstat;
p9_debug(P9_DEBUG_VFS, "\n");
retval = inode_change_ok(dentry->d_inode, iattr);
if (retval)
return retval;
fid = v9fs_fid_lookup(dentry);

Eric Van Hensbergen
committed
if(IS_ERR(fid))
return PTR_ERR(fid);
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 (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 (S_ISREG(dentry->d_inode->i_mode))
filemap_write_and_wait(dentry->d_inode->i_mapping);
retval = p9_client_wstat(fid, &wstat);
if (retval < 0)
return retval;
if ((iattr->ia_valid & ATTR_SIZE) &&
iattr->ia_size != i_size_read(dentry->d_inode))
truncate_setsize(dentry->d_inode, iattr->ia_size);
v9fs_invalidate_inode_attr(dentry->d_inode);
setattr_copy(dentry->d_inode, iattr);
mark_inode_dirty(dentry->d_inode);
return 0;
* v9fs_stat2inode - populate an inode structure with mistat info
* @stat: Plan 9 metadata (mistat) structure
* @inode: inode to populate
* @sb: superblock of filesystem
*
*/
void
v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
char tag_name[14];
unsigned int i_nlink;
struct v9fs_session_info *v9ses = sb->s_fs_info;
struct v9fs_inode *v9inode = V9FS_I(inode);
inode->i_atime.tv_sec = stat->atime;
inode->i_mtime.tv_sec = stat->mtime;
inode->i_ctime.tv_sec = stat->mtime;
inode->i_uid = v9ses->dfltuid;
inode->i_gid = v9ses->dfltgid;
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) && (stat->extension[0] != '\0')) {
/*
* Hadlink support got added later to
* to the .u extension. So there can be
* server out there that doesn't support
* this even with .u extension. So check
* for non NULL stat->extension
*/
strncpy(ext, stat->extension, sizeof(ext));
/* HARDLINKCOUNT %u */
sscanf(ext, "%13s %u", tag_name, &i_nlink);
if (!strncmp(tag_name, "HARDLINKCOUNT", 13))
mode = p9mode2perm(v9ses, stat);
mode |= inode->i_mode & ~S_IALLUGO;
inode->i_mode = mode;

Abhishek Kulkarni
committed
i_size_write(inode, stat->length);
/* not real number of blocks, but 512 byte ones ... */

Abhishek Kulkarni
committed
inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9;
v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR;
}
/**
* v9fs_qid2ino - convert qid into inode number
* @qid: qid to hash
*
* BUG: potential for inode number collisions?
*/
ino_t v9fs_qid2ino(struct p9_qid *qid)
{
u64 path = qid->path + 2;
ino_t i = 0;
if (sizeof(ino_t) == sizeof(path))
memcpy(&i, &path, sizeof(ino_t));
else
i = (ino_t) (path ^ (path >> 32));
return i;
}
/**
* v9fs_readlink - read a symlink's location (internal version)
* @dentry: dentry for symlink
* @buffer: buffer to load symlink location into
* @buflen: length of buffer
*
*/
static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
{
struct v9fs_session_info *v9ses;
struct p9_fid *fid;
struct p9_wstat *st;
p9_debug(P9_DEBUG_VFS, " %s\n", dentry->d_name.name);
fid = v9fs_fid_lookup(dentry);

Eric Van Hensbergen
committed
return PTR_ERR(fid);
if (!v9fs_proto_dotu(v9ses))
st = p9_client_stat(fid);
if (IS_ERR(st))
return PTR_ERR(st);
if (!(st->mode & P9_DMSYMLINK)) {
}
/* copy extension buffer into buffer */
strncpy(buffer, st->extension, buflen);
p9_debug(P9_DEBUG_VFS, "%s -> %s (%s)\n",
dentry->d_name.name, st->extension, buffer);
return retval;
}
/**
* v9fs_vfs_follow_link - follow a symlink path
* @dentry: dentry for symlink
* @nd: nameidata
*
*/
static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)
{
int len = 0;
char *link = __getname();
p9_debug(P9_DEBUG_VFS, "%s\n", dentry->d_name.name);
if (!link)
link = ERR_PTR(-ENOMEM);
else {
len = v9fs_readlink(dentry, link, PATH_MAX);
link = ERR_PTR(len);
} else
}
nd_set_link(nd, link);
return NULL;
}
/**
* v9fs_vfs_put_link - release a symlink path
* @dentry: dentry for symlink
* @nd: nameidata
v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
{
char *s = nd_get_link(nd);
p9_debug(P9_DEBUG_VFS, " %s %s\n",
dentry->d_name.name, IS_ERR(s) ? "<error>" : s);
/**
* v9fs_vfs_mkspecial - create a special file
* @dir: inode to create special file in
* @dentry: dentry to create
* @mode: mode to create special file
* @extension: 9p2000.u format extension string representing special file
*
*/
static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry,
u32 perm, const char *extension)
struct v9fs_session_info *v9ses;
p9_debug(P9_DEBUG_ERROR, "not extended\n");
fid = v9fs_create(v9ses, dir, dentry, (char *) extension, perm,
P9_OREAD);
if (IS_ERR(fid))
return PTR_ERR(fid);

Eric Van Hensbergen
committed
v9fs_invalidate_inode_attr(dir);
}
/**
* v9fs_vfs_symlink - helper function to create symlinks
* @dir: directory inode containing symlink
* @dentry: dentry for symlink
* @symname: symlink data
*
* See Also: 9P2000.u RFC for more information
*
*/
static int
v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
p9_debug(P9_DEBUG_VFS, " %lu,%s,%s\n",
dir->i_ino, dentry->d_name.name, symname);
return v9fs_vfs_mkspecial(dir, dentry, P9_DMSYMLINK, symname);
}
/**
* v9fs_vfs_link - create a hardlink
* @old_dentry: dentry for file to link to
* @dir: inode destination for new link
* @dentry: dentry for link
*
*/
static int
v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *dentry)
{
int retval;
char *name;
struct p9_fid *oldfid;
p9_debug(P9_DEBUG_VFS, " %lu,%s,%s\n",
dir->i_ino, dentry->d_name.name, old_dentry->d_name.name);

Eric Van Hensbergen
committed
oldfid = v9fs_fid_clone(old_dentry);

Eric Van Hensbergen
committed
return PTR_ERR(oldfid);

Eric Van Hensbergen
committed
if (unlikely(!name)) {
retval = -ENOMEM;
goto clunk_fid;
}

Florin Malita
committed
sprintf(name, "%d\n", oldfid->fid);
retval = v9fs_vfs_mkspecial(dir, dentry, P9_DMLINK, name);
if (!retval) {
v9fs_refresh_inode(oldfid, old_dentry->d_inode);
v9fs_invalidate_inode_attr(dir);

Eric Van Hensbergen
committed
clunk_fid:
return retval;
}
/**
* v9fs_vfs_mknod - create a special file
* @dir: inode destination for new link
* @dentry: dentry for file
* @mode: mode for creation
* @rdev: device associated with special file
*
*/
static int
v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev)
struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);

Linus Torvalds
committed
p9_debug(P9_DEBUG_VFS, " %lu,%s mode: %hx MAJOR: %u MINOR: %u\n",
dir->i_ino, dentry->d_name.name, mode,
MAJOR(rdev), MINOR(rdev));
if (!new_valid_dev(rdev))
return -EINVAL;
if (!name)
return -ENOMEM;
/* build extension */
if (S_ISBLK(mode))
sprintf(name, "b %u %u", MAJOR(rdev), MINOR(rdev));
sprintf(name, "c %u %u", MAJOR(rdev), MINOR(rdev));
else if (S_ISFIFO(mode))
else if (S_ISSOCK(mode))
*name = 0;
__putname(name);
return -EINVAL;
perm = unixmode2p9mode(v9ses, mode);
retval = v9fs_vfs_mkspecial(dir, dentry, perm, name);
return retval;
}
int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode)
{
int umode;
dev_t rdev;
loff_t i_size;
struct p9_wstat *st;
struct v9fs_session_info *v9ses;
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->i_mode & S_IFMT) != (umode & S_IFMT))
goto out;
spin_lock(&inode->i_lock);
/*
* We don't want to refresh inode->i_size,
* because we may have cached data
*/
i_size = inode->i_size;
v9fs_stat2inode(st, inode, inode->i_sb);
if (v9ses->cache)
inode->i_size = i_size;
spin_unlock(&inode->i_lock);
p9stat_free(st);
kfree(st);
return 0;
}
static const struct inode_operations v9fs_dir_inode_operations_dotu = {
.create = v9fs_vfs_create,
.lookup = v9fs_vfs_lookup,
.symlink = v9fs_vfs_symlink,
.link = v9fs_vfs_link,
.unlink = v9fs_vfs_unlink,
.mkdir = v9fs_vfs_mkdir,
.rmdir = v9fs_vfs_rmdir,
.mknod = v9fs_vfs_mknod,
.rename = v9fs_vfs_rename,
.getattr = v9fs_vfs_getattr,
.setattr = v9fs_vfs_setattr,
};
static const struct inode_operations v9fs_dir_inode_operations = {
.create = v9fs_vfs_create,
.lookup = v9fs_vfs_lookup,
.unlink = v9fs_vfs_unlink,
.mkdir = v9fs_vfs_mkdir,
.rmdir = v9fs_vfs_rmdir,
.mknod = v9fs_vfs_mknod,
.rename = v9fs_vfs_rename,
.getattr = v9fs_vfs_getattr,
.setattr = v9fs_vfs_setattr,
};
static const struct inode_operations v9fs_file_inode_operations = {
.getattr = v9fs_vfs_getattr,
.setattr = v9fs_vfs_setattr,
};
static const struct inode_operations v9fs_symlink_inode_operations = {
.follow_link = v9fs_vfs_follow_link,
.put_link = v9fs_vfs_put_link,
.getattr = v9fs_vfs_getattr,
.setattr = v9fs_vfs_setattr,
};