]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/cephfs/cephfs.pyx
import ceph pacific 16.2.5
[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_fscid(self):
486 """
487 Return the file system id for this fs client.
488 """
489 self.require_state("mounted")
490 with nogil:
491 ret = ceph_get_fs_cid(self.cluster)
492 if ret < 0:
493 raise make_ex(ret, "error fetching fscid")
494 return ret
495
496 def get_addrs(self):
497 """
498 Get associated client addresses with this RADOS session.
499 """
500 self.require_state("mounted")
501
502 cdef:
503 char* addrs = NULL
504
505 try:
506
507 with nogil:
508 ret = ceph_getaddrs(self.cluster, &addrs)
509 if ret:
510 raise make_ex(ret, "error calling getaddrs")
511
512 return decode_cstr(addrs)
513 finally:
514 ceph_buffer_free(addrs)
515
516
517 def conf_read_file(self, conffile=None):
518 """
519 Load the ceph configuration from the specified config file.
520
521 :param conffile str opt: the path to ceph.conf to override the default settings
522 """
523 conffile = cstr(conffile, 'conffile', opt=True)
524 cdef:
525 char *_conffile = opt_str(conffile)
526 with nogil:
527 ret = ceph_conf_read_file(self.cluster, <const char*>_conffile)
528 if ret != 0:
529 raise make_ex(ret, "error calling conf_read_file")
530
531 def conf_parse_argv(self, argv):
532 """
533 Parse the command line arguments and load the configuration parameters.
534
535 :param argv: the argument list
536 """
537 self.require_state("configuring")
538 cargv = cstr_list(argv, 'argv')
539 cdef:
540 int _argc = len(argv)
541 char **_argv = to_bytes_array(cargv)
542
543 try:
544 with nogil:
545 ret = ceph_conf_parse_argv(self.cluster, _argc,
546 <const char **>_argv)
547 if ret != 0:
548 raise make_ex(ret, "error calling conf_parse_argv")
549 finally:
550 free(_argv)
551
552 def shutdown(self):
553 """
554 Unmount and destroy the ceph mount handle.
555 """
556 if self.state in ["initialized", "mounted"]:
557 with nogil:
558 ceph_shutdown(self.cluster)
559 self.state = "shutdown"
560
561 def __enter__(self):
562 self.mount()
563 return self
564
565 def __exit__(self, type_, value, traceback):
566 self.shutdown()
567 return False
568
569 def __dealloc__(self):
570 self.shutdown()
571
572 def version(self):
573 """
574 Get the version number of the ``libcephfs`` C library.
575
576 :returns: a tuple of ``(major, minor, extra)`` components of the
577 libcephfs version
578 """
579 cdef:
580 int major = 0
581 int minor = 0
582 int extra = 0
583 with nogil:
584 ceph_version(&major, &minor, &extra)
585 return (major, minor, extra)
586
587 def conf_get(self, option):
588 """
589 Gets the configuration value as a string.
590
591 :param option: the config option to get
592 """
593 self.require_state("configuring", "initialized", "mounted")
594
595 option = cstr(option, 'option')
596 cdef:
597 char *_option = option
598 size_t length = 20
599 char *ret_buf = NULL
600
601 try:
602 while True:
603 ret_buf = <char *>realloc_chk(ret_buf, length)
604 with nogil:
605 ret = ceph_conf_get(self.cluster, _option, ret_buf, length)
606 if ret == 0:
607 return decode_cstr(ret_buf)
608 elif ret == -CEPHFS_ENAMETOOLONG:
609 length = length * 2
610 elif ret == -CEPHFS_ENOENT:
611 return None
612 else:
613 raise make_ex(ret, "error calling conf_get")
614 finally:
615 free(ret_buf)
616
617 def conf_set(self, option, val):
618 """
619 Sets a configuration value from a string.
620
621 :param option: the configuration option to set
622 :param value: the value of the configuration option to set
623 """
624 self.require_state("configuring", "initialized", "mounted")
625
626 option = cstr(option, 'option')
627 val = cstr(val, 'val')
628 cdef:
629 char *_option = option
630 char *_val = val
631
632 with nogil:
633 ret = ceph_conf_set(self.cluster, _option, _val)
634 if ret != 0:
635 raise make_ex(ret, "error calling conf_set")
636
637 def set_mount_timeout(self, timeout):
638 """
639 Set mount timeout
640
641 :param timeout: mount timeout
642 """
643 self.require_state("configuring", "initialized")
644 if not isinstance(timeout, int):
645 raise TypeError('timeout must be an integer')
646 if timeout < 0:
647 raise make_ex(CEPHFS_EINVAL, 'timeout must be greater than or equal to 0')
648 cdef:
649 uint32_t _timeout = timeout
650 with nogil:
651 ret = ceph_set_mount_timeout(self.cluster, _timeout)
652 if ret != 0:
653 raise make_ex(ret, "error setting mount timeout")
654
655 def init(self):
656 """
657 Initialize the filesystem client (but do not mount the filesystem yet)
658 """
659 self.require_state("configuring")
660 with nogil:
661 ret = ceph_init(self.cluster)
662 if ret != 0:
663 raise make_ex(ret, "error calling ceph_init")
664 self.state = "initialized"
665
666 def mount(self, mount_root=None, filesystem_name=None):
667 """
668 Perform a mount using the path for the root of the mount.
669 """
670 if self.state == "configuring":
671 self.init()
672 self.require_state("initialized")
673
674 # Configure which filesystem to mount if one was specified
675 if filesystem_name is None:
676 filesystem_name = b""
677 else:
678 filesystem_name = cstr(filesystem_name, 'filesystem_name')
679 cdef:
680 char *_filesystem_name = filesystem_name
681 if filesystem_name:
682 with nogil:
683 ret = ceph_select_filesystem(self.cluster,
684 _filesystem_name)
685 if ret != 0:
686 raise make_ex(ret, "error calling ceph_select_filesystem")
687
688 # Prepare mount_root argument, default to "/"
689 root = b"/" if mount_root is None else mount_root
690 cdef:
691 char *_mount_root = root
692
693 with nogil:
694 ret = ceph_mount(self.cluster, _mount_root)
695 if ret != 0:
696 raise make_ex(ret, "error calling ceph_mount")
697 self.state = "mounted"
698
699 def unmount(self):
700 """
701 Unmount a mount handle.
702 """
703 self.require_state("mounted")
704 with nogil:
705 ret = ceph_unmount(self.cluster)
706 if ret != 0:
707 raise make_ex(ret, "error calling ceph_unmount")
708 self.state = "initialized"
709
710 def abort_conn(self):
711 """
712 Abort mds connections.
713 """
714 self.require_state("mounted")
715 with nogil:
716 ret = ceph_abort_conn(self.cluster)
717 if ret != 0:
718 raise make_ex(ret, "error calling ceph_abort_conn")
719 self.state = "initialized"
720
721 def get_instance_id(self):
722 """
723 Get a global id for current instance
724 """
725 self.require_state("initialized", "mounted")
726 with nogil:
727 ret = ceph_get_instance_id(self.cluster)
728 return ret;
729
730 def statfs(self, path):
731 """
732 Perform a statfs on the ceph file system. This call fills in file system wide statistics
733 into the passed in buffer.
734
735 :param path: any path within the mounted filesystem
736 """
737 self.require_state("mounted")
738 path = cstr(path, 'path')
739 cdef:
740 char* _path = path
741 statvfs statbuf
742
743 with nogil:
744 ret = ceph_statfs(self.cluster, _path, &statbuf)
745 if ret < 0:
746 raise make_ex(ret, "statfs failed: %s" % path)
747 return {'f_bsize': statbuf.f_bsize,
748 'f_frsize': statbuf.f_frsize,
749 'f_blocks': statbuf.f_blocks,
750 'f_bfree': statbuf.f_bfree,
751 'f_bavail': statbuf.f_bavail,
752 'f_files': statbuf.f_files,
753 'f_ffree': statbuf.f_ffree,
754 'f_favail': statbuf.f_favail,
755 'f_fsid': statbuf.f_fsid,
756 'f_flag': statbuf.f_flag,
757 'f_namemax': statbuf.f_namemax}
758
759 def sync_fs(self):
760 """
761 Synchronize all filesystem data to persistent media
762 """
763 self.require_state("mounted")
764 with nogil:
765 ret = ceph_sync_fs(self.cluster)
766 if ret < 0:
767 raise make_ex(ret, "sync_fs failed")
768
769 def fsync(self, int fd, int syncdataonly):
770 """
771 Synchronize an open file to persistent media.
772
773 :param fd: the file descriptor of the file to sync.
774 :param syncdataonly: a boolean whether to synchronize metadata and data (0)
775 or just data (1).
776 """
777 self.require_state("mounted")
778 with nogil:
779 ret = ceph_fsync(self.cluster, fd, syncdataonly)
780 if ret < 0:
781 raise make_ex(ret, "fsync failed")
782
783 def lazyio(self, fd, enable):
784 """
785 Enable/disable lazyio for the file.
786
787 :param fd: the file descriptor of the file for which to enable lazio.
788 :param enable: a boolean to enable lazyio or disable lazyio.
789 """
790
791 self.require_state("mounted")
792 if not isinstance(fd, int):
793 raise TypeError('fd must be an int')
794 if not isinstance(enable, int):
795 raise TypeError('enable must be an int')
796
797 cdef:
798 int _fd = fd
799 int _enable = enable
800
801 with nogil:
802 ret = ceph_lazyio(self.cluster, _fd, _enable)
803 if ret < 0:
804 raise make_ex(ret, "lazyio failed")
805
806 def lazyio_propagate(self, fd, offset, count):
807 """
808 Flushes the write buffer for the file thereby propogating the buffered write to the file.
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_propagate(self.cluster, _fd, _offset, _count)
830 if ret < 0:
831 raise make_ex(ret, "lazyio_propagate failed")
832
833 def lazyio_synchronize(self, fd, offset, count):
834 """
835 Flushes the write buffer for the file and invalidate the read cache. This allows a
836 subsequent read operation to read and cache data directly from the file and hence
837 everyone's propagated writes would be visible.
838
839 :param fd: the file descriptor of the file to sync.
840 :param offset: the byte range starting.
841 :param count: the number of bytes starting from offset.
842 """
843
844 self.require_state("mounted")
845 if not isinstance(fd, int):
846 raise TypeError('fd must be an int')
847 if not isinstance(offset, int):
848 raise TypeError('offset must be an int')
849 if not isinstance(count, int):
850 raise TypeError('count must be an int')
851
852 cdef:
853 int _fd = fd
854 int64_t _offset = offset
855 size_t _count = count
856
857 with nogil:
858 ret = ceph_lazyio_synchronize(self.cluster, _fd, _offset, _count)
859 if ret < 0:
860 raise make_ex(ret, "lazyio_synchronize failed")
861
862 def fallocate(self, fd, offset, length, mode=0):
863 """
864 Preallocate or release disk space for the file for the byte range.
865
866 :param fd: the file descriptor of the file to fallocate.
867 :param mode: the flags determines the operation to be performed on the given
868 range. default operation (0) allocate and initialize to zero
869 the file in the byte range, and the file size will be changed
870 if offset + length is greater than the file size. if the
871 FALLOC_FL_KEEP_SIZE flag is specified in the mode, the file size
872 will not be changed. if the FALLOC_FL_PUNCH_HOLE flag is specified
873 in the mode, the operation is deallocate space and zero the byte range.
874 :param offset: the byte range starting.
875 :param length: the length of the range.
876 """
877
878 self.require_state("mounted")
879 if not isinstance(fd, int):
880 raise TypeError('fd must be an int')
881 if not isinstance(mode, int):
882 raise TypeError('mode must be an int')
883 if not isinstance(offset, int):
884 raise TypeError('offset must be an int')
885 if not isinstance(length, int):
886 raise TypeError('length must be an int')
887
888 cdef:
889 int _fd = fd
890 int _mode = mode
891 int64_t _offset = offset
892 int64_t _length = length
893
894 with nogil:
895 ret = ceph_fallocate(self.cluster, _fd, _mode, _offset, _length)
896 if ret < 0:
897 raise make_ex(ret, "fallocate failed")
898
899 def getcwd(self):
900 """
901 Get the current working directory.
902
903 :rtype the path to the current working directory
904 """
905 self.require_state("mounted")
906 with nogil:
907 ret = ceph_getcwd(self.cluster)
908 return ret
909
910 def chdir(self, path):
911 """
912 Change the current working directory.
913
914 :param path the path to the working directory to change into.
915 """
916 self.require_state("mounted")
917
918 path = cstr(path, 'path')
919 cdef char* _path = path
920 with nogil:
921 ret = ceph_chdir(self.cluster, _path)
922 if ret < 0:
923 raise make_ex(ret, "chdir failed")
924
925 def opendir(self, path):
926 """
927 Open the given directory.
928
929 :param path: the path name of the directory to open. Must be either an absolute path
930 or a path relative to the current working directory.
931 :rtype handle: the open directory stream handle
932 """
933 self.require_state("mounted")
934
935 path = cstr(path, 'path')
936 cdef:
937 char* _path = path
938 ceph_dir_result* handle
939 with nogil:
940 ret = ceph_opendir(self.cluster, _path, &handle);
941 if ret < 0:
942 raise make_ex(ret, "opendir failed")
943 d = DirResult()
944 d.lib = self
945 d.handle = handle
946 return d
947
948 def readdir(self, DirResult handle):
949 """
950 Get the next entry in an open directory.
951
952 :param handle: the open directory stream handle
953 :rtype dentry: the next directory entry or None if at the end of the
954 directory (or the directory is empty. This pointer
955 should not be freed by the caller, and is only safe to
956 access between return and the next call to readdir or
957 closedir.
958 """
959 self.require_state("mounted")
960
961 return handle.readdir()
962
963 def closedir(self, DirResult handle):
964 """
965 Close the open directory.
966
967 :param handle: the open directory stream handle
968 """
969 self.require_state("mounted")
970
971 return handle.close()
972
973 def rewinddir(self, DirResult handle):
974 """
975 Rewind the directory stream to the beginning of the directory.
976
977 :param handle: the open directory stream handle
978 """
979 return handle.rewinddir()
980
981 def telldir(self, DirResult handle):
982 """
983 Get the current position of a directory stream.
984
985 :param handle: the open directory stream handle
986 :return value: The position of the directory stream. Note that the offsets
987 returned by ceph_telldir do not have a particular order (cannot
988 be compared with inequality).
989 """
990 return handle.telldir()
991
992 def seekdir(self, DirResult handle, offset):
993 """
994 Move the directory stream to a position specified by the given offset.
995
996 :param handle: the open directory stream handle
997 :param offset: the position to move the directory stream to. This offset should be
998 a value returned by telldir. Note that this value does not refer to
999 the nth entry in a directory, and can not be manipulated with plus
1000 or minus.
1001 """
1002 return handle.seekdir(offset)
1003
1004 def mkdir(self, path, mode):
1005 """
1006 Create a directory.
1007
1008 :param path: the path of the directory to create. This must be either an
1009 absolute path or a relative path off of the current working directory.
1010 :param mode: the permissions the directory should have once created.
1011 """
1012
1013 self.require_state("mounted")
1014 path = cstr(path, 'path')
1015 if not isinstance(mode, int):
1016 raise TypeError('mode must be an int')
1017 cdef:
1018 char* _path = path
1019 int _mode = mode
1020 with nogil:
1021 ret = ceph_mkdir(self.cluster, _path, _mode)
1022 if ret < 0:
1023 raise make_ex(ret, "error in mkdir {}".format(path.decode('utf-8')))
1024
1025 def mksnap(self, path, name, mode, metadata={}):
1026 """
1027 Create a snapshot.
1028
1029 :param path: path of the directory to snapshot.
1030 :param name: snapshot name
1031 :param mode: permission of the snapshot
1032 :param metadata: metadata key/value to store with the snapshot
1033
1034 :raises: :class: `TypeError`
1035 :raises: :class: `Error`
1036 :returns: int: 0 on success
1037 """
1038
1039 self.require_state("mounted")
1040 path = cstr(path, 'path')
1041 name = cstr(name, 'name')
1042 if not isinstance(mode, int):
1043 raise TypeError('mode must be an int')
1044 if not isinstance(metadata, dict):
1045 raise TypeError('metadata must be an dictionary')
1046 md = {}
1047 for key, value in metadata.items():
1048 if not isinstance(key, str) or not isinstance(value, str):
1049 raise TypeError('metadata key and values should be strings')
1050 md[key.encode('utf-8')] = value.encode('utf-8')
1051 cdef:
1052 char* _path = path
1053 char* _name = name
1054 int _mode = mode
1055 size_t nr = len(md)
1056 snap_metadata *_snap_meta = <snap_metadata *>malloc(nr * sizeof(snap_metadata))
1057 if nr and _snap_meta == NULL:
1058 raise MemoryError("malloc failed")
1059 i = 0
1060 for key, value in md.items():
1061 _snap_meta[i] = snap_metadata(<char*>key, <char*>value)
1062 i += 1
1063 with nogil:
1064 ret = ceph_mksnap(self.cluster, _path, _name, _mode, _snap_meta, nr)
1065 free(_snap_meta)
1066 if ret < 0:
1067 raise make_ex(ret, "mksnap error")
1068 return 0
1069
1070 def rmsnap(self, path, name):
1071 """
1072 Remove a snapshot.
1073
1074 :param path: path of the directory for removing snapshot
1075 :param name: snapshot name
1076
1077 :raises: :class: `Error`
1078 :returns: int: 0 on success
1079 """
1080 self.require_state("mounted")
1081 path = cstr(path, 'path')
1082 name = cstr(name, 'name')
1083 cdef:
1084 char* _path = path
1085 char* _name = name
1086 ret = ceph_rmsnap(self.cluster, _path, _name)
1087 if ret < 0:
1088 raise make_ex(ret, "rmsnap error")
1089 return 0
1090
1091 def snap_info(self, path):
1092 """
1093 Fetch sapshot info
1094
1095 :param path: snapshot path
1096
1097 :raises: :class: `Error`
1098 :returns: dict: snapshot metadata
1099 """
1100 self.require_state("mounted")
1101 path = cstr(path, 'path')
1102 cdef:
1103 char* _path = path
1104 snap_info info
1105 ret = ceph_get_snap_info(self.cluster, _path, &info)
1106 if ret < 0:
1107 raise make_ex(ret, "snap_info error")
1108 md = {}
1109 if info.nr_snap_metadata:
1110 md = {snap_meta.key.decode('utf-8'): snap_meta.value.decode('utf-8') for snap_meta in
1111 info.snap_metadata[:info.nr_snap_metadata]}
1112 ceph_free_snap_info_buffer(&info)
1113 return {'id': info.id, 'metadata': md}
1114
1115 def chmod(self, path, mode) :
1116 """
1117 Change directory mode.
1118
1119 :param path: the path of the directory to create. This must be either an
1120 absolute path or a relative path off of the current working directory.
1121 :param mode: the permissions the directory should have once created.
1122 """
1123 self.require_state("mounted")
1124 path = cstr(path, 'path')
1125 if not isinstance(mode, int):
1126 raise TypeError('mode must be an int')
1127 cdef:
1128 char* _path = path
1129 int _mode = mode
1130 with nogil:
1131 ret = ceph_chmod(self.cluster, _path, _mode)
1132 if ret < 0:
1133 raise make_ex(ret, "error in chmod {}".format(path.decode('utf-8')))
1134
1135 def lchmod(self, path, mode) -> None:
1136 """
1137 Change file mode. If the path is a symbolic link, it won't be dereferenced.
1138
1139 :param path: the path of the file. This must be either an absolute path or
1140 a relative path off of the current working directory.
1141 :param mode: the permissions to be set .
1142 """
1143 self.require_state("mounted")
1144 path = cstr(path, 'path')
1145 if not isinstance(mode, int):
1146 raise TypeError('mode must be an int')
1147 cdef:
1148 char* _path = path
1149 int _mode = mode
1150 with nogil:
1151 ret = ceph_lchmod(self.cluster, _path, _mode)
1152 if ret < 0:
1153 raise make_ex(ret, "error in chmod {}".format(path.decode('utf-8')))
1154
1155 def fchmod(self, fd, mode) :
1156 """
1157 Change file mode based on fd.
1158 :param fd: the file descriptor of the file to change file mode
1159 :param mode: the permissions to be set.
1160 """
1161 self.require_state("mounted")
1162 if not isinstance(fd, int):
1163 raise TypeError('fd must be an int')
1164 if not isinstance(mode, int):
1165 raise TypeError('mode must be an int')
1166 cdef:
1167 int _fd = fd
1168 int _mode = mode
1169 with nogil:
1170 ret = ceph_fchmod(self.cluster, _fd, _mode)
1171 if ret < 0:
1172 raise make_ex(ret, "error in fchmod")
1173
1174 def chown(self, path, uid, gid, follow_symlink=True):
1175 """
1176 Change directory ownership
1177
1178 :param path: the path of the directory to change.
1179 :param uid: the uid to set
1180 :param gid: the gid to set
1181 :param follow_symlink: perform the operation on the target file if @path
1182 is a symbolic link (default)
1183 """
1184 self.require_state("mounted")
1185 path = cstr(path, 'path')
1186 if not isinstance(uid, int):
1187 raise TypeError('uid must be an int')
1188 elif not isinstance(gid, int):
1189 raise TypeError('gid must be an int')
1190
1191 cdef:
1192 char* _path = path
1193 int _uid = uid
1194 int _gid = gid
1195 if follow_symlink:
1196 with nogil:
1197 ret = ceph_chown(self.cluster, _path, _uid, _gid)
1198 else:
1199 with nogil:
1200 ret = ceph_lchown(self.cluster, _path, _uid, _gid)
1201 if ret < 0:
1202 raise make_ex(ret, "error in chown {}".format(path.decode('utf-8')))
1203
1204 def lchown(self, path, uid, gid):
1205 """
1206 Change ownership of a symbolic link
1207 :param path: the path of the symbolic link to change.
1208 :param uid: the uid to set
1209 :param gid: the gid to set
1210 """
1211 self.chown(path, uid, gid, follow_symlink=False)
1212
1213 def fchown(self, fd, uid, gid):
1214 """
1215 Change file ownership
1216 :param fd: the file descriptor of the file to change ownership
1217 :param uid: the uid to set
1218 :param gid: the gid to set
1219 """
1220 self.require_state("mounted")
1221 if not isinstance(fd, int):
1222 raise TypeError('fd must be an int')
1223 if not isinstance(uid, int):
1224 raise TypeError('uid must be an int')
1225 elif not isinstance(gid, int):
1226 raise TypeError('gid must be an int')
1227
1228 cdef:
1229 int _fd = fd
1230 int _uid = uid
1231 int _gid = gid
1232 with nogil:
1233 ret = ceph_fchown(self.cluster, _fd, _uid, _gid)
1234 if ret < 0:
1235 raise make_ex(ret, "error in fchown")
1236
1237 def mkdirs(self, path, mode):
1238 """
1239 Create multiple directories at once.
1240
1241 :param path: the full path of directories and sub-directories that should
1242 be created.
1243 :param mode: the permissions the directory should have once created
1244 """
1245 self.require_state("mounted")
1246 path = cstr(path, 'path')
1247 if not isinstance(mode, int):
1248 raise TypeError('mode must be an int')
1249 cdef:
1250 char* _path = path
1251 int _mode = mode
1252
1253 with nogil:
1254 ret = ceph_mkdirs(self.cluster, _path, _mode)
1255 if ret < 0:
1256 raise make_ex(ret, "error in mkdirs {}".format(path.decode('utf-8')))
1257
1258 def rmdir(self, path):
1259 """
1260 Remove a directory.
1261
1262 :param path: the path of the directory to remove.
1263 """
1264 self.require_state("mounted")
1265 path = cstr(path, 'path')
1266 cdef char* _path = path
1267 ret = ceph_rmdir(self.cluster, _path)
1268 if ret < 0:
1269 raise make_ex(ret, "error in rmdir {}".format(path.decode('utf-8')))
1270
1271 def open(self, path, flags, mode=0):
1272 """
1273 Create and/or open a file.
1274
1275 :param path: the path of the file to open. If the flags parameter includes O_CREAT,
1276 the file will first be created before opening.
1277 :param flags: set of option masks that control how the file is created/opened.
1278 :param mode: the permissions to place on the file if the file does not exist and O_CREAT
1279 is specified in the flags.
1280 """
1281 self.require_state("mounted")
1282 path = cstr(path, 'path')
1283
1284 if not isinstance(mode, int):
1285 raise TypeError('mode must be an int')
1286 if isinstance(flags, str):
1287 cephfs_flags = 0
1288 if flags == '':
1289 cephfs_flags = os.O_RDONLY
1290 else:
1291 access_flags = 0;
1292 for c in flags:
1293 if c == 'r':
1294 access_flags = 1;
1295 elif c == 'w':
1296 access_flags = 2;
1297 cephfs_flags |= os.O_TRUNC | os.O_CREAT
1298 elif access_flags > 0 and c == '+':
1299 access_flags = 3;
1300 else:
1301 raise make_ex(CEPHFS_EOPNOTSUPP,
1302 "open flags doesn't support %s" % c)
1303
1304 if access_flags == 1:
1305 cephfs_flags |= os.O_RDONLY;
1306 elif access_flags == 2:
1307 cephfs_flags |= os.O_WRONLY;
1308 else:
1309 cephfs_flags |= os.O_RDWR;
1310
1311 elif isinstance(flags, int):
1312 cephfs_flags = flags
1313 else:
1314 raise TypeError("flags must be a string or an integer")
1315
1316 cdef:
1317 char* _path = path
1318 int _flags = cephfs_flags
1319 int _mode = mode
1320
1321 with nogil:
1322 ret = ceph_open(self.cluster, _path, _flags, _mode)
1323 if ret < 0:
1324 raise make_ex(ret, "error in open {}".format(path.decode('utf-8')))
1325 return ret
1326
1327 def close(self, fd):
1328 """
1329 Close the open file.
1330
1331 :param fd: the file descriptor referring to the open file.
1332 """
1333
1334 self.require_state("mounted")
1335 if not isinstance(fd, int):
1336 raise TypeError('fd must be an int')
1337 cdef int _fd = fd
1338 with nogil:
1339 ret = ceph_close(self.cluster, _fd)
1340 if ret < 0:
1341 raise make_ex(ret, "error in close")
1342
1343 def read(self, fd, offset, l):
1344 """
1345 Read data from the file.
1346
1347 :param fd: the file descriptor of the open file to read from.
1348 :param offset: the offset in the file to read from. If this value is negative, the
1349 function reads from the current offset of the file descriptor.
1350 :param l: the flag to indicate what type of seeking to perform
1351 """
1352 self.require_state("mounted")
1353 if not isinstance(offset, int):
1354 raise TypeError('offset must be an int')
1355 if not isinstance(l, int):
1356 raise TypeError('l must be an int')
1357 if not isinstance(fd, int):
1358 raise TypeError('fd must be an int')
1359 cdef:
1360 int _fd = fd
1361 int64_t _offset = offset
1362 int64_t _length = l
1363
1364 char *ret_buf
1365 PyObject* ret_s = NULL
1366
1367 ret_s = PyBytes_FromStringAndSize(NULL, _length)
1368 try:
1369 ret_buf = PyBytes_AsString(ret_s)
1370 with nogil:
1371 ret = ceph_read(self.cluster, _fd, ret_buf, _length, _offset)
1372 if ret < 0:
1373 raise make_ex(ret, "error in read")
1374
1375 if ret != _length:
1376 _PyBytes_Resize(&ret_s, ret)
1377
1378 return <object>ret_s
1379 finally:
1380 # We DECREF unconditionally: the cast to object above will have
1381 # INCREFed if necessary. This also takes care of exceptions,
1382 # including if _PyString_Resize fails (that will free the string
1383 # itself and set ret_s to NULL, hence XDECREF).
1384 ref.Py_XDECREF(ret_s)
1385
1386 def preadv(self, fd, buffers, offset):
1387 """
1388 Write data to a file.
1389
1390 :param fd: the file descriptor of the open file to read from
1391 :param buffers: the list of byte object to read from the file
1392 :param offset: the offset of the file read from. If this value is negative, the
1393 function reads from the current offset of the file descriptor.
1394 """
1395 self.require_state("mounted")
1396 if not isinstance(fd, int):
1397 raise TypeError('fd must be an int')
1398 if not isinstance(buffers, list):
1399 raise TypeError('buffers must be a list')
1400 for buf in buffers:
1401 if not isinstance(buf, bytearray):
1402 raise TypeError('buffers must be a list of bytes')
1403 if not isinstance(offset, int):
1404 raise TypeError('offset must be an int')
1405
1406 cdef:
1407 int _fd = fd
1408 int _iovcnt = len(buffers)
1409 int64_t _offset = offset
1410 iovec *_iov = to_iovec(buffers)
1411 try:
1412 with nogil:
1413 ret = ceph_preadv(self.cluster, _fd, _iov, _iovcnt, _offset)
1414 if ret < 0:
1415 raise make_ex(ret, "error in preadv")
1416 return ret
1417 finally:
1418 free(_iov)
1419
1420 def write(self, fd, buf, offset):
1421 """
1422 Write data to a file.
1423
1424 :param fd: the file descriptor of the open file to write to
1425 :param buf: the bytes to write to the file
1426 :param offset: the offset of the file write into. If this value is negative, the
1427 function writes to the current offset of the file descriptor.
1428 """
1429 self.require_state("mounted")
1430 if not isinstance(fd, int):
1431 raise TypeError('fd must be an int')
1432 if not isinstance(buf, bytes):
1433 raise TypeError('buf must be a bytes')
1434 if not isinstance(offset, int):
1435 raise TypeError('offset must be an int')
1436
1437 cdef:
1438 int _fd = fd
1439 char *_data = buf
1440 int64_t _offset = offset
1441
1442 size_t length = len(buf)
1443
1444 with nogil:
1445 ret = ceph_write(self.cluster, _fd, _data, length, _offset)
1446 if ret < 0:
1447 raise make_ex(ret, "error in write")
1448 return ret
1449
1450 def pwritev(self, fd, buffers, offset):
1451 """
1452 Write data to a file.
1453
1454 :param fd: the file descriptor of the open file to write to
1455 :param buffers: the list of byte object to write to the file
1456 :param offset: the offset of the file write into. If this value is negative, the
1457 function writes to the current offset of the file descriptor.
1458 """
1459 self.require_state("mounted")
1460 if not isinstance(fd, int):
1461 raise TypeError('fd must be an int')
1462 if not isinstance(buffers, list):
1463 raise TypeError('buffers must be a list')
1464 for buf in buffers:
1465 if not isinstance(buf, bytes):
1466 raise TypeError('buffers must be a list of bytes')
1467 if not isinstance(offset, int):
1468 raise TypeError('offset must be an int')
1469
1470 cdef:
1471 int _fd = fd
1472 int _iovcnt = len(buffers)
1473 int64_t _offset = offset
1474 iovec *_iov = to_iovec(buffers)
1475 try:
1476 with nogil:
1477 ret = ceph_pwritev(self.cluster, _fd, _iov, _iovcnt, _offset)
1478 if ret < 0:
1479 raise make_ex(ret, "error in pwritev")
1480 return ret
1481 finally:
1482 free(_iov)
1483
1484 def flock(self, fd, operation, owner):
1485 """
1486 Apply or remove an advisory lock.
1487
1488 :param fd: the open file descriptor to change advisory lock.
1489 :param operation: the advisory lock operation to be performed on the file
1490 :param owner: the user-supplied owner identifier (an arbitrary integer)
1491 """
1492 self.require_state("mounted")
1493 if not isinstance(fd, int):
1494 raise TypeError('fd must be an int')
1495 if not isinstance(operation, int):
1496 raise TypeError('operation must be an int')
1497
1498 cdef:
1499 int _fd = fd
1500 int _op = operation
1501 uint64_t _owner = owner
1502
1503 with nogil:
1504 ret = ceph_flock(self.cluster, _fd, _op, _owner)
1505 if ret < 0:
1506 raise make_ex(ret, "error in write")
1507 return ret
1508
1509 def truncate(self, path, size):
1510 """
1511 Truncate the file to the given size. If this operation causes the
1512 file to expand, the empty bytes will be filled in with zeros.
1513
1514 :param path: the path to the file to truncate.
1515 :param size: the new size of the file.
1516 """
1517
1518 if not isinstance(size, int):
1519 raise TypeError('size must be a int')
1520
1521 statx_dict = dict()
1522 statx_dict["size"] = size
1523 self.setattrx(path, statx_dict, CEPH_SETATTR_SIZE, AT_SYMLINK_NOFOLLOW)
1524
1525 def ftruncate(self, fd, size):
1526 """
1527 Truncate the file to the given size. If this operation causes the
1528 file to expand, the empty bytes will be filled in with zeros.
1529
1530 :param path: the path to the file to truncate.
1531 :param size: the new size of the file.
1532 """
1533
1534 if not isinstance(size, int):
1535 raise TypeError('size must be a int')
1536
1537 statx_dict = dict()
1538 statx_dict["size"] = size
1539 self.fsetattrx(fd, statx_dict, CEPH_SETATTR_SIZE)
1540
1541 def mknod(self, path, mode, rdev=0):
1542 """
1543 Make a block or character special file.
1544
1545 :param path: the path to the special file.
1546 :param mode: the permissions to use and the type of special file. The type can be
1547 one of stat.S_IFREG, stat.S_IFCHR, stat.S_IFBLK, stat.S_IFIFO. Both
1548 should be combined using bitwise OR.
1549 :param rdev: If the file type is stat.S_IFCHR or stat.S_IFBLK then this parameter
1550 specifies the major and minor numbers of the newly created device
1551 special file. Otherwise, it is ignored.
1552 """
1553 self.require_state("mounted")
1554 path = cstr(path, 'path')
1555
1556 if not isinstance(mode, int):
1557 raise TypeError('mode must be an int')
1558 if not isinstance(rdev, int):
1559 raise TypeError('rdev must be an int')
1560
1561 cdef:
1562 char* _path = path
1563 mode_t _mode = mode
1564 dev_t _rdev = rdev
1565
1566 with nogil:
1567 ret = ceph_mknod(self.cluster, _path, _mode, _rdev)
1568 if ret < 0:
1569 raise make_ex(ret, "error in mknod {}".format(path.decode('utf-8')))
1570
1571 def getxattr(self, path, name, size=255, follow_symlink=True):
1572 """
1573 Get an extended attribute.
1574
1575 :param path: the path to the file
1576 :param name: the name of the extended attribute to get
1577 :param size: the size of the pre-allocated buffer
1578 """
1579 self.require_state("mounted")
1580
1581 path = cstr(path, 'path')
1582 name = cstr(name, 'name')
1583
1584 cdef:
1585 char* _path = path
1586 char* _name = name
1587
1588 size_t ret_length = size
1589 char *ret_buf = NULL
1590
1591 try:
1592 ret_buf = <char *>realloc_chk(ret_buf, ret_length)
1593 if follow_symlink:
1594 with nogil:
1595 ret = ceph_getxattr(self.cluster, _path, _name, ret_buf,
1596 ret_length)
1597 else:
1598 with nogil:
1599 ret = ceph_lgetxattr(self.cluster, _path, _name, ret_buf,
1600 ret_length)
1601
1602 if ret < 0:
1603 raise make_ex(ret, "error in getxattr")
1604
1605 return ret_buf[:ret]
1606 finally:
1607 free(ret_buf)
1608
1609 def fgetxattr(self, fd, name, size=255):
1610 """
1611 Get an extended attribute given the fd of a file.
1612
1613 :param fd: the open file descriptor referring to the file
1614 :param name: the name of the extended attribute to get
1615 :param size: the size of the pre-allocated buffer
1616 """
1617 self.require_state("mounted")
1618
1619 if not isinstance(fd, int):
1620 raise TypeError('fd must be an int')
1621 name = cstr(name, 'name')
1622
1623 cdef:
1624 int _fd = fd
1625 char* _name = name
1626
1627 size_t ret_length = size
1628 char *ret_buf = NULL
1629
1630 try:
1631 ret_buf = <char *>realloc_chk(ret_buf, ret_length)
1632 with nogil:
1633 ret = ceph_fgetxattr(self.cluster, _fd, _name, ret_buf,
1634 ret_length)
1635
1636 if ret < 0:
1637 raise make_ex(ret, "error in fgetxattr")
1638
1639 return ret_buf[:ret]
1640 finally:
1641 free(ret_buf)
1642
1643 def lgetxattr(self, path, name, size=255):
1644 """
1645 Get an extended attribute without following symbolic links. This
1646 function is identical to ceph_getxattr, but if the path refers to
1647 a symbolic link, we get the extended attributes of the symlink
1648 rather than the attributes of the file it points to.
1649
1650 :param path: the path to the file
1651 :param name: the name of the extended attribute to get
1652 :param size: the size of the pre-allocated buffer
1653 """
1654
1655 return self.getxattr(path, name, size=size, follow_symlink=False)
1656
1657 def setxattr(self, path, name, value, flags, follow_symlink=True):
1658 """
1659 Set an extended attribute on a file.
1660
1661 :param path: the path to the file.
1662 :param name: the name of the extended attribute to set.
1663 :param value: the bytes of the extended attribute value
1664 """
1665 self.require_state("mounted")
1666
1667 name = cstr(name, 'name')
1668 path = cstr(path, 'path')
1669 if not isinstance(flags, int):
1670 raise TypeError('flags must be a int')
1671 if not isinstance(value, bytes):
1672 raise TypeError('value must be a bytes')
1673
1674 cdef:
1675 char *_path = path
1676 char *_name = name
1677 char *_value = value
1678 size_t _value_len = len(value)
1679 int _flags = flags
1680
1681 if follow_symlink:
1682 with nogil:
1683 ret = ceph_setxattr(self.cluster, _path, _name,
1684 _value, _value_len, _flags)
1685 else:
1686 with nogil:
1687 ret = ceph_lsetxattr(self.cluster, _path, _name,
1688 _value, _value_len, _flags)
1689
1690 if ret < 0:
1691 raise make_ex(ret, "error in setxattr")
1692
1693 def fsetxattr(self, fd, name, value, flags):
1694 """
1695 Set an extended attribute on a file.
1696
1697 :param fd: the open file descriptor referring to the file.
1698 :param name: the name of the extended attribute to set.
1699 :param value: the bytes of the extended attribute value
1700 """
1701 self.require_state("mounted")
1702
1703 name = cstr(name, 'name')
1704 if not isinstance(fd, int):
1705 raise TypeError('fd must be an int')
1706 if not isinstance(flags, int):
1707 raise TypeError('flags must be a int')
1708 if not isinstance(value, bytes):
1709 raise TypeError('value must be a bytes')
1710
1711 cdef:
1712 int _fd = fd
1713 char *_name = name
1714 char *_value = value
1715 size_t _value_len = len(value)
1716 int _flags = flags
1717
1718 with nogil:
1719 ret = ceph_fsetxattr(self.cluster, _fd, _name,
1720 _value, _value_len, _flags)
1721 if ret < 0:
1722 raise make_ex(ret, "error in fsetxattr")
1723
1724 def lsetxattr(self, path, name, value, flags):
1725 """
1726 Set an extended attribute on a file but do not follow symbolic link.
1727
1728 :param path: the path to the file.
1729 :param name: the name of the extended attribute to set.
1730 :param value: the bytes of the extended attribute value
1731 """
1732
1733 self.setxattr(path, name, value, flags, follow_symlink=False)
1734
1735 def removexattr(self, path, name, follow_symlink=True):
1736 """
1737 Remove an extended attribute of a file.
1738
1739 :param path: path of the file.
1740 :param name: name of the extended attribute to remove.
1741 """
1742 self.require_state("mounted")
1743
1744 name = cstr(name, 'name')
1745 path = cstr(path, 'path')
1746
1747 cdef:
1748 char *_path = path
1749 char *_name = name
1750
1751 if follow_symlink:
1752 with nogil:
1753 ret = ceph_removexattr(self.cluster, _path, _name)
1754 else:
1755 with nogil:
1756 ret = ceph_lremovexattr(self.cluster, _path, _name)
1757
1758 if ret < 0:
1759 raise make_ex(ret, "error in removexattr")
1760
1761 def fremovexattr(self, fd, name):
1762 """
1763 Remove an extended attribute of a file.
1764
1765 :param fd: the open file descriptor referring to the file.
1766 :param name: name of the extended attribute to remove.
1767 """
1768 self.require_state("mounted")
1769
1770 if not isinstance(fd, int):
1771 raise TypeError('fd must be an int')
1772 name = cstr(name, 'name')
1773
1774 cdef:
1775 int _fd = fd
1776 char *_name = name
1777
1778 with nogil:
1779 ret = ceph_fremovexattr(self.cluster, _fd, _name)
1780 if ret < 0:
1781 raise make_ex(ret, "error in fremovexattr")
1782
1783 def lremovexattr(self, path, name):
1784 """
1785 Remove an extended attribute of a file but do not follow symbolic link.
1786
1787 :param path: path of the file.
1788 :param name: name of the extended attribute to remove.
1789 """
1790 self.removexattr(path, name, follow_symlink=False)
1791
1792 def listxattr(self, path, size=65536, follow_symlink=True):
1793 """
1794 List the extended attribute keys set on a file.
1795
1796 :param path: path of the file.
1797 :param size: the size of list buffer to be filled with extended attribute keys.
1798 """
1799 self.require_state("mounted")
1800
1801 path = cstr(path, 'path')
1802
1803 cdef:
1804 char *_path = path
1805 char *ret_buf = NULL
1806 size_t ret_length = size
1807
1808 try:
1809 ret_buf = <char *>realloc_chk(ret_buf, ret_length)
1810 if follow_symlink:
1811 with nogil:
1812 ret = ceph_listxattr(self.cluster, _path, ret_buf, ret_length)
1813 else:
1814 with nogil:
1815 ret = ceph_llistxattr(self.cluster, _path, ret_buf, ret_length)
1816
1817 if ret < 0:
1818 raise make_ex(ret, "error in listxattr")
1819
1820 return ret, ret_buf[:ret]
1821 finally:
1822 free(ret_buf)
1823
1824 def flistxattr(self, fd, size=65536):
1825 """
1826 List the extended attribute keys set on a file.
1827
1828 :param fd: the open file descriptor referring to the file.
1829 :param size: the size of list buffer to be filled with extended attribute keys.
1830 """
1831 self.require_state("mounted")
1832
1833 if not isinstance(fd, int):
1834 raise TypeError('fd must be an int')
1835
1836 cdef:
1837 int _fd = fd
1838 char *ret_buf = NULL
1839 size_t ret_length = size
1840
1841 try:
1842 ret_buf = <char *>realloc_chk(ret_buf, ret_length)
1843 with nogil:
1844 ret = ceph_flistxattr(self.cluster, _fd, ret_buf, ret_length)
1845
1846 if ret < 0:
1847 raise make_ex(ret, "error in flistxattr")
1848
1849 return ret, ret_buf[:ret]
1850 finally:
1851 free(ret_buf)
1852
1853 def llistxattr(self, path, size=65536):
1854 """
1855 List the extended attribute keys set on a file but do not follow symbolic link.
1856
1857 :param path: path of the file.
1858 :param size: the size of list buffer to be filled with extended attribute keys.
1859 """
1860
1861 return self.listxattr(path, size=size, follow_symlink=False)
1862
1863 def stat(self, path, follow_symlink=True):
1864 """
1865 Get a file's extended statistics and attributes.
1866
1867 :param path: the file or directory to get the statistics of.
1868 """
1869 self.require_state("mounted")
1870 path = cstr(path, 'path')
1871
1872 cdef:
1873 char* _path = path
1874 statx stx
1875
1876 if follow_symlink:
1877 with nogil:
1878 ret = ceph_statx(self.cluster, _path, &stx,
1879 CEPH_STATX_BASIC_STATS_CDEF, 0)
1880 else:
1881 with nogil:
1882 ret = ceph_statx(self.cluster, _path, &stx,
1883 CEPH_STATX_BASIC_STATS_CDEF, AT_SYMLINK_NOFOLLOW_CDEF)
1884
1885 if ret < 0:
1886 raise make_ex(ret, "error in stat: {}".format(path.decode('utf-8')))
1887 return StatResult(st_dev=stx.stx_dev, st_ino=stx.stx_ino,
1888 st_mode=stx.stx_mode, st_nlink=stx.stx_nlink,
1889 st_uid=stx.stx_uid, st_gid=stx.stx_gid,
1890 st_rdev=stx.stx_rdev, st_size=stx.stx_size,
1891 st_blksize=stx.stx_blksize,
1892 st_blocks=stx.stx_blocks,
1893 st_atime=datetime.fromtimestamp(stx.stx_atime.tv_sec),
1894 st_mtime=datetime.fromtimestamp(stx.stx_mtime.tv_sec),
1895 st_ctime=datetime.fromtimestamp(stx.stx_ctime.tv_sec))
1896
1897 def lstat(self, path):
1898 """
1899 Get a file's extended statistics and attributes. When file's a
1900 symbolic link, return the informaion of the link itself rather
1901 than that of the file it points too.
1902
1903 :param path: the file or directory to get the statistics of.
1904 """
1905 return self.stat(path, follow_symlink=False)
1906
1907 def fstat(self, fd):
1908 """
1909 Get an open file's extended statistics and attributes.
1910
1911 :param fd: the file descriptor of the file to get statistics of.
1912 """
1913 self.require_state("mounted")
1914 if not isinstance(fd, int):
1915 raise TypeError('fd must be an int')
1916
1917 cdef:
1918 int _fd = fd
1919 statx stx
1920
1921 with nogil:
1922 ret = ceph_fstatx(self.cluster, _fd, &stx,
1923 CEPH_STATX_BASIC_STATS_CDEF, 0)
1924 if ret < 0:
1925 raise make_ex(ret, "error in fsat")
1926 return StatResult(st_dev=stx.stx_dev, st_ino=stx.stx_ino,
1927 st_mode=stx.stx_mode, st_nlink=stx.stx_nlink,
1928 st_uid=stx.stx_uid, st_gid=stx.stx_gid,
1929 st_rdev=stx.stx_rdev, st_size=stx.stx_size,
1930 st_blksize=stx.stx_blksize,
1931 st_blocks=stx.stx_blocks,
1932 st_atime=datetime.fromtimestamp(stx.stx_atime.tv_sec),
1933 st_mtime=datetime.fromtimestamp(stx.stx_mtime.tv_sec),
1934 st_ctime=datetime.fromtimestamp(stx.stx_ctime.tv_sec))
1935
1936 def statx(self, path, mask, flag):
1937 """
1938 Get a file's extended statistics and attributes.
1939
1940 :param path: the file or directory to get the statistics of.
1941 :param mask: want bitfield of CEPH_STATX_* flags showing designed attributes.
1942 :param flag: bitfield that can be used to set AT_* modifier flags (only AT_NO_ATTR_SYNC and AT_SYMLINK_NOFOLLOW)
1943 """
1944
1945 self.require_state("mounted")
1946 path = cstr(path, 'path')
1947 if not isinstance(mask, int):
1948 raise TypeError('flag must be a int')
1949 if not isinstance(flag, int):
1950 raise TypeError('flag must be a int')
1951
1952 cdef:
1953 char* _path = path
1954 statx stx
1955 int _mask = mask
1956 int _flag = flag
1957 dict_result = dict()
1958
1959 with nogil:
1960 ret = ceph_statx(self.cluster, _path, &stx, _mask, _flag)
1961 if ret < 0:
1962 raise make_ex(ret, "error in stat: %s" % path)
1963
1964 if (_mask & CEPH_STATX_MODE):
1965 dict_result["mode"] = stx.stx_mode
1966 if (_mask & CEPH_STATX_NLINK):
1967 dict_result["nlink"] = stx.stx_nlink
1968 if (_mask & CEPH_STATX_UID):
1969 dict_result["uid"] = stx.stx_uid
1970 if (_mask & CEPH_STATX_GID):
1971 dict_result["gid"] = stx.stx_gid
1972 if (_mask & CEPH_STATX_RDEV):
1973 dict_result["rdev"] = stx.stx_rdev
1974 if (_mask & CEPH_STATX_ATIME):
1975 dict_result["atime"] = datetime.fromtimestamp(stx.stx_atime.tv_sec)
1976 if (_mask & CEPH_STATX_MTIME):
1977 dict_result["mtime"] = datetime.fromtimestamp(stx.stx_mtime.tv_sec)
1978 if (_mask & CEPH_STATX_CTIME):
1979 dict_result["ctime"] = datetime.fromtimestamp(stx.stx_ctime.tv_sec)
1980 if (_mask & CEPH_STATX_INO):
1981 dict_result["ino"] = stx.stx_ino
1982 if (_mask & CEPH_STATX_SIZE):
1983 dict_result["size"] = stx.stx_size
1984 if (_mask & CEPH_STATX_BLOCKS):
1985 dict_result["blocks"] = stx.stx_blocks
1986 if (_mask & CEPH_STATX_BTIME):
1987 dict_result["btime"] = datetime.fromtimestamp(stx.stx_btime.tv_sec)
1988 if (_mask & CEPH_STATX_VERSION):
1989 dict_result["version"] = stx.stx_version
1990
1991 return dict_result
1992
1993 def setattrx(self, path, dict_stx, mask, flags):
1994 """
1995 Set a file's attributes.
1996
1997 :param path: the path to the file/directory to set the attributes of.
1998 :param mask: a mask of all the CEPH_SETATTR_* values that have been set in the statx struct.
1999 :param stx: a dict of statx structure that must include attribute values to set on the file.
2000 :param flags: mask of AT_* flags (only AT_ATTR_NOFOLLOW is respected for now)
2001 """
2002
2003 self.require_state("mounted")
2004 path = cstr(path, 'path')
2005 if not isinstance(dict_stx, dict):
2006 raise TypeError('dict_stx must be a dict')
2007 if not isinstance(mask, int):
2008 raise TypeError('mask must be a int')
2009 if not isinstance(flags, int):
2010 raise TypeError('flags must be a int')
2011
2012 cdef statx stx
2013
2014 if (mask & CEPH_SETATTR_MODE):
2015 stx.stx_mode = dict_stx["mode"]
2016 if (mask & CEPH_SETATTR_UID):
2017 stx.stx_uid = dict_stx["uid"]
2018 if (mask & CEPH_SETATTR_GID):
2019 stx.stx_gid = dict_stx["gid"]
2020 if (mask & CEPH_SETATTR_MTIME):
2021 stx.stx_mtime = to_timespec(dict_stx["mtime"].timestamp())
2022 if (mask & CEPH_SETATTR_ATIME):
2023 stx.stx_atime = to_timespec(dict_stx["atime"].timestamp())
2024 if (mask & CEPH_SETATTR_CTIME):
2025 stx.stx_ctime = to_timespec(dict_stx["ctime"].timestamp())
2026 if (mask & CEPH_SETATTR_SIZE):
2027 stx.stx_size = dict_stx["size"]
2028 if (mask & CEPH_SETATTR_BTIME):
2029 stx.stx_btime = to_timespec(dict_stx["btime"].timestamp())
2030
2031 cdef:
2032 char* _path = path
2033 int _mask = mask
2034 int _flags = flags
2035 dict_result = dict()
2036
2037 with nogil:
2038 ret = ceph_setattrx(self.cluster, _path, &stx, _mask, _flags)
2039 if ret < 0:
2040 raise make_ex(ret, "error in setattrx: %s" % path)
2041
2042 def fsetattrx(self, fd, dict_stx, mask):
2043 """
2044 Set a file's attributes.
2045
2046 :param path: the path to the file/directory to set the attributes of.
2047 :param mask: a mask of all the CEPH_SETATTR_* values that have been set in the statx struct.
2048 :param stx: a dict of statx structure that must include attribute values to set on the file.
2049 """
2050
2051 self.require_state("mounted")
2052 if not isinstance(fd, int):
2053 raise TypeError('fd must be a int')
2054 if not isinstance(dict_stx, dict):
2055 raise TypeError('dict_stx must be a dict')
2056 if not isinstance(mask, int):
2057 raise TypeError('mask must be a int')
2058
2059 cdef statx stx
2060
2061 if (mask & CEPH_SETATTR_MODE):
2062 stx.stx_mode = dict_stx["mode"]
2063 if (mask & CEPH_SETATTR_UID):
2064 stx.stx_uid = dict_stx["uid"]
2065 if (mask & CEPH_SETATTR_GID):
2066 stx.stx_gid = dict_stx["gid"]
2067 if (mask & CEPH_SETATTR_MTIME):
2068 stx.stx_mtime = to_timespec(dict_stx["mtime"].timestamp())
2069 if (mask & CEPH_SETATTR_ATIME):
2070 stx.stx_atime = to_timespec(dict_stx["atime"].timestamp())
2071 if (mask & CEPH_SETATTR_CTIME):
2072 stx.stx_ctime = to_timespec(dict_stx["ctime"].timestamp())
2073 if (mask & CEPH_SETATTR_SIZE):
2074 stx.stx_size = dict_stx["size"]
2075 if (mask & CEPH_SETATTR_BTIME):
2076 stx.stx_btime = to_timespec(dict_stx["btime"].timestamp())
2077
2078 cdef:
2079 int _fd = fd
2080 int _mask = mask
2081 dict_result = dict()
2082
2083 with nogil:
2084 ret = ceph_fsetattrx(self.cluster, _fd, &stx, _mask)
2085 if ret < 0:
2086 raise make_ex(ret, "error in fsetattrx")
2087
2088 def symlink(self, existing, newname):
2089 """
2090 Creates a symbolic link.
2091
2092 :param existing: the path to the existing file/directory to link to.
2093 :param newname: the path to the new file/directory to link from.
2094 """
2095 self.require_state("mounted")
2096 existing = cstr(existing, 'existing')
2097 newname = cstr(newname, 'newname')
2098 cdef:
2099 char* _existing = existing
2100 char* _newname = newname
2101
2102 with nogil:
2103 ret = ceph_symlink(self.cluster, _existing, _newname)
2104 if ret < 0:
2105 raise make_ex(ret, "error in symlink")
2106
2107 def link(self, existing, newname):
2108 """
2109 Create a link.
2110
2111 :param existing: the path to the existing file/directory to link to.
2112 :param newname: the path to the new file/directory to link from.
2113 """
2114
2115 self.require_state("mounted")
2116 existing = cstr(existing, 'existing')
2117 newname = cstr(newname, 'newname')
2118 cdef:
2119 char* _existing = existing
2120 char* _newname = newname
2121
2122 with nogil:
2123 ret = ceph_link(self.cluster, _existing, _newname)
2124 if ret < 0:
2125 raise make_ex(ret, "error in link")
2126
2127 def readlink(self, path, size):
2128 """
2129 Read a symbolic link.
2130
2131 :param path: the path to the symlink to read
2132 :param size: the length of the buffer
2133 :rtype buf: buffer to hold the path of the file that the symlink points to.
2134 """
2135 self.require_state("mounted")
2136 path = cstr(path, 'path')
2137
2138 cdef:
2139 char* _path = path
2140 int64_t _size = size
2141 char *buf = NULL
2142
2143 try:
2144 buf = <char *>realloc_chk(buf, _size)
2145 with nogil:
2146 ret = ceph_readlink(self.cluster, _path, buf, _size)
2147 if ret < 0:
2148 raise make_ex(ret, "error in readlink")
2149 return buf[:ret]
2150 finally:
2151 free(buf)
2152
2153 def unlink(self, path):
2154 """
2155 Removes a file, link, or symbolic link. If the file/link has multiple links to it, the
2156 file will not disappear from the namespace until all references to it are removed.
2157
2158 :param path: the path of the file or link to unlink.
2159 """
2160 self.require_state("mounted")
2161 path = cstr(path, 'path')
2162 cdef char* _path = path
2163 with nogil:
2164 ret = ceph_unlink(self.cluster, _path)
2165 if ret < 0:
2166 raise make_ex(ret, "error in unlink: {}".format(path.decode('utf-8')))
2167
2168 def rename(self, src, dst):
2169 """
2170 Rename a file or directory.
2171
2172 :param src: the path to the existing file or directory.
2173 :param dst: the new name of the file or directory.
2174 """
2175
2176 self.require_state("mounted")
2177
2178 src = cstr(src, 'source')
2179 dst = cstr(dst, 'destination')
2180
2181 cdef:
2182 char* _src = src
2183 char* _dst = dst
2184
2185 with nogil:
2186 ret = ceph_rename(self.cluster, _src, _dst)
2187 if ret < 0:
2188 raise make_ex(ret, "error in rename {} to {}".format(src.decode(
2189 'utf-8'), dst.decode('utf-8')))
2190
2191 def mds_command(self, mds_spec, args, input_data):
2192 """
2193 :return 3-tuple of output status int, output status string, output data
2194 """
2195 mds_spec = cstr(mds_spec, 'mds_spec')
2196 args = cstr(args, 'args')
2197 input_data = cstr(input_data, 'input_data')
2198
2199 cdef:
2200 char *_mds_spec = opt_str(mds_spec)
2201 char **_cmd = to_bytes_array([args])
2202 size_t _cmdlen = 1
2203
2204 char *_inbuf = input_data
2205 size_t _inbuf_len = len(input_data)
2206
2207 char *_outbuf = NULL
2208 size_t _outbuf_len = 0
2209 char *_outs = NULL
2210 size_t _outs_len = 0
2211
2212 try:
2213 with nogil:
2214 ret = ceph_mds_command(self.cluster, _mds_spec,
2215 <const char **>_cmd, _cmdlen,
2216 <const char*>_inbuf, _inbuf_len,
2217 &_outbuf, &_outbuf_len,
2218 &_outs, &_outs_len)
2219 my_outs = decode_cstr(_outs[:_outs_len])
2220 my_outbuf = _outbuf[:_outbuf_len]
2221 if _outs_len:
2222 ceph_buffer_free(_outs)
2223 if _outbuf_len:
2224 ceph_buffer_free(_outbuf)
2225 return (ret, my_outbuf, my_outs)
2226 finally:
2227 free(_cmd)
2228
2229 def umask(self, mode) :
2230 self.require_state("mounted")
2231 cdef:
2232 mode_t _mode = mode
2233 with nogil:
2234 ret = ceph_umask(self.cluster, _mode)
2235 if ret < 0:
2236 raise make_ex(ret, "error in umask")
2237 return ret
2238
2239 def lseek(self, fd, offset, whence):
2240 """
2241 Set the file's current position.
2242
2243 :param fd: the file descriptor of the open file to read from.
2244 :param offset: the offset in the file to read from. If this value is negative, the
2245 function reads from the current offset of the file descriptor.
2246 :param whence: the flag to indicate what type of seeking to performs:SEEK_SET, SEEK_CUR, SEEK_END
2247 """
2248 self.require_state("mounted")
2249 if not isinstance(fd, int):
2250 raise TypeError('fd must be an int')
2251 if not isinstance(offset, int):
2252 raise TypeError('offset must be an int')
2253 if not isinstance(whence, int):
2254 raise TypeError('whence must be an int')
2255
2256 cdef:
2257 int _fd = fd
2258 int64_t _offset = offset
2259 int64_t _whence = whence
2260
2261 with nogil:
2262 ret = ceph_lseek(self.cluster, _fd, _offset, _whence)
2263
2264 if ret < 0:
2265 raise make_ex(ret, "error in lseek")
2266
2267 return ret
2268
2269 def utime(self, path, times=None):
2270 """
2271 Set access and modification time for path
2272
2273 :param path: file path for which timestamps have to be changed
2274 :param times: if times is not None, it must be a tuple (atime, mtime)
2275 """
2276
2277 self.require_state("mounted")
2278 path = cstr(path, 'path')
2279 if times:
2280 if not isinstance(times, tuple):
2281 raise TypeError('times must be a tuple')
2282 if not isinstance(times[0], int):
2283 raise TypeError('atime must be an int')
2284 if not isinstance(times[1], int):
2285 raise TypeError('mtime must be an int')
2286 actime = modtime = int(time.time())
2287 if times:
2288 actime = times[0]
2289 modtime = times[1]
2290
2291 cdef:
2292 char *pth = path
2293 utimbuf buf = utimbuf(actime, modtime)
2294 with nogil:
2295 ret = ceph_utime(self.cluster, pth, &buf)
2296 if ret < 0:
2297 raise make_ex(ret, "error in utime {}".format(path.decode('utf-8')))
2298
2299 def futime(self, fd, times=None):
2300 """
2301 Set access and modification time for a file pointed by descriptor
2302
2303 :param fd: file descriptor of the open file
2304 :param times: if times is not None, it must be a tuple (atime, mtime)
2305 """
2306
2307 self.require_state("mounted")
2308 if not isinstance(fd, int):
2309 raise TypeError('fd must be an int')
2310 if times:
2311 if not isinstance(times, tuple):
2312 raise TypeError('times must be a tuple')
2313 if not isinstance(times[0], int):
2314 raise TypeError('atime must be an int')
2315 if not isinstance(times[1], int):
2316 raise TypeError('mtime must be an int')
2317 actime = modtime = int(time.time())
2318 if times:
2319 actime = times[0]
2320 modtime = times[1]
2321
2322 cdef:
2323 int _fd = fd
2324 utimbuf buf = utimbuf(actime, modtime)
2325 with nogil:
2326 ret = ceph_futime(self.cluster, _fd, &buf)
2327 if ret < 0:
2328 raise make_ex(ret, "error in futime")
2329
2330 def utimes(self, path, times=None, follow_symlink=True):
2331 """
2332 Set access and modification time for path
2333
2334 :param path: file path for which timestamps have to be changed
2335 :param times: if times is not None, it must be a tuple (atime, mtime)
2336 :param follow_symlink: perform the operation on the target file if @path
2337 is a symbolic link (default)
2338 """
2339
2340 self.require_state("mounted")
2341 path = cstr(path, 'path')
2342 if times:
2343 if not isinstance(times, tuple):
2344 raise TypeError('times must be a tuple')
2345 if not isinstance(times[0], (int, float)):
2346 raise TypeError('atime must be an int or a float')
2347 if not isinstance(times[1], (int, float)):
2348 raise TypeError('mtime must be an int or a float')
2349 actime = modtime = time.time()
2350 if times:
2351 actime = float(times[0])
2352 modtime = float(times[1])
2353
2354 cdef:
2355 char *pth = path
2356 timeval *buf = [to_timeval(actime), to_timeval(modtime)]
2357 if follow_symlink:
2358 with nogil:
2359 ret = ceph_utimes(self.cluster, pth, buf)
2360 else:
2361 with nogil:
2362 ret = ceph_lutimes(self.cluster, pth, buf)
2363 if ret < 0:
2364 raise make_ex(ret, "error in utimes {}".format(path.decode('utf-8')))
2365
2366 def lutimes(self, path, times=None):
2367 """
2368 Set access and modification time for a file. If the file is a symbolic
2369 link do not follow to the target.
2370
2371 :param path: file path for which timestamps have to be changed
2372 :param times: if times is not None, it must be a tuple (atime, mtime)
2373 """
2374 self.utimes(path, times=times, follow_symlink=False)
2375
2376 def futimes(self, fd, times=None):
2377 """
2378 Set access and modification time for a file pointer by descriptor
2379
2380 :param fd: file descriptor of the open file
2381 :param times: if times is not None, it must be a tuple (atime, mtime)
2382 """
2383
2384 self.require_state("mounted")
2385 if not isinstance(fd, int):
2386 raise TypeError('fd must be an int')
2387 if times:
2388 if not isinstance(times, tuple):
2389 raise TypeError('times must be a tuple')
2390 if not isinstance(times[0], (int, float)):
2391 raise TypeError('atime must be an int or a float')
2392 if not isinstance(times[1], (int, float)):
2393 raise TypeError('mtime must be an int or a float')
2394 actime = modtime = time.time()
2395 if times:
2396 actime = float(times[0])
2397 modtime = float(times[1])
2398
2399 cdef:
2400 int _fd = fd
2401 timeval *buf = [to_timeval(actime), to_timeval(modtime)]
2402 with nogil:
2403 ret = ceph_futimes(self.cluster, _fd, buf)
2404 if ret < 0:
2405 raise make_ex(ret, "error in futimes")
2406
2407 def futimens(self, fd, times=None):
2408 """
2409 Set access and modification time for a file pointer by descriptor
2410
2411 :param fd: file descriptor of the open file
2412 :param times: if times is not None, it must be a tuple (atime, mtime)
2413 """
2414
2415 self.require_state("mounted")
2416 if not isinstance(fd, int):
2417 raise TypeError('fd must be an int')
2418 if times:
2419 if not isinstance(times, tuple):
2420 raise TypeError('times must be a tuple')
2421 if not isinstance(times[0], (int, float)):
2422 raise TypeError('atime must be an int or a float')
2423 if not isinstance(times[1], (int, float)):
2424 raise TypeError('mtime must be an int or a float')
2425 actime = modtime = time.time()
2426 if times:
2427 actime = float(times[0])
2428 modtime = float(times[1])
2429
2430 cdef:
2431 int _fd = fd
2432 timespec *buf = [to_timespec(actime), to_timespec(modtime)]
2433 with nogil:
2434 ret = ceph_futimens(self.cluster, _fd, buf)
2435 if ret < 0:
2436 raise make_ex(ret, "error in futimens")
2437
2438 def get_file_replication(self, fd):
2439 """
2440 Get the file replication information from an open file descriptor.
2441
2442 :param fd : the open file descriptor referring to the file to get
2443 the replication information of.
2444 """
2445 self.require_state("mounted")
2446 if not isinstance(fd, int):
2447 raise TypeError('fd must be an int')
2448
2449 cdef:
2450 int _fd = fd
2451
2452 with nogil:
2453 ret = ceph_get_file_replication(self.cluster, _fd)
2454 if ret < 0:
2455 raise make_ex(ret, "error in get_file_replication")
2456
2457 return ret
2458
2459 def get_path_replication(self, path):
2460 """
2461 Get the file replication information given the path.
2462
2463 :param path: the path of the file/directory to get the replication information of.
2464 """
2465 self.require_state("mounted")
2466 path = cstr(path, 'path')
2467
2468 cdef:
2469 char* _path = path
2470
2471 with nogil:
2472 ret = ceph_get_path_replication(self.cluster, _path)
2473 if ret < 0:
2474 raise make_ex(ret, "error in get_path_replication")
2475
2476 return ret
2477
2478 def get_pool_id(self, pool_name):
2479 """
2480 Get the id of the named pool.
2481
2482 :param pool_name: the name of the pool.
2483 """
2484
2485 self.require_state("mounted")
2486 pool_name = cstr(pool_name, 'pool_name')
2487
2488 cdef:
2489 char* _pool_name = pool_name
2490
2491 with nogil:
2492 ret = ceph_get_pool_id(self.cluster, _pool_name)
2493 if ret < 0:
2494 raise make_ex(ret, "error in get_pool_id")
2495
2496 return ret
2497
2498 def get_pool_replication(self, pool_id):
2499 """
2500 Get the pool replication factor.
2501
2502 :param pool_id: the pool id to look up
2503 """
2504
2505 self.require_state("mounted")
2506 if not isinstance(pool_id, int):
2507 raise TypeError('pool_id must be an int')
2508
2509 cdef:
2510 int _pool_id = pool_id
2511
2512 with nogil:
2513 ret = ceph_get_pool_replication(self.cluster, _pool_id)
2514 if ret < 0:
2515 raise make_ex(ret, "error in get_pool_replication")
2516
2517 return ret
2518
2519 def debug_get_fd_caps(self, fd):
2520 """
2521 Get the capabilities currently issued to the client given the fd.
2522
2523 :param fd: the file descriptor to get issued
2524 """
2525
2526 self.require_state("mounted")
2527 if not isinstance(fd, int):
2528 raise TypeError('fd must be an int')
2529
2530 cdef:
2531 int _fd = fd
2532
2533 with nogil:
2534 ret = ceph_debug_get_fd_caps(self.cluster, _fd)
2535 if ret < 0:
2536 raise make_ex(ret, "error in debug_get_fd_caps")
2537
2538 return ret
2539
2540 def debug_get_file_caps(self, path):
2541 """
2542 Get the capabilities currently issued to the client given the path.
2543
2544 :param path: the path of the file/directory to get the capabilities of.
2545 """
2546
2547 self.require_state("mounted")
2548 path = cstr(path, 'path')
2549
2550 cdef:
2551 char* _path = path
2552
2553 with nogil:
2554 ret = ceph_debug_get_file_caps(self.cluster, _path)
2555 if ret < 0:
2556 raise make_ex(ret, "error in debug_get_file_caps")
2557
2558 return ret
2559
2560 def get_cap_return_timeout(self):
2561 """
2562 Get the amount of time that the client has to return caps
2563
2564 In the event that a client does not return its caps, the MDS may blocklist
2565 it after this timeout. Applications should check this value and ensure
2566 that they set the delegation timeout to a value lower than this.
2567 """
2568
2569 self.require_state("mounted")
2570
2571 with nogil:
2572 ret = ceph_get_cap_return_timeout(self.cluster)
2573 if ret < 0:
2574 raise make_ex(ret, "error in get_cap_return_timeout")
2575
2576 return ret
2577
2578 def set_uuid(self, uuid):
2579 """
2580 Set ceph client uuid. Must be called before mount.
2581
2582 :param uuid: the uuid to set
2583 """
2584
2585 uuid = cstr(uuid, 'uuid')
2586
2587 cdef:
2588 char* _uuid = uuid
2589
2590 with nogil:
2591 ceph_set_uuid(self.cluster, _uuid)
2592
2593 def set_session_timeout(self, timeout):
2594 """
2595 Set ceph client session timeout. Must be called before mount.
2596
2597 :param timeout: the timeout to set
2598 """
2599
2600 if not isinstance(timeout, int):
2601 raise TypeError('timeout must be an int')
2602
2603 cdef:
2604 int _timeout = timeout
2605
2606 with nogil:
2607 ceph_set_session_timeout(self.cluster, _timeout)
2608
2609 def get_layout(self, fd):
2610 """
2611 Set ceph client session timeout. Must be called before mount.
2612
2613 :param fd: file descriptor of the file/directory for which to get the layout
2614 """
2615
2616 if not isinstance(fd, int):
2617 raise TypeError('fd must be an int')
2618
2619 cdef:
2620 int _fd = fd
2621 int stripe_unit
2622 int stripe_count
2623 int object_size
2624 int pool_id
2625 char *buf = NULL
2626 int buflen = 256
2627 dict_result = dict()
2628
2629 with nogil:
2630 ret = ceph_get_file_layout(self.cluster, _fd, &stripe_unit, &stripe_count, &object_size, &pool_id)
2631 if ret < 0:
2632 raise make_ex(stripe_unit, "error in get_file_layout")
2633 dict_result["stripe_unit"] = stripe_unit
2634 dict_result["stripe_count"] = stripe_count
2635 dict_result["object_size"] = object_size
2636 dict_result["pool_id"] = pool_id
2637
2638 try:
2639 while True:
2640 buf = <char *>realloc_chk(buf, buflen)
2641 with nogil:
2642 ret = ceph_get_file_pool_name(self.cluster, _fd, buf, buflen)
2643 if ret > 0:
2644 dict_result["pool_name"] = decode_cstr(buf)
2645 return dict_result
2646 elif ret == -CEPHFS_ERANGE:
2647 buflen = buflen * 2
2648 else:
2649 raise make_ex(ret, "error in get_file_pool_name")
2650 finally:
2651 free(buf)
2652
2653
2654 def get_default_pool(self):
2655 """
2656 Get the default pool name and id of cephfs. This returns dict{pool_name, pool_id}.
2657 """
2658
2659 cdef:
2660 char *buf = NULL
2661 int buflen = 256
2662 dict_result = dict()
2663
2664 try:
2665 while True:
2666 buf = <char *>realloc_chk(buf, buflen)
2667 with nogil:
2668 ret = ceph_get_default_data_pool_name(self.cluster, buf, buflen)
2669 if ret > 0:
2670 dict_result["pool_name"] = decode_cstr(buf)
2671 break
2672 elif ret == -CEPHFS_ERANGE:
2673 buflen = buflen * 2
2674 else:
2675 raise make_ex(ret, "error in get_default_data_pool_name")
2676
2677 with nogil:
2678 ret = ceph_get_pool_id(self.cluster, buf)
2679 if ret < 0:
2680 raise make_ex(ret, "error in get_pool_id")
2681 dict_result["pool_id"] = ret
2682 return dict_result
2683
2684 finally:
2685 free(buf)