⛏️ index : haiku.git

/*
 * Copyright 2012 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Paweł Dziepak, pdziepak@quarnos.org
 */


#include "IdMap.h"

#include <AutoDeleter.h>
#include <FindDirectory.h>
#include <team.h>
#include <util/AutoLock.h>

#include "idmapper/IdMapper.h"


IdMap*	gIdMapper		= NULL;
mutex	gIdMapperLock;


IdMap::IdMap()
{
	mutex_init(&fLock, NULL);
	fInitStatus = _Repair();
}


IdMap::~IdMap()
{
	delete_port(fRequestPort);
	delete_port(fReplyPort);
	mutex_destroy(&fLock);
}


uid_t
IdMap::GetUserId(const char* owner)
{
	ASSERT(owner != NULL);
	return _GetValue<uid_t>(owner, MsgNameToUID);
}


gid_t
IdMap::GetGroupId(const char* ownerGroup)
{
	ASSERT(ownerGroup != NULL);
	return _GetValue<gid_t>(ownerGroup, MsgNameToGID);
}


char*
IdMap::GetOwner(uid_t user)
{
	return reinterpret_cast<char*>(_GetBuffer(user, MsgUIDToName));
}


char*
IdMap::GetOwnerGroup(gid_t group)
{
	return reinterpret_cast<char*>(_GetBuffer(group, MsgGIDToName));
}


template<typename T>
T
IdMap::_GetValue(const char* buffer, int32 code)
{
	ASSERT(buffer != NULL);

	MutexLocker _(fLock);
	do {
		status_t result = write_port(fRequestPort, MsgNameToUID, buffer,
			strlen(buffer) + 1);
		if (result != B_OK) {
			if (_Repair() != B_OK)
				return 0;
			continue;
		}

		int32 code;
		T value;
		result = read_port(fReplyPort, &code, &value, sizeof(T));
		if (result < B_OK) {
			if (_Repair() != B_OK)
				return 0;
			continue;
		}

		if (code != MsgReply)
			return 0;

		return value;
	} while (true);
}


template<typename T>
void*
IdMap::_GetBuffer(T value, int32 code)
{
	MutexLocker _(fLock);
	do {
		status_t result = write_port(fRequestPort, code, &value, sizeof(value));
		if (result != B_OK) {
			if (_Repair() != B_OK)
				return NULL;
			continue;
		}

		ssize_t size = port_buffer_size(fReplyPort);
		if (size < B_OK) {
			if (_Repair() != B_OK)
				return NULL;
			continue;
		}

		int32 code;
		void* buffer = malloc(size);
		if (buffer == NULL)
			return NULL;
		MemoryDeleter bufferDeleter(buffer);

		size = read_port(fReplyPort, &code, buffer, size);
		if (size < B_OK) {
			if (_Repair() != B_OK)
				return 0;
			continue;
		}

		if (code != MsgReply)
			return NULL;

		bufferDeleter.Detach();
		return buffer;
	} while (true);
}


status_t
IdMap::_Repair()
{
	status_t result = B_OK;

	fRequestPort = create_port(1, kRequestPortName);
	if (fRequestPort < B_OK)
		return fRequestPort;

	fReplyPort = create_port(1, kReplyPortName);
	if (fReplyPort < B_OK) {
		delete_port(fRequestPort);
		return fReplyPort;
	}

	char path[256];
	if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, static_cast<dev_t>(-1),
		false, path, sizeof(path)) != B_OK) {
		delete_port(fReplyPort);
		delete_port(fRequestPort);
		return B_NAME_NOT_FOUND;
	}
	strlcat(path, "/nfs4_idmapper_server", sizeof(path));

	const char* args[] = { path, NULL };
	thread_id thread = load_image_etc(1, args, NULL, B_NORMAL_PRIORITY,
		B_SYSTEM_TEAM, 0);
	if (thread < B_OK) {
		delete_port(fReplyPort);
		delete_port(fRequestPort);
		return thread;
	}

	set_port_owner(fRequestPort, thread);
	set_port_owner(fReplyPort, thread);

	result = resume_thread(thread);
	if (result != B_OK) {
		kill_thread(thread);
		delete_port(fReplyPort);
		delete_port(fRequestPort);
		return result;
	}

	return B_OK;
}