/** Opens a file for read and write @param path Path of the file to open @param fi Informations about the opening action, and filled with addictional data to handle while read(), write() and close() @return 0 if successfull, a negative value otherwise */ static int ifs_open (const char *path, struct fuse_file_info *fi) { int res; ItemHandler *target; OpenedItem *item; set_permissions (); target = verify_exposed_path (path); if (target == NULL) return -ENOENT; res = item_handler_open (target, fi->flags); if (res < 0) return res; item = allocate_opened_item (target, res); if (item == NULL) { /* In absence of a more specific error, here return the one which is fault by design :-P cfr. man 2 open */ return -ENODEV; } OPENED_ITEM_TO_FI (item, fi); return 0; }
/** Truncate a file to a specified length @param path Path of the file to truncate @param size New size for the file @return truncate() */ static int ifs_truncate (const char *path, off_t size) { ItemHandler *target; set_permissions (); target = verify_exposed_path (path); return item_handler_truncate (target, size); }
/** Change ownership for a file. This is possible only in temporary paths @param path Path of the file for which modify owner @param uid UID of the new user owner @param gid GID of the new group owner @return chown(), or -EACCES if the directory is required outside permitted hierarchy */ static int ifs_chown (const char *path, uid_t uid, gid_t gid) { ItemHandler *target; set_permissions (); target = verify_exposed_path (path); return item_handler_chown (target, uid, gid); }
/** Change permissions for a file. This is possible only in temporary paths @param path Path of the file for which modify permissions @param mode New privileges mask to apply @return chmod(), or -EACCES if the directory is required outside permitted hierarchy */ static int ifs_chmod (const char *path, mode_t mode) { ItemHandler *target; set_permissions (); target = verify_exposed_path (path); return item_handler_chmod (target, mode); }
/** Check permissions for a file @param path Path of the file for which check permissions @param mask Permissions mask to check @return access() */ static int ifs_access (const char *path, int mask) { ItemHandler *target; set_permissions (); target = verify_exposed_path (path); return item_handler_access (target, mask); }
/** Read the contents of a symbolic link @param path Path of the link to read @param buf String filled with the path pointed by the link @param size Size of the allocation of "buf" @return 0 if successfull, or -EACCES if the directory is required outside permitted hierarchy */ static int ifs_readlink (const char *path, char *buf, size_t size) { ItemHandler *target; set_permissions (); target = verify_exposed_path (path); return item_handler_readlink (target, buf, size); }
/** Retrieve informations about a file @param path Path of the file to check out @param stbuf Struct to fill with informations @return 0 if successful, otherwise a negative value describing the error */ static int ifs_getattr (const char *path, struct stat *stbuf) { ItemHandler *target; set_permissions (); target = verify_exposed_path (path); return item_handler_stat (target, stbuf); }
/** Creates a new hard link. This is possible only in temporary paths @param from Path of the linked file @param to Path of the link @return link(), or -EACCES if the directory is required outside permitted hierarchy */ static int ifs_link (const char *from, const char *to) { set_permissions (); /** TODO To be implemented */ return -EACCES; }
/** Retrieve contents for a folder @param path Path in the filesystem to iterate @param buf Buffer to fill, is used as parameter of "filler" @param filler Callback to call for each found file @param offset Starting offset for reading @param fi Unused @return 0 if successfull, or a negative value */ static int ifs_readdir (const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { int ret; const gchar *name; gchar *file_path; struct stat st; struct stat *ptr_st; GList *items; GList *iter; ItemHandler *target; ItemHandler *child; NodesCache *cache; set_permissions (); target = verify_exposed_path (path); if (target == NULL) { ret = -ENOENT; } else { if (item_handler_is_folder (target) == FALSE) { ret = -ENOTDIR; } else { cache = get_cache_reference (); items = item_handler_get_children (target); for (iter = items; iter; iter = g_list_next (iter)) { child = (ItemHandler*) iter->data; if (item_handler_get_hidden (child) == TRUE) continue; name = item_handler_exposed_name (child); if (name == NULL) continue; if (item_handler_stat (child, &st) == 0) ptr_st = &st; else ptr_st = NULL; file_path = g_build_filename (path, name, NULL); nodes_cache_set_by_path (cache, child, file_path); if (filler (buf, name, ptr_st, 0)) break; } g_list_free (items); ret = 0; } } return ret; }
/** Renames a file. This is possible only in temporary paths @param from Original path of the file @param to Destination path @return rename(), or -EACCES if the directory is required outside permitted hierarchy */ static int ifs_rename (const char *from, const char *to) { int res; CONTENT_TYPE start_type; ItemHandler *start; ItemHandler *target; HierarchyNode *start_level; HierarchyNode *target_level; set_permissions (); /* Into the effective hierarchy, an existing item can only be moved as another valid item. The procedure of metadata guessing in function of the hierarchy path is re-executed */ start = verify_exposed_path (from); if (start == NULL) return -ENOENT; /* If both paths, origin and destination, refer to something into a mirror folder, so are just maps to the real filesystem, a normal rename() is called so to avoid many (useless) handling */ start_type = item_handler_get_format (start); if ((start_type == ITEM_IS_MIRROR_ITEM || start_type == ITEM_IS_MIRROR_FOLDER)) { start_level = item_handler_get_logic_node (start); /** TODO Actually this works only for "system_folders" nodes, provide to correct so to be correct also for the "mirror_contents" case */ if (strcmp (hierarchy_node_get_mirror_path (start_level), "/") == 0) { target_level = node_at_path (to); if (target_level == item_handler_get_logic_node (start)) { res = rename (from, to); if (res != 0) return -errno; } } } target = verify_exposed_path (to); if (target == NULL) { res = create_item_by_path (to, item_handler_is_folder (start) ? NODE_IS_FOLDER : NODE_IS_FILE, &target); if (res != 0) return res; } replace_hierarchy_node (start, target); nodes_cache_remove_by_path (get_cache_reference (), from); return 0; }
/** Requires a permanent write of buffered data on the disk @param path Path of the file to sync @param isdatasync Unused @param fi Informations about the opened file */ static int ifs_fsync (const char *path, int isdatasync, struct fuse_file_info *fi) { OpenedItem *item; FI_TO_OPENED_ITEM (fi, item); if (item == NULL) return -EBADF; set_permissions (); return fsync (item->fd); }
int qset_acl (char const *name, int desc, mode_t mode) { struct permission_context ctx; int ret; memset (&ctx, 0, sizeof ctx); ctx.mode = mode; ret = set_permissions (&ctx, name, desc); free_permission_context (&ctx); return ret; }
/** * * This function receives a complete path (directory + filename) and creates * the directory portion if it does not exist. The point is that the caller * wants to ensure that the given filename can be opened after this function * returns. (at least it won't fail because of missing directories). **/ gboolean create_containing_directory(gchar *name, gint dir_uid, gint dir_gid, gint dir_mode) { gchar *dirname; struct stat st; gint rc; gchar *p; cap_t saved_caps; /* check that the directory exists */ dirname = g_path_get_dirname(name); rc = stat(dirname, &st); g_free(dirname); if (rc == 0) { /* directory already exists */ return TRUE; } else if (rc < 0 && errno != ENOENT) { /* some real error occurred */ return FALSE; } /* directory does not exist */ p = name + 1; p = strchr(p, '/'); while (p) { *p = 0; if (stat(name, &st) == 0) { if (!S_ISDIR(st.st_mode)) return FALSE; } else if (errno == ENOENT) { if (mkdir(name, dir_mode < 0 ? 0700 : (mode_t) dir_mode) == -1) return FALSE; saved_caps = g_process_cap_save(); g_process_cap_modify(CAP_CHOWN, TRUE); g_process_cap_modify(CAP_FOWNER, TRUE); set_permissions(name, dir_uid, dir_gid, dir_mode); g_process_cap_restore(saved_caps); } *p = '/'; p = strchr(p + 1, '/'); } return TRUE; }
int qcopy_acl (const char *src_name, int source_desc, const char *dst_name, int dest_desc, mode_t mode) { struct permission_context ctx; int ret; ret = get_permissions (src_name, source_desc, mode, &ctx); if (ret != 0) return -2; ret = set_permissions (&ctx, dst_name, dest_desc); free_permission_context (&ctx); return ret; }
/** Change file last access and modification times @param path Path of the file for which modify times @param ts New times @return utimes() */ static int ifs_utimens (const char *path, const struct timespec ts[2]) { struct timeval tv [2]; ItemHandler *target; tv [0].tv_sec = ts [0].tv_sec; tv [0].tv_usec = ts [0].tv_nsec / 1000; tv [1].tv_sec = ts [1].tv_sec; tv [1].tv_usec = ts [1].tv_nsec / 1000; set_permissions (); target = verify_exposed_path (path); return item_handler_utimes (target, tv); }
/** To flush an opened file @param path Path of the file to flush @param fi Informations about the opened file @return 0 if successful, otherwise a negative value describing the error */ static int ifs_flush (const char *path, struct fuse_file_info *fi) { OpenedItem *item; FI_TO_OPENED_ITEM (fi, item); if (item == NULL) return -EBADF; set_permissions (); if (fsync (item->fd) != 0) return errno * -1; return 0; }
/** Retrieve informations about the filesystem @param path Path of a reference file @param stbuf Structure to be filled with informations about the filesystem */ static int ifs_statfs (const char *path, struct statvfs *stbuf) { int res; set_permissions (); /** TODO Customize data into stbuf */ res = statvfs(path, stbuf); if (res == -1) return -errno; return 0; }
AditionalFileDialog::AditionalFileDialog(QWidget *parent, QString a_file, QMap<PisiSPBase::AFileAttr, QString> attr) : QDialog(parent), ui(new Ui::AditionalFileDialog), a_file(a_file), attr(attr) { ui->setupUi(this); ui->le_file->setText(a_file); ui->le_target->setText(attr.value(PisiSPBase::TARGET)); set_permissions(attr.value(PisiSPBase::PERMISSION)); ui->le_owner->setText(attr.value(PisiSPBase::OWNER)); ui->le_group->setText(attr.value(PisiSPBase::GROUP)); connect(this, SIGNAL(accepted()), SLOT(dialog_accepted())); }
/** Read bytes from an opened file @param path Path of the file from which read data @param buf Buffer to fill with read bytes @param size Size of the allocation of "buf" @param offset Starting position for the read @param fi Contains informations about the opened file, such as assigned in ifs_open() or ifs_create() @return pread() */ static int ifs_read (const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { int res; OpenedItem *item; set_permissions (); FI_TO_OPENED_ITEM (fi, item); if (item == NULL) return -EBADF; res = pread (item->fd, buf, size, offset); if (res == -1) res = -errno; return res; }
/** Removes a folder and all its contents. This is possible only in temporary paths @param path Path of the directory to remove @return rmdir(), or -EACCES if the directory is required outside permitted hierarchy */ static int ifs_rmdir (const char *path) { int ret; ItemHandler *target; set_permissions (); target = verify_exposed_path (path); if (target != NULL && item_handler_is_folder (target)) { item_handler_remove (target); nodes_cache_remove_by_path (get_cache_reference (), path); ret = 0; } else { ret = -ENOTDIR; } return ret; }
/** Truncate a file to a specified length @param path Path of the file to truncate @param size New size for the file @param fi Informations about the file to truncate @return ftruncate() */ static int ifs_ftruncate (const char *path, off_t size, struct fuse_file_info *fi) { int ret; OpenedItem *item; FI_TO_OPENED_ITEM (fi, item); if (item == NULL) { /* Just as a safety belt: if something happened to the file descriptor, lets truncate the file using the path */ ret = ifs_truncate (path, size); } else { set_permissions (); ret = ftruncate (item->fd, size); } return ret; }
/** Write bytes to an opened file @param path Path of the file in which write data @param buf Buffer with bytes to write @param size Size of the allocation of "buf" @param offset Starting position for the write @param fi Contains informations about the opened file, such as assigned in ifs_open() or ifs_create() @return pwrite() */ static int ifs_write (const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { int res; OpenedItem *item; set_permissions (); FI_TO_OPENED_ITEM (fi, item); if (item == NULL) return -EBADF; /* Remember about splice(2) for future COW implementation */ res = pwrite (item->fd, buf, size, offset); if (res == -1) res = -errno; return res; }
/** Creates a new file @param path Path for the new file @param mask Permissions mask for the new element @param fi Informations about the creating action, and filled with addictional data to handle while read(), write() and close() @return 0 if successfull, a negative value otherwise */ static int ifs_create (const char *path, mode_t mask, struct fuse_file_info *fi) { int res; ItemHandler *target; OpenedItem *item; set_permissions (); res = create_item_by_path (path, NODE_IS_FILE, &target); if (res != 0) return res; res = item_handler_open (target, fi->flags & ~O_CREAT); if (res < 0) return res; item = allocate_opened_item (target, res); if (item == NULL) return -ENODEV; OPENED_ITEM_TO_FI (item, fi); return 0; }
/** Closes a file opened with ifs_open() or ifs_create() @param path Path of the file to close @param fi Informations about the opened file @return close() */ static int ifs_release (const char *path, struct fuse_file_info *fi) { int res; OpenedItem *item; FI_TO_OPENED_ITEM (fi, item); if (item == NULL) return -EBADF; set_permissions (); if (item->item != NULL) { item_handler_close (item->item, item->fd); res = 0; } else { res = close (item->fd); } free_opened_item (item); fi->fh = 0; return res; }
/** Removes a file. This is possible only in temporary paths @param path Path of the file to remove @return unlink(), or -EACCES if the directory is required outside permitted hierarchy */ static int ifs_unlink (const char *path) { int ret; ItemHandler *target; set_permissions (); target = verify_exposed_path (path); if (target != NULL) { if (item_handler_is_folder (target) == FALSE) { item_handler_remove (target); nodes_cache_remove_by_path (get_cache_reference (), path); ret = 0; } else { ret = -EISDIR; } } else { ret = -ENOENT; } return ret; }
static int process_line() { const char *config_segment = NULL; char *shell = NULL; if (current_argument(&config_segment) < 0 || !config_segment) return 0; if (strcmp(config_segment, "execute") == 0) { if (next_argument(&config_segment) < 0) return -1; shell = strdup(config_segment); int outcome = add_execute_respawn(shell); free(shell); if (outcome < 0) return -1; } else if (strcmp(config_segment, "execute_critical") == 0) { if (next_argument(&config_segment) < 0) return -1; shell = strdup(config_segment); int outcome = add_execute_critical_respawn(shell); free(shell); if (outcome < 0) return -1; } else if (strcmp(config_segment, "system") == 0) { if (next_argument(&config_segment) < 0) return -1; shell = strdup(config_segment); int outcome = add_system_respawn(shell); free(shell); if (outcome < 0) return -1; } else if (strcmp(config_segment, "system_critical") == 0) { if (next_argument(&config_segment) < 0) return -1; shell = strdup(config_segment); int outcome = add_system_critical_respawn(shell); free(shell); if (outcome < 0) return -1; } else if (strcmp(config_segment, "limit") == 0) { if (remaining_line(&config_segment) < 0 || !config_segment) return -1; int new_limit = 0; if (!parse_integer10(config_segment, &new_limit) || new_limit < 0) return -1; set_limit(new_limit); } else if (strcmp(config_segment, "security") == 0) { if (remaining_line(&config_segment) < 0 || !config_segment) return -1; permission_mask new_permissions = 0; if (!parse_permissions(config_segment, &new_permissions)) return -1; set_permissions(new_permissions); } else if (strcmp(config_segment, "priority") == 0) { if (remaining_line(&config_segment) < 0 || !config_segment) return -1; int new_priority = 0; if ( !parse_integer10(config_segment, &new_priority) || new_priority < 0 || new_priority > 255 ) return -1; set_priority(new_priority); } else if (strcmp(config_segment, "log_mode") == 0) { if (remaining_line(&config_segment) < 0 || !config_segment) return -1; logging_mode mode_mask = logging_none; if (!parse_logging_mode(config_segment, &mode_mask)) return -1; set_logging_mode(mode_mask); } else return -1; return 0; }
/** Creates a new folder. This is possible only in temporary paths @param path Path of the new folder @param mode Permissions for the newly created folder @return mkdir(), or -EACCES if the directory is required outside permitted hierarchy */ static int ifs_mkdir (const char *path, mode_t mode) { set_permissions (); return create_item_by_path (path, NODE_IS_FOLDER, NULL); }
static int copy_single_file(const char *from, const char *to) { FILE *from_stream, *to_stream; struct stat st; char buf[4096]; size_t len; if (check_the_same(from, to)) { fprintf(stderr, "%s: '%s' is the same as '%s'\n", __func__, from, to); return RecursiveOp_Callback_Error; } if (lstat(from, &st)) { perror("lstat"); return RecursiveOp_Callback_Error; } if (S_ISLNK(st.st_mode)) { /* symbolic links should be copied in special way */ char *link_buffer; int need_free; ssize_t link_len; /* get large enough buffer to read link content */ if (st.st_size < sizeof(buf)) { link_buffer = buf; need_free = 0; } else { link_buffer = MEM_callocN(st.st_size + 2, "copy_single_file link_buffer"); need_free = 1; } link_len = readlink(from, link_buffer, st.st_size + 1); if (link_len < 0) { perror("readlink"); if (need_free) MEM_freeN(link_buffer); return RecursiveOp_Callback_Error; } link_buffer[link_len] = 0; if (symlink(link_buffer, to)) { perror("symlink"); if (need_free) MEM_freeN(link_buffer); return RecursiveOp_Callback_Error; } if (need_free) MEM_freeN(link_buffer); return RecursiveOp_Callback_OK; } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) { /* copy special type of file */ if (mknod(to, st.st_mode, st.st_rdev)) { perror("mknod"); return RecursiveOp_Callback_Error; } if (set_permissions(to, &st)) return RecursiveOp_Callback_Error; return RecursiveOp_Callback_OK; } else if (!S_ISREG(st.st_mode)) { fprintf(stderr, "Copying of this kind of files isn't supported yet\n"); return RecursiveOp_Callback_Error; } from_stream = fopen(from, "rb"); if (!from_stream) { perror("fopen"); return RecursiveOp_Callback_Error; } to_stream = fopen(to, "wb"); if (!to_stream) { perror("fopen"); fclose(from_stream); return RecursiveOp_Callback_Error; } while ((len = fread(buf, 1, sizeof(buf), from_stream)) > 0) { fwrite(buf, 1, len, to_stream); } fclose(to_stream); fclose(from_stream); if (set_permissions(to, &st)) return RecursiveOp_Callback_Error; return RecursiveOp_Callback_OK; }
int receive_message (int sender_id, struct Message *m) { printf("%d: received message from %d of type %c\n", thisid, sender_id, m->msg_type); int result; switch (m->msg_type) { case (REQUEST_PAGE): { pthread_mutex_lock(&locks[m->page_number]); void *pageaddr = (void *) get_pageaddr(m->page_number); result = set_permissions(pageaddr, PGSIZE, m->permissions); if (result < 0) break; struct Message response = create_message(SEND_PAGE, READING, m->page_number); if (m->index == NO_RESPONSE) { result = E_INCONSISTENT_STATE; break; } response.index = m->index; response.is_response = 1; memcpy((void *) response.page, pageaddr, PGSIZE); result = send_to(sender_id, &response); pthread_mutex_lock(&locks[m->page_number]); break; } case (SEND_PAGE): { pthread_mutex_lock(&locks[m->page_number]); void *pageaddr = (void *) get_pageaddr(m->page_number); result = set_permissions(pageaddr, PGSIZE, PROT_READ_WRITE); if (result < 0) break; memcpy(pageaddr, (void *) m->page, PGSIZE); result = set_permissions(pageaddr, PGSIZE, m->permissions); if (m->is_response) { pthread_cond_signal(&waits[m->index]); } pthread_mutex_unlock(&locks[m->page_number]); break; } case (SET_PERMISSION): { pthread_mutex_lock(&locks[m->page_number]); void *pageaddr = (void *) get_pageaddr(m->page_number); result = set_permissions(pageaddr, PGSIZE, m->permissions); if (m->is_response) { pthread_cond_signal(&waits[m->index]); } pthread_mutex_unlock(&locks[m->page_number]); break; } case (REQUEST_PERMISSION): { if (m->permissions = READING) { give_read_copy(sender_id, m->index, m->page_number); } else if (m->permissions = MODIFYING) { give_write_copy(sender_id, m->index, m->page_number); } else { result = -E_UNHANDLED_PAGE_STATUS; break; } break; } default: { printf("Unhandled message type in give_read_copy: %c\n", m->msg_type); exit(-E_UNHANDLED_MESSAGE_TYPE); } } return result; }
static int give_write_copy (int requester_id, int request_id, int page_number) { int result; printf("%d: pre-lock\n", thisid); pthread_mutex_lock(&locks[page_number]); printf("%d: post-lock\n", thisid); int owner_id = get_owner(page_number); if (owner_id != thisid) { printf("%d: wtf\n", thisid); pthread_mutex_unlock(&locks[page_number]); return -E_INCORRECT_OWNER; } struct PageStatus *page_status = get_page_status(page_number); printf("%d: page_status->status: %d\n", thisid, page_status->status); switch (page_status->status) { case READABLE: { int i; int had_read_only; for (i = 0; i < nowners; i++) { if (i == requester_id) { had_read_only = (page_status->status_by_owner[i] == READING); continue; } if (i == thisid) { if (page_status->status_by_owner[i] == READING) { // Set local page to PROT_NONE at end of function continue; } else { // This shouldn't happen - the local copy should become // readable as soon the page status becomes READABLE result = -E_INCONSISTENT_STATE; break; } } if (page_status->status_by_owner[i] == READING) { // Tell i that it is INVALIDATED printf("%d: Telling %d that it is INVALIDATED\n", thisid, i); struct Message m = create_message(SET_PERMISSION, INVALIDATED, page_number); printf("%d: sending message of type %c\n", thisid, m.msg_type); result = send_to(i, &m); if (result < 0) break; page_status->status_by_owner[i] = INVALIDATED; } } page_status->status_by_owner[requester_id] = MODIFYING; page_status->modifying_owner = requester_id; page_status->status = MODIFIED; if (requester_id == thisid) { // Set local page to PROT_READ_WRITE printf("%d: Setting local page %d to PROT_READ_WRITE\n", thisid, page_number); result = set_permissions((void *) get_pageaddr(page_number), PGSIZE, PROT_READ_WRITE); if (result < 0) break; } else { if (had_read_only) { // Tell requester_id that it has PROT_READ_WRITE access struct Message m = create_message(SET_PERMISSION, MODIFYING, page_number); m.is_response = 1; if (request_id == NO_RESPONSE) { result = E_INCONSISTENT_STATE; break; } m.index = request_id; result = send_to(requester_id, &m); if (result < 0) break; } else { // Transfer page over network to requester_id and tell it // it has PROT_READ_WRITE access struct Message m = create_message(SEND_PAGE, MODIFYING, page_number); memcpy((void *) m.page, (void *) get_pageaddr(page_number), PGSIZE); m.is_response = 1; if (request_id == NO_RESPONSE) { result = E_INCONSISTENT_STATE; break; } m.index = request_id; result = send_to(requester_id, &m); if (result < 0) break; } } // Set local page to PROT_NONE result = set_permissions((void *) get_pageaddr(page_number), PGSIZE, PROT_NONE); if (result < 0) break; page_status->status_by_owner[thisid] = INVALIDATED; result = 0; break; } case MODIFIED: { int modifier = page_status->modifying_owner; if (modifier == requester_id) { // The modifier should have read-write permissions, in which // case a page not fault should not be thrown and this // should never be called. result = -E_INCONSISTENT_STATE; break; } if (modifier == thisid) { // Set local page to PROT_NONE at end of method } else { // Tell modifier that it is INVALIDATED // Get modified page from modifier struct Message m = create_message(REQUEST_PAGE, INVALIDATED, page_number); // Upon receiving the message we should map the page locally result = send_and_wait_for_response(modifier, &m); if (result < 0) break; // Local page should already be set PROT_READ } page_status->status_by_owner[modifier] = INVALIDATED; page_status->status_by_owner[requester_id] = MODIFYING; page_status->modifying_owner = requester_id; if (requester_id == thisid) { // Set local page to PROT_READ_WRITE result = set_permissions((void *) get_pageaddr(page_number), PGSIZE, PROT_READ_WRITE); if (result < 0) break; } else { // Transfer page over network to requester_id and tell it it has // PROT_READ_WRITE access struct Message m = create_message(SEND_PAGE, MODIFYING, page_number); memcpy((void *) m.page, (void *) get_pageaddr(page_number), PGSIZE); m.is_response = 1; if (request_id == NO_RESPONSE) { result = E_INCONSISTENT_STATE; break; } m.index = request_id; result = send_to(requester_id, &m); if (result < 0) break; // Set local page to PROT_NONE result = set_permissions((void *) get_pageaddr(page_number), PGSIZE, PROT_NONE); if (result < 0) break; } result = 0; break; } default: { printf("Unhandled page status in give_write_copy.\n"); exit(-E_UNHANDLED_PAGE_STATUS); } } pthread_mutex_unlock(&locks[page_number]); return result; }