1f841f6adSraf /*
2f841f6adSraf * CDDL HEADER START
3f841f6adSraf *
4f841f6adSraf * The contents of this file are subject to the terms of the
5f841f6adSraf * Common Development and Distribution License (the "License").
6f841f6adSraf * You may not use this file except in compliance with the License.
7f841f6adSraf *
8f841f6adSraf * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9f841f6adSraf * or http://www.opensolaris.org/os/licensing.
10f841f6adSraf * See the License for the specific language governing permissions
11f841f6adSraf * and limitations under the License.
12f841f6adSraf *
13f841f6adSraf * When distributing Covered Code, include this CDDL HEADER in each
14f841f6adSraf * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15f841f6adSraf * If applicable, add the following below this CDDL HEADER, with the
16f841f6adSraf * fields enclosed by brackets "[]" replaced with your own identifying
17f841f6adSraf * information: Portions Copyright [yyyy] [name of copyright owner]
18f841f6adSraf *
19f841f6adSraf * CDDL HEADER END
20f841f6adSraf */
21f841f6adSraf
22f841f6adSraf /*
23a574db85Sraf * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24f841f6adSraf * Use is subject to license terms.
25f841f6adSraf */
26f841f6adSraf
27f841f6adSraf #pragma ident "%Z%%M% %I% %E% SMI"
28f841f6adSraf
29*7257d1b4Sraf #include "lint.h"
30f841f6adSraf #include "mtlib.h"
31f841f6adSraf #include <sys/types.h>
32f841f6adSraf #include <errno.h>
33f841f6adSraf #include <fcntl.h>
34f841f6adSraf #include <sys/stat.h>
35f841f6adSraf #include <unistd.h>
36f841f6adSraf #include <stdlib.h>
37f841f6adSraf #include <limits.h>
38f841f6adSraf #include <pthread.h>
39f841f6adSraf #include <thread.h>
40f841f6adSraf #include <string.h>
41f841f6adSraf #include <dirent.h>
42f841f6adSraf #include <stdio.h>
43f841f6adSraf #include <dlfcn.h>
448cd45542Sraf #include <atomic.h>
45f841f6adSraf #include <md5.h>
46f841f6adSraf #include "pos4obj.h"
47f841f6adSraf
48f841f6adSraf #define HASHSTRLEN 32
49f841f6adSraf
50f841f6adSraf static char *__pos4obj_name(const char *, const char *);
51f841f6adSraf static void __pos4obj_md5toa(unsigned char *, unsigned char *);
52f841f6adSraf static void __pos4obj_clean(char *);
53f841f6adSraf
54f841f6adSraf static char objroot[] = "/tmp/";
55f841f6adSraf static long int name_max = 0;
56f841f6adSraf
57f841f6adSraf int
__open_nc(const char * path,int oflag,mode_t mode)58f841f6adSraf __open_nc(const char *path, int oflag, mode_t mode)
59f841f6adSraf {
60a574db85Sraf int cancel_state;
61a574db85Sraf int val;
62f841f6adSraf struct stat64 statbuf;
63f841f6adSraf
64f841f6adSraf /*
65f841f6adSraf * Ensure path is not a symlink to somewhere else. This provides
66f841f6adSraf * a modest amount of protection against easy security attacks.
67f841f6adSraf */
68f841f6adSraf if (lstat64(path, &statbuf) == 0) {
69f841f6adSraf if (S_ISLNK(statbuf.st_mode)) {
70f841f6adSraf errno = EINVAL;
71f841f6adSraf return (-1);
72f841f6adSraf }
73f841f6adSraf }
74f841f6adSraf
75a574db85Sraf (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
76f841f6adSraf val = open64(path, oflag, mode);
77a574db85Sraf (void) pthread_setcancelstate(cancel_state, NULL);
78f841f6adSraf
79f841f6adSraf return (val);
80f841f6adSraf }
81f841f6adSraf
82f841f6adSraf int
__close_nc(int fildes)83f841f6adSraf __close_nc(int fildes)
84f841f6adSraf {
85a574db85Sraf int cancel_state;
86a574db85Sraf int val;
87f841f6adSraf
88a574db85Sraf (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
89f841f6adSraf val = close(fildes);
90a574db85Sraf (void) pthread_setcancelstate(cancel_state, NULL);
91f841f6adSraf
92f841f6adSraf return (val);
93f841f6adSraf }
94f841f6adSraf
95f841f6adSraf /*
96f841f6adSraf * This is to avoid loading libmd.so.1 unless we absolutely have to.
97f841f6adSraf */
98f841f6adSraf typedef void (*md5_calc_t)(unsigned char *, unsigned char *, unsigned int);
99f841f6adSraf static md5_calc_t real_md5_calc = NULL;
100f841f6adSraf static mutex_t md5_lock = DEFAULTMUTEX;
101f841f6adSraf
102f841f6adSraf static void
load_md5_calc(void)103f841f6adSraf load_md5_calc(void)
104f841f6adSraf {
105f90fe29eSraf void *md5_handle = dlopen("libmd.so.1", RTLD_LAZY);
1068cd45542Sraf md5_calc_t md5_calc = (md5_handle == NULL)? NULL :
1078cd45542Sraf (md5_calc_t)dlsym(md5_handle, "md5_calc");
108f90fe29eSraf
109f841f6adSraf lmutex_lock(&md5_lock);
110f841f6adSraf if (real_md5_calc == NULL) {
1118cd45542Sraf if (md5_calc == NULL)
112f841f6adSraf real_md5_calc = (md5_calc_t)(-1);
113f841f6adSraf else {
1148cd45542Sraf real_md5_calc = md5_calc;
115f90fe29eSraf md5_handle = NULL; /* don't dlclose it */
116f841f6adSraf }
1178cd45542Sraf membar_producer();
118f841f6adSraf }
119f841f6adSraf lmutex_unlock(&md5_lock);
120f90fe29eSraf
121f90fe29eSraf if (md5_handle)
122f90fe29eSraf (void) dlclose(md5_handle);
123f841f6adSraf }
124f841f6adSraf
125f841f6adSraf static char *
__pos4obj_name(const char * path,const char * type)126f841f6adSraf __pos4obj_name(const char *path, const char *type)
127f841f6adSraf {
128f841f6adSraf int shortpath = 1;
129f841f6adSraf int olderrno;
130f841f6adSraf size_t len;
131f841f6adSraf char *dfile;
132f841f6adSraf unsigned char hashbuf[HASHSTRLEN + 1];
133f841f6adSraf unsigned char md5_digest[MD5_DIGEST_LENGTH];
134f841f6adSraf
135f841f6adSraf /*
136f841f6adSraf * If the path is path_max - strlen(type) characters or less,
137f841f6adSraf * the name of the file to use will be the path prefixed by
138f841f6adSraf * the type.
139f841f6adSraf *
140f841f6adSraf * In the special case where the path is longer than
141f841f6adSraf * path_max - strlen(type) characters, we create a string based on the
142f841f6adSraf * MD5 hash of the path. We prefix that string with a '.' to
143f841f6adSraf * make it obscure, and create a directory in objroot with
144f841f6adSraf * that name. In that directory, we create a directory named
145f841f6adSraf * after the type of object requested. Inside the type
146f841f6adSraf * directory, the filename will be the path of the object. This
147f841f6adSraf * prevents collisions in all namespaces.
148f841f6adSraf *
149f841f6adSraf * Example:
150f841f6adSraf * Let objroot = "/tmp/", path = "/<longpath>", and type = ".MQD"
151f841f6adSraf * Let the MD5 hash of "<longpath>" = "<hash>"
152f841f6adSraf *
153f841f6adSraf * The desired file is /tmp/.<hash>/.MQD/<longpath>
154f841f6adSraf */
155f841f6adSraf
156f841f6adSraf /*
157f841f6adSraf * Do not include the leading '/' in the path length.
158f841f6adSraf * Assumes __pos4obj_check(path) has already been called.
159f841f6adSraf */
160f841f6adSraf if ((strlen(path) - 1) > (name_max - strlen(type)))
161f841f6adSraf shortpath = 0;
162f841f6adSraf
163f841f6adSraf if (shortpath) {
164f841f6adSraf /*
165f841f6adSraf * strlen(path) includes leading slash as space for NUL.
166f841f6adSraf */
167f841f6adSraf len = strlen(objroot) + strlen(type) + strlen(path);
168f841f6adSraf } else {
169f841f6adSraf /*
170f841f6adSraf * Long path name. Add 3 for extra '/', '.' and '\0'
171f841f6adSraf */
172f841f6adSraf len = strlen(objroot) + HASHSTRLEN + strlen(type) +
173f841f6adSraf strlen(path) + 3;
174f841f6adSraf }
175f841f6adSraf
176f841f6adSraf if ((dfile = malloc(len)) == NULL)
177f841f6adSraf return (NULL);
178f841f6adSraf
179f841f6adSraf (void) memset(dfile, 0, len);
180f841f6adSraf (void) strcpy(dfile, objroot);
181f841f6adSraf
182f841f6adSraf if (shortpath) {
183f841f6adSraf (void) strcat(dfile, type);
184f841f6adSraf (void) strcat(dfile, path + 1);
185f841f6adSraf return (dfile);
186f841f6adSraf }
187f841f6adSraf
188f841f6adSraf /*
189f841f6adSraf * If we can successfully load it, call md5_calc().
190f841f6adSraf * Otherwise, (this "can't happen") return NULL.
191f841f6adSraf */
192f841f6adSraf if (real_md5_calc == NULL)
193f841f6adSraf load_md5_calc();
194f841f6adSraf if (real_md5_calc == (md5_calc_t)(-1)) {
195f841f6adSraf free(dfile);
196f841f6adSraf return (NULL);
197f841f6adSraf }
198f841f6adSraf
199f841f6adSraf real_md5_calc(md5_digest, (unsigned char *)path + 1, strlen(path + 1));
200f841f6adSraf __pos4obj_md5toa(hashbuf, md5_digest);
201f841f6adSraf (void) strcat(dfile, ".");
202f841f6adSraf (void) strcat(dfile, (const char *)hashbuf);
203f841f6adSraf
204f841f6adSraf /*
205f841f6adSraf * Errno must be preserved across the following calls to
206f841f6adSraf * mkdir. This needs to be done to prevent incorrect error
207f841f6adSraf * reporting in certain cases. When we attempt to open a
208f841f6adSraf * non-existent object without the O_CREAT flag, it will
209f841f6adSraf * always create a lock file first. The lock file is created
210f841f6adSraf * and then the open is attempted, but fails with ENOENT. The
211f841f6adSraf * lock file is then destroyed. In the following code path, we
212f841f6adSraf * are finding the absolute path to the lock file after
213f841f6adSraf * already having attempted the open (which set errno to
214f841f6adSraf * ENOENT). The following calls to mkdir will return -1 and
215f841f6adSraf * set errno to EEXIST, since the hash and type directories
216f841f6adSraf * were created when the lock file was created. The correct
217f841f6adSraf * errno is the ENOENT from the attempted open of the desired
218f841f6adSraf * object.
219f841f6adSraf */
220f841f6adSraf olderrno = errno;
221f841f6adSraf
222f841f6adSraf /*
223f841f6adSraf * Create hash directory. Use 777 permissions so everyone can use it.
224f841f6adSraf */
225f841f6adSraf if (mkdir(dfile, S_IRWXU|S_IRWXG|S_IRWXO) == 0) {
226f841f6adSraf if (chmod(dfile, S_IRWXU|S_IRWXG|S_IRWXO) == -1) {
227f841f6adSraf free(dfile);
228f841f6adSraf return (NULL);
229f841f6adSraf }
230f841f6adSraf } else {
231f841f6adSraf if (errno != EEXIST) {
232f841f6adSraf free(dfile);
233f841f6adSraf return (NULL);
234f841f6adSraf }
235f841f6adSraf }
236f841f6adSraf
237f841f6adSraf (void) strcat(dfile, "/");
238f841f6adSraf (void) strcat(dfile, type);
239f841f6adSraf
240f841f6adSraf /*
241f841f6adSraf * Create directory for requested type. Use 777 perms so everyone
242f841f6adSraf * can use it.
243f841f6adSraf */
244f841f6adSraf if (mkdir(dfile, S_IRWXU|S_IRWXG|S_IRWXO) == 0) {
245f841f6adSraf if (chmod(dfile, S_IRWXU|S_IRWXG|S_IRWXO) == -1) {
246f841f6adSraf free(dfile);
247f841f6adSraf return (NULL);
248f841f6adSraf }
249f841f6adSraf } else {
250f841f6adSraf if (errno != EEXIST) {
251f841f6adSraf free(dfile);
252f841f6adSraf return (NULL);
253f841f6adSraf }
254f841f6adSraf }
255f841f6adSraf
256f841f6adSraf errno = olderrno;
257f841f6adSraf (void) strcat(dfile, path);
258f841f6adSraf return (dfile);
259f841f6adSraf }
260f841f6adSraf
261f841f6adSraf /*
262f841f6adSraf * Takes a 128-bit MD5 digest and transforms to a sequence of 32 ASCII
263f841f6adSraf * characters. Output is the hexadecimal representation of the digest.
264f841f6adSraf *
265f841f6adSraf * The output buffer must be at least HASHSTRLEN + 1 characters
266f841f6adSraf * long. HASHSTRLEN is the size of the MD5 digest (128 bits)
267f841f6adSraf * divided by the number of bits used per char of output (4). The
268f841f6adSraf * extra character at the end is for the NUL terminating character.
269f841f6adSraf */
270f841f6adSraf
271f841f6adSraf static void
__pos4obj_md5toa(unsigned char * dest,unsigned char * src)272f841f6adSraf __pos4obj_md5toa(unsigned char *dest, unsigned char *src)
273f841f6adSraf {
274f841f6adSraf int i;
275f841f6adSraf uint32_t *p;
276f841f6adSraf
277f841f6adSraf /* LINTED pointer cast may result in improper alignment */
278f841f6adSraf p = (uint32_t *)src;
279f841f6adSraf
280f841f6adSraf for (i = 0; i < (MD5_DIGEST_LENGTH / 4); i++)
281f841f6adSraf (void) snprintf((char *)dest + (i * 8), 9, "%.8x", *p++);
282f841f6adSraf
283f841f6adSraf dest[HASHSTRLEN] = '\0';
284f841f6adSraf }
285f841f6adSraf
286f841f6adSraf /*
287f841f6adSraf * This open function assume that there is no simultaneous
288f841f6adSraf * open/unlink operation is going on. The caller is supposed
289f841f6adSraf * to ensure that both open in O_CREAT mode happen atomically.
290f841f6adSraf * It returns the crflag as 1 if file is created else 0.
291f841f6adSraf */
292f841f6adSraf int
__pos4obj_open(const char * name,char * type,int oflag,mode_t mode,int * crflag)293f841f6adSraf __pos4obj_open(const char *name, char *type, int oflag,
294f841f6adSraf mode_t mode, int *crflag)
295f841f6adSraf {
296f841f6adSraf int fd;
297f841f6adSraf char *dfile;
298f841f6adSraf
299f841f6adSraf errno = 0;
300f841f6adSraf *crflag = 0;
301f841f6adSraf
302f841f6adSraf if ((dfile = __pos4obj_name(name, type)) == NULL) {
303f841f6adSraf return (-1);
304f841f6adSraf }
305f841f6adSraf
306f841f6adSraf if (!(oflag & O_CREAT)) {
307f841f6adSraf if ((fd = __open_nc(dfile, oflag, mode)) == -1)
308f841f6adSraf __pos4obj_clean(dfile);
309f841f6adSraf
310f841f6adSraf free(dfile);
311f841f6adSraf return (fd);
312f841f6adSraf }
313f841f6adSraf
314f841f6adSraf /*
315f841f6adSraf * We need to make sure that crflag is set iff we actually create
316f841f6adSraf * the file. We do this by or'ing in O_EXCL, and attempting an
317f841f6adSraf * open. If that fails with an EEXIST, and O_EXCL wasn't specified
318f841f6adSraf * by the caller, then the file seems to exist; we'll try an
319f841f6adSraf * open with O_CREAT cleared. If that succeeds, then the file
320f841f6adSraf * did indeed exist. If that fails with an ENOENT, however, the
321f841f6adSraf * file was removed between the opens; we need to take another
322f841f6adSraf * lap.
323f841f6adSraf */
324f841f6adSraf for (;;) {
325f841f6adSraf if ((fd = __open_nc(dfile, (oflag | O_EXCL), mode)) == -1) {
326f841f6adSraf if (errno == EEXIST && !(oflag & O_EXCL)) {
327f841f6adSraf fd = __open_nc(dfile, oflag & ~O_CREAT, mode);
328f841f6adSraf
329f841f6adSraf if (fd == -1 && errno == ENOENT)
330f841f6adSraf continue;
331f841f6adSraf break;
332f841f6adSraf }
333f841f6adSraf } else {
334f841f6adSraf *crflag = 1;
335f841f6adSraf }
336f841f6adSraf break;
337f841f6adSraf }
338f841f6adSraf
339f841f6adSraf free(dfile);
340f841f6adSraf return (fd);
341f841f6adSraf }
342f841f6adSraf
343f841f6adSraf
344f841f6adSraf int
__pos4obj_unlink(const char * name,const char * type)345f841f6adSraf __pos4obj_unlink(const char *name, const char *type)
346f841f6adSraf {
347f841f6adSraf int err;
348f841f6adSraf char *dfile;
349f841f6adSraf
350f841f6adSraf if ((dfile = __pos4obj_name(name, type)) == NULL) {
351f841f6adSraf return (-1);
352f841f6adSraf }
353f841f6adSraf
354f841f6adSraf err = unlink(dfile);
355f841f6adSraf
356f841f6adSraf __pos4obj_clean(dfile);
357f841f6adSraf
358f841f6adSraf free(dfile);
359f841f6adSraf
360f841f6adSraf return (err);
361f841f6adSraf }
362f841f6adSraf
363f841f6adSraf /*
364f841f6adSraf * This function opens the lock file for each named object
365f841f6adSraf * the presence of this file in the file system is the lock
366f841f6adSraf */
367f841f6adSraf int
__pos4obj_lock(const char * name,const char * ltype)368f841f6adSraf __pos4obj_lock(const char *name, const char *ltype)
369f841f6adSraf {
370f841f6adSraf char *dfile;
371f841f6adSraf int fd;
372f841f6adSraf int limit = 64;
373f841f6adSraf
374f841f6adSraf if ((dfile = __pos4obj_name(name, ltype)) == NULL) {
375f841f6adSraf return (-1);
376f841f6adSraf }
377f841f6adSraf
378f841f6adSraf while (limit-- > 0) {
379f841f6adSraf if ((fd = __open_nc(dfile, O_RDWR | O_CREAT | O_EXCL, 0666))
380f841f6adSraf < 0) {
381f841f6adSraf if (errno != EEXIST)
382f841f6adSraf break;
383f841f6adSraf (void) sleep(1);
384f841f6adSraf continue;
385f841f6adSraf }
386f841f6adSraf
387f841f6adSraf (void) __close_nc(fd);
388f841f6adSraf free(dfile);
389f841f6adSraf return (1);
390f841f6adSraf }
391f841f6adSraf
392f841f6adSraf free(dfile);
393f841f6adSraf return (-1);
394f841f6adSraf }
395f841f6adSraf
396f841f6adSraf /*
397f841f6adSraf * Unlocks the file by unlinking it from the filesystem
398f841f6adSraf */
399f841f6adSraf int
__pos4obj_unlock(const char * path,const char * type)400f841f6adSraf __pos4obj_unlock(const char *path, const char *type)
401f841f6adSraf {
402f841f6adSraf return (__pos4obj_unlink(path, type));
403f841f6adSraf }
404f841f6adSraf
405f841f6adSraf /*
406f841f6adSraf * Removes unused hash and type directories that may exist in specified path.
407f841f6adSraf */
408f841f6adSraf static void
__pos4obj_clean(char * path)409f841f6adSraf __pos4obj_clean(char *path)
410f841f6adSraf {
411f841f6adSraf char *p;
412f841f6adSraf int olderrno;
413f841f6adSraf
414f841f6adSraf /*
415f841f6adSraf * path is either
416f841f6adSraf * 1) /<objroot>/<type><path> or
417f841f6adSraf * 2) /<objroot>/.<hash>/<type>/<path>
418f841f6adSraf *
419f841f6adSraf * In case 1, there is nothing to clean.
420f841f6adSraf *
421f841f6adSraf * Detect case 2 by looking for a '/' after /objroot/ and
422f841f6adSraf * remove the two trailing directories, if empty.
423f841f6adSraf */
424f841f6adSraf if (strchr(path + strlen(objroot), '/') == NULL)
425f841f6adSraf return;
426f841f6adSraf
427f841f6adSraf /*
428f841f6adSraf * Preserve errno across calls to rmdir. See block comment in
429f841f6adSraf * __pos4obj_name() for explanation.
430f841f6adSraf */
431f841f6adSraf olderrno = errno;
432f841f6adSraf
433f841f6adSraf if ((p = strrchr(path, '/')) == NULL)
434f841f6adSraf return;
435f841f6adSraf *p = '\0';
436f841f6adSraf
437f841f6adSraf (void) rmdir(path);
438f841f6adSraf
439f841f6adSraf if ((p = strrchr(path, '/')) == NULL)
440f841f6adSraf return;
441f841f6adSraf *p = '\0';
442f841f6adSraf
443f841f6adSraf (void) rmdir(path);
444f841f6adSraf
445f841f6adSraf errno = olderrno;
446f841f6adSraf }
447f841f6adSraf
448f841f6adSraf
449f841f6adSraf /*
450f841f6adSraf * Check that path starts with a /, does not contain a / within it
451f841f6adSraf * and is not longer than PATH_MAX or NAME_MAX
452f841f6adSraf */
453f841f6adSraf int
__pos4obj_check(const char * path)454f841f6adSraf __pos4obj_check(const char *path)
455f841f6adSraf {
456f841f6adSraf long int i;
457f841f6adSraf
458f841f6adSraf /*
459f841f6adSraf * This assumes that __pos4obj_check() is called before
460f841f6adSraf * any of the other functions in this file
461f841f6adSraf */
462f841f6adSraf if (name_max == 0 || name_max == -1) {
463f841f6adSraf name_max = pathconf(objroot, _PC_NAME_MAX);
464f841f6adSraf if (name_max == -1)
465f841f6adSraf return (-1);
466f841f6adSraf }
467f841f6adSraf
468f841f6adSraf if (*path++ != '/') {
469f841f6adSraf errno = EINVAL;
470f841f6adSraf return (-1);
471f841f6adSraf }
472f841f6adSraf
473f841f6adSraf for (i = 0; *path != '\0'; i++) {
474f841f6adSraf if (*path++ == '/') {
475f841f6adSraf errno = EINVAL;
476f841f6adSraf return (-1);
477f841f6adSraf }
478f841f6adSraf }
479f841f6adSraf
480f841f6adSraf if (i > PATH_MAX || i > name_max) {
481f841f6adSraf errno = ENAMETOOLONG;
482f841f6adSraf return (-1);
483f841f6adSraf }
484f841f6adSraf
485f841f6adSraf return (0);
486f841f6adSraf }
487