import errno
import os
import sys
+import time
# Are we running Python 2.x
if sys.version_info[0] < 3:
cdef extern from "sys/types.h":
ctypedef unsigned long mode_t
+cdef extern from "<utime.h>":
+ cdef struct utimbuf:
+ time_t actime
+ time_t modtime
+
+cdef extern from "sys/time.h":
+ cdef struct timeval:
+ long tv_sec
+ long tv_usec
+
cdef extern from "cephfs/ceph_statx.h":
cdef struct statx "ceph_statx":
uint32_t stx_mask
int64_t ceph_lseek(ceph_mount_info *cmount, int fd, int64_t offset, int whence)
void ceph_buffer_free(char *buf)
mode_t ceph_umask(ceph_mount_info *cmount, mode_t mode)
+ int ceph_utime(ceph_mount_info *cmount, const char *path, utimbuf *buf)
+ int ceph_futime(ceph_mount_info *cmount, int fd, utimbuf *buf)
+ int ceph_utimes(ceph_mount_info *cmount, const char *path, timeval times[2])
+ int ceph_lutimes(ceph_mount_info *cmount, const char *path, timeval times[2])
+ int ceph_futimes(ceph_mount_info *cmount, int fd, timeval times[2])
+ int ceph_futimens(ceph_mount_info *cmount, int fd, timespec times[2])
class Error(Exception):
return val.decode(encoding)
+cdef timeval to_timeval(t):
+ """
+ return timeval equivalent from time
+ """
+ tt = int(t)
+ cdef timeval buf = timeval(tt, (t - tt) * 1000000)
+ return buf
+
+cdef timespec to_timespec(t):
+ """
+ return timespec equivalent from time
+ """
+ tt = int(t)
+ cdef timespec buf = timespec(tt, (t - tt) * 1000000000)
+ return buf
cdef char* opt_str(s) except? NULL:
if s is None:
raise make_ex(ret, "error in lseek")
return ret
+
+ def utime(self, path, times=None):
+ """
+ Set access and modification time for path
+
+ :param path: file path for which timestamps have to be changed
+ :param times: if times is not None, it must be a tuple (atime, mtime)
+ """
+
+ self.require_state("mounted")
+ path = cstr(path, 'path')
+ if times:
+ if not isinstance(times, tuple):
+ raise TypeError('times must be a tuple')
+ if not isinstance(times[0], int):
+ raise TypeError('atime must be an int')
+ if not isinstance(times[1], int):
+ raise TypeError('mtime must be an int')
+ actime = modtime = int(time.time())
+ if times:
+ actime = times[0]
+ modtime = times[1]
+
+ cdef:
+ char *pth = path
+ utimbuf buf = utimbuf(actime, modtime)
+ with nogil:
+ ret = ceph_utime(self.cluster, pth, &buf)
+ if ret < 0:
+ raise make_ex(ret, "error in utime {}".format(path.decode('utf-8')))
+
+ def futime(self, fd, times=None):
+ """
+ Set access and modification time for a file pointed by descriptor
+
+ :param fd: file descriptor of the open file
+ :param times: if times is not None, it must be a tuple (atime, mtime)
+ """
+
+ self.require_state("mounted")
+ if not isinstance(fd, int):
+ raise TypeError('fd must be an int')
+ if times:
+ if not isinstance(times, tuple):
+ raise TypeError('times must be a tuple')
+ if not isinstance(times[0], int):
+ raise TypeError('atime must be an int')
+ if not isinstance(times[1], int):
+ raise TypeError('mtime must be an int')
+ actime = modtime = int(time.time())
+ if times:
+ actime = times[0]
+ modtime = times[1]
+
+ cdef:
+ int _fd = fd
+ utimbuf buf = utimbuf(actime, modtime)
+ with nogil:
+ ret = ceph_futime(self.cluster, _fd, &buf)
+ if ret < 0:
+ raise make_ex(ret, "error in futime")
+
+ def utimes(self, path, times=None, follow_symlink=True):
+ """
+ Set access and modification time for path
+
+ :param path: file path for which timestamps have to be changed
+ :param times: if times is not None, it must be a tuple (atime, mtime)
+ :param follow_symlink: perform the operation on the target file if @path
+ is a symbolic link (default)
+ """
+
+ self.require_state("mounted")
+ path = cstr(path, 'path')
+ if times:
+ if not isinstance(times, tuple):
+ raise TypeError('times must be a tuple')
+ if not isinstance(times[0], (int, float)):
+ raise TypeError('atime must be an int or a float')
+ if not isinstance(times[1], (int, float)):
+ raise TypeError('mtime must be an int or a float')
+ actime = modtime = time.time()
+ if times:
+ actime = float(times[0])
+ modtime = float(times[1])
+
+ cdef:
+ char *pth = path
+ timeval *buf = [to_timeval(actime), to_timeval(modtime)]
+ if follow_symlink:
+ with nogil:
+ ret = ceph_utimes(self.cluster, pth, buf)
+ else:
+ with nogil:
+ ret = ceph_lutimes(self.cluster, pth, buf)
+ if ret < 0:
+ raise make_ex(ret, "error in utimes {}".format(path.decode('utf-8')))
+
+ def lutimes(self, path, times=None):
+ """
+ Set access and modification time for a file. If the file is a symbolic
+ link do not follow to the target.
+
+ :param path: file path for which timestamps have to be changed
+ :param times: if times is not None, it must be a tuple (atime, mtime)
+ """
+ self.utimes(path, times=times, follow_symlink=False)
+
+ def futimes(self, fd, times=None):
+ """
+ Set access and modification time for a file pointer by descriptor
+
+ :param fd: file descriptor of the open file
+ :param times: if times is not None, it must be a tuple (atime, mtime)
+ """
+
+ self.require_state("mounted")
+ if not isinstance(fd, int):
+ raise TypeError('fd must be an int')
+ if times:
+ if not isinstance(times, tuple):
+ raise TypeError('times must be a tuple')
+ if not isinstance(times[0], (int, float)):
+ raise TypeError('atime must be an int or a float')
+ if not isinstance(times[1], (int, float)):
+ raise TypeError('mtime must be an int or a float')
+ actime = modtime = time.time()
+ if times:
+ actime = float(times[0])
+ modtime = float(times[1])
+
+ cdef:
+ int _fd = fd
+ timeval *buf = [to_timeval(actime), to_timeval(modtime)]
+ with nogil:
+ ret = ceph_futimes(self.cluster, _fd, buf)
+ if ret < 0:
+ raise make_ex(ret, "error in futimes")
+
+ def futimens(self, fd, times=None):
+ """
+ Set access and modification time for a file pointer by descriptor
+
+ :param fd: file descriptor of the open file
+ :param times: if times is not None, it must be a tuple (atime, mtime)
+ """
+
+ self.require_state("mounted")
+ if not isinstance(fd, int):
+ raise TypeError('fd must be an int')
+ if times:
+ if not isinstance(times, tuple):
+ raise TypeError('times must be a tuple')
+ if not isinstance(times[0], (int, float)):
+ raise TypeError('atime must be an int or a float')
+ if not isinstance(times[1], (int, float)):
+ raise TypeError('mtime must be an int or a float')
+ actime = modtime = time.time()
+ if times:
+ actime = float(times[0])
+ modtime = float(times[1])
+
+ cdef:
+ int _fd = fd
+ timespec *buf = [to_timespec(actime), to_timespec(modtime)]
+ with nogil:
+ ret = ceph_futimens(self.cluster, _fd, buf)
+ if ret < 0:
+ raise make_ex(ret, "error in futimens")
# vim: expandtab smarttab shiftwidth=4 softtabstop=4
-from nose.tools import assert_raises, assert_equal, with_setup
+from nose.tools import assert_raises, assert_equal, assert_greater, with_setup
import cephfs as libcephfs
import fcntl
import os
+import time
+from datetime import datetime
cephfs = None
assert_raises(libcephfs.Error, cephfs.mount, mount_root = b"/nowhere")
+@with_setup(setup_test)
+def test_utime():
+ fd = cephfs.open(b'/file-1', 'w', 0o755)
+ cephfs.write(fd, b'0000', 0)
+ cephfs.close(fd)
+
+ stx_pre = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ time.sleep(1)
+ cephfs.utime(b'/file-1')
+
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_greater(stx_post['atime'], stx_pre['atime'])
+ assert_greater(stx_post['mtime'], stx_pre['mtime'])
+
+ atime_pre = int(time.mktime(stx_pre['atime'].timetuple()))
+ mtime_pre = int(time.mktime(stx_pre['mtime'].timetuple()))
+
+ cephfs.utime(b'/file-1', (atime_pre, mtime_pre))
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_equal(stx_post['atime'], stx_pre['atime'])
+ assert_equal(stx_post['mtime'], stx_pre['mtime'])
+
+ cephfs.unlink(b'/file-1')
+
+@with_setup(setup_test)
+def test_futime():
+ fd = cephfs.open(b'/file-1', 'w', 0o755)
+ cephfs.write(fd, b'0000', 0)
+
+ stx_pre = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ time.sleep(1)
+ cephfs.futime(fd)
+
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_greater(stx_post['atime'], stx_pre['atime'])
+ assert_greater(stx_post['mtime'], stx_pre['mtime'])
+
+ atime_pre = int(time.mktime(stx_pre['atime'].timetuple()))
+ mtime_pre = int(time.mktime(stx_pre['mtime'].timetuple()))
+
+ cephfs.futime(fd, (atime_pre, mtime_pre))
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_equal(stx_post['atime'], stx_pre['atime'])
+ assert_equal(stx_post['mtime'], stx_pre['mtime'])
+
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-1')
+
+@with_setup(setup_test)
+def test_utimes():
+ fd = cephfs.open(b'/file-1', 'w', 0o755)
+ cephfs.write(fd, b'0000', 0)
+ cephfs.close(fd)
+
+ stx_pre = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ time.sleep(1)
+ cephfs.utimes(b'/file-1')
+
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_greater(stx_post['atime'], stx_pre['atime'])
+ assert_greater(stx_post['mtime'], stx_pre['mtime'])
+
+ atime_pre = time.mktime(stx_pre['atime'].timetuple())
+ mtime_pre = time.mktime(stx_pre['mtime'].timetuple())
+
+ cephfs.utimes(b'/file-1', (atime_pre, mtime_pre))
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_equal(stx_post['atime'], stx_pre['atime'])
+ assert_equal(stx_post['mtime'], stx_pre['mtime'])
+
+ cephfs.unlink(b'/file-1')
+
+@with_setup(setup_test)
+def test_lutimes():
+ fd = cephfs.open(b'/file-1', 'w', 0o755)
+ cephfs.write(fd, b'0000', 0)
+ cephfs.close(fd)
+
+ cephfs.symlink(b'/file-1', b'/file-2')
+
+ stx_pre_t = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+ stx_pre_s = cephfs.statx(b'/file-2', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, libcephfs.AT_SYMLINK_NOFOLLOW)
+
+ time.sleep(1)
+ cephfs.lutimes(b'/file-2')
+
+ stx_post_t = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+ stx_post_s = cephfs.statx(b'/file-2', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, libcephfs.AT_SYMLINK_NOFOLLOW)
+
+ assert_equal(stx_post_t['atime'], stx_pre_t['atime'])
+ assert_equal(stx_post_t['mtime'], stx_pre_t['mtime'])
+
+ assert_greater(stx_post_s['atime'], stx_pre_s['atime'])
+ assert_greater(stx_post_s['mtime'], stx_pre_s['mtime'])
+
+ atime_pre = time.mktime(stx_pre_s['atime'].timetuple())
+ mtime_pre = time.mktime(stx_pre_s['mtime'].timetuple())
+
+ cephfs.lutimes(b'/file-2', (atime_pre, mtime_pre))
+ stx_post_s = cephfs.statx(b'/file-2', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, libcephfs.AT_SYMLINK_NOFOLLOW)
+
+ assert_equal(stx_post_s['atime'], stx_pre_s['atime'])
+ assert_equal(stx_post_s['mtime'], stx_pre_s['mtime'])
+
+ cephfs.unlink(b'/file-2')
+ cephfs.unlink(b'/file-1')
+
+@with_setup(setup_test)
+def test_futimes():
+ fd = cephfs.open(b'/file-1', 'w', 0o755)
+ cephfs.write(fd, b'0000', 0)
+
+ stx_pre = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ time.sleep(1)
+ cephfs.futimes(fd)
+
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_greater(stx_post['atime'], stx_pre['atime'])
+ assert_greater(stx_post['mtime'], stx_pre['mtime'])
+
+ atime_pre = time.mktime(stx_pre['atime'].timetuple())
+ mtime_pre = time.mktime(stx_pre['mtime'].timetuple())
+
+ cephfs.futimes(fd, (atime_pre, mtime_pre))
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_equal(stx_post['atime'], stx_pre['atime'])
+ assert_equal(stx_post['mtime'], stx_pre['mtime'])
+
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-1')
+
+@with_setup(setup_test)
+def test_futimens():
+ fd = cephfs.open(b'/file-1', 'w', 0o755)
+ cephfs.write(fd, b'0000', 0)
+
+ stx_pre = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ time.sleep(1)
+ cephfs.futimens(fd)
+
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_greater(stx_post['atime'], stx_pre['atime'])
+ assert_greater(stx_post['mtime'], stx_pre['mtime'])
+
+ atime_pre = time.mktime(stx_pre['atime'].timetuple())
+ mtime_pre = time.mktime(stx_pre['mtime'].timetuple())
+
+ cephfs.futimens(fd, (atime_pre, mtime_pre))
+ stx_post = cephfs.statx(b'/file-1', libcephfs.CEPH_STATX_ATIME | libcephfs.CEPH_STATX_MTIME, 0)
+
+ assert_equal(stx_post['atime'], stx_pre['atime'])
+ assert_equal(stx_post['mtime'], stx_pre['mtime'])
+
+ cephfs.close(fd)
+ cephfs.unlink(b'/file-1')