xref: /dragonfly/sbin/hammer2/subs.c (revision 279dd846)
1 /*
2  * Copyright (c) 2011-2012 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@dragonflybsd.org>
6  * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "hammer2.h"
37 
38 /*
39  * Obtain a file descriptor that the caller can execute ioctl()'s on.
40  */
41 int
42 hammer2_ioctl_handle(const char *sel_path)
43 {
44 	struct hammer2_ioc_version info;
45 	int fd;
46 
47 	if (sel_path == NULL)
48 		sel_path = ".";
49 
50 	fd = open(sel_path, O_RDONLY, 0);
51 	if (fd < 0) {
52 		fprintf(stderr, "hammer2: Unable to open %s: %s\n",
53 			sel_path, strerror(errno));
54 		return(-1);
55 	}
56 	if (ioctl(fd, HAMMER2IOC_VERSION_GET, &info) < 0) {
57 		fprintf(stderr, "hammer2: '%s' is not a hammer2 filesystem\n",
58 			sel_path);
59 		close(fd);
60 		return(-1);
61 	}
62 	return (fd);
63 }
64 
65 /*
66  * Execute the specified function as a detached independent process/daemon,
67  * unless we are in debug mode.  If we are in debug mode the function is
68  * executed as a pthread in the current process.
69  */
70 void
71 hammer2_demon(void *(*func)(void *), void *arg)
72 {
73 	pthread_t thread = NULL;
74 	pid_t pid;
75 	int ttyfd;
76 
77 	/*
78 	 * Do not disconnect in debug mode
79 	 */
80 	if (DebugOpt) {
81                 pthread_create(&thread, NULL, func, arg);
82 		NormalExit = 0;
83 		return;
84 	}
85 
86 	/*
87 	 * Otherwise disconnect us.  Double-fork to get rid of the ppid
88 	 * association and disconnect the TTY.
89 	 */
90 	if ((pid = fork()) < 0) {
91 		fprintf(stderr, "hammer2: fork(): %s\n", strerror(errno));
92 		exit(1);
93 	}
94 	if (pid > 0) {
95 		while (waitpid(pid, NULL, 0) != pid)
96 			;
97 		return;		/* parent returns */
98 	}
99 
100 	/*
101 	 * Get rid of the TTY/session before double-forking to finish off
102 	 * the ppid.
103 	 */
104 	ttyfd = open("/dev/null", O_RDWR);
105 	if (ttyfd >= 0) {
106 		if (ttyfd != 0)
107 			dup2(ttyfd, 0);
108 		if (ttyfd != 1)
109 			dup2(ttyfd, 1);
110 		if (ttyfd != 2)
111 			dup2(ttyfd, 2);
112 		if (ttyfd > 2)
113 			close(ttyfd);
114 	}
115 
116 	ttyfd = open("/dev/tty", O_RDWR);
117 	if (ttyfd >= 0) {
118 		ioctl(ttyfd, TIOCNOTTY, 0);
119 		close(ttyfd);
120 	}
121 	setsid();
122 
123 	/*
124 	 * Second fork to disconnect ppid (the original parent waits for
125 	 * us to exit).
126 	 */
127 	if ((pid = fork()) < 0) {
128 		_exit(2);
129 	}
130 	if (pid > 0)
131 		_exit(0);
132 
133 	/*
134 	 * The double child
135 	 */
136 	setsid();
137 	pthread_create(&thread, NULL, func, arg);
138 	pthread_exit(NULL);
139 	_exit(2);	/* NOT REACHED */
140 }
141 
142 const char *
143 hammer2_time64_to_str(uint64_t htime64, char **strp)
144 {
145 	struct tm *tp;
146 	time_t t;
147 
148 	if (*strp) {
149 		free(*strp);
150 		*strp = NULL;
151 	}
152 	*strp = malloc(64);
153 	t = htime64 / 1000000;
154 	tp = localtime(&t);
155 	strftime(*strp, 64, "%d-%b-%Y %H:%M:%S", tp);
156 	return (*strp);
157 }
158 
159 const char *
160 hammer2_uuid_to_str(uuid_t *uuid, char **strp)
161 {
162 	uint32_t status;
163 	if (*strp) {
164 		free(*strp);
165 		*strp = NULL;
166 	}
167 	uuid_to_string(uuid, strp, &status);
168 	return (*strp);
169 }
170 
171 const char *
172 hammer2_iptype_to_str(uint8_t type)
173 {
174 	switch(type) {
175 	case HAMMER2_OBJTYPE_UNKNOWN:
176 		return("UNKNOWN");
177 	case HAMMER2_OBJTYPE_DIRECTORY:
178 		return("DIR");
179 	case HAMMER2_OBJTYPE_REGFILE:
180 		return("FILE");
181 	case HAMMER2_OBJTYPE_FIFO:
182 		return("FIFO");
183 	case HAMMER2_OBJTYPE_CDEV:
184 		return("CDEV");
185 	case HAMMER2_OBJTYPE_BDEV:
186 		return("BDEV");
187 	case HAMMER2_OBJTYPE_SOFTLINK:
188 		return("SOFTLINK");
189 	case HAMMER2_OBJTYPE_HARDLINK:
190 		return("HARDLINK");
191 	case HAMMER2_OBJTYPE_SOCKET:
192 		return("SOCKET");
193 	case HAMMER2_OBJTYPE_WHITEOUT:
194 		return("WHITEOUT");
195 	default:
196 		return("ILLEGAL");
197 	}
198 }
199 
200 const char *
201 hammer2_pfstype_to_str(uint8_t type)
202 {
203 	switch(type) {
204 	case HAMMER2_PFSTYPE_NONE:
205 		return("NONE");
206 	case HAMMER2_PFSTYPE_SUPROOT:
207 		return("SUPROOT");
208 	case HAMMER2_PFSTYPE_DUMMY:
209 		return("DUMMY");
210 	case HAMMER2_PFSTYPE_CACHE:
211 		return("CACHE");
212 	case HAMMER2_PFSTYPE_SLAVE:
213 		return("SLAVE");
214 	case HAMMER2_PFSTYPE_SOFT_SLAVE:
215 		return("SOFT_SLAVE");
216 	case HAMMER2_PFSTYPE_SOFT_MASTER:
217 		return("SOFT_MASTER");
218 	case HAMMER2_PFSTYPE_MASTER:
219 		return("MASTER");
220 	default:
221 		return("ILLEGAL");
222 	}
223 }
224 
225 const char *
226 sizetostr(hammer2_off_t size)
227 {
228 	static char buf[32];
229 
230 	if (size < 1024 / 2) {
231 		snprintf(buf, sizeof(buf), "%6.2f", (double)size);
232 	} else if (size < 1024 * 1024 / 2) {
233 		snprintf(buf, sizeof(buf), "%6.2fKB",
234 			(double)size / 1024);
235 	} else if (size < 1024 * 1024 * 1024LL / 2) {
236 		snprintf(buf, sizeof(buf), "%6.2fMB",
237 			(double)size / (1024 * 1024));
238 	} else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
239 		snprintf(buf, sizeof(buf), "%6.2fGB",
240 			(double)size / (1024 * 1024 * 1024LL));
241 	} else {
242 		snprintf(buf, sizeof(buf), "%6.2fTB",
243 			(double)size / (1024 * 1024 * 1024LL * 1024LL));
244 	}
245 	return(buf);
246 }
247 
248 const char *
249 counttostr(hammer2_off_t size)
250 {
251 	static char buf[32];
252 
253 	if (size < 1024 / 2) {
254 		snprintf(buf, sizeof(buf), "%jd",
255 			 (intmax_t)size);
256 	} else if (size < 1024 * 1024 / 2) {
257 		snprintf(buf, sizeof(buf), "%jd",
258 			 (intmax_t)size);
259 	} else if (size < 1024 * 1024 * 1024LL / 2) {
260 		snprintf(buf, sizeof(buf), "%6.2fM",
261 			 (double)size / (1024 * 1024));
262 	} else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
263 		snprintf(buf, sizeof(buf), "%6.2fG",
264 			 (double)(size / (1024 * 1024 * 1024LL)));
265 	} else {
266 		snprintf(buf, sizeof(buf), "%6.2fT",
267 			 (double)(size / (1024 * 1024 * 1024LL * 1024LL)));
268 	}
269 	return(buf);
270 }
271 
272 #if 0
273 /*
274  * Allocation wrappers give us shims for possible future use
275  */
276 void *
277 hammer2_alloc(size_t bytes)
278 {
279 	void *ptr;
280 
281 	ptr = malloc(bytes);
282 	assert(ptr);
283 	bzero(ptr, bytes);
284 	return (ptr);
285 }
286 
287 void
288 hammer2_free(void *ptr)
289 {
290 	free(ptr);
291 }
292 
293 #endif
294 
295 hammer2_key_t
296 dirhash(const unsigned char *name, size_t len)
297 {
298 	const unsigned char *aname = name;
299 	uint32_t crcx;
300 	uint64_t key;
301 	size_t i;
302 	size_t j;
303 
304 	/*
305 	 * Filesystem version 6 or better will create directories
306 	 * using the ALG1 dirhash.  This hash breaks the filename
307 	 * up into domains separated by special characters and
308 	 * hashes each domain independently.
309 	 *
310 	 * We also do a simple sub-sort using the first character
311 	 * of the filename in the top 5-bits.
312 	 */
313 	key = 0;
314 
315 	/*
316 	 * m32
317 	 */
318 	crcx = 0;
319 	for (i = j = 0; i < len; ++i) {
320 		if (aname[i] == '.' ||
321 		    aname[i] == '-' ||
322 		    aname[i] == '_' ||
323 		    aname[i] == '~') {
324 			if (i != j)
325 				crcx += hammer2_icrc32(aname + j, i - j);
326 			j = i + 1;
327 		}
328 	}
329 	if (i != j)
330 		crcx += hammer2_icrc32(aname + j, i - j);
331 
332 	/*
333 	 * The directory hash utilizes the top 32 bits of the 64-bit key.
334 	 * Bit 63 must be set to 1.
335 	 */
336 	crcx |= 0x80000000U;
337 	key |= (uint64_t)crcx << 32;
338 
339 	/*
340 	 * l16 - crc of entire filename
341 	 *
342 	 * This crc reduces degenerate hash collision conditions
343 	 */
344 	crcx = hammer2_icrc32(aname, len);
345 	crcx = crcx ^ (crcx << 16);
346 	key |= crcx & 0xFFFF0000U;
347 
348 	/*
349 	 * Set bit 15.  This allows readdir to strip bit 63 so a positive
350 	 * 64-bit cookie/offset can always be returned, and still guarantee
351 	 * that the values 0x0000-0x7FFF are available for artificial entries.
352 	 * ('.' and '..').
353 	 */
354 	key |= 0x8000U;
355 
356 	return (key);
357 }
358