void MetadataCache::NotifyChanges(const struct stat* oldStat, const struct stat* newStat) { ASSERT(oldStat != NULL); ASSERT(newStat != NULL); uint32 flags = 0; if (oldStat->st_size != newStat->st_size) flags |= B_STAT_SIZE; if (oldStat->st_mode != newStat->st_mode) flags |= B_STAT_MODE; if (oldStat->st_uid != newStat->st_uid) flags |= B_STAT_UID; if (oldStat->st_gid != newStat->st_gid) flags |= B_STAT_GID; if (memcmp(&oldStat->st_atim, &newStat->st_atim, sizeof(struct timespec) == 0)) flags |= B_STAT_ACCESS_TIME; if (memcmp(&oldStat->st_ctim, &newStat->st_ctim, sizeof(struct timespec) == 0)) flags |= B_STAT_CHANGE_TIME; if (memcmp(&oldStat->st_crtim, &newStat->st_crtim, sizeof(struct timespec) == 0)) flags |= B_STAT_CREATION_TIME; if (memcmp(&oldStat->st_mtim, &newStat->st_mtim, sizeof(struct timespec) == 0)) flags |= B_STAT_MODIFICATION_TIME; notify_stat_changed(fInode->GetFileSystem()->DevId(), fInode->ID(), flags); }
static status_t btrfs_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) { file_cookie* cookie = (file_cookie*)_cookie; Volume* volume = (Volume*)_volume->private_volume; Inode* inode = (Inode*)_node->private_node; if (inode->Size() != cookie->last_size) notify_stat_changed(volume->ID(), inode->ID(), B_STAT_SIZE); delete cookie; return B_OK; }
static status_t ext2_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos, const void* buffer, size_t* _length) { TRACE("ext2_write()\n"); Volume* volume = (Volume*)_volume->private_volume; Inode* inode = (Inode*)_node->private_node; if (volume->IsReadOnly()) return B_READ_ONLY_DEVICE; if (inode->IsDirectory()) { *_length = 0; return B_IS_A_DIRECTORY; } TRACE("ext2_write(): Preparing cookie\n"); file_cookie* cookie = (file_cookie*)_cookie; if ((cookie->open_mode & O_APPEND) != 0) pos = inode->Size(); TRACE("ext2_write(): Creating transaction\n"); Transaction transaction; status_t status = inode->WriteAt(transaction, pos, (const uint8*)buffer, _length); if (status == B_OK) status = transaction.Done(); if (status == B_OK) { TRACE("ext2_write(): Finalizing\n"); ReadLocker lock(*inode->Lock()); if (cookie->last_size != inode->Size() && system_time() > cookie->last_notification + INODE_NOTIFICATION_INTERVAL) { notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_MODIFICATION_TIME | B_STAT_SIZE | B_STAT_INTERIM_UPDATE); cookie->last_size = inode->Size(); cookie->last_notification = system_time(); } } TRACE("ext2_write(): Done\n"); return status; }
static status_t ext2_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) { file_cookie* cookie = (file_cookie*)_cookie; Volume* volume = (Volume*)_volume->private_volume; Inode* inode = (Inode*)_node->private_node; if (inode->Size() != cookie->last_size) notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE); if ((cookie->open_mode & O_NOCACHE) != 0) inode->EnableFileCache(); delete cookie; return B_OK; }
status_t fs_write_attrib(fs_volume *_vol, fs_vnode *_node, void *_cookie,off_t pos, const void *buffer, size_t *_length) { nspace *ns = (nspace *)_vol->private_volume; //vnode *node = (vnode *)_node->private_node; attrcookie *cookie = (attrcookie *)_cookie; ntfs_inode *ni = cookie->inode; ntfs_attr *na = cookie->stream; size_t size = *_length; int total = 0; status_t result = B_NO_ERROR; TRACE("%s - ENTER!!\n", __FUNCTION__); if (ns->flags & B_FS_IS_READONLY) { ERROR("ntfs is read-only\n"); return EROFS; } if (pos < 0) { *_length = 0; return EINVAL; } LOCK_VOL(ns); TRACE("%s - ENTER\n", __FUNCTION__); // it is a named stream if (na) { if (cookie->omode & O_APPEND) pos = na->data_size; if (pos + size > na->data_size) { ntfs_mark_free_space_outdated(ns); if (ntfs_attr_truncate(na, pos + size)) size = na->data_size - pos; else notify_stat_changed(ns->id, MREF(ni->mft_no), B_STAT_SIZE); } while (size) { off_t bytesWritten = ntfs_attr_pwrite(na, pos, size, buffer); if (bytesWritten < (s64)size) ERROR("%s - ntfs_attr_pwrite returned less bytes than " "requested.\n", __FUNCTION__); if (bytesWritten <= 0) { ERROR(("%s - ntfs_attr_pwrite()<=0\n", __FUNCTION__)); *_length = 0; result = EINVAL; goto exit; } size -= bytesWritten; pos += bytesWritten; total += bytesWritten; } *_length = total; } else { *_length = 0; return EINVAL; } if (total > 0) fs_ntfs_update_times(_vol, ni, NTFS_UPDATE_ATIME); // XXX needed ? exit: TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); UNLOCK_VOL(ns); return result; }
// _HandleRequest status_t KernelRequestHandler::_HandleRequest(NotifyListenerRequest* request) { // check and execute the request status_t result = B_OK; if (fVolume && request->device != fVolume->GetID()) result = B_BAD_VALUE; // get the names // name char* name = (char*)request->name.GetData(); int32 nameLen = request->name.GetSize(); if (name && (nameLen <= 0)) name = NULL; else if (name) name[nameLen - 1] = '\0'; // NULL-terminate to be safe // old name char* oldName = (char*)request->oldName.GetData(); int32 oldNameLen = request->oldName.GetSize(); if (oldName && (oldNameLen <= 0)) oldName = NULL; else if (oldName) oldName[oldNameLen - 1] = '\0'; // NULL-terminate to be safe // check the names if (result == B_OK) { switch (request->operation) { case B_ENTRY_MOVED: if (!oldName) { ERROR(("NotifyListenerRequest: NULL oldName for " "B_ENTRY_MOVED\n")); result = B_BAD_VALUE; break; } // fall through... case B_ENTRY_CREATED: case B_ENTRY_REMOVED: case B_ATTR_CHANGED: if (!name) { ERROR(("NotifyListenerRequest: NULL name for opcode: %" B_PRId32 "\n", request->operation)); result = B_BAD_VALUE; } break; case B_STAT_CHANGED: break; } } // execute the request if (result == B_OK) { switch (request->operation) { case B_ENTRY_CREATED: PRINT(("notify_entry_created(%" B_PRId32 ", %" B_PRId64 ", " "\"%s\", %" B_PRId64 ")\n", request->device, request->directory, name, request->node)); result = notify_entry_created(request->device, request->directory, name, request->node); break; case B_ENTRY_REMOVED: PRINT(("notify_entry_removed(%" B_PRId32 ", %" B_PRId64 ", " "\"%s\", %" B_PRId64 ")\n", request->device, request->directory, name, request->node)); result = notify_entry_removed(request->device, request->directory, name, request->node); break; case B_ENTRY_MOVED: PRINT(("notify_entry_moved(%" B_PRId32 ", %" B_PRId64 ", " "\"%s\", %" B_PRId64 ", \"%s\", %" B_PRId64 ")\n", request->device, request->oldDirectory, oldName, request->directory, name, request->node)); result = notify_entry_moved(request->device, request->oldDirectory, oldName, request->directory, name, request->node); break; case B_STAT_CHANGED: PRINT(("notify_stat_changed(%" B_PRId32 ", %" B_PRId64 ", " "0x%" B_PRIx32 ")\n", request->device, request->node, request->details)); result = notify_stat_changed(request->device, request->node, request->details); break; case B_ATTR_CHANGED: PRINT(("notify_attribute_changed(%" B_PRId32 ", %" B_PRId64 ", " "\"%s\", 0x%" B_PRIx32 ")\n", request->device, request->node, name, (int32)request->details)); result = notify_attribute_changed(request->device, request->node, name, (int32)request->details); break; default: ERROR(("NotifyQueryRequest: unsupported operation: %" B_PRId32 "\n", request->operation)); result = B_BAD_VALUE; break; } } // prepare the reply RequestAllocator allocator(fPort->GetPort()); NotifyListenerReply* reply; status_t error = AllocateRequest(allocator, &reply); if (error != B_OK) return error; reply->error = result; // send the reply return fPort->SendRequest(&allocator); }
status_t fs_write_attrib(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos, const void *buffer, size_t *_length) { nspace *ns = (nspace *)_vol->private_volume; vnode *node = (vnode *)_node->private_node; attrcookie *cookie = (attrcookie *)_cookie; ntfs_inode *ni = NULL; ntfs_attr *na = NULL; size_t size = *_length; char *attr_name = NULL; char *real_name = NULL; int total = 0; status_t result = B_NO_ERROR; TRACE("%s - ENTER vnode: %d\n", __FUNCTION__, node->vnid); if (ns->flags & B_FS_IS_READONLY) { return B_READ_ONLY_DEVICE; } if (pos < 0) { *_length = 0; return EINVAL; } LOCK_VOL(ns); ni = ntfs_inode_open(ns->ntvol, node->vnid); if (ni == NULL) { result = errno; goto exit; } na = ntfs_attr_open(ni, AT_DATA, cookie->uname, cookie->uname_len); if (na == NULL) { result = errno; goto exit; } pos += sizeof(uint32); // it is a named stream if (na != NULL) { if (cookie->omode & O_APPEND) pos = na->data_size; if (pos + size > na->data_size) { ntfs_mark_free_space_outdated(ns); if (ntfs_attr_truncate(na, pos + size)) size = na->data_size - pos; else notify_stat_changed(ns->id, MREF(ni->mft_no), B_STAT_SIZE); } while (size) { off_t bytesWritten = ntfs_attr_pwrite(na, pos, size, buffer); if (bytesWritten < (s64)size) ERROR("%s - ntfs_attr_pwrite returned less bytes than " "requested.\n", __FUNCTION__); if (bytesWritten <= 0) { ERROR("%s - ntfs_attr_pwrite()<=0\n", __FUNCTION__); *_length = 0; result = EINVAL; goto exit; } size -= bytesWritten; pos += bytesWritten; total += bytesWritten; } *_length = total; } else { *_length = 0; result = EINVAL; goto exit; } if (ntfs_ucstombs(na->name, na->name_len, &attr_name, 0) >= 0) { if (attr_name != NULL) { if(strncmp(attr_name, kHaikuAttrPrefix, strlen(kHaikuAttrPrefix)) !=0 ) goto exit; real_name = attr_name + strlen(kHaikuAttrPrefix); notify_attribute_changed(ns->id, MREF(ni->mft_no), real_name, B_ATTR_CHANGED); free(attr_name); } } exit: if (na != NULL) ntfs_attr_close(na); if (ni != NULL) ntfs_inode_close(ni); TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); UNLOCK_VOL(ns); return result; }
status_t ext2_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat, uint32 mask) { TRACE("ext2_write_stat\n"); Volume* volume = (Volume*)_volume->private_volume; if (volume->IsReadOnly()) return B_READ_ONLY_DEVICE; Inode* inode = (Inode*)_node->private_node; ext2_inode& node = inode->Node(); bool updateTime = false; uid_t uid = geteuid(); bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID(); bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK; TRACE("ext2_write_stat: Starting transaction\n"); Transaction transaction(volume->GetJournal()); inode->WriteLockInTransaction(transaction); if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) { if (inode->IsDirectory()) return B_IS_A_DIRECTORY; if (!inode->IsFile()) return B_BAD_VALUE; if (!hasWriteAccess) return B_NOT_ALLOWED; TRACE("ext2_write_stat: Old size: %ld, new size: %ld\n", (long)inode->Size(), (long)stat->st_size); off_t oldSize = inode->Size(); status_t status = inode->Resize(transaction, stat->st_size); if(status != B_OK) return status; if ((mask & B_STAT_SIZE_INSECURE) == 0) { rw_lock_write_unlock(inode->Lock()); inode->FillGapWithZeros(oldSize, inode->Size()); rw_lock_write_lock(inode->Lock()); } updateTime = true; } if ((mask & B_STAT_MODE) != 0) { // only the user or root can do that if (!isOwnerOrRoot) return B_NOT_ALLOWED; node.UpdateMode(stat->st_mode, S_IUMSK); updateTime = true; } if ((mask & B_STAT_UID) != 0) { // only root should be allowed if (uid != 0) return B_NOT_ALLOWED; node.SetUserID(stat->st_uid); updateTime = true; } if ((mask & B_STAT_GID) != 0) { // only the user or root can do that if (!isOwnerOrRoot) return B_NOT_ALLOWED; node.SetGroupID(stat->st_gid); updateTime = true; } if ((mask & B_STAT_MODIFICATION_TIME) != 0 || updateTime || (mask & B_STAT_CHANGE_TIME) != 0) { // the user or root can do that or any user with write access if (!isOwnerOrRoot && !hasWriteAccess) return B_NOT_ALLOWED; struct timespec newTimespec = { 0, 0}; if ((mask & B_STAT_MODIFICATION_TIME) != 0) newTimespec = stat->st_mtim; if ((mask & B_STAT_CHANGE_TIME) != 0 && stat->st_ctim.tv_sec > newTimespec.tv_sec) newTimespec = stat->st_ctim; if (newTimespec.tv_sec == 0) Inode::_BigtimeToTimespec(real_time_clock_usecs(), &newTimespec); inode->SetModificationTime(&newTimespec); } if ((mask & B_STAT_CREATION_TIME) != 0) { // the user or root can do that or any user with write access if (!isOwnerOrRoot && !hasWriteAccess) return B_NOT_ALLOWED; inode->SetCreationTime(&stat->st_crtim); } status_t status = inode->WriteBack(transaction); if (status == B_OK) status = transaction.Done(); if (status == B_OK) notify_stat_changed(volume->ID(), -1, inode->ID(), mask); return status; }