int main(int argc, char **argv) { VSTRING *why = vstring_alloc(100); msg_vstream_init(argv[0], VSTREAM_ERR); if (argc != 2) msg_fatal("usage: %s file-to-be-locked", argv[0]); mail_conf_read(); if (dot_lockfile(argv[1], why) < 0) msg_fatal("%s", vstring_str(why)); dot_unlockfile(argv[1]); vstring_free(why); return (0); }
void mbox_release(MBOX *mp) { /* * Unfortunately we can't close the stream, because on some file systems * (AFS), the only way to find out if a file was written successfully is * to close it, and therefore the close() operation is in the mail_copy() * routine. If we really insist on owning the vstream member, then we * should export appropriate methods that mail_copy() can use in order to * manipulate a message stream. */ if (mp->locked & MBOX_DOT_LOCK) dot_unlockfile(mp->path); myfree(mp->path); myfree((char *) mp); }
MBOX *mbox_open(const char *path, int flags, mode_t mode, struct stat * st, uid_t chown_uid, gid_t chown_gid, int lock_style, const char *def_dsn, DSN_BUF *why) { struct stat local_statbuf; MBOX *mp; int locked = 0; VSTREAM *fp; if (st == 0) st = &local_statbuf; /* * If this is a regular file, create a dotlock file. This locking method * does not work well over NFS, but it is better than some alternatives. * With NFS, creating files atomically is a problem, and a successful * operation can fail with EEXIST. * * If filename.lock can't be created for reasons other than "file exists", * issue only a warning if the application says it is non-fatal. This is * for bass-awkward compatibility with existing installations that * deliver to files in non-writable directories. * * We dot-lock the file before opening, so we must avoid doing silly things * like dot-locking /dev/null. Fortunately, deliveries to non-mailbox * files execute with recipient privileges, so we don't have to worry * about creating dotlock files in places where the recipient would not * be able to write. * * Note: we use stat() to follow symlinks, because safe_open() allows the * target to be a root-owned symlink, and we don't want to create dotlock * files for /dev/null or other non-file objects. */ if ((lock_style & MBOX_DOT_LOCK) && (stat(path, st) < 0 || S_ISREG(st->st_mode))) { if (dot_lockfile(path, why->reason) == 0) { locked |= MBOX_DOT_LOCK; } else if (errno == EEXIST) { dsb_status(why, mbox_dsn(EAGAIN, def_dsn)); return (0); } else if (lock_style & MBOX_DOT_LOCK_MAY_FAIL) { msg_warn("%s", vstring_str(why->reason)); } else { dsb_status(why, mbox_dsn(errno, def_dsn)); return (0); } } /* * Open or create the target file. In case of a privileged open, the * privileged user may be attacked with hard/soft link tricks in an * unsafe parent directory. In case of an unprivileged open, the mail * system may be attacked by a malicious user-specified path, or the * unprivileged user may be attacked with hard/soft link tricks in an * unsafe parent directory. Open non-blocking to fend off attacks * involving non-file targets. */ if ((fp = safe_open(path, flags | O_NONBLOCK, mode, st, chown_uid, chown_gid, why->reason)) == 0) { dsb_status(why, mbox_dsn(errno, def_dsn)); if (locked & MBOX_DOT_LOCK) dot_unlockfile(path); return (0); } close_on_exec(vstream_fileno(fp), CLOSE_ON_EXEC); /* * If this is a regular file, acquire kernel locks. flock() locks are not * intended to work across a network; fcntl() locks are supposed to work * over NFS, but in the real world, NFS lock daemons often have serious * problems. */ #define HUNKY_DORY(lock_mask, myflock_style) ((lock_style & (lock_mask)) == 0 \ || deliver_flock(vstream_fileno(fp), (myflock_style), why->reason) == 0) if (S_ISREG(st->st_mode)) { if (HUNKY_DORY(MBOX_FLOCK_LOCK, MYFLOCK_STYLE_FLOCK) && HUNKY_DORY(MBOX_FCNTL_LOCK, MYFLOCK_STYLE_FCNTL)) { locked |= lock_style; } else { dsb_status(why, mbox_dsn(errno, def_dsn)); if (locked & MBOX_DOT_LOCK) dot_unlockfile(path); vstream_fclose(fp); return (0); } } /* * Sanity check: reportedly, GNU POP3D creates a new mailbox file and * deletes the old one. This does not play well with software that opens * the mailbox first and then locks it, such as software that that uses * FCNTL or FLOCK locks on open file descriptors (some UNIX systems don't * use dotlock files). * * To detect that GNU POP3D deletes the mailbox file we look at the target * file hard-link count. Note that safe_open() guarantees a hard-link * count of 1, so any change in this count is a sign of trouble. */ if (S_ISREG(st->st_mode) && (fstat(vstream_fileno(fp), st) < 0 || st->st_nlink != 1)) { vstring_sprintf(why->reason, "target file status changed unexpectedly"); dsb_status(why, mbox_dsn(EAGAIN, def_dsn)); msg_warn("%s: file status changed unexpectedly", path); if (locked & MBOX_DOT_LOCK) dot_unlockfile(path); vstream_fclose(fp); return (0); } mp = (MBOX *) mymalloc(sizeof(*mp)); mp->path = mystrdup(path); mp->fp = fp; mp->locked = locked; return (mp); }