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