From owner-freebsd-standards Thu Mar 20 13:38:53 2003 Delivered-To: freebsd-standards@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 96F8737B401 for ; Thu, 20 Mar 2003 13:38:45 -0800 (PST) Received: from mail.nsu.ru (mx.nsu.ru [193.124.215.71]) by mx1.FreeBSD.org (Postfix) with ESMTP id 0B72943F3F for ; Thu, 20 Mar 2003 13:38:42 -0800 (PST) (envelope-from fjoe@iclub.nsu.ru) Received: from drweb by mail.nsu.ru with drweb-scanned (Exim 3.20 #1) id 18w7kF-0006cc-00 for standards@freebsd.org; Fri, 21 Mar 2003 03:38:39 +0600 Received: from iclub.nsu.ru ([193.124.215.97] ident=root) by mail.nsu.ru with esmtp (Exim 3.20 #1) id 18w7kE-0006bd-00 for standards@freebsd.org; Fri, 21 Mar 2003 03:38:38 +0600 Received: from iclub.nsu.ru (fjoe@localhost [127.0.0.1]) by iclub.nsu.ru (8.12.8/8.12.8) with ESMTP id h2KLcMJW093411 for ; Fri, 21 Mar 2003 03:38:22 +0600 (NS) (envelope-from fjoe@iclub.nsu.ru) Received: (from fjoe@localhost) by iclub.nsu.ru (8.12.8/8.12.8/Submit) id h2KLcM8f093410 for standards@freebsd.org; Fri, 21 Mar 2003 03:38:22 +0600 (NS) (envelope-from fjoe) Date: Fri, 21 Mar 2003 03:38:21 +0600 From: Max Khon To: standards@freebsd.org Subject: thread-safe realpath Message-ID: <20030321033821.A91713@iclub.nsu.ru> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="G4iJoqBmSsgzjUCe" Content-Disposition: inline User-Agent: Mutt/1.2.5i X-Spam-Status: No, hits=-0.1 required=5.0 tests=DISCLAIMER,PROFITS,SPAM_PHRASE_00_01,USER_AGENT, USER_AGENT_MUTT version=2.43 X-Envelope-To: standards@freebsd.org Sender: owner-freebsd-standards@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.ORG --G4iJoqBmSsgzjUCe Content-Type: text/plain; charset=us-ascii Content-Disposition: inline hi, there! Constantin Svintsoff has submitted thread-safe realpath() implementation (implementation that does not use chdir(2)). The implementation is feature-compatible with FreeBSD implementation, i.e. if the last component of specified path can't be stat'ed and there is no trailing slash, realpath succeeds. I fixed a couple of bugs in it and would like to commit it to HEAD if there will be no objections. Test program is attached. The test simply creates two threads and calls realpath() in each. If the test is compiled with truepath() #if-0'ed one of the assertions fail after some time (you may need to increase number of iterations if you have very fast machine, mine is Athlon 850). Any comments are highly appreciated. Please reply directly (I am not subscribed). /fjoe --G4iJoqBmSsgzjUCe Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=Makefile PROG= test-realpath CFLAGS= -g -Wall LDADD= -pthread NOMAN= yes NOOBJ= yes .include --G4iJoqBmSsgzjUCe Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="test-realpath.c" /* * Copyright (c) 2003 Constantin S. Svintsoff * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #if 1 char * truepath(const char *path, char resolved_path[MAXPATHLEN]) { unsigned num_symlinks = 0; int saved_errno = errno; char left[MAXPATHLEN]; size_t left_len, resolved_len; if (path[0] == '/') { resolved_path[0] = '/'; resolved_path[1] = '\0'; if (path[1] == '\0') return resolved_path; resolved_len = 1; left_len = strlcpy(left, path + 1, MAXPATHLEN); } else { if (getcwd(resolved_path, MAXPATHLEN) == NULL) { strlcpy(resolved_path, ".", MAXPATHLEN); return NULL; } resolved_len = strlen(resolved_path); left_len = strlcpy(left, path, MAXPATHLEN); } if (left_len >= MAXPATHLEN || resolved_len >= MAXPATHLEN) { errno = ENAMETOOLONG; return NULL; } while (left_len > 0) { struct stat st; char next_token[MAXPATHLEN]; char *p; char *s = (p = strchr(left, '/')) ? p : left + left_len; memmove(next_token, left, s - left); left_len -= s - left; if (p != NULL) memmove(left, s + 1, left_len + 1); next_token[s - left] = '\0'; if (resolved_path[resolved_len - 1] != '/') { if (resolved_len + 1 >= MAXPATHLEN) { errno = ENAMETOOLONG; return NULL; } resolved_path[resolved_len++] = '/'; resolved_path[resolved_len] = '\0'; } if (next_token[0] == '\0') continue; else if (!strcmp(next_token, ".")) continue; else if (!strcmp(next_token, "..")) { if (resolved_len > 1) { char *q; /* trailing slash */ resolved_path[resolved_len - 1] = '\0'; q = strrchr(resolved_path, '/'); *q = '\0'; resolved_len = q - resolved_path; } continue; } /* filename */ resolved_len = strlcat(resolved_path, next_token, MAXPATHLEN); if (resolved_len >= MAXPATHLEN) { errno = ENAMETOOLONG; return NULL; } if (lstat(resolved_path, &st) < 0) { if (errno == ENOENT && p == NULL) { errno = saved_errno; return resolved_path; } return NULL; } if ((st.st_mode & S_IFLNK) == S_IFLNK) { char symlink[MAXPATHLEN]; int slen; if (num_symlinks++ > MAXSYMLINKS) { errno = ELOOP; return NULL; } slen = readlink(resolved_path, symlink, MAXPATHLEN); if (slen < 0) return NULL; symlink[slen] = '\0'; if (symlink[0] == '/') { /* absolute link */ resolved_path[1] = 0; resolved_len = 1; } else if (resolved_len > 1) { char *q; /* trailing slash */ resolved_path[resolved_len - 1] = '\0'; q = strrchr(resolved_path, '/'); *q = '\0'; resolved_len = q - resolved_path; } if (symlink[slen - 1] != '/' && p != NULL) { if (slen >= MAXPATHLEN) { errno = ENAMETOOLONG; return NULL; } symlink[slen] = '/'; symlink[slen + 1] = 0; } if (p != NULL) left_len = strlcat(symlink, left, MAXPATHLEN); if (left_len > MAXPATHLEN) { errno = ENAMETOOLONG; return NULL; } left_len = strlcpy(left, symlink, MAXPATHLEN); } } if (resolved_len > 1 && resolved_path[resolved_len - 1] == '/') resolved_path[resolved_len - 1] = '\0'; return resolved_path; } #define realpath(a, b) truepath((a), (b)) #endif pthread_mutex_t m; pthread_cond_t c; void * thread(void *arg) { const char *name = arg; char rname[MAXPATHLEN]; char dname[MAXPATHLEN]; int i; errno = 0; if (mkdir(name, 0777) < 0 && errno != EEXIST) { perror(name); exit(1); } strlcpy(rname, name, sizeof(rname)); strlcat(rname, "/foobar", sizeof(rname)); errno = 0; if (mkdir(rname, 0777) < 0 && errno != EEXIST) { perror(name); exit(1); } getcwd(dname, sizeof(dname)); strlcat(dname, "/", sizeof(dname)); strlcat(dname, rname, sizeof(dname)); fprintf(stderr, "%s: %s\n", name, dname); /* * wait for start */ fprintf(stderr, "%s: waiting for main thread to wake us up\n", name); pthread_mutex_lock(&m); pthread_cond_wait(&c, &m); pthread_mutex_unlock(&m); fprintf(stderr, "%s: started\n", name); for (i = 0; i < 100000; i++) { char buf[MAXPATHLEN]; if (realpath(rname, buf) == 0) { perror(buf); assert(0); } if (!!strcmp(buf, dname)) { fprintf(stderr, "%s: expected '%s' got '%s'\n", name, dname, buf); assert(0); } } fprintf(stderr, "%s: finished\n", name); rmdir(rname); rmdir(name); return NULL; } void regress() { char resolved[MAXPATHLEN]; static const char loop[] = "loop"; errno = 0; if (symlink(loop, loop) < 0 && errno != EEXIST) { perror(loop); return; } if (realpath(loop, resolved) == NULL) { fprintf(stderr, "realpath: %s: %s\n", resolved, strerror(errno)); } } int main() { pthread_t p1, p2; regress(); pthread_mutex_init(&m, NULL); pthread_cond_init(&c, NULL); pthread_create(&p1, NULL, thread, "p1"); pthread_create(&p2, NULL, thread, "p2"); fprintf(stderr, "main: sleeping for 1 sec\n"); sleep(1); fprintf(stderr, "main: waking up the threads\n"); pthread_mutex_lock(&m); pthread_cond_broadcast(&c); pthread_mutex_unlock(&m); pthread_join(p1, NULL); pthread_join(p2, NULL); pthread_cond_destroy(&c); pthread_mutex_destroy(&m); exit(0); } --G4iJoqBmSsgzjUCe-- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-standards" in the body of the message