1 /* $Header: /home/ksheff/src/e2tools/RCS/mv.c,v 0.1 2002/03/21 09:03:25 ksheff Exp $ */
2 /*
3  * mv.c
4  *
5  * Copyright (C) 2002 Keith W Sheffield.  This file may be redistributed
6  * under the terms of the GNU Public License.
7  *
8  */
9 
10 /* Description */
11 /*
12  * Module to move/rename files
13  *
14  */
15 /*
16  * $Log: mv.c,v $
17  * Revision 0.1  2002/03/21 09:03:25  ksheff
18  * initial revision.
19  *
20  */
21 
22 /* Feature Test Switches */
23 /*  Headers */
24 #include "e2tools.h"
25 
26 /* Macros */
27 #define USAGE "Usage: e2mv [-vfs] source1 [... sourceN] destination\n"
28 
29 /* Local Prototypes */
30 static long
31 do_swap(int force, int verbose, int curidx, int argc, char **argv);
32 
33 /* Name:    main_e2mv()
34  *
35  * Description:
36  *
37  * This function reads the command line arguments and moves or renames files
38  * in an ext2fs file system
39  *
40  * Algorithm:
41  *
42  * Read any command line switches
43  * Get the first source file specification
44  * If we are performing a file swap, call do_swap()
45  * Open the file system
46  * Get the destination and determine if it is a directory
47  *    If not, then get the destination's directory and basename
48  *    Also check that the number of source files are no more than one
49  * For each source file
50  *    Get the directory and basename of the source file
51  *    Determine the inode number for the source file
52  *    Create the link
53  *    Unlink the original source file.
54  *
55  * Global Variables:
56  *
57  * None
58  *
59  * Arguments:
60  *
61  * int argc;             The number of arguments
62  * char *argv[];         The command line arguments
63  *
64  * Return Values:
65  *
66  * 0 - the file was move successfully
67  * an error occurred.
68  *
69  * Author: Keith W. Sheffield
70  * Date:   03/20/2002
71  *
72  * Modification History:
73  *
74  * MM/DD/YY      Name               Description
75  */
76 int
main_e2mv(int argc,char * argv[])77 main_e2mv(int argc, char *argv[])
78 {
79   int verbose=0;
80   int force=0;
81   int swap_files=0;
82   int errcnt=0;
83   char *cur_filesys = NULL;
84   ext2_filsys fs = NULL;
85   ext2_ino_t root;
86   ext2_ino_t srcd;
87   ext2_ino_t destd;
88   ext2_ino_t source_file;
89   char *src_dir;
90   char *dest_dir;
91   char *src_name;
92   char *dest_name;
93   char *result_name;
94   long retval;
95   int c;
96   int curidx;
97 
98 #ifdef HAVE_OPTRESET
99   optreset = 1;     /* Makes BSD getopt happy */
100 #endif
101   while ((c = getopt(argc, argv, "vfs")) != EOF)
102     {
103       switch (c)
104         {
105         case 'v':
106           verbose = 1;
107           break;
108         case 'f':
109           force = E2T_FORCE;
110           break;
111         case 's':
112           swap_files = 1;
113           break;
114         default:
115           errcnt++;
116           break;
117         }
118     }
119 
120   curidx = optind;
121 
122   force |= E2T_DO_MV;
123 
124   if (errcnt || argc < curidx+2)
125     {
126       fputs(USAGE, stderr);
127       return(1);
128     }
129 
130   if (swap_files)
131     return(do_swap(force, verbose, curidx, argc, argv));
132 
133   cur_filesys = argv[curidx++];
134   if (NULL == (src_dir = strchr(cur_filesys, ':')))
135     {
136       fprintf(stderr, "Invalid file specification: %s\n", cur_filesys);
137       return(1);
138     }
139   *src_dir++ = '\0';
140 
141   if ((retval = open_filesystem(cur_filesys, &fs, &root, 1)))
142     {
143       return retval;
144     }
145 
146 
147   /* get the destination directory */
148   dest_name = NULL;
149   if (strcmp(dest_dir = argv[argc-1], ".") != 0)
150     {
151       /* check to see if the file name already exists in the current
152        * directory  and also see if it is a directory.
153        */
154       if ((retval = ext2fs_namei(fs, root, root, dest_dir, &destd)) ||
155           (retval = ext2fs_check_directory(fs, destd)))
156         {
157           if (retval != EXT2_ET_FILE_NOT_FOUND &&
158               retval != EXT2_ET_NO_DIRECTORY)
159             {
160               fprintf(stderr, "%s\n",error_message(retval));
161               ext2fs_close(fs);
162               return(retval);
163             }
164 
165           /* ok, so it's either not there or it's not a directory, so
166            * get the real destination directory and file name.
167            */
168           if (curidx+1 < argc)
169             {
170               fprintf(stderr, "%s must be a directory!\n", dest_dir);
171               ext2fs_close(fs);
172               return(1);
173             }
174 
175           if (get_file_parts(fs, root, dest_dir, &destd, &dest_dir,
176                              &dest_name))
177             {
178               ext2fs_close(fs);
179               return(-1);
180             }
181         }
182       else                  /* we have a directory!!! */
183         dest_name = NULL;
184     }
185   else
186     {
187       destd = root;
188       dest_name = NULL;
189     }
190 
191   do
192     {
193       /* move to the source directory */
194       if (get_file_parts(fs, root, src_dir, &srcd, &src_dir, &src_name))
195         {
196           ext2fs_close(fs);
197           return(-1);
198         }
199 
200       /* get the inode number for the source file */
201       if ((retval = ext2fs_namei(fs, srcd, srcd, src_name, &source_file)))
202         {
203           fprintf(stderr, "%s: source file %s\n",error_message(retval),
204                   src_name);
205           ext2fs_close(fs);
206           return(retval);
207         }
208 
209       result_name = (dest_name) ? dest_name : src_name;
210 
211       /* now create the link */
212       if ((retval = create_hard_link(fs, destd, source_file, result_name,
213                                      force)))
214         {
215           fprintf(stderr, "Error renaming %s/%s as %s/%s\n",
216                   ((src_dir == NULL) ? "." : src_dir), src_name,
217                   ((dest_dir == NULL) ? "." : dest_dir), result_name);
218           ext2fs_close(fs);
219           return(1);
220         }
221 
222       if ((retval = ext2fs_unlink(fs, srcd, src_name, 0, 0)))
223         {
224           fprintf(stderr, "%s - %s\n", src_name, error_message(retval));
225           ext2fs_close(fs);
226           return(retval);
227         }
228 
229       if (verbose)
230         fprintf(stderr, "moved %s/%s as %s/%s\n",
231                 ((src_dir == NULL) ? "." : src_dir), src_name,
232                 ((dest_dir == NULL) ? "." : dest_dir), result_name);
233       src_dir = argv[curidx++];
234     }
235   while (curidx < argc);
236 
237   ext2fs_close(fs);
238   return(0);
239 
240 } /* end of do_mv */
241 
242 /* Name:    get_file_parts()
243  *
244  * Description:
245  *
246  * This function returns each of the following file 'parts': directory name,
247  * base name, inode number of the directory
248  *
249  * Algorithm:
250  *
251  * Use the root directory as the current working directory
252  * Find the last / in the full pathname
253  *     If none are found, set the basename to the full pathname,
254  *     and the directory to NULL
255  * Otherwise,
256  *     Separate the basename from the directory
257  *     Change the working directory
258  * Set the return pointers.
259  *
260  * Global Variables:
261  *
262  * None.
263  *
264  * Arguments:
265  *
266  * ext2_filsys fs;            the filesystem being used
267  * ext2_ino_t root;           the root directory of the filesystem
268  * char *pathname;            the full pathname of the file
269  * ext2_ino_t *dir_ino;       The inode number of the directory
270  * char **dir_name;           the directory the file is in
271  * char **base_name;          The basename of the file
272  *
273  * Return Values:
274  *
275  * 0 - retrieved the information ok
276  * otherwise the error code of what went wrong
277  *
278  * Author: Keith W. Sheffield
279  * Date:   03/21/2002
280  *
281  * Modification History:
282  *
283  * MM/DD/YY      Name               Description
284  *
285  */
286 long
get_file_parts(ext2_filsys fs,ext2_ino_t root,char * pathname,ext2_ino_t * dir_ino,char ** dir_name,char ** base_name)287 get_file_parts(ext2_filsys fs, ext2_ino_t root, char *pathname,
288                ext2_ino_t *dir_ino, char **dir_name, char **base_name)
289 {
290   char *fname;
291   long retval;
292 
293   /* move to the source directory */
294   *dir_name = pathname;
295   *dir_ino = root;
296   if (NULL == (fname = strrchr(pathname, '/')))
297     {
298       fname = pathname;
299       *dir_name = NULL;
300     }
301   else
302     {
303       *fname++ = '\0';
304       if ((*pathname != '\0' && strcmp(pathname, ".") != 0) &&
305           (retval = change_cwd(fs, root, dir_ino, pathname)))
306         {
307           fprintf(stderr, "Error changing to directory %s\n",
308                   pathname);
309           return(retval);
310         }
311     }
312 
313     *base_name = fname;
314     return(0);
315 } /* end of get_file_parts */
316 
317 
318 /* Name:    do_swap()
319  *
320  * Description:
321  *
322  * This function swaps the names of two files and optionally assigns the
323  * first file a new name:
324  *
325  * file1 file2 file3
326  *
327  * After the operation, file1 will reference the file that was originally file2, and file3 will reference the what used to be file1.
328  *
329  * Algorithm:
330  *
331  * check input parameters
332  * Get the directory and inode numbers for the first two files
333  * If a third file exists
334  *     Get the directory info for the file
335  *     Rename the first file to the third
336  *     Rename the 2nd file to the first
337  * Otherwise
338  *     Remove the first file from its directory
339  *     Rename the 2nd file to the first
340  *     Add the first file back as the 2nd file.
341  *
342  * Global Variables:
343  *
344  * None.
345  *
346  * Arguments:
347  *
348  * int force;                 Flag indicating if existing files are to be removed
349  * int verbose;          Flag to print lots of output
350  * int curidx;           The current index in the command line args
351  * int argc;             The total number of arguments
352  * char **argv;          Pointer to an array of argument strings.
353  *
354  * Return Values:
355  *
356  * 0 - the operation was successful
357  * otherwise the error code of what went wrong.
358  *
359  * Author: Keith W. Sheffield
360  * Date:   03/21/2002
361  *
362  * Modification History:
363  *
364  * MM/DD/YY      Name               Description
365  *
366  */
367 
368 static long
do_swap(int force,int verbose,int curidx,int argc,char ** argv)369 do_swap(int force, int verbose, int curidx, int argc, char **argv)
370 {
371   char *cur_filesys = NULL;
372   ext2_filsys fs = NULL;
373   ext2_ino_t root;
374   ext2_ino_t file1_dirno;
375   ext2_ino_t file2_dirno;
376   ext2_ino_t file3_dirno;
377   ext2_ino_t file1_no;
378   ext2_ino_t file2_no;
379   char *file1_dir;
380   char *file2_dir;
381   char *file3_dir;
382   char *file1_name;
383   char *file2_name;
384   char *file3_name;
385   long retval;
386 
387   if (curidx + 2 > argc)
388     {
389       fputs(USAGE, stderr);
390       return(1);
391     }
392 
393   cur_filesys = argv[curidx++];
394   if (NULL == (file1_dir = strchr(cur_filesys, ':')))
395     {
396       fprintf(stderr, "Invalid file specification: %s\n", cur_filesys);
397       return(1);
398     }
399   *file1_dir++ = '\0';
400 
401   if ((retval = open_filesystem(cur_filesys, &fs, &root, 1)))
402     {
403       return retval;
404     }
405 
406   /* move to the file 1 directory */
407   if (get_file_parts(fs, root, file1_dir, &file1_dirno, &file1_dir,
408                      &file1_name))
409     {
410       ext2fs_close(fs);
411       return(-1);
412     }
413 
414   /* get the inode number for the file 1 file */
415   if ((retval = ext2fs_namei(fs, file1_dirno, file1_dirno, file1_name,
416                              &file1_no)))
417     {
418       fprintf(stderr, "%s: file 1 file %s\n",error_message(retval),
419               file1_name);
420       ext2fs_close(fs);
421       return(retval);
422     }
423 
424 
425   /* move to the file 2 directory */
426   if (get_file_parts(fs, root, argv[curidx++], &file2_dirno, &file2_dir,
427                      &file2_name))
428     {
429       ext2fs_close(fs);
430       return(-1);
431     }
432 
433   /* get the inode number for the file 2 file */
434   if ((retval = ext2fs_namei(fs, file2_dirno, file2_dirno, file2_name,
435                              &file2_no)))
436     {
437       fprintf(stderr, "%s: file 2 file %s\n",error_message(retval),
438               file2_name);
439       ext2fs_close(fs);
440       return(retval);
441     }
442 
443   if (curidx < argc)
444     {
445       /* move to the file 3 directory */
446       if (get_file_parts(fs, root, argv[curidx++], &file3_dirno, &file3_dir,
447                          &file3_name))
448         {
449           ext2fs_close(fs);
450           return(-1);
451         }
452 
453       /* now move the first file to the 3rd */
454       if ((retval = create_hard_link(fs, file3_dirno, file1_no, file3_name,
455                                      force)))
456         {
457           fprintf(stderr, "Error renaming %s/%s as %s/%s\n",
458                   ((file1_dir == NULL) ? "." : file1_dir), file1_name,
459                   ((file3_dir == NULL) ? "." : file3_dir), file3_name);
460           ext2fs_close(fs);
461           return(1);
462         }
463 
464       if ((retval = ext2fs_unlink(fs, file1_dirno, file1_name, 0, 0)))
465         {
466           fprintf(stderr, "%s - %s\n", file1_name, error_message(retval));
467           ext2fs_close(fs);
468           return(retval);
469         }
470 
471 
472       /* now move the 2nd file to the 1st */
473       if ((retval = create_hard_link(fs, file1_dirno, file2_no, file1_name,
474                                      force)))
475         {
476           fprintf(stderr, "Error renaming %s/%s as %s/%s\n",
477                   ((file2_dir == NULL) ? "." : file2_dir), file2_name,
478                   ((file1_dir == NULL) ? "." : file1_dir), file1_name);
479           ext2fs_close(fs);
480           return(1);
481         }
482 
483       if ((retval = ext2fs_unlink(fs, file2_dirno, file2_name, 0, 0)))
484         {
485           fprintf(stderr, "%s - %s\n", file2_name, error_message(retval));
486           ext2fs_close(fs);
487           return(retval);
488         }
489 
490       if (verbose)
491         fprintf(stderr, "renamed file %s/%s as %s/%s\n"
492                 "renamed file %s/%s as %s/%s\n",
493                 ((file1_dir == NULL) ? "." : file1_dir), file1_name,
494                 ((file3_dir == NULL) ? "." : file3_dir), file3_name,
495                 ((file2_dir == NULL) ? "." : file2_dir), file2_name,
496                 ((file1_dir == NULL) ? "." : file1_dir), file1_name);
497     }
498   else
499     {
500       /* now remove the first file */
501       if ((retval = ext2fs_unlink(fs, file1_dirno, file1_name, 0, 0)))
502         {
503           fprintf(stderr, "%s - %s\n", file1_name, error_message(retval));
504           ext2fs_close(fs);
505           return(retval);
506         }
507 
508       /* now move the 2nd file to the 1st */
509       if ((retval = create_hard_link(fs, file1_dirno, file2_no, file1_name,
510                                      force)))
511         {
512           fprintf(stderr, "Error renaming %s/%s as %s/%s\n",
513                   ((file2_dir == NULL) ? "." : file2_dir), file2_name,
514                   ((file1_dir == NULL) ? "." : file1_dir), file1_name);
515           ext2fs_close(fs);
516           return(1);
517         }
518 
519       if ((retval = ext2fs_unlink(fs, file2_dirno, file2_name, 0, 0)))
520         {
521           fprintf(stderr, "%s - %s\n", file2_name, error_message(retval));
522           ext2fs_close(fs);
523           return(retval);
524         }
525 
526       if ((retval = create_hard_link(fs, file2_dirno, file1_no, file2_name,
527                                      force)))
528           {
529           fprintf(stderr, "Error renaming %s/%s as %s/%s\n",
530                   ((file1_dir == NULL) ? "." : file1_dir), file1_name,
531                   ((file2_dir == NULL) ? "." : file2_dir), file2_name);
532           ext2fs_close(fs);
533           return(1);
534         }
535 
536       if (verbose)
537         fprintf(stderr, "swapped files %s/%s <-> %s/%s\n",
538                 ((file1_dir == NULL) ? "." : file1_dir), file1_name,
539                 ((file2_dir == NULL) ? "." : file2_dir), file2_name);
540 
541     }
542 
543   ext2fs_close(fs);
544   return(0);
545 
546 } /* end of do_swap */
547