1 /*
2 Virtual File System: interface functions
3
4 Copyright (C) 2011-2021
5 Free Software Foundation, Inc.
6
7 Written by:
8 Slava Zanko <slavazanko@gmail.com>, 2011, 2013
9
10 This file is part of the Midnight Commander.
11
12 The Midnight Commander is free software: you can redistribute it
13 and/or modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation, either version 3 of the License,
15 or (at your option) any later version.
16
17 The Midnight Commander is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 /**
27 * \file
28 * \brief Source: Virtual File System: path handlers
29 * \author Slava Zanko
30 * \date 2011
31 */
32
33
34 #include <config.h>
35
36 #include <stdio.h>
37 #include <stdlib.h> /* For atol() */
38 #include <stdarg.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <sys/types.h>
42 #include <signal.h>
43 #include <ctype.h> /* is_digit() */
44 #include <sys/stat.h>
45 #include <unistd.h>
46 #include <dirent.h>
47 #include <pwd.h>
48 #include <grp.h>
49
50 #include "lib/global.h"
51
52 #include "lib/widget.h" /* message() */
53 #include "lib/strutil.h" /* str_crt_conv_from() */
54 #include "lib/util.h"
55
56 #include "vfs.h"
57 #include "utilvfs.h"
58 #include "path.h"
59 #include "gc.h"
60 #include "xdirentry.h"
61
62 /* TODO: move it to separate private .h */
63 extern GString *vfs_str_buffer;
64 extern vfs_class *current_vfs;
65 extern struct vfs_dirent *mc_readdir_result;
66
67 /*** global variables ****************************************************************************/
68
69 /*** file scope macro definitions ****************************************************************/
70
71 /*** file scope type declarations ****************************************************************/
72
73 /*** file scope variables ************************************************************************/
74
75 /*** file scope functions ************************************************************************/
76 /* --------------------------------------------------------------------------------------------- */
77
78 static vfs_path_t *
mc_def_getlocalcopy(const vfs_path_t * filename_vpath)79 mc_def_getlocalcopy (const vfs_path_t * filename_vpath)
80 {
81 vfs_path_t *tmp_vpath = NULL;
82 int fdin, fdout = -1;
83 ssize_t i;
84 char buffer[BUF_1K * 8];
85 struct stat mystat;
86
87 fdin = mc_open (filename_vpath, O_RDONLY | O_LINEAR);
88 if (fdin == -1)
89 goto fail;
90
91 fdout = vfs_mkstemps (&tmp_vpath, "vfs", vfs_path_get_last_path_str (filename_vpath));
92 if (fdout == -1)
93 goto fail;
94
95 while ((i = mc_read (fdin, buffer, sizeof (buffer))) > 0)
96 {
97 if (write (fdout, buffer, i) != i)
98 goto fail;
99 }
100 if (i == -1)
101 goto fail;
102 i = mc_close (fdin);
103 fdin = -1;
104 if (i == -1)
105 goto fail;
106
107 i = close (fdout);
108 fdout = -1;
109 if (i == -1)
110 goto fail;
111
112 if (mc_stat (filename_vpath, &mystat) != -1)
113 mc_chmod (tmp_vpath, mystat.st_mode);
114
115 return tmp_vpath;
116
117 fail:
118 vfs_path_free (tmp_vpath, TRUE);
119 if (fdout != -1)
120 close (fdout);
121 if (fdin != -1)
122 mc_close (fdin);
123 return NULL;
124 }
125
126 /* --------------------------------------------------------------------------------------------- */
127
128 static int
mc_def_ungetlocalcopy(const vfs_path_t * filename_vpath,const vfs_path_t * local_vpath,gboolean has_changed)129 mc_def_ungetlocalcopy (const vfs_path_t * filename_vpath,
130 const vfs_path_t * local_vpath, gboolean has_changed)
131 {
132 int fdin = -1, fdout = -1;
133 const char *local;
134
135 local = vfs_path_get_last_path_str (local_vpath);
136
137 if (has_changed)
138 {
139 char buffer[BUF_1K * 8];
140 ssize_t i;
141
142 if (vfs_path_get_last_path_vfs (filename_vpath)->write == NULL)
143 goto failed;
144
145 fdin = open (local, O_RDONLY);
146 if (fdin == -1)
147 goto failed;
148 fdout = mc_open (filename_vpath, O_WRONLY | O_TRUNC);
149 if (fdout == -1)
150 goto failed;
151 while ((i = read (fdin, buffer, sizeof (buffer))) > 0)
152 if (mc_write (fdout, buffer, (size_t) i) != i)
153 goto failed;
154 if (i == -1)
155 goto failed;
156
157 if (close (fdin) == -1)
158 {
159 fdin = -1;
160 goto failed;
161 }
162 fdin = -1;
163 if (mc_close (fdout) == -1)
164 {
165 fdout = -1;
166 goto failed;
167 }
168 }
169 unlink (local);
170 return 0;
171
172 failed:
173 message (D_ERROR, _("Changes to file lost"), "%s", vfs_path_get_last_path_str (filename_vpath));
174 if (fdout != -1)
175 mc_close (fdout);
176 if (fdin != -1)
177 close (fdin);
178 unlink (local);
179 return (-1);
180 }
181
182 /* --------------------------------------------------------------------------------------------- */
183 /*** public functions ****************************************************************************/
184 /* --------------------------------------------------------------------------------------------- */
185
186 int
mc_open(const vfs_path_t * vpath,int flags,...)187 mc_open (const vfs_path_t * vpath, int flags, ...)
188 {
189 int result = -1;
190 mode_t mode = 0;
191 const vfs_path_element_t *path_element;
192
193 if (vpath == NULL)
194 return (-1);
195
196 /* Get the mode flag */
197 if ((flags & O_CREAT) != 0)
198 {
199 va_list ap;
200
201 va_start (ap, flags);
202 /* We have to use PROMOTED_MODE_T instead of mode_t. Doing 'va_arg (ap, mode_t)'
203 * fails on systems where 'mode_t' is smaller than 'int' because of C's "default
204 * argument promotions". */
205 mode = va_arg (ap, PROMOTED_MODE_T);
206 va_end (ap);
207 }
208
209 path_element = vfs_path_get_by_index (vpath, -1);
210 if (vfs_path_element_valid (path_element) && path_element->class->open != NULL)
211 {
212 void *info;
213
214 /* open must be supported */
215 info = path_element->class->open (vpath, flags, mode);
216 if (info == NULL)
217 errno = vfs_ferrno (path_element->class);
218 else
219 result = vfs_new_handle (path_element->class, info);
220 }
221 else
222 errno = -EOPNOTSUPP;
223
224 return result;
225 }
226
227 /* --------------------------------------------------------------------------------------------- */
228
229 /* *INDENT-OFF* */
230
231 #define MC_NAMEOP(name, inarg, callarg) \
232 int mc_##name inarg \
233 { \
234 int result; \
235 const vfs_path_element_t *path_element; \
236 \
237 if (vpath == NULL) \
238 return (-1); \
239 \
240 path_element = vfs_path_get_by_index (vpath, -1); \
241 if (!vfs_path_element_valid (path_element)) \
242 return (-1); \
243 \
244 result = path_element->class->name != NULL ? path_element->class->name callarg : -1; \
245 if (result == -1) \
246 errno = path_element->class->name != NULL ? vfs_ferrno (path_element->class) : E_NOTSUPP; \
247 return result; \
248 }
249
250 MC_NAMEOP (chmod, (const vfs_path_t *vpath, mode_t mode), (vpath, mode))
251 MC_NAMEOP (chown, (const vfs_path_t *vpath, uid_t owner, gid_t group), (vpath, owner, group))
252 MC_NAMEOP (utime, (const vfs_path_t *vpath, mc_timesbuf_t * times), (vpath, times))
253 MC_NAMEOP (readlink, (const vfs_path_t *vpath, char *buf, size_t bufsiz), (vpath, buf, bufsiz))
254 MC_NAMEOP (unlink, (const vfs_path_t *vpath), (vpath))
255 MC_NAMEOP (mkdir, (const vfs_path_t *vpath, mode_t mode), (vpath, mode))
256 MC_NAMEOP (rmdir, (const vfs_path_t *vpath), (vpath))
257 MC_NAMEOP (mknod, (const vfs_path_t *vpath, mode_t mode, dev_t dev), (vpath, mode, dev))
258
259 /* *INDENT-ON* */
260
261 /* --------------------------------------------------------------------------------------------- */
262
263 int
mc_symlink(const vfs_path_t * vpath1,const vfs_path_t * vpath2)264 mc_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
265 {
266 int result = -1;
267
268 if (vpath1 != NULL && vpath2 != NULL)
269 {
270 const vfs_path_element_t *path_element;
271
272 path_element = vfs_path_get_by_index (vpath2, -1);
273 if (vfs_path_element_valid (path_element))
274 {
275 result =
276 path_element->class->symlink != NULL ?
277 path_element->class->symlink (vpath1, vpath2) : -1;
278
279 if (result == -1)
280 errno =
281 path_element->class->symlink != NULL ?
282 vfs_ferrno (path_element->class) : E_NOTSUPP;
283 }
284 }
285 return result;
286 }
287
288 /* --------------------------------------------------------------------------------------------- */
289
290 /* *INDENT-OFF* */
291
292 #define MC_HANDLEOP(name) \
293 ssize_t mc_##name (int handle, C void *buf, size_t count) \
294 { \
295 struct vfs_class *vfs; \
296 void *fsinfo = NULL; \
297 int result; \
298 \
299 if (handle == -1) \
300 return (-1); \
301 \
302 vfs = vfs_class_find_by_handle (handle, &fsinfo); \
303 if (vfs == NULL) \
304 return (-1); \
305 \
306 result = vfs->name != NULL ? vfs->name (fsinfo, buf, count) : -1; \
307 if (result == -1) \
308 errno = vfs->name != NULL ? vfs_ferrno (vfs) : E_NOTSUPP; \
309 return result; \
310 }
311
312 #define C
313 MC_HANDLEOP (read)
314 #undef C
315 #define C const
MC_HANDLEOP(write)316 MC_HANDLEOP (write)
317 #undef C
318
319 /* --------------------------------------------------------------------------------------------- */
320
321 #define MC_RENAMEOP(name) \
322 int mc_##name (const vfs_path_t *vpath1, const vfs_path_t *vpath2) \
323 { \
324 int result; \
325 const vfs_path_element_t *path_element1; \
326 const vfs_path_element_t *path_element2; \
327 \
328 if (vpath1 == NULL || vpath2 == NULL) \
329 return (-1); \
330 \
331 path_element1 = vfs_path_get_by_index (vpath1, (-1)); \
332 path_element2 = vfs_path_get_by_index (vpath2, (-1)); \
333 \
334 if (!vfs_path_element_valid (path_element1) || !vfs_path_element_valid (path_element2) || \
335 path_element1->class != path_element2->class) \
336 { \
337 errno = EXDEV; \
338 return (-1); \
339 } \
340 \
341 result = path_element1->class->name != NULL \
342 ? path_element1->class->name (vpath1, vpath2) : -1; \
343 if (result == -1) \
344 errno = path_element1->class->name != NULL ? vfs_ferrno (path_element1->class) : E_NOTSUPP; \
345 return result; \
346 }
347
348 MC_RENAMEOP (link)
349 MC_RENAMEOP (rename)
350
351 /* *INDENT-ON* */
352
353 /* --------------------------------------------------------------------------------------------- */
354
355 int
356 mc_ctl (int handle, int ctlop, void *arg)
357 {
358 struct vfs_class *vfs;
359 void *fsinfo = NULL;
360
361 vfs = vfs_class_find_by_handle (handle, &fsinfo);
362
363 return (vfs == NULL || vfs->ctl == NULL) ? 0 : vfs->ctl (fsinfo, ctlop, arg);
364 }
365
366 /* --------------------------------------------------------------------------------------------- */
367
368 int
mc_setctl(const vfs_path_t * vpath,int ctlop,void * arg)369 mc_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
370 {
371 int result = -1;
372 const vfs_path_element_t *path_element;
373
374 if (vpath == NULL)
375 vfs_die ("You don't want to pass NULL to mc_setctl.");
376
377 path_element = vfs_path_get_by_index (vpath, -1);
378 if (vfs_path_element_valid (path_element))
379 result =
380 path_element->class->setctl != NULL ? path_element->class->setctl (vpath,
381 ctlop, arg) : 0;
382
383 return result;
384 }
385
386 /* --------------------------------------------------------------------------------------------- */
387
388 int
mc_close(int handle)389 mc_close (int handle)
390 {
391 struct vfs_class *vfs;
392 void *fsinfo = NULL;
393 int result;
394
395 if (handle == -1)
396 return (-1);
397
398 vfs = vfs_class_find_by_handle (handle, &fsinfo);
399 if (vfs == NULL || fsinfo == NULL)
400 return (-1);
401
402 if (handle < 3)
403 return close (handle);
404
405 if (vfs->close == NULL)
406 vfs_die ("VFS must support close.\n");
407 result = vfs->close (fsinfo);
408 vfs_free_handle (handle);
409 if (result == -1)
410 errno = vfs_ferrno (vfs);
411
412 return result;
413 }
414
415 /* --------------------------------------------------------------------------------------------- */
416
417 DIR *
mc_opendir(const vfs_path_t * vpath)418 mc_opendir (const vfs_path_t * vpath)
419 {
420 int handle, *handlep;
421 void *info;
422 vfs_path_element_t *path_element;
423
424 if (vpath == NULL)
425 return NULL;
426
427 path_element = (vfs_path_element_t *) vfs_path_get_by_index (vpath, -1);
428 if (!vfs_path_element_valid (path_element))
429 {
430 errno = E_NOTSUPP;
431 return NULL;
432 }
433
434 info = path_element->class->opendir ? path_element->class->opendir (vpath) : NULL;
435 if (info == NULL)
436 {
437 errno = path_element->class->opendir ? vfs_ferrno (path_element->class) : E_NOTSUPP;
438 return NULL;
439 }
440
441 path_element->dir.info = info;
442
443 #ifdef HAVE_CHARSET
444 path_element->dir.converter = (path_element->encoding != NULL) ?
445 str_crt_conv_from (path_element->encoding) : str_cnv_from_term;
446 if (path_element->dir.converter == INVALID_CONV)
447 path_element->dir.converter = str_cnv_from_term;
448 #endif
449
450 handle = vfs_new_handle (path_element->class, vfs_path_element_clone (path_element));
451
452 handlep = g_new (int, 1);
453 *handlep = handle;
454 return (DIR *) handlep;
455 }
456
457 /* --------------------------------------------------------------------------------------------- */
458
459 struct vfs_dirent *
mc_readdir(DIR * dirp)460 mc_readdir (DIR * dirp)
461 {
462 int handle;
463 struct vfs_class *vfs;
464 void *fsinfo = NULL;
465 struct vfs_dirent *entry = NULL;
466 vfs_path_element_t *vfs_path_element;
467
468 if (dirp == NULL)
469 {
470 errno = EFAULT;
471 return NULL;
472 }
473
474 handle = *(int *) dirp;
475
476 vfs = vfs_class_find_by_handle (handle, &fsinfo);
477 if (vfs == NULL || fsinfo == NULL)
478 return NULL;
479
480 vfs_path_element = (vfs_path_element_t *) fsinfo;
481 if (vfs->readdir != NULL)
482 {
483 entry = vfs->readdir (vfs_path_element->dir.info);
484 if (entry == NULL)
485 return NULL;
486
487 g_string_set_size (vfs_str_buffer, 0);
488 #ifdef HAVE_CHARSET
489 str_vfs_convert_from (vfs_path_element->dir.converter, entry->d_name, vfs_str_buffer);
490 #else
491 g_string_assign (vfs_str_buffer, entry->d_name);
492 #endif
493 vfs_dirent_assign (mc_readdir_result, vfs_str_buffer->str, entry->d_ino);
494 vfs_dirent_free (entry);
495 }
496 if (entry == NULL)
497 errno = vfs->readdir ? vfs_ferrno (vfs) : E_NOTSUPP;
498 return (entry != NULL) ? mc_readdir_result : NULL;
499 }
500
501 /* --------------------------------------------------------------------------------------------- */
502
503 int
mc_closedir(DIR * dirp)504 mc_closedir (DIR * dirp)
505 {
506 int handle;
507 struct vfs_class *vfs;
508 void *fsinfo = NULL;
509 int result = -1;
510
511 if (dirp == NULL)
512 return result;
513
514 handle = *(int *) dirp;
515
516 vfs = vfs_class_find_by_handle (handle, &fsinfo);
517 if (vfs != NULL && fsinfo != NULL)
518 {
519 vfs_path_element_t *vfs_path_element = (vfs_path_element_t *) fsinfo;
520
521 #ifdef HAVE_CHARSET
522 if (vfs_path_element->dir.converter != str_cnv_from_term)
523 {
524 str_close_conv (vfs_path_element->dir.converter);
525 vfs_path_element->dir.converter = INVALID_CONV;
526 }
527 #endif
528
529 result = vfs->closedir ? (*vfs->closedir) (vfs_path_element->dir.info) : -1;
530 vfs_free_handle (handle);
531 vfs_path_element_free (vfs_path_element);
532 }
533 g_free (dirp);
534 return result;
535 }
536
537 /* --------------------------------------------------------------------------------------------- */
538
539 int
mc_stat(const vfs_path_t * vpath,struct stat * buf)540 mc_stat (const vfs_path_t * vpath, struct stat *buf)
541 {
542 int result = -1;
543 const vfs_path_element_t *path_element;
544
545 if (vpath == NULL)
546 return (-1);
547
548 path_element = vfs_path_get_by_index (vpath, -1);
549 if (vfs_path_element_valid (path_element))
550 {
551 result = path_element->class->stat ? path_element->class->stat (vpath, buf) : -1;
552 if (result == -1)
553 errno = path_element->class->name ? vfs_ferrno (path_element->class) : E_NOTSUPP;
554 }
555
556 return result;
557 }
558
559 /* --------------------------------------------------------------------------------------------- */
560
561 int
mc_lstat(const vfs_path_t * vpath,struct stat * buf)562 mc_lstat (const vfs_path_t * vpath, struct stat *buf)
563 {
564 int result = -1;
565 const vfs_path_element_t *path_element;
566
567 if (vpath == NULL)
568 return (-1);
569
570 path_element = vfs_path_get_by_index (vpath, -1);
571 if (vfs_path_element_valid (path_element))
572 {
573 result = path_element->class->lstat ? path_element->class->lstat (vpath, buf) : -1;
574 if (result == -1)
575 errno = path_element->class->name ? vfs_ferrno (path_element->class) : E_NOTSUPP;
576 }
577
578 return result;
579 }
580
581 /* --------------------------------------------------------------------------------------------- */
582
583 int
mc_fstat(int handle,struct stat * buf)584 mc_fstat (int handle, struct stat *buf)
585 {
586 struct vfs_class *vfs;
587 void *fsinfo = NULL;
588 int result;
589
590 if (handle == -1)
591 return (-1);
592
593 vfs = vfs_class_find_by_handle (handle, &fsinfo);
594 if (vfs == NULL)
595 return (-1);
596
597 result = vfs->fstat ? vfs->fstat (fsinfo, buf) : -1;
598 if (result == -1)
599 errno = vfs->fstat ? vfs_ferrno (vfs) : E_NOTSUPP;
600 return result;
601 }
602
603 /* --------------------------------------------------------------------------------------------- */
604
605 vfs_path_t *
mc_getlocalcopy(const vfs_path_t * pathname_vpath)606 mc_getlocalcopy (const vfs_path_t * pathname_vpath)
607 {
608 vfs_path_t *result = NULL;
609 const vfs_path_element_t *path_element;
610
611 if (pathname_vpath == NULL)
612 return NULL;
613
614 path_element = vfs_path_get_by_index (pathname_vpath, -1);
615 if (vfs_path_element_valid (path_element))
616 {
617 result = path_element->class->getlocalcopy != NULL ?
618 path_element->class->getlocalcopy (pathname_vpath) :
619 mc_def_getlocalcopy (pathname_vpath);
620 if (result == NULL)
621 errno = vfs_ferrno (path_element->class);
622 }
623 return result;
624 }
625
626 /* --------------------------------------------------------------------------------------------- */
627
628 int
mc_ungetlocalcopy(const vfs_path_t * pathname_vpath,const vfs_path_t * local_vpath,gboolean has_changed)629 mc_ungetlocalcopy (const vfs_path_t * pathname_vpath, const vfs_path_t * local_vpath,
630 gboolean has_changed)
631 {
632 int result = -1;
633 const vfs_path_element_t *path_element;
634
635 if (pathname_vpath == NULL)
636 return (-1);
637
638 path_element = vfs_path_get_by_index (pathname_vpath, -1);
639 if (vfs_path_element_valid (path_element))
640 result = path_element->class->ungetlocalcopy != NULL ?
641 path_element->class->ungetlocalcopy (pathname_vpath, local_vpath, has_changed) :
642 mc_def_ungetlocalcopy (pathname_vpath, local_vpath, has_changed);
643
644 return result;
645 }
646
647 /* --------------------------------------------------------------------------------------------- */
648 /**
649 * VFS chdir.
650 *
651 * @param vpath VFS-path
652 *
653 * @return 0 on success, -1 on failure.
654 */
655
656 int
mc_chdir(const vfs_path_t * vpath)657 mc_chdir (const vfs_path_t * vpath)
658 {
659 struct vfs_class *old_vfs;
660 vfsid old_vfsid;
661 int result;
662 const vfs_path_element_t *path_element;
663 vfs_path_t *cd_vpath;
664
665 if (vpath == NULL)
666 return (-1);
667
668 if (vpath->relative)
669 cd_vpath = vfs_path_to_absolute (vpath);
670 else
671 cd_vpath = vfs_path_clone (vpath);
672
673 path_element = vfs_path_get_by_index (cd_vpath, -1);
674 if (!vfs_path_element_valid (path_element) || path_element->class->chdir == NULL)
675 goto error_end;
676
677
678 result = path_element->class->chdir (cd_vpath);
679 if (result == -1)
680 {
681 errno = vfs_ferrno (path_element->class);
682 goto error_end;
683 }
684
685 old_vfsid = vfs_getid (vfs_get_raw_current_dir ());
686 old_vfs = current_vfs;
687
688 /* Actually change directory */
689 vfs_set_raw_current_dir (cd_vpath);
690
691 current_vfs = path_element->class;
692
693 /* This function uses the new current_dir implicitly */
694 vfs_stamp_create (old_vfs, old_vfsid);
695
696 /* Sometimes we assume no trailing slash on cwd */
697 path_element = vfs_path_get_by_index (vfs_get_raw_current_dir (), -1);
698 if (vfs_path_element_valid (path_element))
699 {
700 if (*path_element->path != '\0')
701 {
702 char *p;
703
704 p = strchr (path_element->path, 0) - 1;
705 if (IS_PATH_SEP (*p) && p > path_element->path)
706 *p = '\0';
707 }
708
709 #ifdef ENABLE_VFS_NET
710 {
711 struct vfs_s_super *super;
712
713 super = vfs_get_super_by_vpath (vpath);
714 if (super != NULL && super->path_element != NULL)
715 {
716 g_free (super->path_element->path);
717 super->path_element->path = g_strdup (path_element->path);
718 }
719 }
720 #endif /* ENABLE_VFS_NET */
721 }
722
723 return 0;
724
725 error_end:
726 vfs_path_free (cd_vpath, TRUE);
727 return (-1);
728 }
729
730 /* --------------------------------------------------------------------------------------------- */
731
732 off_t
mc_lseek(int fd,off_t offset,int whence)733 mc_lseek (int fd, off_t offset, int whence)
734 {
735 struct vfs_class *vfs;
736 void *fsinfo = NULL;
737 off_t result;
738
739 if (fd == -1)
740 return (-1);
741
742 vfs = vfs_class_find_by_handle (fd, &fsinfo);
743 if (vfs == NULL)
744 return (-1);
745
746 result = vfs->lseek ? vfs->lseek (fsinfo, offset, whence) : -1;
747 if (result == -1)
748 errno = vfs->lseek ? vfs_ferrno (vfs) : E_NOTSUPP;
749 return result;
750 }
751
752 /* --------------------------------------------------------------------------------------------- */
753 /* Following code heavily borrows from libiberty, mkstemps.c */
754 /*
755 * Arguments:
756 * pname (output) - pointer to the name of the temp file (needs g_free).
757 * NULL if the function fails.
758 * prefix - part of the filename before the random part.
759 * Prepend $TMPDIR or /tmp if there are no path separators.
760 * suffix - if not NULL, part of the filename after the random part.
761 *
762 * Result:
763 * handle of the open file or -1 if couldn't open any.
764 */
765
766 int
mc_mkstemps(vfs_path_t ** pname_vpath,const char * prefix,const char * suffix)767 mc_mkstemps (vfs_path_t ** pname_vpath, const char *prefix, const char *suffix)
768 {
769 char *p1, *p2;
770 int fd;
771
772 if (strchr (prefix, PATH_SEP) != NULL)
773 p1 = g_strdup (prefix);
774 else
775 {
776 /* Add prefix first to find the position of XXXXXX */
777 p1 = g_build_filename (mc_tmpdir (), prefix, (char *) NULL);
778 }
779
780 p2 = g_strconcat (p1, "XXXXXX", suffix, (char *) NULL);
781 g_free (p1);
782
783 fd = g_mkstemp (p2);
784 if (fd >= 0)
785 *pname_vpath = vfs_path_from_str (p2);
786 else
787 {
788 *pname_vpath = NULL;
789 fd = -1;
790 }
791
792 g_free (p2);
793
794 return fd;
795 }
796
797 /* --------------------------------------------------------------------------------------------- */
798 /**
799 * Return the directory where mc should keep its temporary files.
800 * This directory is (in Bourne shell terms) "${TMPDIR=/tmp}/mc-$USER"
801 * When called the first time, the directory is created if needed.
802 * The first call should be done early, since we are using fprintf()
803 * and not message() to report possible problems.
804 */
805
806 const char *
mc_tmpdir(void)807 mc_tmpdir (void)
808 {
809 static char buffer[PATH_MAX];
810 static const char *tmpdir = NULL;
811 const char *sys_tmp;
812 struct passwd *pwd;
813 struct stat st;
814 const char *error = NULL;
815
816 /* Check if already correctly initialized */
817 if (tmpdir != NULL && lstat (tmpdir, &st) == 0 && S_ISDIR (st.st_mode) &&
818 st.st_uid == getuid () && (st.st_mode & 0777) == 0700)
819 return tmpdir;
820
821 sys_tmp = getenv ("MC_TMPDIR");
822 if (sys_tmp == NULL || !IS_PATH_SEP (sys_tmp[0]))
823 {
824 sys_tmp = getenv ("TMPDIR");
825 if (sys_tmp == NULL || !IS_PATH_SEP (sys_tmp[0]))
826 sys_tmp = TMPDIR_DEFAULT;
827 }
828
829 pwd = getpwuid (getuid ());
830 if (pwd != NULL)
831 g_snprintf (buffer, sizeof (buffer), "%s/mc-%s", sys_tmp, pwd->pw_name);
832 else
833 g_snprintf (buffer, sizeof (buffer), "%s/mc-%lu", sys_tmp, (unsigned long) getuid ());
834
835 canonicalize_pathname (buffer);
836
837 /* Try to create directory */
838 if (mkdir (buffer, S_IRWXU) != 0)
839 {
840 if (errno == EEXIST && lstat (buffer, &st) == 0)
841 {
842 /* Sanity check for existing directory */
843 if (!S_ISDIR (st.st_mode))
844 error = _("%s is not a directory\n");
845 else if (st.st_uid != getuid ())
846 error = _("Directory %s is not owned by you\n");
847 else if (((st.st_mode & 0777) != 0700) && (chmod (buffer, 0700) != 0))
848 error = _("Cannot set correct permissions for directory %s\n");
849 }
850 else
851 {
852 fprintf (stderr,
853 _("Cannot create temporary directory %s: %s\n"),
854 buffer, unix_error_string (errno));
855 error = "";
856 }
857 }
858
859 if (error != NULL)
860 {
861 int test_fd;
862 char *fallback_prefix;
863 gboolean fallback_ok = FALSE;
864 vfs_path_t *test_vpath;
865
866 if (*error != '\0')
867 fprintf (stderr, error, buffer);
868
869 /* Test if sys_tmp is suitable for temporary files */
870 fallback_prefix = g_strdup_printf ("%s/mctest", sys_tmp);
871 test_fd = mc_mkstemps (&test_vpath, fallback_prefix, NULL);
872 g_free (fallback_prefix);
873 if (test_fd != -1)
874 {
875 close (test_fd);
876 test_fd = open (vfs_path_as_str (test_vpath), O_RDONLY);
877 if (test_fd != -1)
878 {
879 close (test_fd);
880 unlink (vfs_path_as_str (test_vpath));
881 fallback_ok = TRUE;
882 }
883 }
884
885 if (fallback_ok)
886 {
887 fprintf (stderr, _("Temporary files will be created in %s\n"), sys_tmp);
888 g_snprintf (buffer, sizeof (buffer), "%s", sys_tmp);
889 error = NULL;
890 }
891 else
892 {
893 fprintf (stderr, _("Temporary files will not be created\n"));
894 g_snprintf (buffer, sizeof (buffer), "%s", "/dev/null/");
895 }
896
897 vfs_path_free (test_vpath, TRUE);
898 fprintf (stderr, "%s\n", _("Press any key to continue..."));
899 getc (stdin);
900 }
901
902 tmpdir = buffer;
903
904 if (error == NULL)
905 g_setenv ("MC_TMPDIR", tmpdir, TRUE);
906
907 return tmpdir;
908 }
909
910 /* --------------------------------------------------------------------------------------------- */
911