/*-
* 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 <sys/uio.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include "compat_sys_stat.h"
#include "compat_stdbool.h"
#include "compat_stdint.h"
#include "compat_inttypes.h"
#include "compat_limits.h"
#include "basedef.h"
#include "cvsync.h"
#include "filetypes.h"
#include "list.h"
#include "logmsg.h"
#include "scanfile.h"
bool scanfile_insert_attr(struct scanfile_args *, struct scanfile_attr *);
bool scanfile_remove_attr(struct scanfile_args *, struct scanfile_attr *);
bool scanfile_flush_attr(struct scanfile_args *, struct scanfile_attr *);
void
scanfile_init(struct scanfile_args *sa)
{
sa->sa_scanfile = NULL;
sa->sa_scanfile_name = NULL;
sa->sa_start = NULL;
sa->sa_end = NULL;
sa->sa_tmp = -1;
sa->sa_tmp_name[0] = '\0';
sa->sa_tmp_mode = 0;
sa->sa_dirlist = NULL;
(void)memset(&sa->sa_attr, 0, sizeof(sa->sa_attr));
sa->sa_changed = false;
}
struct scanfile_args *
scanfile_open(const char *fname)
{
struct scanfile_args *sa;
if ((sa = malloc(sizeof(*sa))) == NULL) {
logmsg_err("%s", strerror(errno));
return (NULL);
}
scanfile_init(sa);
sa->sa_scanfile_name = fname;
if ((sa->sa_scanfile = cvsync_fopen(sa->sa_scanfile_name)) == NULL) {
free(sa);
return (NULL);
}
if (!cvsync_mmap(sa->sa_scanfile, (off_t)0,
sa->sa_scanfile->cf_size)) {
cvsync_fclose(sa->sa_scanfile);
free(sa);
return (NULL);
}
sa->sa_start = sa->sa_scanfile->cf_addr;
sa->sa_end = sa->sa_start + (size_t)sa->sa_scanfile->cf_size;
return (sa);
}
void
scanfile_close(struct scanfile_args *sa)
{
if (sa == NULL)
return;
if (sa->sa_scanfile != NULL)
cvsync_fclose(sa->sa_scanfile);
free(sa);
}
bool
scanfile_read_attr(uint8_t *sp, const uint8_t *bp, struct scanfile_attr *attr)
{
if (bp - sp < 3) {
logmsg_err("Scanfile Error: read attr type/namelen");
return (false);
}
attr->a_type = sp[0];
if ((attr->a_namelen = GetWord(&sp[1])) == 0) {
logmsg_err("Scanfile Error: attr namelen is zero");
return (false);
}
sp += 3;
if ((size_t)(bp - sp) < attr->a_namelen) {
logmsg_err("Scanfile Error: read attr name");
return (false);
}
attr->a_name = sp;
sp += attr->a_namelen;
if (bp - sp < 2) {
logmsg_err("Scanfile Error: read attr auxlen");
return (false);
}
if ((attr->a_auxlen = GetWord(sp)) == 0) {
logmsg_err("Scanfile Error: attr auxlen is zero");
return (false);
}
sp += 2;
if ((size_t)(bp - sp) < attr->a_auxlen) {
logmsg_err("Scanfile Error: read attr aux");
return (false);
}
attr->a_aux = sp;
attr->a_size = attr->a_namelen + attr->a_auxlen + 5;
return (true);
}
bool
scanfile_write_attr(struct scanfile_args *sa, struct scanfile_attr *attr)
{
struct iovec iov[4];
uint8_t buffer1[3], buffer2[2];
size_t len;
ssize_t wn;
buffer1[0] = attr->a_type;
SetWord(&buffer1[1], attr->a_namelen);
SetWord(buffer2, attr->a_auxlen);
iov[0].iov_base = (void *)buffer1;
iov[0].iov_len = 3;
iov[1].iov_base = attr->a_name;
iov[1].iov_len = attr->a_namelen;
iov[2].iov_base = (void *)buffer2;
iov[2].iov_len = 2;
iov[3].iov_base = attr->a_aux;
iov[3].iov_len = attr->a_auxlen;
len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len +
iov[3].iov_len;
if ((wn = writev(sa->sa_tmp, iov, 4)) == -1) {
logmsg_err("Scanfile Error: write attr");
return (false);
}
if ((size_t)wn != len) {
logmsg_err("Scanfile Error: write attr");
return (false);
}
return (true);
}
bool
scanfile_create(struct scanfile_create_args *sca)
{
switch (cvsync_release_pton(sca->sca_release)) {
case CVSYNC_RELEASE_RCS:
if (!scanfile_rcs(sca)) {
logmsg_err("Scanfile Error: %s: failed to create",
sca->sca_name);
return (false);
}
break;
default:
logmsg_err("Scanfile Error: unknown release type");
return (false);
}
return (true);
}
bool
scanfile_create_tmpfile(struct scanfile_args *sa, mode_t mode)
{
const char *ep;
size_t len;
if (sa == NULL)
return (true);
len = strlen(sa->sa_scanfile_name);
for (ep = &sa->sa_scanfile_name[len - 1] ;
ep >= sa->sa_scanfile_name ;
ep--) {
if (*ep == '/')
break;
}
if (ep > sa->sa_scanfile_name)
len = (size_t)(ep - sa->sa_scanfile_name + 1);
else
len = 0;
if (len > 0)
(void)memcpy(sa->sa_tmp_name, sa->sa_scanfile_name, len);
(void)memcpy(&sa->sa_tmp_name[len], CVSYNC_TMPFILE,
CVSYNC_TMPFILE_LEN);
sa->sa_tmp_name[len + CVSYNC_TMPFILE_LEN] = '\0';
if ((sa->sa_tmp = mkstemp(sa->sa_tmp_name)) == -1) {
logmsg_err("Scanfile Error: %s: %s", sa->sa_tmp_name,
strerror(errno));
return (false);
}
sa->sa_tmp_mode = mode;
if ((sa->sa_dirlist = list_init()) == NULL) {
logmsg_err("Scanfile Error: list init");
(void)unlink(sa->sa_tmp_name);
(void)close(sa->sa_tmp);
return (false);
}
list_set_destructor(sa->sa_dirlist, free);
return (true);
}
void
scanfile_remove_tmpfile(struct scanfile_args *sa)
{
if (sa == NULL)
return;
if (sa->sa_tmp != -1)
(void)close(sa->sa_tmp);
if (strlen(sa->sa_tmp_name) != 0)
(void)unlink(sa->sa_tmp_name);
if (sa->sa_dirlist != NULL)
list_destroy(sa->sa_dirlist);
}
bool
scanfile_rename(struct scanfile_args *sa)
{
ssize_t wn;
size_t len;
if (sa == NULL)
return (true);
if (!sa->sa_changed) {
if (close(sa->sa_tmp) == -1) {
logmsg_err("Scanfile Error: %s", strerror(errno));
return (false);
}
sa->sa_tmp = -1;
if (unlink(sa->sa_tmp_name) == -1) {
logmsg_err("Scanfile Error: %s", strerror(errno));
return (false);
}
sa->sa_tmp_name[0] = '\0';
if (sa->sa_dirlist != NULL) {
list_destroy(sa->sa_dirlist);
sa->sa_dirlist = NULL;
}
return (true);
}
if (sa->sa_scanfile != NULL) {
if (!scanfile_flush_attr(sa, NULL)) {
logmsg_err("Scanfile Error: Flush");
return (false);
}
while (sa->sa_start < sa->sa_end) {
if ((len = sa->sa_end - sa->sa_start) > CVSYNC_BSIZE)
len = CVSYNC_BSIZE;
if ((wn = write(sa->sa_tmp, sa->sa_start,
len)) == -1) {
if (errno == EINTR) {
logmsg_intr();
continue;
}
logmsg_err("Scanfile Error: Flush: %s",
strerror(errno));
return (false);
}
if (wn == 0)
break;
sa->sa_start += wn;
}
if (sa->sa_start != sa->sa_end) {
logmsg_err("Scanfile Error: Flush");
return (false);
}
cvsync_fclose(sa->sa_scanfile);
sa->sa_scanfile = NULL;
}
if (sa->sa_tmp != -1) {
if (fchmod(sa->sa_tmp, sa->sa_tmp_mode) == -1) {
logmsg_err("Scanfile Error: %s", strerror(errno));
return (false);
}
if (close(sa->sa_tmp) == -1) {
logmsg_err("Scanfile Error: %s", strerror(errno));
return (false);
}
sa->sa_tmp = -1;
if (rename(sa->sa_tmp_name, sa->sa_scanfile_name) == -1) {
logmsg_err("Scanfile Error: %s", strerror(errno));
return (false);
}
sa->sa_tmp_name[0] = '\0';
}
if (sa->sa_dirlist != NULL) {
list_destroy(sa->sa_dirlist);
sa->sa_dirlist = NULL;
}
return (true);
}
bool
scanfile_add(struct scanfile_args *sa, uint8_t type, void *name,
size_t namelen, void *aux, size_t auxlen)
{
struct scanfile_attr *attr = &sa->sa_attr;
int rv;
sa->sa_changed = true;
while (sa->sa_start < sa->sa_end) {
if (!scanfile_read_attr(sa->sa_start, sa->sa_end, attr))
return (false);
if ((rv = cvsync_cmp_pathname(attr->a_name, attr->a_namelen,
name, namelen)) == 0) {
return (false);
}
if (rv > 0)
break;
/* rv < 0 */
if (!scanfile_flush_attr(sa, attr))
return (false);
if (!scanfile_write_attr(sa, attr))
return (false);
sa->sa_start += attr->a_size;
}
attr->a_type = type;
attr->a_name = name;
attr->a_namelen = namelen;
attr->a_aux = aux;
attr->a_auxlen = auxlen;
if (!scanfile_flush_attr(sa, attr))
return (false);
if (!scanfile_write_attr(sa, attr))
return (false);
return (true);
}
bool
scanfile_remove(struct scanfile_args *sa, uint8_t type, void *name,
size_t namelen)
{
struct scanfile_attr *attr = &sa->sa_attr;
int rv;
sa->sa_changed = true;
while (sa->sa_start < sa->sa_end) {
if (!scanfile_read_attr(sa->sa_start, sa->sa_end, attr))
return (false);
if ((rv = cvsync_cmp_pathname(attr->a_name, attr->a_namelen,
name, namelen)) == 0) {
sa->sa_start += attr->a_size;
break;
}
if (rv > 0)
break;
/* rv < 0 */
if (attr->a_type == FILETYPE_DIR) {
if (!scanfile_insert_attr(sa, attr))
return (false);
} else {
if (!scanfile_flush_attr(sa, attr))
return (false);
if (!scanfile_write_attr(sa, attr))
return (false);
}
sa->sa_start += attr->a_size;
}
attr->a_type = type;
attr->a_name = name;
attr->a_namelen = namelen;
if (attr->a_type == FILETYPE_DIR) {
if (!scanfile_remove_attr(sa, attr))
return (false);
}
return (true);
}
bool
scanfile_replace(struct scanfile_args *sa, uint8_t type, void *name,
size_t namelen, void *aux, size_t auxlen)
{
struct scanfile_attr *attr = &sa->sa_attr;
int rv;
sa->sa_changed = true;
while (sa->sa_start < sa->sa_end) {
if (!scanfile_read_attr(sa->sa_start, sa->sa_end, attr))
return (false);
if ((rv = cvsync_cmp_pathname(attr->a_name, attr->a_namelen,
name, namelen)) > 0) {
return (false);
}
if (rv == 0)
break;
/* rv < 0 */
if (!scanfile_flush_attr(sa, attr))
return (false);
if (!scanfile_write_attr(sa, attr))
return (false);
sa->sa_start += attr->a_size;
}
if (sa->sa_start == sa->sa_end)
return (false);
sa->sa_start += attr->a_size;
attr->a_type = type;
attr->a_name = name;
attr->a_namelen = namelen;
attr->a_aux = aux;
attr->a_auxlen = auxlen;
if (!scanfile_flush_attr(sa, attr))
return (false);
if (!scanfile_write_attr(sa, attr))
return (false);
return (true);
}
bool
scanfile_update(struct scanfile_args *sa, uint8_t type, void *name,
size_t namelen, void *aux, size_t auxlen)
{
struct scanfile_attr *attr = &sa->sa_attr;
int rv;
sa->sa_changed = true;
while (sa->sa_start < sa->sa_end) {
if (!scanfile_read_attr(sa->sa_start, sa->sa_end, attr))
return (false);
if ((rv = cvsync_cmp_pathname(attr->a_name, attr->a_namelen,
name, namelen)) > 0) {
return (false);
}
if (rv == 0)
break;
/* rv < 0 */
if (!scanfile_flush_attr(sa, attr))
return (false);
if (!scanfile_write_attr(sa, attr))
return (false);
sa->sa_start += attr->a_size;
}
if (sa->sa_start == sa->sa_end)
return (false);
sa->sa_start += attr->a_size;
if (attr->a_type != type)
return (false);
if ((attr->a_type != FILETYPE_SYMLINK) && (attr->a_auxlen != auxlen))
return (false);
attr->a_type = type;
attr->a_name = name;
attr->a_namelen = namelen;
attr->a_aux = aux;
attr->a_auxlen = auxlen;
if (!scanfile_flush_attr(sa, attr))
return (false);
if (!scanfile_write_attr(sa, attr))
return (false);
return (true);
}
bool
scanfile_insert_attr(struct scanfile_args *sa, struct scanfile_attr *attr)
{
struct scanfile_attr *dirattr;
if ((dirattr = malloc(sizeof(*dirattr))) == NULL) {
logmsg_err("%s", strerror(errno));
return (false);
}
(void)memcpy(dirattr, attr, sizeof(*dirattr));
if (!list_insert_tail(sa->sa_dirlist, dirattr)) {
free(dirattr);
return (false);
}
return (true);
}
bool
scanfile_remove_attr(struct scanfile_args *sa, struct scanfile_attr *attr)
{
struct listent *lep;
struct scanfile_attr *dirattr;
for (lep = sa->sa_dirlist->l_head ; lep != NULL ; lep = lep->le_next) {
dirattr = lep->le_elm;
if ((dirattr->a_namelen == attr->a_namelen) &&
(memcmp(dirattr->a_name, attr->a_name,
attr->a_namelen) == 0)) {
break;
}
}
if (lep != NULL) {
dirattr = lep->le_elm;
if (!list_remove(sa->sa_dirlist, lep))
return (false);
free(dirattr);
}
return (true);
}
bool
scanfile_flush_attr(struct scanfile_args *sa, struct scanfile_attr *attr)
{
struct scanfile_attr *dirattr;
while (!list_isempty(sa->sa_dirlist)) {
if ((dirattr = list_remove_head(sa->sa_dirlist)) == NULL)
return (false);
if (attr != NULL) {
if (cvsync_cmp_pathname(dirattr->a_name,
dirattr->a_namelen,
attr->a_name,
attr->a_namelen) > 0) {
if (!list_insert_head(sa->sa_dirlist, dirattr))
return (false);
break;
}
}
if (!scanfile_write_attr(sa, dirattr))
return (false);
free(dirattr);
}
return (true);
}
syntax highlighted by Code2HTML, v. 0.9.1