aboutsummaryrefslogtreecommitdiffstats
path: root/src/parse_synclist.c
blob: 0c2a7aa6a8924f77a5b290194d095f0cecd52dd1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
/*  
   Copyright (C)  2006-2008 Renaissance Technologies Corp.
                  main developer: HP Wei <hp@rentec.com>
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

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

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

#include "main.h"
#include <stdlib.h>
#include <limits.h> /* to define PATH_MAX */

/* 
   Get the next to-be-synced file from synclist which is the output of
   parseRsyncList.C 
   The latter in turn parses the output from rsync.
   In other words, we use rsync in dry-run mode to get the
   files that need to be synced.

   to port to large_file environment: use off_t for size
*/

extern int verbose;

char basedir[PATH_MAX]; /* baseDir for the syncing */
FILE *fd;
struct stat st;  /* stat for the current file */

unsigned int nEntries;
int cur_entry;            /* file id -- <0 if it needs backup */
unsigned int nPages;
unsigned int last_bytes;  /* number of bytes for the last page */
int toRmFirst = FALSE;    /* flag rm existing file and then sync */
int file_changed = FALSE; /* flag to indicate if file has been changed during syncing */

unsigned int total_pages;
off_t total_bytes;

int isDelete, isHardlink;
char filename[PATH_MAX];
char fullname[PATH_MAX];
char linktar[PATH_MAX];

int init_synclist(char * synclist_path, char *bdir)
{
  char line[PATH_MAX];
  nEntries = 0;
  strcpy(basedir, bdir);

  if ((fd = fopen(synclist_path, "r")) == NULL) {
    fprintf(stderr, "Cannot open synclist file = %s \n", synclist_path);
    return FAIL;
  }

  while (fgets(line, PATH_MAX, fd) != NULL) nEntries++;
  if (nEntries == 0) { 
    fclose(fd); 
    fprintf(stderr, "Empty entires in synclist file = %s\n", synclist_path);
    return SUCCESS; /* OK, nothing to sync */
  } 
  rewind(fd);

  cur_entry = 0;
  total_pages = 0; 
  total_bytes = 0;
  
  return SUCCESS;
}  

/* 
   pages_for_file calculates the number (int) of pages for the current file
   and returns that number in an (int) type.  
   So, max_pages = 2**31 = 2147483648
   which corresponds to a file_size of 2**31 * 64512 = 1.38e14
   [ general limit = (1<<(sizeof(int)*8)) * PAGE_SIZE ]
   At that time, the type of page_number needs to be upgraded :)

   to_delete   -> -1
   normal_file -> number_of_pages
   softlink    -> 0
   hardlink    -> 0
   directory   -> 0
*/
int pages_for_file()
{  
  if (isDelete) {                              /* to be deleted directory or file */
    return TO_DELETE;
  }

  if (S_ISREG(st.st_mode)){
    int n;
    if (st.st_nlink > 1) return 0;              /* hardlink file */

    n = (int)((st.st_size)/(off_t)PAGE_SIZE);   /* regular file */
    if ((st.st_size)%((off_t)PAGE_SIZE) == 0) {
      last_bytes = (unsigned int)(PAGE_SIZE);
      return n;
    } else {      
      last_bytes = (unsigned int)(st.st_size - (off_t)n * (off_t)PAGE_SIZE);
      return n+1;
    }
    /*return ((st.st_size)%((off_t)PAGE_SIZE) == 0) ? n : n+1 ;*/
  }
  if (S_ISLNK(st.st_mode)){
    return 0;                                   /* softlink */
  }
  return 0;                                     /* directory */
}

off_t bytes_for_file()
{
  return st.st_size;
}

unsigned int get_nPages()  /* for this file */
{
  return nPages;
}

void strip(char * str)
{
  /* remove trailing \n and spaces */
  char *pc = &str[strlen(str)-1];
  while (*pc == ' ' || *pc == '\n') { 
    *(pc--) = '\0';
  }
}

int same_stat_for_file()
{
  /* check if current stat is same as that when get_next_entry is called */
  struct stat st1;

  if(lstat(fullname, &st1) < 0) {
    if (verbose >=1) perror(fullname);
    return FAIL;
  }
  
  if (st1.st_size != st.st_size || st1.st_mode != st.st_mode || 
      st1.st_mtime != st.st_mtime) {
    return FAIL; /* the file has changed */
  }
  return SUCCESS;

}

int is_hardlink_line(char * line)
{
  /* when line is in the form of 
     string1 string2
     it can be either a filename (string1 string2)
     or a hardlink string1 => string2
  */
  struct stat st;
  char fn[PATH_MAX];
  strcpy(fn, basedir);
  strcat(fn, "/");
  strcat(fn, line);

  /* if the whole line is not a file, then we are dealing with hardlink case */
  return (lstat(fn, &st) < 0);
  /* cannot deal with the situation 
        str1  is a file
        str2  is a hardlink to str1
        str1 str2   is a file
     AND if we need to sync 
        str1 and 'str1 str2' at the same time.
     But this situation is very rare. */
}

int get_next_entry(int current_file_id)
{
  char *c;
  char line[PATH_MAX];

  /* inside this function, cur_entry is set to be positve to facilitate processing */
  cur_entry = current_file_id; /* from main loop's index, starting with 1 */
  
  isDelete = FALSE;
  isHardlink = FALSE;

  fgets(line, PATH_MAX, fd);
  strip(line);

  if (current_file_id == nEntries) {
    fclose(fd); /* close the synclist file */
    if (verbose>=2) fprintf(stderr, "no more entry in synclist.\n");
  }

  if (verbose>=2) {
    fprintf(stderr, "Got current entry = %d (total= %d)\n", cur_entry, nEntries);
    fprintf(stderr, "%s\n", line);
  }

  strcpy(fullname, basedir);
  strcat(fullname, "/");
  if (strncmp(line, "deleting ", 9)==0) {
    isDelete = TRUE;
    nPages = -1;
    strcat(fullname, &line[9]);
    strcpy(filename, &line[9]);
    if (needBackup(fullname)) cur_entry = -cur_entry;
    return SUCCESS;
  } else if ((c = strchr(line, ' '))!=NULL && is_hardlink_line(line)) {
    /* is it a hardlink -- two filenames separated by a space */
    char fn[PATH_MAX];
    isHardlink = TRUE;
    strncpy(fn, line, (c - line));
    fn[c-line] = '\0';
    strcat(fullname, fn);
    strcpy(filename, fn);
    strcpy(linktar, c+1);    
  } else {    
    /* normal, single entry */
    strcat(fullname, line);
    strcpy(filename, line);
  }

  /* update stat */
  if(lstat(fullname, &st) < 0) {
    if (verbose >=1) perror(fullname);
    return FAIL;
  }

  if (S_ISLNK(st.st_mode)) {
    int linklen;
    linklen = readlink(fullname, linktar, PATH_MAX);
    /* readlink doesn't null-terminate the string */
    *(linktar + linklen) = '\0';
  } else if (st.st_nlink>1 && !isHardlink) {
    /* this is the target file that others (hard)link to.
       treat it like a normal file */
    st.st_nlink = 1;
  }

  nPages = pages_for_file();
  if (nPages > 0) { /* for regular files */
    total_pages += nPages;
    total_bytes += st.st_size;    
  }

  if (needBackup(fullname)) cur_entry = -cur_entry;

  file_changed = FALSE;

  return SUCCESS;
}

void adjust_totals()
{
  if (nPages > 0) {
    total_pages -= nPages;
    total_bytes -= st.st_size;
  }
}

/* some accessors */
unsigned int total_entries() { return nEntries; }

int current_entry() { return cur_entry; }

int is_softlink() { return S_ISLNK(st.st_mode); }

int is_hardlink() { return isHardlink; }

int is_directory() { return S_ISDIR(st.st_mode); }

char * getFilename() { /* relative to basedir */ return &filename[0]; }

char * getFullname() { return &fullname[0]; }

/* 
   The following three fx are used to fill file_info into the send_buffer.
   They return the number of bytes being written into the buf, including the \0 byte.
*/
unsigned int fill_in_stat(char *buf)
{
  /* load into buf area the stat info in ascii format */
  if (isDelete) 
    sprintf(buf, "0 0 0 0 0 0 0 0");
  else {
    #ifdef _LARGEFILE_SOURCE
    sprintf(buf, "%u %u %u %u %llu %lu %lu %d", st.st_mode, st.st_nlink,
	    st.st_uid, st.st_gid, st.st_size, st.st_atime, st.st_mtime,
	    toRmFirst);
    #else
    sprintf(buf, "%u %u %u %u %lu %lu %lu %d", st.st_mode, st.st_nlink,
	    st.st_uid, st.st_gid, st.st_size, st.st_atime, st.st_mtime,
	    toRmFirst);
    #endif
  }
  
  return strlen(buf)+1;
}

unsigned int fill_in_filename(char * buf)
{
  strcpy(buf, filename);
  return strlen(buf)+1;
}

unsigned int fill_in_linktar(char *buf)
{
  strcpy(buf, linktar);
  return strlen(buf)+1;
}