~ubuntu-branches/ubuntu/vivid/suphp/vivid

« back to all changes in this revision

Viewing changes to src/API_Linux.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Emmanuel Lacour
  • Date: 2009-08-03 15:15:38 UTC
  • mfrom: (1.1.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20090803151538-m9ybo50vm6f0x724
Tags: 0.7.1-1
* New upstream release (closes: #528379, #520182) 
* debian/NEWS: add information about AddHandler -> AddType change introduced
  in 0.6.2-2 (closes: #517805)
* debian/conf/suphp.conf, debian/patches/01_debian.dpatch: switch from
  application/x-httpd-php to application/x-httpd-suphp to allow
  simultaneous use of mod_suphp and mod_php (closes: #519005, #514725)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
    suPHP - (c)2002-2005 Sebastian Marsching <sebastian@marsching.com>
 
2
    suPHP - (c)2002-2008 Sebastian Marsching <sebastian@marsching.com>
3
3
 
4
4
    This file is part of suPHP.
5
5
 
48
48
    throw (SystemException) {
49
49
    struct stat temp;
50
50
    if (lstat(path.c_str(), &temp) == -1) {
51
 
        throw SystemException(std::string("Could not stat \"")
52
 
                              + path + "\": "
53
 
                              + ::strerror(errno), __FILE__, __LINE__);
 
51
        throw SystemException(std::string("Could not stat \"")
 
52
                              + path + "\": "
 
53
                              + ::strerror(errno), __FILE__, __LINE__);
54
54
    }
55
55
    if ((temp.st_mode & S_IFLNK) == S_IFLNK) {
56
 
        return true;
 
56
        return true;
57
57
    } else {
58
 
        return false;
 
58
        return false;
59
59
    }
60
60
}
61
61
std::string suPHP::API_Linux::readSymlink(const std::string path) const
62
62
    throw (SystemException) {
63
63
    char buf[1024] = {0};
64
64
    if (::readlink(path.c_str(), buf, 1023) == -1) {
65
 
        throw SystemException(std::string("Could not read symlink \"")
66
 
                              + path + "\": "
67
 
                              + ::strerror(errno), __FILE__, __LINE__);
 
65
        throw SystemException(std::string("Could not read symlink \"")
 
66
                              + path + "\": "
 
67
                              + ::strerror(errno), __FILE__, __LINE__);
68
68
    }
69
69
    
70
70
    if (buf[0] == '/') {
71
 
        return std::string(buf);
 
71
        return std::string(buf);
72
72
    } else {
73
 
        if (path.rfind('/') == std::string::npos)
74
 
            return std::string(buf);
75
 
        return path.substr(0, path.rfind('/') + 1) + std::string(buf);
 
73
        if (path.rfind('/') == std::string::npos)
 
74
            return std::string(buf);
 
75
        return path.substr(0, path.rfind('/') + 1) + std::string(buf);
76
76
    }
77
77
}
78
78
 
80
80
    Environment env;
81
81
    char **entry = ::environ;
82
82
    while (*entry != NULL) {
83
 
        std::string estr = std::string(*entry);
84
 
        int eqpos = estr.find("=");
85
 
        std::string name = estr.substr(0, eqpos);
86
 
        std::string content = estr.substr(eqpos + 1);
87
 
        env.putVar(name, content);
88
 
        entry++;
 
83
        std::string estr = std::string(*entry);
 
84
        int eqpos = estr.find("=");
 
85
        std::string name = estr.substr(0, eqpos);
 
86
        std::string content = estr.substr(eqpos + 1);
 
87
        env.putVar(name, content);
 
88
        entry++;
89
89
    }
90
90
    return env;
91
91
}
94
94
    throw (LookupException) {
95
95
    struct passwd *tmpuser = ::getpwnam(username.c_str());
96
96
    if (tmpuser == NULL) {
97
 
        throw LookupException(std::string("Could not lookup username \"") 
98
 
                              + username + "\"", __FILE__, __LINE__);
 
97
        throw LookupException(std::string("Could not lookup username \"") 
 
98
                              + username + "\"", __FILE__, __LINE__);
99
99
    }
100
100
    return UserInfo(tmpuser->pw_uid);
101
101
}
108
108
    throw (LookupException) {
109
109
    struct group *tmpgroup = ::getgrnam(groupname.c_str());
110
110
    if (tmpgroup == NULL) {
111
 
        throw LookupException(std::string("Could not lookup groupname \"") 
112
 
                              + groupname + "\"", __FILE__, __LINE__);
 
111
        throw LookupException(std::string("Could not lookup groupname \"") 
 
112
                              + groupname + "\"", __FILE__, __LINE__);
113
113
    }
114
114
    return GroupInfo(tmpgroup->gr_gid);
115
115
}
140
140
 
141
141
Logger& suPHP::API_Linux::getSystemLogger() {
142
142
    if (suPHP::API_Linux::logger.get() == NULL) {
143
 
        suPHP::API_Linux::logger.reset(new API_Linux_Logger());
 
143
        suPHP::API_Linux::logger.reset(new API_Linux_Logger());
144
144
    }
145
145
    return *(suPHP::API_Linux::logger);
146
146
}
150
150
    throw (SystemException) {
151
151
    // Reset supplementary groups
152
152
    if (::setgroups(0, NULL) == -1) {
153
 
        throw SystemException(std::string("setgroups() failed: ")
154
 
                              + ::strerror(errno), __FILE__, __LINE__);
 
153
        throw SystemException(std::string("setgroups() failed: ")
 
154
                              + ::strerror(errno), __FILE__, __LINE__);
155
155
    }
156
156
    
157
157
    try {
158
 
        if (::initgroups(user.getUsername().c_str(), 
159
 
                         user.getGroupInfo().getGid()) 
160
 
            == -1) {
161
 
            throw SystemException(std::string("initgroups() failed: ")
162
 
                                  + ::strerror(errno), __FILE__, __LINE__);
163
 
        }
 
158
        if (::initgroups(user.getUsername().c_str(), 
 
159
                         user.getGroupInfo().getGid()) 
 
160
            == -1) {
 
161
            throw SystemException(std::string("initgroups() failed: ")
 
162
                                  + ::strerror(errno), __FILE__, __LINE__);
 
163
        }
164
164
    } catch (LookupException &e) {
165
 
        // Ignore this exception
166
 
        // If we have a UID, which does not exist in /etc/passwd
167
 
        // we simply cannot use supplementary groups
 
165
        // Ignore this exception
 
166
        // If we have a UID, which does not exist in /etc/passwd
 
167
        // we simply cannot use supplementary groups
168
168
    }
169
169
 
170
170
    if (::setuid(user.getUid()) == -1) {
171
 
        throw SystemException(std::string("setuid() failed: ") 
172
 
                              + ::strerror(errno), __FILE__, __LINE__);
 
171
        throw SystemException(std::string("setuid() failed: ") 
 
172
                              + ::strerror(errno), __FILE__, __LINE__);
173
173
    }
174
174
}
175
175
 
177
177
void suPHP::API_Linux::setProcessGroup(const GroupInfo& group) const
178
178
    throw (SystemException) {
179
179
    if (::setgid(group.getGid()) == -1) {
180
 
        throw SystemException(std::string("setgid() failed: ") 
181
 
                              + ::strerror(errno), __FILE__, __LINE__);
 
180
        throw SystemException(std::string("setgid() failed: ") 
 
181
                              + ::strerror(errno), __FILE__, __LINE__);
182
182
    }
183
183
}
184
184
 
186
186
    throw (LookupException) {
187
187
    struct passwd *tmpuser = ::getpwuid(uinfo.getUid());
188
188
    if (tmpuser == NULL) {
189
 
        throw LookupException(std::string("Could not lookup UID ")
190
 
                              + Util::intToStr(uinfo.getUid()),
191
 
                              __FILE__, __LINE__);
 
189
        throw LookupException(std::string("Could not lookup UID ")
 
190
                              + Util::intToStr(uinfo.getUid()),
 
191
                              __FILE__, __LINE__);
192
192
    }
193
193
    return std::string(tmpuser->pw_name);
194
194
}
198
198
    struct passwd *tmpuser = NULL;
199
199
    tmpuser = getpwuid(uinfo.getUid());
200
200
    if (tmpuser == NULL) {
201
 
        throw LookupException(std::string("Could not lookup UID ") 
202
 
                              + Util::intToStr(uinfo.getUid()), 
203
 
                              __FILE__, __LINE__);
 
201
        throw LookupException(std::string("Could not lookup UID ") 
 
202
                              + Util::intToStr(uinfo.getUid()), 
 
203
                              __FILE__, __LINE__);
204
204
    }
205
205
    return GroupInfo(tmpuser->pw_gid);
206
206
}
207
207
 
 
208
std::string suPHP::API_Linux::UserInfo_getHomeDirectory(const UserInfo& uinfo) const
 
209
    throw (LookupException) {
 
210
    struct passwd *tmpuser = NULL;
 
211
    tmpuser = getpwuid(uinfo.getUid());
 
212
    if (tmpuser == NULL) {
 
213
        throw LookupException(std::string("Could not lookup UID ") 
 
214
                              + Util::intToStr(uinfo.getUid()), 
 
215
                              __FILE__, __LINE__);
 
216
    }
 
217
    return tmpuser->pw_dir;
 
218
}
 
219
 
208
220
bool suPHP::API_Linux::UserInfo_isSuperUser(const UserInfo& uinfo) const {
209
221
    if (uinfo.getUid() == 0)
210
 
        return true;
 
222
        return true;
211
223
    else
212
 
        return false;
 
224
        return false;
213
225
}
214
226
 
215
227
std::string suPHP::API_Linux::GroupInfo_getGroupname(const GroupInfo& ginfo) 
216
228
    const throw (LookupException) {
217
229
    struct group *tmpgroup = ::getgrgid(ginfo.getGid());
218
230
    if (tmpgroup == NULL) {
219
 
        throw LookupException(std::string("Could not lookup GID ") 
220
 
                               + Util::intToStr(ginfo.getGid()),
221
 
                              __FILE__, __LINE__);
 
231
        throw LookupException(std::string("Could not lookup GID ") 
 
232
                               + Util::intToStr(ginfo.getGid()),
 
233
                              __FILE__, __LINE__);
222
234
    }
223
235
    return std::string(tmpgroup->gr_name);
224
236
}
225
237
 
226
238
bool suPHP::API_Linux::File_exists(const File& file) const {
227
239
    struct stat dummy;
228
 
    if (::stat(file.getPath().c_str(), &dummy) == 0)
229
 
        return true;
 
240
    if (::lstat(file.getPath().c_str(), &dummy) == 0)
 
241
        return true;
230
242
    else
231
 
        return false;
 
243
        return false;
232
244
}
233
245
 
234
246
std::string suPHP::API_Linux::File_getRealPath(const File& file) const
238
250
    bool failed = true;
239
251
 
240
252
    if ((currentpath.size() == 0) || (currentpath.at(0) != '/')) {
241
 
        currentpath = this->getCwd() + std::string("/") + currentpath;
 
253
        currentpath = this->getCwd() + std::string("/") + currentpath;
242
254
    }
243
255
    
244
256
    // Limit iterations to avoid infinite symlink loops
245
257
    for (int i=0; i<512; i++) {
246
 
        // If nothing is left, we have finished
247
 
        if (currentpath.size() == 0) {
248
 
            resolvedpath = ("/" + resolvedpath);
249
 
            failed = false;
250
 
            break;
251
 
        }
252
 
        
253
 
        if (this->isSymlink(currentpath)) {
254
 
            currentpath = this->readSymlink(currentpath);
255
 
        } else {
256
 
            // We know last part is not a symlink, so it is resolved
257
 
            std::string part1 = 
258
 
                currentpath.substr(0, currentpath.rfind('/'));
259
 
            std::string part2 = 
260
 
                currentpath.substr(currentpath.rfind('/')+1);
261
 
            currentpath = part1;
262
 
            if (resolvedpath.size() == 0)
263
 
                resolvedpath = part2;
264
 
            else
265
 
                resolvedpath = part2 + "/" + resolvedpath;
266
 
        }
 
258
        // If nothing is left, we have finished
 
259
        if (currentpath.size() == 0) {
 
260
            resolvedpath = ("/" + resolvedpath);
 
261
            failed = false;
 
262
            break;
 
263
        }
 
264
        
 
265
        if (this->isSymlink(currentpath)) {
 
266
            currentpath = this->readSymlink(currentpath);
 
267
        } else {
 
268
            // We know last part is not a symlink, so it is resolved
 
269
            std::string part1 = 
 
270
                currentpath.substr(0, currentpath.rfind('/'));
 
271
            std::string part2 = 
 
272
                currentpath.substr(currentpath.rfind('/')+1);
 
273
            currentpath = part1;
 
274
            if (resolvedpath.size() == 0)
 
275
                resolvedpath = part2;
 
276
            else
 
277
                resolvedpath = part2 + "/" + resolvedpath;
 
278
        }
267
279
    } 
268
280
    
269
281
    if (failed) {
270
 
        throw SystemException("Could not resolve path \"" + 
271
 
                              file.getPath() + "\"", __FILE__, __LINE__);
 
282
        throw SystemException("Could not resolve path \"" + 
 
283
                              file.getPath() + "\"", __FILE__, __LINE__);
272
284
    }
273
285
 
274
286
    while (resolvedpath.find("/./") != std::string::npos) {
275
 
        int pos = resolvedpath.find("/./");
276
 
        resolvedpath = resolvedpath.substr(0, pos)
277
 
            + resolvedpath.substr(pos + 2);
 
287
        int pos = resolvedpath.find("/./");
 
288
        resolvedpath = resolvedpath.substr(0, pos)
 
289
            + resolvedpath.substr(pos + 2);
278
290
    }
279
291
    
280
292
    while (resolvedpath.find("/../") != std::string::npos) {
281
 
        int pos = resolvedpath.find("/../");
282
 
        int pos2 = resolvedpath.rfind('/', pos-1);
283
 
        resolvedpath = resolvedpath.substr(0, pos2)
284
 
            + resolvedpath.substr(pos + 3);
 
293
        int pos = resolvedpath.find("/../");
 
294
        int pos2 = resolvedpath.rfind('/', pos-1);
 
295
        resolvedpath = resolvedpath.substr(0, pos2)
 
296
            + resolvedpath.substr(pos + 3);
285
297
    }
286
298
    
287
299
    if (resolvedpath.find("/..", resolvedpath.size() - 3) 
288
 
        != std::string::npos) {
289
 
        resolvedpath = resolvedpath.substr(0, resolvedpath.size() - 3);
290
 
        resolvedpath = resolvedpath.substr(0, resolvedpath.rfind('/'));
 
300
        != std::string::npos) {
 
301
        resolvedpath = resolvedpath.substr(0, resolvedpath.size() - 3);
 
302
        resolvedpath = resolvedpath.substr(0, resolvedpath.rfind('/'));
291
303
    }
292
304
 
293
305
    if (resolvedpath.find("/.", resolvedpath.size() - 2) 
294
 
        != std::string::npos) {
295
 
        resolvedpath = resolvedpath.substr(0, resolvedpath.size() - 2);
 
306
        != std::string::npos) {
 
307
        resolvedpath = resolvedpath.substr(0, resolvedpath.size() - 2);
296
308
    }
297
309
    
298
310
    if (resolvedpath.size() == 0)
299
 
        resolvedpath = "/";
 
311
        resolvedpath = "/";
300
312
 
301
313
    return resolvedpath;
302
314
}   
304
316
bool suPHP::API_Linux::File_hasPermissionBit(const File& file, FileMode perm) 
305
317
    const throw (SystemException) {
306
318
    struct stat temp;
307
 
    if (stat(file.getPath().c_str(), &temp) == -1) {
308
 
        throw SystemException(std::string("Could not stat \"")
309
 
                              + file.getPath() + "\": "
310
 
                              + ::strerror(errno), __FILE__, __LINE__);
 
319
    if (lstat(file.getPath().c_str(), &temp) == -1) {
 
320
        throw SystemException(std::string("Could not stat \"")
 
321
                              + file.getPath() + "\": "
 
322
                              + ::strerror(errno), __FILE__, __LINE__);
311
323
    }
312
324
    switch (perm) {
313
325
    case FILEMODE_USER_READ:
314
 
        if ((temp.st_mode & S_IRUSR) == S_IRUSR)
315
 
            return true;
316
 
        break;
 
326
        if ((temp.st_mode & S_IRUSR) == S_IRUSR)
 
327
            return true;
 
328
        break;
317
329
 
318
330
    case FILEMODE_USER_WRITE:
319
 
        if ((temp.st_mode & S_IWUSR) == S_IWUSR)
320
 
            return true;
321
 
        break;
 
331
        if ((temp.st_mode & S_IWUSR) == S_IWUSR)
 
332
            return true;
 
333
        break;
322
334
 
323
335
    case FILEMODE_USER_EXEC:
324
 
        if ((temp.st_mode & S_IXUSR) == S_IXUSR)
325
 
            return true;
326
 
        break;
 
336
        if ((temp.st_mode & S_IXUSR) == S_IXUSR)
 
337
            return true;
 
338
        break;
327
339
 
328
340
    case FILEMODE_GROUP_READ:
329
 
        if ((temp.st_mode & S_IRGRP) == S_IRGRP)
330
 
            return true;
331
 
        break;
 
341
        if ((temp.st_mode & S_IRGRP) == S_IRGRP)
 
342
            return true;
 
343
        break;
332
344
 
333
345
    case FILEMODE_GROUP_WRITE:
334
 
        if ((temp.st_mode & S_IWGRP) == S_IWGRP)
335
 
            return true;
336
 
        break;
 
346
        if ((temp.st_mode & S_IWGRP) == S_IWGRP)
 
347
            return true;
 
348
        break;
337
349
 
338
350
    case FILEMODE_GROUP_EXEC:
339
 
        if ((temp.st_mode & S_IXGRP) == S_IXGRP)
340
 
            return true;
341
 
        break;
 
351
        if ((temp.st_mode & S_IXGRP) == S_IXGRP)
 
352
            return true;
 
353
        break;
342
354
 
343
355
    case FILEMODE_OTHERS_READ:
344
 
        if ((temp.st_mode & S_IROTH) == S_IROTH)
345
 
            return true;
346
 
        break;
 
356
        if ((temp.st_mode & S_IROTH) == S_IROTH)
 
357
            return true;
 
358
        break;
347
359
 
348
360
    case FILEMODE_OTHERS_WRITE:
349
 
        if ((temp.st_mode & S_IWOTH) == S_IWOTH)
350
 
            return true;
351
 
        break;
352
 
        
 
361
        if ((temp.st_mode & S_IWOTH) == S_IWOTH)
 
362
            return true;
 
363
        break;
 
364
        
353
365
    case FILEMODE_OTHERS_EXEC:
354
 
        if ((temp.st_mode & S_IXOTH) == S_IXOTH)
355
 
            return true;
356
 
        break;
 
366
        if ((temp.st_mode & S_IXOTH) == S_IXOTH)
 
367
            return true;
 
368
        break;
357
369
    }
358
370
 
359
371
    return false;
362
374
UserInfo suPHP::API_Linux::File_getUser(const File& file) const
363
375
    throw (SystemException) {
364
376
    struct stat temp;
365
 
    if (stat(file.getPath().c_str(), &temp) == -1) {
366
 
        throw SystemException(std::string("Could not stat \"")
367
 
                              + file.getPath() + "\": "
368
 
                              + ::strerror(errno), __FILE__, __LINE__);
 
377
    if (lstat(file.getPath().c_str(), &temp) == -1) {
 
378
        throw SystemException(std::string("Could not stat \"")
 
379
                              + file.getPath() + "\": "
 
380
                              + ::strerror(errno), __FILE__, __LINE__);
369
381
    }
370
382
    return UserInfo(temp.st_uid);
371
383
}
373
385
GroupInfo suPHP::API_Linux::File_getGroup(const File& file) const
374
386
    throw (SystemException) {
375
387
    struct stat temp;
376
 
    if (stat(file.getPath().c_str(), &temp) == -1) {
377
 
        throw SystemException(std::string("Could not stat \"")
378
 
                              + file.getPath() + "\": "
379
 
                              + ::strerror(errno), __FILE__, __LINE__);
 
388
    if (lstat(file.getPath().c_str(), &temp) == -1) {
 
389
        throw SystemException(std::string("Could not stat \"")
 
390
                              + file.getPath() + "\": "
 
391
                              + ::strerror(errno), __FILE__, __LINE__);
380
392
    }
381
393
    return GroupInfo(temp.st_gid);
382
394
}
383
395
 
384
396
 
 
397
bool suPHP::API_Linux::File_isSymlink(const File& file) const throw (SystemException) {
 
398
    return this->isSymlink(file.getPath());
 
399
}
 
400
 
 
401
 
385
402
void suPHP::API_Linux::execute(std::string program, const CommandLine& cline,
386
 
                               const Environment& env) const
 
403
                               const Environment& env) const
387
404
    throw (SystemException) {
388
405
    char **sysCline = NULL;
389
406
    char **sysEnv = NULL;
397
414
    // Construct commandline
398
415
    sysCline = new char*[cline.size() + 1];
399
416
    for (i=0; i<cline.size(); i++) {
400
 
        std::string arg = cline.getArgument(i);
401
 
        sysCline[i] = new char[arg.size()+1];
402
 
        ::strncpy(sysCline[i], arg.c_str(), arg.size()+1);
 
417
        std::string arg = cline.getArgument(i);
 
418
        sysCline[i] = new char[arg.size()+1];
 
419
        ::strncpy(sysCline[i], arg.c_str(), arg.size()+1);
403
420
    }
404
421
    sysCline[cline.size()] = NULL;
405
422
    
408
425
    sysEnv = new char*[map.size() + 1];
409
426
    p = sysEnv;
410
427
    for (std::map<std::string, std::string>::iterator pos = map.begin(); 
411
 
         pos != map.end(); 
412
 
         pos++) {
413
 
        std::string var;
414
 
        var = pos->first + "=" + pos->second;
415
 
        *p = new char[var.size()+1];
416
 
        ::strncpy(*p, var.c_str(), var.size()+1);
417
 
        p++;
 
428
         pos != map.end(); 
 
429
         pos++) {
 
430
        std::string var;
 
431
        var = pos->first + "=" + pos->second;
 
432
        *p = new char[var.size()+1];
 
433
        ::strncpy(*p, var.c_str(), var.size()+1);
 
434
        p++;
418
435
    }
419
436
    *p = NULL;
420
437
 
422
439
    sysProgram = new char[program.size() + 1];
423
440
    ::strncpy(sysProgram, program.c_str(), program.size()+1);
424
441
    if (execve(sysProgram, sysCline, sysEnv) == -1) {
425
 
        throw SystemException("execve() for program \"" + program 
426
 
                              + "\" failed: " + ::strerror(errno),
427
 
                              __FILE__, __LINE__);
 
442
        throw SystemException("execve() for program \"" + program 
 
443
                              + "\" failed: " + ::strerror(errno),
 
444
                              __FILE__, __LINE__);
428
445
    }
429
446
    
430
447
    // We are still here? This cannot be good..
431
448
    throw SystemException("execve() for program \"" + program 
432
 
                          + "\" failed because of unknown reason", 
433
 
                          __FILE__, __LINE__);
 
449
                          + "\" failed because of unknown reason", 
 
450
                          __FILE__, __LINE__);
434
451
}
435
452
 
436
453
std::string suPHP::API_Linux::getCwd() const throw (SystemException) {
437
454
    char buf[4096] = {0};
438
455
    if (::getcwd(buf, 4095) == NULL)
439
 
        throw SystemException(std::string("getcwd() failed: ")
440
 
                              + ::strerror(errno), __FILE__, __LINE__);
 
456
        throw SystemException(std::string("getcwd() failed: ")
 
457
                              + ::strerror(errno), __FILE__, __LINE__);
441
458
    return std::string(buf);
442
459
}
443
460
 
444
461
void suPHP::API_Linux::setCwd(const std::string& dir) const
445
462
    throw (SystemException) {
446
463
    if(::chdir(dir.c_str())) {
447
 
        throw SystemException(std::string("chdir() failed: ")
448
 
                              + ::strerror(errno), __FILE__, __LINE__);
 
464
        throw SystemException(std::string("chdir() failed: ")
 
465
                              + ::strerror(errno), __FILE__, __LINE__);
449
466
    }
450
467
}
451
468
 
456
473
void suPHP::API_Linux::chroot(const std::string& dir) const
457
474
    throw (SystemException) {
458
475
    if (::chroot(dir.c_str())) {
459
 
        throw SystemException(std::string("chroot() failed: ")
460
 
                              + ::strerror(errno), __FILE__, __LINE__);
 
476
        throw SystemException(std::string("chroot() failed: ")
 
477
                              + ::strerror(errno), __FILE__, __LINE__);
461
478
    }
462
479
}