Developed selective timestamp setting for utimensat(2)

This commit is contained in:
jpandre 2009-12-23 14:51:07 +00:00
parent b8a83add46
commit 17a629229d
5 changed files with 112 additions and 1 deletions

View File

@ -313,7 +313,7 @@ AC_CHECK_FUNCS([ \
atexit basename daemon dup2 fdatasync ffs getopt_long hasmntopt \
mbsinit memmove memset realpath regcomp setlocale setxattr \
strcasecmp strchr strdup strerror strnlen strsep strtol strtoul \
sysconf utime fork \
sysconf utime utimensat fork \
])
AC_SYS_LARGEFILE

View File

@ -67,7 +67,23 @@ typedef int (*fuse_fill_dir_t) (void *buf, const char *name,
* Changed in fuse 2.8.0 (regardless of API version)
* Previously, paths were limited to a length of PATH_MAX.
*/
#define HAS_UTIME_OMIT_OK 1
struct fuse_operations {
unsigned int flag_nullpath_ok : 1;
/**
* Flag indicating that the filesystem accepts special
* UTIME_NOW and UTIME_OMIT values in its utimens operation.
*/
unsigned int flag_utime_omit_ok : 1;
/**
* Reserved flags, don't set
*/
unsigned int flag_reserved : 30;
/** Get file attributes.
*
* Similar to stat(). The 'st_dev' and 'st_blksize' fields are

View File

@ -114,6 +114,8 @@ struct fuse_ctx {
#define FUSE_SET_ATTR_SIZE (1 << 3)
#define FUSE_SET_ATTR_ATIME (1 << 4)
#define FUSE_SET_ATTR_MTIME (1 << 5)
#define FUSE_SET_ATTR_ATIME_NOW (1 << 7)
#define FUSE_SET_ATTR_MTIME_NOW (1 << 8)
/* ----------------------------------------------------------- *
* Request methods and replies *

View File

@ -75,6 +75,7 @@ struct fuse {
struct fuse_config conf;
int intr_installed;
struct fuse_fs *fs;
int utime_omit_ok;
};
struct lock {
@ -1187,6 +1188,29 @@ static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
else
err = fuse_fs_truncate(f->fs, path, attr->st_size);
}
#ifdef HAVE_UTIMENSAT
if (!err && f->utime_omit_ok &&
(valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) {
struct timespec tv[2];
tv[0].tv_sec = 0;
tv[1].tv_sec = 0;
tv[0].tv_nsec = UTIME_OMIT;
tv[1].tv_nsec = UTIME_OMIT;
if (valid & FUSE_SET_ATTR_ATIME_NOW)
tv[0].tv_nsec = UTIME_NOW;
else if (valid & FUSE_SET_ATTR_ATIME)
tv[0] = attr->st_atim;
if (valid & FUSE_SET_ATTR_MTIME_NOW)
tv[1].tv_nsec = UTIME_NOW;
else if (valid & FUSE_SET_ATTR_MTIME)
tv[1] = attr->st_mtim;
err = fuse_fs_utimens(f->fs, path, tv);
} else
#endif
if (!err && (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) ==
(FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
struct timespec tv[2];
@ -2606,6 +2630,7 @@ struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
goto out_free;
f->fs = fs;
f->utime_omit_ok = fs->op.flag_utime_omit_ok;
/* Oh f**k, this is ugly! */
if (!fs->op.lock) {
@ -2639,6 +2664,8 @@ struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
fuse_session_add_chan(f->se, ch);
if (f->conf.debug)
fprintf(stderr, "utime_omit_ok: %i\n", f->utime_omit_ok);
f->ctr = 0;
f->generation = 0;
/* FIXME: Dynamic hash table */

View File

@ -2185,6 +2185,63 @@ static int ntfs_fuse_rmdir(const char *path)
return ntfs_fuse_rm(path);
}
#ifdef HAVE_UTIMENSAT
static int ntfs_fuse_utimens(const char *path, const struct timespec tv[2])
{
ntfs_inode *ni;
int res = 0;
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
struct SECURITY_CONTEXT security;
#endif
if (ntfs_fuse_is_named_data_stream(path))
return -EINVAL; /* n/a for named data streams. */
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
/* parent directory must be executable */
if (ntfs_fuse_fill_security_context(&security)
&& !ntfs_allowed_dir_access(&security,path,
(ntfs_inode*)NULL,(ntfs_inode*)NULL,S_IEXEC)) {
return (-errno);
}
#endif
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni)
return -errno;
/* no check or update if both UTIME_OMIT */
if ((tv[0].tv_nsec != UTIME_OMIT) || (tv[1].tv_nsec != UTIME_OMIT)) {
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
if (ntfs_allowed_access(&security, ni, S_IWRITE)
|| ((tv[0].tv_nsec == UTIME_NOW)
&& (tv[0].tv_nsec == UTIME_NOW)
&& ntfs_allowed_as_owner(&security, ni))) {
#endif
ntfs_time_update_flags mask = NTFS_UPDATE_CTIME;
if (tv[0].tv_nsec == UTIME_NOW)
mask |= NTFS_UPDATE_ATIME;
else
if (tv[0].tv_nsec != UTIME_OMIT)
ni->last_access_time = tv[0].tv_sec;
if (tv[1].tv_nsec == UTIME_NOW)
mask |= NTFS_UPDATE_MTIME;
else
if (tv[1].tv_nsec != UTIME_OMIT)
ni->last_data_change_time = tv[1].tv_sec;
ntfs_inode_update_times(ni, mask);
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)
} else
res = -errno;
#endif
}
if (ntfs_inode_close(ni))
set_fuse_error(&res);
return res;
}
#else /* HAVE_UTIMENSAT */
static int ntfs_fuse_utime(const char *path, struct utimbuf *buf)
{
ntfs_inode *ni;
@ -2252,6 +2309,8 @@ static int ntfs_fuse_utime(const char *path, struct utimbuf *buf)
return res;
}
#endif /* HAVE_UTIMENSAT */
static int ntfs_fuse_bmap(const char *path, size_t blocksize, uint64_t *idx)
{
ntfs_inode *ni;
@ -3386,6 +3445,9 @@ static void ntfs_fuse_destroy2(void *unused __attribute__((unused)))
}
static struct fuse_operations ntfs_3g_ops = {
#if defined(HAVE_UTIMENSAT) && defined(HAS_UTIME_OMIT_OK)
.flag_utime_omit_ok = 1, /* accept UTIME_NOW and UTIME_OMIT in utimens */
#endif
.getattr = ntfs_fuse_getattr,
.readlink = ntfs_fuse_readlink,
.readdir = ntfs_fuse_readdir,
@ -3406,7 +3468,11 @@ static struct fuse_operations ntfs_3g_ops = {
.rename = ntfs_fuse_rename,
.mkdir = ntfs_fuse_mkdir,
.rmdir = ntfs_fuse_rmdir,
#ifdef HAVE_UTIMENSAT
.utimens = ntfs_fuse_utimens,
#else
.utime = ntfs_fuse_utime,
#endif
.bmap = ntfs_fuse_bmap,
.destroy = ntfs_fuse_destroy2,
#if !KERNELPERMS | (POSIXACLS & !KERNELACLS)