1/*
2 * Copyright 2004-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2008-2017, Axel D��rfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8/*! A simple class wrapping a path. Has a fixed-sized buffer. */
9
10
11#include <fs/KPath.h>
12
13#include <stdlib.h>
14#include <string.h>
15
16#include <team.h>
17#include <vfs.h>
18#include <slab/Slab.h>
19
20
21// debugging
22#define TRACE(x) ;
23//#define TRACE(x) dprintf x
24
25
26#ifdef _KERNEL_MODE
27extern object_cache* sPathNameCache;
28#endif
29
30
31KPath::KPath(size_t bufferSize)
32	:
33	fBuffer(NULL),
34	fBufferSize(0),
35	fPathLength(0),
36	fLocked(false),
37	fLazy(false),
38	fFailed(false),
39	fIsNull(false)
40{
41	SetTo(NULL, DEFAULT, bufferSize);
42}
43
44
45KPath::KPath(const char* path, int32 flags, size_t bufferSize)
46	:
47	fBuffer(NULL),
48	fBufferSize(0),
49	fPathLength(0),
50	fLocked(false),
51	fLazy(false),
52	fFailed(false),
53	fIsNull(false)
54{
55	SetTo(path, flags, bufferSize);
56}
57
58
59KPath::KPath(const KPath& other)
60	:
61	fBuffer(NULL),
62	fBufferSize(0),
63	fPathLength(0),
64	fLocked(false),
65	fLazy(false),
66	fFailed(false),
67	fIsNull(false)
68{
69	*this = other;
70}
71
72
73KPath::~KPath()
74{
75	_FreeBuffer();
76}
77
78
79status_t
80KPath::SetTo(const char* path, int32 flags, size_t bufferSize)
81{
82	if (bufferSize == 0)
83		bufferSize = B_PATH_NAME_LENGTH + 1;
84
85	// free the previous buffer, if the buffer size differs
86	if (fBuffer != NULL && fBufferSize != bufferSize) {
87		_FreeBuffer();
88		fBufferSize = 0;
89	}
90
91	fPathLength = 0;
92	fLocked = false;
93	fBufferSize = bufferSize;
94	fLazy = (flags & LAZY_ALLOC) != 0;
95	fIsNull = path == NULL;
96
97	if (path != NULL || !fLazy) {
98		status_t status = _AllocateBuffer();
99		if (status != B_OK)
100			return status;
101	}
102
103	return SetPath(path, flags);
104}
105
106
107void
108KPath::Adopt(KPath& other)
109{
110	_FreeBuffer();
111
112	fBuffer = other.fBuffer;
113	fBufferSize = other.fBufferSize;
114	fPathLength = other.fPathLength;
115	fLazy = other.fLazy;
116	fFailed = other.fFailed;
117	fIsNull = other.fIsNull;
118
119	other.fBuffer = NULL;
120	if (!other.fLazy)
121		other.fBufferSize = 0;
122	other.fPathLength = 0;
123	other.fFailed = false;
124	other.fIsNull = other.fLazy;
125}
126
127
128status_t
129KPath::InitCheck() const
130{
131	if (fBuffer != NULL || (fLazy && !fFailed && fBufferSize != 0))
132		return B_OK;
133
134	return fFailed ? B_NO_MEMORY : B_NO_INIT;
135}
136
137
138/*!	\brief Sets the buffer to \a path.
139
140	\param flags Understands the following two options:
141		- \c NORMALIZE
142		- \c TRAVERSE_LEAF_LINK
143*/
144status_t
145KPath::SetPath(const char* path, int32 flags)
146{
147	if (path == NULL && fLazy && fBuffer == NULL) {
148		fIsNull = true;
149		return B_OK;
150	}
151
152	if (fBuffer == NULL) {
153		if (fLazy) {
154			status_t status = _AllocateBuffer();
155			if (status != B_OK)
156				return B_NO_MEMORY;
157		} else
158			return B_NO_INIT;
159	}
160
161	fIsNull = false;
162
163	if (path != NULL) {
164		if ((flags & NORMALIZE) != 0) {
165			// normalize path
166			status_t status = _Normalize(path,
167				(flags & TRAVERSE_LEAF_LINK) != 0);
168			if (status != B_OK)
169				return status;
170		} else {
171			// don't normalize path
172			size_t length = strlen(path);
173			if (length >= fBufferSize)
174				return B_BUFFER_OVERFLOW;
175
176			memcpy(fBuffer, path, length + 1);
177			fPathLength = length;
178			_ChopTrailingSlashes();
179		}
180	} else {
181		fBuffer[0] = '\0';
182		fPathLength = 0;
183		if (fLazy)
184			fIsNull = true;
185	}
186	return B_OK;
187}
188
189
190const char*
191KPath::Path() const
192{
193	return fIsNull ? NULL : fBuffer;
194}
195
196
197/*!	\brief Locks the buffer for external changes.
198
199	\param force In lazy mode, this will allocate a buffer when set.
200		Otherwise, \c NULL will be returned if set to NULL.
201*/
202char*
203KPath::LockBuffer(bool force)
204{
205	if (fBuffer == NULL && fLazy) {
206		if (fIsNull && !force)
207			return NULL;
208
209		_AllocateBuffer();
210	}
211
212	if (fBuffer == NULL || fLocked)
213		return NULL;
214
215	fLocked = true;
216	fIsNull = false;
217
218	return fBuffer;
219}
220
221
222void
223KPath::UnlockBuffer()
224{
225	if (!fLocked) {
226#ifdef _KERNEL_MODE
227		panic("KPath::UnlockBuffer(): Buffer not locked!");
228#endif
229		return;
230	}
231
232	fLocked = false;
233
234	if (fBuffer == NULL)
235		return;
236
237	fPathLength = strnlen(fBuffer, fBufferSize);
238	if (fPathLength == fBufferSize) {
239		TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n"));
240		fPathLength--;
241		fBuffer[fPathLength] = '\0';
242	}
243	_ChopTrailingSlashes();
244}
245
246
247char*
248KPath::DetachBuffer()
249{
250	char* buffer = fBuffer;
251
252	if (fBufferSize == (B_PATH_NAME_LENGTH + 1)) {
253		buffer = (char*)malloc(fBufferSize);
254		memcpy(buffer, fBuffer, fBufferSize);
255		_FreeBuffer();
256	}
257
258	if (fBuffer != NULL) {
259		fBuffer = NULL;
260		fPathLength = 0;
261		fLocked = false;
262	}
263
264	return buffer;
265}
266
267
268const char*
269KPath::Leaf() const
270{
271	if (fBuffer == NULL)
272		return NULL;
273
274	for (int32 i = fPathLength - 1; i >= 0; i--) {
275		if (fBuffer[i] == '/')
276			return fBuffer + i + 1;
277	}
278
279	return fBuffer;
280}
281
282
283status_t
284KPath::ReplaceLeaf(const char* newLeaf)
285{
286	const char* leaf = Leaf();
287	if (leaf == NULL)
288		return B_NO_INIT;
289
290	int32 leafIndex = leaf - fBuffer;
291	// chop off the current leaf (don't replace "/", though)
292	if (leafIndex != 0 || fBuffer[leafIndex - 1]) {
293		fBuffer[leafIndex] = '\0';
294		fPathLength = leafIndex;
295		_ChopTrailingSlashes();
296	}
297
298	// if a leaf was given, append it
299	if (newLeaf != NULL)
300		return Append(newLeaf);
301	return B_OK;
302}
303
304
305bool
306KPath::RemoveLeaf()
307{
308	// get the leaf -- bail out, if not initialized or only the "/" is left
309	const char* leaf = Leaf();
310	if (leaf == NULL || leaf == fBuffer || leaf[0] == '\0')
311		return false;
312
313	// chop off the leaf
314	int32 leafIndex = leaf - fBuffer;
315	fBuffer[leafIndex] = '\0';
316	fPathLength = leafIndex;
317	_ChopTrailingSlashes();
318
319	return true;
320}
321
322
323status_t
324KPath::Append(const char* component, bool isComponent)
325{
326	// check initialization and parameter
327	if (fBuffer == NULL)
328		return B_NO_INIT;
329	if (component == NULL)
330		return B_BAD_VALUE;
331	if (fPathLength == 0)
332		return SetPath(component);
333
334	// get component length
335	size_t componentLength = strlen(component);
336	if (componentLength < 1)
337		return B_OK;
338
339	// if our current path is empty, we just copy the supplied one
340	// compute the result path len
341	bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/'
342		&& component[0] != '/';
343	size_t resultPathLength = fPathLength + componentLength
344		+ (insertSlash ? 1 : 0);
345	if (resultPathLength >= fBufferSize)
346		return B_BUFFER_OVERFLOW;
347
348	// compose the result path
349	if (insertSlash)
350		fBuffer[fPathLength++] = '/';
351	memcpy(fBuffer + fPathLength, component, componentLength + 1);
352	fPathLength = resultPathLength;
353	return B_OK;
354}
355
356
357status_t
358KPath::Normalize(bool traverseLeafLink)
359{
360	if (fBuffer == NULL)
361		return B_NO_INIT;
362	if (fPathLength == 0)
363		return B_BAD_VALUE;
364
365	return _Normalize(fBuffer, traverseLeafLink);
366}
367
368
369KPath&
370KPath::operator=(const KPath& other)
371{
372	if (other.fBuffer == fBuffer)
373		return *this;
374
375	SetTo(other.fBuffer, fLazy ? KPath::LAZY_ALLOC : KPath::DEFAULT,
376		other.fBufferSize);
377	return *this;
378}
379
380
381KPath&
382KPath::operator=(const char* path)
383{
384	SetPath(path);
385	return *this;
386}
387
388
389bool
390KPath::operator==(const KPath& other) const
391{
392	if (fBuffer == NULL)
393		return !other.fBuffer;
394
395	return other.fBuffer != NULL
396		&& fPathLength == other.fPathLength
397		&& strcmp(fBuffer, other.fBuffer) == 0;
398}
399
400
401bool
402KPath::operator==(const char* path) const
403{
404	if (fBuffer == NULL)
405		return path == NULL;
406
407	return path != NULL && strcmp(fBuffer, path) == 0;
408}
409
410
411bool
412KPath::operator!=(const KPath& other) const
413{
414	return !(*this == other);
415}
416
417
418bool
419KPath::operator!=(const char* path) const
420{
421	return !(*this == path);
422}
423
424
425status_t
426KPath::_AllocateBuffer()
427{
428	if (fBuffer == NULL && fBufferSize != 0) {
429#ifdef _KERNEL_MODE
430		if (fBufferSize == (B_PATH_NAME_LENGTH + 1))
431			fBuffer = (char*)object_cache_alloc(sPathNameCache, 0);
432		else
433#endif
434			fBuffer = (char*)malloc(fBufferSize);
435	}
436	if (fBuffer == NULL) {
437		fFailed = true;
438		return B_NO_MEMORY;
439	}
440
441	memset(fBuffer, 0, fBufferSize);
442	fFailed = false;
443	return B_OK;
444}
445
446
447void
448KPath::_FreeBuffer()
449{
450#ifdef _KERNEL_MODE
451	if (fBufferSize == (B_PATH_NAME_LENGTH + 1))
452		object_cache_free(sPathNameCache, fBuffer, 0);
453	else
454#endif
455		free(fBuffer);
456	fBuffer = NULL;
457}
458
459
460status_t
461KPath::_Normalize(const char* path, bool traverseLeafLink)
462{
463	status_t error = vfs_normalize_path(path, fBuffer, fBufferSize,
464		traverseLeafLink,
465		team_get_kernel_team_id() == team_get_current_team_id());
466	if (error != B_OK) {
467		// vfs_normalize_path() might have screwed up the previous
468		// path -- unset it completely to avoid weird problems.
469		fBuffer[0] = '\0';
470		fPathLength = 0;
471		return error;
472	}
473
474	fPathLength = strlen(fBuffer);
475	return B_OK;
476}
477
478
479void
480KPath::_ChopTrailingSlashes()
481{
482	if (fBuffer != NULL) {
483		while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/')
484			fBuffer[--fPathLength] = '\0';
485	}
486}
487
488