]>
Commit | Line | Data |
---|---|---|
b24c88b3 RC |
1 | /* |
2 | * Copyright (c) 2001, Robert Collins. | |
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 | * A copy of the GNU General Public License can be found at | |
10 | * http://www.gnu.org/ | |
11 | * | |
12 | * Written by Robert Collins <rbtcollins@hotmail.com> | |
13 | * | |
14 | */ | |
15 | ||
16 | #if 0 | |
17 | static const char *cvsid = | |
18 | "\n%%% $Id$\n"; | |
19 | #endif | |
20 | ||
21 | ||
22 | #include "win32.h" | |
23 | #include <stdio.h> | |
24 | #include <stdlib.h> | |
ca9506cc | 25 | #include <errno.h> |
b24c88b3 RC |
26 | #include "log.h" |
27 | #include "port.h" | |
28 | #include "mount.h" | |
29 | #include "mkdir.h" | |
30 | #include "mklink2.h" | |
31 | #include <unistd.h> | |
32 | ||
33 | #include "io_stream.h" | |
34 | #include "io_stream_cygfile.h" | |
19911586 RC |
35 | #include "IOStreamProvider.h" |
36 | ||
37 | /* completely private iostream registration class */ | |
38 | class CygFileProvider : public IOStreamProvider | |
39 | { | |
40 | public: | |
41 | int exists (String const &path) const | |
42 | {return io_stream_cygfile::exists(path);} | |
43 | int remove (String const &path) const | |
44 | {return io_stream_cygfile::remove(path);} | |
45 | int mklink (String const &a , String const &b, io_stream_link_t c) const | |
46 | {return io_stream_cygfile::mklink(a,b,c);} | |
47 | io_stream *open (String const &a,String const &b) const | |
48 | {return new io_stream_cygfile (a, b);} | |
49 | ~CygFileProvider (){} | |
50 | int move (String const &a,String const &b) const | |
51 | {return io_stream_cygfile::move (a, b);} | |
52 | int mkdir_p (enum path_type_t isadir, String const &path) const | |
53 | {return cygmkdir_p (isadir, path);} | |
54 | protected: | |
55 | CygFileProvider() // no creating this | |
56 | { | |
57 | io_stream::registerProvider (theInstance, "cygfile://"); | |
58 | } | |
59 | CygFileProvider(CygFileProvider const &); // no copying | |
60 | CygFileProvider &operator=(CygFileProvider const &); // no assignment | |
61 | private: | |
62 | static CygFileProvider theInstance; | |
63 | }; | |
64 | CygFileProvider CygFileProvider::theInstance = CygFileProvider(); | |
65 | ||
b24c88b3 RC |
66 | |
67 | /* For set mtime */ | |
68 | #define FACTOR (0x19db1ded53ea710LL) | |
69 | #define NSPERSEC 10000000LL | |
70 | ||
f2e49cf8 RC |
71 | String io_stream_cygfile::cwd("/"); |
72 | ||
73 | // Normalise a unix style path relative to | |
74 | // cwd. | |
75 | String | |
76 | io_stream_cygfile::normalise (String const &unixpath) | |
77 | { | |
78 | char *path,*tempout; | |
79 | ||
80 | if (unixpath.cstr_oneuse()[0]=='/') | |
81 | { | |
82 | // rooted path | |
83 | path = unixpath.cstr(); | |
84 | tempout = unixpath.cstr(); // paths only shrink. | |
85 | } | |
86 | else | |
87 | { | |
88 | path = (cwd + unixpath).cstr(); | |
89 | tempout = (cwd + unixpath).cstr(); //paths only shrink. | |
90 | } | |
91 | ||
92 | // FIXME: handle .. depth tests to prevent / + ../foo/ stepping out | |
93 | // of the cygwin tree | |
94 | // FIXME: handle /./ sequences | |
95 | bool sawslash = false; | |
96 | char *outptr = tempout; | |
97 | for (char *ptr=path; *ptr; ++ptr) | |
98 | { | |
99 | if (*ptr == '/' && sawslash) | |
100 | --outptr; | |
101 | else if (*ptr == '/') | |
102 | sawslash=true; | |
103 | else | |
104 | sawslash=false; | |
105 | *outptr++ = *ptr; | |
106 | } | |
107 | String rv = tempout; | |
108 | delete[] path; | |
109 | delete[] tempout; | |
110 | return rv; | |
111 | } | |
112 | ||
b24c88b3 RC |
113 | static void |
114 | get_root_dir_now () | |
115 | { | |
3c054baf | 116 | if (get_root_dir ().size()) |
b24c88b3 RC |
117 | return; |
118 | read_mounts (); | |
119 | } | |
120 | ||
3c054baf | 121 | io_stream_cygfile::io_stream_cygfile (String const &name, String const &mode) : fp(), fname() |
b24c88b3 | 122 | { |
b24c88b3 | 123 | errno = 0; |
3c054baf | 124 | if (!name.size() || !mode.size()) |
b24c88b3 RC |
125 | return; |
126 | ||
127 | /* do this every time because the mount points may change due to fwd/back button use... | |
128 | * TODO: make this less...manual | |
129 | */ | |
130 | get_root_dir_now (); | |
3c054baf | 131 | if (!get_root_dir ().size()) |
b24c88b3 RC |
132 | /* TODO: assign a errno for "no mount table :} " */ |
133 | return; | |
134 | ||
f2e49cf8 | 135 | fname = cygpath (normalise(name)); |
3c054baf RC |
136 | lmode = mode; |
137 | fp = fopen (fname.cstr_oneuse(), mode.cstr_oneuse()); | |
b24c88b3 RC |
138 | if (!fp) |
139 | lasterr = errno; | |
140 | } | |
141 | ||
142 | io_stream_cygfile::~io_stream_cygfile () | |
143 | { | |
b24c88b3 RC |
144 | if (fp) |
145 | fclose (fp); | |
e9440f0f | 146 | destroyed = 1; |
b24c88b3 RC |
147 | } |
148 | ||
149 | /* Static members */ | |
150 | int | |
3c054baf | 151 | io_stream_cygfile::exists (String const &path) |
b24c88b3 RC |
152 | { |
153 | get_root_dir_now (); | |
f2e49cf8 | 154 | if (get_root_dir ().size() && _access (cygpath (normalise(path)).cstr_oneuse(), 0) == 0) |
b24c88b3 RC |
155 | return 1; |
156 | return 0; | |
157 | } | |
158 | ||
159 | int | |
3c054baf | 160 | io_stream_cygfile::remove (String const &path) |
b24c88b3 | 161 | { |
3c054baf | 162 | if (!path.size()) |
b24c88b3 RC |
163 | return 1; |
164 | get_root_dir_now (); | |
3c054baf | 165 | if (!get_root_dir ().size()) |
b24c88b3 RC |
166 | /* TODO: assign a errno for "no mount table :} " */ |
167 | return 1; | |
168 | ||
f2e49cf8 | 169 | unsigned long w = GetFileAttributes (cygpath (normalise(path)).cstr_oneuse()); |
b24c88b3 RC |
170 | if (w != 0xffffffff && w & FILE_ATTRIBUTE_DIRECTORY) |
171 | { | |
f2e49cf8 | 172 | char tmp[cygpath (normalise(path)).size() + 10]; |
b24c88b3 RC |
173 | int i = 0; |
174 | do | |
175 | { | |
3c054baf | 176 | ++i; |
f2e49cf8 | 177 | sprintf (tmp, "%s.old-%d", cygpath (normalise(path)).cstr_oneuse(), i); |
b24c88b3 RC |
178 | } |
179 | while (GetFileAttributes (tmp) != 0xffffffff); | |
180 | fprintf (stderr, "warning: moving directory \"%s\" out of the way.\n", | |
f2e49cf8 RC |
181 | normalise(path).cstr_oneuse()); |
182 | MoveFile (cygpath (normalise(path)).cstr_oneuse(), tmp); | |
b24c88b3 | 183 | } |
4849e2fc | 184 | return io_stream::remove (String ("file://") + cygpath (normalise(path)).cstr_oneuse()); |
b24c88b3 RC |
185 | } |
186 | ||
187 | int | |
f2e49cf8 | 188 | io_stream_cygfile::mklink (String const &_from, String const &_to, |
b24c88b3 RC |
189 | io_stream_link_t linktype) |
190 | { | |
f2e49cf8 | 191 | if (!_from.size() || !_to.size()) |
b24c88b3 | 192 | return 1; |
f2e49cf8 RC |
193 | String from(normalise(_from)); |
194 | String to (normalise(_to)); | |
b24c88b3 RC |
195 | switch (linktype) |
196 | { | |
197 | case IO_STREAM_SYMLINK: | |
f2e49cf8 RC |
198 | // symlinks are arbitrary targets, can be anything, and are |
199 | // not subject to translation | |
200 | return mkcygsymlink (cygpath (from).cstr_oneuse(), _to.cstr_oneuse()); | |
b24c88b3 RC |
201 | case IO_STREAM_HARDLINK: |
202 | { | |
203 | /* For now, just copy */ | |
204 | /* textmode alert: should we translate when linking from an binmode to a | |
205 | text mode mount and vice verca? | |
206 | */ | |
2fa7c5a4 | 207 | io_stream *in = io_stream::open (String ("cygfile://") + to, "rb"); |
b24c88b3 RC |
208 | if (!in) |
209 | { | |
3c054baf | 210 | log (LOG_TIMESTAMP, String("could not open ") + to +" for reading in mklink"); |
b24c88b3 RC |
211 | return 1; |
212 | } | |
2fa7c5a4 | 213 | io_stream *out = io_stream::open (String ("cygfile://") + from, "wb"); |
b24c88b3 RC |
214 | if (!out) |
215 | { | |
2fa7c5a4 | 216 | log (LOG_TIMESTAMP, String("could not open ") + from + " for writing in mklink"); |
b24c88b3 RC |
217 | delete in; |
218 | return 1; | |
219 | } | |
220 | ||
3c054baf | 221 | if (io_stream::copy (in, out)) |
b24c88b3 | 222 | { |
3c054baf RC |
223 | log (LOG_TIMESTAMP, String ("Failed to hardlink ")+ from + "->" +to + |
224 | " during file copy."); | |
225 | delete in; | |
226 | delete out; | |
227 | return 1; | |
b24c88b3 RC |
228 | } |
229 | delete in; | |
230 | delete out; | |
3c054baf | 231 | return 0; |
b24c88b3 RC |
232 | } |
233 | } | |
234 | return 1; | |
235 | } | |
236 | ||
237 | ||
238 | /* virtuals */ | |
239 | ||
7c7034e8 RC |
240 | ssize_t |
241 | io_stream_cygfile::read (void *buffer, size_t len) | |
b24c88b3 RC |
242 | { |
243 | if (fp) | |
244 | return fread (buffer, 1, len, fp); | |
245 | return 0; | |
246 | } | |
247 | ||
7c7034e8 RC |
248 | ssize_t |
249 | io_stream_cygfile::write (const void *buffer, size_t len) | |
b24c88b3 RC |
250 | { |
251 | if (fp) | |
252 | return fwrite (buffer, 1, len, fp); | |
253 | return 0; | |
254 | } | |
255 | ||
7c7034e8 RC |
256 | ssize_t |
257 | io_stream_cygfile::peek (void *buffer, size_t len) | |
b24c88b3 | 258 | { |
b24c88b3 RC |
259 | if (fp) |
260 | { | |
7c7034e8 RC |
261 | int pos = ftell (fp); |
262 | ssize_t rv = fread (buffer, 1, len, fp); | |
b24c88b3 RC |
263 | fseek (fp, pos, SEEK_SET); |
264 | return rv; | |
265 | } | |
266 | return 0; | |
267 | } | |
268 | ||
269 | long | |
270 | io_stream_cygfile::tell () | |
271 | { | |
272 | if (fp) | |
273 | { | |
274 | return ftell (fp); | |
275 | } | |
276 | return 0; | |
277 | } | |
278 | ||
ca9506cc RC |
279 | int |
280 | io_stream_cygfile::seek (long where, io_stream_seek_t whence) | |
281 | { | |
282 | if (fp) | |
7c7034e8 RC |
283 | { |
284 | return fseek (fp, where, (int) whence); | |
285 | } | |
ca9506cc RC |
286 | lasterr = EBADF; |
287 | return -1; | |
288 | } | |
289 | ||
b24c88b3 RC |
290 | int |
291 | io_stream_cygfile::error () | |
292 | { | |
293 | if (fp) | |
294 | return ferror (fp); | |
295 | return lasterr; | |
296 | } | |
297 | ||
298 | int | |
f2e49cf8 | 299 | cygmkdir_p (enum path_type_t isadir, String const &_name) |
b24c88b3 | 300 | { |
f2e49cf8 | 301 | if (!_name.size()) |
b24c88b3 | 302 | return 1; |
f2e49cf8 | 303 | String name(io_stream_cygfile::normalise(_name)); |
b24c88b3 | 304 | get_root_dir_now (); |
3c054baf | 305 | if (!get_root_dir ().size()) |
b24c88b3 RC |
306 | /* TODO: assign a errno for "no mount table :} " */ |
307 | return 1; | |
1ac649ed | 308 | return mkdir_p (isadir == PATH_TO_DIR ? 1 : 0, cygpath (name).cstr_oneuse()); |
b24c88b3 RC |
309 | } |
310 | ||
b24c88b3 RC |
311 | int |
312 | io_stream_cygfile::set_mtime (int mtime) | |
313 | { | |
3c054baf | 314 | if (!fname.size()) |
b24c88b3 RC |
315 | return 1; |
316 | if (fp) | |
317 | fclose (fp); | |
318 | long long ftimev = mtime * NSPERSEC + FACTOR; | |
319 | FILETIME ftime; | |
320 | ftime.dwHighDateTime = ftimev >> 32; | |
321 | ftime.dwLowDateTime = ftimev; | |
322 | HANDLE h = | |
3c054baf | 323 | CreateFileA (fname.cstr_oneuse(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, |
b24c88b3 RC |
324 | 0, OPEN_EXISTING, |
325 | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0); | |
326 | if (h) | |
327 | { | |
328 | SetFileTime (h, 0, 0, &ftime); | |
329 | CloseHandle (h); | |
bb849dbd | 330 | #if 0 |
b24c88b3 RC |
331 | fp = fopen (fname, lmode); |
332 | if (!fp) | |
333 | lasterr = errno; | |
bb849dbd | 334 | #endif |
b24c88b3 RC |
335 | return 0; |
336 | } | |
bb849dbd RC |
337 | #if 0 |
338 | // this results in truncated files. | |
339 | // also, semantically, it's nonsense, you cannot write to a file after setting the | |
340 | // mtime without changing the mtime | |
b24c88b3 RC |
341 | fp = fopen (fname, lmode); |
342 | if (!fp) | |
343 | lasterr = errno; | |
bb849dbd | 344 | #endif |
b24c88b3 RC |
345 | return 1; |
346 | } | |
7c7034e8 RC |
347 | |
348 | int | |
f2e49cf8 | 349 | io_stream_cygfile::move (String const &_from, String const &_to) |
7c7034e8 | 350 | { |
f2e49cf8 | 351 | if (!_from.size() || !_to.size()) |
7c7034e8 | 352 | return 1; |
f2e49cf8 RC |
353 | String from (normalise(_from)); |
354 | String to(normalise(_to)); | |
7c7034e8 | 355 | get_root_dir_now (); |
3c054baf | 356 | if (!get_root_dir ().size()) |
7c7034e8 RC |
357 | /* TODO: assign a errno for "no mount table :} " */ |
358 | return 1; | |
1ac649ed | 359 | return rename (cygpath (from).cstr_oneuse(), cygpath (to).cstr_oneuse()); |
7c7034e8 | 360 | } |
341988b9 RC |
361 | |
362 | size_t | |
363 | io_stream_cygfile::get_size () | |
364 | { | |
3c054baf | 365 | if (!fname.size() ) |
341988b9 | 366 | return 0; |
e0a4db64 RC |
367 | #ifdef WIN32 |
368 | HANDLE h; | |
369 | WIN32_FIND_DATA buf; | |
370 | DWORD ret = 0; | |
371 | ||
372 | h = FindFirstFileA (fname.cstr_oneuse(), &buf); | |
373 | if (h != INVALID_HANDLE_VALUE) | |
374 | { | |
375 | if (buf.nFileSizeHigh == 0) | |
376 | ret = buf.nFileSizeLow; | |
377 | FindClose (h); | |
378 | } | |
379 | return ret; | |
380 | #else | |
381 | struct stat buf; | |
382 | /* Should this be lstat? */ | |
383 | if (stat (fname.cstr_oneuse(), &buf)) | |
384 | /* failed - should never happen in this version */ | |
385 | /* Throw an exception? */ | |
386 | return 0; | |
387 | return buf.off_t; | |
388 | #endif | |
341988b9 | 389 | } |