4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
31 #include <sys/types.h>
50 static char *__pos4obj_name(const char *, const char *);
51 static void __pos4obj_md5toa(unsigned char *, unsigned char *);
52 static void __pos4obj_clean(char *);
54 static char objroot
[] = "/tmp/";
55 static long int name_max
= 0;
58 __open_nc(const char *path
, int oflag
, mode_t mode
)
62 struct stat64 statbuf
;
65 * Ensure path is not a symlink to somewhere else. This provides
66 * a modest amount of protection against easy security attacks.
68 if (lstat64(path
, &statbuf
) == 0) {
69 if (S_ISLNK(statbuf
.st_mode
)) {
75 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, &cancel_state
);
76 val
= open64(path
, oflag
, mode
);
77 (void) pthread_setcancelstate(cancel_state
, NULL
);
83 __close_nc(int fildes
)
88 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, &cancel_state
);
90 (void) pthread_setcancelstate(cancel_state
, NULL
);
96 * This is to avoid loading libmd.so.1 unless we absolutely have to.
98 typedef void (*md5_calc_t
)(unsigned char *, unsigned char *, unsigned int);
99 static md5_calc_t real_md5_calc
= NULL
;
100 static mutex_t md5_lock
= DEFAULTMUTEX
;
105 void *md5_handle
= dlopen("libmd.so.1", RTLD_LAZY
);
106 md5_calc_t md5_calc
= (md5_handle
== NULL
)? NULL
:
107 (md5_calc_t
)dlsym(md5_handle
, "md5_calc");
109 lmutex_lock(&md5_lock
);
110 if (real_md5_calc
== NULL
) {
111 if (md5_calc
== NULL
)
112 real_md5_calc
= (md5_calc_t
)(-1);
114 real_md5_calc
= md5_calc
;
115 md5_handle
= NULL
; /* don't dlclose it */
119 lmutex_unlock(&md5_lock
);
122 (void) dlclose(md5_handle
);
126 __pos4obj_name(const char *path
, const char *type
)
132 unsigned char hashbuf
[HASHSTRLEN
+ 1];
133 unsigned char md5_digest
[MD5_DIGEST_LENGTH
];
136 * If the path is path_max - strlen(type) characters or less,
137 * the name of the file to use will be the path prefixed by
140 * In the special case where the path is longer than
141 * path_max - strlen(type) characters, we create a string based on the
142 * MD5 hash of the path. We prefix that string with a '.' to
143 * make it obscure, and create a directory in objroot with
144 * that name. In that directory, we create a directory named
145 * after the type of object requested. Inside the type
146 * directory, the filename will be the path of the object. This
147 * prevents collisions in all namespaces.
150 * Let objroot = "/tmp/", path = "/<longpath>", and type = ".MQD"
151 * Let the MD5 hash of "<longpath>" = "<hash>"
153 * The desired file is /tmp/.<hash>/.MQD/<longpath>
157 * Do not include the leading '/' in the path length.
158 * Assumes __pos4obj_check(path) has already been called.
160 if ((strlen(path
) - 1) > (name_max
- strlen(type
)))
165 * strlen(path) includes leading slash as space for NUL.
167 len
= strlen(objroot
) + strlen(type
) + strlen(path
);
170 * Long path name. Add 3 for extra '/', '.' and '\0'
172 len
= strlen(objroot
) + HASHSTRLEN
+ strlen(type
) +
176 if ((dfile
= malloc(len
)) == NULL
)
179 (void) memset(dfile
, 0, len
);
180 (void) strcpy(dfile
, objroot
);
183 (void) strcat(dfile
, type
);
184 (void) strcat(dfile
, path
+ 1);
189 * If we can successfully load it, call md5_calc().
190 * Otherwise, (this "can't happen") return NULL.
192 if (real_md5_calc
== NULL
)
194 if (real_md5_calc
== (md5_calc_t
)(-1)) {
199 real_md5_calc(md5_digest
, (unsigned char *)path
+ 1, strlen(path
+ 1));
200 __pos4obj_md5toa(hashbuf
, md5_digest
);
201 (void) strcat(dfile
, ".");
202 (void) strcat(dfile
, (const char *)hashbuf
);
205 * Errno must be preserved across the following calls to
206 * mkdir. This needs to be done to prevent incorrect error
207 * reporting in certain cases. When we attempt to open a
208 * non-existent object without the O_CREAT flag, it will
209 * always create a lock file first. The lock file is created
210 * and then the open is attempted, but fails with ENOENT. The
211 * lock file is then destroyed. In the following code path, we
212 * are finding the absolute path to the lock file after
213 * already having attempted the open (which set errno to
214 * ENOENT). The following calls to mkdir will return -1 and
215 * set errno to EEXIST, since the hash and type directories
216 * were created when the lock file was created. The correct
217 * errno is the ENOENT from the attempted open of the desired
223 * Create hash directory. Use 777 permissions so everyone can use it.
225 if (mkdir(dfile
, S_IRWXU
|S_IRWXG
|S_IRWXO
) == 0) {
226 if (chmod(dfile
, S_IRWXU
|S_IRWXG
|S_IRWXO
) == -1) {
231 if (errno
!= EEXIST
) {
237 (void) strcat(dfile
, "/");
238 (void) strcat(dfile
, type
);
241 * Create directory for requested type. Use 777 perms so everyone
244 if (mkdir(dfile
, S_IRWXU
|S_IRWXG
|S_IRWXO
) == 0) {
245 if (chmod(dfile
, S_IRWXU
|S_IRWXG
|S_IRWXO
) == -1) {
250 if (errno
!= EEXIST
) {
257 (void) strcat(dfile
, path
);
262 * Takes a 128-bit MD5 digest and transforms to a sequence of 32 ASCII
263 * characters. Output is the hexadecimal representation of the digest.
265 * The output buffer must be at least HASHSTRLEN + 1 characters
266 * long. HASHSTRLEN is the size of the MD5 digest (128 bits)
267 * divided by the number of bits used per char of output (4). The
268 * extra character at the end is for the NUL terminating character.
272 __pos4obj_md5toa(unsigned char *dest
, unsigned char *src
)
277 /* LINTED pointer cast may result in improper alignment */
280 for (i
= 0; i
< (MD5_DIGEST_LENGTH
/ 4); i
++)
281 (void) snprintf((char *)dest
+ (i
* 8), 9, "%.8x", *p
++);
283 dest
[HASHSTRLEN
] = '\0';
287 * This open function assume that there is no simultaneous
288 * open/unlink operation is going on. The caller is supposed
289 * to ensure that both open in O_CREAT mode happen atomically.
290 * It returns the crflag as 1 if file is created else 0.
293 __pos4obj_open(const char *name
, char *type
, int oflag
,
294 mode_t mode
, int *crflag
)
302 if ((dfile
= __pos4obj_name(name
, type
)) == NULL
) {
306 if (!(oflag
& O_CREAT
)) {
307 if ((fd
= __open_nc(dfile
, oflag
, mode
)) == -1)
308 __pos4obj_clean(dfile
);
315 * We need to make sure that crflag is set iff we actually create
316 * the file. We do this by or'ing in O_EXCL, and attempting an
317 * open. If that fails with an EEXIST, and O_EXCL wasn't specified
318 * by the caller, then the file seems to exist; we'll try an
319 * open with O_CREAT cleared. If that succeeds, then the file
320 * did indeed exist. If that fails with an ENOENT, however, the
321 * file was removed between the opens; we need to take another
325 if ((fd
= __open_nc(dfile
, (oflag
| O_EXCL
), mode
)) == -1) {
326 if (errno
== EEXIST
&& !(oflag
& O_EXCL
)) {
327 fd
= __open_nc(dfile
, oflag
& ~O_CREAT
, mode
);
329 if (fd
== -1 && errno
== ENOENT
)
345 __pos4obj_unlink(const char *name
, const char *type
)
350 if ((dfile
= __pos4obj_name(name
, type
)) == NULL
) {
356 __pos4obj_clean(dfile
);
364 * This function opens the lock file for each named object
365 * the presence of this file in the file system is the lock
368 __pos4obj_lock(const char *name
, const char *ltype
)
374 if ((dfile
= __pos4obj_name(name
, ltype
)) == NULL
) {
378 while (limit
-- > 0) {
379 if ((fd
= __open_nc(dfile
, O_RDWR
| O_CREAT
| O_EXCL
, 0666))
387 (void) __close_nc(fd
);
397 * Unlocks the file by unlinking it from the filesystem
400 __pos4obj_unlock(const char *path
, const char *type
)
402 return (__pos4obj_unlink(path
, type
));
406 * Removes unused hash and type directories that may exist in specified path.
409 __pos4obj_clean(char *path
)
416 * 1) /<objroot>/<type><path> or
417 * 2) /<objroot>/.<hash>/<type>/<path>
419 * In case 1, there is nothing to clean.
421 * Detect case 2 by looking for a '/' after /objroot/ and
422 * remove the two trailing directories, if empty.
424 if (strchr(path
+ strlen(objroot
), '/') == NULL
)
428 * Preserve errno across calls to rmdir. See block comment in
429 * __pos4obj_name() for explanation.
433 if ((p
= strrchr(path
, '/')) == NULL
)
439 if ((p
= strrchr(path
, '/')) == NULL
)
450 * Check that path starts with a /, does not contain a / within it
451 * and is not longer than PATH_MAX or NAME_MAX
454 __pos4obj_check(const char *path
)
459 * This assumes that __pos4obj_check() is called before
460 * any of the other functions in this file
462 if (name_max
== 0 || name_max
== -1) {
463 name_max
= pathconf(objroot
, _PC_NAME_MAX
);
468 if (*path
++ != '/') {
473 for (i
= 0; *path
!= '\0'; i
++) {
474 if (*path
++ == '/') {
480 if (i
> PATH_MAX
|| i
> name_max
) {
481 errno
= ENAMETOOLONG
;