1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 *
4 * Copyright (C) 2002-2021 The DOSBox Team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21 #include "program_mount_common.h"
22 #include "program_mount.h"
23
24 #include "dosbox.h"
25
26 #include "bios_disk.h"
27 #include "control.h"
28 #include "drives.h"
29 #include "fs_utils.h"
30 #include "shell.h"
31 #include "string_utils.h"
32 #include "../ints/int10.h"
33
Move_Z(char new_z)34 void MOUNT::Move_Z(char new_z)
35 {
36 const char new_drive_z = toupper(new_z);
37
38 if (new_drive_z < 'A' || new_drive_z > 'Z') {
39 WriteOut(MSG_Get("PROGRAM_MOUNT_DRIVEID_ERROR"), new_drive_z);
40 return;
41 }
42
43 const uint8_t new_idx = drive_index(new_drive_z);
44
45 if (Drives[new_idx]) {
46 WriteOut(MSG_Get("PROGRAM_MOUNT_MOVE_Z_ERROR_1"), new_drive_z);
47 return;
48 }
49
50 if (new_idx < DOS_DRIVES - 1) {
51 ZDRIVE_NUM = new_idx;
52 /* remap drives */
53 Drives[new_idx] = Drives[25];
54 Drives[25] = 0;
55 if (!first_shell) return; //Should not be possible
56 /* Update environment */
57 std::string line = "";
58 std::string tempenv = {new_drive_z, ':', '\\'};
59 if (first_shell->GetEnvStr("PATH",line)) {
60 std::string::size_type idx = line.find('=');
61 std::string value = line.substr(idx +1 , std::string::npos);
62 while ( (idx = value.find("Z:\\")) != std::string::npos ||
63 (idx = value.find("z:\\")) != std::string::npos )
64 value.replace(idx,3,tempenv);
65 line = std::move(value);
66 }
67 if (!line.size()) line = tempenv;
68 first_shell->SetEnv("PATH",line.c_str());
69 tempenv += "COMMAND.COM";
70 first_shell->SetEnv("COMSPEC",tempenv.c_str());
71
72 /* Update batch file if running from Z: (very likely: autoexec) */
73 if (first_shell->bf) {
74 std::string &name = first_shell->bf->filename;
75 if (starts_with("Z:", name))
76 name[0] = new_drive_z;
77 }
78 /* Change the active drive */
79 if (DOS_GetDefaultDrive() == 25)
80 DOS_SetDrive(new_idx);
81 }
82 }
83
ListMounts()84 void MOUNT::ListMounts()
85 {
86 const std::string header_drive = MSG_Get("PROGRAM_MOUNT_STATUS_DRIVE");
87 const std::string header_type = MSG_Get("PROGRAM_MOUNT_STATUS_TYPE");
88 const std::string header_label = MSG_Get("PROGRAM_MOUNT_STATUS_LABEL");
89
90 const int term_width = real_readw(BIOSMEM_SEG, BIOSMEM_NB_COLS);
91 const auto width_1 = static_cast<int>(header_drive.size());
92 const auto width_3 = std::max(11, static_cast<int>(header_label.size()));
93 const auto width_2 = term_width - 3 - width_1 - width_3;
94
95 auto print_row = [&](const std::string &txt_1,
96 const std::string &txt_2,
97 const std::string &txt_3) {
98 WriteOut("%-*s %-*s %-*s\n",
99 width_1, txt_1.c_str(),
100 width_2, txt_2.c_str(),
101 width_3, txt_3.c_str());
102 };
103
104 WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_1"));
105 print_row(header_drive, header_type, header_label);
106 for (int i = 0; i < term_width; i++)
107 WriteOut_NoParsing("-");
108
109 for (uint8_t d = 0; d < DOS_DRIVES; d++) {
110 if (Drives[d]) {
111 print_row(std::string{drive_letter(d)},
112 Drives[d]->GetInfo(),
113 To_Label(Drives[d]->GetLabel()));
114 }
115 }
116 }
117
Run(void)118 void MOUNT::Run(void) {
119 DOS_Drive * newdrive;char drive;
120 std::string label;
121 std::string umount;
122 std::string newz;
123
124 //Hack To allow long commandlines
125 ChangeToLongCmd();
126 /* Parse the command line */
127 /* if the command line is empty show current mounts */
128 if (!cmd->GetCount()) {
129 ListMounts();
130 return;
131 }
132
133 /* In secure mode don't allow people to change mount points.
134 * Neither mount nor unmount */
135 if (control->SecureMode()) {
136 WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"));
137 return;
138 }
139 bool path_relative_to_last_config = false;
140 if (cmd->FindExist("-pr",true)) path_relative_to_last_config = true;
141
142 /* Check for unmounting */
143 if (cmd->FindString("-u",umount,false)) {
144 WriteOut(UnmountHelper(umount[0]), toupper(umount[0]));
145 return;
146 }
147
148 /* Check for moving Z: */
149 /* Only allowing moving it once. It is merely a convenience added for the wine team */
150 if (ZDRIVE_NUM == 25 && cmd->FindString("-z", newz,false)) {
151 Move_Z(newz[0]);
152 return;
153 }
154
155 if (cmd->FindExist("-cd", false)) {
156 WriteOut(MSG_Get("PROGRAM_MOUNT_NO_OPTION"), "-cd");
157 return;
158 }
159
160 std::string type="dir";
161 cmd->FindString("-t",type,true);
162 bool iscdrom = (type =="cdrom"); //Used for mscdex bug cdrom label name emulation
163 if (type=="floppy" || type=="dir" || type=="cdrom" || type =="overlay") {
164 Bit16u sizes[4] ={0};
165 Bit8u mediaid;
166 std::string str_size = "";
167 if (type=="floppy") {
168 str_size="512,1,2880,2880";/* All space free */
169 mediaid=0xF0; /* Floppy 1.44 media */
170 } else if (type=="dir" || type == "overlay") {
171 // 512*32*32765==~500MB total size
172 // 512*32*16000==~250MB total free size
173 str_size="512,32,32765,16000";
174 mediaid=0xF8; /* Hard Disk */
175 } else if (type=="cdrom") {
176 str_size="2048,1,65535,0";
177 mediaid=0xF8; /* Hard Disk */
178 } else {
179 WriteOut(MSG_Get("PROGAM_MOUNT_ILL_TYPE"),type.c_str());
180 return;
181 }
182 /* Parse the free space in mb's (kb's for floppies) */
183 std::string mb_size;
184 if (cmd->FindString("-freesize",mb_size,true)) {
185 char teststr[1024];
186 Bit16u freesize = static_cast<Bit16u>(atoi(mb_size.c_str()));
187 if (type=="floppy") {
188 // freesize in kb
189 sprintf(teststr,"512,1,2880,%d",freesize*1024/(512*1));
190 } else {
191 Bit32u total_size_cyl=32765;
192 Bit32u free_size_cyl=(Bit32u)freesize*1024*1024/(512*32);
193 if (free_size_cyl>65534) free_size_cyl=65534;
194 if (total_size_cyl<free_size_cyl) total_size_cyl=free_size_cyl+10;
195 if (total_size_cyl>65534) total_size_cyl=65534;
196 sprintf(teststr,"512,32,%d,%d",total_size_cyl,free_size_cyl);
197 }
198 str_size=teststr;
199 }
200
201 cmd->FindString("-size",str_size,true);
202 char number[21] = { 0 };const char * scan = str_size.c_str();
203 Bitu index = 0;Bitu count = 0;
204 /* Parse the str_size string */
205 while (*scan && index < 20 && count < 4) {
206 if (*scan==',') {
207 number[index] = 0;
208 sizes[count++] = atoi(number);
209 index = 0;
210 } else number[index++] = *scan;
211 scan++;
212 }
213 if (count < 4) {
214 number[index] = 0; //always goes correct as index is max 20 at this point.
215 sizes[count] = atoi(number);
216 }
217
218 // get the drive letter
219 cmd->FindCommand(1,temp_line);
220 if ((temp_line.size() > 2) || ((temp_line.size() > 1) && (temp_line[1]!=':'))) goto showusage;
221 const int i_drive = toupper(temp_line[0]);
222
223 if (i_drive < 'A' || i_drive > 'Z') {
224 goto showusage;
225 }
226 drive = int_to_char(i_drive);
227 if (type == "overlay") {
228 //Ensure that the base drive exists:
229 if (!Drives[drive_index(drive)]) {
230 WriteOut(MSG_Get("PROGRAM_MOUNT_OVERLAY_NO_BASE"));
231 return;
232 }
233 } else if (Drives[drive_index(drive)]) {
234 WriteOut(MSG_Get("PROGRAM_MOUNT_ALREADY_MOUNTED"),
235 drive,
236 Drives[drive_index(drive)]->GetInfo());
237 return;
238 }
239
240 if (!cmd->FindCommand(2,temp_line)) {
241 goto showusage;
242 }
243 if (!temp_line.size()) {
244 goto showusage;
245 }
246 if (path_relative_to_last_config && control->configfiles.size() && !Cross::IsPathAbsolute(temp_line)) {
247 std::string lastconfigdir(control->configfiles[control->configfiles.size() - 1]);
248 std::string::size_type pos = lastconfigdir.rfind(CROSS_FILESPLIT);
249 if (pos == std::string::npos) {
250 pos = 0; //No directory then erase string
251 }
252 lastconfigdir.erase(pos);
253 if (lastconfigdir.length()) {
254 temp_line = lastconfigdir + CROSS_FILESPLIT + temp_line;
255 }
256 }
257
258 #if defined(WIN32)
259 /* Removing trailing backslash if not root dir so stat
260 * will succeed */
261 if (temp_line.size() > 3 && temp_line.back() == '\\')
262 temp_line.pop_back();
263 #endif
264
265 const std::string real_path = to_native_path(temp_line);
266 if (real_path.empty()) {
267 LOG_MSG("MOUNT: Path '%s' not found", temp_line.c_str());
268 } else {
269 std::string home_resolve = temp_line;
270 Cross::ResolveHomedir(home_resolve);
271 if (home_resolve == real_path) {
272 LOG_MSG("MOUNT: Path '%s' found",
273 temp_line.c_str());
274 } else {
275 LOG_MSG("MOUNT: Path '%s' found, while looking for '%s'",
276 real_path.c_str(),
277 temp_line.c_str());
278 }
279 temp_line = real_path;
280 }
281
282 struct stat test;
283 if (stat(temp_line.c_str(),&test)) {
284 WriteOut(MSG_Get("PROGRAM_MOUNT_ERROR_1"),temp_line.c_str());
285 return;
286 }
287 /* Not a switch so a normal directory/file */
288 if (!S_ISDIR(test.st_mode)) {
289 WriteOut(MSG_Get("PROGRAM_MOUNT_ERROR_2"),temp_line.c_str());
290 return;
291 }
292
293 if (temp_line[temp_line.size() - 1] != CROSS_FILESPLIT) temp_line += CROSS_FILESPLIT;
294 Bit8u bit8size = (Bit8u)sizes[1];
295
296 if (type == "cdrom") {
297 // Following options were relevant only for physical CD-ROM support:
298 for (auto opt : {"-usecd", "-noioctl", "-ioctl", "-ioctl_dx", "-ioctl_mci", "-ioctl_dio"}) {
299 if (cmd->FindExist(opt, false))
300 WriteOut(MSG_Get("MSCDEX_WARNING_NO_OPTION"), opt);
301 }
302
303 int error = 0;
304 newdrive = new cdromDrive(drive,temp_line.c_str(),sizes[0],bit8size,sizes[2],0,mediaid,error);
305 // Check Mscdex, if it worked out...
306 switch (error) {
307 case 0 : WriteOut(MSG_Get("MSCDEX_SUCCESS")); break;
308 case 1 : WriteOut(MSG_Get("MSCDEX_ERROR_MULTIPLE_CDROMS")); break;
309 case 2 : WriteOut(MSG_Get("MSCDEX_ERROR_NOT_SUPPORTED")); break;
310 case 3 : WriteOut(MSG_Get("MSCDEX_ERROR_PATH")); break;
311 case 4 : WriteOut(MSG_Get("MSCDEX_TOO_MANY_DRIVES")); break;
312 case 5 : WriteOut(MSG_Get("MSCDEX_LIMITED_SUPPORT")); break;
313 default : WriteOut(MSG_Get("MSCDEX_UNKNOWN_ERROR")); break;
314 };
315 if (error && error!=5) {
316 delete newdrive;
317 return;
318 }
319 } else {
320 /* Give a warning when mount c:\ or the / */
321 #if defined (WIN32)
322 if ( (temp_line == "c:\\") || (temp_line == "C:\\") ||
323 (temp_line == "c:/") || (temp_line == "C:/") )
324 WriteOut(MSG_Get("PROGRAM_MOUNT_WARNING_WIN"));
325 #else
326 if (temp_line == "/") WriteOut(MSG_Get("PROGRAM_MOUNT_WARNING_OTHER"));
327 #endif
328 if (type == "overlay") {
329 localDrive *ldp = dynamic_cast<localDrive *>(
330 Drives[drive_index(drive)]);
331 cdromDrive *cdp = dynamic_cast<cdromDrive *>(
332 Drives[drive_index(drive)]);
333 if (!ldp || cdp) {
334 WriteOut(MSG_Get("PROGRAM_MOUNT_OVERLAY_INCOMPAT_BASE"));
335 return;
336 }
337 std::string base = ldp->GetBasedir();
338 Bit8u o_error = 0;
339 newdrive = new Overlay_Drive(base.c_str(),temp_line.c_str(),sizes[0],bit8size,sizes[2],sizes[3],mediaid,o_error);
340 //Erase old drive on success
341 if (o_error) {
342 if (o_error == 1) WriteOut("No mixing of relative and absolute paths. Overlay failed.");
343 else if (o_error == 2) WriteOut("overlay directory can not be the same as underlying file system.");
344 else WriteOut("Something went wrong");
345 delete newdrive;
346 return;
347 }
348 //Copy current directory if not marked as deleted.
349 if (newdrive->TestDir(ldp->curdir)) {
350 safe_strcpy(newdrive->curdir,
351 ldp->curdir);
352 }
353
354 delete Drives[drive_index(drive)];
355 Drives[drive_index(drive)] = nullptr;
356 } else {
357 newdrive = new localDrive(temp_line.c_str(),sizes[0],bit8size,sizes[2],sizes[3],mediaid);
358 }
359 }
360 } else {
361 WriteOut(MSG_Get("PROGRAM_MOUNT_ILL_TYPE"),type.c_str());
362 return;
363 }
364 Drives[drive_index(drive)] = newdrive;
365 /* Set the correct media byte in the table */
366 mem_writeb(Real2Phys(dos.tables.mediaid) + (drive_index(drive)) * 9,
367 newdrive->GetMediaByte());
368 if (type != "overlay")
369 WriteOut(MSG_Get("PROGRAM_MOUNT_STATUS_2"), drive,
370 newdrive->GetInfo());
371 else
372 WriteOut(MSG_Get("PROGRAM_MOUNT_OVERLAY_STATUS"),
373 temp_line.c_str(), drive);
374 /* check if volume label is given and don't allow it to updated in the future */
375 if (cmd->FindString("-label",label,true)) newdrive->dirCache.SetLabel(label.c_str(),iscdrom,false);
376 /* For hard drives set the label to DRIVELETTER_Drive.
377 * For floppy drives set the label to DRIVELETTER_Floppy.
378 * This way every drive except cdroms should get a label.*/
379 else if (type == "dir" || type == "overlay") {
380 label = drive; label += "_DRIVE";
381 newdrive->dirCache.SetLabel(label.c_str(),iscdrom,false);
382 } else if (type == "floppy") {
383 label = drive; label += "_FLOPPY";
384 newdrive->dirCache.SetLabel(label.c_str(),iscdrom,true);
385 }
386 if (type == "floppy") incrementFDD();
387 return;
388 showusage:
389 WriteOut(MSG_Get("SHELL_CMD_MOUNT_HELP_LONG"));
390 return;
391 }
392
MOUNT_ProgramStart(Program ** make)393 void MOUNT_ProgramStart(Program **make) {
394 *make=new MOUNT;
395 }
396