1 package CloudStore::API;
7 use base qw(CloudStore::Gearman CloudStore::MD5sum);
9 use File::Path qw(make_path remove_tree);
11 use Data::Dump qw(dump);
15 my ($class,$slice) = @_;
17 my ( undef, $dir, $port, undef ) = getgrnam($slice);
18 die "can't find group $slice: $!" unless $dir && $port;
20 passwd => '/var/lib/extrausers/passwd',
26 $self->{md5} = $self->user_info("md5") || die "can't find user md5";
27 $self->{md5}->{dir} = "$dir/md5";
28 if ( ! -e $self->{md5}->{dir} ) {
29 make_path $self->{md5}->{dir}, { uid => $self->{md5}->{uid}, gid => $self->{md5}->{gid} };
30 warn "## CREATED md5pool $self->{md5}->{dir}\n";
33 my $name = $self->{SLICE};
36 $self->{quota} = $name . '_quota';
42 my ($self,$login) = @_;
44 confess "need login" unless $login;
46 my @n = qw/ login passwd uid gid quota comment gecos dir shell expire /;
47 my @p = $login =~ m/^\d+$/ ? getpwuid $login : getpwnam $login;
48 die "user_info $login: $@" if $@;
50 $user->{$_} = shift @p foreach @n;
55 my ( $self, $new_email, $new_passwd, $new_quota ) = @_;
60 open(my $fh, '<', $self->{passwd});
62 my ( $login, $passwd, $uid, $gid, $email, $dir, $shell ) = split(/:/,$_);
63 $max_uid = $uid if $uid > $max_uid;
64 $found = $uid if $email eq $new_email;
70 my $dir = "$self->{SLICE}/$max_uid";
71 warn "# create_user $new_email $new_quota = $max_uid $dir";
72 open(my $fh, '>>', $self->{passwd});
73 print $fh "u$max_uid:$new_passwd:$max_uid:$self->{PORT}:$new_email:$dir:/bin/true\n";
78 chown $max_uid, $self->{PORT}, $dir;
80 my $path = "$dir/.meta/secrets";
81 $self->mkbasepath($path);
82 open($fh, '>', $path);
83 print $fh "u$max_uid:$new_passwd\n";
87 # FIXME update quota only on create?
88 $self->gearman_do( "$self->{quota}_set" => "$found $new_quota" );
94 my ($self,$path,$opts) = @_;
95 $path =~ s{/[^/]+$}{};
96 make_path $path unless -d $path;
100 my ( $self,$user, $dir ) = @_;
101 $user = $self->user_info($user) unless ref $user eq 'HASH';
103 if ( exists $user->{dir} ) {
104 $path = $user->{dir} . '/.meta/' . $dir;
106 die "no dir in ", dump $user;
111 $self->mkbasepath( $path, { uid => $user->{uid} } );
112 open(my $fh, '>', $path);
114 chown $user->{uid}, $user->{gid}, $path;
115 warn "# user_dir created $path\n";
118 #warn "### user_dir $path";
124 $self->append_meta( 'usage', @_ );
131 my $path = $self->user_dir( $user => $log );
133 $delimiter = ' ' if $log =~ m/md5sum$/;
134 my $line = join($delimiter,@_);
135 open(my $fh, '>>', $path);
138 warn "## $path $line\n";
142 my ( $self, $user ) = @_;
143 $user = $self->user_info($user) unless ref $user eq 'HASH';
144 my $path = $self->user_dir( $user => 'usage');
146 open(my $fh, '<', $path);
149 my @v = split(/#/,$_);
150 $sum->{ $v[0] } += $v[1];
151 $sum->{_usage} += $v[1];
153 my ( $usage, $quota ) = split(/ /,
154 $self->gearman_do( "$self->{quota}_get" => $user->{uid} )
156 $sum->{_usage} += $usage;
157 $sum->{_quota} = $quota;
158 warn "## usage ",dump($user, $sum), $/;
163 my ( $self, $f_uid,$f_path, $t_uid,$t_path ) = @_;
165 my $f = $self->user_info($f_uid);
166 die "FIXME not on current slice" if $f->{dir} !~ m/^$self->{SLICE}/;
167 my $t = $self->user_info($t_uid);
169 my $f_full = "$f->{dir}/$f_path";
170 my $t_full = "$t->{dir}/$t_path";
172 $self->mkbasepath( $t_full, { uid => $t->{uid} } );
174 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat $f_full;
175 if ( $uid == $f->{uid} ) {
176 warn "# send_file - move $f_uid $f_path to pool\n";
177 chown $self->{md5}->{uid}, $self->{md5}->{gid}, $f_full;
178 chmod oct("0444"), $f_full;
179 $self->append( $f, 'sent', -s $f_full, $t->{uid}, $f_path );
180 } elsif ( $uid == $self->{md5}->{uid} ) {
181 warn "# send_file - shared $f_full\n";
184 $self->delete( $t, $t_path ) if -e $t_full;
186 my $ok = link $f_full, $t_full;
187 $self->append( $t, 'recv', -s $t_full, $f->{uid}, $t_path );
189 $ok = -s $t_full if $ok; # replace true with file size
192 if ( $f->{uid} == $self->{md5}->{uid} ) {
193 $md5 = $f_path; # we don't have local md5sum db for md5 user!
195 $md5 = $self->md5sum($f)->get( $f_path );
196 $self->md5sum_close($f);
199 warn "ERROR: no md5 for $f_path";
203 $self->md5sum($t)->put( $t_path => $md5 );
204 $self->md5sum_close($t);
206 $self->append_meta('md5sum', $t, $md5 => $t_path ); # md5sum for received files!
212 my ( $self, $user, $from, $to ) = @_;
213 $user = $self->user_info($user) unless ref $user eq 'HASH';
215 $self->mkbasepath( "$user->{dir}/$to", { uid => $user->{uid}, gid => $user->{gid} } );
216 my $ok = rename "$user->{dir}/$from", "$user->{dir}/$to";
218 my $md5 = $self->md5sum($user)->get( $from );
220 warn "ERROR: no md5sum for $from";
221 return $ok; # XXX our internal error
224 $self->md5sum($user)->out( $from );
225 $self->md5sum($user)->put( $to => $md5 );
226 $self->md5sum_close($user);
228 $self->append_meta('md5sum', $user, 'rename' => $from );
229 $self->append_meta('md5sum', $user, $md5 => $from );
236 my ( $self, $user, $path ) = @_;
237 $user = $self->user_info($user) unless ref $user eq 'HASH';
239 my $deleted_size = 0;
240 my $full_path = "$user->{dir}/$path";
242 if ( -d $full_path ) {
248 my ($uid,$size) = (stat($_))[4,7];
249 warn "## find $uid $size $_\n";
250 if ( $uid == $self->{md5}->{uid} ) {
251 $deleted_size += $size;
255 remove_tree $full_path;
257 $deleted_size += -s $full_path;
261 warn "delete $deleted_size bytes shared\n";
263 $self->append( $user, 'delete', -$deleted_size, $user->{uid}, $path );
264 $self->append_meta('md5sum', $user, 'delete', $path );
266 $self->md5sum($user)->out( $path );
267 $self->md5sum_close($user);
273 my ( $self, $user, $path ) = @_;
274 $user = $self->user_info($user) unless ref $user eq 'HASH';
276 my $full_path = "$user->{dir}/$path";
277 return -s $full_path;