static int mkdir_chown_full(const char *path, mode_t mode, uid_t uid, gid_t gid, const char *gid_origin) { string_t *str; mode_t old_mask; int ret, orig_errno; old_mask = umask(0); ret = mkdir(path, mode); umask(old_mask); if (ret < 0) { if (errno == EISDIR || errno == ENOSYS) { /* EISDIR check is for BSD/OS which returns it if path contains '/' at the end and it exists. ENOSYS check is for NFS mount points. */ errno = EEXIST; } return -1; } if (chown(path, uid, gid) < 0) { orig_errno = errno; if (rmdir(path) < 0) i_error("rmdir(%s) failed: %m", path); errno = orig_errno; if (errno == EPERM && uid == (uid_t)-1) { i_error("%s", eperm_error_get_chgrp("chown", path, gid, gid_origin)); return -1; } str = t_str_new(256); str_printfa(str, "chown(%s, %ld", path, uid == (uid_t)-1 ? -1L : (long)uid); if (uid != (uid_t)-1) { struct passwd pw; if (i_getpwuid(uid, &pw) > 0) str_printfa(str, "(%s)", pw.pw_name); } str_printfa(str, ", %ld", gid == (gid_t)-1 ? -1L : (long)gid); if (gid != (gid_t)-1) { struct group gr; if (i_getgrgid(uid, &gr) > 0) str_printfa(str, "(%s)", gr.gr_name); } errno = orig_errno; i_error("%s) failed: %m", str_c(str)); return -1; } return 0; }
static int maildir_create_tmp(struct maildir_mailbox *mbox, const char *dir, const char **fname_r) { struct mailbox *box = &mbox->box; const struct mailbox_permissions *perm = mailbox_get_permissions(box); unsigned int prefix_len; const char *tmp_fname; string_t *path; mode_t old_mask; int fd; path = t_str_new(256); str_append(path, dir); str_append_c(path, '/'); prefix_len = str_len(path); do { tmp_fname = maildir_filename_generate(); str_truncate(path, prefix_len); str_append(path, tmp_fname); /* the generated filename is unique. the only reason why it might return an existing filename is if the time moved backwards. so we'll use O_EXCL anyway, although it's mostly useless. */ old_mask = umask(0777 & ~perm->file_create_mode); fd = open(str_c(path), O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0777); umask(old_mask); } while (fd == -1 && errno == EEXIST); *fname_r = tmp_fname; if (fd == -1) { if (ENOQUOTA(errno)) { mail_storage_set_error(box->storage, MAIL_ERROR_NOQUOTA, MAIL_ERRSTR_NO_QUOTA); } else { mail_storage_set_critical(box->storage, "open(%s) failed: %m", str_c(path)); } } else if (perm->file_create_gid != (gid_t)-1) { if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) { if (errno == EPERM) { mail_storage_set_critical(box->storage, "%s", eperm_error_get_chgrp("fchown", str_c(path), perm->file_create_gid, perm->file_create_gid_origin)); } else { mail_storage_set_critical(box->storage, "fchown(%s) failed: %m", str_c(path)); } } } return fd; }
safe_mkstemp_full(string_t *prefix, mode_t mode, uid_t uid, gid_t gid, const char *gid_origin) { size_t prefix_len; struct stat st; unsigned char randbuf[8]; mode_t old_umask; int fd; prefix_len = str_len(prefix); for (;;) { do { random_fill_weak(randbuf, sizeof(randbuf)); str_truncate(prefix, prefix_len); str_append(prefix, binary_to_hex(randbuf, sizeof(randbuf))); } while (lstat(str_c(prefix), &st) == 0); if (errno != ENOENT) { i_error("stat(%s) failed: %m", str_c(prefix)); str_truncate(prefix, prefix_len); return -1; } old_umask = umask(0666 ^ mode); fd = open(str_c(prefix), O_RDWR | O_EXCL | O_CREAT, 0666); umask(old_umask); if (fd != -1) break; if (errno != EEXIST) { if (errno != ENOENT && errno != EACCES) i_error("open(%s) failed: %m", str_c(prefix)); str_truncate(prefix, prefix_len); return -1; } } if (uid == (uid_t)-1 && gid == (gid_t)-1) return fd; if (fchown(fd, uid, gid) < 0) { if (errno == EPERM) { i_error("%s", eperm_error_get_chgrp("fchown", str_c(prefix), gid, gid_origin)); } else { i_error("fchown(%s, %ld, %ld) failed: %m", str_c(prefix), uid == (uid_t)-1 ? -1L : (long)uid, gid == (gid_t)-1 ? -1L : (long)gid); } i_close_fd(&fd); (void)unlink(str_c(prefix)); return -1; } return fd; }
int mdbox_file_create_fd(struct dbox_file *file, const char *path, bool parents) { struct mdbox_file *mfile = (struct mdbox_file *)file; struct mdbox_map *map = mfile->storage->map; struct mailbox_permissions perm; mode_t old_mask; const char *p, *dir; int fd; mailbox_list_get_root_permissions(map->root_list, &perm); old_mask = umask(0666 & ~perm.file_create_mode); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); umask(old_mask); if (fd == -1 && errno == ENOENT && parents && (p = strrchr(path, '/')) != NULL) { dir = t_strdup_until(path, p); if (mailbox_list_mkdir_root(map->root_list, dir, path != file->alt_path ? MAILBOX_LIST_PATH_TYPE_DIR : MAILBOX_LIST_PATH_TYPE_ALT_DIR) < 0) { mail_storage_copy_list_error(&file->storage->storage, map->root_list); return -1; } /* try again */ old_mask = umask(0666 & ~perm.file_create_mode); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); umask(old_mask); } if (fd == -1) { mail_storage_set_critical(&file->storage->storage, "open(%s, O_CREAT) failed: %m", path); } else if (perm.file_create_gid == (gid_t)-1) { /* no group change */ } else if (fchown(fd, (uid_t)-1, perm.file_create_gid) < 0) { if (errno == EPERM) { mail_storage_set_critical(&file->storage->storage, "%s", eperm_error_get_chgrp("fchown", path, perm.file_create_gid, perm.file_create_gid_origin)); } else { mail_storage_set_critical(&file->storage->storage, "fchown(%s, -1, %ld) failed: %m", path, (long)perm.file_create_gid); } /* continue anyway */ } return fd; }
static void mailbox_uidvalidity_write(struct mailbox_list *list, const char *path, uint32_t uid_validity) { char buf[8+1]; int fd; struct mailbox_permissions perm; mode_t old_mask; mailbox_list_get_root_permissions(list, &perm); old_mask = umask(0666 & ~perm.file_create_mode); fd = open(path, O_RDWR | O_CREAT, 0666); umask(old_mask); if (fd == -1) { i_error("open(%s) failed: %m", path); return; } if (perm.file_create_gid != (gid_t)-1 && fchown(fd, (uid_t)-1, perm.file_create_gid) < 0) { if (errno == EPERM) { i_error("%s", eperm_error_get_chgrp("fchown", path, perm.file_create_gid, perm.file_create_gid_origin)); } else { i_error("fchown(%s, -1, %ld) failed: %m", path, (long)perm.file_create_gid); } } if (i_snprintf(buf, sizeof(buf), "%08x", uid_validity) < 0) i_unreached(); if (pwrite_full(fd, buf, strlen(buf), 0) < 0) i_error("write(%s) failed: %m", path); if (close(fd) < 0) i_error("close(%s) failed: %m", path); }
static int maildir_keywords_write_fd(struct maildir_keywords *mk, const char *path, int fd) { struct maildir_mailbox *mbox = mk->mbox; struct mailbox *box = &mbox->box; const struct mailbox_permissions *perm = mailbox_get_permissions(box); const char *const *keywords; unsigned int i, count; string_t *str; struct stat st; str = t_str_new(256); keywords = array_get(&mk->list, &count); for (i = 0; i < count; i++) { if (keywords[i] != NULL) str_printfa(str, "%u %s\n", i, keywords[i]); } if (write_full(fd, str_data(str), str_len(str)) < 0) { mail_storage_set_critical(mk->storage, "write_full(%s) failed: %m", path); return -1; } if (fstat(fd, &st) < 0) { mail_storage_set_critical(mk->storage, "fstat(%s) failed: %m", path); return -1; } if (st.st_gid != perm->file_create_gid && perm->file_create_gid != (gid_t)-1) { if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) { if (errno == EPERM) { mail_storage_set_critical(mk->storage, "%s", eperm_error_get_chgrp("fchown", path, perm->file_create_gid, perm->file_create_gid_origin)); } else { mail_storage_set_critical(mk->storage, "fchown(%s) failed: %m", path); } } } /* mtime must grow every time */ if (st.st_mtime <= mk->synced_mtime) { struct utimbuf ut; mk->synced_mtime = ioloop_time <= mk->synced_mtime ? mk->synced_mtime + 1 : ioloop_time; ut.actime = ioloop_time; ut.modtime = mk->synced_mtime; if (utime(path, &ut) < 0) { mail_storage_set_critical(mk->storage, "utime(%s) failed: %m", path); return -1; } } if (fsync(fd) < 0) { mail_storage_set_critical(mk->storage, "fsync(%s) failed: %m", path); return -1; } return 0; }