xref: /haiku/src/system/kernel/fs/KPath.cpp (revision f94671c33d0b48a87bdce78dd755a450479323cf)
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 
19 
20 // debugging
21 #define TRACE(x) ;
22 //#define TRACE(x) dprintf x
23 
24 
25 KPath::KPath(size_t bufferSize)
26 	:
27 	fBuffer(NULL),
28 	fBufferSize(0),
29 	fPathLength(0),
30 	fLocked(false)
31 {
32 	SetTo(NULL, KPath::DEFAULT, bufferSize);
33 }
34 
35 
36 KPath::KPath(const char* path, int32 flags, size_t bufferSize)
37 	:
38 	fBuffer(NULL),
39 	fBufferSize(0),
40 	fPathLength(0),
41 	fLocked(false)
42 {
43 	SetTo(path, flags, bufferSize);
44 }
45 
46 
47 KPath::KPath(const KPath& other)
48 	:
49 	fBuffer(NULL),
50 	fBufferSize(0),
51 	fPathLength(0),
52 	fLocked(false)
53 {
54 	*this = other;
55 }
56 
57 
58 KPath::~KPath()
59 {
60 	free(fBuffer);
61 }
62 
63 
64 status_t
65 KPath::SetTo(const char* path, int32 flags, size_t bufferSize)
66 {
67 	if (bufferSize == 0)
68 		bufferSize = B_PATH_NAME_LENGTH;
69 
70 	// free the previous buffer, if the buffer size differs
71 	if (fBuffer != NULL && fBufferSize != bufferSize) {
72 		free(fBuffer);
73 		fBuffer = NULL;
74 		fBufferSize = 0;
75 	}
76 	fPathLength = 0;
77 	fLocked = false;
78 
79 	// allocate buffer
80 	if (fBuffer == NULL)
81 		fBuffer = (char*)malloc(bufferSize);
82 	if (fBuffer == NULL)
83 		return B_NO_MEMORY;
84 
85 	fBufferSize = bufferSize;
86 	fBuffer[0] = '\0';
87 
88 	return SetPath(path, flags);
89 }
90 
91 
92 void
93 KPath::Adopt(KPath& other)
94 {
95 	free(fBuffer);
96 
97 	fBuffer = other.fBuffer;
98 	fBufferSize = other.fBufferSize;
99 	fPathLength = other.fPathLength;
100 
101 	other.fBuffer = NULL;
102 	other.fBufferSize = 0;
103 	other.fPathLength = 0;
104 }
105 
106 
107 status_t
108 KPath::InitCheck() const
109 {
110 	return fBuffer != NULL ? B_OK : B_NO_MEMORY;
111 }
112 
113 
114 status_t
115 KPath::SetPath(const char* path, int32 flags)
116 {
117 	if (fBuffer == NULL)
118 		return B_NO_INIT;
119 
120 	if (path != NULL) {
121 		if ((flags & NORMALIZE) != 0) {
122 			// normalize path
123 			status_t error = vfs_normalize_path(path, fBuffer, fBufferSize,
124 				(flags & TRAVERSE_LEAF_LINK) != 0,
125 				team_get_kernel_team_id() == team_get_current_team_id());
126 			if (error != B_OK) {
127 				SetPath(NULL);
128 				return error;
129 			}
130 			fPathLength = strlen(fBuffer);
131 		} else {
132 			// don't normalize path
133 			size_t length = strlen(path);
134 			if (length >= fBufferSize)
135 				return B_BUFFER_OVERFLOW;
136 
137 			memcpy(fBuffer, path, length + 1);
138 			fPathLength = length;
139 			_ChopTrailingSlashes();
140 		}
141 	} else {
142 		fBuffer[0] = '\0';
143 		fPathLength = 0;
144 	}
145 	return B_OK;
146 }
147 
148 
149 const char*
150 KPath::Path() const
151 {
152 	return fBuffer;
153 }
154 
155 
156 char*
157 KPath::LockBuffer()
158 {
159 	if (fBuffer == NULL || fLocked)
160 		return NULL;
161 
162 	fLocked = true;
163 	return fBuffer;
164 }
165 
166 
167 void
168 KPath::UnlockBuffer()
169 {
170 	if (!fLocked) {
171 		TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n"));
172 		return;
173 	}
174 	fLocked = false;
175 	fPathLength = strnlen(fBuffer, fBufferSize);
176 	if (fPathLength == fBufferSize) {
177 		TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n"));
178 		fPathLength--;
179 		fBuffer[fPathLength] = '\0';
180 	}
181 	_ChopTrailingSlashes();
182 }
183 
184 
185 char*
186 KPath::DetachBuffer()
187 {
188 	char* buffer = fBuffer;
189 
190 	if (fBuffer != NULL) {
191 		fBuffer = NULL;
192 		fBufferSize = 0;
193 		fPathLength = 0;
194 		fLocked = false;
195 	}
196 
197 	return buffer;
198 }
199 
200 
201 const char*
202 KPath::Leaf() const
203 {
204 	if (fBuffer == NULL)
205 		return NULL;
206 
207 	for (int32 i = fPathLength - 1; i >= 0; i--) {
208 		if (fBuffer[i] == '/')
209 			return fBuffer + i + 1;
210 	}
211 
212 	return fBuffer;
213 }
214 
215 
216 status_t
217 KPath::ReplaceLeaf(const char* newLeaf)
218 {
219 	const char* leaf = Leaf();
220 	if (leaf == NULL)
221 		return B_NO_INIT;
222 
223 	int32 leafIndex = leaf - fBuffer;
224 	// chop off the current leaf (don't replace "/", though)
225 	if (leafIndex != 0 || fBuffer[leafIndex - 1]) {
226 		fBuffer[leafIndex] = '\0';
227 		fPathLength = leafIndex;
228 		_ChopTrailingSlashes();
229 	}
230 
231 	// if a leaf was given, append it
232 	if (newLeaf != NULL)
233 		return Append(newLeaf);
234 	return B_OK;
235 }
236 
237 
238 bool
239 KPath::RemoveLeaf()
240 {
241 	// get the leaf -- bail out, if not initialized or only the "/" is left
242 	const char* leaf = Leaf();
243 	if (leaf == NULL || leaf == fBuffer || leaf[0] == '\0')
244 		return false;
245 
246 	// chop off the leaf
247 	int32 leafIndex = leaf - fBuffer;
248 	fBuffer[leafIndex] = '\0';
249 	fPathLength = leafIndex;
250 	_ChopTrailingSlashes();
251 
252 	return true;
253 }
254 
255 
256 status_t
257 KPath::Append(const char* component, bool isComponent)
258 {
259 	// check initialization and parameter
260 	if (fBuffer == NULL)
261 		return B_NO_INIT;
262 	if (component == NULL)
263 		return B_BAD_VALUE;
264 	if (fPathLength == 0)
265 		return SetPath(component);
266 
267 	// get component length
268 	size_t componentLength = strlen(component);
269 	if (componentLength < 1)
270 		return B_OK;
271 
272 	// if our current path is empty, we just copy the supplied one
273 	// compute the result path len
274 	bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/'
275 		&& component[0] != '/';
276 	size_t resultPathLength = fPathLength + componentLength
277 		+ (insertSlash ? 1 : 0);
278 	if (resultPathLength >= fBufferSize)
279 		return B_BUFFER_OVERFLOW;
280 
281 	// compose the result path
282 	if (insertSlash)
283 		fBuffer[fPathLength++] = '/';
284 	memcpy(fBuffer + fPathLength, component, componentLength + 1);
285 	fPathLength = resultPathLength;
286 	return B_OK;
287 }
288 
289 
290 status_t
291 KPath::Normalize(bool traverseLeafLink)
292 {
293 	if (fBuffer == NULL)
294 		return B_NO_INIT;
295 	if (fPathLength == 0)
296 		return B_BAD_VALUE;
297 
298 	status_t error = vfs_normalize_path(fBuffer, fBuffer, fBufferSize,
299 		traverseLeafLink,
300 		team_get_kernel_team_id() == team_get_current_team_id());
301 	if (error != B_OK) {
302 		// vfs_normalize_path() might have screwed up the previous path -- unset
303 		// it completely to avoid weird problems.
304 		fBuffer[0] = '\0';
305 		fPathLength = 0;
306 		return error;
307 	}
308 
309 	fPathLength = strlen(fBuffer);
310 	return B_OK;
311 }
312 
313 
314 KPath&
315 KPath::operator=(const KPath& other)
316 {
317 	SetTo(other.fBuffer, false, other.fBufferSize);
318 	return *this;
319 }
320 
321 
322 KPath&
323 KPath::operator=(const char* path)
324 {
325 	SetPath(path);
326 	return *this;
327 }
328 
329 
330 bool
331 KPath::operator==(const KPath& other) const
332 {
333 	if (fBuffer == NULL)
334 		return !other.fBuffer;
335 
336 	return other.fBuffer
337 		&& fPathLength == other.fPathLength
338 		&& strcmp(fBuffer, other.fBuffer) == 0;
339 }
340 
341 
342 bool
343 KPath::operator==(const char* path) const
344 {
345 	if (fBuffer == NULL)
346 		return (!path);
347 
348 	return path && strcmp(fBuffer, path) == 0;
349 }
350 
351 
352 bool
353 KPath::operator!=(const KPath& other) const
354 {
355 	return !(*this == other);
356 }
357 
358 
359 bool
360 KPath::operator!=(const char* path) const
361 {
362 	return !(*this == path);
363 }
364 
365 
366 void
367 KPath::_ChopTrailingSlashes()
368 {
369 	if (fBuffer != NULL) {
370 		while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/')
371 			fBuffer[--fPathLength] = '\0';
372 	}
373 }
374 
375