Skip to content
Snippets Groups Projects
vfs_inode.c 33.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	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);
    
     * 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;
    
    	p9_debug(P9_DEBUG_VFS, "dentry: %p\n", dentry);
    
    	v9ses = v9fs_dentry2v9ses(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))
    
    	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);
    
    	p9stat_free(st);
    
    }
    
    /**
     * 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;
    
    
    	v9ses = v9fs_dentry2v9ses(dentry);
    
    	fid = v9fs_fid_lookup(dentry);
    
    	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 (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);
    
    Christoph Hellwig's avatar
    Christoph Hellwig committed
    	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,
    
    	struct super_block *sb)
    
    Al Viro's avatar
    Al Viro committed
    	umode_t mode;
    
    	char tag_name[14];
    	unsigned int i_nlink;
    
    	struct v9fs_session_info *v9ses = sb->s_fs_info;
    
    	struct v9fs_inode *v9inode = V9FS_I(inode);
    
    	set_nlink(inode, 1);
    
    	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;
    
    	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) && (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))
    
    				set_nlink(inode, i_nlink);
    
    	mode = p9mode2perm(v9ses, stat);
    
    	mode |= inode->i_mode & ~S_IALLUGO;
    	inode->i_mode = mode;
    
    	/* not real number of blocks, but 512 byte ones ... */
    
    	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;
    
    	p9_debug(P9_DEBUG_VFS, " %s\n", dentry->d_name.name);
    
    	v9ses = v9fs_dentry2v9ses(dentry);
    
    	fid = v9fs_fid_lookup(dentry);
    
    Latchesar Ionkov's avatar
    Latchesar Ionkov committed
    	if (IS_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)) {
    
    		retval = -EINVAL;
    
    	}
    
    	/* 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);
    
    Martin Stava's avatar
    Martin Stava committed
    	retval = strnlen(buffer, buflen);
    
    	p9stat_free(st);
    
    	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);
    
    			__putname(link);
    
    			link = ERR_PTR(len);
    		} else
    
    Martin Stava's avatar
    Martin Stava committed
    			link[min(len, PATH_MAX-1)] = 0;
    
    	}
    	nd_set_link(nd, link);
    
    	return NULL;
    }
    
    /**
     * v9fs_vfs_put_link - release a symlink path
     * @dentry: dentry for symlink
     * @nd: nameidata
    
     * @p: unused
    
    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);
    
    	if (!IS_ERR(s))
    
    		__putname(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 p9_fid *fid;
    
    	v9ses = v9fs_inode2v9ses(dir);
    
    	if (!v9fs_proto_dotu(v9ses)) {
    
    		p9_debug(P9_DEBUG_ERROR, "not extended\n");
    
    		return -EPERM;
    
    	fid = v9fs_create(v9ses, dir, dentry, (char *) extension, perm,
    								P9_OREAD);
    	if (IS_ERR(fid))
    		return PTR_ERR(fid);
    
    	p9_client_clunk(fid);
    
    }
    
    /**
     * 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;
    
    	p9_debug(P9_DEBUG_VFS, " %lu,%s,%s\n",
    		 dir->i_ino, dentry->d_name.name, old_dentry->d_name.name);
    
    Latchesar Ionkov's avatar
    Latchesar Ionkov committed
    	if (IS_ERR(oldfid))
    
    	if (unlikely(!name)) {
    		retval = -ENOMEM;
    		goto clunk_fid;
    	}
    
    	sprintf(name, "%d\n", oldfid->fid);
    
    	retval = v9fs_vfs_mkspecial(dir, dentry, P9_DMLINK, name);
    
    	__putname(name);
    
    	if (!retval) {
    		v9fs_refresh_inode(oldfid, old_dentry->d_inode);
    
    	p9_client_clunk(oldfid);
    
    	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
    
    Al Viro's avatar
    Al Viro committed
    v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev)
    
    	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);
    
    	int retval;
    	char *name;
    
    	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;
    
    	name = __getname();
    
    	/* build extension */
    	if (S_ISBLK(mode))
    
    		sprintf(name, "b %u %u", MAJOR(rdev), MINOR(rdev));
    
    	else if (S_ISCHR(mode))
    
    		sprintf(name, "c %u %u", MAJOR(rdev), MINOR(rdev));
    
    	else if (S_ISSOCK(mode))
    		*name = 0;
    
    		__putname(name);
    		return -EINVAL;
    
    	perm = unixmode2p9mode(v9ses, mode);
    	retval = v9fs_vfs_mkspecial(dir, dentry, perm, name);
    
    	__putname(name);
    
    int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode)
    {
    
    	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);
    
    static const struct inode_operations v9fs_dir_inode_operations_dotu = {
    	.create = v9fs_vfs_create,
    	.lookup = v9fs_vfs_lookup,
    	.symlink = v9fs_vfs_symlink,
    
    	.unlink = v9fs_vfs_unlink,
    	.mkdir = v9fs_vfs_mkdir,
    	.rmdir = v9fs_vfs_rmdir,
    
    	.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 = {
    
    Al Viro's avatar
    Al Viro committed
    	.readlink = generic_readlink,
    
    	.follow_link = v9fs_vfs_follow_link,
    	.put_link = v9fs_vfs_put_link,
    	.getattr = v9fs_vfs_getattr,
    	.setattr = v9fs_vfs_setattr,
    };