/* * This file is part of libaacs * Copyright (C) 2013 VideoLAN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #if HAVE_CONFIG_H #include "config.h" #endif #include "path.h" #include #include #include #define MAX_LINKS 32 char *aacs_resolve_path(const char *path, char *resolved_path) { char tmp_path[AACS_PATH_MAX]; char link[AACS_PATH_MAX]; char *new_path = resolved_path; char *path_end = tmp_path + AACS_PATH_MAX - 1; int readlinks = 0; int n; if (!path || !*path || strlen(path) >= AACS_PATH_MAX - 2) { return NULL; } /* shadow original path with a copy */ strcpy(tmp_path, path); path = tmp_path; /* handle relative path */ if (*path != '/') { if (!getcwd(new_path, AACS_PATH_MAX - 1)) { return NULL; } new_path += strlen(new_path); if (new_path[-1] != '/') { *new_path++ = '/'; } } else { *new_path++ = '/'; path++; } while (*path) { /* "/" */ if (*path == '/') { path++; continue; } /* "." */ if (path[0] == '.' && (!path[1] || path[1] == '/')) { path++; continue; } /* ".." */ if (path[0] == '.' && path[1] == '.' && (!path[2] || path[2] == '/')) { path += 2; /* not at root ? -> back up one level */ if (new_path != resolved_path + 1) { while ((--new_path)[-1] != '/'); } continue; } /* copy next component */ while (*path && *path != '/') { if (path >= path_end) { return NULL; } *new_path++ = *path++; } /* avoid symlink loops */ if (readlinks++ > MAX_LINKS) { return NULL; } /* resolve symlink */ *new_path = 0; n = readlink(resolved_path, link, AACS_PATH_MAX - 1); if (n < 0) { if (errno != EINVAL) { return NULL; } /* file exists but isn't a symlink. */ } else if (n >= AACS_PATH_MAX - 1) { return NULL; } else { link[n] = 0; if (*link == '/') { new_path = resolved_path; } else { while (*(--new_path) != '/'); } if (path + n >= path_end) { return NULL; } /* update what's left */ strcat(link, path); strcpy(tmp_path, link); path = tmp_path; } if (*path) { *new_path++ = '/'; } } *new_path = 0; return resolved_path; } #ifdef TEST_AACS_RESOLVE_PATH #include void main(int argc, char *argv[]) { char path[AACS_PATH_MAX]; printf("%s -> %s\n", argv[0], aacs_resolve_path(argv[0], path)); printf("%s -> %s\n", argv[1], aacs_resolve_path(argv[1], path)); } #endif