int r = 0, i = 0;
dout(1) << __func__ << " path " << path << dendl;
+ struct stat statbuf;
+ bool is_block;
+ r = stat(path.c_str(), &statbuf);
+ if (r != 0) {
+ derr << __func__ << " stat got: " << cpp_strerror(r) << dendl;
+ goto out_fail;
+ }
+ is_block = (statbuf.st_mode & S_IFMT) == S_IFBLK;
for (i = 0; i < WRITE_LIFE_MAX; i++) {
- int fd = ::open(path.c_str(), O_RDWR | O_DIRECT);
+ int flags = 0;
+ if (lock_exclusive && is_block && (i == 0)) {
+ // If opening block device use O_EXCL flag. It gives us best protection,
+ // as no other process can overwrite the data for as long as we are running.
+ // For block devices ::flock is not enough,
+ // since 2 different inodes with same major/minor can be locked.
+ // Exclusion by O_EXCL works in containers too.
+ flags |= O_EXCL;
+ }
+ int fd = ::open(path.c_str(), O_RDWR | O_DIRECT | flags);
if (fd < 0) {
r = -errno;
break;
}
if (lock_exclusive) {
+ // We need to keep soft locking (via flock()) because O_EXCL does not work for regular files.
+ // This is as good as we can get. Other processes can still overwrite the data,
+ // but at least we are protected from mounting same device twice in ceph processes.
+ // We also apply soft locking for block devices, as it populates /proc/locks. (see lslocks)
r = _lock();
if (r < 0) {
derr << __func__ << " failed to lock " << path << ": " << cpp_strerror(r)