1
1

Split path handling into path.c and replaced rpath() with a better implementation

This is the first step into replacing all code that relies on PATH_MAX,
more changes will follow.
Этот коммит содержится в:
Yorhel 2009-04-23 19:44:37 +02:00
родитель 1739ee74d6
Коммит 7698bfd980
4 изменённых файлов: 296 добавлений и 122 удалений

Просмотреть файл

@ -1,5 +1,5 @@
bin_PROGRAMS = ncdu
ncdu_SOURCES = browser.c calc.c delete.c exclude.c help.c main.c util.c
ncdu_SOURCES = browser.c calc.c delete.c exclude.c help.c main.c path.c util.c
noinst_HEADERS = browser.h calc.h delete.h exclude.h help.h ncdu.h util.h
noinst_HEADERS = browser.h calc.h delete.h exclude.h help.h ncdu.h path.h util.h

Просмотреть файл

@ -28,6 +28,7 @@
#include "exclude.h"
#include "util.h"
#include "browser.h"
#include "path.h"
#include <string.h>
#include <stdlib.h>
@ -45,21 +46,6 @@
# define S_BLKSIZE 512
#endif
#ifndef LINK_MAX
# ifdef _POSIX_LINK_MAX
# define LINK_MAX _POSIX_LINK_MAX
# else
# define LINK_MAX 32
# endif
#endif
#ifndef S_ISLNK
# ifndef S_IFLNK
# define S_IFLNK 0120000
# endif
# define S_ISLNK(x) (x & S_IFLNK)
#endif
/* external vars */
int calc_delay = 100;
@ -77,107 +63,6 @@ long lastupdate; /* time of the last screen update */
int anpos; /* position of the animation string */
/* My own implementation of realpath()
- assumes that *every* possible path fits in PATH_MAX bytes
- does not set errno on error
- has not yet been fully tested
*/
char *rpath(const char *from, char *to) {
char tmp[PATH_MAX], cwd[PATH_MAX], cur[PATH_MAX], app[PATH_MAX];
int i, j, l, k, last, ll = 0;
struct stat st;
getcwd(cwd, PATH_MAX);
strcpy(cur, from);
app[0] = 0;
loop:
/* not an absolute path, add current directory */
if(cur[0] != '/') {
if(!(cwd[0] == '/' && cwd[1] == 0))
strcpy(tmp, cwd);
else
tmp[0] = 0;
if(strlen(cur) + 2 > PATH_MAX - strlen(tmp))
return(NULL);
strcat(tmp, "/");
strcat(tmp, cur);
} else
strcpy(tmp, cur);
/* now fix things like '.' and '..' */
i = j = last = 0;
l = strlen(tmp);
while(1) {
if(tmp[i] == 0)
break;
/* . */
if(l >= i+2 && tmp[i] == '/' && tmp[i+1] == '.' && (tmp[i+2] == 0 || tmp[i+2] == '/')) {
i+= 2;
continue;
}
/* .. */
if(l >= i+3 && tmp[i] == '/' && tmp[i+1] == '.' && tmp[i+2] == '.' && (tmp[i+3] == 0 || tmp[i+3] == '/')) {
for(k=j; --k>0;)
if(to[k] == '/' && k != j-1)
break;
j -= j-k;
if(j < 1) j = 1;
i += 3;
continue;
}
/* remove double slashes */
if(tmp[i] == '/' && i>0 && tmp[i-1] == '/') {
i++;
continue;
}
to[j++] = tmp[i++];
}
/* remove leading slashes */
while(--j > 0) {
if(to[j] != '/')
break;
}
to[j+1] = 0;
/* make sure we do have something left in case our path is / */
if(to[0] == 0) {
to[0] = '/';
to[1] = 0;
}
/* append 'app' */
if(app[0] != 0)
strcat(to, app);
j = strlen(to);
/* check for symlinks */
for(i=1; i<=j; i++) {
if(to[i] == '/' || to[i] == 0) {
strncpy(tmp, to, i);
tmp[i] = 0;
if(lstat(tmp, &st) < 0)
return(NULL);
if(S_ISLNK(st.st_mode)) {
if(++ll > LINK_MAX || (k = readlink(tmp, cur, PATH_MAX)) < 0)
return(NULL);
cur[k] = 0;
if(to[i] != 0)
strcpy(app, &to[i]);
strcpy(cwd, tmp);
for(k=strlen(cwd); --k>0;)
if(cwd[k] == '/')
break;
cwd[k] = 0;
goto loop;
}
if(!S_ISDIR(st.st_mode))
return(NULL);
}
}
return(to);
}
int calc_item(struct dir *par, char *path, char *name) {
char tmp[PATH_MAX];
struct dir *t, *d;
@ -400,12 +285,12 @@ int calc_key(int ch) {
void calc_process() {
char tmp[PATH_MAX];
char *tmp;
struct stat fs;
struct dir *t;
/* check root directory */
if(rpath(curpath, tmp) == NULL || lstat(tmp, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
if((tmp = path_real(curpath)) == NULL || lstat(tmp, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
failed = 1;
strcpy(errmsg, "Directory not found");
goto fail;
@ -416,8 +301,7 @@ void calc_process() {
t->size = fs.st_blocks * S_BLKSIZE;
t->asize = fs.st_size;
t->flags |= FF_DIR;
t->name = (char *) malloc(strlen(tmp)+1);
strcpy(t->name, orig ? orig->name : tmp);
t->name = tmp;
root = t;
curdev = fs.st_dev;

243
src/path.c Обычный файл
Просмотреть файл

@ -0,0 +1,243 @@
/* ncdu - NCurses Disk Usage
Copyright (c) 2007-2009 Yoran Heling
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "path.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <limits.h>
#include <dirent.h>
#ifndef LINK_MAX
# ifdef _POSIX_LINK_MAX
# define LINK_MAX _POSIX_LINK_MAX
# else
# define LINK_MAX 32
# endif
#endif
#define RPATH_CNKSZ 256
/* splits a path into components and does a bit of cannonicalization.
a pointer to a reversed array of components is stored in res and the
number of components is returned.
cur is modified, and res has to be free()d after use */
int path_split(char *cur, char ***res) {
char **old;
int i, j, n;
cur += strspn(cur, "/");
n = strlen(cur);
/* replace slashes with zeros */
for(i=j=0; i<n; i++)
if(cur[i] == '/') {
cur[i] = 0;
if(cur[i-1] != 0)
j++;
}
/* create array of the components */
old = malloc((j+1)*sizeof(char *));
*res = malloc((j+1)*sizeof(char *));
for(i=j=0; i<n; i++)
if(i == 0 || (cur[i-1] == 0 && cur[i] != 0))
old[j++] = cur+i;
/* re-order and remove parts */
for(i=n=0; --j>=0; ) {
if(!strcmp(old[j], "..")) {
n++;
continue;
}
if(!strcmp(old[j], "."))
continue;
if(n) {
n--;
continue;
}
(*res)[i++] = old[j];
}
free(old);
return i;
}
/* copies path and prepends cwd if needed, to ensure an absolute path
return value has to be free()'d manually */
char *path_absolute(const char *path) {
int i, n;
char *ret;
/* not an absolute path? prepend cwd */
if(path[0] != '/') {
n = RPATH_CNKSZ;
ret = malloc(n);
errno = 0;
while(!getcwd(ret, n) && errno == ERANGE) {
n += RPATH_CNKSZ;
ret = realloc(ret, n);
errno = 0;
}
if(errno) {
free(ret);
return NULL;
}
i = strlen(path) + strlen(ret) + 2;
if(i > n)
ret = realloc(ret, i);
strcat(ret, "/");
strcat(ret, path);
/* otherwise, just make a copy */
} else {
ret = malloc(strlen(path)+1);
strcpy(ret, path);
}
return ret;
}
/* NOTE: cwd and the memory cur points to are unreliable after calling this function */
char *path_real_rec(char *cur, int *links) {
int i, j, n, tmpl, lnkl = 0;
char **arr, *tmp, *lnk, *ret = NULL;
tmpl = strlen(cur);
tmp = malloc(tmpl);
/* split path */
i = path_split(cur, &arr);
/* re-create full path */
strcpy(tmp, "/");
if(i > 0) {
j = 1;
lnkl = RPATH_CNKSZ;
lnk = malloc(lnkl);
if(chdir("/") < 0)
goto path_real_done;
}
while(--i>=0) {
if(arr[i][0] == 0)
continue;
/* check for symlink */
while((n = readlink(arr[i], lnk, lnkl)) == lnkl || (n < 0 && errno == ERANGE)) {
lnkl += RPATH_CNKSZ;
lnk = realloc(lnk, lnkl);
}
if(n < 0 && errno != EINVAL)
goto path_real_done;
if(n > 0) {
if(++*links > LINK_MAX) {
errno = ELOOP;
goto path_real_done;
}
lnk[n] = 0;
/* create new path and call path_real_rec() again */
if(lnk[0] != '/') {
n += strlen(tmp) + 1;
if(tmpl < n) {
tmpl = n;
tmp = realloc(tmp, tmpl);
}
strcat(tmp, lnk);
} else
strcpy(tmp, lnk);
ret = path_real_rec(tmp, links);
goto path_real_done;
}
/* not a symlink, append component and go to the next part */
strcat(tmp, arr[i]);
if(i) {
if(chdir(arr[i]) < 0)
goto path_real_done;
strcat(tmp, "/");
}
}
ret = tmp;
path_real_done:
if(ret != tmp)
free(tmp);
if(lnkl > 0)
free(lnk);
free(arr);
return ret;
}
char *path_real(const char *orig) {
int links = 0;
char *tmp, *ret;
DIR *d;
if(orig == NULL)
return NULL;
if((d = opendir(".")) == NULL)
return NULL;
tmp = path_absolute(orig);
ret = path_real_rec(tmp, &links);
free(tmp);
fchdir(dirfd(d));
closedir(d);
return ret;
}
int path_chdir(const char *path) {
char **arr, *cur;
int i, r = -1;
DIR *d;
if((d = opendir(".")) == NULL)
return -1;
if((cur = path_absolute(path)) == NULL)
return -1;
i = path_split(cur, &arr);
if(chdir("/") < 0)
goto path_chdir_done;
while(--i >= 0)
if(chdir(arr[i]) < 0)
goto path_chdir_done;
r = 0;
path_chdir_done:
if(r < 0)
fchdir(dirfd(d));
free(cur);
free(arr);
return r;
}

47
src/path.h Обычный файл
Просмотреть файл

@ -0,0 +1,47 @@
/* ncdu - NCurses Disk Usage
Copyright (c) 2007-2009 Yoran Heling
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
path.c reimplements realpath() and chdir(), both functions accept
arbitrary long path names not limited by PATH_MAX.
Caveats/bugs:
- path_real uses chdir(), so it's not thread safe
- Process requires +x access for all directory components
- Potentionally slow
- Doesn't check return value of malloc() and realloc()
- path_real doesn't check for the existance of the last component
*/
#ifndef _path_h
#define _path_h
/* path_real reimplements realpath(). The returned string is allocated
by malloc() and should be manually free()d by the programmer. */
extern char *path_real(const char *);
/* works exactly the same as chdir() */
extern int path_chdir(const char *);
#endif