/* hardlink.c - handles hardlinked files
   This has nothing to do with cryptography.
   Copyright (C) 1998 Paul Sheer

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307, USA.  
 */

#include "mostincludes.h"
#include "mirrordir.h"
#include "vfs/vfs.h"
#include "diffie/compat.h"
#include "mad.h"


/*
   Procedure is as follows (done in mirrordir.c):

   If we come accross a hardlink we search for it in the list of previously
   found hardlinks.

   If it does exists, then we reduce the count of the previously found
   hardlink, and make a hardlink instead of copying the file.

   If it doesn't exist, then we add it to the list, set the link count of
   the list entry to the link count of the found hardlink, and copy the
   file.
 */

static struct hardlink_list {
    struct hardlink_list *prev;
    struct hardlink_list *next;
    char *name;
    dev_t mirror_st_dev;
    ino_t mirror_st_ino;
    dev_t st_dev;
    ino_t st_ino;
    nlink_t st_nlink;
} *hl = 0;

void free_all_hardlinks (void)
{
    struct hardlink_list *l;
    if (!hl)
	return;
    rewind (hl);
    do {
	l = hl->next;
	if (hl->name)
	    free (hl->name);
	free (hl);
	hl = l;
    } while (l);
}

void add_hardlink (char *path, struct stat *s, struct stat *m)
{
    struct hardlink_list *l;

    if (hl)
	forward (hl);
    l = malloc (sizeof (struct hardlink_list));

    memset (l, 0, sizeof (struct hardlink_list));
    l->name = (char *) strdup (path);
    if (m) {
	l->mirror_st_dev = m->st_dev;
	l->mirror_st_ino = m->st_ino;
    }
    l->st_dev = s->st_dev;
    l->st_ino = s->st_ino;
    l->st_nlink = s->st_nlink;
    l->prev = hl;
    if (hl)
	hl->next = l;
    hl = l;
}

int correct_link (struct stat *s)
{
    return memcmp (&s->st_dev, &hl->mirror_st_dev, sizeof (hl->mirror_st_dev)) == 0
	&& s->st_ino == hl->mirror_st_ino;
}

int is_hardlink (struct stat *s)
{
    mode_t m = s->st_mode;
    if (handle_hardlinks)
	if (S_ISREG (m) || S_ISLNK (m) || S_IS_SPECIAL (m))
	    if (s->st_nlink >= 2)
		return 1;
    return 0;
}

int find_hardlink (struct stat *s)
{
    if (!hl)
	return 0;

/* sometimes we search for things twice in a row,
   so lets check if its not already found */
    if (memcmp (&hl->st_dev, &s->st_dev, sizeof (hl->mirror_st_dev)) == 0
	&& hl->st_ino == s->st_ino)
	return 1;
    rewind (hl);
    for (;;) {
	if (memcmp (&hl->st_dev, &s->st_dev, sizeof (hl->st_dev)) == 0
	    && hl->st_ino == s->st_ino)
	    return 1;
	if (!hl->next)
	    break;
	hl = hl->next;
    }
    return 0;
}

/* reduces a hardlinks count and deletes the list entry if it reached zero */
void reduce_hardlink (void)
{
    if (!hl)
	return;
    if (hl->st_nlink <= 2) {
	struct hardlink_list *l;
	l = hl->prev;
	if (!l)
	    l = hl->next;
	if (!l) {
	    ;
	} else if (!hl->prev) {
	    hl->next->prev = 0;	/* begining of list */
	} else if (!hl->next)
	    hl->prev->next = 0;	/* end of list */
	else {
	    hl->prev->next = hl->next;
	    hl->next->prev = hl->prev;
	}
	if (hl->name)
	    free (hl->name);
	free (hl);
	hl = l;
	return;
    }
    hl->st_nlink--;
}

void print_hardlinks (void)
{
    if (!hl) {
	verbmess ("all hardlinks located", 0);
	return;
    }
    rewind (hl);
    for (;;) {
	verbmess ("file should have a hardlink to outside of mirror directory", hl->name);
	if (!hl->next)
	    break;
	hl = hl->next;
    }
}

void create_link (char *q)
{
    if (only_erase)
	return;
    if (verbose > 1)
	verbmess ("creating hardlink", q);
    if (!only_test)
	if (mc_link (hl->name, q))
	    progmess_strerror ("error trying to create hardlinks", q);
}

char *retrieve_link (void)
{
    return hl->name;
}






