Imported Upstream version 17.23.5
[platform/upstream/libzypp.git] / zypp / media / Mount.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/media/Mount.cc
10  *
11 */
12
13 #include <mntent.h>
14
15 #include <cstdio>
16 #include <climits>
17 #include <cerrno>
18
19 #include <iostream>
20 #include <fstream>
21 #include <string>
22
23 #include <zypp/base/ExternalDataSource.h>
24 #include <zypp/base/Logger.h>
25 #include <zypp/media/Mount.h>
26 #include <zypp/media/MediaException.h>
27
28 #include <zypp/PathInfo.h>
29
30 using std::endl;
31
32 #ifndef N_
33 #define N_(STR) STR
34 #endif
35
36
37 namespace zypp {
38   namespace media {
39
40     std::ostream & operator<<( std::ostream & str, const MountEntry & obj )
41     {
42       str << obj.src << " on " << obj.dir << " type " << obj.type;
43       if ( ! obj.opts.empty() )
44         str << " (" << obj.opts << ")";
45       return str;
46     }
47
48
49 Mount::Mount()
50 {
51     process = 0;
52     exit_code = -1;
53 }
54
55 Mount::~Mount()
56 {
57    MIL <<  "~Mount()" << endl;
58
59    if ( process )
60       delete process;
61
62    process = NULL;
63
64    MIL << "~Mount() end" << endl;
65 }
66
67 void Mount::mount( const std::string & source,
68                    const std::string & target,
69                    const std::string & filesystem,
70                    const std::string & options,
71                    const Environment & environment )
72 {
73     const char *const argv[] = {
74         "/bin/mount",
75         "-t", filesystem.c_str(),
76         "-o", options.c_str(),
77         source.c_str(),
78         target.c_str(),
79         NULL
80      };
81
82     std::string err;
83
84     this->run(argv, environment, ExternalProgram::Stderr_To_Stdout);
85
86     if ( process == NULL )
87     {
88       ZYPP_THROW(MediaMountException("Mounting media failed", source, target));
89     }
90
91     std::string value;
92     std::string output = process->receiveLine();
93
94     // parse error messages
95     while ( output.length() > 0)
96     {
97         std::string::size_type  ret;
98
99         // extract \n
100         ret = output.find_first_of ( "\n" );
101         if ( ret != std::string::npos )
102         {
103             value.assign ( output, 0, ret );
104         }
105         else
106         {
107             value = output;
108         }
109
110         DBG << "stdout: " << value << endl;
111
112         if  ( value.find ( "is already mounted on" ) != std::string::npos )
113         {
114             err = "Media already mounted";
115         }
116         else if  ( value.find ( "ermission denied" ) != std::string::npos )
117         {
118             err = "Permission denied";
119         }
120         else if  ( value.find ( "wrong fs type" ) != std::string::npos )
121         {
122             err = "Invalid filesystem on media";
123         }
124         else if  ( value.find ( "No medium found" ) != std::string::npos )
125         {
126             err = "No medium found";
127         }
128         else if  ( value.find ( "Not a directory" ) != std::string::npos )
129         {
130             if( filesystem == "nfs" || filesystem == "nfs4" )
131             {
132                 err = "Nfs path is not a directory";
133             }
134             else
135             {
136                err = "Unable to find directory on the media";
137             }
138         }
139
140         output = process->receiveLine();
141     }
142
143     int status = Status();
144
145     if ( status == 0 )
146     {
147         // return codes overwites parsed error message
148         err = "";
149     }
150     else if ( status != 0 && err == "" )
151     {
152         err = "Mounting media failed";
153     }
154
155     if ( err != "" ) {
156       WAR << "mount " << source << " " << target << ": " << err << endl;
157       ZYPP_THROW(MediaMountException(err, source, target, value));
158     } else {
159       MIL << "mounted " << source << " " << target << endl;
160     }
161 }
162
163 void Mount::umount( const std::string & path )
164 {
165     const char *const argv[] = {
166         "/bin/umount",
167         path.c_str(),
168         NULL
169      };
170
171     std::string err;
172
173     this->run(argv, ExternalProgram::Stderr_To_Stdout);
174
175     if ( process == NULL )
176     {
177         ZYPP_THROW(MediaUnmountException("E_mount_failed", path));
178     }
179
180     std::string value;
181     std::string output = process->receiveLine();
182
183     // parse error messages
184     while ( output.length() > 0)
185     {
186         std::string::size_type  ret;
187
188         // extract \n
189         ret = output.find_first_of ( "\n" );
190         if ( ret != std::string::npos )
191         {
192             value.assign ( output, 0, ret );
193         }
194         else
195         {
196             value = output;
197         }
198
199         DBG << "stdout: " << value << endl;
200
201         // if  ( value.find ( "not mounted" ) != std::string::npos )
202         // {
203         //    err = Error::E_already_mounted;
204         // }
205
206         if  ( value.find ( "device is busy" ) != std::string::npos )
207         {
208             err = "Device is busy";
209         }
210
211         output = process->receiveLine();
212     }
213
214     int status = Status();
215
216     if ( status == 0 )
217     {
218         // return codes overwites parsed error message
219         err = "";
220     }
221     else if ( status != 0 && err == "" )
222     {
223         err = "Unmounting media failed";
224     }
225
226     if ( err != "") {
227       WAR << "umount " << path << ": " << err << endl;
228       ZYPP_THROW(MediaUnmountException(err, path));
229     } else {
230       MIL << "unmounted " << path << endl;
231     }
232 }
233
234 void Mount::run( const char *const *argv, const Environment& environment,
235                  ExternalProgram::Stderr_Disposition disp )
236 {
237   exit_code = -1;
238
239   if ( process != NULL )
240   {
241      delete process;
242      process = NULL;
243   }
244   // Launch the program
245
246   process = new ExternalProgram(argv, environment, disp, false, -1, true);
247 }
248
249 /*--------------------------------------------------------------*/
250 /* Return the exit status of the Mount process, closing the     */
251 /* connection if not already done                               */
252 /*--------------------------------------------------------------*/
253 int Mount::Status()
254 {
255    if ( process == NULL )
256       return -1;
257
258    exit_code = process->close();
259    process->kill();
260    delete process;
261    process = 0;
262
263    DBG << "exit code: " << exit_code << endl;
264
265    return exit_code;
266 }
267
268 /* Forcably kill the process */
269 void Mount::Kill()
270 {
271   if (process) process->kill();
272 }
273
274 // STATIC
275 MountEntries
276 Mount::getEntries(const std::string &mtab)
277 {
278   MountEntries             entries;
279   std::vector<std::string> mtabs;
280   bool                     verbose = false;
281
282   if( mtab.empty())
283   {
284     mtabs.push_back("/proc/mounts");
285     // Also read /etc/mtab if it is a file (on newer sytems
286     // mtab is a symlink to /proc/mounts).
287     // Reason for this is the different representation of
288     // mounted loop devices:
289     //   /etc/mtab:    /tmp/SLES-11-SP2-MINI-ISO-x86_64-Beta2-DVD.iso on /mnt type iso9660 (ro,loop=/dev/loop0)
290     //   /proc/mounts: /dev/loop0 /mnt iso9660 ro,relatime 0 0
291     if ( PathInfo( "/etc/mtab", PathInfo::LSTAT ).isFile() )
292       mtabs.push_back("/etc/mtab");
293   }
294   else
295   {
296     mtabs.push_back(mtab);
297   }
298
299   std::vector<std::string>::const_iterator t;
300   for( t=mtabs.begin(); t != mtabs.end(); ++t)
301   {
302     if( verbose)
303     {
304       DBG << "Reading mount table from '" << *t << "'" << std::endl;
305     }
306     FILE *fp = setmntent(t->c_str(), "re");
307     if( fp)
308     {
309       char          buf[PATH_MAX * 4];
310       struct mntent ent;
311
312       memset(buf,  0, sizeof(buf));
313       memset(&ent, 0, sizeof(ent));
314
315       while( getmntent_r(fp, &ent, buf, sizeof(buf)) != NULL)
316       {
317         if( ent.mnt_fsname && *ent.mnt_fsname &&
318             ent.mnt_dir    && *ent.mnt_dir    &&
319             ent.mnt_type   && *ent.mnt_type   &&
320             ent.mnt_opts   && *ent.mnt_opts)
321         {
322           MountEntry entry(
323             ent.mnt_fsname, ent.mnt_dir,
324             ent.mnt_type,   ent.mnt_opts,
325             ent.mnt_freq,   ent.mnt_passno
326           );
327
328           // Attempt quick fix for bnc#710269:
329           // MountEntry is "//dist/install/ on /var/adm/mount/AP_0x00000001 type cifs (ro,relatime,unc=\dist\install,username=,domain=suse.de"
330           // but looking for "Looking for media(cifs<//dist/install>)attached(*/var/adm/mount/AP_0x00000001)"
331           // Kick the trailing '/' in "//dist/install/"
332           // TODO: Check and fix comparison in MediaHandler::checkAttached instead.
333           if ( entry.src.size() > 1     // not for "/"
334                && entry.src[entry.src.size()-1] == '/' )
335           {
336             entry.src.erase( entry.src.size()-1 );
337           }
338           entries.push_back(entry);
339
340           memset(buf,  0, sizeof(buf));
341           memset(&ent, 0, sizeof(ent));
342         }
343       }
344       endmntent(fp);
345
346       if( entries.empty())
347       {
348         WAR << "Unable to read any entry from the mount table '" << *t << "'"
349             << std::endl;
350       }
351       else
352       {
353         // OK, have a non-empty mount table.
354         t = mtabs.end();
355         break;
356       }
357     }
358     else
359     {
360       int err = errno;
361       verbose = true;
362       WAR << "Failed to read the mount table '" << *t << "': "
363           << ::strerror(err)
364           << std::endl;
365       errno = err;
366     }
367   }
368   return entries;
369 }
370
371   } // namespace media
372 } // namespace zypp