078cd8279e
CURRENT_TIME macro is not appropriate for filesystems as it doesn't use the right granularity for filesystem timestamps. Use current_time() instead. CURRENT_TIME is also not y2038 safe. This is also in preparation for the patch that transitions vfs timestamps to use 64 bit time and hence make them y2038 safe. As part of the effort current_time() will be extended to do range checks. Hence, it is necessary for all file system timestamps to use current_time(). Also, current_time() will be transitioned along with vfs to be y2038 safe. Note that whenever a single call to current_time() is used to change timestamps in different inodes, it is because they share the same time granularity. Signed-off-by: Deepa Dinamani <deepa.kernel@gmail.com> Reviewed-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Felipe Balbi <balbi@kernel.org> Acked-by: Steven Whitehouse <swhiteho@redhat.com> Acked-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> Acked-by: David Sterba <dsterba@suse.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
320 lines
7.2 KiB
C
320 lines
7.2 KiB
C
/*
|
|
* Copyright (C) 2005, 2006
|
|
* Avishay Traeger (avishay@gmail.com)
|
|
* Copyright (C) 2008, 2009
|
|
* Boaz Harrosh <ooo@electrozaur.com>
|
|
*
|
|
* Copyrights for code taken from ext2:
|
|
* Copyright (C) 1992, 1993, 1994, 1995
|
|
* Remy Card (card@masi.ibp.fr)
|
|
* Laboratoire MASI - Institut Blaise Pascal
|
|
* Universite Pierre et Marie Curie (Paris VI)
|
|
* from
|
|
* linux/fs/minix/inode.c
|
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
|
*
|
|
* This file is part of exofs.
|
|
*
|
|
* exofs is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation. Since it is based on ext2, and the only
|
|
* valid version of GPL for the Linux kernel is version 2, the only valid
|
|
* version of GPL for exofs is version 2.
|
|
*
|
|
* exofs 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
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with exofs; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "exofs.h"
|
|
|
|
static inline int exofs_add_nondir(struct dentry *dentry, struct inode *inode)
|
|
{
|
|
int err = exofs_add_link(dentry, inode);
|
|
if (!err) {
|
|
d_instantiate(dentry, inode);
|
|
return 0;
|
|
}
|
|
inode_dec_link_count(inode);
|
|
iput(inode);
|
|
return err;
|
|
}
|
|
|
|
static struct dentry *exofs_lookup(struct inode *dir, struct dentry *dentry,
|
|
unsigned int flags)
|
|
{
|
|
struct inode *inode;
|
|
ino_t ino;
|
|
|
|
if (dentry->d_name.len > EXOFS_NAME_LEN)
|
|
return ERR_PTR(-ENAMETOOLONG);
|
|
|
|
ino = exofs_inode_by_name(dir, dentry);
|
|
inode = ino ? exofs_iget(dir->i_sb, ino) : NULL;
|
|
return d_splice_alias(inode, dentry);
|
|
}
|
|
|
|
static int exofs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|
bool excl)
|
|
{
|
|
struct inode *inode = exofs_new_inode(dir, mode);
|
|
int err = PTR_ERR(inode);
|
|
if (!IS_ERR(inode)) {
|
|
inode->i_op = &exofs_file_inode_operations;
|
|
inode->i_fop = &exofs_file_operations;
|
|
inode->i_mapping->a_ops = &exofs_aops;
|
|
mark_inode_dirty(inode);
|
|
err = exofs_add_nondir(dentry, inode);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int exofs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|
dev_t rdev)
|
|
{
|
|
struct inode *inode;
|
|
int err;
|
|
|
|
inode = exofs_new_inode(dir, mode);
|
|
err = PTR_ERR(inode);
|
|
if (!IS_ERR(inode)) {
|
|
init_special_inode(inode, inode->i_mode, rdev);
|
|
mark_inode_dirty(inode);
|
|
err = exofs_add_nondir(dentry, inode);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int exofs_symlink(struct inode *dir, struct dentry *dentry,
|
|
const char *symname)
|
|
{
|
|
struct super_block *sb = dir->i_sb;
|
|
int err = -ENAMETOOLONG;
|
|
unsigned l = strlen(symname)+1;
|
|
struct inode *inode;
|
|
struct exofs_i_info *oi;
|
|
|
|
if (l > sb->s_blocksize)
|
|
goto out;
|
|
|
|
inode = exofs_new_inode(dir, S_IFLNK | S_IRWXUGO);
|
|
err = PTR_ERR(inode);
|
|
if (IS_ERR(inode))
|
|
goto out;
|
|
|
|
oi = exofs_i(inode);
|
|
if (l > sizeof(oi->i_data)) {
|
|
/* slow symlink */
|
|
inode->i_op = &page_symlink_inode_operations;
|
|
inode_nohighmem(inode);
|
|
inode->i_mapping->a_ops = &exofs_aops;
|
|
memset(oi->i_data, 0, sizeof(oi->i_data));
|
|
|
|
err = page_symlink(inode, symname, l);
|
|
if (err)
|
|
goto out_fail;
|
|
} else {
|
|
/* fast symlink */
|
|
inode->i_op = &simple_symlink_inode_operations;
|
|
inode->i_link = (char *)oi->i_data;
|
|
memcpy(oi->i_data, symname, l);
|
|
inode->i_size = l-1;
|
|
}
|
|
mark_inode_dirty(inode);
|
|
|
|
err = exofs_add_nondir(dentry, inode);
|
|
out:
|
|
return err;
|
|
|
|
out_fail:
|
|
inode_dec_link_count(inode);
|
|
iput(inode);
|
|
goto out;
|
|
}
|
|
|
|
static int exofs_link(struct dentry *old_dentry, struct inode *dir,
|
|
struct dentry *dentry)
|
|
{
|
|
struct inode *inode = d_inode(old_dentry);
|
|
|
|
inode->i_ctime = current_time(inode);
|
|
inode_inc_link_count(inode);
|
|
ihold(inode);
|
|
|
|
return exofs_add_nondir(dentry, inode);
|
|
}
|
|
|
|
static int exofs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|
{
|
|
struct inode *inode;
|
|
int err;
|
|
|
|
inode_inc_link_count(dir);
|
|
|
|
inode = exofs_new_inode(dir, S_IFDIR | mode);
|
|
err = PTR_ERR(inode);
|
|
if (IS_ERR(inode))
|
|
goto out_dir;
|
|
|
|
inode->i_op = &exofs_dir_inode_operations;
|
|
inode->i_fop = &exofs_dir_operations;
|
|
inode->i_mapping->a_ops = &exofs_aops;
|
|
|
|
inode_inc_link_count(inode);
|
|
|
|
err = exofs_make_empty(inode, dir);
|
|
if (err)
|
|
goto out_fail;
|
|
|
|
err = exofs_add_link(dentry, inode);
|
|
if (err)
|
|
goto out_fail;
|
|
|
|
d_instantiate(dentry, inode);
|
|
out:
|
|
return err;
|
|
|
|
out_fail:
|
|
inode_dec_link_count(inode);
|
|
inode_dec_link_count(inode);
|
|
iput(inode);
|
|
out_dir:
|
|
inode_dec_link_count(dir);
|
|
goto out;
|
|
}
|
|
|
|
static int exofs_unlink(struct inode *dir, struct dentry *dentry)
|
|
{
|
|
struct inode *inode = d_inode(dentry);
|
|
struct exofs_dir_entry *de;
|
|
struct page *page;
|
|
int err = -ENOENT;
|
|
|
|
de = exofs_find_entry(dir, dentry, &page);
|
|
if (!de)
|
|
goto out;
|
|
|
|
err = exofs_delete_entry(de, page);
|
|
if (err)
|
|
goto out;
|
|
|
|
inode->i_ctime = dir->i_ctime;
|
|
inode_dec_link_count(inode);
|
|
err = 0;
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static int exofs_rmdir(struct inode *dir, struct dentry *dentry)
|
|
{
|
|
struct inode *inode = d_inode(dentry);
|
|
int err = -ENOTEMPTY;
|
|
|
|
if (exofs_empty_dir(inode)) {
|
|
err = exofs_unlink(dir, dentry);
|
|
if (!err) {
|
|
inode->i_size = 0;
|
|
inode_dec_link_count(inode);
|
|
inode_dec_link_count(dir);
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int exofs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
struct inode *new_dir, struct dentry *new_dentry)
|
|
{
|
|
struct inode *old_inode = d_inode(old_dentry);
|
|
struct inode *new_inode = d_inode(new_dentry);
|
|
struct page *dir_page = NULL;
|
|
struct exofs_dir_entry *dir_de = NULL;
|
|
struct page *old_page;
|
|
struct exofs_dir_entry *old_de;
|
|
int err = -ENOENT;
|
|
|
|
old_de = exofs_find_entry(old_dir, old_dentry, &old_page);
|
|
if (!old_de)
|
|
goto out;
|
|
|
|
if (S_ISDIR(old_inode->i_mode)) {
|
|
err = -EIO;
|
|
dir_de = exofs_dotdot(old_inode, &dir_page);
|
|
if (!dir_de)
|
|
goto out_old;
|
|
}
|
|
|
|
if (new_inode) {
|
|
struct page *new_page;
|
|
struct exofs_dir_entry *new_de;
|
|
|
|
err = -ENOTEMPTY;
|
|
if (dir_de && !exofs_empty_dir(new_inode))
|
|
goto out_dir;
|
|
|
|
err = -ENOENT;
|
|
new_de = exofs_find_entry(new_dir, new_dentry, &new_page);
|
|
if (!new_de)
|
|
goto out_dir;
|
|
err = exofs_set_link(new_dir, new_de, new_page, old_inode);
|
|
new_inode->i_ctime = current_time(new_inode);
|
|
if (dir_de)
|
|
drop_nlink(new_inode);
|
|
inode_dec_link_count(new_inode);
|
|
if (err)
|
|
goto out_dir;
|
|
} else {
|
|
err = exofs_add_link(new_dentry, old_inode);
|
|
if (err)
|
|
goto out_dir;
|
|
if (dir_de)
|
|
inode_inc_link_count(new_dir);
|
|
}
|
|
|
|
old_inode->i_ctime = current_time(old_inode);
|
|
|
|
exofs_delete_entry(old_de, old_page);
|
|
mark_inode_dirty(old_inode);
|
|
|
|
if (dir_de) {
|
|
err = exofs_set_link(old_inode, dir_de, dir_page, new_dir);
|
|
inode_dec_link_count(old_dir);
|
|
if (err)
|
|
goto out_dir;
|
|
}
|
|
return 0;
|
|
|
|
|
|
out_dir:
|
|
if (dir_de) {
|
|
kunmap(dir_page);
|
|
put_page(dir_page);
|
|
}
|
|
out_old:
|
|
kunmap(old_page);
|
|
put_page(old_page);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
const struct inode_operations exofs_dir_inode_operations = {
|
|
.create = exofs_create,
|
|
.lookup = exofs_lookup,
|
|
.link = exofs_link,
|
|
.unlink = exofs_unlink,
|
|
.symlink = exofs_symlink,
|
|
.mkdir = exofs_mkdir,
|
|
.rmdir = exofs_rmdir,
|
|
.mknod = exofs_mknod,
|
|
.rename = exofs_rename,
|
|
.setattr = exofs_setattr,
|
|
};
|
|
|
|
const struct inode_operations exofs_special_inode_operations = {
|
|
.setattr = exofs_setattr,
|
|
};
|