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