Files
Arquivotheca.SunOS-4.1.4/usr.etc/tfs/libtfs/searchlink.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

886 lines
18 KiB
C

#ifndef lint
static char sccsid[] = "@(#)searchlink.c 1.1 94/10/31 Copyr 1988 Sun Micro";
#endif
/*
* Copyright (c) 1987 Sun Microsystems, Inc.
*/
/*
* Library routines to read/write .tfs_info files, which contain
* searchlinks, backlinks, and whiteout information. These routines
* are called by the tfsd and by preserve, which creates rev-trees
* behind the tfsd's back, and so needs to look at .tfs_info files
* directly.
*/
#include <nse/stdio.h>
#include <strings.h>
#include <errno.h>
#include <sys/stat.h>
#include <nse/param.h>
#include <nse/util.h>
#include <nse/searchlink.h>
#include <nse/file_err.h>
#define STRIP_EOLN(string) string[strlen(string) - 1] = '\0';
static Nse_err tfs_file_move();
static Nse_err tfs_file_read();
static Nse_err do_file_read();
static Nse_err tfs_file_write();
static Nse_err write_tfs_file_contents();
static Nse_err tfs_fgets();
/*
* Returns the searchlink of 'directory' in the string 'name' (which should
* be MAXPATHLEN chars long.) If there is no searchlink for the directory,
* ENOENT is returned as the error code.
*/
Nse_err
nse_get_searchlink(directory, name)
char *directory;
char *name;
{
Nse_err result;
result = tfs_file_read(directory, name,
(int *) NULL, (Nse_whiteout *) NULL,
(Nse_whiteout *) NULL, (FILE **) NULL);
if ((result == NULL) && (name[0] == '\0')) {
errno = ENOENT;
result = nse_err_format_errno("Tfs read of searchlink in directory \"%s\"",
directory);
}
return (result);
}
/*
* Set searchlink of 'directory' to 'name', regardless of whether or not a
* searchlink already exists for 'directory'.
*/
Nse_err
nse_set_searchlink(directory, name)
char *directory;
char *name;
{
FILE *file;
char old_link[MAXPATHLEN];
int blct;
Nse_whiteout bl;
Nse_whiteout wo;
Nse_err result;
if (result = tfs_file_read(directory, old_link, &blct, &bl,
&wo, &file)) {
return (result);
}
result = tfs_file_write(directory, name, blct, bl, wo, file);
nse_dispose_whiteout(bl);
nse_dispose_whiteout(wo);
return (result);
}
/*
* Set the searchlink of 'directory' to 'name' only if 'directory' does not
* already have a searchlink. An error code is returned in two cases:
* either the directory already has a searchlink which has a value
* different from 'name', in which case NSE_SL_ALREADY_SET_DIFF is
* returned and 'old_name' contains the old searchlink; or an error
* occurred, in which case the intro(2) error code is returned.
*
* The boolean pointed at by `changedp' is set to indicate if the
* searchlink changed.
*/
Nse_err
nse_set_cond_searchlink(directory, name, old_name, changedp)
char *directory;
char *name;
char *old_name;
bool_t *changedp;
{
FILE *file;
Nse_whiteout wo;
int blct;
Nse_whiteout bl;
Nse_err result;
*changedp = FALSE;
if (result = tfs_file_read(directory, old_name, &blct, &bl,
&wo, &file)) {
return (result);
}
if (old_name[0] != '\0') {
fclose(file);
if (!NSE_STREQ(name, old_name)) {
result = nse_err_format(NSE_SL_ALREADY_SET_DIFF,
directory);
}
} else {
*changedp = TRUE;
result = tfs_file_write(directory, name, blct, bl, wo, file);
}
nse_dispose_whiteout(bl);
nse_dispose_whiteout(wo);
return (result);
}
/*
* Returns list of white-out entries in directory.
*/
Nse_err
nse_get_whiteout(directory, wop)
char *directory;
Nse_whiteout *wop;
{
char searchlink[MAXPATHLEN];
return tfs_file_read(directory, searchlink, (int *) NULL,
(Nse_whiteout *) NULL, wop, (FILE **) NULL);
}
/*
* Set a new whiteout entry 'name' in 'directory'. This function is a
* no-op if whiteout entry 'name' is already set for the directory.
*/
Nse_err
nse_set_whiteout(directory, name)
char *directory;
char *name;
{
FILE *file;
char searchlink[MAXPATHLEN];
int blct;
Nse_whiteout bl;
Nse_whiteout wo_first;
Nse_whiteout wo;
int found = 0;
Nse_err result;
if (result = tfs_file_read(directory, searchlink, &blct,
&bl, &wo_first, &file)) {
return (result);
}
wo = wo_first;
while (wo != NULL) {
if (NSE_STREQ(name, wo->name)) {
found = 1;
break;
}
wo = wo->next;
}
if (found) {
fclose(file);
} else {
wo = NSE_NEW(Nse_whiteout);
wo->name = NSE_STRDUP(name);
wo->next = wo_first;
wo_first = wo;
result = tfs_file_write(directory, searchlink, blct, bl,
wo_first, file);
}
nse_dispose_whiteout(bl);
nse_dispose_whiteout(wo_first);
return (result);
}
/*
* Clear whiteout entry 'name' from the whiteout list of 'directory'. If
* 'name' is not in the whiteout list of 'directory', 0 is returned.
*/
Nse_err
nse_clear_whiteout(directory, name)
char *directory;
char *name;
{
FILE *file;
char searchlink[MAXPATHLEN];
int blct;
Nse_whiteout bl;
Nse_whiteout wo_first;
Nse_whiteout wo;
Nse_whiteout wo_prev = NULL;
int found = 0;
Nse_err result;
if (result = tfs_file_read(directory, searchlink, &blct,
&bl, &wo_first, &file)) {
return (result);
}
wo = wo_first;
while (wo != NULL) {
if (NSE_STREQ(name, wo->name)) {
found = 1;
break;
}
wo_prev = wo;
wo = wo->next;
}
if (found) {
if (wo_prev == NULL) {
wo_first = wo->next;
} else {
wo_prev->next = wo->next;
}
free(wo->name);
free((char *) wo);
result = tfs_file_write(directory, searchlink,
blct, bl, wo_first, file);
} else {
fclose(file);
}
nse_dispose_whiteout(bl);
nse_dispose_whiteout(wo_first);
return (result);
}
/*
* Purge all whiteout info from a tfs file and replace search link.
*
*/
Nse_err
nse_purge_whiteout(directory, newlink)
char *directory;
char *newlink;
{
FILE *file;
char searchlink[MAXPATHLEN];
int blct;
Nse_whiteout bl;
Nse_whiteout wo;
Nse_err result;
if (result = tfs_file_read(directory, searchlink, &blct,
&bl, &wo, &file)) {
return result;
}
nse_dispose_whiteout(wo);
result = tfs_file_write(directory, newlink,
blct, bl, (Nse_whiteout) NULL, file);
nse_dispose_whiteout(bl);
return result;
}
/*
* Release the list allocated by nse_get_whiteout.
*/
void
nse_dispose_whiteout(wo)
Nse_whiteout wo;
{
Nse_whiteout wo_next;
while (wo != NULL) {
wo_next = wo->next;
free(wo->name);
free((char *) wo);
wo = wo_next;
}
}
/*
* Returns list of backlink entries in directory.
*/
Nse_err
nse_get_backlink(directory, blp)
char *directory;
Nse_whiteout *blp;
{
char searchlink[MAXPATHLEN];
int blct;
return tfs_file_read(directory, searchlink, &blct, blp,
(Nse_whiteout *) NULL, (FILE **) NULL);
}
/*
* Set a new backlink entry 'name' in 'directory'. This function is a
* no-op if backlink entry 'name' is already set for the directory.
*/
Nse_err
nse_set_backlink(directory, name)
char *directory;
char *name;
{
FILE *file;
char searchlink[MAXPATHLEN];
int blct;
Nse_whiteout bl;
Nse_whiteout bl_first;
Nse_whiteout wo;
int found = 0;
Nse_err result;
if (result = tfs_file_read(directory, searchlink, &blct,
&bl_first, &wo, &file)) {
return (result);
}
bl = bl_first;
while (bl != NULL) {
if (NSE_STREQ(name, bl->name)) {
found = 1;
break;
}
bl = bl->next;
}
if (found) {
fclose(file);
} else {
bl = NSE_NEW(Nse_whiteout);
bl->name = NSE_STRDUP(name);
bl->next = bl_first;
bl_first = bl;
blct++;
result = tfs_file_write(directory, searchlink, blct, bl_first,
wo, file);
}
nse_dispose_whiteout(bl_first);
nse_dispose_whiteout(wo);
return (result);
}
/*
* Clear backlink entry 'name' from the backlink list of 'directory'. If
* 'name' is not in the backlink list of 'directory', 0 is returned.
*/
Nse_err
nse_clear_backlink(directory, name)
char *directory;
char *name;
{
FILE *file;
char searchlink[MAXPATHLEN];
int blct;
Nse_whiteout bl;
Nse_whiteout bl_first;
Nse_whiteout wo;
Nse_whiteout bl_prev = NULL;
int found = 0;
Nse_err result;
if (result = tfs_file_read(directory, searchlink, &blct,
&bl_first, &wo, &file)) {
return (result);
}
bl = bl_first;
while (bl != NULL) {
if (NSE_STREQ(name, bl->name)) {
found = 1;
break;
}
bl_prev = bl;
bl = bl->next;
}
if (found) {
if (bl_prev == NULL) {
bl_first = bl->next;
} else {
bl_prev->next = bl->next;
}
free(bl->name);
free((char *) bl);
blct--;
result = tfs_file_write(directory, searchlink,
blct, bl_first, wo, file);
} else {
fclose(file);
}
nse_dispose_whiteout(bl_first);
nse_dispose_whiteout(wo);
return (result);
}
static FILE *tfs_info_file;
/*
* Return both the searchlink, backlink, and the white-out entries in
* 'directory'.
*/
Nse_err
nse_get_tfs_info(directory, name, blp, wop, will_write_later)
char *directory;
char *name;
Nse_whiteout *blp;
Nse_whiteout *wop;
bool_t will_write_later;
{
FILE **filep;
int blct;
if (will_write_later) {
filep = &tfs_info_file;
} else {
filep = NULL;
}
return tfs_file_read(directory, name, &blct, blp, wop, filep);
}
/*
* Set all tfs info for 'directory'.
*/
Nse_err
nse_set_tfs_info(directory, name, bl_first, wo)
char *directory;
char *name;
Nse_whiteout bl_first;
Nse_whiteout wo;
{
int blct = 0;
Nse_whiteout bl;
Nse_err result;
for (bl = bl_first; bl != NULL; bl = bl->next) {
blct++;
}
result = tfs_file_write(directory, name, blct, bl_first, wo,
tfs_info_file);
nse_dispose_whiteout(bl_first);
nse_dispose_whiteout(wo);
return (result);
}
/*
* Copies all tfs_info except backlinks (searchlink, white-out entries, *and*
* backfiles info) from 'dir1' to 'dir2'.
*/
Nse_err
nse_shift_tfs_info(dir1, dir2)
char *dir1;
char *dir2;
{
char old_link[MAXPATHLEN];
int blct;
Nse_whiteout bl;
Nse_whiteout wo;
Nse_err result;
result = tfs_file_read(dir1, old_link, &blct, &bl, &wo,
(FILE **) NULL);
if (result) {
return result;
}
nse_dispose_whiteout(bl);
result = tfs_file_write(dir2, old_link, 0, (Nse_whiteout) NULL, wo,
(FILE *) NULL);
nse_dispose_whiteout(wo);
if (result) {
return result;
}
result = tfs_file_move(dir1, dir2, NSE_TFS_BACK_FILE, TRUE);
if ((result != NULL) && (result->code == ENOENT)) {
result = NULL;
}
return result;
}
/*
* Copies all tfs_info (searchlink, white-out entries, backlinks, *and*
* backfiles info) from 'dir1' to 'dir2'.
*/
Nse_err
nse_copy_tfs_info(dir1, dir2)
char *dir1;
char *dir2;
{
Nse_err result;
if (result = tfs_file_move(dir1, dir2, NSE_TFS_FILE, TRUE)) {
return result;
}
result = tfs_file_move(dir1, dir2, NSE_TFS_BACK_FILE, TRUE);
if (result == NULL || result->code == ENOENT) {
return NULL;
} else {
return result;
}
}
/*
* Moves all tfs_info (searchlink, white-out entries, backlinks, *and*
* backfiles info) from 'dir1' to 'dir2'.
*/
Nse_err
nse_move_tfs_info(dir1, dir2)
char *dir1;
char *dir2;
{
Nse_err result;
if (result = tfs_file_move(dir1, dir2, NSE_TFS_FILE, FALSE)) {
return result;
}
result = tfs_file_move(dir1, dir2, NSE_TFS_BACK_FILE, FALSE);
if (result == NULL || result->code == ENOENT) {
return NULL;
} else {
return result;
}
}
/*
* Destroy the tfs_info file in 'directory'.
*/
Nse_err
nse_clear_tfs_info(directory)
char *directory;
{
char pathname[MAXPATHLEN];
int r;
strcpy(pathname, directory);
nse_pathcat(pathname, NSE_TFS_FILE);
r = unlink(pathname);
if ((r == 0) || (errno == ENOENT)) {
return NULL;
} else {
return nse_err_format_errno("Unlink of \"%s\"", pathname);
}
}
/*
* Move 'fname' from 'dir1' to 'dir2', or if 'make_copy' is TRUE, copy the
* file.
*/
static Nse_err
tfs_file_move(dir1, dir2, fname, make_copy)
char *dir1;
char *dir2;
char *fname;
bool_t make_copy;
{
char src[MAXPATHLEN];
char dest[MAXPATHLEN];
Nse_err err;
strcpy(src, dir1);
nse_pathcat(src, fname);
strcpy(dest, dir2);
nse_pathcat(dest, fname);
if (make_copy) {
return _nse_copy_file(dest, src, 0);
}
if (rename(src, dest) == 0) {
return NULL;
}
if (errno != EXDEV) {
return nse_err_format_errno("Rename of \"%s\" to \"%s\"",
src, dest);
}
err = _nse_copy_file(dest, src, 1);
if (err == NULL && unlink(src) < 0) {
err = nse_err_format_errno("Unlink of \"%s\"", src);
}
return err;
}
static Nse_err
tfs_file_read(directory, searchlink, blctp, blp, whiteoutp, filep)
char *directory;
char *searchlink;
int *blctp;
Nse_whiteout *blp;
Nse_whiteout *whiteoutp;
FILE **filep;
{
int ntries = 0;
Nse_err err;
/*
* Retry the file read if a stale NFS file handle is encountered.
* We have to do this because it's possible for a process on
* another machine to remove the file after we've opened the file
* for reading but before we've read any data from it. (This
* can only happen if the file is large enough that it is written
* to a tmp file before being renamed to the real name.)
*/
do {
err = do_file_read(directory, searchlink, blctp, blp,
whiteoutp, filep);
/* XXX free *blp && *whiteoutp if err = ESTALE? */
ntries++;
} while (err && ntries < 10 && err->code == ESTALE);
return err;
}
static Nse_err
do_file_read(directory, searchlink, blctp, blp, whiteoutp, filep)
char *directory;
char *searchlink;
int *blctp;
Nse_whiteout *blp;
Nse_whiteout *whiteoutp;
FILE **filep;
{
FILE *tfs_file;
char path[MAXPATHLEN];
char name[MAXPATHLEN];
int first_ch;
char *p;
Nse_whiteout wo;
Nse_whiteout first_wo;
Nse_whiteout prev_wo;
Nse_whiteout bl;
Nse_whiteout first_bl;
Nse_whiteout prev_bl;
int blct;
int i;
int version;
bool_t eof;
Nse_err err = NULL;
strcpy(path, directory);
nse_pathcat(path, NSE_TFS_FILE);
if (filep) {
err = nse_file_open(path, "r+", &tfs_file);
} else {
err = nse_file_open(path, "r", &tfs_file);
}
if (err) {
return err;
}
searchlink[0] = '\0';
if (whiteoutp != NULL) {
*whiteoutp = NULL;
}
if (blp != NULL) {
*blp = NULL;
*blctp = 0;
}
if (tfs_file == NULL) {
if (filep) {
*filep = NULL;
}
return NULL;
}
/*
* Process the 'VERSION X' line (if it exists.)
*/
if (err = nse_fgetc(path, tfs_file, &first_ch)) {
goto done;
}
if (first_ch == NSE_TFS_VERSION_HDR[0]) {
err = tfs_fgets(path, name, MAXPATHLEN, tfs_file, &eof);
if (eof) {
goto done;
}
if ((p = rindex(name, ' ')) == NULL) {
version = 0;
} else {
version = atoi(p + 1);
}
} else {
ungetc(first_ch, tfs_file);
version = 0;
}
if (version > NSE_TFS_VERSION) {
err = nse_err_format(NSE_FILE_BAD_VERS, path, version);
goto done;
}
err = tfs_fgets(path, searchlink, MAXPATHLEN, tfs_file, &eof);
if (eof) {
goto done;
}
STRIP_EOLN(searchlink);
if (NSE_TFS_HAS_BACK(version)) {
err = tfs_fgets(path, name, MAXPATHLEN, tfs_file, &eof);
if (eof) {
goto done;
}
blct = atoi(name);
if (blp == NULL) {
for (i = 0; i < blct; i++) {
err = tfs_fgets(path, name, MAXPATHLEN,
tfs_file, &eof);
if (eof) {
goto done;
}
}
} else {
first_bl = NULL;
bl = NULL;
prev_bl = NULL;
for (i = 0; i < blct; i++) {
err = tfs_fgets(path, name, MAXPATHLEN,
tfs_file, &eof);
if (eof) {
goto done;
}
bl = NSE_NEW(Nse_whiteout);
bl->name = NSE_STRDUP(name);
STRIP_EOLN(bl->name);
bl->next = NULL;
if (prev_bl != NULL) {
prev_bl->next = bl;
}
if (first_bl == NULL) {
first_bl = bl;
}
prev_bl = bl;
}
*blp = first_bl;
*blctp = blct;
}
}
if (whiteoutp == NULL) {
goto done;
}
first_wo = NULL;
wo = NULL;
prev_wo = NULL;
err = tfs_fgets(path, name, MAXPATHLEN, tfs_file, &eof);
while (!eof) {
wo = NSE_NEW(Nse_whiteout);
wo->name = NSE_STRDUP(name);
STRIP_EOLN(wo->name);
wo->next = NULL;
if (prev_wo != NULL) {
prev_wo->next = wo;
}
if (first_wo == NULL) {
first_wo = wo;
}
prev_wo = wo;
err = tfs_fgets(path, name, MAXPATHLEN, tfs_file, &eof);
};
*whiteoutp = first_wo;
done:
if (!err) {
if (filep) {
*filep = tfs_file;
} else {
err = nse_fclose(path, tfs_file);
}
} else {
fclose(tfs_file);
}
return err;
}
/*
* Write out the .tfs_info file in 'directory', with the indicated
* searchlink, back links, and whiteout entries. This routine is careful
* to ensure that the integrity of the file is preserved if the file system
* fills up while trying to write the file.
*/
static Nse_err
tfs_file_write(directory, searchlink, blct, bl, wo, tfs_file)
char *directory;
char *searchlink;
int blct;
Nse_whiteout bl;
Nse_whiteout wo;
FILE *tfs_file;
{
char path[MAXPATHLEN];
long old_size;
long size;
Nse_err err;
strcpy(path, directory);
nse_pathcat(path, NSE_TFS_FILE);
if (err = nse_safe_write_open(path, &tfs_file, &old_size)) {
return err;
}
size = 0;
if (err = write_tfs_file_contents(path, &size, tfs_file, searchlink,
blct, bl, wo)) {
fclose(tfs_file);
return err;
}
return nse_safe_write_close(path, tfs_file, size, old_size);
}
static Nse_err
write_tfs_file_contents(path, sizep, tfs_file, searchlink, blct, bl, wo)
char *path;
long *sizep;
FILE *tfs_file;
char *searchlink;
int blct;
Nse_whiteout bl;
Nse_whiteout wo;
{
int count;
Nse_err err;
if (searchlink != NULL) {
err = nse_fprintf_value(&count, path, tfs_file,
"%s %d\n%s\n%d\n",
NSE_TFS_VERSION_HDR, NSE_TFS_VERSION,
searchlink, blct);
} else {
err = nse_fprintf_value(&count, path, tfs_file,
"%s %d\n\n%d\n",
NSE_TFS_VERSION_HDR, NSE_TFS_VERSION,
blct);
}
if (err) {
return err;
}
*sizep += count;
while (bl != NULL) {
if ((err = nse_fputs(path, bl->name, tfs_file)) ||
(err = nse_fputs(path, "\n", tfs_file))) {
return err;
}
*sizep += strlen(bl->name) + 1;
bl = bl->next;
}
while (wo != NULL) {
if ((err = nse_fputs(path, wo->name, tfs_file)) ||
(err = nse_fputs(path, "\n", tfs_file))) {
return err;
}
*sizep += strlen(wo->name) + 1;
wo = wo->next;
}
return NULL;
}
/*
* fgets from 'stream', checking that the line doesn't start with '\0'.
* (The line can start with '\0' if a previous write of this file was
* interrupted. (See nse_safe_write_close().)
*/
static Nse_err
tfs_fgets(path, buf, n, stream, eofp)
char *path;
char *buf;
int n;
FILE *stream;
bool_t *eofp;
{
Nse_err err;
err = nse_fgets(path, buf, n, stream, eofp);
if (!*eofp && buf[0] == '\0') {
*eofp = TRUE;
}
return err;
}