/* lns - Link Structure Links the content of one directory to another directory, duplicating its structure either by symlinks or hardlinks. Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). Contact: Nokia Corporation (qt-info@nokia.com) */ #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) #define ON_WINDOWS #include "dirent.h" // includes windows.h for us #include #define DIRSEP '\\' #define S_IFDIR _S_IFDIR int link(const char *path1, const char *path2) { return CreateHardLinkA(path2, path1, NULL) ? 0 : -1; } int symlink(const char *path1, const char *path2) { return CreateSymbolicLinkA(path2, path1, 0) ? 0 : -1; } int mkdir(const char *path, int) { return mkdir(path); } #else #define ON_POSIX #include #include #include #include #include #include #define DIRSEP '/' #define MAX_PATH PATH_MAX #endif #include #include #include int verboseness = 1; bool useHardlinks = 0; const char *spath = 0; const char *dpath = 0; long num_files = 0; long num_dirs = 0; long num_failed = 0; long num_files_skipped = 0; long num_dirs_skipped = 0; void usage() { printf("Usage: lns [OPTION]... TARGET_DIRECTORY SOURCE_DIRECTORY\n\n"); printf(" -q quiet\n"); printf(" -h use hardlinks\n"); printf(" -s use symlinks (default)\n"); printf(" -v print '.' for each file and '|' for each directory created\n"); printf(" -vv print name of each linked file and created directory\n"); printf(" -vvv as above, but includes target and source directories, and mode\n"); } void do_link(const char *path1, const char *path2) { if (link(path1, path2)) { #ifdef ON_WINDOWS DWORD res = GetLastError(); if (res == ERROR_ALREADY_EXISTS) #else if (errno == EEXIST) #endif ++num_files_skipped; else ++num_failed; } else { ++num_files; } } void do_symlink(const char *path1, const char *path2) { if (symlink(path1, path2)) { #ifdef ON_WINDOWS DWORD res = GetLastError(); if (res == ERROR_ALREADY_EXISTS) #else if (errno == EEXIST) #endif ++num_files_skipped; else ++num_failed; } else { ++num_files; } } void recreate_structure(char *sd, char *dd) { DIR *sdir = opendir(sd); if (sdir) { int slen = strlen(sd); int dlen = strlen(dd); *(sd+slen) = DIRSEP; *(dd+dlen) = DIRSEP; struct dirent *ent; while ((ent = readdir(sdir)) != NULL) { if (!strcmp(".", ent->d_name) || !strcmp("..", ent->d_name)) continue; strcpy(sd+slen+1, ent->d_name); strcpy(dd+dlen+1, ent->d_name); switch (ent->d_type) { case DT_DIR: if(mkdir(dd, 0777)) { if (errno == ENOENT) { fprintf(stderr, "path not found (%s)\n", dd); ++num_failed; } else ++num_dirs_skipped; } else ++num_dirs; if (verboseness == 2) fputs("|", stdout); else if (verboseness > 2) printf ("[%s]\n", dd); recreate_structure(sd, dd); break; case DT_REG: default: if (useHardlinks) do_link(sd, dd); else do_symlink(sd, dd); if (verboseness == 2) fputs(".", stdout); else if (verboseness > 2) printf ("%c> %s\n", (useHardlinks ? '=' : '-'), dd); break; } } *(sd+slen) = 0; *(dd+dlen) = 0; closedir(sdir); } else { ++num_failed; if (verboseness) { switch(errno) { case EACCES: fprintf(stderr, "permission denied for source '%s'\n", sd); break; #ifdef ON_POSIX case ELOOP: fprintf(stderr, "too many symbolic links were encountered in resolving '%s'\n", sd); break; #endif case ENOENT: fprintf(stderr, "component of '%s' does not name an existing directory\n", sd); break; case ENOTDIR: fprintf(stderr, "component of '%s' is not a directory\n", sd); break; default: fprintf(stderr, "unknown failure '%s' (%d)\n", sd, errno); break; } } } } int main(int argc, char *argv[]) { int i = 1; while (i < argc) { if (!strcmp("-?", argv[i]) || !strcmp("/?", argv[i]) || !strcmp("--help", argv[i])) { usage(); exit(0); } else if (!strcmp("-h", argv[i])) { useHardlinks = 1; } else if (!strcmp("-s", argv[i])) { useHardlinks = 0; } else if (!strcmp("-q", argv[i])) { verboseness = 0; } else if (!strcmp("-v", argv[i])) { verboseness = 2; } else if (!strcmp("-vv", argv[i])) { verboseness = 3; } else if (!strcmp("-vvv", argv[i])) { verboseness = 4; } else { if (!dpath) dpath = argv[i]; else if (!spath) spath = argv[i]; else { fprintf(stderr, "too many arguments!"); exit(1); } } ++i; } if (!spath || !dpath) { fprintf(stderr, "%s no specified!\n", (!dpath ? "target" : "source")); exit(1); } char source_path[MAX_PATH]; char destination_path[MAX_PATH]; strcpy(source_path, spath); strcpy(destination_path, dpath); // Make sure that source exist before we start struct stat st; if (stat(source_path, &st)) { fprintf(stderr, "%s does not exist!\n", source_path); exit(1); } if (!(st.st_mode & S_IFDIR)) { fprintf(stderr, "%s not a directory!\n", source_path); exit(1); } #ifdef ON_WINDOWS if (!useHardlinks) { // Creating symlinks on Windows required the SeCreateSymbolicLinkPrivilege, so we check here if // we've got that. If not, bail out LUID luid; if (!LookupPrivilegeValueA(NULL, "SeCreateSymbolicLinkPrivilege", &luid)) { fprintf(stderr, "couldn't query symlink creation privileges value (%d) (maybe use hardlinks instead?)\n", GetLastError()); exit(2); } HANDLE hToken; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { fprintf(stderr, "couldn't get process token, to check for symlink creation privileges (%d) (maybe use hardlinks instead?)\n", GetLastError()); exit(2); } DWORD buffer_len; GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &buffer_len); TOKEN_PRIVILEGES *tPrivileges = (TOKEN_PRIVILEGES *)malloc(buffer_len); if (!GetTokenInformation(hToken, TokenPrivileges, tPrivileges, buffer_len, &buffer_len)) { fprintf(stderr, "couldn't get process token list, to check for symlink creation privileges (%d) (maybe use hardlinks instead?)\n", GetLastError()); exit(2); } bool hasPrivilege = false; for(DWORD i = 0; i < tPrivileges->PrivilegeCount; ++i) { LUID_AND_ATTRIBUTES *pla = &tPrivileges->Privileges[i]; if (pla->Luid.HighPart == luid.HighPart && pla->Luid.LowPart == luid.LowPart && pla->Attributes != SE_PRIVILEGE_REMOVED) hasPrivilege = true; } free(tPrivileges); if (!hasPrivilege) { fprintf(stderr, "You do not have sufficient privilege to create symlinks.\nEither run process as user with SeCreateSymbolicLinkPrivilege, or use hardlinks instead.\n"); exit(2); } } #endif if (verboseness > 3) printf("source: %s\ntarget: %s\nmode: %s\n---------------\n", source_path, destination_path, (useHardlinks ? "hardlinks" : "symlinks")); // Recreate the structure now.. if(mkdir(destination_path, 0777)) { if (errno == ENOENT) { fprintf(stderr, "path not found (%s)\n", destination_path); ++num_failed; } else ++num_dirs_skipped; } else { ++num_dirs; } #ifdef ON_POSIX // We make source ABS for Posix when creating symlinks, since the source is then relative // to the symlink and not CWD. So, this simplifies the recursive recreate_structure function // considerably. if (!useHardlinks) { char temp_path[MAX_PATH]; if(getcwd(temp_path, PATH_MAX) == NULL) { fprintf(stderr, "couldn't fetch cwd for abs path creation! (%d)\n", errno); exit(1); } int splen = strlen(temp_path); int slen = strlen(spath); if (slen + splen + 2 > PATH_MAX) { fprintf(stderr, "unresolved source path exceeds PATH_MAX!\n"); exit(1); } *(temp_path + splen) = '/'; strcpy(temp_path + splen + 1, spath); if (realpath(temp_path, source_path) == NULL) { fprintf(stderr, "couldn't resolve path for source %s! (%d)\n", temp_path, errno); exit(1); } } #endif recreate_structure(source_path, destination_path); if (verboseness) { if (verboseness == 2) fputs("\n", stdout); printf("%ld %s, and %ld directories created.\n%ld %s, and %ld directories skipped.", num_files, (useHardlinks ? "hardlinks" : "symlinks"), num_dirs, num_files_skipped, (useHardlinks ? "hardlinks" : "symlinks"), num_dirs_skipped); fflush(stdout); } if (num_failed) fprintf(stderr, " %ld files failed to link!", num_failed); if (verboseness) fputs("\n", stdout); return 0; }