linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC] [PATCH] Add btrfs autosnap feature
@ 2012-02-29  2:59 asj
  2012-02-29  2:59 ` [PATCH] [RFC] " asj
  2012-03-06  7:56 ` [PATCH 1/2] Make find_updated_files to return value instead of printing Anand jain
  0 siblings, 2 replies; 16+ messages in thread
From: asj @ 2012-02-29  2:59 UTC (permalink / raw)
  To: linux-btrfs; +Cc: hugo, chris.mason, anand jain

From: anand jain <anand.jain@oracle.com>

Anand Jain (1):
  [RFC] Add btrfs autosnap feature

 Makefile     |    6 +-
 autosnap.c   | 1553 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 autosnap.h   |   81 +++
 btrfs-list.c |  140 +++++-
 btrfs.c      |   46 ++-
 btrfs_cmds.c |  186 +++++++-
 btrfs_cmds.h |    3 +-
 scrub.c      |    1 +
 8 files changed, 1982 insertions(+), 34 deletions(-)
 create mode 100644 autosnap.c
 create mode 100644 autosnap.h


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH] [RFC] Add btrfs autosnap feature
  2012-02-29  2:59 [RFC] [PATCH] Add btrfs autosnap feature asj
@ 2012-02-29  2:59 ` asj
  2012-02-29  3:38   ` Anand Jain
                     ` (2 more replies)
  2012-03-06  7:56 ` [PATCH 1/2] Make find_updated_files to return value instead of printing Anand jain
  1 sibling, 3 replies; 16+ messages in thread
From: asj @ 2012-02-29  2:59 UTC (permalink / raw)
  To: linux-btrfs; +Cc: hugo, chris.mason, Anand Jain, asj

From: Anand Jain <Anand.Jain@oracle.com>

This patch adds btrfs autosnap feature. This creates and
saves the autosnap config at /etc/autosnap/config.
Depending on the configuration, autosnap either schedules
the snapshots by updating the crontab or provides an API
to trigger the snapshots from the respective applications.
The autosnap snapshots are identified and managed using
tag and subvol pair. Further autosnap attributes each
snapshots with creation-time, parent and the tag. Which
makes code to easily identify and retrieve any snapshots.

Signed-off-by: asj <anand.jain@oracle.com>
---
 Makefile     |    6 +-
 autosnap.c   | 1553 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 autosnap.h   |   81 +++
 btrfs-list.c |  140 +++++-
 btrfs.c      |   46 ++-
 btrfs_cmds.c |  186 +++++++-
 btrfs_cmds.h |    3 +-
 scrub.c      |    1 +
 8 files changed, 1982 insertions(+), 34 deletions(-)
 create mode 100644 autosnap.c
 create mode 100644 autosnap.h

diff --git a/Makefile b/Makefile
index 834be47..dee5822 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@ DEPFLAGS = -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@
 INSTALL = install
 prefix ?= /usr/local
 bindir = $(prefix)/bin
-LIBS=-luuid
+LIBS = -luuid -lattr -lcrypto
 RESTORE_LIBS=-lz
 
 progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \
@@ -36,8 +36,8 @@ all: version $(progs) manpages
 version:
 	bash version.sh
 
-btrfs: $(objects) btrfs.o btrfs_cmds.o scrub.o
-	$(CC) $(CFLAGS) -o btrfs btrfs.o btrfs_cmds.o scrub.o \
+btrfs: $(objects) btrfs.o btrfs_cmds.o scrub.o autosnap.o
+	$(CC) $(CFLAGS) -o btrfs btrfs.o btrfs_cmds.o scrub.o autosnap.o\
 		$(objects) $(LDFLAGS) $(LIBS) -lpthread
 
 calc-size: $(objects) calc-size.o
diff --git a/autosnap.c b/autosnap.c
new file mode 100644
index 0000000..beddf68
--- /dev/null
+++ b/autosnap.c
@@ -0,0 +1,1553 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * 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 021110-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <uuid/uuid.h>
+#include <ctype.h>
+#include <attr/attributes.h>
+#include </usr/include/openssl/sha.h>
+#include <sys/statvfs.h>
+#include <sys/syscall.h>
+#include "kerncompat.h"
+#include "ctree.h"
+#include "transaction.h"
+#include "utils.h"
+#include "version.h"
+#include "ioctl.h"
+#include "volumes.h"
+#include "btrfslabel.h"
+#include "autosnap.h"
+#include "btrfs_cmds.h"
+
+/* during run time if not the below we use "/var/spool/cron"; */
+char cron_path[]="/var/spool/cron/crontabs";
+char autosnap_conf_file[]="/etc/autosnap/config";
+char tmp_file[]="/etc/autosnap/tmpfile";
+
+
+/* Take a snapshot with the default dest and adds attributes */
+int do_autosnap_now(int argc, char **argv)
+{
+	int	res;
+	int	opt;
+	int	erropt=-1;
+	int	fd;
+	char	*a[2];
+	char	**ap;
+	char	subvol[BTRFS_VOL_NAME_MAX];
+	char	sspath[BTRFS_VOL_NAME_MAX + 128];
+	char	tag[100];
+	char	new_hash[65];
+	char	*mnt;
+	FILE	*fp;
+	u8	fsid[BTRFS_FSID_SIZE];
+	struct stat sb;
+	struct rpolicy_cfg rp;
+
+	optind=1;
+	while((opt = getopt(argc,argv,"t:")) != -1) {
+		switch(opt) {
+		case 't':
+			strcpy(tag,optarg);
+			erropt++;
+			break;
+		case '?':
+			fprintf(stderr,"Error: Unknow option %c\n",optopt);
+			return -1;
+		}
+	}
+
+	if(((argc - optind) < 1 ) && ((argc - optind) >= 3)) {
+		fprintf(stderr, "Error: need a subvol\n");
+		return -1;
+	}
+	
+	if(optind == 1) {
+		fprintf(stderr,"Error: need tag \n");
+		return -1;
+	}
+
+	strcpy(subvol, argv[optind]);
+
+	if((res = test_issubvolume(subvol)) < 0) {
+		fprintf(stderr, "Error: error accessing '%s'\n", subvol);
+		return -1;
+	}
+
+	if(subvol_to_mnt(subvol, &mnt) == -1)
+		return -1;
+	fd = open_file_or_dir(mnt);
+	get_fsid(fd,&fsid[0]);
+	if ((res = read_config(subvol+strlen(mnt),tag,&rp,NULL,&fsid[0])) == 1) {
+		fprintf(stderr,"need to run autosnap enable for this subvol and tag pair\n");
+		return 1;
+	} else if(res == -1){
+		fprintf(stderr,"read_config failed\n");
+		return 1;
+	}
+
+	if ( take_autosnap(subvol, tag, sspath) !=0 )
+		return -1;
+
+	if (strcmp(rp.idcal, "older") == 0 ) {
+		fp = fopen(tmp_file, "w");
+		tree_scan(sspath, fp);
+		fclose(fp);
+		get_sha256(tmp_file, new_hash);
+		if((stat(rp.last_ss, &sb) == 0) && (strcmp(rp.last_ss_hash,new_hash) == 0)) {
+			printf("Newer snapshot is identical to the previous snapshot, deleting the newer\n"); 
+			a[1] = sspath;
+			ap = a;
+			res = do_delete_subvolume(2,ap);
+			if(res)
+				printf("do_delete_subvolume failed %d\n",res);
+		} else {
+			/* hash does not match so keep the new snasphot  OR
+			Last snapshot was deleted. */
+			update_last_hash(subvol+strlen(mnt),tag,&fsid[0],sspath,new_hash);
+		}
+		unlink(tmp_file);
+	}
+
+	#if 0
+	/* Un-def this when we have synchronous snapshot delete */
+	chk_fslimit(subvol);
+	#endif
+
+	/* clean based on the retain policy */
+	if (rp.rpval != -1) {
+		res = chk_retain_bynum(subvol, rp.rpval, tag);
+		if(res != 0 ) {
+			fprintf(stderr,"Error: Check for the retainable subvol failed %d\n",res);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/* Set and checks fslimit / autosnap threshold */
+int do_autosnap_fslimit(int argc, char **argv)
+{
+	int	opt;
+	int	chk=0;
+	int	fsl=-1;
+	char	*mnt;
+	char	*fsls=NULL;
+
+	optind=1;
+	while((opt = getopt(argc,argv,"cn:")) != -1) {
+		switch(opt) {
+		case 'n':
+			fsl = atoi(optarg);
+			fsls = optarg;
+			if(fsl > 100 || fsl < 0) {
+				printf("%d should number between 0 and 100%%\n",fsl);
+				return -1;
+			}
+			break;
+		case 'c':
+			chk=1;
+			break;
+		case '?':
+			fprintf(stderr,"Error: Unknow option %c\n",optopt);
+			return -1;
+		}
+	}
+
+	if((argc - optind) != 1) {
+		fprintf(stderr, "Error: usage: btrfs autosnap fslimit -n <n> |-c \n");
+		return -1;
+	}
+
+	if(fsl != -1) {
+		if(subvol_to_mnt(argv[argc-1],&mnt) == -1)
+			return -1;
+		attr_set(mnt,"autosnap.fslimit",fsls,strlen(fsls),ATTR_DONTFOLLOW);
+		printf("'%s' autosnap threshold set at %d%%\n",mnt,fsl);
+		free(mnt);
+		printf("Caveat:\n\
+		 Snapshot delete works in async manner, until there is a way\n\
+		 where btrfs can provide more accurate disk space info, this\n\
+		 feature can not be very effective.\n");
+	}
+
+	if(chk == 1 )
+		chk_fslimit(argv[argc-1]);
+	return 0;
+}
+
+/* disable the autosnap, update the config and crontab if needed */
+int do_autosnap_disable(int argc, char **argv)
+{
+	int	res;
+	int	opt;
+	int	t=0;
+	int	fd;
+	char	subvol[BTRFS_VOL_NAME_MAX];
+	char	tag[TAG_MAX_LEN];
+	char	*mnt;
+	u8	fsid[BTRFS_FSID_SIZE];
+	struct autosnap_cron *head = NULL;
+	struct autosnap_cron *tmp = NULL;
+
+	optind=1;
+	while((opt = getopt(argc,argv,"t:")) != -1) {
+		switch(opt) {
+		case 't':
+			strcpy(tag,optarg);
+			t=1;
+			break;
+		case '?':
+			fprintf(stderr,"Error: Unknow option %c\n",optopt);
+			return -1;
+		}
+	}
+
+	if((argc - optind) != 1) {
+		fprintf(stderr, "Error: usage: btrfs autosnap disable -t <tag> <subvol>\n");
+		return -1;
+	}
+
+	strcpy(subvol, argv[optind]);
+	if(subvol_to_mnt(subvol, &mnt) == -1) return -1;
+	fd = open_file_or_dir(mnt);
+	get_fsid(fd, &fsid[0]);
+	/* clean up cron entries */
+	if((res = cron_retrieve(1, &head)) == 0) {
+		/* remove all subvol or just a tag of the subvol */ 
+		if(t) delete_autosnap(&head, subvol, tag);
+		else while (delete_autosnap(&head, subvol, NULL) != 1);
+
+		/* cron_retrieve (above) removes all entries from cron so put the rest back */
+		if(head) {
+			cron_update(head);
+			while(head != NULL) {
+				tmp = head->next;
+				free(head);
+				head = tmp;
+			}
+		}
+	} else {
+		/* failed to read the cron file so clean up and exit */
+		fprintf(stderr,"Failed to read cron %d\n",res);
+		if(head) {
+			while(head != NULL) {
+				tmp = head->next;
+				free(head);
+				head = tmp;
+			}
+		}
+	}
+
+	if(t) 
+		delete_config(subvol+strlen(mnt), tag, &fsid[0]);
+	else
+		delete_config(subvol+strlen(mnt), NULL, &fsid[0]);
+		
+	return 0;
+}
+
+/* display the config info */
+int do_autosnap_show(int argc, char **argv)
+{
+	int	res;
+	int	fsused;
+	int	fd;
+	int	attrlen=ATTR_MAX_LEN;
+	int	opt;
+	int	showtag=0;
+	char	str_retain[100];
+	char	str_idcal[100];
+	char	*mnt;
+	char	attr[ATTR_MAX_LEN];
+	int	fslimit=0;
+	char	uuidbuf[37];
+	char	svpath[BTRFS_VOL_NAME_MAX + 128];
+	u8	fsid[BTRFS_FSID_SIZE];
+	struct rpolicy_cfg *rp=NULL;
+	struct rpolicy_cfg *cur;
+	struct rpolicy_cfg *prev;
+
+	optind=1;
+	while((opt = getopt(argc,argv,"t")) != -1) {
+		switch(opt) {
+		case 't':
+			showtag=1;
+			break;
+		default:
+			printf("Error, unknown option %c\n",optopt);
+			return -1;
+		}
+	}
+
+	if ((res = read_config(NULL,NULL,NULL,&rp,NULL)) == 1) {
+		return 1;
+	} else if(res == -1){
+		fprintf(stderr,"read_config failed\n");
+		return 1;
+	}
+
+	if(showtag == 1){
+		if ((argc - optind) != 1) {
+			printf("Error, need subvol\n"); 
+			return -1;
+		}
+	}
+
+	if(argc <= 1) {
+		printf("tag\tretain\tidentical\tfsid /subvol\n");
+		cur = rp;
+		while(cur != NULL) {
+			if(cur->rpval == -1)
+				sprintf(str_retain,"all");
+			else
+				sprintf(str_retain,"%d",cur->rpval);
+	
+			if(strcmp(cur->idcal, "older") == 0)
+				strcpy(str_idcal,"older");
+			else
+				sprintf(str_idcal,"%s",cur->idcal);
+	
+			printf("%s\t%s\t%s\t\t",cur->tag,str_retain,str_idcal);
+			uuid_unparse(cur->fsid, uuidbuf);
+			printf("%s ",uuidbuf);
+			printf("%s\n",cur->subvol);
+			cur = cur->next;
+		}
+		return 0;
+	}
+
+	if(subvol_to_mnt(argv[optind], &mnt) == -1) {
+		return -1;
+	}
+	fd = open_file_or_dir(argv[optind]);
+	if(get_fsid(fd, &fsid[0]) != 0) {
+		printf("Error: fsid not found\n");
+		free(mnt);
+		return -1;
+	}
+
+	if(showtag != 1){
+		printf("tag\tretain\tidentical\tsubvol\n");
+	}
+	cur = rp;
+	while(cur != NULL) {
+		strcpy(svpath, mnt);
+		strcat(svpath, cur->subvol);
+		if(memcmp(cur->fsid,fsid,BTRFS_FSID_SIZE)!=0) {
+			cur = cur->next;
+			continue;
+		} 
+		if((strcmp(mnt,argv[optind]) != 0) && (strcmp(svpath, argv[optind]) != 0)) {
+			cur = cur->next;
+			continue;
+		} 
+		if(showtag == 1) {
+			printf("%s\n",cur->tag);
+			cur = cur->next;
+			continue;
+		}
+			
+		if(cur->rpval == -1)
+			sprintf(str_retain,"all");
+		else
+			sprintf(str_retain,"%d",cur->rpval);
+
+		if(strcmp(cur->idcal, "older") == 0)
+			strcpy(str_idcal,"older");
+		else
+			sprintf(str_idcal,"%s",cur->idcal);
+
+		printf("%s\t%s\t%s\t\t%s%s\n",cur->tag,str_retain,str_idcal,mnt,cur->subvol);
+		cur = cur->next;
+	}
+	if(showtag == 1)
+		return 0;
+
+	/* also display the current FS full %*/
+	fsused = fs_used(mnt);
+	if(!(attr_get(mnt, "autosnap.fslimit", attr, &attrlen, ATTR_DONTFOLLOW))) {
+		attr[attrlen]='\0';
+		fslimit = atoi(attr);
+	}
+
+	/*attr_get doesn't err when attr not found, which when fslimit will be zero */
+	if(fslimit == 0) fslimit = 100;	
+
+	printf("autosnap threshold %d%%, %s %d%% full\n",fslimit,mnt,fsused);
+	if(fsused > fslimit)
+		printf("run \'btrfs au fslimit -c %s\' to level\n",mnt);
+
+	/* clean up */
+	cur = rp;
+	while(cur != NULL) {
+		prev = cur;
+		cur = cur->next;
+		free(prev);
+	}
+	free(mnt);
+	return 0;
+}
+
+/* Configure the autosnap */
+int do_autosnap_enable(int argc, char **argv)
+{
+	int res;
+	int opt;
+	int rpval=0;
+	int fcnt = 0;
+	int rcnt = 0;
+	int diffsz = 0;
+	int retcnt;
+	int founderr = 0;
+	int cron_ent = 1;
+	int fd;
+	char subvol[BTRFS_VOL_NAME_MAX + 512];
+	char dest[BTRFS_VOL_NAME_MAX + 128];
+	char *mnt;
+	char freq[TAG_MAX_LEN];
+	char tag[TAG_MAX_LEN];
+	char idcal[100];
+	u8 fsid[BTRFS_FSID_SIZE];
+	struct stat sb;
+	struct autosnap_cron *head = NULL;
+	struct autosnap_cron *tmp = NULL;
+	struct autosnap_cron *new = NULL;
+
+	strcpy(freq,"");
+	strcpy(tag,"");
+	strcpy(idcal,"older");
+
+	optind=1;
+	while((opt = getopt(argc,argv,"t:m:hdMwysc:n:")) != -1) {
+		switch(opt) {
+		case 't':
+			/* User externally set frequency so we don't update the cron*/
+			cron_ent = 0;
+			fcnt++;
+			if(strlen(optarg) > TAG_MAX_LEN){
+				fprintf(stderr,"Error: Tag len is gt %d\n",TAG_MAX_LEN);
+				return -1;
+			}
+			strcpy(tag,optarg);
+			break;
+		case 'm':
+			fcnt++;
+			if ((atoi(optarg) > 60) || (atoi(optarg) < 1)) {
+				fprintf(stderr, "Value for option -m: Minutes should be between 1 to 60\n");
+				founderr++;
+			} else {
+				sprintf(freq, "*/%s * * * *", optarg);
+				strcpy(tag,"@minute");
+			}
+			break;
+		case 'h':
+			fcnt++;
+			sprintf(freq, "@hourly");
+			sprintf(tag, "@hourly");
+			break;
+		case 'd':
+			fcnt++;
+			sprintf(freq, "@daily");
+			sprintf(tag, "@daily");
+			break;
+		case 'w':
+			fcnt++;
+			sprintf(freq, "@weekly");
+			sprintf(tag, "@weekly");
+			break;
+		case 'M':
+			fcnt++;
+			sprintf(freq, "@monthly");
+			sprintf(tag, "@monthly");
+			break;
+		case 'y':
+			fcnt++;
+			sprintf(freq, "@yearly");
+			sprintf(tag, "@yearly");
+			break;
+		case 's':
+			rcnt++;
+			rpval = -1;
+			break;
+		case 'c':
+			rcnt++;
+			retcnt = atoi(optarg);
+			if (retcnt <= 0) {
+				fprintf(stderr, "Value for option -c: Should be a number, snapshots to retain\n");
+				founderr++;
+			}
+			rpval = retcnt;
+			break;
+		case 'n':
+			strcpy(idcal,optarg);
+			if (!((strcmp(idcal, "disable") ==0) || (strcmp(idcal, "older") == 0))) {
+				fprintf(stderr, "Error: parameter %s should be one of disable|older\n",idcal);
+				founderr++;
+			}
+			break;
+		case '?':
+			if (optopt == 't' || optopt == 'm' || optopt == 'D' || optopt == 'c' || optopt == 'D')
+				fprintf (stderr, "Option -%c requires an argument.\n", optopt);
+			else if (isprint (optopt))
+				fprintf (stderr, "Unknown option `-%c'.\n", optopt);
+			else
+				fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt);
+			founderr++;
+			break;
+		default:
+			fprintf(stderr, "Unknown\n");
+			founderr++;
+			break;
+		}
+	}
+
+	if (founderr)
+		return -1;
+
+	if (fcnt > 1 || fcnt == 0 || rcnt > 1 || rcnt ==0) {
+		fprintf(stderr, "ERROR: Provide a frequency with a retension\n");
+		return -1;
+	}
+
+	if((argc - optind) < 1 ) {
+		fprintf(stderr, "Error: need a subvol\n");
+		return -1;
+	}
+
+	if((argc - optind) >= 3) {
+		fprintf(stderr, "Error: needs _a_ subvol\n");
+		return -1;
+	}
+
+	strcpy(subvol, argv[optind]);
+
+	if((res = test_issubvolume(subvol)) < 0) {
+		fprintf(stderr, "Error: error accessing '%s'\n", subvol);
+		return -1;
+	}
+
+	if(subvol_to_mnt(subvol,&mnt) == -1) return -1;
+	sprintf(dest,"%s/.autosnap",mnt);
+
+	if (stat(dest,&sb) != 0) {
+		if((res = mkdir(dest, 0777)) != 0) {
+			fprintf(stderr,"Error: mkdir %s failed with error %d\n",dest,res);
+			free(mnt);
+			return res;
+		}
+	}
+
+	fd = open_file_or_dir(mnt);
+	if(get_fsid(fd,&fsid[0]) != 0) {
+		fprintf(stderr,"Error: get_fsid failed\n");
+		return -1;
+	}
+
+	/* Save config to the config file */
+	write_config(subvol+strlen(mnt),rpval,freq,diffsz,tag,idcal,&fsid[0]);
+	free(mnt);
+
+	/* create the cron entries and write them the cron file */
+	if (cron_ent == 1) {
+		new = malloc(sizeof(struct autosnap_cron));
+		memset(new,0,sizeof(struct autosnap_cron));
+		sprintf(new->cronstr,
+			"%s /usr/local/bin/btrfs autosnap now -t %s %s\n",
+			freq, tag, subvol);
+	
+		/* There might be some old entries so retrieve with copy=1*/
+		cron_retrieve(1, &head);
+		if ( head != NULL ) {
+			insert_autosnap(head, new);
+		} else {
+			head = new;
+		}
+		/*write crontab*/
+		cron_update(head);
+	
+		/* cron_retrieve will alloc now de-alloc them */
+		while(head != NULL) {
+			tmp = head->next;
+			free(head);
+			head = tmp;
+		}
+	}
+	printf("successful\n");
+	printf("\tsubvol: %s tag: %s retain: %d identical: %s\n",subvol,tag,rpval,idcal);
+	if (cron_ent == 0) {
+		printf("\tcommand to call in the script:\n");
+		printf("\tbtrfs autosnap now -t %s %s\n",tag,subvol);
+	}
+	return 0;
+}
+
+/* Checks if the number of snapshots have exceeded the retainable and deletes
+ * the oldest if needed.
+ */
+int chk_retain_bynum(char *subvol, int retain, char *tag)
+{
+	int fd;
+	int ret=0;
+	int cnt=0;
+	char *a[2];
+	char **ap;
+	char *mnt;
+	struct sv_list *head=NULL;
+	struct sv_list *cur=NULL;
+	struct sv_list *prev=NULL;
+	struct sv_list *tmp=NULL;
+
+	fd = open_file_or_dir(subvol);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
+		return 12;
+	}
+
+	if(subvol_to_mnt(subvol,&mnt) == -1) {
+		close(fd);
+		return -1;
+	}
+
+	ret = list_subvols(fd, 1, &head, mnt);
+	if (ret) {
+		close(fd);
+		free(mnt);
+		return 19; 
+	}
+
+	free(mnt);
+	/* now count the number of snapshot of a parent and tag tuple */
+	/* TODO: Instead of parent it should be parent_id */
+	cur=head;
+	while(cur) {
+		if (!((strcmp(cur->parent, subvol) == 0) && (strcmp(cur->tag, tag) == 0))) {
+			if (cur == head)
+				head = cur->next;
+			else
+				prev->next = cur->next;
+			tmp = cur;
+			cur = cur->next;
+			free(tmp);
+		} else  {
+			prev = cur;
+			cur = cur->next;
+			cnt++;
+		}
+	}
+
+	if (cnt == 0)
+		goto out;
+
+	/* -1 means We need one old snapshot to be deleted */
+	if (retain == -1) 
+		retain = cnt - 1;
+
+	while(cnt > retain ) {
+		prev=cur=head;
+		/* find the oldest snapshot for a given subvol */
+		while(cur) {
+			if (cur->crtime < prev->crtime)
+				prev = cur;
+			cur=cur->next;
+		}
+
+		/* delete the olderst snapshot */
+		tmp=prev;
+		a[1]=tmp->name;
+		ap=a;
+		ret = do_delete_subvolume(2,ap);
+		if(ret) {
+			printf("do_delete_subvolume failed %d\n",ret);
+			break;
+		}
+		cur = head;
+		prev = NULL;
+		while (cur) {
+			if (cur == tmp) {
+				if (cur == head)
+					head = cur->next;
+				if (prev != NULL)
+					prev->next = cur->next;
+
+				free(cur);
+				break;
+			} 
+			prev = cur;
+			cur = cur->next;
+		}
+		cnt--;
+	}
+
+out:
+	cur=head;
+	while(cur) {
+		prev = cur;
+		cur = cur->next;
+		free(prev);
+	}
+	close(fd);
+	return ret;
+}
+
+
+/* Create a new snapshot of a given subvol */
+int take_autosnap(char *subvol, char *tag, char *sspath)
+{
+	int	res;
+	char	r[10];
+	char	*a[3];
+	char	**ap;
+	char	*mnt;
+	char	ssname[BTRFS_VOL_NAME_MAX];
+	char 	dest[BTRFS_VOL_NAME_MAX + 128];
+	struct stat sb;
+
+        uuid_t  uuid;
+        uuid_generate_time(uuid);
+        uuid_unparse(uuid, ssname);
+
+	/* Ensure the auto-snapshot dir is present */
+	if(subvol_to_mnt(subvol,&mnt) == -1) return -1;
+	sprintf(dest,"%s/.autosnap",mnt);
+	if (stat(dest,&sb) != 0) {
+		if((res = mkdir(dest, 0777)) != 0) {
+			fprintf(stderr,"Error: mkdir %s failed with error %d\n",dest,res);
+			return res;
+		}
+	}
+
+	/* take a snap for the subvol */
+	sprintf(dest,"%s/.autosnap/%s",mnt,ssname);
+	free(mnt);
+	strcpy(r,"-r");
+	a[0]=r;
+	a[1]=subvol;
+	a[2]=dest;
+	ap=a;
+	if((res = do_clone(3, ap)))
+		return res;
+
+	/* set attribute tag to the snapshot */	
+	attr_set(dest,"tag",tag,strlen(tag),ATTR_DONTFOLLOW);
+	strcpy(sspath, dest);
+
+	return 0;
+}
+
+/* To modify the cron first call cron_retrieve */
+/* Adds autosnap entries to the end of the file */
+int cron_update(struct autosnap_cron *head)
+{
+	char *user;
+	FILE *fp;
+	char buffer[100];
+
+	user = getenv("USER");
+	get_cronpath();
+	sprintf(buffer,"%s/%s",cron_path,user);
+
+	if (!(fp = fopen(buffer,"a+"))) return 1;
+
+	/* Important marker in the cron file */
+	fprintf(fp,"%s\n","#BEGIN autosnap entry");
+	while(head != NULL ) {
+		fprintf(fp,"%s",head->cronstr);
+		head = head->next;
+	}
+	fprintf(fp,"%s\n","#END autosnap entry");
+	fclose(fp);
+	return 0;
+}
+
+/* retrieve and deletes cron entries made by autosnap and stores
+ * it in the struct autosnap_cron
+ * copy = 1 will copy into the head and deletes the cron entries
+ * copy = 0 will not copy into head but deletes the cron entries
+ * copy = 2 copies into head but does NOT deletes the cron entries
+ * return 0 success
+*/
+int cron_retrieve(int copy, struct autosnap_cron **head)
+{
+	char *user;
+	char buffer[100];
+	FILE *fp;
+	int fd;
+	char *line=NULL;
+	size_t len = 0;
+	ssize_t read;
+	ssize_t wrote=0;
+	int ret,res=0;
+	struct autosnap_cron *cron = NULL;
+	struct autosnap_cron *tmp = NULL;
+	struct autosnap_cron *tail = NULL;
+	long offset = 0;
+	long startoffset = -1;
+	long endoffset = -1;
+
+	user = getenv("USER");
+	get_cronpath();
+	sprintf(buffer,"%s/%s",cron_path,user);
+
+	fp = fopen(buffer,"r+");
+	if( fp == NULL ) {
+		if(errno == ENOENT) { res=1; } else { res=2; }
+		goto done;
+	}
+
+	/* look for start and end marker if copy != 0 then copy the content
+	   in between.
+	*/
+	while((read = getline(&line, &len, fp)) != -1) {
+		offset = offset + read;
+		if((ret = strcmp(line,"#BEGIN autosnap entry\n")) == 0) {
+			startoffset = offset - read;
+			if (startoffset == -1) { startoffset = 0;}
+			while((read = getline(&line, &len, fp)) != -1) {
+				offset = offset + read;
+				if(strcmp(line,"#END autosnap entry\n") == 0) {
+					endoffset = offset - read;
+					break;
+				}
+				if(!copy) continue;
+				cron = malloc(sizeof(struct autosnap_cron));
+				memset(cron,'\0',sizeof(struct autosnap_cron));
+				strcpy(cron->cronstr,line);
+				if (*head == NULL) {
+					*head = tail = cron;
+				} else {
+					tail->next = cron;
+					tail = cron;
+					cron->next = NULL;
+				}
+			}
+			break;
+		}
+	}
+
+	/* If the marker not found OR never created*/	
+	if (startoffset == -1 || endoffset == -1) {
+		if(copy) {
+			while(*head != NULL) {
+				tmp = (*head)->next;
+				free(*head);
+				*head = tmp;
+			}
+			*head=NULL;
+		}
+		res=3;
+		goto done;
+	}
+
+	/* if copy = 2 then don't remove cron entries just retrieve*/
+	if(copy == 2) {
+		if(line) free(line);
+		fclose(fp);
+		res=0;
+		return res;
+	}
+
+	/* Removes the cron entries */
+	while((read = getline(&line, &len, fp)) != -1 ) {
+		if(fseek(fp, startoffset, SEEK_SET)) {
+			res=4;
+			goto done;
+		}
+		wrote = fprintf(fp,"%s",line);
+		startoffset = startoffset + wrote;
+
+		offset = offset + read;
+		if(fseek(fp, offset, SEEK_SET)) {
+			res=4;
+			goto done;
+		}
+		line = NULL;
+	}
+
+	if(line) free(line);
+	fclose(fp);
+
+	get_cronpath(); 
+	sprintf(buffer,"%s/%s",cron_path,user);
+	fd = open(buffer,O_RDWR);
+	if(ftruncate(fd, startoffset)) {
+		close(fd);
+		res=5;
+	}
+
+done:
+	if( res == 3 || res == 4 ) {
+		if(line) free(line);
+		fclose(fp);
+	}
+
+	switch(res) {
+	case 0:
+		break;
+	case 1:
+		//fprintf(stderr,"cron file not found\n");
+		break;
+	case 2:
+		/* Error opening the cron filre */
+		fprintf(stderr,"%s\n",strerror(errno));
+		break;
+	case 3:
+		//fprintf(stderr,"autosnap is not yet enabled\n");
+		res = 0;
+		break;
+	case 4:
+		fprintf(stderr,"Error reading/writing the cron file\n");
+		break;
+	case 5:
+		fprintf(stderr,"Failed to write EOF to the cron file\n");
+		break;
+	default:
+		fprintf(stderr,"Bug: reached default for %d\n",res);
+		break;
+	}
+	return res;
+}
+
+/* To add an entry to the cron file first add to the link list */
+int insert_autosnap(struct autosnap_cron *head, struct autosnap_cron *new)
+{
+	char	*s1;
+	char 	*s2;
+	struct autosnap_cron *tmp;
+
+	s1 = strstr(new->cronstr, " -t ");
+	tmp = head;
+	while(1) {
+		s2 = strstr(tmp->cronstr, " -t ");
+		if(strcmp(s1, s2) == 0) {
+			/* There is an existing entry for this tag and subvol */
+			strcpy(tmp->cronstr, new->cronstr);
+			free(new);
+			return 0;
+		}
+		if(!tmp->next) break;
+		tmp = tmp->next;
+	}
+	tmp->next = new;
+	new->next = NULL;
+	return 0;
+}
+
+/* To delete an entry from the cron remove it from the link list*/
+int delete_autosnap(struct autosnap_cron **head, char *subvol, char *tag)
+{
+	char	*s1;
+	char	s2[BTRFS_VOL_NAME_MAX + TAG_MAX_LEN + 5];
+	char	*s3;
+	struct	autosnap_cron *prev;
+	struct	autosnap_cron *cur;
+
+	sprintf(s2, " -t %s %s",tag,subvol);
+	cur = *head;
+	prev = *head;
+	while(cur != NULL) {
+		s3 = strstr(cur->cronstr, " -t ");
+		s1 = strdup(s3);
+		s1[strlen(s1) - 1 ] = '\0';
+		if(tag != NULL) {
+			if(strcmp(s1, s2) == 0) {
+				if (cur == prev) {
+					/* if the first entry is a match 
+						head should point to next */
+					*head=(*head)->next;
+				} else {
+					prev->next = cur->next;
+				}
+				free(cur);
+				free(s1);
+				return 0;
+			}
+		} else {
+			s3 = rindex(s1, ' '); s3++;
+			if(strcmp(s3, subvol) == 0) {
+				if (cur == prev) {
+					/* if the first entry is a match 
+						head should point to next */
+					*head=(*head)->next;
+				} else {
+					prev->next = cur->next;
+				}
+				free(s1);
+				free(cur);
+				return 0;
+			}
+		}
+		prev = cur;
+		cur = cur->next;
+		free(s1);
+	}
+	return 1;
+}
+
+/* read the config file into the linked list structures 
+* return : 0 if subvol tag pair is found
+* return : -1 if some read error
+* return : 1 if subvol tag pair is NOT found
+*/
+int read_config(char *subvol, char *tag, struct rpolicy_cfg *retcfg, struct rpolicy_cfg **head, u8 *fsid)
+{
+	int fp,sz;
+	int found=0;
+	ssize_t ret;
+	struct rpolicy_cfg rcfg;
+	struct rpolicy_cfg *cur=NULL;
+	struct rpolicy_cfg *prev=NULL;
+	int i;
+
+	sz = sizeof(struct rpolicy_cfg);
+	fp = open(autosnap_conf_file,O_RDONLY|O_SYNC);
+	if (fp == -1) {
+		printf("open of autosnap_conf_file %s for read failed\n",autosnap_conf_file);
+		return -1;
+	}
+
+	/* This was written using the struct rpolicy_cfg so reading into the 
+	 * same struct will help.A
+	*/
+	while((ret = read(fp, &rcfg, sz)) == sz) {
+		if(head != NULL) {
+			/* which means caller needs all the entries */
+			cur = malloc(sz);
+			memset(cur,0,sz);
+			/* that means requester needs all the entries */
+			if(found == 0) {
+				*head = cur;
+			} else {
+				prev->next = cur;
+			}
+			cur->next = NULL;
+			cur->diffsz = rcfg.diffsz;
+			cur->rpval  = rcfg.rpval;
+			strcpy(cur->subvol, rcfg.subvol);
+			strcpy(cur->tag, rcfg.tag);
+			strcpy(cur->freq, rcfg.freq);
+			strcpy(cur->idcal, rcfg.idcal);
+			strcpy(cur->last_ss_hash, rcfg.last_ss_hash);
+			strcpy(cur->last_ss, rcfg.last_ss);
+			for(i=0; i<BTRFS_FSID_SIZE; i++)
+				cur->fsid[i]= rcfg.fsid[i];
+			prev = cur;
+			found++;
+		} else {
+			if((strcmp(rcfg.subvol,subvol) == 0) && (strcmp(rcfg.tag, tag) == 0) &&\
+					(memcmp(&rcfg.fsid,fsid,BTRFS_FSID_SIZE)==0)) {
+				retcfg->diffsz = rcfg.diffsz;
+				retcfg->rpval  = rcfg.rpval;
+				strcpy(retcfg->subvol, rcfg.subvol);
+				strcpy(retcfg->tag, rcfg.tag);
+				strcpy(retcfg->freq, rcfg.freq);
+				strcpy(retcfg->idcal, rcfg.idcal);
+				strcpy(retcfg->last_ss_hash, rcfg.last_ss_hash);
+				strcpy(retcfg->last_ss, rcfg.last_ss);
+				for(i=0; i<BTRFS_FSID_SIZE; i++)
+					retcfg->fsid[i] = rcfg.fsid[i];
+				close(fp);
+				return 0;
+			}
+		}
+		memset(&rcfg,'\0',sz);
+	}
+	if (ret) {
+		fprintf(stderr,"Failed read %d %s\n",ret,strerror(errno));
+		cur = *head;
+		while(cur != NULL) {
+			prev = cur;
+			cur = cur->next;
+			free(prev);
+		}
+		close(fp);
+		return -1;
+	}
+	close(fp);
+	if(found == 0)
+		return 1;
+	return 0;
+}
+
+/* This will completely rewrite the entire config file */
+int rewrite_config(struct rpolicy_cfg *cfg)
+{
+	int fp;
+	int ret;
+	int sz;
+
+	sz = sizeof(struct rpolicy_cfg);
+
+	unlink(autosnap_conf_file);
+
+	fp = open(autosnap_conf_file, O_RDWR|O_CREAT|O_SYNC,S_IRUSR|S_IWUSR);
+	if (fp == -1) {
+		fprintf(stderr,"open of autosnap_conf_file %s for write failed\n", autosnap_conf_file);
+		return 1;
+	}
+
+	while(cfg != NULL) {
+		ret = write(fp, cfg, sz);
+		if (ret != sz ) {
+			fprintf(stderr,"write failed %s\n",strerror(errno));
+			return 1;
+		}
+		cfg = cfg->next;
+	}
+	close(fp);
+	return 0;
+}
+
+/* Delete the specified config */
+int delete_config(char *subvol, char *tag, u8 *fsid)
+{
+	int	res;
+	struct rpolicy_cfg *head = NULL;
+	struct rpolicy_cfg *cur;
+	struct rpolicy_cfg *prev;
+
+	if ((res = read_config(NULL,NULL,NULL,&head,NULL)) == 1) {
+		//fprintf(stderr,"Nothing to disable\n");
+		return 1;
+	} else if(res == -1) {
+		fprintf(stderr,"read_config failed\n");
+		return 1;
+	}
+
+	cur = head;
+	prev = head;
+	while(cur != NULL) {
+		if(tag != NULL) {
+			if((strcmp(cur->subvol, subvol) == 0) && (strcmp(cur->tag, tag) == 0) &&\
+				(memcmp(&(cur->fsid),fsid,BTRFS_FSID_SIZE) == 0)) {
+				if(head == cur) 
+					head = cur->next;
+				prev->next = cur->next;
+				free(cur);
+				break;
+			}
+		} else {
+			if((strcmp(cur->subvol, subvol) == 0) && (memcmp(&(cur->fsid),fsid,BTRFS_FSID_SIZE) == 0)) {
+				if(head == cur) 
+					head = cur->next;
+				prev->next = cur->next;
+				free(cur);
+			}
+		}
+		prev = cur;
+		cur = cur->next;	
+	}
+	rewrite_config(head);
+	cur = head;
+	while(cur != NULL) {
+		prev = cur;
+		cur = cur->next;
+		free(prev);
+	}
+	return 0;
+}
+
+/* maintain the last snapshot hash info so that identical snapshots are not taken */
+int update_last_hash(char *subvol, char *tag, u8 *fsid,char *last_ss, char *hash)
+{
+	int	res;
+	struct rpolicy_cfg *head = NULL;
+	struct rpolicy_cfg *cur;
+	struct rpolicy_cfg *prev;
+
+	if ((res = read_config(NULL,NULL,NULL,&head,NULL)) == 1) {
+		return 1;
+	} else if(res == -1) {
+		fprintf(stderr,"read_config failed\n");
+		return 1;
+	}
+
+	cur = head;
+	while(cur != NULL) {
+		if((strcmp(cur->subvol, subvol) == 0) && (strcmp(cur->tag, tag) == 0) &&
+					(memcmp(&cur->fsid,fsid,BTRFS_FSID_SIZE)==0)) {
+			strcpy(cur->last_ss_hash,hash);
+			strcpy(cur->last_ss, last_ss);
+			break;
+		}
+		cur = cur->next;	
+	}
+	rewrite_config(head);
+	cur = head;
+	while(cur != NULL) {
+		prev = cur;
+		cur = cur->next;
+		free(prev);
+	}
+	return 0;
+}
+
+/* This will write to the autosnap config file */
+int write_config(char *subvol, int rpval, char *freq, int diffsz, char *tag, char *idcal, u8 *fsid)
+{
+	int fp,sz;
+	ssize_t ret=0;
+	off_t offset=0;
+	struct rpolicy_cfg rcfg;
+	int	i;
+
+	sz = sizeof(struct rpolicy_cfg);
+	memset(&rcfg,0,sz);
+
+	fp = open(autosnap_conf_file, O_RDWR|O_CREAT|O_SYNC, S_IRUSR|S_IWUSR);
+	if (fp == -1) {
+		fprintf(stderr,"open of autosnap_conf_file %s for write failed\n", autosnap_conf_file);
+		return 1;
+	}
+
+	/* need to find if user is modifying an exisiting entry or creating new*/
+	while((ret = read(fp, &rcfg, sz)) > 0) {
+		//if((strcmp(rcfg.subvol, subvol) == 0) && (strcmp(rcfg.tag, tag) == 0)) break;
+		if((strcmp(rcfg.subvol, subvol) == 0) && (strcmp(rcfg.tag, tag) == 0) &&\
+			(memcmp(&rcfg.fsid,fsid,BTRFS_FSID_SIZE) == 0)) break;
+		offset = offset + sz;
+		memset(&rcfg,0,sz);
+	}
+	if (ret < 0) {
+		fprintf(stderr,"read failed %s\n",strerror(errno));
+		return 1;
+	}
+
+	ret = lseek(fp, offset, SEEK_SET);
+	if (ret < 0) {
+		fprintf(stderr,"lseek failed %s\n",strerror(errno));
+		return 1;
+	}
+	rcfg.rpval = rpval;
+	rcfg.diffsz = diffsz;
+	strcpy(rcfg.freq, freq);
+	strcpy(rcfg.subvol, subvol);
+	strcpy(rcfg.tag, tag);
+	strcpy(rcfg.idcal, idcal);
+	strcpy(rcfg.last_ss_hash, "");
+	strcpy(rcfg.last_ss, "");
+
+	for(i=0;i<BTRFS_FSID_SIZE;i++)
+		rcfg.fsid[i] = *(fsid++);
+
+	ret = write(fp, &rcfg, sz);
+	if (ret < 0) {
+		fprintf(stderr,"write failed %s\n",strerror(errno));
+		return 1;
+	}
+
+	close(fp);
+	return 0;
+}
+
+/* Find the oldest snapshot
+ * returns an allocated string with the path to the oldest 
+ * snapshot, called funcation should free.
+*/
+char *find_oldest_snap(char *mnt, char *parent, char *tag)
+{
+	int fd;
+	int ret;
+	char *res;
+	struct	sv_list	*head=NULL;
+	struct	sv_list	*prev=NULL;
+	struct	sv_list	*cur=NULL;
+
+	fd = open_file_or_dir(mnt);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access '%s'\n", mnt);
+		return NULL;
+	}
+
+	ret = list_subvols(fd, 1, &head, mnt);
+	if (ret) {
+		close(fd);
+		return NULL; 
+	}
+
+	/*take only snapshot which is under autosnap */
+	prev = cur = head;
+	while(cur != NULL){
+		if(strstr(cur->name,".autosnap/")) {
+			prev = cur;
+			cur = cur->next;
+		} else {
+			if (cur == head) {
+				head = prev = cur->next;
+				free(cur);
+				cur = prev = head;
+			} else {
+				prev->next = cur->next;
+				free(cur);
+				cur = prev->next;
+			}
+		}
+	}
+
+	/* Take only snapshot which matches with the given parent and tag tuple */
+	/* TODO : Should be parent id instead of parent name */
+	if(!(parent == NULL && tag == NULL)) {
+		prev = cur = head;
+		while(cur != NULL){
+			if((strcmp(cur->parent,parent) || strcmp(cur->tag,tag))) {
+				if (cur == head) {
+					head = prev = cur->next;
+					free(cur);
+					cur = prev = head;
+				} else {
+					prev->next = cur->next;
+					free(cur);
+					cur = prev->next;
+				}
+			} else {
+				prev = cur;
+				cur = cur->next;
+			}
+		}
+	}
+
+	/* Now find the oldest snapshot */
+	if(!(head))
+		return NULL;
+
+	prev = head;
+	cur = head->next;
+	while(cur != NULL) {
+		if(cur->crtime < prev->crtime)
+			prev = cur;
+		cur = cur->next;
+	}
+
+	res = strdup(prev->name);
+	cur = head;
+	while(cur != NULL) {
+		prev = cur;
+		cur=cur->next;
+		free(prev);
+	}
+	return res;
+}
+
+/* check the autosnap threshold */
+int chk_fslimit(char *subvol)
+{
+	int	ret=0;
+	int	attrlen=ATTR_MAX_LEN;
+	int	fslimit;
+	char	attr[ATTR_MAX_LEN];
+	char	*mnt;
+	char	*oldest_ss;
+	char	*a[2];
+	char	**ap;
+
+	if((ret = test_issubvolume(subvol)) < 0) {
+		printf("Error: %s is not a subvol\n",subvol);
+		return -1;
+	}
+
+	if(subvol_to_mnt(subvol, &mnt) == -1)
+		return -1;
+
+	if(!(attr_get(mnt, "autosnap.fslimit", attr, &attrlen, ATTR_DONTFOLLOW))) {
+		attr[attrlen]='\0';
+		fslimit = atoi(attr);
+	} else
+		return -1;
+
+	/* attr_get when attr is not found doesn't error, so then fslimit becomes zero */
+	if(fslimit == 0) fslimit = 100;
+
+	if (fs_used(mnt) <= fslimit)
+		return 0;
+
+	oldest_ss = find_oldest_snap(mnt, NULL, NULL);
+	if(oldest_ss != NULL) {
+                a[1]=oldest_ss;
+                ap=a;
+                if(do_delete_subvolume(2,ap))
+                        printf("do_delete_subvolume failed %d\n",ret);
+	}
+	free(mnt);
+	free(oldest_ss);
+	return 0;
+}
+
+/* get the fsid given the mount point */
+int get_fsid(int fd, u8 *fsidp)
+{
+	int ret = 0;
+	int i;
+	struct btrfs_ioctl_fs_info_args fi_args;
+
+	memset(&fi_args, 0, sizeof(fi_args));
+
+	ret = ioctl(fd, BTRFS_IOC_FS_INFO, &fi_args);
+	if (ret) {
+		fprintf(stderr,"Error: ioctl: %s\n",strerror(errno));
+		return -errno;
+	}
+
+	for(i=0;i<BTRFS_FSID_SIZE;i++)
+		*(fsidp++)=fi_args.fsid[i];
+
+	return 0;
+
+}
+
+/* Find the full-ness of the given mount point */
+int fs_used(char *mnt)
+{
+	struct statvfs statfs;
+	int res;
+	res = statvfs(mnt,&statfs);
+	if (res != 0) {
+		fprintf(stderr,"Error: statvfs failed\n");
+		return -1;
+	}
+	res = ((statfs.f_bsize * statfs.f_bavail) * 100) / ( statfs.f_frsize * statfs.f_blocks);
+	return (100 - res);
+}
+
+/* generate the sha256 code for a given file */
+int get_sha256(char *fpath, char *op)
+{
+	int i;
+	int br = 0;
+	unsigned char hash[SHA256_DIGEST_LENGTH];
+	FILE *fp = fopen(fpath, "r");
+	SHA256_CTX sha256;
+	const int bs = 32768;
+	unsigned char *buf = malloc(bs);
+
+	if(!fp)
+		return -1;
+
+	SHA256_Init(&sha256);
+
+	if(!buf)
+		return -1;
+
+	while((br = fread(buf, 1, bs, fp)))
+		SHA256_Update(&sha256, buf, br);
+
+	SHA256_Final(hash, &sha256);
+
+	for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
+		sprintf(op + (i * 2), "%02x", hash[i]);
+
+	op[64]='\0';
+
+	fclose(fp);
+	free(buf);
+	return 0;
+}
+
+/* Lists all files and dir of a path and its child */
+int tree_scan( const char *path, FILE *fp)
+{
+	char            spath[FILENAME_MAX] = "";
+	char            calc_hash[65];
+	DIR             *dir;
+	struct dirent   *entry;
+	struct stat     sb;
+
+	if( !(dir = opendir( path))) {
+		perror("opendir");
+		return -1;
+	}
+
+	for( entry = readdir( dir); entry; entry = readdir( dir)) {
+		if((strcmp(".",entry->d_name) == 0) || (strcmp("..",entry->d_name) == 0))
+			continue;
+
+		if(strcmp(entry->d_name,".autosnap") == 0)
+			continue;
+
+		sprintf(spath, "%s/%s", path, entry->d_name);
+		stat(spath,&sb);
+		if(!(S_ISREG(sb.st_mode))) {
+			get_sha256(spath, calc_hash);
+			fprintf(fp,"%s %x %x %x %x %s %s %s\n",
+				entry->d_name,sb.st_mode,sb.st_nlink,sb.st_uid,sb.st_gid,\
+				ctime(&sb.st_mtime),ctime(&sb.st_ctime),calc_hash);
+		} else {
+			fprintf(fp,"%s %x %x %x %x %s %s\n",
+				entry->d_name,sb.st_mode,sb.st_nlink,sb.st_uid,sb.st_gid,\
+				ctime(&sb.st_mtime),ctime(&sb.st_ctime));
+		}
+		if(!(S_ISREG(sb.st_mode)) && (strcmp(".",entry->d_name)) && (strcmp("..",entry->d_name))) {
+				tree_scan( spath,fp);
+		}
+	}
+	closedir( dir);
+	return(0);
+}
+
+/* obtain mnt from the subvol path */
+int subvol_to_mnt(char *subvol, char **mnt)
+{
+	int i,x;
+	char *lv;
+
+	if(test_issubvolume(subvol) != 1) {
+		printf("Error: %s is not a subvol\n",subvol);
+		return -1;
+	}
+
+	lv = strdup(subvol);
+	x=strlen(subvol);
+	
+	for (i=0;i<=x;i++) {
+		if(lv[i] == '/') {
+			lv[i] = '\0';
+			if(test_issubvolume(lv) == 1) break;
+			else lv[i] = '/';
+		}
+	}
+	*mnt = lv;
+	return 0;
+}
+
+/* Fedora and ubuntu kind of distribution has different location for crontab
+ * this assumes ubuntu first if dir not found, assume fedora.
+*/
+void get_cronpath()
+{
+	int	fd;
+
+	fd = open_file_or_dir(cron_path);
+
+	/*
+	new string: /var/spool/cron
+	is shorter than
+	old string: /var/spool/cron/crontabs
+	so below code will work.
+	*/
+	if(fd < 0)
+		strcpy(cron_path,"/var/spool/cron");
+
+	close(fd);
+}
diff --git a/autosnap.h b/autosnap.h
new file mode 100644
index 0000000..dc126b6
--- /dev/null
+++ b/autosnap.h
@@ -0,0 +1,81 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * 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 021110-1307, USA.
+ */
+
+#define BTRFS_VOL_NAME_MAX 255
+#define ATTR_MAX_LEN BTRFS_VOL_NAME_MAX
+#define TAG_MAX_LEN 128
+
+struct sv_list {
+	int	inode;
+	char	name[BTRFS_VOL_NAME_MAX];
+	int	id;
+	int	p_id;
+	int	tl;
+	char	parent[BTRFS_VOL_NAME_MAX];
+	char	tag[TAG_MAX_LEN];
+	char	crtime[100];
+	struct	sv_list *next;
+}; 
+
+struct sv_filter {
+	char	*parent;
+	char	*tag;
+};
+
+struct autosnap_cron {
+	char	cronstr[BTRFS_VOL_NAME_MAX + 512];
+	struct autosnap_cron *next;
+};
+
+struct rpolicy_cfg {
+	char	subvol[BTRFS_VOL_NAME_MAX];
+	char 	freq[TAG_MAX_LEN];
+	char	tag[TAG_MAX_LEN];
+	char	idcal[50];
+	char	last_ss_hash[65];
+	char	last_ss[BTRFS_VOL_NAME_MAX];
+	int	rpval;
+	int	diffsz;
+	u8	fsid[BTRFS_FSID_SIZE];
+	struct rpolicy_cfg *next;
+};
+
+/* func declaration */
+int subvol_to_mnt(char *subvol, char **mnt);
+int tree_scan( const char *path, FILE *fp);
+int get_sha256(char *fpath, char *op);
+int fs_used(char *mnt);
+int get_fsid(int fd, u8 *fsidp);
+int chk_fslimit(char *subvol);
+char *find_oldest_snap(char *mnt, char *parent, char *tag);
+int write_config(char *subvol, int rpval, char *freq, int diffsz, char *tag, char *idcal, u8 *fsid);
+int update_last_hash(char *subvol, char *tag, u8 *fsid,char *last_ss, char *hash);
+int delete_config(char *subvol, char *tag, u8 *fsid);
+int rewrite_config(struct rpolicy_cfg *cfg);
+int read_config(char *subvol, char *tag, struct rpolicy_cfg *retcfg, struct rpolicy_cfg **head, u8 *fsid);
+int delete_autosnap(struct autosnap_cron **head, char *subvol, char *tag);
+int cron_retrieve(int copy, struct autosnap_cron **head);
+int cron_update(struct autosnap_cron *head);
+int take_autosnap(char *subvol, char *tag, char *sspath);
+int chk_retain_bynum(char *subvol, int retain, char *tag);
+int insert_autosnap(struct autosnap_cron *head, struct autosnap_cron *new);
+void get_cronpath(void);
+int do_autosnap_now(int argc, char **argv);
+int do_autosnap_fslimit(int argc, char **argv);
+int do_autosnap_disable(int argc, char **argv);
+int do_autosnap_show(int argc, char **argv);
+int do_autosnap_enable(int argc, char **argv);
+int sv_filter(struct sv_list **head, struct sv_filter *filter);
diff --git a/btrfs-list.c b/btrfs-list.c
index 5f4a9be..61eddf9 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -34,6 +34,8 @@
 #include "ctree.h"
 #include "transaction.h"
 #include "utils.h"
+#include "autosnap.h"
+#include <attr/attributes.h>
 
 /* we store all the roots we find in an rbtree so that we can
  * search for them later.
@@ -668,11 +670,53 @@ static int __list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
 	return 0;
 }
 
-int list_subvols(int fd, int print_parent)
+int sv_attr_read(struct sv_list *head)
+{
+	struct sv_list *cur=head;
+	int	attrlen;
+	char	attr[ATTR_MAX_LEN];
+	int	res=0;
+
+	while(cur != NULL) {
+		attrlen=ATTR_MAX_LEN;
+		res = attr_get(cur->name, "tag", attr, &attrlen, ATTR_DONTFOLLOW);
+		if(!res) {
+			attr[attrlen]='\0';
+			strcpy(cur->tag,attr);
+		} else {
+			strcpy(cur->tag,"");
+		}
+
+		attrlen=ATTR_MAX_LEN;
+		res = attr_get(cur->name, "parent", attr, &attrlen, ATTR_DONTFOLLOW);
+		if(!res) {
+			attr[attrlen]='\0';
+			strcpy(cur->parent,attr);
+		} else {
+			strcpy(cur->parent,"");
+		}
+
+		attrlen=ATTR_MAX_LEN;
+		res = attr_get(cur->name, "crtime", attr, &attrlen, ATTR_DONTFOLLOW);
+		if(!res) {
+			attr[attrlen]='\0';
+			strcpy(cur->crtime,attr);
+		} else {
+			strcpy(cur->crtime,"");
+		}
+		cur = cur->next;
+	}
+	return 0;
+}
+
+int list_subvols(int fd, int print_parent, struct sv_list **head, char *mnt)
 {
 	struct root_lookup root_lookup;
 	struct rb_node *n;
 	int ret;
+	struct sv_list *tail;
+	struct sv_list *prev;
+	char	*name_tmp;
 
 	ret = __list_subvol_search(fd, &root_lookup);
 	if (ret) {
@@ -703,20 +747,49 @@ int list_subvols(int fd, int print_parent)
 		entry = rb_entry(n, struct root_info, rb_node);
 		resolve_root(&root_lookup, entry, &root_id, &parent_id,
 				&level, &path);
-		if (print_parent) {
-			printf("ID %llu parent %llu top level %llu path %s\n",
-				(unsigned long long)root_id,
-				(unsigned long long)parent_id,
-				(unsigned long long)level, path);
+		if (head != NULL) {
+			tail = malloc(sizeof(struct sv_list));
+			tail->next = NULL;
+			if (*head == NULL) {
+				*head = tail;
+			}
+			else {
+				prev->next = tail;
+			}
+			tail->id = (unsigned long long)root_id;
+			tail->p_id = (unsigned long long)parent_id;
+			strcpy(tail->name, path);
+			prev = tail;
 		} else {
-			printf("ID %llu top level %llu path %s\n",
-				(unsigned long long)root_id,
-				(unsigned long long)level, path);
+			if (print_parent) {
+				printf("ID %llu parent %llu top level %llu path %s\n",
+					(unsigned long long)root_id,
+					(unsigned long long)parent_id,
+					(unsigned long long)level, path);
+			} else {
+				printf("ID %llu top level %llu path %s\n",
+					(unsigned long long)root_id,
+					(unsigned long long)level, path);
+			}
 		}
 		free(path);
 		n = rb_prev(n);
 	}
 
+	if (head != NULL) {
+		/* prefix mnt */
+		prev = *head;
+		while(prev != NULL) {
+			name_tmp = strdup(prev->name);
+			strcpy(prev->name,mnt);
+			strcat(prev->name,"/");
+			strcat(prev->name,name_tmp);
+			free(name_tmp);
+			prev = prev->next;
+		}
+		sv_attr_read(*head);
+	}
+
 	return ret;
 }
 
@@ -934,3 +1007,52 @@ char *path_for_root(int fd, u64 root)
 
 	return ret_path;
 }
+
+
+/* Filters based on the tag and parent */
+int sv_filter(struct sv_list **head, struct sv_filter *filter)
+{
+	struct sv_list *cur;
+	struct sv_list *prev;
+	struct sv_list *tmp;
+	
+	prev = cur = *head;
+	if(filter->tag != NULL) {
+		while(cur != NULL) {
+			if(strcmp(cur->tag, filter->tag) != 0) {
+				if(*head == cur) {
+					*head = (*head)->next;
+					prev = *head;
+				} else {
+					prev->next = cur->next;
+				}
+				tmp = cur;
+				cur = cur->next;
+				free(tmp);
+			} else {
+				prev = cur;
+				cur = cur->next;
+			}
+		}
+	}
+	cur = *head;
+	if(filter->parent != NULL) {
+		while(cur != NULL) {
+			if(strcmp(cur->parent, filter->parent) != 0) {
+				if(*head == cur) {
+					*head = (*head)->next;
+					prev = *head;
+				} else {
+					prev->next = cur->next;
+				}
+				tmp = cur;
+				cur = cur->next;
+				free(tmp);
+			} else {
+				prev = cur;
+				cur = cur->next;
+			}
+		}
+	}
+	return 0;
+}
diff --git a/btrfs.c b/btrfs.c
index 1def354..2aa61c9 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -20,6 +20,8 @@
 #include <string.h>
 
 #include "kerncompat.h"
+#include "ioctl.h"
+#include "autosnap.h"
 #include "btrfs_cmds.h"
 #include "version.h"
 
@@ -66,11 +68,12 @@ static struct Command commands[] = {
 		"not passed).",
 	  NULL
 	},
-	{ do_subvol_list, -1, "subvolume list", "[-p] <path>\n"
+	{ do_subvol_list, -1, "subvolume list", "[-p] [-t [tag=<t>][,parent=<p>]] <path>\n"
 		"List the snapshot/subvolume of a filesystem.",
 		"[-p] <path>\n"
 		"List the snapshot/subvolume of a filesystem.\n"
-		"-p	   print parent ID"
+		"-p	   print parent ID\n"
+		"-t        print autosnap tag information\n"
 	},
 	{ do_set_default_subvol, 2,
 	  "subvolume set-default", "<id> <path>\n"
@@ -179,6 +182,45 @@ static struct Command commands[] = {
 		"get file system paths for the given logical address.",
 	  NULL
 	},
+	{ do_autosnap_enable, -3,
+	  "autosnap enable", "<frequency|tag> <retension> [identical] <subvol>\n"
+		"Enable autosnap for the tag and subvol pair\n"
+		"  frequency:\n"
+		"     <-m <n>|-h|-d|-M|-w|-y>\n"
+		"      Snapshot every 'n' minutes, hourly, daily, Monthly, weekly, yearly respectively\n"
+		"  retension:\n"
+		"     <-s|-c <n>>\n"
+		"	-s  Save all snapshots\n"
+		"       -c  Keep upto 'n' snapshot per tag and subvol tuple\n"
+		"  identical:\n"
+		"     [-n <older|disable>]\n"
+		"	When two consecutive autosnap snapshots are identical\n"
+		"	older  : (default) Keeps only the older snapshot\n"
+		"	disable: Keeps both the identical snapshots\n",
+	  NULL
+	},
+	{ do_autosnap_fslimit, -2,
+	  "autosnap fslimit", "<-n <x>|-c> <mnt>\n"
+		"Set the disk space threshold when managing the autosnap' snapshots\n"
+		"-n <x>: Configure 'x'% used space above which an autosnap snapshot to be deleted\n"
+		"-c    : Check disk used space and delete a snapshot if used space is above threshold\n",
+	 NULL
+	},
+	{ do_autosnap_disable, -1,
+	  "autosnap disable", "[-t <tag>] <subvol>\n"
+	  "Disable all autosnap tags for a subvol or disable only for the given tag and subvol pair\n",
+	  NULL
+	},
+	{ do_autosnap_show, 999,
+	  "autosnap show", "[-t] [subvol|mnt]\n"
+	  "Show the autosnap configuration\n",
+	  NULL
+	},
+	{ do_autosnap_now, -3,
+	  "autosnap now", "<-t <tag>> <subvol>\n"
+	  "Takes an autosnap snapshot for the given tag and subvol tuple\n",
+	  NULL
+	},
 	{ 0, 0, 0, 0 }
 };
 
diff --git a/btrfs_cmds.c b/btrfs_cmds.c
index b59e9cb..7aab105 100644
--- a/btrfs_cmds.c
+++ b/btrfs_cmds.c
@@ -39,8 +39,10 @@
 #include "ioctl.h"
 #include "volumes.h"
 
+#include "autosnap.h"
 #include "btrfs_cmds.h"
 #include "btrfslabel.h"
+#include <attr/attributes.h>
 
 #ifdef __CHECKER__
 #define BLKGETSIZE64 0
@@ -57,7 +59,7 @@ static inline int ioctl(int fd, int define, void *arg) { return 0; }
  * 1-> path exists and it is  a subvolume
  * -1 -> path is unaccessible
  */
-static int test_issubvolume(char *path)
+int test_issubvolume(char *path)
 {
 
 	struct stat	st;
@@ -303,26 +305,88 @@ int do_subvol_list(int argc, char **argv)
 	int fd;
 	int ret;
 	int print_parent = 0;
+	int print_tag = 0;
+	int print_csv =0;
+	int tag_match = 0;
 	char *subvol;
-        int optind = 1;
+	char *targ;
+	char *argp0;
+	char *argp1;
+	struct sv_filter filter;
+	struct sv_list *head=NULL;
+	struct sv_list *cur;
+	time_t	lt;
+	char	*ct;
+	char	*mnt;
 
+	optind = 1;
 	while(1) {
-		int c = getopt(argc, argv, "p");
+		int c = getopt(argc, argv, "cpt:");
 		if (c < 0) break;
 		switch(c) {
+		case 'c':
+			print_csv =1;
+			break;
 		case 'p':
 			print_parent = 1;
-			optind++;
+			break;
+		case 't':
+			print_tag++;
+			filter.parent = NULL;
+			filter.tag = NULL;
+			targ = strdup(optarg);
+
+			argp0 = strtok(targ,"=");
+			while(argp0 != NULL) {	
+				if(!(strcmp(argp0,"parent"))) {
+					tag_match++;
+					argp1 = strtok(NULL,",");
+					if(argp1 == NULL) {
+						fprintf(stderr,"\"parent=\" argument missing\n"); 
+						return 1;
+					}
+					filter.parent = strdup(argp1);
+				}else if (!(strcmp(argp0,"tag"))) {
+					tag_match++;
+					argp1 = strtok(NULL,",");
+					if(argp1 == NULL) {
+						fprintf(stderr,"\"tag\" must have value\n"); 
+						return 1;
+					}
+					filter.tag = strdup(argp1);
+				}
+				argp0 = strtok(NULL,"=");
+			}
+			free(targ);
+			break;
+		case '?':
+			fprintf(stderr,"Error: unknown option\n");
+			return -1;
 			break;
 		}
 	}
-	
-	if (argc - optind != 1) {
-		fprintf(stderr, "ERROR: invalid arguments for subvolume list\n");
-		return 1;
-	}
 
-	subvol = argv[optind];
+	if(print_tag) {
+		if(tag_match) {
+			if (argc - optind != 1) {
+				fprintf(stderr, "ERROR: invalid arguments for subvolume list\n");
+				return 1;
+			}
+			subvol = argv[optind];
+		} else {
+			if (argc != optind) {
+				fprintf(stderr, "ERROR: invalid arguments for subvolume list\n");
+				return 1;
+			}
+			subvol = argv[optind-1];
+		}
+	} else {
+		if (argc - optind != 1) {
+			fprintf(stderr, "ERROR: invalid arguments for subvolume list\n");
+			return 1;
+		}
+		subvol = argv[optind];
+	}
 
 	ret = test_issubvolume(subvol);
 	if (ret < 0) {
@@ -334,14 +398,73 @@ int do_subvol_list(int argc, char **argv)
 		return 13;
 	}
 
-	fd = open_file_or_dir(subvol);
-	if (fd < 0) {
-		fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
-		return 12;
+	if(print_tag) {
+		fd = open_file_or_dir(subvol);
+		if (fd < 0) {
+			fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
+			return 12;
+		}
+		if(subvol_to_mnt(subvol, &mnt) == -1) {
+			close(fd);
+			return -1;
+		}
+		ret = list_subvols(fd, print_parent, &head, mnt);
+		if (ret) {
+			cur=head;
+			while(cur != NULL) {
+				head = cur->next;
+				free(cur);
+				cur = head;
+			}
+			free(mnt);
+			close(fd);
+			return 19;
+		}
+
+		sv_filter(&head, &filter);
+		if(filter.parent)
+			free(filter.parent);
+		if(filter.tag)
+			free(filter.tag);
+
+		cur = head;
+		ct = "";
+		while(cur) {
+			lt = atoi(cur->crtime);
+			if(lt) {
+				ct = ctime(&lt);
+				ct[strlen(ct)-1] = '\0';
+			}
+			if(print_csv)
+				printf("%s,%s,%s,%s,\n",
+					cur->name,ct,cur->parent,cur->tag);
+			else
+				printf("%s %s %s %s\n",
+					cur->name,ct,cur->parent,cur->tag);
+
+			cur = cur->next;
+			ct = "";
+		}
+		cur=head;
+		while(cur != NULL) {
+			head = cur->next;
+			free(cur);
+			cur = head;
+		}
+		free(mnt);
+	} else {
+		fd = open_file_or_dir(subvol);
+		if (fd < 0) {
+			fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
+			return 12;
+		}
+		ret = list_subvols(fd, print_parent, NULL, NULL);
+		if (ret) {
+			close(fd);
+			return 19;
+		}
 	}
-	ret = list_subvols(fd, print_parent, 0);
-	if (ret)
-		return 19;
+	close(fd);
 	return 0;
 }
 
@@ -350,8 +473,12 @@ int do_clone(int argc, char **argv)
 	char	*subvol, *dst;
 	int	res, fd, fddst, len, e, optind = 0, readonly = 0;
 	char	*newname;
+	char	*sspath;
 	char	*dstdir;
 	struct btrfs_ioctl_vol_args_v2	args;
+	char	*ts;
+	time_t	lt;
+	struct	tm tm;
 
 	memset(&args, 0, sizeof(args));
 
@@ -458,8 +585,29 @@ int do_clone(int argc, char **argv)
 		return 11;
 	}
 
-	return 0;
+	sspath = malloc(strlen(dstdir) + strlen(newname) + 10);
+	sprintf(sspath,"%s/%s",dstdir,newname);
 
+	res = attr_set(sspath,"parent",subvol,strlen(subvol),ATTR_DONTFOLLOW);
+	if (res != 0) {
+		fprintf( stderr, "Error: attr_setf\n");
+	} 
+	
+	lt = time(NULL);
+	tm = *localtime(&lt);
+	ts = (char *)malloc(sizeof(char) * 80);
+	res = strftime(ts, sizeof(char)*80, "%s",&tm); 
+	if (res) {
+		res = attr_set(sspath,"crtime",ts,res,ATTR_DONTFOLLOW);
+		if (res != 0) {
+			fprintf( stderr, "Error: attr_setf\n");
+		} 
+	} else {
+		fprintf(stderr,"Error: strftime failed %d\n",res);
+	}
+	free(ts);
+	free(sspath);
+	return 0;
 }
 
 int do_delete_subvolume(int argc, char **argv)
@@ -1013,7 +1161,7 @@ int do_get_default_subvol(int nargs, char **argv)
 		fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
 		return 12;
 	}
-	ret = list_subvols(fd, 0, 1);
+	ret = list_subvols(fd, 0, NULL, NULL);
 	if (ret)
 		return 19;
 	return 0;
diff --git a/btrfs_cmds.h b/btrfs_cmds.h
index 81182b1..f53c113 100644
--- a/btrfs_cmds.h
+++ b/btrfs_cmds.h
@@ -33,7 +33,7 @@ int do_resize(int nargs, char **argv);
 int do_subvol_list(int nargs, char **argv);
 int do_set_default_subvol(int nargs, char **argv);
 int do_get_default_subvol(int nargs, char **argv);
-int list_subvols(int fd, int print_parent, int get_default);
+int list_subvols(int fd, int print_parent, struct sv_list **head, char *mnt);
 int do_df_filesystem(int nargs, char **argv);
 int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
 int do_find_newer(int argc, char **argv);
@@ -42,3 +42,4 @@ int open_file_or_dir(const char *fname);
 int do_ino_to_path(int nargs, char **argv);
 int do_logical_to_ino(int nargs, char **argv);
 char *path_for_root(int fd, u64 root);
+int test_issubvolume(char *path);
diff --git a/scrub.c b/scrub.c
index 9dca5f6..9130aa9 100644
--- a/scrub.c
+++ b/scrub.c
@@ -34,6 +34,7 @@
 
 #include "ctree.h"
 #include "ioctl.h"
+#include "autosnap.h"
 #include "btrfs_cmds.h"
 #include "utils.h"
 #include "volumes.h"
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH] [RFC] Add btrfs autosnap feature
  2012-02-29  2:59 ` [PATCH] [RFC] " asj
@ 2012-02-29  3:38   ` Anand Jain
  2012-03-01 11:54   ` cwillu
  2012-03-01 13:23   ` Roman Mamedov
  2 siblings, 0 replies; 16+ messages in thread
From: Anand Jain @ 2012-02-29  3:38 UTC (permalink / raw)
  To: linux-btrfs; +Cc: hugo, chris.mason


Hi,

  This patch added autosnap feature for the btrfs-progs.
  The link below provides autosnap guide..

     http://btrfs.ipv5.de/index.php?title=Autosnap:_Configure_your_btrfs_to_create_and_manage_snapshots_automatically_based_on_events_or_at_a_regular_frequency

  Further there is timeslider, a nautilus extension written
  in python which can be downloaded from

     git://github.com/asj/timeslider.git

  Pls follow README and install.sh for the installation of
  the same.

  Further kindly note that as of now there are some limitation
  and known bug which is documented in the above autosnap wiki
  guide.

  Appreciate any feedback.

Thanks, Anand


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] [RFC] Add btrfs autosnap feature
  2012-02-29  2:59 ` [PATCH] [RFC] " asj
  2012-02-29  3:38   ` Anand Jain
@ 2012-03-01 11:54   ` cwillu
  2012-03-02 11:34     ` Arvin Schnell
  2012-03-05  6:51     ` Anand Jain
  2012-03-01 13:23   ` Roman Mamedov
  2 siblings, 2 replies; 16+ messages in thread
From: cwillu @ 2012-03-01 11:54 UTC (permalink / raw)
  To: asj; +Cc: linux-btrfs, hugo, chris.mason

There's a few things that bother me about this, not least of all the
assumptions it makes about cron,(notably the direct modification of
crontab files, which is considered to be an internal detail if I
understand correctly, and I'm fairly certain is broken as written),
and how it writes to its own config file.  Neither has any business in
btrfs-progs in my somewhat irrelevant opinion. :p

It also does not appear to handle mountpoints in its directory walk,
which will cause grief if snapshotting /

There doesn't appear to be any reason for the scratch file to exist at
all (one can build up the hash while reading the directories), and
keeping a scratch file in /etc/ is poor practice in the first place
(that's what /tmp and/or /var/run is for).  It's also a lot of io to
stat every file in the subvolume every time you make a snapshot, and
I'm not convinced that the walk is actually correctly implemented:
what stops an autosnap of / from including all of /proc and /sys in
the hash?

Perhaps all that is unnecessary:  rather than doing the walk, why not
make use of btrfs subvolume find-new (or rather, the syscalls it
uses)?

Prior to making a new snapshot, grab the (stored) transid of the
previous snapshot, and check if any files have been modified in the
source since that transid:  btrfs sub find "${source}"
"${previous_transid}".  If nothing is returned, then you don't have to
bother making the snapshot at all, otherwise after making the
snapshot, grab the transid via btrfs sub find "${new_snapshot}" -1,
and store it some place (even a dot file in the root of the snapshot
would work).

This avoids creating and immediately deleting a snapshot every time
nothing has changed, completely avoids the need to stat the entire
subvolume every time, and removes the dependency on the crypto libs.

There's a decent number of other gripes, more related to the actual
code than the design itself, which I'll leave to someone who spends
more time c than I do.  A bunch of them are moot in the face the above
anyway.

-- Carey

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] [RFC] Add btrfs autosnap feature
  2012-02-29  2:59 ` [PATCH] [RFC] " asj
  2012-02-29  3:38   ` Anand Jain
  2012-03-01 11:54   ` cwillu
@ 2012-03-01 13:23   ` Roman Mamedov
  2 siblings, 0 replies; 16+ messages in thread
From: Roman Mamedov @ 2012-03-01 13:23 UTC (permalink / raw)
  To: asj; +Cc: linux-btrfs, hugo, chris.mason

[-- Attachment #1: Type: text/plain, Size: 1089 bytes --]

On Wed, 29 Feb 2012 10:59:36 +0800
asj <anand.jain@oracle.com> wrote:

> This patch adds btrfs autosnap feature. This creates and
> saves the autosnap config at /etc/autosnap/config.
> Depending on the configuration, autosnap either schedules
> the snapshots by updating the crontab or provides an API
> to trigger the snapshots from the respective applications.

The way I see it, this sort of functionality would look more logical in a
separate userspace daemon, not in 'btrfs' directly.

Such daemon could operate in the background, continuously monitor the FS and
create or remove snapshots based on various criteria (and not depend on cron).

It could even become filesystem-agnostic, over time adding support for other
filesystems with snapshot functionality (ZFS and NILFS2).

It could (and should) be a separate project from the btrfs-tools altogether,
and a separate OS distribution package.

-- 
With respect,
Roman

~~~~~~~~~~~~~~~~~~~~~~~~~~~
"Stallman had a printer,
with code he could not see.
So he began to tinker,
and set the software free."

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] [RFC] Add btrfs autosnap feature
  2012-03-01 11:54   ` cwillu
@ 2012-03-02 11:34     ` Arvin Schnell
  2012-03-02 12:04       ` cwillu
  2012-03-05  6:51     ` Anand Jain
  1 sibling, 1 reply; 16+ messages in thread
From: Arvin Schnell @ 2012-03-02 11:34 UTC (permalink / raw)
  To: linux-btrfs

On Thu, Mar 01, 2012 at 05:54:40AM -0600, cwillu wrote:

> There doesn't appear to be any reason for the scratch file to exist a=
t
> all (one can build up the hash while reading the directories), and
> keeping a scratch file in /etc/ is poor practice in the first place
> (that's what /tmp and/or /var/run is for).  It's also a lot of io to
> stat every file in the subvolume every time you make a snapshot, and
> I'm not convinced that the walk is actually correctly implemented:
> what stops an autosnap of / from including all of /proc and /sys in
> the hash?
>=20
> Perhaps all that is unnecessary:  rather than doing the walk, why not
> make use of btrfs subvolume find-new (or rather, the syscalls it
> uses)?

While developing snapper I faced similar problems and looked at
find-new but unfortunately it is not sufficient. E.g. when a file
is deleted find-new does not report anything, see the reply to my
mail here one year ago [1]. Also for newly created empty files
find-new reports nothing, the same with metadata changes.

If I'm wrong or find-new gets extended I happy to implement it in
snapper.

Regards,
  Arvin

[1] http://www.spinics.net/lists/linux-btrfs/msg08683.html

--=20
Arvin Schnell, <aschnell@suse.de>
Senior Software Engineer, Research & Development
SUSE LINUX Products GmbH, GF: Jeff Hawn, Jennifer Guild, Felix Imend=F6=
rffer, HRB 16746 (AG N=FCrnberg)
Maxfeldstra=DFe 5
90409 N=FCrnberg
Germany
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" =
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] [RFC] Add btrfs autosnap feature
  2012-03-02 11:34     ` Arvin Schnell
@ 2012-03-02 12:04       ` cwillu
  2012-03-02 12:25         ` Sander
  0 siblings, 1 reply; 16+ messages in thread
From: cwillu @ 2012-03-02 12:04 UTC (permalink / raw)
  To: Arvin Schnell; +Cc: linux-btrfs

>> Perhaps all that is unnecessary: =C2=A0rather than doing the walk, w=
hy not
>> make use of btrfs subvolume find-new (or rather, the syscalls it
>> uses)?
>
> While developing snapper I faced similar problems and looked at
> find-new but unfortunately it is not sufficient. E.g. when a file
> is deleted find-new does not report anything, see the reply to my
> mail here one year ago [1]. Also for newly created empty files
> find-new reports nothing, the same with metadata changes.
>
> If I'm wrong or find-new gets extended I happy to implement it in
> snapper.

=46or a system-wide undo'ish sort of thing that I think autosnapper is
going for, it should work quite nicely, but you're right that it
doesn't help a whole lot with a backup system.  It can't tell you
which files were touched or deleted, but it will still tell you that
_something_ in the subvolume was touched, modified or deleted (at
least, as of the last commit), which is all you need if you're only
ever comparing it to its source.

-- Carey
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" =
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] [RFC] Add btrfs autosnap feature
  2012-03-02 12:04       ` cwillu
@ 2012-03-02 12:25         ` Sander
  0 siblings, 0 replies; 16+ messages in thread
From: Sander @ 2012-03-02 12:25 UTC (permalink / raw)
  To: cwillu; +Cc: Arvin Schnell, linux-btrfs

cwillu wrote (ao):
> > While developing snapper I faced similar problems and looked at
> > find-new but unfortunately it is not sufficient. E.g. when a file
> > is deleted find-new does not report anything, see the reply to my
> > mail here one year ago [1]. Also for newly created empty files
> > find-new reports nothing, the same with metadata changes.

> For a system-wide undo'ish sort of thing that I think autosnapper is
> going for, it should work quite nicely, but you're right that it
> doesn't help a whole lot with a backup system.  It can't tell you
> which files were touched or deleted, but it will still tell you that
> _something_ in the subvolume was touched, modified or deleted (at
> least, as of the last commit), which is all you need if you're only
> ever comparing it to its source.

Tar can remove deleted files for you during a restore. This is (imho) a
really cool feature of tar, and I use it in combination with btrfs
snapshots.

https://www.gnu.org/software/tar/manual/tar.html#SEC94

"The option `--listed-incremental' instructs tar to operate on an
incremental archive with additional metadata stored in a standalone
file, called a snapshot file. The purpose of this file is to help
determine which files have been changed, added or deleted since the last
backup"

"When extracting from the incremental backup GNU tar attempts to restore
the exact state the file system had when the archive was created. In
particular, it will delete those files in the file system that did not
exist in their directories when the archive was created"

	Sander

-- 
Humilis IT Services and Solutions
http://www.humilis.net

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] [RFC] Add btrfs autosnap feature
  2012-03-01 11:54   ` cwillu
  2012-03-02 11:34     ` Arvin Schnell
@ 2012-03-05  6:51     ` Anand Jain
  2012-03-05  7:07       ` Fajar A. Nugraha
  2012-03-05 10:21       ` Anand Jain
  1 sibling, 2 replies; 16+ messages in thread
From: Anand Jain @ 2012-03-05  6:51 UTC (permalink / raw)
  To: cwillu; +Cc: linux-btrfs, hugo, chris.mason


>(notably the direct modification of
> crontab files, which is considered to be an internal detail if I
> understand correctly, and I'm fairly certain is broken as written),

  I did came across that point of view however, using crontab cli in the
  program wasn't convincing either, (library call would have been better).
  any other better ways to manage cron entries ?

> and how it writes to its own config file.

  Hm. not sure if I understand this. you mean having binary file not
  a good idea ?

> Neither has any business in btrfs-progs in my somewhat irrelevant opinion. :p

  A separate autosnap script (outside btrfs-progs) was in the original
  plan. But having an integrated solution plus without having to manage
  a new script will certainly help sysadmins IMO. (But if there is no
  strong interest I would separate it out).

> It also does not appear to handle mountpoints in its directory walk,
> which will cause grief if snapshotting /

  Not sure if I understand this correctly. I manage with fsid.
  But, as of now autosnap isn't designed to handle root OR the mount point.
  (this is covered in the caveat section in the btrfs autosnap wiki).
  We still need more work from the btrfs kernel if we are taking the
  snapshot of the mountpoint (as in the wiki FAQ as well).
  
> There doesn't appear to be any reason for the scratch file to exist at
> all (one can build up the hash while reading the directories), and
> keeping a scratch file in /etc/ is poor practice in the first place
> (that's what /tmp and/or /var/run is for).

  Right. sorry, my mistake I didn't change back to tmp location after debugging.

>  It's also a lot of io to
> stat every file in the subvolume every time you make a snapshot, and
> I'm not convinced that the walk is actually correctly implemented:
> what stops an autosnap of / from including all of /proc and /sys in
> the hash?

  Autosnap are for the subvols excluding the mount-point itself.
  Yes we need to look this part when we support the root / mount-point.
  (This is mentioned the caveat section in autosnap wiki, however will
  put more emphasis, thanks for highlighting the real problem).

> Perhaps all that is unnecessary:  rather than doing the walk, why not
> make use of btrfs subvolume find-new (or rather, the syscalls it
> uses)?
>
> Prior to making a new snapshot, grab the (stored) transid of the
> previous snapshot, and check if any files have been modified in the
> source since that transid:  btrfs sub find "${source}"
> "${previous_transid}".  If nothing is returned, then you don't have to
> bother making the snapshot at all, otherwise after making the
> snapshot, grab the transid via btrfs sub find "${new_snapshot}" -1,
> and store it some place (even a dot file in the root of the snapshot
> would work).
>
> This avoids creating and immediately deleting a snapshot every time
> nothing has changed, completely avoids the need to stat the entire
> subvolume every time, and removes the dependency on the crypto libs.
  
  right. Hashing isn't (performance) scalable, and not a good idea to
  fill SSDs with non-application-data at each autosnap. Relaying on
  transaction id will help when transactions are committed. Will get
  this coded this way.

thanks, Anand


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] [RFC] Add btrfs autosnap feature
  2012-03-05  6:51     ` Anand Jain
@ 2012-03-05  7:07       ` Fajar A. Nugraha
  2012-03-05  7:18         ` Arne Jansen
  2012-03-05 10:21       ` Anand Jain
  1 sibling, 1 reply; 16+ messages in thread
From: Fajar A. Nugraha @ 2012-03-05  7:07 UTC (permalink / raw)
  To: Anand Jain; +Cc: cwillu, linux-btrfs, hugo, chris.mason

On Mon, Mar 5, 2012 at 1:51 PM, Anand Jain <Anand.Jain@oracle.com> wrot=
e:
>
>> (notably the direct modification of
>> crontab files, which is considered to be an internal detail if I
>> understand correctly, and I'm fairly certain is broken as written),
>
>
> =A0I did came across that point of view however, using crontab cli in=
 the
> =A0program wasn't convincing either, (library call would have been be=
tter).
> =A0any other better ways to manage cron entries ?
>

/etc/cron.{d,daily,hourly} ?

--=20
=46ajar
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" =
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] [RFC] Add btrfs autosnap feature
  2012-03-05  7:07       ` Fajar A. Nugraha
@ 2012-03-05  7:18         ` Arne Jansen
  0 siblings, 0 replies; 16+ messages in thread
From: Arne Jansen @ 2012-03-05  7:18 UTC (permalink / raw)
  To: Fajar A. Nugraha; +Cc: Anand Jain, cwillu, linux-btrfs, hugo, chris.mason

On 05.03.2012 08:07, Fajar A. Nugraha wrote:
> On Mon, Mar 5, 2012 at 1:51 PM, Anand Jain <Anand.Jain@oracle.com> wrote:
>>
>>> (notably the direct modification of
>>> crontab files, which is considered to be an internal detail if I
>>> understand correctly, and I'm fairly certain is broken as written),
>>
>>
>>  I did came across that point of view however, using crontab cli in the
>>  program wasn't convincing either, (library call would have been better).
>>  any other better ways to manage cron entries ?
>>
> 
> /etc/cron.{d,daily,hourly} ?
> 

An alternative would be to have an hourly (or minutely) fixed cronjob
and let the tool decide whether any snapshots have to be created. This
way you don't have to mess around with the crontab.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] [RFC] Add btrfs autosnap feature
  2012-03-05  6:51     ` Anand Jain
  2012-03-05  7:07       ` Fajar A. Nugraha
@ 2012-03-05 10:21       ` Anand Jain
  2012-03-05 10:28         ` cwillu
  1 sibling, 1 reply; 16+ messages in thread
From: Anand Jain @ 2012-03-05 10:21 UTC (permalink / raw)
  To: cwillu; +Cc: linux-btrfs, hugo, chris.mason



>> Prior to making a new snapshot, grab the (stored) transid of the
>> previous snapshot, and check if any files have been modified in the
>> source since that transid: btrfs sub find "${source}"
>> "${previous_transid}". If nothing is returned, then you don't have to
>> bother making the snapshot at all, otherwise after making the
>> snapshot, grab the transid via btrfs sub find "${new_snapshot}" -1,
>> and store it some place (even a dot file in the root of the snapshot
>> would work).


  there might be small window of time where transid and snapshot could
  be out of sync as we know them. since there is no atomic command which
  provides both - snapshot and transid. As in the example below.
  
  Assume tgw is the transaction group write which happens after we have
  read the transaction group id.
  ---
  sync; read current tran-id and compare
  (new tgw occurs)
  snapshot
  new tgw  occurs
  sync; read current tran-id again and store
  ---

  which will result in failing to take snapshot even if there are changes.

  Certainly there will be some trade off, and below logic seems to be
  more safer...
  ---
  sync; read current tran-id and compare with previous
  new tgw occurs
  snapshot
  new tgw occurs
  store tran_id+2 (since tran_id gets added by two for a snapshot)
  ---

  which might have a situation where we have two identical snapshot.
  but a safer trade off.

thanks, Anand
  

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] [RFC] Add btrfs autosnap feature
  2012-03-05 10:21       ` Anand Jain
@ 2012-03-05 10:28         ` cwillu
  0 siblings, 0 replies; 16+ messages in thread
From: cwillu @ 2012-03-05 10:28 UTC (permalink / raw)
  To: Anand Jain; +Cc: linux-btrfs, hugo, chris.mason

> =C2=A0---
> =C2=A0sync; read current tran-id and compare
> =C2=A0(new tgw occurs)
> =C2=A0snapshot
> =C2=A0new tgw =C2=A0occurs
> =C2=A0sync; read current tran-id again and store
> =C2=A0---
>
> =C2=A0which will result in failing to take snapshot even if there are=
 changes.

"btrfs sub find-new /snapshot-xxxx -1" shows the transid of the latest
change of the snapshot, not the whole filesystem.
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" =
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 1/2] Make find_updated_files to return value instead of printing
  2012-02-29  2:59 [RFC] [PATCH] Add btrfs autosnap feature asj
  2012-02-29  2:59 ` [PATCH] [RFC] " asj
@ 2012-03-06  7:56 ` Anand jain
  2012-03-06  7:56   ` [PATCH 2/2] Use transaction id to determin if there is any change in the subvol Anand jain
  1 sibling, 1 reply; 16+ messages in thread
From: Anand jain @ 2012-03-06  7:56 UTC (permalink / raw)
  To: linux-btrfs; +Cc: chris.mason, hugo, Anand Jain

From: Anand Jain <Anand.Jain@oracle.com>

This patch made the function find_updated_files to update the transid
in a pointer instead of printing it on the stdout. This is needed by
the autosnap and anyother program which may want to find the current
transid. Note that when last_gen 3rd parameter is not -1 then
find_updated_files might still print the values on the stdout.

Signed-off-by: Anand Jain <Anand.Jain@oracle.com>
---
 btrfs-list.c |    4 ++--
 btrfs_cmds.c |    5 ++++-
 btrfs_cmds.h |    2 +-
 3 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/btrfs-list.c b/btrfs-list.c
index 61eddf9..6b642fb 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -872,7 +872,7 @@ static int print_one_extent(int fd, struct btrfs_ioctl_search_header *sh,
 	return 0;
 }
 
-int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
+int find_updated_files(int fd, u64 root_id, u64 oldest_gen, u64 *transid)
 {
 	int ret;
 	struct btrfs_ioctl_search_args args;
@@ -969,7 +969,7 @@ int find_updated_files(int fd, u64 root_id, u64 oldest_gen)
 	}
 	free(cache_dir_name);
 	free(cache_full_name);
-	printf("transid marker was %llu\n", (unsigned long long)max_found);
+	*transid = max_found;
 	return ret;
 }
 
diff --git a/btrfs_cmds.c b/btrfs_cmds.c
index 7aab105..9357305 100644
--- a/btrfs_cmds.c
+++ b/btrfs_cmds.c
@@ -275,6 +275,7 @@ int do_find_newer(int argc, char **argv)
 	int ret;
 	char *subvol;
 	u64 last_gen;
+	u64 *tranid;
 
 	subvol = argv[1];
 	last_gen = atoll(argv[2]);
@@ -294,9 +295,11 @@ int do_find_newer(int argc, char **argv)
 		fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
 		return 12;
 	}
-	ret = find_updated_files(fd, 0, last_gen);
+	ret = find_updated_files(fd, 0, last_gen, tranid);
 	if (ret)
 		return 19;
+
+	printf("transid marker was %llu\n", (unsigned long long)*tranid);
 	return 0;
 }
 
diff --git a/btrfs_cmds.h b/btrfs_cmds.h
index f53c113..218ed20 100644
--- a/btrfs_cmds.h
+++ b/btrfs_cmds.h
@@ -35,7 +35,7 @@ int do_set_default_subvol(int nargs, char **argv);
 int do_get_default_subvol(int nargs, char **argv);
 int list_subvols(int fd, int print_parent, struct sv_list **head, char *mnt);
 int do_df_filesystem(int nargs, char **argv);
-int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
+int find_updated_files(int fd, u64 root_id, u64 oldest_gen, u64 *transid);
 int do_find_newer(int argc, char **argv);
 int do_change_label(int argc, char **argv);
 int open_file_or_dir(const char *fname);
-- 
1.7.9.2.315.g25a78


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 2/2] Use transaction id to determin if there is any change in the subvol
  2012-03-06  7:56 ` [PATCH 1/2] Make find_updated_files to return value instead of printing Anand jain
@ 2012-03-06  7:56   ` Anand jain
  2012-03-06  9:07     ` [PATCH 2/2 v2] " Anand jain
  0 siblings, 1 reply; 16+ messages in thread
From: Anand jain @ 2012-03-06  7:56 UTC (permalink / raw)
  To: linux-btrfs; +Cc: chris.mason, hugo, Anand Jain

From: Anand Jain <Anand.Jain@oracle.com>

Moved from hash method of determining the FS changes to the transaction
record id method

Signed-off-by: Anand Jain <Anand.Jain@oracle.com>
---
 autosnap.c |  106 ++++++++++++++++++++++++++++++++++++++----------------------
 autosnap.h |    4 +--
 2 files changed, 70 insertions(+), 40 deletions(-)

diff --git a/autosnap.c b/autosnap.c
index beddf68..1adaf01 100644
--- a/autosnap.c
+++ b/autosnap.c
@@ -45,7 +45,7 @@
 /* during run time if not the below we use "/var/spool/cron"; */
 char cron_path[]="/var/spool/cron/crontabs";
 char autosnap_conf_file[]="/etc/autosnap/config";
-char tmp_file[]="/etc/autosnap/tmpfile";
+//char tmp_file[]="/etc/autosnap/tmpfile";
 
 
 /* Take a snapshot with the default dest and adds attributes */
@@ -59,10 +59,10 @@ int do_autosnap_now(int argc, char **argv)
 	char	**ap;
 	char	subvol[BTRFS_VOL_NAME_MAX];
 	char	sspath[BTRFS_VOL_NAME_MAX + 128];
-	char	tag[100];
-	char	new_hash[65];
+	char	tag[TAG_MAX_LEN];
+	u64	cur_tranid = 0;
+	u64	ss_tranid = 0;
 	char	*mnt;
-	FILE	*fp;
 	u8	fsid[BTRFS_FSID_SIZE];
 	struct stat sb;
 	struct rpolicy_cfg rp;
@@ -101,6 +101,7 @@ int do_autosnap_now(int argc, char **argv)
 		return -1;
 	fd = open_file_or_dir(mnt);
 	get_fsid(fd,&fsid[0]);
+	close(fd);
 	if ((res = read_config(subvol+strlen(mnt),tag,&rp,NULL,&fsid[0])) == 1) {
 		fprintf(stderr,"need to run autosnap enable for this subvol and tag pair\n");
 		return 1;
@@ -109,28 +110,46 @@ int do_autosnap_now(int argc, char **argv)
 		return 1;
 	}
 
+	/* Check if there is any change in the FS by comparing the transaction id*/
+	if (strcmp(rp.idcal, "older") == 0 ) {
+		/* Sync Subvol*/
+		a[1] = subvol;
+		ap = a;
+		res = do_fssync(1, ap);
+		if(res != 0) {
+			return -1;
+		}
+		fd = open_file_or_dir(subvol);
+		if (fd < 0) {
+			fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
+			return -1;
+		}
+		res = find_updated_files(fd, 0, -1, &cur_tranid);
+		close(fd);
+		if (res)
+			return -1;
+
+		if((stat(rp.last_ss, &sb) == 0) && (rp.last_ss_tranid == cur_tranid)) {
+			printf("FS is identical to the last snapshot. Aborting.\n"); 
+			return -1;
+		}
+	}
+
 	if ( take_autosnap(subvol, tag, sspath) !=0 )
 		return -1;
 
-	if (strcmp(rp.idcal, "older") == 0 ) {
-		fp = fopen(tmp_file, "w");
-		tree_scan(sspath, fp);
-		fclose(fp);
-		get_sha256(tmp_file, new_hash);
-		if((stat(rp.last_ss, &sb) == 0) && (strcmp(rp.last_ss_hash,new_hash) == 0)) {
-			printf("Newer snapshot is identical to the previous snapshot, deleting the newer\n"); 
-			a[1] = sspath;
-			ap = a;
-			res = do_delete_subvolume(2,ap);
-			if(res)
-				printf("do_delete_subvolume failed %d\n",res);
-		} else {
-			/* hash does not match so keep the new snasphot  OR
-			Last snapshot was deleted. */
-			update_last_hash(subvol+strlen(mnt),tag,&fsid[0],sspath,new_hash);
-		}
-		unlink(tmp_file);
+	fd = open_file_or_dir(sspath);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access '%s'\n", sspath);
+		return -1;
 	}
+	res = find_updated_files(fd, 0, -1, &ss_tranid);
+	close(fd);
+	if (res)
+		return -1;
+
+	/* tranid does not match or Last snapshot was deleted. go ahead*/
+	update_last_tranid(subvol+strlen(mnt),tag,&fsid[0],sspath,ss_tranid);
 
 	#if 0
 	/* Un-def this when we have synchronous snapshot delete */
@@ -141,7 +160,8 @@ int do_autosnap_now(int argc, char **argv)
 	if (rp.rpval != -1) {
 		res = chk_retain_bynum(subvol, rp.rpval, tag);
 		if(res != 0 ) {
-			fprintf(stderr,"Error: Check for the retainable subvol failed %d\n",res);
+			fprintf(stderr,"Error: Check for the retainable subvol failed %d\n",
+				res);
 			return -1;
 		}
 	}
@@ -457,7 +477,8 @@ int do_autosnap_enable(int argc, char **argv)
 		case 'm':
 			fcnt++;
 			if ((atoi(optarg) > 60) || (atoi(optarg) < 1)) {
-				fprintf(stderr, "Value for option -m: Minutes should be between 1 to 60\n");
+				fprintf(stderr, "Value for option -m: Minutes should be between\
+					1 to 60\n");
 				founderr++;
 			} else {
 				sprintf(freq, "*/%s * * * *", optarg);
@@ -497,7 +518,8 @@ int do_autosnap_enable(int argc, char **argv)
 			rcnt++;
 			retcnt = atoi(optarg);
 			if (retcnt <= 0) {
-				fprintf(stderr, "Value for option -c: Should be a number, snapshots to retain\n");
+				fprintf(stderr, "Value for option -c: Should be a number,\
+					snapshots to retain\n");
 				founderr++;
 			}
 			rpval = retcnt;
@@ -505,12 +527,14 @@ int do_autosnap_enable(int argc, char **argv)
 		case 'n':
 			strcpy(idcal,optarg);
 			if (!((strcmp(idcal, "disable") ==0) || (strcmp(idcal, "older") == 0))) {
-				fprintf(stderr, "Error: parameter %s should be one of disable|older\n",idcal);
+				fprintf(stderr, "Error: parameter %s should be one of disable|older\n",
+					idcal);
 				founderr++;
 			}
 			break;
 		case '?':
-			if (optopt == 't' || optopt == 'm' || optopt == 'D' || optopt == 'c' || optopt == 'D')
+			if (optopt == 't' || optopt == 'm' || optopt == 'D' || optopt == 'c'\
+				|| optopt == 'D')
 				fprintf (stderr, "Option -%c requires an argument.\n", optopt);
 			else if (isprint (optopt))
 				fprintf (stderr, "Unknown option `-%c'.\n", optopt);
@@ -1050,7 +1074,7 @@ int read_config(char *subvol, char *tag, struct rpolicy_cfg *retcfg, struct rpol
 			strcpy(cur->tag, rcfg.tag);
 			strcpy(cur->freq, rcfg.freq);
 			strcpy(cur->idcal, rcfg.idcal);
-			strcpy(cur->last_ss_hash, rcfg.last_ss_hash);
+			cur->last_ss_tranid  = rcfg.last_ss_tranid;
 			strcpy(cur->last_ss, rcfg.last_ss);
 			for(i=0; i<BTRFS_FSID_SIZE; i++)
 				cur->fsid[i]= rcfg.fsid[i];
@@ -1065,7 +1089,7 @@ int read_config(char *subvol, char *tag, struct rpolicy_cfg *retcfg, struct rpol
 				strcpy(retcfg->tag, rcfg.tag);
 				strcpy(retcfg->freq, rcfg.freq);
 				strcpy(retcfg->idcal, rcfg.idcal);
-				strcpy(retcfg->last_ss_hash, rcfg.last_ss_hash);
+				retcfg->last_ss_tranid = rcfg.last_ss_tranid;
 				strcpy(retcfg->last_ss, rcfg.last_ss);
 				for(i=0; i<BTRFS_FSID_SIZE; i++)
 					retcfg->fsid[i] = rcfg.fsid[i];
@@ -1150,7 +1174,8 @@ int delete_config(char *subvol, char *tag, u8 *fsid)
 				break;
 			}
 		} else {
-			if((strcmp(cur->subvol, subvol) == 0) && (memcmp(&(cur->fsid),fsid,BTRFS_FSID_SIZE) == 0)) {
+			if((strcmp(cur->subvol, subvol) == 0) &&\
+				(memcmp(&(cur->fsid),fsid,BTRFS_FSID_SIZE) == 0)) {
 				if(head == cur) 
 					head = cur->next;
 				prev->next = cur->next;
@@ -1170,8 +1195,8 @@ int delete_config(char *subvol, char *tag, u8 *fsid)
 	return 0;
 }
 
-/* maintain the last snapshot hash info so that identical snapshots are not taken */
-int update_last_hash(char *subvol, char *tag, u8 *fsid,char *last_ss, char *hash)
+/* maintain the trans id when last snapshot occurred */
+int update_last_tranid(char *subvol, char *tag, u8 *fsid,char *last_ss, u64 tranid)
 {
 	int	res;
 	struct rpolicy_cfg *head = NULL;
@@ -1189,7 +1214,7 @@ int update_last_hash(char *subvol, char *tag, u8 *fsid,char *last_ss, char *hash
 	while(cur != NULL) {
 		if((strcmp(cur->subvol, subvol) == 0) && (strcmp(cur->tag, tag) == 0) &&
 					(memcmp(&cur->fsid,fsid,BTRFS_FSID_SIZE)==0)) {
-			strcpy(cur->last_ss_hash,hash);
+			cur->last_ss_tranid = tranid;
 			strcpy(cur->last_ss, last_ss);
 			break;
 		}
@@ -1223,11 +1248,15 @@ int write_config(char *subvol, int rpval, char *freq, int diffsz, char *tag, cha
 		return 1;
 	}
 
+	rcfg.last_ss_tranid = 0; 
+	strcpy(rcfg.last_ss, "");
+
 	/* need to find if user is modifying an exisiting entry or creating new*/
 	while((ret = read(fp, &rcfg, sz)) > 0) {
-		//if((strcmp(rcfg.subvol, subvol) == 0) && (strcmp(rcfg.tag, tag) == 0)) break;
 		if((strcmp(rcfg.subvol, subvol) == 0) && (strcmp(rcfg.tag, tag) == 0) &&\
-			(memcmp(&rcfg.fsid,fsid,BTRFS_FSID_SIZE) == 0)) break;
+			(memcmp(&rcfg.fsid,fsid,BTRFS_FSID_SIZE) == 0)) {
+			break;
+		}
 		offset = offset + sz;
 		memset(&rcfg,0,sz);
 	}
@@ -1247,8 +1276,6 @@ int write_config(char *subvol, int rpval, char *freq, int diffsz, char *tag, cha
 	strcpy(rcfg.subvol, subvol);
 	strcpy(rcfg.tag, tag);
 	strcpy(rcfg.idcal, idcal);
-	strcpy(rcfg.last_ss_hash, "");
-	strcpy(rcfg.last_ss, "");
 
 	for(i=0;i<BTRFS_FSID_SIZE;i++)
 		rcfg.fsid[i] = *(fsid++);
@@ -1431,6 +1458,7 @@ int fs_used(char *mnt)
 	return (100 - res);
 }
 
+#ifndef DELETE
 /* generate the sha256 code for a given file */
 int get_sha256(char *fpath, char *op)
 {
@@ -1498,13 +1526,15 @@ int tree_scan( const char *path, FILE *fp)
 				entry->d_name,sb.st_mode,sb.st_nlink,sb.st_uid,sb.st_gid,\
 				ctime(&sb.st_mtime),ctime(&sb.st_ctime));
 		}
-		if(!(S_ISREG(sb.st_mode)) && (strcmp(".",entry->d_name)) && (strcmp("..",entry->d_name))) {
+		if(!(S_ISREG(sb.st_mode)) && (strcmp(".",entry->d_name)) &&\
+			(strcmp("..",entry->d_name))) {
 				tree_scan( spath,fp);
 		}
 	}
 	closedir( dir);
 	return(0);
 }
+#endif
 
 /* obtain mnt from the subvol path */
 int subvol_to_mnt(char *subvol, char **mnt)
diff --git a/autosnap.h b/autosnap.h
index dc126b6..2b4322c 100644
--- a/autosnap.h
+++ b/autosnap.h
@@ -45,7 +45,7 @@ struct rpolicy_cfg {
 	char 	freq[TAG_MAX_LEN];
 	char	tag[TAG_MAX_LEN];
 	char	idcal[50];
-	char	last_ss_hash[65];
+	u64	last_ss_tranid;
 	char	last_ss[BTRFS_VOL_NAME_MAX];
 	int	rpval;
 	int	diffsz;
@@ -62,7 +62,7 @@ int get_fsid(int fd, u8 *fsidp);
 int chk_fslimit(char *subvol);
 char *find_oldest_snap(char *mnt, char *parent, char *tag);
 int write_config(char *subvol, int rpval, char *freq, int diffsz, char *tag, char *idcal, u8 *fsid);
-int update_last_hash(char *subvol, char *tag, u8 *fsid,char *last_ss, char *hash);
+int update_last_tranid(char *subvol, char *tag, u8 *fsid,char *last_ss, u64 tranid);
 int delete_config(char *subvol, char *tag, u8 *fsid);
 int rewrite_config(struct rpolicy_cfg *cfg);
 int read_config(char *subvol, char *tag, struct rpolicy_cfg *retcfg, struct rpolicy_cfg **head, u8 *fsid);
-- 
1.7.9.2.315.g25a78


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 2/2 v2] Use transaction id to determin if there is any change in the subvol
  2012-03-06  7:56   ` [PATCH 2/2] Use transaction id to determin if there is any change in the subvol Anand jain
@ 2012-03-06  9:07     ` Anand jain
  0 siblings, 0 replies; 16+ messages in thread
From: Anand jain @ 2012-03-06  9:07 UTC (permalink / raw)
  To: linux-btrfs; +Cc: chris.mason, hugo, Anand Jain

From: Anand Jain <Anand.Jain@oracle.com>

Moved from hash method of determining the FS changes to the transaction
record id method

Signed-off-by: Anand Jain <Anand.Jain@oracle.com>
---
 Makefile   |    2 +-
 autosnap.c |  176 ++++++++++++++++++++++--------------------------------------
 autosnap.h |    4 +-
 3 files changed, 66 insertions(+), 116 deletions(-)

diff --git a/Makefile b/Makefile
index dee5822..47095a0 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@ DEPFLAGS = -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@
 INSTALL = install
 prefix ?= /usr/local
 bindir = $(prefix)/bin
-LIBS = -luuid -lattr -lcrypto
+LIBS = -luuid -lattr
 RESTORE_LIBS=-lz
 
 progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \
diff --git a/autosnap.c b/autosnap.c
index beddf68..f2c785f 100644
--- a/autosnap.c
+++ b/autosnap.c
@@ -28,7 +28,6 @@
 #include <uuid/uuid.h>
 #include <ctype.h>
 #include <attr/attributes.h>
-#include </usr/include/openssl/sha.h>
 #include <sys/statvfs.h>
 #include <sys/syscall.h>
 #include "kerncompat.h"
@@ -45,7 +44,6 @@
 /* during run time if not the below we use "/var/spool/cron"; */
 char cron_path[]="/var/spool/cron/crontabs";
 char autosnap_conf_file[]="/etc/autosnap/config";
-char tmp_file[]="/etc/autosnap/tmpfile";
 
 
 /* Take a snapshot with the default dest and adds attributes */
@@ -59,10 +57,10 @@ int do_autosnap_now(int argc, char **argv)
 	char	**ap;
 	char	subvol[BTRFS_VOL_NAME_MAX];
 	char	sspath[BTRFS_VOL_NAME_MAX + 128];
-	char	tag[100];
-	char	new_hash[65];
+	char	tag[TAG_MAX_LEN];
+	u64	cur_tranid = 0;
+	u64	ss_tranid = 0;
 	char	*mnt;
-	FILE	*fp;
 	u8	fsid[BTRFS_FSID_SIZE];
 	struct stat sb;
 	struct rpolicy_cfg rp;
@@ -101,6 +99,7 @@ int do_autosnap_now(int argc, char **argv)
 		return -1;
 	fd = open_file_or_dir(mnt);
 	get_fsid(fd,&fsid[0]);
+	close(fd);
 	if ((res = read_config(subvol+strlen(mnt),tag,&rp,NULL,&fsid[0])) == 1) {
 		fprintf(stderr,"need to run autosnap enable for this subvol and tag pair\n");
 		return 1;
@@ -109,28 +108,46 @@ int do_autosnap_now(int argc, char **argv)
 		return 1;
 	}
 
+	/* Check if there is any change in the FS by comparing the transaction id*/
+	if (strcmp(rp.idcal, "older") == 0 ) {
+		/* Sync Subvol*/
+		a[1] = subvol;
+		ap = a;
+		res = do_fssync(1, ap);
+		if(res != 0) {
+			return -1;
+		}
+		fd = open_file_or_dir(subvol);
+		if (fd < 0) {
+			fprintf(stderr, "ERROR: can't access '%s'\n", subvol);
+			return -1;
+		}
+		res = find_updated_files(fd, 0, -1, &cur_tranid);
+		close(fd);
+		if (res)
+			return -1;
+
+		if((stat(rp.last_ss, &sb) == 0) && (rp.last_ss_tranid == cur_tranid)) {
+			printf("FS is identical to the last snapshot. Aborting.\n"); 
+			return -1;
+		}
+	}
+
 	if ( take_autosnap(subvol, tag, sspath) !=0 )
 		return -1;
 
-	if (strcmp(rp.idcal, "older") == 0 ) {
-		fp = fopen(tmp_file, "w");
-		tree_scan(sspath, fp);
-		fclose(fp);
-		get_sha256(tmp_file, new_hash);
-		if((stat(rp.last_ss, &sb) == 0) && (strcmp(rp.last_ss_hash,new_hash) == 0)) {
-			printf("Newer snapshot is identical to the previous snapshot, deleting the newer\n"); 
-			a[1] = sspath;
-			ap = a;
-			res = do_delete_subvolume(2,ap);
-			if(res)
-				printf("do_delete_subvolume failed %d\n",res);
-		} else {
-			/* hash does not match so keep the new snasphot  OR
-			Last snapshot was deleted. */
-			update_last_hash(subvol+strlen(mnt),tag,&fsid[0],sspath,new_hash);
-		}
-		unlink(tmp_file);
+	fd = open_file_or_dir(sspath);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: can't access '%s'\n", sspath);
+		return -1;
 	}
+	res = find_updated_files(fd, 0, -1, &ss_tranid);
+	close(fd);
+	if (res)
+		return -1;
+
+	/* tranid does not match or Last snapshot was deleted. go ahead*/
+	update_last_tranid(subvol+strlen(mnt),tag,&fsid[0],sspath,ss_tranid);
 
 	#if 0
 	/* Un-def this when we have synchronous snapshot delete */
@@ -141,7 +158,8 @@ int do_autosnap_now(int argc, char **argv)
 	if (rp.rpval != -1) {
 		res = chk_retain_bynum(subvol, rp.rpval, tag);
 		if(res != 0 ) {
-			fprintf(stderr,"Error: Check for the retainable subvol failed %d\n",res);
+			fprintf(stderr,"Error: Check for the retainable subvol failed %d\n",
+				res);
 			return -1;
 		}
 	}
@@ -457,7 +475,8 @@ int do_autosnap_enable(int argc, char **argv)
 		case 'm':
 			fcnt++;
 			if ((atoi(optarg) > 60) || (atoi(optarg) < 1)) {
-				fprintf(stderr, "Value for option -m: Minutes should be between 1 to 60\n");
+				fprintf(stderr, "Value for option -m: Minutes should be between\
+					1 to 60\n");
 				founderr++;
 			} else {
 				sprintf(freq, "*/%s * * * *", optarg);
@@ -497,7 +516,8 @@ int do_autosnap_enable(int argc, char **argv)
 			rcnt++;
 			retcnt = atoi(optarg);
 			if (retcnt <= 0) {
-				fprintf(stderr, "Value for option -c: Should be a number, snapshots to retain\n");
+				fprintf(stderr, "Value for option -c: Should be a number,\
+					snapshots to retain\n");
 				founderr++;
 			}
 			rpval = retcnt;
@@ -505,12 +525,14 @@ int do_autosnap_enable(int argc, char **argv)
 		case 'n':
 			strcpy(idcal,optarg);
 			if (!((strcmp(idcal, "disable") ==0) || (strcmp(idcal, "older") == 0))) {
-				fprintf(stderr, "Error: parameter %s should be one of disable|older\n",idcal);
+				fprintf(stderr, "Error: parameter %s should be one of disable|older\n",
+					idcal);
 				founderr++;
 			}
 			break;
 		case '?':
-			if (optopt == 't' || optopt == 'm' || optopt == 'D' || optopt == 'c' || optopt == 'D')
+			if (optopt == 't' || optopt == 'm' || optopt == 'D' || optopt == 'c'\
+				|| optopt == 'D')
 				fprintf (stderr, "Option -%c requires an argument.\n", optopt);
 			else if (isprint (optopt))
 				fprintf (stderr, "Unknown option `-%c'.\n", optopt);
@@ -1050,7 +1072,7 @@ int read_config(char *subvol, char *tag, struct rpolicy_cfg *retcfg, struct rpol
 			strcpy(cur->tag, rcfg.tag);
 			strcpy(cur->freq, rcfg.freq);
 			strcpy(cur->idcal, rcfg.idcal);
-			strcpy(cur->last_ss_hash, rcfg.last_ss_hash);
+			cur->last_ss_tranid  = rcfg.last_ss_tranid;
 			strcpy(cur->last_ss, rcfg.last_ss);
 			for(i=0; i<BTRFS_FSID_SIZE; i++)
 				cur->fsid[i]= rcfg.fsid[i];
@@ -1065,7 +1087,7 @@ int read_config(char *subvol, char *tag, struct rpolicy_cfg *retcfg, struct rpol
 				strcpy(retcfg->tag, rcfg.tag);
 				strcpy(retcfg->freq, rcfg.freq);
 				strcpy(retcfg->idcal, rcfg.idcal);
-				strcpy(retcfg->last_ss_hash, rcfg.last_ss_hash);
+				retcfg->last_ss_tranid = rcfg.last_ss_tranid;
 				strcpy(retcfg->last_ss, rcfg.last_ss);
 				for(i=0; i<BTRFS_FSID_SIZE; i++)
 					retcfg->fsid[i] = rcfg.fsid[i];
@@ -1150,7 +1172,8 @@ int delete_config(char *subvol, char *tag, u8 *fsid)
 				break;
 			}
 		} else {
-			if((strcmp(cur->subvol, subvol) == 0) && (memcmp(&(cur->fsid),fsid,BTRFS_FSID_SIZE) == 0)) {
+			if((strcmp(cur->subvol, subvol) == 0) &&\
+				(memcmp(&(cur->fsid),fsid,BTRFS_FSID_SIZE) == 0)) {
 				if(head == cur) 
 					head = cur->next;
 				prev->next = cur->next;
@@ -1170,8 +1193,8 @@ int delete_config(char *subvol, char *tag, u8 *fsid)
 	return 0;
 }
 
-/* maintain the last snapshot hash info so that identical snapshots are not taken */
-int update_last_hash(char *subvol, char *tag, u8 *fsid,char *last_ss, char *hash)
+/* maintain the trans id when last snapshot occurred */
+int update_last_tranid(char *subvol, char *tag, u8 *fsid,char *last_ss, u64 tranid)
 {
 	int	res;
 	struct rpolicy_cfg *head = NULL;
@@ -1189,7 +1212,7 @@ int update_last_hash(char *subvol, char *tag, u8 *fsid,char *last_ss, char *hash
 	while(cur != NULL) {
 		if((strcmp(cur->subvol, subvol) == 0) && (strcmp(cur->tag, tag) == 0) &&
 					(memcmp(&cur->fsid,fsid,BTRFS_FSID_SIZE)==0)) {
-			strcpy(cur->last_ss_hash,hash);
+			cur->last_ss_tranid = tranid;
 			strcpy(cur->last_ss, last_ss);
 			break;
 		}
@@ -1223,11 +1246,15 @@ int write_config(char *subvol, int rpval, char *freq, int diffsz, char *tag, cha
 		return 1;
 	}
 
+	rcfg.last_ss_tranid = 0; 
+	strcpy(rcfg.last_ss, "");
+
 	/* need to find if user is modifying an exisiting entry or creating new*/
 	while((ret = read(fp, &rcfg, sz)) > 0) {
-		//if((strcmp(rcfg.subvol, subvol) == 0) && (strcmp(rcfg.tag, tag) == 0)) break;
 		if((strcmp(rcfg.subvol, subvol) == 0) && (strcmp(rcfg.tag, tag) == 0) &&\
-			(memcmp(&rcfg.fsid,fsid,BTRFS_FSID_SIZE) == 0)) break;
+			(memcmp(&rcfg.fsid,fsid,BTRFS_FSID_SIZE) == 0)) {
+			break;
+		}
 		offset = offset + sz;
 		memset(&rcfg,0,sz);
 	}
@@ -1247,8 +1274,6 @@ int write_config(char *subvol, int rpval, char *freq, int diffsz, char *tag, cha
 	strcpy(rcfg.subvol, subvol);
 	strcpy(rcfg.tag, tag);
 	strcpy(rcfg.idcal, idcal);
-	strcpy(rcfg.last_ss_hash, "");
-	strcpy(rcfg.last_ss, "");
 
 	for(i=0;i<BTRFS_FSID_SIZE;i++)
 		rcfg.fsid[i] = *(fsid++);
@@ -1431,81 +1456,6 @@ int fs_used(char *mnt)
 	return (100 - res);
 }
 
-/* generate the sha256 code for a given file */
-int get_sha256(char *fpath, char *op)
-{
-	int i;
-	int br = 0;
-	unsigned char hash[SHA256_DIGEST_LENGTH];
-	FILE *fp = fopen(fpath, "r");
-	SHA256_CTX sha256;
-	const int bs = 32768;
-	unsigned char *buf = malloc(bs);
-
-	if(!fp)
-		return -1;
-
-	SHA256_Init(&sha256);
-
-	if(!buf)
-		return -1;
-
-	while((br = fread(buf, 1, bs, fp)))
-		SHA256_Update(&sha256, buf, br);
-
-	SHA256_Final(hash, &sha256);
-
-	for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
-		sprintf(op + (i * 2), "%02x", hash[i]);
-
-	op[64]='\0';
-
-	fclose(fp);
-	free(buf);
-	return 0;
-}
-
-/* Lists all files and dir of a path and its child */
-int tree_scan( const char *path, FILE *fp)
-{
-	char            spath[FILENAME_MAX] = "";
-	char            calc_hash[65];
-	DIR             *dir;
-	struct dirent   *entry;
-	struct stat     sb;
-
-	if( !(dir = opendir( path))) {
-		perror("opendir");
-		return -1;
-	}
-
-	for( entry = readdir( dir); entry; entry = readdir( dir)) {
-		if((strcmp(".",entry->d_name) == 0) || (strcmp("..",entry->d_name) == 0))
-			continue;
-
-		if(strcmp(entry->d_name,".autosnap") == 0)
-			continue;
-
-		sprintf(spath, "%s/%s", path, entry->d_name);
-		stat(spath,&sb);
-		if(!(S_ISREG(sb.st_mode))) {
-			get_sha256(spath, calc_hash);
-			fprintf(fp,"%s %x %x %x %x %s %s %s\n",
-				entry->d_name,sb.st_mode,sb.st_nlink,sb.st_uid,sb.st_gid,\
-				ctime(&sb.st_mtime),ctime(&sb.st_ctime),calc_hash);
-		} else {
-			fprintf(fp,"%s %x %x %x %x %s %s\n",
-				entry->d_name,sb.st_mode,sb.st_nlink,sb.st_uid,sb.st_gid,\
-				ctime(&sb.st_mtime),ctime(&sb.st_ctime));
-		}
-		if(!(S_ISREG(sb.st_mode)) && (strcmp(".",entry->d_name)) && (strcmp("..",entry->d_name))) {
-				tree_scan( spath,fp);
-		}
-	}
-	closedir( dir);
-	return(0);
-}
-
 /* obtain mnt from the subvol path */
 int subvol_to_mnt(char *subvol, char **mnt)
 {
diff --git a/autosnap.h b/autosnap.h
index dc126b6..2b4322c 100644
--- a/autosnap.h
+++ b/autosnap.h
@@ -45,7 +45,7 @@ struct rpolicy_cfg {
 	char 	freq[TAG_MAX_LEN];
 	char	tag[TAG_MAX_LEN];
 	char	idcal[50];
-	char	last_ss_hash[65];
+	u64	last_ss_tranid;
 	char	last_ss[BTRFS_VOL_NAME_MAX];
 	int	rpval;
 	int	diffsz;
@@ -62,7 +62,7 @@ int get_fsid(int fd, u8 *fsidp);
 int chk_fslimit(char *subvol);
 char *find_oldest_snap(char *mnt, char *parent, char *tag);
 int write_config(char *subvol, int rpval, char *freq, int diffsz, char *tag, char *idcal, u8 *fsid);
-int update_last_hash(char *subvol, char *tag, u8 *fsid,char *last_ss, char *hash);
+int update_last_tranid(char *subvol, char *tag, u8 *fsid,char *last_ss, u64 tranid);
 int delete_config(char *subvol, char *tag, u8 *fsid);
 int rewrite_config(struct rpolicy_cfg *cfg);
 int read_config(char *subvol, char *tag, struct rpolicy_cfg *retcfg, struct rpolicy_cfg **head, u8 *fsid);
-- 
1.7.9.2.315.g25a78


^ permalink raw reply related	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2012-03-06  9:07 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-02-29  2:59 [RFC] [PATCH] Add btrfs autosnap feature asj
2012-02-29  2:59 ` [PATCH] [RFC] " asj
2012-02-29  3:38   ` Anand Jain
2012-03-01 11:54   ` cwillu
2012-03-02 11:34     ` Arvin Schnell
2012-03-02 12:04       ` cwillu
2012-03-02 12:25         ` Sander
2012-03-05  6:51     ` Anand Jain
2012-03-05  7:07       ` Fajar A. Nugraha
2012-03-05  7:18         ` Arne Jansen
2012-03-05 10:21       ` Anand Jain
2012-03-05 10:28         ` cwillu
2012-03-01 13:23   ` Roman Mamedov
2012-03-06  7:56 ` [PATCH 1/2] Make find_updated_files to return value instead of printing Anand jain
2012-03-06  7:56   ` [PATCH 2/2] Use transaction id to determin if there is any change in the subvol Anand jain
2012-03-06  9:07     ` [PATCH 2/2 v2] " Anand jain

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).