]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/bsys.c
9dd7c96c3a8bf5460c5a6e14f75f6c3f65728487
[bacula/bacula] / bacula / src / lib / bsys.c
1 /*
2  * Miscellaneous Bacula memory and thread safe routines
3  *   Generally, these are interfaces to system or standard
4  *   library routines.
5  *
6  *  Bacula utility functions are in util.c
7  *
8  *   Version $Id$
9  */
10 /*
11    Copyright (C) 2000-2006 Kern Sibbald
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License
15    version 2 as amended with additional clauses defined in the
16    file LICENSE in the main source directory.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
21    the file LICENSE for additional details.
22
23  */
24
25
26 #include "bacula.h"
27 #ifdef HAVE_PWD_H
28 #include <pwd.h>
29 #endif
30 #ifdef HAVE_GRP_H
31 #include <grp.h>
32 #endif
33
34 static pthread_mutex_t timer_mutex = PTHREAD_MUTEX_INITIALIZER;
35 static pthread_cond_t timer = PTHREAD_COND_INITIALIZER;
36
37 /*
38  * This routine will sleep (sec, microsec).  Note, however, that if a
39  *   signal occurs, it will return early.  It is up to the caller
40  *   to recall this routine if he/she REALLY wants to sleep the
41  *   requested time.
42  */
43 int bmicrosleep(time_t sec, long usec)
44 {
45    struct timespec timeout;
46    struct timeval tv;
47    struct timezone tz;
48    int stat;
49
50    timeout.tv_sec = sec;
51    timeout.tv_nsec = usec * 1000;
52
53 #ifdef HAVE_NANOSLEEP
54    stat = nanosleep(&timeout, NULL);
55    if (!(stat < 0 && errno == ENOSYS)) {
56       return stat;
57    }
58    /* If we reach here it is because nanosleep is not supported by the OS */
59 #endif
60
61    /* Do it the old way */
62    gettimeofday(&tv, &tz);
63    timeout.tv_nsec += tv.tv_usec * 1000;
64    timeout.tv_sec += tv.tv_sec;
65    while (timeout.tv_nsec >= 1000000000) {
66       timeout.tv_nsec -= 1000000000;
67       timeout.tv_sec++;
68    }
69
70    Dmsg2(200, "pthread_cond_timedwait sec=%d usec=%d\n", sec, usec);
71    /* Note, this unlocks mutex during the sleep */
72    P(timer_mutex);
73    stat = pthread_cond_timedwait(&timer, &timer_mutex, &timeout);
74    if (stat != 0) {
75       berrno be;
76       Dmsg2(200, "pthread_cond_timedwait stat=%d ERR=%s\n", stat,
77          be.strerror(stat));
78    }
79    V(timer_mutex);
80    return stat;
81 }
82
83 /*
84  * Guarantee that the string is properly terminated */
85 char *bstrncpy(char *dest, const char *src, int maxlen)
86 {
87    strncpy(dest, src, maxlen-1);
88    dest[maxlen-1] = 0;
89    return dest;
90 }
91
92 /*
93  * Guarantee that the string is properly terminated */
94 char *bstrncpy(char *dest, POOL_MEM &src, int maxlen)
95 {
96    strncpy(dest, src.c_str(), maxlen-1);
97    dest[maxlen-1] = 0;
98    return dest;
99 }
100
101
102 char *bstrncat(char *dest, const char *src, int maxlen)
103 {
104    strncat(dest, src, maxlen-1);
105    dest[maxlen-1] = 0;
106    return dest;
107 }
108
109 char *bstrncat(char *dest, POOL_MEM &src, int maxlen)
110 {
111    strncat(dest, src.c_str(), maxlen-1);
112    dest[maxlen-1] = 0;
113    return dest;
114 }
115
116 /*
117  * Allows one or both pointers to be NULL
118  */
119 bool bstrcmp(const char *s1, const char *s2)
120 {
121    if (s1 == s2) return true;
122    if (s1 == NULL || s2 == NULL) return false;
123    return strcmp(s1, s2) == 0;
124 }
125
126 /*
127  * Get character length of UTF-8 string
128  *
129  * Valid UTF-8 codes
130  * U-00000000 - U-0000007F: 0xxxxxxx 
131  * U-00000080 - U-000007FF: 110xxxxx 10xxxxxx 
132  * U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx 
133  * U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
134  * U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
135  * U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
136  */
137 int cstrlen(const char *str)
138 {
139    uint8_t *p = (uint8_t *)str;
140    int len = 0;
141    while (*p) {
142       if ((*p & 0xC0) != 0xC0) {
143          p++;
144          len++;
145          continue;
146       }
147       if ((*p & 0xD0) == 0xC0) {
148          p += 2;
149          len++;
150          continue;
151       }
152       if ((*p & 0xF0) == 0xD0) {
153          p += 3;
154          len++;
155          continue;
156       }
157       if ((*p & 0xF8) == 0xF0) {
158          p += 4;
159          len++;
160          continue;
161       }
162       if ((*p & 0xFC) == 0xF8) {
163          p += 5;
164          len++;
165          continue;
166       }
167       if ((*p & 0xFE) == 0xFC) {
168          p += 6;
169          len++;
170          continue;
171       }
172       p++;                      /* Shouln't get here but must advance */
173    }
174    return len;
175 }
176
177
178
179 #ifndef bmalloc
180 void *bmalloc(size_t size)
181 {
182   void *buf;
183
184 #ifdef SMARTALLOC
185   buf = sm_malloc(file, line, size);
186 #else
187   buf = malloc(size);
188 #endif
189   if (buf == NULL) {
190      berrno be;
191      Emsg1(M_ABORT, 0, _("Out of memory: ERR=%s\n"), be.strerror());
192   }
193   return buf;
194 }
195 #endif
196
197 void *b_malloc(const char *file, int line, size_t size)
198 {
199   void *buf;
200
201 #ifdef SMARTALLOC
202   buf = sm_malloc(file, line, size);
203 #else
204   buf = malloc(size);
205 #endif
206   if (buf == NULL) {
207      berrno be;
208      e_msg(file, line, M_ABORT, 0, _("Out of memory: ERR=%s\n"), be.strerror());
209   }
210   return buf;
211 }
212
213
214 void bfree(void *buf)
215 {
216 #ifdef SMARTALLOC
217   sm_free(__FILE__, __LINE__, buf);
218 #else
219   free(buf);
220 #endif
221 }
222
223
224
225
226 void *brealloc (void *buf, size_t size)
227 {
228    buf = realloc(buf, size);
229    if (buf == NULL) {
230       berrno be;
231       Emsg1(M_ABORT, 0, _("Out of memory: ERR=%s\n"), be.strerror());
232    }
233    return buf;
234 }
235
236
237 void *bcalloc (size_t size1, size_t size2)
238 {
239   void *buf;
240
241    buf = calloc(size1, size2);
242    if (buf == NULL) {
243       berrno be;
244       Emsg1(M_ABORT, 0, _("Out of memory: ERR=%s\n"), be.strerror());
245    }
246    return buf;
247 }
248
249 /* Code now in src/lib/bsnprintf.c */
250 #ifndef USE_BSNPRINTF
251
252 #define BIG_BUF 5000
253 /*
254  * Implement snprintf
255  */
256 int bsnprintf(char *str, int32_t size, const char *fmt,  ...)
257 {
258    va_list   arg_ptr;
259    int len;
260
261    va_start(arg_ptr, fmt);
262    len = bvsnprintf(str, size, fmt, arg_ptr);
263    va_end(arg_ptr);
264    return len;
265 }
266
267 /*
268  * Implement vsnprintf()
269  */
270 int bvsnprintf(char *str, int32_t size, const char  *format, va_list ap)
271 {
272 #ifdef HAVE_VSNPRINTF
273    int len;
274    len = vsnprintf(str, size, format, ap);
275    str[size-1] = 0;
276    return len;
277
278 #else
279
280    int len, buflen;
281    char *buf;
282    buflen = size > BIG_BUF ? size : BIG_BUF;
283    buf = get_memory(buflen);
284    len = vsprintf(buf, format, ap);
285    if (len >= buflen) {
286       Emsg0(M_ABORT, 0, _("Buffer overflow.\n"));
287    }
288    memcpy(str, buf, len);
289    str[len] = 0;                /* len excludes the null */
290    free_memory(buf);
291    return len;
292 #endif
293 }
294 #endif /* USE_BSNPRINTF */
295
296 #ifndef HAVE_LOCALTIME_R
297
298 struct tm *localtime_r(const time_t *timep, struct tm *tm)
299 {
300     static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
301     struct tm *ltm,
302
303     P(mutex);
304     ltm = localtime(timep);
305     if (ltm) {
306        memcpy(tm, ltm, sizeof(struct tm));
307     }
308     V(mutex);
309     return ltm ? tm : NULL;
310 }
311 #endif /* HAVE_LOCALTIME_R */
312
313 #ifndef HAVE_READDIR_R
314 #ifndef HAVE_WIN32
315 #include <dirent.h>
316
317 int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
318 {
319     static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
320     struct dirent *ndir;
321     int stat;
322
323     P(mutex);
324     errno = 0;
325     ndir = readdir(dirp);
326     stat = errno;
327     if (ndir) {
328        memcpy(entry, ndir, sizeof(struct dirent));
329        strcpy(entry->d_name, ndir->d_name);
330        *result = entry;
331     } else {
332        *result = NULL;
333     }
334     V(mutex);
335     return stat;
336
337 }
338 #endif
339 #endif /* HAVE_READDIR_R */
340
341
342 int bstrerror(int errnum, char *buf, size_t bufsiz)
343 {
344     static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
345     int stat = 0;
346     const char *msg;
347
348     P(mutex);
349
350     msg = strerror(errnum);
351     if (!msg) {
352        msg = _("Bad errno");
353        stat = -1;
354     }
355     bstrncpy(buf, msg, bufsiz);
356     V(mutex);
357     return stat;
358 }
359
360 /*
361  * These are mutex routines that do error checking
362  *  for deadlock and such.  Normally not turned on.
363  */
364 #ifdef DEBUG_MUTEX
365 void _p(char *file, int line, pthread_mutex_t *m)
366 {
367    int errstat;
368    if ((errstat = pthread_mutex_trylock(m))) {
369       e_msg(file, line, M_ERROR, 0, _("Possible mutex deadlock.\n"));
370       /* We didn't get the lock, so do it definitely now */
371       if ((errstat=pthread_mutex_lock(m))) {
372          berrno be;
373          e_msg(file, line, M_ABORT, 0, _("Mutex lock failure. ERR=%s\n"),
374                be.strerror(errstat));
375       } else {
376          e_msg(file, line, M_ERROR, 0, _("Possible mutex deadlock resolved.\n"));
377       }
378
379    }
380 }
381
382 void _v(char *file, int line, pthread_mutex_t *m)
383 {
384    int errstat;
385
386    /* Note, this trylock *should* fail if the mutex is locked */
387    if ((errstat=pthread_mutex_trylock(m)) == 0) {
388       berrno be;
389       e_msg(file, line, M_ERROR, 0, _("Mutex unlock not locked. ERR=%s\n"),
390            be.strerror(errstat));
391     }
392     if ((errstat=pthread_mutex_unlock(m))) {
393        berrno be;
394        e_msg(file, line, M_ABORT, 0, _("Mutex unlock failure. ERR=%s\n"),
395               be.strerror(errstat));
396     }
397 }
398
399 #else
400
401 void _p(pthread_mutex_t *m)
402 {
403    int errstat;
404    if ((errstat=pthread_mutex_lock(m))) {
405       berrno be;
406       e_msg(__FILE__, __LINE__, M_ABORT, 0, _("Mutex lock failure. ERR=%s\n"),
407             be.strerror(errstat));
408    }
409 }
410
411 void _v(pthread_mutex_t *m)
412 {
413    int errstat;
414    if ((errstat=pthread_mutex_unlock(m))) {
415       berrno be;
416       e_msg(__FILE__, __LINE__, M_ABORT, 0, _("Mutex unlock failure. ERR=%s\n"),
417             be.strerror(errstat));
418    }
419 }
420
421 #endif /* DEBUG_MUTEX */
422
423 #ifdef DEBUG_MEMSET
424 /* These routines are not normally turned on */
425 #undef memset
426 void b_memset(const char *file, int line, void *mem, int val, size_t num)
427 {
428    /* Testing for 2000 byte zero at beginning of Volume block */
429    if (num > 1900 && num < 3000) {
430       Pmsg3(000, _("Memset for %d bytes at %s:%d\n"), (int)num, file, line);
431    }
432    memset(mem, val, num);
433 }
434 #endif
435
436 #if !defined(HAVE_WIN32)
437 static int del_pid_file_ok = FALSE;
438 #endif
439
440 /*
441  * Create a standard "Unix" pid file.
442  */
443 void create_pid_file(char *dir, const char *progname, int port)
444 {
445 #if !defined(HAVE_WIN32)
446    int pidfd, len;
447    int oldpid;
448    char  pidbuf[20];
449    POOLMEM *fname = get_pool_memory(PM_FNAME);
450    struct stat statp;
451
452    Mmsg(&fname, "%s/%s.%d.pid", dir, progname, port);
453    if (stat(fname, &statp) == 0) {
454       /* File exists, see what we have */
455       *pidbuf = 0;
456       if ((pidfd = open(fname, O_RDONLY|O_BINARY, 0)) < 0 ||
457            read(pidfd, &pidbuf, sizeof(pidbuf)) < 0 ||
458            sscanf(pidbuf, "%d", &oldpid) != 1) {
459          Emsg2(M_ERROR_TERM, 0, _("Cannot open pid file. %s ERR=%s\n"), fname, strerror(errno));
460       }
461       /* See if other Bacula is still alive */
462       if (kill(oldpid, 0) != -1 || errno != ESRCH) {
463          Emsg3(M_ERROR_TERM, 0, _("%s is already running. pid=%d\nCheck file %s\n"),
464                progname, oldpid, fname);
465       }
466       /* He is not alive, so take over file ownership */
467       unlink(fname);                  /* remove stale pid file */
468    }
469    /* Create new pid file */
470    if ((pidfd = open(fname, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0640)) >= 0) {
471       len = sprintf(pidbuf, "%d\n", (int)getpid());
472       write(pidfd, pidbuf, len);
473       close(pidfd);
474       del_pid_file_ok = TRUE;         /* we created it so we can delete it */
475    } else {
476       Emsg2(M_ERROR_TERM, 0, _("Could not open pid file. %s ERR=%s\n"), fname, strerror(errno));
477    }
478    free_pool_memory(fname);
479 #endif
480 }
481
482
483 /*
484  * Delete the pid file if we created it
485  */
486 int delete_pid_file(char *dir, const char *progname, int port)
487 {
488 #if !defined(HAVE_WIN32)
489    POOLMEM *fname = get_pool_memory(PM_FNAME);
490
491    if (!del_pid_file_ok) {
492       free_pool_memory(fname);
493       return 0;
494    }
495    del_pid_file_ok = FALSE;
496    Mmsg(&fname, "%s/%s.%d.pid", dir, progname, port);
497    unlink(fname);
498    free_pool_memory(fname);
499 #endif
500    return 1;
501 }
502
503 struct s_state_hdr {
504    char id[14];
505    int32_t version;
506    uint64_t last_jobs_addr;
507    uint64_t reserved[20];
508 };
509
510 static struct s_state_hdr state_hdr = {
511    "Bacula State\n",
512    3,
513    0
514 };
515
516 /*
517  * Open and read the state file for the daemon
518  */
519 void read_state_file(char *dir, const char *progname, int port)
520 {
521    int sfd;
522    ssize_t stat;
523    bool ok = false;
524    POOLMEM *fname = get_pool_memory(PM_FNAME);
525    struct s_state_hdr hdr;
526    int hdr_size = sizeof(hdr);
527
528    Mmsg(&fname, "%s/%s.%d.state", dir, progname, port);
529    /* If file exists, see what we have */
530 // Dmsg1(10, "O_BINARY=%d\n", O_BINARY);
531    if ((sfd = open(fname, O_RDONLY|O_BINARY)) < 0) {
532       Dmsg3(010, "Could not open state file. sfd=%d size=%d: ERR=%s\n",
533                     sfd, sizeof(hdr), strerror(errno));
534       goto bail_out;
535    }
536    if ((stat=read(sfd, &hdr, hdr_size)) != hdr_size) {
537       Dmsg4(010, "Could not read state file. sfd=%d stat=%d size=%d: ERR=%s\n",
538                     sfd, (int)stat, hdr_size, strerror(errno));
539       goto bail_out;
540    }
541    if (hdr.version != state_hdr.version) {
542       Dmsg2(010, "Bad hdr version. Wanted %d got %d\n",
543          state_hdr.version, hdr.version);
544       goto bail_out;
545    }
546    hdr.id[13] = 0;
547    if (strcmp(hdr.id, state_hdr.id) != 0) {
548       Dmsg0(000, "State file header id invalid.\n");
549       goto bail_out;
550    }
551 // Dmsg1(010, "Read header of %d bytes.\n", sizeof(hdr));
552    if (!read_last_jobs_list(sfd, hdr.last_jobs_addr)) {
553       goto bail_out;
554    }
555    ok = true;
556 bail_out:
557    if (sfd >= 0) {
558       close(sfd);
559    }
560    if (!ok) {
561       unlink(fname);
562     }
563    free_pool_memory(fname);
564 }
565
566 /*
567  * Write the state file
568  */
569 void write_state_file(char *dir, const char *progname, int port)
570 {
571    int sfd;
572    bool ok = false;
573    POOLMEM *fname = get_pool_memory(PM_FNAME);
574
575    Mmsg(&fname, "%s/%s.%d.state", dir, progname, port);
576    /* Create new state file */
577    unlink(fname);
578    if ((sfd = open(fname, O_CREAT|O_WRONLY|O_BINARY, 0640)) < 0) {
579       berrno be;
580       Dmsg2(000, "Could not create state file. %s ERR=%s\n", fname, be.strerror());
581       Emsg2(M_ERROR, 0, _("Could not create state file. %s ERR=%s\n"), fname, be.strerror());
582       goto bail_out;
583    }
584    if (write(sfd, &state_hdr, sizeof(state_hdr)) != sizeof(state_hdr)) {
585       berrno be;
586       Dmsg1(000, "Write hdr error: ERR=%s\n", be.strerror());
587       goto bail_out;
588    }
589 // Dmsg1(010, "Wrote header of %d bytes\n", sizeof(state_hdr));
590    state_hdr.last_jobs_addr = sizeof(state_hdr);
591    state_hdr.reserved[0] = write_last_jobs_list(sfd, state_hdr.last_jobs_addr);
592 // Dmsg1(010, "write last job end = %d\n", (int)state_hdr.reserved[0]);
593    if (lseek(sfd, 0, SEEK_SET) < 0) {
594       berrno be;
595       Dmsg1(000, "lseek error: ERR=%s\n", be.strerror());
596       goto bail_out;
597    }
598    if (write(sfd, &state_hdr, sizeof(state_hdr)) != sizeof(state_hdr)) {
599       berrno be;
600       Pmsg1(000, _("Write final hdr error: ERR=%s\n"), be.strerror());
601       goto bail_out;
602    }
603    ok = true;
604 // Dmsg1(010, "rewrote header = %d\n", sizeof(state_hdr));
605 bail_out:
606    if (sfd >= 0) {
607       close(sfd);
608    }
609    if (!ok) {
610       unlink(fname);
611    }
612    free_pool_memory(fname);
613 }
614
615
616 /*
617  * Drop to privilege new userid and new gid if non-NULL
618  */
619 void drop(char *uname, char *gname)
620 {
621 #if   defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
622    struct passwd *passw = NULL;
623    struct group *group = NULL;
624    gid_t gid;
625    uid_t uid;
626    char username[1000];         
627
628    Dmsg2(900, "uname=%s gname=%s\n", uname?uname:"NONE", gname?gname:"NONE");
629    if (!uname && !gname) {
630       return;                            /* Nothing to do */
631    }
632
633    if (uname) {
634       if ((passw = getpwnam(uname)) == NULL) {
635          berrno be;
636          Emsg2(M_ERROR_TERM, 0, _("Could not find userid=%s: ERR=%s\n"), uname,
637             be.strerror());
638       }
639    } else {
640       if ((passw = getpwuid(getuid())) == NULL) {
641          berrno be;
642          Emsg1(M_ERROR_TERM, 0, _("Could not find password entry. ERR=%s\n"),
643             be.strerror());
644       } else {
645          uname = passw->pw_name;
646       }
647    }
648    /* Any OS uname pointer may get overwritten, so save name, uid, and gid */
649    bstrncpy(username, uname, sizeof(username));
650    uid = passw->pw_uid;
651    gid = passw->pw_gid;
652    if (gname) {
653       if ((group = getgrnam(gname)) == NULL) {
654          berrno be;
655          Emsg2(M_ERROR_TERM, 0, _("Could not find group=%s: ERR=%s\n"), gname,
656             be.strerror());
657       }
658       gid = group->gr_gid;
659    }
660    if (initgroups(username, gid)) {
661       berrno be;
662       if (gname) {
663          Emsg3(M_ERROR_TERM, 0, _("Could not initgroups for group=%s, userid=%s: ERR=%s\n"),         
664             gname, username, be.strerror());
665       } else {
666          Emsg2(M_ERROR_TERM, 0, _("Could not initgroups for userid=%s: ERR=%s\n"),         
667             username, be.strerror());
668       }
669    }
670    if (gname) {
671       if (setgid(gid)) {
672          berrno be;
673          Emsg2(M_ERROR_TERM, 0, _("Could not set group=%s: ERR=%s\n"), gname,
674             be.strerror());
675       }
676    }
677    if (setuid(uid)) {
678       berrno be;
679       Emsg1(M_ERROR_TERM, 0, _("Could not set specified userid: %s\n"), username);
680    }
681 #endif
682 }
683
684
685 /* BSDI does not have this.  This is a *poor* simulation */
686 #ifndef HAVE_STRTOLL
687 long long int
688 strtoll(const char *ptr, char **endptr, int base)
689 {
690    return (long long int)strtod(ptr, endptr);
691 }
692 #endif
693
694 /*
695  * Bacula's implementation of fgets(). The difference is that it handles
696  *   being interrupted by a signal (e.g. a SIGCHLD).
697  */
698 #undef fgetc
699 char *bfgets(char *s, int size, FILE *fd)
700 {
701    char *p = s;
702    int ch;
703    *p = 0;
704    for (int i=0; i < size-1; i++) {
705       do {
706          errno = 0;
707          ch = fgetc(fd);
708       } while (ch == EOF && ferror(fd) && (errno == EINTR || errno == EAGAIN));
709       if (ch == EOF) {
710          if (i == 0) {
711             return NULL;
712          } else {
713             return s;
714          }
715       }
716       *p++ = ch;
717       *p = 0;
718       if (ch == '\r') { /* Support for Mac/Windows file format */
719          ch = fgetc(fd);
720          if (ch != '\n') { /* Mac (\r only) */
721             (void)ungetc(ch, fd); /* Push next character back to fd */
722          }
723          p[-1] = '\n';
724          break;
725       }
726       if (ch == '\n') {
727          break;
728       }
729    }
730    return s;
731 }
732
733 /*
734  * Make a "unique" filename.  It is important that if
735  *   called again with the same "what" that the result
736  *   will be identical. This allows us to use the file
737  *   without saving its name, and re-generate the name
738  *   so that it can be deleted.
739  */
740 void make_unique_filename(POOLMEM **name, int Id, char *what)
741 {
742    Mmsg(name, "%s/%s.%s.%d.tmp", working_directory, my_name, what, Id);
743 }
744
745 char *escape_filename(const char *file_path)
746 {
747    if (file_path == NULL || strpbrk(file_path, "\"\\") == NULL) {
748       return NULL;
749    }
750
751    char *escaped_path = (char *)bmalloc(2 * (strlen(file_path) + 1));
752    char *cur_char = escaped_path;
753
754    while (*file_path) {
755       if (*file_path == '\\' || *file_path == '"') {
756          *cur_char++ = '\\';
757       }
758
759       *cur_char++ = *file_path++;
760    }
761
762    *cur_char = '\0';
763
764    return escaped_path;
765 }
766
767