int _user_xsi_semctl(int semaphoreID, int semaphoreNumber, int command, union semun *args) { TRACE(("xsi_semctl: semaphoreID = %d, semaphoreNumber = %d, command = %d\n", semaphoreID, semaphoreNumber, command)); MutexLocker ipcHashLocker(sIpcLock); MutexLocker setHashLocker(sXsiSemaphoreSetLock); XsiSemaphoreSet *semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID); if (semaphoreSet == NULL) { TRACE_ERROR(("xsi_semctl: semaphore set id %d not valid\n", semaphoreID)); return EINVAL; } if (semaphoreNumber < 0 || semaphoreNumber > semaphoreSet->NumberOfSemaphores()) { TRACE_ERROR(("xsi_semctl: semaphore number %d not valid for " "semaphore %d\n", semaphoreNumber, semaphoreID)); return EINVAL; } if (args != 0 && !IS_USER_ADDRESS(args)) { TRACE_ERROR(("xsi_semctl: semun address is not valid\n")); return B_BAD_ADDRESS; } // Lock the semaphore set itself and release both the semaphore // set hash table lock and the ipc hash table lock _only_ if // the command it's not IPC_RMID, this prevents undesidered // situation from happening while (hopefully) improving the // concurrency. MutexLocker setLocker; if (command != IPC_RMID) { setLocker.SetTo(&semaphoreSet->Lock(), false); setHashLocker.Unlock(); ipcHashLocker.Unlock(); } else // We are about to delete the set along with its mutex, so // we can't use the MutexLocker class, as the mutex itself // won't exist on function exit mutex_lock(&semaphoreSet->Lock()); int result = 0; XsiSemaphore *semaphore = semaphoreSet->Semaphore(semaphoreNumber); switch (command) { case GETVAL: { if (!semaphoreSet->HasReadPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not permission " "on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else result = semaphore->Value(); break; } case SETVAL: { if (!semaphoreSet->HasPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not permission " "on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else { int value; if (user_memcpy(&value, &args->val, sizeof(int)) < B_OK) { TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); result = B_BAD_ADDRESS; } else if (value > USHRT_MAX) { TRACE_ERROR(("xsi_semctl: value %d out of range\n", value)); result = ERANGE; } else { semaphore->SetValue(value); semaphoreSet->ClearUndo(semaphoreNumber); } } break; } case GETPID: { if (!semaphoreSet->HasReadPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not permission " "on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else result = semaphore->LastPid(); break; } case GETNCNT: { if (!semaphoreSet->HasReadPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not permission " "on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else result = semaphore->ThreadsWaitingToIncrease(); break; } case GETZCNT: { if (!semaphoreSet->HasReadPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not permission " "on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else result = semaphore->ThreadsWaitingToBeZero(); break; } case GETALL: { if (!semaphoreSet->HasReadPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not read " "permission on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) { semaphore = semaphoreSet->Semaphore(i); unsigned short value = semaphore->Value(); if (user_memcpy(&args->array[i], &value, sizeof(unsigned short)) < B_OK) { TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); result = B_BAD_ADDRESS; break; } } break; } case SETALL: { if (!semaphoreSet->HasPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not permission " "on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else { bool doClear = true; for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) { semaphore = semaphoreSet->Semaphore(i); unsigned short value; if (user_memcpy(&value, &args->array[i], sizeof(unsigned short)) < B_OK) { TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); result = B_BAD_ADDRESS; doClear = false; break; } else semaphore->SetValue(value); } if (doClear) semaphoreSet->ClearUndos(); } break; } case IPC_STAT: { if (!semaphoreSet->HasReadPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not read " "permission on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else { struct semid_ds sem; sem.sem_perm = semaphoreSet->IpcPermission(); sem.sem_nsems = semaphoreSet->NumberOfSemaphores(); sem.sem_otime = semaphoreSet->LastSemopTime(); sem.sem_ctime = semaphoreSet->LastSemctlTime(); if (user_memcpy(args->buf, &sem, sizeof(struct semid_ds)) < B_OK) { TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); result = B_BAD_ADDRESS; } } break; } case IPC_SET: { if (!semaphoreSet->HasPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not " "permission on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); result = EACCES; } else { struct semid_ds sem; if (user_memcpy(&sem, args->buf, sizeof(struct semid_ds)) < B_OK) { TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); result = B_BAD_ADDRESS; } else semaphoreSet->DoIpcSet(&sem); } break; } case IPC_RMID: { // If this was the command, we are still holding // the semaphore set hash table lock along with the // ipc hash table lock and the semaphore set lock // itself, this way we are sure there is not // one waiting in the queue of the mutex. if (!semaphoreSet->HasPermission()) { TRACE_ERROR(("xsi_semctl: calling process has not " "permission on semaphore %d, key %d\n", semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); return EACCES; } key_t key = semaphoreSet->IpcKey(); Ipc *ipcKey = NULL; if (key != -1) { ipcKey = sIpcHashTable.Lookup(key); sIpcHashTable.Remove(ipcKey); } sSemaphoreHashTable.Remove(semaphoreSet); // Wake up of threads waiting on this set // happens in the destructor if (key != -1) delete ipcKey; atomic_add(&sXsiSemaphoreCount, -semaphoreSet->NumberOfSemaphores()); atomic_add(&sXsiSemaphoreSetCount, -1); // Remove any sem_undo request while (struct sem_undo *entry = semaphoreSet->GetUndoList().RemoveHead()) { MutexLocker _(entry->team->xsi_sem_context->lock); entry->team->xsi_sem_context->undo_list.Remove(entry); delete entry; } delete semaphoreSet; return 0; } default: TRACE_ERROR(("xsi_semctl: command %d not valid\n", command)); result = EINVAL; } return result; }
extern "C" status_t vboxsf_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter) { g_cache.Remove((vboxsf_vnode*)vnode->private_node); }