1*d5c9a868SElliott Hughes /* Copyright 1996-1998,2000-2002,2005,2007-2009 Alain Knaff.
2*d5c9a868SElliott Hughes * This file is part of mtools.
3*d5c9a868SElliott Hughes *
4*d5c9a868SElliott Hughes * Mtools is free software: you can redistribute it and/or modify
5*d5c9a868SElliott Hughes * it under the terms of the GNU General Public License as published by
6*d5c9a868SElliott Hughes * the Free Software Foundation, either version 3 of the License, or
7*d5c9a868SElliott Hughes * (at your option) any later version.
8*d5c9a868SElliott Hughes *
9*d5c9a868SElliott Hughes * Mtools is distributed in the hope that it will be useful,
10*d5c9a868SElliott Hughes * but WITHOUT ANY WARRANTY; without even the implied warranty of
11*d5c9a868SElliott Hughes * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12*d5c9a868SElliott Hughes * GNU General Public License for more details.
13*d5c9a868SElliott Hughes *
14*d5c9a868SElliott Hughes * You should have received a copy of the GNU General Public License
15*d5c9a868SElliott Hughes * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
16*d5c9a868SElliott Hughes *
17*d5c9a868SElliott Hughes * mmove.c
18*d5c9a868SElliott Hughes * Renames/moves an MSDOS file
19*d5c9a868SElliott Hughes *
20*d5c9a868SElliott Hughes */
21*d5c9a868SElliott Hughes
22*d5c9a868SElliott Hughes
23*d5c9a868SElliott Hughes #include "sysincludes.h"
24*d5c9a868SElliott Hughes #include "msdos.h"
25*d5c9a868SElliott Hughes #include "mtools.h"
26*d5c9a868SElliott Hughes #include "vfat.h"
27*d5c9a868SElliott Hughes #include "mainloop.h"
28*d5c9a868SElliott Hughes #include "plain_io.h"
29*d5c9a868SElliott Hughes #include "nameclash.h"
30*d5c9a868SElliott Hughes #include "file.h"
31*d5c9a868SElliott Hughes #include "fs.h"
32*d5c9a868SElliott Hughes
33*d5c9a868SElliott Hughes /*
34*d5c9a868SElliott Hughes * Preserve the file modification times after the fclose()
35*d5c9a868SElliott Hughes */
36*d5c9a868SElliott Hughes
37*d5c9a868SElliott Hughes typedef struct Arg_t {
38*d5c9a868SElliott Hughes const char *fromname;
39*d5c9a868SElliott Hughes int verbose;
40*d5c9a868SElliott Hughes MainParam_t mp;
41*d5c9a868SElliott Hughes
42*d5c9a868SElliott Hughes direntry_t *entry;
43*d5c9a868SElliott Hughes ClashHandling_t ch;
44*d5c9a868SElliott Hughes } Arg_t;
45*d5c9a868SElliott Hughes
46*d5c9a868SElliott Hughes
47*d5c9a868SElliott Hughes /*
48*d5c9a868SElliott Hughes * Open the named file for read, create the cluster chain, return the
49*d5c9a868SElliott Hughes * directory structure or NULL on error.
50*d5c9a868SElliott Hughes */
renameit(dos_name_t * dosname,char * longname UNUSEDP,void * arg0,direntry_t * targetEntry)51*d5c9a868SElliott Hughes static int renameit(dos_name_t *dosname,
52*d5c9a868SElliott Hughes char *longname UNUSEDP,
53*d5c9a868SElliott Hughes void *arg0,
54*d5c9a868SElliott Hughes direntry_t *targetEntry)
55*d5c9a868SElliott Hughes {
56*d5c9a868SElliott Hughes Arg_t *arg = (Arg_t *) arg0;
57*d5c9a868SElliott Hughes uint32_t fat;
58*d5c9a868SElliott Hughes
59*d5c9a868SElliott Hughes targetEntry->dir = arg->entry->dir;
60*d5c9a868SElliott Hughes dosnameToDirentry(dosname, &targetEntry->dir);
61*d5c9a868SElliott Hughes
62*d5c9a868SElliott Hughes if(IS_DIR(targetEntry)) {
63*d5c9a868SElliott Hughes direntry_t *movedEntry;
64*d5c9a868SElliott Hughes
65*d5c9a868SElliott Hughes /* get old direntry. It is important that we do this
66*d5c9a868SElliott Hughes * on the actual direntry which is stored in the file,
67*d5c9a868SElliott Hughes * and not on a copy, because we will modify it, and the
68*d5c9a868SElliott Hughes * modification should be visible at file
69*d5c9a868SElliott Hughes * de-allocation time */
70*d5c9a868SElliott Hughes movedEntry = getDirentry(arg->mp.File);
71*d5c9a868SElliott Hughes if(movedEntry->Dir != targetEntry->Dir) {
72*d5c9a868SElliott Hughes /* we are indeed moving it to a new directory */
73*d5c9a868SElliott Hughes direntry_t subEntry;
74*d5c9a868SElliott Hughes Stream_t *oldDir;
75*d5c9a868SElliott Hughes /* we have a directory here. Change its parent link */
76*d5c9a868SElliott Hughes
77*d5c9a868SElliott Hughes initializeDirentry(&subEntry, arg->mp.File);
78*d5c9a868SElliott Hughes
79*d5c9a868SElliott Hughes switch(vfat_lookup(&subEntry, "..", 2, ACCEPT_DIR,
80*d5c9a868SElliott Hughes NULL, 0, NULL, 0)) {
81*d5c9a868SElliott Hughes case -1:
82*d5c9a868SElliott Hughes fprintf(stderr,
83*d5c9a868SElliott Hughes " Directory has no parent entry\n");
84*d5c9a868SElliott Hughes break;
85*d5c9a868SElliott Hughes case -2:
86*d5c9a868SElliott Hughes return ERROR_ONE;
87*d5c9a868SElliott Hughes case 0:
88*d5c9a868SElliott Hughes GET_DATA(targetEntry->Dir, 0, 0, 0, &fat);
89*d5c9a868SElliott Hughes if (fat == fat32RootCluster(targetEntry->Dir)) {
90*d5c9a868SElliott Hughes fat = 0;
91*d5c9a868SElliott Hughes }
92*d5c9a868SElliott Hughes
93*d5c9a868SElliott Hughes subEntry.dir.start[1] = (fat >> 8) & 0xff;
94*d5c9a868SElliott Hughes subEntry.dir.start[0] = fat & 0xff;
95*d5c9a868SElliott Hughes dir_write(&subEntry);
96*d5c9a868SElliott Hughes if(arg->verbose){
97*d5c9a868SElliott Hughes fprintf(stderr,
98*d5c9a868SElliott Hughes "Easy, isn't it? I wonder why DOS can't do this.\n");
99*d5c9a868SElliott Hughes }
100*d5c9a868SElliott Hughes break;
101*d5c9a868SElliott Hughes }
102*d5c9a868SElliott Hughes
103*d5c9a868SElliott Hughes wipeEntry(movedEntry);
104*d5c9a868SElliott Hughes
105*d5c9a868SElliott Hughes /* free the old parent, allocate the new one. */
106*d5c9a868SElliott Hughes oldDir = movedEntry->Dir;
107*d5c9a868SElliott Hughes *movedEntry = *targetEntry;
108*d5c9a868SElliott Hughes COPY(targetEntry->Dir);
109*d5c9a868SElliott Hughes FREE(&oldDir);
110*d5c9a868SElliott Hughes return 0;
111*d5c9a868SElliott Hughes }
112*d5c9a868SElliott Hughes }
113*d5c9a868SElliott Hughes
114*d5c9a868SElliott Hughes /* wipe out original entry */
115*d5c9a868SElliott Hughes wipeEntry(arg->mp.direntry);
116*d5c9a868SElliott Hughes return 0;
117*d5c9a868SElliott Hughes }
118*d5c9a868SElliott Hughes
119*d5c9a868SElliott Hughes
120*d5c9a868SElliott Hughes
rename_file(direntry_t * entry,MainParam_t * mp)121*d5c9a868SElliott Hughes static int rename_file(direntry_t *entry, MainParam_t *mp)
122*d5c9a868SElliott Hughes /* rename a messy DOS file to another messy DOS file */
123*d5c9a868SElliott Hughes {
124*d5c9a868SElliott Hughes int result;
125*d5c9a868SElliott Hughes Stream_t *targetDir;
126*d5c9a868SElliott Hughes char *shortname;
127*d5c9a868SElliott Hughes const char *longname;
128*d5c9a868SElliott Hughes
129*d5c9a868SElliott Hughes Arg_t * arg = (Arg_t *) (mp->arg);
130*d5c9a868SElliott Hughes
131*d5c9a868SElliott Hughes arg->entry = entry;
132*d5c9a868SElliott Hughes targetDir = mp->targetDir;
133*d5c9a868SElliott Hughes
134*d5c9a868SElliott Hughes if (targetDir == entry->Dir){
135*d5c9a868SElliott Hughes arg->ch.ignore_entry = -1;
136*d5c9a868SElliott Hughes arg->ch.source = entry->entry;
137*d5c9a868SElliott Hughes arg->ch.source_entry = entry->entry;
138*d5c9a868SElliott Hughes } else {
139*d5c9a868SElliott Hughes arg->ch.ignore_entry = -1;
140*d5c9a868SElliott Hughes arg->ch.source = -2;
141*d5c9a868SElliott Hughes }
142*d5c9a868SElliott Hughes
143*d5c9a868SElliott Hughes longname = mpPickTargetName(mp);
144*d5c9a868SElliott Hughes shortname = 0;
145*d5c9a868SElliott Hughes result = mwrite_one(targetDir, longname, shortname,
146*d5c9a868SElliott Hughes renameit, (void *)arg, &arg->ch);
147*d5c9a868SElliott Hughes if(result == 1)
148*d5c9a868SElliott Hughes return GOT_ONE;
149*d5c9a868SElliott Hughes else
150*d5c9a868SElliott Hughes return ERROR_ONE;
151*d5c9a868SElliott Hughes }
152*d5c9a868SElliott Hughes
153*d5c9a868SElliott Hughes
rename_directory(direntry_t * entry,MainParam_t * mp)154*d5c9a868SElliott Hughes static int rename_directory(direntry_t *entry, MainParam_t *mp)
155*d5c9a868SElliott Hughes {
156*d5c9a868SElliott Hughes int ret;
157*d5c9a868SElliott Hughes
158*d5c9a868SElliott Hughes /* moves a DOS dir */
159*d5c9a868SElliott Hughes if(isSubdirOf(mp->targetDir, mp->File)) {
160*d5c9a868SElliott Hughes fprintf(stderr, "Cannot move directory ");
161*d5c9a868SElliott Hughes fprintPwd(stderr, entry,0);
162*d5c9a868SElliott Hughes fprintf(stderr, " into one of its own subdirectories (");
163*d5c9a868SElliott Hughes fprintPwd(stderr, getDirentry(mp->targetDir),0);
164*d5c9a868SElliott Hughes fprintf(stderr, ")\n");
165*d5c9a868SElliott Hughes return ERROR_ONE;
166*d5c9a868SElliott Hughes }
167*d5c9a868SElliott Hughes
168*d5c9a868SElliott Hughes if(entry->entry == -3) {
169*d5c9a868SElliott Hughes fprintf(stderr, "Cannot move a root directory: ");
170*d5c9a868SElliott Hughes fprintPwd(stderr, entry,0);
171*d5c9a868SElliott Hughes return ERROR_ONE;
172*d5c9a868SElliott Hughes }
173*d5c9a868SElliott Hughes
174*d5c9a868SElliott Hughes ret = rename_file(entry, mp);
175*d5c9a868SElliott Hughes if(ret & ERROR_ONE)
176*d5c9a868SElliott Hughes return ret;
177*d5c9a868SElliott Hughes
178*d5c9a868SElliott Hughes return ret;
179*d5c9a868SElliott Hughes }
180*d5c9a868SElliott Hughes
rename_oldsyntax(direntry_t * entry,MainParam_t * mp)181*d5c9a868SElliott Hughes static int rename_oldsyntax(direntry_t *entry, MainParam_t *mp)
182*d5c9a868SElliott Hughes {
183*d5c9a868SElliott Hughes int result;
184*d5c9a868SElliott Hughes Stream_t *targetDir;
185*d5c9a868SElliott Hughes const char *shortname, *longname;
186*d5c9a868SElliott Hughes
187*d5c9a868SElliott Hughes Arg_t * arg = (Arg_t *) (mp->arg);
188*d5c9a868SElliott Hughes arg->entry = entry;
189*d5c9a868SElliott Hughes targetDir = entry->Dir;
190*d5c9a868SElliott Hughes
191*d5c9a868SElliott Hughes arg->ch.ignore_entry = -1;
192*d5c9a868SElliott Hughes arg->ch.source = entry->entry;
193*d5c9a868SElliott Hughes arg->ch.source_entry = entry->entry;
194*d5c9a868SElliott Hughes
195*d5c9a868SElliott Hughes #if 0
196*d5c9a868SElliott Hughes if(!strcasecmp(mp->shortname, arg->fromname)){
197*d5c9a868SElliott Hughes longname = mp->longname;
198*d5c9a868SElliott Hughes shortname = mp->targetName;
199*d5c9a868SElliott Hughes } else {
200*d5c9a868SElliott Hughes #endif
201*d5c9a868SElliott Hughes longname = mp->targetName;
202*d5c9a868SElliott Hughes shortname = 0;
203*d5c9a868SElliott Hughes #if 0
204*d5c9a868SElliott Hughes }
205*d5c9a868SElliott Hughes #endif
206*d5c9a868SElliott Hughes result = mwrite_one(targetDir, longname, shortname,
207*d5c9a868SElliott Hughes renameit, (void *)arg, &arg->ch);
208*d5c9a868SElliott Hughes if(result == 1)
209*d5c9a868SElliott Hughes return GOT_ONE;
210*d5c9a868SElliott Hughes else
211*d5c9a868SElliott Hughes return ERROR_ONE;
212*d5c9a868SElliott Hughes }
213*d5c9a868SElliott Hughes
214*d5c9a868SElliott Hughes
215*d5c9a868SElliott Hughes static void usage(int ret) NORETURN;
usage(int ret)216*d5c9a868SElliott Hughes static void usage(int ret)
217*d5c9a868SElliott Hughes {
218*d5c9a868SElliott Hughes fprintf(stderr,
219*d5c9a868SElliott Hughes "Mtools version %s, dated %s\n", mversion, mdate);
220*d5c9a868SElliott Hughes fprintf(stderr,
221*d5c9a868SElliott Hughes "Usage: %s [-vV] [-D clash_option] file targetfile\n", progname);
222*d5c9a868SElliott Hughes fprintf(stderr,
223*d5c9a868SElliott Hughes " %s [-vV] [-D clash_option] file [files...] target_directory\n",
224*d5c9a868SElliott Hughes progname);
225*d5c9a868SElliott Hughes exit(ret);
226*d5c9a868SElliott Hughes }
227*d5c9a868SElliott Hughes
228*d5c9a868SElliott Hughes void mmove(int argc, char **argv, int oldsyntax) NORETURN;
mmove(int argc,char ** argv,int oldsyntax)229*d5c9a868SElliott Hughes void mmove(int argc, char **argv, int oldsyntax)
230*d5c9a868SElliott Hughes {
231*d5c9a868SElliott Hughes Arg_t arg;
232*d5c9a868SElliott Hughes int c;
233*d5c9a868SElliott Hughes char shortname[12*4+1];
234*d5c9a868SElliott Hughes char longname[4*MAX_VNAMELEN+1];
235*d5c9a868SElliott Hughes char def_drive;
236*d5c9a868SElliott Hughes int i;
237*d5c9a868SElliott Hughes
238*d5c9a868SElliott Hughes /* get command line options */
239*d5c9a868SElliott Hughes
240*d5c9a868SElliott Hughes init_clash_handling(& arg.ch);
241*d5c9a868SElliott Hughes
242*d5c9a868SElliott Hughes /* get command line options */
243*d5c9a868SElliott Hughes arg.verbose = 0;
244*d5c9a868SElliott Hughes if(helpFlag(argc, argv))
245*d5c9a868SElliott Hughes usage(0);
246*d5c9a868SElliott Hughes while ((c = getopt(argc, argv, "i:vD:oh")) != EOF) {
247*d5c9a868SElliott Hughes switch (c) {
248*d5c9a868SElliott Hughes case 'i':
249*d5c9a868SElliott Hughes set_cmd_line_image(optarg);
250*d5c9a868SElliott Hughes break;
251*d5c9a868SElliott Hughes case 'v': /* dummy option for mcopy */
252*d5c9a868SElliott Hughes arg.verbose = 1;
253*d5c9a868SElliott Hughes break;
254*d5c9a868SElliott Hughes case 'o':
255*d5c9a868SElliott Hughes handle_clash_options(&arg.ch, (char)c);
256*d5c9a868SElliott Hughes break;
257*d5c9a868SElliott Hughes case 'D':
258*d5c9a868SElliott Hughes if(handle_clash_options(&arg.ch, *optarg))
259*d5c9a868SElliott Hughes usage(1);
260*d5c9a868SElliott Hughes break;
261*d5c9a868SElliott Hughes case 'h':
262*d5c9a868SElliott Hughes usage(0);
263*d5c9a868SElliott Hughes case '?':
264*d5c9a868SElliott Hughes usage(1);
265*d5c9a868SElliott Hughes default:
266*d5c9a868SElliott Hughes break;
267*d5c9a868SElliott Hughes }
268*d5c9a868SElliott Hughes }
269*d5c9a868SElliott Hughes
270*d5c9a868SElliott Hughes if (argc - optind < 2)
271*d5c9a868SElliott Hughes usage(1);
272*d5c9a868SElliott Hughes
273*d5c9a868SElliott Hughes init_mp(&arg.mp);
274*d5c9a868SElliott Hughes arg.mp.arg = (void *) &arg;
275*d5c9a868SElliott Hughes arg.mp.openflags = O_RDWR;
276*d5c9a868SElliott Hughes
277*d5c9a868SElliott Hughes /* look for a default drive */
278*d5c9a868SElliott Hughes def_drive = '\0';
279*d5c9a868SElliott Hughes for(i=optind; i<argc; i++)
280*d5c9a868SElliott Hughes if(argv[i][0] && argv[i][1] == ':' ){
281*d5c9a868SElliott Hughes if(!def_drive)
282*d5c9a868SElliott Hughes def_drive = ch_toupper(argv[i][0]);
283*d5c9a868SElliott Hughes else if(def_drive != ch_toupper(argv[i][0])){
284*d5c9a868SElliott Hughes fprintf(stderr,
285*d5c9a868SElliott Hughes "Cannot move files across different drives\n");
286*d5c9a868SElliott Hughes exit(1);
287*d5c9a868SElliott Hughes }
288*d5c9a868SElliott Hughes }
289*d5c9a868SElliott Hughes
290*d5c9a868SElliott Hughes if(def_drive)
291*d5c9a868SElliott Hughes *(arg.mp.mcwd) = def_drive;
292*d5c9a868SElliott Hughes
293*d5c9a868SElliott Hughes if (oldsyntax && (argc - optind != 2 || strpbrk(":/", argv[argc-1])))
294*d5c9a868SElliott Hughes oldsyntax = 0;
295*d5c9a868SElliott Hughes
296*d5c9a868SElliott Hughes arg.mp.lookupflags =
297*d5c9a868SElliott Hughes ACCEPT_PLAIN | ACCEPT_DIR | DO_OPEN_DIRS | NO_DOTS | NO_UNIX;
298*d5c9a868SElliott Hughes
299*d5c9a868SElliott Hughes if (!oldsyntax){
300*d5c9a868SElliott Hughes target_lookup(&arg.mp, argv[argc-1]);
301*d5c9a868SElliott Hughes arg.mp.callback = rename_file;
302*d5c9a868SElliott Hughes arg.mp.dirCallback = rename_directory;
303*d5c9a868SElliott Hughes } else {
304*d5c9a868SElliott Hughes /* do not look up the target; it will be the same dir as the
305*d5c9a868SElliott Hughes * source */
306*d5c9a868SElliott Hughes arg.fromname = argv[optind];
307*d5c9a868SElliott Hughes if(arg.fromname[0] && arg.fromname[1] == ':')
308*d5c9a868SElliott Hughes arg.fromname += 2;
309*d5c9a868SElliott Hughes arg.fromname = _basename(arg.fromname);
310*d5c9a868SElliott Hughes arg.mp.targetName = strdup(argv[argc-1]);
311*d5c9a868SElliott Hughes arg.mp.callback = rename_oldsyntax;
312*d5c9a868SElliott Hughes }
313*d5c9a868SElliott Hughes
314*d5c9a868SElliott Hughes
315*d5c9a868SElliott Hughes arg.mp.longname.data = longname;
316*d5c9a868SElliott Hughes arg.mp.longname.len = sizeof(longname);
317*d5c9a868SElliott Hughes longname[0]='\0';
318*d5c9a868SElliott Hughes
319*d5c9a868SElliott Hughes arg.mp.shortname.data = shortname;
320*d5c9a868SElliott Hughes arg.mp.shortname.len = sizeof(shortname);
321*d5c9a868SElliott Hughes shortname[0]='\0';
322*d5c9a868SElliott Hughes
323*d5c9a868SElliott Hughes exit(main_loop(&arg.mp, argv + optind, argc - optind - 1));
324*d5c9a868SElliott Hughes }
325