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