correctly create test file
[cloudstore.git] / lib / CloudStore / API.pm
1 package CloudStore::API;
2 use warnings;
3 use strict;
4 use autodie;
5
6 use lib 'lib';
7 use base qw(CloudStore::Gearman);
8
9 use File::Path qw(make_path remove_tree);
10 use File::Find;
11 use Data::Dump qw(dump);
12
13 sub new {
14         my ($class,$group) = @_;
15
16         my ( undef, $dir, $port, undef ) = getgrnam($group) || die "can't find group $group: $!";
17         my $self = {
18                 passwd => '/var/lib/extrausers/passwd',
19                 PORT => $port,
20                 SLICE => $dir,
21         };
22         bless $self, $class;
23
24         $self->{md5} = $self->user_info('md5');
25
26         return $self;
27 }
28
29 sub user_info {
30         my ($self,$login) = @_;
31
32         my @n = qw/ login passwd uid gid quota comment gecos dir shell expire /;
33         my @p = $login =~ m/^\d+$/ ? getpwuid $login : getpwnam $login;
34         my $user;
35         $user->{$_} = shift @p foreach @n;
36         return $user;
37 }
38
39 sub create_user {
40         my ( $self, $new_email, $new_passwd, $new_quota ) = @_;
41
42         my $max_uid = 0;
43         my $found = 0;
44
45         open(my $fh, '<', $self->{passwd});
46         while(<$fh>) {
47                 my ( $login, $passwd, $uid, $gid, $email, $dir, $shell ) = split(/:/,$_);
48                 $max_uid = $uid if $uid > $max_uid;
49                 $found = $uid if $email eq $new_email;
50         }
51         close($fh);
52
53         if ( ! $found ) {
54                 $max_uid++;
55                 my $dir = "$self->{SLICE}/$max_uid";
56                 warn "# create_user $new_email $new_quota = $max_uid $dir";
57                 open(my $fh, '>>', $self->{passwd});
58                 print $fh "u$max_uid:$new_passwd:$max_uid:$self->{PORT}:$new_email:$dir:/bin/true\n";
59                 close($fh);
60                 $found = $max_uid;
61
62                 mkdir $dir;
63                 chown $max_uid, $self->{PORT}, $dir;
64
65                 open($fh, '>', "$dir/.secrets");
66                 print $fh "u$max_uid:$new_passwd\n";
67                 close $fh;
68         }
69
70         # FIXME update quota only on create?
71         $self->gearman_do( 'narada_s1_quota_set' => "$found $new_quota" );
72
73         return $found;
74 }
75
76 sub mkbasepath {
77         my ($self,$path,$opts) = @_;
78         $path =~ s{/[^/]+$}{};
79         make_path $path unless -d $path;
80 }
81
82 sub user_dir {
83         my ( $self,$user, $dir ) = @_;
84         $user = $self->user_info($user) unless ref $user eq 'HASH';
85         my $path;
86         if ( exists $user->{dir} ) {
87                 $path = $user->{dir} . '/' . $dir;
88         } else {
89                 die "no dir in ", dump $user;
90         }
91         $path =~ s{//+}{/}g;
92 #       warn "## user_dir $path";
93         return $path;
94 }
95
96 sub append {
97         my $self = shift @_;
98         my $user = shift @_;
99         my $path = $self->user_dir( $user => '.log');
100         my $line = join('#',@_);
101         open(my $fh, '>>', $path);
102         print $fh "$line\n";
103         close $fh;
104         warn "## $path $line\n";
105         $user = $self->user_info($user) unless ref $user eq 'HASH';
106 }
107
108 sub usage {
109         my ( $self, $user ) = @_;
110         $user = $self->user_info($user) unless ref $user eq 'HASH';
111         my $path = $self->user_dir( $user => '.log');
112         my $sum;
113         open(my $fh, '<', $path);
114         while(<$fh>) {
115                 chomp;
116                 my @v = split(/#/,$_);
117                 $sum->{ $v[0] } += $v[1];
118                 $sum->{_usage}  += $v[1];
119         }
120         my ( $usage, $quota ) = split(/ /,
121                 $self->gearman_do( 'narada_s1_quota_get' => $user->{uid} )
122         );
123         $sum->{_usage} += $usage;
124         $sum->{_quota} = $quota;
125         warn "## usage ",dump($user, $sum), $/;
126         return $sum;
127 }
128
129 sub send_file {
130         my ( $self, $f_uid,$f_path, $t_uid,$t_path ) = @_;
131
132         my $f = $self->user_info($f_uid);
133         my $t = $self->user_info($t_uid);
134
135         my $f_full = "$f->{dir}/$f_path";
136         my $t_full = "$t->{dir}/$t_path";
137
138         $self->mkbasepath( $t_full, { uid => $t->{uid} } );
139
140         my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat $f_full;
141         if ( $uid == $f->{uid} ) {
142                 warn "# send_file - move $f_uid $f_path to pool\n";
143                 chown $self->{md5}->{uid}, $self->{md5}->{gid}, $f_full;
144                 chmod oct("0444"), $f_full;
145                 $self->append( $f, 'sent', -s $f_full, $t->{uid}, $f_path );
146         } elsif ( $uid == $self->{md5}->{uid} ) {
147                 warn "# send_file - shared $f_full\n";
148         }
149
150         $self->delete( $t, $t_path ) if -e $t_full;
151
152         link $f_full, $t_full; 
153         $self->append( $t, 'recv', -s $t_full, $f->{uid}, $t_path );
154 }
155
156 sub rename_file {
157         my ( $self, $user, $from, $to ) = @_;
158         $user = $self->user_info($user) unless ref $user eq 'HASH';
159
160         $self->append( $user, 'rename', $from, $to );
161 }
162
163
164 sub delete {
165         my ( $self, $user, $path ) = @_;
166         $user = $self->user_info($user) unless ref $user eq 'HASH';
167
168         my $deleted_size = 0;
169         my $full_path = "$user->{dir}/$path";
170
171         if ( -d $full_path ) {
172
173                 find({ 
174                 no_chdir => 1,
175                 wanted => sub {
176                         return if -d $_;
177                         my ($uid,$size) = (stat($_))[4,7];
178                         warn "## find $uid $size $_\n";
179                         if ( $uid == $self->{md5}->{uid} ) {
180                                 $deleted_size += $size;
181                         }
182                 }}, $full_path);
183
184                 remove_tree $full_path;
185         } else {
186                 $deleted_size += -s $full_path;
187                 unlink $full_path;
188         }
189
190         warn "delete $deleted_size bytes shared\n";
191
192         $self->append( $user, 'delete', -$deleted_size, $user->{uid}, $path );
193
194         $self->md5sum($user)->out( $path );
195 }
196
197 1;