]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | """ |
2 | This module is a thin wrapper around libcephfs. | |
3 | """ | |
4 | ||
5 | from cpython cimport PyObject, ref, exc | |
6 | from libc cimport errno | |
7 | from libc.stdint cimport * | |
8 | from libc.stdlib cimport malloc, realloc, free | |
9 | ||
10 | cimport rados | |
11 | ||
12 | from collections import namedtuple | |
13 | from datetime import datetime | |
14 | import errno | |
15 | import os | |
16 | import sys | |
9f95a23c | 17 | import time |
7c673cae FG |
18 | |
19 | # Are we running Python 2.x | |
20 | if sys.version_info[0] < 3: | |
21 | str_type = basestring | |
22 | else: | |
23 | str_type = str | |
24 | ||
9f95a23c TL |
25 | AT_NO_ATTR_SYNC = 0x4000 |
26 | AT_SYMLINK_NOFOLLOW = 0x100 | |
27 | cdef int AT_SYMLINK_NOFOLLOW_CDEF = AT_SYMLINK_NOFOLLOW | |
28 | CEPH_STATX_BASIC_STATS = 0x7ff | |
29 | cdef int CEPH_STATX_BASIC_STATS_CDEF = CEPH_STATX_BASIC_STATS | |
30 | CEPH_STATX_MODE = 0x1 | |
31 | CEPH_STATX_NLINK = 0x2 | |
32 | CEPH_STATX_UID = 0x4 | |
33 | CEPH_STATX_GID = 0x8 | |
34 | CEPH_STATX_RDEV = 0x10 | |
35 | CEPH_STATX_ATIME = 0x20 | |
36 | CEPH_STATX_MTIME = 0x40 | |
37 | CEPH_STATX_CTIME = 0x80 | |
38 | CEPH_STATX_INO = 0x100 | |
39 | CEPH_STATX_SIZE = 0x200 | |
40 | CEPH_STATX_BLOCKS = 0x400 | |
41 | CEPH_STATX_BTIME = 0x800 | |
42 | CEPH_STATX_VERSION = 0x1000 | |
7c673cae FG |
43 | |
44 | cdef extern from "Python.h": | |
45 | # These are in cpython/string.pxd, but use "object" types instead of | |
46 | # PyObject*, which invokes assumptions in cpython that we need to | |
47 | # legitimately break to implement zero-copy string buffers in Image.read(). | |
48 | # This is valid use of the Python API and documented as a special case. | |
49 | PyObject *PyBytes_FromStringAndSize(char *v, Py_ssize_t len) except NULL | |
50 | char* PyBytes_AsString(PyObject *string) except NULL | |
51 | int _PyBytes_Resize(PyObject **string, Py_ssize_t newsize) except -1 | |
52 | void PyEval_InitThreads() | |
53 | ||
54 | ||
55 | cdef extern from "sys/statvfs.h": | |
56 | cdef struct statvfs: | |
57 | unsigned long int f_bsize | |
58 | unsigned long int f_frsize | |
59 | unsigned long int f_blocks | |
60 | unsigned long int f_bfree | |
61 | unsigned long int f_bavail | |
62 | unsigned long int f_files | |
63 | unsigned long int f_ffree | |
64 | unsigned long int f_favail | |
65 | unsigned long int f_fsid | |
66 | unsigned long int f_flag | |
67 | unsigned long int f_namemax | |
68 | unsigned long int f_padding[32] | |
69 | ||
70 | ||
eafe8130 TL |
71 | IF UNAME_SYSNAME == "FreeBSD": |
72 | cdef extern from "dirent.h": | |
73 | cdef struct dirent: | |
74 | long int d_ino | |
75 | unsigned short int d_reclen | |
76 | unsigned char d_type | |
77 | char d_name[256] | |
78 | ELSE: | |
79 | cdef extern from "dirent.h": | |
80 | cdef struct dirent: | |
81 | long int d_ino | |
82 | unsigned long int d_off | |
83 | unsigned short int d_reclen | |
84 | unsigned char d_type | |
85 | char d_name[256] | |
7c673cae FG |
86 | |
87 | ||
88 | cdef extern from "time.h": | |
89 | ctypedef long int time_t | |
90 | ||
91 | cdef extern from "time.h": | |
92 | cdef struct timespec: | |
93 | time_t tv_sec | |
94 | long int tv_nsec | |
95 | ||
96 | cdef extern from "sys/types.h": | |
97 | ctypedef unsigned long mode_t | |
98 | ||
9f95a23c TL |
99 | cdef extern from "<utime.h>": |
100 | cdef struct utimbuf: | |
101 | time_t actime | |
102 | time_t modtime | |
103 | ||
104 | cdef extern from "sys/time.h": | |
105 | cdef struct timeval: | |
106 | long tv_sec | |
107 | long tv_usec | |
108 | ||
7c673cae FG |
109 | cdef extern from "cephfs/ceph_statx.h": |
110 | cdef struct statx "ceph_statx": | |
111 | uint32_t stx_mask | |
112 | uint32_t stx_blksize | |
113 | uint32_t stx_nlink | |
114 | uint32_t stx_uid | |
115 | uint32_t stx_gid | |
116 | uint16_t stx_mode | |
117 | uint64_t stx_ino | |
118 | uint64_t stx_size | |
119 | uint64_t stx_blocks | |
120 | uint64_t stx_dev | |
121 | uint64_t stx_rdev | |
122 | timespec stx_atime | |
123 | timespec stx_ctime | |
124 | timespec stx_mtime | |
125 | timespec stx_btime | |
126 | uint64_t stx_version | |
127 | ||
128 | cdef extern from "cephfs/libcephfs.h" nogil: | |
129 | cdef struct ceph_mount_info: | |
130 | pass | |
131 | ||
132 | cdef struct ceph_dir_result: | |
133 | pass | |
134 | ||
135 | ctypedef void* rados_t | |
136 | ||
137 | const char *ceph_version(int *major, int *minor, int *patch) | |
138 | ||
139 | int ceph_create(ceph_mount_info **cmount, const char * const id) | |
140 | int ceph_create_from_rados(ceph_mount_info **cmount, rados_t cluster) | |
141 | int ceph_init(ceph_mount_info *cmount) | |
142 | void ceph_shutdown(ceph_mount_info *cmount) | |
143 | ||
9f95a23c | 144 | int ceph_getaddrs(ceph_mount_info* cmount, char** addrs) |
7c673cae FG |
145 | int ceph_conf_read_file(ceph_mount_info *cmount, const char *path_list) |
146 | int ceph_conf_parse_argv(ceph_mount_info *cmount, int argc, const char **argv) | |
147 | int ceph_conf_get(ceph_mount_info *cmount, const char *option, char *buf, size_t len) | |
148 | int ceph_conf_set(ceph_mount_info *cmount, const char *option, const char *value) | |
149 | ||
150 | int ceph_mount(ceph_mount_info *cmount, const char *root) | |
11fdf7f2 | 151 | int ceph_select_filesystem(ceph_mount_info *cmount, const char *fs_name) |
7c673cae | 152 | int ceph_unmount(ceph_mount_info *cmount) |
11fdf7f2 TL |
153 | int ceph_abort_conn(ceph_mount_info *cmount) |
154 | uint64_t ceph_get_instance_id(ceph_mount_info *cmount) | |
7c673cae FG |
155 | int ceph_fstatx(ceph_mount_info *cmount, int fd, statx *stx, unsigned want, unsigned flags) |
156 | int ceph_statx(ceph_mount_info *cmount, const char *path, statx *stx, unsigned want, unsigned flags) | |
157 | int ceph_statfs(ceph_mount_info *cmount, const char *path, statvfs *stbuf) | |
158 | ||
159 | int ceph_mds_command(ceph_mount_info *cmount, const char *mds_spec, const char **cmd, size_t cmdlen, | |
160 | const char *inbuf, size_t inbuflen, char **outbuf, size_t *outbuflen, | |
161 | char **outs, size_t *outslen) | |
162 | int ceph_rename(ceph_mount_info *cmount, const char *from_, const char *to) | |
163 | int ceph_link(ceph_mount_info *cmount, const char *existing, const char *newname) | |
164 | int ceph_unlink(ceph_mount_info *cmount, const char *path) | |
165 | int ceph_symlink(ceph_mount_info *cmount, const char *existing, const char *newname) | |
166 | int ceph_readlink(ceph_mount_info *cmount, const char *path, char *buf, int64_t size) | |
167 | int ceph_setxattr(ceph_mount_info *cmount, const char *path, const char *name, | |
168 | const void *value, size_t size, int flags) | |
169 | int ceph_getxattr(ceph_mount_info *cmount, const char *path, const char *name, | |
170 | void *value, size_t size) | |
9f95a23c TL |
171 | int ceph_removexattr(ceph_mount_info *cmount, const char *path, const char *name) |
172 | int ceph_listxattr(ceph_mount_info *cmount, const char *path, char *list, size_t size) | |
7c673cae FG |
173 | int ceph_write(ceph_mount_info *cmount, int fd, const char *buf, int64_t size, int64_t offset) |
174 | int ceph_read(ceph_mount_info *cmount, int fd, char *buf, int64_t size, int64_t offset) | |
175 | int ceph_flock(ceph_mount_info *cmount, int fd, int operation, uint64_t owner) | |
176 | int ceph_close(ceph_mount_info *cmount, int fd) | |
177 | int ceph_open(ceph_mount_info *cmount, const char *path, int flags, mode_t mode) | |
178 | int ceph_mkdir(ceph_mount_info *cmount, const char *path, mode_t mode) | |
179 | int ceph_mkdirs(ceph_mount_info *cmount, const char *path, mode_t mode) | |
180 | int ceph_closedir(ceph_mount_info *cmount, ceph_dir_result *dirp) | |
181 | int ceph_opendir(ceph_mount_info *cmount, const char *name, ceph_dir_result **dirpp) | |
81eedcae | 182 | void ceph_rewinddir(ceph_mount_info *cmount, ceph_dir_result *dirp) |
7c673cae FG |
183 | int ceph_chdir(ceph_mount_info *cmount, const char *path) |
184 | dirent * ceph_readdir(ceph_mount_info *cmount, ceph_dir_result *dirp) | |
185 | int ceph_rmdir(ceph_mount_info *cmount, const char *path) | |
186 | const char* ceph_getcwd(ceph_mount_info *cmount) | |
187 | int ceph_sync_fs(ceph_mount_info *cmount) | |
188 | int ceph_fsync(ceph_mount_info *cmount, int fd, int syncdataonly) | |
189 | int ceph_conf_parse_argv(ceph_mount_info *cmount, int argc, const char **argv) | |
11fdf7f2 | 190 | int ceph_chmod(ceph_mount_info *cmount, const char *path, mode_t mode) |
92f5a8d4 | 191 | int ceph_chown(ceph_mount_info *cmount, const char *path, int uid, int gid) |
9f95a23c | 192 | int ceph_lchown(ceph_mount_info *cmount, const char *path, int uid, int gid) |
81eedcae | 193 | int64_t ceph_lseek(ceph_mount_info *cmount, int fd, int64_t offset, int whence) |
7c673cae | 194 | void ceph_buffer_free(char *buf) |
11fdf7f2 | 195 | mode_t ceph_umask(ceph_mount_info *cmount, mode_t mode) |
9f95a23c TL |
196 | int ceph_utime(ceph_mount_info *cmount, const char *path, utimbuf *buf) |
197 | int ceph_futime(ceph_mount_info *cmount, int fd, utimbuf *buf) | |
198 | int ceph_utimes(ceph_mount_info *cmount, const char *path, timeval times[2]) | |
199 | int ceph_lutimes(ceph_mount_info *cmount, const char *path, timeval times[2]) | |
200 | int ceph_futimes(ceph_mount_info *cmount, int fd, timeval times[2]) | |
201 | int ceph_futimens(ceph_mount_info *cmount, int fd, timespec times[2]) | |
7c673cae FG |
202 | |
203 | ||
204 | class Error(Exception): | |
9f95a23c TL |
205 | def get_error_code(self): |
206 | return 1 | |
207 | ||
208 | ||
209 | class LibCephFSStateError(Error): | |
7c673cae FG |
210 | pass |
211 | ||
212 | ||
213 | class OSError(Error): | |
214 | def __init__(self, errno, strerror): | |
81eedcae | 215 | super(OSError, self).__init__(errno, strerror) |
7c673cae | 216 | self.errno = errno |
9f95a23c | 217 | self.strerror = "%s: %s" % (strerror, os.strerror(errno)) |
7c673cae FG |
218 | |
219 | def __str__(self): | |
9f95a23c TL |
220 | return '{} [Errno {}]'.format(self.strerror, self.errno) |
221 | ||
222 | def get_error_code(self): | |
223 | return self.errno | |
7c673cae FG |
224 | |
225 | ||
226 | class PermissionError(OSError): | |
227 | pass | |
228 | ||
229 | ||
230 | class ObjectNotFound(OSError): | |
231 | pass | |
232 | ||
233 | ||
234 | class NoData(OSError): | |
235 | pass | |
236 | ||
237 | ||
238 | class ObjectExists(OSError): | |
239 | pass | |
240 | ||
241 | ||
242 | class IOError(OSError): | |
243 | pass | |
244 | ||
245 | ||
246 | class NoSpace(OSError): | |
247 | pass | |
248 | ||
249 | ||
250 | class InvalidValue(OSError): | |
251 | pass | |
252 | ||
253 | ||
254 | class OperationNotSupported(OSError): | |
255 | pass | |
256 | ||
257 | ||
7c673cae FG |
258 | class WouldBlock(OSError): |
259 | pass | |
260 | ||
261 | ||
262 | class OutOfRange(OSError): | |
263 | pass | |
264 | ||
265 | ||
eafe8130 TL |
266 | class ObjectNotEmpty(OSError): |
267 | pass | |
268 | ||
9f95a23c TL |
269 | class NotDirectory(OSError): |
270 | pass | |
eafe8130 | 271 | |
7c673cae FG |
272 | IF UNAME_SYSNAME == "FreeBSD": |
273 | cdef errno_to_exception = { | |
274 | errno.EPERM : PermissionError, | |
275 | errno.ENOENT : ObjectNotFound, | |
276 | errno.EIO : IOError, | |
277 | errno.ENOSPC : NoSpace, | |
278 | errno.EEXIST : ObjectExists, | |
279 | errno.ENOATTR : NoData, | |
280 | errno.EINVAL : InvalidValue, | |
281 | errno.EOPNOTSUPP : OperationNotSupported, | |
282 | errno.ERANGE : OutOfRange, | |
283 | errno.EWOULDBLOCK: WouldBlock, | |
eafe8130 | 284 | errno.ENOTEMPTY : ObjectNotEmpty, |
7c673cae FG |
285 | } |
286 | ELSE: | |
287 | cdef errno_to_exception = { | |
288 | errno.EPERM : PermissionError, | |
289 | errno.ENOENT : ObjectNotFound, | |
290 | errno.EIO : IOError, | |
291 | errno.ENOSPC : NoSpace, | |
292 | errno.EEXIST : ObjectExists, | |
293 | errno.ENODATA : NoData, | |
294 | errno.EINVAL : InvalidValue, | |
295 | errno.EOPNOTSUPP : OperationNotSupported, | |
296 | errno.ERANGE : OutOfRange, | |
297 | errno.EWOULDBLOCK: WouldBlock, | |
eafe8130 | 298 | errno.ENOTEMPTY : ObjectNotEmpty, |
9f95a23c | 299 | errno.ENOTDIR : NotDirectory |
7c673cae FG |
300 | } |
301 | ||
302 | ||
303 | cdef make_ex(ret, msg): | |
304 | """ | |
305 | Translate a librados return code into an exception. | |
306 | ||
307 | :param ret: the return code | |
308 | :type ret: int | |
309 | :param msg: the error message to use | |
310 | :type msg: str | |
311 | :returns: a subclass of :class:`Error` | |
312 | """ | |
313 | ret = abs(ret) | |
314 | if ret in errno_to_exception: | |
315 | return errno_to_exception[ret](ret, msg) | |
316 | else: | |
eafe8130 | 317 | return Error(msg + ': {} [Errno {:d}]'.format(os.strerror(ret), ret)) |
7c673cae FG |
318 | |
319 | ||
320 | class DirEntry(namedtuple('DirEntry', | |
321 | ['d_ino', 'd_off', 'd_reclen', 'd_type', 'd_name'])): | |
322 | DT_DIR = 0x4 | |
323 | DT_REG = 0xA | |
324 | DT_LNK = 0xC | |
325 | def is_dir(self): | |
326 | return self.d_type == self.DT_DIR | |
327 | ||
328 | def is_symbol_file(self): | |
329 | return self.d_type == self.DT_LNK | |
330 | ||
331 | def is_file(self): | |
332 | return self.d_type == self.DT_REG | |
333 | ||
334 | StatResult = namedtuple('StatResult', | |
335 | ["st_dev", "st_ino", "st_mode", "st_nlink", "st_uid", | |
336 | "st_gid", "st_rdev", "st_size", "st_blksize", | |
337 | "st_blocks", "st_atime", "st_mtime", "st_ctime"]) | |
338 | ||
339 | cdef class DirResult(object): | |
81eedcae TL |
340 | cdef LibCephFS lib |
341 | cdef ceph_dir_result* handle | |
7c673cae | 342 | |
81eedcae TL |
343 | # Bug in older Cython instances prevents this from being a static method. |
344 | # @staticmethod | |
345 | # cdef create(LibCephFS lib, ceph_dir_result* handle): | |
346 | # d = DirResult() | |
347 | # d.lib = lib | |
348 | # d.handle = handle | |
349 | # return d | |
350 | ||
351 | def __dealloc__(self): | |
352 | self.close() | |
353 | ||
354 | def __enter__(self): | |
355 | if not self.handle: | |
356 | raise make_ex(errno.EBADF, "dir is not open") | |
357 | self.lib.require_state("mounted") | |
358 | with nogil: | |
359 | ceph_rewinddir(self.lib.cluster, self.handle) | |
360 | return self | |
361 | ||
362 | def __exit__(self, type_, value, traceback): | |
363 | self.close() | |
364 | return False | |
365 | ||
366 | def readdir(self): | |
367 | self.lib.require_state("mounted") | |
368 | ||
369 | with nogil: | |
370 | dirent = ceph_readdir(self.lib.cluster, self.handle) | |
371 | if not dirent: | |
372 | return None | |
373 | ||
eafe8130 TL |
374 | IF UNAME_SYSNAME == "FreeBSD": |
375 | return DirEntry(d_ino=dirent.d_ino, | |
376 | d_off=0, | |
377 | d_reclen=dirent.d_reclen, | |
378 | d_type=dirent.d_type, | |
379 | d_name=dirent.d_name) | |
380 | ELSE: | |
381 | return DirEntry(d_ino=dirent.d_ino, | |
382 | d_off=dirent.d_off, | |
383 | d_reclen=dirent.d_reclen, | |
384 | d_type=dirent.d_type, | |
385 | d_name=dirent.d_name) | |
81eedcae TL |
386 | |
387 | def close(self): | |
388 | if self.handle: | |
389 | self.lib.require_state("mounted") | |
390 | with nogil: | |
391 | ret = ceph_closedir(self.lib.cluster, self.handle) | |
392 | if ret < 0: | |
393 | raise make_ex(ret, "closedir failed") | |
394 | self.handle = NULL | |
7c673cae FG |
395 | |
396 | def cstr(val, name, encoding="utf-8", opt=False): | |
397 | """ | |
398 | Create a byte string from a Python string | |
399 | ||
400 | :param basestring val: Python string | |
401 | :param str name: Name of the string parameter, for exceptions | |
402 | :param str encoding: Encoding to use | |
403 | :param bool opt: If True, None is allowed | |
404 | :rtype: bytes | |
405 | :raises: :class:`InvalidArgument` | |
406 | """ | |
407 | if opt and val is None: | |
408 | return None | |
409 | if isinstance(val, bytes): | |
410 | return val | |
7c673cae | 411 | else: |
81eedcae TL |
412 | try: |
413 | v = val.encode(encoding) | |
414 | except: | |
415 | raise TypeError('%s must be encodeable as a bytearray' % name) | |
416 | assert isinstance(v, bytes) | |
417 | return v | |
7c673cae FG |
418 | |
419 | def cstr_list(list_str, name, encoding="utf-8"): | |
420 | return [cstr(s, name) for s in list_str] | |
421 | ||
422 | ||
423 | def decode_cstr(val, encoding="utf-8"): | |
424 | """ | |
425 | Decode a byte string into a Python string. | |
426 | ||
427 | :param bytes val: byte string | |
428 | :rtype: unicode or None | |
429 | """ | |
430 | if val is None: | |
431 | return None | |
432 | ||
433 | return val.decode(encoding) | |
434 | ||
9f95a23c TL |
435 | cdef timeval to_timeval(t): |
436 | """ | |
437 | return timeval equivalent from time | |
438 | """ | |
439 | tt = int(t) | |
440 | cdef timeval buf = timeval(tt, (t - tt) * 1000000) | |
441 | return buf | |
442 | ||
443 | cdef timespec to_timespec(t): | |
444 | """ | |
445 | return timespec equivalent from time | |
446 | """ | |
447 | tt = int(t) | |
448 | cdef timespec buf = timespec(tt, (t - tt) * 1000000000) | |
449 | return buf | |
7c673cae FG |
450 | |
451 | cdef char* opt_str(s) except? NULL: | |
452 | if s is None: | |
453 | return NULL | |
454 | return s | |
455 | ||
456 | ||
457 | cdef char ** to_bytes_array(list_bytes): | |
458 | cdef char **ret = <char **>malloc(len(list_bytes) * sizeof(char *)) | |
459 | if ret == NULL: | |
460 | raise MemoryError("malloc failed") | |
461 | for i in xrange(len(list_bytes)): | |
462 | ret[i] = <char *>list_bytes[i] | |
463 | return ret | |
464 | ||
465 | ||
466 | cdef void* realloc_chk(void* ptr, size_t size) except NULL: | |
467 | cdef void *ret = realloc(ptr, size) | |
468 | if ret == NULL: | |
469 | raise MemoryError("realloc failed") | |
470 | return ret | |
471 | ||
472 | ||
473 | cdef class LibCephFS(object): | |
474 | """libcephfs python wrapper""" | |
475 | ||
476 | cdef public object state | |
477 | cdef ceph_mount_info *cluster | |
478 | ||
479 | def require_state(self, *args): | |
480 | if self.state in args: | |
481 | return | |
482 | raise LibCephFSStateError("You cannot perform that operation on a " | |
483 | "CephFS object in state %s." % (self.state)) | |
484 | ||
485 | def __cinit__(self, conf=None, conffile=None, auth_id=None, rados_inst=None): | |
486 | """Create a libcephfs wrapper | |
487 | ||
488 | :param conf dict opt: settings overriding the default ones and conffile | |
489 | :param conffile str opt: the path to ceph.conf to override the default settings | |
490 | :auth_id str opt: the id used to authenticate the client entity | |
491 | :rados_inst Rados opt: a rados.Rados instance | |
492 | """ | |
493 | PyEval_InitThreads() | |
494 | self.state = "uninitialized" | |
495 | if rados_inst is not None: | |
496 | if auth_id is not None or conffile is not None or conf is not None: | |
497 | raise make_ex(errno.EINVAL, | |
498 | "May not pass RADOS instance as well as other configuration") | |
499 | ||
500 | self.create_with_rados(rados_inst) | |
501 | else: | |
502 | self.create(conf, conffile, auth_id) | |
503 | ||
504 | def create_with_rados(self, rados.Rados rados_inst): | |
505 | cdef int ret | |
506 | with nogil: | |
507 | ret = ceph_create_from_rados(&self.cluster, rados_inst.cluster) | |
508 | if ret != 0: | |
509 | raise Error("libcephfs_initialize failed with error code: %d" % ret) | |
510 | self.state = "configuring" | |
511 | ||
512 | def create(self, conf=None, conffile=None, auth_id=None): | |
11fdf7f2 TL |
513 | """ |
514 | Create a mount handle for interacting with Ceph. All libcephfs | |
515 | functions operate on a mount info handle. | |
516 | ||
517 | :param conf dict opt: settings overriding the default ones and conffile | |
518 | :param conffile str opt: the path to ceph.conf to override the default settings | |
519 | :auth_id str opt: the id used to authenticate the client entity | |
520 | """ | |
7c673cae FG |
521 | if conf is not None and not isinstance(conf, dict): |
522 | raise TypeError("conf must be dict or None") | |
523 | cstr(conffile, 'configfile', opt=True) | |
524 | auth_id = cstr(auth_id, 'auth_id', opt=True) | |
525 | ||
526 | cdef: | |
527 | char* _auth_id = opt_str(auth_id) | |
528 | int ret | |
529 | ||
530 | with nogil: | |
531 | ret = ceph_create(&self.cluster, <const char*>_auth_id) | |
532 | if ret != 0: | |
533 | raise Error("libcephfs_initialize failed with error code: %d" % ret) | |
534 | ||
535 | self.state = "configuring" | |
536 | if conffile is not None: | |
537 | # read the default conf file when '' is given | |
538 | if conffile == '': | |
539 | conffile = None | |
540 | self.conf_read_file(conffile) | |
541 | if conf is not None: | |
542 | for key, value in conf.iteritems(): | |
543 | self.conf_set(key, value) | |
544 | ||
9f95a23c TL |
545 | def get_addrs(self): |
546 | """ | |
547 | Get associated client addresses with this RADOS session. | |
548 | """ | |
549 | self.require_state("mounted") | |
550 | ||
551 | cdef: | |
552 | char* addrs = NULL | |
553 | ||
554 | try: | |
555 | ||
556 | with nogil: | |
557 | ret = ceph_getaddrs(self.cluster, &addrs) | |
558 | if ret: | |
559 | raise make_ex(ret, "error calling getaddrs") | |
560 | ||
561 | return decode_cstr(addrs) | |
562 | finally: | |
563 | ceph_buffer_free(addrs) | |
564 | ||
565 | ||
7c673cae | 566 | def conf_read_file(self, conffile=None): |
11fdf7f2 TL |
567 | """ |
568 | Load the ceph configuration from the specified config file. | |
569 | ||
570 | :param conffile str opt: the path to ceph.conf to override the default settings | |
571 | """ | |
7c673cae FG |
572 | conffile = cstr(conffile, 'conffile', opt=True) |
573 | cdef: | |
574 | char *_conffile = opt_str(conffile) | |
575 | with nogil: | |
576 | ret = ceph_conf_read_file(self.cluster, <const char*>_conffile) | |
577 | if ret != 0: | |
578 | raise make_ex(ret, "error calling conf_read_file") | |
579 | ||
580 | def conf_parse_argv(self, argv): | |
11fdf7f2 TL |
581 | """ |
582 | Parse the command line arguments and load the configuration parameters. | |
583 | ||
584 | :param argv: the argument list | |
585 | """ | |
7c673cae FG |
586 | self.require_state("configuring") |
587 | cargv = cstr_list(argv, 'argv') | |
588 | cdef: | |
589 | int _argc = len(argv) | |
590 | char **_argv = to_bytes_array(cargv) | |
591 | ||
592 | try: | |
593 | with nogil: | |
594 | ret = ceph_conf_parse_argv(self.cluster, _argc, | |
595 | <const char **>_argv) | |
596 | if ret != 0: | |
597 | raise make_ex(ret, "error calling conf_parse_argv") | |
598 | finally: | |
599 | free(_argv) | |
600 | ||
601 | def shutdown(self): | |
602 | """ | |
603 | Unmount and destroy the ceph mount handle. | |
604 | """ | |
605 | if self.state in ["initialized", "mounted"]: | |
606 | with nogil: | |
607 | ceph_shutdown(self.cluster) | |
608 | self.state = "shutdown" | |
609 | ||
610 | def __enter__(self): | |
611 | self.mount() | |
612 | return self | |
613 | ||
614 | def __exit__(self, type_, value, traceback): | |
615 | self.shutdown() | |
616 | return False | |
617 | ||
618 | def __dealloc__(self): | |
619 | self.shutdown() | |
620 | ||
621 | def version(self): | |
622 | """ | |
623 | Get the version number of the ``libcephfs`` C library. | |
624 | ||
625 | :returns: a tuple of ``(major, minor, extra)`` components of the | |
626 | libcephfs version | |
627 | """ | |
628 | cdef: | |
629 | int major = 0 | |
630 | int minor = 0 | |
631 | int extra = 0 | |
632 | with nogil: | |
633 | ceph_version(&major, &minor, &extra) | |
634 | return (major, minor, extra) | |
635 | ||
636 | def conf_get(self, option): | |
11fdf7f2 TL |
637 | """ |
638 | Gets the configuration value as a string. | |
639 | ||
640 | :param option: the config option to get | |
641 | """ | |
7c673cae FG |
642 | self.require_state("configuring", "initialized", "mounted") |
643 | ||
644 | option = cstr(option, 'option') | |
645 | cdef: | |
646 | char *_option = option | |
647 | size_t length = 20 | |
648 | char *ret_buf = NULL | |
649 | ||
650 | try: | |
651 | while True: | |
652 | ret_buf = <char *>realloc_chk(ret_buf, length) | |
653 | with nogil: | |
654 | ret = ceph_conf_get(self.cluster, _option, ret_buf, length) | |
655 | if ret == 0: | |
656 | return decode_cstr(ret_buf) | |
657 | elif ret == -errno.ENAMETOOLONG: | |
658 | length = length * 2 | |
659 | elif ret == -errno.ENOENT: | |
660 | return None | |
661 | else: | |
662 | raise make_ex(ret, "error calling conf_get") | |
663 | finally: | |
664 | free(ret_buf) | |
665 | ||
666 | def conf_set(self, option, val): | |
11fdf7f2 TL |
667 | """ |
668 | Sets a configuration value from a string. | |
669 | ||
670 | :param option: the configuration option to set | |
671 | :param value: the value of the configuration option to set | |
672 | """ | |
7c673cae FG |
673 | self.require_state("configuring", "initialized", "mounted") |
674 | ||
675 | option = cstr(option, 'option') | |
676 | val = cstr(val, 'val') | |
677 | cdef: | |
678 | char *_option = option | |
679 | char *_val = val | |
680 | ||
681 | with nogil: | |
682 | ret = ceph_conf_set(self.cluster, _option, _val) | |
683 | if ret != 0: | |
684 | raise make_ex(ret, "error calling conf_set") | |
685 | ||
686 | def init(self): | |
11fdf7f2 TL |
687 | """ |
688 | Initialize the filesystem client (but do not mount the filesystem yet) | |
689 | """ | |
7c673cae FG |
690 | self.require_state("configuring") |
691 | with nogil: | |
692 | ret = ceph_init(self.cluster) | |
693 | if ret != 0: | |
694 | raise make_ex(ret, "error calling ceph_init") | |
695 | self.state = "initialized" | |
696 | ||
11fdf7f2 TL |
697 | def mount(self, mount_root=None, filesystem_name=None): |
698 | """ | |
699 | Perform a mount using the path for the root of the mount. | |
700 | """ | |
7c673cae FG |
701 | if self.state == "configuring": |
702 | self.init() | |
703 | self.require_state("initialized") | |
11fdf7f2 TL |
704 | |
705 | # Configure which filesystem to mount if one was specified | |
706 | if filesystem_name is None: | |
707 | filesystem_name = b"" | |
9f95a23c TL |
708 | else: |
709 | filesystem_name = cstr(filesystem_name, 'filesystem_name') | |
11fdf7f2 TL |
710 | cdef: |
711 | char *_filesystem_name = filesystem_name | |
712 | if filesystem_name: | |
713 | with nogil: | |
714 | ret = ceph_select_filesystem(self.cluster, | |
715 | _filesystem_name) | |
716 | if ret != 0: | |
717 | raise make_ex(ret, "error calling ceph_select_filesystem") | |
718 | ||
719 | # Prepare mount_root argument, default to "/" | |
720 | root = b"/" if mount_root is None else mount_root | |
721 | cdef: | |
722 | char *_mount_root = root | |
723 | ||
7c673cae | 724 | with nogil: |
11fdf7f2 | 725 | ret = ceph_mount(self.cluster, _mount_root) |
7c673cae FG |
726 | if ret != 0: |
727 | raise make_ex(ret, "error calling ceph_mount") | |
728 | self.state = "mounted" | |
729 | ||
730 | def unmount(self): | |
11fdf7f2 TL |
731 | """ |
732 | Unmount a mount handle. | |
733 | """ | |
7c673cae FG |
734 | self.require_state("mounted") |
735 | with nogil: | |
736 | ret = ceph_unmount(self.cluster) | |
737 | if ret != 0: | |
738 | raise make_ex(ret, "error calling ceph_unmount") | |
739 | self.state = "initialized" | |
740 | ||
11fdf7f2 TL |
741 | def abort_conn(self): |
742 | """ | |
743 | Abort mds connections. | |
744 | """ | |
745 | self.require_state("mounted") | |
746 | with nogil: | |
747 | ret = ceph_abort_conn(self.cluster) | |
748 | if ret != 0: | |
749 | raise make_ex(ret, "error calling ceph_abort_conn") | |
750 | self.state = "initialized" | |
751 | ||
752 | def get_instance_id(self): | |
753 | """ | |
754 | Get a global id for current instance | |
755 | """ | |
756 | self.require_state("initialized", "mounted") | |
757 | with nogil: | |
758 | ret = ceph_get_instance_id(self.cluster) | |
759 | return ret; | |
760 | ||
7c673cae | 761 | def statfs(self, path): |
11fdf7f2 TL |
762 | """ |
763 | Perform a statfs on the ceph file system. This call fills in file system wide statistics | |
764 | into the passed in buffer. | |
765 | ||
766 | :param path: any path within the mounted filesystem | |
767 | """ | |
7c673cae FG |
768 | self.require_state("mounted") |
769 | path = cstr(path, 'path') | |
770 | cdef: | |
771 | char* _path = path | |
772 | statvfs statbuf | |
773 | ||
774 | with nogil: | |
775 | ret = ceph_statfs(self.cluster, _path, &statbuf) | |
776 | if ret < 0: | |
777 | raise make_ex(ret, "statfs failed: %s" % path) | |
778 | return {'f_bsize': statbuf.f_bsize, | |
779 | 'f_frsize': statbuf.f_frsize, | |
780 | 'f_blocks': statbuf.f_blocks, | |
781 | 'f_bfree': statbuf.f_bfree, | |
782 | 'f_bavail': statbuf.f_bavail, | |
783 | 'f_files': statbuf.f_files, | |
784 | 'f_ffree': statbuf.f_ffree, | |
785 | 'f_favail': statbuf.f_favail, | |
786 | 'f_fsid': statbuf.f_fsid, | |
787 | 'f_flag': statbuf.f_flag, | |
788 | 'f_namemax': statbuf.f_namemax} | |
789 | ||
790 | def sync_fs(self): | |
11fdf7f2 TL |
791 | """ |
792 | Synchronize all filesystem data to persistent media | |
793 | """ | |
7c673cae FG |
794 | self.require_state("mounted") |
795 | with nogil: | |
796 | ret = ceph_sync_fs(self.cluster) | |
797 | if ret < 0: | |
798 | raise make_ex(ret, "sync_fs failed") | |
799 | ||
800 | def fsync(self, int fd, int syncdataonly): | |
11fdf7f2 TL |
801 | """ |
802 | Synchronize an open file to persistent media. | |
803 | ||
804 | :param fd: the file descriptor of the file to sync. | |
805 | :param syncdataonly: a boolean whether to synchronize metadata and data (0) | |
806 | or just data (1). | |
807 | """ | |
7c673cae FG |
808 | self.require_state("mounted") |
809 | with nogil: | |
810 | ret = ceph_fsync(self.cluster, fd, syncdataonly) | |
811 | if ret < 0: | |
812 | raise make_ex(ret, "fsync failed") | |
813 | ||
814 | def getcwd(self): | |
11fdf7f2 TL |
815 | """ |
816 | Get the current working directory. | |
817 | ||
818 | :rtype the path to the current working directory | |
819 | """ | |
7c673cae FG |
820 | self.require_state("mounted") |
821 | with nogil: | |
822 | ret = ceph_getcwd(self.cluster) | |
823 | return ret | |
824 | ||
825 | def chdir(self, path): | |
11fdf7f2 TL |
826 | """ |
827 | Change the current working directory. | |
828 | ||
829 | :param path the path to the working directory to change into. | |
830 | """ | |
7c673cae FG |
831 | self.require_state("mounted") |
832 | ||
833 | path = cstr(path, 'path') | |
834 | cdef char* _path = path | |
835 | with nogil: | |
836 | ret = ceph_chdir(self.cluster, _path) | |
837 | if ret < 0: | |
838 | raise make_ex(ret, "chdir failed") | |
839 | ||
840 | def opendir(self, path): | |
11fdf7f2 TL |
841 | """ |
842 | Open the given directory. | |
843 | ||
844 | :param path: the path name of the directory to open. Must be either an absolute path | |
845 | or a path relative to the current working directory. | |
81eedcae | 846 | :rtype handle: the open directory stream handle |
11fdf7f2 | 847 | """ |
7c673cae FG |
848 | self.require_state("mounted") |
849 | ||
850 | path = cstr(path, 'path') | |
851 | cdef: | |
852 | char* _path = path | |
81eedcae | 853 | ceph_dir_result* handle |
7c673cae | 854 | with nogil: |
81eedcae | 855 | ret = ceph_opendir(self.cluster, _path, &handle); |
7c673cae FG |
856 | if ret < 0: |
857 | raise make_ex(ret, "opendir failed") | |
858 | d = DirResult() | |
81eedcae TL |
859 | d.lib = self |
860 | d.handle = handle | |
7c673cae FG |
861 | return d |
862 | ||
81eedcae | 863 | def readdir(self, DirResult handle): |
11fdf7f2 TL |
864 | """ |
865 | Get the next entry in an open directory. | |
866 | ||
81eedcae TL |
867 | :param handle: the open directory stream handle |
868 | :rtype dentry: the next directory entry or None if at the end of the | |
869 | directory (or the directory is empty. This pointer | |
870 | should not be freed by the caller, and is only safe to | |
871 | access between return and the next call to readdir or | |
872 | closedir. | |
11fdf7f2 | 873 | """ |
7c673cae FG |
874 | self.require_state("mounted") |
875 | ||
81eedcae | 876 | return handle.readdir() |
7c673cae | 877 | |
81eedcae | 878 | def closedir(self, DirResult handle): |
11fdf7f2 TL |
879 | """ |
880 | Close the open directory. | |
881 | ||
81eedcae | 882 | :param handle: the open directory stream handle |
11fdf7f2 | 883 | """ |
7c673cae | 884 | self.require_state("mounted") |
7c673cae | 885 | |
81eedcae | 886 | return handle.close() |
7c673cae FG |
887 | |
888 | def mkdir(self, path, mode): | |
11fdf7f2 TL |
889 | """ |
890 | Create a directory. | |
891 | ||
892 | :param path: the path of the directory to create. This must be either an | |
893 | absolute path or a relative path off of the current working directory. | |
894 | :param mode the permissions the directory should have once created. | |
895 | """ | |
896 | ||
7c673cae FG |
897 | self.require_state("mounted") |
898 | path = cstr(path, 'path') | |
899 | if not isinstance(mode, int): | |
900 | raise TypeError('mode must be an int') | |
901 | cdef: | |
902 | char* _path = path | |
903 | int _mode = mode | |
904 | with nogil: | |
905 | ret = ceph_mkdir(self.cluster, _path, _mode) | |
906 | if ret < 0: | |
eafe8130 | 907 | raise make_ex(ret, "error in mkdir {}".format(path.decode('utf-8'))) |
7c673cae | 908 | |
11fdf7f2 TL |
909 | def chmod(self, path, mode) : |
910 | """ | |
911 | Change directory mode. | |
912 | :param path: the path of the directory to create. This must be either an | |
913 | absolute path or a relative path off of the current working directory. | |
914 | :param mode the permissions the directory should have once created. | |
915 | """ | |
916 | self.require_state("mounted") | |
917 | path = cstr(path, 'path') | |
918 | if not isinstance(mode, int): | |
919 | raise TypeError('mode must be an int') | |
920 | cdef: | |
921 | char* _path = path | |
922 | int _mode = mode | |
923 | with nogil: | |
924 | ret = ceph_chmod(self.cluster, _path, _mode) | |
925 | if ret < 0: | |
eafe8130 | 926 | raise make_ex(ret, "error in chmod {}".format(path.decode('utf-8'))) |
11fdf7f2 | 927 | |
9f95a23c | 928 | def chown(self, path, uid, gid, follow_symlink=True): |
92f5a8d4 TL |
929 | """ |
930 | Change directory ownership | |
931 | :param path: the path of the directory to change. | |
932 | :param uid: the uid to set | |
933 | :param gid: the gid to set | |
9f95a23c TL |
934 | :param follow_symlink: perform the operation on the target file if @path |
935 | is a symbolic link (default) | |
92f5a8d4 TL |
936 | """ |
937 | self.require_state("mounted") | |
938 | path = cstr(path, 'path') | |
939 | if not isinstance(uid, int): | |
940 | raise TypeError('uid must be an int') | |
941 | elif not isinstance(gid, int): | |
942 | raise TypeError('gid must be an int') | |
943 | ||
944 | cdef: | |
945 | char* _path = path | |
946 | int _uid = uid | |
947 | int _gid = gid | |
9f95a23c TL |
948 | if follow_symlink: |
949 | with nogil: | |
950 | ret = ceph_chown(self.cluster, _path, _uid, _gid) | |
951 | else: | |
952 | with nogil: | |
953 | ret = ceph_lchown(self.cluster, _path, _uid, _gid) | |
92f5a8d4 TL |
954 | if ret < 0: |
955 | raise make_ex(ret, "error in chown {}".format(path.decode('utf-8'))) | |
956 | ||
9f95a23c TL |
957 | def lchown(self, path, uid, gid): |
958 | """ | |
959 | Change ownership of a symbolic link | |
960 | :param path: the path of the symbolic link to change. | |
961 | :param uid: the uid to set | |
962 | :param gid: the gid to set | |
963 | """ | |
964 | self.chown(path, uid, gid, follow_symlink=False) | |
965 | ||
7c673cae | 966 | def mkdirs(self, path, mode): |
11fdf7f2 TL |
967 | """ |
968 | Create multiple directories at once. | |
969 | ||
970 | :param path: the full path of directories and sub-directories that should | |
971 | be created. | |
972 | :param mode the permissions the directory should have once created | |
973 | """ | |
7c673cae FG |
974 | self.require_state("mounted") |
975 | path = cstr(path, 'path') | |
976 | if not isinstance(mode, int): | |
977 | raise TypeError('mode must be an int') | |
978 | cdef: | |
979 | char* _path = path | |
980 | int _mode = mode | |
981 | ||
982 | with nogil: | |
983 | ret = ceph_mkdirs(self.cluster, _path, _mode) | |
984 | if ret < 0: | |
eafe8130 | 985 | raise make_ex(ret, "error in mkdirs {}".format(path.decode('utf-8'))) |
7c673cae FG |
986 | |
987 | def rmdir(self, path): | |
11fdf7f2 TL |
988 | """ |
989 | Remove a directory. | |
990 | ||
991 | :param path: the path of the directory to remove. | |
992 | """ | |
7c673cae FG |
993 | self.require_state("mounted") |
994 | path = cstr(path, 'path') | |
995 | cdef char* _path = path | |
996 | ret = ceph_rmdir(self.cluster, _path) | |
997 | if ret < 0: | |
eafe8130 | 998 | raise make_ex(ret, "error in rmdir {}".format(path.decode('utf-8'))) |
7c673cae FG |
999 | |
1000 | def open(self, path, flags, mode=0): | |
11fdf7f2 TL |
1001 | """ |
1002 | Create and/or open a file. | |
1003 | ||
1004 | :param path: the path of the file to open. If the flags parameter includes O_CREAT, | |
1005 | the file will first be created before opening. | |
1006 | :param flags: set of option masks that control how the file is created/opened. | |
1007 | :param mode: the permissions to place on the file if the file does not exist and O_CREAT | |
1008 | is specified in the flags. | |
1009 | """ | |
7c673cae FG |
1010 | self.require_state("mounted") |
1011 | path = cstr(path, 'path') | |
1012 | ||
1013 | if not isinstance(mode, int): | |
1014 | raise TypeError('mode must be an int') | |
1015 | if isinstance(flags, str): | |
1016 | cephfs_flags = 0 | |
1017 | if flags == '': | |
1018 | cephfs_flags = os.O_RDONLY | |
1019 | else: | |
1020 | access_flags = 0; | |
1021 | for c in flags: | |
1022 | if c == 'r': | |
1023 | access_flags = 1; | |
1024 | elif c == 'w': | |
1025 | access_flags = 2; | |
1026 | cephfs_flags |= os.O_TRUNC | os.O_CREAT | |
1027 | elif access_flags > 0 and c == '+': | |
1028 | access_flags = 3; | |
1029 | else: | |
1030 | raise make_ex(errno.EOPNOTSUPP, | |
1031 | "open flags doesn't support %s" % c) | |
1032 | ||
1033 | if access_flags == 1: | |
1034 | cephfs_flags |= os.O_RDONLY; | |
1035 | elif access_flags == 2: | |
1036 | cephfs_flags |= os.O_WRONLY; | |
1037 | else: | |
1038 | cephfs_flags |= os.O_RDWR; | |
1039 | ||
1040 | elif isinstance(flags, int): | |
1041 | cephfs_flags = flags | |
1042 | else: | |
1043 | raise TypeError("flags must be a string or an integer") | |
1044 | ||
1045 | cdef: | |
1046 | char* _path = path | |
1047 | int _flags = cephfs_flags | |
1048 | int _mode = mode | |
1049 | ||
1050 | with nogil: | |
1051 | ret = ceph_open(self.cluster, _path, _flags, _mode) | |
1052 | if ret < 0: | |
eafe8130 | 1053 | raise make_ex(ret, "error in open {}".format(path.decode('utf-8'))) |
7c673cae FG |
1054 | return ret |
1055 | ||
1056 | def close(self, fd): | |
11fdf7f2 TL |
1057 | """ |
1058 | Close the open file. | |
1059 | ||
1060 | :param fd: the file descriptor referring to the open file. | |
1061 | """ | |
1062 | ||
7c673cae FG |
1063 | self.require_state("mounted") |
1064 | if not isinstance(fd, int): | |
1065 | raise TypeError('fd must be an int') | |
1066 | cdef int _fd = fd | |
1067 | with nogil: | |
1068 | ret = ceph_close(self.cluster, _fd) | |
1069 | if ret < 0: | |
1070 | raise make_ex(ret, "error in close") | |
1071 | ||
1072 | def read(self, fd, offset, l): | |
11fdf7f2 TL |
1073 | """ |
1074 | Read data from the file. | |
1075 | ||
1076 | :param fd : the file descriptor of the open file to read from. | |
1077 | :param offset : the offset in the file to read from. If this value is negative, the | |
1078 | function reads from the current offset of the file descriptor. | |
1079 | :param l : the flag to indicate what type of seeking to perform | |
1080 | """ | |
7c673cae FG |
1081 | self.require_state("mounted") |
1082 | if not isinstance(offset, int): | |
1083 | raise TypeError('offset must be an int') | |
1084 | if not isinstance(l, int): | |
1085 | raise TypeError('l must be an int') | |
1086 | if not isinstance(fd, int): | |
1087 | raise TypeError('fd must be an int') | |
1088 | cdef: | |
1089 | int _fd = fd | |
1090 | int64_t _offset = offset | |
1091 | int64_t _length = l | |
1092 | ||
1093 | char *ret_buf | |
1094 | PyObject* ret_s = NULL | |
1095 | ||
1096 | ret_s = PyBytes_FromStringAndSize(NULL, _length) | |
1097 | try: | |
1098 | ret_buf = PyBytes_AsString(ret_s) | |
1099 | with nogil: | |
1100 | ret = ceph_read(self.cluster, _fd, ret_buf, _length, _offset) | |
1101 | if ret < 0: | |
1102 | raise make_ex(ret, "error in read") | |
1103 | ||
1104 | if ret != _length: | |
1105 | _PyBytes_Resize(&ret_s, ret) | |
1106 | ||
1107 | return <object>ret_s | |
1108 | finally: | |
1109 | # We DECREF unconditionally: the cast to object above will have | |
1110 | # INCREFed if necessary. This also takes care of exceptions, | |
1111 | # including if _PyString_Resize fails (that will free the string | |
1112 | # itself and set ret_s to NULL, hence XDECREF). | |
1113 | ref.Py_XDECREF(ret_s) | |
1114 | ||
1115 | def write(self, fd, buf, offset): | |
11fdf7f2 TL |
1116 | """ |
1117 | Write data to a file. | |
1118 | ||
1119 | :param fd : the file descriptor of the open file to write to | |
1120 | :param buf : the bytes to write to the file | |
1121 | :param offset : the offset of the file write into. If this value is negative, the | |
1122 | function writes to the current offset of the file descriptor. | |
1123 | """ | |
7c673cae FG |
1124 | self.require_state("mounted") |
1125 | if not isinstance(fd, int): | |
1126 | raise TypeError('fd must be an int') | |
1127 | if not isinstance(buf, bytes): | |
1128 | raise TypeError('buf must be a bytes') | |
1129 | if not isinstance(offset, int): | |
1130 | raise TypeError('offset must be an int') | |
1131 | ||
1132 | cdef: | |
1133 | int _fd = fd | |
1134 | char *_data = buf | |
1135 | int64_t _offset = offset | |
1136 | ||
1137 | size_t length = len(buf) | |
1138 | ||
1139 | with nogil: | |
1140 | ret = ceph_write(self.cluster, _fd, _data, length, _offset) | |
1141 | if ret < 0: | |
1142 | raise make_ex(ret, "error in write") | |
1143 | return ret | |
1144 | ||
1145 | def flock(self, fd, operation, owner): | |
11fdf7f2 TL |
1146 | """ |
1147 | Apply or remove an advisory lock. | |
1148 | ||
1149 | :param fd: the open file descriptor to change advisory lock. | |
1150 | :param operation: the advisory lock operation to be performed on the file | |
1151 | :param owner: the user-supplied owner identifier (an arbitrary integer) | |
1152 | """ | |
7c673cae FG |
1153 | self.require_state("mounted") |
1154 | if not isinstance(fd, int): | |
1155 | raise TypeError('fd must be an int') | |
1156 | if not isinstance(operation, int): | |
1157 | raise TypeError('operation must be an int') | |
1158 | ||
1159 | cdef: | |
1160 | int _fd = fd | |
1161 | int _op = operation | |
1162 | uint64_t _owner = owner | |
1163 | ||
1164 | with nogil: | |
1165 | ret = ceph_flock(self.cluster, _fd, _op, _owner) | |
1166 | if ret < 0: | |
1167 | raise make_ex(ret, "error in write") | |
1168 | return ret | |
1169 | ||
1170 | def getxattr(self, path, name, size=255): | |
11fdf7f2 TL |
1171 | """ |
1172 | Get an extended attribute. | |
1173 | ||
1174 | :param path: the path to the file | |
1175 | :param name: the name of the extended attribute to get | |
1176 | :param size: the size of the pre-allocated buffer | |
1177 | """ | |
7c673cae FG |
1178 | self.require_state("mounted") |
1179 | ||
1180 | path = cstr(path, 'path') | |
1181 | name = cstr(name, 'name') | |
1182 | ||
1183 | cdef: | |
1184 | char* _path = path | |
1185 | char* _name = name | |
1186 | ||
1187 | size_t ret_length = size | |
1188 | char *ret_buf = NULL | |
1189 | ||
1190 | try: | |
1191 | ret_buf = <char *>realloc_chk(ret_buf, ret_length) | |
1192 | with nogil: | |
1193 | ret = ceph_getxattr(self.cluster, _path, _name, ret_buf, | |
1194 | ret_length) | |
1195 | ||
1196 | if ret < 0: | |
1197 | raise make_ex(ret, "error in getxattr") | |
1198 | ||
1199 | return ret_buf[:ret] | |
1200 | finally: | |
1201 | free(ret_buf) | |
1202 | ||
1203 | def setxattr(self, path, name, value, flags): | |
11fdf7f2 TL |
1204 | """ |
1205 | Set an extended attribute on a file. | |
1206 | ||
1207 | :param path: the path to the file. | |
1208 | :param name: the name of the extended attribute to set. | |
1209 | :param value: the bytes of the extended attribute value | |
1210 | """ | |
7c673cae FG |
1211 | self.require_state("mounted") |
1212 | ||
1213 | name = cstr(name, 'name') | |
1214 | path = cstr(path, 'path') | |
1215 | if not isinstance(flags, int): | |
1216 | raise TypeError('flags must be a int') | |
1217 | if not isinstance(value, bytes): | |
1218 | raise TypeError('value must be a bytes') | |
1219 | ||
1220 | cdef: | |
1221 | char *_path = path | |
1222 | char *_name = name | |
1223 | char *_value = value | |
1224 | size_t _value_len = len(value) | |
1225 | int _flags = flags | |
1226 | ||
1227 | with nogil: | |
1228 | ret = ceph_setxattr(self.cluster, _path, _name, | |
1229 | _value, _value_len, _flags) | |
1230 | if ret < 0: | |
1231 | raise make_ex(ret, "error in setxattr") | |
1232 | ||
9f95a23c TL |
1233 | def removexattr(self, path, name): |
1234 | """ | |
1235 | Remove an extended attribute of a file. | |
1236 | ||
1237 | :param path: path of the file. | |
1238 | :param name: name of the extended attribute to remove. | |
1239 | """ | |
1240 | self.require_state("mounted") | |
1241 | ||
1242 | name = cstr(name, 'name') | |
1243 | path = cstr(path, 'path') | |
1244 | ||
1245 | cdef: | |
1246 | char *_path = path | |
1247 | char *_name = name | |
1248 | ||
1249 | with nogil: | |
1250 | ret = ceph_removexattr(self.cluster, _path, _name) | |
1251 | if ret < 0: | |
1252 | raise make_ex(ret, "error in removexattr") | |
1253 | ||
1254 | def listxattr(self, path, size=65536): | |
1255 | """ | |
1256 | List the extended attribute keys set on a file. | |
1257 | ||
1258 | :param path: path of the file. | |
1259 | :param size: the size of list buffer to be filled with extended attribute keys. | |
1260 | """ | |
1261 | self.require_state("mounted") | |
1262 | ||
1263 | path = cstr(path, 'path') | |
1264 | ||
1265 | cdef: | |
1266 | char *_path = path | |
1267 | char *ret_buf = NULL | |
1268 | size_t ret_length = size | |
1269 | ||
1270 | try: | |
1271 | ret_buf = <char *>realloc_chk(ret_buf, ret_length) | |
1272 | with nogil: | |
1273 | ret = ceph_listxattr(self.cluster, _path, ret_buf, ret_length) | |
1274 | ||
1275 | if ret < 0: | |
1276 | raise make_ex(ret, "error in listxattr") | |
1277 | ||
1278 | return ret, ret_buf[:ret] | |
1279 | finally: | |
1280 | free(ret_buf) | |
11fdf7f2 | 1281 | |
92f5a8d4 | 1282 | def stat(self, path, follow_symlink=True): |
11fdf7f2 TL |
1283 | """ |
1284 | Get a file's extended statistics and attributes. | |
1285 | ||
1286 | :param path: the file or directory to get the statistics of. | |
1287 | """ | |
7c673cae FG |
1288 | self.require_state("mounted") |
1289 | path = cstr(path, 'path') | |
1290 | ||
1291 | cdef: | |
1292 | char* _path = path | |
1293 | statx stx | |
1294 | ||
92f5a8d4 TL |
1295 | if follow_symlink: |
1296 | with nogil: | |
9f95a23c TL |
1297 | ret = ceph_statx(self.cluster, _path, &stx, |
1298 | CEPH_STATX_BASIC_STATS_CDEF, 0) | |
92f5a8d4 TL |
1299 | else: |
1300 | with nogil: | |
9f95a23c TL |
1301 | ret = ceph_statx(self.cluster, _path, &stx, |
1302 | CEPH_STATX_BASIC_STATS_CDEF, AT_SYMLINK_NOFOLLOW_CDEF) | |
92f5a8d4 | 1303 | |
7c673cae | 1304 | if ret < 0: |
eafe8130 | 1305 | raise make_ex(ret, "error in stat: {}".format(path.decode('utf-8'))) |
7c673cae FG |
1306 | return StatResult(st_dev=stx.stx_dev, st_ino=stx.stx_ino, |
1307 | st_mode=stx.stx_mode, st_nlink=stx.stx_nlink, | |
1308 | st_uid=stx.stx_uid, st_gid=stx.stx_gid, | |
1309 | st_rdev=stx.stx_rdev, st_size=stx.stx_size, | |
1310 | st_blksize=stx.stx_blksize, | |
1311 | st_blocks=stx.stx_blocks, | |
1312 | st_atime=datetime.fromtimestamp(stx.stx_atime.tv_sec), | |
1313 | st_mtime=datetime.fromtimestamp(stx.stx_mtime.tv_sec), | |
1314 | st_ctime=datetime.fromtimestamp(stx.stx_ctime.tv_sec)) | |
1315 | ||
92f5a8d4 TL |
1316 | def lstat(self, path): |
1317 | """ | |
1318 | Get a file's extended statistics and attributes. When file's a | |
1319 | symbolic link, return the informaion of the link itself rather | |
1320 | than that of the file it points too. | |
1321 | ||
1322 | :param path: the file or directory to get the statistics of. | |
1323 | """ | |
1324 | return self.stat(path, follow_symlink=False) | |
1325 | ||
7c673cae | 1326 | def fstat(self, fd): |
11fdf7f2 TL |
1327 | """ |
1328 | Get an open file's extended statistics and attributes. | |
1329 | ||
1330 | :param fd: the file descriptor of the file to get statistics of. | |
1331 | """ | |
7c673cae FG |
1332 | self.require_state("mounted") |
1333 | if not isinstance(fd, int): | |
1334 | raise TypeError('fd must be an int') | |
1335 | ||
1336 | cdef: | |
1337 | int _fd = fd | |
1338 | statx stx | |
1339 | ||
1340 | with nogil: | |
9f95a23c TL |
1341 | ret = ceph_fstatx(self.cluster, _fd, &stx, |
1342 | CEPH_STATX_BASIC_STATS_CDEF, 0) | |
7c673cae FG |
1343 | if ret < 0: |
1344 | raise make_ex(ret, "error in fsat") | |
1345 | return StatResult(st_dev=stx.stx_dev, st_ino=stx.stx_ino, | |
1346 | st_mode=stx.stx_mode, st_nlink=stx.stx_nlink, | |
1347 | st_uid=stx.stx_uid, st_gid=stx.stx_gid, | |
1348 | st_rdev=stx.stx_rdev, st_size=stx.stx_size, | |
1349 | st_blksize=stx.stx_blksize, | |
1350 | st_blocks=stx.stx_blocks, | |
1351 | st_atime=datetime.fromtimestamp(stx.stx_atime.tv_sec), | |
1352 | st_mtime=datetime.fromtimestamp(stx.stx_mtime.tv_sec), | |
1353 | st_ctime=datetime.fromtimestamp(stx.stx_ctime.tv_sec)) | |
9f95a23c TL |
1354 | |
1355 | def statx(self, path, mask, flag): | |
1356 | """ | |
1357 | Get a file's extended statistics and attributes. | |
1358 | ||
1359 | :param path: the file or directory to get the statistics of. | |
1360 | :param mask: want bitfield of CEPH_STATX_* flags showing designed attributes. | |
1361 | :param flag: bitfield that can be used to set AT_* modifier flags (only AT_NO_ATTR_SYNC and AT_SYMLINK_NOFOLLOW) | |
1362 | """ | |
1363 | ||
1364 | self.require_state("mounted") | |
1365 | path = cstr(path, 'path') | |
1366 | if not isinstance(mask, int): | |
1367 | raise TypeError('flag must be a int') | |
1368 | if not isinstance(flag, int): | |
1369 | raise TypeError('flag must be a int') | |
1370 | ||
1371 | cdef: | |
1372 | char* _path = path | |
1373 | statx stx | |
1374 | int _mask = mask | |
1375 | int _flag = flag | |
1376 | dict_result = dict() | |
1377 | ||
1378 | with nogil: | |
1379 | ret = ceph_statx(self.cluster, _path, &stx, _mask, _flag) | |
1380 | if ret < 0: | |
1381 | raise make_ex(ret, "error in stat: %s" % path) | |
1382 | ||
1383 | if (_mask & CEPH_STATX_MODE): | |
1384 | dict_result["mode"] = stx.stx_mode | |
1385 | if (_mask & CEPH_STATX_NLINK): | |
1386 | dict_result["nlink"] = stx.stx_nlink | |
1387 | if (_mask & CEPH_STATX_UID): | |
1388 | dict_result["uid"] = stx.stx_uid | |
1389 | if (_mask & CEPH_STATX_GID): | |
1390 | dict_result["gid"] = stx.stx_gid | |
1391 | if (_mask & CEPH_STATX_RDEV): | |
1392 | dict_result["rdev"] = stx.stx_rdev | |
1393 | if (_mask & CEPH_STATX_ATIME): | |
1394 | dict_result["atime"] = datetime.fromtimestamp(stx.stx_atime.tv_sec) | |
1395 | if (_mask & CEPH_STATX_MTIME): | |
1396 | dict_result["mtime"] = datetime.fromtimestamp(stx.stx_mtime.tv_sec) | |
1397 | if (_mask & CEPH_STATX_CTIME): | |
1398 | dict_result["ctime"] = datetime.fromtimestamp(stx.stx_ctime.tv_sec) | |
1399 | if (_mask & CEPH_STATX_INO): | |
1400 | dict_result["ino"] = stx.stx_ino | |
1401 | if (_mask & CEPH_STATX_SIZE): | |
1402 | dict_result["size"] = stx.stx_size | |
1403 | if (_mask & CEPH_STATX_BLOCKS): | |
1404 | dict_result["blocks"] = stx.stx_blocks | |
1405 | if (_mask & CEPH_STATX_BTIME): | |
1406 | dict_result["btime"] = datetime.fromtimestamp(stx.stx_btime.tv_sec) | |
1407 | if (_mask & CEPH_STATX_VERSION): | |
1408 | dict_result["version"] = stx.stx_version | |
1409 | ||
1410 | return dict_result | |
7c673cae FG |
1411 | |
1412 | def symlink(self, existing, newname): | |
11fdf7f2 TL |
1413 | """ |
1414 | Creates a symbolic link. | |
1415 | ||
1416 | :param existing: the path to the existing file/directory to link to. | |
1417 | :param newname: the path to the new file/directory to link from. | |
1418 | """ | |
7c673cae FG |
1419 | self.require_state("mounted") |
1420 | existing = cstr(existing, 'existing') | |
1421 | newname = cstr(newname, 'newname') | |
1422 | cdef: | |
1423 | char* _existing = existing | |
1424 | char* _newname = newname | |
1425 | ||
1426 | with nogil: | |
1427 | ret = ceph_symlink(self.cluster, _existing, _newname) | |
1428 | if ret < 0: | |
1429 | raise make_ex(ret, "error in symlink") | |
1430 | ||
1431 | def link(self, existing, newname): | |
11fdf7f2 TL |
1432 | """ |
1433 | Create a link. | |
1434 | ||
1435 | :param existing: the path to the existing file/directory to link to. | |
1436 | :param newname: the path to the new file/directory to link from. | |
1437 | """ | |
1438 | ||
7c673cae FG |
1439 | self.require_state("mounted") |
1440 | existing = cstr(existing, 'existing') | |
1441 | newname = cstr(newname, 'newname') | |
1442 | cdef: | |
1443 | char* _existing = existing | |
1444 | char* _newname = newname | |
1445 | ||
1446 | with nogil: | |
1447 | ret = ceph_link(self.cluster, _existing, _newname) | |
1448 | if ret < 0: | |
1449 | raise make_ex(ret, "error in link") | |
1450 | ||
1451 | def readlink(self, path, size): | |
11fdf7f2 TL |
1452 | """ |
1453 | Read a symbolic link. | |
1454 | ||
1455 | :param path: the path to the symlink to read | |
1456 | :param size: the length of the buffer | |
1457 | :rtype buf: buffer to hold the path of the file that the symlink points to. | |
1458 | """ | |
7c673cae FG |
1459 | self.require_state("mounted") |
1460 | path = cstr(path, 'path') | |
1461 | ||
1462 | cdef: | |
1463 | char* _path = path | |
1464 | int64_t _size = size | |
1465 | char *buf = NULL | |
1466 | ||
1467 | try: | |
1468 | buf = <char *>realloc_chk(buf, _size) | |
1469 | with nogil: | |
1470 | ret = ceph_readlink(self.cluster, _path, buf, _size) | |
1471 | if ret < 0: | |
1472 | raise make_ex(ret, "error in readlink") | |
1473 | return buf | |
1474 | finally: | |
1475 | free(buf) | |
1476 | ||
1477 | def unlink(self, path): | |
11fdf7f2 TL |
1478 | """ |
1479 | Removes a file, link, or symbolic link. If the file/link has multiple links to it, the | |
1480 | file will not disappear from the namespace until all references to it are removed. | |
1481 | ||
1482 | :param path: the path of the file or link to unlink. | |
1483 | """ | |
7c673cae FG |
1484 | self.require_state("mounted") |
1485 | path = cstr(path, 'path') | |
1486 | cdef char* _path = path | |
1487 | with nogil: | |
1488 | ret = ceph_unlink(self.cluster, _path) | |
1489 | if ret < 0: | |
eafe8130 | 1490 | raise make_ex(ret, "error in unlink: {}".format(path.decode('utf-8'))) |
7c673cae FG |
1491 | |
1492 | def rename(self, src, dst): | |
11fdf7f2 TL |
1493 | """ |
1494 | Rename a file or directory. | |
1495 | ||
1496 | :param src: the path to the existing file or directory. | |
1497 | :param dst: the new name of the file or directory. | |
1498 | """ | |
1499 | ||
7c673cae FG |
1500 | self.require_state("mounted") |
1501 | ||
1502 | src = cstr(src, 'source') | |
1503 | dst = cstr(dst, 'destination') | |
1504 | ||
1505 | cdef: | |
1506 | char* _src = src | |
1507 | char* _dst = dst | |
1508 | ||
1509 | with nogil: | |
1510 | ret = ceph_rename(self.cluster, _src, _dst) | |
1511 | if ret < 0: | |
eafe8130 TL |
1512 | raise make_ex(ret, "error in rename {} to {}".format(src.decode( |
1513 | 'utf-8'), dst.decode('utf-8'))) | |
7c673cae FG |
1514 | |
1515 | def mds_command(self, mds_spec, args, input_data): | |
1516 | """ | |
1517 | :return 3-tuple of output status int, output status string, output data | |
1518 | """ | |
1519 | mds_spec = cstr(mds_spec, 'mds_spec') | |
1520 | args = cstr_list(args, 'args') | |
1521 | input_data = cstr(input_data, 'input_data') | |
1522 | ||
1523 | cdef: | |
1524 | char *_mds_spec = opt_str(mds_spec) | |
1525 | char **_cmd = to_bytes_array(args) | |
1526 | size_t _cmdlen = len(args) | |
1527 | ||
1528 | char *_inbuf = input_data | |
1529 | size_t _inbuf_len = len(input_data) | |
1530 | ||
b32b8144 FG |
1531 | char *_outbuf = NULL |
1532 | size_t _outbuf_len = 0 | |
1533 | char *_outs = NULL | |
1534 | size_t _outs_len = 0 | |
7c673cae FG |
1535 | |
1536 | try: | |
1537 | with nogil: | |
1538 | ret = ceph_mds_command(self.cluster, _mds_spec, | |
1539 | <const char **>_cmd, _cmdlen, | |
1540 | <const char*>_inbuf, _inbuf_len, | |
1541 | &_outbuf, &_outbuf_len, | |
1542 | &_outs, &_outs_len) | |
b32b8144 FG |
1543 | my_outs = decode_cstr(_outs[:_outs_len]) |
1544 | my_outbuf = _outbuf[:_outbuf_len] | |
1545 | if _outs_len: | |
1546 | ceph_buffer_free(_outs) | |
1547 | if _outbuf_len: | |
1548 | ceph_buffer_free(_outbuf) | |
1549 | return (ret, my_outbuf, my_outs) | |
7c673cae FG |
1550 | finally: |
1551 | free(_cmd) | |
11fdf7f2 TL |
1552 | |
1553 | def umask(self, mode) : | |
1554 | self.require_state("mounted") | |
1555 | cdef: | |
1556 | mode_t _mode = mode | |
1557 | with nogil: | |
1558 | ret = ceph_umask(self.cluster, _mode) | |
1559 | if ret < 0: | |
1560 | raise make_ex(ret, "error in umask") | |
81eedcae TL |
1561 | return ret |
1562 | ||
1563 | def lseek(self, fd, offset, whence): | |
1564 | """ | |
1565 | Set the file's current position. | |
1566 | ||
1567 | :param fd : the file descriptor of the open file to read from. | |
1568 | :param offset : the offset in the file to read from. If this value is negative, the | |
1569 | function reads from the current offset of the file descriptor. | |
1570 | :param whence : the flag to indicate what type of seeking to performs:SEEK_SET, SEEK_CUR, SEEK_END | |
1571 | """ | |
1572 | self.require_state("mounted") | |
1573 | if not isinstance(fd, int): | |
1574 | raise TypeError('fd must be an int') | |
1575 | if not isinstance(offset, int): | |
1576 | raise TypeError('offset must be an int') | |
1577 | if not isinstance(whence, int): | |
1578 | raise TypeError('whence must be an int') | |
1579 | ||
1580 | cdef: | |
1581 | int _fd = fd | |
1582 | int64_t _offset = offset | |
1583 | int64_t _whence = whence | |
1584 | ||
1585 | with nogil: | |
1586 | ret = ceph_lseek(self.cluster, _fd, _offset, _whence) | |
1587 | ||
1588 | if ret < 0: | |
1589 | raise make_ex(ret, "error in lseek") | |
1590 | ||
1591 | return ret | |
9f95a23c TL |
1592 | |
1593 | def utime(self, path, times=None): | |
1594 | """ | |
1595 | Set access and modification time for path | |
1596 | ||
1597 | :param path: file path for which timestamps have to be changed | |
1598 | :param times: if times is not None, it must be a tuple (atime, mtime) | |
1599 | """ | |
1600 | ||
1601 | self.require_state("mounted") | |
1602 | path = cstr(path, 'path') | |
1603 | if times: | |
1604 | if not isinstance(times, tuple): | |
1605 | raise TypeError('times must be a tuple') | |
1606 | if not isinstance(times[0], int): | |
1607 | raise TypeError('atime must be an int') | |
1608 | if not isinstance(times[1], int): | |
1609 | raise TypeError('mtime must be an int') | |
1610 | actime = modtime = int(time.time()) | |
1611 | if times: | |
1612 | actime = times[0] | |
1613 | modtime = times[1] | |
1614 | ||
1615 | cdef: | |
1616 | char *pth = path | |
1617 | utimbuf buf = utimbuf(actime, modtime) | |
1618 | with nogil: | |
1619 | ret = ceph_utime(self.cluster, pth, &buf) | |
1620 | if ret < 0: | |
1621 | raise make_ex(ret, "error in utime {}".format(path.decode('utf-8'))) | |
1622 | ||
1623 | def futime(self, fd, times=None): | |
1624 | """ | |
1625 | Set access and modification time for a file pointed by descriptor | |
1626 | ||
1627 | :param fd: file descriptor of the open file | |
1628 | :param times: if times is not None, it must be a tuple (atime, mtime) | |
1629 | """ | |
1630 | ||
1631 | self.require_state("mounted") | |
1632 | if not isinstance(fd, int): | |
1633 | raise TypeError('fd must be an int') | |
1634 | if times: | |
1635 | if not isinstance(times, tuple): | |
1636 | raise TypeError('times must be a tuple') | |
1637 | if not isinstance(times[0], int): | |
1638 | raise TypeError('atime must be an int') | |
1639 | if not isinstance(times[1], int): | |
1640 | raise TypeError('mtime must be an int') | |
1641 | actime = modtime = int(time.time()) | |
1642 | if times: | |
1643 | actime = times[0] | |
1644 | modtime = times[1] | |
1645 | ||
1646 | cdef: | |
1647 | int _fd = fd | |
1648 | utimbuf buf = utimbuf(actime, modtime) | |
1649 | with nogil: | |
1650 | ret = ceph_futime(self.cluster, _fd, &buf) | |
1651 | if ret < 0: | |
1652 | raise make_ex(ret, "error in futime") | |
1653 | ||
1654 | def utimes(self, path, times=None, follow_symlink=True): | |
1655 | """ | |
1656 | Set access and modification time for path | |
1657 | ||
1658 | :param path: file path for which timestamps have to be changed | |
1659 | :param times: if times is not None, it must be a tuple (atime, mtime) | |
1660 | :param follow_symlink: perform the operation on the target file if @path | |
1661 | is a symbolic link (default) | |
1662 | """ | |
1663 | ||
1664 | self.require_state("mounted") | |
1665 | path = cstr(path, 'path') | |
1666 | if times: | |
1667 | if not isinstance(times, tuple): | |
1668 | raise TypeError('times must be a tuple') | |
1669 | if not isinstance(times[0], (int, float)): | |
1670 | raise TypeError('atime must be an int or a float') | |
1671 | if not isinstance(times[1], (int, float)): | |
1672 | raise TypeError('mtime must be an int or a float') | |
1673 | actime = modtime = time.time() | |
1674 | if times: | |
1675 | actime = float(times[0]) | |
1676 | modtime = float(times[1]) | |
1677 | ||
1678 | cdef: | |
1679 | char *pth = path | |
1680 | timeval *buf = [to_timeval(actime), to_timeval(modtime)] | |
1681 | if follow_symlink: | |
1682 | with nogil: | |
1683 | ret = ceph_utimes(self.cluster, pth, buf) | |
1684 | else: | |
1685 | with nogil: | |
1686 | ret = ceph_lutimes(self.cluster, pth, buf) | |
1687 | if ret < 0: | |
1688 | raise make_ex(ret, "error in utimes {}".format(path.decode('utf-8'))) | |
1689 | ||
1690 | def lutimes(self, path, times=None): | |
1691 | """ | |
1692 | Set access and modification time for a file. If the file is a symbolic | |
1693 | link do not follow to the target. | |
1694 | ||
1695 | :param path: file path for which timestamps have to be changed | |
1696 | :param times: if times is not None, it must be a tuple (atime, mtime) | |
1697 | """ | |
1698 | self.utimes(path, times=times, follow_symlink=False) | |
1699 | ||
1700 | def futimes(self, fd, times=None): | |
1701 | """ | |
1702 | Set access and modification time for a file pointer by descriptor | |
1703 | ||
1704 | :param fd: file descriptor of the open file | |
1705 | :param times: if times is not None, it must be a tuple (atime, mtime) | |
1706 | """ | |
1707 | ||
1708 | self.require_state("mounted") | |
1709 | if not isinstance(fd, int): | |
1710 | raise TypeError('fd must be an int') | |
1711 | if times: | |
1712 | if not isinstance(times, tuple): | |
1713 | raise TypeError('times must be a tuple') | |
1714 | if not isinstance(times[0], (int, float)): | |
1715 | raise TypeError('atime must be an int or a float') | |
1716 | if not isinstance(times[1], (int, float)): | |
1717 | raise TypeError('mtime must be an int or a float') | |
1718 | actime = modtime = time.time() | |
1719 | if times: | |
1720 | actime = float(times[0]) | |
1721 | modtime = float(times[1]) | |
1722 | ||
1723 | cdef: | |
1724 | int _fd = fd | |
1725 | timeval *buf = [to_timeval(actime), to_timeval(modtime)] | |
1726 | with nogil: | |
1727 | ret = ceph_futimes(self.cluster, _fd, buf) | |
1728 | if ret < 0: | |
1729 | raise make_ex(ret, "error in futimes") | |
1730 | ||
1731 | def futimens(self, fd, times=None): | |
1732 | """ | |
1733 | Set access and modification time for a file pointer by descriptor | |
1734 | ||
1735 | :param fd: file descriptor of the open file | |
1736 | :param times: if times is not None, it must be a tuple (atime, mtime) | |
1737 | """ | |
1738 | ||
1739 | self.require_state("mounted") | |
1740 | if not isinstance(fd, int): | |
1741 | raise TypeError('fd must be an int') | |
1742 | if times: | |
1743 | if not isinstance(times, tuple): | |
1744 | raise TypeError('times must be a tuple') | |
1745 | if not isinstance(times[0], (int, float)): | |
1746 | raise TypeError('atime must be an int or a float') | |
1747 | if not isinstance(times[1], (int, float)): | |
1748 | raise TypeError('mtime must be an int or a float') | |
1749 | actime = modtime = time.time() | |
1750 | if times: | |
1751 | actime = float(times[0]) | |
1752 | modtime = float(times[1]) | |
1753 | ||
1754 | cdef: | |
1755 | int _fd = fd | |
1756 | timespec *buf = [to_timespec(actime), to_timespec(modtime)] | |
1757 | with nogil: | |
1758 | ret = ceph_futimens(self.cluster, _fd, buf) | |
1759 | if ret < 0: | |
1760 | raise make_ex(ret, "error in futimens") |