diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEEntry.h b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEEntry.h index 13379d3f8a..60874c83b6 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEEntry.h +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEEntry.h @@ -82,12 +82,14 @@ struct FUSENode : RWLockable, HashTableLink { FUSEEntryList entries; int type; int32 refCount; + bool dirty; FUSENode(ino_t id, int type) : id(id), type(type), - refCount(1) + refCount(1), + dirty(false) { } diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.cpp index bca59f6b28..0af54e2553 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.cpp @@ -338,9 +338,9 @@ FUSEFileSystem::_InitCapabilities() fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_UNMOUNT, true); fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_READ_FS_INFO, fFS->ops.statfs); -// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_WRITE_FS_INFO, -// fFS->ops.wfsstat); -// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_SYNC, fFS->ops.sync); + // missing: FS_VOLUME_CAPABILITY_WRITE_FS_INFO + fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_SYNC, fFS->ops.fsync); + // emulated via fsync() fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_GET_VNODE, true); // emulated diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp index c61ee51bf3..387382afc2 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp @@ -722,9 +722,47 @@ printf("FUSEVolume::Unmount()\n"); status_t FUSEVolume::Sync() { - // TODO: Implement! - // NOTE: There's no hook for sync'ing the whole FS. - return B_UNSUPPORTED; + PRINT(("FUSEVolume::Sync()\n")); + + // There's no FUSE hook for sync'ing the whole FS. We need to individually + // fsync all nodes that have been marked dirty. To keep things simple, we + // hold the volume lock the whole time. That's a concurrency killer, but + // usually sync isn't invoked that often. + + AutoLocker _(fLock); + + // iterate through all nodes + FUSENodeTable::Iterator it = fNodes.GetIterator(); + while (FUSENode* node = it.Next()) { + if (!node->dirty) + continue; + + // node is dirty -- we have to sync it + + // get a path for the node + char path[B_PATH_NAME_LENGTH]; + size_t pathLen; + status_t error = _BuildPath(node, path, pathLen); + if (error != B_OK) + continue; + + // open, sync, and close the node + FileCookie cookie(O_RDONLY); + int fuseError = fuse_fs_open(fFS, path, &cookie); + if (fuseError == 0) { + fuseError = fuse_fs_fsync(fFS, path, 0, &cookie); + // full sync, not only data + fuse_fs_flush(fFS, path, &cookie); + fuse_fs_release(fFS, path, &cookie); + } + + if (fuseError == 0) { + // sync'ing successful -- mark the node not dirty + node->dirty = false; + } + } + + return B_OK; } @@ -753,14 +791,6 @@ FUSEVolume::ReadFSInfo(fs_info* info) } -status_t -FUSEVolume::WriteFSInfo(const struct fs_info* info, uint32 mask) -{ - // TODO: Implement! - return B_UNSUPPORTED; -} - - // #pragma mark - vnodes @@ -885,21 +915,28 @@ FUSEVolume::FSync(void* _node) if (error != B_OK) RETURN_ERROR(error); + // mark the node not dirty + bool dirty = node->dirty; + node->dirty = false; + locker.Unlock(); // open, sync, and close the node FileCookie cookie(O_RDONLY); int fuseError = fuse_fs_open(fFS, path, &cookie); - if (fuseError != 0) - RETURN_ERROR(fuseError); + if (fuseError == 0) { + fuseError = fuse_fs_fsync(fFS, path, 0, &cookie); + // full sync, not only data + fuse_fs_flush(fFS, path, &cookie); + fuse_fs_release(fFS, path, &cookie); + } - fuseError = fuse_fs_fsync(fFS, path, 0, &cookie); - // full sync, not only data - if (fuseError != 0) + if (fuseError != 0) { + // sync'ing failed -- mark the node dirty again + locker.Lock(); + node->dirty |= dirty; RETURN_ERROR(fuseError); - - fuse_fs_flush(fFS, path, &cookie); - fuse_fs_release(fFS, path, &cookie); + } return B_OK; } @@ -967,11 +1004,15 @@ dir->id, name, target, mode)); locker.Unlock(); - // create the dir + // create the symlink int fuseError = fuse_fs_symlink(fFS, target, path); if (fuseError != 0) RETURN_ERROR(fuseError); + // mark the dir dirty + locker.Lock(); + dir->dirty = true; + // TODO: Set the mode?! // TODO: Node monitoring! @@ -1016,6 +1057,11 @@ node, node->id)); if (fuseError != 0) RETURN_ERROR(fuseError); + // mark the dir and the node dirty + locker.Lock(); + dir->dirty = true; + node->dirty = true; + // TODO: Node monitoring! return B_OK; @@ -1053,6 +1099,9 @@ PRINT(("FUSEVolume::Unlink(%p (%lld), \"%s\")\n", dir, dir->id, name)); locker.Lock(); _RemoveEntry(dir, name); + // mark the dir dirty + dir->dirty = true; + // TODO: Node monitoring! return B_OK; @@ -1099,6 +1148,10 @@ oldDir->id, oldName, newDir, newDir->id, newName)); locker.Lock(); _RenameEntry(oldDir, oldName, newDir, newName); + // mark the dirs dirty + oldDir->dirty = true; + newDir->dirty = true; + // TODO: Node monitoring! return B_OK; @@ -1240,6 +1293,10 @@ mask)); RETURN_ERROR(fuseError); } + // mark the node dirty + locker.Lock(); + node->dirty = true; + // TODO: Node monitoring! return B_OK; @@ -1298,6 +1355,11 @@ openMode, mode)); RETURN_ERROR(error); } + // mark the dir and the node dirty + locker.Lock(); + dir->dirty = true; + node->dirty = true; + cookieDeleter.Detach(); *_cookie = cookie; *_vnid = node->id; @@ -1350,6 +1412,11 @@ PRINT(("FUSEVolume::Open(%p (%lld), %#x)\n", node, node->id, openMode)); fuse_fs_release(fFS, path, cookie); RETURN_ERROR(fuseError); } + + // mark the node dirty + locker.Lock(); + node->dirty = true; + // TODO: Node monitoring! } @@ -1499,6 +1566,10 @@ FUSEVolume::Write(void* _node, void* _cookie, off_t pos, const void* buffer, if (bytesWritten < 0) return bytesWritten; + // mark the node dirty + locker.Lock(); + node->dirty = true; + // TODO: Node monitoring? *_bytesWritten = bytesWritten; @@ -1537,6 +1608,10 @@ mode)); if (fuseError != 0) RETURN_ERROR(fuseError); + // mark the dir dirty + locker.Lock(); + dir->dirty = true; + // TODO: Node monitoring! return B_OK; @@ -1574,6 +1649,10 @@ PRINT(("FUSEVolume::RemoveDir(%p (%lld), \"%s\")\n", dir, dir->id, name)); locker.Lock(); _RemoveEntry(dir, name); + // mark the parent dir dirty + locker.Lock(); + dir->dirty = true; + // TODO: Node monitoring! return B_OK; diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.h b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.h index 5f42f0c45b..0ba9a442c6 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.h +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.h @@ -37,8 +37,6 @@ public: virtual status_t Unmount(); virtual status_t Sync(); virtual status_t ReadFSInfo(fs_info* info); - virtual status_t WriteFSInfo(const struct fs_info* info, - uint32 mask); // vnodes virtual status_t Lookup(void* dir, const char* entryName,