Home | History | Annotate | Download | only in syscalls
      1 /* Copyright 2013 The Chromium Authors. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file. */
      4 
      5 #include <assert.h>
      6 #include <stdlib.h>
      7 #include <errno.h>
      8 #include <limits.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include <sys/stat.h>
     12 #include <unistd.h>
     13 
     14 #include "sdk_util/macros.h"
     15 
     16 EXTERN_C_BEGIN
     17 
     18 #if defined(__native_client__)
     19 
     20 // TODO(binji): glibc has realpath, but it fails for all tests. Investigate.
     21 
     22 char* realpath(const char* path, char* resolved_path) {
     23   if (path == NULL) {
     24     errno = EINVAL;
     25     return NULL;
     26   }
     27 
     28   int needs_free = 0;
     29   if (resolved_path == NULL) {
     30     resolved_path = (char*)malloc(PATH_MAX);
     31     needs_free = 1;
     32   }
     33 
     34   struct stat statbuf;
     35   const char* in = path;
     36   char* out = resolved_path;
     37   char* out_end = resolved_path + PATH_MAX - 1;
     38   int done = 0;
     39 
     40   *out = 0;
     41 
     42   if (*in == '/') {
     43     // Absolute path.
     44     strcat(out, "/");
     45     in++;
     46     out++;
     47   } else {
     48     // Relative path.
     49     if (getcwd(out, out_end - out) == NULL)
     50       goto fail;
     51 
     52     out += strlen(out);
     53   }
     54 
     55   if (stat(resolved_path, &statbuf) != 0)
     56     goto fail;
     57 
     58   while (!done) {
     59     const char* next_slash = strchr(in, '/');
     60     size_t namelen;
     61     const char* next_in;
     62     if (next_slash) {
     63       namelen = next_slash - in;
     64       next_in = next_slash + 1;
     65     } else {
     66       namelen = strlen(in);
     67       next_in = in + namelen;  // Move to the '\0'
     68       done = 1;
     69     }
     70 
     71     if (namelen == 0) {
     72       // Empty name, do nothing.
     73     } else if (namelen == 1 && strncmp(in, ".", 1) == 0) {
     74       // Current directory, do nothing.
     75     } else if (namelen == 2 && strncmp(in, "..", 2) == 0) {
     76       // Parent directory, find previous slash in resolved_path.
     77       char* prev_slash = strrchr(resolved_path, '/');
     78       assert(prev_slash != NULL);
     79 
     80       out = prev_slash;
     81       if (prev_slash == resolved_path) {
     82         // Moved to the root. Keep the slash.
     83         ++out;
     84       }
     85 
     86       *out = 0;
     87     } else {
     88       // Append a slash if not at root.
     89       if (out != resolved_path + 1) {
     90         if (out + 1 > out_end) {
     91           errno = ENAMETOOLONG;
     92           goto fail;
     93         }
     94 
     95         strncat(out, "/", namelen);
     96         out++;
     97       }
     98 
     99       if (out + namelen > out_end) {
    100         errno = ENAMETOOLONG;
    101         goto fail;
    102       }
    103 
    104       strncat(out, in, namelen);
    105       out += namelen;
    106     }
    107 
    108     in = next_in;
    109 
    110     if (stat(resolved_path, &statbuf) != 0)
    111       goto fail;
    112 
    113     // If there is more to the path, then the current path must be a directory.
    114     if (!done && !S_ISDIR(statbuf.st_mode)) {
    115       errno = ENOTDIR;
    116       goto fail;
    117     }
    118   }
    119 
    120   return resolved_path;
    121 
    122 fail:
    123   if (needs_free) {
    124     free(resolved_path);
    125   }
    126   return NULL;
    127 }
    128 
    129 EXTERN_C_END
    130 
    131 #endif  // defined(__native_client__)
    132