// Try to lock a file with name based on host ID, // to prevent 2 schedulers from running at same time for same host. // Return: // 0 if successful // In this case store file descriptor in reply struct so we can unlock later // In other cases store -1 in reply struct // PID (>0) if another process has lock // -1 if error (e.g. can't create file) // int lock_sched() { char filename[256]; char pid_string[16]; int fd, pid, count; g_reply->lockfile_fd=-1; sprintf(filename, "%s/CGI_%07d", config.sched_lockfile_dir, g_reply->host.id ); fd = open(filename, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if (fd < 0) return -1; // if we can't get an advisory write lock on the file, // return the PID of the process that DOES hold the lock. // (or -1 if failure) // pid = mylockf(fd); if (pid) { close(fd); return pid; } // write PID into the CGI_<HOSTID> file and flush to disk // count = sprintf(pid_string, "%d\n", getpid()); ssize_t n = write(fd, pid_string, count); if (n < 0) { close(fd); return -1; } fsync(fd); g_reply->lockfile_fd = fd; return 0; }
// always returns HTML reply // int handle_get_file_size(char* file_name) { struct stat sbuf; char path[MAXPATHLEN], buf[256]; int retval, pid, fd; // TODO: check to ensure path doesn't point somewhere bad // Use 64-bit variant // retval = dir_hier_path( file_name, config.upload_dir, config.uldl_dir_fanout, path ); if (retval) { log_messages.printf(MSG_CRITICAL, "Failed to find/create directory for file '%s' in '%s'.\n", file_name, config.upload_dir ); return return_error(ERR_TRANSIENT, "can't open file"); } // if the volume is full, report a transient error // to prevent the client from starting a transfer // if (volume_full(config.upload_dir)) { return return_error(ERR_TRANSIENT, "Server is out of disk space"); } fd = open(path, O_WRONLY|O_APPEND); if (fd<0 && ENOENT==errno) { // file does not exist: return zero length // log_messages.printf(MSG_NORMAL, "handle_get_file_size(): [%s] returning zero\n", file_name ); return return_success("<file_size>0</file_size>"); } if (fd<0) { // can't get file descriptor: try again later // log_messages.printf(MSG_CRITICAL, "handle_get_file_size(): cannot open [%s] %s\n", file_name, strerror(errno) ); return return_error(ERR_TRANSIENT, "can't open file"); } if ((pid=mylockf(fd))) { // file locked by another file_upload_handler: try again later // close(fd); log_messages.printf(MSG_CRITICAL, "handle_get_file_size(): [%s] returning error\n", file_name ); return return_error(ERR_TRANSIENT, "[%s] locked by file_upload_handler PID=%d", file_name, pid ); } // file exists, writable, not locked by anyone else, so return length. // retval = stat(path, &sbuf); close(fd); if (retval) { // file DOES perhaps exist, but can't stat it: try again later // log_messages.printf(MSG_CRITICAL, "handle_get_file_size(): [%s] returning error %s\n", file_name, strerror(errno) ); return return_error(ERR_TRANSIENT, "cannot stat file" ); } log_messages.printf(MSG_NORMAL, "handle_get_file_size(): [%s] returning %d\n", file_name, (int)sbuf.st_size ); sprintf(buf, "<file_size>%d</file_size>", (int)sbuf.st_size); return return_success(buf); }
// read from socket, write to file // ALWAYS returns an HTML reply // int copy_socket_to_file(FILE* in, char* path, double offset, double nbytes) { unsigned char buf[BLOCK_SIZE]; struct stat sbuf; int pid; // open file. Use raw IO not buffered IO so that we can use reliable // posix file locking. // Advisory file locking is not guaranteed reliable when // used with stream buffered IO. // int fd = open(path, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH ); if (fd<0) { return return_error(ERR_TRANSIENT, "can't open file %s: %s\n", path, strerror(errno) ); } // Put an advisory lock on the file. // This will prevent OTHER instances of file_upload_handler // from being able to write to the file. // pid = mylockf(fd); if (pid>0) { close(fd); return return_error(ERR_TRANSIENT, "can't lock file %s: %s locked by PID=%d\n", path, strerror(errno), pid ); } else if (pid < 0) { close(fd); return return_error(ERR_TRANSIENT, "can't lock file %s\n", path); } // check that file length corresponds to offset // TODO: use a 64-bit variant // if (stat(path, &sbuf)) { close(fd); return return_error(ERR_TRANSIENT, "can't stat file %s: %s\n", path, strerror(errno) ); } if (sbuf.st_size < offset) { close(fd); return return_error(ERR_TRANSIENT, "length of file %s %d bytes < offset %.0f bytes", path, (int)sbuf.st_size, offset ); } if (offset) lseek(fd, offset, SEEK_SET); if (sbuf.st_size > offset) { log_messages.printf(MSG_CRITICAL, "file %s length on disk %d bytes; host upload starting at %.0f bytes.\n", this_filename, (int)sbuf.st_size, offset ); } // caller guarantees that nbytes > offset // bytes_left = nbytes - offset; while (bytes_left > 0) { int n, m, to_write; m = bytes_left<(double)BLOCK_SIZE ? (int)bytes_left : BLOCK_SIZE; // try to get m bytes from socket (n>=0 is number actually returned) // n = fread(buf, 1, m, in); // try to write n bytes to file // to_write=n; while (to_write > 0) { ssize_t ret = write(fd, buf+n-to_write, to_write); if (ret < 0) { close(fd); const char* errmsg; if (errno == ENOSPC) { errmsg = "No space left on server"; } else { errmsg = strerror(errno); } return return_error(ERR_TRANSIENT, "can't write file %s: %s\n", path, errmsg ); } to_write -= ret; } // check that we got all bytes from socket that were requested // Note: fread() reads less than requested only if there's // an error or EOF (see the man page) // if (n != m) { close(fd); if (feof(in)) { return return_error(ERR_TRANSIENT, "EOF on socket read : asked for %d, got %d\n", m, n ); } else if (ferror(in)) { return return_error(ERR_TRANSIENT, "error %d (%s) on socket read: asked for %d, got %d\n", ferror(in), strerror(ferror(in)), m, n ); } else { return return_error(ERR_TRANSIENT, "incomplete socket read: asked for %d, got %d\n", m, n ); } } bytes_left -= n; } close(fd); return return_success(0); }