1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "include/int_types.h"
5 #include "include/types.h"
11 #include <sys/types.h>
13 #include <sys/ioctl.h>
15 #include "include/compat.h"
16 #include "include/linux_fiemap.h"
17 #include "include/color.h"
18 #include "include/buffer.h"
19 #include "include/assert.h"
25 #include "common/errno.h"
26 #include "common/config.h"
27 #include "common/sync_filesystem.h"
31 #include "ZFSFileStoreBackend.h"
33 #define dout_subsys ceph_subsys_filestore
35 #define dout_prefix *_dout << "zfsfilestorebackend(" << get_basedir_path() << ") "
37 ZFSFileStoreBackend::ZFSFileStoreBackend(FileStore
*fs
) :
38 GenericFileStoreBackend(fs
), base_zh(NULL
), current_zh(NULL
),
39 m_filestore_zfs_snap(cct
->_conf
->filestore_zfs_snap
)
43 dout(0) << "ZFSFileStoreBackend: failed to init libzfs" << dendl
;
47 base_zh
= zfs
.path_to_zhandle(get_basedir_path().c_str(), ZFS::TYPE_FILESYSTEM
);
49 dout(0) << "ZFSFileStoreBackend: failed to get zfs handler for basedir" << dendl
;
56 ZFSFileStoreBackend::~ZFSFileStoreBackend()
61 zfs
.close(current_zh
);
64 int ZFSFileStoreBackend::update_current_zh()
67 snprintf(path
, sizeof(path
), "%s/current", zfs
.get_name(base_zh
));
68 ZFS::Handle
*zh
= zfs
.open(path
, ZFS::TYPE_FILESYSTEM
);
71 if (zfs
.is_mounted(zh
, &mnt
)) {
72 int ret
= get_current_path() == mnt
;
79 int ret
= zfs
.mount(zh
, NULL
, 0);
82 dout(0) << "update_current_zh: zfs_mount '" << zfs
.get_name(zh
)
83 << "' got " << cpp_strerror(ret
) << dendl
;
89 dout(0) << "update_current_zh: zfs_open '" << path
<< "' got NULL" << dendl
;
93 zh
= zfs
.path_to_zhandle(get_current_path().c_str(), ZFS::TYPE_FILESYSTEM
);
95 if (strcmp(zfs
.get_name(base_zh
), zfs
.get_name(zh
))) {
100 dout(0) << "update_current_zh: basedir and current/ on the same filesystem" << dendl
;
102 dout(0) << "update_current_zh: current/ not exist" << dendl
;
107 int ZFSFileStoreBackend::detect_features()
110 dout(0) << "detect_features: null zfs handle for current/" << dendl
;
114 bool ZFSFileStoreBackend::can_checkpoint()
116 return m_filestore_zfs_snap
&& current_zh
!= NULL
;
119 int ZFSFileStoreBackend::create_current()
122 int ret
= ::stat(get_current_path().c_str(), &st
);
125 if (!S_ISDIR(st
.st_mode
)) {
126 dout(0) << "create_current: current/ exists but is not a directory" << dendl
;
130 } else if (errno
!= ENOENT
) {
132 dout(0) << "create_current: cannot stat current/ " << cpp_strerror(ret
) << dendl
;
137 snprintf(path
, sizeof(path
), "%s/current", zfs
.get_name(base_zh
));
138 ret
= zfs
.create(path
, ZFS::TYPE_FILESYSTEM
);
139 if (ret
< 0 && errno
!= EEXIST
) {
141 dout(0) << "create_current: zfs_create '" << path
<< "' got " << cpp_strerror(ret
) << dendl
;
145 ret
= update_current_zh();
149 static int list_checkpoints_callback(ZFS::Handle
*zh
, void *data
)
151 list
<string
> *ls
= static_cast<list
<string
> *>(data
);
152 string str
= ZFS::get_name(zh
);
153 size_t pos
= str
.find('@');
154 assert(pos
!= string::npos
&& pos
+ 1 != str
.length());
155 ls
->push_back(str
.substr(pos
+ 1));
159 int ZFSFileStoreBackend::list_checkpoints(list
<string
>& ls
)
161 dout(10) << "list_checkpoints:" << dendl
;
166 int ret
= zfs
.iter_snapshots_sorted(current_zh
, list_checkpoints_callback
, &snaps
);
169 dout(0) << "list_checkpoints: zfs_iter_snapshots_sorted got" << cpp_strerror(ret
) << dendl
;
176 int ZFSFileStoreBackend::create_checkpoint(const string
& name
, uint64_t *cid
)
178 dout(10) << "create_checkpoint: '" << name
<< "'" << dendl
;
182 // looks like zfsonlinux doesn't flush dirty data when taking snapshot
183 int ret
= sync_filesystem(get_current_fd());
186 dout(0) << "create_checkpoint: sync_filesystem got" << cpp_strerror(ret
) << dendl
;
191 snprintf(path
, sizeof(path
), "%s@%s", zfs
.get_name(current_zh
), name
.c_str());
192 ret
= zfs
.snapshot(path
, false);
195 dout(0) << "create_checkpoint: zfs_snapshot '" << path
<< "' got" << cpp_strerror(ret
) << dendl
;
203 int ZFSFileStoreBackend::rollback_to(const string
& name
)
205 dout(10) << "rollback_to: '" << name
<< "'" << dendl
;
209 // umount current to avoid triggering online rollback deadlock
211 if (zfs
.is_mounted(current_zh
, NULL
)) {
212 ret
= zfs
.umount(current_zh
, NULL
, 0);
215 dout(0) << "rollback_to: zfs_umount '" << zfs
.get_name(current_zh
) << "' got" << cpp_strerror(ret
) << dendl
;
220 snprintf(path
, sizeof(path
), "%s@%s", zfs
.get_name(current_zh
), name
.c_str());
222 ZFS::Handle
*snap_zh
= zfs
.open(path
, ZFS::TYPE_SNAPSHOT
);
224 dout(0) << "rollback_to: zfs_open '" << path
<< "' got NULL" << dendl
;
228 ret
= zfs
.rollback(current_zh
, snap_zh
, false);
231 dout(0) << "rollback_to: zfs_rollback '" << zfs
.get_name(snap_zh
) << "' got" << cpp_strerror(ret
) << dendl
;
234 if (!zfs
.is_mounted(current_zh
, NULL
)) {
235 int ret
= zfs
.mount(current_zh
, NULL
, 0);
238 dout(0) << "update_current_zh: zfs_mount '" << zfs
.get_name(current_zh
) << "' got " << cpp_strerror(ret
) << dendl
;
247 int ZFSFileStoreBackend::destroy_checkpoint(const string
& name
)
249 dout(10) << "destroy_checkpoint: '" << name
<< "'" << dendl
;
253 int ret
= zfs
.destroy_snaps(current_zh
, name
.c_str(), true);
256 dout(0) << "destroy_checkpoint: zfs_destroy_snaps '" << name
<< "' got" << cpp_strerror(ret
) << dendl
;