/*-
* Copyright (c) 2000-2005 MAEKAWA Masahide <maekawa@cvsync.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include "compat_stdbool.h"
#include "compat_stdint.h"
#include "compat_stdlib.h"
#include "compat_inttypes.h"
#include "compat_limits.h"
#include "compat_unistd.h"
#include "attribute.h"
#include "cvsync.h"
#include "cvsync_attr.h"
#include "filetypes.h"
#include "list.h"
#include "logmsg.h"
#include "mdirent.h"
#include "scanfile.h"
struct scanfile_rcs_args {
struct scanfile_args sra_scanfile;
char sra_path[PATH_MAX + CVSYNC_NAME_MAX + 1];
char *sra_rpath;
char sra_rprefix[PATH_MAX];
size_t sra_pathlen, sra_pathmax, sra_rprefixlen;
struct mdirent_args *sra_mdirent_args;
mode_t sra_umask;
uint8_t sra_aux[CVSYNC_MAXAUXLEN];
size_t sra_auxmax;
};
struct scanfile_rcs_args *scanfile_rcs_init(struct scanfile_create_args *);
void scanfile_rcs_destroy(struct scanfile_rcs_args *);
bool scanfile_rcs_dir(struct scanfile_rcs_args *, struct mdirent_rcs *);
bool scanfile_rcs_file(struct scanfile_rcs_args *, struct mdirent_rcs *);
bool scanfile_rcs_symlink(struct scanfile_rcs_args *, struct mdirent_rcs *);
struct mDIR *scanfile_rcs_opendir(struct scanfile_rcs_args *, size_t);
struct scanfile_rcs_args *
scanfile_rcs_init(struct scanfile_create_args *sca)
{
struct scanfile_rcs_args *sra;
struct stat st;
if ((sra = malloc(sizeof(*sra))) == NULL) {
logmsg_err("%s", strerror(errno));
return (NULL);
}
scanfile_init(&sra->sra_scanfile);
sra->sra_scanfile.sa_scanfile_name = sca->sca_name;
if (!scanfile_create_tmpfile(&sra->sra_scanfile, sca->sca_mode)) {
free(sra);
return (false);
}
if (stat(sca->sca_prefix, &st) == -1) {
logmsg_err("%s: %s", sca->sca_prefix, strerror(errno));
scanfile_remove_tmpfile(&sra->sra_scanfile);
free(sra);
return (NULL);
}
if (!S_ISDIR(st.st_mode)) {
logmsg_err("%s: %s", sca->sca_prefix, strerror(ENOTDIR));
scanfile_remove_tmpfile(&sra->sra_scanfile);
free(sra);
return (NULL);
}
if (realpath(sca->sca_prefix, sra->sra_path) == NULL) {
logmsg_err("%s: %s", sca->sca_prefix, strerror(errno));
scanfile_remove_tmpfile(&sra->sra_scanfile);
free(sra);
return (NULL);
}
sra->sra_pathmax = sizeof(sra->sra_path);
sra->sra_pathlen = strlen(sra->sra_path) + 1;
if (sra->sra_pathlen >= sra->sra_pathmax) {
logmsg_err("%s: %s", sca->sca_prefix, strerror(ENAMETOOLONG));
scanfile_remove_tmpfile(&sra->sra_scanfile);
free(sra);
return (NULL);
}
if ((sra->sra_rprefixlen = sca->sca_rprefixlen) >= sra->sra_pathmax) {
logmsg_err("%.*s: %s", sca->sca_rprefixlen, sca->sca_rprefix,
strerror(ENAMETOOLONG));
scanfile_remove_tmpfile(&sra->sra_scanfile);
free(sra);
return (NULL);
}
sra->sra_path[sra->sra_pathlen - 1] = '/';
sra->sra_path[sra->sra_pathlen] = '\0';
sra->sra_rpath = &sra->sra_path[sra->sra_pathlen];
if (sra->sra_rprefixlen > 0) {
(void)memcpy(sra->sra_rprefix, sca->sca_rprefix,
sca->sca_rprefixlen);
}
sra->sra_mdirent_args = sca->sca_mdirent_args;
sra->sra_umask = sca->sca_umask;
sra->sra_auxmax = sizeof(sra->sra_aux);
return (sra);
}
void
scanfile_rcs_destroy(struct scanfile_rcs_args *sra)
{
if (strlen(sra->sra_scanfile.sa_tmp_name) != 0)
scanfile_remove_tmpfile(&sra->sra_scanfile);
free(sra);
}
bool
scanfile_rcs(struct scanfile_create_args *sca)
{
struct scanfile_rcs_args *sra;
struct mDIR *mdirp;
struct mdirent_rcs *mdp, *entries;
struct list *lp;
size_t len;
if ((sra = scanfile_rcs_init(sca)) == NULL)
return (false);
sra->sra_scanfile.sa_changed = true;
if ((lp = list_init()) == NULL) {
scanfile_rcs_destroy(sra);
return (false);
}
list_set_destructor(lp, mclosedir);
if ((mdirp = scanfile_rcs_opendir(sra, sra->sra_pathlen)) == NULL) {
list_destroy(lp);
scanfile_rcs_destroy(sra);
return (false);
}
mdirp->m_parent_pathlen = sra->sra_pathlen;
if (!list_insert_tail(lp, mdirp)) {
mclosedir(mdirp);
list_destroy(lp);
scanfile_rcs_destroy(sra);
return (false);
}
do {
if ((mdirp = list_remove_tail(lp)) == NULL) {
list_destroy(lp);
scanfile_rcs_destroy(sra);
return (false);
}
while (mdirp->m_offset < mdirp->m_nentries) {
if (cvsync_isinterrupted()) {
mclosedir(mdirp);
list_destroy(lp);
scanfile_rcs_destroy(sra);
return (false);
}
entries = mdirp->m_entries;
mdp = &entries[mdirp->m_offset++];
if (mdp->md_dead)
continue;
switch (mdp->md_stat.st_mode & S_IFMT) {
case S_IFDIR:
if (!scanfile_rcs_dir(sra, mdp)) {
mclosedir(mdirp);
list_destroy(lp);
scanfile_rcs_destroy(sra);
return (false);
}
len = sra->sra_pathlen + mdp->md_namelen + 1;
if (len >= sra->sra_pathmax) {
mclosedir(mdirp);
list_destroy(lp);
scanfile_rcs_destroy(sra);
return (false);
}
(void)memcpy(&sra->sra_path[sra->sra_pathlen],
mdp->md_name, mdp->md_namelen);
sra->sra_path[len - 1] = '/';
sra->sra_path[len] = '\0';
if (!list_insert_tail(lp, mdirp)) {
mclosedir(mdirp);
list_destroy(lp);
scanfile_rcs_destroy(sra);
return (false);
}
mdirp = scanfile_rcs_opendir(sra, len);
if (mdirp == NULL) {
list_destroy(lp);
scanfile_rcs_destroy(sra);
return (false);
}
mdirp->m_parent = mdp;
mdirp->m_parent_pathlen = sra->sra_pathlen;
sra->sra_pathlen = len;
break;
case S_IFREG:
if (!scanfile_rcs_file(sra, mdp)) {
mclosedir(mdirp);
list_destroy(lp);
scanfile_rcs_destroy(sra);
return (false);
}
break;
case S_IFLNK:
if (!scanfile_rcs_symlink(sra, mdp)) {
mclosedir(mdirp);
list_destroy(lp);
scanfile_rcs_destroy(sra);
return (false);
}
break;
default:
mclosedir(mdirp);
list_destroy(lp);
scanfile_rcs_destroy(sra);
return (false);
}
}
sra->sra_pathlen = mdirp->m_parent_pathlen;
sra->sra_path[sra->sra_pathlen] = '\0';
mclosedir(mdirp);
} while (!list_isempty(lp));
list_destroy(lp);
if (!scanfile_rename(&sra->sra_scanfile)) {
scanfile_rcs_destroy(sra);
return (false);
}
scanfile_rcs_destroy(sra);
return (true);
}
bool
scanfile_rcs_dir(struct scanfile_rcs_args *sra, struct mdirent_rcs *mdp)
{
struct scanfile_args *sa = &sra->sra_scanfile;
struct scanfile_attr *attr = &sa->sa_attr;
uint16_t mode = RCS_MODE(mdp->md_stat.st_mode, sra->sra_umask);
size_t rpathlen, namelen, auxlen;
rpathlen = sra->sra_pathlen - (sra->sra_rpath - sra->sra_path);
if ((namelen = rpathlen + mdp->md_namelen) >= sra->sra_pathmax)
return (false);
(void)memcpy(&sra->sra_rpath[rpathlen], mdp->md_name, mdp->md_namelen);
if ((auxlen = attr_rcs_encode_dir(sra->sra_aux, sra->sra_auxmax,
mode)) == 0) {
return (false);
}
attr->a_type = FILETYPE_DIR;
attr->a_name = sra->sra_rpath;
attr->a_namelen = namelen;
attr->a_aux = sra->sra_aux;
attr->a_auxlen = auxlen;
if (!scanfile_write_attr(sa, attr))
return (false);
return (true);
}
bool
scanfile_rcs_file(struct scanfile_rcs_args *sra, struct mdirent_rcs *mdp)
{
struct scanfile_args *sa = &sra->sra_scanfile;
struct scanfile_attr *attr = &sa->sa_attr;
struct stat *st = &mdp->md_stat;
uint16_t mode = RCS_MODE(st->st_mode, sra->sra_umask);
size_t rpathlen, namelen, auxlen;
rpathlen = sra->sra_pathlen - (sra->sra_rpath - sra->sra_path);
if ((namelen = rpathlen + mdp->md_namelen) >= sra->sra_pathmax)
return (false);
(void)memcpy(&sra->sra_rpath[rpathlen], mdp->md_name, mdp->md_namelen);
if (IS_FILE_RCS(mdp->md_name, mdp->md_namelen)) {
if (mdp->md_attic)
attr->a_type = FILETYPE_RCS_ATTIC;
else
attr->a_type = FILETYPE_RCS;
if ((auxlen = attr_rcs_encode_rcs(sra->sra_aux,
sra->sra_auxmax,
st->st_mtime, mode)) == 0) {
return (false);
}
} else {
attr->a_type = FILETYPE_FILE;
if ((auxlen = attr_rcs_encode_file(sra->sra_aux,
sra->sra_auxmax,
st->st_mtime, st->st_size,
mode)) == 0) {
return (false);
}
}
attr->a_name = sra->sra_rpath;
attr->a_namelen = namelen;
attr->a_aux = sra->sra_aux;
attr->a_auxlen = auxlen;
if (!scanfile_write_attr(sa, attr))
return (false);
return (true);
}
bool
scanfile_rcs_symlink(struct scanfile_rcs_args *sra, struct mdirent_rcs *mdp)
{
struct scanfile_args *sa = &sra->sra_scanfile;
struct scanfile_attr *attr = &sa->sa_attr;
size_t rpathlen, namelen;
int auxlen;
rpathlen = sra->sra_pathlen - (sra->sra_rpath - sra->sra_path);
if ((namelen = rpathlen + mdp->md_namelen) >= sra->sra_pathmax)
return (false);
(void)memcpy(&sra->sra_rpath[rpathlen], mdp->md_name, mdp->md_namelen);
sra->sra_rpath[rpathlen + mdp->md_namelen] = '\0';
if ((auxlen = readlink(sra->sra_path, (char *)sra->sra_aux,
sra->sra_auxmax)) == -1) {
logmsg_err("%s: %s", sra->sra_path, strerror(errno));
return (false);
}
attr->a_type = FILETYPE_SYMLINK;
attr->a_name = sra->sra_rpath;
attr->a_namelen = namelen;
attr->a_aux = sra->sra_aux;
attr->a_auxlen = (size_t)auxlen;
if (!scanfile_write_attr(sa, attr))
return (false);
return (true);
}
struct mDIR *
scanfile_rcs_opendir(struct scanfile_rcs_args *sra, size_t pathlen)
{
struct mDIR *mdirp;
struct mdirent_rcs *mdp;
size_t rpathlen = pathlen - (sra->sra_rpath - sra->sra_path), len;
int rv;
if (rpathlen >= sra->sra_rprefixlen) {
if ((mdirp = mopendir_rcs(sra->sra_path, pathlen,
sra->sra_pathmax,
sra->sra_mdirent_args)) == NULL) {
return (NULL);
}
return (mdirp);
}
if ((mdp = malloc(sizeof(*mdp))) == NULL) {
logmsg_err("%s", strerror(errno));
return (NULL);
}
for (len = rpathlen ; len < sra->sra_rprefixlen ; len++) {
if (sra->sra_rprefix[len] == '/')
break;
}
if ((len -= rpathlen) >= sizeof(mdp->md_name)) {
free(mdp);
return (NULL);
}
(void)memcpy(mdp->md_name, &sra->sra_rprefix[rpathlen], len);
mdp->md_namelen = len;
mdp->md_attic = false;
mdp->md_dead = false;
if ((len = pathlen + mdp->md_namelen) >= sra->sra_pathmax) {
free(mdp);
return (NULL);
}
(void)memcpy(&sra->sra_path[pathlen], &sra->sra_rprefix[rpathlen],
mdp->md_namelen);
sra->sra_path[len] = '\0';
if (sra->sra_mdirent_args->mda_symfollow)
rv = stat(sra->sra_path, &mdp->md_stat);
else
rv = lstat(sra->sra_path, &mdp->md_stat);
if (rv == -1) {
free(mdp);
if (errno != ENOENT) {
logmsg_err("%s: %s", sra->sra_path, strerror(errno));
return (NULL);
}
mdp = NULL;
} else {
if (!S_ISDIR(mdp->md_stat.st_mode)) {
logmsg_err("%s: %s", sra->sra_path, strerror(ENOTDIR));
free(mdp);
return (NULL);
}
}
if ((mdirp = malloc(sizeof(*mdirp))) == NULL) {
logmsg_err("%s", strerror(errno));
free(mdp);
return (NULL);
}
mdirp->m_entries = mdp;
mdirp->m_nentries = (mdp != NULL) ? 1 : 0;
mdirp->m_offset = 0;
mdirp->m_parent = NULL;
mdirp->m_parent_pathlen = 0;
return (mdirp);
}
syntax highlighted by Code2HTML, v. 0.9.1