Update copyright notices with scripts/update-copyrights
[jlayton/glibc.git] / misc / mntent_r.c
1 /* Utilities for reading/writing fstab, mtab, etc.
2    Copyright (C) 1995-2014 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <http://www.gnu.org/licenses/>.  */
18
19 #include <alloca.h>
20 #include <mntent.h>
21 #include <stdio.h>
22 #include <stdio_ext.h>
23 #include <string.h>
24 #include <sys/types.h>
25
26 #define flockfile(s) _IO_flockfile (s)
27 #define funlockfile(s) _IO_funlockfile (s)
28
29 #undef __setmntent
30 #undef __endmntent
31 #undef __getmntent_r
32
33 /* Prepare to begin reading and/or writing mount table entries from the
34    beginning of FILE.  MODE is as for `fopen'.  */
35 FILE *
36 __setmntent (const char *file, const char *mode)
37 {
38   /* Extend the mode parameter with "c" to disable cancellation in the
39      I/O functions and "e" to set FD_CLOEXEC.  */
40   size_t modelen = strlen (mode);
41   char newmode[modelen + 3];
42   memcpy (mempcpy (newmode, mode, modelen), "ce", 3);
43   FILE *result = fopen (file, newmode);
44
45   if (result != NULL)
46     /* We do the locking ourselves.  */
47     __fsetlocking (result, FSETLOCKING_BYCALLER);
48
49   return result;
50 }
51 libc_hidden_def (__setmntent)
52 weak_alias (__setmntent, setmntent)
53
54
55 /* Close a stream opened with `setmntent'.  */
56 int
57 __endmntent (FILE *stream)
58 {
59   if (stream)           /* SunOS 4.x allows for NULL stream */
60     fclose (stream);
61   return 1;             /* SunOS 4.x says to always return 1 */
62 }
63 libc_hidden_def (__endmntent)
64 weak_alias (__endmntent, endmntent)
65
66
67 /* Since the values in a line are separated by spaces, a name cannot
68    contain a space.  Therefore some programs encode spaces in names
69    by the strings "\040".  We undo the encoding when reading an entry.
70    The decoding happens in place.  */
71 static char *
72 decode_name (char *buf)
73 {
74   char *rp = buf;
75   char *wp = buf;
76
77   do
78     if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '4' && rp[3] == '0')
79       {
80         /* \040 is a SPACE.  */
81         *wp++ = ' ';
82         rp += 3;
83       }
84     else if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '1' && rp[3] == '1')
85       {
86         /* \011 is a TAB.  */
87         *wp++ = '\t';
88         rp += 3;
89       }
90     else if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '1' && rp[3] == '2')
91       {
92         /* \012 is a NEWLINE.  */
93         *wp++ = '\n';
94         rp += 3;
95       }
96     else if (rp[0] == '\\' && rp[1] == '\\')
97       {
98         /* We have to escape \\ to be able to represent all characters.  */
99         *wp++ = '\\';
100         rp += 1;
101       }
102     else if (rp[0] == '\\' && rp[1] == '1' && rp[2] == '3' && rp[3] == '4')
103       {
104         /* \134 is also \\.  */
105         *wp++ = '\\';
106         rp += 3;
107       }
108     else
109       *wp++ = *rp;
110   while (*rp++ != '\0');
111
112   return buf;
113 }
114
115
116 /* Read one mount table entry from STREAM.  Returns a pointer to storage
117    reused on the next call, or null for EOF or error (use feof/ferror to
118    check).  */
119 struct mntent *
120 __getmntent_r (FILE *stream, struct mntent *mp, char *buffer, int bufsiz)
121 {
122   char *cp;
123   char *head;
124
125   flockfile (stream);
126   do
127     {
128       char *end_ptr;
129
130       if (fgets_unlocked (buffer, bufsiz, stream) == NULL)
131         {
132           funlockfile (stream);
133           return NULL;
134         }
135
136       end_ptr = strchr (buffer, '\n');
137       if (end_ptr != NULL)      /* chop newline */
138         *end_ptr = '\0';
139       else
140         {
141           /* Not the whole line was read.  Do it now but forget it.  */
142           char tmp[1024];
143           while (fgets_unlocked (tmp, sizeof tmp, stream) != NULL)
144             if (strchr (tmp, '\n') != NULL)
145               break;
146         }
147
148       head = buffer + strspn (buffer, " \t");
149       /* skip empty lines and comment lines:  */
150     }
151   while (head[0] == '\0' || head[0] == '#');
152
153   cp = __strsep (&head, " \t");
154   mp->mnt_fsname = cp != NULL ? decode_name (cp) : (char *) "";
155   if (head)
156     head += strspn (head, " \t");
157   cp = __strsep (&head, " \t");
158   mp->mnt_dir = cp != NULL ? decode_name (cp) : (char *) "";
159   if (head)
160     head += strspn (head, " \t");
161   cp = __strsep (&head, " \t");
162   mp->mnt_type = cp != NULL ? decode_name (cp) : (char *) "";
163   if (head)
164     head += strspn (head, " \t");
165   cp = __strsep (&head, " \t");
166   mp->mnt_opts = cp != NULL ? decode_name (cp) : (char *) "";
167   switch (head ? sscanf (head, " %d %d ", &mp->mnt_freq, &mp->mnt_passno) : 0)
168     {
169     case 0:
170       mp->mnt_freq = 0;
171     case 1:
172       mp->mnt_passno = 0;
173     case 2:
174       break;
175     }
176   funlockfile (stream);
177
178   return mp;
179 }
180 libc_hidden_def (__getmntent_r)
181 weak_alias (__getmntent_r, getmntent_r)
182
183
184 /* We have to use an encoding for names if they contain spaces or tabs.
185    To be able to represent all characters we also have to escape the
186    backslash itself.  This "function" must be a macro since we use
187    `alloca'.  */
188 #define encode_name(name) \
189   do {                                                                        \
190     const char *rp = name;                                                    \
191                                                                               \
192     while (*rp != '\0')                                                       \
193       if (*rp == ' ' || *rp == '\t' || *rp == '\n' || *rp == '\\')            \
194         break;                                                                \
195       else                                                                    \
196         ++rp;                                                                 \
197                                                                               \
198     if (*rp != '\0')                                                          \
199       {                                                                       \
200         /* In the worst case the length of the string can increase to         \
201            four times the current length.  */                                 \
202         char *wp;                                                             \
203                                                                               \
204         rp = name;                                                            \
205         name = wp = (char *) alloca (strlen (name) * 4 + 1);                  \
206                                                                               \
207         do                                                                    \
208           if (*rp == ' ')                                                     \
209             {                                                                 \
210               *wp++ = '\\';                                                   \
211               *wp++ = '0';                                                    \
212               *wp++ = '4';                                                    \
213               *wp++ = '0';                                                    \
214             }                                                                 \
215           else if (*rp == '\t')                                               \
216             {                                                                 \
217               *wp++ = '\\';                                                   \
218               *wp++ = '0';                                                    \
219               *wp++ = '1';                                                    \
220               *wp++ = '1';                                                    \
221             }                                                                 \
222           else if (*rp == '\n')                                               \
223             {                                                                 \
224               *wp++ = '\\';                                                   \
225               *wp++ = '0';                                                    \
226               *wp++ = '1';                                                    \
227               *wp++ = '2';                                                    \
228             }                                                                 \
229           else if (*rp == '\\')                                               \
230             {                                                                 \
231               *wp++ = '\\';                                                   \
232               *wp++ = '\\';                                                   \
233             }                                                                 \
234           else                                                                \
235             *wp++ = *rp;                                                      \
236         while (*rp++ != '\0');                                                \
237       }                                                                       \
238   } while (0)
239
240
241 /* Write the mount table entry described by MNT to STREAM.
242    Return zero on success, nonzero on failure.  */
243 int
244 __addmntent (FILE *stream, const struct mntent *mnt)
245 {
246   struct mntent mntcopy = *mnt;
247   if (fseek (stream, 0, SEEK_END))
248     return 1;
249
250   /* Encode spaces and tabs in the names.  */
251   encode_name (mntcopy.mnt_fsname);
252   encode_name (mntcopy.mnt_dir);
253   encode_name (mntcopy.mnt_type);
254   encode_name (mntcopy.mnt_opts);
255
256   return (fprintf (stream, "%s %s %s %s %d %d\n",
257                    mntcopy.mnt_fsname,
258                    mntcopy.mnt_dir,
259                    mntcopy.mnt_type,
260                    mntcopy.mnt_opts,
261                    mntcopy.mnt_freq,
262                    mntcopy.mnt_passno) < 0
263           || fflush (stream) != 0);
264 }
265 weak_alias (__addmntent, addmntent)
266
267
268 /* Search MNT->mnt_opts for an option matching OPT.
269    Returns the address of the substring, or null if none found.  */
270 char *
271 __hasmntopt (const struct mntent *mnt, const char *opt)
272 {
273   const size_t optlen = strlen (opt);
274   char *rest = mnt->mnt_opts, *p;
275
276   while ((p = strstr (rest, opt)) != NULL)
277     {
278       if ((p == rest || p[-1] == ',')
279           && (p[optlen] == '\0' || p[optlen] == '=' || p[optlen] == ','))
280         return p;
281
282       rest = strchr (p, ',');
283       if (rest == NULL)
284         break;
285       ++rest;
286     }
287
288   return NULL;
289 }
290 weak_alias (__hasmntopt, hasmntopt)