static int cfs_getattr(const char *path, struct stat *stbuf) { stbuf->st_uid = geteuid(); stbuf->st_gid = getegid(); if (!strcmp(path, "/")) { stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 2; return 0; } dir_entry *de = path_info(path); if (!de) return -ENOENT; stbuf->st_ctime = stbuf->st_mtime = de->last_modified; if (de->isdir) { stbuf->st_size = 0; stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 2; } else { stbuf->st_size = de->size; /* calc. blocks as if 4K blocksize filesystem; stat uses units of 512B */ stbuf->st_blocks = ((4095 + de->size) / 4096) * 8; fprintf(stderr, "size %ld blocks %ld\n", de->size, stbuf->st_blocks); stbuf->st_mode = S_IFREG | 0666; stbuf->st_nlink = 1; } return 0; }
static int cfs_fgetattr(const char* path, struct stat* stbuf, struct fuse_file_info* info) { debugf(DBG_LEVEL_NORM, KBLU "cfs_fgetattr(%s)", path); openfile* of = (openfile*)(uintptr_t)info->fh; if (of) { //get file. if not in cache will be downloaded. dir_entry* de = path_info(path); if (!de) { debug_list_cache_content(); debugf(DBG_LEVEL_NORM, KBLU"exit 1: cfs_fgetattr(%s) "KYEL"not-in-cache/cloud", path); return -ENOENT; } int default_mode_file; if (option_enable_chmod) default_mode_file = de->chmod; else default_mode_file = 0666; stbuf->st_size = cloudfs_file_size(of->fd); stbuf->st_mode = S_IFREG | default_mode_file; stbuf->st_nlink = 1; debugf(DBG_LEVEL_NORM, KBLU "exit 0: cfs_fgetattr(%s)", path); return 0; } debugf(DBG_LEVEL_NORM, KRED "exit 1: cfs_fgetattr(%s)", path); return -ENOENT; }
static int cfs_rename(const char* src, const char* dst) { debugf(DBG_LEVEL_NORM, KBLU"cfs_rename(%s, %s)", src, dst); dir_entry* src_de = path_info(src); if (!src_de) { debugf(DBG_LEVEL_NORM, KRED"exit 0: cfs_rename(%s,%s) not-found", src, dst); return -ENOENT; } if (src_de->isdir) { debugf(DBG_LEVEL_NORM, KRED"exit 1: cfs_rename(%s,%s) cannot rename dirs!", src, dst); return -EISDIR; } if (cloudfs_copy_object(src, dst)) { /* FIXME this isn't quite right as doesn't preserve last modified */ //fix done in cloudfs_copy_object() update_dir_cache(dst, src_de->size, 0, 0); int result = cfs_unlink(src); dir_entry* dst_de = path_info(dst); if (!dst_de) debugf(DBG_LEVEL_NORM, KRED"cfs_rename(%s,%s) dest-not-found-in-cache", src, dst); else { debugf(DBG_LEVEL_NORM, KBLU"cfs_rename(%s,%s) upload ok", src, dst); //copy attributes, shortcut, rather than forcing a download from cloud copy_dir_entry(src_de, dst_de); } debugf(DBG_LEVEL_NORM, KBLU"exit 3: cfs_rename(%s,%s)", src, dst); return result; } debugf(DBG_LEVEL_NORM, KRED"exit 4: cfs_rename(%s,%s) io error", src, dst); return -EIO; }
static int cfs_rename(const char *src, const char *dst) { dir_entry *src_de = path_info(src); if (!src_de) return -ENOENT; if (src_de->isdir) return -EISDIR; if (cloudfs_copy_object(src, dst)) { /* FIXME this isn't quite right as doesn't preserve last modified */ update_dir_cache(dst, src_de->size, 0, 0); return cfs_unlink(src); } return -EIO; }
//http://man7.org/linux/man-pages/man2/utimensat.2.html static int cfs_utimens(const char* path, const struct timespec times[2]) { debugf(DBG_LEVEL_NORM, KBLU "cfs_utimens(%s)", path); dir_entry* path_de = path_info(path); if (!path_de) { debugf(DBG_LEVEL_NORM, KRED"exit 0: cfs_utimens(%s) file not in cache", path); return -ENOENT; } struct timespec now; clock_gettime(CLOCK_REALTIME, &now); if (path_de->atime.tv_sec != times[0].tv_sec || path_de->atime.tv_nsec != times[0].tv_nsec || path_de->mtime.tv_sec != times[1].tv_sec || path_de->mtime.tv_nsec != times[1].tv_nsec) { debugf(DBG_LEVEL_EXT, KCYN "cfs_utimens: change for %s, prev: atime=%li.%li mtime=%li.%li, new: atime=%li.%li mtime=%li.%li", path, path_de->atime.tv_sec, path_de->atime.tv_nsec, path_de->mtime.tv_sec, path_de->mtime.tv_nsec, times[0].tv_sec, times[0].tv_nsec, times[1].tv_sec, times[1].tv_nsec); char time_str[TIME_CHARS] = ""; get_timespec_as_str(×[1], time_str, sizeof(time_str)); debugf(DBG_LEVEL_EXT, KCYN"cfs_utimens: set mtime=[%s]", time_str); get_timespec_as_str(×[0], time_str, sizeof(time_str)); debugf(DBG_LEVEL_EXT, KCYN"cfs_utimens: set atime=[%s]", time_str); path_de->atime = times[0]; path_de->mtime = times[1]; // not sure how to best obtain ctime from fuse source file. just record current date. path_de->ctime = now; //calling a meta cloud update here is not always needed. //touch for example opens and closes/flush the file. //worth implementing a meta cache functionality to avoid multiple uploads on meta changes //when changing timestamps on very large files, touch command will trigger 2 x long and useless file uploads on cfs_flush() } else debugf(DBG_LEVEL_EXT, KCYN"cfs_utimens: a/m/time not changed"); debugf(DBG_LEVEL_NORM, KBLU "exit 1: cfs_utimens(%s)", path); return 0; }
static int cfs_open(const char *path, struct fuse_file_info *info) { FILE *temp_file = tmpfile(); dir_entry *de = path_info(path); if (!(info->flags & O_WRONLY)) { if (!cloudfs_object_write_fp(path, temp_file)) { fclose(temp_file); return -ENOENT; } update_dir_cache(path, (de ? de->size : 0), 0); } openfile *of = (openfile *)malloc(sizeof(openfile)); of->fd = dup(fileno(temp_file)); fclose(temp_file); of->flags = info->flags; info->fh = (uintptr_t)of; info->direct_io = 1; return 0; }
const std::string download_istream::do_type() const { dgd_scopef(trace_download); dgd_echo(m_type); if( !m_type.empty() ) return m_type; dgd_echo(m_url); QUrl url; url.setUrl(QString::fromStdString(m_url)); QFileInfo path_info(url.path()); QString suffix = path_info.suffix(); dgd_echo(suffix); if( suffix.compare("wrl", Qt::CaseInsensitive) == 0 || suffix.compare("vrml", Qt::CaseInsensitive) == 0 ) { return std::string(openvrml::vrml_media_type); } if( suffix.compare("x3d", Qt::CaseInsensitive) == 0 || suffix.compare("x3dv", Qt::CaseInsensitive) == 0 ) { return std::string(openvrml::x3d_vrml_media_type); } if( suffix.compare("png", Qt::CaseInsensitive) == 0 ) { return std::string("image/png"); } if( suffix.compare("jpg", Qt::CaseInsensitive) == 0 || suffix.compare("jpeg", Qt::CaseInsensitive) == 0 ) { return std::string("image/jpeg"); } return std::string(); }
void PrettyImage::SaveAs() { QString filename = QFileInfo(url_.path()).fileName(); if (filename.isEmpty()) filename = "artwork.jpg"; QSettings s; s.beginGroup(kSettingsGroup); QString last_save_dir = s.value("last_save_dir", QDir::homePath()).toString(); QString path = last_save_dir.isEmpty() ? QDir::homePath() : last_save_dir; QFileInfo path_info(path); if (path_info.isDir()) { path += "/" + filename; } else { path = path_info.path() + "/" + filename; } filename = QFileDialog::getSaveFileName(this, tr("Save image"), path); if (filename.isEmpty()) return; image_.save(filename); s.setValue("last_save_dir", last_save_dir); }
int server_fcgi(struct httpd *env, struct client *clt) { struct server_fcgi_param param; struct server_config *srv_conf = clt->clt_srv_conf; struct http_descriptor *desc = clt->clt_descreq; struct fcgi_record_header *h; struct fcgi_begin_request_body *begin; char hbuf[HOST_NAME_MAX+1]; size_t scriptlen; int pathlen; int fd = -1, ret; const char *stripped, *p, *alias, *errstr = NULL; char *str, *script = NULL; if (srv_conf->socket[0] == ':') { struct sockaddr_storage ss; in_port_t port; p = srv_conf->socket + 1; port = strtonum(p, 0, 0xffff, &errstr); if (errstr != NULL) { log_warn("%s: strtonum %s, %s", __func__, p, errstr); goto fail; } memset(&ss, 0, sizeof(ss)); ss.ss_family = AF_INET; ((struct sockaddr_in *) &ss)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); port = htons(port); if ((fd = server_socket_connect(&ss, port, srv_conf)) == -1) goto fail; } else { struct sockaddr_un sun; size_t len; if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) goto fail; memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; len = strlcpy(sun.sun_path, srv_conf->socket, sizeof(sun.sun_path)); if (len >= sizeof(sun.sun_path)) { errstr = "socket path to long"; goto fail; } sun.sun_len = len; if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) goto fail; } socket_set_blockmode(fd, BM_NONBLOCK); memset(hbuf, 0, sizeof(hbuf)); clt->clt_fcgi_state = FCGI_READ_HEADER; clt->clt_fcgi_toread = sizeof(struct fcgi_record_header); if (clt->clt_srvevb != NULL) evbuffer_free(clt->clt_srvevb); clt->clt_srvevb = evbuffer_new(); if (clt->clt_srvevb == NULL) { errstr = "failed to allocate evbuffer"; goto fail; } close(clt->clt_fd); clt->clt_fd = fd; if (clt->clt_srvbev != NULL) bufferevent_free(clt->clt_srvbev); clt->clt_srvbev_throttled = 0; clt->clt_srvbev = bufferevent_new(fd, server_fcgi_read, NULL, server_file_error, clt); if (clt->clt_srvbev == NULL) { errstr = "failed to allocate fcgi buffer event"; goto fail; } memset(¶m, 0, sizeof(param)); h = (struct fcgi_record_header *)¶m.buf; h->version = 1; h->type = FCGI_BEGIN_REQUEST; h->id = htons(1); h->content_len = htons(sizeof(struct fcgi_begin_request_body)); h->padding_len = 0; begin = (struct fcgi_begin_request_body *)¶m.buf[sizeof(struct fcgi_record_header)]; begin->role = htons(FCGI_RESPONDER); if (bufferevent_write(clt->clt_srvbev, ¶m.buf, sizeof(struct fcgi_record_header) + sizeof(struct fcgi_begin_request_body)) == -1) { errstr = "failed to write to evbuffer"; goto fail; } h->type = FCGI_PARAMS; h->content_len = param.total_len = 0; alias = desc->http_path_alias != NULL ? desc->http_path_alias : desc->http_path; stripped = server_root_strip(alias, srv_conf->strip); if ((pathlen = asprintf(&script, "%s%s", srv_conf->root, stripped)) == -1) { errstr = "failed to get script name"; goto fail; } scriptlen = path_info(script); /* * no part of root should show up in PATH_INFO. * therefore scriptlen should be >= strlen(root) */ if (scriptlen < strlen(srv_conf->root)) scriptlen = strlen(srv_conf->root); if ((int)scriptlen < pathlen) { if (fcgi_add_param(¶m, "PATH_INFO", script + scriptlen, clt) == -1) { errstr = "failed to encode param"; goto fail; } script[scriptlen] = '\0'; } else { /* RFC 3875 mandates that PATH_INFO is empty if not set */ if (fcgi_add_param(¶m, "PATH_INFO", "", clt) == -1) { errstr = "failed to encode param"; goto fail; } } /* * calculate length of http SCRIPT_NAME: * add length of stripped prefix, * subtract length of prepended local root */ scriptlen += (stripped - alias) - strlen(srv_conf->root); if ((str = strndup(alias, scriptlen)) == NULL) goto fail; ret = fcgi_add_param(¶m, "SCRIPT_NAME", str, clt); free(str); if (ret == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "SCRIPT_FILENAME", script, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (desc->http_query) if (fcgi_add_param(¶m, "QUERY_STRING", desc->http_query, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "DOCUMENT_ROOT", srv_conf->root, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "DOCUMENT_URI", alias, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "GATEWAY_INTERFACE", "CGI/1.1", clt) == -1) { errstr = "failed to encode param"; goto fail; } if (srv_conf->flags & SRVFLAG_AUTH) { if (fcgi_add_param(¶m, "REMOTE_USER", clt->clt_remote_user, clt) == -1) { errstr = "failed to encode param"; goto fail; } } /* Add HTTP_* headers */ if (server_headers(clt, desc, server_fcgi_writeheader, ¶m) == -1) { errstr = "failed to encode param"; goto fail; } if (srv_conf->flags & SRVFLAG_TLS) if (fcgi_add_param(¶m, "HTTPS", "on", clt) == -1) { errstr = "failed to encode param"; goto fail; } (void)print_host(&clt->clt_ss, hbuf, sizeof(hbuf)); if (fcgi_add_param(¶m, "REMOTE_ADDR", hbuf, clt) == -1) { errstr = "failed to encode param"; goto fail; } (void)snprintf(hbuf, sizeof(hbuf), "%d", ntohs(clt->clt_port)); if (fcgi_add_param(¶m, "REMOTE_PORT", hbuf, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "REQUEST_METHOD", server_httpmethod_byid(desc->http_method), clt) == -1) { errstr = "failed to encode param"; goto fail; } if (!desc->http_query) { if (fcgi_add_param(¶m, "REQUEST_URI", desc->http_path, clt) == -1) { errstr = "failed to encode param"; goto fail; } } else { if (asprintf(&str, "%s?%s", desc->http_path, desc->http_query) == -1) { errstr = "failed to encode param"; goto fail; } ret = fcgi_add_param(¶m, "REQUEST_URI", str, clt); free(str); if (ret == -1) { errstr = "failed to encode param"; goto fail; } } (void)print_host(&clt->clt_srv_ss, hbuf, sizeof(hbuf)); if (fcgi_add_param(¶m, "SERVER_ADDR", hbuf, clt) == -1) { errstr = "failed to encode param"; goto fail; } (void)snprintf(hbuf, sizeof(hbuf), "%d", ntohs(server_socket_getport(&clt->clt_srv_ss))); if (fcgi_add_param(¶m, "SERVER_PORT", hbuf, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "SERVER_NAME", srv_conf->name, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "SERVER_PROTOCOL", desc->http_version, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "SERVER_SOFTWARE", HTTPD_SERVERNAME, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (param.total_len != 0) { /* send last params record */ if (bufferevent_write(clt->clt_srvbev, ¶m.buf, sizeof(struct fcgi_record_header) + ntohs(h->content_len)) == -1) { errstr = "failed to write to client evbuffer"; goto fail; } } /* send "no more params" message */ h->content_len = 0; if (bufferevent_write(clt->clt_srvbev, ¶m.buf, sizeof(struct fcgi_record_header)) == -1) { errstr = "failed to write to client evbuffer"; goto fail; } bufferevent_settimeout(clt->clt_srvbev, srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec); bufferevent_enable(clt->clt_srvbev, EV_READ|EV_WRITE); if (clt->clt_toread != 0) { server_read_httpcontent(clt->clt_bev, clt); bufferevent_enable(clt->clt_bev, EV_READ); } else { bufferevent_disable(clt->clt_bev, EV_READ); fcgi_add_stdin(clt, NULL); } if (strcmp(desc->http_version, "HTTP/1.1") == 0) { clt->clt_fcgi_chunked = 1; } else { /* HTTP/1.0 does not support chunked encoding */ clt->clt_fcgi_chunked = 0; clt->clt_persist = 0; } clt->clt_fcgi_end = 0; clt->clt_done = 0; free(script); return (0); fail: free(script); if (errstr == NULL) errstr = strerror(errno); server_abort_http(clt, 500, errstr); return (-1); }
void LibraryWatcher::ScanSubdirectory(const QString& path, const Subdirectory& subdir, ScanTransaction* t, bool force_noincremental) { QFileInfo path_info(path); QDir path_dir(path); // Do not scan symlinked dirs that are already in collection if (path_info.isSymLink()) { QString real_path = path_info.symLinkTarget(); for (const Directory& dir : watched_dirs_) { if (real_path.startsWith(dir.path)) { t->AddToProgress(1); return; } } } // Do not scan directories containing a .nomedia or .nomusic file if (path_dir.exists(kNoMediaFile) || path_dir.exists(kNoMusicFile)) { t->AddToProgress(1); return; } if (!t->ignores_mtime() && !force_noincremental && t->is_incremental() && subdir.mtime == path_info.lastModified().toTime_t()) { // The directory hasn't changed since last time t->AddToProgress(1); return; } QMap<QString, QStringList> album_art; QStringList files_on_disk; SubdirectoryList my_new_subdirs; // If a directory is moved then only its parent gets a changed notification, // so we need to look and see if any of our children don't exist any more. // If one has been removed, "rescan" it to get the deleted songs SubdirectoryList previous_subdirs = t->GetImmediateSubdirs(path); for (const Subdirectory& subdir : previous_subdirs) { if (!QFile::exists(subdir.path) && subdir.path != path) { t->AddToProgressMax(1); ScanSubdirectory(subdir.path, subdir, t, true); } } // First we "quickly" get a list of the files in the directory that we // think might be music. While we're here, we also look for new // subdirectories // and possible album artwork. QDirIterator it( path, QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot); while (it.hasNext()) { if (stop_requested_) return; QString child(it.next()); QFileInfo child_info(child); if (child_info.isDir()) { if (!child_info.isHidden() && !t->HasSeenSubdir(child)) { // We haven't seen this subdirectory before - add it to a list and // later we'll tell the backend about it and scan it. Subdirectory new_subdir; new_subdir.directory_id = -1; new_subdir.path = child; new_subdir.mtime = child_info.lastModified().toTime_t(); my_new_subdirs << new_subdir; } } else { QString ext_part(ExtensionPart(child)); QString dir_part(DirectoryPart(child)); if (sValidImages.contains(ext_part)) album_art[dir_part] << child; else if (!child_info.isHidden()) files_on_disk << child; } } if (stop_requested_) return; // Ask the database for a list of files in this directory SongList songs_in_db = t->FindSongsInSubdirectory(path); QSet<QString> cues_processed; // Now compare the list from the database with the list of files on disk for (const QString& file : files_on_disk) { if (stop_requested_) return; // associated cue QString matching_cue = NoExtensionPart(file) + ".cue"; Song matching_song; if (FindSongByPath(songs_in_db, file, &matching_song)) { uint matching_cue_mtime = GetMtimeForCue(matching_cue); // The song is in the database and still on disk. // Check the mtime to see if it's been changed since it was added. QFileInfo file_info(file); if (!file_info.exists()) { // Partially fixes race condition - if file was removed between being // added to the list and now. files_on_disk.removeAll(file); continue; } // cue sheet's path from library (if any) QString song_cue = matching_song.cue_path(); uint song_cue_mtime = GetMtimeForCue(song_cue); bool cue_deleted = song_cue_mtime == 0 && matching_song.has_cue(); bool cue_added = matching_cue_mtime != 0 && !matching_song.has_cue(); // watch out for cue songs which have their mtime equal to // qMax(media_file_mtime, cue_sheet_mtime) bool changed = (matching_song.mtime() != qMax(file_info.lastModified().toTime_t(), song_cue_mtime)) || cue_deleted || cue_added; // Also want to look to see whether the album art has changed QString image = ImageForSong(file, album_art); if ((matching_song.art_automatic().isEmpty() && !image.isEmpty()) || (!matching_song.art_automatic().isEmpty() && !matching_song.has_embedded_cover() && !QFile::exists(matching_song.art_automatic()))) { changed = true; } // the song's changed - reread the metadata from file if (t->ignores_mtime() || changed) { qLog(Debug) << file << "changed"; // if cue associated... if (!cue_deleted && (matching_song.has_cue() || cue_added)) { UpdateCueAssociatedSongs(file, path, matching_cue, image, t); // if no cue or it's about to lose it... } else { UpdateNonCueAssociatedSong(file, matching_song, image, cue_deleted, t); } } // nothing has changed - mark the song available without re-scanning if (matching_song.is_unavailable()) t->readded_songs << matching_song; } else { // The song is on disk but not in the DB SongList song_list = ScanNewFile(file, path, matching_cue, &cues_processed); if (song_list.isEmpty()) { continue; } qLog(Debug) << file << "created"; // choose an image for the song(s) QString image = ImageForSong(file, album_art); for (Song song : song_list) { song.set_directory_id(t->dir()); if (song.art_automatic().isEmpty()) song.set_art_automatic(image); t->new_songs << song; } } } // Look for deleted songs for (const Song& song : songs_in_db) { if (!song.is_unavailable() && !files_on_disk.contains(song.url().toLocalFile())) { qLog(Debug) << "Song deleted from disk:" << song.url().toLocalFile(); t->deleted_songs << song; } } // Add this subdir to the new or touched list Subdirectory updated_subdir; updated_subdir.directory_id = t->dir(); updated_subdir.mtime = path_info.exists() ? path_info.lastModified().toTime_t() : 0; updated_subdir.path = path; if (subdir.directory_id == -1) t->new_subdirs << updated_subdir; else t->touched_subdirs << updated_subdir; t->AddToProgress(1); // Recurse into the new subdirs that we found t->AddToProgressMax(my_new_subdirs.count()); for (const Subdirectory& my_new_subdir : my_new_subdirs) { if (stop_requested_) return; ScanSubdirectory(my_new_subdir.path, my_new_subdir, t, true); } }
static int cfs_open(const char *path, struct fuse_file_info *info) { FILE *temp_file; dir_entry *de = path_info(path); if (*temp_dir) { char tmp_path[PATH_MAX]; strncpy(tmp_path, path, PATH_MAX); char *pch; while((pch = strchr(tmp_path, '/'))) { *pch = '.'; } char file_path[PATH_MAX]; snprintf(file_path, PATH_MAX, "%s/.cloudfuse%ld-%s", temp_dir, (long)getpid(), tmp_path); if(access(file_path, F_OK) != -1) { // file exists temp_file = fopen(file_path, "r"); } else if (!(info->flags & O_WRONLY)) { // we need to lock on the filename another process could open the file // while we are writing to it and then only read part of the file // duplicate the directory caching datastructure to make the code easier // to understand. // each file in the cache needs: // filename, is_writing, last_closed, is_removing // the first time a file is opened a new entry is created in the cache // setting the filename and is_writing to true. This check needs to be // wrapped with a lock. // // each time a file is closed we set the last_closed for the file to now // and we check the cache for files whose last // closed is greater than cache_timeout, then start a new thread rming // that file. // TODO: just to prevent this craziness for now temp_file = fopen(file_path, "w+b"); if (!cloudfs_object_write_fp(path, temp_file)) { fclose(temp_file); return -ENOENT; } } } else { temp_file = tmpfile(); if (!(info->flags & O_WRONLY)) { if (!cloudfs_object_write_fp(path, temp_file)) { fclose(temp_file); return -ENOENT; } } } update_dir_cache(path, (de ? de->size : 0), 0, 0); openfile *of = (openfile *)malloc(sizeof(openfile)); of->fd = dup(fileno(temp_file)); if (of->fd == -1) return -ENOENT; fclose(temp_file); of->flags = info->flags; info->fh = (uintptr_t)of; info->direct_io = 1; return 0; }
static bool newer_than(const QString& path, const QDateTime& time) { QFileInfo path_info(path); if (!path_info.exists()) return false; else if (!path_info.isDir()) return path_info.lastModified() > time; IgnoreList ignore_list; stack<QString> relatives; relatives.push(""); while (!relatives.empty()) { const QString r = relatives.top(); relatives.pop(); // 处理特殊操作 if (r == SPECIAL_ID) { ignore_list.pop_config(); continue; } // 被忽略列表中标记为忽略 if (ignore_list.is_ignored(r)) continue; const QString p = path_join(path, r); if (!QFileInfo(p).isDir()) { if (QFileInfo(p).lastModified() > time) return true; // else continue } else { // 处理忽略列表 if (QFileInfo(path_join(p, IGNORE_FILE1)).exists()) { ignore_list.push_config(r, path_join(p, IGNORE_FILE1)); relatives.push(SPECIAL_ID); } else if (QFileInfo(path_join(p, IGNORE_FILE2)).exists()) { ignore_list.push_config(r, path_join(p, IGNORE_FILE2)); relatives.push(SPECIAL_ID); } else if (QFileInfo(path_join(p, IGNORE_FILE3)).exists()) { ignore_list.push_config(r, path_join(p, IGNORE_FILE3)); relatives.push(SPECIAL_ID); } // 遍历文件夹 QStringList children = QDir(p).entryList(QDir::Files | QDir::Dirs | QDir::AccessMask | QDir::NoDotAndDotDot); for (size_t i = 0, size = children.size(); i < size; ++i) { const QString& e = children.at(i); relatives.push(path_join(r, e)); } } } return false; }
static int cfs_getattr(const char* path, struct stat* stbuf) { debugf(DBG_LEVEL_NORM, KBLU "cfs_getattr(%s)", path); //return standard values for root folder if (!strcmp(path, "/")) { stbuf->st_uid = geteuid(); stbuf->st_gid = getegid(); stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 2; debug_list_cache_content(); debugf(DBG_LEVEL_NORM, KBLU "exit 0: cfs_getattr(%s)", path); return 0; } //get file. if not in cache will be downloaded. dir_entry* de = path_info(path); if (!de) { debug_list_cache_content(); debugf(DBG_LEVEL_NORM, KBLU"exit 1: cfs_getattr(%s) "KYEL"not-in-cache/cloud", path); return -ENOENT; } //lazzy download of file metadata, only when really needed if (option_get_extended_metadata && !de->metadata_downloaded) get_file_metadata(de); if (option_enable_chown) { stbuf->st_uid = de->uid; stbuf->st_gid = de->gid; } else { stbuf->st_uid = geteuid(); stbuf->st_gid = getegid(); } // change needed due to utimens stbuf->st_atime = de->atime.tv_sec; stbuf->st_atim.tv_nsec = de->atime.tv_nsec; stbuf->st_mtime = de->mtime.tv_sec; stbuf->st_mtim.tv_nsec = de->mtime.tv_nsec; stbuf->st_ctime = de->ctime.tv_sec; stbuf->st_ctim.tv_nsec = de->ctime.tv_nsec; char time_str[TIME_CHARS] = ""; get_timespec_as_str(&(de->atime), time_str, sizeof(time_str)); debugf(DBG_LEVEL_EXT, KCYN"cfs_getattr: atime=[%s]", time_str); get_timespec_as_str(&(de->mtime), time_str, sizeof(time_str)); debugf(DBG_LEVEL_EXT, KCYN"cfs_getattr: mtime=[%s]", time_str); get_timespec_as_str(&(de->ctime), time_str, sizeof(time_str)); debugf(DBG_LEVEL_EXT, KCYN"cfs_getattr: ctime=[%s]", time_str); int default_mode_dir, default_mode_file; if (option_enable_chmod) { default_mode_dir = de->chmod; default_mode_file = de->chmod; } else { default_mode_dir = 0755; default_mode_file = 0666; } if (de->isdir) { stbuf->st_size = 0; stbuf->st_mode = S_IFDIR | default_mode_dir; stbuf->st_nlink = 2; } else if (de->islink) { stbuf->st_size = 1; stbuf->st_mode = S_IFLNK | default_mode_dir; stbuf->st_nlink = 1; stbuf->st_size = de->size; /* calc. blocks as if 4K blocksize filesystem; stat uses units of 512B */ stbuf->st_blocks = ((4095 + de->size) / 4096) * 8; } else { stbuf->st_size = de->size; /* calc. blocks as if 4K blocksize filesystem; stat uses units of 512B */ stbuf->st_blocks = ((4095 + de->size) / 4096) * 8; stbuf->st_mode = S_IFREG | default_mode_file; stbuf->st_nlink = 1; } debugf(DBG_LEVEL_NORM, KBLU "exit 2: cfs_getattr(%s)", path); return 0; }
// open (download) file from cloud // todo: implement etag optimisation, download only if content changed, http://www.17od.com/2012/12/19/ten-useful-openstack-swift-features/ static int cfs_open(const char* path, struct fuse_file_info* info) { debugf(DBG_LEVEL_NORM, KBLU "cfs_open(%s)", path); FILE* temp_file = NULL; int errsv; dir_entry* de = path_info(path); if (*temp_dir) { char file_path_safe[NAME_MAX]; get_safe_cache_file_path(path, file_path_safe, temp_dir); debugf(DBG_LEVEL_EXT, "cfs_open: try open (%s)", file_path_safe); if (access(file_path_safe, F_OK) != -1) { // file exists temp_file = fopen(file_path_safe, "r"); errsv = errno; if (temp_file == NULL) { debugf(DBG_LEVEL_NORM, KRED"exit 0: cfs_open can't open temp_file=[%s] err=%d:%s", file_path_safe, errsv, strerror(errsv)); return -ENOENT; } else debugf(DBG_LEVEL_EXT, "cfs_open: file exists"); } else { errsv = errno; debugf(DBG_LEVEL_EXT, "cfs_open: file not in cache, err=%s", strerror(errsv)); //FIXME: commented out as this condition will not be meet in some odd cases and program will crash //if (!(info->flags & O_WRONLY)) { debugf(DBG_LEVEL_EXT, "cfs_open: opening for write"); // we need to lock on the filename another process could open the file // while we are writing to it and then only read part of the file // duplicate the directory caching datastructure to make the code easier // to understand. // each file in the cache needs: // filename, is_writing, last_closed, is_removing // the first time a file is opened a new entry is created in the cache // setting the filename and is_writing to true. This check needs to be // wrapped with a lock. // // each time a file is closed we set the last_closed for the file to now // and we check the cache for files whose last // closed is greater than cache_timeout, then start a new thread rming // that file. // TODO: just to prevent this craziness for now temp_file = fopen(file_path_safe, "w+b"); errsv = errno; if (temp_file == NULL) { debugf(DBG_LEVEL_NORM, KRED"exit 1: cfs_open cannot open temp_file=[%s] err=%d:%s", file_path_safe, errsv, strerror(errsv)); return -ENOENT; } if (!cloudfs_object_write_fp(path, temp_file)) { fclose(temp_file); debugf(DBG_LEVEL_NORM, KRED "exit 2: cfs_open(%s) cannot download/write", path); return -ENOENT; } } } else { temp_file = tmpfile(); if (temp_file == NULL) { debugf(DBG_LEVEL_NORM, KRED"exit 3: cfs_open cannot create temp_file err=%s", strerror(errno)); return -ENOENT; } if (!(info->flags & O_TRUNC)) { if (!cloudfs_object_write_fp(path, temp_file) && !(info->flags & O_CREAT)) { fclose(temp_file); debugf(DBG_LEVEL_NORM, KRED"exit 4: cfs_open(%s) cannot download/write", path); return -ENOENT; } } } update_dir_cache(path, (de ? de->size : 0), 0, 0); openfile* of = (openfile*)malloc(sizeof(openfile)); of->fd = dup(fileno(temp_file)); if (of->fd == -1) { //FIXME: potential leak if free not used? free(of); debugf(DBG_LEVEL_NORM, KRED "exit 5: cfs_open(%s) of->fd", path); return -ENOENT; } fclose(temp_file); //TODO: why this allocation to of? of->flags = info->flags; info->fh = (uintptr_t)of; info->direct_io = 1; info->nonseekable = 0; //FIXME: potential leak if free(of) not used? although if free(of) is used will generate bad descriptor errors debugf(DBG_LEVEL_NORM, KBLU "exit 6: cfs_open(%s)", path); return 0; }