xref: /haiku/src/system/kernel/fs/KPath.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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
27 extern object_cache* sPathNameCache;
28 #endif
29 
30 
31 KPath::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 
45 KPath::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 
59 KPath::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 
73 KPath::~KPath()
74 {
75 	_FreeBuffer();
76 }
77 
78 
79 status_t
80 KPath::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 
107 void
108 KPath::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 
128 status_t
129 KPath::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 */
144 status_t
145 KPath::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 
190 const char*
191 KPath::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 */
202 char*
203 KPath::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 
222 void
223 KPath::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 
247 char*
248 KPath::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 
268 const char*
269 KPath::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 
283 status_t
284 KPath::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 
305 bool
306 KPath::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 
323 status_t
324 KPath::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 
357 status_t
358 KPath::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 
369 KPath&
370 KPath::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 
381 KPath&
382 KPath::operator=(const char* path)
383 {
384 	SetPath(path);
385 	return *this;
386 }
387 
388 
389 bool
390 KPath::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 
401 bool
402 KPath::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 
411 bool
412 KPath::operator!=(const KPath& other) const
413 {
414 	return !(*this == other);
415 }
416 
417 
418 bool
419 KPath::operator!=(const char* path) const
420 {
421 	return !(*this == path);
422 }
423 
424 
425 status_t
426 KPath::_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 
447 void
448 KPath::_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 
460 status_t
461 KPath::_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 
479 void
480 KPath::_ChopTrailingSlashes()
481 {
482 	if (fBuffer != NULL) {
483 		while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/')
484 			fBuffer[--fPathLength] = '\0';
485 	}
486 }
487 
488