История изменений
Исправление firkax, (текущая версия) :
Программа для фикса симлинков как тебе надо (код ниже). Сохраняешь как fixlinks.c, компилируешь (gcc -Wall -o fixlinks fixlinks.c
), запускаешь:
./fixlinks --relative /новый/путь /старый/путь
./fixlinks --relative /home/newuser /home/username1
./fixlinks --absolute /home/newuser /home/username1
/* (c) firk 2024; feel free to use this code for anything */
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
static char const *basepath, *oldbasepath;
static size_t bplen, obplen, inclen;
static int is_relative;
typedef struct { char *s; size_t len, sz; } dynstr;
static dynstr path, lpath, nlpath;
static void dynstr_need_len(dynstr *ds, size_t len) {
size_t newsz;
if(len<ds->sz) return;
newsz = len*2+1000;
if(newsz<=len || !(ds->s = realloc(ds->s,newsz))) { fprintf(stderr, "out of memory\n"); exit(-1); }
ds->sz = newsz;
}
static char const * fix_slashes(char const *title, char *p) {
size_t len;
char *dst;
len = strlen(p);
if(!len || *p!='/') { fprintf(stderr, "%s should be absolute path\n", title); exit(-1); }
if(strstr(p,"//")) { fprintf(stderr, "%s should not contain \"//\"\n", title); exit(-1); }
len += 2;
if(len<=2 || !(dst = malloc(len))) { fprintf(stderr, "out of memory"); exit(-1); }
len -= 2;
bcopy(p, dst, len);
if(dst[len-1]!='/') dst[len++] = '/';
dst[len] = 0;
return dst;
}
static void path_append_dir(size_t savelen, char const *name) {
size_t add;
size_t len;
add = strlen(name);
len = savelen + add + 1;
if(len<=savelen || len<=add || len<=1) { fprintf(stderr, "out of memory\n"); exit(-1); }
dynstr_need_len(&path, len);
bcopy(name, path.s+savelen, add);
path.s[savelen+add] = '/';
path.s[savelen+add+1] = 0;
path.len = savelen+add+1;
}
extern char const * my_readlinkat(int dfd, char const *name, size_t sz) {
ssize_t rsz;
if(sz<256) sz = 256;
dynstr_need_len(&lpath, sz);
while(1) {
if((rsz = readlinkat(dfd, name, lpath.s, lpath.sz))<0) return NULL;
if(lpath.sz>(size_t)rsz) {
lpath.s[rsz] = 0;
lpath.len = rsz;
return lpath.s;
}
dynstr_need_len(&lpath, lpath.sz);
}
}
static void handle_symlink(int dfd, char const *n, struct stat const *sb) {
char const *lnk, *pp, *pl, *nl;
char *newlink;
size_t lnklen, newlen, level, j;
lnk = my_readlinkat(dfd, n, (sb->st_size>256)?sb->st_size:256);
if(!lnk) {
fprintf(stderr, "readlink(%s%s) error %d (%s)\n", path.s, n, errno, strerror(errno));
return;
}
lnklen = strlen(lnk);
if(*lnk!='/' || strncmp(lnk,oldbasepath,obplen) || lnk[obplen]!='/' && lnk[obplen]) {
printf("not changing symlink \"%s%s\" -> \"%s\"\n", path.s, n, lnk);
fflush(stdout);
return;
}
if(!is_relative) {
newlen = lnklen + inclen;
if(newlen<lnklen || newlen<inclen) { fprintf(stderr, "out of memory"); exit(-1); }
dynstr_need_len(&nlpath, newlen);
bcopy(basepath, nlpath.s, bplen);
bcopy(lnk+obplen, nlpath.s+bplen, lnklen-obplen+1);
nl = nlpath.s;
} else {
pp = path.s+bplen;
pl = lnk+obplen;
while(1) {
assert(*pp=='/');
if(!*pl || *pl=='/' && !pl[1]) break;
assert(*pl=='/');
for(j=1; pp[j] && pl[j] && pp[j]!='/' && pl[j]!='/' && pp[j]==pl[j]; j++);
if(pp[j]!='/' || pl[j]!='/' && pl[j]) break;
pp+=j; pl+=j;
}
assert(*pp=='/');
assert(!*pl || *pl=='/');
for(pp++,level=0; *pp; pp++) if(*pp=='/') level++;
if(!level) {
if(!*pl || !pl[1]) nl = "."; else nl = pl+1;
} else {
lnklen = strlen(pl);
newlen = lnklen + level*3;
if(newlen<lnklen || (newlen-lnklen)/3!=level) { fprintf(stderr, "out of memory"); exit(-1); }
dynstr_need_len(&nlpath, newlen);
for(j=0;j<level;j++) { nlpath.s[j*3] = '.'; nlpath.s[j*3+1] = '.'; nlpath.s[j*3+2] = '/'; }
bcopy(pl, nlpath.s+level*3-1, lnklen+1);
nl = nlpath.s;
}
}
printf("replacing symlink \"%s%s\" -> \"%s\" -> \"%s\"\n", path.s, n, lnk, nl);
fflush(stdout);
if(unlinkat(dfd, n, 0)<0) { fprintf(stderr, "unlink(%s%s) error %d (%s)\n", path.s, n, errno, strerror(errno)); return; }
if(symlinkat(nl, dfd, n)<0) { fprintf(stderr, "symlink(..., %s%s) error %d (%s)\n", path.s, n, errno, strerror(errno)); return; }
}
static void recurse_dir(int dfd, int level) {
DIR *dp;
struct dirent *de;
size_t savelen;
char const *n;
struct stat sb;
int fd;
savelen = path.len;
if(!(dp=fdopendir(dfd))) {
fprintf(stderr, "fdopendir(%s) error %d (%s)\n", path.s, errno, strerror(errno));
close(dfd);
return;
}
while(errno=0,de=readdir(dp)) {
n = de->d_name;
if(*n=='.' && (!n[1] || n[1]=='.' && !n[2])) continue;
if(fstatat(dfd, n, &sb, AT_SYMLINK_NOFOLLOW)<0) { fprintf(stderr, "lstat(%s%s) error %d (%s)\n", path.s, n, errno, strerror(errno)); continue; }
if(S_ISLNK(sb.st_mode)) { handle_symlink(dfd,n,&sb); continue; }
if(S_ISDIR(sb.st_mode)) {
if((fd = openat(dfd, n, O_DIRECTORY|O_NOFOLLOW))<0) { fprintf(stderr, "open dir \"%s%s\" error %d (%s)\n", path.s, n, errno, strerror(errno)); return; }
path_append_dir(savelen, n);
recurse_dir(fd, level+1);
path.s[path.len = savelen] = 0;
}
}
if(errno) fprintf(stderr, "readdir(%s) error %d (%s)\n", path.s, errno, strerror(errno));
closedir(dp);
}
int main(int argc, char **argv) {
int fd;
size_t len;
if(argc!=4) {
usage:
fprintf(stderr, "Usage: fixlinks {--absolute|--relative} <basepath> <oldbasepath>\n");
return -1;
}
if(!strcmp(argv[1],"--absolute")) is_relative = 0;
else if(!strcmp(argv[1],"--relative")) is_relative = 1;
else goto usage;
basepath = fix_slashes("basepath", argv[2]);
oldbasepath = fix_slashes("oldbasepath", argv[3]);
bplen = strlen(basepath)-1;
obplen = strlen(oldbasepath)-1;
inclen = (obplen<bplen)?bplen-obplen:0;
if(!(path.s = strdup(basepath))) { fprintf(stderr, "out of memory\n"); return -1; }
path.len = strlen(path.s);
path.sz = path.len + 1;
if((fd = open(basepath, O_DIRECTORY|O_NOFOLLOW))<0) {
fprintf(stderr, "open dir \"%s\" error %d (%s)\n", basepath, errno, strerror(errno));
return -1;
}
recurse_dir(fd,0);
return 0;
}
Исправление firkax, :
Программа для фикса симлинков как тебе надо (код ниже). Сохраняешь как fixlinks.c, компилируешь (gcc -Wall -o fixlinks fixlinks.c
), запускаешь:
./fixlinks --relative /новый/путь /старый/путь
./fixlinks --relative /home/newuser /home/username1
/* (c) firk 2024; feel free to use this code for anything */
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
static char const *basepath, *oldbasepath;
static size_t bplen, obplen, inclen;
static int is_relative;
typedef struct { char *s; size_t len, sz; } dynstr;
static dynstr path, lpath, nlpath;
static void dynstr_need_len(dynstr *ds, size_t len) {
size_t newsz;
if(len<ds->sz) return;
newsz = len*2+1000;
if(newsz<=len || !(ds->s = realloc(ds->s,newsz))) { fprintf(stderr, "out of memory\n"); exit(-1); }
ds->sz = newsz;
}
static char const * fix_slashes(char const *title, char *p) {
size_t len;
char *dst;
len = strlen(p);
if(!len || *p!='/') { fprintf(stderr, "%s should be absolute path\n", title); exit(-1); }
if(strstr(p,"//")) { fprintf(stderr, "%s should not contain \"//\"\n", title); exit(-1); }
len += 2;
if(len<=2 || !(dst = malloc(len))) { fprintf(stderr, "out of memory"); exit(-1); }
len -= 2;
bcopy(p, dst, len);
if(dst[len-1]!='/') dst[len++] = '/';
dst[len] = 0;
return dst;
}
static void path_append_dir(size_t savelen, char const *name) {
size_t add;
size_t len;
add = strlen(name);
len = savelen + add + 1;
if(len<=savelen || len<=add || len<=1) { fprintf(stderr, "out of memory\n"); exit(-1); }
dynstr_need_len(&path, len);
bcopy(name, path.s+savelen, add);
path.s[savelen+add] = '/';
path.s[savelen+add+1] = 0;
path.len = savelen+add+1;
}
extern char const * my_readlinkat(int dfd, char const *name, size_t sz) {
ssize_t rsz;
if(sz<256) sz = 256;
dynstr_need_len(&lpath, sz);
while(1) {
if((rsz = readlinkat(dfd, name, lpath.s, lpath.sz))<0) return NULL;
if(lpath.sz>(size_t)rsz) {
lpath.s[rsz] = 0;
lpath.len = rsz;
return lpath.s;
}
dynstr_need_len(&lpath, lpath.sz);
}
}
static void handle_symlink(int dfd, char const *n, struct stat const *sb) {
char const *lnk, *pp, *pl, *nl;
char *newlink;
size_t lnklen, newlen, level, j;
lnk = my_readlinkat(dfd, n, (sb->st_size>256)?sb->st_size:256);
if(!lnk) {
fprintf(stderr, "readlink(%s%s) error %d (%s)\n", path.s, n, errno, strerror(errno));
return;
}
lnklen = strlen(lnk);
if(*lnk!='/' || strncmp(lnk,oldbasepath,obplen) || lnk[obplen]!='/' && lnk[obplen]) {
printf("not changing symlink \"%s%s\" -> \"%s\"\n", path.s, n, lnk);
fflush(stdout);
return;
}
if(!is_relative) {
newlen = lnklen + inclen;
if(newlen<lnklen || newlen<inclen) { fprintf(stderr, "out of memory"); exit(-1); }
dynstr_need_len(&nlpath, newlen);
bcopy(basepath, nlpath.s, bplen);
bcopy(lnk+obplen, nlpath.s+bplen, lnklen-obplen+1);
nl = nlpath.s;
} else {
pp = path.s+bplen;
pl = lnk+obplen;
while(1) {
assert(*pp=='/');
if(!*pl || *pl=='/' && !pl[1]) break;
assert(*pl=='/');
for(j=1; pp[j] && pl[j] && pp[j]!='/' && pl[j]!='/' && pp[j]==pl[j]; j++);
if(pp[j]!='/' || pl[j]!='/' && pl[j]) break;
pp+=j; pl+=j;
}
assert(*pp=='/');
assert(!*pl || *pl=='/');
for(pp++,level=0; *pp; pp++) if(*pp=='/') level++;
if(!level) {
if(!*pl || !pl[1]) nl = "."; else nl = pl+1;
} else {
lnklen = strlen(pl);
newlen = lnklen + level*3;
if(newlen<lnklen || (newlen-lnklen)/3!=level) { fprintf(stderr, "out of memory"); exit(-1); }
dynstr_need_len(&nlpath, newlen);
for(j=0;j<level;j++) { nlpath.s[j*3] = '.'; nlpath.s[j*3+1] = '.'; nlpath.s[j*3+2] = '/'; }
bcopy(pl, nlpath.s+level*3-1, lnklen+1);
nl = nlpath.s;
}
}
printf("replacing symlink \"%s%s\" -> \"%s\" -> \"%s\"\n", path.s, n, lnk, nl);
fflush(stdout);
if(unlinkat(dfd, n, 0)<0) { fprintf(stderr, "unlink(%s%s) error %d (%s)\n", path.s, n, errno, strerror(errno)); return; }
if(symlinkat(nl, dfd, n)<0) { fprintf(stderr, "symlink(..., %s%s) error %d (%s)\n", path.s, n, errno, strerror(errno)); return; }
}
static void recurse_dir(int dfd, int level) {
DIR *dp;
struct dirent *de;
size_t savelen;
char const *n;
struct stat sb;
int fd;
savelen = path.len;
if(!(dp=fdopendir(dfd))) {
fprintf(stderr, "fdopendir(%s) error %d (%s)\n", path.s, errno, strerror(errno));
close(dfd);
return;
}
while(errno=0,de=readdir(dp)) {
n = de->d_name;
if(*n=='.' && (!n[1] || n[1]=='.' && !n[2])) continue;
if(fstatat(dfd, n, &sb, AT_SYMLINK_NOFOLLOW)<0) { fprintf(stderr, "lstat(%s%s) error %d (%s)\n", path.s, n, errno, strerror(errno)); continue; }
if(S_ISLNK(sb.st_mode)) { handle_symlink(dfd,n,&sb); continue; }
if(S_ISDIR(sb.st_mode)) {
if((fd = openat(dfd, n, O_DIRECTORY|O_NOFOLLOW))<0) { fprintf(stderr, "open dir \"%s%s\" error %d (%s)\n", path.s, n, errno, strerror(errno)); return; }
path_append_dir(savelen, n);
recurse_dir(fd, level+1);
path.s[path.len = savelen] = 0;
}
}
if(errno) fprintf(stderr, "readdir(%s) error %d (%s)\n", path.s, errno, strerror(errno));
closedir(dp);
}
int main(int argc, char **argv) {
int fd;
size_t len;
if(argc!=4) {
usage:
fprintf(stderr, "Usage: fixlinks {--absolute|--relative} <basepath> <oldbasepath>\n");
return -1;
}
if(!strcmp(argv[1],"--absolute")) is_relative = 0;
else if(!strcmp(argv[1],"--relative")) is_relative = 1;
else goto usage;
basepath = fix_slashes("basepath", argv[2]);
oldbasepath = fix_slashes("oldbasepath", argv[3]);
bplen = strlen(basepath)-1;
obplen = strlen(oldbasepath)-1;
inclen = (obplen<bplen)?bplen-obplen:0;
if(!(path.s = strdup(basepath))) { fprintf(stderr, "out of memory\n"); return -1; }
path.len = strlen(path.s);
path.sz = path.len + 1;
if((fd = open(basepath, O_DIRECTORY|O_NOFOLLOW))<0) {
fprintf(stderr, "open dir \"%s\" error %d (%s)\n", basepath, errno, strerror(errno));
return -1;
}
recurse_dir(fd,0);
return 0;
}
Исходная версия firkax, :
Программа для фикса симлинков как тебе надо (код ниже). Сохраняешь как fixlinks.c, компилируешь (gcc -Wall -o fixlinks fixlinks.c
), запускаешь:
./fixlinks --relative /новый/путь /старый/путь
/* (c) firk 2024; feel free to use this code for anything */
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
static char const *basepath, *oldbasepath;
static size_t bplen, obplen, inclen;
static int is_relative;
typedef struct { char *s; size_t len, sz; } dynstr;
static dynstr path, lpath, nlpath;
static void dynstr_need_len(dynstr *ds, size_t len) {
size_t newsz;
if(len<ds->sz) return;
newsz = len*2+1000;
if(newsz<=len || !(ds->s = realloc(ds->s,newsz))) { fprintf(stderr, "out of memory\n"); exit(-1); }
ds->sz = newsz;
}
static char const * fix_slashes(char const *title, char *p) {
size_t len;
char *dst;
len = strlen(p);
if(!len || *p!='/') { fprintf(stderr, "%s should be absolute path\n", title); exit(-1); }
if(strstr(p,"//")) { fprintf(stderr, "%s should not contain \"//\"\n", title); exit(-1); }
len += 2;
if(len<=2 || !(dst = malloc(len))) { fprintf(stderr, "out of memory"); exit(-1); }
len -= 2;
bcopy(p, dst, len);
if(dst[len-1]!='/') dst[len++] = '/';
dst[len] = 0;
return dst;
}
static void path_append_dir(size_t savelen, char const *name) {
size_t add;
size_t len;
add = strlen(name);
len = savelen + add + 1;
if(len<=savelen || len<=add || len<=1) { fprintf(stderr, "out of memory\n"); exit(-1); }
dynstr_need_len(&path, len);
bcopy(name, path.s+savelen, add);
path.s[savelen+add] = '/';
path.s[savelen+add+1] = 0;
path.len = savelen+add+1;
}
extern char const * my_readlinkat(int dfd, char const *name, size_t sz) {
ssize_t rsz;
if(sz<256) sz = 256;
dynstr_need_len(&lpath, sz);
while(1) {
if((rsz = readlinkat(dfd, name, lpath.s, lpath.sz))<0) return NULL;
if(lpath.sz>(size_t)rsz) {
lpath.s[rsz] = 0;
lpath.len = rsz;
return lpath.s;
}
dynstr_need_len(&lpath, lpath.sz);
}
}
static void handle_symlink(int dfd, char const *n, struct stat const *sb) {
char const *lnk, *pp, *pl, *nl;
char *newlink;
size_t lnklen, newlen, level, j;
lnk = my_readlinkat(dfd, n, (sb->st_size>256)?sb->st_size:256);
if(!lnk) {
fprintf(stderr, "readlink(%s%s) error %d (%s)\n", path.s, n, errno, strerror(errno));
return;
}
lnklen = strlen(lnk);
if(*lnk!='/' || strncmp(lnk,oldbasepath,obplen) || lnk[obplen]!='/' && lnk[obplen]) {
printf("not changing symlink \"%s%s\" -> \"%s\"\n", path.s, n, lnk);
fflush(stdout);
return;
}
if(!is_relative) {
newlen = lnklen + inclen;
if(newlen<lnklen || newlen<inclen) { fprintf(stderr, "out of memory"); exit(-1); }
dynstr_need_len(&nlpath, newlen);
bcopy(basepath, nlpath.s, bplen);
bcopy(lnk+obplen, nlpath.s+bplen, lnklen-obplen+1);
nl = nlpath.s;
} else {
pp = path.s+bplen;
pl = lnk+obplen;
while(1) {
assert(*pp=='/');
if(!*pl || *pl=='/' && !pl[1]) break;
assert(*pl=='/');
for(j=1; pp[j] && pl[j] && pp[j]!='/' && pl[j]!='/' && pp[j]==pl[j]; j++);
if(pp[j]!='/' || pl[j]!='/' && pl[j]) break;
pp+=j; pl+=j;
}
assert(*pp=='/');
assert(!*pl || *pl=='/');
for(pp++,level=0; *pp; pp++) if(*pp=='/') level++;
if(!level) {
if(!*pl || !pl[1]) nl = "."; else nl = pl+1;
} else {
lnklen = strlen(pl);
newlen = lnklen + level*3;
if(newlen<lnklen || (newlen-lnklen)/3!=level) { fprintf(stderr, "out of memory"); exit(-1); }
dynstr_need_len(&nlpath, newlen);
for(j=0;j<level;j++) { nlpath.s[j*3] = '.'; nlpath.s[j*3+1] = '.'; nlpath.s[j*3+2] = '/'; }
bcopy(pl, nlpath.s+level*3-1, lnklen+1);
nl = nlpath.s;
}
}
printf("replacing symlink \"%s%s\" -> \"%s\" -> \"%s\"\n", path.s, n, lnk, nl);
fflush(stdout);
if(unlinkat(dfd, n, 0)<0) { fprintf(stderr, "unlink(%s%s) error %d (%s)\n", path.s, n, errno, strerror(errno)); return; }
if(symlinkat(nl, dfd, n)<0) { fprintf(stderr, "symlink(..., %s%s) error %d (%s)\n", path.s, n, errno, strerror(errno)); return; }
}
static void recurse_dir(int dfd, int level) {
DIR *dp;
struct dirent *de;
size_t savelen;
char const *n;
struct stat sb;
int fd;
savelen = path.len;
if(!(dp=fdopendir(dfd))) {
fprintf(stderr, "fdopendir(%s) error %d (%s)\n", path.s, errno, strerror(errno));
close(dfd);
return;
}
while(errno=0,de=readdir(dp)) {
n = de->d_name;
if(*n=='.' && (!n[1] || n[1]=='.' && !n[2])) continue;
if(fstatat(dfd, n, &sb, AT_SYMLINK_NOFOLLOW)<0) { fprintf(stderr, "lstat(%s%s) error %d (%s)\n", path.s, n, errno, strerror(errno)); continue; }
if(S_ISLNK(sb.st_mode)) { handle_symlink(dfd,n,&sb); continue; }
if(S_ISDIR(sb.st_mode)) {
if((fd = openat(dfd, n, O_DIRECTORY|O_NOFOLLOW))<0) { fprintf(stderr, "open dir \"%s%s\" error %d (%s)\n", path.s, n, errno, strerror(errno)); return; }
path_append_dir(savelen, n);
recurse_dir(fd, level+1);
path.s[path.len = savelen] = 0;
}
}
if(errno) fprintf(stderr, "readdir(%s) error %d (%s)\n", path.s, errno, strerror(errno));
closedir(dp);
}
int main(int argc, char **argv) {
int fd;
size_t len;
if(argc!=4) {
usage:
fprintf(stderr, "Usage: fixlinks {--absolute|--relative} <basepath> <oldbasepath>\n");
return -1;
}
if(!strcmp(argv[1],"--absolute")) is_relative = 0;
else if(!strcmp(argv[1],"--relative")) is_relative = 1;
else goto usage;
basepath = fix_slashes("basepath", argv[2]);
oldbasepath = fix_slashes("oldbasepath", argv[3]);
bplen = strlen(basepath)-1;
obplen = strlen(oldbasepath)-1;
inclen = (obplen<bplen)?bplen-obplen:0;
if(!(path.s = strdup(basepath))) { fprintf(stderr, "out of memory\n"); return -1; }
path.len = strlen(path.s);
path.sz = path.len + 1;
if((fd = open(basepath, O_DIRECTORY|O_NOFOLLOW))<0) {
fprintf(stderr, "open dir \"%s\" error %d (%s)\n", basepath, errno, strerror(errno));
return -1;
}
recurse_dir(fd,0);
return 0;
}