1/*
2 * Copyright 2009-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "PackagesDirectory.h"
8
9#include <errno.h>
10
11#include <fs/KPath.h>
12#include <team.h>
13#include <vfs.h>
14
15#include "DebugSupport.h"
16
17
18PackagesDirectory::PackagesDirectory()
19	:
20	fStateName(),
21	fPath(NULL),
22	fDirFD(-1),
23	fNodeRef(),
24	fHashNext(NULL)
25{
26}
27
28
29PackagesDirectory::~PackagesDirectory()
30{
31	if (fDirFD >= 0)
32		close(fDirFD);
33
34	free(fPath);
35}
36
37
38/*static*/ bool
39PackagesDirectory::IsNewer(const PackagesDirectory* a,
40	const PackagesDirectory* b)
41{
42	if (b->fStateName.IsEmpty())
43		return false;
44	if (a->fStateName.IsEmpty())
45		return true;
46	return strcmp(a->fStateName, b->fStateName) > 0;
47}
48
49
50status_t
51PackagesDirectory::Init(const char* path, dev_t mountPointDeviceID,
52	ino_t mountPointNodeID, struct stat& _st)
53{
54	// Open the directory. We want the path be interpreted depending on from
55	// where it came (kernel or userland), but we always want a FD in the
56	// kernel I/O context. There's no VFS service method to do that for us,
57	// so we need to do that ourselves.
58	bool calledFromKernel
59		= team_get_current_team_id() == team_get_kernel_team_id();
60		// Not entirely correct, but good enough for now. The only
61		// alternative is to have that information passed in as well.
62
63	struct vnode* vnode;
64	status_t error;
65	if (path != NULL) {
66		error = vfs_get_vnode_from_path(path, calledFromKernel, &vnode);
67	} else {
68		// No path given -- use the "packages" directory at our mount point.
69		error = vfs_entry_ref_to_vnode(mountPointDeviceID, mountPointNodeID,
70			"packages", &vnode);
71	}
72	if (error != B_OK) {
73		ERROR("Failed to open packages directory \"%s\"\n", strerror(error));
74		RETURN_ERROR(error);
75	}
76
77	return _Init(vnode, _st);
78}
79
80
81status_t
82PackagesDirectory::InitOldState(dev_t adminDirDeviceID, ino_t adminDirNodeID,
83	const char* stateName)
84{
85	if (!fStateName.SetTo(stateName))
86		RETURN_ERROR(B_NO_MEMORY);
87
88	struct vnode* vnode;
89	status_t error = vfs_entry_ref_to_vnode(adminDirDeviceID, adminDirNodeID,
90		stateName, &vnode);
91	if (error != B_OK) {
92		ERROR("Failed to open old state directory \"%s\"\n", strerror(error));
93		RETURN_ERROR(error);
94	}
95
96	struct stat st;
97	return _Init(vnode, st);
98}
99
100
101status_t
102PackagesDirectory::_Init(struct vnode* vnode, struct stat& _st)
103{
104	fDirFD = vfs_open_vnode(vnode, O_RDONLY, true);
105
106	if (fDirFD < 0) {
107		ERROR("Failed to open packages directory \"%s\"\n", strerror(fDirFD));
108		vfs_put_vnode(vnode);
109		RETURN_ERROR(fDirFD);
110	}
111	// Our vnode reference has been transferred to the FD.
112
113	// Is it a directory at all?
114	struct stat& st = _st;
115	if (fstat(fDirFD, &st) < 0)
116		RETURN_ERROR(errno);
117
118	fNodeRef.device = st.st_dev;
119	fNodeRef.node = st.st_ino;
120
121	// get a normalized path
122	KPath normalizedPath;
123	if (normalizedPath.InitCheck() != B_OK)
124		RETURN_ERROR(normalizedPath.InitCheck());
125
126	char* normalizedPathBuffer = normalizedPath.LockBuffer();
127	status_t error = vfs_entry_ref_to_path(fNodeRef.device, fNodeRef.node, NULL,
128		true, normalizedPathBuffer, normalizedPath.BufferSize());
129	if (error != B_OK)
130		RETURN_ERROR(error);
131
132	fPath = strdup(normalizedPathBuffer);
133	if (fPath == NULL)
134		RETURN_ERROR(B_NO_MEMORY);
135
136	return B_OK;
137}
138