]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/cephfs/cephfs.pyx
340035400359e6b67b81695ccccceecf042cc2c0
[ceph.git] / ceph / src / pybind / cephfs / cephfs.pyx
1 """
2 This module is a thin wrapper around libcephfs.
3 """
4
5 from cpython cimport PyObject, ref, exc
6 from libc.stdint cimport *
7 from libc.stdlib cimport malloc, realloc, free
8
9 from types cimport *
10 IF BUILD_DOC:
11 include "mock_cephfs.pxi"
12 cdef class Rados:
13 cdef:
14 rados_t cluster
15 ELSE:
16 from c_cephfs cimport *
17 from rados cimport Rados
18
19 from collections import namedtuple
20 from datetime import datetime
21 import os
22 import time
23
24 AT_NO_ATTR_SYNC = 0x4000
25 AT_SYMLINK_NOFOLLOW = 0x100
26 cdef int AT_SYMLINK_NOFOLLOW_CDEF = AT_SYMLINK_NOFOLLOW
27 CEPH_STATX_BASIC_STATS = 0x7ff
28 cdef int CEPH_STATX_BASIC_STATS_CDEF = CEPH_STATX_BASIC_STATS
29 CEPH_STATX_MODE = 0x1
30 CEPH_STATX_NLINK = 0x2
31 CEPH_STATX_UID = 0x4
32 CEPH_STATX_GID = 0x8
33 CEPH_STATX_RDEV = 0x10
34 CEPH_STATX_ATIME = 0x20
35 CEPH_STATX_MTIME = 0x40
36 CEPH_STATX_CTIME = 0x80
37 CEPH_STATX_INO = 0x100
38 CEPH_STATX_SIZE = 0x200
39 CEPH_STATX_BLOCKS = 0x400
40 CEPH_STATX_BTIME = 0x800
41 CEPH_STATX_VERSION = 0x1000
42
43 FALLOC_FL_KEEP_SIZE = 0x01
44 FALLOC_FL_PUNCH_HOLE = 0x02
45 FALLOC_FL_NO_HIDE_STALE = 0x04
46
47 CEPH_SETATTR_MODE = 0x1
48 CEPH_SETATTR_UID = 0x2
49 CEPH_SETATTR_GID = 0x4
50 CEPH_SETATTR_MTIME = 0x8
51 CEPH_SETATTR_ATIME = 0x10
52 CEPH_SETATTR_SIZE = 0x20
53 CEPH_SETATTR_CTIME = 0x40
54 CEPH_SETATTR_BTIME = 0x200
55
56 # errno definitions
57 cdef enum:
58 CEPHFS_EBLOCKLISTED = 108
59 CEPHFS_EPERM = 1
60 CEPHFS_ESTALE = 116
61 CEPHFS_ENOSPC = 28
62 CEPHFS_ETIMEDOUT = 110
63 CEPHFS_EIO = 5
64 CEPHFS_ENOTCONN = 107
65 CEPHFS_EEXIST = 17
66 CEPHFS_EINTR = 4
67 CEPHFS_EINVAL = 22
68 CEPHFS_EBADF = 9
69 CEPHFS_EROFS = 30
70 CEPHFS_EAGAIN = 11
71 CEPHFS_EACCES = 13
72 CEPHFS_ELOOP = 40
73 CEPHFS_EISDIR = 21
74 CEPHFS_ENOENT = 2
75 CEPHFS_ENOTDIR = 20
76 CEPHFS_ENAMETOOLONG = 36
77 CEPHFS_EBUSY = 16
78 CEPHFS_EDQUOT = 122
79 CEPHFS_EFBIG = 27
80 CEPHFS_ERANGE = 34
81 CEPHFS_ENXIO = 6
82 CEPHFS_ECANCELED = 125
83 CEPHFS_ENODATA = 61
84 CEPHFS_EOPNOTSUPP = 95
85 CEPHFS_EXDEV = 18
86 CEPHFS_ENOMEM = 12
87 CEPHFS_ENOTRECOVERABLE = 131
88 CEPHFS_ENOSYS = 38
89 CEPHFS_EWOULDBLOCK = CEPHFS_EAGAIN
90 CEPHFS_ENOTEMPTY = 39
91 CEPHFS_EDEADLK = 35
92 CEPHFS_EDEADLOCK = CEPHFS_EDEADLK
93 CEPHFS_EDOM = 33
94 CEPHFS_EMLINK = 31
95 CEPHFS_ETIME = 62
96 CEPHFS_EOLDSNAPC = 85
97
98 cdef extern from "Python.h":
99 # These are in cpython/string.pxd, but use "object" types instead of
100 # PyObject*, which invokes assumptions in cpython that we need to
101 # legitimately break to implement zero-copy string buffers in Image.read().
102 # This is valid use of the Python API and documented as a special case.
103 PyObject *PyBytes_FromStringAndSize(char *v, Py_ssize_t len) except NULL
104 char* PyBytes_AsString(PyObject *string) except NULL
105 int _PyBytes_Resize(PyObject **string, Py_ssize_t newsize) except -1
106 void PyEval_InitThreads()
107
108
109 class Error(Exception):
110 def get_error_code(self):
111 return 1
112
113
114 class LibCephFSStateError(Error):
115 pass
116
117
118 class OSError(Error):
119 def __init__(self, errno, strerror):
120 super(OSError, self).__init__(errno, strerror)
121 self.errno = errno
122 self.strerror = "%s: %s" % (strerror, os.strerror(errno))
123
124 def __str__(self):
125 return '{} [Errno {}]'.format(self.strerror, self.errno)
126
127 def get_error_code(self):
128 return self.errno
129
130
131 class PermissionError(OSError):
132 pass
133
134
135 class ObjectNotFound(OSError):
136 pass
137
138
139 class NoData(OSError):
140 pass
141
142
143 class ObjectExists(OSError):
144 pass
145
146
147 class IOError(OSError):
148 pass
149
150
151 class NoSpace(OSError):
152 pass
153
154
155 class InvalidValue(OSError):
156 pass
157
158
159 class OperationNotSupported(OSError):
160 pass
161
162
163 class WouldBlock(OSError):
164 pass
165
166
167 class OutOfRange(OSError):
168 pass
169
170
171 class ObjectNotEmpty(OSError):
172 pass
173
174 class NotDirectory(OSError):
175 pass
176
177 class DiskQuotaExceeded(OSError):
178 pass
179
180
181 cdef errno_to_exception = {
182 CEPHFS_EPERM : PermissionError,
183 CEPHFS_ENOENT : ObjectNotFound,
184 CEPHFS_EIO : IOError,
185 CEPHFS_ENOSPC : NoSpace,
186 CEPHFS_EEXIST : ObjectExists,
187 CEPHFS_ENODATA : NoData,
188 CEPHFS_EINVAL : InvalidValue,
189 CEPHFS_EOPNOTSUPP : OperationNotSupported,
190 CEPHFS_ERANGE : OutOfRange,
191 CEPHFS_EWOULDBLOCK: WouldBlock,
192 CEPHFS_ENOTEMPTY : ObjectNotEmpty,
193 CEPHFS_ENOTDIR : NotDirectory,
194 CEPHFS_EDQUOT : DiskQuotaExceeded,
195 }
196
197
198 cdef make_ex(ret, msg):
199 """
200 Translate a libcephfs return code into an exception.
201
202 :param ret: the return code
203 :type ret: int
204 :param msg: the error message to use
205 :type msg: str
206 :returns: a subclass of :class:`Error`
207 """
208 ret = abs(ret)
209 if ret in errno_to_exception:
210 return errno_to_exception[ret](ret, msg)
211 else:
212 return OSError(ret, msg)
213
214
215 class DirEntry(namedtuple('DirEntry',
216 ['d_ino', 'd_off', 'd_reclen', 'd_type', 'd_name'])):
217 DT_DIR = 0x4
218 DT_REG = 0x8
219 DT_LNK = 0xA
220 def is_dir(self):
221 return self.d_type == self.DT_DIR
222
223 def is_symbol_file(self):
224 return self.d_type == self.DT_LNK
225
226 def is_file(self):
227 return self.d_type == self.DT_REG
228
229 StatResult = namedtuple('StatResult',
230 ["st_dev", "st_ino", "st_mode", "st_nlink", "st_uid",
231 "st_gid", "st_rdev", "st_size", "st_blksize",
232 "st_blocks", "st_atime", "st_mtime", "st_ctime"])
233
234 cdef class DirResult(object):
235 cdef LibCephFS lib
236 cdef ceph_dir_result* handle
237
238 # Bug in older Cython instances prevents this from being a static method.
239 # @staticmethod
240 # cdef create(LibCephFS lib, ceph_dir_result* handle):
241 # d = DirResult()
242 # d.lib = lib
243 # d.handle = handle
244 # return d
245
246 def __dealloc__(self):
247 self.close()
248
249 def __enter__(self):
250 if not self.handle:
251 raise make_ex(CEPHFS_EBADF, "dir is not open")
252 self.lib.require_state("mounted")
253 with nogil:
254 ceph_rewinddir(self.lib.cluster, self.handle)
255 return self
256
257 def __exit__(self, type_, value, traceback):
258 self.close()
259 return False
260
261 def readdir(self):
262 self.lib.require_state("mounted")
263
264 with nogil:
265 dirent = ceph_readdir(self.lib.cluster, self.handle)
266 if not dirent:
267 return None
268
269 IF UNAME_SYSNAME == "FreeBSD" or UNAME_SYSNAME == "Darwin":
270 return DirEntry(d_ino=dirent.d_ino,
271 d_off=0,
272 d_reclen=dirent.d_reclen,
273 d_type=dirent.d_type,
274 d_name=dirent.d_name)
275 ELSE:
276 return DirEntry(d_ino=dirent.d_ino,
277 d_off=dirent.d_off,
278 d_reclen=dirent.d_reclen,
279 d_type=dirent.d_type,
280 d_name=dirent.d_name)
281
282 def close(self):
283 if self.handle:
284 self.lib.require_state("mounted")
285 with nogil:
286 ret = ceph_closedir(self.lib.cluster, self.handle)
287 if ret < 0:
288 raise make_ex(ret, "closedir failed")
289 self.handle = NULL
290
291 def rewinddir(self):
292 if not self.handle:
293 raise make_ex(CEPHFS_EBADF, "dir is not open")
294 self.lib.require_state("mounted")
295 with nogil:
296 ceph_rewinddir(self.lib.cluster, self.handle)
297
298 def telldir(self):
299 if not self.handle:
300 raise make_ex(CEPHFS_EBADF, "dir is not open")
301 self.lib.require_state("mounted")
302 with nogil:
303 ret = ceph_telldir(self.lib.cluster, self.handle)
304 if ret < 0:
305 raise make_ex(ret, "telldir failed")
306 return ret
307
308 def seekdir(self, offset):
309 if not self.handle:
310 raise make_ex(CEPHFS_EBADF, "dir is not open")
311 if not isinstance(offset, int):
312 raise TypeError('offset must be an int')
313 self.lib.require_state("mounted")
314 cdef int64_t _offset = offset
315 with nogil:
316 ceph_seekdir(self.lib.cluster, self.handle, _offset)
317
318
319 def cstr(val, name, encoding="utf-8", opt=False):
320 """
321 Create a byte string from a Python string
322
323 :param basestring val: Python string
324 :param str name: Name of the string parameter, for exceptions
325 :param str encoding: Encoding to use
326 :param bool opt: If True, None is allowed
327 :rtype: bytes
328 :raises: :class:`InvalidArgument`
329 """
330 if opt and val is None:
331 return None
332 if isinstance(val, bytes):
333 return val
334 else:
335 try:
336 v = val.encode(encoding)
337 except:
338 raise TypeError('%s must be encodeable as a bytearray' % name)
339 assert isinstance(v, bytes)
340 return v
341
342 def cstr_list(list_str, name, encoding="utf-8"):
343 return [cstr(s, name) for s in list_str]
344
345
346 def decode_cstr(val, encoding="utf-8"):
347 """
348 Decode a byte string into a Python string.
349
350 :param bytes val: byte string
351 :rtype: str or None
352 """
353 if val is None:
354 return None
355
356 return val.decode(encoding)
357
358 cdef timeval to_timeval(t):
359 """
360 return timeval equivalent from time
361 """
362 tt = int(t)
363 cdef timeval buf = timeval(tt, (t - tt) * 1000000)
364 return buf
365
366 cdef timespec to_timespec(t):
367 """
368 return timespec equivalent from time
369 """
370 tt = int(t)
371 cdef timespec buf = timespec(tt, (t - tt) * 1000000000)
372 return buf
373
374 cdef char* opt_str(s) except? NULL:
375 if s is None:
376 return NULL
377 return s
378
379
380 cdef char ** to_bytes_array(list_bytes):
381 cdef char **ret = <char **>malloc(len(list_bytes) * sizeof(char *))
382 if ret == NULL:
383 raise MemoryError("malloc failed")
384 for i in range(len(list_bytes)):
385 ret[i] = <char *>list_bytes[i]
386 return ret
387
388
389 cdef void* realloc_chk(void* ptr, size_t size) except NULL:
390 cdef void *ret = realloc(ptr, size)
391 if ret == NULL:
392 raise MemoryError("realloc failed")
393 return ret
394
395
396 cdef iovec * to_iovec(buffers) except NULL:
397 cdef iovec *iov = <iovec *>malloc(len(buffers) * sizeof(iovec))
398 cdef char *s = NULL
399 if iov == NULL:
400 raise MemoryError("malloc failed")
401 for i in xrange(len(buffers)):
402 s = <char*>buffers[i]
403 iov[i] = [<void*>s, len(buffers[i])]
404 return iov
405
406
407 cdef class LibCephFS(object):
408 """libcephfs python wrapper"""
409
410 cdef public object state
411 cdef ceph_mount_info *cluster
412
413 def require_state(self, *args):
414 if self.state in args:
415 return
416 raise LibCephFSStateError("You cannot perform that operation on a "
417 "CephFS object in state %s." % (self.state))
418
419 def __cinit__(self, conf=None, conffile=None, auth_id=None, rados_inst=None):
420 """Create a libcephfs wrapper
421
422 :param conf dict opt: settings overriding the default ones and conffile
423 :param conffile str opt: the path to ceph.conf to override the default settings
424 :auth_id str opt: the id used to authenticate the client entity
425 :rados_inst Rados opt: a rados.Rados instance
426 """
427 PyEval_InitThreads()
428 self.state = "uninitialized"
429 if rados_inst is not None:
430 if auth_id is not None or conffile is not None or conf is not None:
431 raise make_ex(CEPHFS_EINVAL,
432 "May not pass RADOS instance as well as other configuration")
433
434 self.create_with_rados(rados_inst)
435 else:
436 self.create(conf, conffile, auth_id)
437
438 def create_with_rados(self, Rados rados_inst):
439 cdef int ret
440 with nogil:
441 ret = ceph_create_from_rados(&self.cluster, rados_inst.cluster)
442 if ret != 0:
443 raise Error("libcephfs_initialize failed with error code: %d" % ret)
444 self.state = "configuring"
445
446 NO_CONF_FILE = -1
447 "special value that indicates no conffile should be read when creating a mount handle"
448 DEFAULT_CONF_FILES = -2
449 "special value that indicates the default conffiles should be read when creating a mount handle"
450
451 def create(self, conf=None, conffile=NO_CONF_FILE, auth_id=None):
452 """
453 Create a mount handle for interacting with Ceph. All libcephfs
454 functions operate on a mount info handle.
455
456 :param conf dict opt: settings overriding the default ones and conffile
457 :param conffile Union[int,str], optional: the path to ceph.conf to override the default settings
458 :auth_id str opt: the id used to authenticate the client entity
459 """
460 if conf is not None and not isinstance(conf, dict):
461 raise TypeError("conf must be dict or None")
462 cstr(conffile, 'configfile', opt=True)
463 auth_id = cstr(auth_id, 'auth_id', opt=True)
464
465 cdef:
466 char* _auth_id = opt_str(auth_id)
467 int ret
468
469 with nogil:
470 ret = ceph_create(&self.cluster, <const char*>_auth_id)
471 if ret != 0:
472 raise Error("libcephfs_initialize failed with error code: %d" % ret)
473
474 self.state = "configuring"
475 if conffile in (self.NO_CONF_FILE, None):
476 pass
477 elif conffile in (self.DEFAULT_CONF_FILES, ''):
478 self.conf_read_file(None)
479 else:
480 self.conf_read_file(conffile)
481 if conf is not None:
482 for key, value in conf.items():
483 self.conf_set(key, value)
484
485 def get_addrs(self):
486 """
487 Get associated client addresses with this RADOS session.
488 """
489 self.require_state("mounted")
490
491 cdef:
492 char* addrs = NULL
493
494 try:
495
496 with nogil:
497 ret = ceph_getaddrs(self.cluster, &addrs)
498 if ret:
499 raise make_ex(ret, "error calling getaddrs")
500
501 return decode_cstr(addrs)
502 finally:
503 ceph_buffer_free(addrs)
504
505
506 def conf_read_file(self, conffile=None):
507 """
508 Load the ceph configuration from the specified config file.
509
510 :param conffile str opt: the path to ceph.conf to override the default settings
511 """
512 conffile = cstr(conffile, 'conffile', opt=True)
513 cdef:
514 char *_conffile = opt_str(conffile)
515 with nogil:
516 ret = ceph_conf_read_file(self.cluster, <const char*>_conffile)
517 if ret != 0:
518 raise make_ex(ret, "error calling conf_read_file")
519
520 def conf_parse_argv(self, argv):
521 """
522 Parse the command line arguments and load the configuration parameters.
523
524 :param argv: the argument list
525 """
526 self.require_state("configuring")
527 cargv = cstr_list(argv, 'argv')
528 cdef:
529 int _argc = len(argv)
530 char **_argv = to_bytes_array(cargv)
531
532 try:
533 with nogil:
534 ret = ceph_conf_parse_argv(self.cluster, _argc,
535 <const char **>_argv)
536 if ret != 0:
537 raise make_ex(ret, "error calling conf_parse_argv")
538 finally:
539 free(_argv)
540
541 def shutdown(self):
542 """
543 Unmount and destroy the ceph mount handle.
544 """
545 if self.state in ["initialized", "mounted"]:
546 with nogil:
547 ceph_shutdown(self.cluster)
548 self.state = "shutdown"
549
550 def __enter__(self):
551 self.mount()
552 return self
553
554 def __exit__(self, type_, value, traceback):
555 self.shutdown()
556 return False
557
558 def __dealloc__(self):
559 self.shutdown()
560
561 def version(self):
562 """
563 Get the version number of the ``libcephfs`` C library.
564
565 :returns: a tuple of ``(major, minor, extra)`` components of the
566 libcephfs version
567 """
568 cdef:
569 int major = 0
570 int minor = 0
571 int extra = 0
572 with nogil:
573 ceph_version(&major, &minor, &extra)
574 return (major, minor, extra)
575
576 def conf_get(self, option):
577 """
578 Gets the configuration value as a string.
579
580 :param option: the config option to get
581 """
582 self.require_state("configuring", "initialized", "mounted")
583
584 option = cstr(option, 'option')
585 cdef:
586 char *_option = option
587 size_t length = 20
588 char *ret_buf = NULL
589
590 try:
591 while True:
592 ret_buf = <char *>realloc_chk(ret_buf, length)
593 with nogil:
594 ret = ceph_conf_get(self.cluster, _option, ret_buf, length)
595 if ret == 0:
596 return decode_cstr(ret_buf)
597 elif ret == -CEPHFS_ENAMETOOLONG:
598 length = length * 2
599 elif ret == -CEPHFS_ENOENT:
600 return None
601 else:
602 raise make_ex(ret, "error calling conf_get")
603 finally:
604 free(ret_buf)
605
606 def conf_set(self, option, val):
607 """
608 Sets a configuration value from a string.
609
610 :param option: the configuration option to set
611 :param value: the value of the configuration option to set
612 """
613 self.require_state("configuring", "initialized", "mounted")
614
615 option = cstr(option, 'option')
616 val = cstr(val, 'val')
617 cdef:
618 char *_option = option
619 char *_val = val
620
621 with nogil:
622 ret = ceph_conf_set(self.cluster, _option, _val)
623 if ret != 0:
624 raise make_ex(ret, "error calling conf_set")
625
626 def init(self):
627 """
628 Initialize the filesystem client (but do not mount the filesystem yet)
629 """
630 self.require_state("configuring")
631 with nogil:
632 ret = ceph_init(self.cluster)
633 if ret != 0:
634 raise make_ex(ret, "error calling ceph_init")
635 self.state = "initialized"
636
637 def mount(self, mount_root=None, filesystem_name=None):
638 """
639 Perform a mount using the path for the root of the mount.
640 """
641 if self.state == "configuring":
642 self.init()
643 self.require_state("initialized")
644
645 # Configure which filesystem to mount if one was specified
646 if filesystem_name is None:
647 filesystem_name = b""
648 else:
649 filesystem_name = cstr(filesystem_name, 'filesystem_name')
650 cdef:
651 char *_filesystem_name = filesystem_name
652 if filesystem_name:
653 with nogil:
654 ret = ceph_select_filesystem(self.cluster,
655 _filesystem_name)
656 if ret != 0:
657 raise make_ex(ret, "error calling ceph_select_filesystem")
658
659 # Prepare mount_root argument, default to "/"
660 root = b"/" if mount_root is None else mount_root
661 cdef:
662 char *_mount_root = root
663
664 with nogil:
665 ret = ceph_mount(self.cluster, _mount_root)
666 if ret != 0:
667 raise make_ex(ret, "error calling ceph_mount")
668 self.state = "mounted"
669
670 def unmount(self):
671 """
672 Unmount a mount handle.
673 """
674 self.require_state("mounted")
675 with nogil:
676 ret = ceph_unmount(self.cluster)
677 if ret != 0:
678 raise make_ex(ret, "error calling ceph_unmount")
679 self.state = "initialized"
680
681 def abort_conn(self):
682 """
683 Abort mds connections.
684 """
685 self.require_state("mounted")
686 with nogil:
687 ret = ceph_abort_conn(self.cluster)
688 if ret != 0:
689 raise make_ex(ret, "error calling ceph_abort_conn")
690 self.state = "initialized"
691
692 def get_instance_id(self):
693 """
694 Get a global id for current instance
695 """
696 self.require_state("initialized", "mounted")
697 with nogil:
698 ret = ceph_get_instance_id(self.cluster)
699 return ret;
700
701 def statfs(self, path):
702 """
703 Perform a statfs on the ceph file system. This call fills in file system wide statistics
704 into the passed in buffer.
705
706 :param path: any path within the mounted filesystem
707 """
708 self.require_state("mounted")
709 path = cstr(path, 'path')
710 cdef:
711 char* _path = path
712 statvfs statbuf
713
714 with nogil:
715 ret = ceph_statfs(self.cluster, _path, &statbuf)
716 if ret < 0:
717 raise make_ex(ret, "statfs failed: %s" % path)
718 return {'f_bsize': statbuf.f_bsize,
719 'f_frsize': statbuf.f_frsize,
720 'f_blocks': statbuf.f_blocks,
721 'f_bfree': statbuf.f_bfree,
722 'f_bavail': statbuf.f_bavail,
723 'f_files': statbuf.f_files,
724 'f_ffree': statbuf.f_ffree,
725 'f_favail': statbuf.f_favail,
726 'f_fsid': statbuf.f_fsid,
727 'f_flag': statbuf.f_flag,
728 'f_namemax': statbuf.f_namemax}
729
730 def sync_fs(self):
731 """
732 Synchronize all filesystem data to persistent media
733 """
734 self.require_state("mounted")
735 with nogil:
736 ret = ceph_sync_fs(self.cluster)
737 if ret < 0:
738 raise make_ex(ret, "sync_fs failed")
739
740 def fsync(self, int fd, int syncdataonly):
741 """
742 Synchronize an open file to persistent media.
743
744 :param fd: the file descriptor of the file to sync.
745 :param syncdataonly: a boolean whether to synchronize metadata and data (0)
746 or just data (1).
747 """
748 self.require_state("mounted")
749 with nogil:
750 ret = ceph_fsync(self.cluster, fd, syncdataonly)
751 if ret < 0:
752 raise make_ex(ret, "fsync failed")
753
754 def lazyio(self, fd, enable):
755 """
756 Enable/disable lazyio for the file.
757
758 :param fd: the file descriptor of the file for which to enable lazio.
759 :param enable: a boolean to enable lazyio or disable lazyio.
760 """
761
762 self.require_state("mounted")
763 if not isinstance(fd, int):
764 raise TypeError('fd must be an int')
765 if not isinstance(enable, int):
766 raise TypeError('enable must be an int')
767
768 cdef:
769 int _fd = fd
770 int _enable = enable
771
772 with nogil:
773 ret = ceph_lazyio(self.cluster, _fd, _enable)
774 if ret < 0:
775 raise make_ex(ret, "lazyio failed")
776
777 def lazyio_propagate(self, fd, offset, count):
778 """
779 Flushes the write buffer for the file thereby propogating the buffered write to the file.
780
781 :param fd: the file descriptor of the file to sync.
782 :param offset: the byte range starting.
783 :param count: the number of bytes starting from offset.
784 """
785
786 self.require_state("mounted")
787 if not isinstance(fd, int):
788 raise TypeError('fd must be an int')
789 if not isinstance(offset, int):
790 raise TypeError('offset must be an int')
791 if not isinstance(count, int):
792 raise TypeError('count must be an int')
793
794 cdef:
795 int _fd = fd
796 int64_t _offset = offset
797 size_t _count = count
798
799 with nogil:
800 ret = ceph_lazyio_propagate(self.cluster, _fd, _offset, _count)
801 if ret < 0:
802 raise make_ex(ret, "lazyio_propagate failed")
803
804 def lazyio_synchronize(self, fd, offset, count):
805 """
806 Flushes the write buffer for the file and invalidate the read cache. This allows a
807 subsequent read operation to read and cache data directly from the file and hence
808 everyone's propagated writes would be visible.
809
810 :param fd: the file descriptor of the file to sync.
811 :param offset: the byte range starting.
812 :param count: the number of bytes starting from offset.
813 """
814
815 self.require_state("mounted")
816 if not isinstance(fd, int):
817 raise TypeError('fd must be an int')
818 if not isinstance(offset, int):
819 raise TypeError('offset must be an int')
820 if not isinstance(count, int):
821 raise TypeError('count must be an int')
822
823 cdef:
824 int _fd = fd
825 int64_t _offset = offset
826 size_t _count = count
827
828 with nogil:
829 ret = ceph_lazyio_synchronize(self.cluster, _fd, _offset, _count)
830 if ret < 0:
831 raise make_ex(ret, "lazyio_synchronize failed")
832
833 def fallocate(self, fd, offset, length, mode=0):
834 """
835 Preallocate or release disk space for the file for the byte range.
836
837 :param fd: the file descriptor of the file to fallocate.
838 :param mode: the flags determines the operation to be performed on the given
839 range. default operation (0) allocate and initialize to zero
840 the file in the byte range, and the file size will be changed
841 if offset + length is greater than the file size. if the
842 FALLOC_FL_KEEP_SIZE flag is specified in the mode, the file size
843 will not be changed. if the FALLOC_FL_PUNCH_HOLE flag is specified
844 in the mode, the operation is deallocate space and zero the byte range.
845 :param offset: the byte range starting.
846 :param length: the length of the range.
847 """
848
849 self.require_state("mounted")
850 if not isinstance(fd, int):
851 raise TypeError('fd must be an int')
852 if not isinstance(mode, int):
853 raise TypeError('mode must be an int')
854 if not isinstance(offset, int):
855 raise TypeError('offset must be an int')
856 if not isinstance(length, int):
857 raise TypeError('length must be an int')
858
859 cdef:
860 int _fd = fd
861 int _mode = mode
862 int64_t _offset = offset
863 int64_t _length = length
864
865 with nogil:
866 ret = ceph_fallocate(self.cluster, _fd, _mode, _offset, _length)
867 if ret < 0:
868 raise make_ex(ret, "fallocate failed")
869
870 def getcwd(self):
871 """
872 Get the current working directory.
873
874 :rtype the path to the current working directory
875 """
876 self.require_state("mounted")
877 with nogil:
878 ret = ceph_getcwd(self.cluster)
879 return ret
880
881 def chdir(self, path):
882 """
883 Change the current working directory.
884
885 :param path the path to the working directory to change into.
886 """
887 self.require_state("mounted")
888
889 path = cstr(path, 'path')
890 cdef char* _path = path
891 with nogil:
892 ret = ceph_chdir(self.cluster, _path)
893 if ret < 0:
894 raise make_ex(ret, "chdir failed")
895
896 def opendir(self, path):
897 """
898 Open the given directory.
899
900 :param path: the path name of the directory to open. Must be either an absolute path
901 or a path relative to the current working directory.
902 :rtype handle: the open directory stream handle
903 """
904 self.require_state("mounted")
905
906 path = cstr(path, 'path')
907 cdef:
908 char* _path = path
909 ceph_dir_result* handle
910 with nogil:
911 ret = ceph_opendir(self.cluster, _path, &handle);
912 if ret < 0:
913 raise make_ex(ret, "opendir failed")
914 d = DirResult()
915 d.lib = self
916 d.handle = handle
917 return d
918
919 def readdir(self, DirResult handle):
920 """
921 Get the next entry in an open directory.
922
923 :param handle: the open directory stream handle
924 :rtype dentry: the next directory entry or None if at the end of the
925 directory (or the directory is empty. This pointer
926 should not be freed by the caller, and is only safe to
927 access between return and the next call to readdir or
928 closedir.
929 """
930 self.require_state("mounted")
931
932 return handle.readdir()
933
934 def closedir(self, DirResult handle):
935 """
936 Close the open directory.
937
938 :param handle: the open directory stream handle
939 """
940 self.require_state("mounted")
941
942 return handle.close()
943
944 def rewinddir(self, DirResult handle):
945 """
946 Rewind the directory stream to the beginning of the directory.
947
948 :param handle: the open directory stream handle
949 """
950 return handle.rewinddir()
951
952 def telldir(self, DirResult handle):
953 """
954 Get the current position of a directory stream.
955
956 :param handle: the open directory stream handle
957 :return value: The position of the directory stream. Note that the offsets
958 returned by ceph_telldir do not have a particular order (cannot
959 be compared with inequality).
960 """
961 return handle.telldir()
962
963 def seekdir(self, DirResult handle, offset):
964 """
965 Move the directory stream to a position specified by the given offset.
966
967 :param handle: the open directory stream handle
968 :param offset: the position to move the directory stream to. This offset should be
969 a value returned by telldir. Note that this value does not refer to
970 the nth entry in a directory, and can not be manipulated with plus
971 or minus.
972 """
973 return handle.seekdir(offset)
974
975 def mkdir(self, path, mode):
976 """
977 Create a directory.
978
979 :param path: the path of the directory to create. This must be either an
980 absolute path or a relative path off of the current working directory.
981 :param mode: the permissions the directory should have once created.
982 """
983
984 self.require_state("mounted")
985 path = cstr(path, 'path')
986 if not isinstance(mode, int):
987 raise TypeError('mode must be an int')
988 cdef:
989 char* _path = path
990 int _mode = mode
991 with nogil:
992 ret = ceph_mkdir(self.cluster, _path, _mode)
993 if ret < 0:
994 raise make_ex(ret, "error in mkdir {}".format(path.decode('utf-8')))
995
996 def mksnap(self, path, name, mode, metadata={}):
997 """
998 Create a snapshot.
999
1000 :param path: path of the directory to snapshot.
1001 :param name: snapshot name
1002 :param mode: permission of the snapshot
1003 :param metadata: metadata key/value to store with the snapshot
1004
1005 :raises: :class: `TypeError`
1006 :raises: :class: `Error`
1007 :returns: int: 0 on success
1008 """
1009
1010 self.require_state("mounted")
1011 path = cstr(path, 'path')
1012 name = cstr(name, 'name')
1013 if not isinstance(mode, int):
1014 raise TypeError('mode must be an int')
1015 if not isinstance(metadata, dict):
1016 raise TypeError('metadata must be an dictionary')
1017 md = {}
1018 for key, value in metadata.items():
1019 if not isinstance(key, str) or not isinstance(value, str):
1020 raise TypeError('metadata key and values should be strings')
1021 md[key.encode('utf-8')] = value.encode('utf-8')
1022 cdef:
1023 char* _path = path
1024 char* _name = name
1025 int _mode = mode
1026 size_t nr = len(md)
1027 snap_metadata *_snap_meta = <snap_metadata *>malloc(nr * sizeof(snap_metadata))
1028 if nr and _snap_meta == NULL:
1029 raise MemoryError("malloc failed")
1030 i = 0
1031 for key, value in md.items():
1032 _snap_meta[i] = snap_metadata(<char*>key, <char*>value)
1033 i += 1
1034 with nogil:
1035 ret = ceph_mksnap(self.cluster, _path, _name, _mode, _snap_meta, nr)
1036 free(_snap_meta)
1037 if ret < 0:
1038 raise make_ex(ret, "mksnap error")
1039 return 0
1040
1041 def rmsnap(self, path, name):
1042 """
1043 Remove a snapshot.
1044
1045 :param path: path of the directory for removing snapshot
1046 :param name: snapshot name
1047
1048 :raises: :class: `Error`
1049 :returns: int: 0 on success
1050 """
1051 self.require_state("mounted")
1052 path = cstr(path, 'path')
1053 name = cstr(name, 'name')
1054 cdef:
1055 char* _path = path
1056 char* _name = name
1057 ret = ceph_rmsnap(self.cluster, _path, _name)
1058 if ret < 0:
1059 raise make_ex(ret, "rmsnap error")
1060 return 0
1061
1062 def snap_info(self, path):
1063 """
1064 Fetch sapshot info
1065
1066 :param path: snapshot path
1067
1068 :raises: :class: `Error`
1069 :returns: dict: snapshot metadata
1070 """
1071 self.require_state("mounted")
1072 path = cstr(path, 'path')
1073 cdef:
1074 char* _path = path
1075 snap_info info
1076 ret = ceph_get_snap_info(self.cluster, _path, &info)
1077 if ret < 0:
1078 raise make_ex(ret, "snap_info error")
1079 md = {}
1080 if info.nr_snap_metadata:
1081 md = {snap_meta.key.decode('utf-8'): snap_meta.value.decode('utf-8') for snap_meta in
1082 info.snap_metadata[:info.nr_snap_metadata]}
1083 ceph_free_snap_info_buffer(&info)
1084 return {'id': info.id, 'metadata': md}
1085
1086 def chmod(self, path, mode) :
1087 """
1088 Change directory mode.
1089
1090 :param path: the path of the directory to create. This must be either an
1091 absolute path or a relative path off of the current working directory.
1092 :param mode: the permissions the directory should have once created.
1093 """
1094 self.require_state("mounted")
1095 path = cstr(path, 'path')
1096 if not isinstance(mode, int):
1097 raise TypeError('mode must be an int')
1098 cdef:
1099 char* _path = path
1100 int _mode = mode
1101 with nogil:
1102 ret = ceph_chmod(self.cluster, _path, _mode)
1103 if ret < 0:
1104 raise make_ex(ret, "error in chmod {}".format(path.decode('utf-8')))
1105
1106 def lchmod(self, path, mode) -> None:
1107 """
1108 Change file mode. If the path is a symbolic link, it won't be dereferenced.
1109
1110 :param path: the path of the file. This must be either an absolute path or
1111 a relative path off of the current working directory.
1112 :param mode: the permissions to be set .
1113 """
1114 self.require_state("mounted")
1115 path = cstr(path, 'path')
1116 if not isinstance(mode, int):
1117 raise TypeError('mode must be an int')
1118 cdef:
1119 char* _path = path
1120 int _mode = mode
1121 with nogil:
1122 ret = ceph_lchmod(self.cluster, _path, _mode)
1123 if ret < 0:
1124 raise make_ex(ret, "error in chmod {}".format(path.decode('utf-8')))
1125
1126 def fchmod(self, fd, mode) :
1127 """
1128 Change file mode based on fd.
1129 :param fd: the file descriptor of the file to change file mode
1130 :param mode: the permissions to be set.
1131 """
1132 self.require_state("mounted")
1133 if not isinstance(fd, int):
1134 raise TypeError('fd must be an int')
1135 if not isinstance(mode, int):
1136 raise TypeError('mode must be an int')
1137 cdef:
1138 int _fd = fd
1139 int _mode = mode
1140 with nogil:
1141 ret = ceph_fchmod(self.cluster, _fd, _mode)
1142 if ret < 0:
1143 raise make_ex(ret, "error in fchmod")
1144
1145 def chown(self, path, uid, gid, follow_symlink=True):
1146 """
1147 Change directory ownership
1148
1149 :param path: the path of the directory to change.
1150 :param uid: the uid to set
1151 :param gid: the gid to set
1152 :param follow_symlink: perform the operation on the target file if @path
1153 is a symbolic link (default)
1154 """
1155 self.require_state("mounted")
1156 path = cstr(path, 'path')
1157 if not isinstance(uid, int):
1158 raise TypeError('uid must be an int')
1159 elif not isinstance(gid, int):
1160 raise TypeError('gid must be an int')
1161
1162 cdef:
1163 char* _path = path
1164 int _uid = uid
1165 int _gid = gid
1166 if follow_symlink:
1167 with nogil:
1168 ret = ceph_chown(self.cluster, _path, _uid, _gid)
1169 else:
1170 with nogil:
1171 ret = ceph_lchown(self.cluster, _path, _uid, _gid)
1172 if ret < 0:
1173 raise make_ex(ret, "error in chown {}".format(path.decode('utf-8')))
1174
1175 def lchown(self, path, uid, gid):
1176 """
1177 Change ownership of a symbolic link
1178 :param path: the path of the symbolic link to change.
1179 :param uid: the uid to set
1180 :param gid: the gid to set
1181 """
1182 self.chown(path, uid, gid, follow_symlink=False)
1183
1184 def fchown(self, fd, uid, gid):
1185 """
1186 Change file ownership
1187 :param fd: the file descriptor of the file to change ownership
1188 :param uid: the uid to set
1189 :param gid: the gid to set
1190 """
1191 self.require_state("mounted")
1192 if not isinstance(fd, int):
1193 raise TypeError('fd must be an int')
1194 if not isinstance(uid, int):
1195 raise TypeError('uid must be an int')
1196 elif not isinstance(gid, int):
1197 raise TypeError('gid must be an int')
1198
1199 cdef:
1200 int _fd = fd
1201 int _uid = uid
1202 int _gid = gid
1203 with nogil:
1204 ret = ceph_fchown(self.cluster, _fd, _uid, _gid)
1205 if ret < 0:
1206 raise make_ex(ret, "error in fchown")
1207
1208 def mkdirs(self, path, mode):
1209 """
1210 Create multiple directories at once.
1211
1212 :param path: the full path of directories and sub-directories that should
1213 be created.
1214 :param mode: the permissions the directory should have once created
1215 """
1216 self.require_state("mounted")
1217 path = cstr(path, 'path')
1218 if not isinstance(mode, int):
1219 raise TypeError('mode must be an int')
1220 cdef:
1221 char* _path = path
1222 int _mode = mode
1223
1224 with nogil:
1225 ret = ceph_mkdirs(self.cluster, _path, _mode)
1226 if ret < 0:
1227 raise make_ex(ret, "error in mkdirs {}".format(path.decode('utf-8')))
1228
1229 def rmdir(self, path):
1230 """
1231 Remove a directory.
1232
1233 :param path: the path of the directory to remove.
1234 """
1235 self.require_state("mounted")
1236 path = cstr(path, 'path')
1237 cdef char* _path = path
1238 ret = ceph_rmdir(self.cluster, _path)
1239 if ret < 0:
1240 raise make_ex(ret, "error in rmdir {}".format(path.decode('utf-8')))
1241
1242 def open(self, path, flags, mode=0):
1243 """
1244 Create and/or open a file.
1245
1246 :param path: the path of the file to open. If the flags parameter includes O_CREAT,
1247 the file will first be created before opening.
1248 :param flags: set of option masks that control how the file is created/opened.
1249 :param mode: the permissions to place on the file if the file does not exist and O_CREAT
1250 is specified in the flags.
1251 """
1252 self.require_state("mounted")
1253 path = cstr(path, 'path')
1254
1255 if not isinstance(mode, int):
1256 raise TypeError('mode must be an int')
1257 if isinstance(flags, str):
1258 cephfs_flags = 0
1259 if flags == '':
1260 cephfs_flags = os.O_RDONLY
1261 else:
1262 access_flags = 0;
1263 for c in flags:
1264 if c == 'r':
1265 access_flags = 1;
1266 elif c == 'w':
1267 access_flags = 2;
1268 cephfs_flags |= os.O_TRUNC | os.O_CREAT
1269 elif access_flags > 0 and c == '+':
1270 access_flags = 3;
1271 else:
1272 raise make_ex(CEPHFS_EOPNOTSUPP,
1273 "open flags doesn't support %s" % c)
1274
1275 if access_flags == 1:
1276 cephfs_flags |= os.O_RDONLY;
1277 elif access_flags == 2:
1278 cephfs_flags |= os.O_WRONLY;
1279 else:
1280 cephfs_flags |= os.O_RDWR;
1281
1282 elif isinstance(flags, int):
1283 cephfs_flags = flags
1284 else:
1285 raise TypeError("flags must be a string or an integer")
1286
1287 cdef:
1288 char* _path = path
1289 int _flags = cephfs_flags
1290 int _mode = mode
1291
1292 with nogil:
1293 ret = ceph_open(self.cluster, _path, _flags, _mode)
1294 if ret < 0:
1295 raise make_ex(ret, "error in open {}".format(path.decode('utf-8')))
1296 return ret
1297
1298 def close(self, fd):
1299 """
1300 Close the open file.
1301
1302 :param fd: the file descriptor referring to the open file.
1303 """
1304
1305 self.require_state("mounted")
1306 if not isinstance(fd, int):
1307 raise TypeError('fd must be an int')
1308 cdef int _fd = fd
1309 with nogil:
1310 ret = ceph_close(self.cluster, _fd)
1311 if ret < 0:
1312 raise make_ex(ret, "error in close")
1313
1314 def read(self, fd, offset, l):
1315 """
1316 Read data from the file.
1317
1318 :param fd: the file descriptor of the open file to read from.
1319 :param offset: the offset in the file to read from. If this value is negative, the
1320 function reads from the current offset of the file descriptor.
1321 :param l: the flag to indicate what type of seeking to perform
1322 """
1323 self.require_state("mounted")
1324 if not isinstance(offset, int):
1325 raise TypeError('offset must be an int')
1326 if not isinstance(l, int):
1327 raise TypeError('l must be an int')
1328 if not isinstance(fd, int):
1329 raise TypeError('fd must be an int')
1330 cdef:
1331 int _fd = fd
1332 int64_t _offset = offset
1333 int64_t _length = l
1334
1335 char *ret_buf
1336 PyObject* ret_s = NULL
1337
1338 ret_s = PyBytes_FromStringAndSize(NULL, _length)
1339 try:
1340 ret_buf = PyBytes_AsString(ret_s)
1341 with nogil:
1342 ret = ceph_read(self.cluster, _fd, ret_buf, _length, _offset)
1343 if ret < 0:
1344 raise make_ex(ret, "error in read")
1345
1346 if ret != _length:
1347 _PyBytes_Resize(&ret_s, ret)
1348
1349 return <object>ret_s
1350 finally:
1351 # We DECREF unconditionally: the cast to object above will have
1352 # INCREFed if necessary. This also takes care of exceptions,
1353 # including if _PyString_Resize fails (that will free the string
1354 # itself and set ret_s to NULL, hence XDECREF).
1355 ref.Py_XDECREF(ret_s)
1356
1357 def preadv(self, fd, buffers, offset):
1358 """
1359 Write data to a file.
1360
1361 :param fd: the file descriptor of the open file to read from
1362 :param buffers: the list of byte object to read from the file
1363 :param offset: the offset of the file read from. If this value is negative, the
1364 function reads from the current offset of the file descriptor.
1365 """
1366 self.require_state("mounted")
1367 if not isinstance(fd, int):
1368 raise TypeError('fd must be an int')
1369 if not isinstance(buffers, list):
1370 raise TypeError('buffers must be a list')
1371 for buf in buffers:
1372 if not isinstance(buf, bytearray):
1373 raise TypeError('buffers must be a list of bytes')
1374 if not isinstance(offset, int):
1375 raise TypeError('offset must be an int')
1376
1377 cdef:
1378 int _fd = fd
1379 int _iovcnt = len(buffers)
1380 int64_t _offset = offset
1381 iovec *_iov = to_iovec(buffers)
1382 try:
1383 with nogil:
1384 ret = ceph_preadv(self.cluster, _fd, _iov, _iovcnt, _offset)
1385 if ret < 0:
1386 raise make_ex(ret, "error in preadv")
1387 return ret
1388 finally:
1389 free(_iov)
1390
1391 def write(self, fd, buf, offset):
1392 """
1393 Write data to a file.
1394
1395 :param fd: the file descriptor of the open file to write to
1396 :param buf: the bytes to write to the file
1397 :param offset: the offset of the file write into. If this value is negative, the
1398 function writes to the current offset of the file descriptor.
1399 """
1400 self.require_state("mounted")
1401 if not isinstance(fd, int):
1402 raise TypeError('fd must be an int')
1403 if not isinstance(buf, bytes):
1404 raise TypeError('buf must be a bytes')
1405 if not isinstance(offset, int):
1406 raise TypeError('offset must be an int')
1407
1408 cdef:
1409 int _fd = fd
1410 char *_data = buf
1411 int64_t _offset = offset
1412
1413 size_t length = len(buf)
1414
1415 with nogil:
1416 ret = ceph_write(self.cluster, _fd, _data, length, _offset)
1417 if ret < 0:
1418 raise make_ex(ret, "error in write")
1419 return ret
1420
1421 def pwritev(self, fd, buffers, offset):
1422 """
1423 Write data to a file.
1424
1425 :param fd: the file descriptor of the open file to write to
1426 :param buffers: the list of byte object to write to the file
1427 :param offset: the offset of the file write into. If this value is negative, the
1428 function writes to the current offset of the file descriptor.
1429 """
1430 self.require_state("mounted")
1431 if not isinstance(fd, int):
1432 raise TypeError('fd must be an int')
1433 if not isinstance(buffers, list):
1434 raise TypeError('buffers must be a list')
1435 for buf in buffers:
1436 if not isinstance(buf, bytes):
1437 raise TypeError('buffers must be a list of bytes')
1438 if not isinstance(offset, int):
1439 raise TypeError('offset must be an int')
1440
1441 cdef:
1442 int _fd = fd
1443 int _iovcnt = len(buffers)
1444 int64_t _offset = offset
1445 iovec *_iov = to_iovec(buffers)
1446 try:
1447 with nogil:
1448 ret = ceph_pwritev(self.cluster, _fd, _iov, _iovcnt, _offset)
1449 if ret < 0:
1450 raise make_ex(ret, "error in pwritev")
1451 return ret
1452 finally:
1453 free(_iov)
1454
1455 def flock(self, fd, operation, owner):
1456 """
1457 Apply or remove an advisory lock.
1458
1459 :param fd: the open file descriptor to change advisory lock.
1460 :param operation: the advisory lock operation to be performed on the file
1461 :param owner: the user-supplied owner identifier (an arbitrary integer)
1462 """
1463 self.require_state("mounted")
1464 if not isinstance(fd, int):
1465 raise TypeError('fd must be an int')
1466 if not isinstance(operation, int):
1467 raise TypeError('operation must be an int')
1468
1469 cdef:
1470 int _fd = fd
1471 int _op = operation
1472 uint64_t _owner = owner
1473
1474 with nogil:
1475 ret = ceph_flock(self.cluster, _fd, _op, _owner)
1476 if ret < 0:
1477 raise make_ex(ret, "error in write")
1478 return ret
1479
1480 def truncate(self, path, size):
1481 """
1482 Truncate the file to the given size. If this operation causes the
1483 file to expand, the empty bytes will be filled in with zeros.
1484
1485 :param path: the path to the file to truncate.
1486 :param size: the new size of the file.
1487 """
1488
1489 if not isinstance(size, int):
1490 raise TypeError('size must be a int')
1491
1492 statx_dict = dict()
1493 statx_dict["size"] = size
1494 self.setattrx(path, statx_dict, CEPH_SETATTR_SIZE, AT_SYMLINK_NOFOLLOW)
1495
1496 def ftruncate(self, fd, size):
1497 """
1498 Truncate the file to the given size. If this operation causes the
1499 file to expand, the empty bytes will be filled in with zeros.
1500
1501 :param path: the path to the file to truncate.
1502 :param size: the new size of the file.
1503 """
1504
1505 if not isinstance(size, int):
1506 raise TypeError('size must be a int')
1507
1508 statx_dict = dict()
1509 statx_dict["size"] = size
1510 self.fsetattrx(fd, statx_dict, CEPH_SETATTR_SIZE)
1511
1512 def mknod(self, path, mode, rdev=0):
1513 """
1514 Make a block or character special file.
1515
1516 :param path: the path to the special file.
1517 :param mode: the permissions to use and the type of special file. The type can be
1518 one of stat.S_IFREG, stat.S_IFCHR, stat.S_IFBLK, stat.S_IFIFO. Both
1519 should be combined using bitwise OR.
1520 :param rdev: If the file type is stat.S_IFCHR or stat.S_IFBLK then this parameter
1521 specifies the major and minor numbers of the newly created device
1522 special file. Otherwise, it is ignored.
1523 """
1524 self.require_state("mounted")
1525 path = cstr(path, 'path')
1526
1527 if not isinstance(mode, int):
1528 raise TypeError('mode must be an int')
1529 if not isinstance(rdev, int):
1530 raise TypeError('rdev must be an int')
1531
1532 cdef:
1533 char* _path = path
1534 mode_t _mode = mode
1535 dev_t _rdev = rdev
1536
1537 with nogil:
1538 ret = ceph_mknod(self.cluster, _path, _mode, _rdev)
1539 if ret < 0:
1540 raise make_ex(ret, "error in mknod {}".format(path.decode('utf-8')))
1541
1542 def getxattr(self, path, name, size=255, follow_symlink=True):
1543 """
1544 Get an extended attribute.
1545
1546 :param path: the path to the file
1547 :param name: the name of the extended attribute to get
1548 :param size: the size of the pre-allocated buffer
1549 """
1550 self.require_state("mounted")
1551
1552 path = cstr(path, 'path')
1553 name = cstr(name, 'name')
1554
1555 cdef:
1556 char* _path = path
1557 char* _name = name
1558
1559 size_t ret_length = size
1560 char *ret_buf = NULL
1561
1562 try:
1563 ret_buf = <char *>realloc_chk(ret_buf, ret_length)
1564 if follow_symlink:
1565 with nogil:
1566 ret = ceph_getxattr(self.cluster, _path, _name, ret_buf,
1567 ret_length)
1568 else:
1569 with nogil:
1570 ret = ceph_lgetxattr(self.cluster, _path, _name, ret_buf,
1571 ret_length)
1572
1573 if ret < 0:
1574 raise make_ex(ret, "error in getxattr")
1575
1576 return ret_buf[:ret]
1577 finally:
1578 free(ret_buf)
1579
1580 def fgetxattr(self, fd, name, size=255):
1581 """
1582 Get an extended attribute given the fd of a file.
1583
1584 :param fd: the open file descriptor referring to the file
1585 :param name: the name of the extended attribute to get
1586 :param size: the size of the pre-allocated buffer
1587 """
1588 self.require_state("mounted")
1589
1590 if not isinstance(fd, int):
1591 raise TypeError('fd must be an int')
1592 name = cstr(name, 'name')
1593
1594 cdef:
1595 int _fd = fd
1596 char* _name = name
1597
1598 size_t ret_length = size
1599 char *ret_buf = NULL
1600
1601 try:
1602 ret_buf = <char *>realloc_chk(ret_buf, ret_length)
1603 with nogil:
1604 ret = ceph_fgetxattr(self.cluster, _fd, _name, ret_buf,
1605 ret_length)
1606
1607 if ret < 0:
1608 raise make_ex(ret, "error in fgetxattr")
1609
1610 return ret_buf[:ret]
1611 finally:
1612 free(ret_buf)
1613
1614 def lgetxattr(self, path, name, size=255):
1615 """
1616 Get an extended attribute without following symbolic links. This
1617 function is identical to ceph_getxattr, but if the path refers to
1618 a symbolic link, we get the extended attributes of the symlink
1619 rather than the attributes of the file it points to.
1620
1621 :param path: the path to the file
1622 :param name: the name of the extended attribute to get
1623 :param size: the size of the pre-allocated buffer
1624 """
1625
1626 return self.getxattr(path, name, size=size, follow_symlink=False)
1627
1628 def setxattr(self, path, name, value, flags, follow_symlink=True):
1629 """
1630 Set an extended attribute on a file.
1631
1632 :param path: the path to the file.
1633 :param name: the name of the extended attribute to set.
1634 :param value: the bytes of the extended attribute value
1635 """
1636 self.require_state("mounted")
1637
1638 name = cstr(name, 'name')
1639 path = cstr(path, 'path')
1640 if not isinstance(flags, int):
1641 raise TypeError('flags must be a int')
1642 if not isinstance(value, bytes):
1643 raise TypeError('value must be a bytes')
1644
1645 cdef:
1646 char *_path = path
1647 char *_name = name
1648 char *_value = value
1649 size_t _value_len = len(value)
1650 int _flags = flags
1651
1652 if follow_symlink:
1653 with nogil:
1654 ret = ceph_setxattr(self.cluster, _path, _name,
1655 _value, _value_len, _flags)
1656 else:
1657 with nogil:
1658 ret = ceph_lsetxattr(self.cluster, _path, _name,
1659 _value, _value_len, _flags)
1660
1661 if ret < 0:
1662 raise make_ex(ret, "error in setxattr")
1663
1664 def fsetxattr(self, fd, name, value, flags):
1665 """
1666 Set an extended attribute on a file.
1667
1668 :param fd: the open file descriptor referring to the file.
1669 :param name: the name of the extended attribute to set.
1670 :param value: the bytes of the extended attribute value
1671 """
1672 self.require_state("mounted")
1673
1674 name = cstr(name, 'name')
1675 if not isinstance(fd, int):
1676 raise TypeError('fd must be an int')
1677 if not isinstance(flags, int):
1678 raise TypeError('flags must be a int')
1679 if not isinstance(value, bytes):
1680 raise TypeError('value must be a bytes')
1681
1682 cdef:
1683 int _fd = fd
1684 char *_name = name
1685 char *_value = value
1686 size_t _value_len = len(value)
1687 int _flags = flags
1688
1689 with nogil:
1690 ret = ceph_fsetxattr(self.cluster, _fd, _name,
1691 _value, _value_len, _flags)
1692 if ret < 0:
1693 raise make_ex(ret, "error in fsetxattr")
1694
1695 def lsetxattr(self, path, name, value, flags):
1696 """
1697 Set an extended attribute on a file but do not follow symbolic link.
1698
1699 :param path: the path to the file.
1700 :param name: the name of the extended attribute to set.
1701 :param value: the bytes of the extended attribute value
1702 """
1703
1704 self.setxattr(path, name, value, flags, follow_symlink=False)
1705
1706 def removexattr(self, path, name, follow_symlink=True):
1707 """
1708 Remove an extended attribute of a file.
1709
1710 :param path: path of the file.
1711 :param name: name of the extended attribute to remove.
1712 """
1713 self.require_state("mounted")
1714
1715 name = cstr(name, 'name')
1716 path = cstr(path, 'path')
1717
1718 cdef:
1719 char *_path = path
1720 char *_name = name
1721
1722 if follow_symlink:
1723 with nogil:
1724 ret = ceph_removexattr(self.cluster, _path, _name)
1725 else:
1726 with nogil:
1727 ret = ceph_lremovexattr(self.cluster, _path, _name)
1728
1729 if ret < 0:
1730 raise make_ex(ret, "error in removexattr")
1731
1732 def fremovexattr(self, fd, name):
1733 """
1734 Remove an extended attribute of a file.
1735
1736 :param fd: the open file descriptor referring to the file.
1737 :param name: name of the extended attribute to remove.
1738 """
1739 self.require_state("mounted")
1740
1741 if not isinstance(fd, int):
1742 raise TypeError('fd must be an int')
1743 name = cstr(name, 'name')
1744
1745 cdef:
1746 int _fd = fd
1747 char *_name = name
1748
1749 with nogil:
1750 ret = ceph_fremovexattr(self.cluster, _fd, _name)
1751 if ret < 0:
1752 raise make_ex(ret, "error in fremovexattr")
1753
1754 def lremovexattr(self, path, name):
1755 """
1756 Remove an extended attribute of a file but do not follow symbolic link.
1757
1758 :param path: path of the file.
1759 :param name: name of the extended attribute to remove.
1760 """
1761 self.removexattr(path, name, follow_symlink=False)
1762
1763 def listxattr(self, path, size=65536, follow_symlink=True):
1764 """
1765 List the extended attribute keys set on a file.
1766
1767 :param path: path of the file.
1768 :param size: the size of list buffer to be filled with extended attribute keys.
1769 """
1770 self.require_state("mounted")
1771
1772 path = cstr(path, 'path')
1773
1774 cdef:
1775 char *_path = path
1776 char *ret_buf = NULL
1777 size_t ret_length = size
1778
1779 try:
1780 ret_buf = <char *>realloc_chk(ret_buf, ret_length)
1781 if follow_symlink:
1782 with nogil:
1783 ret = ceph_listxattr(self.cluster, _path, ret_buf, ret_length)
1784 else:
1785 with nogil:
1786 ret = ceph_llistxattr(self.cluster, _path, ret_buf, ret_length)
1787
1788 if ret < 0:
1789 raise make_ex(ret, "error in listxattr")
1790
1791 return ret, ret_buf[:ret]
1792 finally:
1793 free(ret_buf)
1794
1795 def flistxattr(self, fd, size=65536):
1796 """
1797 List the extended attribute keys set on a file.
1798
1799 :param fd: the open file descriptor referring to the file.
1800 :param size: the size of list buffer to be filled with extended attribute keys.
1801 """
1802 self.require_state("mounted")
1803
1804 if not isinstance(fd, int):
1805 raise TypeError('fd must be an int')
1806
1807 cdef:
1808 int _fd = fd
1809 char *ret_buf = NULL
1810 size_t ret_length = size
1811
1812 try:
1813 ret_buf = <char *>realloc_chk(ret_buf, ret_length)
1814 with nogil:
1815 ret = ceph_flistxattr(self.cluster, _fd, ret_buf, ret_length)
1816
1817 if ret < 0:
1818 raise make_ex(ret, "error in flistxattr")
1819
1820 return ret, ret_buf[:ret]
1821 finally:
1822 free(ret_buf)
1823
1824 def llistxattr(self, path, size=65536):
1825 """
1826 List the extended attribute keys set on a file but do not follow symbolic link.
1827
1828 :param path: path of the file.
1829 :param size: the size of list buffer to be filled with extended attribute keys.
1830 """
1831
1832 return self.listxattr(path, size=size, follow_symlink=False)
1833
1834 def stat(self, path, follow_symlink=True):
1835 """
1836 Get a file's extended statistics and attributes.
1837
1838 :param path: the file or directory to get the statistics of.
1839 """
1840 self.require_state("mounted")
1841 path = cstr(path, 'path')
1842
1843 cdef:
1844 char* _path = path
1845 statx stx
1846
1847 if follow_symlink:
1848 with nogil:
1849 ret = ceph_statx(self.cluster, _path, &stx,
1850 CEPH_STATX_BASIC_STATS_CDEF, 0)
1851 else:
1852 with nogil:
1853 ret = ceph_statx(self.cluster, _path, &stx,
1854 CEPH_STATX_BASIC_STATS_CDEF, AT_SYMLINK_NOFOLLOW_CDEF)
1855
1856 if ret < 0:
1857 raise make_ex(ret, "error in stat: {}".format(path.decode('utf-8')))
1858 return StatResult(st_dev=stx.stx_dev, st_ino=stx.stx_ino,
1859 st_mode=stx.stx_mode, st_nlink=stx.stx_nlink,
1860 st_uid=stx.stx_uid, st_gid=stx.stx_gid,
1861 st_rdev=stx.stx_rdev, st_size=stx.stx_size,
1862 st_blksize=stx.stx_blksize,
1863 st_blocks=stx.stx_blocks,
1864 st_atime=datetime.fromtimestamp(stx.stx_atime.tv_sec),
1865 st_mtime=datetime.fromtimestamp(stx.stx_mtime.tv_sec),
1866 st_ctime=datetime.fromtimestamp(stx.stx_ctime.tv_sec))
1867
1868 def lstat(self, path):
1869 """
1870 Get a file's extended statistics and attributes. When file's a
1871 symbolic link, return the informaion of the link itself rather
1872 than that of the file it points too.
1873
1874 :param path: the file or directory to get the statistics of.
1875 """
1876 return self.stat(path, follow_symlink=False)
1877
1878 def fstat(self, fd):
1879 """
1880 Get an open file's extended statistics and attributes.
1881
1882 :param fd: the file descriptor of the file to get statistics of.
1883 """
1884 self.require_state("mounted")
1885 if not isinstance(fd, int):
1886 raise TypeError('fd must be an int')
1887
1888 cdef:
1889 int _fd = fd
1890 statx stx
1891
1892 with nogil:
1893 ret = ceph_fstatx(self.cluster, _fd, &stx,
1894 CEPH_STATX_BASIC_STATS_CDEF, 0)
1895 if ret < 0:
1896 raise make_ex(ret, "error in fsat")
1897 return StatResult(st_dev=stx.stx_dev, st_ino=stx.stx_ino,
1898 st_mode=stx.stx_mode, st_nlink=stx.stx_nlink,
1899 st_uid=stx.stx_uid, st_gid=stx.stx_gid,
1900 st_rdev=stx.stx_rdev, st_size=stx.stx_size,
1901 st_blksize=stx.stx_blksize,
1902 st_blocks=stx.stx_blocks,
1903 st_atime=datetime.fromtimestamp(stx.stx_atime.tv_sec),
1904 st_mtime=datetime.fromtimestamp(stx.stx_mtime.tv_sec),
1905 st_ctime=datetime.fromtimestamp(stx.stx_ctime.tv_sec))
1906
1907 def statx(self, path, mask, flag):
1908 """
1909 Get a file's extended statistics and attributes.
1910
1911 :param path: the file or directory to get the statistics of.
1912 :param mask: want bitfield of CEPH_STATX_* flags showing designed attributes.
1913 :param flag: bitfield that can be used to set AT_* modifier flags (only AT_NO_ATTR_SYNC and AT_SYMLINK_NOFOLLOW)
1914 """
1915
1916 self.require_state("mounted")
1917 path = cstr(path, 'path')
1918 if not isinstance(mask, int):
1919 raise TypeError('flag must be a int')
1920 if not isinstance(flag, int):
1921 raise TypeError('flag must be a int')
1922
1923 cdef:
1924 char* _path = path
1925 statx stx
1926 int _mask = mask
1927 int _flag = flag
1928 dict_result = dict()
1929
1930 with nogil:
1931 ret = ceph_statx(self.cluster, _path, &stx, _mask, _flag)
1932 if ret < 0:
1933 raise make_ex(ret, "error in stat: %s" % path)
1934
1935 if (_mask & CEPH_STATX_MODE):
1936 dict_result["mode"] = stx.stx_mode
1937 if (_mask & CEPH_STATX_NLINK):
1938 dict_result["nlink"] = stx.stx_nlink
1939 if (_mask & CEPH_STATX_UID):
1940 dict_result["uid"] = stx.stx_uid
1941 if (_mask & CEPH_STATX_GID):
1942 dict_result["gid"] = stx.stx_gid
1943 if (_mask & CEPH_STATX_RDEV):
1944 dict_result["rdev"] = stx.stx_rdev
1945 if (_mask & CEPH_STATX_ATIME):
1946 dict_result["atime"] = datetime.fromtimestamp(stx.stx_atime.tv_sec)
1947 if (_mask & CEPH_STATX_MTIME):
1948 dict_result["mtime"] = datetime.fromtimestamp(stx.stx_mtime.tv_sec)
1949 if (_mask & CEPH_STATX_CTIME):
1950 dict_result["ctime"] = datetime.fromtimestamp(stx.stx_ctime.tv_sec)
1951 if (_mask & CEPH_STATX_INO):
1952 dict_result["ino"] = stx.stx_ino
1953 if (_mask & CEPH_STATX_SIZE):
1954 dict_result["size"] = stx.stx_size
1955 if (_mask & CEPH_STATX_BLOCKS):
1956 dict_result["blocks"] = stx.stx_blocks
1957 if (_mask & CEPH_STATX_BTIME):
1958 dict_result["btime"] = datetime.fromtimestamp(stx.stx_btime.tv_sec)
1959 if (_mask & CEPH_STATX_VERSION):
1960 dict_result["version"] = stx.stx_version
1961
1962 return dict_result
1963
1964 def setattrx(self, path, dict_stx, mask, flags):
1965 """
1966 Set a file's attributes.
1967
1968 :param path: the path to the file/directory to set the attributes of.
1969 :param mask: a mask of all the CEPH_SETATTR_* values that have been set in the statx struct.
1970 :param stx: a dict of statx structure that must include attribute values to set on the file.
1971 :param flags: mask of AT_* flags (only AT_ATTR_NOFOLLOW is respected for now)
1972 """
1973
1974 self.require_state("mounted")
1975 path = cstr(path, 'path')
1976 if not isinstance(dict_stx, dict):
1977 raise TypeError('dict_stx must be a dict')
1978 if not isinstance(mask, int):
1979 raise TypeError('mask must be a int')
1980 if not isinstance(flags, int):
1981 raise TypeError('flags must be a int')
1982
1983 cdef statx stx
1984
1985 if (mask & CEPH_SETATTR_MODE):
1986 stx.stx_mode = dict_stx["mode"]
1987 if (mask & CEPH_SETATTR_UID):
1988 stx.stx_uid = dict_stx["uid"]
1989 if (mask & CEPH_SETATTR_GID):
1990 stx.stx_gid = dict_stx["gid"]
1991 if (mask & CEPH_SETATTR_MTIME):
1992 stx.stx_mtime = to_timespec(dict_stx["mtime"].timestamp())
1993 if (mask & CEPH_SETATTR_ATIME):
1994 stx.stx_atime = to_timespec(dict_stx["atime"].timestamp())
1995 if (mask & CEPH_SETATTR_CTIME):
1996 stx.stx_ctime = to_timespec(dict_stx["ctime"].timestamp())
1997 if (mask & CEPH_SETATTR_SIZE):
1998 stx.stx_size = dict_stx["size"]
1999 if (mask & CEPH_SETATTR_BTIME):
2000 stx.stx_btime = to_timespec(dict_stx["btime"].timestamp())
2001
2002 cdef:
2003 char* _path = path
2004 int _mask = mask
2005 int _flags = flags
2006 dict_result = dict()
2007
2008 with nogil:
2009 ret = ceph_setattrx(self.cluster, _path, &stx, _mask, _flags)
2010 if ret < 0:
2011 raise make_ex(ret, "error in setattrx: %s" % path)
2012
2013 def fsetattrx(self, fd, dict_stx, mask):
2014 """
2015 Set a file's attributes.
2016
2017 :param path: the path to the file/directory to set the attributes of.
2018 :param mask: a mask of all the CEPH_SETATTR_* values that have been set in the statx struct.
2019 :param stx: a dict of statx structure that must include attribute values to set on the file.
2020 """
2021
2022 self.require_state("mounted")
2023 if not isinstance(fd, int):
2024 raise TypeError('fd must be a int')
2025 if not isinstance(dict_stx, dict):
2026 raise TypeError('dict_stx must be a dict')
2027 if not isinstance(mask, int):
2028 raise TypeError('mask must be a int')
2029
2030 cdef statx stx
2031
2032 if (mask & CEPH_SETATTR_MODE):
2033 stx.stx_mode = dict_stx["mode"]
2034 if (mask & CEPH_SETATTR_UID):
2035 stx.stx_uid = dict_stx["uid"]
2036 if (mask & CEPH_SETATTR_GID):
2037 stx.stx_gid = dict_stx["gid"]
2038 if (mask & CEPH_SETATTR_MTIME):
2039 stx.stx_mtime = to_timespec(dict_stx["mtime"].timestamp())
2040 if (mask & CEPH_SETATTR_ATIME):
2041 stx.stx_atime = to_timespec(dict_stx["atime"].timestamp())
2042 if (mask & CEPH_SETATTR_CTIME):
2043 stx.stx_ctime = to_timespec(dict_stx["ctime"].timestamp())
2044 if (mask & CEPH_SETATTR_SIZE):
2045 stx.stx_size = dict_stx["size"]
2046 if (mask & CEPH_SETATTR_BTIME):
2047 stx.stx_btime = to_timespec(dict_stx["btime"].timestamp())
2048
2049 cdef:
2050 int _fd = fd
2051 int _mask = mask
2052 dict_result = dict()
2053
2054 with nogil:
2055 ret = ceph_fsetattrx(self.cluster, _fd, &stx, _mask)
2056 if ret < 0:
2057 raise make_ex(ret, "error in fsetattrx")
2058
2059 def symlink(self, existing, newname):
2060 """
2061 Creates a symbolic link.
2062
2063 :param existing: the path to the existing file/directory to link to.
2064 :param newname: the path to the new file/directory to link from.
2065 """
2066 self.require_state("mounted")
2067 existing = cstr(existing, 'existing')
2068 newname = cstr(newname, 'newname')
2069 cdef:
2070 char* _existing = existing
2071 char* _newname = newname
2072
2073 with nogil:
2074 ret = ceph_symlink(self.cluster, _existing, _newname)
2075 if ret < 0:
2076 raise make_ex(ret, "error in symlink")
2077
2078 def link(self, existing, newname):
2079 """
2080 Create a link.
2081
2082 :param existing: the path to the existing file/directory to link to.
2083 :param newname: the path to the new file/directory to link from.
2084 """
2085
2086 self.require_state("mounted")
2087 existing = cstr(existing, 'existing')
2088 newname = cstr(newname, 'newname')
2089 cdef:
2090 char* _existing = existing
2091 char* _newname = newname
2092
2093 with nogil:
2094 ret = ceph_link(self.cluster, _existing, _newname)
2095 if ret < 0:
2096 raise make_ex(ret, "error in link")
2097
2098 def readlink(self, path, size):
2099 """
2100 Read a symbolic link.
2101
2102 :param path: the path to the symlink to read
2103 :param size: the length of the buffer
2104 :rtype buf: buffer to hold the path of the file that the symlink points to.
2105 """
2106 self.require_state("mounted")
2107 path = cstr(path, 'path')
2108
2109 cdef:
2110 char* _path = path
2111 int64_t _size = size
2112 char *buf = NULL
2113
2114 try:
2115 buf = <char *>realloc_chk(buf, _size)
2116 with nogil:
2117 ret = ceph_readlink(self.cluster, _path, buf, _size)
2118 if ret < 0:
2119 raise make_ex(ret, "error in readlink")
2120 return buf[:ret]
2121 finally:
2122 free(buf)
2123
2124 def unlink(self, path):
2125 """
2126 Removes a file, link, or symbolic link. If the file/link has multiple links to it, the
2127 file will not disappear from the namespace until all references to it are removed.
2128
2129 :param path: the path of the file or link to unlink.
2130 """
2131 self.require_state("mounted")
2132 path = cstr(path, 'path')
2133 cdef char* _path = path
2134 with nogil:
2135 ret = ceph_unlink(self.cluster, _path)
2136 if ret < 0:
2137 raise make_ex(ret, "error in unlink: {}".format(path.decode('utf-8')))
2138
2139 def rename(self, src, dst):
2140 """
2141 Rename a file or directory.
2142
2143 :param src: the path to the existing file or directory.
2144 :param dst: the new name of the file or directory.
2145 """
2146
2147 self.require_state("mounted")
2148
2149 src = cstr(src, 'source')
2150 dst = cstr(dst, 'destination')
2151
2152 cdef:
2153 char* _src = src
2154 char* _dst = dst
2155
2156 with nogil:
2157 ret = ceph_rename(self.cluster, _src, _dst)
2158 if ret < 0:
2159 raise make_ex(ret, "error in rename {} to {}".format(src.decode(
2160 'utf-8'), dst.decode('utf-8')))
2161
2162 def mds_command(self, mds_spec, args, input_data):
2163 """
2164 :return 3-tuple of output status int, output status string, output data
2165 """
2166 mds_spec = cstr(mds_spec, 'mds_spec')
2167 args = cstr(args, 'args')
2168 input_data = cstr(input_data, 'input_data')
2169
2170 cdef:
2171 char *_mds_spec = opt_str(mds_spec)
2172 char **_cmd = to_bytes_array([args])
2173 size_t _cmdlen = 1
2174
2175 char *_inbuf = input_data
2176 size_t _inbuf_len = len(input_data)
2177
2178 char *_outbuf = NULL
2179 size_t _outbuf_len = 0
2180 char *_outs = NULL
2181 size_t _outs_len = 0
2182
2183 try:
2184 with nogil:
2185 ret = ceph_mds_command(self.cluster, _mds_spec,
2186 <const char **>_cmd, _cmdlen,
2187 <const char*>_inbuf, _inbuf_len,
2188 &_outbuf, &_outbuf_len,
2189 &_outs, &_outs_len)
2190 my_outs = decode_cstr(_outs[:_outs_len])
2191 my_outbuf = _outbuf[:_outbuf_len]
2192 if _outs_len:
2193 ceph_buffer_free(_outs)
2194 if _outbuf_len:
2195 ceph_buffer_free(_outbuf)
2196 return (ret, my_outbuf, my_outs)
2197 finally:
2198 free(_cmd)
2199
2200 def umask(self, mode) :
2201 self.require_state("mounted")
2202 cdef:
2203 mode_t _mode = mode
2204 with nogil:
2205 ret = ceph_umask(self.cluster, _mode)
2206 if ret < 0:
2207 raise make_ex(ret, "error in umask")
2208 return ret
2209
2210 def lseek(self, fd, offset, whence):
2211 """
2212 Set the file's current position.
2213
2214 :param fd: the file descriptor of the open file to read from.
2215 :param offset: the offset in the file to read from. If this value is negative, the
2216 function reads from the current offset of the file descriptor.
2217 :param whence: the flag to indicate what type of seeking to performs:SEEK_SET, SEEK_CUR, SEEK_END
2218 """
2219 self.require_state("mounted")
2220 if not isinstance(fd, int):
2221 raise TypeError('fd must be an int')
2222 if not isinstance(offset, int):
2223 raise TypeError('offset must be an int')
2224 if not isinstance(whence, int):
2225 raise TypeError('whence must be an int')
2226
2227 cdef:
2228 int _fd = fd
2229 int64_t _offset = offset
2230 int64_t _whence = whence
2231
2232 with nogil:
2233 ret = ceph_lseek(self.cluster, _fd, _offset, _whence)
2234
2235 if ret < 0:
2236 raise make_ex(ret, "error in lseek")
2237
2238 return ret
2239
2240 def utime(self, path, times=None):
2241 """
2242 Set access and modification time for path
2243
2244 :param path: file path for which timestamps have to be changed
2245 :param times: if times is not None, it must be a tuple (atime, mtime)
2246 """
2247
2248 self.require_state("mounted")
2249 path = cstr(path, 'path')
2250 if times:
2251 if not isinstance(times, tuple):
2252 raise TypeError('times must be a tuple')
2253 if not isinstance(times[0], int):
2254 raise TypeError('atime must be an int')
2255 if not isinstance(times[1], int):
2256 raise TypeError('mtime must be an int')
2257 actime = modtime = int(time.time())
2258 if times:
2259 actime = times[0]
2260 modtime = times[1]
2261
2262 cdef:
2263 char *pth = path
2264 utimbuf buf = utimbuf(actime, modtime)
2265 with nogil:
2266 ret = ceph_utime(self.cluster, pth, &buf)
2267 if ret < 0:
2268 raise make_ex(ret, "error in utime {}".format(path.decode('utf-8')))
2269
2270 def futime(self, fd, times=None):
2271 """
2272 Set access and modification time for a file pointed by descriptor
2273
2274 :param fd: file descriptor of the open file
2275 :param times: if times is not None, it must be a tuple (atime, mtime)
2276 """
2277
2278 self.require_state("mounted")
2279 if not isinstance(fd, int):
2280 raise TypeError('fd must be an int')
2281 if times:
2282 if not isinstance(times, tuple):
2283 raise TypeError('times must be a tuple')
2284 if not isinstance(times[0], int):
2285 raise TypeError('atime must be an int')
2286 if not isinstance(times[1], int):
2287 raise TypeError('mtime must be an int')
2288 actime = modtime = int(time.time())
2289 if times:
2290 actime = times[0]
2291 modtime = times[1]
2292
2293 cdef:
2294 int _fd = fd
2295 utimbuf buf = utimbuf(actime, modtime)
2296 with nogil:
2297 ret = ceph_futime(self.cluster, _fd, &buf)
2298 if ret < 0:
2299 raise make_ex(ret, "error in futime")
2300
2301 def utimes(self, path, times=None, follow_symlink=True):
2302 """
2303 Set access and modification time for path
2304
2305 :param path: file path for which timestamps have to be changed
2306 :param times: if times is not None, it must be a tuple (atime, mtime)
2307 :param follow_symlink: perform the operation on the target file if @path
2308 is a symbolic link (default)
2309 """
2310
2311 self.require_state("mounted")
2312 path = cstr(path, 'path')
2313 if times:
2314 if not isinstance(times, tuple):
2315 raise TypeError('times must be a tuple')
2316 if not isinstance(times[0], (int, float)):
2317 raise TypeError('atime must be an int or a float')
2318 if not isinstance(times[1], (int, float)):
2319 raise TypeError('mtime must be an int or a float')
2320 actime = modtime = time.time()
2321 if times:
2322 actime = float(times[0])
2323 modtime = float(times[1])
2324
2325 cdef:
2326 char *pth = path
2327 timeval *buf = [to_timeval(actime), to_timeval(modtime)]
2328 if follow_symlink:
2329 with nogil:
2330 ret = ceph_utimes(self.cluster, pth, buf)
2331 else:
2332 with nogil:
2333 ret = ceph_lutimes(self.cluster, pth, buf)
2334 if ret < 0:
2335 raise make_ex(ret, "error in utimes {}".format(path.decode('utf-8')))
2336
2337 def lutimes(self, path, times=None):
2338 """
2339 Set access and modification time for a file. If the file is a symbolic
2340 link do not follow to the target.
2341
2342 :param path: file path for which timestamps have to be changed
2343 :param times: if times is not None, it must be a tuple (atime, mtime)
2344 """
2345 self.utimes(path, times=times, follow_symlink=False)
2346
2347 def futimes(self, fd, times=None):
2348 """
2349 Set access and modification time for a file pointer by descriptor
2350
2351 :param fd: file descriptor of the open file
2352 :param times: if times is not None, it must be a tuple (atime, mtime)
2353 """
2354
2355 self.require_state("mounted")
2356 if not isinstance(fd, int):
2357 raise TypeError('fd must be an int')
2358 if times:
2359 if not isinstance(times, tuple):
2360 raise TypeError('times must be a tuple')
2361 if not isinstance(times[0], (int, float)):
2362 raise TypeError('atime must be an int or a float')
2363 if not isinstance(times[1], (int, float)):
2364 raise TypeError('mtime must be an int or a float')
2365 actime = modtime = time.time()
2366 if times:
2367 actime = float(times[0])
2368 modtime = float(times[1])
2369
2370 cdef:
2371 int _fd = fd
2372 timeval *buf = [to_timeval(actime), to_timeval(modtime)]
2373 with nogil:
2374 ret = ceph_futimes(self.cluster, _fd, buf)
2375 if ret < 0:
2376 raise make_ex(ret, "error in futimes")
2377
2378 def futimens(self, fd, times=None):
2379 """
2380 Set access and modification time for a file pointer by descriptor
2381
2382 :param fd: file descriptor of the open file
2383 :param times: if times is not None, it must be a tuple (atime, mtime)
2384 """
2385
2386 self.require_state("mounted")
2387 if not isinstance(fd, int):
2388 raise TypeError('fd must be an int')
2389 if times:
2390 if not isinstance(times, tuple):
2391 raise TypeError('times must be a tuple')
2392 if not isinstance(times[0], (int, float)):
2393 raise TypeError('atime must be an int or a float')
2394 if not isinstance(times[1], (int, float)):
2395 raise TypeError('mtime must be an int or a float')
2396 actime = modtime = time.time()
2397 if times:
2398 actime = float(times[0])
2399 modtime = float(times[1])
2400
2401 cdef:
2402 int _fd = fd
2403 timespec *buf = [to_timespec(actime), to_timespec(modtime)]
2404 with nogil:
2405 ret = ceph_futimens(self.cluster, _fd, buf)
2406 if ret < 0:
2407 raise make_ex(ret, "error in futimens")
2408
2409 def get_file_replication(self, fd):
2410 """
2411 Get the file replication information from an open file descriptor.
2412
2413 :param fd : the open file descriptor referring to the file to get
2414 the replication information of.
2415 """
2416 self.require_state("mounted")
2417 if not isinstance(fd, int):
2418 raise TypeError('fd must be an int')
2419
2420 cdef:
2421 int _fd = fd
2422
2423 with nogil:
2424 ret = ceph_get_file_replication(self.cluster, _fd)
2425 if ret < 0:
2426 raise make_ex(ret, "error in get_file_replication")
2427
2428 return ret
2429
2430 def get_path_replication(self, path):
2431 """
2432 Get the file replication information given the path.
2433
2434 :param path: the path of the file/directory to get the replication information of.
2435 """
2436 self.require_state("mounted")
2437 path = cstr(path, 'path')
2438
2439 cdef:
2440 char* _path = path
2441
2442 with nogil:
2443 ret = ceph_get_path_replication(self.cluster, _path)
2444 if ret < 0:
2445 raise make_ex(ret, "error in get_path_replication")
2446
2447 return ret
2448
2449 def get_pool_id(self, pool_name):
2450 """
2451 Get the id of the named pool.
2452
2453 :param pool_name: the name of the pool.
2454 """
2455
2456 self.require_state("mounted")
2457 pool_name = cstr(pool_name, 'pool_name')
2458
2459 cdef:
2460 char* _pool_name = pool_name
2461
2462 with nogil:
2463 ret = ceph_get_pool_id(self.cluster, _pool_name)
2464 if ret < 0:
2465 raise make_ex(ret, "error in get_pool_id")
2466
2467 return ret
2468
2469 def get_pool_replication(self, pool_id):
2470 """
2471 Get the pool replication factor.
2472
2473 :param pool_id: the pool id to look up
2474 """
2475
2476 self.require_state("mounted")
2477 if not isinstance(pool_id, int):
2478 raise TypeError('pool_id must be an int')
2479
2480 cdef:
2481 int _pool_id = pool_id
2482
2483 with nogil:
2484 ret = ceph_get_pool_replication(self.cluster, _pool_id)
2485 if ret < 0:
2486 raise make_ex(ret, "error in get_pool_replication")
2487
2488 return ret
2489
2490 def debug_get_fd_caps(self, fd):
2491 """
2492 Get the capabilities currently issued to the client given the fd.
2493
2494 :param fd: the file descriptor to get issued
2495 """
2496
2497 self.require_state("mounted")
2498 if not isinstance(fd, int):
2499 raise TypeError('fd must be an int')
2500
2501 cdef:
2502 int _fd = fd
2503
2504 with nogil:
2505 ret = ceph_debug_get_fd_caps(self.cluster, _fd)
2506 if ret < 0:
2507 raise make_ex(ret, "error in debug_get_fd_caps")
2508
2509 return ret
2510
2511 def debug_get_file_caps(self, path):
2512 """
2513 Get the capabilities currently issued to the client given the path.
2514
2515 :param path: the path of the file/directory to get the capabilities of.
2516 """
2517
2518 self.require_state("mounted")
2519 path = cstr(path, 'path')
2520
2521 cdef:
2522 char* _path = path
2523
2524 with nogil:
2525 ret = ceph_debug_get_file_caps(self.cluster, _path)
2526 if ret < 0:
2527 raise make_ex(ret, "error in debug_get_file_caps")
2528
2529 return ret
2530
2531 def get_cap_return_timeout(self):
2532 """
2533 Get the amount of time that the client has to return caps
2534
2535 In the event that a client does not return its caps, the MDS may blocklist
2536 it after this timeout. Applications should check this value and ensure
2537 that they set the delegation timeout to a value lower than this.
2538 """
2539
2540 self.require_state("mounted")
2541
2542 with nogil:
2543 ret = ceph_get_cap_return_timeout(self.cluster)
2544 if ret < 0:
2545 raise make_ex(ret, "error in get_cap_return_timeout")
2546
2547 return ret
2548
2549 def set_uuid(self, uuid):
2550 """
2551 Set ceph client uuid. Must be called before mount.
2552
2553 :param uuid: the uuid to set
2554 """
2555
2556 uuid = cstr(uuid, 'uuid')
2557
2558 cdef:
2559 char* _uuid = uuid
2560
2561 with nogil:
2562 ceph_set_uuid(self.cluster, _uuid)
2563
2564 def set_session_timeout(self, timeout):
2565 """
2566 Set ceph client session timeout. Must be called before mount.
2567
2568 :param timeout: the timeout to set
2569 """
2570
2571 if not isinstance(timeout, int):
2572 raise TypeError('timeout must be an int')
2573
2574 cdef:
2575 int _timeout = timeout
2576
2577 with nogil:
2578 ceph_set_session_timeout(self.cluster, _timeout)
2579
2580 def get_layout(self, fd):
2581 """
2582 Set ceph client session timeout. Must be called before mount.
2583
2584 :param fd: file descriptor of the file/directory for which to get the layout
2585 """
2586
2587 if not isinstance(fd, int):
2588 raise TypeError('fd must be an int')
2589
2590 cdef:
2591 int _fd = fd
2592 int stripe_unit
2593 int stripe_count
2594 int object_size
2595 int pool_id
2596 char *buf = NULL
2597 int buflen = 256
2598 dict_result = dict()
2599
2600 with nogil:
2601 ret = ceph_get_file_layout(self.cluster, _fd, &stripe_unit, &stripe_count, &object_size, &pool_id)
2602 if ret < 0:
2603 raise make_ex(stripe_unit, "error in get_file_layout")
2604 dict_result["stripe_unit"] = stripe_unit
2605 dict_result["stripe_count"] = stripe_count
2606 dict_result["object_size"] = object_size
2607 dict_result["pool_id"] = pool_id
2608
2609 try:
2610 while True:
2611 buf = <char *>realloc_chk(buf, buflen)
2612 with nogil:
2613 ret = ceph_get_file_pool_name(self.cluster, _fd, buf, buflen)
2614 if ret > 0:
2615 dict_result["pool_name"] = decode_cstr(buf)
2616 return dict_result
2617 elif ret == -CEPHFS_ERANGE:
2618 buflen = buflen * 2
2619 else:
2620 raise make_ex(ret, "error in get_file_pool_name")
2621 finally:
2622 free(buf)
2623
2624
2625 def get_default_pool(self):
2626 """
2627 Get the default pool name and id of cephfs. This returns dict{pool_name, pool_id}.
2628 """
2629
2630 cdef:
2631 char *buf = NULL
2632 int buflen = 256
2633 dict_result = dict()
2634
2635 try:
2636 while True:
2637 buf = <char *>realloc_chk(buf, buflen)
2638 with nogil:
2639 ret = ceph_get_default_data_pool_name(self.cluster, buf, buflen)
2640 if ret > 0:
2641 dict_result["pool_name"] = decode_cstr(buf)
2642 break
2643 elif ret == -CEPHFS_ERANGE:
2644 buflen = buflen * 2
2645 else:
2646 raise make_ex(ret, "error in get_default_data_pool_name")
2647
2648 with nogil:
2649 ret = ceph_get_pool_id(self.cluster, buf)
2650 if ret < 0:
2651 raise make_ex(ret, "error in get_pool_id")
2652 dict_result["pool_id"] = ret
2653 return dict_result
2654
2655 finally:
2656 free(buf)