~ubuntu-branches/ubuntu/raring/libtrash/raring

« back to all changes in this revision

Viewing changes to src/rename.c

  • Committer: Bazaar Package Importer
  • Author(s): Timshel Knoll
  • Date: 2002-03-08 00:27:09 UTC
  • Revision ID: james.westby@ubuntu.com-20020308002709-p7uz03e7iouawwih
Tags: upstream-0.8
ImportĀ upstreamĀ versionĀ 0.8

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright 2001, 2002 Manuel Arriaga
 
2
 * 
 
3
 * 
 
4
 * This program is free software; you can redistribute it and/or modify 
 
5
 * it under the terms of the GNU General Public License as published by
 
6
 * the Free Software Foundation; either version 2 of the License, or
 
7
 * (at your option) any later version.
 
8
 * 
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 * GNU General Public License for more details.
 
13
 * 
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software
 
16
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
 * 
 
18
 */
 
19
 
 
20
#include <stdlib.h>
 
21
#include <errno.h>
 
22
 
 
23
#include <sys/stat.h>
 
24
 
 
25
#include "trash.h"
 
26
 
 
27
 
 
28
static int rename_handle_error(const char *oldpath, const char *newpath, 
 
29
                               int (*real_rename) (const char*, const char*), int in_case_of_failure);
 
30
 
 
31
/* When can rename() cause data loss? 
 
32
 * When both its arguments refer to existing files, then the original file
 
33
 * at newpath will be lost.
 
34
 * 
 
35
 * What this version of rename() does: if a file already exists at newpath, we move it 
 
36
 * (with the "real" rename()) to the trash can and then hand over control to GNU libc's 
 
37
 * rename().
 
38
 * 
 
39
 * We also take care so that, if we fail, errno is either zero (if a
 
40
 * "libtrash-specific" error occurred) or has a meaningful value which the
 
41
 * caller should know how to interpret after a call to rename(). This way,
 
42
 * we avoid confusing the caller with a errno set by some other GNU libc
 
43
 * function used by the libtrash wrapper.
 
44
 * 
 
45
 */
 
46
 
 
47
int rename(const char *oldpath, const char *newpath)
 
48
{
 
49
   
 
50
   struct stat path_stat;
 
51
   
 
52
   char *absolute_newpath = NULL;
 
53
   
 
54
   int symlink = NO;
 
55
   
 
56
   int error = 0;
 
57
   
 
58
   int retval = 0;
 
59
   
 
60
   int file_should = 0;
 
61
   
 
62
   config cfg;
 
63
   
 
64
#ifdef DEBUG
 
65
   fprintf(stderr, "\nEntering rename().\n");
 
66
#endif
 
67
   
 
68
   
 
69
   /* First we call init(), which will set the configuration variables: */
 
70
   
 
71
   init(&cfg);
 
72
   
 
73
   /* We always call fini() before quitting. */
 
74
   
 
75
   /* If real_rename_unavailable is set, we must return -1 because there's nothing else we can do: */
 
76
   
 
77
   if (cfg.real_rename_unavailable)
 
78
     {
 
79
#ifdef DEBUG
 
80
        fprintf(stderr, "real_rename_unavailable is set.\nrename returning -1.\n");
 
81
#endif
 
82
        errno = 0; /* we set errno to zero so that, if errno was previously set to some other value, it 
 
83
                    doesn't confuse the caller. */
 
84
        fini(&cfg);
 
85
        return -1;
 
86
     }
 
87
   
 
88
   
 
89
   /* If libtrash_off or rename_off is set, the user has asked us to become temporarily inactive an let the real
 
90
    * rename() perform its task.*/
 
91
   
 
92
   if (cfg.libtrash_off || cfg.rename_off)
 
93
     {
 
94
#ifdef DEBUG
 
95
        fprintf(stderr, "Passing request to rename(%s, %s) to the real rename() because (libtrash|rename)_off is set.\n", 
 
96
                oldpath, newpath);
 
97
#endif
 
98
        fini(&cfg);
 
99
        return (*cfg.real_rename) (oldpath, newpath); /* real rename() sets errno. */
 
100
     }
 
101
   
 
102
   
 
103
   
 
104
   /* If general_failure is set, we know that something went wrong in _init, and we do whatever in_case_of_failure
 
105
    * specifies, returning the appropriate error code.*/
 
106
   
 
107
   if (cfg.general_failure)
 
108
     {     
 
109
#ifdef DEBUG
 
110
        fprintf(stderr, "general_failure is set in rename(), invoking rename_handle_error().\n"
 
111
                "in_case_of_failure has value %d.\n", cfg.in_case_of_failure);
 
112
#endif
 
113
        fini(&cfg);
 
114
        return rename_handle_error(oldpath, newpath, cfg.real_rename, cfg.in_case_of_failure); /* either the real rename() sets errno, or we return -1 and errno is 
 
115
                                                                                                * set to 0. */
 
116
     }
 
117
   
 
118
   
 
119
   /* First of all: does a file called newpath already exist? If it doesn't (or if it is a dir), we don't need to 
 
120
    * do anything: 
 
121
    */
 
122
   
 
123
   error = lstat(newpath, &path_stat);
 
124
   
 
125
   if ( (error && errno == ENOENT) || 
 
126
        (!error && S_ISDIR(path_stat.st_mode)) )
 
127
     {
 
128
#ifdef DEBUG
 
129
        fprintf(stderr, "newpath (%s) either doesn't exit or is a directory.\nCalling the \"real\" rename().\n", newpath);
 
130
#endif
 
131
        fini(&cfg);
 
132
        return (*cfg.real_rename) (oldpath, newpath); /* errno set by real rename(). */
 
133
     } 
 
134
   
 
135
   
 
136
   /* We don't protect symbolic links, except if they are under one of the protected directories; therefore, we must
 
137
    * postpone the decision about whether to ignore a file because it is a symlink until we know if it is "protected":
 
138
    */
 
139
   
 
140
   if (!error && S_ISLNK(path_stat.st_mode))
 
141
     symlink = YES;
 
142
   else
 
143
     symlink = NO;
 
144
   
 
145
   
 
146
   /* Second: does a file called oldpath exist? If it doesn't (or if it is a dir), there's nothing for us to do, because
 
147
    the real call to rename() will necessarily fail (explanation: if oldpath is a dir, we can be sure that the rename()
 
148
    will fail because a _file_ called newpath already exists, and rename() doesn't overwrite files so that it can
 
149
    successfully rename dirs):
 
150
    */
 
151
   
 
152
   error = lstat(oldpath, &path_stat);
 
153
   
 
154
   if ( (error && errno == ENOENT) || (!error && S_ISDIR(path_stat.st_mode)) )
 
155
     {
 
156
#ifdef DEBUG
 
157
        fprintf(stderr, "oldpath (%s) either  doesn't exist or is a directory.\nCalling the \"real\" rename().\n", oldpath);
 
158
#endif
 
159
        fini(&cfg);
 
160
        return (*cfg.real_rename) (oldpath, newpath); /* errno set by real rename() */
 
161
     }
 
162
   
 
163
   
 
164
   
 
165
   /* Even if both files exist, are we sure that we have write-access 
 
166
    * to the directory the file called oldpath is found in? If we don't
 
167
    * have it (either due to lacking write-permission or due to the fact
 
168
    * that oldpath points to a file on a read-only filesystem), then the
 
169
    * call to the "real" rename() will fail and we will end up just moving
 
170
    * the file presently at newpath to the trash can, unless we exit at
 
171
    * this point.
 
172
    * 
 
173
    * How might this happen? If we can write to the dir in which newpath is
 
174
    * found, but not to the one which holds oldpath, we will successfully
 
175
    * move the file presently found at newpath to the trash can and then
 
176
    * once again invoke rename() to move the file called oldpath from
 
177
    * oldpath to newpath; but this second call to rename() will fail and
 
178
    * the user will end up with the file originally called newpath in the
 
179
    * trash can, and the file called old_path in the same place.
 
180
    * 
 
181
    * For this reason, we call the real rename() right away and let it
 
182
    * (fail and) complain about insufficient permissions / a RO FS.
 
183
    * 
 
184
    * Two notes on can_write_to_dir(): (1st) if it fails due to an internal
 
185
    * error, it returns TRUE, so that rename() doesn't exit at this point
 
186
    * (it behaves this way because, if an error occurrs, we prefer to make
 
187
    * an unnecessary copy of a file over the possibility of NOT making a
 
188
    * NECESSARY one) (2nd) can_write_to_dir() performs the permissions
 
189
    * check with the _effective_ UID rather than the real one, so that
 
190
    * SETUID programs work correctly (of course, we are assuming that the
 
191
    * program already did the appropriate permission checks -that's its
 
192
    * responsability). */
 
193
   
 
194
   if (!can_write_to_dir(oldpath))
 
195
     {
 
196
#ifdef DEBUG
 
197
        fprintf(stderr, "We don't have write-access to the dir which contains oldpath (%s).\n"
 
198
                "Calling the \"real\" rename().\n", oldpath);
 
199
#endif
 
200
        fini(&cfg);
 
201
        return (*cfg.real_rename) (oldpath, newpath); /* errno set by real rename() */
 
202
     }
 
203
   
 
204
   
 
205
   
 
206
   /* By now we know that our services are needed: rename(oldpath, newpath) might cause the loss of the file originally
 
207
    * called newpath, because both exist and we have write-permission to the dirs which contain them.
 
208
    */
 
209
   
 
210
   
 
211
   /* Let us begin by building an absolute path, if we weren't passed one: */
 
212
   
 
213
   /* [If no error occurred, memory was dynamically allocated and it is now pointed to by absolute_newpath. 
 
214
    That is the reason why we free absolute_newpath before returning.] */
 
215
   
 
216
   /* We don't use GNU libc's canonicalize_file_name() directly for reasons explained in unlink.c: */
 
217
   
 
218
   absolute_newpath = build_absolute_path(newpath);
 
219
   
 
220
   if (!absolute_newpath)
 
221
     {
 
222
#ifdef DEBUG
 
223
        fprintf(stderr, "Unable to build absolute_newpath.\nInvoking rename_handle_error().\n");
 
224
#endif
 
225
        fini(&cfg);
 
226
        return rename_handle_error(oldpath, newpath, cfg.real_rename,
 
227
                                   cfg.in_case_of_failure); /* errno set either by the real rename() or set to 0 (just like the other
 
228
                                                             * call to rename_handle_error() above). */
 
229
     }
 
230
   
 
231
   
 
232
   
 
233
   /* Independently of the way in which the argument was written, absolute_newpath now holds the absolute
 
234
    * path to the new filename, free of any "/../" or "/./". */
 
235
   
 
236
#ifdef DEBUG
 
237
   fprintf(stderr, "CLEANSED ABSOLUTE_NEWPATH:   |%s|\n", absolute_newpath);
 
238
#endif
 
239
   
 
240
   
 
241
   /* +++++++++++++ */
 
242
   
 
243
   /* By now we want to know whether the file at newpath "qualifies" to be stored in the trash can rather than
 
244
    permanently lost (another possible option is this file being considered "unremovable"). This decision is 
 
245
    taken according to the user's preferences by the function decide_action(): */
 
246
   
 
247
   
 
248
   file_should = decide_action(absolute_newpath, &cfg);
 
249
   
 
250
   switch (file_should)
 
251
     {
 
252
        
 
253
      case BE_REMOVED:
 
254
        
 
255
#ifdef DEBUG
 
256
        fprintf(stderr, "decide_action() told rename() to permanently destroy file %s.\n", absolute_newpath);
 
257
#endif
 
258
        
 
259
        retval = (*cfg.real_rename) (oldpath, newpath); /* errno set by real rename() */
 
260
        
 
261
        break;
 
262
        
 
263
        
 
264
      case BE_LEFT_UNTOUCHED:
 
265
        
 
266
#ifdef DEBUG
 
267
        fprintf(stderr, "decide_action() told rename() to leave file %s untouched.\n", absolute_newpath);
 
268
#endif
 
269
        
 
270
        /* Set errno so that the caller interprets this failure as "insufficient permissions": */
 
271
        
 
272
        errno = EACCES;
 
273
        
 
274
        retval = -1;
 
275
        
 
276
        break;
 
277
        
 
278
        
 
279
      case BE_SAVED:
 
280
        
 
281
#ifdef DEBUG
 
282
        fprintf(stderr, "decide_action() told rename() to save a copy of file %s.\n", absolute_newpath);
 
283
#endif
 
284
        
 
285
        /* But if it is a symlink we refuse to "save" it nonetheless: */
 
286
        
 
287
        if (symlink)
 
288
          {
 
289
#ifdef DEBUG
 
290
             fprintf(stderr, " but its suggestion is being ignored because %s is just a symlink.\n", absolute_newpath);
 
291
#endif
 
292
             retval = (*cfg.real_rename) (oldpath, newpath); /* real rename() sets errno. */
 
293
          }
 
294
        else /* if absolute_newpath isn't a symlink */
 
295
          {
 
296
             /* (See below for information on this code.) */
 
297
             
 
298
             if (found_under_dir(absolute_newpath, cfg.home)) /* (a) */
 
299
               error = graft_file(cfg.absolute_trash_can, absolute_newpath, cfg.home, &cfg);
 
300
             else /* (b) */
 
301
               error = graft_file(cfg.absolute_trash_system_root, absolute_newpath, NULL, &cfg);
 
302
             
 
303
             if (error) /* graft_file() failed. */
 
304
               {
 
305
#ifdef DEBUG
 
306
                  fprintf(stderr, "graft_file() failed, invoking rename_handle_error().\n");
 
307
#endif
 
308
                  retval = rename_handle_error(oldpath, newpath, cfg.real_rename,
 
309
                                               cfg.in_case_of_failure); /* about errno: see explanation near the previous call to
 
310
                                                                         * rename_handle_error(). */
 
311
               }
 
312
             else /* graft_file() succeeded, we just need to perform the "real" operation: */
 
313
               {
 
314
#ifdef DEBUG
 
315
                  fprintf(stderr, "graft_file(), called by rename(), succeeded.\n");
 
316
#endif
 
317
                  retval = (*cfg.real_rename) (oldpath, newpath); /* real rename() setting errno. */
 
318
               }
 
319
          }
 
320
        
 
321
        break;
 
322
     }
 
323
   
 
324
   
 
325
   /* ++++++++++++++ */
 
326
   
 
327
   /* About the code in case BE_SAVED: (a) a file under the user's home dir and (b) a file outside of the user's home 
 
328
    dir with global_protection set. We then pass our arguments to the
 
329
    "real" rename(), now that the file originally at newpath is safely
 
330
    stored in the user's trash can. If graft_file() failed (and a copy of
 
331
    the file ISN'T safely stored in the user's trash can), we invoke
 
332
    rename_handle_error() which will act based on the value the user chose
 
333
    for in_case_of_failure. */
 
334
   
 
335
   /* Free memory before quitting: */
 
336
   
 
337
   free(absolute_newpath);
 
338
   
 
339
   fini(&cfg);
 
340
   return retval; /* By now, errno has been set to a meaningful value in one of the cases above. */
 
341
}
 
342
 
 
343
 
 
344
/* ------------------------------------------------------------------------------------ */
 
345
 
 
346
/* This function is called by rename() in case an error is spotted. It does what the user 
 
347
 * told us to do in these situations (through the variable in_case_of_failure), and rename() just
 
348
 * returns the value passed by rename_trash_error():
 
349
 */
 
350
 
 
351
 
 
352
static int rename_handle_error(const char *oldpath, const char *newpath, 
 
353
                               int (*real_rename) (const char*, const char*), int in_case_of_failure)
 
354
{
 
355
   if (in_case_of_failure == ALLOW_DESTRUCTION)
 
356
     return (*real_rename) (oldpath, newpath); /* real rename() sets errno */
 
357
   else /* if (in_case_of_failure == PROTECT) */
 
358
     {
 
359
        errno = 0;
 
360
        return -1;
 
361
     }
 
362
}
 
363
 
 
364