From 9ae89b83735723c560a8ce5853f9db23bacb17cb Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Sat, 5 Sep 2009 01:54:40 +0000 Subject: [PATCH] * Implement attribute support in write_overlay. It mostly uses the same backends as the other node operations. * Differentiate between data and metadata modification, so that changes to attributes or stat fields (other than size) dont't prevent direct IO relay. * Suppress child finish notifications when sending subrequests to the underlaying filesystem. Otherwise the request can be gone by the time we try to complete it. * Fix offsets used for IORequest::CopyData(). * Some refactoring. * Added locking. With that a write_overlay can be put on attribute enabled filesystems to either make them writeable or snapshot them. This allows for example for a working BFS based LiveCD. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@32937 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../layers/write_overlay/write_overlay.cpp | 667 ++++++++++++++---- 1 file changed, 543 insertions(+), 124 deletions(-) diff --git a/src/add-ons/kernel/file_systems/layers/write_overlay/write_overlay.cpp b/src/add-ons/kernel/file_systems/layers/write_overlay/write_overlay.cpp index d522941aa8..605e428a76 100644 --- a/src/add-ons/kernel/file_systems/layers/write_overlay/write_overlay.cpp +++ b/src/add-ons/kernel/file_systems/layers/write_overlay/write_overlay.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -46,6 +47,7 @@ status_t publish_overlay_vnode(fs_volume *volume, ino_t inodeNumber, class OverlayInode; struct open_cookie { + OverlayInode * node; int open_mode; void * super_cookie; }; @@ -59,6 +61,7 @@ struct open_dir_cookie { struct overlay_dirent { ino_t inode_number; char * name; + OverlayInode * node; // only for attributes void remove_and_dispose(fs_volume *volume, ino_t directoryInode) { @@ -68,6 +71,14 @@ struct overlay_dirent { free(name); free(this); } + + void dispose_attribute(fs_volume *volume, ino_t fileInode) + { + notify_attribute_changed(volume->id, fileInode, name, + B_ATTR_REMOVED); + free(name); + free(this); + } }; @@ -100,20 +111,32 @@ public: OverlayInode(OverlayVolume *volume, fs_vnode *superVnode, ino_t inodeNumber, OverlayInode *parentDir = NULL, - const char *name = NULL, mode_t mode = 0); + const char *name = NULL, mode_t mode = 0, + bool attribute = false, + type_code attributeType = 0); ~OverlayInode(); status_t InitCheck(); + bool Lock() { return recursive_lock_lock(&fLock) == B_OK; } + void Unlock() { recursive_lock_unlock(&fLock); } + bool IsVirtual() { return fIsVirtual; } bool IsModified() { return fIsModified; } + bool IsDataModified() { return fIsDataModified; } + bool IsAttribute() { return fIsAttribute; } fs_volume * Volume() { return fVolume->Volume(); } fs_volume * SuperVolume() { return fVolume->SuperVolume(); } + + void SetSuperVnode(fs_vnode *superVnode); fs_vnode * SuperVnode() { return &fSuperVnode; } + + void SetInodeNumber(ino_t inodeNumber); ino_t InodeNumber() { return fInodeNumber; } void SetModified(); + void SetDataModified(); void CreateCache(); void SetParentDir(OverlayInode *parentDir); @@ -122,6 +145,9 @@ public: bool IsNonEmptyDirectory(); status_t Lookup(const char *name, ino_t *inodeNumber); + status_t LookupAttribute(const char *name, + OverlayInode **node); + void SetName(const char *name); status_t GetName(char *buffer, size_t bufferSize); @@ -129,7 +155,9 @@ public: status_t WriteStat(const struct stat *stat, uint32 statMask); status_t Create(const char *name, int openMode, int perms, - void **cookie, ino_t *newInodeNumber); + void **cookie, ino_t *newInodeNumber, + bool attribute = false, + type_code attributeType = 0); status_t Open(int openMode, void **cookie); status_t Close(void *cookie); status_t FreeCookie(void *cookie); @@ -146,28 +174,34 @@ public: status_t CreateDir(const char *name, int perms); status_t RemoveDir(const char *name); - status_t OpenDir(void **cookie); + status_t OpenDir(void **cookie, bool attribute = false); status_t CloseDir(void *cookie); status_t FreeDirCookie(void *cookie); status_t ReadDir(void *cookie, struct dirent *buffer, - size_t bufferSize, uint32 *num); + size_t bufferSize, uint32 *num, + bool attribute = false); status_t RewindDir(void *cookie); status_t CreateSymlink(const char *name, const char *path, int mode); status_t ReadSymlink(char *buffer, size_t *bufferSize); - status_t AddEntry(overlay_dirent *entry); + status_t AddEntry(overlay_dirent *entry, + bool attribute = false); status_t RemoveEntry(const char *name, - overlay_dirent **entry); + overlay_dirent **entry, bool attribute = false); private: void _TrimBuffers(); + status_t _PopulateStat(); status_t _PopulateDirents(); + status_t _PopulateAttributeDirents(); status_t _CreateCommon(const char *name, int type, int perms, - ino_t *newInodeNumber, OverlayInode **node); + ino_t *newInodeNumber, OverlayInode **node, + bool attribute, type_code attributeType); + recursive_lock fLock; OverlayVolume * fVolume; OverlayInode * fParentDir; const char * fName; @@ -177,11 +211,16 @@ private: off_t fOriginalNodeLength; overlay_dirent ** fDirents; uint32 fDirentCount; + overlay_dirent ** fAttributeDirents; + uint32 fAttributeDirentCount; struct stat fStat; bool fHasStat; bool fHasDirents; + bool fHasAttributeDirents; bool fIsVirtual; + bool fIsAttribute; bool fIsModified; + bool fIsDataModified; void * fFileCache; }; @@ -205,7 +244,8 @@ OverlayVolume::~OverlayVolume() OverlayInode::OverlayInode(OverlayVolume *volume, fs_vnode *superVnode, - ino_t inodeNumber, OverlayInode *parentDir, const char *name, mode_t mode) + ino_t inodeNumber, OverlayInode *parentDir, const char *name, mode_t mode, + bool attribute, type_code attributeType) : fVolume(volume), fParentDir(parentDir), fName(name), @@ -214,14 +254,20 @@ OverlayInode::OverlayInode(OverlayVolume *volume, fs_vnode *superVnode, fOriginalNodeLength(-1), fDirents(NULL), fDirentCount(0), + fAttributeDirents(NULL), + fAttributeDirentCount(0), fHasStat(false), fHasDirents(false), + fHasAttributeDirents(false), fIsVirtual(superVnode == NULL), + fIsAttribute(attribute), fIsModified(false), + fIsDataModified(false), fFileCache(NULL) { TRACE("inode created %lld\n", fInodeNumber); + recursive_lock_init(&fLock, "write overlay inode lock"); if (superVnode != NULL) fSuperVnode = *superVnode; else { @@ -236,7 +282,7 @@ OverlayInode::OverlayInode(OverlayVolume *volume, fs_vnode *superVnode, fStat.st_blksize = 1024; fStat.st_atime = fStat.st_mtime = fStat.st_ctime = fStat.st_crtime = time(NULL); - fStat.st_type = 0; + fStat.st_type = attributeType; fHasStat = true; } } @@ -255,9 +301,19 @@ OverlayInode::~OverlayInode() element = next; } - for (uint32 i = 0; i < fDirentCount; i++) + for (uint32 i = 0; i < fDirentCount; i++) { + free(fDirents[i]->name); free(fDirents[i]); + } free(fDirents); + + for (uint32 i = 0; i < fAttributeDirentCount; i++) { + free(fAttributeDirents[i]->name); + free(fAttributeDirents[i]); + } + free(fAttributeDirents); + + recursive_lock_destroy(&fLock); } @@ -268,9 +324,30 @@ OverlayInode::InitCheck() } +void +OverlayInode::SetSuperVnode(fs_vnode *superVnode) +{ + RecursiveLocker locker(fLock); + fSuperVnode = *superVnode; +} + + +void +OverlayInode::SetInodeNumber(ino_t inodeNumber) +{ + RecursiveLocker locker(fLock); + fInodeNumber = inodeNumber; +} + + void OverlayInode::SetModified() { + if (fIsAttribute) { + fIsModified = true; + return; + } + // we must ensure that a modified node never get's put, as we cannot get it // from the underlying filesystem, so we get an additional reference here // and deliberately leak it @@ -281,6 +358,15 @@ OverlayInode::SetModified() } +void +OverlayInode::SetDataModified() +{ + fIsDataModified = true; + if (!fIsModified) + SetModified(); +} + + void OverlayInode::CreateCache() { @@ -295,6 +381,7 @@ OverlayInode::CreateCache() void OverlayInode::SetParentDir(OverlayInode *parentDir) { + RecursiveLocker locker(fLock); fParentDir = parentDir; if (fHasDirents && fDirentCount >= 2) fDirents[1]->inode_number = parentDir->InodeNumber(); @@ -304,8 +391,9 @@ OverlayInode::SetParentDir(OverlayInode *parentDir) bool OverlayInode::IsNonEmptyDirectory() { + RecursiveLocker locker(fLock); if (!fHasStat) - ReadStat(NULL); + _PopulateStat(); if (!S_ISDIR(fStat.st_mode)) return false; @@ -320,12 +408,14 @@ OverlayInode::IsNonEmptyDirectory() status_t OverlayInode::Lookup(const char *name, ino_t *inodeNumber) { + RecursiveLocker locker(fLock); if (!fHasDirents) _PopulateDirents(); for (uint32 i = 0; i < fDirentCount; i++) { if (strcmp(fDirents[i]->name, name) == 0) { *inodeNumber = fDirents[i]->inode_number; + locker.Unlock(); OverlayInode *node = NULL; status_t result = get_vnode(Volume(), *inodeNumber, @@ -340,9 +430,44 @@ OverlayInode::Lookup(const char *name, ino_t *inodeNumber) } +status_t +OverlayInode::LookupAttribute(const char *name, OverlayInode **node) +{ + RecursiveLocker locker(fLock); + if (!fHasAttributeDirents) + _PopulateAttributeDirents(); + + for (uint32 i = 0; i < fAttributeDirentCount; i++) { + overlay_dirent *dirent = fAttributeDirents[i]; + if (strcmp(dirent->name, name) == 0) { + if (dirent->node == NULL) { + OverlayInode *newNode = new(std::nothrow) OverlayInode(fVolume, + SuperVnode(), fInodeNumber, NULL, dirent->name, 0, true, 0); + if (newNode == NULL) + return B_NO_MEMORY; + + status_t result = newNode->InitCheck(); + if (result != B_OK) { + delete newNode; + return result; + } + + dirent->node = newNode; + } + + *node = dirent->node; + return B_OK; + } + } + + return B_ENTRY_NOT_FOUND; +} + + void OverlayInode::SetName(const char *name) { + RecursiveLocker locker(fLock); fName = name; if (!fIsModified) SetModified(); @@ -352,12 +477,13 @@ OverlayInode::SetName(const char *name) status_t OverlayInode::GetName(char *buffer, size_t bufferSize) { + RecursiveLocker locker(fLock); if (fName != NULL) { strlcpy(buffer, fName, bufferSize); return B_OK; } - if (fIsVirtual) + if (fIsVirtual || fIsAttribute) return B_UNSUPPORTED; if (fSuperVnode.ops->get_vnode_name == NULL) @@ -371,20 +497,9 @@ OverlayInode::GetName(char *buffer, size_t bufferSize) status_t OverlayInode::ReadStat(struct stat *stat) { - if (!fHasStat) { - if (fSuperVnode.ops->read_stat == NULL) - return B_UNSUPPORTED; - - status_t result = fSuperVnode.ops->read_stat(SuperVolume(), - &fSuperVnode, &fStat); - if (result != B_OK) - return result; - - fHasStat = true; - } - - if (stat == NULL) - return B_OK; + RecursiveLocker locker(fLock); + if (!fHasStat) + _PopulateStat(); memcpy(stat, &fStat, sizeof(struct stat)); stat->st_blocks = (stat->st_size + stat->st_blksize - 1) / stat->st_blksize; @@ -395,12 +510,20 @@ OverlayInode::ReadStat(struct stat *stat) status_t OverlayInode::WriteStat(const struct stat *stat, uint32 statMask) { + if (fIsAttribute) + return B_UNSUPPORTED; + + RecursiveLocker locker(fLock); if (!fHasStat) - ReadStat(NULL); + _PopulateStat(); if (statMask & B_STAT_SIZE) { - fStat.st_size = stat->st_size; - _TrimBuffers(); + if (fStat.st_size != stat->st_size) { + fStat.st_size = stat->st_size; + if (!fIsDataModified) + SetDataModified(); + _TrimBuffers(); + } } if (statMask & B_STAT_MODE) @@ -431,11 +554,11 @@ OverlayInode::WriteStat(const struct stat *stat, uint32 statMask) status_t OverlayInode::Create(const char *name, int openMode, int perms, void **cookie, - ino_t *newInodeNumber) + ino_t *newInodeNumber, bool attribute, type_code attributeType) { OverlayInode *newNode = NULL; - status_t result = _CreateCommon(name, S_IFREG, perms, newInodeNumber, - &newNode); + status_t result = _CreateCommon(name, attribute ? S_ATTR : S_IFREG, perms, + newInodeNumber, &newNode, attribute, attributeType); if (result != B_OK) return result; @@ -446,14 +569,16 @@ OverlayInode::Create(const char *name, int openMode, int perms, void **cookie, status_t OverlayInode::Open(int openMode, void **_cookie) { + RecursiveLocker locker(fLock); if (!fHasStat) - ReadStat(NULL); + _PopulateStat(); open_cookie *cookie = (open_cookie *)malloc(sizeof(open_cookie)); if (cookie == NULL) return B_NO_MEMORY; cookie->open_mode = openMode; + cookie->node = this; *_cookie = cookie; if (fIsVirtual) { @@ -465,34 +590,50 @@ OverlayInode::Open(int openMode, void **_cookie) return B_OK; } - if (fSuperVnode.ops->open == NULL) + if ((fIsAttribute && fSuperVnode.ops->open_attr == NULL) + || (!fIsAttribute && fSuperVnode.ops->open == NULL)) return B_UNSUPPORTED; + if (openMode & O_TRUNC) { + if (fStat.st_size != 0) { + fStat.st_size = 0; + _TrimBuffers(); + if (!fIsDataModified) + SetDataModified(); + } + } + + openMode &= ~(O_RDWR | O_WRONLY | O_TRUNC | O_CREAT); + status_t result; + if (fIsAttribute) { + result = fSuperVnode.ops->open_attr(SuperVolume(), &fSuperVnode, + fName, openMode, &cookie->super_cookie); + } else { + result = fSuperVnode.ops->open(SuperVolume(), &fSuperVnode, + openMode, &cookie->super_cookie); + } + + if (result != B_OK) { + free(cookie); + return result; + } + if (fOriginalNodeLength < 0) { struct stat stat; - status_t result = fSuperVnode.ops->read_stat(SuperVolume(), - &fSuperVnode, &stat); + if (fIsAttribute) { + result = fSuperVnode.ops->read_attr_stat(SuperVolume(), + &fSuperVnode, cookie->super_cookie, &stat); + } else { + result = fSuperVnode.ops->read_stat(SuperVolume(), + &fSuperVnode, &stat); + } + if (result != B_OK) return result; fOriginalNodeLength = stat.st_size; } - if (openMode & O_TRUNC) { - fStat.st_size = 0; - _TrimBuffers(); - if (!fIsModified) - SetModified(); - } - - openMode &= ~(O_RDWR | O_WRONLY | O_TRUNC | O_CREAT); - status_t result = fSuperVnode.ops->open(SuperVolume(), &fSuperVnode, - openMode, &cookie->super_cookie); - if (result != B_OK) { - free(cookie); - return result; - } - return B_OK; } @@ -504,6 +645,11 @@ OverlayInode::Close(void *_cookie) return B_OK; open_cookie *cookie = (open_cookie *)_cookie; + if (fIsAttribute) { + return fSuperVnode.ops->close_attr(SuperVolume(), &fSuperVnode, + cookie->super_cookie); + } + return fSuperVnode.ops->close(SuperVolume(), &fSuperVnode, cookie->super_cookie); } @@ -515,8 +661,13 @@ OverlayInode::FreeCookie(void *_cookie) status_t result = B_OK; open_cookie *cookie = (open_cookie *)_cookie; if (!fIsVirtual) { - result = fSuperVnode.ops->free_cookie(SuperVolume(), - &fSuperVnode, cookie->super_cookie); + if (fIsAttribute) { + result = fSuperVnode.ops->free_attr_cookie(SuperVolume(), + &fSuperVnode, cookie->super_cookie); + } else { + result = fSuperVnode.ops->free_cookie(SuperVolume(), + &fSuperVnode, cookie->super_cookie); + } } free(cookie); @@ -528,6 +679,7 @@ status_t OverlayInode::Read(void *_cookie, off_t position, void *buffer, size_t *length, bool readPages, IORequest *ioRequest) { + RecursiveLocker locker(fLock); if (position >= fStat.st_size) { *length = 0; return B_OK; @@ -569,6 +721,8 @@ OverlayInode::Read(void *_cookie, off_t position, void *buffer, size_t *length, if (result != B_OK) return result; + bool wereSuppressed = ioRequest->SuppressChildNotifications(); + ioRequest->SetSuppressChildNotifications(true); result = fSuperVnode.ops->io(SuperVolume(), &fSuperVnode, superCookie, subRequest); if (result != B_OK) @@ -576,6 +730,10 @@ OverlayInode::Read(void *_cookie, off_t position, void *buffer, size_t *length, result = subRequest->Wait(0, 0); readLength = subRequest->TransferredBytes(); + ioRequest->SetSuppressChildNotifications(wereSuppressed); + } else if (fIsAttribute) { + result = fSuperVnode.ops->read_attr(SuperVolume(), &fSuperVnode, + superCookie, position, pointer, &readLength); } else { result = fSuperVnode.ops->read(SuperVolume(), &fSuperVnode, superCookie, position, pointer, &readLength); @@ -614,8 +772,8 @@ OverlayInode::Read(void *_cookie, off_t position, void *buffer, size_t *length, const void *source = element->buffer + (position - element->position); if (ioRequest != NULL) { - ioRequest->CopyData(source, (addr_t)pointer - (addr_t)buffer, - copyLength); + ioRequest->CopyData(source, ioRequest->Offset() + + ((addr_t)pointer - (addr_t)buffer), copyLength); } else memcpy(pointer, source, copyLength); @@ -635,14 +793,15 @@ status_t OverlayInode::Write(void *_cookie, off_t position, const void *buffer, size_t length, IORequest *ioRequest) { + RecursiveLocker locker(fLock); if (_cookie != NULL) { open_cookie *cookie = (open_cookie *)_cookie; if (cookie->open_mode & O_APPEND) position = fStat.st_size; } - if (!fIsModified) - SetModified(); + if (!fIsDataModified) + SetDataModified(); // find insertion point write_buffer **link = &fWriteBuffers; @@ -676,13 +835,18 @@ OverlayInode::Write(void *_cookie, off_t position, const void *buffer, // other chunk completely covers us, just copy void *target = other->buffer + (newPosition - other->position); if (ioRequest != NULL) - ioRequest->CopyData(0, target, length); + ioRequest->CopyData(ioRequest->Offset(), target, length); else memcpy(target, buffer, length); fStat.st_mtime = time(NULL); - notify_stat_changed(SuperVolume()->id, fInodeNumber, - B_STAT_MODIFICATION_TIME); + if (fIsAttribute) { + notify_attribute_changed(SuperVolume()->id, fInodeNumber, + fName, B_ATTR_CHANGED); + } else { + notify_stat_changed(SuperVolume()->id, fInodeNumber, + B_STAT_MODIFICATION_TIME); + } return B_OK; } @@ -735,8 +899,15 @@ OverlayInode::Write(void *_cookie, off_t position, const void *buffer, memcpy(target, buffer, length); fStat.st_mtime = time(NULL); - notify_stat_changed(SuperVolume()->id, fInodeNumber, - B_STAT_MODIFICATION_TIME | (sizeChanged ? B_STAT_SIZE : 0)); + + if (fIsAttribute) { + notify_attribute_changed(SuperVolume()->id, fInodeNumber, fName, + B_ATTR_CHANGED); + } else { + notify_stat_changed(SuperVolume()->id, fInodeNumber, + B_STAT_MODIFICATION_TIME | (sizeChanged ? B_STAT_SIZE : 0)); + } + return B_OK; } @@ -772,7 +943,7 @@ OverlayInode::SetFlags(void *_cookie, int flags) status_t OverlayInode::CreateDir(const char *name, int perms) { - return _CreateCommon(name, S_IFDIR, perms, NULL, NULL); + return _CreateCommon(name, S_IFDIR, perms, NULL, NULL, false, 0); } @@ -784,10 +955,13 @@ OverlayInode::RemoveDir(const char *name) status_t -OverlayInode::OpenDir(void **cookie) +OverlayInode::OpenDir(void **cookie, bool attribute) { - if (!fHasDirents) + RecursiveLocker locker(fLock); + if (!attribute && !fHasDirents) _PopulateDirents(); + else if (attribute && !fHasAttributeDirents) + _PopulateAttributeDirents(); open_dir_cookie *dirCookie = (open_dir_cookie *)malloc( sizeof(open_dir_cookie)); @@ -817,15 +991,19 @@ OverlayInode::FreeDirCookie(void *cookie) status_t OverlayInode::ReadDir(void *cookie, struct dirent *buffer, size_t bufferSize, - uint32 *num) + uint32 *num, bool attribute) { + RecursiveLocker locker(fLock); + uint32 direntCount = attribute ? fAttributeDirentCount : fDirentCount; + overlay_dirent **dirents = attribute ? fAttributeDirents : fDirents; + open_dir_cookie *dirCookie = (open_dir_cookie *)cookie; - if (dirCookie->index >= fDirentCount) { + if (dirCookie->index >= direntCount) { *num = 0; return B_OK; } - overlay_dirent *dirent = fDirents[dirCookie->index++]; + overlay_dirent *dirent = dirents[dirCookie->index++]; size_t nameLength = MIN(strlen(dirent->name), bufferSize - sizeof(struct dirent)) + 1; @@ -855,7 +1033,8 @@ OverlayInode::CreateSymlink(const char *name, const char *path, int mode) { OverlayInode *newNode = NULL; // TODO: find out why mode is ignored - status_t result = _CreateCommon(name, S_IFLNK, 0777, NULL, &newNode); + status_t result = _CreateCommon(name, S_IFLNK, 0777, NULL, &newNode, + false, 0); if (result != B_OK) return result; @@ -882,22 +1061,32 @@ OverlayInode::ReadSymlink(char *buffer, size_t *bufferSize) status_t -OverlayInode::AddEntry(overlay_dirent *entry) +OverlayInode::AddEntry(overlay_dirent *entry, bool attribute) { - if (!fHasDirents) + RecursiveLocker locker(fLock); + if (!attribute && !fHasDirents) _PopulateDirents(); + else if (attribute && !fHasAttributeDirents) + _PopulateAttributeDirents(); - status_t result = RemoveEntry(entry->name, NULL); + status_t result = RemoveEntry(entry->name, NULL, attribute); if (result != B_OK && result != B_ENTRY_NOT_FOUND) return B_FILE_EXISTS; - overlay_dirent **newDirents = (overlay_dirent **)realloc(fDirents, - sizeof(overlay_dirent *) * (fDirentCount + 1)); + overlay_dirent **newDirents = (overlay_dirent **)realloc( + attribute ? fAttributeDirents : fDirents, + sizeof(overlay_dirent *) + * ((attribute ? fAttributeDirentCount : fDirentCount) + 1)); if (newDirents == NULL) return B_NO_MEMORY; - fDirents = newDirents; - fDirents[fDirentCount++] = entry; + if (attribute) { + fAttributeDirents = newDirents; + fAttributeDirents[fAttributeDirentCount++] = entry; + } else { + fDirents = newDirents; + fDirents[fDirentCount++] = entry; + } if (!fIsModified) SetModified(); @@ -907,15 +1096,21 @@ OverlayInode::AddEntry(overlay_dirent *entry) status_t -OverlayInode::RemoveEntry(const char *name, overlay_dirent **_entry) +OverlayInode::RemoveEntry(const char *name, overlay_dirent **_entry, + bool attribute) { - if (!fHasDirents) + RecursiveLocker locker(fLock); + if (!attribute && !fHasDirents) _PopulateDirents(); + else if (attribute && !fHasAttributeDirents) + _PopulateAttributeDirents(); - for (uint32 i = 0; i < fDirentCount; i++) { - overlay_dirent *entry = fDirents[i]; + uint32 direntCount = attribute ? fAttributeDirentCount : fDirentCount; + overlay_dirent **dirents = attribute ? fAttributeDirents : fDirents; + for (uint32 i = 0; i < direntCount; i++) { + overlay_dirent *entry = dirents[i]; if (strcmp(entry->name, name) == 0) { - if (_entry == NULL) { + if (_entry == NULL && !attribute) { // check for non-empty directories when trying // to dispose the entry OverlayInode *node = NULL; @@ -932,12 +1127,18 @@ OverlayInode::RemoveEntry(const char *name, overlay_dirent **_entry) return result; } - for (uint32 j = i + 1; j < fDirentCount; j++) - fDirents[j - 1] = fDirents[j]; - fDirentCount--; + for (uint32 j = i + 1; j < direntCount; j++) + dirents[j - 1] = dirents[j]; + + if (attribute) + fAttributeDirentCount--; + else + fDirentCount--; if (_entry != NULL) *_entry = entry; + else if (attribute) + entry->dispose_attribute(Volume(), fInodeNumber); else entry->remove_and_dispose(Volume(), fInodeNumber); @@ -1003,6 +1204,45 @@ OverlayInode::_TrimBuffers() } +status_t +OverlayInode::_PopulateStat() +{ + if (fHasStat) + return B_OK; + + fHasStat = true; + if (fIsAttribute) { + if (fName == NULL || fSuperVnode.ops->open_attr == NULL + || fSuperVnode.ops->read_attr_stat == NULL) + return B_UNSUPPORTED; + + void *cookie = NULL; + status_t result = fSuperVnode.ops->open_attr(SuperVolume(), + &fSuperVnode, fName, O_RDONLY, &cookie); + if (result != B_OK) + return result; + + result = fSuperVnode.ops->read_attr_stat(SuperVolume(), &fSuperVnode, + cookie, &fStat); + + if (fSuperVnode.ops->close_attr != NULL) + fSuperVnode.ops->close_attr(SuperVolume(), &fSuperVnode, cookie); + + if (fSuperVnode.ops->free_attr_cookie != NULL) { + fSuperVnode.ops->free_attr_cookie(SuperVolume(), &fSuperVnode, + cookie); + } + + return B_OK; + } + + if (fSuperVnode.ops->read_stat == NULL) + return B_UNSUPPORTED; + + return fSuperVnode.ops->read_stat(SuperVolume(), &fSuperVnode, &fStat); +} + + status_t OverlayInode::_PopulateDirents() { @@ -1105,36 +1345,124 @@ close_dir: status_t -OverlayInode::_CreateCommon(const char *name, int type, int perms, - ino_t *newInodeNumber, OverlayInode **_node) +OverlayInode::_PopulateAttributeDirents() { - if (!fHasStat) - ReadStat(NULL); + if (fHasAttributeDirents) + return B_OK; - if (!S_ISDIR(fStat.st_mode)) + fHasAttributeDirents = true; + if (fIsVirtual || fSuperVnode.ops->open_attr_dir == NULL + || fSuperVnode.ops->read_attr_dir == NULL) + return B_OK; + + // we don't really care about errors from here on + void *superCookie = NULL; + status_t result = fSuperVnode.ops->open_attr_dir(SuperVolume(), + &fSuperVnode, &superCookie); + if (result != B_OK) + return B_OK; + + size_t bufferSize = sizeof(struct dirent) + B_FILE_NAME_LENGTH; + struct dirent *buffer = (struct dirent *)malloc(bufferSize); + if (buffer == NULL) + goto close_attr_dir; + + while (true) { + uint32 num = 1; + result = fSuperVnode.ops->read_attr_dir(SuperVolume(), + &fSuperVnode, superCookie, buffer, bufferSize, &num); + if (result != B_OK || num == 0) + break; + + overlay_dirent **newDirents = (overlay_dirent **)realloc( + fAttributeDirents, sizeof(overlay_dirent *) + * (fAttributeDirentCount + num)); + if (newDirents == NULL) { + TRACE_ALWAYS("failed to allocate storage for attribute dirents\n"); + break; + } + + fAttributeDirents = newDirents; + struct dirent *dirent = buffer; + for (uint32 i = 0; i < num; i++) { + overlay_dirent *entry = (overlay_dirent *)malloc( + sizeof(overlay_dirent)); + if (entry == NULL) { + TRACE_ALWAYS("failed to allocate storage for attr dirent\n"); + break; + } + + entry->node = NULL; + entry->inode_number = fInodeNumber; + entry->name = strdup(dirent->d_name); + if (entry->name == NULL) { + TRACE_ALWAYS("failed to duplicate dirent entry name\n"); + free(entry); + break; + } + + fAttributeDirents[fAttributeDirentCount++] = entry; + dirent = (struct dirent *)((uint8 *)dirent + dirent->d_reclen); + } + } + + free(buffer); + +close_attr_dir: + if (fSuperVnode.ops->close_attr_dir != NULL) { + fSuperVnode.ops->close_attr_dir(SuperVolume(), &fSuperVnode, + superCookie); + } + + if (fSuperVnode.ops->free_attr_dir_cookie != NULL) { + fSuperVnode.ops->free_attr_dir_cookie(SuperVolume(), &fSuperVnode, + superCookie); + } + + return B_OK; +} + + +status_t +OverlayInode::_CreateCommon(const char *name, int type, int perms, + ino_t *newInodeNumber, OverlayInode **_node, bool attribute, + type_code attributeType) +{ + RecursiveLocker locker(fLock); + if (!fHasStat) + _PopulateStat(); + + if (!attribute && !S_ISDIR(fStat.st_mode)) return B_NOT_A_DIRECTORY; + locker.Unlock(); + overlay_dirent *entry = (overlay_dirent *)malloc(sizeof(overlay_dirent)); if (entry == NULL) return B_NO_MEMORY; + entry->node = NULL; entry->name = strdup(name); if (entry->name == NULL) { free(entry); return B_NO_MEMORY; } - entry->inode_number = fVolume->BuildInodeNumber(); + if (attribute) + entry->inode_number = fInodeNumber; + else + entry->inode_number = fVolume->BuildInodeNumber(); OverlayInode *node = new(std::nothrow) OverlayInode(fVolume, NULL, - entry->inode_number, this, entry->name, (perms & S_IUMSK) | type); + entry->inode_number, this, entry->name, (perms & S_IUMSK) | type + | (attribute ? S_ATTR : 0), attribute, attributeType); if (node == NULL) { free(entry->name); free(entry); return B_NO_MEMORY; } - status_t result = AddEntry(entry); + status_t result = AddEntry(entry, attribute); if (result != B_OK) { free(entry->name); free(entry); @@ -1142,24 +1470,35 @@ OverlayInode::_CreateCommon(const char *name, int type, int perms, return result; } - result = publish_overlay_vnode(fVolume->Volume(), entry->inode_number, - node, type); - if (result != B_OK) { - RemoveEntry(entry->name, NULL); - delete node; - return result; - } + if (!attribute) { + result = publish_overlay_vnode(fVolume->Volume(), entry->inode_number, + node, type); + if (result != B_OK) { + RemoveEntry(entry->name, NULL); + delete node; + return result; + } + } else + entry->node = node; - node->SetModified(); - node->CreateCache(); + node->Lock(); + node->SetDataModified(); + if (!attribute) + node->CreateCache(); + node->Unlock(); if (newInodeNumber != NULL) *newInodeNumber = entry->inode_number; if (_node != NULL) *_node = node; - notify_entry_created(SuperVolume()->id, fInodeNumber, entry->name, - entry->inode_number); + if (attribute) { + notify_attribute_changed(SuperVolume()->id, fInodeNumber, entry->name, + B_ATTR_CREATED); + } else { + notify_entry_created(SuperVolume()->id, fInodeNumber, entry->name, + entry->inode_number); + } return B_OK; } @@ -1650,21 +1989,24 @@ overlay_rewind_dir(fs_volume *volume, fs_vnode *vnode, void *cookie) static status_t overlay_open_attr_dir(fs_volume *volume, fs_vnode *vnode, void **cookie) { - OVERLAY_CALL(open_attr_dir, cookie) + TRACE("open_attr_dir\n"); + return ((OverlayInode *)vnode->private_node)->OpenDir(cookie, true); } static status_t overlay_close_attr_dir(fs_volume *volume, fs_vnode *vnode, void *cookie) { - OVERLAY_CALL(close_attr_dir, cookie) + TRACE("close_attr_dir\n"); + return ((OverlayInode *)vnode->private_node)->CloseDir(cookie); } static status_t overlay_free_attr_dir_cookie(fs_volume *volume, fs_vnode *vnode, void *cookie) { - OVERLAY_CALL(free_attr_dir_cookie, cookie) + TRACE("free_attr_dir_cookie\n"); + return ((OverlayInode *)vnode->private_node)->FreeDirCookie(cookie); } @@ -1672,14 +2014,17 @@ static status_t overlay_read_attr_dir(fs_volume *volume, fs_vnode *vnode, void *cookie, struct dirent *buffer, size_t bufferSize, uint32 *num) { - OVERLAY_CALL(read_attr_dir, cookie, buffer, bufferSize, num) + TRACE("read_attr_dir\n"); + return ((OverlayInode *)vnode->private_node)->ReadDir(cookie, buffer, + bufferSize, num, true); } static status_t overlay_rewind_attr_dir(fs_volume *volume, fs_vnode *vnode, void *cookie) { - OVERLAY_CALL(rewind_attr_dir, cookie) + TRACE("rewind_attr_dir\n"); + return ((OverlayInode *)vnode->private_node)->RewindDir(cookie); } @@ -1687,7 +2032,9 @@ static status_t overlay_create_attr(fs_volume *volume, fs_vnode *vnode, const char *name, uint32 type, int openMode, void **cookie) { - OVERLAY_CALL(create_attr, name, type, openMode, cookie) + TRACE("create_attr\n"); + return ((OverlayInode *)vnode->private_node)->Create(name, openMode, 0, + cookie, NULL, true, type); } @@ -1695,53 +2042,74 @@ static status_t overlay_open_attr(fs_volume *volume, fs_vnode *vnode, const char *name, int openMode, void **cookie) { - OVERLAY_CALL(open_attr, name, openMode, cookie) + TRACE("open_attr\n"); + OverlayInode *node = NULL; + OverlayInode *parentNode = (OverlayInode *)vnode->private_node; + status_t result = parentNode->LookupAttribute(name, &node); + if (result != B_OK) + return result; + if (node == NULL) + return B_ERROR; + + return node->Open(openMode, cookie); } static status_t -overlay_close_attr(fs_volume *volume, fs_vnode *vnode, void *cookie) +overlay_close_attr(fs_volume *volume, fs_vnode *vnode, void *_cookie) { - OVERLAY_CALL(close_attr, cookie) + TRACE("close_attr\n"); + open_cookie *cookie = (open_cookie *)_cookie; + return cookie->node->Close(cookie); } static status_t -overlay_free_attr_cookie(fs_volume *volume, fs_vnode *vnode, void *cookie) +overlay_free_attr_cookie(fs_volume *volume, fs_vnode *vnode, void *_cookie) { - OVERLAY_CALL(free_attr_cookie, cookie) + TRACE("free_attr_cookie\n"); + open_cookie *cookie = (open_cookie *)_cookie; + return cookie->node->FreeCookie(cookie); } static status_t -overlay_read_attr(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos, +overlay_read_attr(fs_volume *volume, fs_vnode *vnode, void *_cookie, off_t pos, void *buffer, size_t *length) { - OVERLAY_CALL(read_attr, cookie, pos, buffer, length) + TRACE("read_attr\n"); + open_cookie *cookie = (open_cookie *)_cookie; + return cookie->node->Read(cookie, pos, buffer, length, false, NULL); } static status_t -overlay_write_attr(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos, +overlay_write_attr(fs_volume *volume, fs_vnode *vnode, void *_cookie, off_t pos, const void *buffer, size_t *length) { - OVERLAY_CALL(write_attr, cookie, pos, buffer, length) + TRACE("write_attr\n"); + open_cookie *cookie = (open_cookie *)_cookie; + return cookie->node->Write(cookie, pos, buffer, *length, NULL); } static status_t -overlay_read_attr_stat(fs_volume *volume, fs_vnode *vnode, void *cookie, +overlay_read_attr_stat(fs_volume *volume, fs_vnode *vnode, void *_cookie, struct stat *stat) { - OVERLAY_CALL(read_attr_stat, cookie, stat) + TRACE("read_attr_stat\n"); + open_cookie *cookie = (open_cookie *)_cookie; + return cookie->node->ReadStat(stat); } static status_t -overlay_write_attr_stat(fs_volume *volume, fs_vnode *vnode, void *cookie, +overlay_write_attr_stat(fs_volume *volume, fs_vnode *vnode, void *_cookie, const struct stat *stat, int statMask) { - OVERLAY_CALL(write_attr_stat, cookie, stat, statMask) + TRACE("write_attr_stat\n"); + open_cookie *cookie = (open_cookie *)_cookie; + return cookie->node->WriteStat(stat, statMask); } @@ -1749,14 +2117,65 @@ static status_t overlay_rename_attr(fs_volume *volume, fs_vnode *vnode, const char *fromName, fs_vnode *toVnode, const char *toName) { - OVERLAY_CALL(rename_attr, fromName, toVnode, toName) + TRACE("rename attr: \"%s\" -> \"%s\"\n", fromName, toName); + OverlayInode *fromNode = (OverlayInode *)vnode->private_node; + OverlayInode *toNode = (OverlayInode *)toVnode->private_node; + overlay_dirent *entry = NULL; + + status_t result = fromNode->RemoveEntry(fromName, &entry, true); + if (result != B_OK) + return result; + + char *oldName = entry->name; + entry->name = strdup(toName); + if (entry->name == NULL) { + entry->name = oldName; + if (fromNode->AddEntry(entry, true) != B_OK) + entry->dispose_attribute(volume, fromNode->InodeNumber()); + + return B_NO_MEMORY; + } + + result = toNode->AddEntry(entry, true); + if (result != B_OK) { + free(entry->name); + entry->name = oldName; + if (fromNode->AddEntry(entry, true) != B_OK) + entry->dispose_attribute(volume, fromNode->InodeNumber()); + + return result; + } + + OverlayInode *node = entry->node; + if (node == NULL) + return B_ERROR; + + node->SetName(entry->name); + node->SetSuperVnode(toNode->SuperVnode()); + node->SetInodeNumber(toNode->InodeNumber()); + + notify_attribute_changed(volume->id, fromNode->InodeNumber(), fromName, + B_ATTR_REMOVED); + notify_attribute_changed(volume->id, toNode->InodeNumber(), toName, + B_ATTR_CREATED); + + free(oldName); + return B_OK; } static status_t overlay_remove_attr(fs_volume *volume, fs_vnode *vnode, const char *name) { - OVERLAY_CALL(remove_attr, name) + TRACE("remove_attr\n"); + OverlayInode *node = (OverlayInode *)vnode->private_node; + status_t result = node->RemoveEntry(name, NULL, true); + if (result != B_OK) + return result; + + notify_attribute_changed(volume->id, node->InodeNumber(), name, + B_ATTR_REMOVED); + return result; }